aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/documents/Documents.ts14
-rw-r--r--src/client/util/SelectionManager.ts32
-rw-r--r--src/client/views/DocumentDecorations.tsx80
-rw-r--r--src/client/views/InkSelectDecorations.tsx16
-rw-r--r--src/client/views/InkingCanvas.tsx12
-rw-r--r--src/client/views/InkingStroke.tsx107
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx64
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx92
-rw-r--r--src/client/views/nodes/DocumentView.tsx2
-rw-r--r--src/new_fields/InkField.ts20
-rw-r--r--src/new_fields/documentSchemas.ts1
11 files changed, 207 insertions, 233 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 2c6b40cb9..a074d267e 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -48,6 +48,8 @@ import { PresElementBox } from "../views/presentationview/PresElementBox";
import { QueryBox } from "../views/nodes/QueryBox";
import { ColorBox } from "../views/nodes/ColorBox";
import { DocuLinkBox } from "../views/nodes/DocuLinkBox";
+import { InkingStroke } from "../views/InkingStroke";
+import { InkField } from "../../new_fields/InkField";
var requestImageSize = require('../util/request-image-size');
var path = require('path');
@@ -107,6 +109,7 @@ export interface DocumentOptions {
sourcePanel?: Doc; // panel to display in 'targetContainer' as the result of a button onClick script
targetContainer?: Doc; // document whose proto will be set to 'panel' as the result of a onClick click script
dropConverter?: ScriptField; // script to run when documents are dropped on this Document.
+ strokeWidth?: number;
// [key: string]: Opt<Field>;
}
@@ -209,6 +212,9 @@ export namespace Docs {
[DocumentType.PRESELEMENT, {
layout: { view: PresElementBox, dataField: data }
}],
+ [DocumentType.INK, {
+ layout: { view: InkingStroke, dataField: data }
+ }]
]);
// All document prototypes are initialized with at least these values
@@ -411,8 +417,12 @@ export namespace Docs {
return InstanceFromProto(Prototypes.get(DocumentType.TEXT), "", options);
}
- export function InkDocument(options: DocumentOptions = {}) {
- return InstanceFromProto(Prototypes.get(DocumentType.INK), "", options);
+ export function InkDocument(color: string, tool: number, strokeWidth: number, points: { x: number, y: number }[], options: DocumentOptions = {}) {
+ let doc = InstanceFromProto(Prototypes.get(DocumentType.INK), new InkField(points), options);
+ doc.color = color;
+ doc.strokeWidth = strokeWidth;
+ doc.tool = tool;
+ return doc;
}
export function IconDocument(icon: string, options: DocumentOptions = {}) {
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 2a57c67bd..ca61f9014 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -3,8 +3,6 @@ import { Doc, Opt } from "../../new_fields/Doc";
import { DocumentView } from "../views/nodes/DocumentView";
import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
import { NumCast, StrCast } from "../../new_fields/Types";
-import { InkingControl } from "../views/InkingControl";
-import { InkDocAndStroke } from "../views/InkingStroke";
export namespace SelectionManager {
@@ -12,7 +10,6 @@ export namespace SelectionManager {
@observable IsDragging: boolean = false;
@observable SelectedDocuments: Array<DocumentView> = [];
- @observable SelectedInk: Array<{ Document: Doc, Ink: Map<any, any> }> = [];
@action
@@ -43,20 +40,6 @@ export namespace SelectionManager {
DeselectAll(): void {
manager.SelectedDocuments.map(dv => dv.props.whenActiveChanged(false));
manager.SelectedDocuments = [];
- manager.SelectedInk = [];
- }
-
- @action
- SelectInk(ink: { Document: Doc, 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];
- }
}
}
@@ -69,10 +52,6 @@ export namespace SelectionManager {
manager.SelectDoc(docView, ctrlPressed);
}
- export function SelectInk(ink: { Document: Doc, Ink: Map<any, any> }, ctrlPressed: boolean): void {
- manager.SelectInk(ink, ctrlPressed);
- }
-
export function IsSelected(doc: DocumentView): boolean {
return manager.SelectedDocuments.indexOf(doc) !== -1;
}
@@ -95,15 +74,4 @@ export namespace SelectionManager {
export function SelectedDocuments(): Array<DocumentView> {
return manager.SelectedDocuments.slice();
}
-
- export function SelectedInk(): Array<{ Document: Doc, Ink: Map<any, any> }> {
- return manager.SelectedInk.slice();
- }
-
- export function AllSelected(): Array<DocumentView | InkDocAndStroke> {
- let arr: Array<DocumentView | InkDocAndStroke> = [];
- arr = SelectionManager.SelectedDocuments();
- arr.push(...SelectionManager.SelectedInk());
- return arr;
- }
}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index e208e5f3b..10764a9ce 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -24,7 +24,7 @@ import { DocumentView } from "./nodes/DocumentView";
import { FieldView } from "./nodes/FieldView";
import { IconBox } from "./nodes/IconBox";
import React = require("react");
-import { StrokeData } from '../../new_fields/InkField';
+import { PointData } from '../../new_fields/InkField';
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -162,44 +162,23 @@ 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.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)
- };
+ this._lastBox = SelectionManager.SelectedDocuments().reduce((bounds, docView) => {
+ if (docView.props.renderDepth === 0 ||
+ Doc.AreProtosEqual(docView.props.Document, CurrentUserUtils.UserDocument)) {
+ return bounds;
}
- else {
- let left = bounds.x;
- let top = bounds.y;
- let right = bounds.r;
- let bottom = bounds.b;
- let ink;
- docViewOrInk.Ink.forEach((value: StrokeData, key: string) => {
- value.pathData.map(val => {
- ink = docViewOrInk.Document.ink;
- 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
- };
+ let transform = (docView.props.ScreenToLocalTransform().scale(docView.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(docView.props.PanelWidth(), docView.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;
}
@@ -226,7 +205,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
document.removeEventListener("pointerup", this.onBackgroundUp);
document.removeEventListener("pointermove", this.onTitleMove);
document.removeEventListener("pointerup", this.onTitleUp);
- DragManager.StartDocumentDrag(SelectionManager.AllSelected().map(docOrInk => docOrInk instanceof DocumentView ? docOrInk.ContentDiv! : (document.createElement("div"))), dragData, e.x, e.y, {
+ DragManager.StartDocumentDrag(SelectionManager.SelectedDocuments().map(docView => docView.ContentDiv!), dragData, e.x, e.y, {
handlers: { dragComplete: action(() => this._hidden = this.Interacting = false) },
hideSource: true
});
@@ -550,21 +529,16 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
@computed
get selectionTitle(): string {
- if (SelectionManager.AllSelected().length === 1) {
- let selected = SelectionManager.AllSelected()[0];
- if (selected instanceof DocumentView) {
- let field = selected.props.Document[this._fieldKey];
- if (typeof field === "string") {
- return field;
- }
- else if (typeof field === "number") {
- return field.toString();
- }
+ if (SelectionManager.SelectedDocuments().length === 1) {
+ let selected = SelectionManager.SelectedDocuments()[0];
+ let field = selected.props.Document[this._fieldKey];
+ if (typeof field === "string") {
+ return field;
}
- else {
- return "-ink strokes-";
+ else if (typeof field === "number") {
+ return field.toString();
}
- } else if (SelectionManager.AllSelected().length > 1) {
+ } else if (SelectionManager.SelectedDocuments().length > 1) {
return "-multiple-";
}
return "-unset-";
@@ -590,7 +564,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
let minimizeIcon = (
<div className="documentDecorations-minimizeButton" onPointerDown={this.onMinimizeDown}>
{/* Currently, this is set to be enabled if there is no ink selected. It might be interesting to think about minimizing ink if it's useful? -syip2*/}
- {(SelectionManager.SelectedDocuments().length === 1 && SelectionManager.SelectedInk().length === 0) ? IconBox.DocumentIcon(StrCast(SelectionManager.SelectedDocuments()[0].props.Document.layout, "...")) : "..."}
+ {(SelectionManager.SelectedDocuments().length === 1) ? IconBox.DocumentIcon(StrCast(SelectionManager.SelectedDocuments()[0].props.Document.layout, "...")) : "..."}
</div>);
bounds.x = Math.max(0, bounds.x - this._resizeBorderWidth / 2) + this._resizeBorderWidth / 2;
@@ -611,7 +585,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.AllSelected().length > 1 ? 900 : 0,
+ zIndex: SelectionManager.SelectedDocuments().length > 1 ? 900 : 0,
}} onPointerDown={this.onBackgroundDown} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); }} >
</div>
<div className="documentDecorations-container" ref={this.setTextBar} style={{
diff --git a/src/client/views/InkSelectDecorations.tsx b/src/client/views/InkSelectDecorations.tsx
index a8eef3305..95ccc1777 100644
--- a/src/client/views/InkSelectDecorations.tsx
+++ b/src/client/views/InkSelectDecorations.tsx
@@ -1,6 +1,6 @@
import React = require("react");
import { Touchable } from "./Touchable";
-import { StrokeData } from "../../new_fields/InkField";
+import { PointData } from "../../new_fields/InkField";
import { observer } from "mobx-react";
import { computed, observable, action, runInAction } from "mobx";
import "./InkSelectDecorations.scss"
@@ -33,13 +33,13 @@ export default class InkSelectDecorations extends Touchable {
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);
- });
+ this._selectedInkNodes.forEach((value: PointData, 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 };
}
diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx
index a0ea37300..e5253c377 100644
--- a/src/client/views/InkingCanvas.tsx
+++ b/src/client/views/InkingCanvas.tsx
@@ -7,7 +7,7 @@ import { InkingControl } from "./InkingControl";
import { InkingStroke } from "./InkingStroke";
import React = require("react");
import { UndoManager } from "../util/UndoManager";
-import { StrokeData, InkField, InkTool } from "../../new_fields/InkField";
+import { PointData, InkField, InkTool } from "../../new_fields/InkField";
import { Doc } from "../../new_fields/Doc";
import { Cast, PromiseValue, NumCast } from "../../new_fields/Types";
import { Touchable } from "./Touchable";
@@ -26,15 +26,15 @@ export class InkingCanvas extends Touchable<InkCanvasProps> {
maxCanvasDim = 8192 / 2; // 1/2 of the maximum canvas dimension for Chrome
@observable inkMidX: number = 0;
@observable inkMidY: number = 0;
- private previousState?: Map<string, StrokeData>;
+ private previousState?: Map<string, PointData>;
private _currentStrokeId: string = "";
- public static IntersectStrokeRect(stroke: StrokeData, selRect: { left: number, top: number, width: number, height: number }): boolean {
+ public static IntersectStrokeRect(stroke: PointData, selRect: { left: number, top: number, width: number, height: number }): boolean {
return stroke.pathData.reduce((inside: boolean, val) => inside ||
(selRect.left < val.x && selRect.left + selRect.width > val.x &&
selRect.top < val.y && selRect.top + selRect.height > val.y)
, false);
}
- public static StrokeRect(stroke: StrokeData): { left: number, top: number, right: number, bottom: number } {
+ public static StrokeRect(stroke: PointData): { left: number, top: number, right: number, bottom: number } {
return stroke.pathData.reduce((bounds: { left: number, top: number, right: number, bottom: number }, val) =>
({
left: Math.min(bounds.left, val.x), top: Math.min(bounds.top, val.y),
@@ -58,12 +58,12 @@ export class InkingCanvas extends Touchable<InkCanvasProps> {
}
@computed
- get inkData(): Map<string, StrokeData> {
+ get inkData(): Map<string, PointData> {
let map = Cast(this.props.AnnotationDocument[this.props.inkFieldKey], InkField);
return !map ? new Map : new Map(map.inkData);
}
- set inkData(value: Map<string, StrokeData>) {
+ set inkData(value: Map<string, PointData>) {
this.props.AnnotationDocument[this.props.inkFieldKey] = new InkField(value);
}
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 824f40b1f..411b0d3a0 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -1,79 +1,66 @@
import { observer } from "mobx-react";
-import { observable, trace, runInAction } from "mobx";
+import { observable, trace, runInAction, computed } from "mobx";
import { InkingControl } from "./InkingControl";
import React = require("react");
-import { InkTool } from "../../new_fields/InkField";
+import { InkTool, InkField, InkData } from "../../new_fields/InkField";
import "./InkingStroke.scss";
import { AudioBox } from "./nodes/AudioBox";
-import { Doc } from "../../new_fields/Doc";
-import { createSchema, makeInterface } from "../../new_fields/Schema";
+import { Doc, FieldResult } from "../../new_fields/Doc";
+import { createSchema, makeInterface, listSpec } from "../../new_fields/Schema";
import { documentSchema } from "../../new_fields/documentSchemas";
import { DocExtendableComponent } from "./DocComponent";
import { FieldViewProps, FieldView } from "./nodes/FieldView";
-
-
-interface StrokeProps {
- offsetX: number;
- offsetY: number;
- id: string;
- count: number;
- line: Array<{ x: number, y: number }>;
- color: string;
- width: string;
- tool: InkTool;
- creationTime: number;
- deleteCallback: (index: string) => void;
-}
+import { Transform } from "../util/Transform";
+import { Cast, FieldValue } from "../../new_fields/Types";
+import { List } from "../../new_fields/List";
type InkDocument = makeInterface<[typeof documentSchema]>;
const InkDocument = makeInterface(documentSchema);
+export function CreatePolyline(points: { x: number, y: number }[], left: number, top: number, color?: string, width?: number) {
+ let pts = points.reduce((acc: string, pt: { x: number, y: number }) => acc + `${pt.x - left},${pt.y - top} `, "");
+ return (
+ <polyline
+ points={pts}
+ style={{
+ fill: "none",
+ stroke: color ?? InkingControl.Instance.selectedColor,
+ strokeWidth: width ?? InkingControl.Instance.selectedWidth
+ }}
+ />
+ );
+}
+
@observer
-export class InkingStroke extends DocExtendableComponent<FieldViewProps & StrokeProps, InkDocument>(InkDocument) {
+export class InkingStroke extends DocExtendableComponent<FieldViewProps, InkDocument>(InkDocument) {
public static LayoutString(fieldStr: string) { return FieldView.LayoutString(InkingStroke, fieldStr); }
- @observable private _strokeTool: InkTool = this.props.tool;
- @observable private _strokeColor: string = this.props.color;
- @observable private _strokeWidth: string = this.props.width;
-
- deleteStroke = (e: React.PointerEvent): void => {
- if (InkingControl.Instance.selectedTool === InkTool.Eraser && e.buttons === 1) {
- this.props.deleteCallback(this.props.id);
- e.stopPropagation();
- e.preventDefault();
- }
- if (InkingControl.Instance.selectedTool === InkTool.Scrubber && e.buttons === 1) {
- AudioBox.SetScrubTime(this.props.creationTime);
- e.stopPropagation();
- e.preventDefault();
- }
- }
-
- parseData = (line: Array<{ x: number, y: number }>): string => {
- return !line.length ? "" : "M " + line.map(p => (p.x + this.props.offsetX) + " " + (p.y + this.props.offsetY)).join(" L ");
- }
-
- createStyle() {
- switch (this._strokeTool) {
- // add more tool styles here
- default:
- return {
- fill: "none",
- stroke: this._strokeColor,
- strokeWidth: this._strokeWidth + "px",
- };
- }
- }
+ @computed get PanelWidth() { return this.props.PanelWidth(); }
+ @computed get PanelHeight() { return this.props.PanelHeight(); }
render() {
- let pathStyle = this.createStyle();
- let pathData = this.parseData(this.props.line);
- let pathlength = this.props.count; // bcz: this is needed to force reactions to the line's data changes
- let marker = this.props.tool === InkTool.Highlighter ? "-marker" : "";
-
- let pointerEvents: any = InkingControl.Instance.selectedTool === InkTool.Eraser ||
- InkingControl.Instance.selectedTool === InkTool.Scrubber ? "all" : "none";
- return (<path className={`inkingStroke${marker}`} d={pathData} style={{ ...pathStyle, pointerEvents: pointerEvents }}
- strokeLinejoin="round" strokeLinecap="round" onPointerOver={this.deleteStroke} onPointerDown={this.deleteStroke} />);
+ // let pathData = this.parseData(this.props.line);
+ let data: InkData = Cast(this.Document.data, InkField) ?.inkData ?? [];
+ let xs = data.map(p => p.x);
+ let ys = data.map(p => p.y);
+ let left = Math.min(...xs);
+ let top = Math.min(...ys);
+ let right = Math.max(...xs);
+ let bottom = Math.max(...ys);
+ let points = CreatePolyline(data, 0, 0, this.Document.color, this.Document.strokeWidth);
+ let width = right - left;
+ let height = bottom - top;
+ let scaleX = this.PanelWidth / width;
+ let scaleY = this.PanelHeight / height;
+ // let pathlength = this.props.count; // bcz: this is needed to force reactions to the line's data changes
+ return (
+ <svg width={width} height={height} style={{
+ transformOrigin: "top left",
+ transform: `translate(${left}px, ${top}px) scale(${scaleX}, ${scaleY})`,
+ mixBlendMode: this.Document.tool === InkTool.Highlighter ? "multiply" : "unset"
+ }}>
+ {points}
+ </svg>
+ );
}
} \ 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 8294eaaec..21981c25e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -5,7 +5,7 @@ import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../../new_fields/Doc";
import { Id } from "../../../../new_fields/FieldSymbols";
-import { InkField, StrokeData, InkTool } from "../../../../new_fields/InkField";
+import { InkField, PointData, InkTool } from "../../../../new_fields/InkField";
import { createSchema, makeInterface } from "../../../../new_fields/Schema";
import { ScriptField } from "../../../../new_fields/ScriptField";
import { BoolCast, Cast, DateCast, NumCast, StrCast } from "../../../../new_fields/Types";
@@ -40,6 +40,7 @@ import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
import PDFMenu from "../../pdf/PDFMenu";
import { documentSchema, positionSchema } from "../../../../new_fields/documentSchemas";
import { InkingControl } from "../../InkingControl";
+import { InkingStroke, CreatePolyline } from "../../InkingStroke";
library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload);
@@ -108,10 +109,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
added && this.updateCluster(newBox);
return added;
}
- private selectDocuments = (docs: Doc[], ink: { Document: Doc, Ink: Map<any, any> }[]) => {
+ private selectDocuments = (docs: Doc[]) => {
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); }
@@ -284,14 +284,25 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
e.preventDefault();
if (InkingControl.Instance.selectedTool !== InkTool.Eraser && InkingControl.Instance.selectedTool !== InkTool.Scrubber) {
- this._points.push({ x: e.pageX, y: e.pageY });
+ let point = this.getTransform().transformPoint(e.pageX, e.pageY);
+ this._points.push({ x: point[0], y: point[1] });
}
}
}
}
+ @action
onPointerUp = (e: PointerEvent): void => {
if (InteractionUtils.IsType(e, InteractionUtils.TOUCH)) return;
+
+ if (this._points.length > 1) {
+ let B = this.svgBounds;
+ let points = this._points.map(p => ({ x: p.x - B.left, y: p.y - B.top }));
+ let inkDoc = Docs.Create.InkDocument(InkingControl.Instance.selectedColor, InkingControl.Instance.selectedTool, parseInt(InkingControl.Instance.selectedWidth), points, { width: B.width, height: B.height, x: B.left, y: B.top });
+ this.addDocument(inkDoc);
+ this._points = [];
+ }
+
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
document.removeEventListener("touchmove", this.onTouch);
@@ -324,11 +335,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}, [[minx, maxx], [miny, maxy]]);
let ink = this.extensionDoc && Cast(this.extensionDoc.ink, InkField);
if (ink && ink.inkData) {
- ink.inkData.forEach((value: StrokeData, key: string) => {
- let bounds = InkingCanvas.StrokeRect(value);
- ranges[0] = [Math.min(ranges[0][0], bounds.left), Math.max(ranges[0][1], bounds.right)];
- ranges[1] = [Math.min(ranges[1][0], bounds.top), Math.max(ranges[1][1], bounds.bottom)];
- });
+ // ink.inkData.forEach((value: PointData, key: string) => {
+ // let bounds = InkingCanvas.StrokeRect(value);
+ // ranges[0] = [Math.min(ranges[0][0], bounds.left), Math.max(ranges[0][1], bounds.right)];
+ // ranges[1] = [Math.min(ranges[1][0], bounds.top), Math.max(ranges[1][1], bounds.bottom)];
+ // });
}
let cscale = this.props.ContainingCollectionDoc ? NumCast(this.props.ContainingCollectionDoc.scale) : 1;
@@ -363,8 +374,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
this.pan(e);
}
- if (InkingControl.Instance.selectedTool !== InkTool.Eraser && InkingControl.Instance.selectedTool !== InkTool.Scrubber) {
- this._points.push({ x: e.clientX, y: e.clientY });
+ else if (InkingControl.Instance.selectedTool !== InkTool.Eraser && InkingControl.Instance.selectedTool !== InkTool.Scrubber) {
+ let point = this.getTransform().transformPoint(e.clientX, e.clientY);
+ this._points.push({ x: point[0], y: point[1] });
}
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();
@@ -470,7 +482,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
else if (this.props.active()) {
e.stopPropagation();
- this.zoom(e.clientX, e.clientY, e.deltaY)
+ this.zoom(e.clientX, e.clientY, e.deltaY);
}
}
@@ -790,6 +802,31 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
...this.views,
];
}
+
+ @computed get svgBounds() {
+ let xs = this._points.map(p => p.x);
+ let ys = this._points.map(p => p.y);
+ let right = Math.max(...xs);
+ let left = Math.min(...xs);
+ let bottom = Math.max(...ys);
+ let top = Math.min(...ys);
+ return { right: right, left: left, bottom: bottom, top: top, width: right - left, height: bottom - top };
+ }
+
+ @computed get currentStroke() {
+ if (this._points.length <= 1) {
+ return (null);
+ }
+
+ let B = this.svgBounds;
+
+ return (
+ <svg width={B.width} height={B.height} style={{ transform: `translate(${B.left}px, ${B.top}px)` }}>
+ {CreatePolyline(this._points, B.left, B.top)}
+ </svg>
+ );
+ }
+
render() {
// update the actual dimensions of the collection so that they can inquired (e.g., by a minimap)
this.Document.fitX = this.contentBounds && this.contentBounds.x;
@@ -808,9 +845,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
easing={this.easing} zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
{!this.extensionDoc ? (null) :
// <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} AnnotationDocument={this.extensionDoc} inkFieldKey={"ink"} >
- this.childViews
+ this.childViews()
// </InkingCanvas>
}
+ {this.currentStroke}
<CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />
</CollectionFreeFormViewPannableContents>
</MarqueeView>
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 138168fed..b5f6f095e 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,7 +1,7 @@
import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
import { Doc, DocListCast } from "../../../../new_fields/Doc";
-import { InkField, StrokeData } from "../../../../new_fields/InkField";
+import { InkField, PointData } from "../../../../new_fields/InkField";
import { List } from "../../../../new_fields/List";
import { listSpec } from "../../../../new_fields/Schema";
import { SchemaHeaderField } from "../../../../new_fields/SchemaHeaderField";
@@ -198,10 +198,10 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
if (!e.shiftKey) {
SelectionManager.DeselectAll(mselect.length ? undefined : this.props.Document);
}
- let inkselect = this.ink ? this.marqueeInkSelect(this.ink.inkData) : new Map();
- let inks = inkselect.size ? [{ Document: this.inkDoc, Ink: inkselect }] : [];
- let docs = mselect.length ? mselect : (inks.length ? [] : [this.props.Document]);
- this.props.selectDocuments(docs, inks);
+ // let inkselect = this.ink ? this.marqueeInkSelect(this.ink.inkData) : new Map();
+ // let inks = inkselect.size ? [{ Document: this.inkDoc, Ink: inkselect }] : [];
+ let docs = mselect.length ? mselect : [this.props.Document];
+ this.props.selectDocuments(docs, []);
}
if (!this._commandExecuted && (Math.abs(this.Bounds.height * this.Bounds.width) > 100)) {
MarqueeOptionsMenu.Instance.createCollection = this.collection;
@@ -217,7 +217,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this.hideMarquee();
MarqueeOptionsMenu.Instance.fadeOut(true);
document.removeEventListener("pointerdown", hideMarquee);
- }
+ };
document.addEventListener("pointerdown", hideMarquee);
if (e.altKey) {
@@ -294,7 +294,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
delete = () => {
this.marqueeSelect(false).map(d => this.props.removeDocument(d));
if (this.ink) {
- this.marqueeInkDelete(this.ink.inkData);
+ // this.marqueeInkDelete(this.ink.inkData);
}
SelectionManager.DeselectAll();
this.cleanupInteractions(false);
@@ -336,8 +336,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
title: "a nested collection",
});
let dataExtensionField = Doc.CreateDocumentExtensionForField(newCollection, "data");
- dataExtensionField.ink = inkData ? new InkField(this.marqueeInkSelect(inkData)) : undefined;
- this.marqueeInkDelete(inkData);
+ // dataExtensionField.ink = inkData ? new InkField(this.marqueeInkSelect(inkData)) : undefined;
+ // this.marqueeInkDelete(inkData);
this.hideMarquee();
return newCollection;
}
@@ -422,45 +422,45 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this.cleanupInteractions(false);
}
}
- @action
- marqueeInkSelect(ink: Map<any, any>) {
- let idata = new Map();
- let centerShiftX = 0 - (this.Bounds.left + this.Bounds.width / 2); // moves each point by the offset that shifts the selection's center to the origin.
- 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 => {
- 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,
- page: -1
- });
- }
- });
- // InkSelectDecorations.Instance.SetSelected(idata);
- return idata;
- }
+ // @action
+ // marqueeInkSelect(ink: Map<any, any>) {
+ // let idata = new Map();
+ // let centerShiftX = 0 - (this.Bounds.left + this.Bounds.width / 2); // moves each point by the offset that shifts the selection's center to the origin.
+ // let centerShiftY = 0 - (this.Bounds.top + this.Bounds.height / 2);
+ // ink.forEach((value: PointData, 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 => {
+ // 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,
+ // page: -1
+ // });
+ // }
+ // });
+ // // InkSelectDecorations.Instance.SetSelected(idata);
+ // return idata;
+ // }
- @action
- marqueeInkDelete(ink?: Map<any, any>) {
- // bcz: this appears to work but when you restart all the deleted strokes come back -- InkField isn't observing its changes so they aren't written to the DB.
- // ink.forEach((value: StrokeData, key: string, map: any) =>
- // InkingCanvas.IntersectStrokeRect(value, this.Bounds) && ink.delete(key));
+ // @action
+ // marqueeInkDelete(ink?: Map<any, any>) {
+ // // bcz: this appears to work but when you restart all the deleted strokes come back -- InkField isn't observing its changes so they aren't written to the DB.
+ // // ink.forEach((value: StrokeData, key: string, map: any) =>
+ // // InkingCanvas.IntersectStrokeRect(value, this.Bounds) && ink.delete(key));
- if (ink) {
- let idata = new Map();
- ink.forEach((value: StrokeData, key: string, map: any) =>
- !InkingCanvas.IntersectStrokeRect(value, this.Bounds) && idata.set(key, value));
- this.ink = new InkField(idata);
- }
- }
+ // if (ink) {
+ // let idata = new Map();
+ // ink.forEach((value: PointData, key: string, map: any) =>
+ // !InkingCanvas.IntersectStrokeRect(value, this.Bounds) && idata.set(key, value));
+ // this.ink = new InkField(idata);
+ // }
+ // }
marqueeSelect(selectBackgrounds: boolean = true) {
let selRect = this.Bounds;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 62529a5fb..5c89472ce 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -632,7 +632,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
{searchHighlight}
</div>
}
- </>
+ </>;
}
render() {
if (!this.props.Document) return (null);
diff --git a/src/new_fields/InkField.ts b/src/new_fields/InkField.ts
index d94834e91..2d8bb582a 100644
--- a/src/new_fields/InkField.ts
+++ b/src/new_fields/InkField.ts
@@ -12,16 +12,12 @@ export enum InkTool {
Scrubber
}
-export interface StrokeData {
- pathData: Array<{ x: number, y: number }>;
- color: string;
- width: string;
- tool: InkTool;
- displayTimecode: number;
- creationTime: number;
+export interface PointData {
+ x: number;
+ y: number;
}
-export type InkData = Map<string, StrokeData>;
+export type InkData = Array<PointData>;
const pointSchema = createSimpleSchema({
x: true, y: true
@@ -34,16 +30,16 @@ const strokeDataSchema = createSimpleSchema({
@Deserializable("ink")
export class InkField extends ObjectField {
- @serializable(map(object(strokeDataSchema)))
+ @serializable(list(object(strokeDataSchema)))
readonly inkData: InkData;
- constructor(data?: InkData) {
+ constructor(data: InkData) {
super();
- this.inkData = data || new Map;
+ this.inkData = data;
}
[Copy]() {
- return new InkField(DeepCopy(this.inkData));
+ return new InkField(this.inkData);
}
[ToScriptString]() {
diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts
index e2730914f..0b28561bf 100644
--- a/src/new_fields/documentSchemas.ts
+++ b/src/new_fields/documentSchemas.ts
@@ -43,6 +43,7 @@ export const documentSchema = createSchema({
isAnimating: "boolean", // whether the document is in the midst of animating between two layouts (used by icons to de/iconify documents).
animateToDimensions: listSpec("number"), // layout information about the target rectangle a document is animating towards
scrollToLinkID: "string", // id of link being traversed. allows this doc to scroll/highlight/etc its link anchor. scrollToLinkID should be set to undefined by this doc after it sets up its scroll,etc.
+ strokeWidth: "number"
});
export const positionSchema = createSchema({