From 691d5500c0722d0af9ef3b3ab9036042970e75e9 Mon Sep 17 00:00:00 2001 From: Stanley Yip Date: Tue, 21 Jan 2020 13:35:06 -0500 Subject: cffview nested panning --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 01b978c81..132bf9c8e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -347,6 +347,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { this._lastX = pt.pageX; this._lastY = pt.pageY; e.preventDefault(); + e.stopPropagation(); } else { e.preventDefault(); @@ -362,7 +363,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { case GestureUtils.Gestures.Stroke: const points = ge.points; const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height); - const inkDoc = Docs.Create.InkDocument(InkingControl.Instance.selectedColor, InkingControl.Instance.selectedTool, parseInt(InkingControl.Instance.selectedWidth), points, { x: B.x, y: B.y, width: B.width, height: B.height }); + const inkDoc = Docs.Create.InkDocument(InkingControl.Instance.selectedColor, InkingControl.Instance.selectedTool, parseInt(InkingControl.Instance.selectedWidth), points, { title: "ink stroke", x: B.x, y: B.y, width: B.width, height: B.height }); this.addDocument(inkDoc); e.stopPropagation(); break; @@ -384,7 +385,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } return pass; }); - this.addDocument(Docs.Create.FreeformDocument(sel, { x: bounds.x, y: bounds.y, width: bWidth, height: bHeight, panX: 0, panY: 0 })); + this.addDocument(Docs.Create.FreeformDocument(sel, { title: "nested collection", x: bounds.x, y: bounds.y, width: bWidth, height: bHeight, panX: 0, panY: 0 })); sel.forEach(d => this.props.removeDocument(d)); break; -- cgit v1.2.3-70-g09d2 From b04bb12a5942d97eef369936e30b36db62b51f30 Mon Sep 17 00:00:00 2001 From: Stanley Yip Date: Mon, 27 Jan 2020 19:08:05 -0500 Subject: pen updates --- src/client/util/InteractionUtils.tsx | 5 ++- src/client/views/GestureOverlay.tsx | 52 +++++++++++++++++++--- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 28 ++++++------ src/pen-gestures/ndollar.ts | 7 ++- 5 files changed, 72 insertions(+), 22 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx index 1fe95474c..ad1d1b5de 100644 --- a/src/client/util/InteractionUtils.tsx +++ b/src/client/util/InteractionUtils.tsx @@ -63,7 +63,7 @@ export namespace InteractionUtils { export function GetMyTargetTouches(mte: InteractionUtils.MultiTouchEvent, prevPoints: Map, ignorePen: boolean): React.Touch[] { const myTouches = new Array(); for (const pt of mte.touches) { - if (!ignorePen || (pt.radiusX > 1 && pt.radiusY > 1)) { + if (!ignorePen || ((pt as any).radiusX > 1 && (pt as any).radiusY > 1)) { for (const tPt of mte.targetTouches) { if (tPt?.screenX === pt?.screenX && tPt?.screenY === pt?.screenY) { if (pt && prevPoints.has(pt.identifier)) { @@ -73,6 +73,9 @@ export namespace InteractionUtils { } } } + if (mte.touches.length !== myTouches.length) { + throw Error("opo") + } return myTouches; } diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index e8d685139..9dd87554f 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -20,6 +20,12 @@ import { DocumentView } from "./nodes/DocumentView"; import { Transform } from "../util/Transform"; import { DocumentContentsView } from "./nodes/DocumentContentsView"; +/** + * This class handles all of the gesture and touch events first. Native touch and pen + * events should be ignored by all classes and handled up here, and this class will interpret + * these events before dispatching our custom Dash gesture and touch events. Classes that want + * to use touch and pen events should handle custom Dash events, as opposed to native events. + */ @observer export default class GestureOverlay extends Touchable { static Instance: GestureOverlay; @@ -54,6 +60,15 @@ export default class GestureOverlay extends Touchable { GestureOverlay.Instance = this; } + /** + * @description + * Given a touch event, returns three arrays that represent the event's targetTouches, + * changedTouches, and touches after filtering out the touch events that are being handled + * as hands. This helps us separate hand events and touch events, as they are different + * events in the mental model that we are pursuing. + * @param e - Touch event to filter + * @returns \{ newTargetTouches, newChangedTouches, newTouches } + */ getNewTouches(e: React.TouchEvent | TouchEvent) { const ntt: (React.Touch | Touch)[] = Array.from(e.targetTouches); const nct: (React.Touch | Touch)[] = Array.from(e.changedTouches); @@ -61,6 +76,7 @@ export default class GestureOverlay extends Touchable { this._hands.forEach((hand) => { for (let i = 0; i < e.targetTouches.length; i++) { const pt = e.targetTouches.item(i); + // if there is a finger in this hand that matches the current point, ignore the current point if (pt && hand.some((finger) => finger.screenX === pt.screenX && finger.screenY === pt.screenY)) { ntt.splice(ntt.indexOf(pt), 1); } @@ -83,7 +99,11 @@ export default class GestureOverlay extends Touchable { return { ntt, nct, nt }; } + /** + * @description Handler for the native React touchStart event. + */ onReactTouchStart = (te: React.TouchEvent) => { + // clean up any ghost points that are remaining but don't actually exist const actualPts: React.Touch[] = []; for (let i = 0; i < te.touches.length; i++) { const pt: any = te.touches.item(i); @@ -91,9 +111,6 @@ export default 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) { - // if (typeof pt.identifier !== "string") { - // pt.identifier = Utils.GenerateGuid(); - // } this.prevPoints.set(pt.identifier, pt); } } @@ -106,10 +123,11 @@ export default class GestureOverlay extends Touchable { }); ptsToDelete.forEach(pt => this.prevPoints.delete(pt)); - const nts = this.getNewTouches(te); - console.log(nts.nt.length); + // decide whether we should be handling this as a hand event or a touch event + const nts = this.getNewTouches(te); if (nts.nt.length < 5) { + // dispatch a touch event const target = document.elementFromPoint(te.changedTouches.item(0).clientX, te.changedTouches.item(0).clientY); target?.dispatchEvent( new CustomEvent>("dashOnTouchStart", @@ -131,12 +149,17 @@ export default class GestureOverlay extends Touchable { document.addEventListener("touchend", this.onReactTouchEnd); } else { + // handle this event as a hand event this.handleHandDown(te); document.removeEventListener("touchmove", this.onReactTouchMove); document.removeEventListener("touchend", this.onReactTouchEnd); } } + /** + * @description Handler for the native React touchMove event. Filters and dispatches + * the custom Dash touchMove event. + */ onReactTouchMove = (e: TouchEvent) => { const nts: any = this.getNewTouches(e); document.dispatchEvent( @@ -154,7 +177,11 @@ export default class GestureOverlay extends Touchable { ); } + /** + * @description Handler for the native React touchEnd event. + */ onReactTouchEnd = (e: TouchEvent) => { + // filter and dispatch custom touchEnd event const nts: any = this.getNewTouches(e); document.dispatchEvent( new CustomEvent>("dashOnTouchEnd", @@ -169,6 +196,8 @@ export default class GestureOverlay extends Touchable { } }) ); + + // clean up any points that have ended for (let i = 0; i < e.changedTouches.length; i++) { const pt = e.changedTouches.item(i); if (pt) { @@ -178,6 +207,7 @@ export default class GestureOverlay extends Touchable { } } + // clean up events if (this.prevPoints.size === 0) { document.removeEventListener("touchmove", this.onReactTouchMove); document.removeEventListener("touchend", this.onReactTouchEnd); @@ -185,8 +215,12 @@ export default class GestureOverlay extends Touchable { e.stopPropagation(); } + /** + * @description Handler for "handDown" events + */ handleHandDown = async (e: React.TouchEvent) => { const fingers = new Array(); + // log all the fingers on the hand for (let i = 0; i < e.touches.length; i++) { const pt: any = e.touches.item(i); if (pt.radiusX > 1 && pt.radiusY > 1) { @@ -200,6 +234,8 @@ export default class GestureOverlay extends Touchable { } } } + + // figure out left/right hand and thumb/pointer finger const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]); const rightMost = Math.max(...fingers.map(f => f.clientX)); const leftMost = Math.min(...fingers.map(f => f.clientX)); @@ -229,6 +265,7 @@ export default class GestureOverlay extends Touchable { const minX = Math.min(...others.map(f => f.clientX)); const minY = Math.min(...others.map(f => f.clientY)); + // pull up the palette const thumbDoc = await Cast(CurrentUserUtils.setupThumbDoc(CurrentUserUtils.UserDocument), Doc); if (thumbDoc) { runInAction(() => { @@ -246,6 +283,9 @@ export default class GestureOverlay extends Touchable { document.addEventListener("touchend", this.handleHandUp); } + /** + * @description Handler for "handMove" event + */ @action handleHandMove = (e: TouchEvent) => { const fingers = new Array(); @@ -370,7 +410,7 @@ export default class GestureOverlay extends Touchable { else { const result = GestureUtils.GestureRecognizer.Recognize(new Array(points)); let actionPerformed = false; - if (result && result.Score > 0.7) { + if (result && result.Score > 0.8) { switch (result.Name) { case GestureUtils.Gestures.Box: const target = document.elementFromPoint(this._points[0].X, this._points[0].Y); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 132bf9c8e..a0c75c1b7 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -347,7 +347,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { this._lastX = pt.pageX; this._lastY = pt.pageY; e.preventDefault(); - e.stopPropagation(); + // e.stopPropagation(); } else { e.preventDefault(); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index e0913b154..82ee1bd63 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -316,21 +316,23 @@ export class DocumentView extends DocComponent(Docu handle1PointerDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent) => { if (this.Document.onPointerDown) return; const touch = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true)[0]; - this._downX = touch.clientX; - this._downY = touch.clientY; - if (!e.nativeEvent.cancelBubble) { - this._hitTemplateDrag = false; - for (let element = (e.target as any); element && !this._hitTemplateDrag; element = element.parentElement) { - if (element.className && element.className.toString() === "collectionViewBaseChrome-collapse") { - this._hitTemplateDrag = true; + if (touch) { + this._downX = touch.clientX; + this._downY = touch.clientY; + if (!e.nativeEvent.cancelBubble) { + this._hitTemplateDrag = false; + for (let element = (e.target as any); element && !this._hitTemplateDrag; element = element.parentElement) { + if (element.className && element.className.toString() === "collectionViewBaseChrome-collapse") { + this._hitTemplateDrag = true; + } } + if ((this.active || this.Document.onDragStart || this.Document.onClick) && !e.ctrlKey && !this.Document.lockedPosition && !this.Document.inOverlay) e.stopPropagation(); + this.removeMoveListeners(); + this.addMoveListeners(); + this.removeEndListeners(); + this.addEndListeners(); + e.stopPropagation(); } - if ((this.active || this.Document.onDragStart || this.Document.onClick) && !e.ctrlKey && !this.Document.lockedPosition && !this.Document.inOverlay) e.stopPropagation(); - this.removeMoveListeners(); - this.addMoveListeners(); - this.removeEndListeners(); - this.addEndListeners(); - e.stopPropagation(); } } diff --git a/src/pen-gestures/ndollar.ts b/src/pen-gestures/ndollar.ts index ef5ca38c6..9e15ada2d 100644 --- a/src/pen-gestures/ndollar.ts +++ b/src/pen-gestures/ndollar.ts @@ -168,7 +168,12 @@ export class NDollarRecognizer { // this.Multistrokes = new Array(NumMultistrokes); this.Multistrokes[0] = new Multistroke(GestureUtils.Gestures.Box, useBoundedRotationInvariance, new Array( - new Array(new Point(30, 146), new Point(30, 222), new Point(106, 225), new Point(106, 146), new Point(30, 146)) + new Array( + new Point(30, 146), //new Point(29, 160), new Point(30, 180), new Point(31, 200), + new Point(30, 222), //new Point(50, 219), new Point(70, 225), new Point(90, 230), + new Point(106, 225), //new Point(100, 200), new Point(106, 180), new Point(110, 160), + new Point(106, 146), //new Point(80, 150), new Point(50, 146), + new Point(30, 143)) )); this.Multistrokes[1] = new Multistroke(GestureUtils.Gestures.Line, useBoundedRotationInvariance, new Array( new Array(new Point(12, 347), new Point(119, 347)) -- cgit v1.2.3-70-g09d2 From 17dcf7e4d8c2f038dc3a6168b1ff6b8d3cb15536 Mon Sep 17 00:00:00 2001 From: Stanley Yip Date: Thu, 30 Jan 2020 18:39:20 -0500 Subject: i think some bugs are fixed --- src/client/util/InteractionUtils.tsx | 6 +++--- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx index ad1d1b5de..7194feb2e 100644 --- a/src/client/util/InteractionUtils.tsx +++ b/src/client/util/InteractionUtils.tsx @@ -73,9 +73,9 @@ export namespace InteractionUtils { } } } - if (mte.touches.length !== myTouches.length) { - throw Error("opo") - } + // if (mte.touches.length !== myTouches.length) { + // throw Error("opo") + // } return myTouches; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index a0c75c1b7..132bf9c8e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -347,7 +347,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { this._lastX = pt.pageX; this._lastY = pt.pageY; e.preventDefault(); - // e.stopPropagation(); + e.stopPropagation(); } else { e.preventDefault(); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 82ee1bd63..ad28c9b1c 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -316,6 +316,7 @@ export class DocumentView extends DocComponent(Docu handle1PointerDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent) => { if (this.Document.onPointerDown) return; const touch = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true)[0]; + console.log("down"); if (touch) { this._downX = touch.clientX; this._downY = touch.clientY; -- cgit v1.2.3-70-g09d2 From 1cb72c720d2e287032bfe586f25ebd941ab3f393 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 30 Jan 2020 23:16:19 -0500 Subject: small fixes to textbox sidebar. requires testing for annotationskey in COllectionSubView's childLayoutPairs --- src/client/views/collections/CollectionSubView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 14 +++++++------- src/client/views/nodes/FormattedTextBox.tsx | 9 +++++---- 3 files changed, 13 insertions(+), 12 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 8679c8bd1..9482435a0 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -98,7 +98,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { get childLayoutPairs(): { layout: Doc; data: Doc; }[] { const { Document, DataDoc } = this.props; - const validPairs = this.childDocs.map(doc => Doc.GetLayoutDataDocPair(Document, DataDoc, doc)).filter(pair => pair.layout); + const validPairs = this.childDocs.map(doc => Doc.GetLayoutDataDocPair(Document, !this.props.annotationsKey ? DataDoc : undefined, doc)).filter(pair => pair.layout); return validPairs.map(({ data, layout }) => ({ data: data!, layout: layout! })); // this mapping is a bit of a hack to coerce types } get childDocList() { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 41ef8c2a6..0022a4dbd 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -834,23 +834,23 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { layoutDocsInGrid = () => { UndoManager.RunInBatch(() => { - const docs = DocListCast(this.Document[this.props.fieldKey]); + const docs = this.childLayoutPairs; const startX = this.Document._panX || 0; let x = startX; let y = this.Document._panY || 0; let i = 0; - const width = Math.max(...docs.map(doc => NumCast(doc._width))); - const height = Math.max(...docs.map(doc => NumCast(doc._height))); - for (const doc of docs) { - doc.x = x; - doc.y = y; + const width = Math.max(...docs.map(doc => NumCast(doc.layout._width))); + const height = Math.max(...docs.map(doc => NumCast(doc.layout._height))); + docs.forEach(pair => { + pair.layout.x = x; + pair.layout.y = y; x += width + 20; if (++i === 6) { i = 0; x = startX; y += height + 20; } - } + }); }, "arrange contents"); } diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 675f72011..939df2540 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -1062,7 +1062,8 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & } @computed get sidebarWidthPercent() { return StrCast(this.props.Document.sidebarWidthPercent, "0%"); } - @computed get sidebarWidth() { return Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100 * this.props.PanelWidth(); } + sidebarWidth = () => { return Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100 * this.props.PanelWidth(); } + sidebarScreenToLocal = () => this.props.ScreenToLocalTransform().translate(-(this.props.PanelWidth() - this.sidebarWidth()), 0); @computed get sidebarColor() { return StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], "transparent")); } render() { TraceMobx(); @@ -1107,7 +1108,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}> this.sidebarWidth} + PanelWidth={this.sidebarWidth} annotationsKey={this.annotationKey} isAnnotationOverlay={false} focus={this.props.focus} @@ -1118,9 +1119,9 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & whenActiveChanged={this.whenActiveChanged} removeDocument={this.removeDocument} moveDocument={this.moveDocument} - addDocument={(doc: Doc) => { doc._showSidebar = false; return this.addDocument(doc); }} + addDocument={this.addDocument} CollectionView={undefined} - ScreenToLocalTransform={() => this.props.ScreenToLocalTransform().translate(-(this.props.PanelWidth() - this.sidebarWidth), 0)} + ScreenToLocalTransform={this.sidebarScreenToLocal} renderDepth={this.props.renderDepth + 1} ContainingCollectionDoc={this.props.ContainingCollectionDoc} chromeCollapsed={true}> -- cgit v1.2.3-70-g09d2 From b0544ea44ef78a93deb9290815f95d6d9308447d Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 31 Jan 2020 10:12:32 -0500 Subject: fixed text labeling in pivot view --- .../CollectionFreeFormLayoutEngines.tsx | 38 +++++++++++++++++----- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 3 +- 3 files changed, 32 insertions(+), 11 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index be1317b25..18ca71c2a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -4,7 +4,7 @@ import { ScriptBox } from "../../ScriptBox"; import { CompileScript } from "../../../util/Scripting"; import { ScriptField } from "../../../../new_fields/ScriptField"; import { OverlayView, OverlayElementOptions } from "../../OverlayView"; -import { emptyFunction } from "../../../../Utils"; +import { emptyFunction, aggregateBounds } from "../../../../Utils"; import React = require("react"); import { ObservableMap, runInAction } from "mobx"; import { Id, ToString } from "../../../../new_fields/FieldSymbols"; @@ -42,7 +42,13 @@ function toLabel(target: FieldResult) { return String(target); } -export function computePivotLayout(poolData: ObservableMap, pivotDoc: Doc, childDocs: Doc[], childPairs: { layout: Doc, data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: any) => ViewDefResult[]) { +export function computePivotLayout( + poolData: ObservableMap, + pivotDoc: Doc, + pwidth: number, pheight: number, + childDocs: Doc[], + childPairs: { layout: Doc, data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: any) => ViewDefResult[] +) { const pivotAxisWidth = NumCast(pivotDoc.pivotWidth, 200); const pivotColumnGroups = new Map, Doc[]>(); @@ -99,21 +105,37 @@ export function computePivotLayout(poolData: ObservableMap, pivotDo x += pivotAxisWidth * (numCols * expander + gap); }); + const grpEles = groupNames.map(gn => { return { x: gn.x, y: gn.y, width: gn.width, height: gn.height }; }); + const docEles = childPairs.map(pair => { + const newPos = docMap.get(pair.layout) || { x: NumCast(pair.layout.x), y: NumCast(pair.layout.y) }; // new pos is computed pos, or pos written to the document's fields + return { + x: newPos.x, + y: newPos.y, + width: NumCast(pair.layout._width), + height: NumCast(pair.layout._height) + }; + } + ); + const aggBounds = aggregateBounds(docEles.concat(grpEles), 0, 0); + + const scale = Math.min(pheight / (aggBounds.b - aggBounds.y), pwidth / (aggBounds.r - aggBounds.x)); + childPairs.map(pair => { - const defaultPosition = { + const fallbackPos = { x: NumCast(pair.layout.x), y: NumCast(pair.layout.y), z: NumCast(pair.layout.z), width: NumCast(pair.layout._width), height: NumCast(pair.layout._height) }; - const pos = docMap.get(pair.layout) || defaultPosition; - const data = poolData.get(pair.layout[Id]); - if (!data || pos.x !== data.x || pos.y !== data.y || pos.z !== data.z || pos.width !== data.width || pos.height !== data.height) { - runInAction(() => poolData.set(pair.layout[Id], { transition: "transform 1s", ...pos })); + const newPosRaw = docMap.get(pair.layout) || fallbackPos; // new pos is computed pos, or pos written to the document's fields + const newPos = { x: newPosRaw.x * scale, y: newPosRaw.y * scale, z: newPosRaw.z, width: newPosRaw.width * scale, height: newPosRaw.height * scale }; + const lastPos = poolData.get(pair.layout[Id]); // last computed pos + if (!lastPos || newPos.x !== lastPos.x || newPos.y !== lastPos.y || newPos.z !== lastPos.z || newPos.width !== lastPos.width || newPos.height !== lastPos.height) { + runInAction(() => poolData.set(pair.layout[Id], { transition: "transform 1s", ...newPos })); } }); - return { elements: viewDefsToJSX(groupNames) }; + return { elements: viewDefsToJSX(groupNames.map(gname => { return { type: gname.type, text: gname.text, x: gname.x * scale, y: gname.y * scale, width: gname.width * scale, height: gname.height, fontSize: gname.fontSize } })) }; } export function AddCustomFreeFormLayout(doc: Doc, dataKey: string): () => void { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 0022a4dbd..90bed2899 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -776,7 +776,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { }.bind(this)); doPivotLayout(poolData: ObservableMap) { - return computePivotLayout(poolData, this.props.Document, this.childDocs, + return computePivotLayout(poolData, this.props.Document, this.props.PanelWidth(), this.props.PanelHeight(), this.childDocs, this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)), [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX); } diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 939df2540..fe9b8e0ff 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -1070,8 +1070,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : ""; const interactive = InkingControl.Instance.selectedTool || this.layoutDoc.isBackground; if (this.props.isSelected()) { - // TODO: ftong --> update from dash in richtextmenu - RichTextMenu.Instance.updateFromDash(this._editorView!, undefined, this.props); + this._editorView && RichTextMenu.Instance.updateFromDash(this._editorView, undefined, this.props); } else if (FormattedTextBoxComment.textBox === this) { FormattedTextBoxComment.Hide(); } -- cgit v1.2.3-70-g09d2