]> git.mar77i.info Git - zenbook_gui/commitdiff
basic color chooser, basic drawing functionality
authormar77i <mar77i@protonmail.ch>
Tue, 18 Feb 2025 00:59:54 +0000 (01:59 +0100)
committermar77i <mar77i@protonmail.ch>
Tue, 18 Feb 2025 00:59:54 +0000 (01:59 +0100)
bookpaint/bookpaint.py
bookpaint/bookpaint_menu.py
bookpaint/color_chooser.py [new file with mode: 0644]
bookpaint/draw_image.py

index 6af8a4cd58377930307303c08f87ae05e48f0e48..1468f9ec8db23a19f4b19c930636af8375a0a265 100644 (file)
@@ -4,7 +4,7 @@ from ui import Root
 from zenbook_conf.xrandr import XrandrConf
 
 from .bookpaint_menu import BookPaintMenu
-from .draw_image import DrawImage
+from .draw_image import DrawImage, StrokeMethod
 
 
 class BookPaint(Root):
@@ -22,9 +22,12 @@ class BookPaint(Root):
         )
         self.background_color = self.BACKGROUND_COLOR
         self.foreground_color = "white"
+        self.drawing_color = "foreground_color"
+        self.line_width = 1
+        self.stroke_method = StrokeMethod.PYGAME
         self.menu = BookPaintMenu(self)
         self.draw_image = DrawImage(self)
-        self.draw_image.clear(self.background_color)
+        self.clear()
         self.xrandr_conf = XrandrConf(True)
 
     def key_escape(self):
@@ -54,3 +57,6 @@ class BookPaint(Root):
             (0, 0), self.display_flags, display=self.current_display
         )
         self.dirty = True
+
+    def clear(self):
+        self.draw_image.clear(self.background_color)
index dd489a7110e7dec278ad135301626991851408da..80a2dc8f6daa276e259c0354ccc9a0e6a5300b21 100644 (file)
@@ -1,3 +1,4 @@
+from functools import partial
 from pathlib import Path
 
 import pygame
@@ -16,8 +17,17 @@ from ui import (
     TextInput,
 )
 
+from .color_chooser import ColorChooser
+from .draw_image import StrokeMethod
+
 
 class BookPaintMenu(Modal):
+    def get_stroke_methods(self):
+        return (
+            self.root.stroke_method.name.title(),
+            [stroke_method.name.title() for stroke_method in StrokeMethod],
+        )
+
     def __init__(self, parent):
         super().__init__(parent)
         Button(
@@ -81,37 +91,43 @@ class BookPaintMenu(Modal):
             str(Path("book/").absolute()),
             unfocused_align=TextInput.RIGHT,
         )
-        ColorButton(
+        color_chooser = ColorChooser(self)
+        self.background_color = ColorButton(
             self,
             grid_layout.get_rect((1, 1)).inflate((pad, pad)),
             self.root.background_color,
-            lambda: None,
+            partial(color_chooser.activate_for, "background_color"),
         )
         Button(
             self,
             grid_layout.get_rect((2, 1)).inflate((pad, pad)),
             "Fill Page",
-            lambda: None,
+            self.root.clear,
         )
-        ColorButton(
+        self.foreground_color = ColorButton(
             self,
             grid_layout.get_rect((1, 2)).inflate((pad, pad)),
             self.root.foreground_color,
-            lambda: None,
+            partial(color_chooser.activate_for, "foreground_color"),
         )
-        Slider(
+        self.width_slider = Slider(
             self,
             grid_layout.get_rect((1, 3)).inflate((pad, pad)),
             Slider.HORIZONTAL,
-            handle_size=96,
-            callback=lambda _: None,
-        ),
-        DropDown(
+            self.root.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)),
-            "<choose>",
-            ["a", "b", "c"],
-            lambda _: None,
+            *self.get_stroke_methods(),
+            self.set_stroke_method,
         )
         DropDown(
             self,
@@ -144,36 +160,54 @@ class BookPaintMenu(Modal):
             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,
-        ),
-
-    def key_escape(self):
-        self.deactivate()
+        )
 
