from __future__ import annotations from dataclasses import dataclass, field import typing import pygame from button import Button from chat_selector import ChatSelector from active_chat import ActiveChat from config import FPS, DARKER_BLUE, font, WHITE, WIDTH, CHAT_SELECTOR_WIDTH, HEIGHT import degeon_core as dc from input_field import TextField @dataclass class Degeon: """ The main class with everything connected to the app: the data, the Attributes: :param core (dc.Degeon): the rust worker :param chat_selector (ChatSelector): chat list widget :param active_chat (typing.Optional[ActiveChat]): current chat widget :param fps (int): FPS rate """ core: typing.Optional[dc.Degeon] chat_selector: ChatSelector active_chat: typing.Optional[ActiveChat] = None name_input_field: TextField = field( default_factory=lambda: TextField(placeholder='Name', top_left_corner=(WIDTH // 5, HEIGHT // 3))) name_input_button: Button = field(default_factory=lambda: Button(text='Done', width=200, height=100, top_left=(round(2 * WIDTH / 5), 2 * HEIGHT // 3))) has_profile_popup_opened: bool = False has_no_peers_popup: bool = False clock: pygame.time.Clock = field(default_factory=pygame.time.Clock) fps: int = FPS @classmethod def new(cls) -> Degeon: """ Create a new default instance with settings from file :return: a Degeon instance """ if dc.is_data_available(): core: dc.Degeon = dc.new_degeon() else: # core: dc.Degeon = dc.new_degeon_with_name(input('Enter name: ')) core = None chat_selector = ChatSelector() return cls(core=core, chat_selector=chat_selector) def register(self, name: str): """ Create new rust-Degeon instance with name :param name: user's name """ self.core = dc.new_degeon_with_name(name) self.chat_selector.chats = self.core.chats def render(self, screen: pygame.Surface): """ Render everything on the screen :param screen: the main screen """ if self.core is None: screen.fill(DARKER_BLUE) button_surface: pygame.Surface = self.name_input_button.render() screen.blit(button_surface, self.name_input_button.top_left) name_field_surface: pygame.Surface = self.name_input_field.render() screen.blit(name_field_surface, self.name_input_field.top_left_corner) return chats_surface = self.chat_selector.render() screen.blit(chats_surface, (0, 0)) if self.active_chat is not None: active_chat_surface = self.active_chat.render(self.core) screen.blit(active_chat_surface, (self.active_chat.delta_x, 0)) else: text_surface: pygame.Surface = font.render('<- Select chat in the menu', True, WHITE) screen.blit(text_surface, (round(WIDTH / 2 + CHAT_SELECTOR_WIDTH / 2 - text_surface.get_width() / 2), HEIGHT // 2)) def process_core_messages(self): """ Do all the necessary Rust work """ if self.core is None: return while self.core.message_queue_len(): self.core.handling_loop_iteration() def tick(self): """ Handle incoming messages, update chats, create no_peers popup if necessary """ if self.core is None: return # process events in core self.process_core_messages() if self.core is not None: self.chat_selector.chats = self.core.chats if 0 <= self.chat_selector.active_chat < len(self.chat_selector.chats) and self.active_chat is None: self.active_chat = ActiveChat.new(self.chat_selector.chats[self.chat_selector.active_chat]) if self.active_chat is not None and self.core is not None: self.active_chat.chat = self.core.chats[self.chat_selector.active_chat] def process_event(self, event: pygame.event.Event): """ Process an event :param event: pygame event """ if self.core is None: self.name_input_field.process_event(event) if self.name_input_button.process_event(event): self.register(self.name_input_field.value) return if self.chat_selector.process_event(event): if 0 <= self.chat_selector.active_chat < len(self.chat_selector.chats): self.active_chat = ActiveChat.new(self.chat_selector.chats[self.chat_selector.active_chat]) else: self.active_chat = None if self.active_chat is not None: # If the result is a string, it's a message result: typing.Optional[str] = self.active_chat.process_event(event) if result: self.core.send_text_message(result, self.chat_selector.active_chat) def main_loop(self, screen: pygame.Surface): """ Drawing everything and handling events """ while True: screen.fill(DARKER_BLUE) for event in pygame.event.get(): if event.type == pygame.QUIT: return self.process_event(event) self.tick() self.render(screen) self.clock.tick(self.fps) pygame.display.update()