console options: add an overlay grid editor for sequence options

This commit is contained in:
Aldo Cortesi 2017-03-19 10:25:07 +13:00 committed by Aldo Cortesi
parent 4e24c95a61
commit 49b0a67eb9
7 changed files with 84 additions and 18 deletions

View File

@ -182,12 +182,12 @@ class GridWalker(urwid.ListWalker):
self.edit_row = GridRow( self.edit_row = GridRow(
self.focus_col, True, self.editor, self.lst[self.focus] self.focus_col, True, self.editor, self.lst[self.focus]
) )
self.editor.master.loop.widget.footer.update(FOOTER_EDITING) signals.footer_help.send(self, helptext=FOOTER_EDITING)
self._modified() self._modified()
def stop_edit(self): def stop_edit(self):
if self.edit_row: if self.edit_row:
self.editor.master.loop.widget.footer.update(FOOTER) signals.footer_help.send(self, helptext=FOOTER)
try: try:
val = self.edit_row.edit_col.get_data() val = self.edit_row.edit_col.get_data()
except ValueError: except ValueError:
@ -276,9 +276,11 @@ class GridEditor(urwid.WidgetWrap):
first_width = max(len(r), first_width) first_width = max(len(r), first_width)
self.first_width = min(first_width, FIRST_WIDTH_MAX) self.first_width = min(first_width, FIRST_WIDTH_MAX)
title = urwid.Text(self.title) title = None
title = urwid.Padding(title, align="left", width=("relative", 100)) if self.title:
title = urwid.AttrWrap(title, "heading") title = urwid.Text(self.title)
title = urwid.Padding(title, align="left", width=("relative", 100))
title = urwid.AttrWrap(title, "heading")
headings = [] headings = []
for i, col in enumerate(self.columns): for i, col in enumerate(self.columns):
@ -297,10 +299,10 @@ class GridEditor(urwid.WidgetWrap):
self.lb = GridListBox(self.walker) self.lb = GridListBox(self.walker)
w = urwid.Frame( w = urwid.Frame(
self.lb, self.lb,
header=urwid.Pile([title, h]) header=urwid.Pile([title, h]) if title else None
) )
super().__init__(w) super().__init__(w)
self.master.loop.widget.footer.update("") signals.footer_help.send(self, helptext="")
self.show_empty_msg() self.show_empty_msg()
def show_empty_msg(self): def show_empty_msg(self):

View File

@ -245,3 +245,20 @@ class SetCookieEditor(base.GridEditor):
] ]
) )
return vals return vals
class OptionsEditor(base.GridEditor):
title = None
columns = [
col_text.Column("")
]
def __init__(self, master, name, vals):
self.name = name
super().__init__(master, [[i] for i in vals], self.callback)
def callback(self, vals):
setattr(self.master.options, self.name, [i[0] for i in vals])
def is_error(self, col, val):
pass

View File

@ -1,7 +1,7 @@
import urwid import urwid
import blinker import blinker
import textwrap import textwrap
from typing import Optional from typing import Optional, Sequence
from mitmproxy import exceptions from mitmproxy import exceptions
from mitmproxy.tools.console import common from mitmproxy.tools.console import common
@ -213,6 +213,16 @@ class OptionsList(urwid.ListBox):
self.master.options.setter(foc.opt.name) self.master.options.setter(foc.opt.name)
) )
) )
elif foc.opt.typespec == Sequence[str]:
self.master.overlay(
overlay.OptionsOverlay(
self.master,
foc.opt.name,
foc.opt.current()
)
)
else:
raise NotImplementedError()
return super().keypress(size, key) return super().keypress(size, key)
@ -269,4 +279,3 @@ class Options(urwid.Pile):
i = self.widget_list.index(self.focus_item) i = self.widget_list.index(self.focus_item)
tsize = self.get_item_size(size, i, True, item_rows) tsize = self.get_item_size(size, i, True, item_rows)
return self.focus_item.keypress(tsize, key) return self.focus_item.keypress(tsize, key)

View File

