diff --git a/src/widgets/draw_area.py b/src/widgets/draw_area.py index ea76fa7..110d3fb 100644 --- a/src/widgets/draw_area.py +++ b/src/widgets/draw_area.py @@ -10,34 +10,18 @@ from gi.repository import Gtk from gi.repository import Gdk # Application imports -from libs.surface_manager import SurfaceManager -from libs.event_collection import EventCollection - from data.mouse_buttons import MouseButton from data.events import brushes -from .surface import Surface +from .draw_area_base import DrawAreaBase # Note: https://martimm.github.io/gnome-gtk3/content-docs/tutorial/Cairo/drawing-model.html -class DrawArea(Gtk.DrawingArea): +class DrawArea(DrawAreaBase): def __init__(self): super(DrawArea, self).__init__() - self.background_surface: Surface = None - self.primary_surface: Surface = None - self.intermediate_surface: Surface = None - self.surface_manager: SurfaceManager = SurfaceManager() - self.event_collection: EventCollection = EventCollection() - - self.is_drawing: bool = False - self.brush_mode: str = "Line" - self.grid_brush: BrushBase = getattr(brushes, "Grid")() - self.brush: BrushBase = getattr(brushes, self.brush_mode)() - self.brush_color = self.brush.color - self.brush_size = self.brush.size - self._setup_styling() self._setup_signals() self._subscribe_to_events() @@ -47,21 +31,20 @@ class DrawArea(Gtk.DrawingArea): def _setup_styling(self): - margin_px = 24 - self.set_margin_top(margin_px) - self.set_margin_left(margin_px) - self.set_margin_right(margin_px) - self.set_margin_end(margin_px) + super()._setup_styling() def _setup_signals(self): self.set_can_focus(True) self.set_size_request(800, 600) - self.add_events(Gdk.EventMask.KEY_PRESS_MASK) - self.add_events(Gdk.EventMask.KEY_RELEASE_MASK) - self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) - self.add_events(Gdk.EventMask.BUTTON_RELEASE_MASK) - self.add_events(Gdk.EventMask.BUTTON1_MOTION_MASK) + self.add_events( + Gdk.EventMask.KEY_PRESS_MASK | + Gdk.EventMask.KEY_RELEASE_MASK | + Gdk.EventMask.BUTTON_PRESS_MASK | + Gdk.EventMask.BUTTON_RELEASE_MASK | + Gdk.EventMask.BUTTON1_MOTION_MASK | + Gdk.EventMask.POINTER_MOTION_MASK + ) self.connect("key-release-event", self._key_release_event) self.connect("button-press-event", self._button_press_event) @@ -79,53 +62,6 @@ class DrawArea(Gtk.DrawingArea): def _load_widgets(self): ... - def _set_new_surface(self): - size_w, \ - size_h, \ - image_surface = event_system.emit_and_await("get-image-size") - - self.grid_brush.width = size_w - self.grid_brush.height = size_h - self.background_surface = Surface(size_w, size_h) - self.primary_surface = Surface(size_w, size_h) - self.intermediate_surface = Surface(size_w, size_h) - - if image_surface: - self.primary_surface.create_new_area(image_surface) - self.primary_surface.set_image_data(image_surface.get_data()) - - self.event_collection.clear() - self.surface_manager.clear() - - self.grid_brush.update(None) - self.set_size_request(size_w, size_h) - self.background_surface.update(self.grid_brush) - self.surface_manager.append( self.primary_surface ) - - def _save_image(self, fpath: str): - self.primary_surface.area.write_to_png(fpath) - - def _do_save_image(self): - fpath = event_system.emit_and_await("save-as") - if not fpath: return - self._save_image(fpath) - - def _set_brush_color(self, color: Gdk.RGBA): - self.brush = getattr(brushes, self.brush_mode)() - self.brush_color = [color.red, color.green, color.blue, color.alpha] - self.brush.color = self.brush_color - - def _set_brush_size(self, size: int): - self.brush_size = size - self.brush = getattr(brushes, self.brush_mode)() - self.brush.size = self.brush_size - - def _set_brush_mode(self, mode: str): - self.brush_mode = mode - self.brush = getattr(brushes, self.brush_mode)() - self.brush.color = self.brush_color - self.brush.size = self.brush_size - def _button_press_event(self, widget, eve): if not self.has_focus(): self.grab_focus() @@ -149,7 +85,7 @@ class DrawArea(Gtk.DrawingArea): if not self.brush.is_valid: return - self.primary_surface.update(self.brush) + self.active_surface.update(self.brush) self.queue_draw() if eve.type == Gdk.EventType.BUTTON_RELEASE and eve.button == MouseButton.RIGHT_BUTTON: @@ -163,11 +99,11 @@ class DrawArea(Gtk.DrawingArea): if is_control: if keyname == "z": - if len(self.primary_surface.history_manager) == 0: return - self.event_collection.append( self.primary_surface.history_manager.pop() ) + if len(self.active_surface.history_manager) == 0: return + self.event_collection.append( self.active_surface.history_manager.pop() ) elif keyname == "y": if len(self.event_collection) == 0: return - self.primary_surface.history_manager.append( self.event_collection.pop() ) + self.active_surface.history_manager.append( self.event_collection.pop() ) elif keyname == "n": self._set_new_surface() elif keyname == "s": @@ -175,53 +111,59 @@ class DrawArea(Gtk.DrawingArea): self.queue_draw() - def _motion_notify_event(self, area, eve): if not self.is_drawing: return self.brush.update(eve) self.queue_draw() + # Note: In the future I imagine we could have different draw states + # besides T/F where we could call some subset order or even other surfaces def _draw(self, area, brush: cairo.Context): - self._draw_background(area, brush) - self._draw_surfaces(area, brush) - self._draw_overlay(area, brush) + match self.is_drawing: + case False: + self._is_not_drawing_draw_background(area, brush) + self._is_not_drawing_draw_surfaces(area, brush) + self.is_not_drawing_draw_overlay(area, brush) + case True: + self._is_drawing_draw_background(area, brush) + self._is_drawing_draw_surfaces(area, brush) + self._is_drawing_draw_overlay(area, brush) + case _: + ... return False - def _draw_background(self, area, brush: cairo.Context): - # Note: While drawing, only overlay needs to re-calculate its stack. - # This can just copy what exists than re-calculating the surface. - if self.is_drawing: - brush.set_source_surface(self.background_surface.area, 0.0, 0.0) - brush.paint() - return - + def _is_not_drawing_draw_background(self, area, brush: cairo.Context): brush.set_source_surface(self.background_surface.area, 0.0, 0.0) self.background_surface.draw() brush.paint() - def _draw_surfaces(self, area, brush: cairo.Context): - # Note: While drawing, only overlay needs to re-calculate its stack. - # This can just copy what exists than re-calculating each surface. - if self.is_drawing: - for surface in self.surface_manager: - brush.set_source_surface(surface.area, 0.0, 0.0) - - brush.paint() - return + # Note: While drawing, only overlay needs to re-calculate its stack. + # This can just copy what exists than re-calculating the surface. + def _is_drawing_draw_background(self, area, brush: cairo.Context): + brush.set_source_surface(self.background_surface.area, 0.0, 0.0) + brush.paint() + def _is_not_drawing_draw_surfaces(self, area, brush: cairo.Context): for surface in self.surface_manager: brush.set_source_surface(surface.area, 0.0, 0.0) surface.draw() brush.paint() - def _draw_overlay(self, area, brush: cairo.Context): - # Note: When NOT drawing, no overlay data should exist nor be processed... - if not self.is_drawing: - self.intermediate_surface.clear_surface() - return + # Note: While drawing, only overlay needs to re-calculate its stack. + # This can just copy what exists than re-calculating each surface. + def _is_drawing_draw_surfaces(self, area, brush: cairo.Context): + for surface in self.surface_manager: + brush.set_source_surface(surface.area, 0.0, 0.0) + brush.paint() + + # Note: When NOT drawing, no overlay data should exist nor be processed... + def is_not_drawing_draw_overlay(self, area, brush: cairo.Context): + self.intermediate_surface.clear_surface() + + def _is_drawing_draw_overlay(self, area, brush: cairo.Context): brush.set_source_surface(self.intermediate_surface.area, 0.0, 0.0) self.intermediate_surface.draw() brush.paint() diff --git a/src/widgets/draw_area_base.py b/src/widgets/draw_area_base.py new file mode 100644 index 0000000..d01cfa9 --- /dev/null +++ b/src/widgets/draw_area_base.py @@ -0,0 +1,93 @@ +# Python imports + +# Lib imports +import gi +gi.require_version('Gtk', '3.0') + +from gi.repository import Gtk +from gi.repository import Gdk + +# Application imports +from libs.surface_manager import SurfaceManager +from libs.event_collection import EventCollection + +from data.mouse_buttons import MouseButton +from data.events import brushes + +from .surface import Surface + + + +# Note: https://martimm.github.io/gnome-gtk3/content-docs/tutorial/Cairo/drawing-model.html +class DrawAreaBase(Gtk.DrawingArea): + def __init__(self): + super(DrawAreaBase, self).__init__() + + self.background_surface: Surface = None + self.active_surface: Surface = None + self.intermediate_surface: Surface = None + self.surface_manager: SurfaceManager = SurfaceManager() + self.event_collection: EventCollection = EventCollection() + + self.is_drawing: bool = False + self.brush_mode: str = "Line" + self.grid_brush: BrushBase = getattr(brushes, "Grid")() + self.brush: BrushBase = getattr(brushes, self.brush_mode)() + self.brush_color = self.brush.color + self.brush_size = self.brush.size + + + def _setup_styling(self): + margin_px = 24 + self.set_margin_top(margin_px) + self.set_margin_left(margin_px) + self.set_margin_right(margin_px) + self.set_margin_end(margin_px) + + def _set_new_surface(self): + size_w, \ + size_h, \ + image_surface = event_system.emit_and_await("get-image-size") + + self.grid_brush.width = size_w + self.grid_brush.height = size_h + self.background_surface = Surface(size_w, size_h) + self.active_surface = Surface(size_w, size_h) + self.intermediate_surface = Surface(size_w, size_h) + + if image_surface: + self.active_surface.create_new_area(image_surface) + self.active_surface.set_image_data(image_surface.get_data()) + + self.event_collection.clear() + self.surface_manager.clear() + + self.grid_brush.update(None) + self.set_size_request(size_w, size_h) + self.background_surface.update(self.grid_brush) + self.surface_manager.append( self.active_surface ) + + def _save_image(self, fpath: str): + self.active_surface.area.write_to_png(fpath) + + def _do_save_image(self): + fpath = event_system.emit_and_await("save-as") + if not fpath: return + self._save_image(fpath) + + def _set_brush_color(self, color: Gdk.RGBA): + self.brush = getattr(brushes, self.brush_mode)() + self.brush_color = [color.red, color.green, color.blue, color.alpha] + self.brush.color = self.brush_color + + def _set_brush_size(self, size: int): + self.brush_size = size + self.brush = getattr(brushes, self.brush_mode)() + self.brush.size = self.brush_size + + def _set_brush_mode(self, mode: str): + self.brush_mode = mode + self.brush = getattr(brushes, self.brush_mode)() + self.brush.color = self.brush_color + self.brush.size = self.brush_size +