Quick-selection menu sync + scale/shear overlay handling#24
Merged
Conversation
…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]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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_selectionsuppresses 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.promote_selectiontoolbar 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_imageandshearbypassed 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.transformgains scale_image (exact per-axis scaling for every ROI kind) and shear → invalidate (rotate_arbitrary policy).ImageMask.transformnow returnsNonefor scale/shear/affine as its docstring always promised (the code raisedValueError).Test plan
tests/test_quick_selection_lifecycle.py(12 tests) andtests/test_scale_shear_geometry.py(21 tests, incl. a scale rasterisation round-trip).test_roi.pyexpectedshearto raise; now asserts invalidation, with a truly-unknown op still raising.🤖 Generated with Claude Code