added the possibility to prevent an event to propagate to the next widgets

This commit is contained in:
Faraphel 2023-02-19 16:50:05 +01:00
parent d2d2f2fdc1
commit 5dcd570be8
10 changed files with 52 additions and 17 deletions

View file

@ -1,7 +1,9 @@
A faire : A faire :
- Prévisualisation des bateaux sur la grille
- Faire une scène incluant par défaut les boutons "Retour" - Faire une scène incluant par défaut les boutons "Retour"
- Ajouter un moyen d'annuler les évenements de se propager aux widgets en dessous
- Police d'écriture - Police d'écriture
- Combiner le _event_wrapper de Window et Scene (?)
Bug : Bug :
- Appuyez sur "Créer une salle" va lancer un thread, mais il n'est pas fermé s'il l'on revient dans - Appuyez sur "Créer une salle" va lancer un thread, mais il n'est pas fermé s'il l'on revient dans

View file

@ -0,0 +1,2 @@
class StopEventScene(Exception):
pass

View file

@ -0,0 +1,2 @@
class StopEventWidget(Exception):
pass

View file

@ -0,0 +1,2 @@
from .StopEventScene import StopEventScene
from .StopEventWidget import StopEventWidget

View file

@ -5,7 +5,6 @@ import pyglet
from source.gui.scene.abc import Scene from source.gui.scene.abc import Scene
from source.gui import widget, texture from source.gui import widget, texture
from source import core from source import core
from source.utils.dict import dict_add_prefix
if TYPE_CHECKING: if TYPE_CHECKING:
from source.gui.window import Window from source.gui.window import Window

View file

@ -80,7 +80,7 @@ class MainMenu(Scene):
label_batch=self.batch_label label_batch=self.batch_label
) )
self.settings.add_listener("on_click_release", lambda *_: self.window.set_scene(scene.Settings)) self.settings.add_listener("on_click_release", lambda *_: self.window.add_scene(scene.Settings))
def on_draw(self): def on_draw(self):
self.background.draw() self.background.draw()

View file

