From: mar77i Date: Thu, 20 Feb 2025 13:56:00 +0000 (+0100) Subject: consolidate bookpaint_menu<->draw_image interactions X-Git-Url: https://git.mar77i.info/?a=commitdiff_plain;h=21047a1df3e51d290cb30ecc59496e4d9c63bec1;p=zenbook_gui consolidate bookpaint_menu<->draw_image interactions --- diff --git a/bookpaint/bookpaint.py b/bookpaint/bookpaint.py index 1468f9e..20b4f3b 100644 --- a/bookpaint/bookpaint.py +++ b/bookpaint/bookpaint.py @@ -4,7 +4,7 @@ from ui import Root from zenbook_conf.xrandr import XrandrConf from .bookpaint_menu import BookPaintMenu -from .draw_image import DrawImage, StrokeMethod +from .draw_image import DrawImage class BookPaint(Root): @@ -20,14 +20,8 @@ class BookPaint(Root): ), pygame.font.Font(None, size=96), ) - 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.clear() + self.draw_image = DrawImage(self, self.BACKGROUND_COLOR, "white") + self.menu = BookPaintMenu(self, self.draw_image) self.xrandr_conf = XrandrConf(True) def key_escape(self): @@ -57,6 +51,3 @@ 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) diff --git a/bookpaint/bookpaint_menu.py b/bookpaint/bookpaint_menu.py index 80a2dc8..8dd9154 100644 --- a/bookpaint/bookpaint_menu.py +++ b/bookpaint/bookpaint_menu.py @@ -18,18 +18,26 @@ from ui import ( ) from .color_chooser import ColorChooser -from .draw_image import StrokeMethod +from .draw_image import InputMethod, StrokeMethod class BookPaintMenu(Modal): def get_stroke_methods(self): return ( - self.root.stroke_method.name.title(), + self.draw_image.stroke_method.name.title(), [stroke_method.name.title() for stroke_method in StrokeMethod], ) - def __init__(self, parent): + 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)), @@ -95,26 +103,26 @@ class BookPaintMenu(Modal): self.background_color = ColorButton( self, grid_layout.get_rect((1, 1)).inflate((pad, pad)), - self.root.background_color, + 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.root.clear, + self.draw_image.clear, ) self.foreground_color = ColorButton( self, grid_layout.get_rect((1, 2)).inflate((pad, pad)), - self.root.foreground_color, + self.draw_image.foreground_color, partial(color_chooser.activate_for, "foreground_color"), ) self.width_slider = Slider( self, grid_layout.get_rect((1, 3)).inflate((pad, pad)), Slider.HORIZONTAL, - self.root.line_width - 1, + self.draw_image.line_width - 1, 96, self.set_width, ) @@ -129,12 +137,11 @@ class BookPaintMenu(Modal): *self.get_stroke_methods(), self.set_stroke_method, ) - DropDown( + self.input_method = DropDown( self, - grid_layout.get_rect((1, 5)).inflate((pad, pad)), - "", - ["a", "b", "c"], - lambda _: None, + 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), @@ -192,7 +199,7 @@ class BookPaintMenu(Modal): print("book path", value) def set_color(self, attr, color): - setattr(self.root, attr, color) + setattr(self.draw_image, attr, color) getattr(self, attr).color = color self.dirty = True @@ -203,11 +210,15 @@ class BookPaintMenu(Modal): 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 + 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.root.stroke_method = StrokeMethod(i + 1) - self.stroke_method.value = self.root.stroke_method.name.title() + 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/draw_image.py b/bookpaint/draw_image.py index af7bb3f..141c56a 100644 --- a/bookpaint/draw_image.py +++ b/bookpaint/draw_image.py @@ -1,4 +1,5 @@ from enum import Enum, auto +from time import time import pygame @@ -7,65 +8,154 @@ from vectors import StrokeRoundLine, StrokeSquareLine class StrokeMethod(Enum): - PYGAME = auto() ROUND = auto() SQUARE = auto() + 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): - def __init__(self, parent): + FINGER_TIMEOUT = 30 + + def __init__(self, parent, background_color, foreground_color): super().__init__(parent) + self.background_color = background_color + self.foreground_color = foreground_color + self.drawing_color = "foreground_color" + self.line_width = 1 + self.surface = pygame.Surface(self.surf.get_size(), 0, 24) - self.clear(self.root.BACKGROUND_COLOR) - self.prev_pos = None + self.clear() + self.stroke_method = next(iter(StrokeMethod)) + self.input_method = next(iter(InputMethod)) + self.mouse_pos = None + self.fingers = {} - def clear(self, background_color): - self.surface.fill(background_color) + 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 + + 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 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, + ) def pygame_stroke(self, a, b): - root = self.root pygame.draw.line( - self.surface, getattr(root, root.drawing_color), a, b, root.line_width + self.surface, getattr(self, self.drawing_color), a, b, self.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)) + a, b, self.line_width + ).draw(self.surface, getattr(self, self.drawing_color)) def square_stroke(self, a, b): - root = self.root + if a == b: + return StrokeSquareLine( - a, b, root.line_width - ).draw(self.surface, getattr(root, root.drawing_color)) + a, b, self.line_width + ).draw(self.surface, getattr(self, self.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 diff --git a/ui/parent.py b/ui/parent.py index 47c9a77..210cc3f 100644 --- a/ui/parent.py +++ b/ui/parent.py @@ -4,7 +4,6 @@ from .event_method_dispatcher import EventMethodDispatcher class Parent(EventMethodDispatcher): running: bool stop_event: bool - parent: "root.Root" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/vectors/polygon.py b/vectors/polygon.py index 0e6a785..b37ef9e 100644 --- a/vectors/polygon.py +++ b/vectors/polygon.py @@ -18,4 +18,10 @@ class Polygon(Shape): ) def draw(self, surf, color): - pygame.draw.polygon(surf, color, self.points) + num_points = len(self.points) + if num_points == 1: + surf.set_at(self.points[0], color) + elif num_points == 2: + pygame.draw.aaline(surf, color, *self.points) + elif num_points > 2: + pygame.draw.polygon(surf, color, self.points)