rewrite Selector to work like an HTML <select> widget

This was way way harder than it seemed like it should be, but it seems a lot
nicer to me.

For https://bugs.launchpad.net/ubuntu/+source/subiquity/+bug/1654387
This commit is contained in:
Michael Hudson-Doyle 2017-01-09 16:11:32 +13:00
parent 7b65d54624
commit ae05b66a47
7 changed files with 92 additions and 31 deletions

View File

@ -70,7 +70,7 @@ class BcacheView(BaseView):
selector = Selector(avail_devs)
self.selected_disks[section] = selector
items.append(Color.string_input(Pile(selector.group),
items.append(Color.string_input(selector,
focus_map="string_input focus"))
return Pile(items)

View File

@ -53,16 +53,13 @@ class AddFormatView(BaseView):
]
return Pile(buttons)
def _format_edit(self):
return Pile(self.fstype.group)
def _container(self):
total_items = [
Columns(
[
("weight", 0.2, Text("Format", align="right")),
("weight", 0.3,
Color.string_input(self._format_edit(),
Color.string_input(self.fstype,
focus_map="string_input focus"))
], dividechars=4
),

View File

@ -88,9 +88,6 @@ class AddPartitionView(BaseView):
]
return Pile(buttons)
def _format_edit(self):
return Pile(self.fstype.group)
def _container(self):
total_items = [
Columns(
@ -115,7 +112,7 @@ class AddPartitionView(BaseView):
[
("weight", 0.2, Text("Format", align="right")),
("weight", 0.3,
Color.string_input(self._format_edit(),
Color.string_input(self.fstype,
focus_map="string_input focus"))
], dividechars=4
),

View File

@ -85,7 +85,7 @@ class RaidView(BaseView):
[
("weight", 0.2, Text("RAID Level", align="right")),
("weight", 0.3,
Color.string_input(Pile(self.raid_level.group),
Color.string_input(self.raid_level,
focus_map="string_input focus"))
],
dividechars=4

View File

@ -130,7 +130,8 @@ class Application:
additional_opts = {
'screen': urwid.raw_display.Screen(),
'unhandled_input': self.header_hotkeys,
'handle_mouse': False
'handle_mouse': False,
'pop_ups': True,
}
if self.common['opts'].run_on_serial:
palette = STYLES_MONO

View File

@ -16,7 +16,20 @@
""" Re-usable input widgets
"""
from urwid import (Edit, IntEdit, RadioButton, WidgetWrap)
from urwid import (
ACTIVATE,
AttrWrap,
connect_signal,
Edit,
Filler,
IntEdit,
LineBox,
Pile,
PopUpLauncher,
SelectableIcon,
TOP,
WidgetWrap,
)
import logging
import re
@ -134,28 +147,81 @@ class IntegerEditor(WidgetWrap):
return self._edit.get_edit_text()
class Selector(WidgetWrap):
""" Radio selection list of options
"""
def __init__(self, opts):
"""
:param list opts: list of options to display
"""
self.opts = opts
self.group = []
self._add_options()
class _PopUpButton(SelectableIcon):
"""It looks like a radio button, but it just emits 'click' on activation."""
def _add_options(self):
for item in self.opts:
RadioButton(self.group, item)
signals = ['click']
states = {
True: "(X) ",
False: "( ) ",
}
def __init__(self, option, state):
super().__init__(self.states[state] + option, 4)
def keypress(self, size, key):
if self._command_map[key] != ACTIVATE:
return key
self._emit('click')
class _PopUpSelectDialog(WidgetWrap):
"""A list of PopUpButtons with a box around them."""
def __init__(self, parent, cur_index):
self.parent = parent
group = []
for i, option in enumerate(self.parent._options):
btn = _PopUpButton(option, state=i==cur_index)
connect_signal(btn, 'click', self.click, i)
group.append(btn)
pile = Pile(group)
pile.set_focus(group[cur_index])
fill = Filler(pile, valign=TOP)
super().__init__(LineBox(AttrWrap(fill, 'menu_button')))
def click(self, btn, index):
self.parent.index = index
self.parent.close_pop_up()
class Selector(PopUpLauncher):
"""A widget that allows the user to chose between options by popping up this list of options.
(A bit like <select> in an HTML form).
"""
def __init__(self, opts, index=0):
self._options = opts
self._button = SelectableIcon("", 0)
self.index = index
super().__init__(self._button)
def keypress(self, size, key):
if self._command_map[key] != ACTIVATE:
return key
self.open_pop_up()
@property
def index(self):
return self._index
@index.setter
def index(self, val):
self._button.set_text(self._options[val])
self._index = val
@property
def value(self):
for item in self.group:
log.debug(item)
if item.get_state():
return item.label
return "Unknown option"
return self._options[self._index]
def create_pop_up(self):
return _PopUpSelectDialog(self, self.index)
def get_pop_up_parameters(self):
width = max(map(len, self._options)) + 7 # longest option + line on left, "(?) ", space, line on right
return {'left':-5, 'top':-self.index-1, 'overlay_width':width, 'overlay_height':len(self._options) + 2}
class YesNo(Selector):

View File

@ -74,7 +74,7 @@ class NetworkBondInterfacesView(BaseView):
[
("weight", 0.2, Text("Bonding Mode", align="right")),
("weight", 0.3,
Color.string_input(Pile(self.bond_mode.group),
Color.string_input(self.bond_mode,
focus_map="string_input focus"))
],
dividechars=4