diff options
author | Bob Zeleznik <zzzman@gmail.com> | 2020-04-29 21:08:39 -0400 |
---|---|---|
committer | Bob Zeleznik <zzzman@gmail.com> | 2020-04-29 21:08:39 -0400 |
commit | 0cff51fbe53deea53ffa4d1f1cbb80a3c74a918f (patch) | |
tree | 0fb71428039279d3a116686561c457f9afbe6f67 /src | |
parent | 43e573ea0cf4634b65b513c633f90be84846f8df (diff) | |
parent | dadbb74ffa56a0dc55745ce972e7b13925629b7b (diff) |
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web
Diffstat (limited to 'src')
-rw-r--r-- | src/client/util/DragManager.ts | 61 | ||||
-rw-r--r-- | src/client/views/MainView.tsx | 11 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 81 |
3 files changed, 147 insertions, 6 deletions
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 35694a6bd..a905dff0a 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -18,6 +18,8 @@ import { AudioBox } from "../views/nodes/AudioBox"; import { DateField } from "../../new_fields/DateField"; import { DocumentView } from "../views/nodes/DocumentView"; import { UndoManager } from "./UndoManager"; +import { PointData } from "../../new_fields/InkField"; +import { MainView } from "../views/MainView"; export type dropActionType = "alias" | "copy" | "move" | undefined; // undefined = move export function SetupDrag( @@ -73,6 +75,8 @@ export function SetupDrag( export namespace DragManager { let dragDiv: HTMLDivElement; + export let horizSnapLines: number[]; + export let vertSnapLines: number[]; export function Root() { const root = document.getElementById("root"); @@ -292,6 +296,14 @@ export namespace DragManager { StartDrag([ele], {}, downX, downY); } + @action + export function SetSnapLines(horizLines: number[], vertLines: number[]) { + horizSnapLines = horizLines; + vertSnapLines = vertLines; + MainView.Instance._hLines = horizLines; + MainView.Instance._vLines = vertLines; + } + function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void) { eles = eles.filter(e => e); if (!dragDiv) { @@ -307,12 +319,22 @@ export namespace DragManager { const ys: number[] = []; const docs = dragData instanceof DocumentDragData ? dragData.draggedDocuments : dragData instanceof PdfAnnoDragData ? [dragData.dragDocument] : []; + const elesCont = { + left: Number.MAX_SAFE_INTEGER, + top: Number.MAX_SAFE_INTEGER, + right: Number.MIN_SAFE_INTEGER, + bottom: Number.MIN_SAFE_INTEGER + }; const dragElements = eles.map(ele => { if (!ele.parentNode) dragDiv.appendChild(ele); const dragElement = ele.parentNode === dragDiv ? ele : ele.cloneNode(true) as HTMLElement; const rect = ele.getBoundingClientRect(); const scaleX = rect.width / ele.offsetWidth, scaleY = rect.height / ele.offsetHeight; + elesCont.left = Math.min(rect.left, elesCont.left); + elesCont.top = Math.min(rect.top, elesCont.top); + elesCont.right = Math.max(rect.right, elesCont.right); + elesCont.bottom = Math.max(rect.bottom, elesCont.bottom); xs.push(rect.left); ys.push(rect.top); scaleXs.push(scaleX); @@ -362,6 +384,10 @@ export namespace DragManager { let lastX = downX; let lastY = downY; + const xFromLeft = downX - elesCont.left; + const yFromTop = downY - elesCont.top; + const xFromRight = elesCont.right - downX; + const yFromBottom = elesCont.bottom - downY; let alias = "alias"; const moveHandler = (e: PointerEvent) => { e.preventDefault(); // required or dragging text menu link item ends up dragging the link button as native drag/drop @@ -382,11 +408,38 @@ export namespace DragManager { button: 0 }, dragData.droppedDocuments); } + let thisX = e.pageX; + let thisY = e.pageY; + const currLeft = e.pageX - xFromLeft; + const currTop = e.pageY - yFromTop; + const currRight = e.pageX + xFromRight; + const currBottom = e.pageY + yFromBottom; + const closestLeft = vertSnapLines.reduce((prev, curr) => Math.abs(prev - currLeft) > Math.abs(curr - currLeft) ? curr : prev); + const closestTop = horizSnapLines.reduce((prev, curr) => Math.abs(prev - currTop) > Math.abs(curr - currTop) ? curr : prev); + const closestRight = vertSnapLines.reduce((prev, curr) => Math.abs(prev - currRight) > Math.abs(curr - currRight) ? curr : prev); + const closestBottom = horizSnapLines.reduce((prev, curr) => Math.abs(prev - currBottom) > Math.abs(curr - currBottom) ? curr : prev); + const distFromClosestLeft = Math.abs(e.pageX - xFromLeft - closestLeft); + const distFromClosestTop = Math.abs(e.pageY - yFromTop - closestTop); + const distFromClosestRight = Math.abs(e.pageX + xFromRight - closestRight); + const distFromClosestBottom = Math.abs(e.pageY + yFromBottom - closestBottom); + if (distFromClosestLeft < 10 && distFromClosestLeft < distFromClosestRight) { + thisX = closestLeft + xFromLeft; + } + else if (distFromClosestRight < 10) { + thisX = closestRight - xFromRight; + } + if (distFromClosestTop < 10 && distFromClosestTop < distFromClosestRight) { + thisY = closestTop + yFromTop; + } + else if (distFromClosestBottom < 10) { + thisY = closestBottom - yFromBottom; + } + alias = "move"; - const moveX = e.pageX - lastX; - const moveY = e.pageY - lastY; - lastX = e.pageX; - lastY = e.pageY; + const moveX = thisX - lastX; + const moveY = thisY - lastY; + lastX = thisX; + lastY = thisY; dragElements.map((dragElement, i) => (dragElement.style.transform = `translate(${(xs[i] += moveX) + (options?.offsetX || 0)}px, ${(ys[i] += moveY) + (options?.offsetY || 0)}px) scale(${scaleXs[i]}, ${scaleYs[i]})`) ); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index be46e0107..0102d1327 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -42,6 +42,7 @@ import { OverlayView } from './OverlayView'; import PDFMenu from './pdf/PDFMenu'; import { PreviewCursor } from './PreviewCursor'; import { ScriptField } from '../../new_fields/ScriptField'; +import { DragManager } from '../util/DragManager'; import { TimelineMenu } from './animationtimeline/TimelineMenu'; @observer @@ -567,6 +568,9 @@ export class MainView extends React.Component { return this._mainViewRef; } + @observable public _hLines: any; + @observable public _vLines: any; + render() { return (<div className={"mainView-container" + (this.darkScheme ? "-dark" : "")} ref={this._mainViewRef}> <DictationOverlay /> @@ -584,6 +588,13 @@ export class MainView extends React.Component { <MarqueeOptionsMenu /> <RichTextMenu /> <OverlayView /> + {/* TO VIEW SNAP LINES + <div className="snapLines" style={{ position: "absolute", top: 0, left: 0, width: "100%", height: "100%", pointerEvents: "none" }}> + <svg style={{ width: "100%", height: "100%" }}> + {this._hLines?.map(l => <line x1="0" y1={l} x2="2000" y2={l} stroke="black" />)} + {this._vLines?.map(l => <line y1="0" x1={l} y2="2000" x2={l} stroke="black" />)} + </svg> + </div> */} <TimelineMenu /> </div >); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index d85233041..d291cad21 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -7,7 +7,7 @@ import { computedFn } from "mobx-utils"; import { Doc, HeightSym, Opt, WidthSym, DocListCast } from "../../../../new_fields/Doc"; import { documentSchema, positionSchema } from "../../../../new_fields/documentSchemas"; import { Id } from "../../../../new_fields/FieldSymbols"; -import { InkData, InkField, InkTool } from "../../../../new_fields/InkField"; +import { InkData, InkField, InkTool, PointData } from "../../../../new_fields/InkField"; import { List } from "../../../../new_fields/List"; import { RichTextField } from "../../../../new_fields/RichTextField"; import { createSchema, listSpec, makeInterface } from "../../../../new_fields/Schema"; @@ -1133,6 +1133,77 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P this._timelineRef.current!.timelineContextMenu(e); } + intersectRect(r1: { left: number, top: number, width: number, height: number }, + r2: { left: number, top: number, width: number, height: number }) { + return !(r2.left > r1.left + r1.width || r2.left + r2.width < r1.left || r2.top > r1.top + r1.height || r2.top + r2.height < r1.top); + } + + @action + onPointerOver = (e: React.PointerEvent) => { + if (SelectionManager.GetIsDragging()) { + const size = this.props.ScreenToLocalTransform().transformDirection(this.props.PanelWidth(), this.props.PanelHeight()); + const selRect = { left: this.panX() - size[0] / 2, top: this.panY() - size[1] / 2, width: size[0], height: size[1] }; + const selection: Doc[] = []; + this.getActiveDocuments().filter(doc => !doc.isBackground && doc.z === undefined).map(doc => { + const layoutDoc = Doc.Layout(doc); + const x = NumCast(doc.x); + 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 (!selection.length) { + this.getActiveDocuments().filter(doc => doc.z === undefined).map(doc => { + const layoutDoc = Doc.Layout(doc); + const x = NumCast(doc.x); + 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 (!selection.length) { + const otherBounds = { left: this.panX(), top: this.panY(), width: Math.abs(size[0]), height: Math.abs(size[1]) }; + this.getActiveDocuments().filter(doc => doc.z !== undefined).map(doc => { + const layoutDoc = Doc.Layout(doc); + const x = NumCast(doc.x); + 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); + } + }); + } + const horizLines: number[] = []; + const vertLines: number[] = []; + selection.forEach(doc => { + const layoutDoc = Doc.Layout(doc); + const x = NumCast(doc.x); + const y = NumCast(doc.y); + const w = NumCast(layoutDoc._width); + const h = NumCast(layoutDoc._height); + const topLeftInScreen = this.getTransform().inverse().transformPoint(x, y); + const docSize = this.getTransform().inverse().transformDirection(w, h); + horizLines.push(topLeftInScreen[1]); // top line + horizLines.push(topLeftInScreen[1] + docSize[1]); // bottom line + horizLines.push(topLeftInScreen[1] + docSize[1] / 2); // horiz center line + vertLines.push(topLeftInScreen[0]);//left line + vertLines.push(topLeftInScreen[0] + docSize[0]);// right line + vertLines.push(topLeftInScreen[0] + docSize[0] / 2);// vert center line + }); + DragManager.SetSnapLines(horizLines, vertLines); + } + e.stopPropagation(); + } + + @observable private _hLines: number[] | undefined; + @observable private _vLines: number[] | undefined; + private childViews = () => { const children = typeof this.props.children === "function" ? (this.props.children as any)() as JSX.Element[] : []; return [ @@ -1196,6 +1267,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P // otherwise, they are stored in fieldKey. All annotations to this document are stored in the extension document return <div className={"collectionfreeformview-container"} ref={this.createDashEventsTarget} + onPointerOver={this.onPointerOver} onWheel={this.onPointerWheel} onClick={this.onClick} //pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined, onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onExternalDrop.bind(this)} onContextMenu={this.onContextMenu} style={{ @@ -1220,7 +1292,12 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P }}> </div> - + {/* <div className="snapLines" style={{ position: "absolute", top: 0, left: 0, width: "100%", height: "100%", pointerEvents: "none" }}> + <svg style={{ width: "100%", height: "100%" }}> + {this._hLines?.map(l => <line x1="0" y1={l} x2="1000" y2={l} stroke="black" />)} + {this._vLines?.map(l => <line y1="0" x1={l} y2="1000" x2={l} stroke="black" />)} + </svg> + </div> */} </div >; } } |