From 1f45dba20a6b3e3f81a880a29c519230b2f31599 Mon Sep 17 00:00:00 2001 From: mar77i Date: Sun, 11 May 2025 18:34:36 +0200 Subject: [PATCH] make ui key_methods great again. --- bookpaint/book_manager.py | 28 +++++++++++++++++----------- bookpaint/bookpaint.py | 13 ++++++++++--- bookpaint/color_menu.py | 7 +------ bookpaint/line_menu.py | 7 +------ bookpaint/menu.py | 2 +- bookpaint/page_menu.py | 25 +++++++++++-------------- memory.py | 2 +- ui/child.py | 1 + ui/drop_down.py | 2 +- ui/event_method_dispatcher.py | 10 ++++++---- ui/root.py | 6 ++---- ui/text_input.py | 2 +- ui/utils.py | 25 +++++++++++++++++++++++++ 13 files changed, 78 insertions(+), 52 deletions(-) create mode 100644 ui/utils.py diff --git a/bookpaint/book_manager.py b/bookpaint/book_manager.py index e0dd84e..dbe0ec3 100644 --- a/bookpaint/book_manager.py +++ b/bookpaint/book_manager.py @@ -55,27 +55,31 @@ class BookManager: LAST = auto() NEW = auto() + def get_current_index(self): + try: + return self.pages.index(self.current_file) + except ValueError: + return len(self.pages) + def _get_index_for_page(self, nav): - if nav is self.Nav.NEW or len(self.pages) == 0: + len_pages = len(self.pages) + if nav is self.Nav.NEW or len_pages == 0: return -1 elif nav is self.Nav.FIRST: return 0 elif nav is self.Nav.LAST: - return len(self.pages) - 1 - try: - current_index = self.pages.index(self.current_file) - except ValueError: - current_index = -1 + return len_pages - 1 + current_index = self.get_current_index() if nav is self.Nav.PREV: - if current_index == -1: - return len(self.pages) - 1 + if current_index == len_pages: + return len_pages - 1 elif current_index > 0: return current_index - 1 return None elif nav is self.Nav.NEXT: - if current_index == -1: + if current_index == len_pages: return None - elif current_index < len(self.pages) - 1: + elif current_index < len_pages - 1: return current_index + 1 return -1 raise IndexError(nav) @@ -84,7 +88,9 @@ class BookManager: if self.parent.draw_image.image_dirty: self.save_file() i = self._get_index_for_page(nav) - if i >= 0: + if i is None: + return + elif i >= 0: self.current_file = self.pages[i] self.parent.draw_image.set_image(pygame.image.load(self.current_file)) elif self.current_file in self.pages: diff --git a/bookpaint/bookpaint.py b/bookpaint/bookpaint.py index e68de1d..addb0d0 100644 --- a/bookpaint/bookpaint.py +++ b/bookpaint/bookpaint.py @@ -12,12 +12,16 @@ from .line_menu import LineMenu from .menu import Menu from .page_menu import PageMenu -# - page menu; browse through next and prev pages using stylus - class BookPaint(Root): BACKGROUND_COLOR = "black" + def setup_deactivate_keys(self): + for key, value in self.key_methods[frozenset()].items(): + menu = getattr(self, value.keywords["menu_attr"]) + if key not in menu.key_methods[frozenset()]: + menu.key_methods[frozenset()][key] = menu.deactivate.__func__ + def __init__(self): pygame.init() super().__init__( @@ -30,6 +34,8 @@ class BookPaint(Root): self.page_menu = PageMenu(self) self.menu = Menu(self) self.book_manager = BookManager(self, Path("book")) + self.page_menu.update_label() + self.setup_deactivate_keys() def show_menu(self, menu_attr): getattr(self, menu_attr).activate() @@ -39,8 +45,9 @@ class BookPaint(Root): def nav(self, nav): self.book_manager.nav(nav) + self.page_menu.update_label() - KEY_METHODS = { + key_methods = { frozenset(): { pygame.K_ESCAPE: partial(show_menu, menu_attr="menu"), pygame.K_c: partial(show_menu, menu_attr="color_menu"), diff --git a/bookpaint/color_menu.py b/bookpaint/color_menu.py index 892b13b..4067db6 100644 --- a/bookpaint/color_menu.py +++ b/bookpaint/color_menu.py @@ -64,9 +64,4 @@ class ColorMenu(Modal): ), ) - KEY_METHODS = { - frozenset(): { - pygame.K_ESCAPE: Modal.deactivate, - pygame.K_c: Modal.deactivate, - } - } + key_methods = {frozenset(): {pygame.K_ESCAPE: Modal.deactivate}} diff --git a/bookpaint/line_menu.py b/bookpaint/line_menu.py index 705d85e..21536ad 100644 --- a/bookpaint/line_menu.py +++ b/bookpaint/line_menu.py @@ -76,9 +76,4 @@ class LineMenu(Modal): pygame.draw.rect(self.surf, "black", rect) pygame.draw.rect(self.surf, "gray", rect, 1) - KEY_METHODS = { - frozenset(): { - pygame.K_ESCAPE: Modal.deactivate, - pygame.K_l: Modal.deactivate, - } - } + key_methods = {frozenset(): {pygame.K_ESCAPE: Modal.deactivate}} diff --git a/bookpaint/menu.py b/bookpaint/menu.py index 0f3ff72..f471025 100644 --- a/bookpaint/menu.py +++ b/bookpaint/menu.py @@ -68,4 +68,4 @@ class Menu(Modal): pygame.draw.rect(self.surf, "black", rect) pygame.draw.rect(self.surf, "gray", rect, 1) - KEY_METHODS = {frozenset(): {pygame.K_ESCAPE: Modal.deactivate}} + key_methods = {frozenset(): {pygame.K_ESCAPE: Modal.deactivate}} diff --git a/bookpaint/page_menu.py b/bookpaint/page_menu.py index d68eb1b..e1b0852 100644 --- a/bookpaint/page_menu.py +++ b/bookpaint/page_menu.py @@ -14,13 +14,13 @@ class PageMenu(Modal): button_height = 128 self.base_rect = pygame.Rect( (size[0] // 4, size[1] * 6 // 8), - (size[0] // 2, max(size[1] // 8, button_height * 2 + 16)), + (size[0] // 2, button_height * 2 + 16), ) y = self.base_rect.top - Label( + self.label = Label( self, pygame.Rect((self.base_rect.left, y), (self.base_rect.width, button_height)), - "Page Menu", + "", Label.HAlign.CENTER, ) y += button_height + 16 @@ -48,15 +48,12 @@ class PageMenu(Modal): pygame.draw.rect(self.surf, "black", rect) pygame.draw.rect(self.surf, "gray", rect, 1) - def first(self): - pass + key_methods = {frozenset(): {pygame.K_ESCAPE: Modal.deactivate}} - def last(self): - pass - - KEY_METHODS = { - frozenset(): { - pygame.K_ESCAPE: Modal.deactivate, - pygame.K_p: Modal.deactivate, - } - } + def update_label(self): + self.label.value = " / ".join( + ( + str(self.parent.book_manager.get_current_index() + 1), + str(len(self.parent.book_manager.pages)), + ) + ) \ No newline at end of file diff --git a/memory.py b/memory.py index a1611f0..950bc2a 100755 --- a/memory.py +++ b/memory.py @@ -13,7 +13,7 @@ class QuittableModal(Modal): def handle_quit(self, ev): self.parent.handle_quit(ev) - KEY_METHODS = {frozenset(): {pygame.K_ESCAPE: handle_quit}} + key_methods = {frozenset(): {pygame.K_ESCAPE: handle_quit}} class NameMenu(QuittableModal): diff --git a/ui/child.py b/ui/child.py index 6d9ecfe..d197021 100644 --- a/ui/child.py +++ b/ui/child.py @@ -6,6 +6,7 @@ from .root import Root class Child(EventMethodDispatcher): def __init__(self, parent, enabled=True): + super().__init__() self.parent = parent parent.children.append(self) self.enabled = enabled diff --git a/ui/drop_down.py b/ui/drop_down.py index f78fe0f..2d857b1 100644 --- a/ui/drop_down.py +++ b/ui/drop_down.py @@ -23,7 +23,7 @@ class DropDownMenu(Modal): rect.bottomleft, (rect.width, rect.height * len(entries)) ) - KEY_METHODS = {frozenset(): {pygame.K_ESCAPE: Modal.deactivate}} + key_methods = {frozenset(): {pygame.K_ESCAPE: Modal.deactivate}} def choose(self, i=None): self.deactivate() diff --git a/ui/event_method_dispatcher.py b/ui/event_method_dispatcher.py index 8e54854..2bdd8d7 100644 --- a/ui/event_method_dispatcher.py +++ b/ui/event_method_dispatcher.py @@ -2,24 +2,26 @@ from functools import partial import pygame +from .utils import CowDict + class EventMethodDispatcher: MODS = (pygame.KMOD_CTRL, pygame.KMOD_ALT, pygame.KMOD_META, pygame.KMOD_SHIFT) - KEY_METHODS = {} + + def __init__(self): + self.key_methods = CowDict(getattr(type(self), "key_methods", {})) def get_key_method(self, key, mod): mods = set() for mask in self.MODS: if mod & mask: mods.add(mask) - method = self.KEY_METHODS.get(frozenset(mods), {}).get(key) + method = self.key_methods.get(frozenset(mods), {}).get(key) if method is not None: return partial(method, self) return None def handle_keydown(self, ev): - if not self.KEY_METHODS: - return key_method = self.get_key_method(ev.key, ev.mod) if key_method is not None: key_method() diff --git a/ui/root.py b/ui/root.py index 1403fdc..d561d84 100644 --- a/ui/root.py +++ b/ui/root.py @@ -7,8 +7,6 @@ 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 @@ -17,12 +15,12 @@ class Root(Parent): self.clock = pygame.time.Clock() self.stop_event = False self.root = self - self.focus_stack: list[Root | Child] = [self] + self.focus_stack: list[Parent] = [self] def handle_quit(self, _=None): self.running = False - KEY_METHODS = {frozenset(): {pygame.K_ESCAPE: handle_quit}} + key_methods = {frozenset(): {pygame.K_ESCAPE: handle_quit}} @property def focused(self): diff --git a/ui/text_input.py b/ui/text_input.py index 7b5d5c8..325ca1e 100644 --- a/ui/text_input.py +++ b/ui/text_input.py @@ -272,7 +272,7 @@ class TextInput(Focusable, Child): self.cursor = None super().deactivate() - KEY_METHODS = { + key_methods = { frozenset(): { pygame.K_LEFT: key_left, pygame.K_RIGHT: key_right, diff --git a/ui/utils.py b/ui/utils.py new file mode 100644 index 0000000..a0f0742 --- /dev/null +++ b/ui/utils.py @@ -0,0 +1,25 @@ +class CowDict: + def __init__(self, orig_dict): + self.orig_dict = orig_dict + self.copy = None + + @property + def dict(self): + return self.copy or self.orig_dict + + def __getitem__(self, key): + return self.dict[key] + + def _clone(self): + if self.copy is None: + self.copy = self.orig_dict.copy() + + def __setitem__(self, key, value): + self._clone() + self.copy[key] = value + + def __getattr__(self, item): + mod_methods = ("clear", "pop", "popitem", "setdefault", "update") + if self.copy is None and item in mod_methods: + self._clone() + return getattr(self.dict, item) -- 2.51.0