diff options
-rw-r--r-- | src/client/views/DocumentDecorations.tsx | 2 | ||||
-rw-r--r-- | src/client/views/InkControl.tsx | 103 | ||||
-rw-r--r-- | src/client/views/InkStrokeProperties.ts | 141 | ||||
-rw-r--r-- | src/client/views/InkingStroke.tsx | 35 |
4 files changed, 179 insertions, 102 deletions
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index bf939d57c..d1682c09d 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -201,7 +201,7 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b (e: PointerEvent, down: number[], delta: number[]) => { const movement = { X: delta[0], Y: e.clientY - down[1] }; const angle = Math.max(1, Math.abs(movement.Y / 10)); - InkStrokeProperties.Instance?.rotate(2 * movement.X / angle * (Math.PI / 180)); + InkStrokeProperties.Instance?.rotateInk(2 * movement.X / angle * (Math.PI / 180)); return false; }, () => this._rotateUndo?.end(), diff --git a/src/client/views/InkControl.tsx b/src/client/views/InkControl.tsx new file mode 100644 index 000000000..ae0fc0b22 --- /dev/null +++ b/src/client/views/InkControl.tsx @@ -0,0 +1,103 @@ +import React = require("react"); +import { observable, action } from "mobx"; +import { observer } from "mobx-react"; +import { InkStrokeProperties } from "./InkStrokeProperties"; +import { setupMoveUpEvents, emptyFunction } from "../../Utils"; +import { UndoManager } from "../util/UndoManager"; +import { ControlPoint } from "../../fields/InkField"; +import { Transform } from "../util/Transform"; + +export interface InkControlProps { + control: ControlPoint; + left: number; + top: number; + scaleX: number; + scaleY: number; + strokeWidth: number; + ScreenToLocalTransform: () => Transform; +} + +@observer +export class InkControl extends React.Component<InkControlProps> { + @observable private _overControl = false; + + @action + onPointerEnter = () => { + this._overControl = true; + } + + @action + onPointerLeave = () => { + this._overControl = false; + } + + /** + * Handles the movement of a selected control point when the user clicks and drags. + * @param controlNum The index of the currently selected control point. + */ + @action + onControlDown = (e: React.PointerEvent, controlNum: number): void => { + if (InkStrokeProperties.Instance) { + InkStrokeProperties.Instance.moveControl(0, 0, 1); + const controlUndo = UndoManager.StartBatch("DocDecs set radius"); + const screenScale = this.props.ScreenToLocalTransform().Scale; + setupMoveUpEvents(this, e, + (e: PointerEvent, down: number[], delta: number[]) => { + InkStrokeProperties.Instance?.moveControl(-delta[0] * screenScale, -delta[1] * screenScale, controlNum); + return false; + }, + () => controlUndo?.end(), emptyFunction); + } + } + + /** + * Deletes the currently selected points. + * @param e Keyboard Event. + */ + @action + onDelete = (e: KeyboardEvent) => { + if (["-", "Backspace", "Delete"].includes(e.key)) { + if (InkStrokeProperties.Instance?.deletePoints()) e.stopPropagation(); + } + } + + /** + * Changes the current selected control point. + * @param i The number of the point to be selected. + */ + @action + changeCurrPoint = (i: number) => { + if (InkStrokeProperties.Instance) { + InkStrokeProperties.Instance._currPoint = i; + document.addEventListener("keydown", this.onDelete, true); + } + } + + + render() { + const control = this.props.control; + const left = this.props.left; + const top = this.props.top; + const scaleX = this.props.scaleX; + const scaleY = this.props.scaleY; + const strokeWidth = this.props.strokeWidth; + + return ( + <svg height="10" width="10" key={`ctrl${control.I}`}> + <rect + x={(control.X - left) * scaleX} + y={(control.Y - top) * scaleY} + height={this._overControl ? strokeWidth * 1.5 : strokeWidth} + width={this._overControl ? strokeWidth * 1.5 : strokeWidth} + strokeWidth={strokeWidth / 6} stroke="#1F85DE" + fill={InkStrokeProperties.Instance?._currPoint === control.I ? "#1F85DE" : "white"} + onPointerDown={(e) => { this.changeCurrPoint(control.I); this.onControlDown(e, control.I); }} + onMouseEnter={this.onPointerEnter} + onMouseLeave={this.onPointerLeave} + pointerEvents="all" + cursor="default" + /> + </svg> + ) + } +}
\ No newline at end of file diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index 5c5395984..720a89334 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -32,51 +32,52 @@ export class InkStrokeProperties { const inks = SelectionManager.Views().filter(i => Document(i.rootDoc).type === DocumentType.INK); return inks.length ? inks : undefined; } - @computed get unFilled() { return this.selectedInk?.reduce((p, i) => p && !i.rootDoc.fillColor ? true : false, true) || false; } - @computed get unStrokd() { return this.selectedInk?.reduce((p, i) => p && !i.rootDoc.color ? true : false, true) || false; } - @computed get solidFil() { return this.selectedInk?.reduce((p, i) => p && i.rootDoc.fillColor ? true : false, true) || false; } - @computed get solidStk() { return this.selectedInk?.reduce((p, i) => p && i.rootDoc.color && (!i.rootDoc.strokeDash || i.rootDoc.strokeDash === "0") ? true : false, true) || false; } - @computed get dashdStk() { return !this.unStrokd && this.getField("strokeDash") || ""; } - @computed get colorFil() { const ccol = this.getField("fillColor") || ""; ccol && (this._lastFill = ccol); return ccol; } - @computed get colorStk() { const ccol = this.getField("color") || ""; ccol && (this._lastLine = ccol); return ccol; } - @computed get widthStk() { return this.getField("strokeWidth") || "1"; } - @computed get markHead() { return this.getField("strokeStartMarker") || ""; } - @computed get markTail() { return this.getField("strokeEndMarker") || ""; } - @computed get shapeHgt() { return this.getField("_height"); } - @computed get shapeWid() { return this.getField("_width"); } - @computed get shapeXps() { return this.getField("x"); } - @computed get shapeYps() { return this.getField("y"); } - @computed get shapeRot() { return this.getField("rotation"); } - set unFilled(value) { this.colorFil = value ? "" : this._lastFill; } - set solidFil(value) { this.unFilled = !value; } - set colorFil(value) { value && (this._lastFill = value); this.selectedInk?.forEach(i => i.rootDoc.fillColor = value ? value : undefined); } - set colorStk(value) { value && (this._lastLine = value); this.selectedInk?.forEach(i => i.rootDoc.color = value ? value : undefined); } - set markHead(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeStartMarker = value); } - set markTail(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeEndMarker = value); } - set unStrokd(value) { this.colorStk = value ? "" : this._lastLine; } - set solidStk(value) { this.dashdStk = ""; this.unStrokd = !value; } - set dashdStk(value) { - value && (this._lastDash = value) && (this.unStrokd = false); - this.selectedInk?.forEach(i => i.rootDoc.strokeDash = value ? this._lastDash : undefined); - } - set shapeXps(value) { this.selectedInk?.forEach(i => i.rootDoc.x = Number(value)); } - set shapeYps(value) { this.selectedInk?.forEach(i => i.rootDoc.y = Number(value)); } - set shapeRot(value) { this.selectedInk?.forEach(i => i.rootDoc.rotation = Number(value)); } - set widthStk(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeWidth = Number(value)); } - set shapeWid(value) { - this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => { - const oldWidth = NumCast(i.rootDoc._width); - i.rootDoc._width = Number(value); - this._lock && (i.rootDoc._height = (i.rootDoc._width * NumCast(i.rootDoc._height)) / oldWidth); - }); - } - set shapeHgt(value) { - this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => { - const oldHeight = NumCast(i.rootDoc._height); - i.rootDoc._height = Number(value); - this._lock && (i.rootDoc._width = (i.rootDoc._height * NumCast(i.rootDoc._width)) / oldHeight); - }); - } + + // @computed get unFilled() { return this.selectedInk?.reduce((p, i) => p && !i.rootDoc.fillColor ? true : false, true) || false; } + // @computed get unStrokd() { return this.selectedInk?.reduce((p, i) => p && !i.rootDoc.color ? true : false, true) || false; } + // @computed get solidFil() { return this.selectedInk?.reduce((p, i) => p && i.rootDoc.fillColor ? true : false, true) || false; } + // @computed get solidStk() { return this.selectedInk?.reduce((p, i) => p && i.rootDoc.color && (!i.rootDoc.strokeDash || i.rootDoc.strokeDash === "0") ? true : false, true) || false; } + // @computed get dashdStk() { return !this.unStrokd && this.getField("strokeDash") || ""; } + // @computed get colorFil() { const ccol = this.getField("fillColor") || ""; ccol && (this._lastFill = ccol); return ccol; } + // @computed get colorStk() { const ccol = this.getField("color") || ""; ccol && (this._lastLine = ccol); return ccol; } + // @computed get widthStk() { return this.getField("strokeWidth") || "1"; } + // @computed get markHead() { return this.getField("strokeStartMarker") || ""; } + // @computed get markTail() { return this.getField("strokeEndMarker") || ""; } + // @computed get shapeHgt() { return this.getField("_height"); } + // @computed get shapeWid() { return this.getField("_width"); } + // @computed get shapeXps() { return this.getField("x"); } + // @computed get shapeYps() { return this.getField("y"); } + // @computed get shapeRot() { return this.getField("rotation"); } + // set unFilled(value) { this.colorFil = value ? "" : this._lastFill; } + // set solidFil(value) { this.unFilled = !value; } + // set colorFil(value) { value && (this._lastFill = value); this.selectedInk?.forEach(i => i.rootDoc.fillColor = value ? value : undefined); } + // set colorStk(value) { value && (this._lastLine = value); this.selectedInk?.forEach(i => i.rootDoc.color = value ? value : undefined); } + // set markHead(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeStartMarker = value); } + // set markTail(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeEndMarker = value); } + // set unStrokd(value) { this.colorStk = value ? "" : this._lastLine; } + // set solidStk(value) { this.dashdStk = ""; this.unStrokd = !value; } + // set dashdStk(value) { + // value && (this._lastDash = value) && (this.unStrokd = false); + // this.selectedInk?.forEach(i => i.rootDoc.strokeDash = value ? this._lastDash : undefined); + // } + // set shapeXps(value) { this.selectedInk?.forEach(i => i.rootDoc.x = Number(value)); } + // set shapeYps(value) { this.selectedInk?.forEach(i => i.rootDoc.y = Number(value)); } + // set shapeRot(value) { this.selectedInk?.forEach(i => i.rootDoc.rotation = Number(value)); } + // set widthStk(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeWidth = Number(value)); } + // set shapeWid(value) { + // this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => { + // const oldWidth = NumCast(i.rootDoc._width); + // i.rootDoc._width = Number(value); + // this._lock && (i.rootDoc._height = (i.rootDoc._width * NumCast(i.rootDoc._height)) / oldWidth); + // }); + // } + // set shapeHgt(value) { + // this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => { + // const oldHeight = NumCast(i.rootDoc._height); + // i.rootDoc._height = Number(value); + // this._lock && (i.rootDoc._width = (i.rootDoc._height * NumCast(i.rootDoc._width)) / oldHeight); + // }); + // } /** * Adds a new control point to the ink instance when editing its format. @@ -191,7 +192,7 @@ export class InkStrokeProperties { */ @undoBatch @action - rotate = (angle: number) => { + rotateInk = (angle: number) => { this.applyFunction((doc: Doc, ink: InkData, ptsXscale: number, ptsYscale: number) => { const oldXrange = (xs => ({ coord: NumCast(doc.x), min: Math.min(...xs), max: Math.max(...xs) }))(ink.map(p => p.X)); const oldYrange = (ys => ({ coord: NumCast(doc.y), min: Math.min(...ys), max: Math.max(...ys) }))(ink.map(p => p.Y)); @@ -301,29 +302,29 @@ export class InkStrokeProperties { return newPoints; }); - /** - * Changes the color of the border of the ink instance. - * @param color The new hex value to change the border to. - * @returns true. - */ - @undoBatch - @action - switchStk = (color: ColorState) => { - const val = String(color.hex); - this.colorStk = val; - return true; - } + // /** + // * Changes the color of the border of the ink instance. + // * @param color The new hex value to change the border to. + // * @returns true. + // */ + // @undoBatch + // @action + // switchStk = (color: ColorState) => { + // const val = String(color.hex); + // this.colorStk = val; + // return true; + // } - /** - * Changes the color of the fill of the ink instance. - * @param color The new hex value to change the fill to. - * @returns true. - */ - @undoBatch - @action - switchFil = (color: ColorState) => { - const val = String(color.hex); - this.colorFil = val; - return true; - } + // /** + // * Changes the color of the fill of the ink instance. + // * @param color The new hex value to change the fill to. + // * @returns true. + // */ + // @undoBatch + // @action + // switchFil = (color: ColorState) => { + // const val = String(color.hex); + // this.colorFil = val; + // return true; + // } }
\ No newline at end of file diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 163eb05b0..9555557ec 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -18,6 +18,7 @@ import { FieldView, FieldViewProps } from "./nodes/FieldView"; import React = require("react"); import { InkStrokeProperties } from "./InkStrokeProperties"; import { CurrentUserUtils } from "../util/CurrentUserUtils"; +import { InkControl } from "./InkControl"; type InkDocument = makeInterface<[typeof documentSchema]>; const InkDocument = makeInterface(documentSchema); @@ -80,29 +81,6 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume } /** - * Changes the current selected control point. - * @param i The number of the point to be selected. - */ - @action - changeCurrPoint = (i: number) => { - if (InkStrokeProperties.Instance) { - InkStrokeProperties.Instance._currPoint = i; - document.addEventListener("keydown", this.onDelete, true); - } - } - - /** - * Deletes the currently selected points. - * @param e Keyboard Event. - */ - @action - onDelete = (e: KeyboardEvent) => { - if (["-", "Backspace", "Delete"].includes(e.key)) { - if (InkStrokeProperties.Instance?.deletePoints()) e.stopPropagation(); - } - } - - /** * Handles the movement of the entire ink object when the user clicks and drags. * @param e React Pointer Event. */ @@ -113,7 +91,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume } } - public static MaskDim = 50000; + static readonly MaskDim = 50000; render() { TraceMobx(); const formatInstance = InkStrokeProperties.Instance; @@ -143,7 +121,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume StrCast(this.layoutDoc.strokeStartMarker), StrCast(this.layoutDoc.strokeEndMarker), StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5 && lineBot - lineTop > 1 && lineRgt - lineLft > 1, false); - const selectedLine = InteractionUtils.CreatePolyline(data, lineLft - strokeWidth * 3, lineTop - strokeWidth * 3, "#1F85DE", strokeWidth / 6, strokeWidth / 6, + const selectedLine = InteractionUtils.CreatePolyline(data, lineLft - strokeWidth * 4 * scaleX, lineTop - strokeWidth * 4 * scaleX, "#1F85DE", strokeWidth / 6, strokeWidth / 6, StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "none"), StrCast(this.layoutDoc.strokeStartMarker), StrCast(this.layoutDoc.strokeEndMarker), StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5 && lineBot - lineTop > 1 && lineRgt - lineLft > 1, false); @@ -203,12 +181,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume onPointerDown={(e) => this.onHandleDown(e, pts.I)} pointerEvents="all" cursor="default" display={(pts.dot1 === formatInstance._currPoint || pts.dot2 === formatInstance._currPoint) ? "inherit" : "none"} /> </svg>); // Control points of the ink (blue outlined squares) that are made visible to user when editing its format. - const controls = controlPoints.map((pts, i) => - <svg height="10" width="10" key={`ctrl${i}`}> - <rect x={(pts.X - left) * scaleX} y={(pts.Y - top) * scaleY} height={strokeWidth} width={strokeWidth} strokeWidth={strokeWidth / 6} stroke="#1F85DE" fill={formatInstance && formatInstance._currPoint === pts.I ? "#1F85DE" : "white"} - onPointerDown={(e) => { this.changeCurrPoint(pts.I); this.onControlDown(e, pts.I); }} pointerEvents="all" cursor="default" - /> - </svg>); + const controls = controlPoints.map((pts, i) => <InkControl control={pts} left={left} top={top} scaleX={scaleX} scaleY={scaleY} strokeWidth={strokeWidth} ScreenToLocalTransform={this.props.ScreenToLocalTransform} /> ); // Set of two blue lines (each with a handle at the end) that are rendered perpendicular to the current selected point while editing. const handleLines = handleLine.map((pts, i) => <svg height="100" width="100" key={`line${i}`}> |