소스 검색

simplify FocusWidget and advancing focus

Daniel Sheffield 2 년 전
부모
커밋
4dd7b5f205
3개의 변경된 파일75개의 추가작업 그리고 110개의 파일을 삭제
  1. 12 13
      app/activities/PriceCheck.py
  2. 16 14
      app/activities/TransactionEditor.py
  3. 47 83
      app/widgets.py

+ 12 - 13
app/activities/PriceCheck.py

@@ -6,6 +6,7 @@
 # THIS SOFTWARE IS PROVIDED AS IS WITHOUT WARRANTY
 import itertools
 from decimal import Decimal, InvalidOperation
+from itertools import chain
 from typing import Callable, Union
 from urwid import (
     connect_signal,
@@ -39,7 +40,7 @@ class PriceCheck(FocusWidget):
         if isinstance(key, tuple):
             return
 
-        if getattr(self.original_widget.original_widget, 'original_widget', None) is None:
+        if getattr(self._w.original_widget, 'original_widget', None) is None:
             return super().keypress(size, key)
 
         if key == 'tab':
@@ -280,16 +281,14 @@ class PriceCheck(FocusWidget):
         ])
         widget = Filler(widget, 'top')
         widget = AttrMap(widget, 'bg')
-        widget.original_widget.original_widget.set_focus_path([3,0,0])
-        super().__init__(widget, 4, [3,0,0,0], [
-                [0, 0,], [0,1,],
-                [1,],
-                [2,],
-                [2,0,],
-                [2,0,0,], [2,0,2], [2,2,0], [2,2,2],
-                [3,0,], [3,0,0,],
-                [3,0,1,0,], [3,0,1,1,], [3,0,1,2,],
-                [3,1,3,],
-                [4,],
+        super().__init__(widget, map(
+            lambda x: next(w[1] for w in chain(
+                self.buttons.items(),
+                self.edit_fields.items(),
+                self.checkboxes.items(),
+            ) if x == w[0]),
+            [
+                'product', 'organic', 'unit', 'quantity', 'price',
+                'clear', 'exit', 'sort_price', 'sort_date',
             ]
-        )
+        ))

+ 16 - 14
app/activities/TransactionEditor.py

@@ -6,6 +6,7 @@
 # THIS SOFTWARE IS PROVIDED AS IS WITHOUT WARRANTY
 import itertools
 from decimal import Decimal, InvalidOperation
+from itertools import chain
 from typing import Callable, Union
 from urwid import (
     connect_signal,
@@ -39,7 +40,7 @@ class TransactionEditor(FocusWidget):
         if isinstance(key, tuple):
             return
         
-        if getattr(self.original_widget.original_widget, 'original_widget', None) is None:
+        if getattr(self._w.original_widget, 'original_widget', None) is None:
             return super().keypress(size, key)
         
         if key == 'tab':
@@ -331,16 +332,17 @@ class TransactionEditor(FocusWidget):
         ])
         widget = Filler(widget, 'top') 
         widget = AttrMap(widget, 'bg')
-        super().__init__(widget, 5, [2,0,0,0], [
-            [0, 0], [0,1],
-            [1,],
-            [2,], [2,0], [2,0,0],
-            [2, 0, 1, 0],
-            [3, 0], [3,0,1],
-            [3, 1], [3,1,0], [3,1,0,0], [3,1,0,1], [3,1,0,2],
-            [4,],
-            [5,], [5,1],
-            [6,],
-            [6, 1],
-            [7,],
-        ])
+        super().__init__(widget, map(
+            lambda x: next(w[1] for w in chain(
+                self.buttons.items(),
+                self.edit_fields.items(),
+                self.checkboxes.items()
+            ) if x == w[0]),
+            [
+                'product', 'organic',
+                'unit', 'quantity', 'price',
+                'description',
+                'done', 'clear',
+                'category', 'group',
+                'ts', 'store'
+            ]))

+ 47 - 83
app/widgets.py

@@ -9,10 +9,10 @@ from typing import Any, Callable, Iterable, List, Union
 
 import urwid
 from additional_urwid_widgets import IndicativeListBox
+from itertools import chain
 from urwid import numedit
 from urwid.wimp import PopUpLauncher
 
-
 class AutoCompleteEdit(urwid.Edit):
     signals = [ *urwid.Edit.signals, 'apply', 'open' ]
 
@@ -126,100 +126,64 @@ class NoTabCheckBox(urwid.CheckBox):
         else:
             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)
-        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
-    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):
-        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:
-            zipped_paths = zip(paths, [
-                *paths[1:], paths[0]
-            ])
+            self.container.set_focus_path(_prev)
         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
 
 class AutoCompletePopUp(PopUpLauncher):