codekingpro/portable-devtools
114k
1import math2 3import urwid4 5from mitmproxy.tools.console import grideditor6from mitmproxy.tools.console import keymap7from mitmproxy.tools.console import layoutwidget8from mitmproxy.tools.console import signals9 10 11class SimpleOverlay(urwid.Overlay, layoutwidget.LayoutWidget):12 def __init__(self, master, widget, parent, width, valign="middle"):13 self.widget = widget14 self.master = master15 super().__init__(16 widget, parent, align="center", width=width, valign=valign, height="pack"17 )18 19 @property20 def keyctx(self):21 return getattr(self.widget, "keyctx")22 23 # mypy: Cannot override writeable attribute with read-only property24 @keyctx.setter25 def keyctx(self, value):26 raise RuntimeError # pragma: no cover27 28 def key_responder(self):29 return self.widget.key_responder()30 31 def focus_changed(self):32 return self.widget.focus_changed()33 34 def view_changed(self):35 return self.widget.view_changed()36 37 def layout_popping(self):38 return self.widget.layout_popping()39 40 41class Choice(urwid.WidgetWrap):42 def __init__(self, txt, focus, current, shortcut):43 if shortcut:44 selection_type = "option_selected_key" if focus else "key"45 txt = [(selection_type, shortcut), ") ", txt]46 else:47 txt = " " + txt48 if current:49 s = "option_active_selected" if focus else "option_active"50 else:51 s = "option_selected" if focus else "text"52 super().__init__(53 urwid.AttrMap(54 urwid.Padding(urwid.Text(txt)),55 s,56 )57 )58 59 def selectable(self):60 return True61 62 def keypress(self, size, key):63 return key64 65 66class ChooserListWalker(urwid.ListWalker):67 shortcuts = "123456789abcdefghijklmnoprstuvwxyz"68 69 def __init__(self, choices, current):70 self.index = 071 self.choices = choices72 self.current = current73 74 def _get(self, idx, focus):75 c = self.choices[idx]76 return Choice(c, focus, c == self.current, self.shortcuts[idx : idx + 1])77 78 def set_focus(self, index):79 self.index = index80 81 def get_focus(self):82 return self._get(self.index, True), self.index83 84 def get_next(self, pos):85 if pos >= len(self.choices) - 1:86 return None, None87 pos = pos + 188 return self._get(pos, False), pos89 90 def get_prev(self, pos):91 pos = pos - 192 if pos < 0:93 return None, None94 return self._get(pos, False), pos95 96 def choice_by_shortcut(self, shortcut):97 for i, choice in enumerate(self.choices):98 if shortcut == self.shortcuts[i : i + 1]:99 return choice100 return None101 102 103class Chooser(urwid.WidgetWrap, layoutwidget.LayoutWidget):104 keyctx = "chooser"105 106 def __init__(self, master, title, choices, current, callback):107 self.master = master108 self.choices = choices109 self.callback = callback110 choicewidth = max(len(i) for i in choices)111 self.width = max(choicewidth, len(title)) + 7112 113 self.walker = ChooserListWalker(choices, current)114 super().__init__(115 urwid.AttrMap(116 urwid.LineBox(117 urwid.BoxAdapter(urwid.ListBox(self.walker), len(choices)),118 title=title,119 ),120 "background",121 )122 )123 124 def selectable(self):125 return True126 127 def keypress(self, size, key):128 key = self.master.keymap.handle_only("chooser", key)129 choice = self.walker.choice_by_shortcut(key)130 if choice:131 self.callback(choice)132 signals.pop_view_state.send()133 return134 if key == "m_select":135 self.callback(self.choices[self.walker.index])136 signals.pop_view_state.send()137 return138 elif key in ["q", "esc"]:139 signals.pop_view_state.send()140 return141 142 binding = self.master.keymap.get("global", key)143 # This is extremely awkward. We need a better way to match nav keys only.144 if binding and binding.command.startswith("console.nav"):145 self.master.keymap.handle("global", key)146 elif key in keymap.navkeys:147 return super().keypress(size, key)148 149 150class OptionsOverlay(urwid.WidgetWrap, layoutwidget.LayoutWidget):151 keyctx = "grideditor"152 153 def __init__(self, master, name, vals, vspace):154 """155 vspace: how much vertical space to keep clear156 """157 cols, rows = master.ui.get_cols_rows()158 self.ge = grideditor.OptionsEditor(master, name, vals)159 super().__init__(160 urwid.AttrMap(161 urwid.LineBox(urwid.BoxAdapter(self.ge, rows - vspace), title=name),162 "background",163 )164 )165 self.width = math.ceil(cols * 0.8)166 167 def key_responder(self):168 return self.ge.key_responder()169 170 def layout_popping(self):171 return self.ge.layout_popping()172 173 174class DataViewerOverlay(urwid.WidgetWrap, layoutwidget.LayoutWidget):175 keyctx = "dataviewer"176 177 def __init__(self, master, vals):178 """179 vspace: how much vertical space to keep clear180 """181 cols, rows = master.ui.get_cols_rows()182 self.ge = grideditor.DataViewer(master, vals)183 super().__init__(184 urwid.AttrMap(185 urwid.LineBox(urwid.BoxAdapter(self.ge, rows - 5), title="Data viewer"),186 "background",187 )188 )189 self.width = math.ceil(cols * 0.8)190 191 def key_responder(self):192 return self.ge.key_responder()193 194 def layout_popping(self):195 return self.ge.layout_popping()196 