added more widgets

This commit is contained in:
Faraphel 2023-02-09 18:26:03 +01:00
parent 920914ad8c
commit 3cb8a94c13
16 changed files with 162 additions and 228 deletions

View file

@ -1,15 +1,16 @@
import pyglet import pyglet
from source.gui.scene.debug import FPSAbstractScene from source.gui.scene import MainMenuScene
from source.gui.scene.debug import FPSScene
from source.gui.scene.test import TestButtonScene, TestLabelScene from source.gui.scene.test import TestButtonScene, TestLabelScene
from source.gui.window import Window from source.gui.window import Window
# Create a new window # Create a new window
window = Window(resizable=True, vsync=False) window = Window(resizable=True, vsync=True)
window.add_scene(TestButtonScene()) # window.add_scene(TestButtonScene())
# window.add_scene(TestLabelScene()) # window.add_scene(TestLabelScene())
window.add_scene(FPSAbstractScene()) window.add_scene(MainMenuScene())
window.add_scene(FPSScene())
# Start the event loop # Start the event loop
pyglet.app.run(interval=0) pyglet.app.run(interval=0)

View file

@ -0,0 +1,72 @@
from typing import TYPE_CHECKING, Optional
import pyglet
from source.gui.scene.abc import AbstractScene
from source.gui.widget import WidgetLabel, WidgetButton
if TYPE_CHECKING:
from source.gui.window import Window
class MainMenuScene(AbstractScene):
def __init__(self):
super().__init__()
self._title: Optional[WidgetLabel] = None
self._room_create: Optional[WidgetButton] = None
self._room_join: Optional[WidgetButton] = None
def on_window_added(self, window: "Window"):
normal_texture = pyglet.image.load("./assets/image/button/test_button_normal.png")
hover_texture = pyglet.image.load("./assets/image/button/test_button_hover.png")
click_texture = pyglet.image.load("./assets/image/button/test_button_clicking.png")
self.button_atlas = pyglet.image.atlas.TextureAtlas()
normal_region = self.button_atlas.add(normal_texture)
hover_region = self.button_atlas.add(hover_texture)
click_region = self.button_atlas.add(click_texture)
self.background_batch = pyglet.graphics.Batch()
self.label_batch = pyglet.graphics.Batch()
self._title = WidgetLabel(
x=0.1, y=0.9, width=0.2, height=0.1,
text="Bataille Navale",
font_size=30,
label_batch=self.label_batch,
)
self.add_widget(self._title)
self._room_create = WidgetButton(
x=0.1, y=0.5, width=0.3, height=0.1,
label_text="Créer une salle",
normal_texture=normal_region,
hover_texture=hover_region,
click_texture=click_region,
label_batch=self.label_batch,
background_batch=self.background_batch,
)
self.add_widget(self._room_create)
self._room_join = WidgetButton(
x=0.1, y=0.4, width=0.3, height=0.1,
label_text="Rejoindre une salle",
normal_texture=normal_region,
hover_texture=hover_region,
click_texture=click_region,
label_batch=self.label_batch,
background_batch=self.background_batch,
)
self.add_widget(self._room_join)
super().on_window_added(window)
def on_draw(self, window: "Window"):
super().on_draw(window)
self.background_batch.draw()
self.label_batch.draw()

View file

@ -0,0 +1 @@
from .MainMenuScene import MainMenuScene

View file

