From 920914ad8cacf076ee98b61aa5445d88d4833419 Mon Sep 17 00:00:00 2001 From: Faraphel Date: Tue, 17 Jan 2023 22:24:03 +0100 Subject: [PATCH] restructured some part of the code, implemented Label and Button --- assets/image/input/inputbox.png | Bin 0 -> 8095 bytes main.pyw | 8 +- source/gui/README.md | 8 ++ .../BaseScene.py => abc/AbstractScene.py} | 20 ++-- source/gui/scene/abc/__init__.py | 1 + source/gui/scene/base/__init__.py | 1 - source/gui/scene/debug/FPSScene.py | 12 ++- source/gui/scene/debug/__init__.py | 2 +- ...{HelloWorldScene.py => TestButtonScene.py} | 18 ++-- source/gui/scene/test/TestLabelScene.py | 40 +++++++ source/gui/scene/test/__init__.py | 4 +- source/gui/sprite/__init__.py | 1 - source/gui/widget/Button.py | 87 +++++++-------- source/gui/widget/Image.py | 5 + source/gui/widget/Input.py | 74 +++++++++++++ source/gui/widget/Label.py | 102 ++++++++++++++++++ source/gui/widget/__init__.py | 4 + .../AbstractBoxWidget.py} | 6 +- .../gui/widget/abc/AbstractResizableWidget.py | 50 +++++++++ source/gui/widget/abc/AbstractWidget.py | 51 +++++++++ source/gui/widget/abc/__init__.py | 3 + .../gui/widget/base/BaseAdaptativeWidget.py | 44 -------- source/gui/widget/base/BaseWidget.py | 50 --------- source/gui/widget/base/Label.py | 11 ++ source/gui/{sprite => widget/base}/Sprite.py | 10 ++ source/gui/widget/base/__init__.py | 5 +- source/gui/window/Window.py | 8 +- 27 files changed, 446 insertions(+), 179 deletions(-) create mode 100644 assets/image/input/inputbox.png create mode 100644 source/gui/README.md rename source/gui/scene/{base/BaseScene.py => abc/AbstractScene.py} (86%) create mode 100644 source/gui/scene/abc/__init__.py delete mode 100644 source/gui/scene/base/__init__.py rename source/gui/scene/test/{HelloWorldScene.py => TestButtonScene.py} (80%) create mode 100644 source/gui/scene/test/TestLabelScene.py delete mode 100644 source/gui/sprite/__init__.py create mode 100644 source/gui/widget/Image.py create mode 100644 source/gui/widget/Input.py create mode 100644 source/gui/widget/Label.py rename source/gui/widget/{base/BaseBoxWidget.py => abc/AbstractBoxWidget.py} (89%) create mode 100644 source/gui/widget/abc/AbstractResizableWidget.py create mode 100644 source/gui/widget/abc/AbstractWidget.py create mode 100644 source/gui/widget/abc/__init__.py delete mode 100644 source/gui/widget/base/BaseAdaptativeWidget.py delete mode 100644 source/gui/widget/base/BaseWidget.py create mode 100644 source/gui/widget/base/Label.py rename source/gui/{sprite => widget/base}/Sprite.py (75%) diff --git a/assets/image/input/inputbox.png b/assets/image/input/inputbox.png new file mode 100644 index 0000000000000000000000000000000000000000..c031d935f052878fc669aed914af18f4678211ff GIT binary patch literal 8095 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 None: + def add_widget(self, *widgets: "AbstractWidget", priority: int = 0) -> None: for widget in widgets: self._widgets.insert(priority, widget) widget.on_scene_added(self) - def remove_widget(self, *widgets: "BaseWidget") -> None: + def remove_widget(self, *widgets: "AbstractWidget") -> None: for widget in widgets: widget.on_scene_removed(self) self._widgets.remove(widget) @@ -44,12 +46,12 @@ class BaseScene: return self._window @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) self._window = 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"): for widget in self._widgets: widget.on_draw(window, self) diff --git a/source/gui/scene/abc/__init__.py b/source/gui/scene/abc/__init__.py new file mode 100644 index 0000000..67c0fcc --- /dev/null +++ b/source/gui/scene/abc/__init__.py @@ -0,0 +1 @@ +from .AbstractScene import AbstractScene diff --git a/source/gui/scene/base/__init__.py b/source/gui/scene/base/__init__.py deleted file mode 100644 index ab09621..0000000 --- a/source/gui/scene/base/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .BaseScene import BaseScene diff --git a/source/gui/scene/debug/FPSScene.py b/source/gui/scene/debug/FPSScene.py index 906793c..aed723f 100644 --- a/source/gui/scene/debug/FPSScene.py +++ b/source/gui/scene/debug/FPSScene.py @@ -1,17 +1,21 @@ -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional import pyglet.window -from source.gui.scene.base import BaseScene +from source.gui.scene.abc import AbstractScene if TYPE_CHECKING: 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): super().__init__() - self._fps_display = None + self._fps_display: Optional[pyglet.window.FPSDisplay] = None def on_window_added(self, window: "Window"): self._fps_display = pyglet.window.FPSDisplay(window) diff --git a/source/gui/scene/debug/__init__.py b/source/gui/scene/debug/__init__.py index a8d0b54..b5f6215 100644 --- a/source/gui/scene/debug/__init__.py +++ b/source/gui/scene/debug/__init__.py @@ -1 +1 @@ -from .FPSScene import FPSScene +from .FPSScene import FPSAbstractScene diff --git a/source/gui/scene/test/HelloWorldScene.py b/source/gui/scene/test/TestButtonScene.py similarity index 80% rename from source/gui/scene/test/HelloWorldScene.py rename to source/gui/scene/test/TestButtonScene.py index af6c0fb..7029a00 100644 --- a/source/gui/scene/test/HelloWorldScene.py +++ b/source/gui/scene/test/TestButtonScene.py @@ -2,19 +2,23 @@ from typing import TYPE_CHECKING import pyglet -from source.gui.scene.base import BaseScene -from source.gui.widget.Button import Button +from source.gui.scene.abc import AbstractScene +from source.gui.widget import Button if TYPE_CHECKING: from source.gui.window import Window -class HelloWorldScene(BaseScene): +class TestButtonScene(AbstractScene): + """ + A scene used to test the Button widget + """ + def __init__(self): super().__init__() self.button_atlas = None - self.sprite_batch = None + self.background_batch = None self.label_batch = None def on_window_added(self, window: "Window") -> None: @@ -27,7 +31,7 @@ class HelloWorldScene(BaseScene): hover_region = self.button_atlas.add(hover_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() for x in range(10): @@ -43,7 +47,7 @@ class HelloWorldScene(BaseScene): label_multiline=True, label_batch=self.label_batch, - sprite_batch=self.sprite_batch, + background_batch=self.background_batch, )) super().on_window_added(window) @@ -51,5 +55,5 @@ class HelloWorldScene(BaseScene): def on_draw(self, window: "Window"): super().on_draw(window) - self.sprite_batch.draw() + self.background_batch.draw() self.label_batch.draw() diff --git a/source/gui/scene/test/TestLabelScene.py b/source/gui/scene/test/TestLabelScene.py new file mode 100644 index 0000000..6b7ad1b --- /dev/null +++ b/source/gui/scene/test/TestLabelScene.py @@ -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() diff --git a/source/gui/scene/test/__init__.py b/source/gui/scene/test/__init__.py index 7d9c684..eb8343c 100644 --- a/source/gui/scene/test/__init__.py +++ b/source/gui/scene/test/__init__.py @@ -1 +1,3 @@ -from .HelloWorldScene import HelloWorldScene +from .TestButtonScene import TestButtonScene +from .TestLabelScene import TestLabelScene + diff --git a/source/gui/sprite/__init__.py b/source/gui/sprite/__init__.py deleted file mode 100644 index d2ab302..0000000 --- a/source/gui/sprite/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .Sprite import Sprite diff --git a/source/gui/widget/Button.py b/source/gui/widget/Button.py index da11499..45ce227 100644 --- a/source/gui/widget/Button.py +++ b/source/gui/widget/Button.py @@ -1,18 +1,18 @@ -from typing import TYPE_CHECKING, Callable +from typing import TYPE_CHECKING, Callable, Optional import pyglet.image -from source.gui.sprite import Sprite -from source.gui.widget.base import BaseAdaptativeWidget +from source.gui.widget.base import Sprite, Label +from source.gui.widget.abc import AbstractResizableWidget from source.type import Percentage from source.utils import in_bbox if TYPE_CHECKING: 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, # position @@ -45,11 +45,11 @@ class Button(BaseAdaptativeWidget): # batch label_batch: pyglet.graphics.Batch = None, - sprite_batch: pyglet.graphics.Batch = None, + background_batch: pyglet.graphics.Batch = None, # group label_group: pyglet.graphics.Group = None, - sprite_group: pyglet.graphics.Group = None, + background_group: pyglet.graphics.Group = None, ): super().__init__(x, y, width, height) @@ -61,7 +61,7 @@ class Button(BaseAdaptativeWidget): self.on_press = on_press self.on_release = on_release - self._label = None + self._label: Optional[Label] = None self._label_kwargs = { "text": label_text, "font_name": label_font_name, @@ -79,15 +79,32 @@ class Button(BaseAdaptativeWidget): "group": label_group, } - self._sprite = None - self._sprite_kwargs = { - "batch": sprite_batch, - "group": sprite_group, + self._background: Optional[Sprite] = None + self._background_kwargs = { + "batch": background_batch, + "group": background_group, } self._hover = 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 @property @@ -97,7 +114,7 @@ class Button(BaseAdaptativeWidget): @hover.setter def hover(self, hover: bool) -> None: self._hover = hover - self._update_sprite() + self.update_sprite() @property def click(self) -> bool: @@ -106,7 +123,7 @@ class Button(BaseAdaptativeWidget): @click.setter def click(self, click: bool) -> None: self._click = click - self._update_sprite() + self.update_sprite() @property def background_texture(self) -> pyglet.image.AbstractImage: @@ -116,47 +133,19 @@ class Button(BaseAdaptativeWidget): self._normal_texture ) - def _update_sprite(self): - self._sprite.image = self.background_texture + def update_sprite(self): + self._background.image = self.background_texture 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 - self._label.width = self.width + self._background.update_size(self.x, self.y, self.width, self.height) + self._label.update_size(self.x + self.width // 2, self.y + self.height // 2, self.width) # event - 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): + def on_mouse_motion(self, window: "Window", scene: "AbstractScene", 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): + 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 self.click = True @@ -164,7 +153,7 @@ class Button(BaseAdaptativeWidget): 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: "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 self.click = False diff --git a/source/gui/widget/Image.py b/source/gui/widget/Image.py new file mode 100644 index 0000000..aa917d7 --- /dev/null +++ b/source/gui/widget/Image.py @@ -0,0 +1,5 @@ +from source.gui.widget.abc import AbstractResizableWidget + + +class Image(AbstractResizableWidget): + ... diff --git a/source/gui/widget/Input.py b/source/gui/widget/Input.py new file mode 100644 index 0000000..34cefa4 --- /dev/null +++ b/source/gui/widget/Input.py @@ -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"): + ... diff --git a/source/gui/widget/Label.py b/source/gui/widget/Label.py new file mode 100644 index 0000000..870de6f --- /dev/null +++ b/source/gui/widget/Label.py @@ -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 diff --git a/source/gui/widget/__init__.py b/source/gui/widget/__init__.py index e69de29..3c4cb60 100644 --- a/source/gui/widget/__init__.py +++ b/source/gui/widget/__init__.py @@ -0,0 +1,4 @@ +from .Label import Label +from .Input import Input +from .Button import Button +from .Image import Image diff --git a/source/gui/widget/base/BaseBoxWidget.py b/source/gui/widget/abc/AbstractBoxWidget.py similarity index 89% rename from source/gui/widget/base/BaseBoxWidget.py rename to source/gui/widget/abc/AbstractBoxWidget.py index 7441630..37f90cd 100644 --- a/source/gui/widget/base/BaseBoxWidget.py +++ b/source/gui/widget/abc/AbstractBoxWidget.py @@ -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 -class BaseBoxWidget(BaseWidget): +class AbstractBoxWidget(AbstractWidget, ABC): def __init__(self, x: int, y: int, width: int, height: int): self._x = x self._y = y diff --git a/source/gui/widget/abc/AbstractResizableWidget.py b/source/gui/widget/abc/AbstractResizableWidget.py new file mode 100644 index 0000000..adbbec9 --- /dev/null +++ b/source/gui/widget/abc/AbstractResizableWidget.py @@ -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() diff --git a/source/gui/widget/abc/AbstractWidget.py b/source/gui/widget/abc/AbstractWidget.py new file mode 100644 index 0000000..29fd6e9 --- /dev/null +++ b/source/gui/widget/abc/AbstractWidget.py @@ -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 diff --git a/source/gui/widget/abc/__init__.py b/source/gui/widget/abc/__init__.py new file mode 100644 index 0000000..77304a3 --- /dev/null +++ b/source/gui/widget/abc/__init__.py @@ -0,0 +1,3 @@ +from .AbstractWidget import AbstractWidget +from .AbstractBoxWidget import AbstractBoxWidget +from .AbstractResizableWidget import AbstractResizableWidget diff --git a/source/gui/widget/base/BaseAdaptativeWidget.py b/source/gui/widget/base/BaseAdaptativeWidget.py deleted file mode 100644 index fc77a16..0000000 --- a/source/gui/widget/base/BaseAdaptativeWidget.py +++ /dev/null @@ -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() diff --git a/source/gui/widget/base/BaseWidget.py b/source/gui/widget/base/BaseWidget.py deleted file mode 100644 index 48a9c3f..0000000 --- a/source/gui/widget/base/BaseWidget.py +++ /dev/null @@ -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 diff --git a/source/gui/widget/base/Label.py b/source/gui/widget/base/Label.py new file mode 100644 index 0000000..d9895e0 --- /dev/null +++ b/source/gui/widget/base/Label.py @@ -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 diff --git a/source/gui/sprite/Sprite.py b/source/gui/widget/base/Sprite.py similarity index 75% rename from source/gui/sprite/Sprite.py rename to source/gui/widget/base/Sprite.py index 44a4ae1..0990740 100644 --- a/source/gui/sprite/Sprite.py +++ b/source/gui/widget/base/Sprite.py @@ -11,6 +11,8 @@ class Sprite(pyglet.sprite.Sprite): if width is not None: self.width = width if height is not None: self.height = height + # property + @pyglet.sprite.Sprite.width.setter def width(self, width: int): self.scale_x = width / self._orig_width @@ -18,3 +20,11 @@ class Sprite(pyglet.sprite.Sprite): @pyglet.sprite.Sprite.height.setter def height(self, height: int): 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 diff --git a/source/gui/widget/base/__init__.py b/source/gui/widget/base/__init__.py index e3b8a26..70f4050 100644 --- a/source/gui/widget/base/__init__.py +++ b/source/gui/widget/base/__init__.py @@ -1,3 +1,2 @@ -from .BaseWidget import BaseWidget -from .BaseBoxWidget import BaseBoxWidget -from .BaseAdaptativeWidget import BaseAdaptativeWidget +from .Label import Label +from .Sprite import Sprite diff --git a/source/gui/window/Window.py b/source/gui/window/Window.py index f8a134e..5a32fdd 100644 --- a/source/gui/window/Window.py +++ b/source/gui/window/Window.py @@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Callable import pyglet.window if TYPE_CHECKING: - from source.gui.scene import BaseScene + from source.gui.scene.abc import AbstractScene class Window(pyglet.window.Window): # NOQA @@ -26,16 +26,16 @@ class Window(pyglet.window.Window): # NOQA # scene system - def set_scene(self, *scenes: "BaseScene"): + def set_scene(self, *scenes: "AbstractScene"): self.clear() 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: self._scenes.insert(priority, scene) scene.on_window_added(self) - def remove_scene(self, *scenes: "BaseScene"): + def remove_scene(self, *scenes: "AbstractScene"): for scene in scenes: scene.on_window_removed(self) self._scenes.remove(scene)