recoded the structure of the gui
This commit is contained in:
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 |
19
main.pyw
19
main.pyw
|
@ -1,19 +1,14 @@
|
|||
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.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()
|
||||
fps_counter_scene = FPSCounterScene()
|
||||
# Start the event loop
|
||||
pyglet.app.run(interval=0)
|
||||
|
||||
window.add_scene(button_scene, fps_counter_scene)
|
||||
|
||||
# Lance la fenêtre
|
||||
window.set_visible(True)
|
||||
pyglet.app.run()
|
||||
|
|
|
@ -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)
|
||||
|
|
@ -1 +0,0 @@
|
|||
from source.gui.scene.test.HelloWorldScene import HelloWorldScene
|
|
@ -1,37 +1,33 @@
|
|||
from pyglet.event import EventDispatcher
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from source.gui.widget.base import Widget
|
||||
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.
|
||||
|
||||
The scene can represent anything like the main menu, the game, the
|
||||
options' menu, the multiplayer menu, ...
|
||||
A scene that can be attached to a window
|
||||
"""
|
||||
|
||||
def __init__(self, widgets: list["Widget"] = None):
|
||||
self._widgets: list["Widget"] = []
|
||||
if widgets is not None: self.add_widget(*widgets)
|
||||
def __init__(self):
|
||||
self._widgets: list["BaseWidget"] = []
|
||||
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:
|
||||
self._widgets.insert(priority, widget)
|
||||
widget.on_scene_added(self)
|
||||
|
||||
def remove_widget(self, *widgets: "Widget") -> None:
|
||||
def remove_widget(self, *widgets: "BaseWidget") -> None:
|
||||
for widget in widgets:
|
||||
widget.on_scene_removed(self)
|
||||
self._widgets.remove(widget)
|
||||
|
||||
def clear_widget(self) -> None:
|
||||
for widget in self._widgets: widget.on_scene_removed(self)
|
||||
self._widgets.clear()
|
||||
self.remove_widget(*self._widgets)
|
||||
|
||||
# scene event
|
||||
|
||||
|
@ -41,7 +37,19 @@ class Scene(EventDispatcher):
|
|||
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 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"):
|
||||
for widget in self._widgets: widget.on_draw(window, self)
|
|
@ -1 +1 @@
|
|||
from .Scene import Scene
|
||||
from .BaseScene import BaseScene
|
||||
|
|
|
@ -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()
|
20
source/gui/scene/debug/FPSScene.py
Normal file
20
source/gui/scene/debug/FPSScene.py
Normal 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()
|
|
@ -1 +1 @@
|
|||
from .FPSCounterScene import FPSCounterScene
|
||||
from .FPSScene import FPSScene
|
||||
|
|
|
@ -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
|
||||
))
|
|
@ -1,59 +1,55 @@
|
|||
from datetime import datetime, timedelta
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import pyglet
|
||||
|
||||
from source.gui.scene.base import Scene
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from source.gui.scene.base import BaseScene
|
||||
from source.gui.widget.Button import Button
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from source.gui.window import Window
|
||||
|
||||
|
||||
class HelloWorldScene(Scene):
|
||||
"""
|
||||
This scene is a simple Hello World.
|
||||
class HelloWorldScene(BaseScene):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
You can type anything with the keyboard or use backspace to remove characters.
|
||||
The text is centered on the screen.
|
||||
"""
|
||||
self.button_atlas = None
|
||||
self.sprite_batch = None
|
||||
self.label_batch = None
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
def on_window_added(self, window: "Window") -> None:
|
||||
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(
|
||||
"Hello World !",
|
||||
anchor_x="center",
|
||||
anchor_y="center"
|
||||
)
|
||||
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)
|
||||
|
||||
# remember the cooldown for the backspace button
|
||||
self._hold_backspace_last_call: datetime = datetime.now()
|
||||
self.sprite_batch = pyglet.graphics.Batch()
|
||||
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)
|
||||
|
||||
self.label.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]
|
||||
self.sprite_batch.draw()
|
||||
self.label_batch.draw()
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
from .HelloWorldScene import HelloWorldScene
|
||||
from .ButtonScene import ButtonScene
|
||||
|
|
|
@ -2,12 +2,15 @@ import pyglet.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)
|
||||
|
||||
self._orig_width: int = self.width
|
||||
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
|
||||
def width(self, width: int):
|
||||
self.scale_x = width / self._orig_width
|
||||
|
|
|
@ -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.widget.base import AdaptativeWidget
|
||||
from source.gui.widget.base import BaseAdaptativeWidget
|
||||
from source.type import Percentage
|
||||
from source.utils import in_bbox
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Self
|
||||
from source.gui.scene.base import Scene
|
||||
from source.gui.window import Window
|
||||
from source.gui.scene.base import BaseScene
|
||||
|
||||
|
||||
class Button(AdaptativeWidget):
|
||||
class Button(BaseAdaptativeWidget):
|
||||
def __init__(self,
|
||||
|
||||
# position
|
||||
x: int | Percentage,
|
||||
y: int | Percentage,
|
||||
width: int | Percentage,
|
||||
height: int | Percentage,
|
||||
|
||||
normal_image: pyglet.image.AbstractImage,
|
||||
hover_image: pyglet.image.AbstractImage = None,
|
||||
click_image: pyglet.image.AbstractImage = None,
|
||||
# background
|
||||
normal_texture: pyglet.image.AbstractImage,
|
||||
hover_texture: pyglet.image.AbstractImage = None,
|
||||
click_texture: pyglet.image.AbstractImage = None,
|
||||
|
||||
on_press: Optional[Callable] = None,
|
||||
on_release: Optional[Callable] = None,
|
||||
# label
|
||||
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)
|
||||
|
||||
# TODO: use batch ?
|
||||
# TODO: use texture bin and animation to simplify the image handling ?
|
||||
# TODO: font_size dynamic sizing too ?
|
||||
self._normal_texture = normal_texture
|
||||
self._hover_texture = hover_texture
|
||||
self._click_texture = click_texture
|
||||
|
||||
# the label used for the text
|
||||
self._label = pyglet.text.Label(
|
||||
anchor_x="center", anchor_y="center",
|
||||
*args, **kwargs
|
||||
self.on_press = on_press
|
||||
self.on_release = on_release
|
||||
|
||||
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
|
||||
self._hovering = False
|
||||
self._clicking = False
|
||||
def _update_sprite(self):
|
||||
self._sprite.image = self.background_texture
|
||||
|
||||
self._sprite = Sprite(normal_image)
|
||||
|
||||
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):
|
||||
def update_size(self):
|
||||
self._sprite.x = self.x
|
||||
self._sprite.y = self.y
|
||||
self._sprite.width = self.width
|
||||
self._sprite.height = self.height
|
||||
|
||||
self._label.x = self.x + (self.width // 2)
|
||||
self._label.y = self.y + (self.height // 2)
|
||||
|
||||
# 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()
|
||||
self._label.x = self.x + self.width / 2
|
||||
self._label.y = self.y + self.height / 2
|
||||
self._label.width = self.width
|
||||
|
||||
# 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
|
||||
|
||||
self.clicking = True
|
||||
self.click = True
|
||||
|
||||
if self.on_press is not None:
|
||||
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):
|
||||
old_clicking = self.clicking
|
||||
self.clicking = False
|
||||
def on_mouse_release(self, window: "Window", scene: "BaseScene", x: int, y: int, button: int, modifiers: int):
|
||||
old_click = self.click
|
||||
self.click = False
|
||||
|
||||
if not in_bbox((x, y), self.bbox): return
|
||||
|
||||
# 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)
|
||||
|
||||
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"):
|
||||
if self._sprite is not None: self._sprite.draw()
|
||||
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()
|
||||
# TODO: on_resize seem really slow
|
||||
# TODO: make the percentage dynamic or non dynamic
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
from .Button import Button
|
|
@ -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
|
44
source/gui/widget/base/BaseAdaptativeWidget.py
Normal file
44
source/gui/widget/base/BaseAdaptativeWidget.py
Normal 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()
|
48
source/gui/widget/base/BaseBoxWidget.py
Normal file
48
source/gui/widget/base/BaseBoxWidget.py
Normal 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
|
50
source/gui/widget/base/BaseWidget.py
Normal file
50
source/gui/widget/base/BaseWidget.py
Normal 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
|
|
@ -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
|
|
@ -1,2 +1,3 @@
|
|||
from .Widget import Widget
|
||||
from .AdaptativeWidget import AdaptativeWidget
|
||||
from .BaseWidget import BaseWidget
|
||||
from .BaseBoxWidget import BaseBoxWidget
|
||||
from .BaseAdaptativeWidget import BaseAdaptativeWidget
|
||||
|
|
|
@ -1,76 +1,47 @@
|
|||
from typing import Optional, Callable, TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Callable
|
||||
|
||||
import pyglet.window
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
Same as the original pyglet class, but allow to set a scene.
|
||||
"""
|
||||
|
||||
def __init__(self, scenes: Optional[list["Scene"]] = None, *args, **kwargs):
|
||||
def __init__(self, *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)
|
||||
# scene
|
||||
self._scenes = []
|
||||
|
||||
# a dictionary linking a key pressed to the corresponding event function
|
||||
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:
|
||||
"""
|
||||
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)
|
||||
# scene system
|
||||
|
||||
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 set_scene(self, *scenes: "BaseScene"):
|
||||
self.clear()
|
||||
self.add_scene(*scenes)
|
||||
|
||||
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
|
||||
"""
|
||||
def add_scene(self, *scenes: "BaseScene", priority: int = 0):
|
||||
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
|
||||
"""
|
||||
def remove_scene(self, *scenes: "BaseScene"):
|
||||
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
|
||||
def clear_scene(self):
|
||||
self.remove_scene(*self._scenes)
|
||||
|
||||
# NOTE: it is too difficult to refactor all the event because :
|
||||
# - There is no event "on_any_event" or equivalent
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
from .Window import Window
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ from source.core.enums import Orientation, BombState
|
|||
from source.core.error import InvalidBoatPosition, InvalidBombPosition, PositionAlreadyShot
|
||||
|
||||
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}")
|
||||
|
||||
|
|
|
@ -8,7 +8,8 @@ from source.core.enums import Orientation, BombState
|
|||
from source.core.error import InvalidBoatPosition, InvalidBombPosition, PositionAlreadyShot
|
||||
|
||||
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()
|
||||
conn, addr = s.accept()
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
Point2D = tuple[int, int]
|
||||
BBox = tuple[int, int, int, int]
|
||||
Percentage = float # a percentage is a value between 0 and 1
|
||||
Percentage = float
|
||||
|
|
Loading…
Reference in a new issue