restructured some part of the code, implemented Label and Button
This commit is contained in:
parent
7cc837b68e
commit
920914ad8c
27 changed files with 446 additions and 179 deletions
BIN
assets/image/input/inputbox.png
Normal file
BIN
assets/image/input/inputbox.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.9 KiB |
8
main.pyw
8
main.pyw
|
@ -1,13 +1,15 @@
|
||||||
import pyglet
|
import pyglet
|
||||||
|
|
||||||
from source.gui.scene.debug import FPSScene
|
from source.gui.scene.debug import FPSAbstractScene
|
||||||
from source.gui.scene.test import HelloWorldScene
|
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=False)
|
||||||
window.add_scene(HelloWorldScene(), FPSScene())
|
window.add_scene(TestButtonScene())
|
||||||
|
# window.add_scene(TestLabelScene())
|
||||||
|
window.add_scene(FPSAbstractScene())
|
||||||
|
|
||||||
# Start the event loop
|
# Start the event loop
|
||||||
pyglet.app.run(interval=0)
|
pyglet.app.run(interval=0)
|
||||||
|
|
8
source/gui/README.md
Normal file
8
source/gui/README.md
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
Ce module est une "surcouche" pour le module pyglet afin d'implémenter quelques éléments qui
|
||||||
|
ne sont pas déjà disponible nativement dans cette librairie, tel que :
|
||||||
|
|
||||||
|
- Les scènes (attachable à une fenêtre afin de changer rapidement de menu / d'interface)
|
||||||
|
- Les widgets (attachable à des scènes afin de rajouter des boutons, des textes, ...)
|
||||||
|
|
||||||
|
Ces éléments permettent de plus facilement gérer le redimentionnement de la fenêtre tout en
|
||||||
|
restant suffisament rapide pour ne pas causer de problème de ralentissement.
|
|
@ -1,27 +1,29 @@
|
||||||
|
from abc import ABC
|
||||||
from typing import TYPE_CHECKING, Optional
|
from typing import TYPE_CHECKING, Optional
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from source.gui.window import Window
|
from source.gui.window import Window
|
||||||
from source.gui.widget.base import BaseWidget
|
from source.gui.widget.abc import AbstractWidget
|
||||||
|
|
||||||
|
|
||||||
class BaseScene:
|
class AbstractScene(ABC):
|
||||||
"""
|
"""
|
||||||
A scene that can be attached to a window
|
An abstract scene that can be attached to a window.
|
||||||
|
Can be used to create a menu, an overlay, ...
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._widgets: list["BaseWidget"] = []
|
self._widgets: list["AbstractWidget"] = [] # the lists of the widgets in the scene
|
||||||
self._window: Optional["Window"] = None
|
self._window: Optional["Window"] = None # the window where the scene is attached
|
||||||
|
|
||||||
# widget
|
# widget
|
||||||
|
|
||||||
def add_widget(self, *widgets: "BaseWidget", priority: int = 0) -> None:
|
def add_widget(self, *widgets: "AbstractWidget", 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: "BaseWidget") -> None:
|
def remove_widget(self, *widgets: "AbstractWidget") -> 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)
|
||||||
|
@ -44,12 +46,12 @@ class BaseScene:
|
||||||
return self._window
|
return self._window
|
||||||
|
|
||||||
@window.setter
|
@window.setter
|
||||||
def window(self, window: "Window"):
|
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)
|
if self._window is not None: self.on_window_removed(self._window)
|
||||||
self._window = window
|
self._window = window
|
||||||
if self._window is not None: self.on_window_added(self._window)
|
if self._window is not None: self.on_window_added(self._window)
|
||||||
|
|
||||||
# event
|
# all the events of the window are directly available here or in the widgets
|
||||||
|
|
||||||
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)
|
1
source/gui/scene/abc/__init__.py
Normal file
1
source/gui/scene/abc/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
from .AbstractScene import AbstractScene
|
|
@ -1 +0,0 @@
|
||||||
from .BaseScene import BaseScene
|
|
|
@ -1,17 +1,21 @@
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING, Optional
|
||||||
|
|
||||||
import pyglet.window
|
import pyglet.window
|
||||||
|
|
||||||
from source.gui.scene.base import BaseScene
|
from source.gui.scene.abc import AbstractScene
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from source.gui.window import Window
|
from source.gui.window import Window
|
||||||
|
|
||||||
|
|
||||||
class FPSScene(BaseScene):
|
class FPSAbstractScene(AbstractScene):
|
||||||
|
"""
|
||||||
|
A base scene that can be used as an overlay to display the FPS
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._fps_display = None
|
self._fps_display: Optional[pyglet.window.FPSDisplay] = None
|
||||||
|
|
||||||
def on_window_added(self, window: "Window"):
|
def on_window_added(self, window: "Window"):
|
||||||
self._fps_display = pyglet.window.FPSDisplay(window)
|
self._fps_display = pyglet.window.FPSDisplay(window)
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
from .FPSScene import FPSScene
|
from .FPSScene import FPSAbstractScene
|
||||||
|
|
|
@ -2,19 +2,23 @@ from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import pyglet
|
import pyglet
|
||||||
|
|
||||||
from source.gui.scene.base import BaseScene
|
from source.gui.scene.abc import AbstractScene
|
||||||
from source.gui.widget.Button import Button
|
from source.gui.widget import Button
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from source.gui.window import Window
|
from source.gui.window import Window
|
||||||
|
|
||||||
|
|
||||||
class HelloWorldScene(BaseScene):
|
class TestButtonScene(AbstractScene):
|
||||||
|
"""
|
||||||
|
A scene used to test the Button widget
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.button_atlas = None
|
self.button_atlas = None
|
||||||
self.sprite_batch = None
|
self.background_batch = None
|
||||||
self.label_batch = None
|
self.label_batch = None
|
||||||
|
|
||||||
def on_window_added(self, window: "Window") -> None:
|
def on_window_added(self, window: "Window") -> None:
|
||||||
|
@ -27,7 +31,7 @@ class HelloWorldScene(BaseScene):
|
||||||
hover_region = self.button_atlas.add(hover_texture)
|
hover_region = self.button_atlas.add(hover_texture)
|
||||||
click_region = self.button_atlas.add(click_texture)
|
click_region = self.button_atlas.add(click_texture)
|
||||||
|
|
||||||
self.sprite_batch = pyglet.graphics.Batch()
|
self.background_batch = pyglet.graphics.Batch()
|
||||||
self.label_batch = pyglet.graphics.Batch()
|
self.label_batch = pyglet.graphics.Batch()
|
||||||
|
|
||||||
for x in range(10):
|
for x in range(10):
|
||||||
|
@ -43,7 +47,7 @@ class HelloWorldScene(BaseScene):
|
||||||
label_multiline=True,
|
label_multiline=True,
|
||||||
|
|
||||||
label_batch=self.label_batch,
|
label_batch=self.label_batch,
|
||||||
sprite_batch=self.sprite_batch,
|
background_batch=self.background_batch,
|
||||||
))
|
))
|
||||||
|
|
||||||
super().on_window_added(window)
|
super().on_window_added(window)
|
||||||
|
@ -51,5 +55,5 @@ class HelloWorldScene(BaseScene):
|
||||||
def on_draw(self, window: "Window"):
|
def on_draw(self, window: "Window"):
|
||||||
super().on_draw(window)
|
super().on_draw(window)
|
||||||
|
|
||||||
self.sprite_batch.draw()
|
self.background_batch.draw()
|
||||||
self.label_batch.draw()
|
self.label_batch.draw()
|
40
source/gui/scene/test/TestLabelScene.py
Normal file
40
source/gui/scene/test/TestLabelScene.py
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
import pyglet
|
||||||
|
|
||||||
|
from source.gui.scene.abc import AbstractScene
|
||||||
|
from source.gui.widget import Label
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from source.gui.window import Window
|
||||||
|
|
||||||
|
|
||||||
|
class TestLabelScene(AbstractScene):
|
||||||
|
"""
|
||||||
|
A scene used to test the Label widget
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.label_batch = None
|
||||||
|
|
||||||
|
def on_window_added(self, window: "Window") -> None:
|
||||||
|
self.label_batch = pyglet.graphics.Batch()
|
||||||
|
|
||||||
|
for x in range(10):
|
||||||
|
for y in range(10):
|
||||||
|
self.add_widget(Label(
|
||||||
|
x=x*0.1, y=y*0.1, width=0.1, height=0.1,
|
||||||
|
|
||||||
|
text=f"{x}.{y}",
|
||||||
|
|
||||||
|
label_batch=self.label_batch,
|
||||||
|
))
|
||||||
|
|
||||||
|
super().on_window_added(window)
|
||||||
|
|
||||||
|
def on_draw(self, window: "Window"):
|
||||||
|
super().on_draw(window)
|
||||||
|
|
||||||
|
self.label_batch.draw()
|
|
@ -1 +1,3 @@
|
||||||
from .HelloWorldScene import HelloWorldScene
|
from .TestButtonScene import TestButtonScene
|
||||||
|
from .TestLabelScene import TestLabelScene
|
||||||
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
from .Sprite import Sprite
|
|
|
@ -1,18 +1,18 @@
|
||||||
from typing import TYPE_CHECKING, Callable
|
from typing import TYPE_CHECKING, Callable, Optional
|
||||||
|
|
||||||
import pyglet.image
|
import pyglet.image
|
||||||
|
|
||||||
from source.gui.sprite import Sprite
|
from source.gui.widget.base import Sprite, Label
|
||||||
from source.gui.widget.base import BaseAdaptativeWidget
|
from source.gui.widget.abc import AbstractResizableWidget
|
||||||
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 source.gui.window import Window
|
from source.gui.window import Window
|
||||||
from source.gui.scene.base import BaseScene
|
from source.gui.scene.abc import AbstractScene
|
||||||
|
|
||||||
|
|
||||||
class Button(BaseAdaptativeWidget):
|
class Button(AbstractResizableWidget):
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
|
|
||||||
# position
|
# position
|
||||||
|
@ -45,11 +45,11 @@ class Button(BaseAdaptativeWidget):
|
||||||
|
|
||||||
# batch
|
# batch
|
||||||
label_batch: pyglet.graphics.Batch = None,
|
label_batch: pyglet.graphics.Batch = None,
|
||||||
sprite_batch: pyglet.graphics.Batch = None,
|
background_batch: pyglet.graphics.Batch = None,
|
||||||
|
|
||||||
# group
|
# group
|
||||||
label_group: pyglet.graphics.Group = None,
|
label_group: pyglet.graphics.Group = None,
|
||||||
sprite_group: pyglet.graphics.Group = None,
|
background_group: pyglet.graphics.Group = None,
|
||||||
):
|
):
|
||||||
|
|
||||||
super().__init__(x, y, width, height)
|
super().__init__(x, y, width, height)
|
||||||
|
@ -61,7 +61,7 @@ class Button(BaseAdaptativeWidget):
|
||||||
self.on_press = on_press
|
self.on_press = on_press
|
||||||
self.on_release = on_release
|
self.on_release = on_release
|
||||||
|
|
||||||
self._label = None
|
self._label: Optional[Label] = None
|
||||||
self._label_kwargs = {
|
self._label_kwargs = {
|
||||||
"text": label_text,
|
"text": label_text,
|
||||||
"font_name": label_font_name,
|
"font_name": label_font_name,
|
||||||
|
@ -79,15 +79,32 @@ class Button(BaseAdaptativeWidget):
|
||||||
"group": label_group,
|
"group": label_group,
|
||||||
}
|
}
|
||||||
|
|
||||||
self._sprite = None
|
self._background: Optional[Sprite] = None
|
||||||
self._sprite_kwargs = {
|
self._background_kwargs = {
|
||||||
"batch": sprite_batch,
|
"batch": background_batch,
|
||||||
"group": sprite_group,
|
"group": background_group,
|
||||||
}
|
}
|
||||||
|
|
||||||
self._hover = False
|
self._hover = False
|
||||||
self._click = False
|
self._click = False
|
||||||
|
|
||||||
|
def on_window_added(self, window: "Window", scene: "AbstractScene"):
|
||||||
|
super().on_window_added(window, scene)
|
||||||
|
|
||||||
|
self._label = 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._background = Sprite(
|
||||||
|
self._normal_texture,
|
||||||
|
x=self.x, y=self.y, width=self.width, height=self.height,
|
||||||
|
|
||||||
|
**self._background_kwargs
|
||||||
|
)
|
||||||
|
|
||||||
# button update
|
# button update
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -97,7 +114,7 @@ class Button(BaseAdaptativeWidget):
|
||||||
@hover.setter
|
@hover.setter
|
||||||
def hover(self, hover: bool) -> None:
|
def hover(self, hover: bool) -> None:
|
||||||
self._hover = hover
|
self._hover = hover
|
||||||
self._update_sprite()
|
self.update_sprite()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def click(self) -> bool:
|
def click(self) -> bool:
|
||||||
|
@ -106,7 +123,7 @@ class Button(BaseAdaptativeWidget):
|
||||||
@click.setter
|
@click.setter
|
||||||
def click(self, click: bool) -> None:
|
def click(self, click: bool) -> None:
|
||||||
self._click = click
|
self._click = click
|
||||||
self._update_sprite()
|
self.update_sprite()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def background_texture(self) -> pyglet.image.AbstractImage:
|
def background_texture(self) -> pyglet.image.AbstractImage:
|
||||||
|
@ -116,47 +133,19 @@ class Button(BaseAdaptativeWidget):
|
||||||
self._normal_texture
|
self._normal_texture
|
||||||
)
|
)
|
||||||
|
|
||||||
def _update_sprite(self):
|
def update_sprite(self):
|
||||||
self._sprite.image = self.background_texture
|
self._background.image = self.background_texture
|
||||||
|
|
||||||
def update_size(self):
|
def update_size(self):
|
||||||
self._sprite.x = self.x
|
self._background.update_size(self.x, self.y, self.width, self.height)
|
||||||
self._sprite.y = self.y
|
self._label.update_size(self.x + self.width // 2, self.y + self.height // 2, self.width)
|
||||||
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
|
|
||||||
self._label.width = self.width
|
|
||||||
|
|
||||||
# event
|
# event
|
||||||
|
|
||||||
def on_window_added(self, window: "Window", scene: "BaseScene"):
|
def on_mouse_motion(self, window: "Window", scene: "AbstractScene", x: int, y: int, dx: int, dy: int):
|
||||||
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)
|
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):
|
def on_mouse_press(self, window: "Window", scene: "AbstractScene", 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.click = True
|
self.click = True
|
||||||
|
@ -164,7 +153,7 @@ class Button(BaseAdaptativeWidget):
|
||||||
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: "BaseScene", x: int, y: int, button: int, modifiers: int):
|
def on_mouse_release(self, window: "Window", scene: "AbstractScene", x: int, y: int, button: int, modifiers: int):
|
||||||
old_click = self.click
|
old_click = self.click
|
||||||
self.click = False
|
self.click = False
|
||||||
|
|
||||||
|
|
5
source/gui/widget/Image.py
Normal file
5
source/gui/widget/Image.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
from source.gui.widget.abc import AbstractResizableWidget
|
||||||
|
|
||||||
|
|
||||||
|
class Image(AbstractResizableWidget):
|
||||||
|
...
|
74
source/gui/widget/Input.py
Normal file
74
source/gui/widget/Input.py
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
import pyglet.text
|
||||||
|
|
||||||
|
from source.gui.widget.abc import AbstractResizableWidget
|
||||||
|
from source.type import Percentage
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from source.gui.window import Window
|
||||||
|
from source.gui.scene.abc import AbstractScene
|
||||||
|
|
||||||
|
|
||||||
|
class Input(AbstractResizableWidget):
|
||||||
|
def __init__(self,
|
||||||
|
|
||||||
|
# position
|
||||||
|
x: int | Percentage,
|
||||||
|
y: int | Percentage,
|
||||||
|
width: int | Percentage,
|
||||||
|
height: int | Percentage,
|
||||||
|
|
||||||
|
# background
|
||||||
|
texture: pyglet.image.AbstractImage,
|
||||||
|
|
||||||
|
# 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,
|
||||||
|
|
||||||
|
# batch
|
||||||
|
label_batch: pyglet.graphics.Batch = None,
|
||||||
|
background_batch: pyglet.graphics.Batch = None,
|
||||||
|
|
||||||
|
# group
|
||||||
|
label_group: pyglet.graphics.Group = None,
|
||||||
|
background_group: pyglet.graphics.Group = None,
|
||||||
|
):
|
||||||
|
|
||||||
|
super().__init__(x, y, width, height)
|
||||||
|
|
||||||
|
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._background_kwargs = {
|
||||||
|
"img": texture,
|
||||||
|
"batch": background_batch,
|
||||||
|
"group": background_group,
|
||||||
|
}
|
||||||
|
|
||||||
|
def on_window_added(self, window: "Window", scene: "AbstractScene"):
|
||||||
|
...
|
102
source/gui/widget/Label.py
Normal file
102
source/gui/widget/Label.py
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
import pyglet.text
|
||||||
|
|
||||||
|
from source.gui.widget.base import Sprite
|
||||||
|
from source.gui.widget.abc import AbstractResizableWidget
|
||||||
|
from source.type import Percentage
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from source.gui.window import Window
|
||||||
|
from source.gui.scene.abc import AbstractScene
|
||||||
|
|
||||||
|
|
||||||
|
class Label(AbstractResizableWidget):
|
||||||
|
def __init__(self,
|
||||||
|
|
||||||
|
# position
|
||||||
|
x: int | Percentage,
|
||||||
|
y: int | Percentage,
|
||||||
|
width: int | Percentage = None,
|
||||||
|
height: int | Percentage = None,
|
||||||
|
|
||||||
|
# label
|
||||||
|
text: str = "",
|
||||||
|
font_name: str = None,
|
||||||
|
font_size: int = None,
|
||||||
|
bold: bool = False,
|
||||||
|
italic: bool = False,
|
||||||
|
stretch: bool = False,
|
||||||
|
color: tuple[int, int, int, int] = (255, 255, 255, 255),
|
||||||
|
align: str = "center",
|
||||||
|
multiline: bool = False,
|
||||||
|
dpi: int = None,
|
||||||
|
rotation: int = 0,
|
||||||
|
|
||||||
|
# background
|
||||||
|
texture: pyglet.image.AbstractImage = None,
|
||||||
|
|
||||||
|
# batch
|
||||||
|
label_batch: pyglet.graphics.Batch = None,
|
||||||
|
background_batch: pyglet.graphics.Batch = None,
|
||||||
|
|
||||||
|
# group
|
||||||
|
label_group: pyglet.graphics.Group = None,
|
||||||
|
background_group: pyglet.graphics.Group = None
|
||||||
|
):
|
||||||
|
|
||||||
|
super().__init__(x, y, width, height)
|
||||||
|
|
||||||
|
self._label = None
|
||||||
|
self._label_kwargs = {
|
||||||
|
"text": text,
|
||||||
|
"font_name": font_name,
|
||||||
|
"font_size": font_size,
|
||||||
|
"bold": bold,
|
||||||
|
"italic": italic,
|
||||||
|
"stretch": stretch,
|
||||||
|
"color": color,
|
||||||
|
"align": align,
|
||||||
|
"multiline": multiline,
|
||||||
|
"dpi": dpi,
|
||||||
|
"rotation": rotation,
|
||||||
|
|
||||||
|
"batch": label_batch,
|
||||||
|
"group": label_group,
|
||||||
|
}
|
||||||
|
|
||||||
|
if texture is not None and width is None or height is None:
|
||||||
|
raise ValueError("You need to set a width and a height to create a Label with a background !")
|
||||||
|
|
||||||
|
self._background = None
|
||||||
|
self._background_kwargs = {
|
||||||
|
"img": texture,
|
||||||
|
"batch": background_batch,
|
||||||
|
"group": background_group,
|
||||||
|
} if texture is not None else None
|
||||||
|
|
||||||
|
def on_window_added(self, window: "Window", scene: "AbstractScene"):
|
||||||
|
super().on_window_added(window, scene)
|
||||||
|
|
||||||
|
self._label = pyglet.text.Label(
|
||||||
|
x=self.x, y=self.y, width=self.width, height=self.height,
|
||||||
|
**self._label_kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
if self._background_kwargs is not None:
|
||||||
|
self._background = Sprite(
|
||||||
|
x=self.x, y=self.y, width=self.width, height=self.height,
|
||||||
|
**self._background_kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
def update_size(self):
|
||||||
|
self._label.x = self.x
|
||||||
|
self._label.y = self.y
|
||||||
|
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
|
|
@ -0,0 +1,4 @@
|
||||||
|
from .Label import Label
|
||||||
|
from .Input import Input
|
||||||
|
from .Button import Button
|
||||||
|
from .Image import Image
|
|
@ -1,8 +1,10 @@
|
||||||
from source.gui.widget.base import BaseWidget
|
from abc import ABC
|
||||||
|
|
||||||
|
from source.gui.widget.abc import AbstractWidget
|
||||||
from source.type import BBox
|
from source.type import BBox
|
||||||
|
|
||||||
|
|
||||||
class BaseBoxWidget(BaseWidget):
|
class AbstractBoxWidget(AbstractWidget, ABC):
|
||||||
def __init__(self, x: int, y: int, width: int, height: int):
|
def __init__(self, x: int, y: int, width: int, height: int):
|
||||||
self._x = x
|
self._x = x
|
||||||
self._y = y
|
self._y = y
|
50
source/gui/widget/abc/AbstractResizableWidget.py
Normal file
50
source/gui/widget/abc/AbstractResizableWidget.py
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
from abc import ABC
|
||||||
|
from typing import Optional, TYPE_CHECKING
|
||||||
|
|
||||||
|
from source.gui.widget.abc import AbstractBoxWidget
|
||||||
|
from source.type import Percentage
|
||||||
|
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from source.gui.window import Window
|
||||||
|
from source.gui.scene.abc import AbstractScene
|
||||||
|
|
||||||
|
|
||||||
|
class AbstractResizableWidget(AbstractBoxWidget, ABC):
|
||||||
|
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: "AbstractScene"):
|
||||||
|
self._window_width = window.width
|
||||||
|
self._window_height = window.height
|
||||||
|
|
||||||
|
@property
|
||||||
|
def x(self) -> int:
|
||||||
|
return self._x * self._window_width if isinstance(self._x, Percentage) else self._x
|
||||||
|
|
||||||
|
@property
|
||||||
|
def y(self) -> int:
|
||||||
|
return self._y * self._window_height if isinstance(self._y, Percentage) else self._y
|
||||||
|
|
||||||
|
@property
|
||||||
|
def width(self) -> int:
|
||||||
|
return self._width * self._window_width if isinstance(self._width, Percentage) else self._width
|
||||||
|
|
||||||
|
@property
|
||||||
|
def height(self) -> int:
|
||||||
|
return self._height * self._window_height if isinstance(self._height, Percentage) else self._height
|
||||||
|
|
||||||
|
# event
|
||||||
|
|
||||||
|
def update_size(self): pass
|
||||||
|
|
||||||
|
def on_resize(self, window: "Window", scene: "AbstractScene", width: int, height: int):
|
||||||
|
self._window_width = width
|
||||||
|
self._window_height = height
|
||||||
|
|
||||||
|
self.update_size()
|
51
source/gui/widget/abc/AbstractWidget.py
Normal file
51
source/gui/widget/abc/AbstractWidget.py
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
from abc import ABC
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from source.gui.scene.abc import AbstractScene
|
||||||
|
from source.gui.window import Window
|
||||||
|
|
||||||
|
|
||||||
|
class AbstractWidget(ABC):
|
||||||
|
"""
|
||||||
|
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: "AbstractScene"): pass
|
||||||
|
def on_scene_removed(self, scene: "AbstractScene"): pass
|
||||||
|
|
||||||
|
# scene event
|
||||||
|
|
||||||
|
def on_window_added(self, window: "Window", scene: "AbstractScene"): pass
|
||||||
|
def on_window_removed(self, window: "Window", scene: "AbstractScene"): pass
|
||||||
|
|
||||||
|
# global event
|
||||||
|
|
||||||
|
def on_draw(self, window: "Window", scene: "AbstractScene"): pass
|
||||||
|
def on_resize(self, window: "Window", scene: "AbstractScene", width: int, height: int): pass
|
||||||
|
def on_hide(self, window: "Window", scene: "AbstractScene"): pass
|
||||||
|
def on_show(self, window: "Window", scene: "AbstractScene"): pass
|
||||||
|
def on_close(self, window: "Window", scene: "AbstractScene"): pass
|
||||||
|
def on_expose(self, window: "Window", scene: "AbstractScene"): pass
|
||||||
|
def on_activate(self, window: "Window", scene: "AbstractScene"): pass
|
||||||
|
def on_deactivate(self, window: "Window", scene: "AbstractScene"): pass
|
||||||
|
def on_text(self, window: "Window", scene: "AbstractScene", char: str): pass
|
||||||
|
def on_move(self, window: "Window", scene: "AbstractScene", x: int, y: int): pass
|
||||||
|
def on_context_lost(self, window: "Window", scene: "AbstractScene"): pass
|
||||||
|
def on_context_state_lost(self, window: "Window", scene: "AbstractScene"): pass
|
||||||
|
def on_key_press(self, window: "Window", scene: "AbstractScene", symbol: int, modifiers: int): pass
|
||||||
|
def on_key_release(self, window: "Window", scene: "AbstractScene", symbol: int, modifiers: int): pass
|
||||||
|
def on_key_held(self, window: "Window", scene: "AbstractScene", dt: float, symbol: int, modifiers: int): pass
|
||||||
|
def on_mouse_enter(self, window: "Window", scene: "AbstractScene", x: int, y: int): pass
|
||||||
|
def on_mouse_leave(self, window: "Window", scene: "AbstractScene", x: int, y: int): pass
|
||||||
|
def on_text_motion(self, window: "Window", scene: "AbstractScene", motion: int): pass
|
||||||
|
def on_text_motion_select(self, window: "Window", scene: "AbstractScene", motion: int): pass
|
||||||
|
def on_mouse_motion(self, window: "Window", scene: "AbstractScene", x: int, y: int, dx: int, dy: int): pass
|
||||||
|
def on_mouse_press(self, window: "Window", scene: "AbstractScene", x: int, y: int, button: int, modifiers: int): pass
|
||||||
|
def on_mouse_release(self, window: "Window", scene: "AbstractScene", x: int, y: int, button: int, modifiers: int): pass
|
||||||
|
def on_mouse_drag(self, window: "Window", scene: "AbstractScene", x: int, y: int, dx: int, dy: int, buttons: int, modifiers: int): pass
|
||||||
|
def on_mouse_scroll(self, window: "Window", scene: "AbstractScene", x: int, y: int, scroll_x: float, scroll_y: float): pass
|
3
source/gui/widget/abc/__init__.py
Normal file
3
source/gui/widget/abc/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from .AbstractWidget import AbstractWidget
|
||||||
|
from .AbstractBoxWidget import AbstractBoxWidget
|
||||||
|
from .AbstractResizableWidget import AbstractResizableWidget
|
|
@ -1,44 +0,0 @@
|
||||||
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()
|
|
|
@ -1,50 +0,0 @@
|
||||||
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
|
|
11
source/gui/widget/base/Label.py
Normal file
11
source/gui/widget/base/Label.py
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import pyglet.text
|
||||||
|
|
||||||
|
|
||||||
|
class Label(pyglet.text.Label):
|
||||||
|
def update_size(self, x: int, y: int, width: Optional[int] = None, height: Optional[int] = None):
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
|
@ -11,6 +11,8 @@ class Sprite(pyglet.sprite.Sprite):
|
||||||
if width is not None: self.width = width
|
if width is not None: self.width = width
|
||||||
if height is not None: self.height = height
|
if height is not None: self.height = height
|
||||||
|
|
||||||
|
# property
|
||||||
|
|
||||||
@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
|
||||||
|
@ -18,3 +20,11 @@ class Sprite(pyglet.sprite.Sprite):
|
||||||
@pyglet.sprite.Sprite.height.setter
|
@pyglet.sprite.Sprite.height.setter
|
||||||
def height(self, height: int):
|
def height(self, height: int):
|
||||||
self.scale_y = height / self._orig_height
|
self.scale_y = height / self._orig_height
|
||||||
|
|
||||||
|
# other event
|
||||||
|
|
||||||
|
def update_size(self, x: int, y: int, width: int, height: int):
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
|
@ -1,3 +1,2 @@
|
||||||
from .BaseWidget import BaseWidget
|
from .Label import Label
|
||||||
from .BaseBoxWidget import BaseBoxWidget
|
from .Sprite import Sprite
|
||||||
from .BaseAdaptativeWidget import BaseAdaptativeWidget
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Callable
|
||||||
import pyglet.window
|
import pyglet.window
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from source.gui.scene import BaseScene
|
from source.gui.scene.abc import AbstractScene
|
||||||
|
|
||||||
|
|
||||||
class Window(pyglet.window.Window): # NOQA
|
class Window(pyglet.window.Window): # NOQA
|
||||||
|
@ -26,16 +26,16 @@ class Window(pyglet.window.Window): # NOQA
|
||||||
|
|
||||||
# scene system
|
# scene system
|
||||||
|
|
||||||
def set_scene(self, *scenes: "BaseScene"):
|
def set_scene(self, *scenes: "AbstractScene"):
|
||||||
self.clear()
|
self.clear()
|
||||||
self.add_scene(*scenes)
|
self.add_scene(*scenes)
|
||||||
|
|
||||||
def add_scene(self, *scenes: "BaseScene", priority: int = 0):
|
def add_scene(self, *scenes: "AbstractScene", priority: int = 0):
|
||||||
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: "BaseScene"):
|
def remove_scene(self, *scenes: "AbstractScene"):
|
||||||
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)
|
||||||
|
|
Loading…
Reference in a new issue