- Refactor LayersSheet component to support mask editing state and improve layer visibility handling. - Introduce functions to collect mask layer IDs and count display layers excluding masks. - Update BrushControls to include mask view mode options and a Done button for exiting mask editing. - Modify brush session handling to support brush previews when editing masks. - Implement a new BrushPreviewRenderer for rendering brush strokes with visual feedback. - Add document geometry utilities for transforming points and resolving layer bounds. - Ensure proper cleanup of brush preview on pointer leave and other interactions.
100 lines
3.9 KiB
TypeScript
100 lines
3.9 KiB
TypeScript
import { commandIds } from "@commands/ids";
|
|
import type { Dispatch } from "@commands/dispatcher";
|
|
import type { ImageDocument } from "@core/document";
|
|
import type { ArtboardId, LayerId } from "@core/id";
|
|
import type { Layer } from "@core/layer";
|
|
import type { KeybindEvent } from "./keyboard";
|
|
|
|
export type LayerInfo = {
|
|
artboardId: ArtboardId;
|
|
parentGroupId?: LayerId;
|
|
layer: Layer;
|
|
};
|
|
|
|
export type LayerDropTarget = {
|
|
artboardId: ArtboardId;
|
|
layer: Layer;
|
|
};
|
|
|
|
export type DeleteSelectionState = {
|
|
artboardId?: ArtboardId;
|
|
layerIds: LayerId[];
|
|
};
|
|
|
|
export function handleDeleteSelectionKey(options: { event: KeybindEvent; selection: DeleteSelectionState; dispatch: Dispatch }): boolean {
|
|
if (options.event.altKey || options.event.ctrlKey || options.event.metaKey) return false;
|
|
if (options.event.key !== "Backspace" && options.event.key !== "Delete") return false;
|
|
|
|
for (const layerId of options.selection.layerIds) options.dispatch(commandIds.documentRemoveLayer, { layerId });
|
|
if (options.selection.layerIds.length === 0 && options.selection.artboardId) {
|
|
options.dispatch(commandIds.documentRemoveArtboard, { id: options.selection.artboardId });
|
|
}
|
|
return true;
|
|
}
|
|
|
|
export function resolveLayerDrop(options: {
|
|
document: ImageDocument;
|
|
sourceLayerId: LayerId;
|
|
target: LayerDropTarget;
|
|
verticalRatio: number;
|
|
}): { layerId: LayerId; toArtboardId: ArtboardId; toParentGroupId?: LayerId; toIndex: number } | undefined {
|
|
const targetInfo = findLayerInfoInDocument(options.document, options.target.layer.id);
|
|
const sourceInfo = findLayerInfoInDocument(options.document, options.sourceLayerId);
|
|
if (!targetInfo || !sourceInfo || options.sourceLayerId === options.target.layer.id) return undefined;
|
|
|
|
const verticalRatio = Math.max(0, Math.min(1, options.verticalRatio));
|
|
const dropIntoGroup = options.target.layer.type === "group" && verticalRatio >= 0.33 && verticalRatio <= 0.66;
|
|
if (dropIntoGroup) {
|
|
return {
|
|
layerId: options.sourceLayerId,
|
|
toArtboardId: options.target.artboardId,
|
|
toParentGroupId: options.target.layer.id,
|
|
toIndex: options.target.layer.children.length,
|
|
};
|
|
}
|
|
|
|
const siblings = targetInfo.parentGroupId
|
|
? findGroup(options.document, targetInfo.parentGroupId)?.children
|
|
: options.document.artboards.find((artboard) => artboard.id === targetInfo.artboardId)?.layers;
|
|
if (!siblings) return undefined;
|
|
|
|
const targetIndex = siblings.findIndex((layer) => layer.id === options.target.layer.id);
|
|
const sourceIndex = sourceInfo.parentGroupId === targetInfo.parentGroupId && sourceInfo.artboardId === targetInfo.artboardId
|
|
? siblings.findIndex((layer) => layer.id === options.sourceLayerId)
|
|
: -1;
|
|
const rawIndex = verticalRatio < 0.5 ? targetIndex : targetIndex + 1;
|
|
const adjustedIndex = sourceIndex >= 0 && sourceIndex < rawIndex ? rawIndex - 1 : rawIndex;
|
|
|
|
return {
|
|
layerId: options.sourceLayerId,
|
|
toArtboardId: targetInfo.artboardId,
|
|
toParentGroupId: targetInfo.parentGroupId,
|
|
toIndex: adjustedIndex,
|
|
};
|
|
}
|
|
|
|
export function findLayerInfoInDocument(document: ImageDocument, layerId?: LayerId): LayerInfo | undefined {
|
|
if (!layerId) return undefined;
|
|
for (const artboard of document.artboards) {
|
|
const found = findLayerInfo(artboard.layers, layerId, artboard.id);
|
|
if (found) return found;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
export function findGroup(document: ImageDocument, groupId: LayerId): Extract<Layer, { type: "group" }> | undefined {
|
|
const info = findLayerInfoInDocument(document, groupId);
|
|
return info?.layer.type === "group" ? info.layer : undefined;
|
|
}
|
|
|
|
function findLayerInfo(layers: Layer[], layerId: LayerId, artboardId: ArtboardId, parentGroupId?: LayerId): LayerInfo | undefined {
|
|
for (const layer of layers) {
|
|
if (layer.id === layerId) return { artboardId, parentGroupId, layer };
|
|
if (layer.type === "group") {
|
|
const found = findLayerInfo(layer.children, layerId, artboardId, layer.id);
|
|
if (found) return found;
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|