aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/DocumentView.tsx
diff options
context:
space:
mode:
authortschicke-brown <tyler_schicke@brown.edu>2019-02-18 23:02:26 -0500
committerGitHub <noreply@github.com>2019-02-18 23:02:26 -0500
commit70f0ba1275fa879e86d05c1aa3bb5b1567e3d04c (patch)
tree7744b7432aa9e65604f18a6a4db9d10c32f30480 /src/client/views/nodes/DocumentView.tsx
parent3f98d6ec6050e7faa15179871f0d9669c1188a78 (diff)
parent842f571bfb4952d12804a8dbdc66aedbf2bf6b81 (diff)
Merge pull request #6 from browngraphicslab/transforms
Transforms
Diffstat (limited to 'src/client/views/nodes/DocumentView.tsx')
-rw-r--r--src/client/views/nodes/DocumentView.tsx267
1 files changed, 170 insertions, 97 deletions
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index c5270e0cd..f368fdeaf 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -14,6 +14,10 @@ import { ImageBox } from "../nodes/ImageBox";
import "./DocumentView.scss";
import React = require("react");
import { Transform } from "../../util/Transform";
+import { SelectionManager } from "../../util/SelectionManager";
+import { DragManager } from "../../util/DragManager";
+import { ContextMenu } from "../ContextMenu";
+import { TextField } from "../../../fields/TextField";
const JsxParser = require('react-jsx-parser').default;//TODO Why does this need to be imported like this?
export interface DocumentViewProps {
@@ -23,32 +27,35 @@ export interface DocumentViewProps {
Document: Document;
AddDocument?: (doc: Document) => void;
RemoveDocument?: (doc: Document) => boolean;
- GetTransform: () => Transform;
- ParentScaling: number;
+ ScreenToLocalTransform: () => Transform;
+ isTopMost: boolean;
+ Scaling: number;
}
@observer
export class DocumentView extends React.Component<DocumentViewProps> {
- protected _renderDoc = React.createRef<any>();
- protected _mainCont = React.createRef<any>();
+ private _mainCont = React.createRef<HTMLDivElement>();
get MainContent() {
return this._mainCont;
}
-
- @computed
- get parentScaling(): number {
- return this._renderDoc.current ? this._renderDoc.current.props.ParentScaling : this.props.ParentScaling > 0 ? this.props.ParentScaling : 1;
+ get screenRect(): ClientRect | DOMRect {
+ if (this._mainCont.current) {
+ return this._mainCont.current.getBoundingClientRect();
+ }
+ return new DOMRect();
}
-
@computed
get layout(): string {
return this.props.Document.GetText(KeyStore.Layout, "<p>Error loading layout data</p>");
}
@computed
- get backgroundLayout(): string {
- return this.props.Document.GetText(KeyStore.BackgroundLayout, "");
+ get backgroundLayout(): string | undefined {
+ let field = this.props.Document.GetT(KeyStore.BackgroundLayout, TextField);
+ if (field && field !== "<Waiting>") {
+ return field.Data;
+ }
}
@computed
@@ -61,93 +68,159 @@ export class DocumentView extends React.Component<DocumentViewProps> {
return this.props.Document.GetData(KeyStore.LayoutFields, ListField, new Array<Key>());
}
- //
- // returns the cumulative scaling between the document and the screen
- //
@computed
- public get ScalingToScreenSpace(): number {
- if (this.props.ContainingCollectionView != undefined &&
- this.props.ContainingCollectionView.props.ContainingDocumentView != undefined) {
- let ss = this.props.ContainingCollectionView.props.DocumentForCollection.GetNumber(KeyStore.Scale, 1);
- return this.props.ContainingCollectionView.props.ContainingDocumentView.ScalingToScreenSpace * ss;
+ 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);
}
- return 1;
}
+ @action
+ dragComplete = (e: DragManager.DragCompleteEvent) => {
+ }
+
+ @computed
+ get topMost(): boolean {
+ return this.props.ContainingCollectionView == undefined || this.props.ContainingCollectionView instanceof CollectionDockingView;
+ }
- public LeftCorner(): number {
- if (this.props.ContainingCollectionView) {
- if (this.props.ContainingCollectionView instanceof CollectionDockingView) {
- // this is a hacky way to account for the titles/pane placement/etc of a CollectionDockingView
- // this only works if the collectionDockingView is the root collection, too.
- // need to find a better way.
- var { translateX: rx, translateY: ry } = Utils.GetScreenTransform(this.MainContent.current!);
- return rx + COLLECTION_BORDER_WIDTH;
+ 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.ScreenToLocalTransform().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
+ })
}
- return COLLECTION_BORDER_WIDTH; // assumes all collections have the same border
}
- return 0;
+ e.stopPropagation();
+ e.preventDefault();
}
- public TopCorner(): number {
- if (this.props.ContainingCollectionView) {
- if (this.props.ContainingCollectionView instanceof CollectionDockingView) {
- // this is a hacky way to account for the titles/pane placement/etc of a CollectionDockingView
- // this only works if the collectionDockingView is the root collection, too.
- // need to find a better way.
- var { translateX: rx, translateY: ry } = Utils.GetScreenTransform(this.MainContent.current!);
- return ry + COLLECTION_BORDER_WIDTH;
- }
- return COLLECTION_BORDER_WIDTH; // assumes all collections have the same border
+ 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);
}
- return 0;
}
- //
- // Converts a coordinate in the screen space of the app to a local point in the space of the DocumentView.
- // This also returns the point in the coordinate space of this document's containing CollectionView
- //
- public TransformToLocalPoint(screenX: number, screenY: number) {
- // if this collection view is nested within another collection view, then
- // first transform the screen point into the parent collection's coordinate space.
- let containingCollectionViewDoc = this.props.ContainingCollectionView ? this.props.ContainingCollectionView.props.ContainingDocumentView : undefined;
- let { LocalX: parentX, LocalY: parentY } = !containingCollectionViewDoc ? { LocalX: screenX, LocalY: screenY } :
- containingCollectionViewDoc.TransformToLocalPoint(screenX, screenY);
- let ContainerX: number = parentX - COLLECTION_BORDER_WIDTH;
- let ContainerY: number = parentY - COLLECTION_BORDER_WIDTH;
- let Ss = this.props.Document.GetNumber(KeyStore.Scale, 1);
- let Panxx = this.props.Document.GetNumber(KeyStore.PanX, 0);
- let Panyy = this.props.Document.GetNumber(KeyStore.PanY, 0);
- let LocalX = (ContainerX - (this.LeftCorner() + Panxx)) / Ss;
- let LocalY = (ContainerY - (this.TopCorner() + Panyy)) / Ss;
+ 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();
- return { LocalX, LocalY, ContainerX, ContainerY };
+ 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
//
- // Converts a point in the coordinate space of the document to coordinate in app screen coordinates
- //
- public TransformToScreenPoint(localX: number, localY: number, applyViewXf: boolean = false): { ScreenX: number, ScreenY: number } {
- var parentScaling = applyViewXf ? this.parentScaling : 1;
- let Panxx = applyViewXf ? this.props.Document.GetNumber(KeyStore.PanX, 0) : 0;
- let Panyy = applyViewXf ? this.props.Document.GetNumber(KeyStore.PanY, 0) : 0;
- var Zoom = applyViewXf ? this.props.Document.GetNumber(KeyStore.Scale, 1) : 1;
-
- let parentX = this.LeftCorner() + (Panxx + (localX - COLLECTION_BORDER_WIDTH) * Zoom) * parentScaling;
- let parentY = this.TopCorner() + (Panyy + (localY - COLLECTION_BORDER_WIDTH) * Zoom) * parentScaling;
- // if this collection view is nested within another collection view, then
- // first transform the local point into the parent collection's coordinate space.
- let containingDocView = this.props.ContainingCollectionView ? this.props.ContainingCollectionView.props.ContainingDocumentView : undefined;
- if (containingDocView) {
- let { ScreenX, ScreenY } = containingDocView.TransformToScreenPoint(parentX + COLLECTION_BORDER_WIDTH * parentScaling, parentY + COLLECTION_BORDER_WIDTH * parentScaling, true);
- parentX = ScreenX;
- parentY = ScreenY;
+ @computed
+ public get ScalingToScreenSpace(): number {
+ if (this.props.ContainingCollectionView != undefined &&
+ this.props.ContainingCollectionView.props.ContainingDocumentView != undefined) {
+ let ss = this.props.ContainingCollectionView.props.DocumentForCollection.GetNumber(KeyStore.Scale, 1);
+ return this.props.ContainingCollectionView.props.ContainingDocumentView.ScalingToScreenSpace * ss;
}
- return { ScreenX: parentX, ScreenY: parentY };
+ 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
}
@@ -155,33 +228,33 @@ 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.
- }
- if (this.backgroundLayout) {
- var backgroundview = <JsxParser
- components={{ FormattedTextBox: FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView }}
+ /*
+ Should this be moved to CollectionFreeformView or another component that renders
+ Document backgrounds (or contents based on a layout key, which could be used here as well)
+ that CollectionFreeformView uses? It seems like a lot for it to be here considering only one view currently uses it...
+ */
+ let backgroundLayout = this.backgroundLayout;
+ if (backgroundLayout) {
+ let backgroundView = () => (<JsxParser
+ components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView }}
bindings={bindings}
jsx={this.backgroundLayout}
showWarnings={true}
onError={(test: any) => { console.log(test) }}
- />;
- bindings["BackgroundView"] = backgroundview;
+ />);
+ bindings.BackgroundView = backgroundView;
}
- var nativewidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 0);
- var nativeheight = this.props.Document.GetNumber(KeyStore.NativeHeight, 0);
- var width = nativewidth > 0 ? nativewidth + "px" : "100%";
- var height = nativeheight > 0 ? nativeheight + "px" : "100%";
+
+ 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="documentView-node" ref={this._mainCont}
- style={{
- width: width,
- height: height,
- transformOrigin: "top left",
- transform: `scale(${this.props.ParentScaling},${this.props.ParentScaling})`
- }}>
+ <div className="documentView-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}