]> git.mar77i.info Git - zenbook_gui/commitdiff
hook up page navigation
authormar77i <mar77i@protonmail.ch>
Sat, 10 May 2025 11:34:46 +0000 (13:34 +0200)
committermar77i <mar77i@protonmail.ch>
Sat, 10 May 2025 11:34:46 +0000 (13:34 +0200)
bookpaint/book_manager.py
bookpaint/bookpaint.py
bookpaint/color_menu.py
bookpaint/draw_image.py
bookpaint/line_menu.py
bookpaint/menu.py
bookpaint/page_menu.py

index 39fd1f7ab579cd800c116832401f1ba42171c827..e0dd84e4e7970dec55b1ed42da160c907e951188 100644 (file)
@@ -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()
index e42e6147819a7c73b59cf638e0b030f43538835b..e68de1d9ed43a7f928ddfeac2f5a9b5f2088e861 100644 (file)
@@ -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),
         },
     }
index ead9b8ddd65a9e7e40998a27ef21197143f32b51..892b13bb5d685586abe913cb3fb36361a7c71c8d 100644 (file)
@@ -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,
+        }
+    }
index a55dc182f93ad5cb471744a87a60cfcbcb848474..20706de7cf542a2d526e2e69fd35b05892d6846d 100644 (file)
@@ -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
index a3468d585fa8d49837a1e8f15a7ca35427a225f6..705d85efab07a6a8fb9d309720b7cb9387faa6b6 100644 (file)
@@ -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,
+        }
+    }
index cbfe73bf0746ac85fbbdcc20b6433b78e29f4c9a..0f3ff7217bda979f208eac6240333836524a0366 100644 (file)
@@ -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}}
index 2f19c8f862dd95d9437726b1ccee23c9897ce723..d68eb1b25e57c681bc24341cf9ea0242781e0304 100644 (file)
@@ -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,
+        }
+    }