import pygame from math import floor class Button: # constructor that can be used to set parameters of Button instance def __init__( self, position, dimensions, text="", box_color=(195, 195, 195), font=pygame.font.get_default_font(), font_color=(50, 50, 50), font_size=None, is_visible=True, is_active=True, fit_text=True, outline=True, outline_thickness=None, outline_color=(50, 50, 50), box_transform_hover=1.16, box_transform_inactive=0.9, font_transform_hover=1.24, font_transform_inactive=0.85 ): # extracting and setting values smaller_dimension = min(dimensions) width, height = dimensions # counting default outline thickness default_outline_thickness = (smaller_dimension + (width + height) / 4) / 50 # setting attributes self.position = position self.dimensions = dimensions self.text = text self.box_color = _get_proper_rgb(box_color) self.font_color = _get_proper_rgb(font_color) self.font_size = _return_value_or_default(font_size, floor(smaller_dimension / 2)) self.font = pygame.font.SysFont(font, self.font_size) self.is_visible = is_visible self.is_active = is_active self.fit_text = fit_text self.outline = outline self.outline_thickness = _return_value_or_default(outline_thickness, default_outline_thickness) self.outline_color = _get_proper_rgb(outline_color) self.box_transform_hover = box_transform_hover self.box_transform_inactive = box_transform_inactive self.font_transform_hover = font_transform_hover self.font_transform_inactive = font_transform_inactive # rendering text to get it's width and height rendered_text = self.font.render(text, True, (0, 0, 0)) # if text is out of bounds and fit_text=True - resizing text to fit the box if self.fit_text and rendered_text.get_width() > self.dimensions[0]: self.font_size = floor(self.font_size / (rendered_text.get_width() / width + 0.1)) self.font = pygame.font.SysFont(font, self.font_size) # counting colors on: hover, inactive self.box_hover_color = _get_proper_rgb(tuple(_transform(self.box_color, self.box_transform_hover))) self.box_inactive_color = _get_proper_rgb(tuple(_transform(self.box_color, self.box_transform_inactive))) self.font_hover_color = _get_proper_rgb(tuple(_transform(self.font_color, self.font_transform_hover))) self.font_inactive_color = _get_proper_rgb(tuple(_transform(self.font_color, self.font_transform_inactive))) # draws the Button instance def draw(self, window, mouse_position): # if is_visible=True - drawing Button if self.is_visible: # extracting and setting values from attributes box_x, box_y = self.position width, height = self.dimensions # if outline=True - drawing an outline if self.outline: padding = self.outline_thickness outline_coordinates = (box_x - padding, box_y - padding, width + 2 * padding, height + 2 * padding) pygame.draw.rect(window, self.outline_color, outline_coordinates) # setting values accordingly to InputBox'es state # is active and mouse is not over if not self.is_over(mouse_position) and self.is_active: pygame.draw.rect(window, self.box_color, (box_x, box_y, width, height)) text_color = self.font_color # is active and mouse is over elif self.is_active: pygame.draw.rect(window, self.box_hover_color, (box_x, box_y, width, height)) text_color = self.font_hover_color # is not active else: pygame.draw.rect(window, self.box_inactive_color, (box_x, box_y, width, height)) text_color = self.font_inactive_color # rendering text and counting its coordinates rendered_text = self.font.render(self.text, True, text_color) rendered_text_x = box_x + width / 2 - rendered_text.get_width() / 2 rendered_text_y = box_y + height / 2 - rendered_text.get_height() / 2 # drawing the text on the coordinates window.blit(rendered_text, (rendered_text_x, rendered_text_y)) # returns True if the Button is clicked, or False otherwise def is_clicked(self, mouse_position, events): # if is_active=True and mouse is over the Button - checking if mouse is clicked if self.is_active and self.is_over(mouse_position): # if mouse is clicked returning True, otherwise False for event in events: if event.type == pygame.MOUSEBUTTONUP: return True return False # checks if a position is over the InputBox and returns True or False def is_over(self, position): mouse_x, mouse_y = position button_x, button_y = self.position width, height = self.dimensions return button_x <= mouse_x <= (button_x + width) and button_y <= mouse_y <= (button_y + height) # returns text value def get_text(self): return self.text # sets chosen coordinates def set_coordinates(self, position=None, dimensions=None, outline_thickness=None): if position is not None: self.position = position if dimensions is not None: self.dimensions = dimensions if outline_thickness is not None: self.outline_thickness = outline_thickness # sets text attribute def set_text(self, text): self.text = text # sets chosen font attributes def set_font(self, font=None, font_size=None): if font_size is not None: self.font_size = font_size # rendering text to get it's width and height rendered_text = self.font.render(self.text, True, (0, 0, 0)) # if text is out of bounds and fit_text=True - resizing text to fit the box if self.fit_text and rendered_text.get_width() > self.dimensions[0]: self.font_size = floor(self.font_size / (rendered_text.get_width() / self.dimensions[0] + 0.1)) self.font = pygame.font.SysFont(font, self.font_size) if font is not None: self.font = pygame.font.SysFont(font, self.font_size) # sets chosen color attributes def set_colors( self, box_color=None, font_color=None, outline_color=None, box_transform_hover=None, box_transform_inactive=None, font_transform_hover=None, font_transform_inactive=None ): if box_color is not None: self.box_color = _get_proper_rgb(box_color) if font_color is not None: self.font_color = _get_proper_rgb(font_color) if outline_color is not None: self.outline_color = _get_proper_rgb(outline_color) if box_transform_hover is not None: self.box_transform_hover = box_transform_hover if box_transform_inactive is not None: self.box_transform_inactive = box_transform_inactive if font_transform_hover is not None: self.font_transform_hover = font_transform_hover if font_transform_inactive is not None: self.font_transform_inactive = font_transform_inactive self.box_hover_color = _get_proper_rgb(tuple(_transform(self.box_color, self.box_transform_hover))) self.box_inactive_color = _get_proper_rgb(tuple(_transform(self.box_color, self.box_transform_inactive))) self.font_hover_color = _get_proper_rgb(tuple(_transform(self.font_color, self.font_transform_hover))) self.font_inactive_color = _get_proper_rgb(tuple(_transform(self.font_color, self.font_transform_inactive))) # sets chosen flags def set_flags(self, is_visible=None, is_active=None, outline=None): if is_visible is not None: self.is_visible = is_visible if is_active is not None: self.is_active = is_active if outline is not None: self.outline = outline def _get_proper_rgb(x): r, g, b = x r, g, b = max(0, min(255, floor(r))), max(0, min(255, floor(g))), max(0, min(255, floor(b))) return tuple((r, g, b)) def _transform(iterable, transform_value): return [x * transform_value for x in iterable] def _return_value_or_default(value, default): return default if value is None else value