L3-Bataille-Navale/source/gui/window/Window.py

169 lines
6.3 KiB
Python

from typing import Optional, Callable, TYPE_CHECKING
import pyglet.window
if TYPE_CHECKING:
from source.gui.scene import Scene
class Window(pyglet.window.Window): # NOQA - pycharm think pyglet window is abstract
"""
This class represent a Window based on the pyglet Window object.
Allow to use a "Scene" system to create very different interface like
a main menu, options menu, ... that can overlay each other without
putting everything in the window code.
"""
def __init__(self, scenes: Optional["Scene"] = None, *args, **kwargs):
super().__init__(*args, **kwargs)
self._scenes: list["Scene"] = []
if scenes is not None: self.add_scene(*scenes)
# add a keys handler to the window
self.keys = pyglet.window.key.KeyStateHandler()
self.push_handlers(self.keys)
# a dictionary linking a key pressed to the corresponding event function
self._on_key_held_events: dict[(int, int), Callable] = {}
# scene methods
def set_scene(self, *scenes: "Scene") -> None:
"""
Set the scene(s) of the window
:param scenes: the scene(s) to set
"""
self.clear_scene()
for scene in scenes: self.add_scene(scene)
def clear_scene(self) -> None:
"""
Clear all the scenes of the window
"""
for scene in self._scenes: scene.on_window_removed(self)
self._scenes.clear()
def add_scene(self, *scenes: "Scene", priority: int = 0) -> None:
"""
Add a scene to the window
:param scenes: the scene to add
:param priority: the priority level of the scene. The higher, the more the scene will be drawn on top
"""
for scene in scenes:
self._scenes.insert(priority, scene)
scene.on_window_added(self)
def remove_scene(self, *scenes: "Scene") -> None:
"""
Remove a scene from the window
:param scenes: the scene to remove
"""
for scene in scenes:
scene.on_window_removed(self)
self._scenes.remove(scene)
def get_scenes(self):
"""
Get the list of the scenes
:return: the list of all the scenes currently used
"""
return self._scenes.copy()
# window event methods
# NOTE: it is too difficult to refactor all the event because :
# - 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
self.clear() # clear the window to reset it
for scene in self._scenes: scene.on_draw(self)
def on_resize(self, width: int, height: int):
super().on_resize(width, height) # this function is already defined and used
for scene in self._scenes: scene.on_resize(self, width, height)
def on_hide(self):
for scene in self._scenes: scene.on_hide(self)
def on_show(self):
for scene in self._scenes: scene.on_show(self)
def on_close(self):
super().close() # this function is already defined and used
for scene in self._scenes: scene.on_close(self)
def on_expose(self):
for scene in self._scenes: scene.on_expose(self)
def on_activate(self):
for scene in self._scenes: scene.on_activate(self)
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)