Browse Source

Input field in interface

master
Alexey 3 years ago committed by ennucore
parent
commit
49824ef937
  1. 13
      degeon_py/active_chat.py
  2. 17
      degeon_py/chat_selector.py
  3. 12
      degeon_py/degeon.py
  4. 73
      degeon_py/input_field.py
  5. 8
      degeon_py/message.py

13
degeon_py/active_chat.py

@ -1,13 +1,14 @@
from __future__ import annotations from __future__ import annotations
import typing import typing
from dataclasses import dataclass from dataclasses import dataclass, field
import pygame import pygame
from config import HEIGHT, WIDTH, CHAT_SELECTOR_WIDTH, DARKER_BLUE, CHAT_PREVIEW_HEIGHT, BLUE, WHITE, font, DARK_BLUE, \ from config import HEIGHT, WIDTH, CHAT_SELECTOR_WIDTH, DARKER_BLUE, CHAT_PREVIEW_HEIGHT, WHITE, font, DARK_BLUE, \
MESSAGE_HEIGHT MESSAGE_HEIGHT
import degeon_core as dc import degeon_core as dc
from input_field import TextField
from message import Message from message import Message
@ -24,6 +25,7 @@ class ActiveChat:
:param header_height (int): height of the header (the title) :param header_height (int): height of the header (the title)
""" """
chat: dc.Chat chat: dc.Chat
input_field: TextField = field(default_factory=lambda: TextField())
height: int = HEIGHT height: int = HEIGHT
width: int = WIDTH - CHAT_SELECTOR_WIDTH - 10 width: int = WIDTH - CHAT_SELECTOR_WIDTH - 10
delta_x: int = CHAT_SELECTOR_WIDTH + 10 delta_x: int = CHAT_SELECTOR_WIDTH + 10
@ -71,13 +73,16 @@ class ActiveChat:
# Render messages # Render messages
# This is the y0 for the last message # This is the y0 for the last message
last_message_y = self.height - 60 last_message_y = self.height - MESSAGE_HEIGHT * 2
for i, message in zip(range(30), self.get_messages()): for i, message in zip(range(30), self.get_messages()):
msg_surface = message.render() msg_surface = message.render()
surface.blit(msg_surface, (0, last_message_y - (MESSAGE_HEIGHT + 30) * (i + 1))) surface.blit(msg_surface, (0, last_message_y - (MESSAGE_HEIGHT + 30) * (i + 1)))
# Render header # Render header
header = self.get_header() header = self.get_header()
surface.blit(header, (0, 10)) surface.blit(header, (0, 10))
# Render message input
input_field_surface: pygame.Surface = self.input_field.render()
surface.blit(input_field_surface, (0, self.height - input_field_surface.get_height()))
return surface return surface
def process_event(self, event: pygame.event.Event): def process_event(self, event: pygame.event.Event):
@ -85,4 +90,4 @@ class ActiveChat:
Process a click: select the necessary chat if this click is in the widget Process a click: select the necessary chat if this click is in the widget
:param event: a pygame event :param event: a pygame event
""" """
pass # todo self.input_field.process_event(event)

17
degeon_py/chat_selector.py

@ -47,14 +47,12 @@ class ChatSelector:
surface.blit(title_surface, (7, i * self.chat_height + 10)) surface.blit(title_surface, (7, i * self.chat_height + 10))
return surface return surface
def process_event(self, event: pygame.event.Event): def process_event(self, event: pygame.event.Event) -> bool:
""" """
Process a click: select the necessary chat if this click is in the widget Process a click: select the necessary chat if this click is in the widget
:param event: a pygame event :param event: a pygame event
:return: True if a chat was changed
""" """
if event.type == pygame.MOUSEBUTTONUP and event.pos[0] < self.width:
self.active_chat = event.pos[1] // self.chat_height
self.hovered_chat = None
if event.type == pygame.MOUSEMOTION: if event.type == pygame.MOUSEMOTION:
if 0 < event.pos[0] < self.width \ if 0 < event.pos[0] < self.width \
and 0 < event.pos[1] < min(self.height, len(self.chats) * self.chat_height) - 2: and 0 < event.pos[1] < min(self.height, len(self.chats) * self.chat_height) - 2:
@ -62,6 +60,17 @@ class ChatSelector:
else: else:
self.hovered_chat = None self.hovered_chat = None
if event.type == pygame.MOUSEBUTTONUP and event.pos[0] < self.width:
self.hovered_chat = None
self.active_chat = event.pos[1] // self.chat_height
return True
return False
@classmethod @classmethod
def from_chats(cls, chats: typing.List['dc.Chat'], **kwargs) -> ChatSelector: def from_chats(cls, chats: typing.List['dc.Chat'], **kwargs) -> ChatSelector:
"""
Create a new ChatSelector from a list of Rust chats
:param chats: the list of chats
:param kwargs: optional additional arguments
"""
return cls(chats, **kwargs) return cls(chats, **kwargs)

