diff --git a/degeon_py/active_chat.py b/degeon_py/active_chat.py index ee0e4c6..8ad3732 100644 --- a/degeon_py/active_chat.py +++ b/degeon_py/active_chat.py @@ -26,7 +26,7 @@ class ActiveChat: :param header_height (int): height of the header (the title) """ chat: dc.Chat - input_field: TextField = field(default_factory=lambda: TextField()) + input_field: TextField = field(default_factory=lambda: TextField(placeholder='New message')) send_button: Button = field(default_factory=lambda: Button(height=CHAT_PREVIEW_HEIGHT, width=100, text='Send', top_left=(WIDTH - 112, HEIGHT - 85))) height: int = HEIGHT @@ -79,7 +79,8 @@ class ActiveChat: last_message_y = self.height - MESSAGE_HEIGHT * 2 for i, message in zip(range(30), self.get_messages(core)): msg_surface = message.render() - surface.blit(msg_surface, (0, last_message_y - (MESSAGE_HEIGHT + 30) * (i + 1))) + last_message_y -= msg_surface.get_height() + 30 + surface.blit(msg_surface, (0, last_message_y - 30)) # Render header header = self.get_header() surface.blit(header, (0, 10)) diff --git a/degeon_py/config.py b/degeon_py/config.py index 8e42b43..153f723 100644 --- a/degeon_py/config.py +++ b/degeon_py/config.py @@ -4,6 +4,7 @@ import pygame pygame.init() # Fonts +font_medium = pygame.font.Font('CRC35.otf', 25) font = pygame.font.Font('CRC35.otf', 32) font_large = pygame.font.Font('CRC35.otf', 50) diff --git a/degeon_py/degeon.py b/degeon_py/degeon.py index 2375924..1827ff1 100644 --- a/degeon_py/degeon.py +++ b/degeon_py/degeon.py @@ -4,12 +4,15 @@ 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: @@ -22,9 +25,13 @@ class Degeon: :param active_chat (typing.Optional[ActiveChat]): current chat widget :param fps (int): FPS rate """ - core: 'dc.Degeon' + 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) @@ -36,15 +43,34 @@ class Degeon: Create a new default instance with settings from file :return: a Degeon instance """ - core: dc.Degeon = dc.new_degeon() + 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: @@ -59,6 +85,8 @@ class Degeon: """ Do all the necessary Rust work """ + if self.core is None: + return while self.core.message_queue_len(): self.core.handling_loop_iteration() @@ -66,12 +94,15 @@ class Degeon: """ 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() - self.chat_selector.chats = self.core.chats + 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: + 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): @@ -79,6 +110,11 @@ class Degeon: 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]) diff --git a/degeon_py/input_field.py b/degeon_py/input_field.py index 3828d9d..b736607 100644 --- a/degeon_py/input_field.py +++ b/degeon_py/input_field.py @@ -32,7 +32,7 @@ class TextField: 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) + placeholder_text: pygame.Surface = font.render(self.placeholder, True, (GREY + WHITE) // 2) surface.blit( placeholder_text, ( diff --git a/degeon_py/message.py b/degeon_py/message.py index e09a764..bee2d57 100644 --- a/degeon_py/message.py +++ b/degeon_py/message.py @@ -4,7 +4,8 @@ from dataclasses import dataclass import pygame -from config import WIDTH, CHAT_SELECTOR_WIDTH, MESSAGE_HEIGHT, font, BLUE, DARK_BLUE, WHITE, DARKER_BLUE +from config import WIDTH, CHAT_SELECTOR_WIDTH, MESSAGE_HEIGHT, font_medium, BLUE, DARK_BLUE, WHITE, DARKER_BLUE +from utils import render_text @dataclass @@ -27,16 +28,21 @@ class Message: Creates a surface with a rectangle and the message text written on it :return: the surface with rendered message """ - surface = pygame.Surface((self.chat_width, self.height)) + surface = pygame.Surface((self.chat_width, self.height * 10)) surface.fill(DARKER_BLUE) bg_color = BLUE * self.is_from_me + DARK_BLUE * (not self.is_from_me) - text_surface: pygame.Surface = font.render(self.text, True, WHITE) - padding = 5 + padding = 7 # Size of the scaled text surface blit_height: int = self.height - padding * 2 - blit_width: int = round(text_surface.get_width() * blit_height / text_surface.get_height()) + blit_width: int = self.chat_width // 2 x: int = 0 if not self.is_from_me else self.chat_width - blit_width - padding * 3.5 - pygame.draw.rect(surface, bg_color, (x, 0, blit_width + padding * 2, self.height)) - text_surface: pygame.Surface = pygame.transform.smoothscale(text_surface, (blit_width, blit_height)) - surface.blit(text_surface, (x + padding, padding)) - return surface + pygame.draw.rect(surface, bg_color, (x, 0, blit_width + padding * 2, self.height * 10)) + text_surface: pygame.Surface = pygame.Surface((blit_width, blit_height * 10)) + text_surface.fill(bg_color) + text_height = render_text(text_surface, (0, 0), font_medium, self.text, WHITE) + # text_surface = pygame.transform.chop(text_surface, (0, 0, blit_width, text_height)) + surface.blit(text_surface, (x + padding, padding, blit_width, text_height)) + new_surface = pygame.Surface((self.chat_width, text_height + 2 * padding)) + new_surface.fill(bg_color) + new_surface.blit(surface, (0, 0, new_surface.get_width(), new_surface.get_height())) + return new_surface diff --git a/degeon_py/utils.py b/degeon_py/utils.py index b67e581..a242b27 100644 --- a/degeon_py/utils.py +++ b/degeon_py/utils.py @@ -16,11 +16,11 @@ def get_word_length(ft, word: str): def render_text(surface, position: typing.Tuple[int, int], font: pygame.font, text: str, c=pygame.Color('black'), - cursor_position: Optional[int] = None): + cursor_position: Optional[int] = None) -> int: """Render text with hyphenation""" - if len(text) == 0: - return None + if len(list(filter(bool, text.split()))) == 0: + return 0 need_cursor = cursor_position is not None lines = [word.split() for word in text.splitlines()] @@ -48,3 +48,4 @@ def render_text(surface, position: typing.Tuple[int, int], font: pygame.font, te x += w + space_size y += h x = position[0] + return y