feat: Focus indicator (#10613)
* feat: Focus indicator Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Snapshot update Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Move visualdebug to utils and introduce volume bindable volume visualization Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Move visualdebug to elements Due to dep circles Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Possible test timeout Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Incorrect hit test point input Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Add fallback when dragged outside of allowed area Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Elbow arrows don't need focus point mgmt Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: End bound indirect fix Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Show indicator when arrow endpoint dragging Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Update bound arrow endpoint at mid-point drag Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * chore: Refactor Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Curve endpoint intersection Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Outline focus point is reset on existing arrow drag Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Tests Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * chore: Fix lint Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Dragging focus point off Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Don't show the focus indicator when arrow endpoint is dragged Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Drag area for focus handles Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Focus point size unified Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Size bump for focus knob Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Cache hits and scene lookups Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * chore: Remove debug Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Consider hit threshold and inside override too Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Other shape switching Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * perf: Update tolerance params Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Focus know line width Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: knob offset Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Full overlap Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * chore: Remove Map caching Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: incorrect threshold Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: threshold setting Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Hit caching Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: cache override Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Snapshots Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Redesigned focus point handling Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Inside-inside mode Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * chore: Remove comment Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Allow focus knob outside the shape Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Arrow endpoint offset Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Focus knob element distance Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Increase iteration on curve intersection calc Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Handle disabled binding Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Alt mode Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Nested shape focus rewrite Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Alt + Ctrl + arrow endpoitn Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Hit ordering for focus points Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Focus point visibility Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * dry out renderFocusPointIndicator * do not higlight point when dragging & make focus point smaller * optimize retrieval of selectedLinearElement * move focus highlighting into renderFocusPointIndicator to DRY out and colocate * remove `disabled` state from focus highlight * make focus point stroke color less prominent * fix: No focus point for multi-point arrows Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Arrow edit mode drag focus point release Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * DRY out arrow point-like drag * move `focus.ts` to `arrows/focus.ts` --------- Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com>
This commit is contained in:
@@ -59,8 +59,11 @@ import { LinearElementEditor } from "./linearElementEditor";
|
||||
|
||||
import { distanceToElement } from "./distance";
|
||||
|
||||
import { getBindingGap } from "./binding";
|
||||
|
||||
import type {
|
||||
ElementsMap,
|
||||
ExcalidrawArrowElement,
|
||||
ExcalidrawBindableElement,
|
||||
ExcalidrawDiamondElement,
|
||||
ExcalidrawElement,
|
||||
@@ -290,7 +293,7 @@ export const getAllHoveredElementAtPoint = (
|
||||
point: Readonly<GlobalPoint>,
|
||||
elements: readonly Ordered<NonDeletedExcalidrawElement>[],
|
||||
elementsMap: NonDeletedSceneElementsMap,
|
||||
toleranceFn?: (element: ExcalidrawBindableElement) => number,
|
||||
tolerance?: number,
|
||||
): NonDeleted<ExcalidrawBindableElement>[] => {
|
||||
const candidateElements: NonDeleted<ExcalidrawBindableElement>[] = [];
|
||||
// We need to to hit testing from front (end of the array) to back (beginning of the array)
|
||||
@@ -306,7 +309,7 @@ export const getAllHoveredElementAtPoint = (
|
||||
|
||||
if (
|
||||
isBindableElement(element, false) &&
|
||||
bindingBorderTest(element, point, elementsMap, toleranceFn?.(element))
|
||||
bindingBorderTest(element, point, elementsMap, tolerance)
|
||||
) {
|
||||
candidateElements.push(element);
|
||||
|
||||
@@ -323,13 +326,13 @@ export const getHoveredElementForBinding = (
|
||||
point: Readonly<GlobalPoint>,
|
||||
elements: readonly Ordered<NonDeletedExcalidrawElement>[],
|
||||
elementsMap: NonDeletedSceneElementsMap,
|
||||
toleranceFn?: (element: ExcalidrawBindableElement) => number,
|
||||
tolerance?: number,
|
||||
): NonDeleted<ExcalidrawBindableElement> | null => {
|
||||
const candidateElements = getAllHoveredElementAtPoint(
|
||||
point,
|
||||
elements,
|
||||
elementsMap,
|
||||
toleranceFn,
|
||||
tolerance,
|
||||
);
|
||||
|
||||
if (!candidateElements || candidateElements.length === 0) {
|
||||
@@ -348,6 +351,56 @@ export const getHoveredElementForBinding = (
|
||||
.pop() as NonDeleted<ExcalidrawBindableElement>;
|
||||
};
|
||||
|
||||
export const getHoveredElementForFocusPoint = (
|
||||
point: GlobalPoint,
|
||||
arrow: ExcalidrawArrowElement,
|
||||
elements: readonly Ordered<NonDeletedExcalidrawElement>[],
|
||||
elementsMap: NonDeletedSceneElementsMap,
|
||||
tolerance?: number,
|
||||
): ExcalidrawBindableElement | null => {
|
||||
const candidateElements: NonDeleted<ExcalidrawBindableElement>[] = [];
|
||||
// We need to to hit testing from front (end of the array) to back (beginning of the array)
|
||||
// because array is ordered from lower z-index to highest and we want element z-index
|
||||
// with higher z-index
|
||||
for (let index = elements.length - 1; index >= 0; --index) {
|
||||
const element = elements[index];
|
||||
|
||||
invariant(
|
||||
!element.isDeleted,
|
||||
"Elements in the function parameter for getAllElementsAtPositionForBinding() should not contain deleted elements",
|
||||
);
|
||||
|
||||
if (
|
||||
isBindableElement(element, false) &&
|
||||
bindingBorderTest(element, point, elementsMap, tolerance)
|
||||
) {
|
||||
candidateElements.push(element);
|
||||
}
|
||||
}
|
||||
|
||||
if (!candidateElements || candidateElements.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (candidateElements.length === 1) {
|
||||
return candidateElements[0];
|
||||
}
|
||||
|
||||
const distanceFilteredCandidateElements = candidateElements
|
||||
// Resolve by distance
|
||||
.filter(
|
||||
(el) =>
|
||||
distanceToElement(el, elementsMap, point) <= getBindingGap(el, arrow) ||
|
||||
isPointInElement(point, el, elementsMap),
|
||||
);
|
||||
|
||||
if (distanceFilteredCandidateElements.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return distanceFilteredCandidateElements[0] as NonDeleted<ExcalidrawBindableElement>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Intersect a line with an element for binding test
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user