recoded the structure of the gui

This commit is contained in:
Faraphel 2023-01-14 00:11:35 +01:00
parent e9c39ce2e3
commit 7cc837b68e
28 changed files with 396 additions and 439 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 B

View file

@ -1,19 +1,14 @@
import pyglet import pyglet
from source.gui.scene.debug import FPSScene
from source.gui.scene.test import HelloWorldScene
from source.gui.window import Window from source.gui.window import Window
from source.gui.scene.debug import FPSCounterScene
from source.gui.scene.test import ButtonScene
# Créer une fenêtre
window = Window(resizable=True, visible=False)
# performance and button test # Create a new window
window = Window(resizable=True, vsync=False)
window.add_scene(HelloWorldScene(), FPSScene())
button_scene = ButtonScene() # Start the event loop
fps_counter_scene = FPSCounterScene() pyglet.app.run(interval=0)
window.add_scene(button_scene, fps_counter_scene)
# Lance la fenêtre
window.set_visible(True)
pyglet.app.run()

View file

@ -1,15 +0,0 @@
from source.gui.scene.base import Scene
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from source.gui.window import Window
class MainMenuScene(Scene):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def on_draw(self, window: "Window"):
super().on_draw(window)

View file

@ -1 +0,0 @@
from source.gui.scene.test.HelloWorldScene import HelloWorldScene

View file

@ -1,37 +1,33 @@
from pyglet.event import EventDispatcher from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from source.gui.widget.base import Widget
from source.gui.window import Window from source.gui.window import Window
from source.gui.widget.base import BaseWidget
class Scene(EventDispatcher): class BaseScene:
""" """
This class represent a scene that can be applied to a pyglet window. A scene that can be attached to a window
The scene can represent anything like the main menu, the game, the
options' menu, the multiplayer menu, ...
""" """
def __init__(self, widgets: list["Widget"] = None): def __init__(self):
self._widgets: list["Widget"] = [] self._widgets: list["BaseWidget"] = []
if widgets is not None: self.add_widget(*widgets) self._window: Optional["Window"] = None
def add_widget(self, *widgets: "Widget", priority: int = 0) -> None: # widget
def add_widget(self, *widgets: "BaseWidget", priority: int = 0) -> None:
for widget in widgets: for widget in widgets:
self._widgets.insert(priority, widget) self._widgets.insert(priority, widget)
widget.on_scene_added(self) widget.on_scene_added(self)
def remove_widget(self, *widgets: "Widget") -> None: def remove_widget(self, *widgets: "BaseWidget") -> None:
for widget in widgets: for widget in widgets:
widget.on_scene_removed(self) widget.on_scene_removed(self)
self._widgets.remove(widget) self._widgets.remove(widget)
def clear_widget(self) -> None: def clear_widget(self) -> None:
for widget in self._widgets: widget.on_scene_removed(self) self.remove_widget(*self._widgets)
self._widgets.clear()
# scene event # scene event
@ -41,7 +37,19 @@ class Scene(EventDispatcher):
def on_window_removed(self, window: "Window"): # when the Scene is removed from a window 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) for widget in self._widgets: widget.on_window_removed(window, self)
# window event # window
@property
def window(self) -> "Window":
return self._window
@window.setter
def window(self, window: "Window"):
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)
# event
def on_draw(self, window: "Window"): def on_draw(self, window: "Window"):
for widget in self._widgets: widget.on_draw(window, self) for widget in self._widgets: widget.on_draw(window, self)

View file

@ -1 +1 @@
from .Scene import Scene from .BaseScene import BaseScene

View file

