Let the sort knob win over the corner handle it sits on (#228)
The CI run for 30de4d3 failed on e2e 37b-ii: dragging the sort knob of
a nine-slice frame resized the frame instead (h 18, lifted null). The
knob's middle sits exactly 8 px left of the frame, which is also the
edge of the corner handle's 8 px tolerance. Until now only the rounding
of the pointer decided which of the two won. The slimmer header bars of
#245 moved that rounding to the corner handle's side. The bug itself
has been there since #228.
- sortLineAt bounds the knob where it is drawn, from a gap to the reach
left of the frame, instead of by the handle tolerance.
- TextureCanvas passes that gap in image pixels, scaled by the zoom.
- The corner handle stays reachable at the corner itself and to the
right of and below it.
Tests:
- ./check.sh: 178 files, 2576 tests, CHECK EXIT 0
- GM_STEPS=1,37: ALL PASSED, 26 checks (37b-ii: lifted 6, h 24)
- New unit test: the knob's middle belongs to the knob even inside
the corner handle's reach. It fails with the old bound.
- Full e2e run not repeated. 37b-ii was the only failure in CI.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
30de4d35a8
commit
3a3401f8ac
3 files changed
+22
-10
No files matched your search
@@ -730,8 +730,9 @@ export class TextureCanvas extends SheetCanvas<TextureAsset> {
|
||||
}
|
||||
|
||||
private sortLineAt(frame: TextureFrame, point: Vec2, tolerance: number): 'knob' | 'line' | null {
|
||||
const gap = SORT_KNOB_GAP / this.view.zoom;
|
||||
const reach = (SORT_KNOB_GAP + SORT_KNOB_SIZE + 2) / this.view.zoom;
|
||||
return sortLineAt(frame, frameSortLine(frame), point, tolerance, reach);
|
||||
return sortLineAt(frame, frameSortLine(frame), point, tolerance, gap, reach);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -174,19 +174,22 @@ export function sortLineY(rect: FrameRect, line: number): number {
|
||||
|
||||
/**
|
||||
* What a press grabs of the sort line: its knob left of the frame, reachable wherever the line sits,
|
||||
* or the line itself inside the frame. `reach` is how far left of the frame the knob goes, in image
|
||||
* pixels. The press tests the knob before the frame's handles and the line after its border lines,
|
||||
* which at 0 all lie on the bottom edge with it.
|
||||
* or the line itself inside the frame. The knob spans from `gap` to `reach` left of the frame, in
|
||||
* image pixels. The press tests the knob before the frame's handles and the line after its border
|
||||
* lines, which at 0 all lie on the bottom edge with it. So the knob takes its whole drawn width,
|
||||
* even where the corner handle's tolerance reaches over it – bounding it by that tolerance instead
|
||||
* left its very middle to the corner handle.
|
||||
*/
|
||||
export function sortLineAt(
|
||||
rect: FrameRect,
|
||||
line: number,
|
||||
point: Vec2,
|
||||
tolerance: number,
|
||||
gap: number,
|
||||
reach: number,
|
||||
): 'knob' | 'line' | null {
|
||||
if (Math.abs(point.y - sortLineY(rect, line)) > tolerance) return null;
|
||||
if (point.x >= rect.x - reach && point.x < rect.x - tolerance) return 'knob';
|
||||
if (point.x >= rect.x - reach && point.x < rect.x - gap) return 'knob';
|
||||
return point.x >= rect.x && point.x <= rect.x + rect.w ? 'line' : null;
|
||||
}
|
||||
|
||||
|
||||
@@ -297,11 +297,19 @@ describe('the sort line of a texture frame (#228)', () => {
|
||||
const rect = { x: 10, y: 20, w: 30, h: 40 };
|
||||
expect(sortLineY(rect, 0)).toBe(60);
|
||||
expect(sortLineY(rect, 6)).toBe(54);
|
||||
expect(sortLineAt(rect, 6, { x: 4, y: 54 }, 1, 8)).toBe('knob');
|
||||
expect(sortLineAt(rect, 6, { x: 25, y: 54.5 }, 1, 8)).toBe('line');
|
||||
expect(sortLineAt(rect, 6, { x: 25, y: 57 }, 1, 8)).toBeNull();
|
||||
expect(sortLineAt(rect, 6, { x: 0, y: 54 }, 1, 8)).toBeNull();
|
||||
expect(sortLineAt(rect, 6, { x: 45, y: 54 }, 1, 8)).toBeNull();
|
||||
expect(sortLineAt(rect, 6, { x: 4, y: 54 }, 1, 1, 8)).toBe('knob');
|
||||
expect(sortLineAt(rect, 6, { x: 25, y: 54.5 }, 1, 1, 8)).toBe('line');
|
||||
expect(sortLineAt(rect, 6, { x: 25, y: 57 }, 1, 1, 8)).toBeNull();
|
||||
expect(sortLineAt(rect, 6, { x: 0, y: 54 }, 1, 1, 8)).toBeNull();
|
||||
expect(sortLineAt(rect, 6, { x: 45, y: 54 }, 1, 1, 8)).toBeNull();
|
||||
});
|
||||
|
||||
it('takes the middle of its knob even where the corner handle reaches over it', () => {
|
||||
// The editor's sizes at zoom 1: handles grab 8 px around the corner, the knob is drawn from
|
||||
// 4 to 12 px left of the frame, so its middle sits exactly 8 px out – on the handle's edge.
|
||||
const rect = { x: 100, y: 20, w: 24, h: 24 };
|
||||
expect(sortLineAt(rect, 0, { x: 92, y: 44 }, 8, 4, 14)).toBe('knob');
|
||||
expect(sortLineAt(rect, 0, { x: 97, y: 44 }, 8, 4, 14)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user