diff --git a/.gitignore b/.gitignore index bee8a64..083d732 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ __pycache__ +.idea diff --git a/dariuino-ide.py b/dariuino-ide.py index 73a5f36..f8aa5a1 100755 --- a/dariuino-ide.py +++ b/dariuino-ide.py @@ -21,19 +21,33 @@ class Statement: def main(): + ui = gui.UI('Dariuino IDE', gui.Dimensions(1920, 1080)) + resources = gui.ResourceContainer.from_json_file('res/resources.json') container = gui.Container(gui.Position(0, 0), gui.Dimensions(600, 600)) c = gui.Dragable(resources.get_image('icon-for-32'), gui.Position(100, 300)) - c.on_click = lambda : print('Hello world') + c.on_click = lambda: print('Hello world') a = gui.Dragable(resources.get_image('icon-while-32'), gui.Position(150, 350)) + a.on_drag = lambda: a.set_transparency(0.5) + a.on_drop = lambda dropable: a.set_transparency(0.0) + a.on_fail = lambda drag : a.set_transparency(0.0) + d = gui.Dropable(gui.Position(0, 0), gui.Dimensions(50, 100)) + d.background_color = gui.Color(255, 0, 0) + d.on_drop = lambda dragable: print(dragable) + + container.add_dropable(d) container.add_clickable(c) container.add_clickable(a) - ui = gui.UI('Dariuino IDE', gui.Dimensions(1920, 1080)) + block = CodeBlock(BlockColor.VIOLET, resources.get_image('icon-for-32')) + block.add_text('Hallo liebe rosa Welt mit Regenbögen und Einhörnern.') + + container.add_clickable(block) + ui.add_container(container) ui.init() ui.update() diff --git a/src/dariuino.py b/src/dariuino.py index 526793c..fbe11bd 100644 --- a/src/dariuino.py +++ b/src/dariuino.py @@ -1,3 +1,5 @@ +import pygame.draw + import src.gui as gui @@ -9,8 +11,71 @@ class BlockColor: YELLOW = gui.Color(255, 255, 0) -class CodeBlock(gui.Clickable): - def __init__(self, color: gui.Color, on_click_callback: callable): - Clickable.__init__(self, gui.Canvas(500, 200), gui.Position(0, 0)) - self.on_click = on_click_callable +class CodeBlock(gui.ListItem): + TEXT_SIZE = 24 + TEXT_COLOR = gui.Color(255, 255, 255) + TEXT_FONT = 'freesansbold.ttf' + RADIUS_CORNERS = 30 + PADDING = 20 + SPACE = 10 + def __init__(self, color: gui.Color, icon: gui.Image): + gui.ListItem.__init__(self, gui.Canvas(gui.Dimensions(500, 100))) + self.icon = icon + self.color = color + self.content = [] + + def add_text(self, text: str) -> None: + for word in text.split(' '): + self.add_visualization( + gui.Text(word, size = self.TEXT_SIZE, font_family = self.TEXT_FONT, color = self.TEXT_COLOR) + ) + + def add_visualization(self, visualization: gui.Visualization) -> None: + self.content.append(visualization) + + def draw(self, canvas: gui.Canvas) -> None: + self.canvas.clear() + + self._render_background() + + self.canvas.draw_inside(self.icon, gui.Position(self.PADDING, self.PADDING)) + + line_dimensions = gui.Dimensions(self.icon.get_dimensions().width, self.PADDING) + line_dimensions.width += self.PADDING + + max_height = self.icon.get_dimensions().height + + for drawable in self.content: + line_dimensions.width += self.SPACE + max_height = max(max_height, drawable.get_dimensions().height) + + if line_dimensions.width + drawable.get_dimensions().width > self.canvas.get_dimensions().width: + line_dimensions.width = self.PADDING + self.icon.get_dimensions().width + self.SPACE + line_dimensions.height += max_height + max_height = drawable.get_dimensions().height + + self.canvas.draw_inside(drawable, gui.Position(line_dimensions.width, line_dimensions.height)) + + line_dimensions.width += drawable.get_dimensions().width + + gui.Visualization.draw(self, canvas) + + def _render_background(self): + self.canvas.clear(None) + + pygame.draw.rect( + self.canvas.surface, + self.color.as_tuple(), + (0, 0, self.canvas.get_dimensions().width, self.canvas.get_dimensions().height), + 0, + self.RADIUS_CORNERS + ) + + +class Code(gui.ItemList): + def __init__(self, position: gui.Position): + gui.ItemList.__init__(self, position, gui.Dimensions(500, 250)) + + def add_code_block(self, code_block: CodeBlock): + self.add_item(code_block) diff --git a/src/gui.py b/src/gui.py index 1126674..bb7068c 100755 --- a/src/gui.py +++ b/src/gui.py @@ -53,11 +53,14 @@ class Dimensions: self.width = width self.height = height + def __add__(self, dimensions: Self): + return Dimensions(self.width + dimensions.width, self.height + dimensions.height) + def as_tuple(self): - return (self.width, self.height) + return self.width, self.height @staticmethod - def from_tuple(tuple): + def from_tuple(tuple: Tuple): return Dimensions(tuple[0], tuple[1]) @@ -69,6 +72,10 @@ class Drawable: self.surface.blit(drawable.surface, position.as_tuple()) def clear(self, color = Color(0, 0, 0)): + if color is None: + self.surface.fill(pygame.Color(0, 0, 0, 0)) + return + self.surface.fill(color.as_tuple()) def get_image(self): @@ -80,6 +87,9 @@ class Drawable: def get_dimensions(self) -> Dimensions: return Dimensions.from_tuple(self.surface.get_size()) + def set_transparency(self, transparency: float): + self.surface.set_alpha(255 - 255 * transparency) + class Image(Drawable): def __init__(self, path = None): @@ -98,6 +108,7 @@ class Screen: def __init__(self, title: str, resolution: Dimensions): self.resolution = resolution self.screen = pygame.display.set_mode(resolution.as_tuple(), pygame.NOFRAME | pygame.FULLSCREEN | pygame.DOUBLEBUF) + pygame.display.set_caption(title) def get_canvas(self): @@ -106,9 +117,6 @@ class Screen: return canvas - def get_image(self): - image = Image() - def update(self) -> None: pygame.dispay.flip() @@ -122,14 +130,20 @@ class Screen: return resolutions -class Visualization(): - def __init__(self, surface: Canvas, position: Position): - self.surface = surface +class Visualization: + def __init__(self, canvas: Canvas, position: Position): + self.canvas = canvas self.position = position self.on_hover = lambda : None def draw(self, canvas: Canvas) -> None: - canvas.draw_inside(self.surface, self.position) + canvas.draw_inside(self.canvas, self.position) + + def set_transparency(self, transparency: float): + self.canvas.set_transparency(transparency) + + def get_dimensions(self) -> Dimensions: + return self.canvas.get_dimensions() def is_inside(self, position: Position) -> bool: if position.x < self.position.x: @@ -138,7 +152,7 @@ class Visualization(): if position.y < self.position.y: return False - dimensions = self.surface.get_dimensions() + dimensions = self.canvas.get_dimensions() if position.x > self.position.x + dimensions.width: return False @@ -161,33 +175,21 @@ class Clickable(Visualization): class Dragable(Clickable): def __init__(self, canvas: Canvas, position: Position): Clickable.__init__(self, canvas, position) - self.on_drag = lambda : None - self.on_drop = lambda : None - self.on_fail = lambda drag : None + self.on_drag = lambda: None + self.on_drop = lambda dropable: None + self.on_fail = lambda drag: None def drag(self) -> None: self.on_drag() - def drop(self) -> None: - self.on_drop() + def drop(self, dropable: 'Dropable') -> None: + self.on_drop(dropable) def fail(self, drag: 'Drag'): self.on_fail(drag) -class Dropable(Visualization): - def __init__(self, canvas: Canvas, position: Position): - Visualization.__init__(self, canvas, position) - self.on_drop = lambda dragable: None - - def drop(self, dragable: Dragable) -> None: - self.on_drop(dragable) - - class Container(Visualization): - ROWS = 0 - COLUMNS = 1 - def __init__(self, position: Position, dimensions: Dimensions): Visualization.__init__(self, Canvas(dimensions), position) @@ -195,18 +197,19 @@ class Container(Visualization): self.background_color = Color(0, 0, 0) self.visualizations = [] self.clickables = [] + self.dropables = [] def set_background_image(self, image: Image) -> None: self.background_image = image def render(self) -> None: - self.surface.clear(self.background_color) + self.canvas.clear(self.background_color) if self.background_image is not None: - self.surface.draw_inside(self.background_image, Dimensions(0, 0)) + self.canvas.draw_inside(self.background_image, Dimensions(0, 0)) for visualization in self.visualizations: - visualization.draw(self.surface) + visualization.draw(self.canvas) def add_visualization(self, visualization: Visualization) -> None: self.visualizations.append(visualization) @@ -215,6 +218,33 @@ class Container(Visualization): self.add_visualization(clickable) self.clickables.append(clickable) + def add_dropable(self, dropable: 'Dropable'): + self.add_visualization(dropable) + self.dropables.append(dropable) + + +class Dropable(Container): + def __init__(self, position: Position, dimensions: Dimensions): + Container.__init__(self, position, dimensions) + self.on_drop = lambda dragable: None + + def drop(self, dragable: Dragable) -> None: + self.on_drop(dragable) + + +class Text(Visualization, Drawable): + def __init__(self, text: str, size: int = 12, font_family: str = 'freesansbold.ttf', color: Color = Color(255, 255, 255)): + font = pygame.font.Font(font_family, size) + self.surface = font.render(text, True, color.as_tuple()) + + Visualization.__init__( + self, + Canvas(Dimensions.from_tuple(self.surface.get_size())), + Position(0, 0) + ) + + self.canvas.surface = self.surface + class Drag: def __init__(self, dragable: Dragable, cursor_offset: Position): @@ -234,10 +264,10 @@ class UI: self.drag = None self.containers = [] - def init(self): + def init(self) -> None: pass - def update(self): + def update(self) -> None: self.canvas.clear() for container in self.containers: @@ -246,13 +276,13 @@ class UI: pygame.display.update() - def add_container(self, container: Container): + def add_container(self, container: Container) -> None: self.containers.append(container) - def exit(self): + def exit(self) -> None: self.is_running = False - def main(self): + def main(self) -> None: while self.is_running: event = pygame.event.wait() @@ -284,7 +314,7 @@ class UI: clickable = clickables_clicked[-1] - if type(clickable) is Dragable: + if self._is_in_filter(clickable, [Dragable]): self.drag = Drag( clickable, Position.from_tuple(event.pos) - clickable.position @@ -292,7 +322,7 @@ class UI: clickable.click() - def _handle_drag(self, event: pygame.event.Event): + def _handle_drag(self, event: pygame.event.Event) -> None: position_cursor = Position.from_tuple(event.pos) position = position_cursor - self.drag.cursor_offset @@ -300,7 +330,7 @@ class UI: self.drag.dragable.position = position self.drag.dragable.drag() - def _handle_drop(self, event: pygame.event.Event): + def _handle_drop(self, event: pygame.event.Event) -> None: if self.drag is None: return @@ -317,11 +347,11 @@ class UI: self.drag = None - def _handle_mouse_motion(self, event: pygame.event.Event): + def _handle_mouse_motion(self, event: pygame.event.Event) -> None: if type(self.drag) is Drag: self._handle_drag(event) - def _get_visualizations_hovered(self, cursor_position: Position, filter: list = []): + def _get_visualizations_hovered(self, cursor_position: Position, filter: list = []) -> list[Visualization]: containers_hovered = [] visualizations_hovered = [] @@ -341,13 +371,17 @@ class UI: return visualizations_hovered - def _get_clickables_clicked(self, event: pygame.event.Event): - return self._get_visualizations_hovered( + def _get_clickables_clicked(self, event: pygame.event.Event) -> list[Clickable]: + clickables = self._get_visualizations_hovered( Position.from_tuple(event.pos), [Clickable, Dragable] ) - def _is_in_filter(self, visualization: Visualization, filter: list = []) -> bool: + print(clickables) + + return clickables + + def _is_in_filter(self, visualization: Visualization, filter: list[Visualization] = []) -> bool: if len(filter) == 0: return True @@ -359,20 +393,27 @@ class UI: return False -class IconButton(Clickable): - def __init__(self, icon: Image, icon_hover = None): - self.icon = icon - self.icon_hover = icon_hover +class ListItem(Dragable): + def __init__(self, canvas: Canvas): + Dragable.__init__(self, canvas, Position(0, 0)) + + +class ItemList(Dropable): + def __init__(self, position: Position, dimensions: Dimensions): + Dropable.__init__(self, position, dimensions) + + def add_item(self, item: ListItem) -> None: + self.add_dropable(item) class ResourceContainer: def __init__(self): self.images = {} - def add_image(self, name, url): + def add_image(self, name, url) -> None: self.images[name] = Image(url) - def get_image(self, name): + def get_image(self, name) -> Image: return self._get_resource(self.images, name) def _get_resource(self, resources: list, name: str): @@ -382,7 +423,7 @@ class ResourceContainer: raise Exception('Resource "%s" not found!' % (name)) @staticmethod - def from_json_file(json_file): + def from_json_file(json_file) -> Self: with open(json_file, 'r') as file: data = json.loads(file.read())