]> git.mar77i.info Git - zenbook_gui/commitdiff
consolidate label, start over with bookpaint
authormar77i <mar77i@protonmail.ch>
Thu, 8 May 2025 00:32:20 +0000 (02:32 +0200)
committermar77i <mar77i@protonmail.ch>
Thu, 8 May 2025 00:32:20 +0000 (02:32 +0200)
16 files changed:
bookpaint/book_manager.py [new file with mode: 0644]
bookpaint/bookpaint.py
bookpaint/bookpaint_menu.py [deleted file]
bookpaint/color_chooser.py [deleted file]
bookpaint/color_circle.py [deleted file]
bookpaint/color_plane.py [deleted file]
bookpaint/draw_image.py
bookpaint/menu.py [new file with mode: 0644]
bookpaint/utils.py [deleted file]
bookpaint/value_slider.py [deleted file]
memory.py
ui/__init__.py
ui/drop_down.py
ui/label.py
ui/text_input.py
vs_memory.py

diff --git a/bookpaint/book_manager.py b/bookpaint/book_manager.py
new file mode 100644 (file)
index 0000000..39fd1f7
--- /dev/null
@@ -0,0 +1,66 @@
+from pathlib import Path
+
+import pygame
+
+
+class BookManager:
+    SUFFIX = ".png"
+
+    def get_pages(self):
+        free_page_no = 1
+        pages = []
+        numbered_pages = []
+        for p in self.path.iterdir():
+            if p.suffix != self.SUFFIX:
+                continue
+            try:
+                page_no = int(p.stem)
+            except ValueError:
+                pages.append(p)
+            else:
+                numbered_pages.append(p)
+                free_page_no = max(free_page_no, page_no + 1)
+        pages = sorted(pages)
+        pages.extend(sorted(numbered_pages))
+        return free_page_no, pages
+
+    def __init__(self, path: Path):
+        self.path = path
+        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):
+        if self.current_file not in self.pages:
+            if self.current_file.exists():
+                self.pages.clear()
+                self.free_page_no, pages = self.get_pages()
+                self.pages.extend(pages)
+                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)
+
+    NewFile = type("NewFileType", (), {})()
+
+    def next_file(self):
+        if self.current_file not in self.pages:
+            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)
+
+    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)
index 20b4f3bbf9b6da9faa9685ef337ccc5676a6706b..473f03dc050d05aefa60e4d0f3d8872a548f2958 100644 (file)
@@ -1,10 +1,19 @@
+from pathlib import Path
+
 import pygame
 
 from ui import Root
-from zenbook_conf.xrandr import XrandrConf
 
-from .bookpaint_menu import BookPaintMenu
+from .book_manager import BookManager
 from .draw_image import DrawImage
+from .menu import Menu
+
+
+# - color menu; set background (default for new pages) and foreground color
+#   - stub with a 6 hex-digit textinputs and a little rectangle
+# - page menu; browse through next and prev pages using stylus
+# - info layer (F3)
+# - list color and page menus in the esc menu but also using keyboard shortcuts c/p
 
 
 class BookPaint(Root):
@@ -12,42 +21,45 @@ class BookPaint(Root):
 
     def __init__(self):
         pygame.init()
-        self.current_display = 0
-        self.display_flags = pygame.FULLSCREEN
         super().__init__(
-            pygame.display.set_mode(
-                (0, 0), self.display_flags, display=self.current_display
-            ),
+            pygame.display.set_mode((0, 0), pygame.FULLSCREEN),
             pygame.font.Font(None, size=96),
         )
-        self.draw_image = DrawImage(self, self.BACKGROUND_COLOR, "white")
-        self.menu = BookPaintMenu(self, self.draw_image)
-        self.xrandr_conf = XrandrConf(True)
+        self.menu = Menu(self)
+        self.draw_image = DrawImage(self, "white")
+        book_path = Path("/dev/shm/book")
+        if not book_path.exists():
+            book_path.mkdir(0o755)
+        self.book_manager = BookManager(book_path)
 
     def key_escape(self):
         self.menu.activate()
 
