aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/InkingStroke.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/InkingStroke.tsx')
-rw-r--r--src/client/views/InkingStroke.tsx79
1 files changed, 56 insertions, 23 deletions
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 59efb36dd..ecc82a580 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -1,28 +1,29 @@
import React = require("react");
import { action, IReactionDisposer, observable, reaction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, HeightSym, WidthSym } from "../../fields/Doc";
+import { Doc, WidthSym } from "../../fields/Doc";
import { documentSchema } from "../../fields/documentSchemas";
import { InkData, InkField, InkTool } from "../../fields/InkField";
import { makeInterface } from "../../fields/Schema";
import { BoolCast, Cast, NumCast, StrCast } from "../../fields/Types";
import { TraceMobx } from "../../fields/util";
-import { emptyFunction, returnFalse, setupMoveUpEvents, OmitKeys } from "../../Utils";
+import { OmitKeys, returnFalse, setupMoveUpEvents } from "../../Utils";
import { CognitiveServices } from "../cognitive_services/CognitiveServices";
import { CurrentUserUtils } from "../util/CurrentUserUtils";
import { InteractionUtils } from "../util/InteractionUtils";
import { SnappingManager } from "../util/SnappingManager";
+import { Transform } from "../util/Transform";
+import { UndoManager } from "../util/UndoManager";
import { ContextMenu } from "./ContextMenu";
import { ViewBoxBaseComponent } from "./DocComponent";
import { Colors } from "./global/globalEnums";
-import { InkControlPtHandles } from "./InkControlPtHandles";
+import { InkControlPtHandles, InkEndPtHandles } from "./InkControlPtHandles";
import "./InkStroke.scss";
import { InkStrokeProperties } from "./InkStrokeProperties";
import { InkTangentHandles } from "./InkTangentHandles";
import { FieldView, FieldViewProps } from "./nodes/FieldView";
-import Color = require("color");
-import { Transform } from "../util/Transform";
import { FormattedTextBox } from "./nodes/formattedText/FormattedTextBox";
+import Color = require("color");
type InkDocument = makeInterface<[typeof documentSchema]>;
const InkDocument = makeInterface(documentSchema);
@@ -89,21 +90,49 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
/**
* Handles the movement of the entire ink object when the user clicks and drags.
*/
+ @action
onPointerDown = (e: React.PointerEvent) => {
+ const ptFromScreen = this.ptFromScreen;
this._handledClick = false;
- if (this.props.isSelected(true)) {
- setupMoveUpEvents(this, e, returnFalse, emptyFunction,
+ if (InkStrokeProperties.Instance && ptFromScreen) {
+ const inkView = this.props.docViewPath().lastElement();
+ const { inkData, inkScaleX, inkScaleY, inkStrokeWidth, inkTop, inkLeft } = this.inkScaledData();
+ const screenPts = inkData.map(point => this.props.ScreenToLocalTransform().inverse().transformPoint(
+ (point.X - inkLeft - inkStrokeWidth / 2) * inkScaleX + inkStrokeWidth / 2,
+ (point.Y - inkTop - inkStrokeWidth / 2) * inkScaleY + inkStrokeWidth / 2)).map(p => ({ X: p[0], Y: p[1] }));
+ const { nearestSeg } = InkStrokeProperties.nearestPtToStroke(screenPts, { X: e.clientX, Y: e.clientY });
+ const controlIndex = nearestSeg;
+ const wasSelected = InkStrokeProperties.Instance?._currentPoint === controlIndex;
+ var controlUndo: UndoManager.Batch | undefined;
+ const isEditing = this._properties?._controlButton && this.props.isSelected();
+ setupMoveUpEvents(this, e,
+ !isEditing ? returnFalse : action((e: PointerEvent, down: number[], delta: number[]) => {
+ if (!controlUndo) controlUndo = UndoManager.StartBatch("drag ink ctrl pt");
+ const inkMoveEnd = ptFromScreen({ X: delta[0], Y: delta[1] });
+ const inkMoveStart = ptFromScreen({ X: 0, Y: 0 });
+ InkStrokeProperties.Instance?.moveControlPtHandle(inkView, inkMoveEnd.X - inkMoveStart.X, inkMoveEnd.Y - inkMoveStart.Y, controlIndex);
+ InkStrokeProperties.Instance?.moveControlPtHandle(inkView, inkMoveEnd.X - inkMoveStart.X, inkMoveEnd.Y - inkMoveStart.Y, controlIndex + 3);
+ return false;
+ }),
+ !isEditing ? returnFalse : action(() => {
+ if (controlUndo) {
+ InkStrokeProperties.Instance?.snapControl(inkView, controlIndex);
+ InkStrokeProperties.Instance?.snapControl(inkView, controlIndex + 3);
+ }
+ controlUndo?.end();
+ controlUndo = undefined;
+ UndoManager.FilterBatches(["data", "x", "y", "width", "height"]);
+ }),
action((e: PointerEvent, doubleTap: boolean | undefined) => {
doubleTap = doubleTap || this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick;
if (doubleTap && this._properties) {
this._properties._controlButton = true;
InkStrokeProperties.Instance && (InkStrokeProperties.Instance._currentPoint = -1);
this._handledClick = true; // mark the double-click pseudo pointerevent so we can block the real mouse event from propagating to DocumentView
- } else if (this._properties?._controlButton) {
+ } else if (isEditing) {
this._nearestT && this._nearestSeg !== undefined && InkStrokeProperties.Instance?.addPoints(this.props.docViewPath().lastElement(), this._nearestT, this._nearestSeg, this.inkScaledData().inkData.slice());
}
- }), this._properties?._controlButton, this._properties?._controlButton
- );
+ }), isEditing, isEditing, () => wasSelected && InkStrokeProperties.Instance && (InkStrokeProperties.Instance._currentPoint = -1));
}
}
@@ -185,7 +214,6 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
this._nearestScrPt = nearestPt;
}
-
nearestScreenPt = () => this._nearestScrPt;
componentUI = (boundsLeft: number, boundsTop: number) => {
const inkDoc = this.props.Document;
@@ -200,11 +228,17 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
const startMarker = StrCast(this.layoutDoc.strokeStartMarker);
const endMarker = StrCast(this.layoutDoc.strokeEndMarker);
- return SnappingManager.GetIsDragging() ? (null) : <div className="inkstroke-UI" style={{
- clip: `rect(${boundsTop}px, 10000px, 10000px, ${boundsLeft}px)`
- }} >
- {!this._properties?._controlButton ? (null) :
- <>
+ return SnappingManager.GetIsDragging() ? (null) :
+ !this._properties?._controlButton ?
+ (!this.props.isSelected() || InkingStroke.IsClosed(inkData) ? (null) :
+ <div className="inkstroke-UI" style={{ clip: `rect(${boundsTop}px, 10000px, 10000px, ${boundsLeft}px)` }}>
+ <InkEndPtHandles
+ inkView={this.props.docViewPath().lastElement()}
+ inkDoc={inkDoc}
+ startPt={this.ptToScreen(inkData[0])}
+ endPt={this.ptToScreen(inkData.lastElement())}
+ screenSpaceLineWidth={screenSpaceCenterlineStrokeWidth} /></div>) :
+ <div className="inkstroke-UI" style={{ clip: `rect(${boundsTop}px, 10000px, 10000px, ${boundsLeft}px)` }}>
{InteractionUtils.CreatePolyline(screenPts, 0, 0, Colors.MEDIUM_BLUE, screenInkWidth[0], screenSpaceCenterlineStrokeWidth,
StrCast(inkDoc.strokeLineJoin), StrCast(this.layoutDoc.strokeLineCap), StrCast(inkDoc.strokeBezier),
"none", startMarker, endMarker, StrCast(inkDoc.strokeDash), 1, 1, "", "none", 1.0, false)}
@@ -222,8 +256,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
screenCtrlPoints={screenHdlPts}
screenSpaceLineWidth={screenSpaceCenterlineStrokeWidth}
ScreenToLocalTransform={this.props.ScreenToLocalTransform} />
- </>}
- </div>;
+ </div>;
}
render() {
@@ -246,11 +279,12 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
StrCast(this.layoutDoc.strokeOutlineColor, !closed && fillColor && fillColor !== "transparent" ? StrCast(this.layoutDoc.color, "transparent") : "transparent") :
["transparent", "rgb(68, 118, 247)", "rgb(68, 118, 247)", "yellow", "magenta", "cyan", "orange"][highlightIndex];
// Invisible polygonal line that enables the ink to be selected by the user.
- const clickableLine = InteractionUtils.CreatePolyline(inkData, inkLeft, inkTop, highlightColor,
- inkStrokeWidth, inkStrokeWidth + (highlightIndex && closed && fillColor && (new Color(fillColor)).alpha() < 1 ? 6 : 15),
+ const clickableLine = (downHdlr?: (e: React.PointerEvent) => void) => InteractionUtils.CreatePolyline(inkData, inkLeft, inkTop, highlightColor,
+ inkStrokeWidth, inkStrokeWidth + (CurrentUserUtils.SelectedTool === InkTool.Eraser ? 0 : highlightIndex && closed && fillColor && (new Color(fillColor)).alpha() < 1 ? 6 : 15),
StrCast(this.layoutDoc.strokeLineJoin), StrCast(this.layoutDoc.strokeLineCap),
StrCast(this.layoutDoc.strokeBezier), !closed ? "none" : fillColor === "transparent" ? "none" : fillColor, startMarker, endMarker,
- undefined, inkScaleX, inkScaleY, "", this.props.pointerEvents ?? (this.props.layerProvider?.(this.props.Document) === false ? "none" : "visiblepainted"), 0.0, false);
+ undefined, inkScaleX, inkScaleY, "", this.props.pointerEvents ?? (this.props.layerProvider?.(this.props.Document) === false ? "none" : "visiblepainted"), 0.0,
+ false, downHdlr);
// Set of points rendered upon the ink that can be added if a user clicks on one.
return <div className="inkStroke-wrapper" style={{ display: "flex", alignItems: "center", height: "100%" }}>
@@ -266,7 +300,6 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
}}
onPointerLeave={action(e => this._nearestScrPt = undefined)}
onPointerMove={this.props.isSelected() ? this.onPointerMove : undefined}
- onPointerDown={this.onPointerDown}
onClick={e => this._handledClick && e.stopPropagation()}
onContextMenu={() => {
const cm = ContextMenu.Instance;
@@ -275,7 +308,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
cm?.addItem({ description: "Edit Points", event: action(() => { if (this._properties) { this._properties._controlButton = !this._properties._controlButton; } }), icon: "paint-brush" });
}}
>
- {clickableLine}
+ {clickableLine(this.onPointerDown)}
{inkLine}
</svg>
{!closed ? (null) :