@ -12,9 +12,9 @@ class AbstractScene(ABC):
Can be used to create a menu, an overlay, ... Can be used to create a menu, an overlay, ...
""" """
def __init__(self): def __init__(self, window: "Window"):
self._widgets: list["AbstractWidget"] = [] # the lists of the widgets in the scene self._widgets: list["AbstractWidget"] = [] # the lists of the widgets in the scene
self._window: Optional["Window"] = None # the window where the scene is attached self._window: "Window" = window # the window where the scene is attached
# widget # widget
@ -31,96 +31,3 @@ class AbstractScene(ABC):
def clear_widget(self) -> None: def clear_widget(self) -> None:
self.remove_widget(*self._widgets) self.remove_widget(*self._widgets)
# scene event
def on_window_added(self, window: "Window"): # when the Scene is added to a window
for widget in self._widgets: widget.on_window_added(window, self)
def on_window_removed(self, window: "Window"): # when the Scene is removed from a window
for widget in self._widgets: widget.on_window_removed(window, self)
# window
@property
def window(self) -> "Window":
return self._window
@window.setter
def window(self, window: "Window"): # trying to change the window will trigger the event
if self._window is not None: self.on_window_removed(self._window)
self._window = window
if self._window is not None: self.on_window_added(self._window)
# all the events of the window are directly available here or in the widgets
def on_draw(self, window: "Window"):
for widget in self._widgets: widget.on_draw(window, self)
def on_resize(self, window: "Window", width: int, height: int):
for widget in self._widgets: widget.on_resize(window, self, width, height)
def on_hide(self, window: "Window"):
for widget in self._widgets: widget.on_hide(window, self)
def on_show(self, window: "Window"):
for widget in self._widgets: widget.on_show(window, self)
def on_close(self, window: "Window"):
for widget in self._widgets: widget.on_close(window, self)
def on_expose(self, window: "Window"):
for widget in self._widgets: widget.on_expose(window, self)
def on_activate(self, window: "Window"):
for widget in self._widgets: widget.on_activate(window, self)
def on_deactivate(self, window: "Window"):
for widget in self._widgets: widget.on_deactivate(window, self)
def on_text(self, window: "Window", char: str):
for widget in self._widgets: widget.on_text(window, self, char)
def on_move(self, window: "Window", x: int, y: int):
for widget in self._widgets: widget.on_move(window, self, x, y)
def on_context_lost(self, window: "Window"):
for widget in self._widgets: widget.on_context_lost(window, self)
def on_context_state_lost(self, window: "Window"):
for widget in self._widgets: widget.on_context_state_lost(window, self)
def on_key_press(self, window: "Window", symbol: int, modifiers: int):
for widget in self._widgets: widget.on_key_press(window, self, symbol, modifiers)
def on_key_release(self, window: "Window", symbol: int, modifiers: int):
for widget in self._widgets: widget.on_key_release(window, self, symbol, modifiers)
def on_key_held(self, window: "Window", dt: float, symbol: int, modifiers: int):
for widget in self._widgets: widget.on_key_held(window, self, dt, symbol, modifiers)
def on_mouse_enter(self, window: "Window", x: int, y: int):
for widget in self._widgets: widget.on_mouse_enter(window, self, x, y)
def on_mouse_leave(self, window: "Window", x: int, y: int):
for widget in self._widgets: widget.on_mouse_leave(window, self, x, y)
def on_text_motion(self, window: "Window", motion: int):
for widget in self._widgets: widget.on_text_motion(window, self, motion)
def on_text_motion_select(self, window: "Window", motion: int):
for widget in self._widgets: widget.on_text_motion_select(window, self, motion)
def on_mouse_motion(self, window: "Window", x: int, y: int, dx: int, dy: int):
for widget in self._widgets: widget.on_mouse_motion(window, self, x, y, dx, dy)
def on_mouse_press(self, window: "Window", x: int, y: int, button: int, modifiers: int):
for widget in self._widgets: widget.on_mouse_press(window, self, x, y, button, modifiers)
def on_mouse_release(self, window: "Window", x: int, y: int, button: int, modifiers: int):
for widget in self._widgets: widget.on_mouse_release(window, self, x, y, button, modifiers)
def on_mouse_drag(self, window: "Window", x: int, y: int, dx: int, dy: int, buttons: int, modifiers: int):
for widget in self._widgets: widget.on_mouse_drag(window, self, x, y, dx, dy, buttons, modifiers)
def on_mouse_scroll(self, window: "Window", x: int, y: int, scroll_x: float, scroll_y: float):
for widget in self._widgets: widget.on_mouse_scroll(window, self, x, y, scroll_x, scroll_y)

View file

@ -8,7 +8,7 @@ if TYPE_CHECKING:
from source.gui.window import Window from source.gui.window import Window
class FPSAbstractScene(AbstractScene): class FPSScene(AbstractScene):
""" """
A base scene that can be used as an overlay to display the FPS A base scene that can be used as an overlay to display the FPS
""" """

View file

@ -1 +1 @@
from .FPSScene import FPSAbstractScene from .FPSScene import FPSScene

View file