@ -1,30 +0,0 @@
from typing import Optional, TYPE_CHECKING
import pyglet
from source.gui.scene.base import Scene
if TYPE_CHECKING:
from source.gui.window import Window
class FPSCounterScene(Scene):
"""
This scene represent a simple FPS Counter.
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fps_display: Optional[pyglet.window.FPSDisplay] = None
def on_window_added(self, window: "Window"):
super().on_window_added(window)
# the fps display need to be defined here because it is the moment where the window is first accessible
self.fps_display = pyglet.window.FPSDisplay(window=window)
def on_draw(self, window: "Window") -> None:
super().on_draw(window)
self.fps_display.draw()

View file

@ -0,0 +1,20 @@
from typing import TYPE_CHECKING
import pyglet.window
from source.gui.scene.base import BaseScene
if TYPE_CHECKING:
from source.gui.window import Window
class FPSScene(BaseScene):
def __init__(self):
super().__init__()
self._fps_display = None
def on_window_added(self, window: "Window"):
self._fps_display = pyglet.window.FPSDisplay(window)
def on_draw(self, window: "Window"):
self._fps_display.draw()

View file

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

View file

@ -1,30 +0,0 @@
import pyglet
from source.gui.scene.base import Scene
from source.gui.widget import Button
class ButtonScene(Scene):
"""
This is a simple scene to test Button and their adaptable size
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
button_normal_image = pyglet.image.load("./assets/test_button_normal.png")
button_hover_image = pyglet.image.load("./assets/test_button_hover.png")
button_click_image = pyglet.image.load("./assets/test_button_clicking.png")
for x in range(10):
for y in range(10):
self.add_widget(Button(
x * 0.1, y * 0.1, 0.1, 0.1,
text=f"{x}-{y}",
font_size=10,
on_release=lambda self, *a, **b: print(self, "clicked !"),
normal_image=button_normal_image,
hover_image=button_hover_image,
click_image=button_click_image
))

View file

@ -1,59 +1,55 @@
from datetime import datetime, timedelta from typing import TYPE_CHECKING
import pyglet import pyglet
from source.gui.scene.base import Scene from source.gui.scene.base import BaseScene
from source.gui.widget.Button import Button
from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from source.gui.window import Window from source.gui.window import Window
class HelloWorldScene(Scene): class HelloWorldScene(BaseScene):
""" def __init__(self):
This scene is a simple Hello World. super().__init__()
You can type anything with the keyboard or use backspace to remove characters. self.button_atlas = None
The text is centered on the screen. self.sprite_batch = None
""" self.label_batch = None
def __init__(self, *args, **kwargs): def on_window_added(self, window: "Window") -> None:
super().__init__(*args, **kwargs) 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.label = pyglet.text.Label( self.button_atlas = pyglet.image.atlas.TextureAtlas()
"Hello World !", normal_region = self.button_atlas.add(normal_texture)
anchor_x="center", hover_region = self.button_atlas.add(hover_texture)
anchor_y="center" click_region = self.button_atlas.add(click_texture)
)
# remember the cooldown for the backspace button self.sprite_batch = pyglet.graphics.Batch()
self._hold_backspace_last_call: datetime = datetime.now() self.label_batch = pyglet.graphics.Batch()
def on_draw(self, window: "Window") -> None: for x in range(10):
for y in range(10):
self.add_widget(Button(
x=x*0.1, y=y*0.1, width=0.1, height=0.1,
normal_texture=normal_region,
hover_texture=hover_region,
click_texture=click_region,
label_text=f"TEST TEST CENTERING {x}.{y}",
label_multiline=True,
label_batch=self.label_batch,
sprite_batch=self.sprite_batch,
))
super().on_window_added(window)
def on_draw(self, window: "Window"):
super().on_draw(window) super().on_draw(window)
self.label.draw() self.sprite_batch.draw()
self.label_batch.draw()
def on_resize(self, window: "Window", width: int, height: int) -> None:
super().on_resize(window, width, height)
self.label.x = width // 2
self.label.y = height // 2
def on_text(self, window: "Window", char: str):
super().on_text(window, char)
self.label.text += char
def on_key_held(self, window: "Window", dt: float, symbol: int, modifiers: int):
super().on_key_held(window, dt, symbol, modifiers)
if symbol == pyglet.window.key.BACKSPACE:
# add a cooldown of 0.1 second on the backspace key
now = datetime.now()
if self._hold_backspace_last_call + timedelta(seconds=0.1) < now:
self._hold_backspace_last_call = now
self.label.text = self.label.text[:-1]

View file

@ -1,2 +1 @@
from .HelloWorldScene import HelloWorldScene from .HelloWorldScene import HelloWorldScene
from .ButtonScene import ButtonScene

View file

