added simple ui classes; updated .gitignore file to ignore .idea files

This commit is contained in:
JakubR 2021-05-04 17:04:43 +02:00
parent fa5339685d
commit b1d1066e00
4 changed files with 738 additions and 0 deletions

3
.gitignore vendored
View File

@ -1,3 +1,6 @@
# .idea files
.idea
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]

221
ui/button.py Normal file
View File

@ -0,0 +1,221 @@
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

377
ui/input_box.py Normal file
View File

@ -0,0 +1,377 @@
import pygame
from math import floor
class InputBox:
backspace_tick = 0
backspace_deleted_streak = 0
backspace_breakpoint = 6
deleting_speeds = [12, 4]
# constructor that can be used to set parameters of InputBox instance
def __init__(
self,
position,
dimensions,
input_box_position=None,
input_box_dimensions=None,
label="",
user_input="",
box_color=(195, 195, 195),
input_box_color=(225, 245, 245),
inner_box_color=(205, 205, 205),
bottom_strip_color=(180, 180, 180),
font=pygame.font.get_default_font(),
font_color=(50, 50, 50),
font_size=None,
is_visible=True,
is_active=True,
input_centered=False,
fit_text=True,
outline=True,
outline_thickness=None,
outline_color=(50, 50, 50),
input_box_transform_hover=1.07,
input_box_transform_selected=2,
input_box_transform_inactive=0.9
):
# extracting and setting values
smaller_dimension = min(dimensions)
box_x, box_y = position
width, height = dimensions
# picking default input_box position and dimensions depending on the boxes width
if width < 70:
default_input_box_position = (box_x + width / 20, box_y + height / 2)
default_input_box_dimensions = (width - width / 10, 3 * height / 10)
else:
default_input_box_position = (box_x + width / 12, box_y + height / 2)
default_input_box_dimensions = (width - width / 6, 3 * height / 10)
# counting default outline thickness
default_outline_thickness = (smaller_dimension + (width + height) / 4) / 50
# setting attributes
self.position = position
self.dimensions = dimensions
self.input_box_position = _return_value_or_default(input_box_position, default_input_box_position)
self.input_box_dimensions = _return_value_or_default(input_box_dimensions, default_input_box_dimensions)
self.label = label
self.user_input = user_input
self.box_color = _get_proper_rgb(box_color)
self.ib_color = _get_proper_rgb(input_box_color)
self.inner_box_color = _get_proper_rgb(inner_box_color)
self.bottom_strip_color = _get_proper_rgb(bottom_strip_color)
self.font_color = _get_proper_rgb(font_color)
self.font_size = _return_value_or_default(font_size, floor(smaller_dimension / 2.8))
self.font = pygame.font.SysFont(font, self.font_size)
self.input_font_size = max(15, floor(self.input_box_dimensions[1] - 2))
self.input_font = pygame.font.SysFont(font, self.input_font_size)
self.is_visible = is_visible
self.is_active = is_active
self.is_selected = False
self.input_centered = input_centered
self.fit_text = fit_text
self.outline = outline
self.outline_thickness = _return_value_or_default(outline_thickness, default_outline_thickness)
self.outline_color = outline_color
self.ib_transform_hover = input_box_transform_hover
self.ib_transform_selected = input_box_transform_selected
self.ib_transform_inactive = input_box_transform_inactive
self.backspace_pressed_down = False
# rendering label to get it's width and height
rendered_label = self.font.render(label, 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_label.get_width() > self.input_box_dimensions[0]:
self.font_size = floor(self.font_size / (rendered_label.get_width() / width + 0.1))
self.font = pygame.font.SysFont(font, self.font_size)
# counting colors on: hover, inactive, selected
self.ib_hover_color = _get_proper_rgb(tuple(_transform(self.ib_color, self.ib_transform_hover)))
self.ib_inactive_color = _get_proper_rgb(tuple(_transform(self.ib_color, self.ib_transform_inactive)))
self.ib_selected_color = _get_proper_rgb(tuple(_transform(self.ib_color, self.ib_transform_selected)))
# draws, updates and selects on mouse click the InputBox instance
def run(self, window, mouse_position, events):
self.select_on_click(mouse_position, events)
self.draw(window, mouse_position)
self.update(events)
# draws an InputBox instance (doesn't run interactions [for everything use run(self, ...) method])
def draw(self, window, mouse_position):
# if is_visible=True drawing the InputBox
if self.is_visible:
# extracting and setting values from attributes
box_x, box_y = self.position
width, height = self.dimensions
input_box_x, input_box_y = self.input_box_position
input_width, input_height = self.input_box_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 "transparent" background color
text_background_color = self.ib_color
# setting values accordingly to InputBox'es state
# is selected
if self.is_selected:
input_box_color = self.ib_selected_color
text_background_color = (150, 255, 255)
# is active and mouse is not over
elif not self.is_over(mouse_position) and self.is_active:
input_box_color = self.ib_color
# is active and mouse is over
elif self.is_active:
input_box_color = self.ib_hover_color
# is not active
else:
input_box_color = self.ib_inactive_color
# drawing outer box
pygame.draw.rect(window, self.box_color, (box_x, box_y, width, height))
# drawing inner (upper) box
pygame.draw.rect(window,
self.inner_box_color,
(input_box_x, box_y + height / 10, input_width, height - input_height - height / 2.5)
)
# drawing input (lower) box
pygame.draw.rect(window, input_box_color, (input_box_x, input_box_y, input_width, input_height))
# drawing bottom strip
pygame.draw.rect(window, self.bottom_strip_color, (box_x, box_y + 9 * height / 10, width, 1 * height / 10))
# rendering label and counting its coordinates
rendered_label = self.font.render(self.label, True, self.font_color)
rendered_label_x = box_x + width / 2 - rendered_label.get_width() / 2
rendered_label_y = box_y + height / 4 - rendered_label.get_height() / 2
# drawing the label on the coordinates
window.blit(rendered_label, (rendered_label_x, rendered_label_y))
# rendering input
rendered_input_text = self.input_font.render(self.user_input, True, self.font_color, text_background_color)
# picking input coordinates on center or not depending on input_centered flag value
if self.input_centered:
rendered_input_text_x = input_box_x + input_width / 2 - rendered_input_text.get_width() / 2
rendered_input_text_y = input_box_y + input_height / 2 - rendered_input_text.get_height() / 2
else:
rendered_input_text_x = input_box_x + 2
rendered_input_text_y = input_box_y + input_height / 2 - rendered_input_text.get_height() / 2
# drawing the input on the coordinates
window.blit(rendered_input_text, (rendered_input_text_x, rendered_input_text_y))
# updates InputBox'es attributes if user types something in it
def update(self, events):
# if the InputBox is selected - updating the input text
if self.is_selected:
self._delete_characters_if_backspace_pressed()
# rendering input to get it's width and height
rendered_input = self.input_font.render(self.user_input, True, self.font_color, self.ib_inactive_color)
# deleting or adding characters depending on user's input
for event in events:
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_BACKSPACE:
if not self.backspace_pressed_down:
self.user_input = self.user_input[:-1]
self.backspace_pressed_down = True
elif event.key == pygame.K_RETURN:
self.is_selected = False
# if text isn't too long - adding a character
elif rendered_input.get_width() + 10 < self.input_box_dimensions[0]:
self.user_input += event.unicode
elif event.type == pygame.KEYUP:
if event.key == pygame.K_BACKSPACE:
self.backspace_pressed_down = False
self.backspace_tick = 0
self.backspace_deleted_streak = 0
# checks if the InputBox is clicked and changes is_selected to True or False
def select_on_click(self, mouse_position, events):
# if is active set is_selected attribute accordingly
if self.is_active:
for event in events:
# if mouse is clicked set as selected if mouse is over, or set as not selected otherwise
if event.type == pygame.MOUSEBUTTONUP:
self.set_is_selected(self.is_over(mouse_position))
# 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 InputBox'es label
def get_label(self):
return self.label
# returns InputBox'es user's input
def get_input(self):
return self.user_input
# sets chosen coordinates
def set_space(
self,
position=None,
dimensions=None,
input_box_position=None,
input_box_dimensions=None,
outline_thickness=None
):
if position is not None:
self.position = position
if dimensions is not None:
self.dimensions = dimensions
if input_box_position is not None:
self.input_box_position = input_box_position
if input_box_dimensions is not None:
self.input_box_dimensions = input_box_dimensions
if outline_thickness is not None:
self.outline_thickness = outline_thickness
# sets chosen text attributes
def set_texts(self, label=None, user_input=None):
if label is not None:
self.label = label
if user_input is not None:
self.user_input = user_input
# 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.user_input, 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,
input_box_color=None,
inner_box_color=None,
bottom_strip_color=None,
font_color=None,
outline_color=None,
input_box_transform_hover=None,
input_box_transform_selected=None,
input_box_transform_inactive=None
):
if box_color is not None:
self.box_color = _get_proper_rgb(box_color)
if input_box_color is not None:
self.ib_color = _get_proper_rgb(input_box_color)
if inner_box_color is not None:
self.inner_box_color = _get_proper_rgb(inner_box_color)
if bottom_strip_color is not None:
self.bottom_strip_color = _get_proper_rgb(bottom_strip_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 input_box_transform_hover is not None:
self.ib_transform_hover = input_box_transform_hover
if input_box_transform_selected is not None:
self.ib_transform_selected = input_box_transform_selected
if input_box_transform_inactive is not None:
self.ib_transform_inactive = input_box_transform_inactive
self.ib_hover_color = _get_proper_rgb(tuple(_transform(self.ib_color, self.ib_transform_hover)))
self.ib_inactive_color = _get_proper_rgb(tuple(_transform(self.ib_color, self.ib_transform_inactive)))
self.ib_selected_color = _get_proper_rgb(tuple(_transform(self.ib_color, self.ib_transform_selected)))
# sets chosen flag attributes
def set_flags(self, is_visible=None, is_active=None, input_centered=None, fit_text=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 input_centered is not None:
self.input_centered = input_centered
if fit_text is not None:
self.fit_text = fit_text
if outline is not None:
self.outline = outline
# sets is_selected with a given value
def set_is_selected(self, is_selected):
self.is_selected = is_selected
def _delete_characters_if_backspace_pressed(self):
if self.backspace_pressed_down and \
(self.backspace_tick + 1) % self.deleting_speeds[0] == 0 and \
self.backspace_deleted_streak <= 6:
self.user_input = self.user_input[:-1]
self.backspace_tick = 0
self.backspace_deleted_streak += 1
elif self.backspace_pressed_down and \
(self.backspace_tick + 1) % self.deleting_speeds[1] == 0 and \
self.backspace_deleted_streak > 6:
self.user_input = self.user_input[:-1]
self.backspace_tick = 0
self.backspace_deleted_streak += 1
elif self.backspace_pressed_down:
self.backspace_tick += 1
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

137
ui/text_box.py Normal file
View File

@ -0,0 +1,137 @@
import pygame
from math import floor
class TextBox:
# constructor that can be used to set parameters of TextBox instance
def __init__(
self,
position,
dimensions,
text="",
box_color=(200, 200, 200),
font=pygame.font.get_default_font(),
font_color=(0, 0, 0),
font_size=None,
is_visible=True,
fit_text=True,
outline=True,
outline_thickness=None,
outline_color=(50, 50, 50)
):
# 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.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)
# 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)
# draws the TextBox
def draw(self, window):
# if is_visible=True drawing the TextBox
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)
# drawing the box
pygame.draw.rect(window, self.box_color, (box_x, box_y, width, height))
# rendering text and counting it's coordinates
rendered_text = self.font.render(self.text, True, self.font_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 box on the coordinates
window.blit(rendered_text, (rendered_text_x, rendered_text_y))
# 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 the TextBox's text
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):
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 = outline_color
# sets chosen flags
def set_flags(self, is_visible=None, outline=None):
if is_visible is not None:
self.is_visible = is_visible
if outline is not None:
self.outline = outline
# returns values that are not out of range
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))
# returns value or default depending on if passed value is None or not
def _return_value_or_default(value, default):
return default if value is None else value