Skip to content

Quick-selection menu sync + scale/shear overlay handling#24

Merged
jacobson30-bot merged 2 commits into
mainfrom
selection-and-scale-shear-overlays
Jun 11, 2026
Merged

Quick-selection menu sync + scale/shear overlay handling#24
jacobson30-bot merged 2 commits into
mainfrom
selection-and-scale-shear-overlays

Conversation

@jacobson30-bot

Copy link
Copy Markdown
Contributor

Summary

Adversarial review pass 2, items 5–6: quick-selection lifecycle and scale/shear overlay geometry.

Quick-selection lifecycle (image_viewer_selection_mixin.py, image_viewer_toolbar_mixin.py)

  • _clear_quick_selection suppresses the canvas signal, which also skipped the menu/toolbar resync its handler performs — selection-gated UI stayed stale after a transform drop, context-menu drop, navigation, or reset. The helper now resyncs itself.
  • The promote_selection toolbar action is now gated on a selection existing (it was never gated, unlike its siblings), with matching tooltips.

Scale/shear overlay handling (core/roi.py, core/mask.py, export mixin)

scale_image and shear bypassed the overlay transform path: ROIs and quick selections were left silently mislocated after a resample (vector geometry rasterises against any shape — no shape guard saves it), and shear didn't even warn.

  • ROI.transform gains scale_image (exact per-axis scaling for every ROI kind) and shear → invalidate (rotate_arbitrary policy).
  • ImageMask.transform now returns None for scale/shear/affine as its docstring always promised (the code raised ValueError).
  • The viewer handlers route both ops through the same transform path as flips/rotations: scale rescales ROIs + selection and invalidates raster masks; shear drops all overlays and reports it in the status message.

Test plan

  • tests/test_quick_selection_lifecycle.py (12 tests) and tests/test_scale_shear_geometry.py (21 tests, incl. a scale rasterisation round-trip).
  • One existing pin updated: test_roi.py expected shear to raise; now asserts invalidation, with a truly-unknown op still raising.
  • Full suite green locally: 2321 passed, 3 skipped.

🤖 Generated with Claude Code

jacobson30-bot and others added 2 commits June 11, 2026 21:31
…ction

Two quick-selection lifecycle gaps (adversarial review pass 2):

- _clear_quick_selection suppresses the canvas signal (emit=False) to avoid
  a noisy status update, but that also skipped the menu/toolbar resync the
  signal handler performs. Every caller was affected: the display-op
  transform path (a rotation dropping the selection), the context-menu
  "Drop selection" action, viewer navigation, and reset. The helper now
  resyncs itself.
- The promote_selection quick-toolbar action was never gated on a selection
  existing (unlike its mask/invert/line siblings) — clicking with no
  selection was handled gracefully, but the button lied about being
  actionable. It is now enabled only while a quick selection exists, with
  matching tooltips.

tests/test_quick_selection_lifecycle.py pins the lifecycle: clear-resync,
Escape clearing through the signalling path, flip carrying the selection,
rotate_arbitrary dropping it (with resync + status), and promotion for all
four area kinds producing an independent managed ROI.

Co-Authored-By: Claude Fable 5 <[email protected]>
scale_image and shear bypassed the overlay transform path entirely: ROI
vector geometry rasterises against any array shape (no shape guard saves it,
unlike masks), so after a resample every ROI and quick selection sat
silently mislocated over the new pixels; shear didn't even warn (doc Test 4:
"overlays no longer aligned", "selections staying in the wrong place").

- ROI.transform gains scale_image (exact per-axis coordinate scaling for
  every kind — vector geometry resamples losslessly) and shear → None
  (invalidate; a rectangle/ellipse cannot represent the sheared shape,
  matching the rotate_arbitrary policy).
- ImageMask.transform: scale/shear/affine now return None (invalidate) as
  its docstring always promised — the code raised ValueError for them.
- _on_scale_image_applied / _on_shear_applied route through
  _transform_image_roi_set_for_display_op like every other geometric op:
  scale rescales ROIs + the quick selection and invalidates raster masks;
  shear drops all overlays and says so in the status message.

tests/test_scale_shear_geometry.py covers the per-kind scale math (incl. a
rasterisation round-trip), shear invalidation, the mask doc/code agreement,
and the viewer wiring. test_roi.py's pin of "shear raises" updated to the
new contract (unknown ops still raise).

Co-Authored-By: Claude Fable 5 <[email protected]>
@jacobson30-bot jacobson30-bot merged commit e063bab into main Jun 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant