commented widgets
This commit is contained in:
parent
89c6b81ba9
commit
51eb35c43c
16 changed files with 206 additions and 132 deletions
|
@ -6,5 +6,9 @@ path = path_sound / "ambient"
|
||||||
|
|
||||||
|
|
||||||
class SoundAmbient(MediaGroup):
|
class SoundAmbient(MediaGroup):
|
||||||
|
"""
|
||||||
|
Groupe contenant les sons ambient du jeu.
|
||||||
|
"""
|
||||||
|
|
||||||
menu = Sound(path / "menu.wav")
|
menu = Sound(path / "menu.wav")
|
||||||
sea = Sound(path / "sea.wav")
|
sea = Sound(path / "sea.wav")
|
||||||
|
|
|
@ -6,6 +6,10 @@ path = path_sound / "effect"
|
||||||
|
|
||||||
|
|
||||||
class SoundEffect(MediaGroup):
|
class SoundEffect(MediaGroup):
|
||||||
|
"""
|
||||||
|
Groupe contenant les effets sonores du jeu.
|
||||||
|
"""
|
||||||
|
|
||||||
placed = Sound(path / "placed.wav")
|
placed = Sound(path / "placed.wav")
|
||||||
touched = Sound(path / "touched.wav")
|
touched = Sound(path / "touched.wav")
|
||||||
missed = Sound(path / "missed.wav")
|
missed = Sound(path / "missed.wav")
|
||||||
|
|
|
@ -5,7 +5,7 @@ import pyglet
|
||||||
|
|
||||||
class MediaGroup(ABC):
|
class MediaGroup(ABC):
|
||||||
"""
|
"""
|
||||||
This class represent a music group that can be played.
|
Cette classe représente un groupe de musique pouvant être joué.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
player: pyglet.media.Player
|
player: pyglet.media.Player
|
||||||
|
|
|
@ -11,6 +11,10 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
class Sound(Media):
|
class Sound(Media):
|
||||||
|
"""
|
||||||
|
Représente un son pouvant être joué par le lecteur.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, path: Path):
|
def __init__(self, path: Path):
|
||||||
self.path = path
|
self.path = path
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,21 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
class Media(ABC):
|
class Media(ABC):
|
||||||
loaded_media: dict[Path, pyglet.media.Source] = {}
|
"""
|
||||||
|
Représente un type de média
|
||||||
|
"""
|
||||||
|
|
||||||
|
loaded_media: dict[Path, pyglet.media.Source] = {} # cache des médias chargés
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_media(cls, path: Path, owner: "MediaGroup") -> pyglet.media.Source:
|
def get_media(cls, path: Path, owner: "MediaGroup") -> pyglet.media.Source:
|
||||||
|
"""
|
||||||
|
Renvoie le média correspondant au chemin donné
|
||||||
|
:param path: le chemin du media
|
||||||
|
:param owner: la classe qui a appelé la fonction
|
||||||
|
:return: le média
|
||||||
|
"""
|
||||||
|
|
||||||
if (media := cls.loaded_media.get(path)) is None:
|
if (media := cls.loaded_media.get(path)) is None:
|
||||||
# charge le son
|
# charge le son
|
||||||
media = pyglet.media.load(path)
|
media = pyglet.media.load(path)
|
||||||
|
@ -38,4 +49,10 @@ class Media(ABC):
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def __get__(self, instance, owner) -> pyglet.media.Source:
|
def __get__(self, instance, owner) -> pyglet.media.Source:
|
||||||
|
"""
|
||||||
|
Renvoie le média correspondant à l'instance donnée
|
||||||
|
:param instance: instance de la classe qui a appelé la fonction
|
||||||
|
:param owner: classe ayant appelé la fonction
|
||||||
|
:return: le media
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -14,8 +14,9 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
class Button(BoxWidget):
|
class Button(BoxWidget):
|
||||||
"""
|
"""
|
||||||
A button widget with a background texture that change depending on if it is clicked or hovered, and a label.
|
Un bouton avec une texture de fond qui change en fonction de s'il est cliqué ou survolé et un label.
|
||||||
You can pass parameter to the background and label by adding "background_" and "label_" before the parameter.
|
Vous pouvez passer des paramètres pour le background et au label en ajoutant "background_" et "label_"
|
||||||
|
devant le paramètre.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, scene: "Scene",
|
def __init__(self, scene: "Scene",
|
||||||
|
@ -48,17 +49,17 @@ class Button(BoxWidget):
|
||||||
self.add_listener("on_hover_change", lambda *_: self._refresh_background())
|
self.add_listener("on_hover_change", lambda *_: self._refresh_background())
|
||||||
self.add_listener("on_click_change", lambda *_: self._refresh_background())
|
self.add_listener("on_click_change", lambda *_: self._refresh_background())
|
||||||
|
|
||||||
self._refresh_size() # refresh the size and position for the background and label
|
self._refresh_size() # rafraîchit la taille et la position du background et du label
|
||||||
|
|
||||||
# background
|
# background
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def background_texture(self) -> pyglet.image.AbstractImage:
|
def background_texture(self) -> pyglet.image.AbstractImage:
|
||||||
"""
|
"""
|
||||||
Return the correct texture for the background.
|
Renvoie la bonne texture pour le fond.
|
||||||
The clicking texture per default, if hover the hovered texture (if it exists)
|
Utilise la texture normale par défaut, si survolé la texture de survol (si elle existe)
|
||||||
and if click the clicking texture (if it exists)
|
et la texture de clic (si elle existe) si cliqué
|
||||||
:return: the corresponding texture
|
:return: la texture correspondante
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -12,7 +12,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
class Checkbox(BoxWidget):
|
class Checkbox(BoxWidget):
|
||||||
"""
|
"""
|
||||||
A checkbox widget with a background texture that change depending on if it is checked or unchecked.
|
Un widget de checkbox avec une texture d'arrière-plan qui change en fonction de si elle est cochée ou non.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, scene: "Scene",
|
def __init__(self, scene: "Scene",
|
||||||
|
@ -43,7 +43,7 @@ class Checkbox(BoxWidget):
|
||||||
|
|
||||||
self._refresh_size()
|
self._refresh_size()
|
||||||
|
|
||||||
# refreshing
|
# rafraichissement
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tick_texture(self):
|
def tick_texture(self):
|
||||||
|
@ -56,7 +56,7 @@ class Checkbox(BoxWidget):
|
||||||
self.tick.x, self.tick.y = self.xy
|
self.tick.x, self.tick.y = self.xy
|
||||||
self.tick.width, self.tick.height = self.size
|
self.tick.width, self.tick.height = self.size
|
||||||
|
|
||||||
# property
|
# propriétés
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
|
|
|
@ -19,7 +19,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
class GameGrid(BoxWidget):
|
class GameGrid(BoxWidget):
|
||||||
"""
|
"""
|
||||||
A widget that represent a game grid.
|
Un widget représentant la grille du jeu
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, scene: "Scene",
|
def __init__(self, scene: "Scene",
|
||||||
|
@ -49,7 +49,7 @@ class GameGrid(BoxWidget):
|
||||||
self.group_cursor = pyglet.graphics.Group(order=1)
|
self.group_cursor = pyglet.graphics.Group(order=1)
|
||||||
self.group_line = pyglet.graphics.Group(order=2)
|
self.group_line = pyglet.graphics.Group(order=2)
|
||||||
|
|
||||||
# the list of the size of the boats to place
|
# la liste des tailles des bateaux a placé sur la grille
|
||||||
self.boats_length = [] if boats_length is None else sorted(boats_length, reverse=True)
|
self.boats_length = [] if boats_length is None else sorted(boats_length, reverse=True)
|
||||||
|
|
||||||
# créer la planche du jeu
|
# créer la planche du jeu
|
||||||
|
@ -104,13 +104,13 @@ class GameGrid(BoxWidget):
|
||||||
int((rel_y-1) / self.cell_height)
|
int((rel_y-1) / self.cell_height)
|
||||||
)
|
)
|
||||||
|
|
||||||
# refresh
|
# rafraichissement
|
||||||
|
|
||||||
def _refresh_size(self):
|
def _refresh_size(self):
|
||||||
self.background.x, self.background.y = self.xy
|
self.background.x, self.background.y = self.xy
|
||||||
self.background.width, self.background.height = self.size
|
self.background.width, self.background.height = self.size
|
||||||
|
|
||||||
# lines
|
# lignes
|
||||||
|
|
||||||
for column, line in enumerate(self.lines[:self.columns - 1], start=1):
|
for column, line in enumerate(self.lines[:self.columns - 1], start=1):
|
||||||
line.x = self.x + self.cell_width * column
|
line.x = self.x + self.cell_width * column
|
||||||
|
@ -149,7 +149,7 @@ class GameGrid(BoxWidget):
|
||||||
self.cursor.y = self.y + cell_y * self.height / self.rows
|
self.cursor.y = self.y + cell_y * self.height / self.rows
|
||||||
self.cursor.width, self.cursor.height = self.cell_size
|
self.cursor.width, self.cursor.height = self.cell_size
|
||||||
|
|
||||||
self.preview_boat((cell_x, cell_y)) # display the preview of the boat on this cell
|
self.preview_boat((cell_x, cell_y)) # affiche la grille du jeu en prévisualisant cette cellule
|
||||||
|
|
||||||
# function
|
# function
|
||||||
|
|
||||||
|
@ -161,7 +161,11 @@ class GameGrid(BoxWidget):
|
||||||
self.display_board(self.board)
|
self.display_board(self.board)
|
||||||
|
|
||||||
def display_board(self, board: Board, preview: bool = False):
|
def display_board(self, board: Board, preview: bool = False):
|
||||||
# remplacer par l'utilisation de board.boats ?
|
"""
|
||||||
|
Affiche la grille du jeu.
|
||||||
|
:param board: la grille du jeu à afficher
|
||||||
|
:param preview: la prévisualisation du dernier bateau est-elle activée ?
|
||||||
|
"""
|
||||||
|
|
||||||
matrice = board.boats
|
matrice = board.boats
|
||||||
max_boat: int = np.max(matrice)
|
max_boat: int = np.max(matrice)
|
||||||
|
@ -220,12 +224,21 @@ class GameGrid(BoxWidget):
|
||||||
self._refresh_size()
|
self._refresh_size()
|
||||||
|
|
||||||
def swap_orientation(self):
|
def swap_orientation(self):
|
||||||
|
"""
|
||||||
|
Inverse l'orientation du bateau en cours de placement.
|
||||||
|
"""
|
||||||
|
|
||||||
self.orientation = (
|
self.orientation = (
|
||||||
Orientation.HORIZONTAL if self.orientation is Orientation.VERTICAL else
|
Orientation.HORIZONTAL if self.orientation is Orientation.VERTICAL else
|
||||||
Orientation.VERTICAL
|
Orientation.VERTICAL
|
||||||
)
|
)
|
||||||
|
|
||||||
def place_boat(self, cell: Point2D):
|
def place_boat(self, cell: Point2D):
|
||||||
|
"""
|
||||||
|
Place un bateau sur la grille du jeu.
|
||||||
|
:param cell: position sur laquelle placer le bateau
|
||||||
|
"""
|
||||||
|
|
||||||
if len(self.boats_length) == 0: return
|
if len(self.boats_length) == 0: return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -234,10 +247,10 @@ class GameGrid(BoxWidget):
|
||||||
cell
|
cell
|
||||||
)
|
)
|
||||||
except InvalidBoatPosition:
|
except InvalidBoatPosition:
|
||||||
pass # if the boat can't be placed, ignore
|
pass # si le bateau n'a pas pu être placé, ignore
|
||||||
|
|
||||||
else: # if the boat have been placed
|
else: # si le bateau a bien été placé
|
||||||
self.boats_length.pop(0) # remove the boat from the list of boat to place
|
self.boats_length.pop(0) # retire la taille du bateau de la liste des bateaux à placer
|
||||||
|
|
||||||
self.trigger_event("on_boat_placed")
|
self.trigger_event("on_boat_placed")
|
||||||
if len(self.boats_length) == 0:
|
if len(self.boats_length) == 0:
|
||||||
|
@ -246,6 +259,11 @@ class GameGrid(BoxWidget):
|
||||||
self.refresh_board() # rafraichi l'affichage
|
self.refresh_board() # rafraichi l'affichage
|
||||||
|
|
||||||
def preview_boat(self, cell: Point2D):
|
def preview_boat(self, cell: Point2D):
|
||||||
|
"""
|
||||||
|
Prévisualise le prochain bateau à placer.
|
||||||
|
:param cell: position où visualiser le bateau
|
||||||
|
"""
|
||||||
|
|
||||||
if len(self.boats_length) == 0: return
|
if len(self.boats_length) == 0: return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -260,6 +278,13 @@ class GameGrid(BoxWidget):
|
||||||
else: self.display_board(preview_board, preview=True)
|
else: self.display_board(preview_board, preview=True)
|
||||||
|
|
||||||
def place_bomb(self, cell: Point2D, force_touched: bool = None) -> BombState:
|
def place_bomb(self, cell: Point2D, force_touched: bool = None) -> BombState:
|
||||||
|
"""
|
||||||
|
Place une bombe sur la grille du jeu.
|
||||||
|
:param cell: cellule sur laquelle placer la bombe
|
||||||
|
:param force_touched: la cellule doit-elle forcer l'affichage comment étant manqué ou touché ?
|
||||||
|
:return: l'état de la bombe
|
||||||
|
"""
|
||||||
|
|
||||||
bomb_state = self.board.bomb(cell)
|
bomb_state = self.board.bomb(cell)
|
||||||
|
|
||||||
if force_touched is not None:
|
if force_touched is not None:
|
||||||
|
@ -270,7 +295,10 @@ class GameGrid(BoxWidget):
|
||||||
return bomb_state
|
return bomb_state
|
||||||
|
|
||||||
def remove_bomb(self, cell: Point2D):
|
def remove_bomb(self, cell: Point2D):
|
||||||
# retire une bombe de la planche
|
"""
|
||||||
|
Retire une bombe de la grille du jeu.
|
||||||
|
:param cell: cellule de la bombe à retirer
|
||||||
|
"""
|
||||||
self.board.remove_bomb(cell)
|
self.board.remove_bomb(cell)
|
||||||
self.refresh_board()
|
self.refresh_board()
|
||||||
|
|
||||||
|
@ -278,29 +306,27 @@ class GameGrid(BoxWidget):
|
||||||
cell = self.get_cell_from_rel(rel_x, rel_y)
|
cell = self.get_cell_from_rel(rel_x, rel_y)
|
||||||
|
|
||||||
match button:
|
match button:
|
||||||
|
# si le joueur fait un clic droit, inverse l'orientation du bateau en cours de placement
|
||||||
case pyglet.window.mouse.RIGHT:
|
case pyglet.window.mouse.RIGHT:
|
||||||
self.swap_orientation()
|
self.swap_orientation()
|
||||||
self.preview_boat(cell)
|
self.preview_boat(cell)
|
||||||
|
|
||||||
|
# si le joueur fait un clic gauche, place un bateau ou une bombe
|
||||||
case pyglet.window.mouse.LEFT:
|
case pyglet.window.mouse.LEFT:
|
||||||
self.place_boat(cell)
|
self.place_boat(cell)
|
||||||
self.trigger_event("on_request_place_bomb", cell)
|
self.trigger_event("on_request_place_bomb", cell)
|
||||||
|
|
||||||
# property
|
# propriétés
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cell_width(self) -> float:
|
def cell_width(self) -> float: return self.width / self.columns
|
||||||
return self.width / self.columns
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cell_height(self) -> float:
|
def cell_height(self) -> float: return self.height / self.rows
|
||||||
return self.height / self.rows
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cell_size(self) -> tuple[float, float]:
|
def cell_size(self) -> tuple[float, float]: return self.cell_width, self.cell_height
|
||||||
return self.cell_width, self.cell_height
|
|
||||||
|
|
||||||
# event
|
# événements
|
||||||
|
|
||||||
def on_resize(self, width: int, height: int):
|
def on_resize(self, width: int, height: int): self._refresh_size()
|
||||||
self._refresh_size()
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
class Image(BoxWidget):
|
class Image(BoxWidget):
|
||||||
"""
|
"""
|
||||||
An image widget with a texture.
|
Un widget d'image avec une texture.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, scene: "Scene",
|
def __init__(self, scene: "Scene",
|
||||||
|
|
|
@ -15,7 +15,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
class Input(BoxWidget):
|
class Input(BoxWidget):
|
||||||
"""
|
"""
|
||||||
An input widget with a background texture and a label. A regex pattern can be added to validate the input.
|
Un widget d'entrée avec une texture de fond et un label. Des paternes regex peut être ajouté pour valider l'entrée.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, scene: "Scene",
|
def __init__(self, scene: "Scene",
|
||||||
|
@ -64,18 +64,19 @@ class Input(BoxWidget):
|
||||||
@property
|
@property
|
||||||
def background_texture(self) -> pyglet.image.AbstractImage:
|
def background_texture(self) -> pyglet.image.AbstractImage:
|
||||||
"""
|
"""
|
||||||
Return the correct texture for the background.
|
Renvoie la texture de fond correspondante.
|
||||||
The clicking texture per default, if hover the hovered texture (if it exists)
|
Si le widget est activé, renvoie la texture active (si elle existe),
|
||||||
and if click the clicking texture (if it exists)
|
la texture de clic (si elle existe) sinon la texture normale
|
||||||
:return: the corresponding texture
|
:return: la texture de fond correspondante
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return (
|
return (
|
||||||
texture if self.activated and (texture := self.style.get("active")) is not None else # NOQA
|
texture if self.activated and (texture := self.style.get("active")) is not None else # NOQA
|
||||||
texture if not self.valid and (texture := self.style.get("error")) is not None else
|
texture if not self.valid and (texture := self.style.get("error")) is not None else
|
||||||
self.style.get("normal")
|
self.style.get("normal")
|
||||||
)
|
)
|
||||||
|
|
||||||
# refresh
|
# rafraichissement
|
||||||
|
|
||||||
def _refresh_background(self) -> None:
|
def _refresh_background(self) -> None:
|
||||||
self.background.image = self.background_texture
|
self.background.image = self.background_texture
|
||||||
|
@ -94,8 +95,7 @@ class Input(BoxWidget):
|
||||||
# property
|
# property
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def valid(self):
|
def valid(self): return self._valid
|
||||||
return self._valid
|
|
||||||
|
|
||||||
@valid.setter
|
@valid.setter
|
||||||
def valid(self, valid: bool):
|
def valid(self, valid: bool):
|
||||||
|
@ -103,14 +103,12 @@ class Input(BoxWidget):
|
||||||
self._refresh_background()
|
self._refresh_background()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def text(self):
|
def text(self): return self.label.text
|
||||||
return self.label.text
|
|
||||||
|
|
||||||
@text.setter
|
@text.setter
|
||||||
def text(self, text: str):
|
def text(self, text: str): self.label.text = text
|
||||||
self.label.text = text
|
|
||||||
|
|
||||||
# event
|
# événements
|
||||||
|
|
||||||
def on_key_press(self, symbol: int, modifiers: int):
|
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 not self.activated: return # ignore si ce widget est désactivé / non sélectionné
|
||||||
|
|
|
@ -12,8 +12,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
class Scroller(BoxWidget):
|
class Scroller(BoxWidget):
|
||||||
"""
|
"""
|
||||||
A scroller widget with a background texture, a scroller and a label.
|
Un widget qui affiche un curseur qui peut être déplacé entre deux valeurs.
|
||||||
The cursor can be moved between the "from" and the "to" value
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, scene: "Scene",
|
def __init__(self, scene: "Scene",
|
||||||
|
@ -64,7 +63,7 @@ class Scroller(BoxWidget):
|
||||||
self._to = to
|
self._to = to
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
# refresh
|
# rafraichissement
|
||||||
|
|
||||||
def _refresh(self):
|
def _refresh(self):
|
||||||
# background
|
# background
|
||||||
|
@ -91,7 +90,7 @@ class Scroller(BoxWidget):
|
||||||
def _refresh_cursor(self, rel_x: int):
|
def _refresh_cursor(self, rel_x: int):
|
||||||
self.value = (rel_x / self.width) * (self.to - self.from_) + self.from_
|
self.value = (rel_x / self.width) * (self.to - self.from_) + self.from_
|
||||||
|
|
||||||
# property
|
# propriétés
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def value(self):
|
def value(self):
|
||||||
|
@ -122,7 +121,7 @@ class Scroller(BoxWidget):
|
||||||
self._to = to
|
self._to = to
|
||||||
self._refresh()
|
self._refresh()
|
||||||
|
|
||||||
# event
|
# événements
|
||||||
|
|
||||||
def on_resize(self, width: int, height: int):
|
def on_resize(self, width: int, height: int):
|
||||||
self._refresh()
|
self._refresh()
|
||||||
|
|
|
@ -11,7 +11,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
class Text(BoxWidget):
|
class Text(BoxWidget):
|
||||||
"""
|
"""
|
||||||
A widget that display a text
|
Un widget qui affiche du texte
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, scene: "Scene",
|
def __init__(self, scene: "Scene",
|
||||||
|
|
|
@ -11,7 +11,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
class BoxWidget(Widget, ABC):
|
class BoxWidget(Widget, ABC):
|
||||||
"""
|
"""
|
||||||
Same as a basic widget, but inside a box
|
Pareil qu'un Widget, mais dans une boîte de collision (bbox)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, scene: "Scene",
|
def __init__(self, scene: "Scene",
|
||||||
|
@ -21,23 +21,23 @@ class BoxWidget(Widget, ABC):
|
||||||
height: Distance = None):
|
height: Distance = None):
|
||||||
super().__init__(scene)
|
super().__init__(scene)
|
||||||
|
|
||||||
# memorize the value with a percent value
|
# Défini les bordures de la boîte de collision. Peut utiliser des nombres ou des unités de distance
|
||||||
self.x = x
|
self.x = x
|
||||||
self.y = y
|
self.y = y
|
||||||
self.width = width
|
self.width = width
|
||||||
self.height = height
|
self.height = height
|
||||||
|
|
||||||
self.hovering = False # is the button currently hovered ?
|
self.hovering = False # La bbox est-elle actuellement survolée ?
|
||||||
self.clicking = False # is the button currently clicked ?
|
self.clicking = False # La bbox est-elle actuellement cliqué ?
|
||||||
self.activated = False # is the button activated ? (the last click was inside this widget)
|
self.activated = False # La bbox est-il actuellement activé ? (le dernier clic a été à l'intérieur)
|
||||||
|
|
||||||
# property
|
# propriétés
|
||||||
|
|
||||||
def _getter_distance(self, raw_distance: Distance) -> int:
|
def _getter_distance(self, raw_distance: Distance) -> int:
|
||||||
"""
|
"""
|
||||||
Return the true distance in pixel from a more abstract distance
|
Renvoie la distance en pixel d'une distance abstraite
|
||||||
:param raw_distance: the distance object to convert to pixel
|
:param raw_distance: la distance à convertir en pixel
|
||||||
:return: the true distance in pixel
|
:return: la vrai distance en pixel
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if isinstance(raw_distance, int): return raw_distance
|
if isinstance(raw_distance, int): return raw_distance
|
||||||
|
@ -47,88 +47,71 @@ class BoxWidget(Widget, ABC):
|
||||||
raise TypeError(f"Invalid type for the distance : {type(raw_distance)}")
|
raise TypeError(f"Invalid type for the distance : {type(raw_distance)}")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def x(self) -> int:
|
def x(self) -> int: return self._getter_distance(self._x)
|
||||||
return self._getter_distance(self._x)
|
|
||||||
|
|
||||||
@x.setter
|
@x.setter
|
||||||
def x(self, x: Distance):
|
def x(self, x: Distance): self._x = x
|
||||||
self._x = x
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def y(self) -> int:
|
def y(self) -> int: return self._getter_distance(self._y)
|
||||||
return self._getter_distance(self._y)
|
|
||||||
|
|
||||||
@y.setter
|
@y.setter
|
||||||
def y(self, y: Distance):
|
def y(self, y: Distance): self._y = y
|
||||||
self._y = y
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def xy(self) -> tuple[int, int]:
|
def xy(self) -> tuple[int, int]: return self.x, self.y
|
||||||
return self.x, self.y
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def x2(self) -> int:
|
def x2(self) -> int: return self.x + self.width
|
||||||
return self.x + self.width
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def y2(self) -> int:
|
def y2(self) -> int: return self.y + self.height
|
||||||
return self.y + self.height
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def xy2(self) -> tuple[int, int]:
|
def xy2(self) -> tuple[int, int]: return self.x2, self.y2
|
||||||
return self.x2, self.y2
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def width(self) -> int:
|
def width(self) -> int: return self._getter_distance(self._width)
|
||||||
return self._getter_distance(self._width)
|
|
||||||
|
|
||||||
@width.setter
|
@width.setter
|
||||||
def width(self, width: Optional[Distance]):
|
def width(self, width: Optional[Distance]): self._width = width
|
||||||
self._width = width
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def height(self) -> int:
|
def height(self) -> int: return self._getter_distance(self._height)
|
||||||
return self._getter_distance(self._height)
|
|
||||||
|
|
||||||
@height.setter
|
@height.setter
|
||||||
def height(self, height: Optional[Distance]):
|
def height(self, height: Optional[Distance]): self._height = height
|
||||||
self._height = height
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def size(self) -> tuple[int, int]:
|
def size(self) -> tuple[int, int]: return self.width, self.height
|
||||||
return self.width, self.height
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bbox(self) -> tuple[int, int, int, int]:
|
def bbox(self) -> tuple[int, int, int, int]: return self.x, self.y, self.x2, self.y2
|
||||||
return self.x, self.y, self.x2, self.y2
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def center_x(self) -> float:
|
def center_x(self) -> float: return self.x + (self.width / 2)
|
||||||
return self.x + (self.width / 2)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def center_y(self) -> float:
|
def center_y(self) -> float: return self.y + (self.height / 2)
|
||||||
return self.y + (self.height / 2)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def center(self) -> tuple[float, float]:
|
def center(self) -> tuple[float, float]: return self.center_x, self.center_y
|
||||||
return self.center_x, self.center_y
|
|
||||||
|
|
||||||
# function
|
# fonctions
|
||||||
|
|
||||||
def in_bbox(self, point: Point2D) -> bool:
|
def in_bbox(self, point: Point2D) -> bool:
|
||||||
return in_bbox(point, self.bbox)
|
return in_bbox(point, self.bbox)
|
||||||
|
|
||||||
# event
|
# événements
|
||||||
|
|
||||||
def on_mouse_motion(self, x: int, y: int, dx: int, dy: int):
|
def on_mouse_motion(self, x: int, y: int, dx: int, dy: int):
|
||||||
"""
|
"""
|
||||||
When the mouse is moved, this event is triggered.
|
Lorsque la souris est déplacée, cet événement est déclenché.
|
||||||
Allow the implementation of the on_hover_enter and on_hover_leave events
|
Permet d'implémenter les événements on_hover, on_hover_enter et on_hover_leave
|
||||||
:x: the x position of the mouse
|
:param x: la position x de la souris
|
||||||
:y: the y position of the mouse
|
:param y: la position y de la souris
|
||||||
:dx: the difference of the x mouse axis
|
:param dx: la différence de la position x de la souris
|
||||||
:dy: the difference of the y mouse axis
|
:param dy: la différence de la position y de la souris
|
||||||
"""
|
"""
|
||||||
|
|
||||||
rel_x, rel_y = x - self.x, y - self.y
|
rel_x, rel_y = x - self.x, y - self.y
|
||||||
|
@ -136,39 +119,59 @@ class BoxWidget(Widget, ABC):
|
||||||
old_hovering = self.hovering
|
old_hovering = self.hovering
|
||||||
self.hovering = self.in_bbox((x, y))
|
self.hovering = self.in_bbox((x, y))
|
||||||
|
|
||||||
if old_hovering != self.hovering: # if the hover changed
|
if old_hovering != self.hovering: # si le survole a changé d'état
|
||||||
# call the hover changed event
|
# appelle d'événement on_hover_change
|
||||||
self.trigger_event("on_hover_change", rel_x, rel_y)
|
self.trigger_event("on_hover_change", rel_x, rel_y)
|
||||||
# call the hover enter / leave event
|
# appelle l'événement on_hover_enter ou on_hover_leave selon la valeur
|
||||||
self.trigger_event("on_hover_enter" if self.hovering else "on_hover_leave", rel_x, rel_y)
|
self.trigger_event("on_hover_enter" if self.hovering else "on_hover_leave", rel_x, rel_y)
|
||||||
|
|
||||||
if self.hovering: # if the mouse motion is inside the collision
|
if self.hovering: # si la souris est dans la bbox actuellement
|
||||||
self.trigger_event("on_hover", rel_x, rel_y) # call the hover event
|
self.trigger_event("on_hover", rel_x, rel_y) # appelle l'événement on_hover
|
||||||
|
|
||||||
def on_mouse_press(self, x: int, y: int, button: int, modifiers: int):
|
def on_mouse_press(self, x: int, y: int, button: int, modifiers: int):
|
||||||
|
"""
|
||||||
|
Lorsque la souris est cliqué, cet événement est déclenché.
|
||||||
|
:param x: la position x de la souris
|
||||||
|
:param y: la position y de la souris
|
||||||
|
:param button: button de la souris cliqué
|
||||||
|
:param modifiers: modificateur du bouton de la souris
|
||||||
|
"""
|
||||||
|
|
||||||
rel_x, rel_y = x - self.x, y - self.y
|
rel_x, rel_y = x - self.x, y - self.y
|
||||||
|
|
||||||
self.activated = self.in_bbox((x, y))
|
self.activated = self.in_bbox((x, y))
|
||||||
self.trigger_event("on_activate_change", rel_x, rel_y, button, modifiers)
|
self.trigger_event("on_activate_change", rel_x, rel_y, button, modifiers)
|
||||||
|
|
||||||
if self.activated: # if the click was inside the widget
|
if self.activated: # si le clic s'est produit dans la bbox
|
||||||
|
# appel des événements on_activate_enter
|
||||||
self.trigger_event("on_activate_enter", rel_x, rel_y, button, modifiers)
|
self.trigger_event("on_activate_enter", rel_x, rel_y, button, modifiers)
|
||||||
|
|
||||||
self.clicking = True # the widget is also now clicked
|
self.clicking = True # défini le widget comme étant à présent cliqué
|
||||||
|
# appel des événements on_click_change et on_click_press
|
||||||
self.trigger_event("on_click_change", rel_x, rel_y, button, modifiers)
|
self.trigger_event("on_click_change", rel_x, rel_y, button, modifiers)
|
||||||
self.trigger_event("on_click_press", rel_x, rel_y, button, modifiers)
|
self.trigger_event("on_click_press", rel_x, rel_y, button, modifiers)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
# si le clic n'était pas dans la bbox, appel de l'événement on_activate_leave
|
||||||
self.trigger_event("on_activate_leave", rel_x, rel_y, button, modifiers)
|
self.trigger_event("on_activate_leave", rel_x, rel_y, button, modifiers)
|
||||||
|
|
||||||
def on_mouse_release(self, x: int, y: int, button: int, modifiers: int):
|
def on_mouse_release(self, x: int, y: int, button: int, modifiers: int):
|
||||||
|
"""
|
||||||
|
Lorsque le clic de la souris est relâché, cet événement est déclenché.
|
||||||
|
:param x: la position x de la souris
|
||||||
|
:param y: la position y de la souris
|
||||||
|
:param button: button de la souris cliqué
|
||||||
|
:param modifiers: modificateur du bouton de la souris
|
||||||
|
"""
|
||||||
|
|
||||||
rel_x, rel_y = x - self.x, y - self.y
|
rel_x, rel_y = x - self.x, y - self.y
|
||||||
|
|
||||||
old_click: bool = self.clicking
|
old_click: bool = self.clicking
|
||||||
self.clicking = False # the widget is no longer clicked
|
self.clicking = False # le widget n'est plus cliqué
|
||||||
|
|
||||||
if not self.in_bbox((x, y)): return # if the release was not in the collision, ignore
|
if not self.in_bbox((x, y)): return # si le clic n'a pas été relâché dans la bbox, ignore
|
||||||
|
|
||||||
if old_click: # if this button was the one hovered when the click was pressed
|
if old_click: # si ce bouton était celui qui était survolé lorsque le bouton a été cliqué
|
||||||
|
# déclenche les événements on_click_change et on_click_release
|
||||||
self.trigger_event("on_click_change", rel_x, rel_y, button, modifiers)
|
self.trigger_event("on_click_change", rel_x, rel_y, button, modifiers)
|
||||||
self.trigger_event("on_click_release", rel_x, rel_y, button, modifiers)
|
self.trigger_event("on_click_release", rel_x, rel_y, button, modifiers)
|
||||||
|
|
|
@ -9,9 +9,9 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
class Widget(Listener, ABC):
|
class Widget(Listener, ABC):
|
||||||
"""
|
"""
|
||||||
A Widget that can be attached to a scene.
|
Un widget pouvant être attaché à une scène.
|
||||||
|
|
||||||
It can react to any "on_" event from the scene.
|
Il peut réagir à n'importe quel événement "on_" de la scène.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, scene: "Scene", **kwargs):
|
def __init__(self, scene: "Scene", **kwargs):
|
||||||
|
|
|
@ -9,7 +9,7 @@ from source.type import ColorRGBA
|
||||||
|
|
||||||
class GameWindow(Window): # NOQA
|
class GameWindow(Window): # NOQA
|
||||||
"""
|
"""
|
||||||
Similar to the normal Window, but add small feature useful for a game like a fps counter.
|
Similaire à la classe Window, mais ajoute quelque fonctionnalités pratique pour un jeu. (Option, FPS, etc.)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
|
@ -34,6 +34,10 @@ class GameWindow(Window): # NOQA
|
||||||
pyglet.clock.schedule_once(lambda *_: self.load_option(), 0)
|
pyglet.clock.schedule_once(lambda *_: self.load_option(), 0)
|
||||||
|
|
||||||
def load_option(self):
|
def load_option(self):
|
||||||
|
"""
|
||||||
|
Charge les options depuis le fichier self.option_path.
|
||||||
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if self.option_path.exists():
|
if self.option_path.exists():
|
||||||
self.option = Option.load(self, self.option_path)
|
self.option = Option.load(self, self.option_path)
|
||||||
|
@ -43,4 +47,5 @@ class GameWindow(Window): # NOQA
|
||||||
if self.option is None: self.option = Option(window=self)
|
if self.option is None: self.option = Option(window=self)
|
||||||
|
|
||||||
def on_draw_after(self):
|
def on_draw_after(self):
|
||||||
|
# après que tous les éléments ont été dessinés, dessiner le compteur de FPS s'il est activé.
|
||||||
if self.fps_enable: self._fps_counter.draw()
|
if self.fps_enable: self._fps_counter.draw()
|
||||||
|
|
|
@ -12,8 +12,8 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
class Window(pyglet.window.Window, EventPropagationMixin): # NOQA
|
class Window(pyglet.window.Window, EventPropagationMixin): # NOQA
|
||||||
"""
|
"""
|
||||||
A window. Based on the pyglet window object.
|
Une fenêtre basée sur l'objet Window de pyglet.
|
||||||
Scene can be added to the window
|
Des scènes peuvent y être placé.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -24,12 +24,21 @@ class Window(pyglet.window.Window, EventPropagationMixin): # NOQA
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def childs(self):
|
def childs(self):
|
||||||
|
"""
|
||||||
|
Renvoie les scènes de la fenêtre. Utilisé pour la propagation d'événements.
|
||||||
|
:return: les scènes de la fenêtre.
|
||||||
|
"""
|
||||||
return self._scenes
|
return self._scenes
|
||||||
|
|
||||||
# FPS
|
# FPS
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_fps() -> float:
|
def get_fps() -> float:
|
||||||
|
"""
|
||||||
|
Renvoie le nombre de FPS actuel de la fenêtre.
|
||||||
|
:return: le nombre de FPS actuel de la fenêtre.
|
||||||
|
"""
|
||||||
|
|
||||||
# on récupère la fonction responsable du rafraichissement de la fenêtre
|
# on récupère la fonction responsable du rafraichissement de la fenêtre
|
||||||
refresh_func = pyglet.app.event_loop._redraw_windows # NOQA
|
refresh_func = pyglet.app.event_loop._redraw_windows # NOQA
|
||||||
|
|
||||||
|
@ -47,6 +56,11 @@ class Window(pyglet.window.Window, EventPropagationMixin): # NOQA
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def set_fps(value: float):
|
def set_fps(value: float):
|
||||||
|
"""
|
||||||
|
Définit le nombre de FPS de la fenêtre.
|
||||||
|
:param value: nombre de FPS souhaité
|
||||||
|
"""
|
||||||
|
|
||||||
# on récupère la fonction responsable du rafraichissement de la fenêtre
|
# on récupère la fonction responsable du rafraichissement de la fenêtre
|
||||||
refresh_func = pyglet.app.event_loop._redraw_windows # NOQA
|
refresh_func = pyglet.app.event_loop._redraw_windows # NOQA
|
||||||
|
|
||||||
|
@ -62,24 +76,23 @@ class Window(pyglet.window.Window, EventPropagationMixin): # NOQA
|
||||||
|
|
||||||
# Scene Managing
|
# Scene Managing
|
||||||
|
|
||||||
def set_scene(self, scene_class: Type["Scene"], *scene_args, **scene_kwargs) -> "Scene":
|
def set_scene(self, scene_class: Type["Scene"], **scene_kwargs) -> "Scene":
|
||||||
"""
|
"""
|
||||||
Set the scene of the window.
|
Défini la scène actuelle pour la fenêtre.
|
||||||
:scene_class: the class of the scene to add.
|
:scene_class: la classe de la scène à ajouter
|
||||||
:scene_args: args for the creation of the scene object.
|
:scene_kwargs: les arguments clés de la scène
|
||||||
:scene_kwargs: kwargs for the creation of the scene object.
|
:return: la nouvelle scène créée
|
||||||
:return: the new created scene.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.clear_scene()
|
self.clear_scene()
|
||||||
return self.add_scene(scene_class, *scene_args, **scene_kwargs)
|
return self.add_scene(scene_class, **scene_kwargs)
|
||||||
|
|
||||||
def add_scene(self, scene_class: Type["Scene"], priority: int = 0, **scene_kwargs) -> "Scene":
|
def add_scene(self, scene_class: Type["Scene"], priority: int = 0, **scene_kwargs) -> "Scene":
|
||||||
"""
|
"""
|
||||||
Add a scene of the window.
|
Ajoute une scène à la fenêtre.
|
||||||
:scene_class: the class of the scene to add.
|
:scene_class: la classe de la scène à ajouter
|
||||||
:scene_kwargs: kwargs for the creation of the scene object.
|
:scene_kwargs: les arguments clés de la scène
|
||||||
:return: the new created scene.
|
:return: la nouvelle scène créée
|
||||||
"""
|
"""
|
||||||
|
|
||||||
scene: "Scene" = scene_class(window=self, **scene_kwargs)
|
scene: "Scene" = scene_class(window=self, **scene_kwargs)
|
||||||
|
@ -88,15 +101,15 @@ class Window(pyglet.window.Window, EventPropagationMixin): # NOQA
|
||||||
|
|
||||||
def remove_scene(self, scene: "Scene") -> None:
|
def remove_scene(self, scene: "Scene") -> None:
|
||||||
"""
|
"""
|
||||||
Remove a scene from the window.
|
Retire une scène spécifique de la fenêtre
|
||||||
:scene: the scene to remove.
|
:scene: la scène à retirer
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self._scenes.remove(scene)
|
self._scenes.remove(scene)
|
||||||
|
|
||||||
def clear_scene(self) -> None:
|
def clear_scene(self) -> None:
|
||||||
"""
|
"""
|
||||||
Clear the window from all the scenes.
|
Retire toutes les scènes de la fenêtre.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self._scenes.clear()
|
self._scenes.clear()
|
||||||
|
|
Loading…
Reference in a new issue