diff options
| author | Bob Zeleznik <zzzman@gmail.com> | 2020-06-08 20:54:11 -0400 |
|---|---|---|
| committer | Bob Zeleznik <zzzman@gmail.com> | 2020-06-08 20:54:11 -0400 |
| commit | 56e3d5c6958f51f09cc8a2d19e5c00e5bb59093c (patch) | |
| tree | 5ff0da85eeb77d086394b783ab01f93534e205e3 /src/client/views/collections/collectionFreeForm | |
| parent | 3e7cc70f7588c738c34de1d087e1d53b9410d9fe (diff) | |
| parent | ef17b488348b0ac9f869878829fc6a60f64f9304 (diff) | |
Merge branch 'ink_menu'
Diffstat (limited to 'src/client/views/collections/collectionFreeForm')
4 files changed, 299 insertions, 19 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 80ee2a65d..379156179 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -29,7 +29,6 @@ import { undoBatch, UndoManager } from "../../../util/UndoManager"; import { COLLECTION_BORDER_WIDTH } from "../../../views/globalCssVariables.scss"; import { ContextMenu } from "../../ContextMenu"; import { ContextMenuProps } from "../../ContextMenuItem"; -import { InkingControl } from "../../InkingControl"; import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView"; import { DocumentViewProps, DocumentView } from "../../nodes/DocumentView"; import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox"; @@ -46,6 +45,7 @@ import React = require("react"); import { CollectionViewType } from "../CollectionView"; import { Timeline } from "../../animationtimeline/Timeline"; import { SnappingManager } from "../../../util/SnappingManager"; +import { InkingStroke, ActiveInkColor, ActiveInkWidth, ActiveInkBezierApprox } from "../../InkingStroke"; library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload); @@ -389,7 +389,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P @action onPointerDown = (e: React.PointerEvent): void => { - if (e.nativeEvent.cancelBubble || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) || InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) { + if (e.nativeEvent.cancelBubble || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) || InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (Doc.GetSelectedTool() === InkTool.Highlighter || Doc.GetSelectedTool() === InkTool.Pen)) { return; } this._hitCluster = this.props.Document.useClusters ? this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY)) !== -1 : false; @@ -406,7 +406,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P document.addEventListener("pointermove", this.onPointerMove); document.addEventListener("pointerup", this.onPointerUp); // if not using a pen and in no ink mode - if (InkingControl.Instance.selectedTool === InkTool.None) { + if (Doc.GetSelectedTool() === InkTool.None) { this._downX = this._lastX = e.pageX; this._downY = this._lastY = e.pageY; } @@ -430,13 +430,13 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P this.addMoveListeners(); this.removeEndListeners(); this.addEndListeners(); - // if (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen) { + // if (Doc.SelectedTool() === InkTool.Highlighter || Doc.SelectedTool() === InkTool.Pen) { // e.stopPropagation(); // e.preventDefault(); // const point = this.getTransform().transformPoint(pt.pageX, pt.pageY); // this._points.push({ X: point[0], Y: point[1] }); // } - if (InkingControl.Instance.selectedTool === InkTool.None) { + if (Doc.GetSelectedTool() === InkTool.None) { this._lastX = pt.pageX; this._lastY = pt.pageY; e.preventDefault(); @@ -456,7 +456,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P case GestureUtils.Gestures.Stroke: const points = ge.points; const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height); - const inkDoc = Docs.Create.InkDocument(InkingControl.Instance.selectedColor, InkingControl.Instance.selectedTool, InkingControl.Instance.selectedWidth, points, { title: "ink stroke", x: B.x, y: B.y, _width: B.width, _height: B.height }); + const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), Doc.GetSelectedTool(), ActiveInkWidth(), ActiveInkBezierApprox(), points, { title: "ink stroke", x: B.x, y: B.y, _width: B.width, _height: B.height }); this.addDocument(inkDoc); e.stopPropagation(); break; @@ -617,7 +617,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P return; } if (!e.cancelBubble) { - const selectedTool = InkingControl.Instance.selectedTool; + const selectedTool = Doc.GetSelectedTool(); if (selectedTool === InkTool.None) { if (this._hitCluster && this.tryDragCluster(e)) { e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers @@ -639,7 +639,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true); const pt = myTouches[0]; if (pt) { - if (InkingControl.Instance.selectedTool === InkTool.None) { + if (Doc.GetSelectedTool() === InkTool.None) { if (this._hitCluster && this.tryDragCluster(e)) { e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers e.preventDefault(); diff --git a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.scss b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.scss new file mode 100644 index 000000000..a7f4d4e53 --- /dev/null +++ b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.scss @@ -0,0 +1,36 @@ +.antimodeMenu-button { + .color-preview { + width: 100%; + height: 100%; + } + + +} + +.sketch-picker { + background: #323232; + + .flexbox-fit { + background: #323232; + } +} + +.btn-group { + display: grid; + grid-template-columns: auto auto auto auto; + /* Make the buttons appear below each other */ +} + +.btn2-group { + display: block; + background: #323232; + grid-template-columns: auto; + + /* Make the buttons appear below each other */ + .antimodeMenu-button { + background: #323232; + display: block; + + + } +}
\ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx new file mode 100644 index 000000000..5a27f74e5 --- /dev/null +++ b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx @@ -0,0 +1,140 @@ +import React = require("react"); +import AntimodeMenu from "../../AntimodeMenu"; +import { observer } from "mobx-react"; +import { observable, action, computed } from "mobx"; +import "./InkOptionsMenu.scss"; +import { ActiveInkColor, ActiveInkBezierApprox, SetActiveInkWidth, SetActiveInkColor, SetActiveBezierApprox } from "../../InkingStroke"; +import { Scripting } from "../../../util/Scripting"; +import { InkTool } from "../../../../fields/InkField"; +import { ColorState } from "react-color"; +import { Utils } from "../../../../Utils"; +import GestureOverlay from "../../GestureOverlay"; +import { Doc } from "../../../../fields/Doc"; + +@observer +export default class InkOptionsMenu extends AntimodeMenu { + static Instance: InkOptionsMenu; + + private _palette = ["D0021B", "F5A623", "F8E71C", "8B572A", "7ED321", "417505", "9013FE", "4A90E2", "50E3C2", "B8E986", "000000", "4A4A4A", "9B9B9B", "FFFFFF"]; + private _width = ["1", "5", "10", "100", "200", "300"]; + private _buttons = ["circle", "triangle", "rectangle", "arrow", "line"]; + private _icons = ["O", "∆", "ロ", "➜", "-"]; + + @observable _colorBtn = false; + @observable _widthBtn = false; + + constructor(props: Readonly<{}>) { + super(props); + InkOptionsMenu.Instance = this; + this._canFade = false; // don't let the inking menu fade away + } + + @action + changeColor = (color: string) => { + const col: ColorState = { + hex: color, hsl: { a: 0, h: 0, s: 0, l: 0, source: "" }, hsv: { a: 0, h: 0, s: 0, v: 0, source: "" }, + rgb: { a: 0, r: 0, b: 0, g: 0, source: "" }, oldHue: 0, source: "", + }; + SetActiveInkColor(Utils.colorString(col)); + } + + @action + changeBezier = (e: React.PointerEvent): void => { + SetActiveBezierApprox(!ActiveInkBezierApprox() ? "300" : ""); + } + + @computed get widthPicker() { + var widthPicker = <button + className="antimodeMenu-button" + key="width" + onPointerDown={action(e => this._widthBtn = !this._widthBtn)} + style={{ backgroundColor: this._widthBtn ? "121212" : "" }}> + W + </button>; + if (this._widthBtn) { + widthPicker = <div className="btn2-group" key="width"> + {widthPicker} + {this._width.map(wid => { + return <button + className="antimodeMenu-button" + key={wid} + onPointerDown={action(() => { SetActiveInkWidth(wid); this._widthBtn = false; })} + style={{ backgroundColor: this._widthBtn ? "121212" : "" }}> + {wid} + </button>; + })} + </div>; + } + return widthPicker; + } + + @computed get colorPicker() { + var colorPicker = <button + className="antimodeMenu-button" + key="color" + title="colorChanger" + onPointerDown={action(e => this._colorBtn = !this._colorBtn)} + style={{ backgroundColor: this._colorBtn ? "121212" : "" }}> + <div className="color-preview" style={{ backgroundColor: ActiveInkColor() ?? "121212" }}></div> + </button>; + if (this._colorBtn) { + colorPicker = <div className="btn-group" key="color"> + {colorPicker} + {this._palette.map(color => { + return <button + className="antimodeMenu-button" + key={color} + onPointerDown={action(() => { this.changeColor(color); this._colorBtn = false; })} + style={{ backgroundColor: this._colorBtn ? "121212" : "" }}> + <div className="color-preview" style={{ backgroundColor: color }}></div> + </button>; + })} + </div>; + } + return colorPicker; + } + + @computed get shapeButtons() { + return <> + {this._buttons.map((btn, i) => <button + className="antimodeMenu-button" + title={`Draw ${btn}`} + key={btn} + onPointerDown={action(e => GestureOverlay.Instance.InkShape = btn)} + style={{ backgroundColor: btn === GestureOverlay.Instance.InkShape ? "121212" : "" }}> + {this._icons[i]} + </button>)}, + </>; + } + + @computed get bezierButton() { + return <button + className="antimodeMenu-button" + title="Bezier changer" + key="bezier" + onPointerDown={e => this.changeBezier(e)} + style={ { backgroundColor:ActiveInkBezierApprox() ? "121212":"" } }> + B + </button>; + } + + render() { + const buttons = [ + <button className="antimodeMenu-button" title="Drag" key="drag" onPointerDown={e => this.dragStart(e)}> ✜ </button>, + this.shapeButtons, + this.bezierButton, + this.widthPicker, + this.colorPicker, + ]; + return this.getElement(buttons); + } +} +Scripting.addGlobal(function activatePen(penBtn: any) { + if (penBtn) { + Doc.SetSelectedTool(InkTool.Pen); + InkOptionsMenu.Instance.jumpTo(300, 300); + } else { + Doc.SetSelectedTool(InkTool.None); + InkOptionsMenu.Instance.fadeOut(true); + } +});
\ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 46804c848..73dd41a15 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -42,6 +42,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque @observable _downY: number = 0; @observable _visible: boolean = false; _commandExecuted = false; + @observable _pointsX: number[] = []; + @observable _pointsY: number[] = []; + @observable _freeHand: boolean = false; componentDidMount() { this.props.setPreviewCursor?.(this.setPreviewCursor); @@ -57,6 +60,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque if (hideMarquee) { this._visible = false; } + this._pointsX = []; + this._pointsY = []; + this._freeHand = false; } @undoBatch @@ -191,6 +197,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque onPointerMove = (e: PointerEvent): void => { this._lastX = e.pageX; this._lastY = e.pageY; + this._pointsX.push(e.clientX); + this._pointsY.push(e.clientY); if (!e.cancelBubble) { if (Math.abs(this._lastX - this._downX) > Utils.DRAG_THRESHOLD || Math.abs(this._lastY - this._downY) > Utils.DRAG_THRESHOLD) { @@ -519,6 +527,17 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque } this.cleanupInteractions(false); } + if (e.key === "r") { + this._commandExecuted = true; + e.stopPropagation(); + e.preventDefault(); + this.changeFreeHand(true); + } + } + + @action + changeFreeHand = (x: boolean) => { + this._freeHand = x; } // @action // marqueeInkSelect(ink: Map<any, any>) { @@ -559,7 +578,51 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque // this.ink = new InkField(idata); // } // } + touchesLine(r1: { left: number, top: number, width: number, height: number }) { + for (var i = 0; i < this._pointsX.length; i++) { + const topLeft = this.props.getTransform().transformPoint(this._pointsX[i], this._pointsY[i]); + if (topLeft[0] > r1.left && + topLeft[0] < r1.left + r1.width && + topLeft[1] > r1.top && + topLeft[1] < r1.top + r1.height) { + return true; + } + } + return false; + } + boundingShape(r1: { left: number, top: number, width: number, height: number }) { + const trueLeft = this.props.getTransform().transformPoint(Math.min(...this._pointsX), Math.min(...this._pointsY))[0]; + const trueTop = this.props.getTransform().transformPoint(Math.min(...this._pointsX), Math.min(...this._pointsY))[1]; + const trueRight = this.props.getTransform().transformPoint(Math.max(...this._pointsX), Math.max(...this._pointsY))[0]; + const trueBottom = this.props.getTransform().transformPoint(Math.max(...this._pointsX), Math.max(...this._pointsY))[1]; + + if (r1.left > trueLeft && r1.top > trueTop && r1.left + r1.width < trueRight && r1.top + r1.height < trueBottom) { + var hasTop = false; + var hasLeft = false; + var hasBottom = false; + var hasRight = false; + for (var i = 0; i < this._pointsX.length; i++) { + const truePoint = this.props.getTransform().transformPoint(this._pointsX[i], this._pointsY[i]); + if (!hasLeft && (truePoint[0] > trueLeft && truePoint[0] < r1.left) && (truePoint[1] > r1.top && truePoint[1] < r1.top + r1.height)) { + hasLeft = true; + } + if (!hasTop && (truePoint[1] > trueTop && truePoint[1] < r1.top) && (truePoint[0] > r1.left && truePoint[0] < r1.left + r1.width)) { + hasTop = true; + } + if (!hasRight && (truePoint[0] < trueRight && truePoint[0] > r1.left + r1.width) && (truePoint[1] > r1.top && truePoint[1] < r1.top + r1.height)) { + hasRight = true; + } + if (!hasBottom && (truePoint[1] < trueBottom && truePoint[1] > r1.top + r1.height) && (truePoint[0] > r1.left && truePoint[0] < r1.left + r1.width)) { + hasBottom = true; + } + if (hasTop && hasLeft && hasBottom && hasRight) { + return true; + } + } + } + return false; + } marqueeSelect(selectBackgrounds: boolean = true) { const selRect = this.Bounds; const selection: Doc[] = []; @@ -569,8 +632,15 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque const y = NumCast(doc.y); const w = NumCast(layoutDoc._width); const h = NumCast(layoutDoc._height); - if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) { - selection.push(doc); + if (this._freeHand === false) { + if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) { + selection.push(doc); + } + } else { + if (this.touchesLine({ left: x, top: y, width: w, height: h }) || + this.boundingShape({ left: x, top: y, width: w, height: h })) { + selection.push(doc); + } } }); if (!selection.length && selectBackgrounds) { @@ -597,8 +667,15 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque const y = NumCast(doc.y); const w = NumCast(layoutDoc._width); const h = NumCast(layoutDoc._height); - if (this.intersectRect({ left: x, top: y, width: w, height: h }, otherBounds)) { - selection.push(doc); + if (this._freeHand === false) { + if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) { + selection.push(doc); + } + } else { + if (this.touchesLine({ left: x, top: y, width: w, height: h }) || + this.boundingShape({ left: x, top: y, width: w, height: h })) { + selection.push(doc); + } } }); } @@ -614,13 +691,40 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque * This contains the "C for collection, ..." text on marquees. * Commented out by syip2 when the marquee menu was added. */ - return <div className="marquee" style={{ - transform: `translate(${p[0]}px, ${p[1]}px)`, - width: `${Math.abs(v[0])}`, - height: `${Math.abs(v[1])}`, zIndex: 2000 - }} > - {/* <span className="marquee-legend" /> */} - </div>; + if (!this._freeHand) { + return <div className="marquee" style={{ + transform: `translate(${p[0]}px, ${p[1]}px)`, + width: `${Math.abs(v[0])}`, + height: `${Math.abs(v[1])}`, zIndex: 2000 + }} > + {/* <span className="marquee-legend" /> */} + </div>; + + } else { + //subtracted 250 for offset + var str: string = ""; + for (var i = 0; i < this._pointsX.length; i++) { + var x = 0; + x = this._pointsX[i] - 250; + str += x.toString(); + str += ","; + str += this._pointsY[i].toString(); + str += (" "); + } + + //hardcoded height and width. + return <div className="marquee" style={{ zIndex: 2000 }}> + <svg height={2000} width={2000}> + <polyline + points={str} + fill="none" + stroke="black" + strokeWidth="1" + strokeDasharray="3" + /> + </svg> + </div>; + } } render() { |