@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
import pyglet import pyglet
from source.gui.scene.abc import AbstractScene from source.gui.scene.abc import AbstractScene
from source.gui.widget import Button from source.gui.widget import WidgetButton
if TYPE_CHECKING: if TYPE_CHECKING:
from source.gui.window import Window from source.gui.window import Window
@ -36,7 +36,7 @@ class TestButtonScene(AbstractScene):
for x in range(10): for x in range(10):
for y in range(10): for y in range(10):
self.add_widget(Button( self.add_widget(WidgetButton(
x=x*0.1, y=y*0.1, width=0.1, height=0.1, x=x*0.1, y=y*0.1, width=0.1, height=0.1,
normal_texture=normal_region, normal_texture=normal_region,

View file

@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
import pyglet import pyglet
from source.gui.scene.abc import AbstractScene from source.gui.scene.abc import AbstractScene
from source.gui.widget import Label from source.gui.widget import WidgetLabel
if TYPE_CHECKING: if TYPE_CHECKING:
from source.gui.window import Window from source.gui.window import Window
@ -24,7 +24,7 @@ class TestLabelScene(AbstractScene):
for x in range(10): for x in range(10):
for y in range(10): for y in range(10):
self.add_widget(Label( self.add_widget(WidgetLabel(
x=x*0.1, y=y*0.1, width=0.1, height=0.1, x=x*0.1, y=y*0.1, width=0.1, height=0.1,
text=f"{x}.{y}", text=f"{x}.{y}",

View file

@ -12,7 +12,7 @@ if TYPE_CHECKING:
from source.gui.scene.abc import AbstractScene from source.gui.scene.abc import AbstractScene
class Button(AbstractResizableWidget): class WidgetButton(AbstractResizableWidget):
def __init__(self, def __init__(self,
# position # position

View file

@ -1,5 +1,5 @@
from source.gui.widget.abc import AbstractResizableWidget from source.gui.widget.abc import AbstractResizableWidget
class Image(AbstractResizableWidget): class WidgetImage(AbstractResizableWidget):
... ...

View file

@ -10,7 +10,7 @@ if TYPE_CHECKING:
from source.gui.scene.abc import AbstractScene from source.gui.scene.abc import AbstractScene
class Input(AbstractResizableWidget): class WidgetInput(AbstractResizableWidget):
def __init__(self, def __init__(self,
# position # position

View file

@ -1,8 +1,8 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING, Optional
import pyglet.text import pyglet.text
from source.gui.widget.base import Sprite from source.gui.widget.base import Sprite, Label
from source.gui.widget.abc import AbstractResizableWidget from source.gui.widget.abc import AbstractResizableWidget
from source.type import Percentage from source.type import Percentage
@ -11,7 +11,7 @@ if TYPE_CHECKING:
from source.gui.scene.abc import AbstractScene from source.gui.scene.abc import AbstractScene
class Label(AbstractResizableWidget): class WidgetLabel(AbstractResizableWidget):
def __init__(self, def __init__(self,
# position # position
@ -47,7 +47,7 @@ class Label(AbstractResizableWidget):
super().__init__(x, y, width, height) super().__init__(x, y, width, height)
self._label = None self._label: Optional[Label] = None
self._label_kwargs = { self._label_kwargs = {
"text": text, "text": text,
"font_name": font_name, "font_name": font_name,
@ -78,7 +78,7 @@ class Label(AbstractResizableWidget):
def on_window_added(self, window: "Window", scene: "AbstractScene"): def on_window_added(self, window: "Window", scene: "AbstractScene"):
super().on_window_added(window, scene) super().on_window_added(window, scene)
self._label = pyglet.text.Label( self._label = Label(
x=self.x, y=self.y, width=self.width, height=self.height, x=self.x, y=self.y, width=self.width, height=self.height,
**self._label_kwargs **self._label_kwargs
) )
@ -90,13 +90,5 @@ class Label(AbstractResizableWidget):
) )
def update_size(self): def update_size(self):
self._label.x = self.x self._label.update_size(self.x, self.y, self.width, self.height)
self._label.y = self.y if self._background is not None: self._background.update_size(self.x, self.y, self.width, self.height)
self._label.width = self.width
self._label.height = self.height
if self._background is not None:
self._background.x = self.x
self._background.y = self.y
self._background.width = self.width
self._background.height = self.height

View file

@ -1,4 +1,4 @@
from .Label import Label from .WidgetLabel import WidgetLabel
from .Input import Input from .WidgetInput import WidgetInput
from .Button import Button from .WidgetButton import WidgetButton
from .Image import Image from .WidgetImage import WidgetImage

View file

@ -4,6 +4,10 @@ import pyglet.text
class Label(pyglet.text.Label): class Label(pyglet.text.Label):
"""
Similar to the pyglet Label, but allow to update the size from a function
"""
def update_size(self, x: int, y: int, width: Optional[int] = None, height: Optional[int] = None): def update_size(self, x: int, y: int, width: Optional[int] = None, height: Optional[int] = None):
self.x = x self.x = x
self.y = y self.y = y

View file

@ -2,6 +2,11 @@ import pyglet.sprite
class Sprite(pyglet.sprite.Sprite): class Sprite(pyglet.sprite.Sprite):
"""
Similar to the pyglet Sprite, but allow to change the width and height directly
and update the size from a function
"""
def __init__(self, *args, width: int = None, height: int = None, **kwargs): def __init__(self, *args, width: int = None, height: int = None, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)

View file

@ -1,4 +1,4 @@
from typing import TYPE_CHECKING, Callable from typing import TYPE_CHECKING, Callable, Type
import pyglet.window import pyglet.window
@ -8,17 +8,17 @@ if TYPE_CHECKING:
class Window(pyglet.window.Window): # NOQA class Window(pyglet.window.Window): # NOQA
""" """
Same as the original pyglet class, but allow to set a scene. Similaire à la fenêtre de base de pyglet, mais permet d'ajouter des scènes.
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
# scene # scene
self._scenes = [] self._scenes: list["AbstractScene"] = []
# a dictionary linking a key pressed to the corresponding event function # a dictionary linking a key pressed to the corresponding event function
self._on_key_held_events: dict[(int, int), Callable] = {} self._on_key_held_events: dict[tuple[int, int], Callable] = {}
# keys event handler # keys event handler
self.keys_handler = pyglet.window.key.KeyStateHandler() self.keys_handler = pyglet.window.key.KeyStateHandler()
@ -26,115 +26,67 @@ class Window(pyglet.window.Window): # NOQA
# scene system # scene system
def set_scene(self, *scenes: "AbstractScene"): def set_scene(self, scenes_type: Type["AbstractScene"]) -> "AbstractScene":
"""
Clear all the previous scene and add a scene to the window
:param scenes_type: the class of the scene to add
:return: the scene
"""
self.clear() self.clear()
self.add_scene(*scenes) return self.add_scene(scenes_type)
def add_scene(self, *scenes: "AbstractScene", priority: int = 0): def add_scene(self, scene_type: Type["AbstractScene"], priority: int = 0) -> "AbstractScene":
for scene in scenes: """
self._scenes.insert(priority, scene) Add a scene to the window
scene.on_window_added(self) :param scene_type: the class of the scene to add
:param priority: the priority of the scene in the display
:return: the scene
"""
scene: "AbstractScene" = scene_type(self)
self._scenes.insert(priority, scene)
return scene
def remove_scene(self, *scenes: "AbstractScene"): def remove_scene(self, *scenes: "AbstractScene") -> None:
"""
Remove scenes from the window
:param scenes: the scenes to remove
"""
for scene in scenes: for scene in scenes:
scene.on_window_removed(self)
self._scenes.remove(scene) self._scenes.remove(scene)
def clear_scene(self): def clear_scene(self) -> None:
"""
Remove all the scenes from the window
"""
self.remove_scene(*self._scenes) self.remove_scene(*self._scenes)
# NOTE: it is too difficult to refactor all the event because : # event
# - There is no event "on_any_event" or equivalent
# - The list of all the event is not available in the source
# - Some event need special code like on_draw with the clear, on_resize with the super, ...
def on_draw(self): # NOQA def scene_event_wrapper(self, name: str, func: Callable) -> Callable:
self.clear() # clear the window to reset it """
for scene in self._scenes: scene.on_draw(self) Un wrapper permettant d'appeler l'événement de toutes les scènes attachées.
:param name: nom de la fonction à appeler dans la scène.
:param func: fonction originale à entourer du wrapper
:return: une fonction appelant l'événement original ainsi que ceux des scènes.
"""
def on_resize(self, width: int, height: int): def _func(*args, **kwargs):
super().on_resize(width, height) # this function is already defined and used func(*args, **kwargs)
for scene in self._scenes: scene.on_resize(self, width, height) for scene in self._scenes: getattr(scene, name, lambda *_, **__: "pass")(*args, **kwargs)
def on_hide(self): return _func
for scene in self._scenes: scene.on_hide(self)
def on_show(self): def __getattribute__(self, item: str):
for scene in self._scenes: scene.on_show(self) """
Fonction appelée dès que l'on essaye d'accéder à l'un des attributs de l'objet.
:param item: nom de l'attribut recherché
:return: l'attribut de l'objet correspondant.
"""
def on_close(self): # print(".", end="")
super().close() # this function is already defined and used
for scene in self._scenes: scene.on_close(self)
def on_expose(self): attribute = super().__getattribute__(item)
for scene in self._scenes: scene.on_expose(self)
def on_activate(self): # si l'attribut est un évenement (commence par "on_"), alors renvoie le dans un wrapper
for scene in self._scenes: scene.on_activate(self) return self.scene_event_wrapper(item, attribute) if item.startswith("on_") else attribute
def on_deactivate(self):
for scene in self._scenes: scene.on_deactivate(self)
def on_text(self, char: str):
for scene in self._scenes: scene.on_text(self, char)
def on_move(self, x: int, y: int):
for scene in self._scenes: scene.on_move(self, x, y)
def on_context_lost(self):
for scene in self._scenes: scene.on_context_lost(self)
def on_context_state_lost(self):
for scene in self._scenes: scene.on_context_state_lost(self)
def on_key_press(self, symbol: int, modifiers: int):
super().on_key_press(symbol, modifiers) # this function is already defined and used
# this allows the on_key_held event to be called every frame after a press
pyglet.clock.schedule(self.get_on_key_held_function(symbol, modifiers))
for scene in self._scenes: scene.on_key_press(self, symbol, modifiers)
def on_key_release(self, symbol: int, modifiers: int):
# this allows the on_key_held event to stop after the key is released
pyglet.clock.unschedule(self.get_on_key_held_function(symbol, modifiers))
for scene in self._scenes: scene.on_key_release(self, symbol, modifiers)
def get_on_key_held_function(self, symbol: int, modifiers: int):
key: tuple[int, int] = (symbol, modifiers)
if key not in self._on_key_held_events:
self._on_key_held_events[key] = lambda dt: self.on_key_held(dt, symbol, modifiers)
return self._on_key_held_events[key]
def on_key_held(self, dt: float, symbol: int, modifiers: int):
for scene in self._scenes: scene.on_key_held(self, dt, symbol, modifiers)
def on_mouse_enter(self, x: int, y: int):
for scene in self._scenes: scene.on_mouse_enter(self, x, y)
def on_mouse_leave(self, x: int, y: int):
for scene in self._scenes: scene.on_mouse_leave(self, x, y)
def on_text_motion(self, motion: int):
for scene in self._scenes: scene.on_text_motion(self, motion)
def on_text_motion_select(self, motion: int):
for scene in self._scenes: scene.on_text_motion_select(self, motion)
def on_mouse_motion(self, x: int, y: int, dx: int, dy: int):
for scene in self._scenes: scene.on_mouse_motion(self, x, y, dx, dy)
def on_mouse_press(self, x: int, y: int, button: int, modifiers: int):
for scene in self._scenes: scene.on_mouse_press(self, x, y, button, modifiers)
def on_mouse_release(self, x: int, y: int, button: int, modifiers: int):
for scene in self._scenes: scene.on_mouse_release(self, x, y, button, modifiers)
def on_mouse_drag(self, x: int, y: int, dx: int, dy: int, buttons: int, modifiers: int):
for scene in self._scenes: scene.on_mouse_drag(self, x, y, dx, dy, buttons, modifiers)
def on_mouse_scroll(self, x: int, y: int, scroll_x: float, scroll_y: float):
for scene in self._scenes: scene.on_mouse_scroll(self, x, y, scroll_x, scroll_y)