From d11b1778188cbc25a35c93b7e23c05b5e4f1a0e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Wegener?= Date: Tue, 5 Nov 2024 20:03:10 +0100 Subject: [PATCH] Click and release behaviour (#254) A button needs to be pressed first, and then on release, it triggers on_click --- drinks_touch/config.py | 4 +- drinks_touch/elements/base_elm.py | 49 +++++++++++++++++++++++-- drinks_touch/elements/button.py | 19 ++-------- drinks_touch/elements/elm_list.py | 11 +++++- drinks_touch/elements/hbox.py | 22 ++++------- drinks_touch/elements/image.py | 12 +++--- drinks_touch/elements/label.py | 47 ++++++++++++++---------- drinks_touch/elements/progress.py | 31 ++++++++++++---- drinks_touch/elements/vbox.py | 24 +++++++----- drinks_touch/overlays.py | 19 +++++++--- drinks_touch/screens/enter_pin.py | 5 ++- drinks_touch/screens/git/log_screen.py | 4 +- drinks_touch/screens/main.py | 2 +- drinks_touch/screens/profile.py | 24 ++++++------ drinks_touch/screens/recharge_screen.py | 12 +++--- drinks_touch/screens/screen.py | 22 ----------- drinks_touch/screens/wait_scan.py | 13 +++---- poetry.lock | 44 +--------------------- pyproject.toml | 2 +- 19 files changed, 185 insertions(+), 181 deletions(-) diff --git a/drinks_touch/config.py b/drinks_touch/config.py index adcf49e7..da9a9d15 100644 --- a/drinks_touch/config.py +++ b/drinks_touch/config.py @@ -22,8 +22,8 @@ ] COLORS = { - "infragelb": (246, 198, 0), - "disabled": (50, 50, 50), + "infragelb": (246, 198, 0, 255), + "disabled": (50, 50, 50, 255), } if bn := os.environ.get("BUILD_NUMBER"): diff --git a/drinks_touch/elements/base_elm.py b/drinks_touch/elements/base_elm.py index 31ab2b40..2c416953 100644 --- a/drinks_touch/elements/base_elm.py +++ b/drinks_touch/elements/base_elm.py @@ -5,6 +5,7 @@ class BaseElm(object): def __init__( self, + children: list["BaseElm"] | None = None, pos=None, height=None, width=None, @@ -13,8 +14,6 @@ def __init__( padding: ( int | tuple[int, int] | tuple[int, int, int] | tuple[int, int, int, int] ) = 0, - *args, - **kwargs ): if pos is None: pos = (0, 0) @@ -24,6 +23,10 @@ def __init__( self.is_visible = True self.align_right = align_right self.align_bottom = align_bottom + self.focus = False + if children is None: + children = [] + self.children = children if not isinstance(padding, tuple): self.padding_top = padding self.padding_right = padding @@ -74,14 +77,52 @@ def screen_pos(self): def box(self): return self.screen_pos + (self.width, self.height) - def events(self, events): - pass + def events(self, events, pos=None): + for event in events: + consumed = "consumed" in event.dict and event.consumed + if pos is None and hasattr(event, "pos"): + pos = event.pos + if pos is None: + continue + collides = self.collides_with(pos) + if collides: + transformed_pos = ( + pos[0] - self.screen_pos[0], + pos[1] - self.screen_pos[1], + ) + else: + transformed_pos = None + + for child in self.children: + child.events(events, transformed_pos) + + if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: + if collides: + self.focus = True + else: + self.focus = False + + elif event.type == pygame.MOUSEBUTTONUP and event.button == 1: + if not hasattr(self, "on_click"): + continue + + if not consumed and self.focus and collides: + self.on_click(*transformed_pos) + event.consumed = True + self.focus = False @property def visible(self): # TODO get rid of is_visible return self.is_visible + def collides_with(self, pos: tuple[int, int]) -> bool: + return ( + self.box is not None + and self.visible + and pygame.Rect(self.box).collidepoint(pos[0], pos[1]) + ) + def render(self, *args, **kwargs) -> Surface: surface = pygame.font.SysFont("monospace", 25).render( "Return surface in render()", 1, (255, 0, 255) diff --git a/drinks_touch/elements/button.py b/drinks_touch/elements/button.py index eec19e3f..1ecead3c 100644 --- a/drinks_touch/elements/button.py +++ b/drinks_touch/elements/button.py @@ -10,6 +10,7 @@ class Button(BaseElm): def __init__( self, + children: list["BaseElm"] | None = None, font=FONTS["monospace"], size=30, text=None, @@ -27,7 +28,7 @@ def __init__( ): from . import Label - super().__init__(pos, size, size, *args, padding=padding, **kwargs) + super().__init__(children, pos, size, size, *args, padding=padding, **kwargs) self.size = size self.color = color @@ -44,14 +45,6 @@ def __init__( ) self.inner = inner - self.clicking = False - - def pre_click(self): - self.clicking = True - - def post_click(self): - self.clicking = False - def render(self, *args, **kwargs) -> pygame.Surface: inner = self.inner.render(*args, **kwargs) @@ -64,7 +57,7 @@ def render(self, *args, **kwargs) -> pygame.Surface: self.height = size[1] surface = pygame.Surface(size, pygame.SRCALPHA) - if self.clicking: + if self.focus: surface.fill(tuple(c * 0.7 for c in self.color), (0, 0, *size)) if inner is not None: @@ -75,8 +68,4 @@ def render(self, *args, **kwargs) -> pygame.Surface: def on_click(self, x, y): if self.on_click_handler is None: raise NotImplementedError("No on_click handler defined") - self.pre_click() - try: - self.on_click_handler() - finally: - self.post_click() + self.on_click_handler() diff --git a/drinks_touch/elements/elm_list.py b/drinks_touch/elements/elm_list.py index 5af4fc21..12957bfc 100644 --- a/drinks_touch/elements/elm_list.py +++ b/drinks_touch/elements/elm_list.py @@ -4,8 +4,15 @@ class ElmList(BaseElm): - def __init__(self, height, width, pos=(0, 0), **kwargs): - super().__init__(pos, height, width) + def __init__( + self, + children: list["BaseElm"] | None = None, + height=None, + width=None, + pos=(0, 0), + **kwargs + ): + super().__init__(children, pos, height, width) self.pos = pos self.elm_margin = kwargs.get("elm_margin", 5) self.max_elm_count = kwargs.get("max_elm_count", 10) diff --git a/drinks_touch/elements/hbox.py b/drinks_touch/elements/hbox.py index ec636470..f970868d 100644 --- a/drinks_touch/elements/hbox.py +++ b/drinks_touch/elements/hbox.py @@ -7,21 +7,20 @@ class HBox(BaseElm): def __init__( self, - elements: list[BaseElm], + children: list["BaseElm"] | None = None, gap=5, pos=(0, 0), *args, **kwargs, ): - super().__init__(pos, 0, 0, *args, **kwargs) + super().__init__(children, pos, 0, 0, *args, **kwargs) self.pos = pos - self.elements = elements self.gap = gap def render(self, *args, **kwargs) -> pygame.Surface: surface = pygame.Surface((self.width, self.height), pygame.SRCALPHA) x = self.padding_left - for element in self.elements: + for element in self.children: element.pos = (x, self.padding_top) element_surface = element.render(*args, **kwargs) surface.blit(element_surface, element.pos) @@ -31,8 +30,8 @@ def render(self, *args, **kwargs) -> pygame.Surface: @property def width(self): return ( - sum([element.width for element in self.elements]) - + (len(self.elements) - 1) * self.gap + sum([element.width for element in self.children]) + + (len(self.children) - 1) * self.gap + self.padding_left + self.padding_right ) @@ -40,22 +39,15 @@ def width(self): @property def height(self): return ( - max([element.height for element in self.elements]) + max([element.height for element in self.children]) + self.padding_top + self.padding_bottom ) - def on_click(self, x, y): - for obj in self.elements: - if pygame.Rect(obj.box).collidepoint(x, y): - if hasattr(obj, "on_click"): - obj.on_click(x - obj.pos[0], y - obj.pos[1]) - break - def render_debug(self) -> pygame.Surface: surface = pygame.Surface((self.width, self.height), pygame.SRCALPHA) surface.fill((0, 0, 255, 100)) - for element in self.elements: + for element in self.children: element_surface = element.render_debug() surface.blit(element_surface, element.pos) pygame.draw.rect(surface, (255, 0, 0), (0, 0, self.width, self.height), 1) diff --git a/drinks_touch/elements/image.py b/drinks_touch/elements/image.py index 8fccaaa8..1c7d2bd7 100644 --- a/drinks_touch/elements/image.py +++ b/drinks_touch/elements/image.py @@ -7,13 +7,15 @@ class Image(BaseElm): - def __init__(self, *args, **kwargs): - self.src = kwargs.get("src", "drinks_touch/resources/images/test.jpg") - self.size = kwargs.get("size", None) + def __init__( + self, src="drinks_touch/resources/images/test.jpg", size=None, *args, **kwargs + ): + self.src = src + self.size = size self.img = pygame.image.load(self.src).convert_alpha() - if self.size: - self.img = pygame.transform.smoothscale(self.img, self.size) + if size: + self.img = pygame.transform.smoothscale(self.img, size) super(Image, self).__init__(*args, **kwargs) @property diff --git a/drinks_touch/elements/label.py b/drinks_touch/elements/label.py index 3f22ef7c..0fa81d2f 100644 --- a/drinks_touch/elements/label.py +++ b/drinks_touch/elements/label.py @@ -1,4 +1,4 @@ -from config import COLORS +import config from .base_elm import BaseElm import contextlib @@ -12,25 +12,34 @@ class Label(BaseElm): _font_cache = {} - def __init__(self, *args, **kwargs): - self.font_face = kwargs.get("font", "sans serif") - self.size = kwargs.get("size", 50) - self.max_width = kwargs.get("max_width", None) - # if True, pos marks top-right instead of top-left corner - self.align_right = kwargs.get("align_right", False) - self.text = kwargs.get("text", "