simplified event in Widget with Listener, added more useful property to BoxWidget
This commit is contained in:
parent
ca59abee01
commit
446e377b19
13 changed files with 118 additions and 124 deletions
17
source/event/Listener.py
Normal file
17
source/event/Listener.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
from typing import Callable
|
||||
|
||||
|
||||
class Listener:
|
||||
def __init__(self):
|
||||
self._events_listener: dict[str, set[Callable]] = {}
|
||||
|
||||
def add_listener(self, name: str, callback: Callable):
|
||||
if name not in self._events_listener: self._events_listener[name] = set()
|
||||
self._events_listener[name].add(callback)
|
||||
|
||||
def remove_listener(self, name: str, callback: Callable):
|
||||
self._events_listener[name].remove(callback)
|
||||
|
||||
def trigger_event(self, name: str, *args, **kwargs):
|
||||
for listener in self._events_listener.get(name, set()):
|
||||
listener(*args, **kwargs)
|
1
source/event/__init__.py
Normal file
1
source/event/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
from .Listener import Listener
|
|
@ -45,7 +45,7 @@ class MainMenu(Scene):
|
|||
texture_click=texture_button_click
|
||||
)
|
||||
|
||||
self.game_create.on_release = lambda *_: self.window.set_scene(scene.RoomCreate)
|
||||
self.game_create.add_listener("on_click_release", lambda *_: self.window.set_scene(scene.RoomCreate))
|
||||
|
||||
self.game_join = self.add_widget(
|
||||
widget.Button,
|
||||
|
@ -60,7 +60,7 @@ class MainMenu(Scene):
|
|||
texture_click=texture_button_click
|
||||
)
|
||||
|
||||
self.game_join.on_release = lambda *_: self.window.set_scene(scene.RoomJoin)
|
||||
self.game_join.add_listener("on_click_release", lambda *_: self.window.set_scene(scene.RoomJoin))
|
||||
|
||||
self.settings = self.add_widget(
|
||||
widget.Button,
|
||||
|
@ -75,7 +75,7 @@ class MainMenu(Scene):
|
|||
texture_click=texture_button_click
|
||||
)
|
||||
|
||||
self.settings.on_release = lambda *_: self.window.set_scene(scene.Settings)
|
||||
self.settings.add_listener("on_click_release", lambda *_: self.window.set_scene(scene.Settings))
|
||||
|
||||
self.fps_display = self.add_widget(FPSDisplay, color=(255, 255, 255, 180))
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ class RoomCreate(Scene):
|
|||
)
|
||||
|
||||
from source.gui.scene import MainMenu
|
||||
self.back.on_release = lambda *_: self.window.set_scene(MainMenu)
|
||||
self.back.add_listener("on_click_release", lambda *_: self.window.set_scene(MainMenu))
|
||||
|
||||
self.label_ip = self.add_widget(
|
||||
widget.Text,
|
||||
|
|
|
@ -34,7 +34,7 @@ class RoomJoin(Scene):
|
|||
)
|
||||
|
||||
from source.gui.scene import MainMenu
|
||||
self.back.on_release = lambda *_: self.window.set_scene(MainMenu)
|
||||
self.back.add_listener("on_click_release", lambda *_: self.window.set_scene(MainMenu))
|
||||
|
||||
self.entry_ip = self.add_widget(
|
||||
widget.Input,
|
||||
|
@ -69,12 +69,12 @@ class RoomJoin(Scene):
|
|||
texture_click=texture_button_click
|
||||
)
|
||||
|
||||
self.connect.on_release = lambda *_: network.Client(
|
||||
self.connect.add_listener("on_click_release", lambda *_: network.Client(
|
||||
window=self.window,
|
||||
ip_address=self.entry_ip.text,
|
||||
daemon=True,
|
||||
username="Client"
|
||||
).start()
|
||||
).start())
|
||||
|
||||
def on_draw(self):
|
||||
self.back.draw()
|
||||
|
|
|
@ -37,7 +37,7 @@ class Settings(Scene):
|
|||
)
|
||||
|
||||
from source.gui.scene import MainMenu
|
||||
self.back.on_release = lambda *_: self.window.set_scene(MainMenu)
|
||||
self.back.add_listener("on_click_release", lambda *_: self.window.set_scene(MainMenu))
|
||||
|
||||
self.checkbox = self.add_widget(
|
||||
widget.Checkbox,
|
||||
|
|
|
@ -47,6 +47,9 @@ class Button(BoxWidget):
|
|||
**dict_prefix("label_", kwargs)
|
||||
)
|
||||
|
||||
self.add_listener("on_hover_change", lambda *_: self._refresh_background())
|
||||
self.add_listener("on_click_change", lambda *_: self._refresh_background())
|
||||
|
||||
self._refresh_size() # refresh the size and position for the background and label
|
||||
|
||||
# background
|
||||
|
@ -76,20 +79,7 @@ class Button(BoxWidget):
|
|||
self.background.width, self.background.height = self.size
|
||||
|
||||
# center the label
|
||||
self.label.x = self.x + (self.width / 2)
|
||||
self.label.y = self.y + (self.height / 2)
|
||||
|
||||
@BoxWidget.hovering.setter
|
||||
def hovering(self, hovering: bool):
|
||||
# when the hover state is changed, update the background
|
||||
BoxWidget.hovering.fset(self, hovering)
|
||||
self._refresh_background()
|
||||
|
||||
@BoxWidget.clicking.setter
|
||||
def clicking(self, clicking: bool):
|
||||
# when the clicking state is changed, update the background
|
||||
BoxWidget.clicking.fset(self, clicking)
|
||||
self._refresh_background()
|
||||
self.label.x, self.label.y = self.center
|
||||
|
||||
# event
|
||||
|
||||
|
|
|
@ -34,6 +34,8 @@ class Checkbox(BoxWidget):
|
|||
|
||||
self.state = state
|
||||
|
||||
self.add_listener("on_click_release", lambda *_: self.swap_state())
|
||||
|
||||
self._refresh_size()
|
||||
|
||||
# refreshing
|
||||
|
@ -60,14 +62,13 @@ class Checkbox(BoxWidget):
|
|||
self._state = state
|
||||
self._refresh_tick()
|
||||
|
||||
def swap_state(self):
|
||||
self.state = not self.state # inverse l'état
|
||||
|
||||
# event
|
||||
|
||||
def on_resize(self, width: int, height: int):
|
||||
self._refresh_size()
|
||||
|
||||
def on_release(self, rel_x: int, rel_y: int, button: int, modifiers: int):
|
||||
# lorsque le bouton est enclenché, inverse son état
|
||||
self.state = not self.state
|
||||
|
||||
def draw(self):
|
||||
self.tick.draw()
|
||||
|
|
|
@ -49,6 +49,11 @@ class GameGrid(BoxWidget):
|
|||
**dict_prefix("cursor_", kwargs)
|
||||
)
|
||||
|
||||
self.add_listener("on_hover_leave", lambda *_: self.hide_cursor())
|
||||
self.add_listener("on_hover", self._refresh_cursor)
|
||||
self.add_listener("on_click_release",
|
||||
lambda rel_x, rel_y, *_: print("click", self.get_cell_from_rel(rel_x, rel_y)))
|
||||
|
||||
self._refresh_size()
|
||||
|
||||
def get_cell_from_rel(self, rel_x: int, rel_y: int) -> tuple[int, int]:
|
||||
|
@ -68,14 +73,24 @@ class GameGrid(BoxWidget):
|
|||
line.x = self.x + self.cell_width * column
|
||||
line.x2 = line.x
|
||||
line.y = self.y
|
||||
line.y2 = self.y + self.height
|
||||
line.y2 = self.y2
|
||||
|
||||
for row, line in enumerate(self.lines[-self._rows+1:], start=1):
|
||||
line.x = self.x
|
||||
line.x2 = self.x + self.width
|
||||
line.x2 = self.x2
|
||||
line.y = self.y + self.cell_height * row
|
||||
line.y2 = line.y
|
||||
|
||||
def _refresh_cursor(self, rel_x: int, rel_y: int):
|
||||
cell_x, cell_y = self.get_cell_from_rel(rel_x, rel_y)
|
||||
|
||||
self.cursor.x = self.x + cell_x * self.width / self._columns
|
||||
self.cursor.y = self.y + cell_y * self.height / self._rows
|
||||
self.cursor.width, self.cursor.height = self.cell_size
|
||||
|
||||
def hide_cursor(self):
|
||||
self.cursor.width, self.cursor.height = 0, 0
|
||||
|
||||
# property
|
||||
|
||||
@property
|
||||
|
@ -92,19 +107,6 @@ class GameGrid(BoxWidget):
|
|||
|
||||
# event
|
||||
|
||||
def on_hover(self, rel_x: int, rel_y: int):
|
||||
cell_x, cell_y = self.get_cell_from_rel(rel_x, rel_y)
|
||||
|
||||
self.cursor.x = self.x + cell_x * self.width / self._columns
|
||||
self.cursor.y = self.y + cell_y * self.height / self._rows
|
||||
self.cursor.width, self.cursor.height = self.cell_size
|
||||
|
||||
def on_hover_leave(self, rel_x: int, rel_y: int):
|
||||
self.cursor.width, self.cursor.height = 0, 0
|
||||
|
||||
def on_release(self, rel_x: int, rel_y: int, button: int, modifiers: int):
|
||||
print("click", (rel_x, rel_y), self.get_cell_from_rel(rel_x, rel_y))
|
||||
|
||||
def on_resize(self, width: int, height: int):
|
||||
self._refresh_size()
|
||||
|
||||
|
|
|
@ -47,6 +47,8 @@ class Input(BoxWidget):
|
|||
**dict_prefix("label_", kwargs)
|
||||
)
|
||||
|
||||
self.add_listener("on_activate_change", lambda *_: self._refresh_background())
|
||||
|
||||
self._refresh_size()
|
||||
|
||||
# background
|
||||
|
@ -76,13 +78,7 @@ class Input(BoxWidget):
|
|||
self.background.width, self.background.height = self.size
|
||||
|
||||
# center the label
|
||||
self.label.x = self.x + (self.width / 2)
|
||||
self.label.y = self.y + (self.height / 2)
|
||||
|
||||
@BoxWidget.activated.setter
|
||||
def activated(self, activated: bool) -> None:
|
||||
BoxWidget.activated.fset(self, activated)
|
||||
self._refresh_background()
|
||||
self.label.x, self.label.y = self.center
|
||||
|
||||
# property
|
||||
|
||||
|
|
|
@ -48,6 +48,8 @@ class Scroller(BoxWidget):
|
|||
**dict_prefix("label_", kwargs)
|
||||
)
|
||||
|
||||
self.add_listener("on_click_release", lambda rel_x, *_: self._refresh_cursor(rel_x))
|
||||
|
||||
self._from = from_
|
||||
self._to = to
|
||||
self.value = value
|
||||
|
@ -73,15 +75,14 @@ class Scroller(BoxWidget):
|
|||
)
|
||||
|
||||
# label
|
||||
self.label.x = self.x + (self.width / 2)
|
||||
self.label.y = self.y + (self.height / 2)
|
||||
self.label.x, self.label.y = self.center
|
||||
self.label.text = str(self.text_transform(self.value))
|
||||
|
||||
# property
|
||||
|
||||
def on_pressed(self, rel_x: int, rel_y: int, button: int, modifiers: int):
|
||||
def _refresh_cursor(self, rel_x: int):
|
||||
self.value = rel_x / self.width
|
||||
|
||||
# property
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self._value
|
||||
|
|
|
@ -27,9 +27,9 @@ class BoxWidget(Widget, ABC):
|
|||
self.width = width
|
||||
self.height = height
|
||||
|
||||
self._hovering = False # is the button currently hovered ?
|
||||
self._clicking = False # is the button currently clicked ?
|
||||
self._activated = False # is the button activated ? (the last click was inside this widget)
|
||||
self.hovering = False # is the button currently hovered ?
|
||||
self.clicking = False # is the button currently clicked ?
|
||||
self.activated = False # is the button activated ? (the last click was inside this widget)
|
||||
|
||||
# property
|
||||
|
||||
|
@ -64,6 +64,22 @@ class BoxWidget(Widget, ABC):
|
|||
def y(self, y: Distance):
|
||||
self._y = y
|
||||
|
||||
@property
|
||||
def xy(self) -> tuple[int, int]:
|
||||
return self.x, self.y
|
||||
|
||||
@property
|
||||
def x2(self) -> int:
|
||||
return self.x + self.width
|
||||
|
||||
@property
|
||||
def y2(self) -> int:
|
||||
return self.y + self.height
|
||||
|
||||
@property
|
||||
def xy2(self) -> tuple[int, int]:
|
||||
return self.x2, self.y2
|
||||
|
||||
@property
|
||||
def width(self) -> int:
|
||||
return self._getter_distance(self.scene.window.width, self._width)
|
||||
|
@ -80,47 +96,29 @@ class BoxWidget(Widget, ABC):
|
|||
def height(self, height: Optional[Distance]):
|
||||
self._height = height
|
||||
|
||||
@property
|
||||
def xy(self) -> tuple[int, int]:
|
||||
return self.x, self.y
|
||||
|
||||
@property
|
||||
def size(self) -> tuple[int, int]:
|
||||
return self.width, self.height
|
||||
|
||||
@property
|
||||
def bbox(self) -> tuple[int, int, int, int]:
|
||||
return self.x, self.y, self.x + self.width, self.y + self.height
|
||||
|
||||
# property that can be used to add event when these value are modified in some specific widget.
|
||||
return self.x, self.y, self.x2, self.y2
|
||||
|
||||
@property
|
||||
def hovering(self):
|
||||
return self._hovering
|
||||
|
||||
@hovering.setter
|
||||
def hovering(self, hovering: bool):
|
||||
self._hovering = hovering
|
||||
def center_x(self) -> float:
|
||||
return self.x + (self.width / 2)
|
||||
|
||||
@property
|
||||
def clicking(self):
|
||||
return self._clicking
|
||||
|
||||
@clicking.setter
|
||||
def clicking(self, clicking: bool):
|
||||
self._clicking = clicking
|
||||
def center_y(self) -> float:
|
||||
return self.y + (self.height / 2)
|
||||
|
||||
@property
|
||||
def activated(self):
|
||||
return self._activated
|
||||
|
||||
@activated.setter
|
||||
def activated(self, activated: bool):
|
||||
self._activated = activated
|
||||
def center(self) -> tuple[float, float]:
|
||||
return self.center_x, self.center_y
|
||||
|
||||
# event
|
||||
|
||||
def on_mouse_motion(self, x: int, y: int, dx: int, dy: int): # NOQA
|
||||
def on_mouse_motion(self, x: int, y: int, dx: int, dy: int):
|
||||
"""
|
||||
When the mouse is moved, this event is triggered.
|
||||
Allow the implementation of the on_hover_enter and on_hover_leave events
|
||||
|
@ -130,60 +128,44 @@ class BoxWidget(Widget, ABC):
|
|||
:dy: the difference of the y mouse axis
|
||||
"""
|
||||
|
||||
rel_x, rel_y = x - self.x, y - self.y
|
||||
|
||||
old_hovering = self.hovering
|
||||
self.hovering = in_bbox((x, y), self.bbox)
|
||||
|
||||
rel_x, rel_y = x - self.x, y - self.y
|
||||
|
||||
if old_hovering != self.hovering: # if the hover changed
|
||||
if self.hovering: self.on_hover_enter(rel_x, rel_y) # call the hover enter event
|
||||
else: self.on_hover_leave(rel_x, rel_y) # call the hover leave event
|
||||
# call the hover changed event
|
||||
self.trigger_event("on_hover_change", rel_x, rel_y)
|
||||
# call the hover enter / leave event
|
||||
self.trigger_event("on_hover_enter" if self.hovering else "on_hover_leave", rel_x, rel_y)
|
||||
|
||||
if self.hovering: # if the mouse motion is inside the collision
|
||||
self.on_hover(rel_x, rel_y) # call the hover event
|
||||
|
||||
def on_hover(self, rel_x: int, rel_y: int):
|
||||
"""
|
||||
This event is called when the mouse move in the bbox of the widget
|
||||
"""
|
||||
|
||||
def on_hover_enter(self, rel_x: int, rel_y: int):
|
||||
"""
|
||||
This event is called when the mouse enter the bbox of the widget
|
||||
"""
|
||||
|
||||
def on_hover_leave(self, rel_x: int, rel_y: int):
|
||||
"""
|
||||
This event is called when the mouse leave the bbox of the widget
|
||||
"""
|
||||
self.trigger_event("on_hover", rel_x, rel_y) # call the hover event
|
||||
|
||||
def on_mouse_press(self, x: int, y: int, button: int, modifiers: int):
|
||||
# if this button was the one hovered when the click was pressed
|
||||
rel_x, rel_y = x - self.x, y - self.y
|
||||
|
||||
if not in_bbox((x, y), self.bbox):
|
||||
self.activated = False # if the click was not in the bbox, disable the activated state
|
||||
return
|
||||
self.activated = in_bbox((x, y), self.bbox)
|
||||
self.trigger_event("on_activate_change", rel_x, rel_y, button, modifiers)
|
||||
|
||||
self.activated = True # if the click is inside the bbox, enable the activated state
|
||||
self.clicking = True # the widget is now clicked
|
||||
if self.activated: # if the click was inside the widget
|
||||
self.trigger_event("on_activate_enter", rel_x, rel_y, button, modifiers)
|
||||
|
||||
self.on_pressed(x - self.x, y - self.y, button, modifiers)
|
||||
self.clicking = True # the widget is also now clicked
|
||||
self.trigger_event("on_click_change", rel_x, rel_y, button, modifiers)
|
||||
self.trigger_event("on_click_press", rel_x, rel_y, button, modifiers)
|
||||
|
||||
else:
|
||||
self.trigger_event("on_activate_leave", rel_x, rel_y, button, modifiers)
|
||||
|
||||
def on_mouse_release(self, x: int, y: int, button: int, modifiers: int):
|
||||
old_click: bool = self._clicking
|
||||
rel_x, rel_y = x - self.x, y - self.y
|
||||
|
||||
old_click: bool = self.clicking
|
||||
self.clicking = False # the widget is no longer clicked
|
||||
|
||||
if not in_bbox((x, y), self.bbox): return
|
||||
if not in_bbox((x, y), self.bbox): return # if the release was not in the collision, ignore
|
||||
|
||||
# if this button was the one hovered when the click was pressed
|
||||
if old_click: self.on_release(x - self.x, y - self.y, button, modifiers)
|
||||
|
||||
def on_pressed(self, rel_x: int, rel_y: int, button: int, modifiers: int):
|
||||
"""
|
||||
This event is called when the bbox is pressed
|
||||
"""
|
||||
|
||||
def on_release(self, rel_x: int, rel_y: int, button: int, modifiers: int):
|
||||
"""
|
||||
This event is called when the bbox is released
|
||||
"""
|
||||
if old_click: # if this button was the one hovered when the click was pressed
|
||||
self.trigger_event("on_click_change", rel_x, rel_y, button, modifiers)
|
||||
self.trigger_event("on_click_release", rel_x, rel_y, button, modifiers)
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from source.event import Listener
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from source.gui.scene.abc import Scene
|
||||
|
||||
|
||||
class Widget(ABC):
|
||||
class Widget(Listener, ABC):
|
||||
"""
|
||||
A Widget that can be attached to a scene.
|
||||
|
||||
|
@ -13,6 +15,8 @@ class Widget(ABC):
|
|||
"""
|
||||
|
||||
def __init__(self, scene: "Scene", **kwargs):
|
||||
super().__init__()
|
||||
|
||||
self.scene = scene
|
||||
|
||||
@abstractmethod
|
||||
|
|
Loading…
Reference in a new issue