aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authormehekj <mehek.jethani@gmail.com>2022-06-02 13:32:17 -0400
committermehekj <mehek.jethani@gmail.com>2022-06-02 13:32:17 -0400
commiteee5c6e67b25c0ff49289458acea0ef6f11d92de (patch)
tree652b91ba9881c4fb45b07df5ae56ead79e26013f /src
parent53cae5e2ab9267295a824ff721c119ada5e5dc20 (diff)
parent30347f1ac971b3299cfbefd5505e20f5e82702e0 (diff)
Merge branch 'inking-naafi-mehek' into master-cleanup
Diffstat (limited to 'src')
-rw-r--r--src/Utils.ts7
-rw-r--r--src/client/documents/Documents.ts5
-rw-r--r--src/client/util/CurrentUserUtils.ts3
-rw-r--r--src/client/util/InteractionUtils.tsx3
-rw-r--r--src/client/views/GestureOverlay.tsx30
-rw-r--r--src/client/views/InkTranscription.scss5
-rw-r--r--src/client/views/InkTranscription.tsx312
-rw-r--r--src/client/views/InkingStroke.tsx1
-rw-r--r--src/client/views/LightboxView.tsx3
-rw-r--r--src/client/views/MainView.tsx2
-rw-r--r--src/client/views/Touchable.tsx2
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx2
-rw-r--r--src/client/views/collections/CollectionMenu.tsx1
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx60
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx11
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx7
-rw-r--r--src/client/views/nodes/DocumentView.tsx4
-rw-r--r--src/client/views/nodes/EquationBox.scss36
-rw-r--r--src/client/views/nodes/ImageBox.tsx2
-rw-r--r--src/client/views/nodes/MapBox/MapBox.tsx2
-rw-r--r--src/client/views/nodes/WebBox.tsx2
-rw-r--r--src/client/views/nodes/button/FontIconBox.tsx92
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx1
-rw-r--r--src/client/views/pdf/PDFViewer.tsx2
-rw-r--r--src/client/views/search/SearchBox.tsx1
-rw-r--r--src/fields/InkField.ts5
-rw-r--r--src/typings/index.d.ts1
27 files changed, 564 insertions, 38 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index dbae0aa8c..6f3a31b49 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -5,6 +5,12 @@ import { Socket } from 'socket.io';
import { Colors } from './client/views/global/globalEnums';
import { Message } from './server/Message';
import Color = require('color');
+import { InkTool } from './fields/InkField';
+import { action } from 'mobx';
+import { CurrentUserUtils } from './client/util/CurrentUserUtils';
+import { CollectionFreeFormView } from './client/views/collections/collectionFreeForm';
+import { WidthSym, HeightSym } from './fields/Doc';
+import { NumCast } from './fields/Types';
export namespace Utils {
export let DRAG_THRESHOLD = 4;
@@ -414,6 +420,7 @@ export function formatTime(time: number) {
return (hours ? hours.toString() + ":" : "") + minutes.toString().padStart(2, '0') + ':' + seconds.toString().padStart(2, '0');
}
+// x is furthest left, y is furthest top, r is furthest right, b is furthest bottom
export function aggregateBounds(boundsList: { x: number, y: number, width?: number, height?: number }[], xpad: number, ypad: number) {
const bounds = boundsList.map(b => ({ x: b.x, y: b.y, r: b.x + (b.width || 0), b: b.y + (b.height || 0) })).reduce((bounds, b) => ({
x: Math.min(b.x, bounds.x), y: Math.min(b.y, bounds.y),
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 8d29bcebe..18829a936 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -4,7 +4,7 @@ import { DateField } from "../../fields/DateField";
import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Initializing, Opt, updateCachedAcls, WidthSym } from "../../fields/Doc";
import { Id } from "../../fields/FieldSymbols";
import { HtmlField } from "../../fields/HtmlField";
-import { InkField } from "../../fields/InkField";
+import { InkField, PointData } from "../../fields/InkField";
import { List } from "../../fields/List";
import { ProxyField } from "../../fields/Proxy";
import { RichTextField } from "../../fields/RichTextField";
@@ -749,7 +749,7 @@ export namespace Docs {
return linkDoc;
}
- export function InkDocument(color: string, tool: string, strokeWidth: number, strokeBezier: string, fillColor: string, arrowStart: string, arrowEnd: string, dash: string, points: { X: number, Y: number }[], options: DocumentOptions = {}) {
+ export function InkDocument(color: string, tool: string, strokeWidth: number, strokeBezier: string, fillColor: string, arrowStart: string, arrowEnd: string, dash: string, points: PointData[], options: DocumentOptions = {}) {
const I = new Doc();
I[Initializing] = true;
I.type = DocumentType.INK;
@@ -774,6 +774,7 @@ export namespace Docs {
I.author = Doc.CurrentUserEmail;
I.rotation = 0;
I.data = new InkField(points);
+ I.creationDate = new DateField;
I["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
I["acl-Override"] = "None";
I.links = ComputedField.MakeFunction("links(self)");
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index c4e2afd08..cf7601563 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -752,7 +752,8 @@ export class CurrentUserUtils {
static inkTools(doc: Doc) {
const tools: Button[] = [
- { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("pen", _readOnly_)' },
+ { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", click: 'setActiveInkTool("pen", _readOnly_)' },
+ { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("write", _readOnly_)' },
{ title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", click: 'setActiveInkTool("eraser", _readOnly_)' },
// { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setActiveInkTool("highlighter")' },
{ title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle", _readOnly_)' },
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index 3e051dec8..e2c5fab64 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -286,6 +286,8 @@ export namespace InteractionUtils {
// pen and eraser are both pointer type 'pen', but pen is button 0 and eraser is button 5. -syip2
case PENTYPE: return e.pointerType === PENTYPE && (e.button === -1 || e.button === 0);
case ERASERTYPE: return e.pointerType === PENTYPE && e.button === (e instanceof PointerEvent ? ERASER_BUTTON : ERASER_BUTTON);
+ case TOUCHTYPE:
+ return e.pointerType === TOUCHTYPE;
default: return e.pointerType === type;
}
}
@@ -355,6 +357,7 @@ export namespace InteractionUtils {
}
export function IsDragging(oldTouches: Map<number, React.Touch>, newTouches: React.Touch[], leniency: number): boolean {
+ console.log("getting here");
for (const touch of newTouches) {
if (touch) {
const oldTouch = oldTouches.get(touch.identifier);
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 482b62479..226983138 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -24,6 +24,7 @@ import { RadialMenu } from "./nodes/RadialMenu";
import HorizontalPalette from "./Palette";
import { Touchable } from "./Touchable";
import TouchScrollableMenu, { TouchScrollableMenuItem } from "./TouchScrollableMenu";
+import { checkInksToGroup, createInkGroup } from "./nodes/button/FontIconBox";
@observer
export class GestureOverlay extends Touchable {
@@ -73,6 +74,8 @@ export class GestureOverlay extends Touchable {
this._inkToTextDoc = FieldValue(Cast(this._thumbDoc?.inkToTextDoc, Doc));
}
+ // TODO: nda - add dragging groups with one finger drag and have to click into group to scroll within the group
+
/**
* Ignores all touch events that belong to a hand being held down.
*/
@@ -124,6 +127,8 @@ export class GestureOverlay extends Touchable {
// pen is also a touch, but with a radius of 0.5 (at least with the surface pens)
// and this seems to be the only way of differentiating pen and touch on touch events
if (pt.radiusX > 1 && pt.radiusY > 1) {
+ createInkGroup();
+ Doc.UserDoc().activeInkTool = InkTool.None;
this.prevPoints.set(pt.identifier, pt);
}
}
@@ -205,7 +210,7 @@ export class GestureOverlay extends Touchable {
const nts: any = this.getNewTouches(e);
this._holdTimer && clearTimeout(this._holdTimer);
this._holdTimer = undefined;
-
+
document.dispatchEvent(
new CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>("dashOnTouchMove",
{
@@ -292,7 +297,7 @@ export class GestureOverlay extends Touchable {
pointer = fingers.reduce((a, v) => a.clientX < v.clientX || v.identifier === thumb.identifier ? a : v);
}
else {
- console.log("not hand");
+ // console.log("not hand");
}
this.pointerIdentifier = pointer?.identifier;
@@ -491,7 +496,19 @@ export class GestureOverlay extends Touchable {
@action
onPointerDown = (e: React.PointerEvent) => {
- if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
+ if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
+ setupMoveUpEvents(this, e, returnFalse, returnFalse, action((e: PointerEvent, doubleTap?: boolean) => {
+ if (doubleTap) {
+ createInkGroup();
+ CurrentUserUtils.SelectedTool = InkTool.None;
+ return;
+ }
+ }));
+ }
+ if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
+ if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
+ CurrentUserUtils.SelectedTool = InkTool.Write;
+ }
this._points.push({ X: e.clientX, Y: e.clientY });
setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction);
// if (CurrentUserUtils.SelectedTool === InkTool.Highlighter) SetActiveInkColor("rgba(245, 230, 95, 0.75)");
@@ -615,6 +632,7 @@ export class GestureOverlay extends Touchable {
if (!actionPerformed) {
const newPoints = this._points.reduce((p, pts) => { p.push([pts.X, pts.Y]); return p; }, [] as number[][]);
newPoints.pop();
+ // console.log("getting to bezier math");
const controlPoints: { X: number, Y: number }[] = [];
const bezierCurves = fitCurve(newPoints, 10);
@@ -625,14 +643,14 @@ export class GestureOverlay extends Touchable {
controlPoints.push({ X: curve[2][0], Y: curve[2][1] });
controlPoints.push({ X: curve[3][0], Y: curve[3][1] });
-
}
const dist = Math.sqrt((controlPoints[0].X - controlPoints.lastElement().X) * (controlPoints[0].X - controlPoints.lastElement().X) +
(controlPoints[0].Y - controlPoints.lastElement().Y) * (controlPoints[0].Y - controlPoints.lastElement().Y));
if (controlPoints.length > 4 && dist < 10) controlPoints[controlPoints.length - 1] = controlPoints[0];
this._points = controlPoints;
-
this.dispatchGesture(GestureUtils.Gestures.Stroke);
+ // TODO: nda - check inks to group here
+ checkInksToGroup();
}
this._points = [];
}
@@ -808,7 +826,7 @@ export class GestureOverlay extends Touchable {
points: stroke ?? this._points,
gesture: gesture as any,
bounds: this.getBounds(stroke ?? this._points),
- text: data
+ text: data,
}
}
)
diff --git a/src/client/views/InkTranscription.scss b/src/client/views/InkTranscription.scss
new file mode 100644
index 000000000..bbb0a1afa
--- /dev/null
+++ b/src/client/views/InkTranscription.scss
@@ -0,0 +1,5 @@
+.ink-transcription {
+ .error-msg {
+ display: none !important;
+ }
+} \ No newline at end of file
diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
new file mode 100644
index 000000000..5e0667bed
--- /dev/null
+++ b/src/client/views/InkTranscription.tsx
@@ -0,0 +1,312 @@
+import * as iink from 'iink-js';
+import { action, observable } from 'mobx';
+import * as React from 'react';
+import { Doc, DocListCast, HeightSym, WidthSym } from '../../fields/Doc';
+import { InkData, InkField } from "../../fields/InkField";
+import { Cast, DateCast, NumCast } from '../../fields/Types';
+import { DocumentType } from "../documents/DocumentTypes";
+import './InkTranscription.scss';
+import { aggregateBounds } from '../../Utils';
+import { CollectionFreeFormView } from './collections/collectionFreeForm';
+import { DocumentManager } from "../util/DocumentManager";
+import { InkingStroke } from './InkingStroke';
+
+
+export class InkTranscription extends React.Component {
+ static Instance: InkTranscription;
+
+ @observable _mathRegister: any;
+ @observable _mathRef: any;
+ @observable _textRegister: any;
+ @observable _textRef: any;
+ private lastJiix: any;
+ private currGroup?: Doc;
+ private containingLayout?: Doc;
+
+ constructor(props: Readonly<{}>) {
+ super(props);
+
+ InkTranscription.Instance = this;
+ }
+
+ componentWillUnmount() {
+ this._mathRef.removeEventListener('exported', (e: any) => this.exportInk(e, this._mathRef));
+ this._textRef.removeEventListener('exported', (e: any) => this.exportInk(e, this._textRef));
+ }
+
+ @action
+ setMathRef = (r: any) => {
+ if (!this._mathRegister) {
+ this._mathRegister = r ? iink.register(r, {
+ recognitionParams: {
+ type: 'MATH',
+ protocol: 'WEBSOCKET',
+ server: {
+ host: 'cloud.myscript.com',
+ applicationKey: '7277ec34-0c2e-4ee1-9757-ccb657e3f89f',
+ hmacKey: 'f5cb18f2-1f95-4ddb-96ac-3f7c888dffc1',
+ websocket: {
+ pingEnabled: false,
+ autoReconnect: true
+ }
+ },
+ iink: {
+ math: {
+ mimeTypes: ['application/x-latex', 'application/vnd.myscript.jiix']
+ },
+ export: {
+ jiix: {
+ strokes: true
+ }
+ }
+ }
+ }
+ }) : null;
+ }
+
+ r.addEventListener('exported', (e: any) => this.exportInk(e, this._mathRef));
+
+ return this._mathRef = r;
+ }
+
+ @action
+ setTextRef = (r: any) => {
+ if (!this._textRegister) {
+ this._textRegister = r ? iink.register(r, {
+ recognitionParams: {
+ type: 'TEXT',
+ protocol: 'WEBSOCKET',
+ server: {
+ host: 'cloud.myscript.com',
+ applicationKey: '7277ec34-0c2e-4ee1-9757-ccb657e3f89f',
+ hmacKey: 'f5cb18f2-1f95-4ddb-96ac-3f7c888dffc1',
+ websocket: {
+ pingEnabled: false,
+ autoReconnect: true
+ }
+ },
+ iink: {
+ text: {
+ mimeTypes: ['text/plain']
+ },
+ export: {
+ jiix: {
+ strokes: true
+ }
+ }
+ }
+ }
+ }) : null;
+ }
+
+ r.addEventListener('exported', (e: any) => this.exportInk(e, this._textRef));
+
+ return this._textRef = r;
+ }
+
+ transcribeInk = (groupDoc: Doc | undefined, containingLayout: Doc, inkDocs: Doc[], math: boolean, ffView?: CollectionFreeFormView) => {
+ if (!groupDoc) return;
+ const validInks = inkDocs.filter(s => s.type === DocumentType.INK);
+
+ const strokes: InkData[] = [];
+ const times: number[] = [];
+ // console.log(validInks);
+ validInks.filter(i => Cast(i.data, InkField)).forEach(i => {
+ const d = Cast(i.data, InkField, null);
+ // const left = Math.min(...d?.inkData.map(pd => pd.X) ?? [0]);
+ // const top = Math.min(...d?.inkData.map(pd => pd.Y) ?? [0]);
+ // strokes.push(d.inkData.map(pd => ({ X: pd.X + NumCast(i.x) - left, Y: pd.Y + NumCast(i.y) - top })));
+ const inkStroke = DocumentManager.Instance.getDocumentView(i)?.ComponentView as InkingStroke;
+ strokes.push(d.inkData.map(pd => (inkStroke.ptToScreen({ X: pd.X, Y: pd.Y }))));
+ times.push(DateCast(i.creationDate).getDate().getTime());
+ });
+
+ this.currGroup = groupDoc;
+ this.containingLayout = containingLayout;
+
+ const pointerData = { "events": strokes.map((stroke, i) => this.inkJSON(stroke, times[i])) };
+ // console.log(JSON.stringify(pointerData));
+ // console.log(pointerData);
+ const processGestures = false;
+
+ if (math) {
+ this._mathRef.editor.pointerEvents(pointerData, processGestures);
+ }
+ else {
+ this._textRef.editor.pointerEvents(pointerData, processGestures);
+ }
+ }
+
+ inkJSON = (stroke: InkData, time: number) => {
+ return {
+ "pointerType": "PEN",
+ "pointerId": 1,
+ "x": stroke.map(point => point.X),
+ "y": stroke.map(point => point.Y),
+ "t": new Array(stroke.length).fill(time),
+ "p": new Array(stroke.length).fill(1.0)
+ };
+ }
+
+ mmToPixel = (mm: number) => {
+ return ((96 * mm) / 25.4);
+ }
+
+ calcBounds = (coords: any) => {
+ // find max and min x values and subtract
+ const max = Math.max(...coords);
+ const min = Math.min(...coords);
+ return max - min;
+ }
+
+ subgroupsTranscriptions = (wordInkDocMap: Map<string, Doc[]>) => {
+ // loop through the words in wordInkDocMap
+ // for each word, get the inkDocs
+
+ // iterate through the keys of wordInkDocMap
+ wordInkDocMap.forEach(async (inkDocs: Doc[], word: string) => {
+ const selected = inkDocs.slice();
+ if (!selected) {
+ return;
+ }
+ // TODO: nda - probably have to cast this to an actual Doc
+ const ctx = await Cast(selected[0].context, Doc);
+ if (!ctx) {
+ return;
+ }
+ const docView: CollectionFreeFormView = DocumentManager.Instance.getDocumentView(ctx)?.ComponentView as CollectionFreeFormView;
+
+ if (!docView) return;
+ const marqViewRef = docView._marqueeViewRef.current;
+ if (!marqViewRef) return;
+ // loop through selected an get the bound
+ const bounds: { x: number, y: number, width?: number, height?: number }[] = []
+
+ selected.map(action(d => {
+ const x = NumCast(d.x);
+ const y = NumCast(d.y);
+ const width = d[WidthSym]();
+ const height = d[HeightSym]();
+ bounds.push({ x, y, width, height });
+ }))
+
+ const aggregBounds = aggregateBounds(bounds, 0, 0);
+
+ if (marqViewRef) {
+ marqViewRef._downX = aggregBounds.x;
+ marqViewRef._downY = aggregBounds.y;
+ marqViewRef._lastX = aggregBounds.r;
+ marqViewRef._lastY = aggregBounds.b;
+ }
+
+ // set the vals for bounds in marqueeView
+
+ selected.map(action(d => {
+ const dx = NumCast(d.x);
+ const dy = NumCast(d.y);
+ delete d.x;
+ delete d.y;
+ delete d.activeFrame;
+ delete d._timecodeToShow; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+ delete d._timecodeToHide; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+ // calculate pos based on bounds
+ if (marqViewRef?.Bounds) {
+ d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2;
+ d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2;
+ }
+ return d;
+ }));
+
+ docView.props.removeDocument?.(selected);
+ const newCollection = marqViewRef?.getCollection(selected, undefined, [], true);
+ if (newCollection) {
+ newCollection.height = newCollection[HeightSym]();
+ newCollection.width = newCollection[WidthSym]();
+ newCollection.title = word;
+ }
+ // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
+ // console.log(newCollection);
+ newCollection && docView.props.addDocument?.(newCollection);
+ });
+ }
+
+ exportInk = (e: any, ref: any) => {
+ const exports = e.detail.exports;
+ // console.log(e);
+ if (exports) {
+ if (exports['application/x-latex']) {
+ const latex = exports['application/x-latex'];
+ // console.log(latex);
+
+ if (this.currGroup) {
+ this.currGroup.text = latex;
+ this.currGroup.title = latex;
+ }
+
+ ref.editor.clear();
+ }
+ else if (exports['text/plain']) {
+ if (exports['application/vnd.myscript.jiix']) {
+ this.lastJiix = JSON.parse(exports['application/vnd.myscript.jiix']);
+ // map timestamp to strokes
+ const timestampWord = new Map<number, string>();
+ this.lastJiix.words.map((word: any) => {
+ if (word.items) {
+ word.items.forEach((i: { id: string, timestamp: string, X: Array<number>, Y: Array<number>, F: Array<number> }) => {
+ const ms = Date.parse(i.timestamp);
+ timestampWord.set(ms, word.label);
+ })
+ }
+ })
+
+ const wordInkDocMap = new Map<string, Doc[]>();
+ if (this.currGroup) {
+ const docList = DocListCast(this.currGroup.data)
+ docList.forEach((inkDoc: Doc) => {
+ // just having the times match up and be a unique value (actual timestamp doesn't matter)
+ const ms = DateCast(inkDoc.creationDate).getDate().getTime() + 14400000;
+ const word = timestampWord.get(ms);
+ if (!word) {
+ return;
+ }
+ const entry = wordInkDocMap.get(word);
+ if (entry) {
+ entry.push(inkDoc);
+ wordInkDocMap.set(word, entry);
+ } else {
+ const newEntry = [inkDoc];
+ wordInkDocMap.set(word, newEntry);
+ }
+ });
+ if (this.lastJiix.words.length > 1) this.subgroupsTranscriptions(wordInkDocMap);
+ }
+ }
+ const text = exports['text/plain'];
+ // console.log(text);
+
+ if (this.currGroup) {
+ // console.log("curr grouping");
+ this.currGroup.transcription = text;
+ this.currGroup.title = text.split("\n")[0];
+ }
+
+ ref.editor.clear();
+ }
+ }
+ }
+
+ render() {
+ return (
+ <div className="ink-transcription">
+ <div className='math-editor'
+ ref={this.setMathRef}
+ touch-action="none">
+ </div>
+ <div className='text-editor'
+ ref={this.setTextRef}
+ touch-action="none">
+ </div>
+ </div>
+ )
+ }
+} \ No newline at end of file
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index f97b713dd..9a3e247aa 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -270,6 +270,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
(point.X - inkLeft - inkStrokeWidth / 2) * inkScaleX + inkStrokeWidth / 2,
(point.Y - inkTop - inkStrokeWidth / 2) * inkScaleY + inkStrokeWidth / 2)).map(p => ({ X: p[0], Y: p[1] }));
const screenHdlPts = screenPts;
+ console.log(screenPts);
const startMarker = StrCast(this.layoutDoc.strokeStartMarker);
const endMarker = StrCast(this.layoutDoc.strokeEndMarker);
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index 9882693ee..dd415212c 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -5,7 +5,7 @@ import "normalize.css";
import * as React from 'react';
import { Doc, DocListCast, Opt } from '../../fields/Doc';
import { Cast, NumCast, StrCast } from '../../fields/Types';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, Utils } from '../../Utils';
import { DocUtils } from '../documents/Documents';
import { DocumentManager } from '../util/DocumentManager';
import { LinkManager } from '../util/LinkManager';
@@ -17,6 +17,7 @@ import "./LightboxView.scss";
import { DocumentView } from './nodes/DocumentView';
import { DefaultStyleProvider, wavyBorderPath } from './StyleProvider';
import { CollectionMenu } from './collections/CollectionMenu';
+import { utils } from 'mocha';
interface LightboxViewProps {
PanelWidth: number;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 55190001b..e51aee40c 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -43,6 +43,7 @@ import { GestureOverlay } from './GestureOverlay';
import { DASHBOARD_SELECTOR_HEIGHT, LEFT_MENU_WIDTH } from './global/globalCssVariables.scss';
import { Colors } from './global/globalEnums';
import { KeyManager } from './GlobalKeyHandler';
+import { InkTranscription } from './InkTranscription';
import { LightboxView } from './LightboxView';
import { LinkMenu } from './linking/LinkMenu';
import "./MainView.scss";
@@ -692,6 +693,7 @@ export class MainView extends React.Component {
<OverlayView />
<TimelineMenu />
<RichTextMenu />
+ <InkTranscription />
{this.snapLines}
<div className="mainView-webRef" ref={this.makeWebRef} />
<LightboxView PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} />
diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx
index 789756a78..c712e7ed3 100644
--- a/src/client/views/Touchable.tsx
+++ b/src/client/views/Touchable.tsx
@@ -127,11 +127,13 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
}
handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>): any => {
+ console.log("getting to handle1PointersMove");
e.stopPropagation();
e.preventDefault();
}
handle2PointersMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>): any => {
+ console.log("getting to handle2PointersMove");
e.stopPropagation();
e.preventDefault();
}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 509da18cb..140b33870 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -374,7 +374,7 @@ export class CollectionDockingView extends CollectionSubView() {
}
}
if (!e.nativeEvent.cancelBubble && !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) &&
- ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
+ ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
e.stopPropagation();
}
}
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 96029a8e4..23fd4206c 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -731,6 +731,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
@computed get drawButtons() {
const func = action((e: React.MouseEvent | React.PointerEvent, i: number, keep: boolean) => {
this._keepPrimitiveMode = keep;
+ // these are for shapes
if (this._selectedPrimitive !== i) {
this._selectedPrimitive = i;
if (this._title[i] === "highlighter") {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 259cc1ee5..e0a7c52b4 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -3,7 +3,7 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction
import { observer } from "mobx-react";
import { computedFn } from "mobx-utils";
import { DateField } from "../../../../fields/DateField";
-import { DataSym, Doc, HeightSym, Opt, StrListCast, WidthSym } from "../../../../fields/Doc";
+import { Doc, DocListCast, HeightSym, Opt, StrListCast, WidthSym } from "../../../../fields/Doc";
import { Id } from "../../../../fields/FieldSymbols";
import { InkData, InkField, InkTool, PointData, Segment } from "../../../../fields/InkField";
import { List } from "../../../../fields/List";
@@ -18,7 +18,7 @@ import { GestureUtils } from "../../../../pen-gestures/GestureUtils";
import { aggregateBounds, emptyFunction, intersectRect, returnFalse, setupMoveUpEvents, Utils } from "../../../../Utils";
import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
import { DocServer } from "../../../DocServer";
-import { Docs, DocUtils } from "../../../documents/Documents";
+import { Docs, DocumentOptions, DocUtils } from "../../../documents/Documents";
import { DocumentType } from "../../../documents/DocumentTypes";
import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
import { DocumentManager } from "../../../util/DocumentManager";
@@ -56,6 +56,7 @@ import "./CollectionFreeFormView.scss";
import { MarqueeView } from "./MarqueeView";
import React = require("react");
import { FieldView, FieldViewProps } from "../../nodes/FieldView";
+import { InkTranscription } from "../../InkTranscription";
export const panZoomSchema = createSchema({
_panX: "number",
@@ -105,6 +106,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
private _lastTap = 0;
private _batch: UndoManager.Batch | undefined = undefined;
+ // private isWritingMode: boolean = true;
+ // private writingModeDocs: Doc[] = [];
+
private get isAnnotationOverlay() { return this.props.isAnnotationOverlay; }
private get scaleFieldKey() { return this.props.scaleField || "_viewScale"; }
private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; }
@@ -121,6 +125,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@observable _deleteList: DocumentView[] = [];
@observable _timelineRef = React.createRef<Timeline>();
@observable _marqueeRef = React.createRef<HTMLDivElement>();
+ @observable _marqueeViewRef = React.createRef<MarqueeView>();
@observable _keyframeEditing = false;
@observable ChildDrag: DocumentView | undefined; // child document view being dragged. needed to update drop areas of groups when a group item is dragged.
@@ -437,6 +442,19 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
@action
+ onPenUp = (e: PointerEvent): void => {
+ if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
+ document.removeEventListener("pointerup", this.onPenUp);
+ const currentCol = DocListCast(this.rootDoc.currentInkDoc)
+ const rootDocList = DocListCast(this.rootDoc.data);
+ currentCol.push(rootDocList[rootDocList.length - 1]);
+ console.log(currentCol);
+
+ this._batch?.end();
+ }
+ }
+
+ @action
onPointerDown = (e: React.PointerEvent): void => {
this._downX = this._lastX = e.pageX;
this._downY = this._lastY = e.pageY;
@@ -447,7 +465,12 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
!InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
switch (CurrentUserUtils.SelectedTool) {
case InkTool.Highlighter:
- case InkTool.Pen: break; // the GestureOverlay handles ink stroke input -- either as gestures, or drying as ink strokes that are added to document views
+ break;
+ // TODO: nda - this where we want to create the new "writingDoc" collection that we add strokes to
+ case InkTool.Write:
+ break;
+ case InkTool.Pen:
+ break; // the GestureOverlay handles ink stroke input -- either as gestures, or drying as ink strokes that are added to document views
case InkTool.Eraser:
document.addEventListener("pointermove", this.onEraserMove);
document.addEventListener("pointerup", this.onEraserUp);
@@ -493,6 +516,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
}
+ public unprocessedDocs: Doc[] = [];
+ public static collectionsWithUnprocessedInk = new Set<CollectionFreeFormView>();
@undoBatch
onGesture = (e: Event, ge: GestureUtils.GestureEvent) => {
switch (ge.gesture) {
@@ -501,6 +526,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points,
{ title: "ink stroke", x: B.x - ActiveInkWidth() / 2, y: B.y - ActiveInkWidth() / 2, _width: B.width + ActiveInkWidth(), _height: B.height + ActiveInkWidth() });
+ if (CurrentUserUtils.SelectedTool === InkTool.Write) {
+ this.unprocessedDocs.push(inkDoc);
+ CollectionFreeFormView.collectionsWithUnprocessedInk.add(this);
+ }
this.addDocument(inkDoc);
e.stopPropagation();
break;
@@ -685,8 +714,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
onPointerMove = (e: PointerEvent): void => {
+ console.log("this is onPointerMove");
if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return;
if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
+ Doc.UserDoc().activeInkTool = InkTool.None;
if (this.props.isContentActive(true)) e.stopPropagation();
} else if (!e.cancelBubble) {
if (this.tryDragCluster(e, this._hitCluster)) {
@@ -806,6 +837,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => {
+ console.log("getting here yeet");
if (!e.cancelBubble) {
const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true);
if (myTouches[0]) {
@@ -817,6 +849,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
document.removeEventListener("pointerup", this.onPointerUp);
return;
}
+ // TODO: nda - this allows us to pan collections with finger -> only want to do this when collection is selected'
this.pan(myTouches[0]);
}
}
@@ -1579,6 +1612,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: "compress-arrows-alt" });
this.props.ContainingCollectionView &&
appearanceItems.push({ description: "Ungroup collection", event: this.promoteCollection, icon: "table" });
+
+ this.props.Document._isGroup && this.Document.transcription && appearanceItems.push({ description: "Ink to text", event: () => this.transcribeStrokes(false), icon: "font" });
+
+ // this.props.Document._isGroup && this.childDocs.filter(s => s.type === DocumentType.INK).length > 0 && appearanceItems.push({ description: "Ink to math", event: () => this.transcribeStrokes(true), icon: "square-root-alt" });
+
!Doc.UserDoc().noviceMode ? appearanceItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }) : null;
!appearance && ContextMenu.Instance.addItem({ description: "Appearance...", subitems: appearanceItems, icon: "eye" });
@@ -1636,6 +1674,21 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
input.click();
}
+ @undoBatch
+ @action
+ transcribeStrokes = (math: boolean) => {
+ if (this.props.Document._isGroup && this.props.Document.transcription) {
+ if (!math) {
+ const text = StrCast(this.props.Document.transcription);
+
+ const lines = text.split("\n");
+ const height = 30 + 15 * lines.length;
+
+ this.props.ContainingCollectionView?.addDocument(Docs.Create.TextDocument(text, { title: lines[0], x: NumCast(this.layoutDoc.x) + NumCast(this.layoutDoc._width) + 20, y: NumCast(this.layoutDoc.y), _width: 200, _height: height }));
+ }
+ }
+ }
+
@action
setupDragLines = (snapToDraggedDoc: boolean = false) => {
const activeDocs = this.getActiveDocuments();
@@ -1698,6 +1751,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
TraceMobx();
return <MarqueeView
{...this.props}
+ ref={this._marqueeViewRef}
ungroup={this.props.Document._isGroup ? this.promoteCollection : undefined}
nudge={this.isAnnotationOverlay || this.props.renderDepth > 0 ? undefined : this.nudge}
addDocTab={this.addDocTab}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index 1f59f9732..8a8b528f6 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -14,7 +14,6 @@ export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
public createCollection: (e: KeyboardEvent | React.PointerEvent | undefined, group?: boolean) => void = unimplementedFunction;
public delete: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
public summarize: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
- public inkToText: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
public showMarquee: () => void = unimplementedFunction;
public hideMarquee: () => void = unimplementedFunction;
public pinWithView: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
@@ -64,16 +63,6 @@ export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
</button>
</Tooltip>,
];
- if (false && !SelectionManager.Views().some(v => v.props.Document.type !== DocumentType.INK)) {
- buttons.push(
- <Tooltip key="inkToText" title={<div className="dash-tooltip">Change to Text</div>} placement="bottom">
- <button
- className="antimodeMenu-button"
- onPointerDown={this.inkToText}>
- <FontAwesomeIcon icon="font" size="lg" />
- </button>
- </Tooltip>);
- }
return this.getElement(buttons);
}
} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 29bdae170..40851a88a 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -58,7 +58,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@computed get Transform() { return this.props.getTransform(); }
@computed get Bounds() {
+ // nda - ternary argument to transformPoint is returning the lower of the downX/Y and lastX/Y and passing in as args x,y
const topLeft = this.Transform.transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY);
+ // nda - args to transformDirection is just x and y diff btw downX/Y and lastX/Y
const size = this.Transform.transformDirection(this._lastX - this._downX, this._lastY - this._downY);
return { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) };
}
@@ -263,7 +265,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
MarqueeOptionsMenu.Instance.createCollection = this.collection;
MarqueeOptionsMenu.Instance.delete = this.delete;
MarqueeOptionsMenu.Instance.summarize = this.summary;
- MarqueeOptionsMenu.Instance.inkToText = this.syntaxHighlight;
+ // MarqueeOptionsMenu.Instance.inkToText = this.syntaxHighlight;
MarqueeOptionsMenu.Instance.showMarquee = this.showMarquee;
MarqueeOptionsMenu.Instance.hideMarquee = this.hideMarquee;
MarqueeOptionsMenu.Instance.jumpTo(e.clientX, e.clientY);
@@ -434,6 +436,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
}));
this.props.removeDocument?.(selected);
}
+ // TODO: nda - this is the code to actually get a new grouped collection
const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t" ? Docs.Create.StackingDocument : undefined, group);
this.props.addDocument?.(newCollection);
this.props.selectDocuments([newCollection]);
@@ -642,7 +645,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
return <div className="marqueeView"
style={{
overflow: StrCast(this.props.Document._overflow),
- cursor: CurrentUserUtils.SelectedTool === InkTool.Pen ? "crosshair" : "pointer"
+ cursor: CurrentUserUtils.SelectedTool === InkTool.Pen || CurrentUserUtils.SelectedTool === InkTool.Write ? "crosshair" : "pointer"
}}
onDragOver={e => e.preventDefault()}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 6b0b92889..373e3ee57 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -541,7 +541,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
onPointerDown = (e: React.PointerEvent): void => {
if (this.rootDoc.type === DocumentType.INK && CurrentUserUtils.SelectedTool === InkTool.Eraser) return;
// continue if the event hasn't been canceled AND we are using a mouse or this has an onClick or onDragStart function (meaning it is a button document)
- if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || [InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool))) {
+ if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool))) {
if (!InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
e.stopPropagation();
if (SelectionManager.IsSelected(this.props.DocumentView(), true) && this.props.Document._viewType !== CollectionViewType.Docking) e.preventDefault(); // goldenlayout needs to be able to move its tabs, so can't preventDefault for it
@@ -575,7 +575,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
onPointerMove = (e: PointerEvent): void => {
if (e.cancelBubble) return;
- if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool))) return;
+ if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool))) return;
if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) {
if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
diff --git a/src/client/views/nodes/EquationBox.scss b/src/client/views/nodes/EquationBox.scss
index 6c9d53d10..229a1a485 100644
--- a/src/client/views/nodes/EquationBox.scss
+++ b/src/client/views/nodes/EquationBox.scss
@@ -1,3 +1,39 @@
+@import "../global/globalCssVariables.scss";
+
.equationBox-cont {
transform-origin: top left;
+ overflow: visible;
+ width: 100%;
+ height: 100%;
+}
+
+.button {
+ position: absolute;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ top: 0;
+ right: 0;
+ width: 20px;
+ height: 20px;
+ border-radius: 5px;
+ background: $dark-gray;
+ color: white;
+
+ svg {
+ width: 12px;
+ height: 12px;
+ }
+}
+
+.ink-editor {
+ top: 20px;
+ min-width: 500px;
+ min-height: 300px;
+ background: $light-gray;
+ pointer-events: all;
+
+ button {
+ float: right;
+ }
} \ No newline at end of file
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index dd0acb311..ab4ed6b33 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -357,7 +357,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
return <div className="imageBox-annotationLayer" style={{ height: this.props.PanelHeight() }} ref={this._annotationLayer} />;
}
marqueeDown = (e: React.PointerEvent) => {
- if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
+ if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
setupMoveUpEvents(this, e, action(e => {
MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
this._marqueeing = [e.clientX, e.clientY];
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index c0fd8d8a0..9f1c019fe 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -471,7 +471,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@action
onMarqueeDown = (e: React.PointerEvent) => {
- if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
+ if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
setupMoveUpEvents(this, e, action(e => {
MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
this._marqueeing = [e.clientX, e.clientY];
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index dbbcdc9de..28af6bfa1 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -559,7 +559,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@action
onMarqueeDown = (e: React.PointerEvent) => {
- if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
+ if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
setupMoveUpEvents(this, e, action(e => {
MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
this._marqueeing = [e.clientX, e.clientY];
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 625f5d14a..3e18e86b3 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -5,17 +5,20 @@ import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { ColorState, SketchPicker } from 'react-color';
-import { Doc, StrListCast } from '../../../../fields/Doc';
+import { DataSym, Doc, DocListCast, HeightSym, LayoutSym, StrListCast, WidthSym } from '../../../../fields/Doc';
import { InkTool } from '../../../../fields/InkField';
import { createSchema } from '../../../../fields/Schema';
import { ScriptField } from '../../../../fields/ScriptField';
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
import { WebField } from '../../../../fields/URLField';
-import { Utils } from '../../../../Utils';
+import { aggregateBounds, Utils } from '../../../../Utils';
import { DocumentType } from '../../../documents/DocumentTypes';
+import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
+import { DocumentManager } from '../../../util/DocumentManager';
import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
import { SelectionManager } from '../../../util/SelectionManager';
import { undoBatch, UndoManager } from '../../../util/UndoManager';
+import { CollectionFreeFormView } from '../../collections/collectionFreeForm';
import { CollectionViewType } from '../../collections/CollectionView';
import { ContextMenu } from '../../ContextMenu';
import { DocComponent } from '../../DocComponent';
@@ -23,6 +26,7 @@ import { EditableView } from '../../EditableView';
import { GestureOverlay } from '../../GestureOverlay';
import { Colors } from '../../global/globalEnums';
import { ActiveFillColor, ActiveInkColor, ActiveInkWidth, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
+import { InkTranscription } from '../../InkTranscription';
import { StyleProp } from '../../StyleProvider';
import { FieldView, FieldViewProps } from '.././FieldView';
import { RichTextMenu } from '../formattedText/RichTextMenu';
@@ -260,7 +264,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
"Comic Sans MS", "Tahoma", "Impact", "Crimson Text"];
}
} catch (e) {
- console.log(e);
+ // console.log(e);
}
// Get items to place into the list
@@ -705,6 +709,82 @@ ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) {
+export function checkInksToGroup() {
+ // console.log("getting here to inks group");
+ if (CurrentUserUtils.SelectedTool === InkTool.Write) {
+ CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
+ // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
+ // find all inkDocs in ffView.unprocessedDocs that are within 200 pixels of each other
+ const inksToGroup = ffView.unprocessedDocs.filter(inkDoc => {
+ // console.log(inkDoc.x, inkDoc.y);
+ });
+ });
+ }
+}
+
+export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) {
+ // TODO nda - if document being added to is a inkGrouping then we can just add to that group
+ if (CurrentUserUtils.SelectedTool === InkTool.Write) {
+ CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
+ // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
+ const selected = ffView.unprocessedDocs;
+ // loop through selected an get the bound
+ const bounds: { x: number, y: number, width?: number, height?: number }[] = []
+
+ selected.map(action(d => {
+ const x = NumCast(d.x);
+ const y = NumCast(d.y);
+ const width = d[WidthSym]();
+ const height = d[HeightSym]();
+ bounds.push({ x, y, width, height });
+ }))
+
+ const aggregBounds = aggregateBounds(bounds, 0, 0);
+ const marqViewRef = ffView._marqueeViewRef.current;
+
+ // set the vals for bounds in marqueeView
+ if (marqViewRef) {
+ marqViewRef._downX = aggregBounds.x;
+ marqViewRef._downY = aggregBounds.y;
+ marqViewRef._lastX = aggregBounds.r;
+ marqViewRef._lastY = aggregBounds.b;
+ }
+
+ selected.map(action(d => {
+ const dx = NumCast(d.x);
+ const dy = NumCast(d.y);
+ delete d.x;
+ delete d.y;
+ delete d.activeFrame;
+ delete d._timecodeToShow; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+ delete d._timecodeToHide; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+ // calculate pos based on bounds
+ if (marqViewRef?.Bounds) {
+ d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2;
+ d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2;
+ }
+ return d;
+ }));
+ ffView.props.removeDocument?.(selected);
+ // TODO: nda - this is the code to actually get a new grouped collection
+ const newCollection = marqViewRef?.getCollection(selected, undefined, [], true);
+ if (newCollection) {
+ newCollection.height = newCollection[HeightSym]();
+ newCollection.width = newCollection[WidthSym]();
+ }
+
+ // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
+ newCollection && ffView.props.addDocument?.(newCollection);
+ // TODO: nda - will probably need to go through and only remove the unprocessed selected docs
+ ffView.unprocessedDocs = [];
+
+ InkTranscription.Instance.transcribeInk(newCollection, ffView.layoutDoc, selected, false, ffView);
+ });
+ }
+ CollectionFreeFormView.collectionsWithUnprocessedInk.clear();
+}
+
+
/** INK
* setActiveInkTool
* setStrokeWidth
@@ -712,6 +792,8 @@ ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) {
**/
ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boolean) {
+ createInkGroup();
+
if (checkResult) {
return ((Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ?
Colors.MEDIUM_BLUE : "transparent";
@@ -727,6 +809,10 @@ ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boole
} else if (tool) { // pen or eraser
if (Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance.InkShape) {
Doc.UserDoc().activeInkTool = InkTool.None;
+ } else if (tool == "write") {
+ // console.log("write mode selected - create groupDoc here!", tool)
+ Doc.UserDoc().activeInkTool = tool;
+ GestureOverlay.Instance.InkShape = "";
} else {
Doc.UserDoc().activeInkTool = tool;
GestureOverlay.Instance.InkShape = "";
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index ce82821b6..e866e96d6 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -754,6 +754,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
}
+ // TODO: nda -- Look at how link anchors are added
makeLinkAnchor(anchorDoc?: Doc, location?: string, targetHref?: string, title?: string) {
const state = this._editorView?.state;
if (state) {
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 305b1fe68..ebf886eec 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -333,7 +333,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
if ((e.button !== 0 || e.altKey) && this.props.isContentActive(true)) {
this._setPreviewCursor?.(e.clientX, e.clientY, true, false);
}
- if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
+ if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
this.props.select(false);
MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
this._marqueeing = [e.clientX, e.clientY];
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 152b7bbcb..321af69a1 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -108,6 +108,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
this.selectElement(doc, () => DocumentManager.Instance.getFirstDocumentView(doc)?.ComponentView?.search?.(this._searchString, undefined, false));
});
+ // TODO: nda -- Change this method to change what happens when you click on the item.
makeLink = action((linkTo: Doc) => {
if (this.props.linkFrom) {
const linkFrom = this.props.linkFrom();
diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts
index 31024e805..31fa6dfac 100644
--- a/src/fields/InkField.ts
+++ b/src/fields/InkField.ts
@@ -11,7 +11,8 @@ export enum InkTool {
Pen = "pen",
Highlighter = "highlighter",
Eraser = "eraser",
- Stamp = "stamp"
+ Stamp = "stamp",
+ Write = "write"
}
@@ -83,7 +84,7 @@ export class InkField extends ObjectField {
}
[ToScriptString]() {
- return "new InkField([" + this.inkData.map(i => `{X: ${i.X}, Y: ${i.Y}} `) + "])";
+ return "new InkField([" + this.inkData.map(i => `{X: ${i.X}, Y: ${i.Y}`) + "])";
}
[ToString]() {
return "InkField";
diff --git a/src/typings/index.d.ts b/src/typings/index.d.ts
index 068ac2159..bc7727426 100644
--- a/src/typings/index.d.ts
+++ b/src/typings/index.d.ts
@@ -8,6 +8,7 @@ declare module 'webrtc-adapter';
declare module 'bezier-curve';
declare module 'fit-curve';
declare module 'react-audio-waveform';
+declare module 'iink-js';
declare module 'reveal';
declare module 'react-reveal';