Added configurable glyph sets

This commit is contained in:
Mark Qvist 2021-07-02 13:35:10 +02:00
parent 3c949ce73e
commit 6537a3d5cd
6 changed files with 103 additions and 43 deletions

View File

@ -244,6 +244,9 @@ class NomadNetworkApp:
if not "editor" in self.config["textui"]: if not "editor" in self.config["textui"]:
self.config["textui"]["editor"] = "editor" self.config["textui"]["editor"] = "editor"
if not "glyphs" in self.config["textui"]:
self.config["textui"]["glyphs"] = "unicode"
if not "mouse_enabled" in self.config["textui"]: if not "mouse_enabled" in self.config["textui"]:
self.config["textui"]["mouse_enabled"] = True self.config["textui"]["mouse_enabled"] = True
else: else:
@ -350,6 +353,16 @@ colormode = 16
# colormode = 256 # colormode = 256
# colormode = 24bit # colormode = 24bit
# By default, unicode glyphs are used. If
# you have a Nerd Font installed, you can
# enable this for a better user interface.
# You can also enable plain text glyphs if
# your terminal doesn't support unicode.
# glyphs = plain
glyphs = unicode
# glyphs = nerdfont
# You can specify whether mouse events # You can specify whether mouse events
# should be considered as input to the # should be considered as input to the
# application. On by default. # application. On by default.

View File

@ -42,6 +42,29 @@ THEMES = {
] ]
} }
GLYPHSETS = {
"plain": 1,
"unicode": 2,
"nerdfont": 3
}
GLYPHS = {
# Glyph name # Plain # Unicode # Nerd Font
("check", "=", "\u2713", "\u2713"),
("cross", "X", "\u2715", "\u2715"),
("unknown", "?", "?", "?"),
("lock", "E", "\U0001f512", "\uf023"),
("unlock", "!", "\U0001f513", "\uf09c"),
("arrow_r", "->", "\u2192", "\u2192"),
("arrow_l", "<-", "\u2190", "\u2190"),
("arrow_u", "/\\", "\u2191", "\u2191"),
("arrow_d", "\\/", "\u2193", "\u2193"),
("warning", "!", "\u26a0", "\uf12a"),
("info", "i", "\u2139", "\ufb4d"),
("divider1", "-", "\u2504", "\u2504"),
("decoration_menu", "", "", " \uf93a"),
}
class TextUI: class TextUI:
def __init__(self): def __init__(self):
@ -65,6 +88,20 @@ class TextUI:
palette = THEMES[theme] palette = THEMES[theme]
if self.app.config["textui"]["glyphs"] == "plain":
glyphset = "plain"
elif self.app.config["textui"]["glyphs"] == "unicoode":
glyphset = "unicode"
elif self.app.config["textui"]["glyphs"] == "nerdfont":
glyphset = "nerdfont"
else:
glyphset = "unicode"
self.glyphs = {}
for glyph in GLYPHS:
self.glyphs[glyph[0]] = glyph[GLYPHSETS[glyphset]]
self.screen = urwid.raw_display.Screen() self.screen = urwid.raw_display.Screen()
self.screen.register_palette(palette) self.screen.register_palette(palette)

View File