@ -1,6 +1,10 @@
import math
import urwid
from mitmproxy.tools.console import common from mitmproxy.tools.console import common
from mitmproxy.tools.console import signals from mitmproxy.tools.console import signals
import urwid from mitmproxy.tools.console import grideditor
class SimpleOverlay(urwid.Overlay): class SimpleOverlay(urwid.Overlay):
@ -15,9 +19,11 @@ class SimpleOverlay(urwid.Overlay):
) )
def keypress(self, size, key): def keypress(self, size, key):
key = super().keypress(size, key)
if key == "esc": if key == "esc":
signals.pop_view_state.send(self) signals.pop_view_state.send(self)
return super().keypress(size, key) else:
return key
class Choice(urwid.WidgetWrap): class Choice(urwid.WidgetWrap):
@ -97,4 +103,22 @@ class Chooser(urwid.WidgetWrap):
if key == "enter": if key == "enter":
self.callback(self.choices[self.walker.index]) self.callback(self.choices[self.walker.index])
signals.pop_view_state.send(self) signals.pop_view_state.send(self)
return super().keypress(size, key) return super().keypress(size, key)
class OptionsOverlay(urwid.WidgetWrap):
def __init__(self, master, name, vals):
cols, rows = master.ui.get_cols_rows()
super().__init__(
urwid.AttrWrap(
urwid.LineBox(
urwid.BoxAdapter(
grideditor.OptionsEditor(master, name, vals),
math.ceil(rows * 0.5)
),
title="text"
),
"background"
)
)
self.width = math.ceil(cols * 0.8)

View File

@ -30,6 +30,9 @@ call_in = blinker.Signal()
# Focus the body, footer or header of the main window # Focus the body, footer or header of the main window
focus = blinker.Signal() focus = blinker.Signal()
# Focus the body, footer or header of the main window
footer_help = blinker.Signal()
# Fired when settings change # Fired when settings change
update_settings = blinker.Signal() update_settings = blinker.Signal()

View File

@ -5,7 +5,6 @@ import urwid
from mitmproxy.tools.console import common from mitmproxy.tools.console import common
from mitmproxy.tools.console import pathedit from mitmproxy.tools.console import pathedit
from mitmproxy.tools.console import signals from mitmproxy.tools.console import signals
from mitmproxy.utils import human
class PromptPath: class PromptPath:
@ -143,10 +142,15 @@ class StatusBar(urwid.WidgetWrap):
super().__init__(urwid.Pile([self.ib, self.master.ab])) super().__init__(urwid.Pile([self.ib, self.master.ab]))
signals.update_settings.connect(self.sig_update) signals.update_settings.connect(self.sig_update)
signals.flowlist_change.connect(self.sig_update) signals.flowlist_change.connect(self.sig_update)
signals.footer_help.connect(self.sig_footer_help)
master.options.changed.connect(self.sig_update) master.options.changed.connect(self.sig_update)
master.view.focus.sig_change.connect(self.sig_update) master.view.focus.sig_change.connect(self.sig_update)
self.redraw() self.redraw()
def sig_footer_help(self, sender, helptext):
self.helptext = helptext
self.redraw()
def sig_update(self, sender, updated=None): def sig_update(self, sender, updated=None):
self.redraw() self.redraw()
@ -281,10 +285,5 @@ class StatusBar(urwid.WidgetWrap):
]), "heading") ]), "heading")
self.ib._w = status self.ib._w = status
def update(self, text):
self.helptext = text
self.redraw()
self.master.loop.draw_screen()
def selectable(self): def selectable(self):
return True return True

View File

@ -140,6 +140,18 @@ class Rec():
def test_subscribe(): def test_subscribe():
o = TO() o = TO()
r = Rec() r = Rec()
# pytest.raises keeps a reference here that interferes with the cleanup test
# further down.
try:
o.subscribe(r, ["unknown"])
except exceptions.OptionsError:
pass
else:
raise AssertionError
assert len(o.changed.receivers) == 0
o.subscribe(r, ["two"]) o.subscribe(r, ["two"])
o.one = 2 o.one = 2
assert not r.called assert not r.called