diff options
author | Tyler Schicke <tyler_schicke@brown.edu> | 2019-02-13 04:50:56 -0500 |
---|---|---|
committer | Tyler Schicke <tyler_schicke@brown.edu> | 2019-02-13 04:50:56 -0500 |
commit | c93c3e1970c6ecc91b60f1782e82b1bfdc7fef30 (patch) | |
tree | 47c30b57d3ec6ac3d1f535e8186c0cf527640010 /src | |
parent | 46a4c26dbadefaf981637f79fb5f76e458baac9d (diff) |
A lot of stuff working, a lot of other stuff not working
Diffstat (limited to 'src')
-rw-r--r-- | src/client/util/DragManager.ts | 2 | ||||
-rw-r--r-- | src/client/util/SelectionManager.ts | 12 | ||||
-rw-r--r-- | src/client/util/Transform.ts | 70 | ||||
-rw-r--r-- | src/client/views/DocumentDecorations.tsx | 39 | ||||
-rw-r--r-- | src/client/views/Main.tsx | 1 | ||||
-rw-r--r-- | src/client/views/collections/CollectionDockingView.tsx | 10 | ||||
-rw-r--r-- | src/client/views/collections/CollectionFreeFormView.tsx | 55 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSchemaView.tsx | 4 | ||||
-rw-r--r-- | src/client/views/collections/CollectionViewBase.tsx | 10 | ||||
-rw-r--r-- | src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 138 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 184 | ||||
-rw-r--r-- | src/client/views/nodes/FieldView.tsx | 6 | ||||
-rw-r--r-- | src/client/views/nodes/FormattedTextBox.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/ImageBox.tsx | 4 |
14 files changed, 298 insertions, 239 deletions
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index f4dcce7c8..93b927f8b 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -78,6 +78,8 @@ export namespace DragManager { dragElement.style.transformOrigin = "0 0"; dragElement.style.zIndex = "1000"; dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`; + dragElement.style.width = `${rect.width}px`; + dragElement.style.height = `${rect.height}px`; dragDiv.appendChild(dragElement); _lastPointerX = dragData["xOffset"] + rect.left; _lastPointerY = dragData["yOffset"] + rect.top; diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 0759ae110..1a711ae64 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -1,13 +1,13 @@ -import { CollectionFreeFormDocumentView } from "../views/nodes/CollectionFreeFormDocumentView"; import { observable, action } from "mobx"; +import { DocumentView } from "../views/nodes/DocumentView"; export namespace SelectionManager { class Manager { @observable - SelectedDocuments: Array<CollectionFreeFormDocumentView> = []; + SelectedDocuments: Array<DocumentView> = []; @action - SelectDoc(doc: CollectionFreeFormDocumentView, ctrlPressed: boolean): void { + SelectDoc(doc: DocumentView, ctrlPressed: boolean): void { // if doc is not in SelectedDocuments, add it if (!ctrlPressed) { manager.SelectedDocuments = []; @@ -21,11 +21,11 @@ export namespace SelectionManager { const manager = new Manager; - export function SelectDoc(doc: CollectionFreeFormDocumentView, ctrlPressed: boolean): void { + export function SelectDoc(doc: DocumentView, ctrlPressed: boolean): void { manager.SelectDoc(doc, ctrlPressed) } - export function IsSelected(doc: CollectionFreeFormDocumentView): boolean { + export function IsSelected(doc: DocumentView): boolean { return manager.SelectedDocuments.indexOf(doc) !== -1; } @@ -33,7 +33,7 @@ export namespace SelectionManager { manager.SelectedDocuments = [] } - export function SelectedDocuments(): Array<CollectionFreeFormDocumentView> { + export function SelectedDocuments(): Array<DocumentView> { return manager.SelectedDocuments; } }
\ No newline at end of file diff --git a/src/client/util/Transform.ts b/src/client/util/Transform.ts index 3edc8d569..8ae3f837f 100644 --- a/src/client/util/Transform.ts +++ b/src/client/util/Transform.ts @@ -23,29 +23,13 @@ export class Transform { return this; } - translated = (x: number, y: number): Transform => { - return this.copy().translate(x, y); - } - - preTranslate = (x: number, y: number): Transform => { - this._translateX += x * this._scale; - this._translateY += y * this._scale; - return this; - } - - preTranslated = (x: number, y: number): Transform => { - return this.copy().preTranslate(x, y); - } - scale = (scale: number): Transform => { this._scale *= scale; + this._translateX *= scale; + this._translateY *= scale; return this; } - scaled = (scale: number): Transform => { - return this.copy().scale(scale); - } - scaleAbout = (scale: number, x: number, y: number): Transform => { this._translateX += x * this._scale - x * this._scale * scale; this._translateY += y * this._scale - y * this._scale * scale; @@ -53,21 +37,6 @@ export class Transform { return this; } - scaledAbout = (scale: number, x: number, y: number): Transform => { - return this.copy().scaleAbout(scale, x, y); - } - - preScale = (scale: number): Transform => { - this._scale *= scale; - this._translateX *= scale; - this._translateY *= scale; - return this; - } - - preScaled = (scale: number): Transform => { - return this.copy().preScale(scale); - } - transform = (transform: Transform): Transform => { this._translateX += transform._translateX * this._scale; this._translateY += transform._translateY * this._scale; @@ -75,8 +44,15 @@ export class Transform { return this; } - transformed = (transform: Transform): Transform => { - return this.copy().transform(transform); + preTranslate = (x: number, y: number): Transform => { + this._translateX += this._scale * x; + this._translateY += this._scale * y; + return this; + } + + preScale = (scale: number): Transform => { + this._scale *= scale; + return this; } preTransform = (transform: Transform): Transform => { @@ -86,6 +62,30 @@ export class Transform { return this; } + translated = (x: number, y: number): Transform => { + return this.copy().translate(x, y); + } + + preTranslated = (x: number, y: number): Transform => { + return this.copy().preTranslate(x, y); + } + + scaled = (scale: number): Transform => { + return this.copy().scale(scale); + } + + scaledAbout = (scale: number, x: number, y: number): Transform => { + return this.copy().scaleAbout(scale, x, y); + } + + preScaled = (scale: number): Transform => { + return this.copy().preScale(scale); + } + + transformed = (transform: Transform): Transform => { + return this.copy().transform(transform); + } + preTransformed = (transform: Transform): Transform => { return this.copy().preTransform(transform); } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 85b44307f..5abe3f5b2 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -4,6 +4,8 @@ import { SelectionManager } from "../util/SelectionManager"; import { observer } from "mobx-react"; import './DocumentDecorations.scss' import { CollectionFreeFormView } from "./collections/CollectionFreeFormView"; +import { KeyStore } from "../../fields/Key"; +import { NumberField } from "../../fields/NumberField"; @observer export class DocumentDecorations extends React.Component { @@ -25,9 +27,12 @@ export class DocumentDecorations extends React.Component { !(element.props.ContainingCollectionView instanceof CollectionFreeFormView)) { return bounds; } - let transform = element.getTransform().inverse(); + let transform = element.props.GetTransform().inverse(); var [sptX, sptY] = transform.transformPoint(0, 0); - var [bptX, bptY] = transform.transformDirection(element.width, element.height); + // var [bptX, bptY] = transform.transformDirection(element.width, element.height); + let doc = element.props.Document; + let [bptX, bptY] = [doc.GetNumber(KeyStore.Width, 0), doc.GetNumber(KeyStore.Height, 0)]; + [bptX, bptY] = transform.transformPoint(bptX, bptY); return { x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y), r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b) @@ -106,16 +111,32 @@ export class DocumentDecorations extends React.Component { SelectionManager.SelectedDocuments().forEach(element => { const rect = element.screenRect; + // if (rect.width !== 0) { + // let scale = element.width / rect.width; + // let actualdW = Math.max(element.width + (dW * scale), 20); + // let actualdH = Math.max(element.height + (dH * scale), 20); + // element.x += dX * (actualdW - element.width); + // element.y += dY * (actualdH - element.height); + // if (Math.abs(dW) > Math.abs(dH)) + // element.width = actualdW; + // else + // element.height = actualdH; + // } if (rect.width !== 0) { - let scale = element.width / rect.width; - let actualdW = Math.max(element.width + (dW * scale), 20); - let actualdH = Math.max(element.height + (dH * scale), 20); - element.x += dX * (actualdW - element.width); - element.y += dY * (actualdH - element.height); + let doc = element.props.Document; + let width = doc.GetOrCreate(KeyStore.Width, NumberField); + let height = doc.GetOrCreate(KeyStore.Height, NumberField); + let x = doc.GetOrCreate(KeyStore.X, NumberField); + let y = doc.GetOrCreate(KeyStore.X, NumberField); + let scale = width.Data / rect.width; + let actualdW = Math.max(width.Data + (dW * scale), 20); + let actualdH = Math.max(height.Data + (dH * scale), 20); + x.Data += dX * (actualdW - width.Data); + y.Data += dY * (actualdH - height.Data); if (Math.abs(dW) > Math.abs(dH)) - element.width = actualdW; + width.Data = actualdW; else - element.height = actualdH; + height.Data = actualdH; } }) } diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 6ca0000b8..c7ad3e249 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -84,6 +84,7 @@ ReactDOM.render(( <DocumentView Document={mainContainer} AddDocument={undefined} RemoveDocument={undefined} GetTransform={() => Transform.Identity} Scaling={1} + isTopMost={true} ContainingCollectionView={undefined} DocumentView={undefined} /> <DocumentDecorations /> <ContextMenu /> diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index d3e90d11c..c870a9cf0 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -16,6 +16,7 @@ import "./CollectionDockingView.scss"; import { CollectionViewBase, CollectionViewProps, COLLECTION_BORDER_WIDTH } from "./CollectionViewBase"; import React = require("react"); import { changeDependenciesStateTo0 } from "mobx/lib/internal"; +import { Utils } from "../../../Utils"; @observer export class CollectionDockingView extends CollectionViewBase { @@ -100,6 +101,7 @@ export class CollectionDockingView extends CollectionViewBase { return (<DocumentView key={value[i].Id} Document={value[i]} AddDocument={this.addDocument} RemoveDocument={this.removeDocument} GetTransform={() => Transform.Identity} + isTopMost={true} Scaling={1} ContainingCollectionView={this} DocumentView={undefined} />); } @@ -307,12 +309,16 @@ class RenderClass { this.render(); } render() { - var nativeWidth = this._document.GetNumber(KeyStore.NativeWidth, 0); + let nativeWidth = this._document.GetNumber(KeyStore.NativeWidth, 0); let scaling = nativeWidth > 0 ? this._htmlElement!.clientWidth / nativeWidth : 1; ReactDOM.render(( <DocumentView key={this._document.Id} Document={this._document} AddDocument={this._collectionDockingView.addDocument} RemoveDocument={this._collectionDockingView.removeDocument} - GetTransform={() => Transform.Identity} + GetTransform={() => { + let { scale, translateX, translateY } = Utils.GetScreenTransform(this._htmlElement); + return this._collectionDockingView.props.GetTransform().scale(scale).translate(-translateX, -translateY) + }} + isTopMost={true} Scaling={scaling} ContainingCollectionView={this._collectionDockingView} DocumentView={undefined} /> ), diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx index 975eaaae8..7947b1c51 100644 --- a/src/client/views/collections/CollectionFreeFormView.tsx +++ b/src/client/views/collections/CollectionFreeFormView.tsx @@ -14,6 +14,7 @@ import { NumberField } from "../../../fields/NumberField"; import { Documents } from "../../documents/Documents"; import { FieldWaiting } from "../../../fields/Field"; import { Transform } from "../../util/Transform"; +import { DocumentView } from "../nodes/DocumentView"; @observer export class CollectionFreeFormView extends CollectionViewBase { @@ -45,23 +46,21 @@ export class CollectionFreeFormView extends CollectionViewBase { @action drop = (e: Event, de: DragManager.DropEvent) => { - const doc = de.data["document"]; + const doc: DocumentView = de.data["document"]; var me = this; - if (doc instanceof CollectionFreeFormDocumentView) { - if (doc.props.ContainingCollectionView && doc.props.ContainingCollectionView !== this) { - doc.props.ContainingCollectionView.removeDocument(doc.props.Document); - this.addDocument(doc.props.Document); - } - const xOffset = de.data["xOffset"] as number || 0; - const yOffset = de.data["yOffset"] as number || 0; - const { translateX, translateY } = Utils.GetScreenTransform(this._canvasRef.current!); - const currScale = this.resizeScaling * this.zoomScaling * this.props.ContainingDocumentView!.ScalingToScreenSpace; - const screenX = de.x - xOffset; - const screenY = de.y - yOffset; - doc.x = (screenX - translateX) / currScale; - doc.y = (screenY - translateY) / currScale; - this.bringToFront(doc); + if (doc.props.ContainingCollectionView && doc.props.ContainingCollectionView !== this) { + doc.props.ContainingCollectionView.removeDocument(doc.props.Document); + this.addDocument(doc.props.Document); } + const xOffset = de.data["xOffset"] as number || 0; + const yOffset = de.data["yOffset"] as number || 0; + const transform = doc.props.GetTransform(); + const screenX = de.x - xOffset; + const screenY = de.y - yOffset; + const [x, y] = transform.transformPoint(screenX, screenY); + doc.props.Document.SetNumber(KeyStore.X, x); + doc.props.Document.SetNumber(KeyStore.Y, y); + this.bringToFront(doc); e.stopPropagation(); } @@ -94,8 +93,8 @@ export class CollectionFreeFormView extends CollectionViewBase { document.removeEventListener("pointerup", this.onPointerUp); e.stopPropagation(); if (Math.abs(this._downX - e.clientX) < 3 && Math.abs(this._downY - e.clientY) < 3) { - if (!SelectionManager.IsSelected(this.props.ContainingDocumentView as CollectionFreeFormDocumentView)) { - SelectionManager.SelectDoc(this.props.ContainingDocumentView as CollectionFreeFormDocumentView, false); + if (!this.props.isSelected()) { + this.props.select(false); } } } @@ -105,11 +104,11 @@ export class CollectionFreeFormView extends CollectionViewBase { if (!e.cancelBubble && this.active) { e.preventDefault(); e.stopPropagation(); - let currScale: number = this.props.ContainingDocumentView!.ScalingToScreenSpace; let x = this.props.DocumentForCollection.GetNumber(KeyStore.PanX, 0); let y = this.props.DocumentForCollection.GetNumber(KeyStore.PanY, 0); + let [dx, dy] = this.props.GetTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY); - this.SetPan(x + (e.pageX - this._lastX) / currScale, y + (e.pageY - this._lastY) / currScale); + this.SetPan(x + dx, y + dy); } this._lastX = e.pageX; this._lastY = e.pageY; @@ -124,15 +123,14 @@ export class CollectionFreeFormView extends CollectionViewBase { // if (modes[e.deltaMode] == 'pixels') coefficient = 50; // else if (modes[e.deltaMode] == 'lines') coefficient = 1000; // This should correspond to line-height?? let transform = this.getTransform(); - let localTransform = this.getLocalTransform(); - let deltaScale = (1 - (e.deltaY / coefficient)) * transform.Scale; - let newDeltaScale = this.isAnnotationOverlay ? Math.max(1, deltaScale) : deltaScale; + let deltaScale = (1 - (e.deltaY / coefficient)); let [x, y] = transform.transformPoint(e.clientX, e.clientY); - localTransform.scaleAbout(newDeltaScale, x, y); + let localTransform = this.getLocalTransform(); + localTransform.scaleAbout(deltaScale, x, y); - this.props.DocumentForCollection.SetNumber(KeyStore.Scale, newDeltaScale); + this.props.DocumentForCollection.SetNumber(KeyStore.Scale, localTransform.Scale); this.SetPan(localTransform.TranslateX, localTransform.TranslateY); } @@ -182,7 +180,7 @@ export class CollectionFreeFormView extends CollectionViewBase { } @action - bringToFront(doc: CollectionFreeFormDocumentView) { + bringToFront(doc: DocumentView) { const { CollectionFieldKey: fieldKey, DocumentForCollection: Document } = this.props; const value: Document[] = Document.GetList<Document>(fieldKey, []); @@ -213,12 +211,12 @@ export class CollectionFreeFormView extends CollectionViewBase { getTransform = (): Transform => { const [x, y] = this.translate; - return this.props.GetTransform().scaled(this.scale).translate(x, y); + return this.props.GetTransform().translate(-COLLECTION_BORDER_WIDTH, -COLLECTION_BORDER_WIDTH).transform(this.getLocalTransform().inverse()) } getLocalTransform = (): Transform => { const [x, y] = this.translate; - return new Transform(x, y, this.scale); + return Transform.Identity.scale(this.scale).translate(x, y); } render() { @@ -249,8 +247,9 @@ export class CollectionFreeFormView extends CollectionViewBase { AddDocument={this.addDocument} RemoveDocument={this.removeDocument} GetTransform={this.getTransform} + isTopMost={false} Scaling={1} - ContainingCollectionView={this} DocumentView={undefined} />); + ContainingCollectionView={this} DocumentView={this.props.ContainingDocumentView} />); })} </div> </div> diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index f1e882e20..adae8560e 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -27,7 +27,8 @@ export class CollectionSchemaView extends CollectionViewBase { let props: FieldViewProps = { doc: rowProps.value[0], fieldKey: rowProps.value[1], - DocumentViewForField: undefined, + isSelected: () => false, + isTopMost: false } let contents = ( <FieldView {...props} /> @@ -110,6 +111,7 @@ export class CollectionSchemaView extends CollectionViewBase { AddDocument={this.addDocument} RemoveDocument={this.removeDocument} GetTransform={() => Transform.Identity}//TODO This should probably be an actual transform Scaling={1} + isTopMost={false} DocumentView={undefined} ContainingCollectionView={this} /> ) } else { diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx index cf5bc70c4..2c950c8ae 100644 --- a/src/client/views/collections/CollectionViewBase.tsx +++ b/src/client/views/collections/CollectionViewBase.tsx @@ -18,6 +18,9 @@ export interface CollectionViewProps { DocumentForCollection: Document; ContainingDocumentView: Opt<DocumentView>; GetTransform: () => Transform; + isSelected: () => boolean; + isTopMost: boolean; + select: (ctrlPressed: boolean) => void; BackgroundView: Opt<DocumentView>; Scaling: number; } @@ -29,16 +32,15 @@ export class CollectionViewBase extends React.Component<CollectionViewProps> { public static LayoutString(collectionType: string, fieldKey: string = "DataKey") { return `<${collectionType} Scaling={Scaling} DocumentForCollection={Document} - GetTransform={GetTransform} CollectionFieldKey={${fieldKey}} + GetTransform={GetTransform} CollectionFieldKey={${fieldKey}} isSelected={isSelected} select={select} + isTopMost={isTopMost} ContainingDocumentView={DocumentView} BackgroundView={BackgroundView} />`; } @computed public get active(): boolean { var isSelected = (this.props.ContainingDocumentView instanceof CollectionFreeFormDocumentView && SelectionManager.IsSelected(this.props.ContainingDocumentView)); var childSelected = SelectionManager.SelectedDocuments().some(view => view.props.ContainingCollectionView == this); - var topMost = this.props.ContainingDocumentView != undefined && ( - this.props.ContainingDocumentView.props.ContainingCollectionView == undefined || - this.props.ContainingDocumentView.props.ContainingCollectionView instanceof CollectionDockingView); + var topMost = this.props.isTopMost; return isSelected || childSelected || topMost; } @action diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 9cd42a069..54bc7327b 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -14,11 +14,8 @@ import { Transform } from "../../util/Transform"; @observer -export class CollectionFreeFormDocumentView extends DocumentView { - private _contextMenuCanOpen = false; - private _downX: number = 0; - private _downY: number = 0; - // private _mainCont = React.createRef<HTMLDivElement>(); +export class CollectionFreeFormDocumentView extends React.Component<DocumentViewProps> { + private _mainCont = React.createRef<HTMLDivElement>(); constructor(props: DocumentViewProps) { super(props); @@ -95,130 +92,6 @@ export class CollectionFreeFormDocumentView extends DocumentView { this.props.Document.SetData(KeyStore.ZIndex, h, NumberField) } - @action - dragComplete = (e: DragManager.DragCompleteEvent) => { - } - - @computed - get active(): boolean { - return SelectionManager.IsSelected(this) || this.props.ContainingCollectionView === undefined || - this.props.ContainingCollectionView.active; - } - - @computed - get topMost(): boolean { - return this.props.ContainingCollectionView == undefined || this.props.ContainingCollectionView instanceof CollectionDockingView; - } - - onPointerDown = (e: React.PointerEvent): void => { - this._downX = e.clientX; - this._downY = e.clientY; - var me = this; - if (e.shiftKey && e.buttons === 1) { - CollectionDockingView.StartOtherDrag(this._mainCont.current!, this.props.Document); - e.stopPropagation(); - return; - } - this._contextMenuCanOpen = e.button == 2; - if (this.active && !e.isDefaultPrevented()) { - e.stopPropagation(); - if (e.buttons === 2) { - e.preventDefault(); - } - document.removeEventListener("pointermove", this.onPointerMove) - document.addEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp) - document.addEventListener("pointerup", this.onPointerUp); - } - } - - onPointerMove = (e: PointerEvent): void => { - if (e.cancelBubble) { - this._contextMenuCanOpen = false; - return; - } - if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) { - this._contextMenuCanOpen = false; - if (this._mainCont.current != null && !this.topMost) { - this._contextMenuCanOpen = false; - const rect = this.screenRect; - let dragData: { [id: string]: any } = {}; - dragData["document"] = this; - dragData["xOffset"] = e.x - rect.left; - dragData["yOffset"] = e.y - rect.top; - DragManager.StartDrag(this._mainCont.current, dragData, { - handlers: { - dragComplete: this.dragComplete, - }, - hideSource: true - }) - } - } - e.stopPropagation(); - e.preventDefault(); - } - - onPointerUp = (e: PointerEvent): void => { - document.removeEventListener("pointermove", this.onPointerMove) - document.removeEventListener("pointerup", this.onPointerUp) - e.stopPropagation(); - if (Math.abs(e.clientX - this._downX) < 4 && Math.abs(e.clientY - this._downY) < 4) { - SelectionManager.SelectDoc(this, e.ctrlKey); - } - } - - openRight = (e: React.MouseEvent): void => { - CollectionDockingView.AddRightSplit(this.props.Document); - } - - deleteClicked = (e: React.MouseEvent): void => { - if (this.props.ContainingCollectionView instanceof CollectionFreeFormView) { - this.props.ContainingCollectionView.removeDocument(this.props.Document) - } - } - @action - fullScreenClicked = (e: React.MouseEvent): void => { - CollectionDockingView.OpenFullScreen(this.props.Document); - ContextMenu.Instance.clearItems(); - ContextMenu.Instance.addItem({ description: "Close Full Screen", event: this.closeFullScreenClicked }); - ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15) - } - @action - closeFullScreenClicked = (e: React.MouseEvent): void => { - CollectionDockingView.CloseFullScreen(); - ContextMenu.Instance.clearItems(); - ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked }) - ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15) - } - - @action - onContextMenu = (e: React.MouseEvent): void => { - if (!SelectionManager.IsSelected(this)) { - return; - } - e.preventDefault() - - if (!this._contextMenuCanOpen) { - return; - } - - if (this.topMost) { - ContextMenu.Instance.clearItems() - ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked }) - ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15) - } - else { - // DocumentViews should stop propogation of this event - e.stopPropagation(); - - ContextMenu.Instance.clearItems(); - ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked }) - ContextMenu.Instance.addItem({ description: "Open Right", event: this.openRight }) - ContextMenu.Instance.addItem({ description: "Delete", event: this.deleteClicked }) - ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15) - SelectionManager.SelectDoc(this, e.ctrlKey); - } - } getTransform = (): Transform => { return this.props.GetTransform().translated(this.x, this.y); @@ -234,11 +107,10 @@ export class CollectionFreeFormDocumentView extends DocumentView { height: freestyling ? this.height : "100%", position: freestyling ? "absolute" : "relative", zIndex: freestyling ? this.zIndex : 0, - }} - onContextMenu={this.onContextMenu} - onPointerDown={this.onPointerDown}> + backgroundColor: "transparent" + }} > - <DocumentView {...this.props} Scaling={this.width / this.nativeWidth} GetTransform={this.getTransform} DocumentView={this} /> + <DocumentView {...this.props} Scaling={this.width / this.nativeWidth} GetTransform={this.getTransform} /> </div> ); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index d7ecc6d9d..cb080c08e 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -16,6 +16,9 @@ import { ImageBox } from "../nodes/ImageBox"; import "./NodeView.scss"; import React = require("react"); import { Transform } from "../../util/Transform"; +import { SelectionManager } from "../../util/SelectionManager"; +import { DragManager } from "../../util/DragManager"; +import { ContextMenu } from "../ContextMenu"; const JsxParser = require('react-jsx-parser').default;//TODO Why does this need to be imported like this? export interface DocumentViewProps { @@ -26,24 +29,34 @@ export interface DocumentViewProps { AddDocument?: (doc: Document) => void; RemoveDocument?: (doc: Document) => boolean; GetTransform: () => Transform; + isTopMost: boolean; Scaling: number; } @observer export class DocumentView extends React.Component<DocumentViewProps> { - protected _mainCont = React.createRef<any>(); + private _mainCont = React.createRef<HTMLDivElement>(); get MainContent() { return this._mainCont; } + get screenRect(): ClientRect | DOMRect { + if (this._mainCont.current) { + return this._mainCont.current.getBoundingClientRect(); + } + return new DOMRect(); + } @computed get layout(): string { return this.props.Document.GetData(KeyStore.Layout, TextField, String("<p>Error loading layout data</p>")); } @computed - get backgroundLayout(): string { - return this.props.Document.GetData(KeyStore.BackgroundLayout, TextField, String("<p>Error loading layout data</p>")); + get backgroundLayout(): string | undefined { + let field = this.props.Document.GetT(KeyStore.BackgroundLayout, TextField); + if (field && field !== "<Waiting>") { + return field.Data; + } } @computed @@ -56,6 +69,134 @@ export class DocumentView extends React.Component<DocumentViewProps> { return this.props.Document.GetData(KeyStore.LayoutFields, ListField, new Array<Key>()); } + @computed + get active(): boolean { + return SelectionManager.IsSelected(this) || this.props.ContainingCollectionView === undefined || + this.props.ContainingCollectionView.active; + } + + private _contextMenuCanOpen = false; + private _downX: number = 0; + private _downY: number = 0; + onPointerDown = (e: React.PointerEvent): void => { + this._downX = e.clientX; + this._downY = e.clientY; + var me = this; + if (e.shiftKey && e.buttons === 1) { + CollectionDockingView.StartOtherDrag(this._mainCont.current!, this.props.Document); + e.stopPropagation(); + return; + } + this._contextMenuCanOpen = e.button == 2; + if (this.active && !e.isDefaultPrevented()) { + e.stopPropagation(); + if (e.buttons === 2) { + e.preventDefault(); + } + document.removeEventListener("pointermove", this.onPointerMove) + document.addEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp) + document.addEventListener("pointerup", this.onPointerUp); + } + } + + @action + dragComplete = (e: DragManager.DragCompleteEvent) => { + } + + @computed + get topMost(): boolean { + return this.props.ContainingCollectionView == undefined || this.props.ContainingCollectionView instanceof CollectionDockingView; + } + + onPointerMove = (e: PointerEvent): void => { + if (e.cancelBubble) { + this._contextMenuCanOpen = false; + return; + } + if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) { + this._contextMenuCanOpen = false; + if (this._mainCont.current != null && !this.topMost) { + this._contextMenuCanOpen = false; + const [left, top] = this.props.GetTransform().inverse().transformPoint(0, 0); + let dragData: { [id: string]: any } = {}; + dragData["document"] = this; + dragData["xOffset"] = e.x - left; + dragData["yOffset"] = e.y - top; + DragManager.StartDrag(this._mainCont.current, dragData, { + handlers: { + dragComplete: this.dragComplete, + }, + hideSource: true + }) + } + } + e.stopPropagation(); + e.preventDefault(); + } + + onPointerUp = (e: PointerEvent): void => { + document.removeEventListener("pointermove", this.onPointerMove) + document.removeEventListener("pointerup", this.onPointerUp) + e.stopPropagation(); + if (Math.abs(e.clientX - this._downX) < 4 && Math.abs(e.clientY - this._downY) < 4) { + SelectionManager.SelectDoc(this, e.ctrlKey); + } + } + + openRight = (e: React.MouseEvent): void => { + CollectionDockingView.AddRightSplit(this.props.Document); + } + + deleteClicked = (e: React.MouseEvent): void => { + if (this.props.RemoveDocument) { + this.props.RemoveDocument(this.props.Document); + } + } + @action + fullScreenClicked = (e: React.MouseEvent): void => { + CollectionDockingView.OpenFullScreen(this.props.Document); + ContextMenu.Instance.clearItems(); + ContextMenu.Instance.addItem({ description: "Close Full Screen", event: this.closeFullScreenClicked }); + ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15) + } + @action + closeFullScreenClicked = (e: React.MouseEvent): void => { + CollectionDockingView.CloseFullScreen(); + ContextMenu.Instance.clearItems(); + ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked }) + ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15) + } + + @action + onContextMenu = (e: React.MouseEvent): void => { + if (!SelectionManager.IsSelected(this)) { + return; + } + e.preventDefault() + + if (!this._contextMenuCanOpen) { + return; + } + + if (this.topMost) { + ContextMenu.Instance.clearItems() + ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked }) + ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15) + } + else { + // DocumentViews should stop propogation of this event + e.stopPropagation(); + + ContextMenu.Instance.clearItems(); + ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked }) + ContextMenu.Instance.addItem({ description: "Open Right", event: this.openRight }) + ContextMenu.Instance.addItem({ description: "Delete", event: this.deleteClicked }) + ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15) + SelectionManager.SelectDoc(this, e.ctrlKey); + } + } + // // returns the cumulative scaling between the document and the screen // @@ -69,9 +210,19 @@ export class DocumentView extends React.Component<DocumentViewProps> { return 1; } + isSelected = () => { + return SelectionManager.IsSelected(this); + } + + select = (ctrlPressed: boolean) => { + SelectionManager.SelectDoc(this, ctrlPressed) + } + render() { let bindings = { ...this.props } as any; + bindings.isSelected = this.isSelected; + bindings.select = this.select; for (const key of this.layoutKeys) { bindings[key.Name + "Key"] = key; // this maps string values of the form <keyname>Key to an actual key Kestore.keyname e.g, "DataKey" => KeyStore.Data } @@ -82,26 +233,29 @@ export class DocumentView extends React.Component<DocumentViewProps> { let field = this.props.Document.Get(key); bindings[key.Name] = field && field != FieldWaiting ? field.GetValue() : field; } - if (bindings.DocumentView === undefined) { - bindings.DocumentView = this; // set the DocumentView to this if it hasn't already been set by a sub-class during its render method. + let annotated = null; + let backgroundLayout = this.backgroundLayout; + if (backgroundLayout) { + annotated = <JsxParser + components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView }} + bindings={bindings} + jsx={this.backgroundLayout} + showWarnings={true} + onError={(test: any) => { console.log(test) }} + />; } - var annotated = <JsxParser - components={{ FormattedTextBox: FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView }} - bindings={bindings} - jsx={this.backgroundLayout} - showWarnings={true} - onError={(test: any) => { console.log(test) }} - />; - bindings["BackgroundView"] = this.backgroundLayout ? annotated : null; + bindings.BackgroundView = annotated; var width = this.props.Document.GetNumber(KeyStore.NativeWidth, 0); var strwidth = width > 0 ? width.toString() + "px" : "100%"; var height = this.props.Document.GetNumber(KeyStore.NativeHeight, 0); var strheight = height > 0 ? height.toString() + "px" : "100%"; return ( - <div className="node" ref={this._mainCont} style={{ width: strwidth, height: strheight, transformOrigin: "left top", transform: `scale(${this.props.Scaling},${this.props.Scaling})` }}> + <div className="node" ref={this._mainCont} style={{ width: strwidth, height: strheight, transformOrigin: "left top", transform: `scale(${this.props.Scaling},${this.props.Scaling})` }} + onContextMenu={this.onContextMenu} + onPointerDown={this.onPointerDown} > <JsxParser - components={{ FormattedTextBox: FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView }} + components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView }} bindings={bindings} jsx={this.layout} showWarnings={true} diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 12371eb2e..08de53e1c 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -10,7 +10,6 @@ import { ImageField } from "../../../fields/ImageField"; import { Key } from "../../../fields/Key"; import { FormattedTextBox } from "./FormattedTextBox"; import { ImageBox } from "./ImageBox"; -import { DocumentView } from "./DocumentView"; // // these properties get assigned through the render() method of the DocumentView when it creates this node. @@ -20,12 +19,13 @@ import { DocumentView } from "./DocumentView"; export interface FieldViewProps { fieldKey: Key; doc: Document; - DocumentViewForField: Opt<DocumentView> + isSelected: () => boolean; + isTopMost: boolean; } @observer export class FieldView extends React.Component<FieldViewProps> { - public static LayoutString(fieldType: string) { return `<${fieldType} doc={Document} DocumentViewForField={DocumentView} fieldKey={DataKey} />`; } + public static LayoutString(fieldType: string) { return `<${fieldType} doc={Document} DocumentViewForField={DocumentView} fieldKey={DataKey} isSelected={isSelected} isTopMost={isTopMost} />`; } @computed get field(): FieldValue<Field> { const { doc, fieldKey } = this.props; diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index f6bd0c0f8..2e3d396c1 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -110,7 +110,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> { } onPointerDown = (e: React.PointerEvent): void => { let me = this; - if (e.buttons === 1 && me.props.DocumentViewForField instanceof CollectionFreeFormDocumentView && SelectionManager.IsSelected(me.props.DocumentViewForField)) { + if (e.buttons === 1 && this.props.isSelected()) { e.stopPropagation(); } } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 2fa70734d..e1fa26e2f 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -41,7 +41,7 @@ export class ImageBox extends React.Component<FieldViewProps> { onPointerDown = (e: React.PointerEvent): void => { if (Date.now() - this._lastTap < 300) { - if (e.buttons === 1 && this.props.DocumentViewForField instanceof CollectionFreeFormDocumentView && SelectionManager.IsSelected(this.props.DocumentViewForField)) { + if (e.buttons === 1 && this.props.isSelected()) { e.stopPropagation(); this._downX = e.clientX; this._downY = e.clientY; @@ -63,7 +63,7 @@ export class ImageBox extends React.Component<FieldViewProps> { lightbox = (path: string) => { const images = [path, "http://www.cs.brown.edu/~bcz/face.gif"]; - if (this._isOpen && this.props.DocumentViewForField instanceof CollectionFreeFormDocumentView && SelectionManager.IsSelected(this.props.DocumentViewForField)) { + if (this._isOpen && this.props.isSelected()) { return (<Lightbox mainSrc={images[this._photoIndex]} nextSrc={images[(this._photoIndex + 1) % images.length]} |