@ -127,6 +127,7 @@ class ConversationsDisplay():
self.columns_widget.contents[0] = (overlay, options) self.columns_widget.contents[0] = (overlay, options)
def edit_selected_in_directory(self): def edit_selected_in_directory(self):
g = self.app.ui.glyphs
self.dialog_open = True self.dialog_open = True
source_hash_text = self.ilb.get_selected_item().source_hash source_hash_text = self.ilb.get_selected_item().source_hash
display_name = self.ilb.get_selected_item().display_name display_name = self.ilb.get_selected_item().display_name
@ -195,7 +196,7 @@ class ConversationsDisplay():
source_is_known = self.app.directory.is_known(bytes.fromhex(source_hash_text)) source_is_known = self.app.directory.is_known(bytes.fromhex(source_hash_text))
if source_is_known: if source_is_known:
known_section = urwid.Divider("\u2504") known_section = urwid.Divider(g["divider1"])
else: else:
def query_action(sender, user_data): def query_action(sender, user_data):
self.close_conversation_by_hash(user_data) self.close_conversation_by_hash(user_data)
@ -206,12 +207,12 @@ class ConversationsDisplay():
(urwid.Button("OK", on_press=dismiss_dialog), options) (urwid.Button("OK", on_press=dismiss_dialog), options)
] ]
query_button = urwid.Button("Query network for keys", on_press=query_action, user_data=source_hash_text) query_button = urwid.Button("Query network for keys", on_press=query_action, user_data=source_hash_text)
known_section = urwid.Pile([urwid.Divider("\u2504"), urwid.Text("\u2139\n", align="center"), urwid.Text("The identity of this peer is not known, and you cannot currently communicate.\n", align="center"), query_button, urwid.Divider("\u2504")]) known_section = urwid.Pile([urwid.Divider(g["divider1"]), urwid.Text(g["info"]+"\n", align="center"), urwid.Text("The identity of this peer is not known, and you cannot currently communicate.\n", align="center"), query_button, urwid.Divider(g["divider1"])])
dialog_pile = urwid.Pile([ dialog_pile = urwid.Pile([
selected_id_widget, selected_id_widget,
e_name, e_name,
urwid.Divider("\u2504"), urwid.Divider(g["divider1"]),
r_untrusted, r_untrusted,
r_unknown, r_unknown,
r_trusted, r_trusted,
@ -364,8 +365,10 @@ class ConversationsDisplay():
display_name = conversation[1] display_name = conversation[1]
source_hash = conversation[0] source_hash = conversation[0]
g = self.app.ui.glyphs
if trust_level == DirectoryEntry.UNTRUSTED: if trust_level == DirectoryEntry.UNTRUSTED:
symbol = "\u2715" symbol = g["cross"]
style = "list_untrusted" style = "list_untrusted"
focus_style = "list_focus_untrusted" focus_style = "list_focus_untrusted"
elif trust_level == DirectoryEntry.UNKNOWN: elif trust_level == DirectoryEntry.UNKNOWN:
@ -373,15 +376,15 @@ class ConversationsDisplay():
style = "list_unknown" style = "list_unknown"
focus_style = "list_focus" focus_style = "list_focus"
elif trust_level == DirectoryEntry.TRUSTED: elif trust_level == DirectoryEntry.TRUSTED:
symbol = "\u2713" symbol = g["check"]
style = "list_trusted" style = "list_trusted"
focus_style = "list_focus_trusted" focus_style = "list_focus_trusted"
elif trust_level == DirectoryEntry.WARNING: elif trust_level == DirectoryEntry.WARNING:
symbol = "\u26A0" symbol = g["warning"]
style = "list_warning" style = "list_warning"
focus_style = "list_focus" focus_style = "list_focus"
else: else:
symbol = "\u26A0" symbol = g["warning"]
style = "list_untrusted" style = "list_untrusted"
focus_style = "list_focus_untrusted" focus_style = "list_focus_untrusted"
@ -471,6 +474,8 @@ class ConversationFrame(urwid.Frame):
class ConversationWidget(urwid.WidgetWrap): class ConversationWidget(urwid.WidgetWrap):
def __init__(self, source_hash): def __init__(self, source_hash):
self.app = nomadnet.NomadNetworkApp.get_shared_instance()
g = self.app.ui.glyphs
if source_hash == None: if source_hash == None:
self.frame = None self.frame = None
display_widget = urwid.LineBox(urwid.Filler(urwid.Text("\n No conversation selected"), "top")) display_widget = urwid.LineBox(urwid.Filler(urwid.Text("\n No conversation selected"), "top"))
@ -500,7 +505,7 @@ class ConversationWidget(urwid.WidgetWrap):
header = None header = None
if self.conversation.trust_level == DirectoryEntry.UNTRUSTED: if self.conversation.trust_level == DirectoryEntry.UNTRUSTED:
header = urwid.AttrMap(urwid.Padding(urwid.Text("\u26A0 Warning: Conversation with untrusted peer \u26A0", align="center")), "msg_warning_untrusted") header = urwid.AttrMap(urwid.Padding(urwid.Text(g["warning"]+" Warning: Conversation with untrusted peer "+g["warning"], align="center")), "msg_warning_untrusted")
self.minimal_editor = urwid.AttrMap(msg_editor, "msg_editor") self.minimal_editor = urwid.AttrMap(msg_editor, "msg_editor")
self.minimal_editor.name = "minimal_editor" self.minimal_editor.name = "minimal_editor"
@ -548,12 +553,13 @@ class ConversationWidget(urwid.WidgetWrap):
self.full_editor_active = True self.full_editor_active = True
def check_editor_allowed(self): def check_editor_allowed(self):
g = self.app.ui.glyphs
if self.frame: if self.frame:
allowed = nomadnet.NomadNetworkApp.get_shared_instance().directory.is_known(bytes.fromhex(self.source_hash)) allowed = nomadnet.NomadNetworkApp.get_shared_instance().directory.is_known(bytes.fromhex(self.source_hash))
if allowed: if allowed:
self.frame.contents["footer"] = (self.minimal_editor, None) self.frame.contents["footer"] = (self.minimal_editor, None)
else: else:
warning = urwid.AttrMap(urwid.Padding(urwid.Text("\u2139 You cannot currently communicate with this peer, since it's identity keys are unknown", align="center")), "msg_header_caution") warning = urwid.AttrMap(urwid.Padding(urwid.Text(g["info"]+" You cannot currently communicate with this peer, since it's identity keys are unknown", align="center")), "msg_header_caution")
self.frame.contents["footer"] = (warning, None) self.frame.contents["footer"] = (warning, None)
def toggle_focus_area(self): def toggle_focus_area(self):
@ -630,34 +636,35 @@ class ConversationWidget(urwid.WidgetWrap):
class LXMessageWidget(urwid.WidgetWrap): class LXMessageWidget(urwid.WidgetWrap):
def __init__(self, message): def __init__(self, message):
app = nomadnet.NomadNetworkApp.get_shared_instance() app = nomadnet.NomadNetworkApp.get_shared_instance()
g = app.ui.glyphs
self.timestamp = message.get_timestamp() self.timestamp = message.get_timestamp()
time_format = app.time_format time_format = app.time_format
message_time = datetime.fromtimestamp(self.timestamp) message_time = datetime.fromtimestamp(self.timestamp)
encryption_string = "" encryption_string = ""
if message.get_transport_encrypted(): if message.get_transport_encrypted():
encryption_string = " [\U0001F512"+str(message.get_transport_encryption())+"]" encryption_string = " ["+g["lock"]+" "+str(message.get_transport_encryption())+"]"
else: else:
encryption_string = " [\U0001F513"+str(message.get_transport_encryption())+"]" encryption_string = " ["+g["unlock"]+" "+str(message.get_transport_encryption())+"]"
title_string = message_time.strftime(time_format)+encryption_string title_string = message_time.strftime(time_format)+encryption_string
if app.lxmf_destination.hash == message.lxm.source_hash: if app.lxmf_destination.hash == message.lxm.source_hash:
if message.lxm.state == LXMF.LXMessage.DELIVERED: if message.lxm.state == LXMF.LXMessage.DELIVERED:
header_style = "msg_header_delivered" header_style = "msg_header_delivered"
title_string = "\u2713 " + title_string title_string = g["check"]+" "+title_string
elif message.lxm.state == LXMF.LXMessage.FAILED: elif message.lxm.state == LXMF.LXMessage.FAILED:
header_style = "msg_header_failed" header_style = "msg_header_failed"
title_string = "\u2715 " + title_string title_string = g["cross"]+" "+title_string
else: else:
header_style = "msg_header_sent" header_style = "msg_header_sent"
title_string = "\u2192 " + title_string title_string = g["arrow_r"]+" "+title_string
else: else:
if message.signature_validated(): if message.signature_validated():
header_style = "msg_header_ok" header_style = "msg_header_ok"
title_string = "\u2713 " + title_string title_string = g["check"]+" "+title_string
else: else:
header_style = "msg_header_caution" header_style = "msg_header_caution"
title_string = "\u26A0 "+message.get_signature_description() + "\n " + title_string title_string = g["warning"]+" "+message.get_signature_description() + "\n " + title_string
if message.get_title() != "": if message.get_title() != "":
title_string += " | " + message.get_title() title_string += " | " + message.get_title()

View File

@ -14,11 +14,6 @@ class LogDisplay():
self.app = app self.app = app
self.log_term = LogTerminal(self.app) self.log_term = LogTerminal(self.app)
pile = urwid.Pile([
("weight", 90, self.log_term),
("fixed", 1, urwid.Text(("body_text", "Log Display \U0001F332"))),
])
self.shortcuts_display = LogDisplayShortcuts(self.app) self.shortcuts_display = LogDisplayShortcuts(self.app)
self.widget = urwid.LineBox(self.log_term) self.widget = urwid.LineBox(self.log_term)

View File

@ -140,8 +140,9 @@ class MenuColumns(urwid.Columns):
class MenuDisplay(): class MenuDisplay():
def __init__(self, app, handler): def __init__(self, app, handler):
self.app = app self.app = app
g = self.app.ui.glyphs
menu_text = ("pack", urwid.Text(" \U00002638")) menu_text = ("pack", urwid.Text(g["decoration_menu"]))
button_network = (11, MenuButton("Network", on_press=handler.show_network)) button_network = (11, MenuButton("Network", on_press=handler.show_network))
button_conversations = (17, MenuButton("Conversations", on_press=handler.show_conversations)) button_conversations = (17, MenuButton("Conversations", on_press=handler.show_conversations))
button_directory = (13, MenuButton("Directory", on_press=handler.show_directory)) button_directory = (13, MenuButton("Directory", on_press=handler.show_directory))

View File

@ -8,8 +8,9 @@ from nomadnet.vendor.additional_urwid_widgets import IndicativeListBox, MODIFIER
class NetworkDisplayShortcuts(): class NetworkDisplayShortcuts():
def __init__(self, app): def __init__(self, app):
self.app = app self.app = app
g = app.ui.glyphs
self.widget = urwid.AttrMap(urwid.Text("[C-\u2191\u2193] Navigate announces"), "shortcutbar") self.widget = urwid.AttrMap(urwid.Text("[C-"+g["arrow_u"]+g["arrow_d"]+"] Navigate announces"), "shortcutbar")
class DialogLineBox(urwid.LineBox): class DialogLineBox(urwid.LineBox):
@ -49,6 +50,7 @@ class AnnounceInfo(urwid.WidgetWrap):
def __init__(self, announce, parent, app): def __init__(self, announce, parent, app):
self.app = nomadnet.NomadNetworkApp.get_shared_instance() self.app = nomadnet.NomadNetworkApp.get_shared_instance()
self.parent = self.app.ui.main_display.sub_displays.network_display self.parent = self.app.ui.main_display.sub_displays.network_display
g = self.app.ui.glyphs
source_hash = announce[1] source_hash = announce[1]
time_format = app.time_format time_format = app.time_format
@ -70,23 +72,23 @@ class AnnounceInfo(urwid.WidgetWrap):
if trust_level == DirectoryEntry.UNTRUSTED: if trust_level == DirectoryEntry.UNTRUSTED:
trust_str = "Untrusted" trust_str = "Untrusted"
symbol = "\u2715" symbol = g["cross"]
style = "list_untrusted" style = "list_untrusted"
elif trust_level == DirectoryEntry.UNKNOWN: elif trust_level == DirectoryEntry.UNKNOWN:
trust_str = "Unknown" trust_str = "Unknown"
symbol = "?" symbol = g["unknown"]
style = "list_unknown" style = "list_unknown"
elif trust_level == DirectoryEntry.TRUSTED: elif trust_level == DirectoryEntry.TRUSTED:
trust_str = "Trusted" trust_str = "Trusted"
symbol = "\u2713" symbol = g["check"]
style = "list_trusted" style = "list_trusted"
elif trust_level == DirectoryEntry.WARNING: elif trust_level == DirectoryEntry.WARNING:
trust_str = "Warning" trust_str = "Warning"
symbol = "\u26A0" symbol = g["warning"]
style = "list_warning" style = "list_warning"
else: else:
trust_str = "Untrusted" trust_str = "Warning"
symbol = "\u26A0" symbol = g["warning"]
style = "list_untrusted" style = "list_untrusted"
def show_announce_stream(sender): def show_announce_stream(sender):
@ -118,9 +120,9 @@ class AnnounceInfo(urwid.WidgetWrap):
urwid.Text("Addr : "+addr_str, align="left"), urwid.Text("Addr : "+addr_str, align="left"),
urwid.Text("Name : "+display_str, align="left"), urwid.Text("Name : "+display_str, align="left"),
urwid.Text(["Trust : ", (style, trust_str)], align="left"), urwid.Text(["Trust : ", (style, trust_str)], align="left"),
urwid.Divider("\u2504"), urwid.Divider(g["divider1"]),
urwid.Text(["Announce Data: \n", (data_style, data_str)], align="left"), urwid.Text(["Announce Data: \n", (data_style, data_str)], align="left"),
urwid.Divider("\u2504"), urwid.Divider(g["divider1"]),
urwid.Columns([("weight", 0.45, urwid.Button("Back", on_press=show_announce_stream)), ("weight", 0.1, urwid.Text("")), ("weight", 0.45, urwid.Button("Converse", on_press=converse))]) urwid.Columns([("weight", 0.45, urwid.Button("Back", on_press=show_announce_stream)), ("weight", 0.1, urwid.Text("")), ("weight", 0.45, urwid.Button("Converse", on_press=converse))])
]) ])
@ -138,28 +140,29 @@ class AnnounceStreamEntry(urwid.WidgetWrap):
time_format = app.time_format time_format = app.time_format
dt = datetime.fromtimestamp(self.timestamp) dt = datetime.fromtimestamp(self.timestamp)
ts_string = dt.strftime(time_format) ts_string = dt.strftime(time_format)
g = self.app.ui.glyphs
trust_level = self.app.directory.trust_level(source_hash) trust_level = self.app.directory.trust_level(source_hash)
display_str = self.app.directory.simplest_display_str(source_hash) display_str = self.app.directory.simplest_display_str(source_hash)
if trust_level == DirectoryEntry.UNTRUSTED: if trust_level == DirectoryEntry.UNTRUSTED:
symbol = "\u2715" symbol = g["cross"]
style = "list_untrusted" style = "list_untrusted"
focus_style = "list_focus_untrusted" focus_style = "list_focus_untrusted"
elif trust_level == DirectoryEntry.UNKNOWN: elif trust_level == DirectoryEntry.UNKNOWN:
symbol = "?" symbol = g["unknown"]
style = "list_unknown" style = "list_unknown"
focus_style = "list_focus" focus_style = "list_focus"
elif trust_level == DirectoryEntry.TRUSTED: elif trust_level == DirectoryEntry.TRUSTED:
symbol = "\u2713" symbol = g["check"]
style = "list_trusted" style = "list_trusted"
focus_style = "list_focus_trusted" focus_style = "list_focus_trusted"
elif trust_level == DirectoryEntry.WARNING: elif trust_level == DirectoryEntry.WARNING:
symbol = "\u26A0" symbol = g["warning"]
style = "list_warning" style = "list_warning"
focus_style = "list_focus" focus_style = "list_focus"
else: else:
symbol = "\u26A0" symbol = g["warning"]
style = "list_untrusted" style = "list_untrusted"
focus_style = "list_focus_untrusted" focus_style = "list_focus_untrusted"
@ -268,6 +271,7 @@ class KnownNodes(urwid.WidgetWrap):
def __init__(self, app): def __init__(self, app):
self.app = app self.app = app
self.node_list = app.directory.known_nodes() self.node_list = app.directory.known_nodes()
g = self.app.ui.glyphs
self.ilb = IndicativeListBox( self.ilb = IndicativeListBox(
self.make_node_widgets(), self.make_node_widgets(),
@ -283,7 +287,7 @@ class KnownNodes(urwid.WidgetWrap):
else: else:
self.no_content = True self.no_content = True
widget_style = "inactive_text" widget_style = "inactive_text"
self.display_widget = urwid.Pile([urwid.Text(("warning_text", "- i -\n"), align="center"), SelectText(("warning_text", "Currently, no nodes are known\n\n"), align="center")]) self.display_widget = urwid.Pile([urwid.Text(("warning_text", g["info"]+"\n"), align="center"), SelectText(("warning_text", "Currently, no nodes are known\n\n"), align="center")])
urwid.WidgetWrap.__init__(self, urwid.AttrMap(urwid.LineBox(self.display_widget, title="Known Nodes"), widget_style)) urwid.WidgetWrap.__init__(self, urwid.AttrMap(urwid.LineBox(self.display_widget, title="Known Nodes"), widget_style))
@ -345,6 +349,7 @@ class LocalPeer(urwid.WidgetWrap):
def __init__(self, app, parent): def __init__(self, app, parent):
self.app = app self.app = app
self.parent = parent self.parent = parent
g = self.app.ui.glyphs
self.dialog_open = False self.dialog_open = False
display_name = self.app.lxmf_destination.display_name display_name = self.app.lxmf_destination.display_name
if display_name == None: if display_name == None:
@ -364,7 +369,7 @@ class LocalPeer(urwid.WidgetWrap):
urwid.Pile([ urwid.Pile([
urwid.Text("\n\n\nSaved\n\n", align="center"), urwid.Text("\n\n\nSaved\n\n", align="center"),
urwid.Button("OK", on_press=dismiss_dialog) urwid.Button("OK", on_press=dismiss_dialog)
]), title="i" ]), title=g["info"]
) )
dialog.delegate = self dialog.delegate = self
bottom = self bottom = self
@ -387,7 +392,7 @@ class LocalPeer(urwid.WidgetWrap):
urwid.Pile([ urwid.Pile([
urwid.Text("\n\n\nAnnounce Sent\n\n", align="center"), urwid.Text("\n\n\nAnnounce Sent\n\n", align="center"),
urwid.Button("OK", on_press=dismiss_dialog) urwid.Button("OK", on_press=dismiss_dialog)
]), title="i" ]), title=g["info"]
) )
dialog.delegate = self dialog.delegate = self
bottom = self bottom = self
@ -416,10 +421,10 @@ class LocalPeer(urwid.WidgetWrap):
[ [
t_id, t_id,
e_name, e_name,
urwid.Divider("\u2504"), urwid.Divider(g["divider1"]),
self.t_last_announce, self.t_last_announce,
announce_button, announce_button,
urwid.Divider("\u2504"), urwid.Divider(g["divider1"]),
urwid.Columns([("weight", 0.45, urwid.Button("Save", on_press=save_query)), ("weight", 0.1, urwid.Text("")), ("weight", 0.45, urwid.Button("Node Cfg", on_press=node_settings_query))]) urwid.Columns([("weight", 0.45, urwid.Button("Save", on_press=save_query)), ("weight", 0.1, urwid.Text("")), ("weight", 0.45, urwid.Button("Node Cfg", on_press=node_settings_query))])
] ]
) )
@ -434,6 +439,7 @@ class NodeSettings(urwid.WidgetWrap):
def __init__(self, app, parent): def __init__(self, app, parent):
self.app = app self.app = app
self.parent = parent self.parent = parent
g = self.app.ui.glyphs
def show_peer_info(sender): def show_peer_info(sender):
options = self.parent.left_pile.options(height_type="pack", height_amount=None) options = self.parent.left_pile.options(height_type="pack", height_amount=None)
@ -441,7 +447,7 @@ class NodeSettings(urwid.WidgetWrap):
widget_style = "inactive_text" widget_style = "inactive_text"
pile = urwid.Pile([ pile = urwid.Pile([
urwid.Text("- i -\n", align="center"), urwid.Text("\n"+g["info"], align="center"),
urwid.Text("\nNode Hosting currently unavailable\n\n", align="center"), urwid.Text("\nNode Hosting currently unavailable\n\n", align="center"),
urwid.Padding(urwid.Button("Back", on_press=show_peer_info), "center", "pack") urwid.Padding(urwid.Button("Back", on_press=show_peer_info), "center", "pack")
]) ])
@ -517,6 +523,7 @@ class NetworkDisplay():
def __init__(self, app): def __init__(self, app):
self.app = app self.app = app
g = self.app.ui.glyphs
self.known_nodes_display = KnownNodes(self.app) self.known_nodes_display = KnownNodes(self.app)
self.network_stats_display = NetworkStats(self.app, self) self.network_stats_display = NetworkStats(self.app, self)
@ -532,7 +539,7 @@ class NetworkDisplay():
]) ])
self.left_area = self.left_pile self.left_area = self.left_pile
self.right_area = urwid.AttrMap(urwid.LineBox(urwid.Filler(urwid.Text("Disconnected\n\u2190 \u2192", align="center"), "middle"), title="Remote Node"), "inactive_text") self.right_area = urwid.AttrMap(urwid.LineBox(urwid.Filler(urwid.Text("Disconnected\n"+g["arrow_l"]+" "+g["arrow_r"], align="center"), "middle"), title="Remote Node"), "inactive_text")
self.columns = urwid.Columns( self.columns = urwid.Columns(
[ [