aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/util/SelectionManager.ts30
-rw-r--r--src/client/views/DocumentDecorations.tsx57
-rw-r--r--src/client/views/InkSelectDecorations.scss5
-rw-r--r--src/client/views/InkSelectDecorations.tsx59
-rw-r--r--src/client/views/MainView.tsx2
-rw-r--r--src/client/views/Touchable.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx5
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx18
8 files changed, 152 insertions, 26 deletions
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 2d717ca57..3ae43e029 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -11,6 +11,7 @@ export namespace SelectionManager {
@observable IsDragging: boolean = false;
@observable SelectedDocuments: Array<DocumentView> = [];
+ @observable SelectedInk: Array<Map<any, any>> = [];
@action
@@ -41,6 +42,20 @@ export namespace SelectionManager {
DeselectAll(): void {
manager.SelectedDocuments.map(dv => dv.props.whenActiveChanged(false));
manager.SelectedDocuments = [];
+ manager.SelectedInk = [];
+ }
+
+ @action
+ SelectInk(ink: Map<any, any>, ctrlPressed: boolean): void {
+ if (manager.SelectedInk.indexOf(ink) === -1) {
+ if (!ctrlPressed) {
+ this.DeselectAll();
+ }
+
+ manager.SelectedInk.push(ink);
+ } else if (!ctrlPressed && manager.SelectedDocuments.length > 1) {
+ manager.SelectedInk = [ink];
+ }
}
}
@@ -53,6 +68,10 @@ export namespace SelectionManager {
manager.SelectDoc(docView, ctrlPressed);
}
+ export function SelectInk(ink: Map<any, any>, ctrlPressed: boolean): void {
+ manager.SelectInk(ink, ctrlPressed);
+ }
+
export function IsSelected(doc: DocumentView): boolean {
return manager.SelectedDocuments.indexOf(doc) !== -1;
}
@@ -75,4 +94,15 @@ export namespace SelectionManager {
export function SelectedDocuments(): Array<DocumentView> {
return manager.SelectedDocuments.slice();
}
+
+ export function SelectedInk(): Array<Map<any, any>> {
+ return manager.SelectedInk.slice();
+ }
+
+ export function AllSelected(): Array<DocumentView | Map<any, any>> {
+ let arr: Array<DocumentView | Map<any, any>> = [];
+ arr = SelectionManager.SelectedDocuments();
+ arr.push(...SelectionManager.SelectedInk());
+ return arr;
+ }
}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 3d73f048d..90d6e1e8d 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -25,6 +25,8 @@ import { FormattedTextBox } from "./nodes/FormattedTextBox";
import { IconBox } from "./nodes/IconBox";
import React = require("react");
import { TooltipTextMenu } from '../util/TooltipTextMenu';
+import { InkingCanvas } from './InkingCanvas';
+import { StrokeData } from '../../new_fields/InkField';
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -166,23 +168,42 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
@computed
get Bounds(): { x: number, y: number, b: number, r: number } {
let x = this._forceUpdate;
- this._lastBox = SelectionManager.SelectedDocuments().reduce((bounds, documentView) => {
- if (documentView.props.renderDepth === 0 ||
- Doc.AreProtosEqual(documentView.props.Document, CurrentUserUtils.UserDocument)) {
- return bounds;
+ this._lastBox = SelectionManager.AllSelected().reduce((bounds, docViewOrInk) => {
+ if (docViewOrInk instanceof DocumentView) {
+ if (docViewOrInk.props.renderDepth === 0 ||
+ Doc.AreProtosEqual(docViewOrInk.props.Document, CurrentUserUtils.UserDocument)) {
+ return bounds;
+ }
+ let transform = (docViewOrInk.props.ScreenToLocalTransform().scale(docViewOrInk.props.ContentScaling())).inverse();
+ if (transform.TranslateX === 0 && transform.TranslateY === 0) {
+ setTimeout(action(() => this._forceUpdate++), 0); // bcz: fix CollectionStackingView's getTransform() somehow...without this, resizing things in the library view, for instance, show the wrong bounds
+ return this._lastBox;
+ }
+
+ var [sptX, sptY] = transform.transformPoint(0, 0);
+ let [bptX, bptY] = transform.transformPoint(docViewOrInk.props.PanelWidth(), docViewOrInk.props.PanelHeight());
+ 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)
+ };
}
- let transform = (documentView.props.ScreenToLocalTransform().scale(documentView.props.ContentScaling())).inverse();
- if (transform.TranslateX === 0 && transform.TranslateY === 0) {
- setTimeout(action(() => this._forceUpdate++), 0); // bcz: fix CollectionStackingView's getTransform() somehow...without this, resizing things in the library view, for instance, show the wrong bounds
- return this._lastBox;
+ else {
+ let left = bounds.x;
+ let top = bounds.y;
+ let right = bounds.r;
+ let bottom = bounds.b;
+ docViewOrInk.forEach((value: StrokeData, key: string) => {
+ value.pathData.map(val => {
+ left = Math.min(val.x, left);
+ top = Math.min(val.y, top);
+ right = Math.max(val.x, right);
+ bottom = Math.max(val.y, bottom);
+ });
+ });
+ return {
+ x: left, y: top, r: right, b: bottom
+ };
}
-
- var [sptX, sptY] = transform.transformPoint(0, 0);
- let [bptX, bptY] = transform.transformPoint(documentView.props.PanelWidth(), documentView.props.PanelHeight());
- 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)
- };
}, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE });
return this._lastBox;
}
@@ -559,7 +580,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
}
render() {
var bounds = this.Bounds;
- let seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
+ let seldoc = SelectionManager.AllSelected().length ? SelectionManager.AllSelected()[0] : undefined;
if (bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
return (null);
}
@@ -586,7 +607,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
left: bounds.x - this._resizeBorderWidth / 2,
top: bounds.y - this._resizeBorderWidth / 2,
pointerEvents: this.Interacting ? "none" : "all",
- zIndex: SelectionManager.SelectedDocuments().length > 1 ? 900 : 0,
+ zIndex: SelectionManager.AllSelected().length > 1 ? 900 : 0,
}} onPointerDown={this.onBackgroundDown} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); }} >
</div>
<div className="documentDecorations-container" ref={this.setTextBar} style={{
@@ -615,7 +636,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
<div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
<div id="documentDecorations-borderRadius" className="documentDecorations-radius" onPointerDown={this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}><span className="borderRadiusTooltip" title="Drag Corner Radius"></span></div>
<div className="link-button-container">
- <DocumentButtonBar views={SelectionManager.SelectedDocuments()} />
+ {(SelectionManager.SelectedDocuments.length && SelectionManager.SelectedDocuments()[0]) ? <DocumentButtonBar views={SelectionManager.SelectedDocuments()} /> : (null)}
</div>
</div >
</div>
diff --git a/src/client/views/InkSelectDecorations.scss b/src/client/views/InkSelectDecorations.scss
new file mode 100644
index 000000000..daff58fd6
--- /dev/null
+++ b/src/client/views/InkSelectDecorations.scss
@@ -0,0 +1,5 @@
+.inkSelectDecorations {
+ position: absolute;
+ border: black 1px solid;
+ z-index: 9001;
+} \ No newline at end of file
diff --git a/src/client/views/InkSelectDecorations.tsx b/src/client/views/InkSelectDecorations.tsx
new file mode 100644
index 000000000..a8eef3305
--- /dev/null
+++ b/src/client/views/InkSelectDecorations.tsx
@@ -0,0 +1,59 @@
+import React = require("react");
+import { Touchable } from "./Touchable";
+import { StrokeData } from "../../new_fields/InkField";
+import { observer } from "mobx-react";
+import { computed, observable, action, runInAction } from "mobx";
+import "./InkSelectDecorations.scss"
+
+@observer
+export default class InkSelectDecorations extends Touchable {
+ static Instance: InkSelectDecorations;
+
+ @observable private _selectedInkNodes: Map<any, any> = new Map();
+
+ constructor(props: Readonly<{}>) {
+ super(props);
+
+ InkSelectDecorations.Instance = this;
+ }
+
+ @action
+ public SetSelected = (inkNodes: Map<any, any>, keepOld: boolean = false) => {
+ if (!keepOld) {
+ this._selectedInkNodes = new Map();
+ }
+ inkNodes.forEach((value: any, key: any) => {
+ runInAction(() => this._selectedInkNodes.set(key, value));
+ });
+ }
+
+ @computed
+ get Bounds(): { x: number, y: number, b: number, r: number } {
+ let left = Number.MAX_VALUE;
+ let top = Number.MAX_VALUE;
+ let right = -Number.MAX_VALUE;
+ let bottom = -Number.MAX_VALUE;
+ this._selectedInkNodes.forEach((value: StrokeData, key: string) => {
+ value.pathData.map(val => {
+ left = Math.min(val.x, left);
+ top = Math.min(val.y, top);
+ right = Math.max(val.x, right);
+ bottom = Math.max(val.y, bottom);
+ });
+ });
+ return { x: left, y: top, b: bottom, r: right };
+ }
+
+ render() {
+ let bounds = this.Bounds;
+ return (
+ <div style={{
+ top: bounds.y, left: bounds.x,
+ height: bounds.b - bounds.y,
+ width: bounds.r - bounds.x
+ }}>
+
+ </div>
+ )
+ }
+} \ No newline at end of file
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 0ede0b770..41de32f1f 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -37,6 +37,7 @@ import { OverlayView } from './OverlayView';
import PDFMenu from './pdf/PDFMenu';
import { PreviewCursor } from './PreviewCursor';
import MarqueeOptionsMenu from './collections/collectionFreeForm/MarqueeOptionsMenu';
+import InkSelectDecorations from './InkSelectDecorations';
@observer
export class MainView extends React.Component {
@@ -512,6 +513,7 @@ export class MainView extends React.Component {
<SharingManager />
<GoogleAuthenticationManager />
<DocumentDecorations />
+ <InkSelectDecorations />
{this.mainContent}
<PreviewCursor />
<ContextMenu />
diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx
index 4955129ba..26779b168 100644
--- a/src/client/views/Touchable.tsx
+++ b/src/client/views/Touchable.tsx
@@ -2,7 +2,7 @@ import * as React from 'react';
import { action } from 'mobx';
import { InteractionUtils } from '../util/InteractionUtils';
-export abstract class Touchable<T> extends React.Component<T> {
+export abstract class Touchable<T = {}> extends React.Component<T> {
protected _touchDrag: boolean = false;
protected prevPoints: Map<number, React.Touch> = new Map<number, React.Touch>();
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 123941b03..c24e52aba 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -103,9 +103,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
added && this.updateCluster(newBox);
return added;
}
- private selectDocuments = (docs: Doc[]) => {
+ private selectDocuments = (docs: Doc[], ink: Map<any, any>[]) => {
SelectionManager.DeselectAll();
docs.map(doc => DocumentManager.Instance.getDocumentView(doc)).map(dv => dv && SelectionManager.SelectDoc(dv, true));
+ ink.forEach(i => SelectionManager.SelectInk(i, true));
}
public isCurrent(doc: Doc) { return !doc.isMinimized && (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document.currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); }
@@ -190,7 +191,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
// hacky way to get a list of DocumentViews in the current view given a list of Documents in the current view
let prevSelected = SelectionManager.SelectedDocuments();
- this.selectDocuments(eles);
+ this.selectDocuments(eles, []);
let clusterDocs = SelectionManager.SelectedDocuments();
SelectionManager.DeselectAll();
prevSelected.map(dv => SelectionManager.SelectDoc(dv, true));
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 07db6354f..cd9ac7ecc 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -20,6 +20,7 @@ import { CollectionFreeFormView } from "./CollectionFreeFormView";
import "./MarqueeView.scss";
import React = require("react");
import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
+import InkSelectDecorations from "../../InkSelectDecorations";
interface MarqueeViewProps {
getContainerTransform: () => Transform;
@@ -27,7 +28,7 @@ interface MarqueeViewProps {
container: CollectionFreeFormView;
addDocument: (doc: Doc) => boolean;
activeDocuments: () => Doc[];
- selectDocuments: (docs: Doc[]) => void;
+ selectDocuments: (docs: Doc[], ink: Map<any, any>[]) => void;
removeDocument: (doc: Doc) => boolean;
addLiveTextDocument: (doc: Doc) => void;
isSelected: () => boolean;
@@ -190,13 +191,14 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
@action
onPointerUp = (e: PointerEvent): void => {
- if (!this.props.container.props.active()) this.props.selectDocuments([this.props.container.props.Document]);
+ if (!this.props.container.props.active()) this.props.selectDocuments([this.props.container.props.Document], []);
if (this._visible) {
let mselect = this.marqueeSelect();
if (!e.shiftKey) {
SelectionManager.DeselectAll(mselect.length ? undefined : this.props.container.props.Document);
}
- this.props.selectDocuments(mselect.length ? mselect : [this.props.container.props.Document]);
+ this.props.selectDocuments(mselect.length ? mselect : [this.props.container.props.Document],
+ this.ink ? [this.marqueeInkSelect(this.ink.inkData)] : []);
}
if (!this._commandExecuted && (Math.abs(this.Bounds.height * this.Bounds.width) > 100)) {
MarqueeOptionsMenu.Instance.createCollection = this.collection;
@@ -350,7 +352,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
}
let newCollection = this.getCollection(selected);
this.props.addDocument(newCollection);
- this.props.selectDocuments([newCollection]);
+ this.props.selectDocuments([newCollection], []);
MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
}
@@ -422,9 +424,14 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
let centerShiftY = 0 - (this.Bounds.top + this.Bounds.height / 2);
ink.forEach((value: StrokeData, key: string, map: any) => {
if (InkingCanvas.IntersectStrokeRect(value, this.Bounds)) {
+ // let transform = this.props.container.props.ScreenToLocalTransform().scale(this.props.container.props.ContentScaling());
idata.set(key,
{
- pathData: value.pathData.map(val => ({ x: val.x + centerShiftX, y: val.y + centerShiftY })),
+ pathData: value.pathData.map(val => {
+ let tVal = this.props.getTransform().inverse().transformPoint(val.x, val.y);
+ return { x: tVal[0], y: tVal[1] };
+ // return { x: val.x + centerShiftX, y: val.y + centerShiftY }
+ }),
color: value.color,
width: value.width,
tool: value.tool,
@@ -432,6 +439,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
});
}
});
+ // InkSelectDecorations.Instance.SetSelected(idata);
return idata;
}