0.18.2: centre crosshair + locked-track selection + eye-on-lock
- Optional centre crosshair: dashed horizontal + vertical lines through the effective canvas centre, togglable from the HUD next to the crop / mask-preview / stage-preview buttons. Scales with zoom and stays visible inside clip-edit. - Locked clip tracks now behave as if they weren't on the stage — the ci-group is listening:false + draggable:false, so selection, drag, and transform all pass through. deselectIfLocked also clears a clip-instance selection that just got locked out (directly or via a parent group). - Eye toggle stays clickable on locked layer / clip-track rows — previous pointer-events:none rule only whitelisted the lock button. Hide-in-editor is orthogonal to lock. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
5af0cdb3d4
commit
21cf6e9a12
7 files changed
+371
-19
No files matched your search
@@ -5,6 +5,7 @@ import {
|
||||
PhCircle,
|
||||
PhCornersIn,
|
||||
PhCrop,
|
||||
PhCrosshair,
|
||||
PhCursorClick,
|
||||
PhFrameCorners,
|
||||
PhMaskHappy,
|
||||
@@ -56,6 +57,10 @@ const viewX = ref(0)
|
||||
const viewY = ref(0)
|
||||
const cropObjects = ref(false)
|
||||
const maskPreview = ref(false)
|
||||
// Dashed horizontal + vertical lines through the center of the
|
||||
// effective canvas — a composition guide the user can toggle on/off
|
||||
// from the HUD. Not persisted; local to this editor session.
|
||||
const centerCrosshair = ref(false)
|
||||
// Stage-preview mode: when false (default) buttons behave like normal
|
||||
// assets — draggable, no hover/click interaction. When true the canvas
|
||||
// turns into a live preview with buttons responding to input.
|
||||
@@ -593,6 +598,41 @@ const clipGroupConfig = computed(() => {
|
||||
}
|
||||
})
|
||||
|
||||
/** Horizontal + vertical centre-crosshair line configs. Null when
|
||||
* the toggle is off. Lines span the effective canvas so the centre
|
||||
* is exactly where layer keyframes default to (canvas.width/2,
|
||||
* canvas.height/2). Stroke + dash are scaled by `1 / zoom` so they
|
||||
* stay 1 screen-pixel thick and the dashes stay the same visual
|
||||
* length at any zoom level. `listening: false` keeps the lines out
|
||||
* of hit-testing — they're purely a visual guide. */
|
||||
const centerCrosshairConfigs = computed(() => {
|
||||
if (!centerCrosshair.value) return null
|
||||
const w = effectiveCanvas.value.width
|
||||
const h = effectiveCanvas.value.height
|
||||
const cx = w / 2
|
||||
const cy = h / 2
|
||||
const z = zoom.value || 1
|
||||
const strokeColor = 'rgba(79, 140, 255, 0.7)'
|
||||
const strokeWidth = 1 / z
|
||||
const dash = [6 / z, 4 / z]
|
||||
return {
|
||||
horizontal: {
|
||||
points: [0, cy, w, cy],
|
||||
stroke: strokeColor,
|
||||
strokeWidth,
|
||||
dash,
|
||||
listening: false,
|
||||
},
|
||||
vertical: {
|
||||
points: [cx, 0, cx, h],
|
||||
stroke: strokeColor,
|
||||
strokeWidth,
|
||||
dash,
|
||||
listening: false,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
const backgroundConfig = computed(() => {
|
||||
// In button-asset edit: checkerboard-ish transparent background so the
|
||||
// user sees where the button's canvas ends.
|
||||
@@ -935,6 +975,20 @@ function isLayerEffectivelyLocked(layer: Layer): boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
/** Clip-track counterpart to `isLayerEffectivelyLocked`. Returns true
|
||||
* when the ClipTrack's own `locked` is set OR the track sits inside
|
||||
* a locked group. Used to disable the ci-group's listening + drag so
|
||||
* a locked clip behaves as if it weren't on the stage — the user
|
||||
* can't select, drag, or transform its instance. */
|
||||
function isClipTrackEffectivelyLocked(trackId: string): boolean {
|
||||
const track = store.scene.clipTracks?.find((t) => t.id === trackId)
|
||||
if (track?.locked) return true
|
||||
for (const g of store.scene.groups ?? []) {
|
||||
if (g.locked && g.childIds.includes(trackId)) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function isLayerGhostLocked(layer: Layer): boolean {
|
||||
return isLayerEffectivelyLocked(layer) && !showLocked.value
|
||||
}
|
||||
@@ -1280,6 +1334,12 @@ function imageConfig(
|
||||
* events funnel to the outer group and drag/transform apply as a
|
||||
* unit. */
|
||||
function clipInstanceGroupConfig(inst: ClipInstance) {
|
||||
// A locked clip track must behave on the stage as if its instance
|
||||
// weren't there at all — clicks pass through, drag is off, and the
|
||||
// transformer can't latch on. `listening: false` on the group
|
||||
// cascades to every descendant (frame rect + inner shapes), so a
|
||||
// single flag covers the selection, drag, and transform paths.
|
||||
const locked = isClipTrackEffectivelyLocked(inst.clipTrackId)
|
||||
return {
|
||||
id: `ci-group:${inst.id}`,
|
||||
x: inst.x ?? 0,
|
||||
@@ -1287,7 +1347,8 @@ function clipInstanceGroupConfig(inst: ClipInstance) {
|
||||
scaleX: inst.scaleX ?? 1,
|
||||
scaleY: inst.scaleY ?? 1,
|
||||
rotation: inst.rotation ?? 0,
|
||||
draggable: true,
|
||||
draggable: !locked,
|
||||
listening: !locked,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3479,6 +3540,10 @@ function onResizeHandlePointerUp(e: PointerEvent) {
|
||||
</template>
|
||||
</v-group>
|
||||
<v-rect :config="canvasBorderConfig" />
|
||||
<template v-if="centerCrosshairConfigs">
|
||||
<v-line :config="centerCrosshairConfigs.horizontal" />
|
||||
<v-line :config="centerCrosshairConfigs.vertical" />
|
||||
</template>
|
||||
<v-rect
|
||||
v-for="h in canvasResizeHandles"
|
||||
:key="`canvas-resize-${h.edge}`"
|
||||
@@ -3627,6 +3692,14 @@ function onResizeHandlePointerUp(e: PointerEvent) {
|
||||
<PhMaskHappy v-if="!maskPreview" :size="13" />
|
||||
<PhPencilSimple v-else :size="13" />
|
||||
</button>
|
||||
<button
|
||||
class="zc-btn center-cross-btn"
|
||||
:class="{ active: centerCrosshair }"
|
||||
v-tooltip="centerCrosshair ? t('canvas.centerCrosshairOff') : t('canvas.centerCrosshairOn')"
|
||||
@click="centerCrosshair = !centerCrosshair"
|
||||
>
|
||||
<PhCrosshair :size="13" />
|
||||
</button>
|
||||
<button
|
||||
class="zc-btn stage-preview-btn"
|
||||
:class="{ active: stagePreview }"
|
||||
|
||||
Reference in new issue
Block a user