add actionmenu widget
This commit is contained in:
parent
77a693c4fc
commit
de80a06f47
|
@ -0,0 +1,134 @@
|
|||
# Copyright 2018 Canonical, Ltd.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from urwid import (
|
||||
ACTIVATE,
|
||||
AttrWrap,
|
||||
Button,
|
||||
connect_signal,
|
||||
LineBox,
|
||||
PopUpLauncher,
|
||||
SelectableIcon,
|
||||
Text,
|
||||
WidgetWrap,
|
||||
Widget,
|
||||
)
|
||||
|
||||
from subiquitycore.ui.container import Columns, ListBox
|
||||
from subiquitycore.ui.selector import Option
|
||||
from subiquitycore.ui.utils import Color
|
||||
|
||||
|
||||
class ActionBackButton(Button):
|
||||
button_left = Text("<")
|
||||
button_right = Text("")
|
||||
|
||||
|
||||
class ActionMenuButton(Button):
|
||||
button_left = Text("")
|
||||
button_right = Text(">")
|
||||
|
||||
|
||||
class _ActionMenuDialog(WidgetWrap):
|
||||
"""A list of action buttons with a box around them."""
|
||||
|
||||
def __init__(self, parent):
|
||||
self.parent = parent
|
||||
close = ActionBackButton("(close)")
|
||||
connect_signal(close, "click", self.close)
|
||||
group = [Color.menu_button(close)]
|
||||
width = 0
|
||||
for i, option in enumerate(self.parent._options):
|
||||
if option.enabled:
|
||||
if isinstance(option.label, Widget):
|
||||
btn = option.label
|
||||
else:
|
||||
btn = Color.menu_button(ActionMenuButton(option.label))
|
||||
width = max(width, len(btn.base_widget.label))
|
||||
connect_signal(
|
||||
btn.base_widget, 'click', self.click, option.value)
|
||||
else:
|
||||
label = option.label
|
||||
if isinstance(label, Widget):
|
||||
label = label.base_widget.label
|
||||
width = max(width, len(label))
|
||||
btn = Columns([
|
||||
('fixed', 1, Text("")),
|
||||
Text(label),
|
||||
('fixed', 1, Text(">")),
|
||||
], dividechars=1)
|
||||
btn = AttrWrap(btn, 'info_minor')
|
||||
group.append(btn)
|
||||
self.width = width
|
||||
super().__init__(LineBox(ListBox(group)))
|
||||
|
||||
def close(self, sender):
|
||||
self.parent.close_pop_up()
|
||||
|
||||
def click(self, btn, value):
|
||||
self.parent._action(value)
|
||||
self.parent.close_pop_up()
|
||||
|
||||
def keypress(self, size, key):
|
||||
if key == 'esc':
|
||||
self.parent.close_pop_up()
|
||||
else:
|
||||
return super().keypress(size, key)
|
||||
|
||||
|
||||
class ActionMenu(PopUpLauncher):
|
||||
|
||||
icon = ">"
|
||||
|
||||
signals = ['action', 'open', 'close']
|
||||
|
||||
def __init__(self, opts):
|
||||
self._options = []
|
||||
for opt in opts:
|
||||
if not isinstance(opt, Option):
|
||||
opt = Option(opt)
|
||||
self._options.append(opt)
|
||||
self._button = SelectableIcon(self.icon, 0)
|
||||
super().__init__(self._button)
|
||||
self._dialog = _ActionMenuDialog(self)
|
||||
|
||||
def keypress(self, size, key):
|
||||
if self._command_map[key] != ACTIVATE:
|
||||
return key
|
||||
self.open_pop_up()
|
||||
|
||||
def _action(self, action):
|
||||
self._emit("action", action)
|
||||
|
||||
def open_pop_up(self):
|
||||
self._dialog._w.base_widget.focus_position = 0
|
||||
self._emit("open")
|
||||
super().open_pop_up()
|
||||
|
||||
def close_pop_up(self):
|
||||
self._emit("close")
|
||||
super().close_pop_up()
|
||||
|
||||
def create_pop_up(self):
|
||||
return self._dialog
|
||||
|
||||
def get_pop_up_parameters(self):
|
||||
width = self._dialog.width + 7
|
||||
return {
|
||||
'left': 1,
|
||||
'top': -1,
|
||||
'overlay_width': width,
|
||||
'overlay_height': len(self._options) + 3,
|
||||
}
|
|
@ -66,8 +66,9 @@ Example:
|
|||
from collections import defaultdict
|
||||
import logging
|
||||
|
||||
from subiquitycore.ui.container import Columns, Pile, WidgetWrap
|
||||
|
||||
from subiquitycore.ui.actionmenu import ActionMenu
|
||||
from subiquitycore.ui.container import Columns, Pile, WidgetWrap
|
||||
|
||||
import attr
|
||||
|
||||
|
@ -108,7 +109,7 @@ def widget_width(w):
|
|||
"""Return the natural width of the widget w."""
|
||||
if isinstance(w, urwid.CheckBox):
|
||||
return widget_width(w._wrapped_widget)
|
||||
elif isinstance(w, urwid.AttrMap):
|
||||
elif isinstance(w, (ActionMenu, urwid.AttrMap)):
|
||||
return widget_width(w._original_widget)
|
||||
elif isinstance(w, urwid.Text):
|
||||
return len(demarkup(w.text))
|
||||
|
|
Loading…
Reference in New Issue