From: mar77i Date: Sat, 10 May 2025 11:34:46 +0000 (+0200) Subject: hook up page navigation X-Git-Url: https://git.mar77i.info/?a=commitdiff_plain;h=a32b59c1e6b078f473d062e08e430bf0cc322bb7;p=zenbook_gui hook up page navigation --- diff --git a/bookpaint/book_manager.py b/bookpaint/book_manager.py index 39fd1f7..e0dd84e 100644 --- a/bookpaint/book_manager.py +++ b/bookpaint/book_manager.py @@ -1,3 +1,5 @@ +from enum import Enum, auto +from operator import itemgetter from pathlib import Path import pygame @@ -18,21 +20,24 @@ class BookManager: except ValueError: pages.append(p) else: - numbered_pages.append(p) + numbered_pages.append((p, page_no)) free_page_no = max(free_page_no, page_no + 1) pages = sorted(pages) - pages.extend(sorted(numbered_pages)) + pages.extend(p[0] for p in sorted(numbered_pages, key=itemgetter(1))) return free_page_no, pages - def __init__(self, path: Path): + def __init__(self, parent, path: Path): + self.parent = parent self.path = path + if not path.exists(): + path.mkdir(0o755) self.free_page_no, self.pages = self.get_pages() self.current_file = self.get_new_file() def get_new_file(self): return self.path / f"{self.free_page_no}{self.SUFFIX}" - def save_file(self, surf): + def save_file(self): if self.current_file not in self.pages: if self.current_file.exists(): self.pages.clear() @@ -41,26 +46,47 @@ class BookManager: self.current_file = self.get_new_file() self.pages.append(self.current_file) self.free_page_no += 1 - pygame.image.save(surf, self.current_file) + pygame.image.save(self.parent.draw_image.surface, self.current_file) - NewFile = type("NewFileType", (), {})() + class Nav(Enum): + FIRST = auto() + PREV = auto() + NEXT = auto() + LAST = auto() + NEW = auto() - def next_file(self): - if self.current_file not in self.pages: + def _get_index_for_page(self, nav): + if nav is self.Nav.NEW or len(self.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 + if nav is self.Nav.PREV: + if current_index == -1: + return len(self.pages) - 1 + elif current_index > 0: + return current_index - 1 return None - i = self.pages.index(self.current_file) - if i == len(self.pages) - 1: - self.current_file = self.get_new_file() - return self.NewFile - self.current_file = self.pages[i + 1] - return pygame.image.load(self.current_file) + elif nav is self.Nav.NEXT: + if current_index == -1: + return None + elif current_index < len(self.pages) - 1: + return current_index + 1 + return -1 + raise IndexError(nav) - def prev_file(self): - if self.current_file not in self.pages: - i = len(self.pages) - else: - i = self.pages.index(self.current_file) - if i == 0: - return None - self.current_file = self.pages[i - 1] - return pygame.image.load(self.current_file) + def nav(self, nav): + if self.parent.draw_image.image_dirty: + self.save_file() + i = self._get_index_for_page(nav) + if 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: + self.current_file = self.get_new_file() + self.parent.draw_image.clear() diff --git a/bookpaint/bookpaint.py b/bookpaint/bookpaint.py index e42e614..e68de1d 100644 --- a/bookpaint/bookpaint.py +++ b/bookpaint/bookpaint.py @@ -12,13 +12,7 @@ from .line_menu import LineMenu from .menu import Menu from .page_menu import PageMenu - -# - color menu; set background (default for new pages) and foreground color -# - stub with a 6 hex-digit textinput and a little rectangle to show the color # - page menu; browse through next and prev pages using stylus -# - line menu: choose line width and stroke method -# - info layer (F3) -# - list color and page menus in the main menu but also using keyboard shortcuts c/p/l class BookPaint(Root): @@ -35,33 +29,16 @@ class BookPaint(Root): self.line_menu = LineMenu(self) self.page_menu = PageMenu(self) self.menu = Menu(self) - book_path = Path("book") - if not book_path.exists(): - book_path.mkdir(0o755) - self.book_manager = BookManager(book_path) + self.book_manager = BookManager(self, Path("book")) def show_menu(self, menu_attr): getattr(self, menu_attr).activate() def save_file(self): - self.book_manager.save_file(self.draw_image.surface) - self.draw_image.image_dirty = False - - def set_image(self, surf): - if surf == BookManager.NewFile: - self.draw_image.clear() - elif surf: - self.draw_image.set_image(surf) - - def next_file(self): - if self.draw_image.image_dirty: - self.save_file() - self.set_image(self.book_manager.next_file()) + self.book_manager.save_file() - def prev_file(self): - if self.draw_image.image_dirty: - self.save_file() - self.set_image(self.book_manager.prev_file()) + def nav(self, nav): + self.book_manager.nav(nav) KEY_METHODS = { frozenset(): { @@ -72,7 +49,10 @@ class BookPaint(Root): }, frozenset({pygame.KMOD_CTRL}): { pygame.K_s: save_file, - pygame.K_PAGEDOWN: next_file, - pygame.K_PAGEUP: prev_file, + pygame.K_HOME: partial(nav, nav=BookManager.Nav.FIRST), + pygame.K_PAGEUP: partial(nav, nav=BookManager.Nav.PREV), + pygame.K_PAGEDOWN: partial(nav, nav=BookManager.Nav.NEXT), + pygame.K_END: partial(nav, nav=BookManager.Nav.LAST), + pygame.K_n: partial(nav, nav=BookManager.Nav.NEW), }, } diff --git a/bookpaint/color_menu.py b/bookpaint/color_menu.py index ead9b8d..892b13b 100644 --- a/bookpaint/color_menu.py +++ b/bookpaint/color_menu.py @@ -64,4 +64,9 @@ class ColorMenu(Modal): ), ) - KEY_METHODS = {frozenset(set()): {pygame.K_ESCAPE: Modal.deactivate}} + KEY_METHODS = { + frozenset(): { + pygame.K_ESCAPE: Modal.deactivate, + pygame.K_c: Modal.deactivate, + } + } diff --git a/bookpaint/draw_image.py b/bookpaint/draw_image.py index a55dc18..20706de 100644 --- a/bookpaint/draw_image.py +++ b/bookpaint/draw_image.py @@ -12,8 +12,6 @@ class DrawImage(Child): SQUARE = auto() PYGAME = auto() - DEFAULT_STROKE_METHOD = StrokeMethod.ROUND - def __init__(self, parent, color): super().__init__(parent) if not isinstance(color, pygame.Color): @@ -21,27 +19,27 @@ class DrawImage(Child): self.color = color self.line_width = 1 self.image = pygame.Surface(self.surf.get_size(), 0, 24) - self.stroke_method = self.DEFAULT_STROKE_METHOD + self.stroke_method = self.StrokeMethod.ROUND self.image_dirty = False self.prev_pos = None self.clear() def clear(self): self.image.fill("black") + self.prev_pos = None + self.image_dirty = False + self.dirty = True + + def set_image(self, surf): + if self.image_dirty: + self.parent.book_manager.save_file() + self.image.blit(surf, (0, 0)) self.image_dirty = False self.dirty = True - self.prev_pos = None def dot(self, surf, pos): if self.stroke_method == self.StrokeMethod.SQUARE: - hw = self.line_width // 2 - pygame.draw.rect( - surf, - self.color, - pygame.Rect( - (pos[0] - hw, pos[1] - hw), (self.line_width, self.line_width) - ) - ) + pass elif self.line_width // 2 == 0: surf.set_at(pos, self.color) else: @@ -49,7 +47,8 @@ class DrawImage(Child): def draw(self): self.surf.blit(self.image, (0, 0)) - self.dot(self.surf, pygame.mouse.get_pos()) + if self.root.focus_stack[-1] is self.parent: + self.dot(self.surf, pygame.mouse.get_pos()) def handle_mousebuttondown(self, ev): if ev.button == 1: @@ -82,7 +81,3 @@ class DrawImage(Child): if ev.button == 1: self.handle_mousemotion(ev) self.prev_pos = None - - def set_image(self, surf): - self.image.blit(surf, (0, 0)) - self.dirty = True diff --git a/bookpaint/line_menu.py b/bookpaint/line_menu.py index a3468d5..705d85e 100644 --- a/bookpaint/line_menu.py +++ b/bookpaint/line_menu.py @@ -36,18 +36,17 @@ class LineMenu(Modal): "", ) y += button_height + 16 - self.stroke_methods = list(DrawImage.StrokeMethod) self.stroke_method = DropDown( self, pygame.Rect((self.size_third[0], y), (x, 128)), - self.parent.draw_image.stroke_method.name.title(), - [m.name.title() for m in self.stroke_methods], + "", + [m.name.title() for m in DrawImage.StrokeMethod], self.set_stroke_method, ) def activate(self): super().activate() - self.update_value(self.parent.draw_image.line_width) + self.update_values() def set_line_width(self, value): if isinstance(value, str): @@ -55,16 +54,19 @@ class LineMenu(Modal): if value <= 0: value = 1 self.parent.draw_image.line_width = value - self.update_value(value) + self.update_values() def set_stroke_method(self, value): - value = self.stroke_methods[value] - self.parent.draw_image.stroke_method = value - self.stroke_method.value = value.name.title() + self.parent.draw_image.stroke_method = DrawImage.StrokeMethod[ + self.stroke_method.dropdown_menu.children[value].value.upper() + ] + self.update_values() - def update_value(self, value): - self.slider.value = value - self.input.value = str(value) + def update_values(self): + line_width = self.parent.draw_image.line_width + self.slider.value = line_width + self.input.value = str(line_width) + self.stroke_method.value = self.parent.draw_image.stroke_method.name.title() self.dirty = True def draw_modal(self): @@ -74,5 +76,9 @@ class LineMenu(Modal): pygame.draw.rect(self.surf, "black", rect) pygame.draw.rect(self.surf, "gray", rect, 1) - - KEY_METHODS = {frozenset(set()): {pygame.K_ESCAPE: Modal.deactivate}} + KEY_METHODS = { + frozenset(): { + pygame.K_ESCAPE: Modal.deactivate, + pygame.K_l: Modal.deactivate, + } + } diff --git a/bookpaint/menu.py b/bookpaint/menu.py index cbfe73b..0f3ff72 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(set()): {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 2f19c8f..d68eb1b 100644 --- a/bookpaint/page_menu.py +++ b/bookpaint/page_menu.py @@ -1,14 +1,62 @@ +from functools import partial + import pygame -from ui import Label, Modal +from ui import Button, Label, Modal + +from .book_manager import BookManager class PageMenu(Modal): def __init__(self, parent): super().__init__(parent) - Label(self, pygame.Rect((100, 100), (200, 128)), "Page Menu") + size = self.surf.get_size() + 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)), + ) + y = self.base_rect.top + Label( + self, + pygame.Rect((self.base_rect.left, y), (self.base_rect.width, button_height)), + "Page Menu", + Label.HAlign.CENTER, + ) + y += button_height + 16 + callbacks_per_value = ( + ("|<", partial(self.parent.nav, BookManager.Nav.FIRST)), + ("<", partial(self.parent.nav, BookManager.Nav.PREV)), + (">", partial(self.parent.nav, BookManager.Nav.NEXT)), + (">|", partial(self.parent.nav, BookManager.Nav.LAST)), + ("NEW", partial(self.parent.nav, BookManager.Nav.NEW)), + ) + button_width = size[0] // (2 * len(callbacks_per_value)) + x = self.base_rect.left + for value, callback in callbacks_per_value: + Button( + self, + pygame.Rect((x, y), (button_width, button_height)), + value, + callback + ) + x += button_width def draw_modal(self): + rect = self.base_rect.copy() + rect.inflate_ip((32, 32)) + pygame.draw.rect(self.surf, "black", rect) + pygame.draw.rect(self.surf, "gray", rect, 1) + + def first(self): + pass + + def last(self): pass - KEY_METHODS = {frozenset(set()): {pygame.K_ESCAPE: Modal.deactivate}} + KEY_METHODS = { + frozenset(): { + pygame.K_ESCAPE: Modal.deactivate, + pygame.K_p: Modal.deactivate, + } + }