-    KEY_METHODS = {frozenset(set()): {pygame.K_ESCAPE: key_escape}}
+    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.root, 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.root.line_width != width:
+            self.root.line_width = width
+            self.width_label.value = str(width)
+            self.dirty = True
+
+    def set_stroke_method(self, i):
+        self.root.stroke_method = StrokeMethod(i + 1)
+        self.stroke_method.value = self.root.stroke_method.name.title()
diff --git a/bookpaint/color_chooser.py b/bookpaint/color_chooser.py
new file mode 100644 (file)
index 0000000..0887fc5
--- /dev/null
@@ -0,0 +1,86 @@
+from colorsys import hsv_to_rgb
+from functools import partial
+
+import pygame
+
+from ui import Modal, Rect
+
+from ui import ColorButton
+
+
+def hsv_to_color(hsv):
+    return pygame.Color(*(int(c * 255) for c in hsv_to_rgb(*(c / 255 for c in hsv))))
+
+
+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(8):
+                        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] // (2 - bool(x % 8))
+                        if x < 32:
+                            continue
+                        x, pos[0] = 0, lefttop[0]
+                        y += 1
+                        pos[1] += cell_size[1] // (2 - bool(y % 8))
+        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)
+        # self.rect = rect
+        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 = None
+        for r, color in self.get_color_grid((rect.left + 48, rect.top + 192), (64, 64)):
+            ColorButton(self, r, color, partial(self.set_color, color))
+
+    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)
index 1179854dc3646a61a1ec5ced7afbfb76699b0cb9..af7bb3f59796c52f0f5c32e0c4c770bfb2dff44e 100644 (file)
@@ -1,6 +1,15 @@
+from enum import Enum, auto
+
 import pygame
 
 from ui import Child
+from vectors import StrokeRoundLine, StrokeSquareLine
+
+
+class StrokeMethod(Enum):
+    PYGAME = auto()
+    ROUND = auto()
+    SQUARE = auto()
 
 
 class DrawImage(Child):
@@ -8,9 +17,55 @@ class DrawImage(Child):
         super().__init__(parent)
         self.surface = pygame.Surface(self.surf.get_size(), 0, 24)
         self.clear(self.root.BACKGROUND_COLOR)
+        self.prev_pos = None
 
     def clear(self, background_color):
         self.surface.fill(background_color)
 
     def draw(self):
         self.surf.blit(self.surface, (0, 0))
+
+    def pygame_stroke(self, a, b):
+        root = self.root
+        pygame.draw.line(
+            self.surface, getattr(root, root.drawing_color), a, b, root.line_width
+        )
+
+    def round_stroke(self, a, b):
+        root = self.root
+        StrokeRoundLine(
+            a, b, root.line_width
+        ).draw(self.surface, getattr(root, root.drawing_color))
+
+    def square_stroke(self, a, b):
+        root = self.root
+        StrokeSquareLine(
+            a, b, root.line_width
+        ).draw(self.surface, getattr(root, root.drawing_color))
+
+    STROKE_MAPPING = {
+        StrokeMethod.PYGAME: pygame_stroke,
+        StrokeMethod.ROUND: round_stroke,
+        StrokeMethod.SQUARE: square_stroke
+    }
+
+    def handle_mousebuttondown(self, ev):
+        if ev.button == 1:
+            self.root.drawing_color = "foreground_color"
+        elif ev.button == 3:
+            self.root.drawing_color = "background_color"
+        else:
+            return
+        self.prev_pos = ev.pos
+
+    def handle_mousemotion(self, ev):
+        if self.prev_pos is not None:
+            self.STROKE_MAPPING[self.root.stroke_method](self, self.prev_pos, ev.pos)
+            self.prev_pos = ev.pos
+            self.dirty = True
+
+    def handle_mousebuttonup(self, ev):
+        if self.prev_pos is not None:
+            self.STROKE_MAPPING[self.root.stroke_method](self, self.prev_pos, ev.pos)
+            self.prev_pos = None
+            self.dirty = True