import textwrap import urwid from mitmproxy.tools.console import layoutwidget from mitmproxy.tools.console import signals from mitmproxy.utils import signals as utils_signals HELP_HEIGHT = 5 class KeyItem(urwid.WidgetWrap): def __init__(self, walker, binding, focused): self.walker, self.binding, self.focused = walker, binding, focused super().__init__(self.get_widget()) def get_widget(self): cmd = textwrap.dedent(self.binding.command).strip() parts = [ (4, urwid.Text([("focus", ">> " if self.focused else " ")])), (10, urwid.Text([("title", self.binding.key)])), (12, urwid.Text([("highlight", "\n".join(self.binding.contexts))])), urwid.Text([("text", cmd)]), ] return urwid.Columns(parts) def get_edit_text(self): return self._w[1].get_edit_text() def selectable(self): return True def keypress(self, size, key): return key class KeyListWalker(urwid.ListWalker): def __init__(self, master, keybinding_focus_change): self.keybinding_focus_change = keybinding_focus_change self.master = master self.index = 0 self.focusobj = None self.bindings = list(master.keymap.list("all")) self.set_focus(0) signals.keybindings_change.connect(self.sig_modified) def sig_modified(self): self.bindings = list(self.master.keymap.list("all")) self.set_focus(min(self.index, len(self.bindings) - 1)) self._modified() def get_edit_text(self): return self.focus_obj.get_edit_text() def _get(self, pos): binding = self.bindings[pos] return KeyItem(self, binding, pos == self.index) def get_focus(self): return self.focus_obj, self.index def set_focus(self, index): binding = self.bindings[index] self.index = index self.focus_obj = self._get(self.index) self.keybinding_focus_change.send(binding.help or "") self._modified() def get_next(self, pos): if pos >= len(self.bindings) - 1: return None, None pos = pos + 1 return self._get(pos), pos def get_prev(self, pos): pos = pos - 1 if pos < 0: return None, None return self._get(pos), pos def positions(self, reverse=False): if reverse: return reversed(range(len(self.bindings))) else: return range(len(self.bindings)) class KeyList(urwid.ListBox): def __init__(self, master, keybinding_focus_change): self.master = master self.walker = KeyListWalker(master, keybinding_focus_change) super().__init__(self.walker) def keypress(self, size, key): if key == "m_select": foc, idx = self.get_focus() # Act here elif key == "m_start": self.set_focus(0) self.walker._modified() elif key == "m_end": self.set_focus(len(self.walker.bindings) - 1) self.walker._modified() return super().keypress(size, key) class KeyHelp(urwid.Frame): def __init__(self, master, keybinding_focus_change): self.master = master super().__init__(self.widget("")) self.set_active(False) keybinding_focus_change.connect(self.sig_mod) def set_active(self, val): h = urwid.Text("Key Binding Help") style = "heading" if val else "heading_inactive" self.header = urwid.AttrMap(h, style) def widget(self, txt): cols, _ = self.master.ui.get_cols_rows() return urwid.ListBox([urwid.Text(i) for i in textwrap.wrap(txt, cols)]) def sig_mod(self, txt): self.body = self.widget(txt) class KeyBindings(urwid.Pile, layoutwidget.LayoutWidget): title = "Key Bindings" keyctx = "keybindings" focus_position: int def __init__(self, master): keybinding_focus_change = utils_signals.SyncSignal(lambda text: None) oh = KeyHelp(master, keybinding_focus_change) super().__init__( [ KeyList(master, keybinding_focus_change), (HELP_HEIGHT, oh), ] ) self.master = master def get_focused_binding(self): if self.focus_position != 0: return None f = self.contents[0][0] return f.walker.get_focus()[0].binding def keypress(self, size, key): if key == "m_next": self.focus_position = (self.focus_position + 1) % len(self.widget_list) self.contents[1][0].set_active(self.focus_position == 1) key = None # This is essentially a copypasta from urwid.Pile's keypress handler. # So much for "closed for modification, but open for extension". item_rows = None if len(size) == 2: item_rows = self.get_item_rows(size, focus=True) tsize = self.get_item_size(size, self.focus_position, True, item_rows) return self.focus.keypress(tsize, key)