feat: enhance layer masking functionality and brush controls
- 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.
This commit is contained in:
@@ -2,14 +2,13 @@ import { commandIds } from "@commands/ids";
|
||||
import type { Dispatch } from "@commands/dispatcher";
|
||||
import type { ImageDocument } from "@core/document";
|
||||
import type { Layer } from "@core/layer";
|
||||
import { resolveTransformTargetBounds } from "@editor/transform-targets";
|
||||
import type { ViewportState } from "@editor/state";
|
||||
import { resolveTransformTargetBounds, viewportPointToDocumentPoint, type InputViewportState } from "./document-geometry";
|
||||
import type { PointerInputEvent } from "./pointer";
|
||||
|
||||
export function handleArtboardSelection(options: {
|
||||
event: PointerInputEvent;
|
||||
document: ImageDocument;
|
||||
viewport: ViewportState;
|
||||
viewport: InputViewportState;
|
||||
dispatch: Dispatch;
|
||||
}): boolean {
|
||||
if (options.event.pointerType !== "mouse" || (options.event.buttons & 1) !== 1) return false;
|
||||
@@ -37,20 +36,21 @@ export function handleArtboardSelection(options: {
|
||||
}
|
||||
|
||||
function findTopmostLayerAtPoint(document: ImageDocument, point: { x: number; y: number }) {
|
||||
const maskLayerIds = collectMaskLayerIds(document.artboards.flatMap((artboard) => artboard.layers));
|
||||
for (const artboard of [...document.artboards].reverse()) {
|
||||
if (!artboard.visible || artboard.locked) continue;
|
||||
const layerId = findTopmostLayerInTreeAtPoint(document, [...artboard.layers].reverse(), point);
|
||||
const layerId = findTopmostLayerInTreeAtPoint(document, [...artboard.layers].reverse(), point, maskLayerIds);
|
||||
if (layerId) return { artboardId: artboard.id, layerId };
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function findTopmostLayerInTreeAtPoint(document: ImageDocument, layers: Layer[], point: { x: number; y: number }): string | undefined {
|
||||
function findTopmostLayerInTreeAtPoint(document: ImageDocument, layers: Layer[], point: { x: number; y: number }, maskLayerIds: ReadonlySet<string>): string | undefined {
|
||||
for (const layer of layers) {
|
||||
if (!layer.visible || layer.locked) continue;
|
||||
if (!layer.visible || layer.locked || maskLayerIds.has(layer.id)) continue;
|
||||
if (layer.type === "group") {
|
||||
const childId = findTopmostLayerInTreeAtPoint(document, [...layer.children].reverse(), point);
|
||||
const childId = findTopmostLayerInTreeAtPoint(document, [...layer.children].reverse(), point, maskLayerIds);
|
||||
if (childId) return childId;
|
||||
}
|
||||
|
||||
@@ -63,9 +63,11 @@ function findTopmostLayerInTreeAtPoint(document: ImageDocument, layers: Layer[],
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function viewportPointToDocumentPoint(point: PointerInputEvent["position"], viewport: ViewportState) {
|
||||
return {
|
||||
x: viewport.center.x + (point.x - viewport.size.w / 2) / viewport.zoom,
|
||||
y: viewport.center.y + (point.y - viewport.size.h / 2) / viewport.zoom,
|
||||
};
|
||||
function collectMaskLayerIds(layers: readonly Layer[], ids = new Set<string>()): Set<string> {
|
||||
for (const layer of layers) {
|
||||
if (layer.clippingMask) ids.add(layer.clippingMask.maskLayerId);
|
||||
if (layer.type === "group") collectMaskLayerIds(layer.children, ids);
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user