Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions rayforge/doceditor/editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ def __init__(
self._on_processing_state_changed
)

# Global preference for maintaining aspect ratio during transforms.
self.aspect_ratio_locked: bool = True

# Instantiate and link command handlers, passing dependencies.
self.asset = AssetCmd(self)
self.edit = EditCmd(self)
Expand Down
9 changes: 8 additions & 1 deletion rayforge/doceditor/ui/item_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def __init__(

# Fixed Ratio Switch
self.fixed_ratio_switch = Adw.SwitchRow(
title=_("Fixed Ratio"), active=True
title=_("Fixed Ratio"), active=self.editor.aspect_ratio_locked
)
self.fixed_ratio_switch.connect(
"notify::active", self._on_fixed_ratio_toggled
Expand Down Expand Up @@ -415,7 +415,10 @@ def _on_shear_changed(self, spin_row, GParamSpec):
self._in_update = False

def _on_fixed_ratio_toggled(self, switch_row, GParamSpec):
if self._in_update:
return
logger.debug(f"Fixed ratio toggled: {switch_row.get_active()}")
self.editor.aspect_ratio_locked = switch_row.get_active()
# Check if the primary selected item is a workpiece or stock item
is_ratio_lockable = self.items and isinstance(
self.items[0], (WorkPiece, StockItem, Group)
Expand Down Expand Up @@ -705,6 +708,10 @@ def _update_row_visibility_and_details(self, item: DocItem):
is_single_workpiece or is_single_stockitem or is_single_group
)

self._in_update = True
self.fixed_ratio_switch.set_active(self.editor.aspect_ratio_locked)
self._in_update = False

self.source_file_row.set_visible(is_single_workpiece)
self.fixed_ratio_switch.set_sensitive(
is_single_item_with_size or is_single_group
Expand Down
28 changes: 26 additions & 2 deletions rayforge/workbench/canvas/canvas.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from .region import (
ElementRegion,
BBOX_REGIONS,
CORNER_RESIZE_HANDLES,
RESIZE_HANDLES,
ROTATE_HANDLES,
ROTATE_SHEAR_HANDLES,
Expand Down Expand Up @@ -139,6 +140,23 @@ def find_by_data(self, data: Any) -> Optional[CanvasElement]:
"""
return self.root.find_by_data(data)

def _get_ratio_lock_default(self) -> bool:
"""
Base implementation for whether aspect ratio should be locked during
resize. Subclasses can override to bind to UI state.
"""
return False

def _should_constrain_aspect(self, active_region: ElementRegion) -> bool:
"""
Determines if the current resize interaction should maintain aspect
ratio, applying Shift as an inverter of the default preference. Only
corner handles honor aspect locking; edge handles always deform.
"""
if active_region not in CORNER_RESIZE_HANDLES:
return False
return self._get_ratio_lock_default() ^ self._shift_pressed

def find_by_type(
self, thetype: Any
) -> Generator[CanvasElement, None, None]:
Expand Down Expand Up @@ -863,13 +881,16 @@ def on_mouse_drag(self, gesture, offset_x: float, offset_y: float):
self._selection_group.apply_move(world_dx, world_dy)
elif self._resizing:
if self._active_origin:
constrain_aspect = self._should_constrain_aspect(
self._active_region
)
self._selection_group.resize_from_drag(
self._active_region,
world_dx,
world_dy,
self._active_origin,
self._ctrl_pressed,
self._shift_pressed,
constrain_aspect,
)
for elem in self._selection_group.elements:
elem.trigger_update()
Expand Down Expand Up @@ -930,6 +951,9 @@ def on_mouse_drag(self, gesture, offset_x: float, offset_y: float):
and self._initial_transform
and self._initial_world_transform
):
constrain_aspect = self._should_constrain_aspect(
self._active_region
)
transform.resize_element(
element=self._drag_target,
world_dx=world_dx,
Expand All @@ -938,7 +962,7 @@ def on_mouse_drag(self, gesture, offset_x: float, offset_y: float):
initial_world_transform=self._initial_world_transform,
active_region=self._active_region,
view_transform=self.view_transform,
shift_pressed=self._shift_pressed,
constrain_aspect=constrain_aspect,
ctrl_pressed=self._ctrl_pressed,
)
self._drag_target.trigger_update()
Expand Down
4 changes: 2 additions & 2 deletions rayforge/workbench/canvas/multiselect.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ def resize_from_drag(
offset_y: float,
active_origin: Tuple[float, float, float, float],
ctrl_pressed: bool,
shift_pressed: bool,
constrain_aspect: bool,
):
"""
Calculates and applies the new group bounding box by calling the
Expand All @@ -267,7 +267,7 @@ def resize_from_drag(
active_region=active_region,
drag_delta=(offset_x, offset_y),
is_flipped=self.canvas.view_transform.is_flipped(),
constrain_aspect=shift_pressed,
constrain_aspect=constrain_aspect,
from_center=ctrl_pressed,
min_size=min_size_world,
)
Expand Down
4 changes: 2 additions & 2 deletions rayforge/workbench/canvas/transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def resize_element(
initial_world_transform: Matrix,
active_region: ElementRegion,
view_transform: Matrix,
shift_pressed: bool,
constrain_aspect: bool,
ctrl_pressed: bool,
):
"""
Expand All @@ -156,7 +156,7 @@ def resize_element(
active_region=active_region,
drag_delta=local_delta,
is_flipped=view_transform.is_flipped(), # Pass the flag
constrain_aspect=shift_pressed,
constrain_aspect=constrain_aspect,
from_center=ctrl_pressed,
min_size=min_size_local,
)
Expand Down
16 changes: 16 additions & 0 deletions rayforge/workbench/surface.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ def __init__(
self.edit_sketch_requested = Signal()
self.edit_stock_item_requested = Signal()

# Aspect-ratio lock preference shared with the properties panel.
self.editor.aspect_ratio_locked = (
getattr(self.editor, "aspect_ratio_locked", True) is True
)

# Connect to generic signals from the base Canvas class
self.move_begin.connect(self._on_any_transform_begin)
self.resize_begin.connect(self._on_resize_begin)
Expand Down Expand Up @@ -120,6 +125,17 @@ def show_travel_moves(self) -> bool:
"""Returns True if travel moves should be rendered."""
return self._show_travel_moves

def _get_ratio_lock_default(self) -> bool:
"""
Returns the current aspect-ratio lock preference shared with the
properties panel. Defaults to True for workpieces.
"""
return bool(getattr(self.editor, "aspect_ratio_locked", True))

def set_aspect_ratio_locked(self, locked: bool) -> None:
"""Synchronizes the lock state with the editor."""
self.editor.aspect_ratio_locked = locked

def set_laser_dot_visible(self, visible: bool = True) -> None:
self._laser_dot.set_visible(visible)
self.queue_draw()
Expand Down
Loading