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:
parent
7b65d54624
commit
ae05b66a47
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
),
|
||||
|
|
|
@ -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
|
||||
),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue