Initial push
This commit is contained in:
225
src/widgets/draw_area.py
Normal file
225
src/widgets/draw_area.py
Normal file
@@ -0,0 +1,225 @@
|
||||
# Python imports
|
||||
from copy import copy, deepcopy
|
||||
|
||||
# Lib imports
|
||||
import cairo
|
||||
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 .surface import Surface
|
||||
|
||||
from . import brushes
|
||||
|
||||
|
||||
class DrawArea(Gtk.DrawingArea):
|
||||
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()
|
||||
self._load_widgets()
|
||||
|
||||
self._set_new_surface()
|
||||
|
||||
|
||||
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 _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.connect("key-release-event", self._key_release_event)
|
||||
self.connect("button-press-event", self._button_press_event)
|
||||
self.connect("button-release-event", self._button_release_event)
|
||||
self.connect("motion-notify-event", self._motion_notify_event)
|
||||
self.connect("draw", self._draw)
|
||||
|
||||
|
||||
def _subscribe_to_events(self):
|
||||
event_system.subscribe("save-image", self._save_image)
|
||||
event_system.subscribe("set-brush-color", self._set_brush_color)
|
||||
event_system.subscribe("set-brush-size", self._set_brush_size)
|
||||
event_system.subscribe("set-brush-mode", self._set_brush_mode)
|
||||
|
||||
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()
|
||||
|
||||
if eve.type == Gdk.EventType.BUTTON_PRESS and eve.button == MouseButton.LEFT_BUTTON:
|
||||
self.event_collection.clear()
|
||||
|
||||
self.is_drawing = True
|
||||
self._set_brush_mode(self.brush_mode)
|
||||
self.intermediate_surface.update(self.brush)
|
||||
self.brush.update(eve)
|
||||
|
||||
|
||||
if eve.type == Gdk.EventType.BUTTON_PRESS and eve.button == MouseButton.RIGHT_BUTTON:
|
||||
...
|
||||
|
||||
def _button_release_event(self, widget, eve):
|
||||
if eve.type == Gdk.EventType.BUTTON_RELEASE and eve.button == MouseButton.LEFT_BUTTON:
|
||||
self.is_drawing = False
|
||||
self.intermediate_surface.history_manager.clear()
|
||||
|
||||
if not self.brush.is_valid: return
|
||||
|
||||
self.primary_surface.update(self.brush)
|
||||
self.queue_draw()
|
||||
|
||||
if eve.type == Gdk.EventType.BUTTON_RELEASE and eve.button == MouseButton.RIGHT_BUTTON:
|
||||
...
|
||||
|
||||
def _key_release_event(self, widget, eve):
|
||||
keyname = Gdk.keyval_name(eve.keyval)
|
||||
modifiers = Gdk.ModifierType(eve.get_state() & ~Gdk.ModifierType.LOCK_MASK)
|
||||
is_control = True if modifiers & Gdk.ModifierType.CONTROL_MASK else False
|
||||
is_shift = True if modifiers & Gdk.ModifierType.SHIFT_MASK else False
|
||||
|
||||
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() )
|
||||
elif keyname == "y":
|
||||
if len(self.event_collection) == 0: return
|
||||
self.primary_surface.history_manager.append( self.event_collection.pop() )
|
||||
elif keyname == "n":
|
||||
self._set_new_surface()
|
||||
elif keyname == "s":
|
||||
self._do_save_image()
|
||||
|
||||
self.queue_draw()
|
||||
|
||||
|
||||
def _motion_notify_event(self, area, eve):
|
||||
if not self.is_drawing: return
|
||||
self.brush.update(eve)
|
||||
self.queue_draw()
|
||||
|
||||
def _draw(self, area, brush: cairo.Context):
|
||||
self._draw_background(area, brush)
|
||||
self._draw_surfaces(area, brush)
|
||||
self._draw_overlay(area, brush)
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
brush.set_source_surface(self.intermediate_surface.area, 0.0, 0.0)
|
||||
self.intermediate_surface.draw()
|
||||
brush.paint()
|
Reference in New Issue
Block a user