aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/util/InteractionUtils.tsx2
-rw-r--r--src/client/views/InkControlPtHandles.tsx95
-rw-r--r--src/client/views/InkTangentHandles.tsx55
-rw-r--r--src/client/views/InkingStroke.tsx1
4 files changed, 82 insertions, 71 deletions
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index 633876683..d43ebd692 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -99,7 +99,7 @@ export namespace InteractionUtils {
const toScr = (p: { X: number, Y: number }) => ` ${!p ? 0 : (p.X - left - width / 2) * scalex + width / 2}, ${!p ? 0 : (p.Y - top - width / 2) * scaley + width / 2} `;
const strpts = bezier ?
- pts.reduce((acc: string, pt, i) => acc + (i % 4 !== 0 ? "" : "M" + toScr(pt) + "C" + toScr(pts[i + 1]) + toScr(pts[i + 2]) + toScr(pts[i + 3])), "") :
+ pts.reduce((acc: string, pt, i) => acc + (i % 4 !== 0 ? "" : (i === 0 ? "M" + toScr(pt) : "") + "C" + toScr(pts[i + 1]) + toScr(pts[i + 2]) + toScr(pts[i + 3])), "") :
pts.reduce((acc: string, pt) => acc + `${toScr(pt)} `, "");
const dashArray = dash && Number(dash) ? String(Number(width) * Number(dash)) : undefined;
diff --git a/src/client/views/InkControlPtHandles.tsx b/src/client/views/InkControlPtHandles.tsx
index 898c3bf26..f80aca268 100644
--- a/src/client/views/InkControlPtHandles.tsx
+++ b/src/client/views/InkControlPtHandles.tsx
@@ -10,6 +10,7 @@ import { Transform } from "../util/Transform";
import { UndoManager } from "../util/UndoManager";
import { Colors } from "./global/globalEnums";
import { InkStrokeProperties } from "./InkStrokeProperties";
+import { List } from "../../fields/List";
export interface InkControlProps {
inkDoc: Doc;
@@ -24,6 +25,8 @@ export interface InkControlProps {
export class InkControlPtHandles extends React.Component<InkControlProps> {
@observable private _overControl = -1;
+
+ @observable controlUndo: UndoManager.Batch | undefined;
/**
* Handles the movement of a selected control point when the user clicks and drags.
* @param controlIndex The index of the currently selected control point.
@@ -31,34 +34,40 @@ export class InkControlPtHandles extends React.Component<InkControlProps> {
@action
onControlDown = (e: React.PointerEvent, controlIndex: number): void => {
if (InkStrokeProperties.Instance) {
- var controlUndo: UndoManager.Batch | undefined;
const screenScale = this.props.ScreenToLocalTransform().Scale;
const order = controlIndex % 4;
const handleIndexA = ((order === 3 ? controlIndex - 1 : controlIndex - 2) + this.props.inkCtrlPoints.length) % this.props.inkCtrlPoints.length;
const handleIndexB = (order === 3 ? controlIndex + 2 : controlIndex + 1) % this.props.inkCtrlPoints.length;
const brokenIndices = Cast(this.props.inkDoc.brokenInkIndices, listSpec("number"));
setupMoveUpEvents(this, e,
- (e: PointerEvent, down: number[], delta: number[]) => {
- if (!controlUndo) controlUndo = UndoManager.StartBatch("drag ink ctrl pt");
+ action((e: PointerEvent, down: number[], delta: number[]) => {
+ if (!this.controlUndo) this.controlUndo = UndoManager.StartBatch("drag ink ctrl pt");
InkStrokeProperties.Instance?.moveControl(-delta[0] * screenScale, -delta[1] * screenScale, controlIndex);
return false;
- },
- () => {
- controlUndo?.end();
+ }),
+ action(() => {
+ this.controlUndo?.end();
+ this.controlUndo = undefined;
UndoManager.FilterBatches(["data", "x", "y", "width", "height"]);
- },
+ }),
action((e: PointerEvent, doubleTap: boolean | undefined) => {
const equivIndex = controlIndex === 0 ? this.props.inkCtrlPoints.length - 1 : controlIndex === this.props.inkCtrlPoints.length - 1 ? 0 : controlIndex;
- if (doubleTap) {
- if (brokenIndices?.includes(equivIndex)) {
- if (!controlUndo) controlUndo = UndoManager.StartBatch("make smooth");
- InkStrokeProperties.Instance?.snapHandleTangent(equivIndex, handleIndexA, handleIndexB);
- }
- if (equivIndex !== controlIndex && brokenIndices?.includes(controlIndex)) {
- if (!controlUndo) controlUndo = UndoManager.StartBatch("make smooth");
- InkStrokeProperties.Instance?.snapHandleTangent(controlIndex, handleIndexA, handleIndexB);
+ if (doubleTap || e.button === 2) {
+ if (!brokenIndices?.includes(equivIndex) && !brokenIndices?.includes(controlIndex)) {
+ if (brokenIndices) brokenIndices.push(controlIndex);
+ else this.props.inkDoc.brokenInkIndices = new List<number>([controlIndex]);
+ } else {
+ if (brokenIndices?.includes(equivIndex)) {
+ if (!this.controlUndo) this.controlUndo = UndoManager.StartBatch("make smooth");
+ InkStrokeProperties.Instance?.snapHandleTangent(equivIndex, handleIndexA, handleIndexB);
+ }
+ if (equivIndex !== controlIndex && brokenIndices?.includes(controlIndex)) {
+ if (!this.controlUndo) this.controlUndo = UndoManager.StartBatch("make smooth");
+ InkStrokeProperties.Instance?.snapHandleTangent(controlIndex, handleIndexA, handleIndexB);
+ }
}
- controlUndo?.end();
+ this.controlUndo?.end();
+ this.controlUndo = undefined;
}
}));
}
@@ -92,9 +101,6 @@ export class InkControlPtHandles extends React.Component<InkControlProps> {
}
render() {
- const formatInstance = InkStrokeProperties.Instance;
- if (!formatInstance) return (null);
-
// Accessing the current ink's data and extracting all control points.
const scrData = this.props.screenCtrlPoints;
const sreenCtrlPoints: ControlPoint[] = [];
@@ -111,41 +117,46 @@ export class InkControlPtHandles extends React.Component<InkControlProps> {
}
const screenSpaceLineWidth = this.props.screenSpaceLineWidth;
- const rectHdlSize = (i: number) => this._overControl === i ? screenSpaceLineWidth * 6 : screenSpaceLineWidth * 4;
-
const nearestScreenPt = this.props.nearestScreenPt();
+ const TagType = (broken?: boolean) => broken ? "rect" : "circle";
+ const hdl = (control: { X: number, Y: number, I: number }, scale: number, color: string) => {
+ const broken = Cast(this.props.inkDoc.brokenInkIndices, listSpec("number"))?.includes(control.I);
+ const Tag = TagType(broken) as keyof JSX.IntrinsicElements;
+ return <circle key={control.I.toString() + scale}
+ x={control.X - screenSpaceLineWidth * 3 * scale}
+ y={control.Y - screenSpaceLineWidth * 3 * scale}
+ cx={control.X}
+ cy={control.Y}
+ r={screenSpaceLineWidth * 2 * scale}
+ opacity={this.controlUndo ? 0.15 : 1}
+ height={screenSpaceLineWidth * 6 * scale}
+ width={screenSpaceLineWidth * 6 * scale}
+ strokeWidth={screenSpaceLineWidth / 2}
+ stroke={Colors.MEDIUM_BLUE}
+ fill={broken ? Colors.MEDIUM_BLUE : color}
+ onPointerDown={(e: any) => {
+ this.changeCurrPoint(control.I);
+ this.onControlDown(e, control.I);
+ }}
+ onMouseEnter={() => this.onEnterControl(control.I)}
+ onMouseLeave={this.onLeaveControl}
+ pointerEvents="all"
+ cursor="default"
+ />;
+ }
return (<svg>
{!nearestScreenPt ? (null) :
<circle key={"npt"}
cx={nearestScreenPt.X}
cy={nearestScreenPt.Y}
- r={screenSpaceLineWidth * 4}
+ r={screenSpaceLineWidth * 2}
fill={"#00007777"}
stroke={"#00007777"}
strokeWidth={0}
pointerEvents="none"
- cursor="all-scroll"
/>
}
- {sreenCtrlPoints.map((control, i) =>
- <rect key={i}
- x={control.X - rectHdlSize(i) / 2}
- y={control.Y - rectHdlSize(i) / 2}
- height={rectHdlSize(i)}
- width={rectHdlSize(i)}
- strokeWidth={screenSpaceLineWidth / 2}
- stroke={Colors.MEDIUM_BLUE}
- fill={formatInstance?._currentPoint === control.I ? Colors.MEDIUM_BLUE : Colors.WHITE}
- onPointerDown={e => {
- this.changeCurrPoint(control.I);
- this.onControlDown(e, control.I);
- }}
- onMouseEnter={e => this.onEnterControl(i)}
- onMouseLeave={this.onLeaveControl}
- pointerEvents="all"
- cursor="default"
- />
- )}
+ {sreenCtrlPoints.map(control => hdl(control, this._overControl !== control.I ? 1 : 3 / 2, Colors.WHITE))}
</svg>
);
}
diff --git a/src/client/views/InkTangentHandles.tsx b/src/client/views/InkTangentHandles.tsx
index 759e48134..4f1a406c2 100644
--- a/src/client/views/InkTangentHandles.tsx
+++ b/src/client/views/InkTangentHandles.tsx
@@ -68,62 +68,61 @@ export class InkTangentHandles extends React.Component<InkHandlesProps> {
// Accessing the current ink's data and extracting all handle points and handle lines.
const data = this.props.screenCtrlPoints;
- const handlePoints: HandlePoint[] = [];
- const handleLines: HandleLine[] = [];
+ const tangentHandles: HandlePoint[] = [];
+ const tangentLines: HandleLine[] = [];
const closed = data.lastElement().X === data[0].X && data.lastElement().Y === data[0].Y;
if (data.length >= 4) {
for (let i = 0; i <= data.length - 4; i += 4) {
- handlePoints.push({ ...data[i + 1], I: i + 1, dot1: i, dot2: i === 0 ? (closed ? data.length - 1 : i) : i - 1 });
- handlePoints.push({ ...data[i + 2], I: i + 2, dot1: i + 3, dot2: i === data.length ? (closed ? (i + 4) % data.length : i + 3) : i + 4 });
+ tangentHandles.push({ ...data[i + 1], I: i + 1, dot1: i, dot2: i === 0 ? (closed ? data.length - 1 : i) : i - 1 });
+ tangentHandles.push({ ...data[i + 2], I: i + 2, dot1: i + 3, dot2: i === data.length ? (closed ? (i + 4) % data.length : i + 3) : i + 4 });
}
// Adding first and last (single) handle lines.
if (closed) {
- handleLines.push({ X1: data[data.length - 2].X, Y1: data[data.length - 2].Y, X2: data[0].X, Y2: data[0].Y, X3: data[1].X, Y3: data[1].Y, dot1: 0, dot2: data.length - 1 });
+ tangentLines.push({ X1: data[data.length - 2].X, Y1: data[data.length - 2].Y, X2: data[0].X, Y2: data[0].Y, X3: data[1].X, Y3: data[1].Y, dot1: 0, dot2: data.length - 1 });
}
else {
- handleLines.push({ X1: data[0].X, Y1: data[0].Y, X2: data[0].X, Y2: data[0].Y, X3: data[1].X, Y3: data[1].Y, dot1: 0, dot2: 0 });
- handleLines.push({ X1: data[data.length - 2].X, Y1: data[data.length - 2].Y, X2: data[data.length - 1].X, Y2: data[data.length - 1].Y, X3: data[data.length - 1].X, Y3: data[data.length - 1].Y, dot1: data.length - 1, dot2: data.length - 1 });
+ tangentLines.push({ X1: data[0].X, Y1: data[0].Y, X2: data[0].X, Y2: data[0].Y, X3: data[1].X, Y3: data[1].Y, dot1: 0, dot2: 0 });
+ tangentLines.push({ X1: data[data.length - 2].X, Y1: data[data.length - 2].Y, X2: data[data.length - 1].X, Y2: data[data.length - 1].Y, X3: data[data.length - 1].X, Y3: data[data.length - 1].Y, dot1: data.length - 1, dot2: data.length - 1 });
}
for (let i = 2; i < data.length - 4; i += 4) {
- handleLines.push({ X1: data[i].X, Y1: data[i].Y, X2: data[i + 1].X, Y2: data[i + 1].Y, X3: data[i + 3].X, Y3: data[i + 3].Y, dot1: i + 1, dot2: i + 2 });
+ tangentLines.push({ X1: data[i].X, Y1: data[i].Y, X2: data[i + 1].X, Y2: data[i + 1].Y, X3: data[i + 3].X, Y3: data[i + 3].Y, dot1: i + 1, dot2: i + 2 });
}
}
const screenSpaceLineWidth = this.props.screenSpaceLineWidth;
return (
<>
- {handlePoints.map((pts, i) =>
+ {tangentHandles.map((pts, i) =>
<svg height="10" width="10" key={`hdl${i}`}>
<circle
cx={pts.X}
cy={pts.Y}
r={screenSpaceLineWidth * 2}
- strokeWidth={0}
- fill={Colors.MEDIUM_BLUE}
+ fill={"transparent"}
+ strokeWidth={1}
+ stroke={Colors.MEDIUM_BLUE}
+ strokeDasharray={"1 1"}
onPointerDown={e => this.onHandleDown(e, pts.I)}
pointerEvents="all"
cursor="default"
display={(pts.dot1 === formatInstance._currentPoint || pts.dot2 === formatInstance._currentPoint) ? "inherit" : "none"} />
</svg>)}
- {handleLines.map((pts, i) =>
- <svg height="100" width="100" key={`line${i}`}>
- <line
- x1={pts.X1}
- y1={pts.Y1}
- x2={pts.X2}
- y2={pts.Y2}
- stroke={Colors.MEDIUM_BLUE}
- strokeWidth={screenSpaceLineWidth}
- display={(pts.dot1 === formatInstance._currentPoint || pts.dot2 === formatInstance._currentPoint) ? "inherit" : "none"} />
+ {tangentLines.map((pts, i) => {
+ const tangentLine = (x1: number, y1: number, x2: number, y2: number) =>
<line
- x1={pts.X2}
- y1={pts.Y2}
- x2={pts.X3}
- y2={pts.Y3}
+ x1={x1}
+ y1={y1}
+ x2={x2}
+ y2={y2}
stroke={Colors.MEDIUM_BLUE}
- strokeWidth={screenSpaceLineWidth}
- display={(pts.dot1 === formatInstance._currentPoint || pts.dot2 === formatInstance._currentPoint) ? "inherit" : "none"} />
- </svg>)}
+ strokeDasharray={"1 1"}
+ strokeWidth={1}
+ display={(pts.dot1 === formatInstance._currentPoint || pts.dot2 === formatInstance._currentPoint) ? "inherit" : "none"} />;
+ return <svg height="100" width="100" key={`line${i}`}>
+ {tangentLine(pts.X1, pts.Y1, pts.X2, pts.Y2)}
+ {tangentLine(pts.X2, pts.Y2, pts.X3, pts.Y3)}
+ </svg>;
+ })}
</>
);
}
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 867677005..4cd7f7698 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -210,6 +210,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
transform: this.props.Document.isInkMask ? `translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined,
mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? "multiply" : "unset",
overflow: "visible",
+ cursor: this.props.isSelected() ? "default" : undefined
}}
onPointerLeave={action(e => this._nearestScrPt = undefined)}
onPointerMove={this.props.isSelected() ? this.onPointerMove : undefined}