|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
from dataclasses import dataclass, field
|
|
|
|
import typing
|
|
|
|
import pygame
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class Degeon:
|
|
|
|
"""
|
|
|
|
The main class with everything connected to the app: the data, the
|
|
|
|
|
|
|
|
Attributes
|
|
|
|
"""
|
|
|
|
core: 'dc.Degeon'
|
|
|
|
chat_selector: ChatSelector
|
|
|
|
active_chat: typing.Optional[ActiveChat] = None
|
|
|
|
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
|
|
|
|
"""
|
|
|
|
core: dc.Degeon = dc.new_degeon()
|
|
|
|
chat_selector = ChatSelector()
|
|
|
|
return cls(core=core, chat_selector=chat_selector)
|
|
|
|
|
|
|
|
def render(self, screen: pygame.Surface):
|
|
|
|
"""
|
|
|
|
Render everything on the screen
|
|
|
|
:param screen: the main screen
|
|
|
|
"""
|
|
|
|
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()
|
|
|
|
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
|
|
|
|
"""
|
|
|
|
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
|
|
|
|
"""
|
|
|
|
# process events in core
|
|
|
|
self.process_core_messages()
|
|
|
|
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:
|
|
|
|
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.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()
|