1.2.3: track-name shift/ctrl select, polygon level-1/level-2, cross-scope polygon ops
- Track-name click semantics in the Timeline:
- plain → single-select that layer
- shift → range-select every parent layer between anchor and click
(locked + locked-group children are filtered out)
- ctrl/cmd → toggle the clicked layer in/out of the selection
Anchor moves to the just-clicked id like a file manager.
- Polygon / freeform selection now lands at "level 1" — the regular
size + rotation transformer attaches like any other shape. A
double-click promotes to level 2 (vertex handles + edge-insert
dbl-click + tangent handles); selecting another layer drops back
to level 1. Same UX inside the SVG sub-editor.
- New layersUsingAsset(project, assetId) helper walks every layer
collection in the project (scenes, clips, button assets, svg
assets). All six polygon-mutation actions (addPolygonPoint,
removePolygonPoint, setPolygonPointCount, reversePolygonPoints,
movePolygonVertex, insertPolygonPointOnEdge) now use it, so
changes to the asset's points propagate to kf.props.points on
layers in any scope. Fixes "no node added on dblclick of a
freeform line inside an SVG asset".
- SvgLayerPanel buttons reset padding:0 — the global button reset
was eating the 22×22 row-button area with box-sizing:border-box
and clipping the Phosphor icons out of view.
This commit is contained in:
1 parent
c17e2e8a9f
commit
127812d3bf
7 files changed
+581
-17
No files matched your search
@@ -1757,10 +1757,10 @@ interface VertexHandle {
|
||||
}
|
||||
|
||||
const polygonVertexHandles = computed<VertexHandle[]>(() => {
|
||||
// Vertex handles only apply to a SOLO polygon selection — in a
|
||||
// mixed multi-selection the shared transformer bounding box owns
|
||||
// scale / rotate, and per-vertex handles would visually fight it.
|
||||
if (store.selectedLayerIds.length > 1) return []
|
||||
// Vertex handles only apply to a SOLO polygon selection in
|
||||
// *vertex-edit mode* (entered via double-click). Level-1
|
||||
// selection of a polygon shows the regular transformer instead.
|
||||
if (!polygonInVertexEdit.value) return []
|
||||
const id = store.selectedLayerId
|
||||
if (!id) return []
|
||||
const layer = store.activeLayers.find((l) => l.id === id)
|
||||
@@ -2085,10 +2085,26 @@ const selectedPolygonLayer = computed(() => {
|
||||
return { layer, asset }
|
||||
})
|
||||
|
||||
/** Layer id of the polygon currently in vertex-edit mode. A
|
||||
* polygon's first selection lands at "level 1" — the regular
|
||||
* transformer (size + rotation) attaches like any other shape.
|
||||
* Double-click promotes the polygon to "level 2" / vertex edit:
|
||||
* per-vertex handles + tangent handles + edge-insert dbl-clicks
|
||||
* light up. Cleared on selection change, scope change, or Escape. */
|
||||
const polygonVertexEditId = ref<string | null>(null)
|
||||
|
||||
const polygonInVertexEdit = computed(() => {
|
||||
if (store.selectedLayerIds.length > 1) return false
|
||||
const id = store.selectedLayerId
|
||||
if (!id) return false
|
||||
return polygonVertexEditId.value === id
|
||||
})
|
||||
|
||||
watch(
|
||||
() => store.selectedLayerId,
|
||||
() => {
|
||||
selectedVertexIndices.value = new Set()
|
||||
polygonVertexEditId.value = null
|
||||
closeVertexMenu()
|
||||
},
|
||||
)
|
||||
@@ -2313,8 +2329,10 @@ function attachTransformer(retriesLeft = 3) {
|
||||
|
||||
// Multi-select: wrap every selected layer node in one transformer
|
||||
// so the bounding box + scale / rotate handles span the group.
|
||||
// A SOLE polygon selection still bypasses the transformer because
|
||||
// a single polygon is edited via vertex handles; mixed selections
|
||||
// A SOLE polygon selection bypasses the transformer ONLY when
|
||||
// the user has promoted it to vertex-edit mode (double-click);
|
||||
// in level-1 / single-click selection the polygon gets the
|
||||
// regular transformer like any other shape. Mixed selections
|
||||
// that happen to include a polygon fold it into the shared bbox.
|
||||
const ids = store.selectedLayerIds.length
|
||||
? store.selectedLayerIds
|
||||
@@ -2333,7 +2351,13 @@ function attachTransformer(retriesLeft = 3) {
|
||||
const layer = store.activeLayers.find((l) => l.id === id)
|
||||
if (!layer) continue
|
||||
const asset = assetMap.value.get(layer.assetId)
|
||||
if (asset?.kind === 'polygon' && single) continue
|
||||
if (
|
||||
asset?.kind === 'polygon' &&
|
||||
single &&
|
||||
polygonInVertexEdit.value
|
||||
) {
|
||||
continue
|
||||
}
|
||||
const node = stage.findOne<Konva.Node>(`#${id}`)
|
||||
if (!node) {
|
||||
anyMissing = true
|
||||
@@ -2359,6 +2383,7 @@ watch(
|
||||
store.selectedClipInstanceId,
|
||||
stackItems.value,
|
||||
store.playhead,
|
||||
polygonVertexEditId.value,
|
||||
] as const,
|
||||
async () => {
|
||||
await nextTick()
|
||||
@@ -2700,7 +2725,9 @@ function handleStageMouseDown(e: Konva.KonvaEventObject<MouseEvent>) {
|
||||
// selection — inside a multi-select the box always targets
|
||||
// layers so new picks add to the existing group.
|
||||
const mode: 'vertex' | 'layer' =
|
||||
selectedPolygonLayer.value && store.selectedLayerIds.length <= 1
|
||||
selectedPolygonLayer.value &&
|
||||
store.selectedLayerIds.length <= 1 &&
|
||||
polygonInVertexEdit.value
|
||||
? 'vertex'
|
||||
: 'layer'
|
||||
boxSelect.value = {
|
||||
@@ -2894,6 +2921,16 @@ function onContainerDblClick(e: MouseEvent) {
|
||||
return
|
||||
}
|
||||
if (asset.kind !== 'polygon') return
|
||||
// First dbl-click on a level-1-selected polygon → enter
|
||||
// vertex-edit mode (level 2). The transformer detaches in the
|
||||
// attachTransformer watcher, vertex handles light up. Subsequent
|
||||
// dbl-clicks (already in vertex-edit mode) fall through to the
|
||||
// edge-insert / open-polygon resume gestures below. SVG-edit
|
||||
// scope skips this beat since polygons are always level-2 there.
|
||||
if (!polygonInVertexEdit.value) {
|
||||
polygonVertexEditId.value = layer.id
|
||||
return
|
||||
}
|
||||
const sampled = sampleLayer(layer, store.playhead)
|
||||
const pts = sampled.points ?? asset.points
|
||||
if (pts.length < 1) return
|
||||
|
||||
Reference in new issue
Block a user