|
@@ -9,10 +9,10 @@ from typing import Any, Callable, Iterable, List, Union
|
|
|
|
|
|
import urwid
|
|
import urwid
|
|
from additional_urwid_widgets import IndicativeListBox
|
|
from additional_urwid_widgets import IndicativeListBox
|
|
|
|
+from itertools import chain
|
|
from urwid import numedit
|
|
from urwid import numedit
|
|
from urwid.wimp import PopUpLauncher
|
|
from urwid.wimp import PopUpLauncher
|
|
|
|
|
|
-
|
|
|
|
class AutoCompleteEdit(urwid.Edit):
|
|
class AutoCompleteEdit(urwid.Edit):
|
|
signals = [ *urwid.Edit.signals, 'apply', 'open' ]
|
|
signals = [ *urwid.Edit.signals, 'apply', 'open' ]
|
|
|
|
|
|
@@ -126,100 +126,64 @@ class NoTabCheckBox(urwid.CheckBox):
|
|
else:
|
|
else:
|
|
return super().keypress(size, key)
|
|
return super().keypress(size, key)
|
|
|
|
|
|
-class FocusWidget(urwid.WidgetPlaceholder):
|
|
|
|
-
|
|
|
|
- def __init__(self, widget, max_focus_path_len, initial_focus, skip_focus):
|
|
|
|
|
|
+class FocusWidget(urwid.WidgetWrap):
|
|
|
|
+ ignore_focus = True
|
|
|
|
+ def __init__(self, widget, focus_widgets):
|
|
super().__init__(widget)
|
|
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
|
|
|
|
|
|
+ self._focus_widgets = [ w for w in focus_widgets ]
|
|
|
|
+ assert len(self._focus_widgets) > 0
|
|
|
|
+ for w, p in self.iter_focus_paths():
|
|
|
|
+ if w is self._focus_widgets[0]:
|
|
|
|
+ self.container.set_focus_path(p)
|
|
|
|
|
|
@property
|
|
@property
|
|
- def skip_focus(self):
|
|
|
|
- return self._skip_focus
|
|
|
|
|
|
+ def container(self):
|
|
|
|
+ return self._w.original_widget.original_widget
|
|
|
|
|
|
- @property
|
|
|
|
- def initial_focus(self):
|
|
|
|
- return list(self._initial_focus)
|
|
|
|
|
|
+ def find_widget(self, widget=None):
|
|
|
|
+ if widget in self._focus_widgets:
|
|
|
|
+ return widget
|
|
|
|
+ _w1 = getattr(widget, 'original_widget', None)
|
|
|
|
+ _w2 = getattr(widget, 'contents', None)
|
|
|
|
+ if _w2 is not None:
|
|
|
|
+ return
|
|
|
|
+ if _w1 is not None:
|
|
|
|
+ return self.find_widget(widget=_w1)
|
|
|
|
|
|
- @property
|
|
|
|
- def container(self):
|
|
|
|
- return self.original_widget.original_widget.original_widget
|
|
|
|
-
|
|
|
|
- def _can_set_focus_path(self, path):
|
|
|
|
- old = self.container.get_focus_path()
|
|
|
|
- try:
|
|
|
|
- self.container.set_focus_path(path)
|
|
|
|
- return True
|
|
|
|
- except IndexError:
|
|
|
|
- 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
|
|
|
|
|
|
+ def iter_containers(self, path=None, widget=None):
|
|
|
|
+ widget = widget or self.container
|
|
|
|
+ path = path or []
|
|
|
|
+ _w1 = getattr(widget, 'original_widget', None)
|
|
|
|
+ _w2 = getattr(widget, 'contents', None)
|
|
|
|
+ _chain = []
|
|
|
|
+ if _w2 is not None:
|
|
|
|
+ yield widget, path
|
|
|
|
+ for idx, c in enumerate(_w2):
|
|
|
|
+ _chain.append(self.iter_containers(path=[*path, idx], widget=c[0]))
|
|
|
|
|
|
- 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
|
|
|
|
|
|
+ if _w1 is not None:
|
|
|
|
+ _chain.append(self.iter_containers(path=[*path], widget=_w1))
|
|
|
|
+ yield from chain(*_chain)
|
|
|
|
|
|
def iter_focus_paths(self):
|
|
def iter_focus_paths(self):
|
|
- path = None
|
|
|
|
- self._set_focus_path(self.initial_focus)
|
|
|
|
- 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):
|
|
|
|
|
|
+ for c, path in self.iter_containers():
|
|
|
|
+ for idx, w in enumerate(c.contents):
|
|
|
|
+ yield self.find_widget(widget=w[0]), [*path, idx]
|
|
|
|
|
|
- path = self.container.get_focus_path()
|
|
|
|
-
|
|
|
|
- paths = [ i for i in self.iter_focus_paths() ]
|
|
|
|
|
|
|
|
|
|
+ def advance_focus(self, reverse=False):
|
|
|
|
+ _c = self.container.get_focus_path()
|
|
|
|
+ _p = [
|
|
|
|
+ x[1] for x in filter(lambda x: x[0] is not None,
|
|
|
|
+ self.iter_focus_paths())
|
|
|
|
+ ]
|
|
|
|
+ for _prev, _cur, _next in zip([_p[-1], *_p[:-1]], _p, [*_p[1:], _p[0]]):
|
|
|
|
+ if list(_c) == list(_cur):
|
|
|
|
+ break
|
|
if reverse:
|
|
if reverse:
|
|
- zipped_paths = zip(paths, [
|
|
|
|
- *paths[1:], paths[0]
|
|
|
|
- ])
|
|
|
|
|
|
+ self.container.set_focus_path(_prev)
|
|
else:
|
|
else:
|
|
- zipped_paths = zip([
|
|
|
|
- *paths[1:], paths[0]
|
|
|
|
- ], paths)
|
|
|
|
-
|
|
|
|
- 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)
|
|
|
|
|
|
+ self.container.set_focus_path(_next)
|
|
return
|
|
return
|
|
|
|
|
|
class AutoCompletePopUp(PopUpLauncher):
|
|
class AutoCompletePopUp(PopUpLauncher):
|