From ea4588185f165f66a4d596d51a8a8ad955985762 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 22 Feb 2023 15:58:20 -0500 Subject: fixed pres hide before/after, select w/o nav, and updating contents.. made collections fitWidth by default so that they resize like a normal window. Made freeform pan/zoom animate when changing keyframes. allow nesting of keyframe collections.. made presEffects work even if there's no transition movement. made nested collection timelines follow parent by default, but with ability to change ordering. fixed treeView incremental render. treat doc anchors are preselements instead of delegating. don't highlight ink when dragging ctrl pts. --- src/client/util/CurrentUserUtils.ts | 4 +- src/client/views/InkControlPtHandles.tsx | 268 +++++++++++---------- src/client/views/InkTangentHandles.tsx | 114 +++++---- src/client/views/InkingStroke.tsx | 24 +- src/client/views/MainView.tsx | 2 +- src/client/views/PropertiesView.tsx | 14 +- src/client/views/collections/CollectionMenu.tsx | 9 +- .../views/collections/CollectionTreeView.tsx | 2 +- src/client/views/collections/TabDocView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 54 ++++- .../collections/collectionFreeForm/MarqueeView.tsx | 1 + .../views/nodes/CollectionFreeFormDocumentView.tsx | 28 +-- src/client/views/nodes/DocumentView.tsx | 16 +- src/client/views/nodes/button/FontIconBox.tsx | 2 +- src/client/views/nodes/trails/PresBox.tsx | 38 +-- src/client/views/nodes/trails/PresElementBox.tsx | 118 +++------ src/fields/util.ts | 1 + 17 files changed, 345 insertions(+), 352 deletions(-) (limited to 'src') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 3d95cb947..67c730089 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -258,7 +258,7 @@ export class CurrentUserUtils { }[] = [ {key: "Note", creator: opts => Docs.Create.TextDocument("", opts), opts: { _width: 200, _autoHeight: true }}, {key: "Noteboard", creator: opts => Docs.Create.NoteTakingDocument([], opts), opts: { _width: 250, _height: 200 }}, - {key: "Collection", creator: opts => Docs.Create.FreeformDocument([], opts), opts: { _width: 150, _height: 100 }}, + {key: "Collection", creator: opts => Docs.Create.FreeformDocument([], opts), opts: { _width: 150, _height: 100, _fitWidth: true }}, {key: "Equation", creator: opts => Docs.Create.EquationDocument(opts), opts: { _width: 300, _height: 35, _backgroundGridShow: true, }}, {key: "Webpage", creator: opts => Docs.Create.WebDocument("",opts), opts: { _width: 400, _height: 512, _nativeWidth: 850, useCors: true, }}, {key: "Comparison", creator: Docs.Create.ComparisonDocument, opts: { _width: 300, _height: 300 }}, @@ -669,7 +669,7 @@ export class CurrentUserUtils { title: "Perspective", toolTip: "View", btnType: ButtonType.DropdownList, ignoreClick: true, width: 100, scripts: { script: 'setView(value, _readOnly_)'}}, { title: "Pin", icon: "map-pin", toolTip: "Pin View to Trail", btnType: ButtonType.ClickButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "tab")'}, width: 20, scripts: { onClick: 'pinWithView(_readOnly_, altKey)'}}, { title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()'}, width: 20, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}}, - { title: "Num",icon: "",toolTip: "Frame Number (click to toggle edit mode)",btnType: ButtonType.TextButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()', buttonText: 'selectedDocs()?.lastElement()?.currentFrame?.toString()'}, width: 20, scripts: { script: '{ return curKeyFrame(_readOnly_);}'}}, + { title: "Num",icon: "",toolTip: "Frame Number (click to toggle edit mode)",btnType: ButtonType.TextButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()', buttonText: 'selectedDocs()?.lastElement()?.currentFrame?.toString()'}, width: 20, scripts: { onClick: '{ return curKeyFrame(_readOnly_);}'}}, { title: "Fwd", icon: "chevron-right", toolTip: "Next Animation Frame", btnType: ButtonType.ClickButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()'}, width: 20, scripts: { onClick: 'nextKeyFrame(_readOnly_)'}}, { title: "Fill", icon: "fill-drip", toolTip: "Background Fill Color",btnType: ButtonType.ColorButton, funcs: {hidden: '!SelectionManager_selectedDocType()'}, ignoreClick: true, width: 20, scripts: { script: 'return setBackgroundColor(value, _readOnly_)'}}, // Only when a document is selected { title: "Header", icon: "heading", toolTip: "Header Color", btnType: ButtonType.ColorButton, funcs: {hidden: '!SelectionManager_selectedDocType()'}, ignoreClick: true, scripts: { script: 'return setHeaderColor(value, _readOnly_)'}}, diff --git a/src/client/views/InkControlPtHandles.tsx b/src/client/views/InkControlPtHandles.tsx index d036a636a..9447b2e72 100644 --- a/src/client/views/InkControlPtHandles.tsx +++ b/src/client/views/InkControlPtHandles.tsx @@ -1,23 +1,23 @@ -import React = require("react"); -import { action, observable } from "mobx"; -import { observer } from "mobx-react"; -import { Doc } from "../../fields/Doc"; -import { ControlPoint, InkData, PointData, InkField } from "../../fields/InkField"; -import { List } from "../../fields/List"; -import { listSpec } from "../../fields/Schema"; -import { Cast, NumCast } from "../../fields/Types"; -import { setupMoveUpEvents, returnFalse } from "../../Utils"; -import { Transform } from "../util/Transform"; -import { UndoManager } from "../util/UndoManager"; -import { Colors } from "./global/globalEnums"; -import { InkingStroke } from "./InkingStroke"; -import { InkStrokeProperties } from "./InkStrokeProperties"; -import { DocumentView } from "./nodes/DocumentView"; -import { SelectionManager } from "../util/SelectionManager"; +import React = require('react'); +import { action, observable } from 'mobx'; +import { observer } from 'mobx-react'; +import { Doc } from '../../fields/Doc'; +import { ControlPoint, InkData, PointData, InkField } from '../../fields/InkField'; +import { List } from '../../fields/List'; +import { listSpec } from '../../fields/Schema'; +import { Cast, NumCast } from '../../fields/Types'; +import { setupMoveUpEvents, returnFalse } from '../../Utils'; +import { Transform } from '../util/Transform'; +import { UndoManager } from '../util/UndoManager'; +import { Colors } from './global/globalEnums'; +import { InkingStroke } from './InkingStroke'; +import { InkStrokeProperties } from './InkStrokeProperties'; +import { DocumentView } from './nodes/DocumentView'; +import { SelectionManager } from '../util/SelectionManager'; export interface InkControlProps { inkDoc: Doc; - inkView: DocumentView; + inkView: InkingStroke; inkCtrlPoints: InkData; screenCtrlPoints: InkData; screenSpaceLineWidth: number; @@ -26,16 +26,16 @@ export interface InkControlProps { @observer export class InkControlPtHandles extends React.Component { - @observable private _overControl = -1; - - @observable controlUndo: UndoManager.Batch | undefined; + get docView() { + return this.props.inkView.props.docViewPath().lastElement(); + } componentDidMount() { - document.addEventListener("keydown", this.onDelete, true); + document.addEventListener('keydown', this.onDelete, true); } componentWillUnmount() { - document.removeEventListener("keydown", this.onDelete, true); + document.removeEventListener('keydown', this.onDelete, true); } /** * Handles the movement of a selected control point when the user clicks and drags. @@ -43,30 +43,32 @@ export class InkControlPtHandles extends React.Component { */ @action onControlDown = (e: React.PointerEvent, controlIndex: number): void => { - const ptFromScreen = this.props.inkView.ComponentView?.ptFromScreen; + const ptFromScreen = this.props.inkView.ptFromScreen; if (ptFromScreen) { const order = controlIndex % 4; const handleIndexA = ((order === 3 ? controlIndex - 1 : controlIndex - 2) + this.props.inkCtrlPoints.length) % this.props.inkCtrlPoints.length; const handleIndexB = (order === 3 ? controlIndex + 2 : controlIndex + 1) % this.props.inkCtrlPoints.length; - const brokenIndices = Cast(this.props.inkDoc.brokenInkIndices, listSpec("number")); + const brokenIndices = Cast(this.props.inkDoc.brokenInkIndices, listSpec('number')); const wasSelected = InkStrokeProperties.Instance._currentPoint === controlIndex; if (!wasSelected) InkStrokeProperties.Instance._currentPoint = -1; const origInk = this.props.inkCtrlPoints.slice(); - setupMoveUpEvents(this, e, + setupMoveUpEvents( + this, + e, action((e: PointerEvent, down: number[], delta: number[]) => { - if (!this.controlUndo) this.controlUndo = UndoManager.StartBatch("drag ink ctrl pt"); + if (!this.props.inkView.controlUndo) this.props.inkView.controlUndo = UndoManager.StartBatch('drag ink ctrl pt'); const inkMoveEnd = ptFromScreen({ X: delta[0], Y: delta[1] }); const inkMoveStart = ptFromScreen({ X: 0, Y: 0 }); - InkStrokeProperties.Instance.moveControlPtHandle(this.props.inkView, inkMoveEnd.X - inkMoveStart.X, inkMoveEnd.Y - inkMoveStart.Y, controlIndex, origInk); + InkStrokeProperties.Instance.moveControlPtHandle(this.docView, inkMoveEnd.X - inkMoveStart.X, inkMoveEnd.Y - inkMoveStart.Y, controlIndex, origInk); return false; }), action(() => { - if (this.controlUndo) { - InkStrokeProperties.Instance.snapControl(this.props.inkView, controlIndex); + if (this.props.inkView.controlUndo) { + InkStrokeProperties.Instance.snapControl(this.docView, controlIndex); } - this.controlUndo?.end(); - this.controlUndo = undefined; - UndoManager.FilterBatches(["data", "x", "y", "width", "height"]); + this.props.inkView.controlUndo?.end(); + this.props.inkView.controlUndo = undefined; + UndoManager.FilterBatches(['data', 'x', 'y', 'width', 'height']); }), action((e: PointerEvent, doubleTap: boolean | undefined) => { const equivIndex = controlIndex === 0 ? this.props.inkCtrlPoints.length - 1 : controlIndex === this.props.inkCtrlPoints.length - 1 ? 0 : controlIndex; @@ -76,44 +78,52 @@ export class InkControlPtHandles extends React.Component { else this.props.inkDoc.brokenInkIndices = new List([controlIndex]); } else { if (brokenIndices?.includes(equivIndex)) { - if (!this.controlUndo) this.controlUndo = UndoManager.StartBatch("make smooth"); - InkStrokeProperties.Instance.snapHandleTangent(this.props.inkView, equivIndex, handleIndexA, handleIndexB); + if (!this.props.inkView.controlUndo) this.props.inkView.controlUndo = UndoManager.StartBatch('make smooth'); + InkStrokeProperties.Instance.snapHandleTangent(this.docView, equivIndex, handleIndexA, handleIndexB); } if (equivIndex !== controlIndex && brokenIndices?.includes(controlIndex)) { - if (!this.controlUndo) this.controlUndo = UndoManager.StartBatch("make smooth"); - InkStrokeProperties.Instance.snapHandleTangent(this.props.inkView, controlIndex, handleIndexA, handleIndexB); + if (!this.props.inkView.controlUndo) this.props.inkView.controlUndo = UndoManager.StartBatch('make smooth'); + InkStrokeProperties.Instance.snapHandleTangent(this.docView, controlIndex, handleIndexA, handleIndexB); } } - this.controlUndo?.end(); - this.controlUndo = undefined; + this.props.inkView.controlUndo?.end(); + this.props.inkView.controlUndo = undefined; } this.changeCurrPoint(controlIndex); - }), undefined, undefined, () => wasSelected && this.changeCurrPoint(-1)); + }), + undefined, + undefined, + () => wasSelected && this.changeCurrPoint(-1) + ); } - } + }; /** * Updates whether a user has hovered over a particular control point or point that could be added * on click. */ - @action onEnterControl = (i: number) => { this._overControl = i; }; - @action onLeaveControl = () => { this._overControl = -1; }; + @action onEnterControl = (i: number) => { + this._overControl = i; + }; + @action onLeaveControl = () => { + this._overControl = -1; + }; /** * Deletes the currently selected point. */ @action onDelete = (e: KeyboardEvent) => { - if (["-", "Backspace", "Delete"].includes(e.key)) { - InkStrokeProperties.Instance.deletePoints(this.props.inkView, e.shiftKey); + if (['-', 'Backspace', 'Delete'].includes(e.key)) { + InkStrokeProperties.Instance.deletePoints(this.docView, e.shiftKey); e.stopPropagation(); } - } + }; /** * Changes the current selected control point. */ @action - changeCurrPoint = (i: number) => InkStrokeProperties.Instance._currentPoint = i + changeCurrPoint = (i: number) => (InkStrokeProperties.Instance._currentPoint = i); render() { // Accessing the current ink's data and extracting all control points. @@ -133,101 +143,115 @@ export class InkControlPtHandles extends React.Component { const closed = InkingStroke.IsClosed(inkData); const nearestScreenPt = this.props.nearestScreenPt(); - const TagType = (broken?: boolean) => broken ? "rect" : "circle"; - const hdl = (control: { X: number, Y: number, I: number }, scale: number, color: string) => { - const broken = Cast(this.props.inkDoc.brokenInkIndices, listSpec("number"))?.includes(control.I); + const TagType = (broken?: boolean) => (broken ? 'rect' : 'circle'); + const hdl = (control: { X: number; Y: number; I: number }, scale: number, color: string) => { + const broken = Cast(this.props.inkDoc.brokenInkIndices, listSpec('number'))?.includes(control.I); const Tag = TagType((control.I === 0 || control.I === inkData.length - 1) && !closed) as keyof JSX.IntrinsicElements; - return this.onControlDown(e, control.I)} - onMouseEnter={() => this.onEnterControl(control.I)} - onMouseLeave={this.onLeaveControl} - pointerEvents="all" - cursor="default" - />; - }; - return ( - {!nearestScreenPt ? (null) : - this.onControlDown(e, control.I)} + onMouseEnter={() => this.onEnterControl(control.I)} + onMouseLeave={this.onLeaveControl} + pointerEvents="all" + cursor="default" /> - } - {sreenCtrlPoints.map(control => hdl(control, this._overControl !== control.I ? 1 : 3 / 2, Colors.WHITE))} - + ); + }; + return ( + + {!nearestScreenPt ? null : } + {sreenCtrlPoints.map(control => hdl(control, this._overControl !== control.I ? 1 : 3 / 2, Colors.WHITE))} + ); } } - export interface InkEndProps { inkDoc: Doc; - inkView: DocumentView; + inkView: InkingStroke; screenSpaceLineWidth: number; startPt: PointData; endPt: PointData; } @observer export class InkEndPtHandles extends React.Component { - @observable controlUndo: UndoManager.Batch | undefined; @observable _overStart: boolean = false; @observable _overEnd: boolean = false; @action - dragRotate = (e: React.PointerEvent, p1: () => { X: number, Y: number }, p2: () => { X: number, Y: number }) => { - setupMoveUpEvents(this, e, (e) => { - if (!this.controlUndo) this.controlUndo = UndoManager.StartBatch("stretch ink"); - // compute stretch factor by finding scaling along axis between start and end points - const v1 = { X: p1().X - p2().X, Y: p1().Y - p2().Y }; - const v2 = { X: e.clientX - p2().X, Y: e.clientY - p2().Y }; - const v1len = Math.sqrt(v1.X * v1.X + v1.Y * v1.Y); - const v2len = Math.sqrt(v2.X * v2.X + v2.Y * v2.Y); - const scaling = v2len / v1len; - const v1n = { X: v1.X / v1len, Y: v1.Y / v1len }; - const v2n = { X: v2.X / v2len, Y: v2.Y / v2len }; - const angle = Math.acos(v1n.X * v2n.X + v1n.Y * v2n.Y) * Math.sign(v1.X * v2.Y - v2.X * v1.Y); - InkStrokeProperties.Instance.stretchInk(SelectionManager.Views(), scaling, p2(), v1n, e.shiftKey); - InkStrokeProperties.Instance.rotateInk(SelectionManager.Views(), angle, p2()); - return false; - }, action(() => { - this.controlUndo?.end(); - this.controlUndo = undefined; - UndoManager.FilterBatches(["data", "x", "y", "width", "height"]); - }), returnFalse); - } + dragRotate = (e: React.PointerEvent, p1: () => { X: number; Y: number }, p2: () => { X: number; Y: number }) => { + setupMoveUpEvents( + this, + e, + action(e => { + if (!this.props.inkView.controlUndo) this.props.inkView.controlUndo = UndoManager.StartBatch('stretch ink'); + // compute stretch factor by finding scaling along axis between start and end points + const v1 = { X: p1().X - p2().X, Y: p1().Y - p2().Y }; + const v2 = { X: e.clientX - p2().X, Y: e.clientY - p2().Y }; + const v1len = Math.sqrt(v1.X * v1.X + v1.Y * v1.Y); + const v2len = Math.sqrt(v2.X * v2.X + v2.Y * v2.Y); + const scaling = v2len / v1len; + const v1n = { X: v1.X / v1len, Y: v1.Y / v1len }; + const v2n = { X: v2.X / v2len, Y: v2.Y / v2len }; + const angle = Math.acos(v1n.X * v2n.X + v1n.Y * v2n.Y) * Math.sign(v1.X * v2.Y - v2.X * v1.Y); + InkStrokeProperties.Instance.stretchInk(SelectionManager.Views(), scaling, p2(), v1n, e.shiftKey); + InkStrokeProperties.Instance.rotateInk(SelectionManager.Views(), angle, p2()); + return false; + }), + action(() => { + this.props.inkView.controlUndo?.end(); + this.props.inkView.controlUndo = undefined; + UndoManager.FilterBatches(['data', 'x', 'y', 'width', 'height']); + }), + returnFalse + ); + }; render() { - const hdl = (key: string, pt: PointData, dragFunc: (e: React.PointerEvent) => void) => this._overStart = false)} - onPointerEnter={action(() => this._overStart = true)} - onPointerDown={dragFunc} - pointerEvents="all" - />; - return ( - {hdl("start", this.props.startPt, (e: React.PointerEvent) => this.dragRotate(e, () => this.props.startPt, () => this.props.endPt))} - {hdl("end", this.props.endPt, (e: React.PointerEvent) => this.dragRotate(e, () => this.props.endPt, () => this.props.startPt))} - + const hdl = (key: string, pt: PointData, dragFunc: (e: React.PointerEvent) => void) => ( + (this._overStart = false))} + onPointerEnter={action(() => (this._overStart = true))} + onPointerDown={dragFunc} + pointerEvents="all" + /> + ); + return ( + + {hdl('start', this.props.startPt, (e: React.PointerEvent) => + this.dragRotate( + e, + () => this.props.startPt, + () => this.props.endPt + ) + )} + {hdl('end', this.props.endPt, (e: React.PointerEvent) => + this.dragRotate( + e, + () => this.props.endPt, + () => this.props.startPt + ) + )} + ); } -} \ No newline at end of file +} diff --git a/src/client/views/InkTangentHandles.tsx b/src/client/views/InkTangentHandles.tsx index ae35bc980..c4a2f603e 100644 --- a/src/client/views/InkTangentHandles.tsx +++ b/src/client/views/InkTangentHandles.tsx @@ -1,21 +1,21 @@ -import React = require("react"); -import { action } from "mobx"; -import { observer } from "mobx-react"; -import { Doc } from "../../fields/Doc"; -import { HandleLine, HandlePoint, InkData } from "../../fields/InkField"; -import { List } from "../../fields/List"; -import { listSpec } from "../../fields/Schema"; -import { Cast } from "../../fields/Types"; -import { emptyFunction, setupMoveUpEvents } from "../../Utils"; -import { Transform } from "../util/Transform"; -import { UndoManager } from "../util/UndoManager"; -import { Colors } from "./global/globalEnums"; -import { InkingStroke } from "./InkingStroke"; -import { InkStrokeProperties } from "./InkStrokeProperties"; -import { DocumentView } from "./nodes/DocumentView"; +import React = require('react'); +import { action } from 'mobx'; +import { observer } from 'mobx-react'; +import { Doc } from '../../fields/Doc'; +import { HandleLine, HandlePoint, InkData } from '../../fields/InkField'; +import { List } from '../../fields/List'; +import { listSpec } from '../../fields/Schema'; +import { Cast } from '../../fields/Types'; +import { emptyFunction, setupMoveUpEvents } from '../../Utils'; +import { Transform } from '../util/Transform'; +import { UndoManager } from '../util/UndoManager'; +import { Colors } from './global/globalEnums'; +import { InkingStroke } from './InkingStroke'; +import { InkStrokeProperties } from './InkStrokeProperties'; +import { DocumentView } from './nodes/DocumentView'; export interface InkHandlesProps { inkDoc: Doc; - inkView: DocumentView; + inkView: InkingStroke; screenCtrlPoints: InkData; screenSpaceLineWidth: number; ScreenToLocalTransform: () => Transform; @@ -23,49 +23,51 @@ export interface InkHandlesProps { @observer export class InkTangentHandles extends React.Component { + get docView() { + return this.props.inkView.props.docViewPath().lastElement(); + } /** * Handles the movement of a selected handle point when the user clicks and drags. * @param handleNum The index of the currently selected handle point. */ onHandleDown = (e: React.PointerEvent, handleIndex: number): void => { - const ptFromScreen = this.props.inkView.ComponentView?.ptFromScreen; - if (!ptFromScreen) return; - const screenScale = this.props.ScreenToLocalTransform().Scale; - var controlUndo: UndoManager.Batch | undefined; const order = handleIndex % 4; const oppositeHandleRawIndex = order === 1 ? handleIndex - 3 : handleIndex + 3; const oppositeHandleIndex = (oppositeHandleRawIndex < 0 ? this.props.screenCtrlPoints.length + oppositeHandleRawIndex : oppositeHandleRawIndex) % this.props.screenCtrlPoints.length; const controlIndex = (order === 1 ? handleIndex - 1 : handleIndex + 2) % this.props.screenCtrlPoints.length; - setupMoveUpEvents(this, e, + setupMoveUpEvents( + this, + e, (e: PointerEvent, down: number[], delta: number[]) => { - if (!controlUndo) controlUndo = UndoManager.StartBatch("DocDecs move tangent"); + if (!this.props.inkView.controlUndo) this.props.inkView.controlUndo = UndoManager.StartBatch('DocDecs move tangent'); if (e.altKey) this.onBreakTangent(controlIndex); - const inkMoveEnd = ptFromScreen({ X: delta[0], Y: delta[1] }); - const inkMoveStart = ptFromScreen({ X: 0, Y: 0 }); - InkStrokeProperties.Instance.moveTangentHandle(this.props.inkView, -(inkMoveEnd.X - inkMoveStart.X), -(inkMoveEnd.Y - inkMoveStart.Y), handleIndex, oppositeHandleIndex, controlIndex); + const inkMoveEnd = this.props.inkView.ptFromScreen({ X: delta[0], Y: delta[1] }); + const inkMoveStart = this.props.inkView.ptFromScreen({ X: 0, Y: 0 }); + InkStrokeProperties.Instance.moveTangentHandle(this.docView, -(inkMoveEnd.X - inkMoveStart.X), -(inkMoveEnd.Y - inkMoveStart.Y), handleIndex, oppositeHandleIndex, controlIndex); return false; - }, () => { - controlUndo?.end(); - UndoManager.FilterBatches(["data", "x", "y", "width", "height"]); - }, emptyFunction + }, + () => { + this.props.inkView.controlUndo?.end(); + UndoManager.FilterBatches(['data', 'x', 'y', 'width', 'height']); + }, + emptyFunction ); - } + }; /** - * Breaks tangent handle movement when ‘Alt’ key is held down. Adds the current handle index and + * Breaks tangent handle movement when ‘Alt’ key is held down. Adds the current handle index and * its matching (opposite) handle to a list of broken handle indices. * @param handleNum The index of the currently selected handle point. */ @action onBreakTangent = (controlIndex: number) => { const closed = InkingStroke.IsClosed(this.props.screenCtrlPoints); - const brokenIndices = Cast(this.props.inkDoc.brokenInkIndices, listSpec("number")); - if (!brokenIndices?.includes(controlIndex) && - ((controlIndex > 0 && controlIndex < this.props.screenCtrlPoints.length - 1) || closed)) { + const brokenIndices = Cast(this.props.inkDoc.brokenInkIndices, listSpec('number')); + if (!brokenIndices?.includes(controlIndex) && ((controlIndex > 0 && controlIndex < this.props.screenCtrlPoints.length - 1) || closed)) { if (brokenIndices) brokenIndices.push(controlIndex); else this.props.inkDoc.brokenInkIndices = new List([controlIndex]); } - } + }; render() { // Accessing the current ink's data and extracting all handle points and handle lines. @@ -81,10 +83,18 @@ export class InkTangentHandles extends React.Component { // Adding first and last (single) handle lines. if (closed) { tangentLines.push({ X1: data[data.length - 2].X, Y1: data[data.length - 2].Y, X2: data[0].X, Y2: data[0].Y, X3: data[1].X, Y3: data[1].Y, dot1: 0, dot2: data.length - 1 }); - } - else { + } else { tangentLines.push({ X1: data[0].X, Y1: data[0].Y, X2: data[0].X, Y2: data[0].Y, X3: data[1].X, Y3: data[1].Y, dot1: 0, dot2: 0 }); - tangentLines.push({ X1: data[data.length - 2].X, Y1: data[data.length - 2].Y, X2: data[data.length - 1].X, Y2: data[data.length - 1].Y, X3: data[data.length - 1].X, Y3: data[data.length - 1].Y, dot1: data.length - 1, dot2: data.length - 1 }); + tangentLines.push({ + X1: data[data.length - 2].X, + Y1: data[data.length - 2].Y, + X2: data[data.length - 1].X, + Y2: data[data.length - 1].Y, + X3: data[data.length - 1].X, + Y3: data[data.length - 1].Y, + dot1: data.length - 1, + dot2: data.length - 1, + }); } for (let i = 2; i < data.length - 4; i += 4) { tangentLines.push({ X1: data[i].X, Y1: data[i].Y, X2: data[i + 1].X, Y2: data[i + 1].Y, X3: data[i + 3].X, Y3: data[i + 3].Y, dot1: i + 1, dot2: i + 2 }); @@ -94,7 +104,7 @@ export class InkTangentHandles extends React.Component { return ( <> - {tangentHandles.map((pts, i) => + {tangentHandles.map((pts, i) => ( { onPointerDown={e => this.onHandleDown(e, pts.I)} pointerEvents="all" cursor="default" - display={(pts.dot1 === InkStrokeProperties.Instance._currentPoint || pts.dot2 === InkStrokeProperties.Instance._currentPoint) ? "inherit" : "none"} /> - )} + display={pts.dot1 === InkStrokeProperties.Instance._currentPoint || pts.dot2 === InkStrokeProperties.Instance._currentPoint ? 'inherit' : 'none'} + /> + + ))} {tangentLines.map((pts, i) => { - const tangentLine = (x1: number, y1: number, x2: number, y2: number) => + const tangentLine = (x1: number, y1: number, x2: number, y2: number) => ( ; - return - {tangentLine(pts.X1, pts.Y1, pts.X2, pts.Y2)} - {tangentLine(pts.X2, pts.Y2, pts.X3, pts.Y3)} - ; + display={pts.dot1 === InkStrokeProperties.Instance._currentPoint || pts.dot2 === InkStrokeProperties.Instance._currentPoint ? 'inherit' : 'none'} + /> + ); + return ( + + {tangentLine(pts.X1, pts.Y1, pts.X2, pts.Y2)} + {tangentLine(pts.X2, pts.Y2, pts.X3, pts.Y3)} + + ); })} ); } -} \ No newline at end of file +} diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 8291ff3f1..76c1aa80a 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -124,6 +124,7 @@ export class InkingStroke extends ViewBoxBaseComponent() { public static toggleMask = action((inkDoc: Doc) => { inkDoc.isInkMask = !inkDoc.isInkMask; }); + @observable controlUndo: UndoManager.Batch | undefined; /** * Drags the a simple bezier segment of the stroke. * Also adds a control point when double clicking on the stroke. @@ -143,15 +144,15 @@ export class InkingStroke extends ViewBoxBaseComponent() { const { nearestSeg } = InkStrokeProperties.nearestPtToStroke(screenPts, { X: e.clientX, Y: e.clientY }); const controlIndex = nearestSeg; const wasSelected = InkStrokeProperties.Instance._currentPoint === controlIndex; - var controlUndo: UndoManager.Batch | undefined; const isEditing = InkStrokeProperties.Instance._controlButton && this.props.isSelected(); + this.controlUndo = undefined; setupMoveUpEvents( this, e, !isEditing ? returnFalse : action((e: PointerEvent, down: number[], delta: number[]) => { - if (!controlUndo) controlUndo = UndoManager.StartBatch('drag ink ctrl pt'); + if (!this.controlUndo) this.controlUndo = UndoManager.StartBatch('drag ink ctrl pt'); const inkMoveEnd = this.ptFromScreen({ X: delta[0], Y: delta[1] }); const inkMoveStart = this.ptFromScreen({ X: 0, Y: 0 }); InkStrokeProperties.Instance.moveControlPtHandle(inkView, inkMoveEnd.X - inkMoveStart.X, inkMoveEnd.Y - inkMoveStart.Y, controlIndex); @@ -161,8 +162,8 @@ export class InkingStroke extends ViewBoxBaseComponent() { !isEditing ? returnFalse : action(() => { - controlUndo?.end(); - controlUndo = undefined; + this.controlUndo?.end(); + this.controlUndo = undefined; UndoManager.FilterBatches(['data', 'x', 'y', 'width', 'height']); }), action((e: PointerEvent, doubleTap: boolean | undefined) => { @@ -304,7 +305,7 @@ export class InkingStroke extends ViewBoxBaseComponent() { return SnappingManager.GetIsDragging() ? null : !InkStrokeProperties.Instance._controlButton ? ( !this.props.isSelected() || InkingStroke.IsClosed(inkData) ? null : (
- +
) ) : ( @@ -331,15 +332,8 @@ export class InkingStroke extends ViewBoxBaseComponent() { 1.0, false )} - - + + ); }; @@ -389,7 +383,7 @@ export class InkingStroke extends ViewBoxBaseComponent() { 1.0, false ); - const highlight = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Highlighting); + const highlight = !this.controlUndo && this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Highlighting); const highlightIndex = highlight?.highlightIndex; const highlightColor = highlight?.highlightIndex ? highlight?.highlightColor : StrCast(this.layoutDoc.strokeOutlineColor, !closed && fillColor && fillColor !== 'transparent' ? StrCast(this.layoutDoc.color, 'transparent') : 'transparent'); // Invisible polygonal line that enables the ink to be selected by the user. diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 895ed9bda..625dc2748 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -606,7 +606,7 @@ export class MainView extends React.Component { @computed get mainDocView() { return ( <> - {this._hideUI ? null : this.headerBarDocView} + {this._hideUI || !this.headerBarDocHeight?.() ? null : this.headerBarDocView} { {!selectedItem ? null : (
-
{ - this.openPresTransitions = !this.openPresTransitions; - })} - style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}> +
(this.openPresTransitions = !this.openPresTransitions))} style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}>     Transitions
@@ -1830,12 +1825,7 @@ export class PropertiesView extends React.Component { )} {!selectedItem || (type !== DocumentType.VID && type !== DocumentType.AUDIO) ? null : (
-
{ - this.openSlideOptions = !this.openSlideOptions; - })} - style={{ backgroundColor: this.openSlideOptions ? 'black' : '' }}> +
(this.openSlideOptions = !this.openSlideOptions))} style={{ backgroundColor: this.openSlideOptions ? 'black' : '' }}>     {type === DocumentType.AUDIO ? 'Audio Options' : 'Video Options'}
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index e2594b6ae..c83f4e689 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -15,6 +15,7 @@ import { RichTextField } from '../../../fields/RichTextField'; import { listSpec } from '../../../fields/Schema'; import { ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types'; +import { GestureUtils } from '../../../pen-gestures/GestureUtils'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; @@ -40,7 +41,7 @@ import { CollectionLinearView } from './collectionLinear'; import './CollectionMenu.scss'; import { COLLECTION_BORDER_WIDTH } from './CollectionView'; import { TabDocView } from './TabDocView'; -import { GestureUtils } from '../../../pen-gestures/GestureUtils'; +import { CollectionFreeFormView } from './collectionFreeForm'; interface CollectionMenuProps { panelHeight: () => number; @@ -733,7 +734,7 @@ export class CollectionFreeFormViewChrome extends React.Component this.addDoc(doc, relativeTo, before); const moveDoc = (d: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument?.(d, target, addDoc) || false; - if (this.treeChildren.length < this._renderCount) setTimeout(action(() => (this._renderCount = Math.min(this.treeChildren.length, this._renderCount + 20)))); + if (this._renderCount < this.treeChildren.length) setTimeout(action(() => (this._renderCount = Math.min(this.treeChildren.length, this._renderCount + 20)))); return TreeView.GetChildElements( this.treeChildren, this, diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index d6bc0a4b2..fa648eb44 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -248,7 +248,7 @@ export class TabDocView extends React.Component { return; } const anchorDoc = DocumentManager.Instance.getDocumentView(doc)?.ComponentView?.getAnchor?.(false); - const pinDoc = Doc.MakeDelegate(anchorDoc ?? doc); + const pinDoc = anchorDoc && anchorDoc !== doc ? anchorDoc : Doc.MakeDelegate(doc); pinDoc.presentationTargetDoc = anchorDoc ?? doc; pinDoc.title = doc.title + ' - Slide'; pinDoc.data = new List(); // the children of the alias' layout are the presentation slide children. the alias' data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 4d6e0dff2..e5b4b76a8 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -54,6 +54,7 @@ import './CollectionFreeFormView.scss'; import { MarqueeView } from './MarqueeView'; import React = require('react'); import { DocumentDecorations } from '../../DocumentDecorations'; +import { PresEffect } from '../../nodes/trails'; export type collectionFreeformViewProps = { annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox) @@ -150,7 +151,7 @@ export class CollectionFreeFormView extends CollectionSubView e.bounds && !e.bounds.z).map(e => e.bounds!), + this._layoutElements.filter(e => e.bounds && e.bounds.width && !e.bounds.z).map(e => e.bounds!), NumCast(this.layoutDoc._xPadding, 10), NumCast(this.layoutDoc._yPadding, 10) ); @@ -185,6 +186,32 @@ export class CollectionFreeFormView extends CollectionSubView { + CollectionFreeFormDocumentView.animFields.forEach(val => { + const findexed = Cast(doc[`${val.key}-indexed`], listSpec('number'), null); + findexed?.length <= timecode + 1 && findexed.push(undefined as any as number); + }); + CollectionFreeFormDocumentView.animStringFields.forEach(val => { + const findexed = Cast(doc[`${val}-indexed`], listSpec('string'), null); + findexed?.length <= timecode + 1 && findexed.push(undefined as any as string); + }); + CollectionFreeFormDocumentView.animDataFields(doc).forEach(val => { + const findexed = Cast(doc[`${val}-indexed`], listSpec(InkField), null); + findexed?.length <= timecode + 1 && findexed.push(undefined as any); + }); + }); + return newTimer; + } + changeKeyFrame = (back = false) => { const currentFrame = Cast(this.Document._currentFrame, 'number', null); if (currentFrame === undefined) { @@ -192,10 +219,10 @@ export class CollectionFreeFormView extends CollectionSubView this.isCurrent(entry[1].pair.layout)) - .forEach((entry, i) => + .forEach((entry, i) => { + const childData: ViewDefBounds = this.childDataProvider(entry[1].pair.layout, entry[1].replica); + const childSize = this.childSizeProvider(entry[1].pair.layout, entry[1].replica); elements.push({ ele: this.getChildDocView(entry[1]), - bounds: this.childDataProvider(entry[1].pair.layout, entry[1].replica), + bounds: childData.opacity === 0 ? { ...childData, width: 0, height: 0 } : { ...childData, width: childSize.width, height: childSize.height }, inkMask: BoolCast(entry[1].pair.layout.isInkMask) ? NumCast(entry[1].pair.layout.opacity, 1) : -1, - }) - ); + }); + }); if (this.props.isAnnotationOverlay && this.props.Document[this.scaleFieldKey]) { // don't zoom out farther than 1-1 if it's a bounded item (image, video, pdf), otherwise don't allow zooming in closer than 1-1 if it's a text sidebar @@ -1988,7 +2018,7 @@ export class CollectionFreeFormView extends CollectionSubView {this.children} diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index bc3b17cd9..bd33c4d80 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -397,6 +397,7 @@ export class MarqueeView extends React.Component (d.context = newCollection)); this.hideMarquee(); return newCollection; diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index f8ef87fb1..ba510e1dc 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -40,6 +40,8 @@ export class CollectionFreeFormDocumentView extends DocComponent { - CollectionFreeFormDocumentView.animFields.forEach(val => { - const findexed = Cast(doc[`${val.key}-indexed`], listSpec('number'), null); - findexed?.length <= timecode + 1 && findexed.push(undefined as any as number); - }); - CollectionFreeFormDocumentView.animStringFields.forEach(val => { - const findexed = Cast(doc[`${val}-indexed`], listSpec('string'), null); - findexed?.length <= timecode + 1 && findexed.push(undefined as any as string); - }); - CollectionFreeFormDocumentView.animDataFields(doc).forEach(val => { - const findexed = Cast(doc[`${val}-indexed`], listSpec(InkField), null); - findexed?.length <= timecode + 1 && findexed.push(undefined as any); - }); - }); - return newTimer; - } - - public static gotoKeyframe(timer: NodeJS.Timeout | undefined, docs: Doc[], duration = 1000) { - if (timer) clearTimeout(timer); - return DocumentView.SetViewTransition(docs, 'all', duration, undefined, true); - } - public static setupZoom(doc: Doc, targDoc: Doc) { const width = new List(); const height = new List(); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 3d89566ee..c3b0412de 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -17,7 +17,7 @@ import { BoolCast, Cast, DocCast, ImageCast, NumCast, ScriptCast, StrCast } from import { AudioField } from '../../../fields/URLField'; import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util'; import { MobileInterface } from '../../../mobile/MobileInterface'; -import { emptyFunction, isTargetChildOf as isParentOf, lightOrDark, OmitKeys, returnEmptyString, returnFalse, returnTrue, returnVal, simulateMouseClick, Utils } from '../../../Utils'; +import { emptyFunction, isTargetChildOf as isParentOf, lightOrDark, OmitKeys, returnEmptyString, returnFalse, returnNone, returnTrue, returnVal, simulateMouseClick, Utils } from '../../../Utils'; import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; import { DocServer } from '../../DocServer'; import { Docs, DocUtils } from '../../documents/Documents'; @@ -252,6 +252,7 @@ export class DocumentViewInternal extends DocComponent StrCast(Doc.LayoutField(this.layoutDoc))?.includes(ScriptingBox.name); (this.rootDoc._raiseWhenDragged === undefined ? DragManager.GetRaiseWhenDragged() : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc); - if (this._doubleTap && (this.props.Document.type !== DocumentType.FONTICON || this.onDoubleClickHandler)) { + if (this._doubleTap && (![DocumentType.FONTICON, DocumentType.PRES].includes(this.props.Document.type as any) || this.onDoubleClickHandler)) { // && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click if (this._timeout) { clearTimeout(this._timeout); @@ -718,6 +719,7 @@ export class DocumentViewInternal extends DocComponent 0))) { // if this is part of a template, let the event go up to the tempalte root unless right/ctrl clicking if ( @@ -777,11 +779,10 @@ export class DocumentViewInternal extends DocComponent() { className="list-item" key={`${value}`} style={{ - backgroundColor: value === checkResult ? Colors.LIGHT_BLUE : undefined, + backgroundColor: value.toString() === checkResult ? Colors.LIGHT_BLUE : undefined, }} onClick={() => setValue(value)}> {value} diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index e07517113..929bf1230 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -1,10 +1,10 @@ import React = require('react'); import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; -import { action, computed, IReactionDisposer, observable, ObservableSet, observe, reaction, runInAction } from 'mobx'; +import { action, computed, IReactionDisposer, observable, ObservableSet, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { ColorState, SketchPicker } from 'react-color'; -import { AnimationSym, Doc, DocListCast, FieldResult, HighlightSym, Opt, StrListCast } from '../../../../fields/Doc'; +import { AnimationSym, Doc, DocListCast, FieldResult, Opt, StrListCast } from '../../../../fields/Doc'; import { Copy, Id } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; import { List } from '../../../../fields/List'; @@ -14,7 +14,7 @@ import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Ty import { AudioField } from '../../../../fields/URLField'; import { emptyFunction, emptyPath, returnFalse, returnOne, setupMoveUpEvents, StopEvent } from '../../../../Utils'; import { DocServer } from '../../../DocServer'; -import { Docs, DocumentOptions } from '../../../documents/Documents'; +import { Docs } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { DocumentManager } from '../../../util/DocumentManager'; import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; @@ -23,19 +23,17 @@ import { SettingsManager } from '../../../util/SettingsManager'; import { undoBatch, UndoManager } from '../../../util/UndoManager'; import { CollectionDockingView } from '../../collections/CollectionDockingView'; import { CollectionFreeFormView, computeTimelineLayout, MarqueeViewBounds } from '../../collections/collectionFreeForm'; +import { CollectionStackedTimeline } from '../../collections/CollectionStackedTimeline'; import { CollectionView } from '../../collections/CollectionView'; import { TabDocView } from '../../collections/TabDocView'; import { ViewBoxBaseComponent } from '../../DocComponent'; import { Colors } from '../../global/globalEnums'; import { LightboxView } from '../../LightboxView'; -import { CollectionFreeFormDocumentView } from '../CollectionFreeFormDocumentView'; import { DocFocusOptions, DocumentView, OpenWhere, OpenWhereMod } from '../DocumentView'; import { FieldView, FieldViewProps } from '../FieldView'; import { ScriptingBox } from '../ScriptingBox'; import './PresBox.scss'; import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums'; -import { CollectionStackedTimeline } from '../../collections/CollectionStackedTimeline'; -import { PresElementBox } from './PresElementBox'; const { Howl } = require('howler'); export interface PinProps { @@ -296,7 +294,7 @@ export class PresBox extends ViewBoxBaseComponent() { if (context) { const ffview = DocumentManager.Instance.getFirstDocumentView(context)?.ComponentView as CollectionFreeFormView; if (ffview?.childDocs) { - this._keyTimer = CollectionFreeFormDocumentView.gotoKeyframe(this._keyTimer, ffview.childDocs.slice(), transTime); + this._keyTimer = CollectionFreeFormView.gotoKeyframe(this._keyTimer, ffview.childDocs, transTime); context._currentFrame = NumCast(activeFrame); } } @@ -598,7 +596,7 @@ export class PresBox extends ViewBoxBaseComponent() { tagDoc.opacity = 1; } } - const hidingIndBef = itemIndexes.find(item => item >= this.itemIndex); + const hidingIndBef = itemIndexes.find(item => item >= this.itemIndex) ?? itemIndexes.slice().reverse().lastElement(); if (curDoc.presHideBefore && index === hidingIndBef) { if (index > this.itemIndex) { tagDoc.opacity = 0; @@ -606,10 +604,11 @@ export class PresBox extends ViewBoxBaseComponent() { tagDoc.opacity = 1; } } - const hidingIndAft = itemIndexes - .slice() - .reverse() - .find(item => item < this.itemIndex); + const hidingIndAft = + itemIndexes + .slice() + .reverse() + .find(item => item <= this.itemIndex) ?? itemIndexes.lastElement(); if (curDoc.presHideAfter && index === hidingIndAft) { if (index < this.itemIndex) { tagDoc.opacity = 0; @@ -875,7 +874,12 @@ export class PresBox extends ViewBoxBaseComponent() { @action selectElement = async (doc: Doc, noNav = false) => { CollectionStackedTimeline.CurrentlyPlaying?.map((clip, i) => DocumentManager.Instance.getDocumentView(clip)?.ComponentView?.Pause?.()); - !noNav && this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem); + if (noNav) { + const index = this.childDocs.indexOf(doc); + if (index >= 0 && index < this.childDocs.length) { + this.rootDoc._itemIndex = index; + } + } else this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem); this.updateCurrentPresentation(DocCast(doc.context)); }; @@ -911,19 +915,19 @@ export class PresBox extends ViewBoxBaseComponent() { //regular click @action - regularSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, selectPres = true, noNav = false) => { + regularSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, noNav: boolean, selectPres = true) => { this.clearSelectedArray(); this.addToSelectedArray(doc); this._eleArray.splice(0, this._eleArray.length, ref); this._dragArray.splice(0, this._dragArray.length, drag); - focus && this.selectElement(doc, noNav); + this.selectElement(doc, noNav); selectPres && this.selectPres(); }; - modifierSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, cmdClick: boolean, shiftClick: boolean, noNav: boolean = false) => { + modifierSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, noNav: boolean, cmdClick: boolean, shiftClick: boolean) => { if (cmdClick) this.multiSelect(doc, ref, drag); else if (shiftClick) this.shiftSelect(doc, ref, drag); - else this.regularSelect(doc, ref, drag, focus, noNav); + else this.regularSelect(doc, ref, drag, noNav); }; static keyEventsWrapper = (e: KeyboardEvent) => PresBox.Instance?.keyEvents(e); diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index f265c1315..40535c8cb 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -139,7 +139,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { onClick={e => { e.stopPropagation(); e.preventDefault(); - this.presBoxView?.modifierSelect(doc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey); + this.presBoxView?.modifierSelect(doc, this._itemRef.current!, this._dragRef.current!, e.shiftKey || e.ctrlKey || e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey); this.presExpandDocumentClick(); }}>
{`${ind + 1}.`}
@@ -177,22 +177,14 @@ export class PresElementBox extends ViewBoxBaseComponent() { const element = e.target as any; e.stopPropagation(); e.preventDefault(); - if (element && !(e.ctrlKey || e.metaKey)) { - if (this.selectedArray?.has(this.rootDoc)) { - this.selectedArray.size === 1 && this.presBoxView?.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false, false); - setupMoveUpEvents(this, e, this.startDrag, emptyFunction, emptyFunction); - } else { - setupMoveUpEvents( - this, - e, - (e: PointerEvent) => { - this.presBoxView?.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false, false); - return this.startDrag(e); - }, - emptyFunction, - emptyFunction - ); - } + if (element && !(e.ctrlKey || e.metaKey || e.button === 2)) { + this.presBoxView?.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, true, false); + setupMoveUpEvents(this, e, this.startDrag, emptyFunction, e => { + e.stopPropagation(); + e.preventDefault(); + this.presBoxView?.modifierSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, e.shiftKey || e.ctrlKey || e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey); + this.presBoxView?.activeItem && this.showRecording(this.presBoxView?.activeItem); + }); } }; @@ -314,50 +306,14 @@ export class PresElementBox extends ViewBoxBaseComponent() { /** * Method called for updating the view of the currently selected document * - * @param targetDoc + * @param presTargetDoc * @param activeItem */ @undoBatch @action - updateCapturedViewContents = (targetDoc: Doc, activeItem: Doc) => { - switch (targetDoc.type) { - case DocumentType.PDF: - case DocumentType.WEB: - case DocumentType.RTF: - const scroll = targetDoc._scrollTop; - activeItem.presPinViewScroll = scroll; - if (targetDoc.type === DocumentType.RTF) { - activeItem.presData = targetDoc[Doc.LayoutFieldKey(targetDoc)] instanceof RichTextField ? (targetDoc[Doc.LayoutFieldKey(targetDoc)] as RichTextField)[Copy]() : targetDoc.text; - } - break; - case DocumentType.INK: - activeItem.presData = targetDoc[Doc.LayoutFieldKey(targetDoc)] instanceof InkField ? (targetDoc[Doc.LayoutFieldKey(targetDoc)] as InkField)[Copy]() : targetDoc.data; - break; - case DocumentType.VID: - case DocumentType.AUDIO: - activeItem.presStartTime = targetDoc._currentTimecode; - break; - case DocumentType.COMPARISON: - const clipWidth = targetDoc._clipWidth; - activeItem.presPinClipWidth = clipWidth; - break; - case DocumentType.COL: - activeItem.presPinLayoutData = new List(DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]).map(d => JSON.stringify({ id: d[Id], x: NumCast(d.x), y: NumCast(d.y), w: NumCast(d._width), h: NumCast(d._height) }))); - default: - const bestView = DocumentManager.Instance.getFirstDocumentView(targetDoc); - if (activeItem.presPinViewBounds && bestView) { - const bounds = MarqueeView.CurViewBounds(targetDoc, bestView.props.PanelWidth(), bestView.props.PanelHeight()); - activeItem.presPinView = true; - activeItem.presPinViewScale = NumCast(targetDoc._viewScale, 1); - activeItem.presPinViewX = bounds.left + bounds.width / 2; - activeItem.presPinViewY = bounds.top + bounds.height / 2; - activeItem.presPinViewBounds = new List([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]); - } else { - activeItem.presPinViewX = targetDoc._panX; - activeItem.presPinViewY = targetDoc._panY; - activeItem.presPinViewScale = targetDoc._viewScale; - } - } + updateCapturedViewContents = (presTargetDoc: Doc, activeItem: Doc) => { + const target = DocCast(presTargetDoc.annotationOn) ?? presTargetDoc; + PresBox.pinDocView(activeItem, { pinDocContent: true, pinData: PresBox.pinDataTypes(target) }, target); }; @computed get recordingIsInOverlay() { @@ -465,24 +421,26 @@ export class PresElementBox extends ViewBoxBaseComponent() { const activeItem: Doc = this.rootDoc; const items: JSX.Element[] = []; - if (activeItem.presPinLayout) { - items.push( - Update captured doc layout
}> -
this.updateCapturedContainerLayout(targetDoc, activeItem)} style={{ fontWeight: 700, display: 'flex' }}> - L -
- - ); - } - if (activeItem.presPinData || activeItem.presPinView) { - items.push( - Update captured doc content
}> -
this.updateCapturedViewContents(targetDoc, activeItem)} style={{ fontWeight: 700, display: 'flex' }}> - C -
- - ); - } + items.push( + Update captured doc layout
}> +
setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.updateCapturedContainerLayout(targetDoc, activeItem), true)} + style={{ opacity: activeItem.presPinLayout ? 1 : 0.5, fontWeight: 700, display: 'flex' }}> + L +
+ + ); + items.push( + Update captured doc content
}> +
setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.updateCapturedViewContents(targetDoc, activeItem))} + style={{ opacity: activeItem.presPinData || activeItem.presPinView ? 1 : 0.5, fontWeight: 700, display: 'flex' }}> + C +
+ + ); if (!Doc.noviceMode) { items.push( {this.recordingIsInOverlay ? 'Hide Recording' : `${PresElementBox.videoIsRecorded(activeItem) ? 'Show' : 'Start'} recording`}
}> @@ -557,15 +515,9 @@ export class PresElementBox extends ViewBoxBaseComponent() { paddingTop: NumCast(this.layoutDoc._yPadding, this.props.yPadding), paddingBottom: NumCast(this.layoutDoc._yPadding, this.props.yPadding), }} - onClick={e => { - e.stopPropagation(); - e.preventDefault(); - this.presBoxView?.modifierSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey); - this.showRecording(activeItem); - }} onDoubleClick={action(e => { this.toggleProperties(); - this.presBoxView?.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, true); + this.presBoxView?.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false); })} onPointerOver={this.onPointerOver} onPointerLeave={this.onPointerLeave} @@ -599,7 +551,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { onPointerDown={e => { e.stopPropagation(); if (this._itemRef.current && this._dragRef.current) { - this.presBoxView?.modifierSelect(activeItem, this._itemRef.current, this._dragRef.current, false, false, false, true); + this.presBoxView?.modifierSelect(activeItem, this._itemRef.current, this._dragRef.current, true, false, false); } }} onClick={e => e.stopPropagation()}>{`${this.indexInPres + 1}. `}
diff --git a/src/fields/util.ts b/src/fields/util.ts index 3a7484cfd..6024705ec 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -1,3 +1,4 @@ +import { forEach } from 'lodash'; import { $mobx, action, observable, runInAction, trace } from 'mobx'; import { computedFn } from 'mobx-utils'; import { DocServer } from '../client/DocServer'; -- cgit v1.2.3-70-g09d2