diff --git a/src/data/events/brushes/__init__.py b/src/data/events/brushes/__init__.py index 8f4906d..9135bf3 100644 --- a/src/data/events/brushes/__init__.py +++ b/src/data/events/brushes/__init__.py @@ -1,6 +1,7 @@ from .brush_event import BrushEvent from .grid import Grid from .arrow import Arrow +from .dual_arrow import DualArrow from .line import Line from .circle import Circle from .square import Square diff --git a/src/data/events/brushes/arrow.py b/src/data/events/brushes/arrow.py index 2b28062..fd0f25d 100644 --- a/src/data/events/brushes/arrow.py +++ b/src/data/events/brushes/arrow.py @@ -6,7 +6,6 @@ import cairo # Application imports from data.point import Point -from data.points import Points from . import BrushEvent @@ -15,14 +14,6 @@ class Arrow(BrushEvent): def __init__(self): super(Arrow, self).__init__() - self.points: Points = Points() - - def __copy__(self): - copy = type(self)() - copy.color = self.color - - return copy - def update(self, eve = None): if len(self.points) < 2: @@ -44,11 +35,23 @@ class Arrow(BrushEvent): x1, y1 = self.points[0].x, self.points[0].y x2, y2 = self.points[1].x, self.points[1].y - # Arrow body + self.draw_arrow_body(brush, x1, y1, x2, y2) + self.draw_end_arrow_head(brush, x1, y1, x2, y2) + + def draw_start_arrow_head(self, brush, x1, y1, x2, y2): + ah1, ay1, \ + ah2, ay2 = self.get_arrow_head_points(x2, y2, x1, y1) + + brush.move_to(x1, y1) + brush.line_to(ah1, ay1) + brush.move_to(x1, y1) + brush.line_to(ah2, ay2) + + def draw_arrow_body(self, brush, x1, y1, x2, y2): brush.move_to(x1, y1) brush.line_to(x2, y2) - # Arrow head + def draw_end_arrow_head(self, brush, x1, y1, x2, y2): ah1, ay1, \ ah2, ay2 = self.get_arrow_head_points(x1, y1, x2, y2) diff --git a/src/data/events/brushes/brush_event.py b/src/data/events/brushes/brush_event.py index 0d2c970..6acbed3 100644 --- a/src/data/events/brushes/brush_event.py +++ b/src/data/events/brushes/brush_event.py @@ -6,6 +6,9 @@ import cairo # Application imports from data.events.event import Event +from data.points import Points + + class UnboundException(Exception): ... @@ -19,6 +22,8 @@ class BrushEvent(Event): self.size: int = 12 self.color: [] = [0.0, 0.0, 0.0, 1.0] + self.points: Points = Points() + def update(self, eve): raise UnboundException("Method hasn't been overriden...") diff --git a/src/data/events/brushes/circle.py b/src/data/events/brushes/circle.py index 20bbcd6..54ec5dd 100644 --- a/src/data/events/brushes/circle.py +++ b/src/data/events/brushes/circle.py @@ -5,7 +5,6 @@ import cairo # Application imports from data.point import Point -from data.points import Points from . import BrushEvent @@ -14,14 +13,6 @@ class Circle(BrushEvent): def __init__(self): super(Circle, self).__init__() - self.points: Points = Points() - - def __copy__(self): - copy = type(self)() - copy.color = self.color - - return copy - def update(self, eve): self.points.append( Point(eve.x, eve.y) ) diff --git a/src/data/events/brushes/dual_arrow.py b/src/data/events/brushes/dual_arrow.py new file mode 100644 index 0000000..c099df5 --- /dev/null +++ b/src/data/events/brushes/dual_arrow.py @@ -0,0 +1,24 @@ +# Python imports + +# Lib imports +import cairo + +# Application imports +from .arrow import Arrow + + + +class DualArrow(Arrow): + def __init__(self): + super(DualArrow, self).__init__() + + + def process(self, brush: cairo.Context): + self.set_brush_data(brush) + + x1, y1 = self.points[0].x, self.points[0].y + x2, y2 = self.points[1].x, self.points[1].y + + self.draw_start_arrow_head(brush, x1, y1, x2, y2) + self.draw_arrow_body(brush, x1, y1, x2, y2) + self.draw_end_arrow_head(brush, x1, y1, x2, y2) diff --git a/src/data/events/brushes/erase.py b/src/data/events/brushes/erase.py index 4755c85..5aa1983 100644 --- a/src/data/events/brushes/erase.py +++ b/src/data/events/brushes/erase.py @@ -4,9 +4,6 @@ import cairo # Application imports -from data.point import Point -from data.points import Points - from . import Line @@ -14,8 +11,6 @@ class Erase(Line): def __init__(self): super(Erase, self).__init__() - self.points: Points = Points() - def set_brush_data(self, brush): brush.set_line_width(self.size) brush.set_line_cap(1) # 0 = BUTT, 1 = ROUND, 2 = SQUARE diff --git a/src/data/events/brushes/grid.py b/src/data/events/brushes/grid.py index bad6ff4..8b4db74 100644 --- a/src/data/events/brushes/grid.py +++ b/src/data/events/brushes/grid.py @@ -16,12 +16,6 @@ class Grid(BrushEvent): self.grid_spacing = 12 # px self.color = [0.2, 0.2, 0.2, 0.64] # light gray - def __copy__(self): - copy = type(self)() - copy.color = self.color - - return copy - def update(self, eve): self.is_valid = True diff --git a/src/data/events/brushes/line.py b/src/data/events/brushes/line.py index 3cfe163..bb77b31 100644 --- a/src/data/events/brushes/line.py +++ b/src/data/events/brushes/line.py @@ -5,7 +5,6 @@ import cairo # Application imports from data.point import Point -from data.points import Points from . import BrushEvent @@ -14,14 +13,6 @@ class Line(BrushEvent): def __init__(self): super(Line, self).__init__() - self.points: Points = Points() - - def __copy__(self): - copy = type(self)() - copy.color = self.color - - return copy - def update(self, eve = None): self.points.append( Point(eve.x, eve.y) ) diff --git a/src/data/events/brushes/square.py b/src/data/events/brushes/square.py index 3d193dd..b412a4c 100644 --- a/src/data/events/brushes/square.py +++ b/src/data/events/brushes/square.py @@ -5,7 +5,6 @@ import cairo # Application imports from data.point import Point -from data.points import Points from . import BrushEvent @@ -14,14 +13,6 @@ class Square(BrushEvent): def __init__(self): super(Square, self).__init__() - self.points: Points = Points() - - def __copy__(self): - copy = type(self)() - copy.color = self.color - - return copy - def update(self, eve): if len(self.points) < 2: diff --git a/src/widgets/containers/container.py b/src/widgets/containers/container.py index c3e45cc..6d5e4e0 100644 --- a/src/widgets/containers/container.py +++ b/src/widgets/containers/container.py @@ -7,7 +7,8 @@ gi.require_version('Gtk', '3.0') from gi.repository import Gtk # Application imports -from widgets.button_box import ButtonBox +from widgets.controls.primary_button_box import PrimaryButtonBox +from widgets.controls.secondary_button_box import SecondaryButtonBox from widgets.draw_area import DrawArea @@ -35,6 +36,29 @@ class ScrollWindow(Gtk.ScrolledWindow): view_port.add( DrawArea() ) self.add(view_port) + +class MainContainer(Gtk.Box): + def __init__(self): + super(MainContainer, self).__init__() + + self._setup_styling() + self._setup_signals() + self._load_widgets() + + self.show() + + + def _setup_styling(self): + self.set_orientation(Gtk.Orientation.HORIZONTAL) + + def _setup_signals(self): + ... + + def _load_widgets(self): + self.add( SecondaryButtonBox() ) + self.add( ScrollWindow() ) + + class Container(Gtk.Box): def __init__(self): super(Container, self).__init__() @@ -53,5 +77,5 @@ class Container(Gtk.Box): ... def _load_widgets(self): - self.add( ButtonBox() ) - self.add( ScrollWindow() ) \ No newline at end of file + self.add( PrimaryButtonBox() ) + self.add( MainContainer() ) diff --git a/src/widgets/controls/arrows_box.py b/src/widgets/controls/arrows_box.py new file mode 100644 index 0000000..3428fa7 --- /dev/null +++ b/src/widgets/controls/arrows_box.py @@ -0,0 +1,47 @@ +# Python imports + +# Lib imports +import gi + +gi.require_version('Gtk', '3.0') + +from gi.repository import Gtk + +# Application imports + + + +class ArrowsBox(Gtk.ComboBoxText): + def __init__(self): + super(ArrowsBox, self).__init__() + + self._setup_styling() + self._setup_signals() + self._load_widgets() + + self.show_all() + + + def _setup_styling(self): + ... + + def _setup_signals(self): + self.connect("changed", self._changed) + + + def _load_widgets(self): + self.prepend_text("End Arrow") + self.prepend_text("Start-End Arrow") + + self.set_active(0) + + def _changed(self, widget): + text = widget.get_active_text() + if text == "End Arrow": + mode = "Arrow" + elif text == "Start-End Arrow": + mode = "DualArrow" + else: + return + + event_system.emit("set-brush-mode", (mode,)) diff --git a/src/widgets/controls/brush_color_button.py b/src/widgets/controls/brush_color_button.py new file mode 100644 index 0000000..be90851 --- /dev/null +++ b/src/widgets/controls/brush_color_button.py @@ -0,0 +1,43 @@ +# Python imports + +# Lib imports +import gi + +gi.require_version('Gtk', '3.0') + +from gi.repository import Gtk +from gi.repository import Gdk + +# Application imports + + + +class BrushColorButton(Gtk.ColorButton): + def __init__(self): + super(BrushColorButton, self).__init__() + + self._setup_styling() + self._setup_signals() + self._subscribe_to_events() + self._load_widgets() + + self.show_all() + + + def _setup_styling(self): + self.set_title("Pick a Color") + self.set_use_alpha(True) + self.set_rgba( Gdk.RGBA(0.0, 0.0, 0.0, 1.0) ) + + + def _setup_signals(self): + self.connect("color-set", self.set_brush_color) + + def _subscribe_to_events(self): + ... + + def _load_widgets(self): + ... + + def set_brush_color(self, widget: any): + event_system.emit("set-brush-color", (widget.get_rgba(),)) diff --git a/src/widgets/controls/brush_size_spinner.py b/src/widgets/controls/brush_size_spinner.py new file mode 100644 index 0000000..abeda51 --- /dev/null +++ b/src/widgets/controls/brush_size_spinner.py @@ -0,0 +1,43 @@ +# Python imports + +# Lib imports +import gi + +gi.require_version('Gtk', '3.0') + +from gi.repository import Gtk + +# Application imports + + +class BrushSizeSpinner(Gtk.SpinButton): + def __init__(self): + super(BrushSizeSpinner, self).__init__() + + self._setup_styling() + self._setup_signals() + self._subscribe_to_events() + self._load_widgets() + + self.show_all() + + + def _setup_styling(self): + self.set_numeric(True) + self.set_digits(0) + self.set_snap_to_ticks(True) + self.set_increments(1.0, 10.0) + self.set_range(1.0, 100.0) + self.set_value(12.0) + + def _setup_signals(self): + self.connect("value-changed", self.set_brush_size) + + def _subscribe_to_events(self): + ... + + def _load_widgets(self): + ... + + def set_brush_size(self, widget: any): + event_system.emit("set-brush-size", (widget.get_value(),)) diff --git a/src/widgets/button_box.py b/src/widgets/controls/primary_button_box.py similarity index 58% rename from src/widgets/button_box.py rename to src/widgets/controls/primary_button_box.py index 8099733..913c2ad 100644 --- a/src/widgets/button_box.py +++ b/src/widgets/controls/primary_button_box.py @@ -6,15 +6,19 @@ import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk -from gi.repository import Gdk # Application imports -from .controls.save_as_button import SaveAsButton +from .toggle_button import ToggleButton +from .save_as_button import SaveAsButton +from .brush_color_button import BrushColorButton +from .brush_size_spinner import BrushSizeSpinner +from .arrows_box import ArrowsBox -class ButtonBox(Gtk.Box): + +class PrimaryButtonBox(Gtk.Box): def __init__(self): - super(ButtonBox, self).__init__() + super(PrimaryButtonBox, self).__init__() self.active_btn = None @@ -33,32 +37,24 @@ class ButtonBox(Gtk.Box): def _load_widgets(self): - arrow_btn = Gtk.ToggleButton(label = "Arrow") - line_btn = Gtk.ToggleButton(label = "Line") - circle_btn = Gtk.ToggleButton(label = "Circle") - square_btn = Gtk.ToggleButton(label = "Square") - erase_btn = Gtk.ToggleButton(label = "Erase") + arrow_btn = ToggleButton(label = "Arrows") + line_btn = ToggleButton(label = "Line") + circle_btn = ToggleButton(label = "Circle") + square_btn = ToggleButton(label = "Square") + erase_btn = ToggleButton(label = "Erase") save_btn = SaveAsButton() - color_btn = Gtk.ColorButton() - size_btn = Gtk.SpinButton() + color_btn = BrushColorButton() + size_btn = BrushSizeSpinner() self.active_btn = line_btn line_btn.set_active(True) - color_btn.set_title("Pick a Color") - color_btn.set_use_alpha(True) - color_btn.set_rgba( Gdk.RGBA(0.0, 0.0, 0.0, 1.0) ) - - size_btn.set_numeric(True) - size_btn.set_digits(0) - size_btn.set_snap_to_ticks(True) - size_btn.set_increments(1.0, 10.0) - size_btn.set_range(1.0, 100.0) - size_btn.set_value(12.0) - - color_btn.connect("color-set", self.set_brush_color) - size_btn.connect("value-changed", self.set_brush_size) + arrow_btn.secondary = ["Arrow", "DualArrow"] + line_btn.secondary = [] + circle_btn.secondary = [] + square_btn.secondary = [] + erase_btn.secondary = [] self.add(arrow_btn) self.add(line_btn) @@ -81,12 +77,6 @@ class ButtonBox(Gtk.Box): except Exception as e: ... - def set_brush_color(self, widget: any): - event_system.emit("set-brush-color", (widget.get_rgba(),)) - - def set_brush_size(self, widget: any): - event_system.emit("set-brush-size", (widget.get_value(),)) - def set_brush_mode(self, widget: any, mode: str): if mode in ["Save As"]: return @@ -94,6 +84,10 @@ class ButtonBox(Gtk.Box): widget.set_active(True) self.active_btn = widget + event_system.emit("load-secondary-buttons", (widget,)) + if mode in ["Arrows"]: + return + event_system.emit("set-brush-mode", (mode,)) def save_image(self, widget: any): diff --git a/src/widgets/controls/secondary_button_box.py b/src/widgets/controls/secondary_button_box.py new file mode 100644 index 0000000..bd7d2e3 --- /dev/null +++ b/src/widgets/controls/secondary_button_box.py @@ -0,0 +1,76 @@ +# Python imports + +# Lib imports +import gi + +gi.require_version('Gtk', '3.0') + +from gi.repository import Gtk + +# Application imports + + + +class SecondaryButtonBox(Gtk.Box): + def __init__(self): + super(SecondaryButtonBox, self).__init__() + + self.active_btn = None + self.signals: [] = [] + + self._setup_styling() + self._setup_signals() + self._subscribe_to_events() + self._load_widgets() + + self.show_all() + + + def _setup_styling(self): + margin_px = 24 + + self.set_margin_top(margin_px) + self.set_orientation(Gtk.Orientation.VERTICAL) + self.set_hexpand(False) + + def _setup_signals(self): + ... + + def _subscribe_to_events(self): + event_system.subscribe("load-secondary-buttons", self._load_secondary_buttons) + + def _load_widgets(self, mode = None): + if not mode: return + + for entry in mode.secondary: + child = Gtk.ToggleButton(label = entry) + sig_id = child.connect("released", self.set_brush_mode, child.get_label()) + self.signals.append(sig_id) + self.add(child) + + if len(mode.secondary) > 0: + child = self.get_children()[0] + self.set_brush_mode(child, child.get_label()) + + def _load_secondary_buttons(self, mode): + self.clear() + self._load_widgets(mode) + self.show_all() + + def set_brush_mode(self, widget: any, mode: str): + if self.active_btn: + self.active_btn.set_active(False) + + widget.set_active(True) + self.active_btn = widget + + event_system.emit("set-brush-mode", (mode,)) + + def clear(self): + self.active_btn = None + + for sig_id in self.signals: + self.disconnect(sig_id) + + for widget in self.get_children(): + self.remove(widget) diff --git a/src/widgets/controls/toggle_button.py b/src/widgets/controls/toggle_button.py new file mode 100644 index 0000000..8e89262 --- /dev/null +++ b/src/widgets/controls/toggle_button.py @@ -0,0 +1,37 @@ +# Python imports + +# Lib imports +import gi + +gi.require_version('Gtk', '3.0') + +from gi.repository import Gtk + +# Application imports + + +class ToggleButton(Gtk.ToggleButton): + def __init__(self, label = ""): + super(ToggleButton, self).__init__(label) + + self.secondary: [] = [] + + self._setup_styling() + self._setup_signals() + self._subscribe_to_events() + self._load_widgets() + + self.show_all() + + + def _setup_styling(self): + ... + + def _setup_signals(self): + ... + + def _subscribe_to_events(self): + ... + + def _load_widgets(self): + ...