diff options
| author | bobzel <zzzman@gmail.com> | 2024-05-19 01:01:02 -0400 |
|---|---|---|
| committer | bobzel <zzzman@gmail.com> | 2024-05-19 01:01:02 -0400 |
| commit | 6e72f969029c22fe797397a6437836a0482260b6 (patch) | |
| tree | e8ccde75702e557b2226c9069263e1bc3bd21a4b /src/client/views/InkControlPtHandles.tsx | |
| parent | 5ff0bef5d3c4825aa7210a26c98aae3b24f4a835 (diff) | |
| parent | 13dc6de0e0099f699ad0d2bb54401e6a0aa25018 (diff) | |
Merge branch 'restoringEslint' into alyssa-starter
Diffstat (limited to 'src/client/views/InkControlPtHandles.tsx')
| -rw-r--r-- | src/client/views/InkControlPtHandles.tsx | 124 |
1 files changed, 71 insertions, 53 deletions
diff --git a/src/client/views/InkControlPtHandles.tsx b/src/client/views/InkControlPtHandles.tsx index 31b13d2c8..a49923ffb 100644 --- a/src/client/views/InkControlPtHandles.tsx +++ b/src/client/views/InkControlPtHandles.tsx @@ -1,18 +1,20 @@ import * as React from 'react'; -import { action, observable } from 'mobx'; +import { action, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import { Doc } from '../../fields/Doc'; -import { ControlPoint, InkData, PointData } from '../../fields/InkField'; +import { ControlPoint, InkData } from '../../fields/InkField'; import { List } from '../../fields/List'; import { listSpec } from '../../fields/Schema'; import { Cast } from '../../fields/Types'; -import { returnFalse, setupMoveUpEvents } from '../../Utils'; -import { SelectionManager } from '../util/SelectionManager'; +import { returnFalse, setupMoveUpEvents } from '../../ClientUtils'; import { UndoManager } from '../util/UndoManager'; import { Colors } from './global/globalEnums'; import { InkingStroke } from './InkingStroke'; import { InkStrokeProperties } from './InkStrokeProperties'; import { SnappingManager } from '../util/SnappingManager'; +import { ObservableReactComponent } from './ObservableReactComponent'; +import { PointData } from '../../pen-gestures/GestureTypes'; +import { DocumentView } from './nodes/DocumentView'; export interface InkControlProps { inkDoc: Doc; @@ -24,10 +26,15 @@ export interface InkControlProps { } @observer -export class InkControlPtHandles extends React.Component<InkControlProps> { +export class InkControlPtHandles extends ObservableReactComponent<InkControlProps> { @observable private _overControl = -1; get docView() { - return this.props.inkView.DocumentView?.(); + return this._props.inkView.DocumentView?.(); + } + + constructor(props: InkControlProps) { + super(props); + makeObservable(this); } componentDidMount() { @@ -42,51 +49,51 @@ export class InkControlPtHandles extends React.Component<InkControlProps> { */ @action onControlDown = (e: React.PointerEvent, controlIndex: number): void => { - const ptFromScreen = this.props.inkView.ptFromScreen; + const { ptFromScreen } = this._props.inkView; if (ptFromScreen) { 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')); + 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')); const wasSelected = InkStrokeProperties.Instance._currentPoint === controlIndex; if (!wasSelected) InkStrokeProperties.Instance._currentPoint = -1; - const origInk = this.props.inkCtrlPoints.slice(); + const origInk = this._props.inkCtrlPoints.slice(); setupMoveUpEvents( this, e, - action((e: PointerEvent, down: number[], delta: number[]) => { - if (!this.props.inkView.controlUndo) this.props.inkView.controlUndo = UndoManager.StartBatch('drag ink ctrl pt'); + action((moveEv: PointerEvent, down: number[], delta: number[]) => { + if (!this._props.inkView.controlUndo) this._props.inkView.controlUndo = UndoManager.StartBatch('drag ink ctrl pt'); const inkMoveEnd = ptFromScreen({ X: delta[0], Y: delta[1] }); const inkMoveStart = ptFromScreen({ X: 0, Y: 0 }); this.docView && InkStrokeProperties.Instance.moveControlPtHandle(this.docView, inkMoveEnd.X - inkMoveStart.X, inkMoveEnd.Y - inkMoveStart.Y, controlIndex, origInk); return false; }), action(() => { - if (this.props.inkView.controlUndo && this.docView) { + if (this._props.inkView.controlUndo && this.docView) { InkStrokeProperties.Instance.snapControl(this.docView, controlIndex); } - this.props.inkView.controlUndo?.end(); - this.props.inkView.controlUndo = undefined; + this._props.inkView.controlUndo?.end(); + this._props.inkView.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 || e.button === 2) { + action((moveEv: PointerEvent, doubleTap: boolean | undefined) => { + const equivIndex = controlIndex === 0 ? this._props.inkCtrlPoints.length - 1 : controlIndex === this._props.inkCtrlPoints.length - 1 ? 0 : controlIndex; + if (doubleTap || moveEv.button === 2) { if (!brokenIndices?.includes(equivIndex) && !brokenIndices?.includes(controlIndex)) { if (brokenIndices) brokenIndices.push(controlIndex); - else this.props.inkDoc.brokenInkIndices = new List<number>([controlIndex]); + else this._props.inkDoc.brokenInkIndices = new List<number>([controlIndex]); } else { if (brokenIndices?.includes(equivIndex)) { - if (!this.props.inkView.controlUndo) this.props.inkView.controlUndo = UndoManager.StartBatch('make smooth'); + if (!this._props.inkView.controlUndo) this._props.inkView.controlUndo = UndoManager.StartBatch('make smooth'); this.docView && InkStrokeProperties.Instance.snapHandleTangent(this.docView, equivIndex, handleIndexA, handleIndexB); } if (equivIndex !== controlIndex && brokenIndices?.includes(controlIndex)) { - if (!this.props.inkView.controlUndo) this.props.inkView.controlUndo = UndoManager.StartBatch('make smooth'); + if (!this._props.inkView.controlUndo) this._props.inkView.controlUndo = UndoManager.StartBatch('make smooth'); this.docView && InkStrokeProperties.Instance.snapHandleTangent(this.docView, controlIndex, handleIndexA, handleIndexB); } } - this.props.inkView.controlUndo?.end(); - this.props.inkView.controlUndo = undefined; + this._props.inkView.controlUndo?.end(); + this._props.inkView.controlUndo = undefined; } this.changeCurrPoint(controlIndex); }), @@ -122,18 +129,20 @@ export class InkControlPtHandles extends React.Component<InkControlProps> { * Changes the current selected control point. */ @action - changeCurrPoint = (i: number) => (InkStrokeProperties.Instance._currentPoint = i); + changeCurrPoint = (i: number) => { + InkStrokeProperties.Instance._currentPoint = i; + }; render() { // Accessing the current ink's data and extracting all control points. - const scrData = this.props.screenCtrlPoints; + const scrData = this._props.screenCtrlPoints; const sreenCtrlPoints: ControlPoint[] = []; for (let i = 0; i <= scrData.length - 4; i += 4) { sreenCtrlPoints.push({ ...scrData[i], I: i }); sreenCtrlPoints.push({ ...scrData[i + 3], I: i + 3 }); } - const inkData = this.props.inkCtrlPoints; + const inkData = this._props.inkCtrlPoints; const inkCtrlPts: ControlPoint[] = []; for (let i = 0; i <= inkData.length - 4; i += 4) { inkCtrlPts.push({ ...inkData[i], I: i }); @@ -141,23 +150,23 @@ export class InkControlPtHandles extends React.Component<InkControlProps> { } const closed = InkingStroke.IsClosed(inkData); - const nearestScreenPt = this.props.nearestScreenPt(); + 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 broken = Cast(this._props.inkDoc.brokenInkIndices, listSpec('number'))?.includes(control.I); const Tag = TagType((control.I === 0 || control.I === inkData.length - 1) && !closed) as keyof JSX.IntrinsicElements; return ( <Tag key={control.I.toString() + scale} - x={control.X - this.props.screenSpaceLineWidth * 2 * scale} - y={control.Y - this.props.screenSpaceLineWidth * 2 * scale} + x={control.X - this._props.screenSpaceLineWidth * 2 * scale} + y={control.Y - this._props.screenSpaceLineWidth * 2 * scale} cx={control.X} cy={control.Y} - r={this.props.screenSpaceLineWidth * 2 * scale} - opacity={this.props.inkView.controlUndo ? 0.35 : 1} - height={this.props.screenSpaceLineWidth * 4 * scale} - width={this.props.screenSpaceLineWidth * 4 * scale} - strokeWidth={this.props.screenSpaceLineWidth / 2} + r={this._props.screenSpaceLineWidth * 2 * scale} + opacity={this._props.inkView.controlUndo ? 0.35 : 1} + height={this._props.screenSpaceLineWidth * 4 * scale} + width={this._props.screenSpaceLineWidth * 4 * scale} + strokeWidth={this._props.screenSpaceLineWidth / 2} stroke={Colors.MEDIUM_BLUE} fill={broken ? Colors.MEDIUM_BLUE : color} onPointerDown={(e: React.PointerEvent) => this.onControlDown(e, control.I)} @@ -170,7 +179,7 @@ export class InkControlPtHandles extends React.Component<InkControlProps> { }; return ( <svg> - {!nearestScreenPt ? null : <circle key={'npt'} cx={nearestScreenPt.X} cy={nearestScreenPt.Y} r={this.props.screenSpaceLineWidth * 2} fill={'#00007777'} stroke={'#00007777'} strokeWidth={0} pointerEvents="none" />} + {!nearestScreenPt ? null : <circle key="npt" cx={nearestScreenPt.X} cy={nearestScreenPt.Y} r={this._props.screenSpaceLineWidth * 2} fill="#00007777" stroke="#00007777" strokeWidth={0} pointerEvents="none" />} {sreenCtrlPoints.map(control => hdl(control, this._overControl !== control.I ? 1 : 3 / 2, Colors.WHITE))} </svg> ); @@ -185,10 +194,15 @@ export interface InkEndProps { endPt: () => PointData; } @observer -export class InkEndPtHandles extends React.Component<InkEndProps> { +export class InkEndPtHandles extends ObservableReactComponent<InkEndProps> { @observable _overStart: boolean = false; @observable _overEnd: boolean = false; + constructor(props: InkEndProps) { + super(props); + makeObservable(this); + } + _throttle = 0; // need to throttle dragging since the position may change when the control points change. this allows the stroke to settle so that we don't get increasingly bad jitter @action dragRotate = (e: React.PointerEvent, pt1: () => { X: number; Y: number }, pt2: () => { X: number; Y: number }) => { @@ -196,28 +210,28 @@ export class InkEndPtHandles extends React.Component<InkEndProps> { setupMoveUpEvents( this, e, - action(e => { + action(moveEv => { if (this._throttle++ % 2 !== 0) return false; - if (!this.props.inkView.controlUndo) this.props.inkView.controlUndo = UndoManager.StartBatch('stretch ink'); + if (!this._props.inkView.controlUndo) this._props.inkView.controlUndo = UndoManager.StartBatch('stretch ink'); // compute stretch factor by finding scaling along axis between start and end points const p1 = pt1(); const p2 = pt2(); const v1 = { X: p1.X - p2.X, Y: p1.Y - p2.Y }; - const v2 = { X: e.clientX - p2.X, Y: e.clientY - p2.Y }; + const v2 = { X: moveEv.clientX - p2.X, Y: moveEv.clientY - p2.Y }; const v1len = Math.sqrt(v1.X * v1.X + v1.Y * v1.Y); const v2len = Math.sqrt(v2.X * v2.X + v2.Y * v2.Y); const scaling = v2len / v1len; const v1n = { X: v1.X / v1len, Y: v1.Y / v1len }; const v2n = { X: v2.X / v2len, Y: v2.Y / v2len }; const angle = Math.acos(v1n.X * v2n.X + v1n.Y * v2n.Y) * Math.sign(v1.X * v2.Y - v2.X * v1.Y); - InkStrokeProperties.Instance.stretchInk(SelectionManager.Views, scaling, p2, v1n, e.shiftKey); - InkStrokeProperties.Instance.rotateInk(SelectionManager.Views, angle, pt2()); // bcz: call pt2() func here because pt2 will have changed from previous stretchInk call + InkStrokeProperties.Instance.stretchInk(DocumentView.Selected(), scaling, p2, v1n, moveEv.shiftKey); + InkStrokeProperties.Instance.rotateInk(DocumentView.Selected(), angle, pt2()); // bcz: call pt2() func here because pt2 will have changed from previous stretchInk call return false; }), action(() => { SnappingManager.SetIsDragging(false); - this.props.inkView.controlUndo?.end(); - this.props.inkView.controlUndo = undefined; + this._props.inkView.controlUndo?.end(); + this._props.inkView.controlUndo = undefined; UndoManager.FilterBatches(['stroke', 'x', 'y', 'width', 'height']); }), returnFalse @@ -228,22 +242,26 @@ export class InkEndPtHandles extends React.Component<InkEndProps> { const hdl = (key: string, pt: PointData, dragFunc: (e: React.PointerEvent) => void) => ( <circle key={key} - cx={pt.X} - cy={pt.Y} - r={this.props.screenSpaceLineWidth * 2} + cx={pt?.X} + cy={pt?.Y} + r={this._props.screenSpaceLineWidth * 2} fill={this._overStart ? '#aaaaaa' : '#99999977'} - stroke={'#00007777'} + stroke="#00007777" strokeWidth={0} - onPointerLeave={action(() => (this._overStart = false))} - onPointerEnter={action(() => (this._overStart = true))} + onPointerLeave={action(() => { + this._overStart = false; + })} + onPointerEnter={action(() => { + this._overStart = true; + })} onPointerDown={dragFunc} pointerEvents="all" /> ); return ( <svg> - {hdl('start', this.props.startPt(), e => this.dragRotate(e, this.props.startPt, this.props.endPt))} - {hdl('end', this.props.endPt(), e => this.dragRotate(e, this.props.endPt, this.props.startPt))} + {hdl('start', this._props.startPt(), e => this.dragRotate(e, this._props.startPt, this._props.endPt))} + {hdl('end', this._props.endPt(), e => this.dragRotate(e, this._props.endPt, this._props.startPt))} </svg> ); } |
