mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-05 06:22:20 +02:00
Squash Odysseus development history
This commit is contained in:
@@ -13,21 +13,37 @@
|
||||
* tool and was handled (so the dispatcher can short-circuit).
|
||||
*
|
||||
* @param {{
|
||||
* beginMove: (e: Event) => void,
|
||||
* composite: () => void,
|
||||
* drawTransformHandles: () => void,
|
||||
* reapplyTransform: () => void,
|
||||
* getTransformHandle: (x: number, y: number) => string | null,
|
||||
* cursorForHandle: (id: string | null) => string,
|
||||
* pointInTransformFrame: (x: number, y: number) => boolean,
|
||||
* snapTransformFrame?: (frame: object, center: {x:number,y:number}) => object,
|
||||
* }} deps
|
||||
*/
|
||||
import { state } from '../state.js';
|
||||
import { canvasCoords } from '../canvas-coords.js';
|
||||
import { resizeTransformFrame } from '../transform-frame-geometry.js';
|
||||
import { createDirectManipulationSession } from '../direct-manipulation-session.js';
|
||||
|
||||
export function createTransformDragTool({
|
||||
beginMove, composite, drawTransformHandles, reapplyTransform,
|
||||
getTransformHandle, cursorForHandle,
|
||||
composite, drawTransformHandles, reapplyTransform,
|
||||
getTransformHandle, cursorForHandle, pointInTransformFrame,
|
||||
snapTransformFrame,
|
||||
}) {
|
||||
const gesture = createDirectManipulationSession({
|
||||
name: 'transform-frame',
|
||||
getContext: () => ({ canvas: state.mainCanvas, items: state.transformItems }),
|
||||
isContextCurrent: context => state.transformActive &&
|
||||
context.canvas === state.mainCanvas && context.items === state.transformItems,
|
||||
onCancel: () => {
|
||||
state.transformHandle = null;
|
||||
state.activeSnapGuides = null;
|
||||
composite();
|
||||
drawTransformHandles();
|
||||
},
|
||||
});
|
||||
return {
|
||||
/**
|
||||
* Called on pointerdown. Returns true if the transform tool handled
|
||||
@@ -36,33 +52,36 @@ export function createTransformDragTool({
|
||||
tryBegin(e) {
|
||||
if (!state.transformActive) return false;
|
||||
const coords = canvasCoords(e, state.mainCanvas);
|
||||
state.transformHandle = getTransformHandle(coords.x, coords.y);
|
||||
const pointerType = e.pointerType || (e.touches ? 'touch' : 'mouse');
|
||||
state.transformHandle = getTransformHandle(coords.x, coords.y, { pointerType });
|
||||
if (state.transformHandle) {
|
||||
state.transformStartX = coords.x;
|
||||
state.transformStartY = coords.y;
|
||||
// Snapshot offset + size at drag-start so each frame computes
|
||||
// "start + dx" (correct delta) rather than accumulating off the
|
||||
// running offset, which was making top/left grabs drift.
|
||||
const layer = state.transformLayer;
|
||||
const off = state.layerOffsets.get(layer.id) || { x: 0, y: 0 };
|
||||
state.transformStartOffX = off.x;
|
||||
state.transformStartOffY = off.y;
|
||||
state.transformOrigW = layer.canvas.width;
|
||||
state.transformOrigH = layer.canvas.height;
|
||||
const center = state.transformCenter || { x: 0, y: 0 };
|
||||
state.transformStartCenter = { ...center };
|
||||
state.transformOrigW = state.transformPendingW;
|
||||
state.transformOrigH = state.transformPendingH;
|
||||
state.transformStartRotation = state.transformPendingRot;
|
||||
state.transformStartFlipH = state.transformPendingFlipH;
|
||||
state.transformStartFlipV = state.transformPendingFlipV;
|
||||
state.transformStartOffX = center.x - state.transformOrigW / 2;
|
||||
state.transformStartOffY = center.y - state.transformOrigH / 2;
|
||||
gesture.begin(e, { handle: state.transformHandle }, { captureTarget: e.currentTarget });
|
||||
return true;
|
||||
}
|
||||
// No corner hit — if click inside the layer's bounding box, act
|
||||
// like Move so the user can drag the layer around without
|
||||
// switching tools.
|
||||
if (state.transformLayer) {
|
||||
const off = state.layerOffsets.get(state.transformLayer.id) || { x: 0, y: 0 };
|
||||
const w = state.transformLayer.canvas.width;
|
||||
const h = state.transformLayer.canvas.height;
|
||||
if (coords.x >= off.x && coords.x <= off.x + w &&
|
||||
coords.y >= off.y && coords.y <= off.y + h) {
|
||||
beginMove(e);
|
||||
return true;
|
||||
}
|
||||
// No handle hit: dragging inside the staged frame moves the transform
|
||||
// itself. Keeping this in the transform session is essential for
|
||||
// selection transforms, which must never fall through to layer Move.
|
||||
if (pointInTransformFrame?.(coords.x, coords.y)) {
|
||||
state.transformHandle = 'move';
|
||||
state.transformStartX = coords.x;
|
||||
state.transformStartY = coords.y;
|
||||
state.transformStartCenter = { ...(state.transformCenter || { x: 0, y: 0 }) };
|
||||
gesture.begin(e, { handle: 'move' }, { captureTarget: e.currentTarget });
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
@@ -79,8 +98,12 @@ export function createTransformDragTool({
|
||||
// No drag in progress — just hover-cursor + pulse.
|
||||
if (!state.transformHandle && state.mainCanvas) {
|
||||
const coords = canvasCoords(e, state.mainCanvas);
|
||||
const hovered = getTransformHandle(coords.x, coords.y);
|
||||
state.mainCanvas.style.cursor = hovered ? cursorForHandle(hovered) : 'default';
|
||||
const pointerType = e.pointerType || (e.touches ? 'touch' : 'mouse');
|
||||
const hovered = getTransformHandle(coords.x, coords.y, { pointerType });
|
||||
const bodyHovered = !hovered && pointInTransformFrame?.(coords.x, coords.y);
|
||||
state.mainCanvas.style.cursor = hovered
|
||||
? cursorForHandle(hovered, state.transformPendingRot)
|
||||
: bodyHovered ? 'move' : 'default';
|
||||
if (hovered !== state.hoveredHandle) {
|
||||
state.hoveredHandle = hovered;
|
||||
composite();
|
||||
@@ -88,15 +111,38 @@ export function createTransformDragTool({
|
||||
return false; // didn't fully consume the event
|
||||
}
|
||||
if (!state.transformHandle) return false;
|
||||
if (!gesture.update(e)) return false;
|
||||
e.preventDefault();
|
||||
const coords = canvasCoords(e, state.mainCanvas);
|
||||
if (state.transformHandle === 'move') {
|
||||
let center = {
|
||||
x: state.transformStartCenter.x + coords.x - state.transformStartX,
|
||||
y: state.transformStartCenter.y + coords.y - state.transformStartY,
|
||||
};
|
||||
const modifier = e.ctrlKey || e.metaKey;
|
||||
const snapping = state.snapEnabled ? !modifier : modifier;
|
||||
if (snapping && snapTransformFrame) {
|
||||
const snapped = snapTransformFrame({
|
||||
centerX: state.transformStartCenter.x,
|
||||
centerY: state.transformStartCenter.y,
|
||||
width: state.transformPendingW,
|
||||
height: state.transformPendingH,
|
||||
rotation: state.transformPendingRot,
|
||||
}, center);
|
||||
center = { x: snapped.centerX, y: snapped.centerY };
|
||||
state.activeSnapGuides = snapped.guides || null;
|
||||
} else {
|
||||
state.activeSnapGuides = null;
|
||||
}
|
||||
state.transformCenter = center;
|
||||
reapplyTransform();
|
||||
return true;
|
||||
}
|
||||
// Rotation grip — angle measured from the layer's geometric
|
||||
// centre to the cursor. Mirror into the popup if it's open.
|
||||
if (state.transformHandle === 'rot') {
|
||||
const layer = state.transformLayer;
|
||||
const off = state.layerOffsets.get(layer.id) || { x: 0, y: 0 };
|
||||
const cx = off.x + layer.canvas.width / 2;
|
||||
const cy = off.y + layer.canvas.height / 2;
|
||||
const cx = state.transformCenter?.x || 0;
|
||||
const cy = state.transformCenter?.y || 0;
|
||||
const rad = Math.atan2(coords.y - cy, coords.x - cx) + Math.PI / 2;
|
||||
let deg = Math.round((rad * 180) / Math.PI);
|
||||
if (e.shiftKey) deg = Math.round(deg / 15) * 15; // 15° snap
|
||||
@@ -110,50 +156,35 @@ export function createTransformDragTool({
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Resize via corner / edge handle.
|
||||
const dx = coords.x - state.transformStartX;
|
||||
const dy = coords.y - state.transformStartY;
|
||||
const layer = state.transformLayer;
|
||||
let newW = layer.canvas.width;
|
||||
let newH = layer.canvas.height;
|
||||
if (state.transformHandle.includes('r')) newW = state.transformOrigW + dx;
|
||||
if (state.transformHandle.includes('l')) newW = state.transformOrigW - dx;
|
||||
if (state.transformHandle.includes('b')) newH = state.transformOrigH + dy;
|
||||
if (state.transformHandle.includes('t')) newH = state.transformOrigH - dy;
|
||||
// Shift = lock aspect ratio. Use whichever axis moved more
|
||||
// (relative to the original) as the driver.
|
||||
if (e.shiftKey && state.transformOrigW > 0 && state.transformOrigH > 0) {
|
||||
const aspect = state.transformOrigW / state.transformOrigH;
|
||||
const wDelta = Math.abs(newW - state.transformOrigW);
|
||||
const hDelta = Math.abs(newH - state.transformOrigH);
|
||||
if (wDelta >= hDelta) {
|
||||
newH = Math.max(1, Math.round(newW / aspect));
|
||||
} else {
|
||||
newW = Math.max(1, Math.round(newH * aspect));
|
||||
}
|
||||
}
|
||||
newW = Math.max(1, Math.round(newW));
|
||||
newH = Math.max(1, Math.round(newH));
|
||||
// Route through the popup-driven pipeline so popup + drag stay
|
||||
// in sync. Anchor the opposite corner via transformOrigOffset so
|
||||
// handles don't slide while the user drags.
|
||||
state.transformPendingW = newW;
|
||||
state.transformPendingH = newH;
|
||||
const anchorOffX = state.transformStartOffX +
|
||||
(state.transformHandle.includes('l') ? (state.transformOrigW - newW) : 0);
|
||||
const anchorOffY = state.transformStartOffY +
|
||||
(state.transformHandle.includes('t') ? (state.transformOrigH - newH) : 0);
|
||||
state.transformOrigOffset = {
|
||||
x: anchorOffX + newW / 2 - state.transformOrigW / 2,
|
||||
y: anchorOffY + newH / 2 - state.transformOrigH / 2,
|
||||
};
|
||||
// Resize in the frame's LOCAL axes. This keeps rotated side handles
|
||||
// attached to the pointer and fixes the opposite edge in document space.
|
||||
const resized = resizeTransformFrame({
|
||||
centerX: state.transformStartCenter.x,
|
||||
centerY: state.transformStartCenter.y,
|
||||
width: state.transformOrigW,
|
||||
height: state.transformOrigH,
|
||||
rotation: state.transformStartRotation,
|
||||
}, state.transformHandle, {
|
||||
x: state.transformStartX,
|
||||
y: state.transformStartY,
|
||||
}, coords, {
|
||||
centered: !!e.altKey,
|
||||
lockAspect: !!e.shiftKey,
|
||||
flipH: state.transformStartFlipH,
|
||||
flipV: state.transformStartFlipV,
|
||||
});
|
||||
state.transformPendingW = resized.width;
|
||||
state.transformPendingH = resized.height;
|
||||
state.transformPendingFlipH = resized.flipH;
|
||||
state.transformPendingFlipV = resized.flipV;
|
||||
state.transformCenter = { x: resized.centerX, y: resized.centerY };
|
||||
reapplyTransform();
|
||||
// Mirror the new W/H into the popup if it's open.
|
||||
if (state.transformPopup) {
|
||||
const wIn = state.transformPopup.querySelector('#ge-transform-w');
|
||||
const hIn = state.transformPopup.querySelector('#ge-transform-h');
|
||||
if (wIn) wIn.value = String(state.transformPendingFlipH ? -newW : newW);
|
||||
if (hIn) hIn.value = String(state.transformPendingFlipV ? -newH : newH);
|
||||
if (wIn) wIn.value = String(state.transformPendingFlipH ? -resized.width : resized.width);
|
||||
if (hIn) hIn.value = String(state.transformPendingFlipV ? -resized.height : resized.height);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
@@ -161,14 +192,19 @@ export function createTransformDragTool({
|
||||
/**
|
||||
* Called on pointerup. Returns true if handled.
|
||||
*/
|
||||
tryEnd() {
|
||||
tryEnd(e) {
|
||||
if (!(state.transformActive && state.transformHandle)) return false;
|
||||
if (!gesture.commit(e)) return false;
|
||||
state.transformHandle = null;
|
||||
state.transformOrigW = state.transformLayer?.canvas.width || 0;
|
||||
state.transformOrigH = state.transformLayer?.canvas.height || 0;
|
||||
state.transformOrigW = state.transformPendingW;
|
||||
state.transformOrigH = state.transformPendingH;
|
||||
state.activeSnapGuides = null;
|
||||
composite();
|
||||
drawTransformHandles();
|
||||
return true;
|
||||
},
|
||||
cancel(reason) {
|
||||
return gesture.cancel(reason);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user