added Image and Input widgets
This commit is contained in:
parent
abf653a4e9
commit
48da47c234
13 changed files with 232 additions and 22 deletions
Binary file not shown.
Before Width: | Height: | Size: 180 B |
Binary file not shown.
Before Width: | Height: | Size: 892 B |
Binary file not shown.
Before Width: | Height: | Size: 747 B |
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 |
63
main.pyw
63
main.pyw
|
@ -1,7 +1,7 @@
|
|||
import pyglet
|
||||
|
||||
from source.gui.scene.abc import Scene
|
||||
from source.gui.widget import Text, FPSDisplay, Button
|
||||
from source.gui.widget import FPSDisplay, Button, Image, Input
|
||||
from source.gui.window import Window
|
||||
|
||||
# Test Scene
|
||||
|
@ -13,30 +13,45 @@ class TestScene(Scene):
|
|||
|
||||
# loading resource
|
||||
|
||||
texture_normal = pyglet.image.load("./assets/image/button/test_button_normal.png")
|
||||
texture_hover = pyglet.image.load("./assets/image/button/test_button_hover.png")
|
||||
texture_click = pyglet.image.load("./assets/image/button/test_button_clicking.png")
|
||||
texture_button_normal = pyglet.image.load("./assets/image/button/normal.png")
|
||||
texture_button_hover = pyglet.image.load("./assets/image/button/hovering.png")
|
||||
texture_button_click = pyglet.image.load("./assets/image/button/clicking.png")
|
||||
texture_input_normal = pyglet.image.load("./assets/image/input/inputbox.png")
|
||||
texture_input_active = pyglet.image.load("./assets/image/input/active.png")
|
||||
texture_input_error = pyglet.image.load("./assets/image/input/error.png")
|
||||
|
||||
button_atlas = pyglet.image.atlas.TextureAtlas()
|
||||
region_normal = button_atlas.add(texture_normal)
|
||||
region_hover = button_atlas.add(texture_hover)
|
||||
region_click = button_atlas.add(texture_click)
|
||||
texture_atlas = pyglet.image.atlas.TextureAtlas()
|
||||
region_button_normal = texture_atlas.add(texture_button_normal)
|
||||
region_button_hover = texture_atlas.add(texture_button_hover)
|
||||
region_button_click = texture_atlas.add(texture_button_click)
|
||||
region_input_normal = texture_atlas.add(texture_input_normal)
|
||||
region_input_active = texture_atlas.add(texture_input_active)
|
||||
region_input_error = texture_atlas.add(texture_input_error)
|
||||
|
||||
self.background_batch = pyglet.graphics.Batch()
|
||||
self.label_batch = pyglet.graphics.Batch()
|
||||
|
||||
# the widgets
|
||||
|
||||
self.fps_display = self.add_widget(FPSDisplay)
|
||||
self.fps_display = self.add_widget(FPSDisplay, color=(255, 255, 255, 127))
|
||||
|
||||
background = self.add_widget(
|
||||
Image,
|
||||
|
||||
x=0, y=0, width=1, height=1,
|
||||
|
||||
image=region_input_normal,
|
||||
batch=self.background_batch,
|
||||
)
|
||||
|
||||
label = self.add_widget(
|
||||
Button,
|
||||
|
||||
x=0.5, y=0.5, width=0.5, height=0.5,
|
||||
|
||||
texture_normal=region_normal,
|
||||
texture_hover=region_hover,
|
||||
texture_click=region_click,
|
||||
texture_normal=region_button_normal,
|
||||
texture_hover=region_button_hover,
|
||||
texture_click=region_button_click,
|
||||
|
||||
label_text="Hello World !",
|
||||
|
||||
|
@ -44,15 +59,37 @@ class TestScene(Scene):
|
|||
label_batch=self.label_batch,
|
||||
)
|
||||
|
||||
label.on_pressed = lambda button, modifiers: print("pressed", label, button, modifiers)
|
||||
label.on_pressed = lambda button, modifiers: window.set_scene(TestScene2)
|
||||
label.on_release = lambda button, modifiers: print("release", label, button, modifiers)
|
||||
|
||||
input_ = self.add_widget(
|
||||
Input,
|
||||
|
||||
x=0.1, y=0.2, width=0.4, height=0.1,
|
||||
|
||||
texture_normal=region_input_normal,
|
||||
texture_active=region_input_active,
|
||||
texture_error=region_input_error,
|
||||
|
||||
# 4 numéros de 1 à 3 chiffres aséparés par des points (IP), optionnellement suivi
|
||||
# de deux points ainsi que de 1 à 5 chiffres (port)
|
||||
regex=r"\d{1,3}(\.\d{1,3}){3}(:\d{1,5})?",
|
||||
|
||||
background_batch=self.background_batch,
|
||||
label_batch=self.label_batch,
|
||||
)
|
||||
|
||||
def on_draw(self):
|
||||
self.background_batch.draw()
|
||||
self.label_batch.draw()
|
||||
self.fps_display.draw()
|
||||
|
||||
|
||||
class TestScene2(Scene):
|
||||
def __init__(self, window: "Window"):
|
||||
super().__init__(window)
|
||||
|
||||
|
||||
# Create a new window
|
||||
window = Window(resizable=True, vsync=False)
|
||||
window.add_scene(TestScene)
|
||||
|
|
|
@ -42,6 +42,7 @@ class Button(BoxWidget):
|
|||
)
|
||||
|
||||
self.label = pyglet.text.Label(
|
||||
width=None, height=None,
|
||||
anchor_x="center", anchor_y="center",
|
||||
**dict_prefix("label_", kwargs)
|
||||
)
|
||||
|
@ -71,11 +72,10 @@ class Button(BoxWidget):
|
|||
self.background.image = self.background_texture
|
||||
|
||||
def _refresh_size(self) -> None:
|
||||
self.background.x = self.x
|
||||
self.background.y = self.y
|
||||
self.background.width = self.width
|
||||
self.background.height = self.height
|
||||
self.background.x, self.background.y = self.x, self.y
|
||||
self.background.width, self.background.height = self.width, self.height
|
||||
|
||||
# center the label
|
||||
self.label.x = self.x + (self.width / 2)
|
||||
self.label.y = self.y + (self.height / 2)
|
||||
|
||||
|
|
|
@ -13,10 +13,10 @@ class FPSDisplay(Widget):
|
|||
A widget that display the current FPS of the scene's window
|
||||
"""
|
||||
|
||||
def __init__(self, scene: "Scene"):
|
||||
def __init__(self, scene: "Scene", *args, **kwargs):
|
||||
super().__init__(scene)
|
||||
|
||||
self.fps_display = pyglet.window.FPSDisplay(scene.window)
|
||||
self.fps_display = pyglet.window.FPSDisplay(scene.window, *args, **kwargs)
|
||||
|
||||
def draw(self):
|
||||
self.fps_display.draw()
|
||||
|
|
43
source/gui/widget/Image.py
Normal file
43
source/gui/widget/Image.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
from typing import TYPE_CHECKING
|
||||
|
||||
import pyglet.image
|
||||
|
||||
from source.gui.sprite import Sprite
|
||||
from source.gui.widget.abc import BoxWidget
|
||||
from source.type import Percentage
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from source.gui.scene.abc import Scene
|
||||
|
||||
|
||||
class Image(BoxWidget):
|
||||
def __init__(self, scene: "Scene",
|
||||
|
||||
image: pyglet.image.AbstractImage,
|
||||
|
||||
x: Percentage = 0,
|
||||
y: Percentage = 0,
|
||||
width: Percentage = None,
|
||||
height: Percentage = None,
|
||||
*args, **kwargs):
|
||||
super().__init__(scene, x, y, width, height)
|
||||
|
||||
self.image = Sprite(img=image, *args, **kwargs)
|
||||
|
||||
self._refresh_size()
|
||||
|
||||
# refresh
|
||||
|
||||
def _refresh_size(self):
|
||||
self.image.width, self.image.height = self.width, self.height
|
||||
|
||||
# event
|
||||
|
||||
def on_resize(self, width: int, height: int):
|
||||
self._refresh_size()
|
||||
|
||||
# draw
|
||||
|
||||
def draw(self):
|
||||
self.image.draw()
|
115
source/gui/widget/Input.py
Normal file
115
source/gui/widget/Input.py
Normal file
|
@ -0,0 +1,115 @@
|
|||
import re
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
import pyglet.image
|
||||
|
||||
from source.gui.sprite import Sprite
|
||||
from source.gui.widget.abc import BoxWidget
|
||||
from source.type import Percentage
|
||||
from source.utils import dict_prefix
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from source.gui.scene.abc import Scene
|
||||
|
||||
|
||||
class Input(BoxWidget):
|
||||
def __init__(self, scene: "Scene",
|
||||
|
||||
texture_normal: pyglet.image.AbstractImage,
|
||||
texture_active: pyglet.image.AbstractImage = None,
|
||||
texture_error: pyglet.image.AbstractImage = None,
|
||||
|
||||
regex: Optional[str | re.Pattern] = None,
|
||||
|
||||
x: Percentage = 0,
|
||||
y: Percentage = 0,
|
||||
width: Percentage = None,
|
||||
height: Percentage = None,
|
||||
*args, **kwargs):
|
||||
super().__init__(scene, x, y, width, height)
|
||||
|
||||
self._texture_normal: pyglet.image.AbstractImage = texture_normal
|
||||
self._texture_active: Optional[pyglet.image.AbstractImage] = texture_active
|
||||
self._texture_error: Optional[pyglet.image.AbstractImage] = texture_error
|
||||
|
||||
self._invalid = False
|
||||
|
||||
self.regex = re.compile(regex) if isinstance(regex, str) else regex
|
||||
|
||||
self.background = Sprite(
|
||||
img=self._texture_normal,
|
||||
**dict_prefix("background_", kwargs)
|
||||
)
|
||||
self.label = pyglet.text.Label(
|
||||
width=None, height=None,
|
||||
anchor_x="center", anchor_y="center",
|
||||
**dict_prefix("label_", kwargs)
|
||||
)
|
||||
|
||||
# background
|
||||
|
||||
@property
|
||||
def background_texture(self) -> pyglet.image.AbstractImage:
|
||||
"""
|
||||
Return the correct texture for the background.
|
||||
The clicking texture per default, if hover the hovered texture (if it exists)
|
||||
and if click the clicking texture (if it exists)
|
||||
:return: the corresponding texture
|
||||
"""
|
||||
|
||||
return (
|
||||
self._texture_active if self.activated and self._texture_active is not None else
|
||||
self._texture_error if self.invalid and self._texture_error is not None else
|
||||
self._texture_normal
|
||||
)
|
||||
|
||||
# refresh
|
||||
|
||||
def _refresh_background(self) -> None:
|
||||
self.background.image = self.background_texture
|
||||
|
||||
def _refresh_size(self) -> None:
|
||||
self.background.x, self.background.y = self.x, self.y
|
||||
self.background.width, self.background.height = self.width, self.height
|
||||
|
||||
# center the label
|
||||
self.label.x = self.x + (self.width / 2)
|
||||
self.label.y = self.y + (self.height / 2)
|
||||
|
||||
@BoxWidget.activated.setter
|
||||
def activated(self, activated: bool) -> None:
|
||||
BoxWidget.activated.fset(self, activated)
|
||||
self._refresh_background()
|
||||
|
||||
# property
|
||||
|
||||
@property
|
||||
def invalid(self):
|
||||
return self._invalid
|
||||
|
||||
@invalid.setter
|
||||
def invalid(self, invalid: bool):
|
||||
self._invalid = invalid
|
||||
self._refresh_background()
|
||||
|
||||
# event
|
||||
|
||||
def on_key_press(self, symbol: int, modifiers: int):
|
||||
if not self.activated: return # ignore si ce widget est désactivé / non sélectionné
|
||||
|
||||
if symbol == pyglet.window.key.BACKSPACE: # si la touche "supprimé" est enfoncé
|
||||
self.label.text = self.label.text[0:-1] # retire le dernier caractère du texte
|
||||
|
||||
def on_text(self, char: str):
|
||||
if not self.activated: return # ignore si ce widget est désactivé / non sélectionné
|
||||
self.label.text += char # ajoute le caractère au label
|
||||
|
||||
if self.regex is not None: # si il y a un regex de validation, applique le pour vérifier le texte
|
||||
self.invalid = self.regex.fullmatch(self.label.text) is None
|
||||
|
||||
def on_resize(self, width: int, height: int):
|
||||
self._refresh_size()
|
||||
|
||||
def draw(self):
|
||||
self.background.draw()
|
||||
self.label.draw()
|
|
@ -1,3 +1,5 @@
|
|||
from .Text import Text
|
||||
from .FPSDisplay import FPSDisplay
|
||||
from .Button import Button
|
||||
from .Input import Input
|
||||
from .Image import Image
|
||||
|
|
|
@ -29,6 +29,7 @@ class BoxWidget(Widget, ABC):
|
|||
|
||||
self._hovering = False # is the button currently hovered ?
|
||||
self._clicking = False # is the button currently clicked ?
|
||||
self._activated = False # is the button activated ? (the last click was inside this widget)
|
||||
|
||||
# property
|
||||
|
||||
|
@ -94,6 +95,14 @@ class BoxWidget(Widget, ABC):
|
|||
def clicking(self, clicking: bool):
|
||||
self._clicking = clicking
|
||||
|
||||
@property
|
||||
def activated(self):
|
||||
return self._activated
|
||||
|
||||
@activated.setter
|
||||
def activated(self, activated: bool):
|
||||
self._activated = activated
|
||||
|
||||
# event
|
||||
|
||||
def on_mouse_motion(self, x: int, y: int, dx: int, dy: int): # NOQA
|
||||
|
@ -125,15 +134,19 @@ class BoxWidget(Widget, ABC):
|
|||
|
||||
def on_mouse_press(self, x: int, y: int, button: int, modifiers: int):
|
||||
# if this button was the one hovered when the click was pressed
|
||||
if not in_bbox((x, y), self.bbox): return
|
||||
|
||||
self.clicking = True
|
||||
if not in_bbox((x, y), self.bbox):
|
||||
self.activated = False # if the click was not in the bbox, disable the activated state
|
||||
return
|
||||
|
||||
self.activated = True # if the click is inside the bbox, enable the activated state
|
||||
self.clicking = True # the widget is now clicked
|
||||
|
||||
self.on_press(button, modifiers)
|
||||
|
||||
def on_mouse_release(self, x: int, y: int, button: int, modifiers: int):
|
||||
old_click: bool = self._clicking
|
||||
self.clicking = False
|
||||
self.clicking = False # the widget is no longer clicked
|
||||
|
||||
if not in_bbox((x, y), self.bbox): return
|
||||
|
||||
|
|
Loading…
Reference in a new issue