From: mar77i Date: Fri, 14 Feb 2025 00:45:53 +0000 (+0100) Subject: cleanup: add enabled flag rather than reparenting with tab_bar X-Git-Url: https://git.mar77i.info/?a=commitdiff_plain;h=8571e9f87c7c6040a80e2794b6de828e15983839;p=zenbook_gui cleanup: add enabled flag rather than reparenting with tab_bar --- diff --git a/ui/button.py b/ui/button.py index 26345cf..15cf575 100644 --- a/ui/button.py +++ b/ui/button.py @@ -4,12 +4,12 @@ from .child import Child class Button(Child): - def __init__(self, parent, rect, value, callback, is_active=False): + def __init__(self, parent, rect, value, callback, highlight=False): super().__init__(parent) self.rect = rect self.value = value self.callback = callback - self.is_active = is_active + self.highlight = highlight self.pushed = False def draw_value(self, color): @@ -20,7 +20,7 @@ class Button(Child): def draw(self): if not self.pushed: - value_color = "lime" if self.is_active else "gray" + value_color = "lime" if self.highlight else "gray" colors = ("black", value_color, value_color) else: colors = ("darkgray", "lightgray", "black") diff --git a/ui/child.py b/ui/child.py index d70296d..35e0a84 100644 --- a/ui/child.py +++ b/ui/child.py @@ -5,24 +5,10 @@ from .root import Root class Child(EventMethodDispatcher): - def __init__(self, parent): - self._parent = parent - if parent is not None: - parent.children.append(self) - - @property - def parent(self): - return self._parent - - @parent.setter - def parent(self, parent): - if self._parent is not None: - self._parent.children.remove(self) - self._parent = parent - if parent is not None: - parent.children.append(self) - if "root" in self.__dict__: - self.__dict__.pop("root") + def __init__(self, parent, enabled=True): + self.parent = parent + parent.children.append(self) + self.enabled = enabled @cached_property def root(self): diff --git a/ui/drop_down.py b/ui/drop_down.py index ac48dee..8a76442 100644 --- a/ui/drop_down.py +++ b/ui/drop_down.py @@ -4,7 +4,6 @@ import pygame from .button import Button from .modal import Modal -from .parent import Parent class DropDownMenu(Modal): @@ -36,6 +35,6 @@ class DropDownMenu(Modal): class DropDown(Button): - def __init__(self, parent, rect, value, entries, callback, is_active=False): + def __init__(self, parent, rect, value, entries, callback, highlight=False): self.dropdown_menu = DropDownMenu(parent, rect, entries, callback) - super().__init__(parent, rect, value, self.dropdown_menu.activate, is_active) + super().__init__(parent, rect, value, self.dropdown_menu.activate, highlight) diff --git a/ui/focus.py b/ui/focus.py index 07fe6d1..2d3c609 100644 --- a/ui/focus.py +++ b/ui/focus.py @@ -1,37 +1,20 @@ -import pygame +from .root import Root -class FocusRootMixin: - surf: pygame.Surface - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.focus_stack: list[FocusableMixin | FocusRootMixin] = [self] - - @property - def active(self): - return self.focus_stack[-1] is self - - def handle_event(self, ev): - focused = self.focus_stack[-1] - (super() if focused is self else focused).handle_event(ev) - - -class FocusableMixin: - root: FocusRootMixin +class Focusable: + root: Root + dirty: bool @property - def active(self): + def focused(self): return self.root.focus_stack[-1] is self def activate(self): - focus_stack = self.root.focus_stack - assert self not in focus_stack - focus_stack.append(self) + assert self not in self.root.focus_stack + self.root.focus_stack.append(self) self.dirty = True def deactivate(self): - focus_stack = self.root.focus_stack - assert self.active - focus_stack.pop() + assert self.focused + self.root.focus_stack.pop() self.dirty = True diff --git a/ui/icon_button.py b/ui/icon_button.py index 9b952a6..a08aec0 100644 --- a/ui/icon_button.py +++ b/ui/icon_button.py @@ -12,7 +12,7 @@ class IconButton(Button): if self.pushed: pygame.draw.rect(self.surf, "honeydew4", self.rect) self.shape.draw(self.surf, "black" if self.pushed else "white") - if self.is_active: + if self.highlight: color = "lime" elif self.pushed: color = "red" diff --git a/ui/modal.py b/ui/modal.py index 20c3b37..55808f9 100644 --- a/ui/modal.py +++ b/ui/modal.py @@ -1,33 +1,35 @@ import pygame from .child import Child -from .focus import FocusableMixin +from .focus import Focusable from .parent import Parent -class Modal(FocusableMixin, Parent, Child): +class Modal(Focusable, Parent, Child): def __init__(self, parent): - super().__init__(parent) - self.draw = self._check_active(self.draw) - self.update = self._check_active(self.update) - self.handle_event = self._check_active(self.handle_event) - self.activate = self._check_active(self.activate, False) - self.deactivate = self._check_active(self.deactivate) + super().__init__(parent, False) + self.draw = self._check_enabled(self.draw) + self.update = self._check_enabled(self.update) + self.handle_event = self._check_enabled(self.handle_event) + self.activate = self._check_enabled(self.activate, False) + self.deactivate = self._check_enabled(self.deactivate) - def _check_active(self, method, cmp=True): + def _check_enabled(self, method, cmp=True): def inner(*args, **kwargs): - if self.active is cmp: + if self.enabled is cmp: return method(*args, **kwargs) return None return inner def activate(self): super().activate() + self.enabled = True self.root.stop_event = True self.dirty = True def deactivate(self): super().deactivate() + self.enabled = False self.root.stop_event = True self.dirty = True diff --git a/ui/parent.py b/ui/parent.py index 22fed96..f5e5cb4 100644 --- a/ui/parent.py +++ b/ui/parent.py @@ -15,14 +15,24 @@ class Parent(EventMethodDispatcher): if not self.running or self.stop_event: return for child in self.children: + if not child.enabled: + continue child.handle_event(ev) if not self.running or self.stop_event: break def update(self): + s = super() + if hasattr(s, "update"): + s.update() for child in self.children: - child.update() + if child.enabled: + child.update() def draw(self): + s = super() + if hasattr(s, "draw"): + s.draw() for child in self.children: - child.draw() + if child.enabled: + child.draw() diff --git a/ui/root.py b/ui/root.py index 89974c6..6d6f241 100644 --- a/ui/root.py +++ b/ui/root.py @@ -1,13 +1,14 @@ import pygame from .parent import Parent -from .focus import FocusRootMixin -class Root(FocusRootMixin, Parent): +class Root(Parent): BACKGROUND_COLOR: pygame.Color def __init__(self, surf, font=None): + from .child import Child + super().__init__() self.font = font self.running = True @@ -16,17 +17,25 @@ class Root(FocusRootMixin, Parent): self.clock = pygame.time.Clock() self.stop_event = False self.root = self + self.focus_stack: list[Root | Child] = [self] def handle_quit(self, _=None): self.running = False KEY_METHODS = {frozenset(set()): {pygame.K_ESCAPE: handle_quit}} + @property + def focused(self): + return self.focus_stack[-1] is self + def handle_event(self, ev): if ev.type in (pygame.WINDOWEXPOSED, pygame.ACTIVEEVENT): self.dirty = True return - super().handle_event(ev) + focused = self.focus_stack[-1] + if focused is self: + focused = super() + focused.handle_event(ev) def draw(self): if hasattr(self, "BACKGROUND_COLOR"): diff --git a/ui/spinner.py b/ui/spinner.py index 86bd7d3..ff66c0a 100644 --- a/ui/spinner.py +++ b/ui/spinner.py @@ -15,9 +15,9 @@ class RepeatButton(Button): DELAY_MS = 500 REPEAT_MS = 100 - def __init__(self, parent, rect, value, callback, is_active=False): + def __init__(self, parent, rect, value, callback, highlight=False): self._pushed = False - super().__init__(parent, rect, value, callback, is_active) + super().__init__(parent, rect, value, callback, highlight) self.repeat_ts = None @property diff --git a/ui/tab_bar.py b/ui/tab_bar.py index c1732ec..8ee76fc 100644 --- a/ui/tab_bar.py +++ b/ui/tab_bar.py @@ -8,11 +8,11 @@ from .parent import Parent class TabBar(Parent, Child): - def __init__(self, parent, rect, labels, groups, active): + def __init__(self, parent, rect, labels, groups, current_tab): super().__init__(parent) self.labels = labels self.groups = groups - self.active = active + self.current_tab = current_tab num_labels = len(labels) self.buttons = [ Button( @@ -31,23 +31,23 @@ class TabBar(Parent, Child): def update_children(self, i=None): if i is not None: - if self.active == i: + if self.current_tab == i: return - self.active = i + self.current_tab = i for i, (button, group) in enumerate(zip(self.buttons, self.groups)): - is_group_active = i == self.active - if button.is_active != is_group_active: - button.is_active = is_group_active + is_current_tab = i == self.current_tab + if button.highlight != is_current_tab: + button.highlight = is_current_tab self.dirty = True for item in group: if item.parent is not self: item.parent = self - is_child_active = item in self.children - if is_group_active == is_child_active: + is_child_enabled = item.enabled + if is_current_tab == is_child_enabled: continue - if is_group_active: - self.children.append(item) - elif is_child_active: - self.children.remove(item) + if is_current_tab: + item.enabled = True + elif is_child_enabled: + item.enabled = False self.dirty = True assert len(self.children) == len(set(self.children)) diff --git a/ui/text_input.py b/ui/text_input.py index 772c621..3f30c35 100644 --- a/ui/text_input.py +++ b/ui/text_input.py @@ -8,7 +8,7 @@ from typing import Optional, Callable import pygame from .child import Child -from .focus import FocusableMixin +from .focus import Focusable @dataclass @@ -64,7 +64,7 @@ def search_same_isspace_forward(value, pos): return pos -class TextInput(FocusableMixin, Child): +class TextInput(Focusable, Child): def __init__(self, parent, rect, callback, value="", value_filter=None): super().__init__(parent) self.rect = rect @@ -123,7 +123,7 @@ class TextInput(FocusableMixin, Child): def draw(self): pygame.draw.rect(self.surf, "black", self.rect) - if self.active: + if self.focused: fs = self.get_font_surface() else: fs = self.font.render(self.value, True, "gray") @@ -162,7 +162,7 @@ class TextInput(FocusableMixin, Child): self.cursor.pos = self.pos_from_offset( ev.pos[0] - self.rect.left - 16 + self.offset ) - elif self.active: + elif self.focused: self.deactivate(True) @contextmanager @@ -223,13 +223,13 @@ class TextInput(FocusableMixin, Child): self.cursor.pos = pos def activate(self): - if self.active: + if self.focused: return super().activate() self.cursor = Cursor(self.value) def deactivate(self, restore=False): - if not self.active: + if not self.focused: return if restore: self.value = self.cursor.old_value @@ -257,7 +257,7 @@ class TextInput(FocusableMixin, Child): } def handle_keydown(self, ev): - if not self.active: + if not self.focused: return if (key_method := self.get_key_method(ev.key, ev.mod)) is not None: key_callback = key_method @@ -267,7 +267,7 @@ class TextInput(FocusableMixin, Child): return with self.check_dirty(): key_callback() - if self.active: + if self.focused: self.cursor.press_key(key_callback, ev.key) def handle_keyup(self, ev): diff --git a/zenbook_conf/zenbook_conf.py b/zenbook_conf/zenbook_conf.py index a0ec61a..41f8d66 100644 --- a/zenbook_conf/zenbook_conf.py +++ b/zenbook_conf/zenbook_conf.py @@ -160,9 +160,9 @@ class ZenbookConf(Root): for device_type, value in settings_per_device_type.items(): if value is not None: self.xinput_conf.update_by_type(device_type, value) - self.single_button.is_active = self.xrandr_conf.is_active("single") - self.double_button.is_active = self.xrandr_conf.is_active("double") - self.vertical_button.is_active = self.xrandr_conf.is_active("vertical") + self.single_button.highlight = self.xrandr_conf.is_active("single") + self.double_button.highlight = self.xrandr_conf.is_active("double") + self.vertical_button.highlight = self.xrandr_conf.is_active("vertical") self.dirty = True def quit(self):