import abc import uuid from typing import Optional import pygame from pygame import Surface from sprite.BoundingBox import BoundingBox from sprite.PositionScale import PositionScale from physics.TickData import TickData from ui_elements import CoordinateTransform class UiElement: def __init__(self): self.position_scale = PositionScale() self.visible = True self.click_listeners = [] self.bounding_box = BoundingBox(0, 0, 0, 0) self.bounding_box_margin = (0, 0) self.uuid = uuid.uuid4() self.last_image = None self.last_scaled_image = None def add_click_listener(self, listener): self.click_listeners.append(listener) def remove_click_listener(self, listener): self.click_listeners.remove(listener) def click(self, click_event): for listener in self.click_listeners: listener(click_event) @abc.abstractmethod def tick(self, tick_data: TickData): pass @abc.abstractmethod def render_sprite_image(self) -> Optional[Surface]: pass @abc.abstractmethod def get_bounding_box(self) -> BoundingBox: pass def draw(self, screen: pygame.Surface, screen_transform: PositionScale): if not self.visible: return image = self.render_sprite_image() target_image = None target_position = CoordinateTransform.transform_world_to_screen(self.position_scale.position, screen_transform) if not self.last_image == image or self.last_scaled_image is None: if image is not None: screen_scale = screen_transform.scale object_scale = self.position_scale.scale target_size = (int(screen_scale[0] * object_scale[0] * image.get_width()), int(screen_scale[1] * object_scale[1] * image.get_height())) target_image = UiElement.get_scaled_image(image, target_size) self.last_scaled_image = target_image self.last_image = image else: target_image = self.last_scaled_image if target_image is not None: screen.blit(target_image, target_position) @staticmethod def get_scaled_image(image, resize): return pygame.transform.scale(image, resize)