@ -2,12 +2,15 @@ import pyglet.sprite
class Sprite(pyglet.sprite.Sprite): class Sprite(pyglet.sprite.Sprite):
def __init__(self, *args, **kwargs): def __init__(self, *args, width: int = None, height: int = None, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self._orig_width: int = self.width self._orig_width: int = self.width
self._orig_height: int = self.height self._orig_height: int = self.height
if width is not None: self.width = width
if height is not None: self.height = height
@pyglet.sprite.Sprite.width.setter @pyglet.sprite.Sprite.width.setter
def width(self, width: int): def width(self, width: int):
self.scale_x = width / self._orig_width self.scale_x = width / self._orig_width

View file

@ -1,155 +1,179 @@
from typing import Callable, Optional, TYPE_CHECKING from typing import TYPE_CHECKING, Callable
import pyglet import pyglet.image
from source.gui.sprite import Sprite from source.gui.sprite import Sprite
from source.gui.widget.base import AdaptativeWidget from source.gui.widget.base import BaseAdaptativeWidget
from source.type import Percentage from source.type import Percentage
from source.utils import in_bbox from source.utils import in_bbox
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Self
from source.gui.scene.base import Scene
from source.gui.window import Window from source.gui.window import Window
from source.gui.scene.base import BaseScene
class Button(AdaptativeWidget): class Button(BaseAdaptativeWidget):
def __init__(self, def __init__(self,
# position
x: int | Percentage, x: int | Percentage,
y: int | Percentage, y: int | Percentage,
width: int | Percentage, width: int | Percentage,
height: int | Percentage, height: int | Percentage,
normal_image: pyglet.image.AbstractImage, # background
hover_image: pyglet.image.AbstractImage = None, normal_texture: pyglet.image.AbstractImage,
click_image: pyglet.image.AbstractImage = None, hover_texture: pyglet.image.AbstractImage = None,
click_texture: pyglet.image.AbstractImage = None,
on_press: Optional[Callable] = None, # label
on_release: Optional[Callable] = None, label_text: str = "",
label_font_name: str = None,
label_font_size: int = None,
label_bold: bool = False,
label_italic: bool = False,
label_stretch: bool = False,
label_color: tuple[int, int, int, int] = (255, 255, 255, 255),
label_align: str = "center",
label_multiline: bool = False,
label_dpi: int = None,
label_rotation: int = 0,
*args, **kwargs # callback function
on_press: Callable = None,
on_release: Callable = None,
# batch
label_batch: pyglet.graphics.Batch = None,
sprite_batch: pyglet.graphics.Batch = None,
# group
label_group: pyglet.graphics.Group = None,
sprite_group: pyglet.graphics.Group = None,
): ):
super().__init__(x, y, width, height) super().__init__(x, y, width, height)
# TODO: use batch ? self._normal_texture = normal_texture
# TODO: use texture bin and animation to simplify the image handling ? self._hover_texture = hover_texture
# TODO: font_size dynamic sizing too ? self._click_texture = click_texture
# the label used for the text self.on_press = on_press
self._label = pyglet.text.Label( self.on_release = on_release
anchor_x="center", anchor_y="center",
*args, **kwargs self._label = None
self._label_kwargs = {
"text": label_text,
"font_name": label_font_name,
"font_size": label_font_size,
"bold": label_bold,
"italic": label_italic,
"stretch": label_stretch,
"color": label_color,
"align": label_align,
"multiline": label_multiline,
"dpi": label_dpi,
"rotation": label_rotation,
"batch": label_batch,
"group": label_group,
}
self._sprite = None
self._sprite_kwargs = {
"batch": sprite_batch,
"group": sprite_group,
}
self._hover = False
self._click = False
# button update
@property
def hover(self) -> bool:
return self._hover
@hover.setter
def hover(self, hover: bool) -> None:
self._hover = hover
self._update_sprite()
@property
def click(self) -> bool:
return self._click
@click.setter
def click(self, click: bool) -> None:
self._click = click
self._update_sprite()
@property
def background_texture(self) -> pyglet.image.AbstractImage:
return (
self._click_texture if self._click else
self._hover_texture if self._hover else
self._normal_texture
) )
# the button background def _update_sprite(self):
self._hovering = False self._sprite.image = self.background_texture
self._clicking = False
self._sprite = Sprite(normal_image) def update_size(self):
self._normal_image = normal_image
self._hover_image = hover_image if hover_image is not None else normal_image
self._click_image = click_image if click_image is not None else normal_image
# the event when the button is clicked
self.on_press: Optional[Callable[["Self", "Window", "Scene", int, int, int, int], None]] = on_press
self.on_release: Optional[Callable[["Self", "Window", "Scene", int, int, int, int], None]] = on_release
# update the size of the widget
self._update_size()
# function
def _update_sprite(self) -> None:
self._sprite.image = self.background_image
def _update_size(self):
self._sprite.x = self.x self._sprite.x = self.x
self._sprite.y = self.y self._sprite.y = self.y
self._sprite.width = self.width self._sprite.width = self.width
self._sprite.height = self.height self._sprite.height = self.height
self._label.x = self.x + (self.width // 2) self._label.x = self.x + self.width / 2
self._label.y = self.y + (self.height // 2) self._label.y = self.y + self.height / 2
self._label.width = self.width
# button getter and setter
@property
def hovering(self) -> bool: return self._hovering
@hovering.setter
def hovering(self, hovering: bool):
self._hovering = hovering
self._update_sprite()
@property
def clicking(self) -> bool:
return self._clicking
@clicking.setter
def clicking(self, clicking: bool):
self._clicking = clicking
self._update_sprite()
@property
def background_image(self) -> pyglet.image.AbstractImage:
return (
self._click_image if self._clicking
else self._hover_image if self._hovering
else self._normal_image
)
# label getter and setter
@AdaptativeWidget.x.setter
def x(self, x: int):
super().x = x
self._update_size()
@AdaptativeWidget.y.setter
def y(self, y: int):
super().y = y
self._update_size()
@AdaptativeWidget.width.setter
def width(self, width: int):
super().width = width
self._update_size()
@AdaptativeWidget.height.setter
def height(self, height: int):
super().height = height
self._update_size()
# event # event
def on_mouse_press(self, window: "Window", scene: "Scene", x: int, y: int, button: int, modifiers: int): def on_window_added(self, window: "Window", scene: "BaseScene"):
super().on_window_added(window, scene)
self._label = pyglet.text.Label(
x=self.x + self.width / 2,
y=self.y + self.height / 2,
width=self.width,
anchor_x="center", anchor_y="center",
**self._label_kwargs
)
self._sprite = Sprite(
self._normal_texture,
x=self.x, y=self.y,
width=self.width, height=self.height,
**self._sprite_kwargs
)
def on_mouse_motion(self, window: "Window", scene: "BaseScene", x: int, y: int, dx: int, dy: int):
self.hover = in_bbox((x, y), self.bbox)
def on_mouse_press(self, window: "Window", scene: "BaseScene", x: int, y: int, button: int, modifiers: int):
if not in_bbox((x, y), self.bbox): return if not in_bbox((x, y), self.bbox): return
self.clicking = True self.click = True
if self.on_press is not None: if self.on_press is not None:
self.on_press(self, window, scene, x, y, button, modifiers) self.on_press(self, window, scene, x, y, button, modifiers)
def on_mouse_release(self, window: "Window", scene: "Scene", x: int, y: int, button: int, modifiers: int): def on_mouse_release(self, window: "Window", scene: "BaseScene", x: int, y: int, button: int, modifiers: int):
old_clicking = self.clicking old_click = self.click
self.clicking = False self.click = False
if not in_bbox((x, y), self.bbox): return if not in_bbox((x, y), self.bbox): return
# if this button was the one hovered when the click was pressed # if this button was the one hovered when the click was pressed
if old_clicking and self.on_release is not None: if old_click and self.on_release is not None:
self.on_release(self, window, scene, x, y, button, modifiers) self.on_release(self, window, scene, x, y, button, modifiers)
def on_mouse_motion(self, window: "Window", scene: "Scene", x: int, y: int, dx: int, dy: int):
self.hovering = in_bbox((x, y), self.bbox)
def on_draw(self, window: "Window", scene: "Scene"): # TODO: on_resize seem really slow
if self._sprite is not None: self._sprite.draw() # TODO: make the percentage dynamic or non dynamic
self._label.draw()
def on_resize(self, window: "Window", scene: "Scene", width: int, height: int):
super().on_resize(window, scene, width, height)
self._update_size()

View file

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

View file

@ -1,75 +0,0 @@
from source.gui.widget.base import Widget
from typing import TYPE_CHECKING
from source.type import BBox, Percentage
if TYPE_CHECKING:
from source.gui.window import Window
from source.gui.scene.base import Scene
class AdaptativeWidget(Widget):
"""
Similar to a normal Widget
If the x, y, width or height is a float (representing a percentage), it will change depending on the window size
"""
def __init__(self,
x: int | Percentage,
y: int | Percentage,
width: int | Percentage,
height: int | Percentage,
):
self._x = x
self._y = y
self._width = width
self._height = height
self._window_width = 0
self._window_height = 0
# getter / setter
@property
def x(self) -> int:
return self._x if isinstance(self._x, int) else self._x * self._window_width
@x.setter
def x(self, x: int | Percentage) -> None:
self._x = x
@property
def y(self) -> int:
return self._y if isinstance(self._y, int) else self._y * self._window_height
@y.setter
def y(self, y: int | Percentage) -> None:
self._y = y
@property
def width(self) -> int:
return self._width if isinstance(self._width, int) else self._width * self._window_width
@width.setter
def width(self, width: int | Percentage) -> None:
self._width = width
@property
def height(self) -> int:
return self._height if isinstance(self._height, int) else self._height * self._window_height
@height.setter
def height(self, height: int | Percentage) -> None:
self._height = height
@property
def bbox(self) -> BBox:
return self.x, self.y, self.x + self.width, self.y + self.height
def on_resize(self, window: "Window", scene: "Scene", width: int, height: int):
self._window_width = width
self._window_height = height

View file

@ -0,0 +1,44 @@
from typing import Optional
from source.gui.widget.base import BaseBoxWidget
from source.type import Percentage
class BaseAdaptativeWidget(BaseBoxWidget):
def __init__(self, x: int | Percentage, y: int | Percentage, width: int | Percentage, height: int | Percentage):
super().__init__(x, y, width, height)
self._window_width: Optional[int] = None
self._window_height: Optional[int] = None
# getter / setter
def on_window_added(self, window: "Window", scene: "BaseScene"):
self._window_width = window.width
self._window_height = window.height
@property
def x(self) -> int:
return self._x if isinstance(self._x, int) else self._x * self._window_width
@property
def y(self) -> int:
return self._y if isinstance(self._y, int) else self._y * self._window_height
@property
def width(self) -> int:
return self._width if isinstance(self._width, int) else self._width * self._window_width
@property
def height(self) -> int:
return self._height if isinstance(self._height, int) else self._height * self._window_height
# event
def update_size(self): pass
def on_resize(self, window: "Window", scene: "BaseScene", width: int, height: int):
self._window_width = width
self._window_height = height
self.update_size()

View file

@ -0,0 +1,48 @@
from source.gui.widget.base import BaseWidget
from source.type import BBox
class BaseBoxWidget(BaseWidget):
def __init__(self, x: int, y: int, width: int, height: int):
self._x = x
self._y = y
self._width = width
self._height = height
# getter and setter, allow subclass to react when one of these value is changed
@property
def x(self):
return self._x
@x.setter
def x(self, x: int) -> None:
self._x = x
@property
def y(self):
return self._y
@y.setter
def y(self, y: int) -> None:
self._y = y
@property
def width(self):
return self._width
@width.setter
def width(self, width: int) -> None:
self._width = width
@property
def height(self):
return self._height
@height.setter
def height(self, height: int) -> None:
self._height = height
@property
def bbox(self) -> BBox:
return self.x, self.y, self.x + self.width, self.y + self.height

View file

@ -0,0 +1,50 @@
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from source.gui.scene.base import BaseScene
from source.gui.window import Window
class BaseWidget:
"""
This class represent a widget that can be attached to a Scene.
It can be used to create a button, a label, ...
"""
# widget event
def on_scene_added(self, scene: "BaseScene"): pass
def on_scene_removed(self, scene: "BaseScene"): pass
# scene event
def on_window_added(self, window: "Window", scene: "BaseScene"): pass
def on_window_removed(self, window: "Window", scene: "BaseScene"): pass
# global event
def on_draw(self, window: "Window", scene: "BaseScene"): pass
def on_resize(self, window: "Window", scene: "BaseScene", width: int, height: int): pass
def on_hide(self, window: "Window", scene: "BaseScene"): pass
def on_show(self, window: "Window", scene: "BaseScene"): pass
def on_close(self, window: "Window", scene: "BaseScene"): pass
def on_expose(self, window: "Window", scene: "BaseScene"): pass
def on_activate(self, window: "Window", scene: "BaseScene"): pass
def on_deactivate(self, window: "Window", scene: "BaseScene"): pass
def on_text(self, window: "Window", scene: "BaseScene", char: str): pass
def on_move(self, window: "Window", scene: "BaseScene", x: int, y: int): pass
def on_context_lost(self, window: "Window", scene: "BaseScene"): pass
def on_context_state_lost(self, window: "Window", scene: "BaseScene"): pass
def on_key_press(self, window: "Window", scene: "BaseScene", symbol: int, modifiers: int): pass
def on_key_release(self, window: "Window", scene: "BaseScene", symbol: int, modifiers: int): pass
def on_key_held(self, window: "Window", scene: "BaseScene", dt: float, symbol: int, modifiers: int): pass
def on_mouse_enter(self, window: "Window", scene: "BaseScene", x: int, y: int): pass
def on_mouse_leave(self, window: "Window", scene: "BaseScene", x: int, y: int): pass
def on_text_motion(self, window: "Window", scene: "BaseScene", motion: int): pass
def on_text_motion_select(self, window: "Window", scene: "BaseScene", motion: int): pass
def on_mouse_motion(self, window: "Window", scene: "BaseScene", x: int, y: int, dx: int, dy: int): pass
def on_mouse_press(self, window: "Window", scene: "BaseScene", x: int, y: int, button: int, modifiers: int): pass
def on_mouse_release(self, window: "Window", scene: "BaseScene", x: int, y: int, button: int, modifiers: int): pass
def on_mouse_drag(self, window: "Window", scene: "BaseScene", x: int, y: int, dx: int, dy: int, buttons: int, modifiers: int): pass
def on_mouse_scroll(self, window: "Window", scene: "BaseScene", x: int, y: int, scroll_x: float, scroll_y: float): pass

View file

@ -1,50 +0,0 @@
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from source.gui.scene.base import Scene
from source.gui.window import Window
class Widget:
"""
This class represent a widget that can be attached to a Scene.
It can be used to create a button, a label, ...
"""
# widget event
def on_scene_added(self, scene: "Scene"): pass
def on_scene_removed(self, scene: "Scene"): pass
# scene event
def on_window_added(self, window: "Window", scene: "Scene"): pass
def on_window_removed(self, window: "Window", scene: "Scene"): pass
# global event
def on_draw(self, window: "Window", scene: "Scene"): pass
def on_resize(self, window: "Window", scene: "Scene", width: int, height: int): pass
def on_hide(self, window: "Window", scene: "Scene"): pass
def on_show(self, window: "Window", scene: "Scene"): pass
def on_close(self, window: "Window", scene: "Scene"): pass
def on_expose(self, window: "Window", scene: "Scene"): pass
def on_activate(self, window: "Window", scene: "Scene"): pass
def on_deactivate(self, window: "Window", scene: "Scene"): pass
def on_text(self, window: "Window", scene: "Scene", char: str): pass
def on_move(self, window: "Window", scene: "Scene", x: int, y: int): pass
def on_context_lost(self, window: "Window", scene: "Scene"): pass
def on_context_state_lost(self, window: "Window", scene: "Scene"): pass
def on_key_press(self, window: "Window", scene: "Scene", symbol: int, modifiers: int): pass
def on_key_release(self, window: "Window", scene: "Scene", symbol: int, modifiers: int): pass
def on_key_held(self, window: "Window", scene: "Scene", dt: float, symbol: int, modifiers: int): pass
def on_mouse_enter(self, window: "Window", scene: "Scene", x: int, y: int): pass
def on_mouse_leave(self, window: "Window", scene: "Scene", x: int, y: int): pass
def on_text_motion(self, window: "Window", scene: "Scene", motion: int): pass
def on_text_motion_select(self, window: "Window", scene: "Scene", motion: int): pass
def on_mouse_motion(self, window: "Window", scene: "Scene", x: int, y: int, dx: int, dy: int): pass
def on_mouse_press(self, window: "Window", scene: "Scene", x: int, y: int, button: int, modifiers: int): pass
def on_mouse_release(self, window: "Window", scene: "Scene", x: int, y: int, button: int, modifiers: int): pass
def on_mouse_drag(self, window: "Window", scene: "Scene", x: int, y: int, dx: int, dy: int, buttons: int, modifiers: int): pass
def on_mouse_scroll(self, window: "Window", scene: "Scene", x: int, y: int, scroll_x: float, scroll_y: float): pass

View file

@ -1,2 +1,3 @@
from .Widget import Widget from .BaseWidget import BaseWidget
from .AdaptativeWidget import AdaptativeWidget from .BaseBoxWidget import BaseBoxWidget
from .BaseAdaptativeWidget import BaseAdaptativeWidget

View file

@ -1,76 +1,47 @@
from typing import Optional, Callable, TYPE_CHECKING from typing import TYPE_CHECKING, Callable
import pyglet.window import pyglet.window
if TYPE_CHECKING: if TYPE_CHECKING:
from source.gui.scene.base import Scene from source.gui.scene import BaseScene
class Window(pyglet.window.Window): # NOQA - pycharm think pyglet window is abstract class Window(pyglet.window.Window): # NOQA
""" """
This class represent a Window based on the pyglet Window object. Same as the original pyglet class, but allow to set a scene.
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[list["Scene"]] = None, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*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 # scene
self.keys = pyglet.window.key.KeyStateHandler() self._scenes = []
self.push_handlers(self.keys)
# 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[(int, int), Callable] = {}
# scene methods # keys event handler
self.keys_handler = pyglet.window.key.KeyStateHandler()
self.push_handlers(self.keys_handler)
def set_scene(self, *scenes: "Scene") -> None: # scene system
"""
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: def set_scene(self, *scenes: "BaseScene"):
""" self.clear()
Clear all the scenes of the window self.add_scene(*scenes)
"""
for scene in self._scenes: scene.on_window_removed(self)
self._scenes.clear()
def add_scene(self, *scenes: "Scene", priority: int = 0) -> None: def add_scene(self, *scenes: "BaseScene", priority: int = 0):
"""
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: for scene in scenes:
self._scenes.insert(priority, scene) self._scenes.insert(priority, scene)
scene.on_window_added(self) scene.on_window_added(self)
def remove_scene(self, *scenes: "Scene") -> None: def remove_scene(self, *scenes: "BaseScene"):
"""
Remove a scene from the window
:param scenes: the scene to remove
"""
for scene in scenes: for scene in scenes:
scene.on_window_removed(self) scene.on_window_removed(self)
self._scenes.remove(scene) self._scenes.remove(scene)
def get_scenes(self): def clear_scene(self):
""" self.remove_scene(*self._scenes)
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 : # NOTE: it is too difficult to refactor all the event because :
# - There is no event "on_any_event" or equivalent # - There is no event "on_any_event" or equivalent

View file

@ -1,2 +1 @@
from .Window import Window from .Window import Window

View file

@ -8,7 +8,7 @@ from source.core.enums import Orientation, BombState
from source.core.error import InvalidBoatPosition, InvalidBombPosition, PositionAlreadyShot from source.core.error import InvalidBoatPosition, InvalidBombPosition, PositionAlreadyShot
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect(("127.0.0.1", 7878)) s.connect(("127.0.0.1", 52321))
print(f"[Client] Connecté avec {s}") print(f"[Client] Connecté avec {s}")

View file

@ -8,7 +8,8 @@ from source.core.enums import Orientation, BombState
from source.core.error import InvalidBoatPosition, InvalidBombPosition, PositionAlreadyShot from source.core.error import InvalidBoatPosition, InvalidBombPosition, PositionAlreadyShot
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(("127.0.0.1", 7878)) host = socket.gethostname()
s.bind((host, 52321))
s.listen() s.listen()
conn, addr = s.accept() conn, addr = s.accept()

View file

@ -1,3 +1,3 @@
Point2D = tuple[int, int] Point2D = tuple[int, int]
BBox = tuple[int, int, int, int] BBox = tuple[int, int, int, int]
Percentage = float # a percentage is a value between 0 and 1 Percentage = float