|
9 | 9 | detectCornerZone, |
10 | 10 | detectEdgeZone |
11 | 11 | } 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"; |
13 | 13 | import { type ClipBounds, createClipBounds, createSnapContext, filterContainedClips, snap, snapRotation, visualToLogical } from "@core/interaction/snap-system"; |
14 | 14 | import { updateSvgViewBox, isSimpleRectSvg } from "@core/shared/svg-utils"; |
15 | 15 | import { Pointer } from "@inputs/pointer"; |
@@ -127,6 +127,7 @@ export class SelectionHandles implements CanvasOverlayRegistration { |
127 | 127 | this.app = app; |
128 | 128 |
|
129 | 129 | // Create outline |
| 130 | + this.outline.eventMode = "static"; |
130 | 131 | this.container.addChild(this.outline); |
131 | 132 |
|
132 | 133 | // Create corner handles |
@@ -239,6 +240,10 @@ export class SelectionHandles implements CanvasOverlayRegistration { |
239 | 240 | this.outline.strokeStyle = { width: SELECTION_CONSTANTS.OUTLINE_WIDTH / uiScale, color }; |
240 | 241 | this.outline.rect(0, 0, size.width, size.height); |
241 | 242 | 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); |
242 | 247 | } |
243 | 248 |
|
244 | 249 | private drawHandles(): void { |
@@ -362,17 +367,18 @@ export class SelectionHandles implements CanvasOverlayRegistration { |
362 | 367 | } |
363 | 368 | } |
364 | 369 |
|
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()) { |
368 | 372 | const hitZone = SELECTION_CONSTANTS.EDGE_HIT_ZONE / this.getUIScale(); |
369 | 373 | const edge = detectEdgeZone(localPoint, size, hitZone); |
370 | | - if (edge && this.selectedPlayer.supportsEdgeResize()) { |
| 374 | + if (edge) { |
371 | 375 | this.startEdgeResize(event, edge); |
372 | 376 | return; |
373 | 377 | } |
| 378 | + } |
374 | 379 |
|
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) { |
376 | 382 | this.startDrag(event); |
377 | 383 | } |
378 | 384 | } |
@@ -804,23 +810,30 @@ export class SelectionHandles implements CanvasOverlayRegistration { |
804 | 810 | const playerContainer = this.selectedPlayer.getContainer(); |
805 | 811 | const localPoint = event.getLocalPosition(playerContainer); |
806 | 812 | const size = this.selectedPlayer.getSize(); |
| 813 | + const uiScale = this.getUIScale(); |
| 814 | + const rotation = this.selectedPlayer.getRotation() ?? 0; |
807 | 815 |
|
808 | 816 | this.isHovering = localPoint.x >= 0 && localPoint.x <= size.width && localPoint.y >= 0 && localPoint.y <= size.height; |
809 | 817 |
|
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 | + |
811 | 826 | if (this.selectedPlayer.supportsEdgeResize()) { |
812 | | - const hitZone = SELECTION_CONSTANTS.EDGE_HIT_ZONE / this.getUIScale(); |
| 827 | + const hitZone = SELECTION_CONSTANTS.EDGE_HIT_ZONE / uiScale; |
813 | 828 | const edge = detectEdgeZone(localPoint, size, hitZone); |
814 | 829 |
|
815 | 830 | if (edge) { |
816 | | - const rotation = this.selectedPlayer.getRotation() ?? 0; |
817 | 831 | const baseAngle = CURSOR_BASE_ANGLES[edge] ?? 0; |
818 | 832 | this.outline.cursor = buildResizeCursor(baseAngle + rotation); |
819 | 833 | return; |
820 | 834 | } |
821 | 835 | } |
822 | 836 |
|
823 | | - // Reset cursor when not over an edge |
824 | 837 | this.outline.cursor = this.isHovering ? "move" : "default"; |
825 | 838 | } |
826 | 839 |
|
|
0 commit comments