12
degeon_py/degeon.py

@ -64,17 +64,21 @@ class Degeon:
# process events in core # process events in core
self.process_core_messages() self.process_core_messages()
self.chat_selector.chats = self.core.chats self.chat_selector.chats = self.core.chats
if 0 <= self.chat_selector.active_chat < len(self.chat_selector.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]) self.active_chat = ActiveChat.new(self.chat_selector.chats[self.chat_selector.active_chat])
else:
self.active_chat = None
def process_event(self, event: pygame.event.Event): def process_event(self, event: pygame.event.Event):
""" """
Process an event Process an event
:param event: pygame event :param event: pygame event
""" """
self.chat_selector.process_event(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:
self.active_chat.process_event(event)
def main_loop(self, screen: pygame.Surface): def main_loop(self, screen: pygame.Surface):
""" """

73
degeon_py/input_field.py

@ -0,0 +1,73 @@
import typing
import pygame
from config import font, MESSAGE_HEIGHT, WIDTH, CHAT_SELECTOR_WIDTH, HEIGHT, WHITE, DARKER_BLUE, GREY
from dataclasses import dataclass
@dataclass
class TextField:
"""
Field for message input
"""
value: str = ''
width: int = WIDTH - CHAT_SELECTOR_WIDTH - 80
height: int = MESSAGE_HEIGHT * 2
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)
surface.blit(
placeholder_text,
(
(self.width - placeholder_text.get_width()) // 2,
(self.height - placeholder_text.get_height()) // 2
)
)
if self.value:
message_text: pygame.Surface = font.render(self.value, True, WHITE)
surface.blit(
message_text,
(
(self.width - message_text.get_width()) // 2,
(self.height - message_text.get_height()) // 2
)
)
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
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]
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
# print(self.is_focused, event.type, getattr(event, 'key', None), getattr(event, 'unicode', None), self.value)

8
degeon_py/message.py

@ -33,10 +33,10 @@ class Message:
text_surface: pygame.Surface = font.render(self.text, True, WHITE) text_surface: pygame.Surface = font.render(self.text, True, WHITE)
padding = 5 padding = 5
# Size of the scaled text surface # Size of the scaled text surface
blit_height = self.height - padding * 2 blit_height: int = self.height - padding * 2
blit_width = round(text_surface.get_width() * blit_height / text_surface.get_height()) blit_width: int = round(text_surface.get_width() * blit_height / text_surface.get_height())
x = 0 if not self.is_from_me else self.chat_width - blit_width - padding * 2 x: int = 0 if not self.is_from_me else self.chat_width - blit_width - padding * 2
pygame.draw.rect(surface, bg_color, (x, 0, blit_width + padding * 2, self.height)) pygame.draw.rect(surface, bg_color, (x, 0, blit_width + padding * 2, self.height))
text_surface = pygame.transform.smoothscale(text_surface, (blit_width, blit_height)) text_surface: pygame.Surface = pygame.transform.smoothscale(text_surface, (blit_width, blit_height))
surface.blit(text_surface, (x + padding, padding)) surface.blit(text_surface, (x + padding, padding))
return surface return surface

Loading…
Cancel
Save