diff options
6 files changed, 78 insertions, 101 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 64583ae55..1c13eb079 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -138,6 +138,7 @@ export interface DocumentOptions { textTransform?: string; // is linear view expanded letterSpacing?: string; // is linear view expanded flexDirection?: "unset" | "row" | "column" | "row-reverse" | "column-reverse"; + selectedIndex?: number; } class EmptyBox { diff --git a/src/client/views/GestureOverlay.scss b/src/client/views/GestureOverlay.scss index f425c438e..2fad87ee0 100644 --- a/src/client/views/GestureOverlay.scss +++ b/src/client/views/GestureOverlay.scss @@ -17,19 +17,18 @@ position: absolute; width: 300px; overflow: hidden; + pointer-events: none; .inkToTextDoc-scroller { overflow: visible; position: absolute; width: 100%; - left: -24px; - .collectionView { - overflow: visible; - - .collectionLinearView-outer { - overflow: visible; - } + .menuItem-cont { + width: 100%; + height: 20px; + padding: 2.5px; + border-bottom: .5px solid black; } } diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 0fd6f3dba..e8262af1b 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -26,7 +26,7 @@ import { ScriptField } from "../../new_fields/ScriptField"; import { listSpec } from "../../new_fields/Schema"; import { List } from "../../new_fields/List"; import { CollectionViewType } from "./collections/CollectionView"; -import InkCanvas from "./InkCanvas"; +import TouchScrollableMenu, { TouchScrollableMenuItem } from "./TouchScrollableMenu"; @observer export default class GestureOverlay extends Touchable { @@ -40,12 +40,15 @@ export default class GestureOverlay extends Touchable { @observable private _thumbX?: number; @observable private _thumbY?: number; + @observable private _selectedIndex: number = -1; + @observable private _menuX: number = -300; + @observable private _menuY: number = -300; @observable private _pointerY?: number; @observable private _points: { X: number, Y: number }[] = []; @observable private _strokes: InkData[] = []; @observable private _palette?: JSX.Element; @observable private _clipboardDoc?: JSX.Element; - @observable private _possibilities: Doc[] = []; + @observable private _possibilities: JSX.Element[] = []; @computed private get height(): number { return Math.max(this._pointerY && this._thumbY ? this._thumbY - this._pointerY : 300, 300); } @computed private get showBounds() { return this.Tool !== ToolglassTools.None; } @@ -254,6 +257,8 @@ export default class GestureOverlay extends Touchable { this._thumbDoc = thumbDoc; this._thumbX = thumb.clientX; this._thumbY = thumb.clientY; + this._menuX = thumb.clientX + 50; + this._menuY = thumb.clientY; this._palette = <HorizontalPalette x={minX} y={minY} thumb={[thumb.clientX, thumb.clientY]} thumbDoc={thumbDoc} />; }); } @@ -294,15 +299,14 @@ export default class GestureOverlay extends Touchable { const pt = e.changedTouches.item(i); if (pt && pt.identifier === this.thumbIdentifier && this._thumbY) { if (this._thumbX && this._thumbDoc) { - if (Math.abs(pt.clientX - this._thumbX) > 20) { + if (Math.abs(pt.clientX - this._thumbX) > 30) { this._thumbDoc.selectedIndex = Math.max(0, NumCast(this._thumbDoc.selectedIndex) - Math.sign(pt.clientX - this._thumbX)); this._thumbX = pt.clientX; } } if (this._thumbY && this._inkToTextDoc) { if (Math.abs(pt.clientY - this._thumbY) > 20) { - this._inkToTextDoc.selectedIndex = Math.max(0, NumCast(this._inkToTextDoc.selectedIndex) - Math.sign(pt.clientY - this._thumbY)); - this._thumbY = pt.clientY; + this._selectedIndex = Math.max(0, -Math.floor((pt.clientY - this._thumbY) / 20)); } } } @@ -324,11 +328,10 @@ export default class GestureOverlay extends Touchable { let scriptWorked = false; if (NumCast(this._inkToTextDoc?.selectedIndex) > -1) { const selectedButton = this._possibilities[NumCast(this._inkToTextDoc?.selectedIndex)]; - if (Cast(selectedButton?.proto?.onClick, ScriptField)?.script.run({ this: selectedButton }, console.log).success) { + if (selectedButton) { + selectedButton.props.onClick(); scriptWorked = true; - console.log("success"); - }; - console.log(Cast(selectedButton?.proto?.onClick, ScriptField)?.script); + } } if (!scriptWorked) { @@ -337,6 +340,7 @@ export default class GestureOverlay extends Touchable { }); } this._strokes = []; + this._points = []; this._possibilities = []; document.removeEventListener("touchend", this.handleHandUp); } @@ -398,12 +402,12 @@ export default class GestureOverlay extends Touchable { } @action - onPointerUp = async (e: PointerEvent) => { + onPointerUp = (e: PointerEvent) => { if (this._points.length > 1) { const B = this.svgBounds; const points = this._points.map(p => ({ X: p.X - B.left, Y: p.Y - B.top })); - const initialPoint = this._points[0]; + const initialPoint = this._points[0.]; const xInGlass = initialPoint.X > (this._thumbX ?? Number.MAX_SAFE_INTEGER) && initialPoint.X < (this._thumbX ?? Number.MAX_SAFE_INTEGER) + this.height; const yInGlass = initialPoint.Y > (this._thumbY ?? Number.MAX_SAFE_INTEGER) - this.height && initialPoint.Y < (this._thumbY ?? Number.MAX_SAFE_INTEGER); @@ -414,28 +418,21 @@ export default class GestureOverlay extends Touchable { document.removeEventListener("pointerup", this.onPointerUp); this._strokes.push(new Array(...this._points)); this._points = []; - const results = await CognitiveServices.Inking.Appliers.InterpretStrokes(this._strokes); - const wordResults = results.filter((r: any) => r.category === "inkWord"); - const possibilities: string[] = []; - if (wordResults[0]?.recognizedText) { - possibilities.push(wordResults[0]?.recognizedText) - } - possibilities.push(...wordResults[0]?.alternates?.map((a: any) => a.recognizedString)); - const r = Math.max(this.svgBounds.right, ...this._strokes.map(s => this.getBounds(s).right)); - const l = Math.min(this.svgBounds.left, ...this._strokes.map(s => this.getBounds(s).left)); - const t = Math.min(this.svgBounds.top, ...this._strokes.map(s => this.getBounds(s).top)); - runInAction(() => { - const buttons = possibilities.map(p => Docs.Create.ButtonDocument({ - _height: 25, _width: r - l, backgroundColor: "lightgrey", color: "rgb(34, 34, 34)", x: l, y: t, _viewType: CollectionViewType.Linear, - isExpanded: true, - title: p, fontSize: 10, letterSpacing: "0px", textTransform: "unset", boxShadow: ".5px .5px 0px rgb(34, 34, 34)", - onClick: ScriptField.MakeScript('createText(this.proto.title, this.x, this.y)') - })); - if (this._inkToTextDoc && this._thumbDoc) { - this._inkToTextDoc = this._thumbDoc.inkToTextDoc = Docs.Create.LinearDocument(buttons, { _width: 300, _height: 25, _autoHeight: true, _chromeStatus: "disabled", isExpanded: true, flexDirection: "column" }); - this._inkToTextDoc.selectedIndex = 0; + CognitiveServices.Inking.Appliers.InterpretStrokes(this._strokes).then((results) => { + const wordResults = results.filter((r: any) => r.category === "inkWord" || r.category === "paragraph"); + const possibilities: string[] = []; + if (wordResults[0]?.recognizedText) { + possibilities.push(wordResults[0]?.recognizedText) } - this._possibilities = buttons; + possibilities.push(...wordResults[0]?.alternates?.map((a: any) => a.recognizedString)); + console.log(possibilities); + const r = Math.max(this.svgBounds.right, ...this._strokes.map(s => this.getBounds(s).right)); + const l = Math.min(this.svgBounds.left, ...this._strokes.map(s => this.getBounds(s).left)); + const t = Math.min(this.svgBounds.top, ...this._strokes.map(s => this.getBounds(s).top)); + runInAction(() => { + this._possibilities = possibilities.map(p => + <TouchScrollableMenuItem text={p} onClick={() => GestureOverlay.Instance.dispatchGesture(GestureUtils.Gestures.Text, [{ X: l, Y: t }], p)} />); + }); }); break; case ToolglassTools.IgnoreGesture: @@ -511,10 +508,9 @@ export default class GestureOverlay extends Touchable { return [ this.props.children, this._palette, - [this._strokes.map(l => { const b = this.getBounds(l); - return <svg width={b.width} height={b.height} style={{ transform: `translate(${b.left}px, ${b.top}px)`, pointerEvents: "none", position: "absolute", zIndex: 30000 }}> + return <svg key={b.left} width={b.width} height={b.height} style={{ transform: `translate(${b.left}px, ${b.top}px)`, pointerEvents: "none", position: "absolute", zIndex: 30000 }}> {InteractionUtils.CreatePolyline(l, b.left, b.top, GestureOverlay.Instance.Color, GestureOverlay.Instance.Width)} </svg>; }), @@ -558,58 +554,6 @@ export default class GestureOverlay extends Touchable { this._clipboardDoc = undefined; } - @computed - private get suggestionContent() { - const b = Math.max(this.svgBounds.bottom, ...this._strokes.map(s => this.getBounds(s).bottom)); - const r = Math.max(this.svgBounds.right, ...this._strokes.map(s => this.getBounds(s).right)); - const l = Math.min(this.svgBounds.left, ...this._strokes.map(s => this.getBounds(s).left)); - const t = Math.min(this.svgBounds.top, ...this._strokes.map(s => this.getBounds(s).top)); - return ( - <div className="inkToTextDoc-cont" style={{ - transform: `translate(${l}px, ${b}px)`, - width: r - l, - height: this._possibilities.length * 25 - }}> - <div className="inkToTextDoc-scroller" style={{ transform: `translate(0, ${-NumCast(this._inkToTextDoc!.selectedIndex) * 25}px)` }}> - <DocumentView - Document={this._inkToTextDoc!} - DataDoc={undefined} - LibraryPath={emptyPath} - addDocument={undefined} - addDocTab={returnFalse} - pinToPres={emptyFunction} - onClick={undefined} - removeDocument={undefined} - ScreenToLocalTransform={() => new Transform(-l, -(b + NumCast(this._inkToTextDoc?.selectedIndex, 0) * 25), 1)} - ContentScaling={returnOne} - PanelWidth={() => 300} - PanelHeight={() => 300} - renderDepth={0} - backgroundColor={returnEmptyString} - focus={emptyFunction} - parentActive={returnTrue} - whenActiveChanged={emptyFunction} - bringToFront={emptyFunction} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} - zoomToScale={emptyFunction} - getScale={returnOne} - />; - </div> - <div className="shadow" style={{ height: `calc(100% - 25px - ${NumCast(this._inkToTextDoc!.selectedIndex) * 25}px)` }}> - </div> - </div>) - } - - private get inkToTextSuggestions() { - if (this._inkToTextDoc && this._possibilities.length) { - return ( - this.suggestionContent - ); - } - return null; - } - render() { trace(); return ( @@ -618,8 +562,8 @@ export default class GestureOverlay extends Touchable { <div className="clipboardDoc-cont" style={{ transform: `translate(${this._thumbX}px, ${(this._thumbY ?? 0) - this.height}px)`, - height: this.height, - width: this.height, + height: this.height * 2, + width: this.height * 2, pointerEvents: this._clipboardDoc ? "unset" : "none", touchAction: this._clipboardDoc ? "unset" : "none", }}> @@ -627,14 +571,14 @@ export default class GestureOverlay extends Touchable { </div> <div className="filter-cont" style={{ transform: `translate(${this._thumbX}px, ${(this._thumbY ?? 0) - this.height}px)`, - height: this.height, - width: this.height, + height: this.height * 2, + width: this.height * 2, pointerEvents: "none", touchAction: "none", display: this.showBounds ? "unset" : "none", }}> </div> - {this.inkToTextSuggestions} + <TouchScrollableMenu options={this._possibilities} bounds={this.svgBounds} selectedIndex={this._selectedIndex} x={this._menuX} y={this._menuY} /> </div>); } } @@ -666,6 +610,5 @@ Scripting.addGlobal(function resetPen() { }); }); Scripting.addGlobal(function createText(text: any, x: any, y: any) { - console.log("creating"); GestureOverlay.Instance.dispatchGesture("text", [{ X: x, Y: y }], text); });
\ 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 17139d9d9..2b160f2ff 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -984,6 +984,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { </CollectionFreeFormViewPannableContents> </MarqueeView>; } + @computed get contentScaling() { if (this.props.annotationsKey) return 0; const hscale = this.nativeHeight ? this.props.PanelHeight() / this.nativeHeight : 1; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx index 71f265484..db4b674b5 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx @@ -11,6 +11,7 @@ export default class MarqueeOptionsMenu extends AntimodeMenu { public createCollection: (e: KeyboardEvent | React.PointerEvent | undefined) => 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; @@ -43,6 +44,13 @@ export default class MarqueeOptionsMenu extends AntimodeMenu { onPointerDown={this.delete}> <FontAwesomeIcon icon="trash-alt" size="lg" /> </button>, + <button + className="antimodeMenu-button" + title="Change to Text" + key="inkToText" + onPointerDown={this.inkToText}> + <FontAwesomeIcon icon="font" size="lg" /> + </button>, ]; return this.getElement(buttons); } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index ef2fc2ad1..112df8f05 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -1,12 +1,12 @@ import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast } from "../../../../new_fields/Doc"; +import { Doc, DocListCast, DataSym, WidthSym, HeightSym } from "../../../../new_fields/Doc"; import { InkField } from "../../../../new_fields/InkField"; import { List } from "../../../../new_fields/List"; import { listSpec } from "../../../../new_fields/Schema"; import { SchemaHeaderField } from "../../../../new_fields/SchemaHeaderField"; import { ComputedField } from "../../../../new_fields/ScriptField"; -import { Cast, NumCast, StrCast } from "../../../../new_fields/Types"; +import { Cast, NumCast, StrCast, FieldValue } from "../../../../new_fields/Types"; import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils"; import { Utils } from "../../../../Utils"; import { Docs } from "../../../documents/Documents"; @@ -19,6 +19,7 @@ import "./MarqueeView.scss"; import React = require("react"); import MarqueeOptionsMenu from "./MarqueeOptionsMenu"; import { SubCollectionViewProps } from "../CollectionSubView"; +import { CognitiveServices } from "../../../cognitive_services/CognitiveServices"; interface MarqueeViewProps { getContainerTransform: () => Transform; @@ -204,6 +205,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.showMarquee = this.showMarquee; MarqueeOptionsMenu.Instance.hideMarquee = this.hideMarquee; MarqueeOptionsMenu.Instance.jumpTo(e.clientX, e.clientY); @@ -362,6 +364,29 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque } @action + syntaxHighlight = (e: KeyboardEvent | React.PointerEvent | undefined) => { + const selected = this.marqueeSelect(false); + if (e instanceof KeyboardEvent ? e.key === "i" : true) { + const inks = selected.filter(s => s.proto?.type === "ink"); + const sets = selected.filter(s => s.proto?.type === "text") + const inkFields = inks.map(i => FieldValue(Cast(i.data, InkField))); + CognitiveServices.Inking.Appliers.InterpretStrokes(inkFields.filter(i => i instanceof InkField).map(i => i!.inkData)).then((results) => { + const wordResults = results.filter((r: any) => r.category === "inkWord"); + console.log(wordResults); + for (const word of wordResults) { + const indices: number[] = word.strokeIds; + const r = Math.floor(Math.random() * 256); + const g = Math.floor(Math.random() * 256); + const b = Math.floor(Math.random() * 256); + indices.forEach(i => { + inks[i].color = `rgb(${r}, ${g}, ${b})`; + }) + } + }); + } + } + + @action summary = (e: KeyboardEvent | React.PointerEvent | undefined) => { const bounds = this.Bounds; const selected = this.marqueeSelect(false); |