|
@@ -1,20 +1,36 @@
|
|
#
|
|
#
|
|
-# Copyright (c) Daniel Sheffield 2021 - 2022
|
|
|
|
|
|
+# Copyright (c) Daniel Sheffield 2021 - 2023
|
|
#
|
|
#
|
|
# All rights reserved
|
|
# All rights reserved
|
|
#
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED AS IS WITHOUT WARRANTY
|
|
# THIS SOFTWARE IS PROVIDED AS IS WITHOUT WARRANTY
|
|
from decimal import Decimal
|
|
from decimal import Decimal
|
|
-from typing import Any, Callable, Iterable, List, Union
|
|
|
|
-
|
|
|
|
-import urwid
|
|
|
|
-from additional_urwid_widgets import IndicativeListBox
|
|
|
|
|
|
+from typing import Any, Callable, Iterable, Union
|
|
from itertools import chain, product
|
|
from itertools import chain, product
|
|
-from urwid import numedit
|
|
|
|
|
|
+from additional_urwid_widgets import IndicativeListBox
|
|
|
|
+
|
|
|
|
+from urwid import (
|
|
|
|
+ AttrMap,
|
|
|
|
+ AttrWrap,
|
|
|
|
+ BarGraph,
|
|
|
|
+ Button,
|
|
|
|
+ connect_signal,
|
|
|
|
+ CheckBox,
|
|
|
|
+ Columns,
|
|
|
|
+ Edit,
|
|
|
|
+ GraphVScale,
|
|
|
|
+ Pile,
|
|
|
|
+ SimpleFocusListWalker,
|
|
|
|
+ Text,
|
|
|
|
+ WidgetWrap,
|
|
|
|
+)
|
|
|
|
+from urwid.numedit import (
|
|
|
|
+ FloatEdit,
|
|
|
|
+)
|
|
from urwid.wimp import PopUpLauncher
|
|
from urwid.wimp import PopUpLauncher
|
|
|
|
|
|
-class AutoCompleteEdit(urwid.Edit):
|
|
|
|
- signals = [ *urwid.Edit.signals, 'apply', 'open' ]
|
|
|
|
|
|
+class AutoCompleteEdit(Edit):
|
|
|
|
+ signals = [ *Edit.signals, 'apply', 'open' ]
|
|
|
|
|
|
def __init__(self, name: str, *args: Iterable, **kwargs):
|
|
def __init__(self, name: str, *args: Iterable, **kwargs):
|
|
if isinstance(name, tuple):
|
|
if isinstance(name, tuple):
|
|
@@ -32,15 +48,15 @@ class AutoCompleteEdit(urwid.Edit):
|
|
def keypress(self, size, key):
|
|
def keypress(self, size, key):
|
|
if key == 'enter':
|
|
if key == 'enter':
|
|
self._emit('apply', self.name)
|
|
self._emit('apply', self.name)
|
|
- return
|
|
|
|
- elif key == 'delete':
|
|
|
|
|
|
+ return None
|
|
|
|
+ if key == 'delete':
|
|
self.set_edit_text('')
|
|
self.set_edit_text('')
|
|
- return
|
|
|
|
|
|
+ return None
|
|
|
|
|
|
- return urwid.Edit.keypress(self, (size[0],), key)
|
|
|
|
|
|
+ return super().keypress(size, key)
|
|
|
|
|
|
-class AutoCompleteFloatEdit(numedit.FloatEdit):
|
|
|
|
- signals = [ *numedit.FloatEdit.signals, 'apply', 'open' ]
|
|
|
|
|
|
+class AutoCompleteFloatEdit(FloatEdit):
|
|
|
|
+ signals = [ *FloatEdit.signals, 'apply', 'open' ]
|
|
|
|
|
|
def __init__(self, name: str, *args: Iterable, **kwargs: dict):
|
|
def __init__(self, name: str, *args: Iterable, **kwargs: dict):
|
|
self.last_val = None
|
|
self.last_val = None
|
|
@@ -101,32 +117,32 @@ class AutoCompleteFloatEdit(numedit.FloatEdit):
|
|
if self.op in ops:
|
|
if self.op in ops:
|
|
self.calc()
|
|
self.calc()
|
|
self.set_op(key)
|
|
self.set_op(key)
|
|
- return
|
|
|
|
- elif key == 'enter':
|
|
|
|
|
|
+ return None
|
|
|
|
+ if key == 'enter':
|
|
if self.get_edit_text() == '' or self.value() == Decimal(0.0):
|
|
if self.get_edit_text() == '' or self.value() == Decimal(0.0):
|
|
self._emit('apply', self.name)
|
|
self._emit('apply', self.name)
|
|
- return
|
|
|
|
|
|
+ return None
|
|
self.calc()
|
|
self.calc()
|
|
- return
|
|
|
|
- elif key == '=':
|
|
|
|
|
|
+ return None
|
|
|
|
+ if key == '=':
|
|
self.calc()
|
|
self.calc()
|
|
- return
|
|
|
|
- elif key == 'delete':
|
|
|
|
|
|
+ return None
|
|
|
|
+ if key == 'delete':
|
|
self.set_edit_text('')
|
|
self.set_edit_text('')
|
|
self.op = '='
|
|
self.op = '='
|
|
self.update_caption()
|
|
self.update_caption()
|
|
- return
|
|
|
|
|
|
+ return None
|
|
|
|
|
|
- return numedit.FloatEdit.keypress(self, (size[0],), key)
|
|
|
|
|
|
+ return super().keypress(size, key)
|
|
|
|
|
|
-class NoTabCheckBox(urwid.CheckBox):
|
|
|
|
|
|
+class NoTabCheckBox(CheckBox):
|
|
|
|
+
|
|
def keypress(self, size, key):
|
|
def keypress(self, size, key):
|
|
if not isinstance(key, tuple) and key == 'tab':
|
|
if not isinstance(key, tuple) and key == 'tab':
|
|
- return
|
|
|
|
- else:
|
|
|
|
- return super().keypress(size, key)
|
|
|
|
|
|
+ return None
|
|
|
|
+ return super().keypress(size, key)
|
|
|
|
|
|
-class FocusWidget(urwid.WidgetWrap):
|
|
|
|
|
|
+class FocusWidget(WidgetWrap):
|
|
ignore_focus = True
|
|
ignore_focus = True
|
|
def __init__(self, widget, focus_order, focus_widgets):
|
|
def __init__(self, widget, focus_order, focus_widgets):
|
|
super().__init__(widget)
|
|
super().__init__(widget)
|
|
@@ -146,7 +162,7 @@ class FocusWidget(urwid.WidgetWrap):
|
|
_w1 = getattr(widget, 'original_widget', None)
|
|
_w1 = getattr(widget, 'original_widget', None)
|
|
_w2 = getattr(widget, 'contents', None)
|
|
_w2 = getattr(widget, 'contents', None)
|
|
if _w2 is not None:
|
|
if _w2 is not None:
|
|
- return
|
|
|
|
|
|
+ return None
|
|
if _w1 is not None:
|
|
if _w1 is not None:
|
|
return self.find_widget(widget=_w1)
|
|
return self.find_widget(widget=_w1)
|
|
|
|
|
|
@@ -186,14 +202,15 @@ class FocusWidget(urwid.WidgetWrap):
|
|
self._focus_widgets()
|
|
self._focus_widgets()
|
|
) if k == n]
|
|
) if k == n]
|
|
|
|
|
|
- for _prev, _cur, _next in zip([_p[-1], *_p[:-1]], _p, [*_p[1:], _p[0]]):
|
|
|
|
- if list(_c) == list(_cur):
|
|
|
|
- break
|
|
|
|
|
|
+ _prev, _, _next = next(filter(
|
|
|
|
+ lambda x: _c == list(x[1]),
|
|
|
|
+ zip([_p[-1], *_p[:-1]], _p, [*_p[1:], _p[0]])
|
|
|
|
+ ))
|
|
if reverse:
|
|
if reverse:
|
|
self.container.set_focus_path(_prev)
|
|
self.container.set_focus_path(_prev)
|
|
else:
|
|
else:
|
|
self.container.set_focus_path(_next)
|
|
self.container.set_focus_path(_next)
|
|
- return
|
|
|
|
|
|
+
|
|
|
|
|
|
class AutoCompletePopUp(PopUpLauncher):
|
|
class AutoCompletePopUp(PopUpLauncher):
|
|
_default_pop_up_parameters = {
|
|
_default_pop_up_parameters = {
|
|
@@ -211,7 +228,7 @@ class AutoCompletePopUp(PopUpLauncher):
|
|
self._pop_up_parameters = dict(**self._default_pop_up_parameters)
|
|
self._pop_up_parameters = dict(**self._default_pop_up_parameters)
|
|
self.apply_choice_cb = apply_choice_cb
|
|
self.apply_choice_cb = apply_choice_cb
|
|
self.close_cb = close_cb
|
|
self.close_cb = close_cb
|
|
- urwid.connect_signal(self._original_widget, 'open', lambda _, options: self._open_pop_up(options))
|
|
|
|
|
|
+ connect_signal(self._original_widget, 'open', lambda _, options: self._open_pop_up(options))
|
|
|
|
|
|
def _open_pop_up(self, options):
|
|
def _open_pop_up(self, options):
|
|
self.options = options
|
|
self.options = options
|
|
@@ -243,13 +260,13 @@ class AutoCompletePopUp(PopUpLauncher):
|
|
self._original_widget.name, self.options,
|
|
self._original_widget.name, self.options,
|
|
self.apply_choice_cb,
|
|
self.apply_choice_cb,
|
|
)
|
|
)
|
|
- urwid.connect_signal(pop_up, 'close', lambda _: self._close_pop_up())
|
|
|
|
|
|
+ connect_signal(pop_up, 'close', lambda _: self._close_pop_up())
|
|
return pop_up
|
|
return pop_up
|
|
|
|
|
|
def get_pop_up_parameters(self):
|
|
def get_pop_up_parameters(self):
|
|
return self._pop_up_parameters
|
|
return self._pop_up_parameters
|
|
|
|
|
|
-class SuggestionPopup(urwid.WidgetWrap):
|
|
|
|
|
|
+class SuggestionPopup(WidgetWrap):
|
|
|
|
|
|
signals = ['close']
|
|
signals = ['close']
|
|
|
|
|
|
@@ -261,12 +278,12 @@ class SuggestionPopup(urwid.WidgetWrap):
|
|
self.apply_cb = lambda _, v: apply_cb(name, v)
|
|
self.apply_cb = lambda _, v: apply_cb(name, v)
|
|
body = []
|
|
body = []
|
|
for c in options:
|
|
for c in options:
|
|
- button = urwid.Button(c)
|
|
|
|
- urwid.connect_signal(button, 'click', self.apply_cb, c)
|
|
|
|
- urwid.connect_signal(button, 'click', lambda _: self._emit("close"))
|
|
|
|
- body.append(urwid.AttrMap(button, None, focus_map='popup_focus'))
|
|
|
|
- walker = urwid.SimpleFocusListWalker(body, wrap_around=False)
|
|
|
|
- super().__init__(urwid.AttrWrap(IndicativeListBox(walker,
|
|
|
|
|
|
+ button = Button(c)
|
|
|
|
+ connect_signal(button, 'click', self.apply_cb, c)
|
|
|
|
+ connect_signal(button, 'click', lambda _: self._emit("close"))
|
|
|
|
+ body.append(AttrMap(button, None, focus_map='popup_focus'))
|
|
|
|
+ walker = SimpleFocusListWalker(body, wrap_around=False)
|
|
|
|
+ super().__init__(AttrWrap(IndicativeListBox(walker,
|
|
topBar_endExposed_prop=("───", None, None),
|
|
topBar_endExposed_prop=("───", None, None),
|
|
topBar_endCovered_prop=("▲ {} more ▲", None, None),
|
|
topBar_endCovered_prop=("▲ {} more ▲", None, None),
|
|
bottomBar_endCovered_prop=("▼ {} more ▼", None, None),
|
|
bottomBar_endCovered_prop=("▼ {} more ▼", None, None),
|
|
@@ -277,14 +294,13 @@ class SuggestionPopup(urwid.WidgetWrap):
|
|
def keypress(self, size, key):
|
|
def keypress(self, size, key):
|
|
if key == 'esc':
|
|
if key == 'esc':
|
|
self._emit("close")
|
|
self._emit("close")
|
|
- return
|
|
|
|
-
|
|
|
|
|
|
+ return None
|
|
if key == 'tab':
|
|
if key == 'tab':
|
|
- return
|
|
|
|
|
|
+ return None
|
|
|
|
|
|
return super().keypress(size, key)
|
|
return super().keypress(size, key)
|
|
|
|
|
|
-class FlowBarGraph(urwid.BarGraph):
|
|
|
|
|
|
+class FlowBarGraph(BarGraph):
|
|
ignore_focus = True
|
|
ignore_focus = True
|
|
def __init__(self, height, *args, **kwargs):
|
|
def __init__(self, height, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
super().__init__(*args, **kwargs)
|
|
@@ -297,7 +313,7 @@ class FlowBarGraph(urwid.BarGraph):
|
|
_size = (size[0], self.rows(size))
|
|
_size = (size[0], self.rows(size))
|
|
return super().render(_size,focus=focus)
|
|
return super().render(_size,focus=focus)
|
|
|
|
|
|
-class FlowGraphVScale(urwid.GraphVScale):
|
|
|
|
|
|
+class FlowGraphVScale(GraphVScale):
|
|
ignore_focus = True
|
|
ignore_focus = True
|
|
def __init__(self, graph, *args, **kwargs):
|
|
def __init__(self, graph, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
super().__init__(*args, **kwargs)
|
|
@@ -310,7 +326,7 @@ class FlowGraphVScale(urwid.GraphVScale):
|
|
_size = (size[0], self.rows(size))
|
|
_size = (size[0], self.rows(size))
|
|
return super().render(_size,focus=focus)
|
|
return super().render(_size,focus=focus)
|
|
|
|
|
|
-class FlowBarGraphWithVScale(urwid.Columns):
|
|
|
|
|
|
+class FlowBarGraphWithVScale(Columns):
|
|
ignore_focus = True
|
|
ignore_focus = True
|
|
|
|
|
|
@property
|
|
@property
|
|
@@ -327,9 +343,6 @@ class FlowBarGraphWithVScale(urwid.Columns):
|
|
|
|
|
|
@height.setter
|
|
@height.setter
|
|
def height(self, value):
|
|
def height(self, value):
|
|
- self._invalidate
|
|
|
|
- self.graph._invalidate
|
|
|
|
- self.graph_vscale._invalidate
|
|
|
|
self.graph.height = value
|
|
self.graph.height = value
|
|
|
|
|
|
def set_bar_width(self, width):
|
|
def set_bar_width(self, width):
|
|
@@ -359,10 +372,10 @@ class FlowBarGraphWithVScale(urwid.Columns):
|
|
self._canvas_width = cols - 6
|
|
self._canvas_width = cols - 6
|
|
self.graph = FlowBarGraph(rows, attr, hatt=hatt)
|
|
self.graph = FlowBarGraph(rows, attr, hatt=hatt)
|
|
self.graph_vscale = FlowGraphVScale(self.graph, [], 0)
|
|
self.graph_vscale = FlowGraphVScale(self.graph, [], 0)
|
|
- self.graph_caption = urwid.Text(u'')
|
|
|
|
|
|
+ self.graph_caption = Text(u'')
|
|
super().__init__([
|
|
super().__init__([
|
|
(self._vscale_width, self.graph_vscale),
|
|
(self._vscale_width, self.graph_vscale),
|
|
- (self._canvas_width, urwid.Pile([
|
|
|
|
|
|
+ (self._canvas_width, Pile([
|
|
self.graph,
|
|
self.graph,
|
|
self.graph_caption
|
|
self.graph_caption
|
|
]))
|
|
]))
|