Skip to content

Commit 11ea68f

Browse files
authored
feat: add rotation cursor support and expand selection handle hit areas (#85)
1 parent db613d9 commit 11ea68f

1 file changed

Lines changed: 23 additions & 10 deletions

File tree

src/core/ui/selection-handles.ts

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
detectCornerZone,
1010
detectEdgeZone
1111
} from "@core/interaction/clip-interaction";
12-
import { SELECTION_CONSTANTS, CURSOR_BASE_ANGLES, type CornerName, buildResizeCursor } from "@core/interaction/selection-overlay";
12+
import { SELECTION_CONSTANTS, CURSOR_BASE_ANGLES, type CornerName, buildResizeCursor, buildRotationCursor, calculateHitArea } from "@core/interaction/selection-overlay";
1313
import { type ClipBounds, createClipBounds, createSnapContext, filterContainedClips, snap, snapRotation, visualToLogical } from "@core/interaction/snap-system";
1414
import { updateSvgViewBox, isSimpleRectSvg } from "@core/shared/svg-utils";
1515
import { Pointer } from "@inputs/pointer";
@@ -127,6 +127,7 @@ export class SelectionHandles implements CanvasOverlayRegistration {
127127
this.app = app;
128128

129129
// Create outline
130+
this.outline.eventMode = "static";
130131
this.container.addChild(this.outline);
131132

132133
// Create corner handles
@@ -239,6 +240,10 @@ export class SelectionHandles implements CanvasOverlayRegistration {
239240
this.outline.strokeStyle = { width: SELECTION_CONSTANTS.OUTLINE_WIDTH / uiScale, color };
240241
this.outline.rect(0, 0, size.width, size.height);
241242
this.outline.stroke();
243+
244+
// Expand hit area to cover rotation zones outside corners
245+
const hitRect = calculateHitArea(size, uiScale);
246+
this.outline.hitArea = new pixi.Rectangle(hitRect.x, hitRect.y, hitRect.width, hitRect.height);
242247
}
243248

244249
private drawHandles(): void {
@@ -362,17 +367,18 @@ export class SelectionHandles implements CanvasOverlayRegistration {
362367
}
363368
}
364369

365-
// Check if inside player bounds for drag
366-
if (localPoint.x >= 0 && localPoint.x <= size.width && localPoint.y >= 0 && localPoint.y <= size.height) {
367-
// Check for edge resize first
370+
// Check for edge resize (zone extends outside clip bounds)
371+
if (this.selectedPlayer.supportsEdgeResize()) {
368372
const hitZone = SELECTION_CONSTANTS.EDGE_HIT_ZONE / this.getUIScale();
369373
const edge = detectEdgeZone(localPoint, size, hitZone);
370-
if (edge && this.selectedPlayer.supportsEdgeResize()) {
374+
if (edge) {
371375
this.startEdgeResize(event, edge);
372376
return;
373377
}
378+
}
374379

375-
// Start position drag
380+
// Check if inside player bounds for drag
381+
if (localPoint.x >= 0 && localPoint.x <= size.width && localPoint.y >= 0 && localPoint.y <= size.height) {
376382
this.startDrag(event);
377383
}
378384
}
@@ -804,23 +810,30 @@ export class SelectionHandles implements CanvasOverlayRegistration {
804810
const playerContainer = this.selectedPlayer.getContainer();
805811
const localPoint = event.getLocalPosition(playerContainer);
806812
const size = this.selectedPlayer.getSize();
813+
const uiScale = this.getUIScale();
814+
const rotation = this.selectedPlayer.getRotation() ?? 0;
807815

808816
this.isHovering = localPoint.x >= 0 && localPoint.x <= size.width && localPoint.y >= 0 && localPoint.y <= size.height;
809817

810-
// Update cursor for edge resize zones
818+
// Priority order mirrors onPointerDown: rotation → edges → default
819+
const rotationCorner = this.getRotationCorner(localPoint, size);
820+
if (rotationCorner) {
821+
const baseAngle = CURSOR_BASE_ANGLES[rotationCorner] ?? 0;
822+
this.outline.cursor = buildRotationCursor(baseAngle + rotation);
823+
return;
824+
}
825+
811826
if (this.selectedPlayer.supportsEdgeResize()) {
812-
const hitZone = SELECTION_CONSTANTS.EDGE_HIT_ZONE / this.getUIScale();
827+
const hitZone = SELECTION_CONSTANTS.EDGE_HIT_ZONE / uiScale;
813828
const edge = detectEdgeZone(localPoint, size, hitZone);
814829

815830
if (edge) {
816-
const rotation = this.selectedPlayer.getRotation() ?? 0;
817831
const baseAngle = CURSOR_BASE_ANGLES[edge] ?? 0;
818832
this.outline.cursor = buildResizeCursor(baseAngle + rotation);
819833
return;
820834
}
821835
}
822836

823-
// Reset cursor when not over an edge
824837
this.outline.cursor = this.isHovering ? "move" : "default";
825838
}
826839

0 commit comments

Comments
 (0)