diff options
Diffstat (limited to 'src/client/views/collections')
8 files changed, 336 insertions, 53 deletions
diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index a0b7cd8a8..cfec3a6bc 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -42,7 +42,7 @@ const query = async (data: string | google.maps.LatLngLiteral) => { }; @observer -class CollectionMapView extends CollectionSubView<MapSchema, Partial<IMapProps> & { google: any }>(MapSchema) { +export class CollectionMapView extends CollectionSubView<MapSchema, Partial<IMapProps> & { google: any }>(MapSchema) { private _cancelAddrReq = new Map<string, boolean>(); private _cancelLocReq = new Map<string, boolean>(); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 56c6ce457..47d571bcc 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -6,27 +6,19 @@ import { Id } from "../../../fields/FieldSymbols"; import { List } from "../../../fields/List"; import { listSpec } from "../../../fields/Schema"; import { ScriptField } from "../../../fields/ScriptField"; +import { WebField } from "../../../fields/URLField"; import { Cast, ScriptCast, NumCast } from "../../../fields/Types"; import { GestureUtils } from "../../../pen-gestures/GestureUtils"; -import { CurrentUserUtils } from "../../util/CurrentUserUtils"; import { Upload } from "../../../server/SharedMediaTypes"; import { Utils } from "../../../Utils"; -import { GooglePhotos } from "../../apis/google_docs/GooglePhotosClientUtils"; import { DocServer } from "../../DocServer"; -import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents"; -import { DocumentType } from "../../documents/DocumentTypes"; import { Networking } from "../../Network"; -import { DragManager, dropActionType } from "../../util/DragManager"; import { ImageUtils } from "../../util/Import & Export/ImageUtils"; import { InteractionUtils } from "../../util/InteractionUtils"; import { undoBatch, UndoManager } from "../../util/UndoManager"; import { DocComponent } from "../DocComponent"; import { FieldViewProps } from "../nodes/FieldView"; -import { FormattedTextBox, GoogleRef } from "../nodes/formattedText/FormattedTextBox"; -import { CollectionView } from "./CollectionView"; import React = require("react"); -import { SelectionManager } from "../../util/SelectionManager"; -import { WebField } from "../../../fields/URLField"; export interface CollectionViewProps extends FieldViewProps { addDocument: (document: Doc | Doc[]) => boolean; @@ -229,7 +221,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: } else { added = this.addDocument(docDragData.droppedDocuments); } - e.stopPropagation(); + added && e.stopPropagation(); return added; } else if (de.complete.annoDragData) { @@ -405,7 +397,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: const stringContents = await new Promise<string>(resolve => item.getAsString(resolve)); const type = "html";// (await rp.head(Utils.CorsProxy(stringContents)))["content-type"]; if (type) { - const doc = await Docs.Get.DocumentFromType(type, stringContents, options); + const doc = await DocUtils.DocumentFromType(type, stringContents, options); doc && generatedDocuments.push(doc); } } @@ -435,7 +427,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: } const full = { ...options, _width: 400, title: name }; const pathname = Utils.prepend(result.accessPaths.agnostic.client); - const doc = await Docs.Get.DocumentFromType(type, pathname, full); + const doc = await DocUtils.DocumentFromType(type, pathname, full); if (!doc) { continue; } @@ -450,9 +442,9 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: generatedDocuments.push(doc); } if (generatedDocuments.length) { - const set = generatedDocuments.length > 1 && generatedDocuments.map(d => Doc.iconify(d)); + const set = generatedDocuments.length > 1 && generatedDocuments.map(d => DocUtils.iconify(d)); if (set) { - addDocument(Doc.pileup(generatedDocuments, options.x!, options.y!)!); + addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!)!); } else { generatedDocuments.forEach(addDocument); } @@ -469,3 +461,10 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: return CollectionSubView; } +import { DragManager, dropActionType } from "../../util/DragManager"; +import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents"; +import { CurrentUserUtils } from "../../util/CurrentUserUtils"; +import { DocumentType } from "../../documents/DocumentTypes"; +import { FormattedTextBox, GoogleRef } from "../nodes/formattedText/FormattedTextBox"; +import { CollectionView } from "./CollectionView"; +import { SelectionManager } from "../../util/SelectionManager"; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index b2e1c0f73..e891c4274 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -757,7 +757,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll const existingOnClick = ContextMenu.Instance.findByDescription("OnClick..."); const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : []; onClicks.push({ - description: "Edit onChecked Script", event: () => UndoManager.RunInBatch(() => Doc.makeCustomViewClicked(this.props.Document, undefined, "onCheckedClick"), "edit onCheckedClick"), icon: "edit" + description: "Edit onChecked Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onCheckedClick"), "edit onCheckedClick"), icon: "edit" }); !existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" }); } diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index fecba32c5..7f2b5ff8e 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -1,21 +1,28 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faEye, faEdit } from '@fortawesome/free-regular-svg-icons'; +import { faEdit, faEye } from '@fortawesome/free-regular-svg-icons'; +import { faColumns, faCopy, faEllipsisV, faFingerprint, faGlobeAmericas, faImage, faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faColumns, faCopy, faEllipsisV, faFingerprint, faImage, faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree, faGlobeAmericas } from '@fortawesome/free-solid-svg-icons'; -import { action, observable, computed } from 'mobx'; +import { action, computed, observable } from 'mobx'; import { observer } from "mobx-react"; import * as React from 'react'; import Lightbox from 'react-image-lightbox-with-rotate'; import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app import { DateField } from '../../../fields/DateField'; -import { DataSym, Doc, DocListCast, Field, Opt, AclSym, AclAddonly, AclReadonly } from '../../../fields/Doc'; +import { AclAddonly, AclReadonly, AclSym, DataSym, Doc, DocListCast, Field, Opt } from '../../../fields/Doc'; +import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; -import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from '../../../fields/Types'; +import { ObjectField } from '../../../fields/ObjectField'; +import { listSpec } from '../../../fields/Schema'; +import { ComputedField, ScriptField } from '../../../fields/ScriptField'; +import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; -import { Utils, setupMoveUpEvents, returnFalse, returnZero, emptyPath, emptyFunction, returnOne } from '../../../Utils'; +import { emptyFunction, emptyPath, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from '../../../Utils'; +import { Docs } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; +import { CurrentUserUtils } from '../../util/CurrentUserUtils'; import { ImageUtils } from '../../util/Import & Export/ImageUtils'; +import { InteractionUtils } from '../../util/InteractionUtils'; import { ContextMenu } from "../ContextMenu"; import { FieldView, FieldViewProps } from '../nodes/FieldView'; import { ScriptBox } from '../ScriptBox'; @@ -25,8 +32,10 @@ import { CollectionDockingView } from "./CollectionDockingView"; import { AddCustomFreeFormLayout } from './collectionFreeForm/CollectionFreeFormLayoutEngines'; import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView'; import { CollectionLinearView } from './CollectionLinearView'; +import { CollectionMapView } from './CollectionMapView'; import { CollectionMulticolumnView } from './collectionMulticolumn/CollectionMulticolumnView'; import { CollectionMultirowView } from './collectionMulticolumn/CollectionMultirowView'; +import { CollectionPileView } from './CollectionPileView'; import { CollectionSchemaView } from "./CollectionSchemaView"; import { CollectionStackingView } from './CollectionStackingView'; import { CollectionStaffView } from './CollectionStaffView'; @@ -35,20 +44,12 @@ import { CollectionTimeView } from './CollectionTimeView'; import { CollectionTreeView } from "./CollectionTreeView"; import './CollectionView.scss'; import { CollectionViewBaseChrome } from './CollectionViewChromes'; -import { CurrentUserUtils } from '../../util/CurrentUserUtils'; -import { Id } from '../../../fields/FieldSymbols'; -import { listSpec } from '../../../fields/Schema'; -import { Docs } from '../../documents/Documents'; -import { ScriptField, ComputedField } from '../../../fields/ScriptField'; -import { InteractionUtils } from '../../util/InteractionUtils'; -import { ObjectField } from '../../../fields/ObjectField'; -import CollectionMapView from './CollectionMapView'; -import { CollectionPileView } from './CollectionPileView'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; export const COLLECTION_BORDER_WIDTH = 2; const path = require('path'); + library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faGlobeAmericas, faEllipsisV, faImage, faEye as any, faCopy); export enum CollectionViewType { @@ -162,7 +163,8 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus if (Doc.AreProtosEqual(this.props.Document, targetCollection)) { return true; } - return this.removeDocument(doc) ? addDocument(doc) : false; + const first = doc instanceof Doc ? doc : doc[0]; + return !first?.cantLeaveCollection && this.removeDocument(doc) ? addDocument(doc) : false; } showIsTagged = () => { @@ -519,4 +521,6 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus {this.facetWidth() < 10 ? (null) : this.filterView} </div>); } -}
\ No newline at end of file +} + + diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 80ee2a65d..d512f815c 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); @@ -202,7 +202,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P const x = (z ? xpo : xp) - docDragData.offset[0]; const y = (z ? ypo : yp) - docDragData.offset[1]; const zsorted = this.childLayoutPairs.map(pair => pair.layout).slice().sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex)); - zsorted.forEach((doc, index) => doc.zIndex = index + 1); + zsorted.forEach((doc, index) => doc.zIndex = doc.isInkMask ? 5000 : index + 1); const dropPos = [NumCast(docDragData.droppedDocuments[0].x), NumCast(docDragData.droppedDocuments[0].y)]; for (let i = 0; i < docDragData.droppedDocuments.length; i++) { const d = docDragData.droppedDocuments[i]; @@ -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() { |
