From 82b910958520937525febcee418ad75b4f892cd4 Mon Sep 17 00:00:00 2001 From: Alexey Date: Mon, 13 Dec 2021 10:46:18 +0300 Subject: [PATCH] Wrote a pygame button --- degeon_py/active_chat.py | 5 +- degeon_py/button.py | 99 ++++++++++++++++++++++++++++++++++++++++ degeon_py/input_field.py | 13 +++++- 3 files changed, 113 insertions(+), 4 deletions(-) create mode 100644 degeon_py/button.py diff --git a/degeon_py/active_chat.py b/degeon_py/active_chat.py index d0cf65f..b4475e3 100644 --- a/degeon_py/active_chat.py +++ b/degeon_py/active_chat.py @@ -85,9 +85,10 @@ class ActiveChat: surface.blit(input_field_surface, (0, self.height - input_field_surface.get_height())) return surface - def process_event(self, event: pygame.event.Event): + def process_event(self, event: pygame.event.Event) -> typing.Optional[str]: """ - Process a click: select the necessary chat if this click is in the widget + Process clicks in the active chat widget. Return a message to send if needed :param event: a pygame event + :return: A message to send if there is one """ self.input_field.process_event(event) diff --git a/degeon_py/button.py b/degeon_py/button.py new file mode 100644 index 0000000..95af420 --- /dev/null +++ b/degeon_py/button.py @@ -0,0 +1,99 @@ +import pygame +import typing + +from config import font, WHITE, BLUE, BLACK +from dataclasses import dataclass + + +@dataclass +class Button: + """ + Just a button element for pygame + Attributes: + :param text (str): Text on the button + :param top_left ((int, int)): the coordinates of the top-left point of the button + :param width (int): the width of the button + :param height (int): the height of the button + :param padding (int): padding - the distance between rectangle border and button content + :param bg_color (Color): the background color + :param text_color (Color): color of the text + :param hovered_color (Color): the background color when the button is hovered + :param hovered_text_color (Color): the text color when the button is hovered + :param pressed_color (Color): the background color when the button is pressed + :param pressed_text_color (Color): the text color when the button is pressed + """ + text: str + top_left: typing.Tuple[int, int] + width: int + height: int + padding: int = 5 + bg_color: pygame.Color = BLUE + text_color: pygame.Color = WHITE + hovered_color: pygame.Color = (BLUE * 3 + WHITE) // 4 + hovered_text_color: pygame.Color = BLACK + pressed_color: pygame.Color = (BLUE + WHITE * 3) // 4 + pressed_text_color: pygame.Color = BLACK + is_hovered: bool = False + is_pressed: bool = False + + @property + def bottom(self) -> int: + """ + Return the y coordinate of the bottom of the button + :return: y_bottom + """ + return self.top_left[1] + self.height + + @property + def right(self) -> int: + """ + Return the x coordinate of the right edge of the button + :return: x_right + """ + return self.top_left[0] + self.width + + def get_colors(self) -> typing.Tuple[pygame.Color, pygame.Color]: + """ + Get background and text color considering hovered and pressed states + :return: the button's background color and the button's text color + """ + if self.is_pressed: + return self.pressed_color, self.pressed_text_color + if self.is_hovered: + return self.hovered_color, self.hovered_text_color + return self.bg_color, self.text_color + + def render(self) -> pygame.Surface: + """ + Draw the button + :return: a pygame surface with the button + """ + surface: pygame.Surface = pygame.Surface((self.width, self.height)) + bg_color, text_color = self.get_colors() + surface.fill(bg_color) + text_surface: pygame.Surface = font.render(self.text, True, text_color) + text_height: int = self.height - 2 * self.padding + text_width: int = round(text_surface.get_width() * text_height / text_surface.get_height()) + text_surface: pygame.Surface = pygame.transform.scale(text_surface, (text_width, text_height)) + surface.blit(text_surface, ((self.width - text_width) // 2, self.padding)) + return surface + + def process_event(self, event: pygame.event.Event) -> bool: + """ + Process a pygame event. If it's a click on this button, return true + :param event: a pygame event + :return: True if there was a click on the button + """ + # if this is a mouse event + if event.type in [pygame.MOUSEBUTTONUP, pygame.MOUSEMOTION, pygame.MOUSEBUTTONDOWN]: + # if the mouse event is inside the button + if self.top_left[0] <= event.pos[0] < self.bottom and self.top_left[1] <= event.pos[1] < self.right: + if event.type == pygame.MOUSEBUTTONUP: + self.is_pressed = False + return True + elif event.type == pygame.MOUSEBUTTONDOWN: + self.is_hovered = False + self.is_pressed = True + elif event.type == pygame.MOUSEMOTION: + self.is_hovered = True + return False diff --git a/degeon_py/input_field.py b/degeon_py/input_field.py index 3ee957d..5f4584d 100644 --- a/degeon_py/input_field.py +++ b/degeon_py/input_field.py @@ -43,7 +43,7 @@ class TextField: surface.blit( message_text, ( - (self.width - message_text.get_width()) // 2, + 0, # or, if we want to center, `(self.width - message_text.get_width()) // 2,` (self.height - message_text.get_height()) // 2 ) ) @@ -58,7 +58,6 @@ class TextField: if event.type == pygame.MOUSEBUTTONUP: self.is_focused = self.top_left_corner[0] <= event.pos[0] < self.top_left_corner[0] + self.width \ and self.top_left_corner[1] <= event.pos[1] < self.top_left_corner[1] + self.height - print(self.is_focused) if self.is_focused and hasattr(event, 'key') and event.type == 768: if event.key == pygame.K_BACKSPACE: self.value = self.value[:-1] @@ -71,3 +70,13 @@ class TextField: self.value = self.value[:self.cursor_position] + event.unicode + self.value[self.cursor_position:] self.cursor_position += 1 # print(self.is_focused, event.type, getattr(event, 'key', None), getattr(event, 'unicode', None), self.value) + + def collect(self) -> str: + """ + Get the current value and clear the field + :return: the value of the text input + """ + value = self.value + self.value = '' + self.cursor_position = 0 + return value