Added secondary mode button logic; added arrow bi-directionality; cleaned up primary button box

This commit is contained in:
2025-09-22 00:42:25 -05:00
parent 49237d39e4
commit 662fec201b
16 changed files with 341 additions and 82 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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...")

View File

@@ -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) )

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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) )

View File

@@ -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:

View File

@@ -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() )
self.add( PrimaryButtonBox() )
self.add( MainContainer() )

View File

@@ -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,))

View File

@@ -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(),))

View File

@@ -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(),))

View File

@@ -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):

View File

@@ -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)

View File

@@ -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):
...