aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/views/GestureOverlay.tsx34
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx7
-rw-r--r--src/client/views/nodes/DocumentView.tsx7
3 files changed, 27 insertions, 21 deletions
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index b0a750a9a..777a34ebc 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -64,6 +64,8 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
@observable private _pointerY?: number = undefined;
@observable private _points: { X: number; Y: number }[] = [];
@observable private _clipboardDoc?: JSX.Element = undefined;
+ @observable private _debugCusps: { X: number; Y: number }[] = [];
+ @observable private _debugGestures = false;
@computed private get height(): number {
return 2 * Math.max(this._pointerY && this._thumbY ? this._thumbY - this._pointerY : 100, 100);
@@ -170,9 +172,9 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
* Determines if what the array of cusp/intersection data corresponds to a scribble.
* true if there are at least 4 cusps and either:
* 1) the initial and final quarters of the array contain objects
- * 2) or half of the cusps contain objects
+ * 2) or a declining percentage (ranges from 0.5 to 0.2 - based on the number of cusps) of cusp lines intersect strokes
* @param intersectArray array of booleans coresponding to which scribble sections (regions separated by a cusp) contain Docs
- * @returns
+ * @returns truthy if it's a scribble
*/
determineIfScribble = (intersectArray: boolean[]) => {
const quarterArrayLength = Math.ceil(intersectArray.length / 3.9); // use 3.9 instead of 4 to work better with strokes with only 4 cusps
@@ -182,7 +184,7 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
}), { start: false, end: false }); // prettier-ignore
const percentCuspsWithContent = intersectArray.filter(value => value).length / intersectArray.length;
- return intersectArray.length > 3 && (percentCuspsWithContent >= 0.5 || (start && end));
+ return intersectArray.length > 3 && (percentCuspsWithContent >= Math.max(0.2, 1 / (intersectArray.length - 1)) || (start && end));
};
/**
* determines if inks intersect
@@ -236,9 +238,8 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
};
@action
onPointerUp = (e: PointerEvent) => {
- const ffView = DocumentView.DownDocView?.ComponentView instanceof CollectionFreeFormView && DocumentView.DownDocView.ComponentView;
- const downView = DocumentView.DownDocView;
- DocumentView.DownDocView = undefined;
+ const ffView = CollectionFreeFormView.DownFfview;
+ CollectionFreeFormView.DownFfview = undefined;
if (this._points.length > 1) {
const B = this.svgBounds;
const points = this._points.map(p => ({ X: p.X - B.left, Y: p.Y - B.top }));
@@ -251,6 +252,8 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
new Result(Gestures.Stroke, 1, Date.now); // prettier-ignore
const cuspArray = this.getCusps(points);
+ const rect = this._overlayRef.current?.getBoundingClientRect();
+ this._debugCusps = rect ? cuspArray.map(p => ({ X: p.X + B.left - rect?.left, Y: p.Y + B.top - rect.top })) : [];
// if any of the shape is activated in the CollectionFreeFormViewChrome
// need to decide when to turn gestures back on
const actionPerformed = ((name: Gestures) => {
@@ -272,14 +275,14 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
if (!actionPerformed) {
const scribbledOver = ffView && this.isScribble(ffView, cuspArray, this._points);
+ this.dryInk();
if (scribbledOver) {
- undoable(() => ffView.removeDocument(scribbledOver), 'scribble erase')();
- } else {
- this.dryInk();
+ // can undo the erase without undoing the scribble, or undo a second time to undo the scribble
+ setTimeout(undoable(() => ffView.removeDocument(scribbledOver.concat([ffView.childDocs.lastElement()])), 'scribble erase'));
}
}
} else {
- (downView?.ComponentView as CollectionFreeFormView)?._marqueeViewRef?.current?.setPreviewCursor?.(this._points[0].X, this._points[0].Y, false, false, undefined);
+ ffView?._marqueeViewRef?.current?.setPreviewCursor?.(this._points[0].X, this._points[0].Y, false, false, undefined);
e.preventDefault();
}
this.primCreated();
@@ -336,13 +339,14 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
getCusps(points: InkData) {
const arrayOfPoints: { X: number; Y: number }[] = [];
arrayOfPoints.push(points[0]);
- for (let i = 0; i < points.length - 2; i++) {
+ for (let i = 0; i < points.length - 4; i++) {
const point1 = points[i];
- const point2 = points[i + 1];
- const point3 = points[i + 2];
+ const point2 = points[i + 2];
+ const point3 = points[i + 4];
if (this.find_angle(point1, point2, point3) < 90) {
// NOTE: this is not an accurate way to find cusps -- it is highly dependent on sampling rate and doesn't work well with slowly drawn scribbles
arrayOfPoints.push(point2);
+ i += 2;
}
}
arrayOfPoints.push(points[points.length - 1]);
@@ -538,7 +542,7 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
}
get elements() {
- const selView = DocumentView.DownDocView;
+ const selView = CollectionFreeFormView.DownFfview;
const width = Number(ActiveInkWidth()) * NumCast(selView?.Document._freeform_scale, 1); // * (selView?.screenToViewTransform().Scale || 1);
const rect = this._overlayRef.current?.getBoundingClientRect();
const B = { left: -20000, right: 20000, top: -20000, bottom: 20000, width: 40000, height: 40000 }; // this.getBounds(this._points, true);
@@ -590,7 +594,7 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
return (
<div className="gestureOverlay-cont" style={{ pointerEvents: this._props.isActive ? 'all' : 'none' }} ref={this._overlayRef} onPointerDown={this.onPointerDown}>
{this.elements}
-
+ {this._debugGestures && this._debugCusps.map(c => <div key={c.toString()} style={{ top: 0, left: 0, position: 'absolute', transform: `translate(${c.X}px, ${c.Y}px)`, width: 4, height: 4, background: 'red' }} />)}
<div
className="clipboardDoc-cont"
style={{
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 3de97f836..49b432d7c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -98,6 +98,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const parent = CollectionFreeFormDocumentView.from(dv)?._props.reactParent;
return parent instanceof CollectionFreeFormView ? parent : undefined;
}
+ /**
+ * The Freeformview below the cursor at the start of a gesture (that receives the pointerDown event). Used by GestureOverlay to determine the doc a gesture should apply to.
+ */
+ // eslint-disable-next-line no-use-before-define
+ public static DownFfview: CollectionFreeFormView | undefined; // the first DocView that receives a pointerdown event. used by GestureOverlay to determine the doc a gesture should apply to.
private _clusters = new CollectionFreeFormClusters(this);
private _oldWheel: HTMLDivElement | null = null;
@@ -497,6 +502,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
onPointerDown = (e: React.PointerEvent): void => {
+ if (!CollectionFreeFormView.DownFfview) CollectionFreeFormView.DownFfview = this;
+
this._downX = this._lastX = e.pageX;
this._downY = this._lastY = e.pageY;
this._downTime = Date.now();
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 92e98db87..4bfa7fc92 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -355,7 +355,6 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
onPointerDown = (e: React.PointerEvent): void => {
if (this._props.isGroupActive?.() === GroupActive.child && !this._props.isDocumentActive?.()) return;
this._longPressSelector = setTimeout(() => SnappingManager.LongPress && this._props.select(false), 1000);
- if (!DocumentView.DownDocView) DocumentView.DownDocView = this._docView;
this._downX = e.clientX;
this._downY = e.clientY;
@@ -459,7 +458,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
}
if (annoData || this.Document !== linkdrag.linkSourceDoc.embedContainer) {
const dropDoc = annoData?.dropDocument ?? this._componentView?.getAnchor?.(true) ?? this.Document;
- const linkDoc = DocUtils.MakeLink(linkdrag.linkSourceDoc, dropDoc, {layout_isSvg: true }, undefined, [de.x, de.y - 50]);
+ const linkDoc = DocUtils.MakeLink(linkdrag.linkSourceDoc, dropDoc, { layout_isSvg: true }, undefined, [de.x, de.y - 50]);
if (linkDoc) {
de.complete.linkDocument = linkDoc;
DocumentView.linkCommonAncestor(linkDoc)?.ComponentView?.addDocument?.(linkDoc);
@@ -1075,10 +1074,6 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
?.ComponentView?.updateIcon?.()
.then(() => ImageCast(DocCast(doc).icon));
}
- /**
- * The DocumentView below the cursor at the start of a gesture (that receives the pointerDown event). Used by GestureOverlay to determine the doc a gesture should apply to.
- */
- public static DownDocView: DocumentView | undefined; // the first DocView that receives a pointerdown event. used by GestureOverlay to determine the doc a gesture should apply to.
public get displayName() { return 'DocumentView(' + (this.Document?.title??"") + ')'; } // prettier-ignore
private _htmlOverlayEffect: Opt<Doc>;