@@ -36,6 +36,7 @@ class Alignment:
3636 MID_RIGHT = "midright"
3737 CENTER = "center"
3838
39+ @staticmethod
3940 def get_center_pos (parent_size : tuple [int , int ] | Vector2 , element_size : tuple [int , int ] | Vector2 ):
4041 return Vector2 (parent_size [0 ]/ 2 - element_size [0 ]/ 2 , parent_size [1 ]/ 2 - element_size [1 ]/ 2 )
4142
@@ -93,8 +94,9 @@ def handle_events(cls) -> None:
9394
9495
9596class Graphic :
96- def __init__ (self ) -> None :
97+ def __init__ (self , parent : "Panel" = None ) -> None :
9798 self ._visible = True
99+ self .set_parent (parent )
98100 Canvas .add_managed_object (self )
99101
100102 @property
@@ -105,6 +107,20 @@ def visible(self):
105107 def visible (self , value ):
106108 self ._visible = value
107109
110+ @property
111+ def is_rendered (self ):
112+ if self .parent :
113+ return self .visible and self .parent .visible
114+ return self .visible
115+
116+ def _override_rendering (self ):
117+ Canvas .remove_managed_object (self )
118+
119+ def set_parent (self , parent : "Panel" ):
120+ self .parent = parent
121+ if parent :
122+ parent .add_child_element (self )
123+
108124 @abstractmethod
109125 def draw (self , surface : pygame .Surface ):
110126 ...
@@ -129,6 +145,10 @@ def update_managed_objects(cls, graphic_elements: list[Graphic]):
129145 @classmethod
130146 def add_managed_object (cls , graphic : Graphic ):
131147 cls .graphic_elements .append (graphic )
148+
149+ @classmethod
150+ def remove_managed_object (cls , graphic : Graphic ):
151+ cls .graphic_elements .remove (graphic )
132152
133153 @classmethod
134154 def draw (cls ) -> None :
@@ -137,17 +157,52 @@ def draw(cls) -> None:
137157 if not cls .main_surface :
138158 cls .main_surface = pygame .display .get_surface ()
139159 for graphic in cls .graphic_elements :
140- if graphic .visible :
160+ if graphic .is_rendered :
141161 graphic .draw (cls .main_surface )
142162
143163
164+ class Panel (Graphic ):
165+ def __init__ (self , position : Vector2 = Vector2 (0 , 0 ), size : Vector2 = None ,
166+ color : pygame .Color | tuple [int , int , int , int ]= (80 , 80 , 80 , 100 )) -> None :
167+ super ().__init__ ()
168+ self .position = position
169+ self .size = size
170+ self .color = color
171+ self .panel_surface = None
172+ self .child_elements = []
173+
174+ @property
175+ def visible (self ):
176+ return self ._visible
177+
178+ @visible .setter
179+ def visible (self , value ):
180+ self ._visible = value
181+
182+ def add_child_element (self , child_element : Graphic ):
183+ self .child_elements .append (child_element )
184+
185+ def _render (self , surface : pygame .Surface ):
186+ # If size is set to none, it defaults to the screen/surface size
187+ if not self .size :
188+ self .size = surface .get_size ()
189+ self .panel_surface = pygame .Surface (self .size , pygame .SRCALPHA )
190+ self .panel_surface .fill (self .color )
191+
192+ def draw (self , surface : pygame .Surface ):
193+ if not self .panel_surface :
194+ self ._render (surface )
195+ surface .blit (self .panel_surface , self .position )
196+
197+
144198class UiImage (Graphic ):
145199 def __init__ (self , image_surface : pygame .surface = None , file_name : str = None , position : Vector2 = Vector2 (0 , 0 ), size : Vector2 = None ,
146- color_tint : pygame .Color | tuple [int , int , int , int ]= (255 , 255 , 255 , 255 ), blend_mode : int = pygame .BLEND_RGBA_MULT ) -> None :
200+ color_tint : pygame .Color | tuple [int , int , int , int ]= (255 , 255 , 255 , 255 ), blend_mode : int = pygame .BLEND_RGBA_MULT ,
201+ parent : Panel = None ) -> None :
147202 """
148203 Specify either the image surface or the image path, not both.
149204 """
150- super ().__init__ ()
205+ super ().__init__ (parent )
151206 self .position = position
152207 self .size = size
153208 self .color_tint = color_tint
@@ -182,32 +237,11 @@ def draw(self, surface: pygame.Surface):
182237 surface .blit (self .image_surface , self .position )
183238
184239
185- class Panel (Graphic ):
186- def __init__ (self , position : Vector2 = Vector2 (0 , 0 ), size : Vector2 = None ,
187- color : pygame .Color | tuple [int , int , int , int ]= (80 , 80 , 80 , 100 )) -> None :
188- super ().__init__ ()
189- self .position = position
190- self .size = size
191- self .color = color
192- self .panel_surface = None
193-
194- def _render (self , surface : pygame .Surface ):
195- # If size is set to none, it defaults to the screen/surface size
196- if not self .size :
197- self .size = surface .get_size ()
198- self .panel_surface = pygame .Surface (self .size , pygame .SRCALPHA )
199- self .panel_surface .fill (self .color )
200-
201- def draw (self , surface : pygame .Surface ):
202- if not self .panel_surface :
203- self ._render (surface )
204- surface .blit (self .panel_surface , self .position )
205-
206-
207240class Label (Graphic ):
208241 def __init__ (self , text : str | Any = "" , color : pygame .Color | tuple [int , int , int ]= (255 , 255 , 255 ),
209- font_name : str = None , font_size : int = 28 , position : Vector2 = Vector2 (0 , 0 ), anchor : Alignment | str = "midleft" ) -> None :
210- super ().__init__ ()
242+ font_name : str = None , font_size : int = 28 , position : Vector2 = Vector2 (0 , 0 ), anchor : Alignment | str = "midleft" ,
243+ parent : Panel = None ) -> None :
244+ super ().__init__ (parent )
211245 self .font = pygame .font .Font (font_name , font_size )
212246 self .text = str (text )
213247 self .color = color
@@ -257,11 +291,13 @@ def __init__(self, image: UiImage=None, on_click: Callable=None,
257291 position : Vector2 = Vector2 (0 , 0 ), size : Vector2 = Vector2 (150 , 75 ),
258292 color : pygame .Color | tuple [int , int , int ]= (255 , 255 , 255 ), hover_color : pygame .Color | tuple [int , int , int ]= (220 , 220 , 220 ),
259293 pressed_color : pygame .Color | tuple [int , int , int ]= (185 , 185 , 185 ), disabled_color : pygame .Color | tuple [int , int , int ]= (165 , 165 , 165 ),
260- border_radius : int = 0 , disabled : bool = False , label : Label = None , label_alignment : Alignment | str = "center" ) -> None :
261- Graphic .__init__ (self )
294+ border_radius : int = 0 , disabled : bool = False , label : Label = None , label_alignment : Alignment | str = "center" ,
295+ parent : Panel = None ) -> None :
296+ Graphic .__init__ (self , parent )
262297 Graphic_Event .__init__ (self )
263298 self .button_image = image
264299 if self .button_image :
300+ self .button_image ._override_rendering ()
265301 self .button_image .position = position
266302 self .button_image .size = size
267303 self .button_image .scale_image ()
@@ -282,21 +318,10 @@ def __init__(self, image: UiImage=None, on_click: Callable=None,
282318 self .label = label
283319 self .label_alignment = label_alignment
284320 if self .label :
321+ self .label ._override_rendering ()
285322 self .label ._render ()
286323 self .set_label_pos ()
287324
288- @property
289- def visible (self ):
290- return self ._visible
291-
292- @visible .setter
293- def visible (self , value ):
294- if self .button_image :
295- self .button_image .visible = value
296- if self .label :
297- self .label .visible = value
298- self ._visible = value
299-
300325 def set_color (self , color : pygame .Color | tuple [int , int , int ]):
301326 self .current_color = color
302327 if self .button_image :
@@ -395,9 +420,9 @@ def __init__(self, on_value_change: Callable=None,
395420 color : pygame .Color | tuple [int , int , int ]= (255 , 255 , 255 ), hover_color : pygame .Color | tuple [int , int , int ]= (220 , 220 , 220 ),
396421 pressed_color : pygame .Color | tuple [int , int , int ]= (185 , 185 , 185 ), disabled_color : pygame .Color | tuple [int , int , int ]= (165 , 165 , 165 ),
397422 tick_color : pygame .Color | tuple [int , int , int ]= (55 , 55 , 55 ), is_on : bool = False ,
398- border_radius : int = 0 , disabled : bool = False , label_alignment : Alignment | str = "midleft" ) -> None :
423+ border_radius : int = 0 , disabled : bool = False , label_alignment : Alignment | str = "midleft" , parent : Panel = None ) -> None :
399424 super ().__init__ (None , on_value_change , position , size , color , hover_color , pressed_color , disabled_color ,
400- border_radius , disabled , None , label_alignment )
425+ border_radius , disabled , None , label_alignment , parent )
401426 self .tick_color = tick_color
402427 self .is_on = is_on
403428 self .tick_rect = pygame .Rect (Vector2 (position .x + size .x / 4 , position .y + size .y / 4 ), Vector2 (size .x / 2 , size .y / 2 ))
@@ -419,8 +444,9 @@ def __init__(self, on_value_change: Callable=None, on_delete: Callable=None,
419444 box_color : pygame .Color | tuple [int , int , int ]= (255 , 255 , 255 ), border_thickness : int = 2 ,
420445 border_color_active : pygame .Color | tuple [int , int , int ]= (20 , 20 , 20 ),
421446 border_color_inactive : pygame .Color | tuple [int , int , int ]= (100 , 100 , 100 ),
422- text_color : pygame .Color | tuple [int , int , int ]= (20 , 20 , 20 ), text : str | Any = "" , font_name : str = None , font_size : int = 28 ):
423- super ().__init__ (text , text_color , font_name , font_size , position )
447+ text_color : pygame .Color | tuple [int , int , int ]= (20 , 20 , 20 ), text : str | Any = "" , font_name : str = None , font_size : int = 28 ,
448+ parent : Panel = None ) -> None :
449+ super ().__init__ (text , text_color , font_name , font_size , position , parent = parent )
424450 Graphic_Event .__init__ (self )
425451 self .rect = pygame .Rect (position , size )
426452 self .position = position
0 commit comments