@@ -51,6 +51,9 @@ def __init__(self,
5151 # Framerate
5252 self .framerate = 60
5353
54+ # Clicked
55+ self .clicked = False
56+
5457
5558 """
5659 Setters
@@ -249,10 +252,22 @@ def is_clicked(self):
249252 # Check mouse over and clicked conditions
250253 if self .rect .collidepoint (mouse_pos ):
251254 if pygame .mouse .get_pressed ()[0 ] == 1 : # == 1 is left click
255+ self .clicked = True
252256 return True
253257
254258 return False
255259
260+ def was_clicked (self ):
261+ """
262+ Returnes true if the element was clicked and then released
263+ """
264+ if self .is_clicked ():
265+ return
266+
267+ if self .clicked :
268+ self .clicked = False
269+ return True
270+
256271 """
257272 Basic functions
258273 """
@@ -303,14 +318,15 @@ def __init__(self,
303318 self .content = content
304319 self .font_size = font_size
305320 self .font_family = font_family
321+ self .font = pygame .font .SysFont (self .font_family , self .font_size )
306322
307323 # Get the dimensions of the text
308324 if width != 0 and height != 0 :
309325 text_dimensions = (width , height )
310326 else :
311327 text_dimensions = self .get_text_rect_dimensions ()
312328
313- super ().__init__ (position , text_dimensions [0 ], text_dimensions [1 ], color , centered )
329+ super ().__init__ (position , width = text_dimensions [0 ], height = text_dimensions [1 ], color = color , centered = centered )
314330
315331 # Render text
316332 self .text_surface = self .render_text ()
@@ -336,6 +352,24 @@ def render_text(self) -> pygame.Surface:
336352 text_surface = font .render (self .content , True , self .color )
337353 return text_surface
338354
355+ def change_text (self , new_text : str ) -> None :
356+ """
357+ Change the text of the element
358+ :param new_text: str with the new text
359+ :return: None
360+ """
361+ self .content = new_text
362+ self .text_surface = self .render_text ()
363+
364+ def change_text_color (self , new_color : tuple [int , int , int ]) -> None :
365+ """
366+ Change the color of the text
367+ :param new_color: tuple[int, int, int] with the new color
368+ :return: None
369+ """
370+ self .color = new_color
371+ self .text_surface = self .render_text ()
372+
339373 def draw (self , surface : pygame .Surface ) -> None :
340374 """
341375 Draw the text in the surface
@@ -346,7 +380,13 @@ def draw(self, surface: pygame.Surface) -> None:
346380 if not self .display :
347381 return
348382
349- surface .blit (self .text_surface , self .rect )
383+ rect = self .text_surface .get_rect ()
384+ if self .centered :
385+ rect .center = self .rect .center
386+ else :
387+ rect .topleft = self .rect .topleft
388+
389+ surface .blit (self .text_surface , rect )
350390
351391class Image (Element ):
352392 """
@@ -392,6 +432,9 @@ def draw(self, surface: pygame.Surface) -> None:
392432 surface .blit (self .image , self .rect )
393433
394434class Input (Text ):
435+ """
436+ Input element that can be displayed
437+ """
395438 def __init__ (self ,
396439 position : tuple [int , int ],
397440 width : int = 200 ,
@@ -406,8 +449,30 @@ def __init__(self,
406449 font_family : str = "Arial" ,
407450 hint : str = "" ,
408451 centered : bool = False ):
409-
410- super ().__init__ (position , content = hint , color = passive_text_color , font_size = font_size , font_family = font_family , width = width , height = height , centered = centered )
452+ """
453+ Create an input element
454+ :param position: Where the input will be positioned
455+ :param width: Width of the input
456+ :param height: Height of the input
457+ :param passive_text_color: Color of the text when the input is not active
458+ :param active_text_color: Color of the text when the input is active
459+ :param passive_border_color: Color of the border when the input is not active
460+ :param active_border_color: Color of the border when the input is active
461+ :param border_radius: Radius of the border
462+ :param border_width: Width of the border
463+ :param font_size: Size of the font
464+ :param font_family: Font family of the text
465+ :param hint: Hint of the input
466+ """
467+
468+ super ().__init__ (position ,
469+ content = hint ,
470+ color = passive_text_color ,
471+ font_size = font_size ,
472+ font_family = font_family ,
473+ width = width ,
474+ height = height ,
475+ centered = centered )
411476
412477 # Visual attributes
413478 self .passive_text_color = passive_text_color
@@ -417,13 +482,246 @@ def __init__(self,
417482 self .border_radius = border_radius
418483 self .border_width = border_width
419484
485+ # Text attributes
486+ self .hint = hint
487+ self .text = ""
488+
420489 # States
421490 self .active = False
422491
492+ # Filer
493+ self .filter = None
494+ self .filter_mode_exclude = True
495+
496+ # Keys
497+ self .exclude_keys = [pygame .KMOD_SHIFT , pygame .KMOD_CAPS , pygame .K_CAPSLOCK , pygame .K_LSHIFT , pygame .K_RSHIFT ]
498+ self .exit_keys = [pygame .K_RETURN , pygame .K_KP_ENTER , pygame .K_TAB , pygame .K_ESCAPE ]
499+
500+ # Cursor
501+ self .cursor = False
502+ self .cursor_index = 0
503+
504+ """
505+ Getters
506+ """
507+
508+ def get_text (self ):
509+ """
510+ Get the text of the input
511+ """
512+ return self .text
513+
514+ """
515+ Filter
516+ """
517+
518+ def set_filter (self , new_filter : str , exclude_mode : bool = True ) -> None :
519+ """
520+ Set the filter of the input
521+ :param new_filter: str with the new filter
522+ :param exclude_mode: If the true, the filter will exclude the characters in the filter, if false, the filter will only allow characters in the filter
523+ """
524+ self .filter = new_filter
525+ self .filter_mode_exclude = exclude_mode
526+
527+ def allow_key (self , key : str ):
528+ """
529+ Check if the key is allowed by the filter
530+ :param key: str with the key to be checked
531+ """
532+ if not self .filter :
533+ return True
534+
535+ if self .filter_mode_exclude :
536+ if key not in self .filter :
537+ return True
538+ else :
539+ if key in self .filter :
540+ return True
541+
542+ return False
543+
544+ """
545+ Typing
546+ """
547+ def handle_keys (self , events : list ) -> None :
548+ """
549+ Handle the typing of the input
550+ :param events: list with the events
551+ """
552+ for event in events :
553+ if event .type != pygame .KEYDOWN :
554+ continue
555+ elif event .key in self .exit_keys :
556+ self .active = False
557+ elif event .key in self .exclude_keys :
558+ continue
559+ elif event .key == pygame .K_RIGHT :
560+ if self .cursor_index >= len (self .text ):
561+ continue
562+ self .cursor_index += 1
563+ elif event .key == pygame .K_LEFT :
564+ if self .cursor_index <= 0 :
565+ continue
566+ self .cursor_index -= 1
567+ elif event .key == pygame .K_BACKSPACE :
568+ if self .cursor_index == 0 :
569+ continue
570+
571+ self .text = self .text [:self .cursor_index - 1 ] + self .text [self .cursor_index :]
572+ self .cursor_index -= 1
573+ elif event .key == pygame .K_DELETE :
574+ if self .cursor_index == len (self .text ):
575+ continue
576+
577+ self .text = self .text [:self .cursor_index ] + self .text [self .cursor_index + 1 :]
578+ else :
579+ if not self .allow_key (event .unicode ):
580+ continue
581+
582+ self .text = self .text [:self .cursor_index ] + event .unicode + self .text [self .cursor_index :]
583+ self .cursor_index += 1
584+
585+ self .change_text (self .text )
586+
587+ def draw_cursor (self , surface : pygame .Surface ):
588+ """
589+ Draw the cursor
590+ :param surface: pygame.Surface where the cursor will be drawn
591+ """
592+ if not self .cursor :
593+ return
594+
595+ font = pygame .font .SysFont (self .font_family , self .font_size )
596+ cursor_surface = font .render ("|" , True , self .active_text_color )
597+
598+ text_to_cursor = Text (self .get_position (), self .text [:self .cursor_index ], self .active_text_color ,
599+ self .font_size , self .font_family , self .centered )
600+
601+ cursor_position = (self .rect .x + text_to_cursor .rect .width , self .rect .y )
602+
603+ if self .centered :
604+ print ((self .rect .width - self .get_text_rect_dimensions ()[0 ])// 2 )
605+ cursor_position = (self .rect .x + (self .rect .width - self .get_text_rect_dimensions ()[0 ]), self .rect .centery - text_to_cursor .rect .height // 2 )
606+
607+ surface .blit (cursor_surface , cursor_position )
608+
609+ """
610+ Basic functions
611+ """
612+
423613 def draw (self , surface : pygame .surface ):
614+ """
615+ Draw the input and the cursor
616+ :param surface: pygame.Surface where the input will be drawn
617+ """
424618 super ().draw (surface )
425619
426620 if self .active :
621+ self .draw_cursor (surface )
427622 pygame .draw .rect (surface , self .active_border_color , self .rect , self .border_width , border_radius = self .border_radius )
428623 else :
429- pygame .draw .rect (surface , self .passive_border_color , self .rect , self .border_width , border_radius = self .border_radius )
624+ pygame .draw .rect (surface , self .passive_border_color , self .rect , self .border_width , border_radius = self .border_radius )
625+
626+ def update (self , events : list ) -> None :
627+ """
628+ Update the input
629+ :param events: list with the events
630+ """
631+ super ().update ()
632+
633+ # Check if the input was clicked
634+ if self .was_clicked ():
635+ self .active = True
636+ # Check if the input was clicked outside, if so, deactivate it
637+ elif self .active :
638+ self .handle_keys (events )
639+ if pygame .mouse .get_pressed ()[0 ] == 1 and not self .is_clicked ():
640+ self .active = False
641+ if self .text != "" :
642+ self .change_text (self .text )
643+ else :
644+ self .change_text (self .hint )
645+
646+ class Button (Element ):
647+ """
648+ Button element that can be displayed
649+ """
650+ def __init__ (self ,
651+ position : tuple [int , int ],
652+ width : int = 200 ,
653+ height : int = 50 ,
654+ border_radius : int = 10 ,
655+ content : str = "Click me." ,
656+ color : tuple [int , int , int ] = (255 , 255 , 255 ),
657+ hover_color : tuple [int , int , int ] = (200 , 200 , 200 ),
658+ click_color : tuple [int , int , int ] = (150 , 150 , 150 ),
659+ text_color : tuple [int , int , int ] = (100 , 100 , 100 ),
660+ text_hover_color : tuple [int , int , int ] = (0 , 0 , 0 ),
661+ text_click_color : tuple [int , int , int ] = (0 , 0 , 0 ),
662+ font_size : int = 20 ,
663+ font_family : str = "Arial" ,
664+ centered : bool = False ):
665+ """
666+ Create a button element
667+ :param position: Where the button will be positioned
668+ :param width: Width of the button
669+ :param height: Height of the button
670+ :param content: Text of the button
671+ :param color: Color of the button
672+ :param hover_color: Color of the button when hovered
673+ :param click_color: Color of the button when clicked
674+ :param font_size: Size of the font
675+ :param font_family: Font family of the text
676+ :param centered: If the button will be centered in the position
677+ """
678+
679+ super ().__init__ (position , width , height , color , border_radius , centered )
680+
681+ self .text_object = Text (position , content , text_color , font_size , font_family , width , height , centered )
682+
683+ # Button attributes
684+ self .text = content
685+ self .color = color
686+ self .hover_color = hover_color
687+ self .click_color = click_color
688+ self .border_radius = border_radius
689+
690+ # Text attributes
691+ self .text_color = text_color
692+ self .text_hover_color = text_hover_color
693+ self .text_click_color = text_click_color
694+
695+ # States
696+ self .hovered = False
697+ self .clicked = False
698+
699+ def draw (self , surface : pygame .Surface ) -> None :
700+ """
701+ Draw the button
702+ :param surface: pygame.Surface where the button will be drawn
703+ """
704+ if not self .display :
705+ return
706+
707+ if self .clicked :
708+ pygame .draw .rect (surface , self .click_color , self .rect , border_radius = self .border_radius )
709+ self .text_object .change_text_color (self .text_click_color )
710+ elif self .hovered :
711+ pygame .draw .rect (surface , self .hover_color , self .rect , border_radius = self .border_radius )
712+ self .text_object .change_text_color (self .text_hover_color )
713+ else :
714+ pygame .draw .rect (surface , self .color , self .rect , border_radius = self .border_radius )
715+ self .text_object .change_text_color (self .text_color )
716+
717+ self .text_object .draw (surface )
718+
719+ def update (self ) -> None :
720+ """
721+ Update the button,
722+ Collects the events and updates the button
723+ """
724+ self .text_object .update ()
725+
726+ # Check if the button is hovered
727+ self .hovered = self .is_hovered ()
0 commit comments