import time import typing import pygame from config import font, MESSAGE_HEIGHT, WIDTH, CHAT_SELECTOR_WIDTH, HEIGHT, WHITE, DARKER_BLUE, GREY from utils import render_text from dataclasses import dataclass @dataclass class TextField: """ Field for message input """ value: str = '' width: int = WIDTH - CHAT_SELECTOR_WIDTH - 140 height: int = MESSAGE_HEIGHT * 1.5 top_left_corner: typing.Tuple[int, int] = (CHAT_SELECTOR_WIDTH, HEIGHT - MESSAGE_HEIGHT * 2) is_focused: bool = False placeholder: str = '' cursor_position: int = 0 def render(self) -> pygame.Surface: """ Render the text field onto a pygame surface :return: a surface with the field """ surface = pygame.Surface((self.width, self.height)) surface.fill(WHITE) padding = 5 pygame.draw.rect(surface, DARKER_BLUE, (padding, padding, self.width - padding * 2, self.height - padding * 2)) if not self.value and self.placeholder: placeholder_text: pygame.Surface = font.render(self.placeholder, True, (GREY + WHITE) // 2) surface.blit( placeholder_text, ( (self.width - placeholder_text.get_width()) // 2, (self.height - placeholder_text.get_height()) // 2 ) ) if self.value: render_text(surface, (10, 10), font, self.value, WHITE, self.cursor_position if time.time() % 1 < 0.5 else None) return surface def process_event(self, event: pygame.event.Event): """ Handle a typing event or a click (to focus) :param event: a pygame event """ # If we have a click, we should focus the field if the click was inside or unfocus if it was outside 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 if self.is_focused and hasattr(event, 'key') and event.type == 768: if event.key == pygame.K_BACKSPACE: self.value = self.value[:-1] self.cursor_position -= 1 elif event.key == pygame.K_LEFT: self.cursor_position -= 1 elif event.key == pygame.K_RIGHT: self.cursor_position += 1 elif event.unicode: self.value = self.value[:self.cursor_position] + event.unicode + self.value[self.cursor_position:] self.cursor_position += 1 self.cursor_position = max(0, min(self.cursor_position, len(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