@ -2,6 +2,7 @@ from typing import TYPE_CHECKING
import pyglet import pyglet
from source.event.signal import StopEventScene
from source.gui.scene.abc import Scene from source.gui.scene.abc import Scene
from source.gui import widget, texture from source.gui import widget, texture
@ -31,8 +32,7 @@ class Settings(Scene):
label_batch=self.batch_label label_batch=self.batch_label
) )
from source.gui.scene import MainMenu self.back.add_listener("on_click_release", lambda *_: self.window.remove_scene(self))
self.back.add_listener("on_click_release", lambda *_: self.window.set_scene(MainMenu))
self.checkbox = self.add_widget( self.checkbox = self.add_widget(
widget.Checkbox, widget.Checkbox,
@ -67,3 +67,9 @@ class Settings(Scene):
self.batch_checkbox.draw() self.batch_checkbox.draw()
self.batch_label.draw() self.batch_label.draw()
def on_mouse_press_after(self, x: int, y: int, button: int, modifiers: int):
raise StopEventScene()
def on_mouse_motion_after(self, x: int, y: int, button: int, modifiers: int):
raise StopEventScene()

View file

@ -2,6 +2,8 @@ from abc import ABC
from functools import lru_cache from functools import lru_cache
from typing import TYPE_CHECKING, Callable, Type, Any from typing import TYPE_CHECKING, Callable, Type, Any
from source.event.signal import StopEventWidget
if TYPE_CHECKING: if TYPE_CHECKING:
from source.gui.window import Window from source.gui.window import Window
from source.gui.widget.abc import Widget from source.gui.widget.abc import Widget
@ -17,7 +19,7 @@ class Scene(ABC):
def __init__(self, window: "Window", *args, **kwargs): def __init__(self, window: "Window", *args, **kwargs):
self.window = window self.window = window
self._widgets: set["Widget"] = set() self._widgets: list["Widget"] = list()
# Widget Managing # Widget Managing
@ -31,13 +33,13 @@ class Scene(ABC):
""" """
widget: "Widget" = widget_class(self, **widget_kwargs) widget: "Widget" = widget_class(self, **widget_kwargs)
self._widgets.add(widget) self._widgets.append(widget)
return widget return widget
def remove_widget(self, widget: "Widget") -> None: def remove_widget(self, widget: "Widget") -> None:
""" """
Remove a widget from the scene. Remove a widget from the scene.
:param scene: the widget to remove. :param widget: the widget to remove.
""" """
self._widgets.remove(widget) self._widgets.remove(widget)
@ -59,15 +61,25 @@ class Scene(ABC):
:return: une fonction appelant l'événement original ainsi que ceux des scènes. :return: une fonction appelant l'événement original ainsi que ceux des scènes.
""" """
# Récupère la fonction originale. S'il n'y en a pas, renvoie une fonction sans effet.* # Récupère la fonction originale. S'il n'y en a pas, renvoie une fonction sans effet.
func = None func = None
try: func = super().__getattribute__(item) try: func = super().__getattribute__(item)
except AttributeError: pass except AttributeError: pass
# Récupère une fonction qui devra s'exécuter après tout le reste
func_after = None
try: func_after = super().__getattribute__(item + "_after")
except AttributeError: pass
def _func(*args, **kwargs) -> None: def _func(*args, **kwargs) -> None:
if func is not None: func(*args, **kwargs) if func is not None: func(*args, **kwargs)
for widget in self._widgets: for widget in self._widgets:
getattr(widget, item, lambda *_, **__: "pass")(*args, **kwargs) try: getattr(widget, item, lambda *_, **__: "pass")(*args, **kwargs)
# si l'erreur StopEventWidget est détecté, les autres scènes ne recevront pas l'event
except StopEventWidget: break
if func_after is not None: func_after(*args, **kwargs)
return _func return _func

View file

@ -62,7 +62,7 @@ class Input(BoxWidget):
return ( return (
texture if self.activated and (texture := self.style.get("active")) is not None else # NOQA texture if self.activated and (texture := self.style.get("active")) is not None else # NOQA
texture if self.invalid and (texture := self.style.get("error")) is not None else texture if self.invalid and (texture := self.style.get("signal")) is not None else
self.style.get("normal") self.style.get("normal")
) )

View file

@ -3,6 +3,7 @@ from typing import Type, Callable, TYPE_CHECKING, Any
import pyglet import pyglet
from source.event.signal import StopEventScene
if TYPE_CHECKING: if TYPE_CHECKING:
from source.gui.scene.abc import Scene from source.gui.scene.abc import Scene
@ -17,7 +18,7 @@ class Window(pyglet.window.Window): # NOQA
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self._scenes: set["Scene"] = set() self._scenes: list["Scene"] = list()
# Scene Managing # Scene Managing
@ -33,7 +34,7 @@ class Window(pyglet.window.Window): # NOQA
self.clear_scene() self.clear_scene()
return self.add_scene(scene_class, *scene_args, **scene_kwargs) return self.add_scene(scene_class, *scene_args, **scene_kwargs)
def add_scene(self, scene_class: Type["Scene"], *scene_args, **scene_kwargs) -> "Scene": def add_scene(self, scene_class: Type["Scene"], priority: int = 0, *scene_args, **scene_kwargs) -> "Scene":
""" """
Add a scene of the window. Add a scene of the window.
:scene_class: the class of the scene to add. :scene_class: the class of the scene to add.
@ -43,7 +44,7 @@ class Window(pyglet.window.Window): # NOQA
""" """
scene: "Scene" = scene_class(window=self, *scene_args, **scene_kwargs) scene: "Scene" = scene_class(window=self, *scene_args, **scene_kwargs)
self._scenes.add(scene) self._scenes.insert(priority, scene)
return scene return scene
def remove_scene(self, scene: "Scene") -> None: def remove_scene(self, scene: "Scene") -> None:
@ -76,6 +77,13 @@ class Window(pyglet.window.Window): # NOQA
:return: une fonction appelant l'événement original ainsi que ceux des scènes. :return: une fonction appelant l'événement original ainsi que ceux des scènes.
""" """
# if the event is the drawing of the objects, reverse the order of the scenes
# so that the last drawn are the one on top
scene_transform = (
(lambda scenes: reversed(scenes)) if item == "on_draw" else
(lambda scenes: scenes)
)
# try to get the original function # try to get the original function
func = None func = None
try: func = super().__getattribute__(item) try: func = super().__getattribute__(item)
@ -89,8 +97,10 @@ class Window(pyglet.window.Window): # NOQA
def _func(*args, **kwargs) -> None: def _func(*args, **kwargs) -> None:
if func is not None: func(*args, **kwargs) if func is not None: func(*args, **kwargs)
for scene in self._scenes: for scene in scene_transform(self._scenes):
getattr(scene, item)(*args, **kwargs) try: getattr(scene, item)(*args, **kwargs)
# si l'erreur StopEventScene est détecté, les autres scènes ne recevront pas l'event
except StopEventScene: break
if func_after is not None: func_after(*args, **kwargs) if func_after is not None: func_after(*args, **kwargs)