-    KEY_METHODS = {frozenset(set()): {pygame.K_ESCAPE: key_escape}}
-
-    def quit(self):
-        self.running = False
-
-    def next_display(self):
-        self.xrandr_conf.renew()
-        if self.xrandr_conf.count_active() != len(pygame.display.get_desktop_sizes()):
-            pygame.display.quit()
-            pygame.display.init()
-            reset = True
-        else:
-            reset = False
-        num_displays = len(pygame.display.get_desktop_sizes())
-        current_display = (self.current_display + 1) % num_displays
-        if current_display != self.current_display:
-            self.current_display = current_display
-            reset = True
-        if not reset:
-            return
-        self.surf = pygame.display.set_mode(
-            (0, 0), self.display_flags, display=self.current_display
-        )
-        self.dirty = True
+    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())
+
+    def prev_file(self):
+        if self.draw_image.image_dirty:
+            self.save_file()
+        self.set_image(self.book_manager.prev_file())
+
+    KEY_METHODS = {
+        frozenset(): {pygame.K_ESCAPE: key_escape},
+        frozenset({pygame.KMOD_CTRL}): {
+            pygame.K_s: save_file,
+            pygame.K_PAGEDOWN: next_file,
+            pygame.K_PAGEUP: prev_file,
+        },
+    }
diff --git a/bookpaint/bookpaint_menu.py b/bookpaint/bookpaint_menu.py
deleted file mode 100644 (file)
index 44a364c..0000000
+++ /dev/null
@@ -1,230 +0,0 @@
-from functools import partial
-from pathlib import Path
-
-import pygame
-
-from layout import BarLayout, GridLayout
-from ui import (
-    Button,
-    CenterLabel,
-    ColorButton,
-    DropDown,
-    Modal,
-    Rect,
-    RepeatButton,
-    RightLabel,
-    Slider,
-    TextInput,
-)
-
-from .color_chooser import ColorChooser
-from .draw_image import InputMethod, StrokeMethod
-from .utils import color_to_hex
-
-
-class BookPaintMenu(Modal):
-    def get_stroke_methods(self):
-        return (
-            self.draw_image.stroke_method.name.title(),
-            [stroke_method.name.title() for stroke_method in StrokeMethod],
-        )
-
-    def get_input_methods(self):
-        labels = InputMethod.labels()
-        return (
-            labels[tuple(InputMethod).index(self.draw_image.input_method)],
-            labels,
-        )
-
-    def __init__(self, parent, draw_image):
-        super().__init__(parent)
-        self.draw_image = draw_image
-        Button(
-            self,
-            pygame.Rect((self.surf.get_width() - 128, 0), (128, 128)),
-            "×",
-            self.root.quit,
-        )
-        Button(
-            self,
-            pygame.Rect((self.surf.get_width() - 256, 0), (128, 128)),
-            "_",
-            pygame.display.iconify,
-        )
-        Button(
-            self,
-            pygame.Rect((self.surf.get_width() - 384, 0), (128, 128)),
-            "»",
-            self.root.next_display,
-        )
-        size = self.surf.get_size()
-        grid_layout = GridLayout(
-            pygame.Rect((512, 256), (size[0] - 1024, size[1] - 768)), (3, 6)
-        )
-        RightLabel(
-            self,
-            grid_layout.get_rect((0, 0)),
-            "Book Path",
-        )
-        RightLabel(
-            self,
-            grid_layout.get_rect((0, 1)),
-            "New Page Color",
-        )
-        RightLabel(
-            self,
-            grid_layout.get_rect((0, 2)),
-            "Line Color",
-        )
-        RightLabel(
-            self,
-            grid_layout.get_rect((0, 3)),
-            "Line Width",
-        )
-        RightLabel(
-            self,
-            grid_layout.get_rect((0, 4)),
-            "Stroke Method",
-        )
-        RightLabel(
-            self,
-            grid_layout.get_rect((0, 5)),
-            "Input",
-        )
-        rect = grid_layout.get_rect((1, 0), (2, 1))
-        pad = 96 - rect.height
-        self.path_input = TextInput(
-            self,
-            rect.inflate((pad, pad)),
-            self.set_book_path,
-            str(Path("book/").absolute()),
-            unfocused_align=TextInput.RIGHT,
-        )
-        color_chooser = ColorChooser(self)
-        self.background_color = ColorButton(
-            self,
-            grid_layout.get_rect((1, 1)).inflate((pad, pad)),
-            self.draw_image.background_color,
-            partial(color_chooser.activate_for, "background_color"),
-        )
-        Button(
-            self,
-            grid_layout.get_rect((2, 1)).inflate((pad, pad)),
-            "Fill Page",
-            self.draw_image.clear,
-        )
-        self.foreground_color = ColorButton(
-            self,
-            grid_layout.get_rect((1, 2)).inflate((pad, pad)),
-            self.draw_image.foreground_color,
-            partial(color_chooser.activate_for, "foreground_color"),
-        )
-        self.foreground_color_value = CenterLabel(
-            self,
-            grid_layout.get_rect((2, 2)),
-            color_to_hex(self.draw_image.foreground_color),
-        )
-        self.width_slider = Slider(
-            self,
-            grid_layout.get_rect((1, 3)).inflate((pad, pad)),
-            Slider.HORIZONTAL,
-            self.draw_image.line_width - 1,
-            96,
-            self.set_width,
-        )
-        self.width_label = CenterLabel(
-            self,
-            grid_layout.get_rect((2, 3)),
-            "1",
-        )
-        self.stroke_method = DropDown(
-            self,
-            grid_layout.get_rect((1, 4)).inflate((pad, pad)),
-            *self.get_stroke_methods(),
-            self.set_stroke_method,
-        )
-        self.input_method = DropDown(
-            self,
-            grid_layout.get_rect((1, 5), (2, 1)).inflate((pad, pad)),
-            *self.get_input_methods(),
-            self.set_input_method,
-        )
-        rect = pygame.Rect(
-            (512, size[1] - 384),
-            (size[0] - 1024, 256),
-        )
-        Rect(self, rect, "black", "darkgray")
-
-        self.page_label = CenterLabel(
-            self,
-            pygame.Rect(
-                (rect.left, rect.top + rect.height // 4 - 48), (rect.width, 96)
-            ),
-            "0 / 0",
-        )
-        button_bar_layout = BarLayout(
-            pygame.Rect(
-                (rect.left, rect.top + rect.height * 3 // 4 - 48), (rect.width, 96)
-            ),
-            abs(pad),
-        )
-        Button(
-            self,
-            button_bar_layout.get_rect(),
-            "|<",
-            lambda: None,
-        )
-        RepeatButton(
-            self,
-            button_bar_layout.get_rect(),
-            "<",
-            lambda: None,
-        )
-        RepeatButton(
-            self,
-            button_bar_layout.get_rect(),
-            ">",
-            lambda: None,
-        )
-        Button(
-            self,
-            button_bar_layout.get_rect(),
-            ">|",
-            lambda: None,
-        )
-        Button(
-            self,
-            button_bar_layout.get_rect(),
-            "New",
-            lambda: None,
-        )
-
-    KEY_METHODS = {frozenset(set()): {pygame.K_ESCAPE: Modal.deactivate}}
-
-    def set_book_path(self, value):
-        print("book path", value)
-
-    def set_color(self, attr, color):
-        setattr(self.draw_image, attr, color)
-        getattr(self, attr).color = color
-        self.dirty = True
-
-    def set_width(self, slider_width):
-        if slider_width < 0:
-            slider_width = 0
-        if self.width_slider.value != slider_width:
-            self.width_slider.value = slider_width
-            self.dirty = True
-        width = (slider_width * 256 // self.width_slider.extent) + 1
-        if self.draw_image.line_width != width:
-            self.draw_image.line_width = width
-            self.width_label.value = str(width)
-            self.dirty = True
-
-    def set_stroke_method(self, i):
-        self.draw_image.stroke_method = StrokeMethod(i + 1)
-        self.stroke_method.value = self.draw_image.stroke_method.name.title()
-
-    def set_input_method(self, i):
-        self.draw_image.input_method = InputMethod(i + 1)
-        self.input_method.value = InputMethod.labels()[i]
diff --git a/bookpaint/color_chooser.py b/bookpaint/color_chooser.py
deleted file mode 100644 (file)
index a0ce42a..0000000
+++ /dev/null
@@ -1,233 +0,0 @@
-from functools import partial
-
-import pygame
-
-from ui import Button, ColorButton, Label, Modal, Rect, TabBar
-
-from .color_circle import ColorCircle
-from .color_plane import ColorPlane
-from .utils import hsv_to_color
-
-
-class ColorChooser(Modal):
-    def get_color_grid(self, lefttop, cell_size):
-        """
-        8 * 8 * 8 hsv colors => 351 unique colors of that cube.
-        Arrange them in a visually pleasing way.
-        """
-        x = y = 0
-        pos = list(lefttop)
-        colors_seen = set()
-        grays = []
-        grays_pos = None
-        for v_range in (range(7, 3, -1), range(3, -1, -1)):
-            for s in range(7, -1, -1):
-                for v in v_range:
-                    for h in range(7):
-                        color = hsv_to_color((h * 255 / 7, s * 255 / 7, v * 255 / 7))
-                        int_color = (color.r << 16) | (color.g << 8) | color.b
-                        if int_color not in colors_seen:
-                            colors_seen.add(int_color)
-                            if v == 0:
-                                grays_pos = pos.copy()
-                                grays.insert(0, color)
-                            elif s == 0:
-                                grays.append(color)
-                            else:
-                                yield (pygame.Rect(pos, cell_size), color)
-                        x += 1
-                        pos[0] += cell_size[0]
-                        if x % 7 == 0:
-                            pos[0] += cell_size[0] // 2
-                        if x < 28:
-                            continue
-                        x, pos[0] = 0, lefttop[0]
-                        y += 1
-                        if y % 8 == 0:
-                            pos[1] += cell_size[1] // 2
-                        else:
-                            pos[1] += cell_size[1]
-        for i, gray in enumerate((*grays[1:], grays[0])):
-            yield (
-                pygame.Rect(
-                    (
-                        grays_pos[0] + (i % 4) * cell_size[0],
-                        grays_pos[1] + (i // 4) * cell_size[1],
-                    ),
-                    cell_size,
-                ),
-                gray,
-            )
-
-    def __init__(self, parent):
-        super().__init__(parent)
-        size = self.surf.get_size()
-        rect = pygame.Rect(
-            (448, 192),
-            (size[0] - 896, size[1] - 384),
-        )
-        Rect(self, rect, "black", "gray34")
-        self.attr = None
-        self.color = pygame.Color("red")
-        buttons = []
-        for r, color in self.get_color_grid((rect.left + 48, rect.top + 192), (64, 64)):
-            buttons.append(ColorButton(self, r, color, partial(self.set_color, color)))
-        self.color_circle = ColorCircle(
-            self,
-            pygame.Rect((rect.left + 1024, rect.top + 256), (512, 512)),
-            self.chooser_set_hsv,
-            self.color,
-        )
-        self.chooser_button = ColorButton(
-            self,
-            pygame.Rect((rect.right - 1024, rect.bottom - 192), (768, 128)),
-            self.color,
-            self.deactivate,
-        )
-        #self.hsv_rgb_buttons = (
-        #    Button(
-        #        self,
-        #        pygame.Rect((rect.left + 1024, rect.top + 160), (96, 96)),
-        #        "H",
-        #        partial(self.set_plane_axis, 0, AxisSetting.HUE),
-        #    ),
-        #    Button(
-        #        self,
-        #        pygame.Rect((rect.left + 1120, rect.top + 160), (96, 96)),
-        #        "S",
-        #        partial(self.set_plane_axis, 0, AxisSetting.SATURATION),
-        #        True,
-        #    ),
-        #    Button(
-        #        self,
-        #        pygame.Rect((rect.left + 1216, rect.top + 160), (96, 96)),
-        #        "V",
-        #        partial(self.set_plane_axis, 0, AxisSetting.VALUE)
-        #    ),
-        #    Button(
-        #        self,
-        #        pygame.Rect((rect.left + 1312, rect.top + 160), (96, 96)),
-        #        "R",
-        #        partial(self.set_plane_axis, 0, AxisSetting.RED)
-        #    ),
-        #    Button(
-        #        self,
-        #        pygame.Rect((rect.left + 1408, rect.top + 160), (96, 96)),
-        #        "G",
-        #        partial(self.set_plane_axis, 0, AxisSetting.GREEN)
-        #    ),
-        #    Button(
-        #        self,
-        #        pygame.Rect((rect.left + 1504, rect.top + 160), (96, 96)),
-        #        "B",
-        #        partial(self.set_plane_axis, 0, AxisSetting.BLUE)
-        #    ),
-        #    Button(
-        #        self,
-        #        pygame.Rect((rect.left + 1792, rect.top + 256), (96, 96)),
-        #        "H",
-        #        partial(self.set_plane_axis, 1, AxisSetting.HUE)
-        #    ),
-        #    Button(
-        #        self,
-        #        pygame.Rect((rect.left + 1792, rect.top + 352), (96, 96)),
-        #        "S",
-        #        partial(self.set_plane_axis, 1, AxisSetting.SATURATION)
-        #    ),
-        #    Button(
-        #        self,
-        #        pygame.Rect((rect.left + 1792, rect.top + 448), (96, 96)),
-        #        "V",
-        #        partial(self.set_plane_axis, 1, AxisSetting.VALUE),
-        #        True,
-        #    ),
-        #    Button(
-        #        self,
-        #        pygame.Rect((rect.left + 1792, rect.top + 544), (96, 96)),
-        #        "R",
-        #        partial(self.set_plane_axis, 1, AxisSetting.RED)
-        #    ),
-        #    Button(
-        #        self,
-        #        pygame.Rect((rect.left + 1792, rect.top + 640), (96, 96)),
-        #        "G",
-        #        partial(self.set_plane_axis, 1, AxisSetting.GREEN)
-        #    ),
-        #    Button(
-        #        self,
-        #        pygame.Rect((rect.left + 1792, rect.top + 736), (96, 96)),
-        #        "B",
-        #        partial(self.set_plane_axis, 1, AxisSetting.BLUE)
-        #    ),
-        #)
-        #self.hsv_rgb_slider = Slider(
-        #    self,
-        #    pygame.Rect((rect.left + 928, rect.top + 256), (96, 768)),
-        #    Slider.VERTICAL,
-        #    0,
-        #    96,
-        #    self.set_hsv_rgb_slider,
-        #)
-        #self.hsv_rgb_slider.value = self.hsv_rgb_slider.extent
-        #self.hsv_rgb_slider_label = Label(
-        #    self, pygame.Rect((rect.left + 928, rect.top + 160), (96, 96)), "H"
-        #)
-        #self.color_plane = ColorPlane(
-        #    self,
-        #    pygame.Rect((rect.left + 1024, rect.top + 256), (768, 768)),
-        #    self.color,
-        #)
-        TabBar(
-            self,
-            pygame.Rect(rect.topleft, (1024, 128)),
-            ("Grid", "Chooser"),
-            (
-                buttons,
-                (
-                    self.color_circle,
-                    #self.hsv_rgb_slider_label,
-                    #self.hsv_rgb_slider,
-                    #*self.hsv_rgb_buttons,
-                    #self.color_plane,
-                    self.chooser_button,
-                ),
-            ),
-            1,
-        )
-
-    KEY_METHODS = {frozenset(set()): {pygame.K_ESCAPE: Modal.deactivate}}
-
-    def activate_for(self, attr):
-        self.attr = attr
-        super().activate()
-
-    def set_color(self, color):
-        self.color = color
-        self.deactivate()
-
-    def deactivate(self):
-        super().deactivate()
-        self.parent.set_color(self.attr, self.color)
-
-    def chooser_set_hsv(self, hsv):
-        self.chooser_button.color = self.color = hsv_to_color(hsv)
-        self.dirty = True
-
-    def set_plane_axis(self, axis, axis_setting):
-        if axis == 0:
-            self.color_plane.set_horizontal(axis_setting)
-        else:
-            self.color_plane.set_vertical(axis_setting)
-        #for button in self.hsv_rgb_buttons:
-        #    if button.callback.args[0] == 0:
-        #        button.highlight = button.callback.args[1] == self.color_plane.horizontal_setting
-        #    else:  # button.callback.args[0] == 1
-        #        button.highlight = button.callback.args[1] == self.color_plane.vertical_setting
-
-    def set_hsv_rgb_slider(self, value):
-        if value < 0:
-            value = 0
-        elif value > self.hsv_rgb_slider.extent:
-            value = self.hsv_rgb_slider.extent
-        self.hsv_rgb_slider.value = value
-        self.color_plane.set_base_value(value * 255 / self.hsv_rgb_slider.extent)
diff --git a/bookpaint/color_circle.py b/bookpaint/color_circle.py
deleted file mode 100644 (file)
index 45334b9..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-from math import atan2, cos, sin, tau
-from operator import itemgetter
-
-import pygame
-
-from ui import Child
-
-from .utils import distance, hsv_to_color, color_to_hsv
-
-
-class ColorCircle(Child):
-    @staticmethod
-    def get_hue_circle(size):
-        surf = pygame.Surface(size, pygame.SRCALPHA, 32)
-        surf.fill(pygame.Color(0, 0, 0, 0))
-        center = tuple(x // 2 for x in size)
-        outer_radius = min(center)
-        inner_radius = outer_radius * 3 // 4
-        inner_radius_squared = inner_radius ** 2
-        outer_radius_squared = outer_radius ** 2
-        with pygame.PixelArray(surf) as pa:
-            for x, col in enumerate(pa):
-                for y in range(len(col)):
-                    pos = (x - center[0]), (y - center[1])
-                    distance_squared = pos[0] ** 2 + pos[1] ** 2
-                    if inner_radius_squared <= distance_squared <= outer_radius_squared:
-                        angle = atan2(pos[1], pos[0])
-                        if angle < 0:
-                            angle += tau
-                        pa[x][y] = hsv_to_color((angle * 255 / tau, 255, 255))
-        return inner_radius, surf
-
-    get_alphas = (itemgetter(1), itemgetter(0))
-
-    @classmethod
-    def get_overlay_surfs(cls, size):
-        overlay_surfs = (
-            pygame.Surface(size, pygame.SRCALPHA, 32),
-            pygame.Surface(size, pygame.SRCALPHA, 32),
-        )
-        for surf, get_alpha in zip(overlay_surfs, cls.get_alphas):
-            with pygame.PixelArray(surf) as pa:
-                for x, col in enumerate(pa):
-                    for y in range(len(col)):
-                        pa[x][y] = pygame.Color(
-                            y // 2, y // 2, y // 2, 255 - get_alpha((x, y)),
-                        )
-        return overlay_surfs
-
-    def __init__(self, parent, rect, callback, color):
-        super().__init__(parent)
-        self.rect = rect
-        min_size = min(rect.size) // 2
-        self.inner_radius, self.hue_circle = self.get_hue_circle(rect.size)
-        self.overlay_surfs = self.get_overlay_surfs((min_size, min_size))
-        self.callback = callback
-        self.hsv = color_to_hsv(color)
-        self.sv_surf = None
-        self.pushed = None
-
-    def set_hue(self, rel_pos):
-        angle = atan2(rel_pos[1], rel_pos[0])
-        if angle < 0:
-            angle += tau
-        hue = angle * 256 / tau
-        if hue >= 256:
-            hue -= 256
-        if hue == self.hsv[0]:
-            return
-        self.sv_surf = None
-        self.hsv = (hue, *self.hsv[1:])
-        self.callback(self.hsv)
-        self.dirty = True
-
-    def set_sv(self, rel_pos):
-        hue_angle = self.hsv[0] * tau / 255
-        sv_surf_angle = tau * 9 / 8 - hue_angle
-        if sv_surf_angle >= tau:
-            sv_surf_angle -= tau
-        center = tuple(a // 2 for a in self.overlay_surfs[0].get_size())
-        dist = distance(rel_pos, (0, 0))
-        angle = atan2(rel_pos[1], rel_pos[0]) + sv_surf_angle
-        self.hsv = (
-            self.hsv[0],
-            min(max(0, center[0] + cos(angle) * dist), 255),
-            min(max(0, center[1] + sin(angle) * dist), 255),
-        )
-        self.callback(self.hsv)
-        self.dirty = True
-
-    def handle_mousebuttondown(self, ev):
-        if ev.button != 1 or not self.rect.collidepoint(ev.pos):
-            return
-        rel_pos = (ev.pos[0] - self.rect.centerx, ev.pos[1] - self.rect.centery)
-        self.pushed = distance(rel_pos, (0, 0)) >= self.inner_radius
-        if self.pushed:
-            self.set_hue(rel_pos)
-        else:
-            self.set_sv(rel_pos)
-
-    def handle_mousemotion(self, ev):
-        if self.pushed is None:
-            return
-        rel_pos = tuple(p - c for p, c in zip(ev.pos, self.rect.center))
-        if self.pushed:
-            self.set_hue(rel_pos)
-        else:
-            self.set_sv(rel_pos)
-
-    def handle_mousebuttonup(self, ev):
-        if self.pushed is None:
-            return
-        rel_pos = tuple(p - c for p, c in zip(ev.pos, self.rect.center))
-        if self.pushed is True:
-            self.set_hue(rel_pos)
-        else:
-            self.set_sv(rel_pos)
-        self.pushed = None
-
-    def draw_cursor(self, pos):
-        pygame.draw.circle(self.surf, "black", pos, 12)
-        pygame.draw.circle(self.surf, "darkgray", pos, 12, 2)
-
-    def draw_sv_surf(self, hue_angle):
-        sv_surf_size = self.overlay_surfs[0].get_size()
-        if self.sv_surf is None:
-            self.sv_surf = pygame.Surface(sv_surf_size, pygame.SRCALPHA, 32)
-            self.sv_surf.fill(hsv_to_color((self.hsv[0], 255, 255)))
-            for overlay_surf in self.overlay_surfs:
-                self.sv_surf.blit(overlay_surf, (0, 0))
-        sv_surf_angle = tau * 9 / 8 - hue_angle
-        if sv_surf_angle >= tau:
-            sv_surf_angle -= tau
-        sv_surf = pygame.transform.rotate(self.sv_surf, sv_surf_angle * 360 / tau)
-        self.surf.blit(
-            sv_surf,
-            tuple(
-                rc - sc
-                for rc, sc in zip(
-                    self.rect.center, tuple(a // 2 for a in sv_surf.get_size())
-                )
-            )
-        )
-
-        center = tuple(a // 2 for a in sv_surf_size)
-        cursor_distance = distance(self.hsv[1:], center)
-        cursor_angle = atan2(
-            self.hsv[2] - center[0], self.hsv[1] - center[1]
-        ) - sv_surf_angle
-        while cursor_angle < 0:
-            cursor_angle += tau
-        self.draw_cursor(
-            tuple(
-                c + f(cursor_angle) * cursor_distance
-                for c, f in zip(self.rect.center, (cos, sin))
-            )
-        )
-
-    def draw(self):
-        self.surf.blit(self.hue_circle, self.rect.topleft)
-        hue_angle = self.hsv[0] * tau / 255
-        radius = min(x // 2 for x in self.rect.size) * 7 // 8
-        self.draw_cursor(
-            (
-                self.rect.centerx + cos(hue_angle) * radius,
-                self.rect.centery + sin(hue_angle) * radius,
-            )
-        )
-        self.draw_sv_surf(hue_angle)
diff --git a/bookpaint/color_plane.py b/bookpaint/color_plane.py
deleted file mode 100644 (file)
index 84a26a4..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-from enum import Enum, auto
-
-import pygame
-
-from ui import Child
-from .utils import color_to_hsv, hsv_to_color
-
-
-class AxisSetting(Enum):
-    HUE = auto()
-    SATURATION = auto()
-    VALUE = auto()
-    RED = auto()
-    GREEN = auto()
-    BLUE = auto()
-
-
-class ColorPlane(Child):
-    """
-    6 modes at the top R/G/B/H/S/V
-    6 modes on the side R/G/B/H/S/V
-
-    Both modes can probably be the same, but they must at all times be
-    from the same group: R/G/B or H/S/V
-
-    So when you turn on a new group, we can set the other dimension
-    to the same - or not the same - value.
-    """
-    def get_surface(self):
-        size = self.rect.size
-        surf = pygame.Surface(size)
-        with pygame.PixelArray(surf) as pa:
-            for x, col in enumerate(pa):
-                for y in range(size[1]):
-                    col[y] = hsv_to_color(
-                        (int(y * 256 / size[1]), int(x * 256 / size[0]), 255)
-                    )
-        return surf
-
-    def __init__(self, parent, rect, color):
-        super().__init__(parent)
-        self.rect = rect
-        self.horizontal_setting = AxisSetting.SATURATION
-        self.vertical_setting = AxisSetting.VALUE
-        hsv = color_to_hsv(color)
-        self.values = hsv[1:]
-        self.base = hsv[0]
-        self.surface = self.get_surface()
-
-    def draw(self):
-        self.surf.blit(self.surface, self.rect.topleft)
-
-    HSV = (AxisSetting.HUE, AxisSetting.SATURATION, AxisSetting.VALUE)
-    RGB = (AxisSetting.RED, AxisSetting.GREEN, AxisSetting.BLUE)
-
-    def set_horizontal(self, axis_setting):
-        self.horizontal_setting = axis_setting
-        group = self.HSV if self.horizontal_setting in self.HSV else self.RGB
-        if self.vertical_setting not in group:
-            self.vertical_setting = group[group[0] == self.horizontal_setting]
-
-    def set_vertical(self, axis_setting):
-        self.vertical_setting = axis_setting
-        group = self.HSV if self.vertical_setting in self.HSV else self.RGB
-        if self.horizontal_setting not in group:
-            self.horizontal_setting = group[group[0] == self.vertical_setting]
-
-    def set_base_value(self, value):
-        if value == self.base:
-            return
-        self.base = value
-        self.surface = self.get_surface()
index 141c56a3f3b61b7c0afb351205fde3281384c0a0..46b9075f4df4498b2bc729cc897b912f3fe5d1b9 100644 (file)
@@ -1,5 +1,4 @@
 from enum import Enum, auto
-from time import time
 
 import pygame
 
@@ -13,149 +12,79 @@ class StrokeMethod(Enum):
     PYGAME = auto()
 
 
-class InputMethod(Enum):
-    SDL_ANY = auto()
-    SDL_MOUSE = auto()
-    SDL_FINGERS = auto()
-    SDL_KEYBOARD = auto()
-
-    @staticmethod
-    def labels():
-        return (
-            "Mouse or Fingers (SDL)",
-            "Mouse/Stylus (SDL)",
-            "Fingers (SDL)",
-            "Keyboard (SDL)",
-        )
-
-
 class DrawImage(Child):
     FINGER_TIMEOUT = 30
 
-    def __init__(self, parent, background_color, foreground_color):
+    def __init__(self, parent, color):
         super().__init__(parent)
-        self.background_color = background_color
-        self.foreground_color = foreground_color
-        self.drawing_color = "foreground_color"
+        self.color = color
         self.line_width = 1
 
         self.surface = pygame.Surface(self.surf.get_size(), 0, 24)
         self.clear()
+        self.image_dirty = False
         self.stroke_method = next(iter(StrokeMethod))
-        self.input_method = next(iter(InputMethod))
-        self.mouse_pos = None
-        self.fingers = {}
+        self.prev_pos = None
 
     def clear(self):
-        self.surface.fill(self.background_color)
-
-    def handle_mousebuttondown(self, ev):
-        if self.input_method in (InputMethod.SDL_ANY, InputMethod.SDL_MOUSE):
-            if ev.button == 1:
-                self.drawing_color = "foreground_color"
-                self.mouse_pos = ev.pos
-            elif ev.button == 3:
-                self.drawing_color = "background_color"
-                self.mouse_pos = ev.pos
-
-    def mouse_stroke(self, buttons, pos):
-        if (
-            self.input_method in (InputMethod.SDL_ANY, InputMethod.SDL_MOUSE)
-            and self.mouse_pos is not None
-            and (buttons[0] or buttons[2])
-        ):
-            self.STROKE_MAPPING[self.stroke_method](self, self.mouse_pos, pos)
-            self.mouse_pos = pos
-            self.dirty = True
-
-    def handle_mousemotion(self, ev):
-        self.mouse_stroke(ev.buttons, ev.pos)
-
-    def handle_mousebuttonup(self, ev):
-        self.mouse_stroke(tuple(ev.button == i for i in range(1, 4)), ev.pos)
-
-    def get_finger_pos(self, key):
-        item = self.fingers.get(key)
-        if item is None:
-            return None
-        prev_pos, t = item
-        if t < time():
-            self.fingers.pop(key)
-            return None
-        return prev_pos
-
-    def handle_fingerdown(self, ev):
-        if self.input_method not in (InputMethod.SDL_ANY, InputMethod.SDL_FINGERS):
-            self.fingers.clear()
-            return
-        key = (ev.touch_id, ev.finger_id)
-        size = self.surface.get_size()
-        pos = (int(size[0] * ev.x), int(size[1] * ev.y))
-        prev_pos = self.get_finger_pos(key)
-        if prev_pos is not None:
-            self.STROKE_MAPPING[self.stroke_method](self, prev_pos, pos)
-            self.dirty = True
-        if ev.type == pygame.FINGERUP:
-            self.fingers.pop(key, None)
-        else:
-            self.fingers[key] = pos, time() + self.FINGER_TIMEOUT
-
-    handle_fingermotion = handle_fingerdown
-    handle_fingerup = handle_fingerdown
+        self.surface.fill("black")
+        self.image_dirty = False
+        self.dirty = True
 
-    def update(self):
-        if not self.root.focused:
-            if self.mouse_pos is not None:
-                self.mouse_pos = None
-                self.dirty = True
-            if len(self.fingers) > 0:
-                self.fingers.clear()
-                self.dirty = True
-            return
-        buttons = pygame.mouse.get_pressed()
-        if buttons[0] or buttons[2]:
-            return
-        pos = pygame.mouse.get_pos()
-        if pos != self.mouse_pos:
-            self.mouse_pos = pos
-            self.dirty = True
+    def dot(self, pos):
+        pygame.draw.circle(self.surf, self.color, pos, self.line_width // 2)
 
     def draw(self):
         self.surf.blit(self.surface, (0, 0))
-        if self.mouse_pos is not None:
-            pygame.draw.circle(
-                self.surf,
-                getattr(self, self.drawing_color),
-                self.mouse_pos,
-                self.line_width // 2,
-            )
-            pygame.draw.circle(
-                self.surf,
-                "black",
-                self.mouse_pos,
-                self.line_width // 2,
-                2,
-            )
+        self.dot(pygame.mouse.get_pos())
 
-    def pygame_stroke(self, a, b):
-        pygame.draw.line(
-            self.surface, getattr(self, self.drawing_color), a, b, self.line_width
-        )
+    def handle_mousebuttondown(self, ev):
+        if ev.button == 1:
+            self.prev_pos = ev.pos
+            self.dirty = True
 
-    def round_stroke(self, a, b):
-        StrokeRoundLine(
-            a, b, self.line_width
-        ).draw(self.surface, getattr(self, self.drawing_color))
+    def handle_mousemotion(self, ev):
+        if self.prev_pos is not None:
+            if self.stroke_method == StrokeMethod.ROUND:
+                if ev.pos == self.prev_pos:
+                    self.dot(ev.pos)
+                else:
+                    StrokeRoundLine(
+                        self.prev_pos, ev.pos, self.line_width
+                    ).draw(self.surface, self.color)
+            elif self.stroke_method == StrokeMethod.SQUARE:
+                if ev.pos == self.prev_pos:
+                    pygame.draw.rect(
+                        self.surface,
+                        self.color,
+                        pygame.Rect(
+                            (
+                                ev.pos[0] - self.line_width // 2,
+                                ev.pos[1] - self.line_width // 2,
+                            ),
+                            (self.line_width, self.line_width),
+                        )
+                    )
+                else:
+                    StrokeSquareLine(
+                        self.prev_pos, ev.pos, self.line_width
+                    ).draw(self.surface, self.color)
+            elif self.stroke_method == StrokeMethod.PYGAME:
+                if ev.pos == self.prev_pos:
+                    self.dot(ev.pos)
+                else:
+                    pygame.draw.line(
+                        self.surface, self.color, self.prev_pos, ev.pos, self.line_width
+                    )
+            self.prev_pos = ev.pos
+            self.image_dirty = True
+        self.dirty = True
 
-    def square_stroke(self, a, b):
-        if a == b:
-            return
-        StrokeSquareLine(
-            a, b, self.line_width
-        ).draw(self.surface, getattr(self, self.drawing_color))
+    def handle_mousebuttonup(self, ev):
+        if ev.button == 1 and self.prev_pos is not None:
+            self.handle_mousemotion(ev)
+            self.prev_pos = None
 
-    STROKE_MAPPING = {
-        StrokeMethod.PYGAME: pygame_stroke,
-        StrokeMethod.ROUND: round_stroke,
-        StrokeMethod.SQUARE: square_stroke
-    }
+    def set_image(self, surf):
+        self.surface.blit(surf, (0, 0))
+        self.dirty = True
diff --git a/bookpaint/menu.py b/bookpaint/menu.py
new file mode 100644 (file)
index 0000000..3b836b6
--- /dev/null
@@ -0,0 +1,21 @@
+import pygame
+
+from ui import (
+    Button,
+    Modal,
+)
+
+
+class Menu(Modal):
+    def __init__(self, parent):
+        super().__init__(parent)
+        size = self.root.surf.get_size()
+        width_third = size[0] // 3
+        Button(
+            self,
+            pygame.Rect((width_third, size[1] * 2 // 3), (width_third, 128)),
+            "Quit",
+            self.root.handle_quit,
+        )
+
+    KEY_METHODS = {frozenset(set()): {pygame.K_ESCAPE: Modal.deactivate}}
diff --git a/bookpaint/utils.py b/bookpaint/utils.py
deleted file mode 100644 (file)
index bd94bc7..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-from colorsys import hsv_to_rgb, rgb_to_hsv
-from math import sqrt
-
-import pygame
-
-
-def distance(a, b):
-    return sqrt((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2)
-
-
-def hsv_to_color(hsv):
-    return pygame.Color(
-        *(int(c * 255) for c in hsv_to_rgb(hsv[0] / 256, hsv[1] / 255, hsv[2] / 255))
-    )
-
-
-def color_to_hsv(color):
-    hsv = rgb_to_hsv(color.r / 255, color.g / 255, color.b / 255)
-    hue = hsv[0] * 256
-    if hue >= 256:
-        hue -= 256
-    return (hue, hsv[1] * 255, hsv[2] * 255)
-
-
-def color_to_hex(color):
-    assert isinstance(color, pygame.Color)
-    return str(, 16)
diff --git a/bookpaint/value_slider.py b/bookpaint/value_slider.py
deleted file mode 100644 (file)
index c19d71e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-from ui import Slider
-
-
-class ValueSlider(Slider):
-    pass
index dfea62513b2b7a287f41dc5345e78fa85ab6c42e..a1611f0334c6217574e3b13d275a4239c481b5b0 100755 (executable)
--- a/memory.py
+++ b/memory.py
@@ -6,7 +6,7 @@ from secrets import choice
 
 import pygame
 
-from ui import Button, CenterLabel, Child, Modal, Root, TextInput
+from ui import Button, Child, Label, Modal, Root, TextInput
 
 
 class QuittableModal(Modal):
@@ -22,10 +22,11 @@ class NameMenu(QuittableModal):
         # add exit button
         super().__init__(parent)
         self.lw = parent.surf.get_width() / 3
-        CenterLabel(
+        Label(
             self,
             pygame.Rect((int(self.lw / 2), 144), (self.lw, 128)),
             "Enter player names",
+            Label.HAlign.CENTER,
         )
         self.inputs = [
             TextInput(
@@ -106,11 +107,17 @@ class EndGameMenu(QuittableModal):
         self.rect = rect
         th = rect.height // 2
         self.labels = (
-            CenterLabel(self, pygame.Rect(rect.topleft, (rect.width, th)), ""),
-            CenterLabel(
+            Label(
+                self,
+                pygame.Rect(rect.topleft, (rect.width, th)),
+                "",
+                Label.HAlign.CENTER,
+            ),
+            Label(
                 self,
                 pygame.Rect((rect.left, rect.top + th), (rect.width, th)),
                 "NEW GAME",
+                Label.HAlign.CENTER,
             ),
         )
 
index 13086f2c850b7ffd4a7493311907df9623c3b268..824209fbd172729242912eaf01e8b73fb2c83801 100644 (file)
@@ -6,7 +6,7 @@ from .event_method_dispatcher import EventMethodDispatcher
 from .fps_widget import FPSWidget
 from .icon import Icon
 from .icon_button import IconButton
-from .label import CenterLabel, Label, LeftLabel, RightLabel
+from .label import Label
 from .message_box import MessageBox
 from .modal import Modal
 from .parent import Parent
@@ -21,7 +21,6 @@ from .text_input import TextInput
 
 __all__ = [
     "Button",
-    "CenterLabel",
     "Child",
     "ColorButton",
     "DropDown",
@@ -31,13 +30,11 @@ __all__ = [
     "Icon",
     "IconButton",
     "Label",
-    "LeftLabel",
     "MessageBox",
     "Modal",
     "Parent",
     "Rect",
     "RepeatButton",
-    "RightLabel",
     "Root",
     "Scroll",
     "Slider",
index 61ce3d65994dbdc732048f79133b916c61f45dd0..f78fe0f8e2cf8cfa2caa95463d8485cb2ed1fc50 100644 (file)
@@ -23,7 +23,7 @@ class DropDownMenu(Modal):
             rect.bottomleft, (rect.width, rect.height * len(entries))
         )
 
-    KEY_METHODS = {frozenset(set()): {pygame.K_ESCAPE: Modal.deactivate}}
+    KEY_METHODS = {frozenset(): {pygame.K_ESCAPE: Modal.deactivate}}
 
     def choose(self, i=None):
         self.deactivate()
index 343ac9afa32e0bddd4ece70739f49a5f4b1e220f..e76c5deb57e1450d41b0d6423e9356b23dd86b7d 100644 (file)
@@ -1,35 +1,40 @@
+from enum import Enum
+
 from .child import Child
 
 
-class CenterLabel(Child):
-    def __init__(self, parent, rect, value):
+class Label(Child):
+    class HAlign(Enum):
+        LEFT = "left"
+        CENTER = "center"
+        RIGHT = "right"
+
+    def __init__(self, parent, rect, value, align=HAlign.LEFT):
         super().__init__(parent)
         self.rect = rect
         self.value = value
+        self.get_blit_pos = {
+            self.HAlign.LEFT: self.get_blit_pos_left,
+            self.HAlign.CENTER: self.get_blit_pos_center,
+            self.HAlign.RIGHT: self.get_blit_pos_right,
+        }[align]
 
-    def get_blit_pos(self):
-        fs_size = self.font.size(self.value)
-        return (
-            self.rect.centerx - fs_size[0] // 2, self.rect.centery - fs_size[1] // 2
-        )
-
-    def draw(self):
-        fs = self.font.render(self.value, True, "gray")
-        self.surf.blit(fs, self.get_blit_pos())
-
-
-class LeftLabel(CenterLabel):
-    def get_blit_pos(self):
+    def get_blit_pos_left(self):
         return (
             self.rect.left + 16,
             self.rect.centery - self.font.size(self.value)[1] // 2,
         )
 
+    def get_blit_pos_center(self):
+        fs_size = self.font.size(self.value)
+        return (
+            self.rect.centerx - fs_size[0] // 2, self.rect.centery - fs_size[1] // 2
+        )
 
-class RightLabel(CenterLabel):
-    def get_blit_pos(self):
+    def get_blit_pos_right(self):
         fs_size = self.font.size(self.value)
         return (self.rect.right - 16 - fs_size[0], self.rect.centery - fs_size[1] // 2)
 
-
-Label = LeftLabel
+    def draw(self):
+        fs = self.font.render(self.value, True, "gray")
+        self.surf.blit(fs, self.get_blit_pos())
index 51fa4a3a986f4f2a734da4a3762f0b320e76a061..7b5d5c8b97b9358cb05f85ba056265b647397f19 100644 (file)
@@ -273,7 +273,7 @@ class TextInput(Focusable, Child):
         super().deactivate()
 
     KEY_METHODS = {
-        frozenset(set()): {
+        frozenset(): {
             pygame.K_LEFT: key_left,
             pygame.K_RIGHT: key_right,
             pygame.K_HOME: key_home,
index 7a4c52c1cbdc3017bbb925339af7141024e7642f..42ba26ff974cc1cf71bbde0b8850cea818923782 100755 (executable)
@@ -1,14 +1,11 @@
 #!/usr/bin/env python3
 
-import sys
 from pathlib import Path
 from secrets import choice, randbelow
 from time import time
 
 import pygame
 
-sys.path.append(str(Path(__file__).parents[1]))
-
 from ui import Button, Child, FloatSpinner, Label, Root, Spinner
 
 
@@ -108,7 +105,9 @@ class VSMemory(Root):
             pygame.font.Font(None, size=96),
         )
         self.teams = tuple(
-            Team(file) for file in (Path(__file__).parent / "logos").iterdir()
+            Team(file)
+            for file in (Path(__file__).parent / "logos").iterdir()
+            if file.stem != "National League"
         )
         len_teams = len(self.teams)
         # fits one column of memory destinations (todo)