|
@@ -5,7 +5,7 @@
|
|
|
#
|
|
|
# THIS SOFTWARE IS PROVIDED AS IS WITHOUT WARRANTY
|
|
|
from decimal import Decimal
|
|
|
-from typing import Callable, Iterable, Union
|
|
|
+from typing import Callable, Iterable, List, Union
|
|
|
|
|
|
import urwid
|
|
|
from additional_urwid_widgets import IndicativeListBox
|
|
@@ -128,10 +128,11 @@ class NoTabCheckBox(urwid.CheckBox):
|
|
|
|
|
|
class FocusWidget(urwid.WidgetPlaceholder):
|
|
|
|
|
|
- def __init__(self, widget, initial_focus, skip_focus):
|
|
|
+ def __init__(self, widget, max_focus_path_len, initial_focus, skip_focus):
|
|
|
super().__init__(widget)
|
|
|
self._initial_focus = tuple([ i for i in initial_focus ])
|
|
|
self._skip_focus = tuple([ i for i in skip_focus ])
|
|
|
+ self.max_focus_path_len = max_focus_path_len
|
|
|
|
|
|
@property
|
|
|
def skip_focus(self):
|
|
@@ -144,69 +145,82 @@ class FocusWidget(urwid.WidgetPlaceholder):
|
|
|
@property
|
|
|
def container(self):
|
|
|
return self.original_widget.original_widget.original_widget
|
|
|
-
|
|
|
- def _set_focus_path(self, path):
|
|
|
+
|
|
|
+ def _can_set_focus_path(self, path):
|
|
|
+ old = self.container.get_focus_path()
|
|
|
try:
|
|
|
self.container.set_focus_path(path)
|
|
|
- return
|
|
|
+ return True
|
|
|
except IndexError:
|
|
|
- pass
|
|
|
+ return False
|
|
|
+ finally:
|
|
|
+ self.container.set_focus_path(old)
|
|
|
+
|
|
|
+ def _set_focus_path(self, path):
|
|
|
+ self.container.set_focus_path(path)
|
|
|
+
|
|
|
+ def _next_path(self, path: Iterable, ext=True) -> List:
|
|
|
+ if len(path) < self.max_focus_path_len:
|
|
|
+ next_path = [*path, 0]
|
|
|
+ if self._can_set_focus_path(next_path):
|
|
|
+ return next_path
|
|
|
|
|
|
- if path[-1] == 0 and len(path) > 1:
|
|
|
- self._set_focus_path(path[:-1])
|
|
|
- return
|
|
|
-
|
|
|
- raise IndexError
|
|
|
+ next_path = [*path[:-1], path[-1]+1]
|
|
|
+ if self._can_set_focus_path(next_path):
|
|
|
+ return next_path
|
|
|
+
|
|
|
+ next_path = path
|
|
|
+ while len(next_path) > 1 and next_path[-1] == 0:
|
|
|
+ next_path = [*next_path[:-1]]
|
|
|
+ if self._can_set_focus_path(next_path):
|
|
|
+ return next_path
|
|
|
+ if len(next_path) >= 2:
|
|
|
+ next_path = [*next_path[:-2], next_path[-2]+1, 0]
|
|
|
+ if self._can_set_focus_path(next_path):
|
|
|
+ return next_path
|
|
|
+ return self._next_path(next_path)
|
|
|
+ if self._can_set_focus_path([*path[:-1], path[-1]+1]):
|
|
|
+ return self._next_path(next_path)
|
|
|
+ return
|
|
|
|
|
|
def iter_focus_paths(self):
|
|
|
+ path = None
|
|
|
self._set_focus_path(self.initial_focus)
|
|
|
- while True:
|
|
|
- path = self.container.get_focus_path()
|
|
|
- yield path
|
|
|
- self.advance_focus()
|
|
|
- path = self.container.get_focus_path()
|
|
|
- while len(path) < len(self.initial_focus):
|
|
|
- path.extend([0])
|
|
|
- if path == self.initial_focus:
|
|
|
- return
|
|
|
-
|
|
|
+ while path != self.initial_focus:
|
|
|
+ if path is None:
|
|
|
+ path = self.container.get_focus_path()
|
|
|
+ if path not in self.skip_focus:
|
|
|
+ yield path
|
|
|
+ path = self._next_path(path)
|
|
|
+ if path is None:
|
|
|
+ path = self._next_path([0])
|
|
|
+ self._set_focus_path(path)
|
|
|
+
|
|
|
def advance_focus(self, reverse=False):
|
|
|
|
|
|
path = self.container.get_focus_path()
|
|
|
-
|
|
|
+
|
|
|
+ paths = [ i for i in self.iter_focus_paths() ]
|
|
|
+
|
|
|
if reverse:
|
|
|
- paths = [ i for i in self.iter_focus_paths() ]
|
|
|
zipped_paths = zip(paths, [
|
|
|
*paths[1:], paths[0]
|
|
|
])
|
|
|
- prev_path = map(lambda x: x[0], filter(
|
|
|
- lambda x: x[1] == path,
|
|
|
- zipped_paths
|
|
|
- ))
|
|
|
- p = next(prev_path)
|
|
|
- self._set_focus_path(p)
|
|
|
- return
|
|
|
+ else:
|
|
|
+ zipped_paths = zip([
|
|
|
+ *paths[1:], paths[0]
|
|
|
+ ], paths)
|
|
|
|
|
|
- _iter = [ i for i in enumerate(path) ][::-1]
|
|
|
-
|
|
|
- for idx, part in _iter:
|
|
|
- p = [ i for i in path ]
|
|
|
- if reverse:
|
|
|
- p[idx] -= 1
|
|
|
- else:
|
|
|
- p[idx] += 1
|
|
|
-
|
|
|
- try:
|
|
|
- self._set_focus_path(p)
|
|
|
- if p in self.skip_focus:
|
|
|
- self.advance_focus(reverse=reverse)
|
|
|
- return
|
|
|
- except IndexError:
|
|
|
- path[idx] = 0
|
|
|
- while len(path) < len(self.initial_focus):
|
|
|
- path.extend([0])
|
|
|
-
|
|
|
- self._set_focus_path(self.initial_focus)
|
|
|
+ next_path = map(lambda x: x[0], filter(
|
|
|
+ lambda x: x[1] == path,
|
|
|
+ zipped_paths
|
|
|
+ ))
|
|
|
+ p = next(next_path)
|
|
|
+ if p in self.skip_focus:
|
|
|
+ self.advance_focus(reverse=reverse)
|
|
|
+ return
|
|
|
+ self._set_focus_path(p)
|
|
|
+ return
|
|
|
|
|
|
class AutoCompletePopUp(PopUpLauncher):
|
|
|
_default_pop_up_parameters = {
|