From 3ba733ffffb3036ea941bdbb5baf4c79bc7764af Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 19 Oct 2023 23:27:59 -0400 Subject: made new golden layouts splits smaller --- src/client/goldenLayout.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js index 588bf57d1..e789d8e20 100644 --- a/src/client/goldenLayout.js +++ b/src/client/goldenLayout.js @@ -3987,7 +3987,7 @@ else variableItemCount++; } - newItemSize = (1 / variableItemCount) * (100 - fixedItemSize); + newItemSize = (1 / (variableItemCount+1)) * (100 - fixedItemSize); if (_$suspendResize === true) { this.emitBubblingEvent('stateChanged'); -- cgit v1.2.3-70-g09d2 From 661c1367d27fa23c3aeb62369e92cd36eb5edabd Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 21 Oct 2023 00:41:23 -0400 Subject: change to doc decorations to be more "lightweight". made linkBox render links in a freeform view as a DocView. added an auto-reset view option for freeforms. fixed highlighting ink strokes. Made groups behave better for selecting things 'inside' the group bounding box that aren't in the group. Added vertically centered text option. --- src/Utils.ts | 2 +- src/client/documents/Documents.ts | 35 ++++--- src/client/util/CurrentUserUtils.ts | 4 +- src/client/util/DropConverter.ts | 6 +- src/client/util/InteractionUtils.tsx | 2 +- src/client/util/LinkManager.ts | 4 +- src/client/views/DocumentDecorations.scss | 42 ++++---- src/client/views/DocumentDecorations.tsx | 50 +++++----- src/client/views/InkControlPtHandles.tsx | 12 +-- src/client/views/InkingStroke.tsx | 8 +- src/client/views/PropertiesButtons.tsx | 15 ++- src/client/views/StyleProvider.tsx | 21 ++-- .../views/collections/CollectionNoteTakingView.tsx | 2 +- .../views/collections/CollectionStackingView.tsx | 2 +- src/client/views/collections/CollectionSubView.tsx | 5 +- src/client/views/collections/TreeView.tsx | 6 +- .../CollectionFreeFormLayoutEngines.tsx | 1 + .../collectionFreeForm/CollectionFreeFormView.tsx | 65 +++++++------ .../collections/collectionFreeForm/MarqueeView.tsx | 1 - src/client/views/global/globalCssVariables.scss | 1 + .../views/nodes/CollectionFreeFormDocumentView.tsx | 11 ++- src/client/views/nodes/DocumentView.tsx | 23 +++-- src/client/views/nodes/LinkBox.tsx | 108 ++++++++++++++++++++- .../views/nodes/RecordingBox/RecordingBox.tsx | 2 +- .../nodes/formattedText/FormattedTextBox.scss | 8 ++ .../views/nodes/formattedText/FormattedTextBox.tsx | 2 +- src/fields/Doc.ts | 16 ++- 27 files changed, 300 insertions(+), 154 deletions(-) (limited to 'src') diff --git a/src/Utils.ts b/src/Utils.ts index b5ca53a33..34419f665 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -555,7 +555,7 @@ export function returnAll(): 'all' { return 'all'; } -export function returnNone() { +export function returnNone(): 'none' { return 'none'; } diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 11b5f9f08..d61e4b3e9 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -215,6 +215,7 @@ export class DocumentOptions { _lockedTransform?: BOOLt = new BoolInfo('lock the freeform_panx,freeform_pany and scale parameters of the document so that it be panned/zoomed'); layout?: string | Doc; // default layout string or template document + layout_isSvg?: BOOLt = new BoolInfo('whether document decorations and other selections should handle pointerEvents for svg content or use doc bounding box'); layout_keyValue?: STRt = new StrInfo('layout definition for showing keyValue view of document', false); layout_explainer?: STRt = new StrInfo('explanation displayed at top of a collection to describe its purpose', false); layout_headerButton?: DOCt = new DocInfo('the (button) Doc to display at the top of a collection.', false); @@ -569,10 +570,10 @@ export namespace Docs { childDontRegisterViews: true, onClick: FollowLinkScript(), layout_hideLinkAnchors: true, - _height: 150, + _height: 1, + _width: 1, link: '', link_description: '', - layout_showCaption: 'link_description', backgroundColor: 'lightblue', // lightblue is default color for linking dot and link documents text comment area _dropPropertiesToRemove: new List(['onClick']), }, @@ -667,7 +668,7 @@ export namespace Docs { { // NOTE: this is unused!! ink fields are filled in directly within the InkDocument() method layout: { view: InkingStroke, dataField: 'stroke' }, - options: { systemIcon: 'BsFillPencilFill' }, + options: { systemIcon: 'BsFillPencilFill', nativeDimModifiable: true, nativeHeightUnfrozen: true, layout_isSvg: true, layout_forceReflow: true }, }, ], [ @@ -770,7 +771,7 @@ export namespace Docs { const existing = actualProtos[id] as Doc; const type = id.replace(suffix, '') as DocumentType; // get or create prototype of the specified type... - const target = existing || buildPrototype(type, id); + const target = buildPrototype(type, id, existing); // ...and set it if not undefined (can be undefined only if TemplateMap does not contain // an entry dedicated to the given DocumentType) target && PrototypeMap.set(type, target); @@ -818,7 +819,7 @@ export namespace Docs { * @param options any value specified in the DocumentOptions object likewise * becomes the default value for that key for all delegates */ - function buildPrototype(type: DocumentType, prototypeId: string): Opt { + function buildPrototype(type: DocumentType, prototypeId: string, existing?: Doc): Opt { // load template from type const template = TemplateMap.get(type); if (!template) { @@ -844,12 +845,14 @@ export namespace Docs { layout: layout.view?.LayoutString(layout.dataField), data: template.data, }; - Object.entries(options).map(pair => { - if (typeof pair[1] === 'string' && pair[1].startsWith('@')) { - (options as any)[pair[0]] = ComputedField.MakeFunction(pair[1].substring(1)); - } - }); - return Doc.assign(new Doc(prototypeId, true), options as any, undefined, true); + Object.entries(options) + .filter(pair => typeof pair[1] === 'string' && pair[1].startsWith('@')) + .map(pair => { + if (!existing || ScriptCast(existing[pair[0]])?.script.originalScript !== pair[1].substring(1)) { + (options as any)[pair[0]] = ComputedField.MakeFunction(pair[1].substring(1)); + } + }); + return Doc.assign(existing ?? new Doc(prototypeId, true), OmitKeys(options, Object.keys(existing ?? {})).omit, undefined, true); } } @@ -1034,6 +1037,10 @@ export namespace Docs { I[Initializing] = true; I.type = DocumentType.INK; I.layout = InkingStroke.LayoutString('stroke'); + I.nativeDimModifiable = true; + I.nativeHeightUnfrozen = true; + I.layout_isSvg = true; + I.layout_forceReflow = true; I.layout_fitWidth = false; I.layout_hideDecorationTitle = true; // don't show title when selected // I.layout_hideOpenButton = true; // don't show open full screen button when selected @@ -1402,8 +1409,10 @@ export namespace DocUtils { link_relationship: linkSettings.link_relationship, link_description: linkSettings.link_description, link_autoMoveAnchors: true, - _layout_showCaption: 'link_description', - _layout_showTitle: 'link_relationship', + _layout_showCaption: '', // removed since they conflict with showing a link with a LinkBox (ie, line, not comparison box) + _layout_showTitle: '', + // _layout_showCaption: 'link_description', + // _layout_showTitle: 'link_relationship', }, id ), diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index cc8f72ddf..268ee2790 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -659,7 +659,7 @@ export class CurrentUserUtils { subMenu: [ { title: "Left", toolTip: "Left align (Cmd-[)", btnType: ButtonType.ToggleButton, icon: "align-left", toolType:"left", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}' }}, { title: "Center", toolTip: "Center align (Cmd-\\)",btnType: ButtonType.ToggleButton, icon: "align-center",toolType:"center",ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, - { title: "Right", toolTip: "Right align (Cmd-])", btnType: ButtonType.ToggleButton, icon: "align-right", toolType:"right", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, + { title: "Right", toolTip: "Right align (Cmd-])", btnType: ButtonType.ToggleButton, icon: "align-right", toolType:"right", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, ] }, { title: "Dictate", toolTip: "Dictate", btnType: ButtonType.ToggleButton, icon: "microphone", toolType:"dictation", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'}}, @@ -832,7 +832,7 @@ export class CurrentUserUtils { // childContextMenuLabels: new List(["Add to Dashboards",]), // childContextMenuIcons: new List(["user-plus",]), "acl-Guest": SharingPermissions.Augment, "_acl-Guest": SharingPermissions.Augment, - childDragAction: "embed", isSystem: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true, + childDragAction: "embed", isSystem: true, contentPointerEvents: "none", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true, // NOTE: treeView_HideTitle & _layout_showTitle is for a TreeView's editable title, _layout_showTitle is for DocumentViews title bar _layout_showTitle: "title", treeView_HideTitle: true, ignoreClick: true, _lockedPosition: true, layout_boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true, layout_explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'" diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index dbdf580cd..2c371f28e 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -7,7 +7,7 @@ import { Cast, StrCast } from '../../fields/Types'; import { ImageField } from '../../fields/URLField'; import { Docs } from '../documents/Documents'; import { DocumentType } from '../documents/DocumentTypes'; -import { ButtonType } from '../views/nodes/FontIconBox/FontIconBox'; +import { ButtonType, FontIconBox } from '../views/nodes/FontIconBox/FontIconBox'; import { DragManager } from './DragManager'; import { ScriptingGlobals } from './ScriptingGlobals'; @@ -56,7 +56,7 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) { data?.draggedDocuments.map((doc, i) => { let dbox = doc; // bcz: isButtonBar is intended to allow a collection of linear buttons to be dropped and nested into another collection of buttons... it's not being used yet, and isn't very elegant - if (doc.type === DocumentType.FONTICON || StrCast(Doc.Layout(doc).layout).includes('FontIconBox')) { + if (doc.type === DocumentType.FONTICON || StrCast(Doc.Layout(doc).layout).includes(FontIconBox.name)) { if (data.dropPropertiesToRemove || dbox.dropPropertiesToRemove) { //dbox = Doc.MakeEmbedding(doc); // don't need to do anything if dropping an icon doc onto an icon bar since there should be no layout data for an icon dbox = Doc.MakeEmbedding(dbox); @@ -78,7 +78,7 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) { backgroundColor: StrCast(doc.backgroundColor), title: StrCast(layoutDoc.title), btnType: ButtonType.ClickButton, - icon: layoutDoc.isTemplateDoc ? 'font' : 'bolt', + icon: 'bolt', }); dbox.dragFactory = layoutDoc; dbox.dropPropertiesToRemove = doc.dropPropertiesToRemove instanceof ObjectField ? ObjectField.MakeCopy(doc.dropPropertiesToRemove) : undefined; diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx index 4e32ed67f..6406624bb 100644 --- a/src/client/util/InteractionUtils.tsx +++ b/src/client/util/InteractionUtils.tsx @@ -172,7 +172,7 @@ export namespace InteractionUtils { (); private _inkDragDocs: { doc: Doc; x: number; y: number; width: number; height: number }[] = []; @@ -70,8 +71,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P DocumentDecorations.Instance = this; reaction( () => SelectionManager.Views().slice(), - action(docs => { - this._showNothing = !DocumentView.LongPress && docs.length === 1; // show decorations if multiple docs are selected or we're long pressing + action(views => { + this._showNothing = !DocumentView.LongPress && views.length === 1; // show decorations if multiple docs are selected or we're long pressing this._editingTitle = false; }) ); @@ -80,8 +81,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P 'pointermove', action(e => { if (this.Bounds.x || this.Bounds.y || this.Bounds.r || this.Bounds.b) { - if (this.Bounds.x !== Number.MAX_VALUE && (this.Bounds.x > e.clientX || this.Bounds.r < e.clientX || this.Bounds.y > e.clientY || this.Bounds.b < e.clientY)) { + if (this.Bounds.x !== Number.MAX_VALUE && (this.Bounds.x > e.clientX + 10 || this.Bounds.r < e.clientX - 10 || this.Bounds.y > e.clientY + 10 || this.Bounds.b < e.clientY - 10)) { this._showNothing = false; + } else { + this._showNothing = true; } } }) @@ -225,10 +228,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P e.x, e.y, { - dragComplete: action(e => { - dragData.canEmbed && SelectionManager.DeselectAll(); - this._hidden = false; - }), + dragComplete: action(e => (this._hidden = false)), hideSource: true, } ); @@ -255,7 +255,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P iconView.props.removeDocument?.(iconView.props.Document); } }); - SelectionManager.DeselectAll(); + views.forEach(v => SelectionManager.DeselectView()); } this._iconifyBatch?.end(); this._iconifyBatch = undefined; @@ -403,10 +403,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P }), // moveEvent action(action(() => (this._isRotating = false))), // upEvent action((e, doubleTap) => { - if (doubleTap) { - seldocview.rootDoc.rotation_centerX = 0.5; - seldocview.rootDoc.rotation_centerY = 0.5; - } + seldocview.rootDoc.rotation_centerX = 0; + seldocview.rootDoc.rotation_centerY = 0; }) ); }; @@ -777,10 +775,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P const hideDecorations = seldocview.props.hideDecorations || seldocview.rootDoc.hideDecorations; const hideResizers = ![AclAdmin, AclEdit, AclAugment].includes(GetEffectiveAcl(seldocview.rootDoc)) || hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.layout_hideResizeHandles || this._isRounding || this._isRotating; - const hideTitle = hideDecorations || seldocview.props.hideDecorationTitle || seldocview.rootDoc.layout_hideDecorationTitle || this._isRounding || this._isRotating; + const hideTitle = this._showNothing || hideDecorations || seldocview.props.hideDecorationTitle || seldocview.rootDoc.layout_hideDecorationTitle || this._isRounding || this._isRotating; const hideDocumentButtonBar = hideDecorations || seldocview.props.hideDocumentButtonBar || seldocview.rootDoc.layout_hideDocumentButtonBar || this._isRounding || this._isRotating; // if multiple documents have been opened at the same time, then don't show open button const hideOpenButton = + this._showNothing || hideDecorations || seldocview.props.hideOpenButton || seldocview.rootDoc.layout_hideOpenButton || @@ -788,6 +787,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P this._isRounding || this._isRotating; const hideDeleteButton = + this._showNothing || hideDecorations || this._isRounding || this._isRotating || @@ -821,7 +821,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P // Radius constants const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox || seldocview.ComponentView instanceof CollectionFreeFormView; - const borderRadius = numberValue(StrCast(seldocview.rootDoc.layout_borderRounding)); + const borderRadius = numberValue(Cast(seldocview.rootDoc.layout_borderRounding, 'string', null)); const docMax = Math.min(NumCast(seldocview.rootDoc.width) / 2, NumCast(seldocview.rootDoc.height) / 2); const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2); const radiusHandle = (borderRadius / docMax) * maxDist; @@ -884,9 +884,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P )} ); - + const freeformDoc = SelectionManager.Views().some(v => v.props.CollectionFreeFormDocumentView?.()); return ( -
+
e.preventDefault()} /> )} - {hideDocumentButtonBar ? null : ( + {hideDocumentButtonBar || this._showNothing ? null : (
{this._isRotating ? null : ( tap to set rotate center, drag to rotate
}> -
e.preventDefault()}> - } color={Colors.LIGHT_GRAY} /> +
e.preventDefault()}> + } color={SettingsManager.userColor} />
)} diff --git a/src/client/views/InkControlPtHandles.tsx b/src/client/views/InkControlPtHandles.tsx index 07e3270b1..0d7f7ebd8 100644 --- a/src/client/views/InkControlPtHandles.tsx +++ b/src/client/views/InkControlPtHandles.tsx @@ -2,18 +2,16 @@ 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 { ControlPoint, InkData, PointData } 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 { Cast } from '../../fields/Types'; +import { returnFalse, setupMoveUpEvents } from '../../Utils'; +import { SelectionManager } from '../util/SelectionManager'; 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; @@ -155,7 +153,7 @@ export class InkControlPtHandles extends React.Component { cx={control.X} cy={control.Y} r={this.props.screenSpaceLineWidth * 2 * scale} - opacity={this.props.inkView.controlUndo ? 0.15 : 1} + opacity={this.props.inkView.controlUndo ? 0.35 : 1} height={this.props.screenSpaceLineWidth * 4 * scale} width={this.props.screenSpaceLineWidth * 4 * scale} strokeWidth={this.props.screenSpaceLineWidth / 2} diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 9b52f5870..d619f5123 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -372,6 +372,7 @@ export class InkingStroke extends ViewBoxBaseComponent() { } render() { TraceMobx(); + if (!this.rootDoc._layout_isSvg) setTimeout(action(() => (this.rootDoc._layout_isSvg = true))); const { inkData, inkStrokeWidth, inkLeft, inkTop, inkScaleX, inkScaleY, inkWidth, inkHeight } = this.inkScaledData(); const startMarker = StrCast(this.layoutDoc.stroke_startMarker); @@ -390,7 +391,7 @@ export class InkingStroke extends ViewBoxBaseComponent() { } const highlight = !this.controlUndo && this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Highlighting); const highlightIndex = highlight?.highlightIndex; - const highlightColor = (!this.props.isSelected() || !isInkMask) && highlight?.highlightIndex ? highlight?.highlightColor : undefined; + const highlightColor = !this.props.isSelected() && !isInkMask && highlight?.highlightIndex ? highlight?.highlightColor : undefined; const color = StrCast(this.layoutDoc.stroke_outlineColor, !closed && fillColor && fillColor !== 'transparent' ? StrCast(this.layoutDoc.color, 'transparent') : 'transparent'); // Visually renders the polygonal line made by the user. @@ -419,15 +420,16 @@ export class InkingStroke extends ViewBoxBaseComponent() { undefined, color === 'transparent' ? highlightColor : undefined ); + const higlightMargin = Math.min(12, Math.max(2, 0.3 * inkStrokeWidth)); // Invisible polygonal line that enables the ink to be selected by the user. const clickableLine = (downHdlr?: (e: React.PointerEvent) => void, mask: boolean = false) => InteractionUtils.CreatePolyline( inkData, inkLeft, inkTop, - mask && color === 'transparent' ? this.strokeColor : color, + mask && color === 'transparent' ? this.strokeColor : highlightColor ?? color, inkStrokeWidth, - inkStrokeWidth + NumCast(this.layoutDoc.stroke_borderWidth) + (fillColor ? (closed ? 2 : (highlightIndex ?? 0) + 2) : 2), + inkStrokeWidth + NumCast(this.layoutDoc.stroke_borderWidth) + (fillColor ? (closed ? higlightMargin : (highlightIndex ?? 0) + higlightMargin) : higlightMargin), StrCast(this.layoutDoc.stroke_lineJoin), StrCast(this.layoutDoc.stroke_lineCap), StrCast(this.layoutDoc.stroke_bezier), diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index d1561fd67..61aa616ec 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -61,8 +61,8 @@ export class PropertiesButtons extends React.Component<{}, {}> { text={label(targetDoc?.[property])} color={SettingsManager.userColor} icon={icon(targetDoc?.[property] as any)} - iconPlacement={'left'} - align={'flex-start'} + iconPlacement="left" + align="flex-start" fillWidth={true} toggleType={ToggleType.BUTTON} onClick={undoable(() => { @@ -173,6 +173,16 @@ export class PropertiesButtons extends React.Component<{}, {}> { ); } + @computed get verticalAlignButton() { + //select text + return this.propertyToggleBtn( + on => (on ? 'ALIGN TOP' : 'ALIGN CENTER'), + '_layout_centered', + on => `${on ? 'Text is aligned with top of document' : 'Text is aligned with center of document'} `, + on => // 'eye' + ); + } + @computed get fitContentButton() { return this.propertyToggleBtn( on => (on ? 'PREVIOUS VIEW' : 'VIEW ALL'), //'View All', @@ -512,6 +522,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { {toggle(this.layout_fitWidthButton)} {/* {toggle(this.freezeThumb)} */} {toggle(this.forceActiveButton)} + {toggle(this.verticalAlignButton, { display: !isText ? 'none' : '' })} {toggle(this.fitContentButton, { display: !isFreeForm && !isMap ? 'none' : '' })} {/* {toggle(this.isLightboxButton, { display: !isFreeForm && !isMap ? 'none' : '' })} */} {toggle(this.layout_autoHeightButton, { display: !isText && !isStacking && !isTree ? 'none' : '' })} diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 6ee96de5b..48c96d064 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -1,10 +1,11 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; -import { Dropdown, DropdownType, IconButton, IListItemProps, ListBox, ListItem, Popup, Shadows, Size, Type } from 'browndash-components'; +import { Dropdown, DropdownType, IconButton, IListItemProps, Shadows, Size, Type } from 'browndash-components'; import { action, runInAction, untracked } from 'mobx'; import { extname } from 'path'; import { BsArrowDown, BsArrowDownUp, BsArrowUp } from 'react-icons/bs'; +import { FaFilter } from 'react-icons/fa'; import { Doc, Opt, StrListCast } from '../../fields/Doc'; import { BoolCast, Cast, DocCast, ImageCast, NumCast, StrCast } from '../../fields/Types'; import { DashColor, lightOrDark, Utils } from '../../Utils'; @@ -13,18 +14,16 @@ import { DocFocusOrOpen, DocumentManager } from '../util/DocumentManager'; import { IsFollowLinkScript } from '../util/LinkFollower'; import { LinkManager } from '../util/LinkManager'; import { SelectionManager } from '../util/SelectionManager'; -import { ColorScheme, SettingsManager } from '../util/SettingsManager'; +import { SettingsManager } from '../util/SettingsManager'; import { undoBatch, UndoManager } from '../util/UndoManager'; import { TreeSort } from './collections/TreeSort'; import { Colors } from './global/globalEnums'; -import { InkingStroke } from './InkingStroke'; import { DocumentView, DocumentViewProps } from './nodes/DocumentView'; import { FieldViewProps } from './nodes/FieldView'; import { KeyValueBox } from './nodes/KeyValueBox'; +import { PropertiesView } from './PropertiesView'; import './StyleProvider.scss'; import React = require('react'); -import { PropertiesView } from './PropertiesView'; -import { FaFilter } from 'react-icons/fa'; export enum StyleProp { TreeViewIcon = 'treeView_Icon', @@ -85,7 +84,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt doc && StrCast(Doc.Layout(doc).layout).includes(InkingStroke.name) && !props?.LayoutTemplateString; + const isInk = () => doc && doc._layout_isSvg && !props?.LayoutTemplateString; const isOpen = property.includes(':open'); const isEmpty = property.includes(':empty'); const boxBackground = property.includes(':box'); @@ -124,7 +123,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt, props: Opt, props: Opt, props: Opt { diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx index c7ad80f11..afeef5a8f 100644 --- a/src/client/views/collections/CollectionNoteTakingView.tsx +++ b/src/client/views/collections/CollectionNoteTakingView.tsx @@ -265,7 +265,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { addDocument={this.props.addDocument} moveDocument={this.props.moveDocument} removeDocument={this.props.removeDocument} - contentPointerEvents={StrCast(this.layoutDoc.contentPointerEvents)} + contentPointerEvents={StrCast(this.layoutDoc.contentPointerEvents) as any} whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged} addDocTab={this.props.addDocTab} bringToFront={returnFalse} diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index c43a9d2b8..0b29e7286 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -357,7 +357,7 @@ export class CollectionStackingView extends CollectionSubView(moreProps?: X) { (de.embedKey || dropAction || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.rootDoc)) && (dropAction !== 'inSame' || docDragData.draggedDocuments.every(d => d.embedContainer === this.rootDoc)); const moved = docDragData.moveDocument(movedDocs, this.rootDoc, canAdd ? this.addDocument : returnFalse); added = canAdd || moved ? moved : undefined; - } else { + } else if (addedDocs.length) { + added = this.addDocument(addedDocs); + } + if (!added && ScriptCast(this.rootDoc.dropConverter)) { ScriptCast(this.rootDoc.dropConverter)?.script.run({ dragData: docDragData }); added = addedDocs.length ? this.addDocument(addedDocs) : true; } diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index f89aa065b..b57402531 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -201,12 +201,10 @@ export class TreeView extends React.Component { if (!docView) { this._editTitle = false; } else if (docView.isSelected()) { - const doc = docView.Document; - SelectionManager.SelectSchemaViewDoc(doc); this._editTitle = true; this._disposers.selection = reaction( - () => SelectionManager.SelectedSchemaDoc(), - seldoc => seldoc !== doc && this.setEditTitle(undefined) + () => docView.isSelected(), + isSel => !isSel && this.setEditTitle(undefined) ); } else { docView.select(false); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index c1c01eacb..d93e44ab7 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -42,6 +42,7 @@ export interface PoolData { transition?: string; highlight?: boolean; replica: string; + pointerEvents?: string; // without this, toggling lockPosition of a group/collection in a freeform view won't update until something else invalidates the freeform view's documents forcing -- this is a problem with doLayoutComputation which makes a performance test to insure somethingChanged pair: { layout: Doc; data?: Doc }; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index bfc61f601..5eff6b8e0 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -16,17 +16,18 @@ import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } fro import { ImageField } from '../../../../fields/URLField'; import { TraceMobx } from '../../../../fields/util'; import { GestureUtils } from '../../../../pen-gestures/GestureUtils'; -import { aggregateBounds, emptyFunction, intersectRect, lightOrDark, returnFalse, returnNone, returnTrue, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils'; +import { aggregateBounds, emptyFunction, intersectRect, lightOrDark, returnFalse, returnNone, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils'; import { CognitiveServices } from '../../../cognitive_services/CognitiveServices'; import { Docs, DocUtils } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager, dropActionType } from '../../../util/DragManager'; import { InteractionUtils } from '../../../util/InteractionUtils'; +import { FollowLinkScript } from '../../../util/LinkFollower'; import { ReplayMovements } from '../../../util/ReplayMovements'; import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; import { SelectionManager } from '../../../util/SelectionManager'; -import { ColorScheme, freeformScrollMode } from '../../../util/SettingsManager'; +import { freeformScrollMode } from '../../../util/SettingsManager'; import { SnappingManager } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; import { undoBatch, UndoManager } from '../../../util/UndoManager'; @@ -51,7 +52,6 @@ import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCurso import './CollectionFreeFormView.scss'; import { MarqueeView } from './MarqueeView'; import React = require('react'); -import { FollowLinkScript } from '../../../util/LinkFollower'; export type collectionFreeformViewProps = { NativeWidth?: () => number; @@ -156,12 +156,11 @@ export class CollectionFreeFormView extends CollectionSubView e.bounds?.width && !e.bounds.z).map(e => e.bounds!), - NumCast(this.layoutDoc._xPadding, 10), - NumCast(this.layoutDoc._yPadding, 10) - ); + : aggregateBounds( + this._layoutElements.filter(e => e.bounds?.width && !e.bounds.z).map(e => e.bounds!), + NumCast(this.layoutDoc._xPadding, 10), + NumCast(this.layoutDoc._yPadding, 10) + ); } @computed get nativeWidth() { return this.props.NativeWidth?.() || (this.fitContentsToBox ? 0 : Doc.NativeWidth(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null))); @@ -1183,23 +1182,24 @@ export class CollectionFreeFormView extends CollectionSubView { - if (sendToBack) { - const docs = this.childLayoutPairs.map(pair => pair.layout).slice(); - docs.sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex)); - let zfirst = docs.length ? NumCast(docs[0].zIndex) : 0; - doc.zIndex = zfirst - 1; - } else if (doc.stroke_isInkMask) { + if (doc.stroke_isInkMask) { doc.zIndex = 5000; } else { - const docs = this.childLayoutPairs.map(pair => pair.layout).slice(); - docs.sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex)); - let zlast = docs.length ? Math.max(docs.length, NumCast(docs.lastElement().zIndex)) : 1; - if (docs.lastElement() !== doc) { - if (zlast - docs.length > 100) { - for (let i = 0; i < docs.length; i++) doc.zIndex = i + 1; - zlast = docs.length + 1; + // prettier-ignore + const docs = this.childLayoutPairs.map(pair => pair.layout) + .sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex)); + if (sendToBack) { + const zfirst = docs.length ? NumCast(docs[0].zIndex) : 0; + doc.zIndex = zfirst - 1; + } else { + let zlast = docs.length ? Math.max(docs.length, NumCast(docs.lastElement().zIndex)) : 1; + if (docs.lastElement() !== doc) { + if (zlast - docs.length > 100) { + for (let i = 0; i < docs.length; i++) doc.zIndex = i + 1; + zlast = docs.length + 1; + } + doc.zIndex = zlast + 1; } - doc.zIndex = zlast + 1; } } }; @@ -1291,25 +1291,28 @@ export class CollectionFreeFormView extends CollectionSubView this._pointerEvents; + childPointerEventsFunc = () => this.childPointerEvents; childContentsActive = () => (this.props.childContentsActive ?? this.isContentActive() === false ? returnFalse : emptyFunction)(); + groupChildPointerEvents = () => (this.props.isDocumentActive?.() ? 'all' : 'none'); getChildDocView(entry: PoolData) { const childLayout = entry.pair.layout; const childData = entry.pair.data; return ( ); @@ -1412,6 +1415,7 @@ export class CollectionFreeFormView extends CollectionSubView this.isContentActive(), - active => this.rootDoc[this.autoResetFieldKey] && !active && this.resetView() + () => this.isContentActive(), // if autoreset is on, then whenever the view is selected, it will be restored to it default pan/zoom positions + active => this.rootDoc[this.autoResetFieldKey] && active && this.resetView() ); this._disposers.fitContent = reaction( diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 5c053fefc..a30ec5302 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -377,7 +377,6 @@ export class MarqueeView extends React.Component 'none' | 'all' | undefined; // pointer events for this freeform doc view wrapper that are not passed to the docView. This allows items in a group to trigger the group to be selected, without selecting the items themselves } @observer @@ -191,17 +191,18 @@ export class CollectionFreeFormDocumentView extends DocComponent this.sizeProvider?.height || this.props.PanelHeight?.(); screenToLocalTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.X, -this.Y); returnThis = () => this; + render() { TraceMobx(); const divProps: DocumentViewProps = { - ...this.props, + ...OmitKeys(this.props, ['GroupPointerEvents']).omit, CollectionFreeFormDocumentView: this.returnThis, styleProvider: this.styleProvider, ScreenToLocalTransform: this.screenToLocalTransform, PanelWidth: this.panelWidth, PanelHeight: this.panelHeight, }; - const isInk = StrCast(this.layoutDoc.layout).includes(InkingStroke.name) && !this.props.LayoutTemplateString && !this.layoutDoc._stroke_isInkMask; + const isInk = this.layoutDoc._layout_isSvg && !this.props.LayoutTemplateString && !this.layoutDoc._stroke_isInkMask; return (
{this.props.renderCutoffProvider(this.props.Document) ? (
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index da665a502..c2355e4f7 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,5 +1,5 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; -import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; +import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx'; import { observer } from 'mobx-react'; import { computedFn } from 'mobx-utils'; import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal'; @@ -53,6 +53,7 @@ import { PresEffect, PresEffectDirection } from './trails'; import { PinProps, PresBox } from './trails/PresBox'; import React = require('react'); import { KeyValueBox } from './KeyValueBox'; +import { LinkBox } from './LinkBox'; const { Howl } = require('howler'); interface Window { @@ -142,6 +143,7 @@ export interface DocComponentView { annotationKey?: string; getTitle?: () => string; getCenter?: (xf: Transform) => { X: number; Y: number }; + screenBounds?: () => { left: number; top: number; right: number; bottom: number; center?:{X:number, Y:number} }; ptToScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number }; ptFromScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number }; snapPt?: (pt: { X: number; Y: number }, excludeSegs?: number[]) => { nearestPt: { X: number; Y: number }; distance: number }; @@ -154,7 +156,6 @@ export interface DocumentViewSharedProps { renderDepth: number; Document: Doc; DataDoc?: Doc; - contentBounds?: () => undefined | { x: number; y: number; r: number; b: number }; fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _freeform_fitContentsToBox property on a Document suppressSetHeight?: boolean; setContentView?: (view: DocComponentView) => any; @@ -219,7 +220,7 @@ export interface DocumentViewProps extends DocumentViewSharedProps { hideLinkAnchors?: boolean; isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events isContentActive: () => boolean | undefined; // whether document contents should handle pointer events - contentPointerEvents?: string; // pointer events allowed for content of a document view. eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents + contentPointerEvents?: 'none' | 'all' | undefined; // pointer events allowed for content of a document view. eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents radialMenu?: String[]; LayoutTemplateString?: string; dontCenter?: 'x' | 'y' | 'xy'; @@ -425,7 +426,7 @@ export class DocumentViewInternal extends DocComponent boolean = returnFalse; onClick = action((e: React.MouseEvent | React.PointerEvent) => { - if (!this.Document.ignoreClick && this.pointerEvents !== 'none' && this.props.renderDepth >= 0 && Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, this._downTime)) { + if (!this.Document.ignoreClick && this.props.renderDepth >= 0 && Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, this._downTime)) { let stopPropagate = true; let preventDefault = true; !this.rootDoc._keepZWhenDragged && this.props.bringToFront(this.rootDoc); @@ -536,7 +537,6 @@ export class DocumentViewInternal extends DocComponent this._contentPointerEvents; @computed get contents() { TraceMobx(); - const isInk = StrCast(this.layoutDoc.layout).includes(InkingStroke.name) && !this.props.LayoutTemplateString; + const isInk = this.layoutDoc._layout_isSvg && !this.props.LayoutTemplateString; return (
{ return this.props.dontCenter?.includes('y') ? 0 : this.Yshift; } - public toggleNativeDimensions = () => this.docView && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.NativeDimScaling, this.props.PanelWidth(), this.props.PanelHeight()); + public toggleNativeDimensions = () => this.docView && this.rootDoc.type !== DocumentType.INK && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.NativeDimScaling, this.props.PanelWidth(), this.props.PanelHeight()); public getBounds = () => { if (!this.docView?.ContentDiv || this.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) { return undefined; } + if (this.docView._componentView?.screenBounds) { + return this.docView._componentView.screenBounds(); + } const xf = this.docView.props .ScreenToLocalTransform() .scale(this.trueNativeWidth() ? this.nativeScaling : 1) .inverse(); const [[left, top], [right, bottom]] = [xf.transformPoint(0, 0), xf.transformPoint(this.panelWidth, this.panelHeight)]; + if (this.docView.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) { const docuBox = this.docView.ContentDiv.getElementsByClassName('linkAnchorBox-cont'); if (docuBox.length) return { ...docuBox[0].getBoundingClientRect(), center: undefined }; diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index efb949a47..d871c88ba 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -1,11 +1,19 @@ import React = require('react'); +import { Bezier } from 'bezier-js'; +import { computed, action } from 'mobx'; import { observer } from 'mobx-react'; -import { emptyFunction, returnAlways, returnFalse, returnTrue } from '../../../Utils'; +import { Height, Width } from '../../../fields/DocSymbols'; +import { Id } from '../../../fields/FieldSymbols'; +import { DocCast, StrCast } from '../../../fields/Types'; +import { aggregateBounds, emptyFunction, returnAlways, returnFalse, Utils } from '../../../Utils'; +import { DocumentManager } from '../../util/DocumentManager'; import { ViewBoxBaseComponent } from '../DocComponent'; import { StyleProp } from '../StyleProvider'; import { ComparisonBox } from './ComparisonBox'; import { FieldView, FieldViewProps } from './FieldView'; import './LinkBox.scss'; +import { CollectionFreeFormView } from '../collections/collectionFreeForm'; +import { Transform } from '../../util/Transform'; @observer export class LinkBox extends ViewBoxBaseComponent() { @@ -17,8 +25,104 @@ export class LinkBox extends ViewBoxBaseComponent() { componentDidMount() { this.props.setContentView?.(this); } + @computed get anchor1() { + const anchor1 = DocCast(this.rootDoc.link_anchor_1); + const anchor_1 = anchor1?.layout_unrendered ? DocCast(anchor1.annotationOn) : anchor1; + return DocumentManager.Instance.getDocumentView(anchor_1); + } + @computed get anchor2() { + const anchor2 = DocCast(this.rootDoc.link_anchor_2); + const anchor_2 = anchor2?.layout_unrendered ? DocCast(anchor2.annotationOn) : anchor2; + return DocumentManager.Instance.getDocumentView(anchor_2); + } + screenBounds = () => { + if (this.layoutDoc._layout_isSvg && this.anchor1 && this.anchor2 && this.anchor1.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView) { + const a_invXf = this.anchor1.props.ScreenToLocalTransform().inverse(); + const b_invXf = this.anchor2.props.ScreenToLocalTransform().inverse(); + const a_scrBds = { tl: a_invXf.transformPoint(0, 0), br: a_invXf.transformPoint(this.anchor1.rootDoc[Width](), this.anchor1.rootDoc[Height]()) }; + const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(this.anchor2.rootDoc[Width](), this.anchor2.rootDoc[Height]()) }; + + const pts = [] as number[][]; + pts.push([(a_scrBds.tl[0] + a_scrBds.br[0]) / 2, (a_scrBds.tl[1] + a_scrBds.br[1]) / 2]); + pts.push(Utils.getNearestPointInPerimeter(a_scrBds.tl[0], a_scrBds.tl[1], a_scrBds.br[0] - a_scrBds.tl[0], a_scrBds.br[1] - a_scrBds.tl[1], (b_scrBds.tl[0] + b_scrBds.br[0]) / 2, (b_scrBds.tl[1] + b_scrBds.br[1]) / 2)); + pts.push(Utils.getNearestPointInPerimeter(b_scrBds.tl[0], b_scrBds.tl[1], b_scrBds.br[0] - b_scrBds.tl[0], b_scrBds.br[1] - b_scrBds.tl[1], (a_scrBds.tl[0] + a_scrBds.br[0]) / 2, (a_scrBds.tl[1] + a_scrBds.br[1]) / 2)); + pts.push([(b_scrBds.tl[0] + b_scrBds.br[0]) / 2, (b_scrBds.tl[1] + b_scrBds.br[1]) / 2]); + const agg = aggregateBounds( + pts.map(pt => ({ x: pt[0], y: pt[1] })), + 0, + 0 + ); + return { left: agg.x, top: agg.y, right: agg.r, bottom: agg.b, center: undefined }; + } + return { left: 0, top: 0, right: 0, bottom: 0, center: undefined }; + }; render() { - if (this.dataDoc.treeView_Open === undefined) setTimeout(() => (this.dataDoc.treeView_Open = true)); + if (this.layoutDoc._layout_isSvg && (this.anchor1 || this.anchor2)?.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView) { + const a = (this.anchor1 ?? this.anchor2)!; + const b = (this.anchor2 ?? this.anchor1)!; + + const parxf = this.props.docViewPath()[this.props.docViewPath().length - 2].ComponentView as CollectionFreeFormView; + const this_xf = parxf?.getTransform() ?? Transform.Identity; //this.props.ScreenToLocalTransform(); + const a_invXf = a.props.ScreenToLocalTransform().inverse(); + const b_invXf = b.props.ScreenToLocalTransform().inverse(); + const a_scrBds = { tl: a_invXf.transformPoint(0, 0), br: a_invXf.transformPoint(a.rootDoc[Width](), a.rootDoc[Height]()) }; + const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(b.rootDoc[Width](), b.rootDoc[Height]()) }; + const a_bds = { tl: this_xf.transformPoint(a_scrBds.tl[0], a_scrBds.tl[1]), br: this_xf.transformPoint(a_scrBds.br[0], a_scrBds.br[1]) }; + const b_bds = { tl: this_xf.transformPoint(b_scrBds.tl[0], b_scrBds.tl[1]), br: this_xf.transformPoint(b_scrBds.br[0], b_scrBds.br[1]) }; + + const ppt1 = [(a_bds.tl[0] + a_bds.br[0]) / 2, (a_bds.tl[1] + a_bds.br[1]) / 2]; + const pt1 = Utils.getNearestPointInPerimeter(a_bds.tl[0], a_bds.tl[1], a_bds.br[0] - a_bds.tl[0], a_bds.br[1] - a_bds.tl[1], (b_bds.tl[0] + b_bds.br[0]) / 2, (b_bds.tl[1] + b_bds.br[1]) / 2); + const pt2 = Utils.getNearestPointInPerimeter(b_bds.tl[0], b_bds.tl[1], b_bds.br[0] - b_bds.tl[0], b_bds.br[1] - b_bds.tl[1], (a_bds.tl[0] + a_bds.br[0]) / 2, (a_bds.tl[1] + a_bds.br[1]) / 2); + const ppt2 = [(b_bds.tl[0] + b_bds.br[0]) / 2, (b_bds.tl[1] + b_bds.br[1]) / 2]; + + const pts = [ppt1, pt1, pt2, ppt2].map(pt => [pt[0], pt[1]]); + const [lx, rx, ty, by] = [Math.min(pt1[0], pt2[0]), Math.max(pt1[0], pt2[0]), Math.min(pt1[1], pt2[1]), Math.max(pt1[1], pt2[1])]; + setTimeout( + action(() => { + this.layoutDoc.x = lx; + this.layoutDoc.y = ty; + this.layoutDoc._width = rx - lx; + this.layoutDoc._height = by - ty; + }) + ); + + const highlight = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Highlighting); + const highlightColor = highlight?.highlightIndex ? highlight?.highlightColor : undefined; + + const bez = new Bezier(pts.map(p => ({ x: p[0], y: p[1] }))); + const text = bez.get(0.5); + const linkDesc = StrCast(this.rootDoc.link_description) || 'description'; + return ( +
+ + + + + + + + + + + + +   + {linkDesc.substring(0, 50) + (linkDesc.length > 50 ? '...' : '')} +   + + +
+ ); + } return (
() { Doc.AddToMyOverlay(value); DocumentManager.Instance.AddViewRenderedCb(value, docView => { Doc.UserDoc().currentRecording = docView.rootDoc; - SelectionManager.SelectSchemaViewDoc(value); + docView.select(false); RecordingBox.resumeWorkspaceReplaying(value); }); } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss index 348bdd79e..6765e1dea 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.scss +++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss @@ -5,6 +5,14 @@ height: 100%; min-height: 100%; } +.formattedTextBox-inner.centered, +.formattedTextBox-inner-rounded.centered { + align-items: center; + display: flex; + .ProseMirror { + min-height: unset; + } +} .ProseMirror:focus { outline: none !important; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index fcdfbdf2a..41b1c59b0 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -2149,7 +2149,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent
{ - const [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)]; - const [bptX, bptY] = [sptX + doc[Width](), sptY + doc[Height]()]; - return { - x: Math.min(sptX, bounds.x), - y: Math.min(sptY, bounds.y), - r: Math.max(bptX, bounds.r), - b: Math.max(bptY, bounds.b), - }; - }, + (bounds, doc) => ({ + x: Math.min(NumCast(doc.x), bounds.x), + y: Math.min(NumCast(doc.y), bounds.y), + r: Math.max(NumCast(doc.x) + doc[Width](), bounds.r), + b: Math.max(NumCast(doc.y) + doc[Height](), bounds.b), + }), { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: -Number.MAX_VALUE, b: -Number.MAX_VALUE } ); return bounds; -- cgit v1.2.3-70-g09d2 From 3141c62397071efee60510ef90df69ff04701757 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 21 Oct 2023 14:26:33 -0400 Subject: change doc decorations rotation icon to bottom right. cleaned up lock icon. fixed autoresetview of collections to not fire when dragging. --- src/client/views/DocumentDecorations.scss | 17 +++++++++++------ src/client/views/DocumentDecorations.tsx | 8 +++----- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- 3 files changed, 15 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index 2764339e6..f41cf1385 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -14,8 +14,9 @@ $resizeHandler: 8px; border-radius: 100%; height: 30; width: 30; - right: -30; - top: calc(50% - 15px); + right: -40; + bottom: -40; + //top: calc(50% - 15px); position: absolute; pointer-events: all; cursor: pointer; @@ -364,15 +365,19 @@ $resizeHandler: 8px; position: relative; background: black; color: rgb(145, 144, 144); - height: 14; - width: 14; + height: 20; + width: 20; pointer-events: all; margin: auto; display: flex; align-items: center; - flex-direction: column; - border-radius: 15%; + border-radius: 100%; cursor: default; + svg { + width: 10; + height: 10; + margin: auto; + } } .documentDecorations-rotationPath { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 8b8b7fa4d..f40d2ae8b 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -845,9 +845,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P   */}
- ) : ( -
- ); + ) : null; const titleArea = this._editingTitle ? ( {this._isRotating ? null : ( tap to set rotate center, drag to rotate
}> -
e.preventDefault()}> +
e.preventDefault()}> } color={SettingsManager.userColor} />
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 5eff6b8e0..6c1308c0a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1631,7 +1631,7 @@ export class CollectionFreeFormView extends CollectionSubView this.isContentActive(), // if autoreset is on, then whenever the view is selected, it will be restored to it default pan/zoom positions - active => this.rootDoc[this.autoResetFieldKey] && active && this.resetView() + active => !SnappingManager.GetIsDragging() && this.rootDoc[this.autoResetFieldKey] && active && this.resetView() ); this._disposers.fitContent = reaction( -- cgit v1.2.3-70-g09d2 From a91d5f3bdf1daf9b10a3f02acc79db7a0174a1d8 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 24 Oct 2023 13:46:36 -0400 Subject: fixed hide before/after in trails. move ink mask to developer. fixed tangent dragging on some curves that have no initial tangent. fixed tree view highlights when dragging. --- src/client/documents/Documents.ts | 4 --- src/client/util/CurrentUserUtils.ts | 2 +- src/client/views/InkStrokeProperties.ts | 5 ++-- src/client/views/PropertiesButtons.tsx | 31 +++++++++------------- src/client/views/collections/TreeView.tsx | 4 +-- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/trails/PresBox.tsx | 2 +- 7 files changed, 21 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index d61e4b3e9..161aba6e1 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1055,10 +1055,6 @@ export namespace Docs { I.stroke_isInkMask = isInkMask; I.text_align = 'center'; I.title = 'ink'; - I.x = options.x as number; - I.y = options.y as number; - I._width = options._width as number; - I._height = options._height as number; I.author = Doc.CurrentUserEmail; I.rotation = 0; I.defaultDoubleClick = 'click'; diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 268ee2790..dc988b04d 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -678,7 +678,7 @@ export class CurrentUserUtils { { title: "Circle", toolTip: "Circle (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "circle", toolType:GestureUtils.Gestures.Circle, scripts: {onClick:`{ return setActiveTool(self.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(self.toolType, true, _readOnly_);}`} }, { title: "Square", toolTip: "Square (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "square", toolType:GestureUtils.Gestures.Rectangle, scripts: {onClick:`{ return setActiveTool(self.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(self.toolType, true, _readOnly_);}`} }, { title: "Line", toolTip: "Line (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "minus", toolType:GestureUtils.Gestures.Line, scripts: {onClick:`{ return setActiveTool(self.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(self.toolType, true, _readOnly_);}`} }, - { title: "Mask", toolTip: "Mask", btnType: ButtonType.ToggleButton, icon: "user-circle",toolType: "inkMask", scripts: {onClick:'{ return setInkProperty(self.toolType, value, _readOnly_);}'} }, + { title: "Mask", toolTip: "Mask", btnType: ButtonType.ToggleButton, icon: "user-circle",toolType: "inkMask", scripts: {onClick:'{ return setInkProperty(self.toolType, value, _readOnly_);}'}, funcs: {hidden:"IsNoviceMode()" } }, { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberSliderButton, toolType: "strokeWidth", ignoreClick: true, scripts: {script: '{ return setInkProperty(self.toolType, value, _readOnly_);}'}, numBtnMin: 1}, { title: "Ink", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", toolType: "strokeColor", ignoreClick: true, scripts: {script: '{ return setInkProperty(self.toolType, value, _readOnly_);}'} }, ]; diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index abc4381a6..13bd12361 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -12,6 +12,7 @@ import { DocumentManager } from '../util/DocumentManager'; import { undoBatch } from '../util/UndoManager'; import { InkingStroke } from './InkingStroke'; import { DocumentView } from './nodes/DocumentView'; +import _ = require('lodash'); export class InkStrokeProperties { static _Instance: InkStrokeProperties | undefined; @@ -262,7 +263,7 @@ export class InkStrokeProperties { var endDir = { x: 0, y: 0 }; for (var i = 0; i < nearestSeg / 4 + 1; i++) { const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y }))); - if (i === 0) startDir = bez.derivative(0); + if (i === 0) startDir = bez.derivative(_.isEqual(bez.derivative(0), { x: 0, y: 0, t: 0 }) ? 1e-8 : 0); if (i === nearestSeg / 4) endDir = bez.derivative(nearestT); for (var t = 0; t < (i === nearestSeg / 4 ? nearestT + 0.05 : 1); t += 0.05) { const pt = bez.compute(i !== nearestSeg / 4 ? t : Math.min(nearestT, t)); @@ -273,7 +274,7 @@ export class InkStrokeProperties { for (var i = nearestSeg / 4; i < splicedPoints.length / 4; i++) { const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y }))); if (i === nearestSeg / 4) startDir = bez.derivative(nearestT); - if (i === splicedPoints.length / 4 - 1) endDir = bez.derivative(1); + if (i === splicedPoints.length / 4 - 1) endDir = bez.derivative(_.isEqual(bez.derivative(1), { x: 0, y: 0, t: 1 }) ? 1 - 1e-8 : 1); for (var t = i === nearestSeg / 4 ? nearestT : 0; t < (i === nearestSeg / 4 ? 1 + 0.05 + 1e-7 : 1 + 1e-7); t += 0.05) { const pt = bez.compute(Math.min(1, t)); samplesRight.push(new Point(pt.x, pt.y)); diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index 61aa616ec..ff79a8390 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -1,38 +1,33 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Icon, Tooltip } from '@material-ui/core'; +import { Dropdown, DropdownType, IListItemProps, Toggle, ToggleType, Type } from 'browndash-components'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; +import { AiOutlineColumnWidth } from 'react-icons/ai'; +import { BiHide, BiShow } from 'react-icons/bi'; +import { BsGrid3X3GapFill } from 'react-icons/bs'; +import { CiGrid31 } from 'react-icons/ci'; +import { FaBraille, FaLock, FaLockOpen } from 'react-icons/fa'; +import { MdClosedCaption, MdClosedCaptionDisabled, MdGridOff, MdGridOn, MdSubtitles, MdSubtitlesOff, MdTouchApp } from 'react-icons/md'; +import { RxWidth } from 'react-icons/rx'; +import { TbEditCircle, TbEditCircleOff, TbHandOff, TbHandStop, TbHighlight, TbHighlightOff } from 'react-icons/tb'; +import { TfiBarChart } from 'react-icons/tfi'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; -import { Id } from '../../fields/FieldSymbols'; import { InkField } from '../../fields/InkField'; import { RichTextField } from '../../fields/RichTextField'; -import { BoolCast, ScriptCast, StrCast } from '../../fields/Types'; +import { BoolCast, ScriptCast } from '../../fields/Types'; import { ImageField } from '../../fields/URLField'; -import { Utils } from '../../Utils'; import { DocUtils } from '../documents/Documents'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; import { IsFollowLinkScript } from '../util/LinkFollower'; import { LinkManager } from '../util/LinkManager'; import { SelectionManager } from '../util/SelectionManager'; +import { SettingsManager } from '../util/SettingsManager'; import { undoable, undoBatch } from '../util/UndoManager'; import { Colors } from './global/globalEnums'; import { InkingStroke } from './InkingStroke'; import { DocumentView, OpenWhere } from './nodes/DocumentView'; -import { pasteImageBitmap } from './nodes/WebBoxRenderer'; import './PropertiesButtons.scss'; import React = require('react'); -import { JsxElement } from 'typescript'; -import { FaBraille, FaHighlighter, FaLock, FaLockOpen, FaThumbtack } from 'react-icons/fa'; -import { AiOutlineApple, AiOutlineColumnWidth, AiOutlinePicture } from 'react-icons/ai'; -import { MdClosedCaption, MdClosedCaptionDisabled, MdGridOff, MdGridOn, MdSubtitles, MdSubtitlesOff, MdTouchApp } from 'react-icons/md'; -import { TbEditCircle, TbEditCircleOff, TbHandOff, TbHandStop, TbHighlight, TbHighlightOff } from 'react-icons/tb'; -import { BiHide, BiShow } from 'react-icons/bi'; -import { BsGrid3X3GapFill } from 'react-icons/bs'; -import { TfiBarChart } from 'react-icons/tfi'; -import { CiGrid31 } from 'react-icons/ci'; -import { RxWidth } from 'react-icons/rx'; -import { Dropdown, DropdownType, IListItemProps, Toggle, ToggleType, Type } from 'browndash-components'; -import { SettingsManager } from '../util/SettingsManager'; enum UtilityButtonState { Default, @@ -526,7 +521,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { {toggle(this.fitContentButton, { display: !isFreeForm && !isMap ? 'none' : '' })} {/* {toggle(this.isLightboxButton, { display: !isFreeForm && !isMap ? 'none' : '' })} */} {toggle(this.layout_autoHeightButton, { display: !isText && !isStacking && !isTree ? 'none' : '' })} - {toggle(this.maskButton, { display: !isInk ? 'none' : '' })} + {toggle(this.maskButton, { display: isNovice || !isInk ? 'none' : '' })} {toggle(this.hideImageButton, { display: !isImage ? 'none' : '' })} {toggle(this.chromeButton, { display: isNovice ? 'none' : '' })} {toggle(this.gridButton, { display: !isCollection ? 'none' : '' })} diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index b57402531..193c70add 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -751,7 +751,7 @@ export class TreeView extends React.Component { : { pointerEvents: this.props.isContentActive() ? 'all' : undefined, opacity: checked === 'unchecked' || typeof iconType !== 'string' ? undefined : 0.4, - color: StrCast(this.doc.color, checked === 'unchecked' ? 'white' : 'inherit'), + color: checked === 'unchecked' ? SettingsManager.userColor : 'inherit', } }> {this.props.treeView.outlineMode ? ( @@ -881,7 +881,7 @@ export class TreeView extends React.Component { // just render a title for a tree view label (identified by treeViewDoc being set in 'props') maxWidth: props?.PanelWidth() || undefined, background: props?.styleProvider?.(doc, props, StyleProp.BackgroundColor), - outline: `solid ${highlightColor} ${highlightIndex}px`, + outline: SnappingManager.GetIsDragging() ? undefined: `solid ${highlightColor} ${highlightIndex}px`, paddingLeft: NumCast(treeView.rootDoc.childXPadding, NumCast(treeView.props.childXPadding, Doc.IsComicStyle(doc)?20:0)), paddingRight: NumCast(treeView.rootDoc.childXPadding, NumCast(treeView.props.childXPadding, Doc.IsComicStyle(doc)?20:0)), paddingTop: treeView.props.childYPadding, diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 6c1308c0a..7fa22804d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -714,7 +714,7 @@ export class CollectionFreeFormView extends CollectionSubView() { this.childDocs.forEach((doc, index) => { const curDoc = Cast(doc, Doc, null); const tagDoc = PresBox.targetRenderedDoc(curDoc); - const itemIndexes: number[] = this.getAllIndexes(this.tagDocs, tagDoc); + const itemIndexes: number[] = this.getAllIndexes(this.tagDocs, curDoc); let opacity: Opt = index === this.itemIndex ? 1 : undefined; if (curDoc.presentation_hide) { if (index !== this.itemIndex) { -- cgit v1.2.3-70-g09d2 From 6d60f2835502cb58d680aeeda8ce022f73ddcfa6 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 24 Oct 2023 13:57:59 -0400 Subject: adjustment to group pointer events for ink. --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 7fa22804d..5f7dda562 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1300,7 +1300,7 @@ export class CollectionFreeFormView extends CollectionSubView this.childPointerEvents; childContentsActive = () => (this.props.childContentsActive ?? this.isContentActive() === false ? returnFalse : emptyFunction)(); - groupChildPointerEvents = () => (this.props.isDocumentActive?.() ? 'all' : 'none'); + groupChildPointerEvents = () => (this.props.isDocumentActive?.() && !this.isContentActive() ? 'all' : 'none'); getChildDocView(entry: PoolData) { const childLayout = entry.pair.layout; const childData = entry.pair.data; -- cgit v1.2.3-70-g09d2 From afe6157e26b42cf4e5075d70b83997b2c466343b Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 25 Oct 2023 11:40:58 -0400 Subject: fixed problem setting ink arrows where they don't show up until the line is modified. --- src/client/util/InteractionUtils.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx index 6406624bb..7caeee588 100644 --- a/src/client/util/InteractionUtils.tsx +++ b/src/client/util/InteractionUtils.tsx @@ -170,7 +170,7 @@ export namespace InteractionUtils { )} Date: Wed, 25 Oct 2023 18:45:30 -0400 Subject: fix to group pointer events for nested groups. --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 5f7dda562..9639ec73c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1307,7 +1307,7 @@ export class CollectionFreeFormView extends CollectionSubView Date: Thu, 26 Oct 2023 11:31:15 -0400 Subject: enabled different title colors per doc, not just per user. added support for screen space doc titles, and for proper title clipping when borderRadius is set. added dropdown for setting title field to display and tweaked editableView to enable ellipsis for overfow --- src/client/documents/Documents.ts | 2 + src/client/util/CurrentUserUtils.ts | 2 +- src/client/util/SettingsManager.tsx | 15 ++- src/client/views/DocumentDecorations.tsx | 1 + src/client/views/EditableView.scss | 2 +- src/client/views/EditableView.tsx | 17 ++- src/client/views/FilterPanel.tsx | 110 ++++++++--------- src/client/views/StyleProvider.tsx | 5 +- src/client/views/global/globalScripts.ts | 15 ++- src/client/views/nodes/DocumentView.scss | 6 +- src/client/views/nodes/DocumentView.tsx | 131 ++++++++++++++++----- .../nodes/formattedText/FormattedTextBox.scss | 1 - 12 files changed, 201 insertions(+), 106 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 161aba6e1..5c913513a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -444,6 +444,8 @@ export class DocumentOptions { userBackgroundColor?: STRt = new StrInfo('background color associated with a Dash user (seen in header fields of shared documents)'); userColor?: STRt = new StrInfo('color associated with a Dash user (seen in header fields of shared documents)'); } + +export const DocOptions = new DocumentOptions(); export namespace Docs { export let newAccount: boolean = false; diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index dc988b04d..591bc7430 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -707,7 +707,7 @@ export class CurrentUserUtils { CollectionViewType.Grid, CollectionViewType.NoteTaking]), 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, expertMode: false, width: 30, scripts: { onClick: 'pinWithView(altKey)'}, funcs: {hidden: "IsNoneSelected()"}}, - { title: "Header", icon: "heading", toolTip: "Header Color", btnType: ButtonType.ColorButton, expertMode: true, ignoreClick: true, scripts: { script: 'return setHeaderColor(value, _readOnly_)'}, funcs: {hidden: "IsNoneSelected()"}}, + { title: "Header", icon: "heading", toolTip: "Doc Titlebar Color", btnType: ButtonType.ColorButton, expertMode: true, ignoreClick: true, scripts: { script: 'return setHeaderColor(value, _readOnly_)'} }, { title: "Fill", icon: "fill-drip", toolTip: "Background Fill Color",btnType: ButtonType.ColorButton, expertMode: false, ignoreClick: true, width: 30, scripts: { script: 'return setBackgroundColor(value, _readOnly_)'}, funcs: {hidden: "IsNoneSelected()"}}, // Only when a document is selected { title: "Overlay", icon: "layer-group", toolTip: "Overlay", btnType: ButtonType.ToggleButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)'}, scripts: { onClick: '{ return toggleOverlay(_readOnly_); }'}}, // Only when floating document is selected in freeform { title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode)'}, width: 30, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}}, diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index dc852596f..f75322905 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -7,7 +7,7 @@ import { BsGoogle } from 'react-icons/bs'; import { FaFillDrip, FaPalette } from 'react-icons/fa'; import { Doc } from '../../fields/Doc'; import { DashVersion } from '../../fields/DocSymbols'; -import { BoolCast, Cast, StrCast } from '../../fields/Types'; +import { BoolCast, Cast, NumCast, StrCast } from '../../fields/Types'; import { addStyleSheet, addStyleSheetRule, Utils } from '../../Utils'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; import { DocServer } from '../DocServer'; @@ -269,6 +269,19 @@ export class SettingsManager extends React.Component<{}> { size={Size.XSMALL} color={SettingsManager.userColor} /> + + console.log('GOT: ' + (Doc.UserDoc().headerHeight = val))} + /> +
); } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index f40d2ae8b..9dafb12fb 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -944,6 +944,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P key="rad" className="documentDecorations-borderRadius" style={{ + opacity: 0.5, background: `${this._isRounding ? Colors.MEDIUM_BLUE : SettingsManager.userColor}`, transform: `translate(${radiusHandleLocation ?? 0}px, ${(radiusHandleLocation ?? 0) + (this._showNothing ? 0 : this._titleHeight)}px)`, }} diff --git a/src/client/views/EditableView.scss b/src/client/views/EditableView.scss index f7c03caf9..27b260450 100644 --- a/src/client/views/EditableView.scss +++ b/src/client/views/EditableView.scss @@ -3,7 +3,7 @@ overflow-wrap: break-word; word-wrap: break-word; hyphens: auto; - overflow: auto; + overflow: hidden; height: 100%; min-width: 20; text-overflow: ellipsis; diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index ca4ffaf3a..ed7c544fc 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -283,11 +283,24 @@ export class EditableView extends React.Component {
- {this.props.contents ? this.props.contents?.valueOf() : this.props.placeholder?.valueOf()} + + {this.props.contents ? this.props.contents?.valueOf() : this.props.placeholder?.valueOf()} +
); } diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index 0df88f970..cb5c9b085 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -7,7 +7,7 @@ import { CiCircleRemove } from 'react-icons/ci'; import Select from 'react-select'; import { Doc, DocListCast, Field, LinkedTo, StrListCast } from '../../fields/Doc'; import { RichTextField } from '../../fields/RichTextField'; -import { DocumentOptions, FInfo } from '../documents/Documents'; +import { DocOptions, DocumentOptions, FInfo } from '../documents/Documents'; import { DocumentManager } from '../util/DocumentManager'; import { UserOptions } from '../util/GroupManager'; import { SearchUtil } from '../util/SearchUtil'; @@ -18,6 +18,7 @@ import { Handle, Tick, TooltipRail, Track } from './nodes/SliderBox-components'; import { SettingsManager } from '../util/SettingsManager'; import { Id } from '../../fields/FieldSymbols'; import { List } from '../../fields/List'; +import { emptyFunction } from '../../Utils'; interface filterProps { rootDoc: Doc; @@ -25,8 +26,6 @@ interface filterProps { @observer export class FilterPanel extends React.Component { - private _documentOptions: DocumentOptions = new DocumentOptions(); - public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FilterPanel, fieldKey); } @@ -51,12 +50,10 @@ export class FilterPanel extends React.Component { if (targetDoc) { SearchUtil.foreachRecursiveDoc([this.targetDoc], (depth, doc) => allDocs.add(doc)); } - console.log('this is all Docs' + Array.from(allDocs)); return Array.from(allDocs); } @computed get _allFacets() { - // trace(); const noviceReqFields = ['author', 'tags', 'text', 'type', LinkedTo]; const noviceLayoutFields: string[] = []; //["_layout_curPage"]; const noviceFields = [...noviceReqFields, ...noviceLayoutFields]; @@ -68,11 +65,8 @@ export class FilterPanel extends React.Component { .filter(key => key.indexOf('modificationDate') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('_')) || noviceFields.includes(key) || !Doc.noviceMode) .sort(); - // console.log('THIS IS HERE ' + Doc.UserDoc().color + 'space ' + Doc.UserDoc().color); noviceFields.forEach(key => sortedKeys.splice(sortedKeys.indexOf(key), 1)); - console.log('this is novice fields ' + noviceFields + 'and this is sorted Keys ' + sortedKeys); - return [...noviceFields, ...sortedKeys]; } @@ -206,13 +200,7 @@ export class FilterPanel extends React.Component { */ @action - facetClick = (facetHeader: string) => { - // just when someone chooses a facet - - this._selectedFacetHeaders.add(facetHeader); - - return; - }; + facetClick = (facetHeader: string) => this._selectedFacetHeaders.add(facetHeader); @action sortingCurrentFacetValues = (facetHeader: string) => { @@ -260,57 +248,59 @@ export class FilterPanel extends React.Component { return nonNumbers / facetValues.length > 0.1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2)); }; - render() { - let filteredOptions: string[] = ['author', 'tags', 'text', 'acl-Guest', ...this._allFacets.filter(facet => facet[0] === facet.charAt(0).toUpperCase())]; + @computed get fieldsDropdown() { + const filteredOptions = ['author', 'tags', 'text', 'acl-Guest', ...this._allFacets.filter(facet => facet[0] === facet.charAt(0).toUpperCase())]; - Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => { - if (pair[1].filterable) { - filteredOptions.push(pair[0]); - } - }); + Object.entries(DocOptions) + .filter(opts => opts[1].filterable) + .forEach((pair: [string, FInfo]) => filteredOptions.push(pair[0])); + const options = filteredOptions.map(facet => ({ value: facet, label: facet })); - let options = filteredOptions.map(facet => ({ value: facet, label: facet })); + return ( + ({ - ...baseStyles, - color: SettingsManager.userColor, - background: SettingsManager.userBackgroundColor, - }), - placeholder: (baseStyles, state) => ({ - ...baseStyles, - color: SettingsManager.userColor, - background: SettingsManager.userBackgroundColor, - }), - input: (baseStyles, state) => ({ - ...baseStyles, - color: SettingsManager.userColor, - background: SettingsManager.userBackgroundColor, - }), - option: (baseStyles, state) => ({ - ...baseStyles, - color: SettingsManager.userColor, - background: !state.isFocused ? SettingsManager.userBackgroundColor : SettingsManager.userVariantColor, - }), - menuList: (baseStyles, state) => ({ - ...baseStyles, - backgroundColor: SettingsManager.userBackgroundColor, - }), - }} - placeholder="Add a filter..." - options={options} - isMulti={false} - onChange={val => this.facetClick((val as UserOptions).value)} - onKeyDown={e => e.stopPropagation()} - value={null} - closeMenuOnSelect={true} - /> -
+
{this.fieldsDropdown}
{/* THE FOLLOWING CODE SHOULD BE DEVELOPER FOR BOOLEAN EXPRESSION (AND / OR) */} {/*
{ - e.stopPropagation(); - text = e.target.value; - }} onKeyDown={(e) => { - if (e.keyCode === 13) { - event(text); - this.closeMenu(); - e.stopPropagation(); - } - }} />
); - } else if (type === "button") { - this._currentMenu.push(

{ - e.preventDefault(); - e.stopPropagation(); - event(e); - this.closeMenu(); - }}>{title}

); + let text = ''; + this._currentMenu.push( +
+ + { + e.stopPropagation(); + text = e.target.value; + }} + onKeyDown={e => { + if (e.keyCode === 13) { + event(Number(text)); + this.closeMenu(); + e.stopPropagation(); + } + }} + /> +
+ ); + } else if (type === 'button') { + this._currentMenu.push( +
+ +

{ + e.preventDefault(); + e.stopPropagation(); + event(e); + this.closeMenu(); + }}> + {title} +

+
+ ); } - } + }; @action addMenu = (title: string) => { - this._currentMenu.unshift(

{title}

); - } + this._currentMenu.unshift( +
+

{title}

+
+ ); + }; render() { return ( -
+
{this._currentMenu}
); } - -} \ No newline at end of file +} diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx index 81a5587e4..82ac69a3b 100644 --- a/src/client/views/animationtimeline/TimelineOverview.tsx +++ b/src/client/views/animationtimeline/TimelineOverview.tsx @@ -1,11 +1,9 @@ -import * as React from "react"; -import { observable, action, computed, runInAction, reaction, IReactionDisposer } from "mobx"; -import { observer } from "mobx-react"; -import "./TimelineOverview.scss"; -import * as $ from 'jquery'; -import { Timeline } from "./Timeline"; -import { Keyframe, KeyframeFunc } from "./Keyframe"; - +import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; +import { observer } from 'mobx-react'; +import * as React from 'react'; +import { RegionHelpers } from './Region'; +import { Timeline } from './Timeline'; +import './TimelineOverview.scss'; interface TimelineOverviewProps { totalLength: number; @@ -21,9 +19,8 @@ interface TimelineOverviewProps { tickIncrement: number; } - @observer -export class TimelineOverview extends React.Component{ +export class TimelineOverview extends React.Component { @observable private _visibleRef = React.createRef(); @observable private _scrubberRef = React.createRef(); @observable private authoringContainer = React.createRef(); @@ -49,13 +46,13 @@ export class TimelineOverview extends React.Component{ this.setOverviewWidth(); }); } - }, + } ); - } + }; componentWillUnmount = () => { this._authoringReaction && this._authoringReaction(); - } + }; @action setOverviewWidth() { @@ -66,8 +63,7 @@ export class TimelineOverview extends React.Component{ if (this.props.isAuthoring) { this.activeOverviewWidth = this.overviewBarWidth; - } - else { + } else { this.activeOverviewWidth = this.playbarWidth; } } @@ -76,37 +72,37 @@ export class TimelineOverview extends React.Component{ onPointerDown = (e: React.PointerEvent) => { e.stopPropagation(); e.preventDefault(); - document.removeEventListener("pointermove", this.onPanX); - document.removeEventListener("pointerup", this.onPointerUp); - document.addEventListener("pointermove", this.onPanX); - document.addEventListener("pointerup", this.onPointerUp); - } + document.removeEventListener('pointermove', this.onPanX); + document.removeEventListener('pointerup', this.onPointerUp); + document.addEventListener('pointermove', this.onPanX); + document.addEventListener('pointerup', this.onPointerUp); + }; @action onPanX = (e: PointerEvent) => { e.stopPropagation(); e.preventDefault(); - const movX = (this.props.visibleStart / this.props.totalLength) * (this.DEFAULT_WIDTH) + e.movementX; - this.props.movePanX((movX / (this.DEFAULT_WIDTH)) * this.props.totalLength); - } + const movX = (this.props.visibleStart / this.props.totalLength) * this.DEFAULT_WIDTH + e.movementX; + this.props.movePanX((movX / this.DEFAULT_WIDTH) * this.props.totalLength); + }; @action onPointerUp = (e: PointerEvent) => { e.stopPropagation(); e.preventDefault(); - document.removeEventListener("pointermove", this.onPanX); - document.removeEventListener("pointerup", this.onPointerUp); - } + document.removeEventListener('pointermove', this.onPanX); + document.removeEventListener('pointerup', this.onPointerUp); + }; @action onScrubberDown = (e: React.PointerEvent) => { e.preventDefault(); e.stopPropagation(); - document.removeEventListener("pointermove", this.onScrubberMove); - document.removeEventListener("pointerup", this.onScrubberUp); - document.addEventListener("pointermove", this.onScrubberMove); - document.addEventListener("pointerup", this.onScrubberUp); - } + document.removeEventListener('pointermove', this.onScrubberMove); + document.removeEventListener('pointerup', this.onScrubberUp); + document.addEventListener('pointermove', this.onScrubberMove); + document.addEventListener('pointerup', this.onScrubberUp); + }; @action onScrubberMove = (e: PointerEvent) => { @@ -115,22 +111,22 @@ export class TimelineOverview extends React.Component{ const scrubberRef = this._scrubberRef.current!; const left = scrubberRef.getBoundingClientRect().left; const offsetX = Math.round(e.clientX - left); - this.props.changeCurrentBarX((((offsetX) / this.activeOverviewWidth) * this.props.totalLength) + this.props.currentBarX); - } + this.props.changeCurrentBarX((offsetX / this.activeOverviewWidth) * this.props.totalLength + this.props.currentBarX); + }; @action onScrubberUp = (e: PointerEvent) => { e.preventDefault(); e.stopPropagation(); - document.removeEventListener("pointermove", this.onScrubberMove); - document.removeEventListener("pointerup", this.onScrubberUp); - } + document.removeEventListener('pointermove', this.onScrubberMove); + document.removeEventListener('pointerup', this.onScrubberUp); + }; @action getTimes() { - const vis = KeyframeFunc.convertPixelTime(this.props.visibleLength, "mili", "time", this.props.tickSpacing, this.props.tickIncrement); - const x = KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement); - const start = KeyframeFunc.convertPixelTime(this.props.visibleStart, "mili", "time", this.props.tickSpacing, this.props.tickIncrement); + const vis = RegionHelpers.convertPixelTime(this.props.visibleLength, 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement); + const x = RegionHelpers.convertPixelTime(this.props.currentBarX, 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement); + const start = RegionHelpers.convertPixelTime(this.props.visibleStart, 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement); this.visibleTime = vis; this.currentX = x; this.visibleStart = start; @@ -144,7 +140,7 @@ export class TimelineOverview extends React.Component{ const visibleBarWidth = percentVisible * this.activeOverviewWidth; const percentScrubberStart = this.currentX / this.props.time; - let scrubberStart = this.props.currentBarX / this.props.totalLength * this.activeOverviewWidth; + let scrubberStart = (this.props.currentBarX / this.props.totalLength) * this.activeOverviewWidth; if (scrubberStart > this.activeOverviewWidth) scrubberStart = this.activeOverviewWidth; const percentBarStart = this.visibleStart / this.props.time; @@ -153,29 +149,25 @@ export class TimelineOverview extends React.Component{ let playWidth = (this.props.currentBarX / this.props.totalLength) * this.activeOverviewWidth; if (playWidth > this.activeOverviewWidth) playWidth = this.activeOverviewWidth; - const timeline = this.props.isAuthoring ? [ - -
-
, -
-
-
-
- ] : [ -
-
-
, -
- ]; + const timeline = this.props.isAuthoring + ? [ +
+
, +
+
+
+
, + ] + : [ +
+
+
, +
, + ]; return (
-
- {timeline} -
+
{timeline}
); } - } - - diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 1010332f5..2fd062a88 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -1,17 +1,19 @@ import { action, computed, intercept, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { Doc, DocListCast, Opt, DocListCastAsync } from '../../../fields/Doc'; +import { Doc, DocListCast, DocListCastAsync, Opt } from '../../../fields/Doc'; import { Copy } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; import { listSpec } from '../../../fields/Schema'; import { Cast, NumCast } from '../../../fields/Types'; import { Transform } from '../../util/Transform'; -import { Keyframe, KeyframeFunc, RegionData } from './Keyframe'; +import { Region, RegionData, RegionHelpers } from './Region'; +import { Timeline } from './Timeline'; import './Track.scss'; interface IProps { + timeline: Timeline; animatedDoc: Doc; currentBarX: number; transform: Transform; @@ -32,14 +34,14 @@ export class Track extends React.Component { @observable private _newKeyframe: boolean = false; private readonly MAX_TITLE_HEIGHT = 75; @observable private _trackHeight = 0; - private primitiveWhitelist = ['x', 'y', '_width', '_height', 'opacity', '_layout_scrollTop']; + private primitiveWhitelist = ['x', 'y', '_width', '_height', '_rotation', 'opacity', '_layout_scrollTop']; private objectWhitelist = ['data']; @computed private get regions() { return DocListCast(this.props.animatedDoc.regions); } @computed private get time() { - return NumCast(KeyframeFunc.convertPixelTime(this.props.currentBarX, 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement)); + return NumCast(RegionHelpers.convertPixelTime(this.props.currentBarX, 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement)); } async componentDidMount() { @@ -85,36 +87,36 @@ export class Track extends React.Component { */ @action saveKeyframe = async () => { - const keyframes = Cast(this.saveStateRegion?.keyframes, listSpec(Doc)) as List; - const kfIndex = keyframes.indexOf(this.saveStateKf!); + if (this.props.timeline.IsPlaying || !this.saveStateRegion || !this.saveStateKf) { + this.saveStateKf = undefined; + this.saveStateRegion = undefined; + return; + } + const keyframes = Cast(this.saveStateRegion.keyframes, listSpec(Doc)) as List; + const kfIndex = keyframes.indexOf(this.saveStateKf); const kf = keyframes[kfIndex] as Doc; //index in the keyframe if (this._newKeyframe) { - DocListCast(this.saveStateRegion?.keyframes).forEach((kf, index) => { + DocListCast(this.saveStateRegion.keyframes).forEach((kf, index) => { this.copyDocDataToKeyFrame(kf); kf.opacity = index === 0 || index === 3 ? 0.1 : 1; }); this._newKeyframe = false; } if (!kf) return; - if (kf.type === KeyframeFunc.KeyframeType.default) { - // only save for non-fades - this.copyDocDataToKeyFrame(kf); - const leftkf = KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, kf); // lef keyframe, if it exists - const rightkf = KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, kf); //right keyframe, if it exists - if (leftkf?.type === KeyframeFunc.KeyframeType.fade) { + // only save for non-fades + if (this.copyDocDataToKeyFrame(kf)) { + const leftkf = RegionHelpers.calcMinLeft(this.saveStateRegion, this.time, kf); // lef keyframe, if it exists + const rightkf = RegionHelpers.calcMinRight(this.saveStateRegion, this.time, kf); //right keyframe, if it exists + if (leftkf?.type === RegionHelpers.KeyframeType.end) { //replicating this keyframe to fades - const edge = KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, leftkf); + const edge = RegionHelpers.calcMinLeft(this.saveStateRegion, this.time, leftkf); edge && this.copyDocDataToKeyFrame(edge); leftkf && this.copyDocDataToKeyFrame(leftkf); - edge && (edge.opacity = 0.1); - leftkf && (leftkf.opacity = 1); } - if (rightkf?.type === KeyframeFunc.KeyframeType.fade) { - const edge = KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, rightkf); + if (rightkf?.type === RegionHelpers.KeyframeType.end) { + const edge = RegionHelpers.calcMinRight(this.saveStateRegion, this.time, rightkf); edge && this.copyDocDataToKeyFrame(edge); rightkf && this.copyDocDataToKeyFrame(rightkf); - edge && (edge.opacity = 0.1); - rightkf && (rightkf.opacity = 1); } } keyframes[kfIndex] = kf; @@ -143,7 +145,7 @@ export class Track extends React.Component { const r = region as RegionData; //for some region is returning undefined... which is not the case if (DocListCast(r.keyframes).find(kf => kf.time === this.time) === undefined) { //basically when there is no additional keyframe at that timespot - this.makeKeyData(r, this.time, KeyframeFunc.KeyframeType.default); + this.makeKeyData(r, this.time, RegionHelpers.KeyframeType.default); } } }, @@ -222,20 +224,20 @@ export class Track extends React.Component { */ @action timeChange = async () => { - if (this.saveStateKf !== undefined) { - await this.saveKeyframe(); - } else if (this._newKeyframe) { + if (this.saveStateKf !== undefined || this._newKeyframe) { await this.saveKeyframe(); } const regiondata = await this.findRegion(Math.round(this.time)); //finds a region that the scrubber is on if (regiondata) { - const leftkf: Doc | undefined = await KeyframeFunc.calcMinLeft(regiondata, this.time); // lef keyframe, if it exists - const rightkf: Doc | undefined = await KeyframeFunc.calcMinRight(regiondata, this.time); //right keyframe, if it exists + const leftkf: Doc | undefined = await RegionHelpers.calcMinLeft(regiondata, this.time); // lef keyframe, if it exists + const rightkf: Doc | undefined = await RegionHelpers.calcMinRight(regiondata, this.time); //right keyframe, if it exists const currentkf: Doc | undefined = await this.calcCurrent(regiondata); //if the scrubber is on top of the keyframe if (currentkf) { await this.applyKeys(currentkf); - this.saveStateKf = currentkf; - this.saveStateRegion = regiondata; + runInAction(() => { + this.saveStateKf = currentkf; + this.saveStateRegion = regiondata; + }); } else if (leftkf && rightkf) { await this.interpolate(leftkf, rightkf); } @@ -277,7 +279,7 @@ export class Track extends React.Component { @action interpolate = async (left: Doc, right: Doc) => { this.primitiveWhitelist.forEach(key => { - if (left[key] && right[key] && typeof left[key] === 'number' && typeof right[key] === 'number') { + if (typeof left[key] === 'number' && typeof right[key] === 'number') { //if it is number, interpolate const dif = NumCast(right[key]) - NumCast(left[key]); const deltaLeft = this.time - NumCast(left.time); @@ -306,7 +308,7 @@ export class Track extends React.Component { onInnerDoubleClick = (e: React.MouseEvent) => { const inner = this._inner.current!; const offsetX = Math.round((e.clientX - inner.getBoundingClientRect().left) * this.props.transform.Scale); - this.createRegion(KeyframeFunc.convertPixelTime(offsetX, 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement)); + this.createRegion(RegionHelpers.convertPixelTime(offsetX, 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement)); }; /** @@ -316,10 +318,10 @@ export class Track extends React.Component { createRegion = (time: number) => { if (this.findRegion(time) === undefined) { //check if there is a region where double clicking (prevents phantom regions) - const regiondata = KeyframeFunc.defaultKeyframe(); //create keyframe data + const regiondata = RegionHelpers.defaultKeyframe(); //create keyframe data regiondata.position = time; //set position - const rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, regiondata, this.regions); + const rightRegion = RegionHelpers.findAdjacentRegion(RegionHelpers.Direction.right, regiondata, this.regions); if (rightRegion && rightRegion.position - regiondata.position <= 4000) { //edge case when there is less than default 4000 duration space between this and right region @@ -335,7 +337,7 @@ export class Track extends React.Component { }; @action - makeKeyData = (regiondata: RegionData, time: number, type: KeyframeFunc.KeyframeType = KeyframeFunc.KeyframeType.default) => { + makeKeyData = (regiondata: RegionData, time: number, type: RegionHelpers.KeyframeType = RegionHelpers.KeyframeType.default) => { //Kfpos is mouse offsetX, representing time const trackKeyFrames = DocListCast(regiondata.keyframes); const existingkf = trackKeyFrames.find(TK => TK.time === time); @@ -359,16 +361,21 @@ export class Track extends React.Component { @action copyDocDataToKeyFrame = (doc: Doc) => { + var somethingChanged = false; this.primitiveWhitelist.map(key => { const originalVal = this.props.animatedDoc[key]; - doc[key] = originalVal instanceof ObjectField ? originalVal[Copy]() : originalVal; + somethingChanged = somethingChanged || originalVal !== doc[key]; + if (doc.type === RegionHelpers.KeyframeType.end && key === 'opacity') doc.opacity = 0; + else doc[key] = originalVal instanceof ObjectField ? originalVal[Copy]() : originalVal; }); + return somethingChanged; }; /** * UI sstuff here. Not really much to change */ render() { + const saveStateKf = this.saveStateKf; return (
@@ -380,7 +387,7 @@ export class Track extends React.Component { onPointerOver={() => Doc.BrushDoc(this.props.animatedDoc)} onPointerOut={() => Doc.UnBrushDoc(this.props.animatedDoc)}> {this.regions?.map((region, i) => { - return ; + return ; })}
diff --git a/src/fields/List.ts b/src/fields/List.ts index f3fcc87f7..da007e972 100644 --- a/src/fields/List.ts +++ b/src/fields/List.ts @@ -236,7 +236,10 @@ class ListImpl extends ObjectField { const list = new Proxy(this, { set: setter, get: ListImpl.listGetter, - ownKeys: target => Object.keys(target.__fieldTuples), + ownKeys: target => { + const keys = Object.keys(target.__fieldTuples); + return [...keys, '__realFields']; + }, getOwnPropertyDescriptor: (target, prop) => { if (prop in target[FieldTuples]) { return { -- cgit v1.2.3-70-g09d2 From b2233984df663be6554c935fe9e40f4778237e67 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 27 Oct 2023 10:23:21 -0400 Subject: removed async nonsense from tracks and regions. added some comments. --- src/client/views/animationtimeline/Region.tsx | 4 +-- src/client/views/animationtimeline/Timeline.tsx | 9 ++++- src/client/views/animationtimeline/Track.tsx | 47 +++++++++++++------------ 3 files changed, 34 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/client/views/animationtimeline/Region.tsx b/src/client/views/animationtimeline/Region.tsx index df00924c6..53c5c4718 100644 --- a/src/client/views/animationtimeline/Region.tsx +++ b/src/client/views/animationtimeline/Region.tsx @@ -313,7 +313,7 @@ export class Region extends React.Component { }; @action - createKeyframe = async (clientX: number) => { + createKeyframe = (clientX: number) => { this._mouseToggled = true; const bar = this._bar.current!; const offset = RegionHelpers.convertPixelTime(Math.round((clientX - bar.getBoundingClientRect().left) * this.props.transform.Scale), 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement); @@ -327,7 +327,7 @@ export class Region extends React.Component { }; @action - moveKeyframe = async (e: React.MouseEvent, kf: Doc) => { + moveKeyframe = (e: React.MouseEvent, kf: Doc) => { e.preventDefault(); e.stopPropagation(); this.props.changeCurrentBarX(RegionHelpers.convertPixelTime(NumCast(kf.time!), 'mili', 'pixel', this.props.tickSpacing, this.props.tickIncrement)); diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 3675238fd..71517d4fe 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -22,13 +22,20 @@ import { Track } from './Track'; * * The hierarchy works this way: * - * Timeline.tsx --> Track.tsx --> Region.tsx + * Timeline.tsx --> Track.tsx --> Region.tsx | | | TimelineMenu.tsx (timeline's custom contextmenu) | | TimelineOverview.tsx (youtube like dragging thing is play mode, complex dragging thing in editing mode) + + Timeline (Track[]) + Track(Region[],animatedDoc) -> Region1(K[]) Region2 ... + F1 K1 K2...FL K1 K2 K... + K(x,y,_width,opacity) + ... + Track Most style changes are in SCSS file. If you have any questions, email me or text me. diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 2fd062a88..b90ff5eaf 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -44,18 +44,19 @@ export class Track extends React.Component { return NumCast(RegionHelpers.convertPixelTime(this.props.currentBarX, 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement)); } - async componentDidMount() { - const regions = await DocListCastAsync(this.props.animatedDoc.regions); - if (!regions) this.props.animatedDoc.regions = new List(); //if there is no region, then create new doc to store stuff - //these two lines are exactly same from timeline.tsx - const relativeHeight = window.innerHeight / 20; - runInAction(() => (this._trackHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT)); //for responsiveness - this._timelineVisibleReaction = this.timelineVisibleReaction(); - this._currentBarXReaction = this.currentBarXReaction(); - if (DocListCast(this.props.animatedDoc.regions).length === 0) this.createRegion(this.time); - this.props.animatedDoc.hidden = false; - this.props.animatedDoc.opacity = 1; - // this.autoCreateKeyframe(); + componentDidMount() { + DocListCastAsync(this.props.animatedDoc.regions).then(regions => { + if (!regions) this.props.animatedDoc.regions = new List(); //if there is no region, then create new doc to store stuff + //these two lines are exactly same from timeline.tsx + const relativeHeight = window.innerHeight / 20; + runInAction(() => (this._trackHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT)); //for responsiveness + this._timelineVisibleReaction = this.timelineVisibleReaction(); + this._currentBarXReaction = this.currentBarXReaction(); + if (DocListCast(this.props.animatedDoc.regions).length === 0) this.createRegion(this.time); + this.props.animatedDoc.hidden = false; + this.props.animatedDoc.opacity = 1; + // this.autoCreateKeyframe(); + }); } /** @@ -86,7 +87,7 @@ export class Track extends React.Component { * */ @action - saveKeyframe = async () => { + saveKeyframe = () => { if (this.props.timeline.IsPlaying || !this.saveStateRegion || !this.saveStateKf) { this.saveStateKf = undefined; this.saveStateRegion = undefined; @@ -223,23 +224,23 @@ export class Track extends React.Component { * when scrubber position changes. Need to edit the logic */ @action - timeChange = async () => { + timeChange = () => { if (this.saveStateKf !== undefined || this._newKeyframe) { - await this.saveKeyframe(); + this.saveKeyframe(); } - const regiondata = await this.findRegion(Math.round(this.time)); //finds a region that the scrubber is on + const regiondata = this.findRegion(Math.round(this.time)); //finds a region that the scrubber is on if (regiondata) { - const leftkf: Doc | undefined = await RegionHelpers.calcMinLeft(regiondata, this.time); // lef keyframe, if it exists - const rightkf: Doc | undefined = await RegionHelpers.calcMinRight(regiondata, this.time); //right keyframe, if it exists - const currentkf: Doc | undefined = await this.calcCurrent(regiondata); //if the scrubber is on top of the keyframe + const leftkf: Doc | undefined = RegionHelpers.calcMinLeft(regiondata, this.time); // lef keyframe, if it exists + const rightkf: Doc | undefined = RegionHelpers.calcMinRight(regiondata, this.time); //right keyframe, if it exists + const currentkf: Doc | undefined = this.calcCurrent(regiondata); //if the scrubber is on top of the keyframe if (currentkf) { - await this.applyKeys(currentkf); + this.applyKeys(currentkf); runInAction(() => { this.saveStateKf = currentkf; this.saveStateRegion = regiondata; }); } else if (leftkf && rightkf) { - await this.interpolate(leftkf, rightkf); + this.interpolate(leftkf, rightkf); } } }; @@ -249,7 +250,7 @@ export class Track extends React.Component { * need to change the logic here */ @action - private applyKeys = async (kf: Doc) => { + private applyKeys = (kf: Doc) => { this.primitiveWhitelist.forEach(key => { if (!kf[key]) { this.props.animatedDoc[key] = undefined; @@ -277,7 +278,7 @@ export class Track extends React.Component { * basic linear interpolation function */ @action - interpolate = async (left: Doc, right: Doc) => { + interpolate = (left: Doc, right: Doc) => { this.primitiveWhitelist.forEach(key => { if (typeof left[key] === 'number' && typeof right[key] === 'number') { //if it is number, interpolate -- cgit v1.2.3-70-g09d2 From a866baea5fdbf30650cbfbf4aa383019ef61ec3d Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 27 Oct 2023 13:39:04 -0400 Subject: added collection itself to timeline --- src/client/views/animationtimeline/Timeline.tsx | 14 +++----------- src/client/views/animationtimeline/Track.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 2 +- 3 files changed, 5 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 71517d4fe..4be3b05ab 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -543,7 +543,7 @@ export class Timeline extends React.Component {
- {this.children.map(doc => ( + {[...this.children, this.props.Document].map(doc => ( this.mapOfTracks.push(ref)} timeline={this} @@ -562,16 +562,8 @@ export class Timeline extends React.Component {
Current: {this.getCurrentTime()}
- {this.children.map(doc => ( -
{ - Doc.BrushDoc(doc); - }} - onPointerOut={() => { - Doc.UnBrushDoc(doc); - }}> + {[...this.children, this.props.Document].map(doc => ( +
Doc.BrushDoc(doc)} onPointerOut={() => Doc.UnBrushDoc(doc)}>

{StrCast(doc.title)}

))} diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index b90ff5eaf..dfab849a4 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -34,7 +34,7 @@ export class Track extends React.Component { @observable private _newKeyframe: boolean = false; private readonly MAX_TITLE_HEIGHT = 75; @observable private _trackHeight = 0; - private primitiveWhitelist = ['x', 'y', '_width', '_height', '_rotation', 'opacity', '_layout_scrollTop']; + private primitiveWhitelist = ['x', 'y', '_freeform_panX', '_freeform_panY', '_width', '_height', '_rotation', 'opacity', '_layout_scrollTop']; private objectWhitelist = ['data']; @computed private get regions() { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 4a43ffe3e..43be4b724 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1312,7 +1312,7 @@ export class DocumentViewInternal extends DocComponent <> - {DocumentViewInternal.AnimationEffect(renderDoc, this.rootDoc[Animation], this.rootDoc)} + {this._componentView instanceof KeyValueBox ? renderDoc : DocumentViewInternal.AnimationEffect(renderDoc, this.rootDoc[Animation], this.rootDoc)} {borderPath?.jsx}
-- cgit v1.2.3-70-g09d2 From 7e01a1fdad85e5aa3d73f166b7663d34337bb040 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 27 Oct 2023 13:51:34 -0400 Subject: from last --- src/client/views/animationtimeline/Track.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index dfab849a4..f36b5ade8 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -181,7 +181,7 @@ export class Track extends React.Component { this.timeChange(); } else { this.props.animatedDoc.hidden = true; - this.props.animatedDoc.opacity = 0; + this.props.animatedDoc !== this.props.collection && (this.props.animatedDoc.opacity = 0); //if (this._autoKfReaction) this._autoKfReaction(); } } @@ -280,6 +280,9 @@ export class Track extends React.Component { @action interpolate = (left: Doc, right: Doc) => { this.primitiveWhitelist.forEach(key => { + if (key === 'opacity' && this.props.animatedDoc === this.props.collection) { + return; + } if (typeof left[key] === 'number' && typeof right[key] === 'number') { //if it is number, interpolate const dif = NumCast(right[key]) - NumCast(left[key]); -- cgit v1.2.3-70-g09d2 From 8a6ed7624fa1eb8b0b38a51e3f77af159c7cb09f Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 30 Oct 2023 11:40:49 -0400 Subject: fix for group child dragging that still allows titles to be clipped when border radius is set. --- src/client/views/nodes/DocumentView.scss | 4 +++- src/client/views/nodes/trails/PresBox.tsx | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index 874723895..931594568 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -24,7 +24,8 @@ width: 100%; height: 100%; border-radius: inherit; - overflow: hidden; // need this so that title will be clipped when borderRadius is set + // bcz: can't clip the title this way because groups need to be able to render outside of overflow region to support drag/drop-extending the group borders + // overflow: hidden; // need this so that title will be clipped when borderRadius is set // transition: outline 0.3s linear; // background: $white; //overflow: hidden; @@ -166,6 +167,7 @@ height: 100%; border-radius: inherit; white-space: normal; + overflow: hidden; // so that titles will clip when borderRadius is set .documentView-styleContentWrapper { width: 100%; diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 944302934..ceeb10d93 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -792,7 +792,7 @@ export class PresBox extends ViewBoxBaseComponent() { willPan: activeItem.presentation_movement !== PresMovement.None, willZoomCentered: activeItem.presentation_movement === PresMovement.Zoom || activeItem.presentation_movement === PresMovement.Jump || activeItem.presentation_movement === PresMovement.Center, zoomScale: activeItem.presentation_movement === PresMovement.Center ? 0 : NumCast(activeItem.config_zoom, 1), - zoomTime: activeItem.presentation_movement === PresMovement.Jump ? 0 : Math.min(Math.max(effect ? 750 : 500, (effect ? 0.2 : 1) * presTime), presTime), + zoomTime: activeItem.presentation_movement === PresMovement.Jump ? 0 : Math.min(Math.max(effect ? 750 : 500, (effect ? 1 : 1) * presTime), presTime), effect: activeItem, noSelect: true, openLocation: OpenWhere.addLeft, @@ -864,7 +864,7 @@ export class PresBox extends ViewBoxBaseComponent() { opacity = 0; } } - opacity !== undefined && (tagDoc.opacity = opacity); + opacity !== undefined && (tagDoc.opacity = opacity === 1 ? undefined : opacity); }); }; -- cgit v1.2.3-70-g09d2 From a091c6142db5c1da94807abf14e78ed69e62f794 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 31 Oct 2023 12:21:30 -0400 Subject: fixed groups to use actual document boundaries for ink or link lines, instead of boundingbox. fixed doc title css. started to make link lines more compatible with ink. --- src/client/util/InteractionUtils.tsx | 6 +++--- src/client/views/DocumentDecorations.tsx | 11 ++++------- src/client/views/EditableView.tsx | 2 ++ src/client/views/InkingStroke.tsx | 1 - src/client/views/PropertiesButtons.tsx | 2 +- src/client/views/PropertiesView.tsx | 7 +------ src/client/views/StyleProvider.scss | 2 +- src/client/views/StyleProvider.tsx | 19 ++++++++++--------- .../collectionFreeForm/CollectionFreeFormView.tsx | 5 +---- .../views/nodes/CollectionFreeFormDocumentView.tsx | 17 +++++++++++++---- src/client/views/nodes/DocumentView.tsx | 17 +++++++++-------- src/client/views/nodes/LinkBox.tsx | 12 ++++++++++-- src/client/views/nodes/trails/PresBox.tsx | 2 +- src/client/views/pdf/PDFViewer.tsx | 2 +- 14 files changed, 57 insertions(+), 48 deletions(-) (limited to 'src') diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx index 7caeee588..be885312d 100644 --- a/src/client/util/InteractionUtils.tsx +++ b/src/client/util/InteractionUtils.tsx @@ -94,7 +94,7 @@ export namespace InteractionUtils { width: number, strokeWidth: number, lineJoin: string, - lineCap: string, + strokeLineCap: string, bezier: string, fill: string, arrowStart: string, @@ -181,8 +181,8 @@ export namespace InteractionUtils { // opacity: strokeWidth !== width ? 0.5 : undefined, pointerEvents: (pevents as any) === 'all' ? 'visiblepainted' : (pevents as any), stroke: color ?? 'rgb(0, 0, 0)', - strokeWidth: strokeWidth, - strokeLinecap: lineCap as any, + strokeWidth, + strokeLinecap: strokeLineCap as any, strokeDasharray: dashArray, transition: 'inherit', }} diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 9dafb12fb..65f7c7a70 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -475,6 +475,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P @action onPointerDown = (e: React.PointerEvent): void => { + this._isResizing = true; setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction); DocumentView.Interacting = true; // turns off pointer events on things like youtube videos and web pages so that dragging doesn't get "stuck" when cursor moves over them this._resizeHdlId = e.currentTarget.className; @@ -685,6 +686,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P @action onPointerUp = (e: PointerEvent): void => { + this._isResizing = false; this._resizeHdlId = ''; DocumentView.Interacting = false; this._resizeUndo?.end(); @@ -772,7 +774,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P var shareSymbolIcon = ReverseHierarchyMap.get(shareMode)?.image; // hide the decorations if the parent chooses to hide it or if the document itself hides it - const hideDecorations = seldocview.props.hideDecorations || seldocview.rootDoc.hideDecorations; + const hideDecorations = this._isResizing || seldocview.props.hideDecorations || seldocview.rootDoc.layout_hideDecorations; const hideResizers = ![AclAdmin, AclEdit, AclAugment].includes(GetEffectiveAcl(seldocview.rootDoc)) || hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.layout_hideResizeHandles || this._isRounding || this._isRotating; const hideTitle = this._showNothing || hideDecorations || seldocview.props.hideDecorationTitle || seldocview.rootDoc.layout_hideDecorationTitle || this._isRounding || this._isRotating; @@ -861,12 +863,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P onPointerDown={e => e.stopPropagation()} /> ) : ( -
{ - e.stopPropagation; - }}> +
e.stopPropagation}> {hideTitle ? null : ( {this.selectionTitle} diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index ed7c544fc..abb7ed7ee 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -290,6 +290,8 @@ export class EditableView extends React.Component { whiteSpace: this.props.oneLine ? 'nowrap' : 'pre-line', height: this.props.height, maxHeight: this.props.maxHeight, + fontStyle: this.props.fontStyle, + fontSize: this.props.fontSize, }} //onPointerDown={this.stopPropagation} onClick={this.onClick} diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index d619f5123..c3a6b1607 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -372,7 +372,6 @@ export class InkingStroke extends ViewBoxBaseComponent() { } render() { TraceMobx(); - if (!this.rootDoc._layout_isSvg) setTimeout(action(() => (this.rootDoc._layout_isSvg = true))); const { inkData, inkStrokeWidth, inkLeft, inkTop, inkScaleX, inkScaleY, inkWidth, inkHeight } = this.inkScaledData(); const startMarker = StrCast(this.layoutDoc.stroke_startMarker); diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index ff79a8390..18f53b8e7 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -492,7 +492,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { render() { const layoutField = this.selectedDoc?.[Doc.LayoutFieldKey(this.selectedDoc)]; const isText = layoutField instanceof RichTextField; - const isInk = layoutField instanceof InkField; + const isInk = this.selectedDoc?.layout_isSvg; const isImage = layoutField instanceof ImageField; const isMap = this.selectedDoc?.type === DocumentType.MAP; const isCollection = this.selectedDoc?.type === DocumentType.COL; diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 04a948a27..d37971517 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -73,9 +73,6 @@ export class PropertiesView extends React.Component { @computed get isPres(): boolean { return this.selectedDoc?.type === DocumentType.PRES; } - @computed get isLink(): boolean { - return this.selectedDoc?.type === DocumentType.LINK; - } @computed get dataDoc() { return this.selectedDoc?.[DocData]; } @@ -1174,12 +1171,10 @@ export class PropertiesView extends React.Component { } @computed get inkSubMenu() { - let isDouble = false; - return ( <> (this.openAppearance = bool)} onDoubleClick={() => this.CloseAll()}> - {this.isInk ? this.appearanceEditor : null} + {this.selectedDoc?.layout_isSvg ? this.appearanceEditor : null} (this.openTransform = bool)} onDoubleClick={() => this.CloseAll()}> {this.transformEditor} diff --git a/src/client/views/StyleProvider.scss b/src/client/views/StyleProvider.scss index f069e7e1b..4d3096f71 100644 --- a/src/client/views/StyleProvider.scss +++ b/src/client/views/StyleProvider.scss @@ -12,7 +12,7 @@ opacity: 0.3; display: flex; flex-direction: column; - color: gold; + color: red; border-radius: 3px; justify-content: center; cursor: default; diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 5ff743a30..c6d3efd0c 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -83,8 +83,9 @@ export function DefaultStyleProvider(doc: Opt, props: Opt doc && doc._layout_isSvg && !props?.LayoutTemplateString; + const isInk = () => doc?._layout_isSvg && !props?.LayoutTemplateString; const isOpen = property.includes(':open'); const isEmpty = property.includes(':empty'); const boxBackground = property.includes(':box'); @@ -121,14 +122,14 @@ export function DefaultStyleProvider(doc: Opt, props: Opt, props: Opt, props: Opt, props: Opt, props: Opt, props: Opt toggleLockedPosition(doc)}> - +
); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 9639ec73c..0d43d6ab8 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1300,16 +1300,15 @@ export class CollectionFreeFormView extends CollectionSubView this.childPointerEvents; childContentsActive = () => (this.props.childContentsActive ?? this.isContentActive() === false ? returnFalse : emptyFunction)(); - groupChildPointerEvents = () => (this.props.isDocumentActive?.() && !this.isContentActive() ? 'all' : 'none'); getChildDocView(entry: PoolData) { const childLayout = entry.pair.layout; const childData = entry.pair.data; return ( this.transcribeStrokes(false), icon: 'font' }); - // this.props.Document._isGroup && this.childDocs.filter(s => s.type === DocumentType.INK).length > 0 && appearanceItems.push({ description: "Ink to math", event: () => this.transcribeStrokes(true), icon: "square-root-alt" }); - !Doc.noviceMode ? appearanceItems.push({ description: 'Arrange contents in grid', event: this.layoutDocsInGrid, icon: 'table' }) : null; !appearance && ContextMenu.Instance.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' }); diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 199835dd9..9f7ebc5d9 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -25,7 +25,6 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { dataTransition?: string; replica: string; CollectionFreeFormView: CollectionFreeFormView; - GroupPointerEvents?: () => 'none' | 'all' | undefined; // pointer events for this freeform doc view wrapper that are not passed to the docView. This allows items in a group to trigger the group to be selected, without selecting the items themselves } @observer @@ -192,17 +191,27 @@ export class CollectionFreeFormDocumentView extends DocComponent this.props.ScreenToLocalTransform().translate(-this.X, -this.Y); returnThis = () => this; + /// this indicates whether the doc view is activated because of its relationshop to a group + // 'group' - this is a group that is activated because it's on an active canvas, but is not part of some other group + // 'child' - this is a group child that is activated because its containing group is activated + // 'inactive' - this is a group child but it is not active + // undefined - this is not activated by a group + isGroupActive = () => { + if (this.props.CollectionFreeFormView.isAnyChildContentActive()) return undefined; + const isGroup = this.rootDoc._isGroup && (!this.rootDoc.backgroundColor || this.rootDoc.backgroundColor === 'transparent'); + return isGroup ? (this.props.isDocumentActive?.() ? 'group' : this.props.isGroupActive?.() ? 'child' : 'inactive') : this.props.isGroupActive?.() ? 'child' : undefined; + }; render() { TraceMobx(); const divProps: DocumentViewProps = { - ...OmitKeys(this.props, ['GroupPointerEvents']).omit, + ...this.props, CollectionFreeFormDocumentView: this.returnThis, styleProvider: this.styleProvider, ScreenToLocalTransform: this.screenToLocalTransform, PanelWidth: this.panelWidth, PanelHeight: this.panelHeight, + isGroupActive: this.isGroupActive, }; - const isInk = this.layoutDoc._layout_isSvg && !this.props.LayoutTemplateString && !this.layoutDoc._stroke_isInkMask; return (
{this.props.renderCutoffProvider(this.props.Document) ? (
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 43be4b724..a2a084200 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,5 +1,6 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; -import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx'; +import { Dropdown, DropdownType, Type } from 'browndash-components'; +import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { computedFn } from 'mobx-utils'; import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal'; @@ -38,7 +39,6 @@ import { ContextMenuProps } from '../ContextMenuItem'; import { DocComponent } from '../DocComponent'; import { EditableView } from '../EditableView'; import { GestureOverlay } from '../GestureOverlay'; -import { InkingStroke } from '../InkingStroke'; import { LightboxView } from '../LightboxView'; import { StyleProp } from '../StyleProvider'; import { UndoStack } from '../UndoStack'; @@ -48,14 +48,11 @@ import { DocumentLinksButton } from './DocumentLinksButton'; import './DocumentView.scss'; import { FieldViewProps } from './FieldView'; import { FormattedTextBox } from './formattedText/FormattedTextBox'; +import { KeyValueBox } from './KeyValueBox'; import { LinkAnchorBox } from './LinkAnchorBox'; import { PresEffect, PresEffectDirection } from './trails'; import { PinProps, PresBox } from './trails/PresBox'; import React = require('react'); -import { KeyValueBox } from './KeyValueBox'; -import { LinkBox } from './LinkBox'; -import { FilterPanel } from '../FilterPanel'; -import { Dropdown, DropdownType, Type } from 'browndash-components'; const { Howl } = require('howler'); interface Window { @@ -159,6 +156,7 @@ export interface DocumentViewSharedProps { Document: Doc; DataDoc?: Doc; fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _freeform_fitContentsToBox property on a Document + isGroupActive?: () => string | undefined; // is this document part of a group that is active suppressSetHeight?: boolean; setContentView?: (view: DocComponentView) => any; CollectionFreeFormDocumentView?: () => CollectionFreeFormDocumentView; @@ -428,6 +426,7 @@ export class DocumentViewInternal extends DocComponent boolean = returnFalse; onClick = action((e: React.MouseEvent | React.PointerEvent) => { + if (this.props.isGroupActive?.() === 'child' && !this.props.isDocumentActive?.()) return; if (!this.Document.ignoreClick && this.props.renderDepth >= 0 && Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, this._downTime)) { let stopPropagate = true; let preventDefault = true; @@ -516,6 +515,7 @@ export class DocumentViewInternal extends DocComponent { + if (this.props.isGroupActive?.() === 'child' && !this.props.isDocumentActive?.()) return; this._longPressSelector = setTimeout(() => { if (DocumentView.LongPress) { if (this.rootDoc.undoIgnoreFields) { @@ -906,11 +906,12 @@ export class DocumentViewInternal extends DocComponent !isParentOf(this.ContentDiv, document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y)) && Doc.UnBrushDoc(this.rootDoc)} style={{ borderRadius: this.borderRounding, - pointerEvents: this.pointerEvents === 'visiblePainted' ? 'none' : this.pointerEvents, + pointerEvents: this.pointerEvents === 'visiblePainted' ? 'none' : this.pointerEvents, // visible painted means that the underlying doc contents are irregular and will process their own pointer events (otherwise, the contents are expected to fill the entire doc view box so we can handle pointer events here) }}> <> {this._componentView instanceof KeyValueBox ? renderDoc : DocumentViewInternal.AnimationEffect(renderDoc, this.rootDoc[Animation], this.rootDoc)} diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index d871c88ba..682267ef1 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -4,7 +4,7 @@ import { computed, action } from 'mobx'; import { observer } from 'mobx-react'; import { Height, Width } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; -import { DocCast, StrCast } from '../../../fields/Types'; +import { DocCast, NumCast, StrCast } from '../../../fields/Types'; import { aggregateBounds, emptyFunction, returnAlways, returnFalse, Utils } from '../../../Utils'; import { DocumentManager } from '../../util/DocumentManager'; import { ViewBoxBaseComponent } from '../DocComponent'; @@ -92,6 +92,9 @@ export class LinkBox extends ViewBoxBaseComponent() { const bez = new Bezier(pts.map(p => ({ x: p[0], y: p[1] }))); const text = bez.get(0.5); const linkDesc = StrCast(this.rootDoc.link_description) || 'description'; + const strokeWidth = NumCast(this.rootDoc.stroke_width, 4); + const dash = StrCast(this.rootDoc.stroke_dash); + const strokeDasharray = dash && Number(dash) ? String(strokeWidth * Number(dash)) : undefined; return (
@@ -106,7 +109,12 @@ export class LinkBox extends ViewBoxBaseComponent() { diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index ceeb10d93..70d9443a2 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -792,7 +792,7 @@ export class PresBox extends ViewBoxBaseComponent() { willPan: activeItem.presentation_movement !== PresMovement.None, willZoomCentered: activeItem.presentation_movement === PresMovement.Zoom || activeItem.presentation_movement === PresMovement.Jump || activeItem.presentation_movement === PresMovement.Center, zoomScale: activeItem.presentation_movement === PresMovement.Center ? 0 : NumCast(activeItem.config_zoom, 1), - zoomTime: activeItem.presentation_movement === PresMovement.Jump ? 0 : Math.min(Math.max(effect ? 750 : 500, (effect ? 1 : 1) * presTime), presTime), + zoomTime: activeItem.presentation_movement === PresMovement.Jump ? 0 : Math.min(Math.max(effect ? 750 : 500, (effect ? 0.2 : 1) * presTime), presTime), effect: activeItem, noSelect: true, openLocation: OpenWhere.addLeft, diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 58a54764d..939928c1c 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -519,7 +519,7 @@ export class PDFViewer extends React.Component { childStyleProvider = (doc: Doc | undefined, props: Opt, property: string): any => { if (doc instanceof Doc && property === StyleProp.PointerEvents) { if (this.inlineTextAnnotations.includes(doc) || this.props.isContentActive() === false) return 'none'; - const isInk = doc && StrCast(Doc.Layout(doc).layout).includes(InkingStroke.name) && !props?.LayoutTemplateString; + const isInk = doc.layout_isSvg && !props?.LayoutTemplateString; return isInk ? 'visiblePainted' : 'all'; } return this.props.styleProvider?.(doc, props, property); -- cgit v1.2.3-70-g09d2 From cf95923feebb274249283c7bb82de5849060a9a8 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 31 Oct 2023 14:21:51 -0400 Subject: fixed keyframe animation of ink and links. fixed getDocumentview with preferred collection --- src/client/documents/Documents.ts | 26 ++++++++++++-------------- src/client/util/DocumentManager.ts | 18 +++++------------- src/client/views/InkStrokeProperties.ts | 2 +- src/client/views/InkingStroke.tsx | 2 +- src/client/views/nodes/LinkBox.tsx | 9 +++++---- 5 files changed, 24 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 5c913513a..4086ede20 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -670,7 +670,15 @@ export namespace Docs { { // NOTE: this is unused!! ink fields are filled in directly within the InkDocument() method layout: { view: InkingStroke, dataField: 'stroke' }, - options: { systemIcon: 'BsFillPencilFill', nativeDimModifiable: true, nativeHeightUnfrozen: true, layout_isSvg: true, layout_forceReflow: true }, + options: { + systemIcon: 'BsFillPencilFill', // + nativeDimModifiable: true, + nativeHeightUnfrozen: true, + layout_hideDecorationTitle: true, // don't show title when selected + fitWidth: false, + layout_isSvg: true, + layout_forceReflow: true, + }, }, ], [ @@ -1035,16 +1043,8 @@ export namespace Docs { } export function InkDocument(color: string, strokeWidth: number, stroke_bezier: string, fillColor: string, arrowStart: string, arrowEnd: string, dash: string, points: PointData[], isInkMask: boolean, options: DocumentOptions = {}) { - const I = new Doc(); - I[Initializing] = true; - I.type = DocumentType.INK; - I.layout = InkingStroke.LayoutString('stroke'); - I.nativeDimModifiable = true; - I.nativeHeightUnfrozen = true; - I.layout_isSvg = true; - I.layout_forceReflow = true; - I.layout_fitWidth = false; - I.layout_hideDecorationTitle = true; // don't show title when selected + const ink = InstanceFromProto(Prototypes.get(DocumentType.INK), '', { title: 'ink', ...options }); + const I = Doc.GetProto(ink); // I.layout_hideOpenButton = true; // don't show open full screen button when selected I.color = color; I.fillColor = fillColor; @@ -1056,8 +1056,6 @@ export namespace Docs { I.stroke_dash = dash; I.stroke_isInkMask = isInkMask; I.text_align = 'center'; - I.title = 'ink'; - I.author = Doc.CurrentUserEmail; I.rotation = 0; I.defaultDoubleClick = 'click'; I.author_date = new DateField(); @@ -1065,7 +1063,7 @@ export namespace Docs { //I['acl-Override'] = SharingPermissions.Unset; I[Initializing] = false; - return InstanceFromProto(I, '', options); + return ink; } export function PdfDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) { diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index b9f6059f4..f7eca2379 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -146,22 +146,14 @@ export class DocumentManager { return this.getDocumentViewsById(doc[Id]); } - public getDocumentView(toFind: Doc | undefined, preferredCollection?: DocumentView): DocumentView | undefined { - const doc = - // bcz: this was temporary code used to match documents by data url instead of by id. intended only for repairing the DB - // Array.from(DocumentManager.Instance.DocumentViews).find( - // dv => - // ((dv.rootDoc.data as any)?.url?.href && (dv.rootDoc.data as any)?.url?.href === (toFind.data as any)?.url?.href) || - // ((DocCast(dv.rootDoc.annotationOn)?.data as any)?.url?.href && (DocCast(dv.rootDoc.annotationOn)?.data as any)?.url?.href === (DocCast(toFind.annotationOn)?.data as any)?.url?.href) - // )?.rootDoc ?? - toFind; + public getDocumentView(target: Doc | undefined, preferredCollection?: DocumentView): DocumentView | undefined { const docViewArray = DocumentManager.Instance.DocumentViews; - const passes = !doc ? [] : preferredCollection ? [preferredCollection, undefined] : [undefined]; + const passes = !target ? [] : preferredCollection ? [preferredCollection, undefined] : [undefined]; return passes.reduce( - (pass, toReturn) => + (toReturn, pass) => toReturn ?? - docViewArray.filter(view => view.rootDoc === doc).find(view => !pass || view.props.docViewPath().lastElement() === preferredCollection) ?? - docViewArray.filter(view => Doc.AreProtosEqual(view.rootDoc, doc)).find(view => !pass || view.props.docViewPath().lastElement() === preferredCollection), + docViewArray.filter(view => view.rootDoc === target).find(view => !pass || view.props.docViewPath().lastElement() === preferredCollection) ?? + docViewArray.filter(view => Doc.AreProtosEqual(view.rootDoc, target)).find(view => !pass || view.props.docViewPath().lastElement() === preferredCollection), undefined as Opt ); } diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index 13bd12361..736ca8d90 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -65,7 +65,7 @@ export class InkStrokeProperties { doc._height = (newYrange.max - newYrange.min) * ptsYscale + NumCast(doc.stroke_width); doc.x = oldXrange.coord + (newXrange.min - oldXrange.min) * ptsXscale; doc.y = oldYrange.coord + (newYrange.min - oldYrange.min) * ptsYscale; - Doc.GetProto(doc).stroke = new InkField(newPoints); + Doc.SetInPlace(doc, 'stroke', new InkField(newPoints), true); appliedFunc = true; } } diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index c3a6b1607..d26c7761e 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -246,7 +246,7 @@ export class InkingStroke extends ViewBoxBaseComponent() { * factor for converting between ink and screen space. */ inkScaledData = () => { - const inkData = Cast(this.dataDoc[this.fieldKey], InkField)?.inkData ?? []; + const inkData = Cast(this.rootDoc[this.fieldKey], InkField)?.inkData ?? []; const inkStrokeWidth = NumCast(this.rootDoc.stroke_width, 1); const inkTop = Math.min(...inkData.map(p => p.Y)) - inkStrokeWidth / 2; const inkBottom = Math.max(...inkData.map(p => p.Y)) + inkStrokeWidth / 2; diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index 682267ef1..38ff21209 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -28,12 +28,12 @@ export class LinkBox extends ViewBoxBaseComponent() { @computed get anchor1() { const anchor1 = DocCast(this.rootDoc.link_anchor_1); const anchor_1 = anchor1?.layout_unrendered ? DocCast(anchor1.annotationOn) : anchor1; - return DocumentManager.Instance.getDocumentView(anchor_1); + return DocumentManager.Instance.getDocumentView(anchor_1, this.props.docViewPath()[this.props.docViewPath().length - 2]); // this.props.docViewPath().lastElement()); } @computed get anchor2() { const anchor2 = DocCast(this.rootDoc.link_anchor_2); const anchor_2 = anchor2?.layout_unrendered ? DocCast(anchor2.annotationOn) : anchor2; - return DocumentManager.Instance.getDocumentView(anchor_2); + return DocumentManager.Instance.getDocumentView(anchor_2, this.props.docViewPath()[this.props.docViewPath().length - 2]); // this.props.docViewPath().lastElement()); } screenBounds = () => { if (this.layoutDoc._layout_isSvg && this.anchor1 && this.anchor2 && this.anchor1.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView) { @@ -96,8 +96,8 @@ export class LinkBox extends ViewBoxBaseComponent() { const dash = StrCast(this.rootDoc.stroke_dash); const strokeDasharray = dash && Number(dash) ? String(strokeWidth * Number(dash)) : undefined; return ( -
- +
+ @@ -114,6 +114,7 @@ export class LinkBox extends ViewBoxBaseComponent() { stroke: highlightColor ?? 'lightblue', strokeDasharray, strokeWidth, + transition: 'inherit', }} d={`M ${pts[1][0] - lx} ${pts[1][1] - ty} C ${pts[1][0] + pts[1][0] - pts[0][0] - lx} ${pts[1][1] + pts[1][1] - pts[0][1] - ty}, ${pts[2][0] + pts[2][0] - pts[3][0] - lx} ${pts[2][1] + pts[2][1] - pts[3][1] - ty}, ${pts[2][0] - lx} ${pts[2][1] - ty}`} -- cgit v1.2.3-70-g09d2 From bc14d740a23d922df96f92f99eacb51f70139643 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 31 Oct 2023 17:35:45 -0400 Subject: fixed deselecting video/audio with escape. fixed focus or open on iconified docs to just show them (not toggle), and fixed to showDocument on target unless its container isn't displayed. --- src/client/util/DocumentManager.ts | 7 ++- .../collections/CollectionStackedTimeline.tsx | 1 - .../collectionFreeForm/CollectionFreeFormView.tsx | 59 +++++++++------------- src/client/views/nodes/trails/PresBox.tsx | 6 +++ 4 files changed, 36 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index f7eca2379..7cc8afaa6 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -298,7 +298,10 @@ export class DocumentManager { let contextView: DocumentView | undefined; // view containing context that contains target let focused = false; while (true) { - docView.rootDoc.layout_fieldKey === 'layout_icon' ? await new Promise(res => docView.iconify(res)) : undefined; + if (docView.rootDoc.layout_fieldKey === 'layout_icon') { + await new Promise(res => docView.iconify(res)); + options.didMove = true; + } const nextFocus = docView.props.focus(docView.rootDoc, options); // focus the view within its container focused = focused || (nextFocus === undefined ? false : true); // keep track of whether focusing on a view needed to actually change anything const { childDocView, viewSpec } = await iterator(docView); @@ -343,7 +346,7 @@ export function DocFocusOrOpen(doc: Doc, options: DocFocusOptions = { willZoomCe DocumentManager.Instance.showDocumentView(dv, options).then(() => dv && Doc.linkFollowHighlight(dv.rootDoc)); } else { const container = DocCast(containingDoc ?? doc.embedContainer ?? Doc.BestEmbedding(doc)); - const showDoc = !Doc.IsSystem(container) ? container : doc; + const showDoc = !Doc.IsSystem(container) && !cv ? container : doc; options.toggleTarget = undefined; DocumentManager.Instance.showDocument(showDoc, options, () => DocumentManager.Instance.showDocument(doc, { ...options, openLocation: undefined })).then(() => { const cv = DocumentManager.Instance.getDocumentView(containingDoc); diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index c4650647c..7c61bc4da 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -203,7 +203,6 @@ export class CollectionStackedTimeline extends CollectionSubView = new Map(); @@ -92,7 +93,6 @@ export class CollectionFreeFormView extends CollectionSubView(); private _layoutSizeData = observable.map(); private _cachedPool: Map = new Map(); - private _lastTap = 0; private _batch: UndoManager.Batch | undefined = undefined; private _brushtimer: any; private _brushtimer1: any; @@ -130,10 +130,6 @@ export class CollectionFreeFormView extends CollectionSubView ele.bounds && !ele.bounds.z && ele.inkMask !== -1 && ele.inkMask !== undefined).map(ele => ele.ele); const renderableEles = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && (ele.inkMask === -1 || ele.inkMask === undefined)).map(ele => ele.ele); @@ -640,6 +636,7 @@ export class CollectionFreeFormView extends CollectionSubView { this._downX = this._lastX = e.pageX; this._downY = this._lastY = e.pageY; + this._downTime = Date.now(); if (e.button === 0 && !e.altKey && !e.ctrlKey && this.props.isContentActive(true)) { if ( !this.props.Document._isGroup && // group freeforms don't pan when dragged -- instead let the event go through to allow the group itself to drag @@ -809,31 +806,24 @@ export class CollectionFreeFormView extends CollectionSubView { if (this._lightboxDoc) this._lightboxDoc = undefined; - if (this.onBrowseClickHandler()) { - if (this.props.DocumentView?.()) { - this.onBrowseClickHandler().script.run({ documentView: this.props.DocumentView(), clientX: e.clientX, clientY: e.clientY }); - } - e.stopPropagation(); - e.preventDefault(); - } else if (Math.abs(e.pageX - this._downX) < 3 && Math.abs(e.pageY - this._downY) < 3) { - if (e.shiftKey && (this.props.renderDepth === 0 || this.isContentActive())) { - if (Date.now() - this._lastTap < 300) { - // reset zoom of freeform view to 1-to-1 on a shift + double click - this.zoomSmoothlyAboutPt(this.getTransform().transformPoint(e.clientX, e.clientY), 1); - } + if (Utils.isClick(e.pageX, e.pageY, this._downX, this._downY, this._downTime)) { + if (this.onBrowseClickHandler()) { + this.onBrowseClickHandler().script.run({ documentView: this.props.DocumentView?.(), clientX: e.clientX, clientY: e.clientY }); + e.stopPropagation(); + e.preventDefault(); + } else if (this.isContentActive() && e.shiftKey) { + // reset zoom of freeform view to 1-to-1 on a shift + double click + this.zoomSmoothlyAboutPt(this.getTransform().transformPoint(e.clientX, e.clientY), 1); e.stopPropagation(); e.preventDefault(); } - this._lastTap = Date.now(); } }; @action scrollPan = (e: WheelEvent | { deltaX: number; deltaY: number }): void => { PresBox.Instance?.pauseAutoPres(); - const dx = e.deltaX; - const dy = e.deltaY; - this.setPan(NumCast(this.Document[this.panXFieldKey]) - dx, NumCast(this.Document[this.panYFieldKey]) - dy, 0, true); + this.setPan(NumCast(this.Document[this.panXFieldKey]) - e.deltaX, NumCast(this.Document[this.panYFieldKey]) - e.deltaY, 0, true); }; @action @@ -2301,20 +2291,21 @@ class CollectionFreeFormBackgroundGrid extends React.Component { - if (!focused) { - const selfFfview = !dv.rootDoc._isGroup && dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined; - let containers = dv.props.docViewPath(); - let parFfview = dv.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView; - for (var cont of containers) { - parFfview = parFfview ?? cont.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView; + dv && + DocumentManager.Instance.showDocument(dv.rootDoc, { zoomScale: 0.8, willZoomCentered: true }, (focused: boolean) => { + if (!focused) { + const selfFfview = !dv.rootDoc._isGroup && dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined; + let containers = dv.props.docViewPath(); + let parFfview = dv.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView; + for (var cont of containers) { + parFfview = parFfview ?? cont.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView; + } + while (parFfview?.rootDoc._isGroup) parFfview = parFfview.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView; + const ffview = selfFfview && selfFfview.rootDoc[selfFfview.scaleFieldKey] !== 0.5 ? selfFfview : parFfview; // if focus doc is a freeform that is not at it's default 0.5 scale, then zoom out on it. Otherwise, zoom out on the parent ffview + ffview?.zoomSmoothlyAboutPt(ffview.getTransform().transformPoint(clientX, clientY), ffview?.isAnnotationOverlay ? 1 : 0.5, browseTransitionTime); + Doc.linkFollowHighlight(dv?.props.Document, false); } - while (parFfview?.rootDoc._isGroup) parFfview = parFfview.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView; - const ffview = selfFfview && selfFfview.rootDoc[selfFfview.scaleFieldKey] !== 0.5 ? selfFfview : parFfview; // if focus doc is a freeform that is not at it's default 0.5 scale, then zoom out on it. Otherwise, zoom out on the parent ffview - ffview?.zoomSmoothlyAboutPt(ffview.getTransform().transformPoint(clientX, clientY), ffview?.isAnnotationOverlay ? 1 : 0.5, browseTransitionTime); - Doc.linkFollowHighlight(dv?.props.Document, false); - } - }); + }); } ScriptingGlobals.add(CollectionBrowseClick); ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) { diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 70d9443a2..6bbf3208c 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -518,6 +518,12 @@ export class PresBox extends ViewBoxBaseComponent() { changed = true; } } + if (pinDataTypes?.temporal || (!pinDataTypes && activeItem.timecodeToShow !== undefined)) { + if (bestTarget._layout_currentTimecode !== activeItem.timecodeToShow) { + bestTarget._layout_currentTimecode = activeItem.timecodeToShow; + changed = true; + } + } if (pinDataTypes?.inkable || (!pinDataTypes && (activeItem.config_fillColor !== undefined || activeItem.color !== undefined))) { if (bestTarget.fillColor !== activeItem.config_fillColor) { Doc.GetProto(bestTarget).fillColor = activeItem.config_fillColor; -- cgit v1.2.3-70-g09d2 From 66c5b6625ecd9690dbbff8af248626f21a21c1bd Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 1 Nov 2023 00:45:54 -0400 Subject: cleaned up presentaton path lines --- .../collectionFreeForm/CollectionFreeFormView.scss | 4 + .../collectionFreeForm/CollectionFreeFormView.tsx | 115 +++++------------ src/client/views/nodes/trails/PresBox.tsx | 136 +++++++++------------ 3 files changed, 95 insertions(+), 160 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss index c90fdf013..250760bd5 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss @@ -54,10 +54,14 @@ } } + .presPathLabels { + pointer-events: none; + } svg.presPaths { position: absolute; z-index: 100000; overflow: visible; + pointer-events: none; } svg.presPaths-hidden { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 10d0117ef..079f1b9d6 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -128,7 +128,7 @@ export class CollectionFreeFormView extends CollectionSubView(); @observable GroupChildDrag: boolean = false; // child document view being dragged. needed to update drop areas of groups when a group item is dragged. - @observable _brushedView = { width: 0, height: 0, panX: 0, panY: 0, opacity: 0 }; // highlighted region of freeform canvas used by presentations to indicate a region + @observable _brushedView: { width: number; height: number; panX: number; panY: number } | undefined; // highlighted region of freeform canvas used by presentations to indicate a region @computed get views() { const viewsMask = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && ele.inkMask !== -1 && ele.inkMask !== undefined).map(ele => ele.ele); @@ -729,9 +729,7 @@ export class CollectionFreeFormView extends CollectionSubView p.X)), Math.max(...ge.points.map(p => p.Y))); const setDocs = this.getActiveDocuments().filter(s => DocCast(s.proto)?.type === DocumentType.RTF && s.color); - const sets = setDocs.map(sd => { - return Cast(sd.text, RichTextField)?.Text as string; - }); + const sets = setDocs.map(sd => Cast(sd.text, RichTextField)?.Text as string); if (sets.length && sets[0]) { this._wordPalette.clear(); const colors = setDocs.map(sd => FieldValue(sd.color) as string); @@ -1919,12 +1917,8 @@ export class CollectionFreeFormView extends CollectionSubView (CollectionFreeFormView.ShowPresPaths ? PresBox.Instance.getPaths(this.rootDoc) : null); - brushedView = () => this._brushedView; - gridColor = () => { - const backColor = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor + ':box'); - return lightOrDark(backColor); - }; + gridColor = () => lightOrDark(this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor + ':box')); @computed get marqueeView() { TraceMobx(); return ( @@ -1952,7 +1946,7 @@ export class CollectionFreeFormView extends CollectionSubView {this.layoutDoc._freeform_backgroundGrid ? (
- ) : null} {this.children} @@ -1999,12 +1991,12 @@ export class CollectionFreeFormView extends CollectionSubView { this._brushtimer1 && clearTimeout(this._brushtimer1); this._brushtimer && clearTimeout(this._brushtimer); - this._brushedView = { width: 0, height: 0, panX: 0, panY: 0, opacity: 0 }; + this._brushedView = undefined; this._brushtimer1 = setTimeout( action(() => { - this._brushedView = { ...viewport, panX: viewport.panX - viewport.width / 2, panY: viewport.panY - viewport.height / 2, opacity: 1 }; + this._brushedView = { ...viewport, panX: viewport.panX - viewport.width / 2, panY: viewport.panY - viewport.height / 2 }; this._brushtimer = setTimeout( - action(() => (this._brushedView.opacity = 0)), + action(() => (this._brushedView = undefined)), 2500 ); }), @@ -2076,7 +2068,6 @@ export class CollectionFreeFormView extends CollectionSubView} - {/* // uncomment to show snap lines */}
{(this._hLines ?? []) @@ -2108,69 +2099,22 @@ class CollectionFreeFormOverlayView extends React.Component string; - zoomScaling: () => number; + rootDoc: Doc; viewDefDivClick?: ScriptField; children?: React.ReactNode | undefined; - //children: () => JSX.Element[]; transition?: string; - presPaths: () => JSX.Element | null; - presPinView?: boolean; isAnnotationOverlay: boolean | undefined; - isAnnotationOverlayScrollable: boolean | undefined; - brushedView: () => { panX: number; panY: number; width: number; height: number; opacity: number }; + presPaths: () => JSX.Element | null; + transform: () => string; + brushedView: () => { panX: number; panY: number; width: number; height: number } | undefined; } @observer class CollectionFreeFormViewPannableContents extends React.Component { - @observable _drag: string = ''; - - //Adds event listener so knows pointer is down and moving - onPointerDown = (e: React.PointerEvent): void => { - e.stopPropagation(); - e.preventDefault(); - this._drag = (e.target as any)?.id ?? ''; - document.getElementById(this._drag) && setupMoveUpEvents(e.target, e, this.onPointerMove, emptyFunction, emptyFunction); - }; - - //Adjusts the value in NodeStore - @action - onPointerMove = (e: PointerEvent) => { - const doc = document.getElementById('resizable'); - const toNumber = (original: number, delta: number) => original + delta * this.props.zoomScaling(); - if (doc) { - switch (this._drag) { - case 'resizer-br': - doc.style.width = toNumber(doc.offsetWidth, e.movementX) + 'px'; - doc.style.height = toNumber(doc.offsetHeight, e.movementY) + 'px'; - break; - case 'resizer-bl': - doc.style.width = toNumber(doc.offsetWidth, -e.movementX) + 'px'; - doc.style.height = toNumber(doc.offsetHeight, e.movementY) + 'px'; - doc.style.left = toNumber(doc.offsetLeft, e.movementX) + 'px'; - break; - case 'resizer-tr': - doc.style.width = toNumber(doc.offsetWidth, -e.movementX) + 'px'; - doc.style.height = toNumber(doc.offsetHeight, -e.movementY) + 'px'; - doc.style.top = toNumber(doc.offsetTop, e.movementY) + 'px'; - case 'resizer-tl': - doc.style.width = toNumber(doc.offsetWidth, -e.movementX) + 'px'; - doc.style.height = toNumber(doc.offsetHeight, -e.movementY) + 'px'; - doc.style.top = toNumber(doc.offsetTop, e.movementY) + 'px'; - doc.style.left = toNumber(doc.offsetLeft, e.movementX) + 'px'; - case 'resizable': - doc.style.top = toNumber(doc.offsetTop, e.movementY) + 'px'; - doc.style.left = toNumber(doc.offsetLeft, e.movementX) + 'px'; - } - return false; - } - return true; - }; - @computed get presPaths() { return !this.props.presPaths() ? null : ( <> -
{PresBox.Instance?.order}
+
{PresBox.Instance?.orderedPathLabels(this.props.rootDoc)}
@@ -2188,39 +2132,40 @@ class CollectionFreeFormViewPannableContents extends React.Component ); } + // rectangle highlight used when following trail/link to a region of a collection that isn't a document + @computed get brushedView() { + const brushedView = this.props.brushedView(); + return !brushedView ? null : ( +
+ ); + } render() { - const brushedView = this.props.brushedView(); return (
{ const target = e.target as any; if (getComputedStyle(target)?.overflow === 'visible') { - // if collection is visible, then scrolling will mess things up since there are no scroll bars - target.scrollTop = target.scrollLeft = 0; + target.scrollTop = target.scrollLeft = 0; // if collection is visible, scrolling messes things up since there are no scroll bars } }} style={{ transform: this.props.transform(), transition: this.props.transition, width: this.props.isAnnotationOverlay ? undefined : 0, // if not an overlay, then this will be the size of the collection, but panning and zooming will move it outside the visible border of the collection and make it selectable. This problem shows up after zooming/panning on a background collection -- you can drag the collection by clicking on apparently empty space outside the collection - //willChange: "transform" }}> {this.props.children} - { -
- } {this.presPaths} + {this.brushedView}
); } diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 6bbf3208c..6493117b0 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -3,7 +3,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; import { action, computed, IReactionDisposer, observable, ObservableSet, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; -import { Doc, DocListCast, FieldResult, Opt, StrListCast } from '../../../../fields/Doc'; +import { Doc, DocListCast, FieldResult, NumListCast, Opt, StrListCast } from '../../../../fields/Doc'; import { Animation } from '../../../../fields/DocSymbols'; import { Copy, Id } from '../../../../fields/FieldSymbols'; import { InkField } from '../../../../fields/InkField'; @@ -1307,71 +1307,55 @@ export class PresBox extends ViewBoxBaseComponent() { getAllIndexes = (arr: Doc[], val: Doc) => arr.map((doc, i) => (doc === val ? i : -1)).filter(i => i !== -1); // Adds the index in the pres path graphically - @computed get order() { + orderedPathLabels = (collection: Doc) => { const order: JSX.Element[] = []; - const docs: Doc[] = []; - const presCollection = DocumentManager.GetContextPath(this.activeItem).reverse().lastElement(); + const docs = new Set(); + const presCollection = collection; const dv = DocumentManager.Instance.getDocumentView(presCollection); - this.childDocs - .filter(doc => Cast(doc.presentation_targetDoc, Doc, null)) - .forEach((doc, index) => { - const tagDoc = Cast(doc.presentation_targetDoc, Doc, null); - const srcContext = Cast(tagDoc.embedContainer, Doc, null); + this.childDocs.forEach((doc, index) => { + const tagDoc = PresBox.targetRenderedDoc(doc); + const srcContext = Cast(tagDoc.embedContainer, Doc, null); + const labelCreator = (top: number, left: number, edge: number, fontSize: number) => ( +
this.selectElement(doc)}> +
{index + 1}
+
+ ); + if (presCollection === srcContext) { + const gap = 2; const width = NumCast(tagDoc._width) / 10; const height = Math.max(NumCast(tagDoc._height) / 10, 15); const edge = Math.max(width, height); const fontSize = edge * 0.8; - const gap = 2; - if (presCollection === srcContext) { - // Case A: Document is contained within the collection - if (docs.includes(tagDoc)) { - const prevOccurances: number = this.getAllIndexes(docs, tagDoc).length; - docs.push(tagDoc); - order.push( -
this.selectElement(doc)}> -
{index + 1}
-
- ); - } else { - docs.push(tagDoc); - order.push( -
this.selectElement(doc)}> -
{index + 1}
-
- ); - } - } else if (doc.config_pinView && presCollection === tagDoc && dv) { - // Case B: Document is presPinView and is presCollection - const scale: number = 1 / NumCast(doc.config_viewScale); - const height: number = dv.props.PanelHeight() * scale; - const width: number = dv.props.PanelWidth() * scale; - const indWidth = width / 10; - const indHeight = Math.max(height / 10, 15); - const indEdge = Math.max(indWidth, indHeight); - const indFontSize = indEdge * 0.8; - const xLoc: number = NumCast(doc.config_panX) - width / 2; - const yLoc: number = NumCast(doc.config_panY) - height / 2; - docs.push(tagDoc); - order.push( - <> -
this.selectElement(doc)}> -
{index + 1}
-
-
- - ); + // Case A: Document is contained within the collection + if (docs.has(tagDoc)) { + const prevOccurences = this.getAllIndexes(Array.from(docs), tagDoc).length; + order.push(labelCreator(NumCast(tagDoc.y) + (prevOccurences * (edge + gap) - edge / 2), NumCast(tagDoc.x) - edge / 2, edge, fontSize)); + } else { + order.push(labelCreator(NumCast(tagDoc.y) - edge / 2, NumCast(tagDoc.x) - edge / 2, edge, fontSize)); } - }); + } else if (doc.config_pinView && presCollection === tagDoc && dv) { + // Case B: Document is presPinView and is presCollection + const scale = 1 / NumCast(doc.config_viewScale); + const viewBounds = NumListCast(doc.config_viewBounds, [0, 0, dv.props.PanelWidth(), dv.props.PanelHeight()]); + const height = (viewBounds[3] - viewBounds[1]) * scale; + const width = (viewBounds[2] - viewBounds[0]) * scale; + const indWidth = width / 10; + const indHeight = Math.max(height / 10, 15); + const indEdge = Math.max(indWidth, indHeight); + const indFontSize = indEdge * 0.8; + const left = NumCast(doc.config_panX) - width / 2; + const top = NumCast(doc.config_panY) - height / 2; + order.push( + <> + {labelCreator(top - indEdge / 2, left - indEdge / 2, indEdge, indFontSize)} +
+ + ); + } + docs.add(tagDoc); + }); return order; - } + }; /** * Method called for viewing paths which adds a single line with @@ -1381,22 +1365,24 @@ export class PresBox extends ViewBoxBaseComponent() { * (Design needed for when documents in presentation trail are in another * collection) */ - @computed get paths() { + pathLines = (collection: Doc) => { let pathPoints = ''; - this.childDocs.forEach((doc, index) => { - const tagDoc = PresBox.targetRenderedDoc(doc); - if (tagDoc) { - const n1x = NumCast(tagDoc.x) + NumCast(tagDoc._width) / 2; - const n1y = NumCast(tagDoc.y) + NumCast(tagDoc._height) / 2; - if ((index = 0)) pathPoints = n1x + ',' + n1y; - else pathPoints = pathPoints + ' ' + n1x + ',' + n1y; - } else if (doc.config_pinView) { - const n1x = NumCast(doc.config_panX); - const n1y = NumCast(doc.config_panY); - if ((index = 0)) pathPoints = n1x + ',' + n1y; - else pathPoints = pathPoints + ' ' + n1x + ',' + n1y; - } - }); + this.childDocs + .filter(doc => PresBox.targetRenderedDoc(doc)?.embedContainer === collection) + .forEach((doc, index) => { + const tagDoc = PresBox.targetRenderedDoc(doc); + if (tagDoc) { + const n1x = NumCast(tagDoc.x) + NumCast(tagDoc._width) / 2; + const n1y = NumCast(tagDoc.y) + NumCast(tagDoc._height) / 2; + if ((index = 0)) pathPoints = n1x + ',' + n1y; + else pathPoints = pathPoints + ' ' + n1x + ',' + n1y; + } else if (doc.config_pinView) { + const n1x = NumCast(doc.config_panX); + const n1y = NumCast(doc.config_panY); + if ((index = 0)) pathPoints = n1x + ',' + n1y; + else pathPoints = pathPoints + ' ' + n1x + ',' + n1y; + } + }); return ( () { markerEnd="url(#markerSquareFilled)" /> ); - } - getPaths = (collection: Doc) => this.paths; // needs to be smarter and figure out the paths to draw for this specific collection. or better yet, draw everything in an overlay layer instad of within a collection + }; + getPaths = (collection: Doc) => this.pathLines(collection); // needs to be smarter and figure out the paths to draw for this specific collection. or better yet, draw everything in an overlay layer instad of within a collection // Converts seconds to ms and updates presentation_transition public static SetTransitionTime = (number: String, setter: (timeInMS: number) => void, change?: number) => { -- cgit v1.2.3-70-g09d2 From e00122142a0e5960bd5c0ff2dfda5ea807eb5e2f Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 1 Nov 2023 13:07:39 -0400 Subject: changed collection background grid to dots like figma until zoomed in, then lines. --- .../collectionFreeForm/CollectionFreeFormView.tsx | 35 ++++++++++++++-------- 1 file changed, 23 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 079f1b9d6..1e341f60d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -16,7 +16,7 @@ import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } fro import { ImageField } from '../../../../fields/URLField'; import { TraceMobx } from '../../../../fields/util'; import { GestureUtils } from '../../../../pen-gestures/GestureUtils'; -import { aggregateBounds, emptyFunction, intersectRect, lightOrDark, returnFalse, returnNone, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils'; +import { aggregateBounds, DashColor, emptyFunction, intersectRect, lightOrDark, returnFalse, returnNone, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils'; import { CognitiveServices } from '../../../cognitive_services/CognitiveServices'; import { Docs, DocUtils } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; @@ -1918,7 +1918,10 @@ export class CollectionFreeFormView extends CollectionSubView (CollectionFreeFormView.ShowPresPaths ? PresBox.Instance.getPaths(this.rootDoc) : null); brushedView = () => this._brushedView; - gridColor = () => lightOrDark(this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor + ':box')); + gridColor = () => + DashColor(lightOrDark(this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor))) + .fade(0.6) + .toString(); @computed get marqueeView() { TraceMobx(); return ( @@ -2187,9 +2190,9 @@ interface CollectionFreeFormViewBackgroundGridProps { @observer class CollectionFreeFormBackgroundGrid extends React.Component { chooseGridSpace = (gridSpace: number): number => { - if (!this.props.zoomScaling()) return 50; - const divisions = this.props.PanelWidth() / this.props.zoomScaling() / gridSpace + 3; - return divisions < 60 ? gridSpace : this.chooseGridSpace(gridSpace * 10); + if (!this.props.zoomScaling()) return gridSpace; + const divisions = this.props.PanelWidth() / this.props.zoomScaling() / gridSpace; + return divisions < 90 ? gridSpace : this.chooseGridSpace(gridSpace * 2); }; render() { const gridSpace = this.chooseGridSpace(NumCast(this.props.layoutDoc['_backgroundGrid-spacing'], 50)); @@ -2215,14 +2218,22 @@ class CollectionFreeFormBackgroundGrid extends React.Component 1) { + for (let x = Cx - renderGridSpace; x <= w - Cx; x += renderGridSpace) { + ctx.moveTo(x, Cy - h); + ctx.lineTo(x, Cy + h); + } + for (let y = Cy - renderGridSpace; y <= h - Cy; y += renderGridSpace) { + ctx.moveTo(Cx - w, y); + ctx.lineTo(Cx + w, y); + } + } else { + for (let x = Cx - renderGridSpace; x <= w - Cx; x += renderGridSpace) + for (let y = Cy - renderGridSpace; y <= h - Cy; y += renderGridSpace) { + ctx.fillRect(Math.round(x), Math.round(y), 1, 1); + } } ctx.stroke(); } -- cgit v1.2.3-70-g09d2 From 58213b0201ea0191f06f42beac9c3a17ebfc98ea Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 1 Nov 2023 15:38:22 -0400 Subject: fixed so that ink properties can be set before drawing a stroke. fixed turning off ink when menu is collapsed. added meta-click to fit all for collections, or zoom in on a document. --- src/client/util/CurrentUserUtils.ts | 8 ++--- .../views/collections/CollectionTreeView.tsx | 2 -- .../collectionFreeForm/CollectionFreeFormView.tsx | 15 ++++------ .../collectionLinear/CollectionLinearView.tsx | 1 + src/client/views/global/globalScripts.ts | 35 ++++++++++++---------- src/client/views/nodes/DocumentView.tsx | 14 +++++++-- .../views/nodes/generativeFill/GenerativeFill.tsx | 32 +++++++++++--------- src/client/views/nodes/trails/PresBox.tsx | 4 +-- 8 files changed, 61 insertions(+), 50 deletions(-) (limited to 'src') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 591bc7430..87ee1b252 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -635,9 +635,9 @@ export class CurrentUserUtils { return [ { title: "Snap", icon: "th", toolTip: "Show Snap Lines", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"snaplines", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform { title: "Grid", icon: "border-all", toolTip: "Show Grid", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"grid", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform - { title: "View All", icon: "object-group", toolTip: "Fit all Docs to View",btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"viewAll", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform + { title: "View All", icon: "object-group", toolTip: "Keep all Docs in View",btnType: ButtonType.ToggleButton, ignoreClick:true, expertMode: false, toolType:"viewAll", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform // want the same style as toggle button, but don't want it to act as an actual toggle, so set disableToggle to true, - { title: "Fit All", icon: "arrows-left-right", toolTip: "Fit all Docs to View (persistent)", btnType: ButtonType.ClickButton, ignoreClick: false, expertMode: false, toolType:"viewAllPersist", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform + { title: "Fit All", icon: "arrows-left-right", toolTip: "Fit Docs to View (once)",btnType: ButtonType.ClickButton,ignoreClick:false,expertMode: false, toolType:"fitOnce", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform { title: "Clusters", icon: "braille", toolTip: "Show Doc Clusters", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"clusters", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform { title: "Cards", icon: "brain", toolTip: "Flashcards", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"flashcards", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform { title: "Arrange", icon:"arrow-down-short-wide",toolTip:"Toggle Auto Arrange", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"arrange", funcs: {hidden: 'IsNoviceMode()'}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform @@ -673,7 +673,7 @@ export class CurrentUserUtils { static inkTools():Button[] { return [ { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", toolType: "pen", scripts: {onClick:'{ return setActiveTool(self.toolType, false, _readOnly_);}' }}, - { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", toolType: "write", scripts: {onClick:'{ return setActiveTool(self.toolType, false, _readOnly_);}'} }, + { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", toolType: "write", scripts: {onClick:'{ return setActiveTool(self.toolType, false, _readOnly_);}' }, funcs: {hidden:"IsNoviceMode()" }}, { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", toolType: "eraser", scripts: {onClick:'{ return setActiveTool(self.toolType, false, _readOnly_);}' }, funcs: {hidden:"IsNoviceMode()" }}, { title: "Circle", toolTip: "Circle (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "circle", toolType:GestureUtils.Gestures.Circle, scripts: {onClick:`{ return setActiveTool(self.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(self.toolType, true, _readOnly_);}`} }, { title: "Square", toolTip: "Square (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "square", toolType:GestureUtils.Gestures.Rectangle, scripts: {onClick:`{ return setActiveTool(self.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(self.toolType, true, _readOnly_);}`} }, @@ -708,7 +708,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, expertMode: false, width: 30, scripts: { onClick: 'pinWithView(altKey)'}, funcs: {hidden: "IsNoneSelected()"}}, { title: "Header", icon: "heading", toolTip: "Doc Titlebar Color", btnType: ButtonType.ColorButton, expertMode: true, ignoreClick: true, scripts: { script: 'return setHeaderColor(value, _readOnly_)'} }, - { title: "Fill", icon: "fill-drip", toolTip: "Background Fill Color",btnType: ButtonType.ColorButton, expertMode: false, ignoreClick: true, width: 30, scripts: { script: 'return setBackgroundColor(value, _readOnly_)'}, funcs: {hidden: "IsNoneSelected()"}}, // Only when a document is selected + { title: "Fill", icon: "fill-drip", toolTip: "Fill/Background Color",btnType: ButtonType.ColorButton, expertMode: false, ignoreClick: true, width: 30, scripts: { script: 'return setBackgroundColor(value, _readOnly_)'}, funcs: {hidden: "IsNoneSelected()"}}, // Only when a document is selected { title: "Overlay", icon: "layer-group", toolTip: "Overlay", btnType: ButtonType.ToggleButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)'}, scripts: { onClick: '{ return toggleOverlay(_readOnly_); }'}}, // Only when floating document is selected in freeform { title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode)'}, width: 30, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}}, { title: "Num", icon:"", toolTip: "Frame Number (click to toggle edit mode)", btnType: ButtonType.TextButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode)', buttonText: 'selectedDocs()?.lastElement()?.currentFrame?.toString()'}, width: 20, scripts: { onClick: '{ return curKeyFrame(_readOnly_);}'}}, diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 9e5ac77d9..e408c193a 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -98,8 +98,6 @@ export class CollectionTreeView extends CollectionSubView disposer?.()); } - shrinkWrap = () => {}; // placeholder to allow setContentView to work - componentDidMount() { //this.props.setContentView?.(this); this._disposers.autoheight = reaction( diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 1e341f60d..a8b743896 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -312,6 +312,12 @@ export class CollectionFreeFormView extends CollectionSubView { if (this._lightboxDoc) return; + if (anchor === this.rootDoc) { + if (options.willZoomCentered && options.zoomScale) { + this.fitContentOnce(); + options.didMove = true; + } + } if (anchor.type !== DocumentType.CONFIG && !DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]).includes(anchor)) return; const xfToCollection = options?.docTransform ?? Transform.Identity(); const savedState = { panX: NumCast(this.Document[this.panXFieldKey]), panY: NumCast(this.Document[this.panYFieldKey]), scale: options?.willZoomCentered ? this.Document[this.scaleFieldKey] : undefined }; @@ -1620,15 +1626,6 @@ export class CollectionFreeFormView extends CollectionSubView this.isContentActive(), // if autoreset is on, then whenever the view is selected, it will be restored to it default pan/zoom positions active => !SnappingManager.GetIsDragging() && this.rootDoc[this.autoResetFieldKey] && active && this.resetView() ); - - this._disposers.fitContent = reaction( - () => this.rootDoc.fitContentOnce, - fitContentOnce => { - if (fitContentOnce) this.fitContentOnce(); - this.rootDoc.fitContentOnce = undefined; - }, - { fireImmediately: true, name: 'fitContent' } - ); }) ); } diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index faf7501c4..9a2c79a22 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -215,6 +215,7 @@ export class CollectionLinearView extends CollectionSubView() { toggleStatus={BoolCast(this.layoutDoc.linearView_IsOpen)} onClick={() => { this.layoutDoc.linearView_IsOpen = !isExpanded; + ScriptCast(this.rootDoc.onClick)?.script.run({ this: this.rootDoc }, console.log); }} tooltip={isExpanded ? 'Close' : 'Open'} fillWidth={true} diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts index ced11447c..894afebfd 100644 --- a/src/client/views/global/globalScripts.ts +++ b/src/client/views/global/globalScripts.ts @@ -13,12 +13,13 @@ import { SelectionManager } from '../../util/SelectionManager'; import { undoable, UndoManager } from '../../util/UndoManager'; import { GestureOverlay } from '../GestureOverlay'; import { InkTranscription } from '../InkTranscription'; -import { ActiveFillColor, SetActiveFillColor, ActiveIsInkMask, SetActiveIsInkMask, ActiveInkWidth, SetActiveInkWidth, ActiveInkColor, SetActiveInkColor } from '../InkingStroke'; +import { ActiveFillColor, SetActiveFillColor, ActiveIsInkMask, SetActiveIsInkMask, ActiveInkWidth, SetActiveInkWidth, ActiveInkColor, SetActiveInkColor, InkingStroke } from '../InkingStroke'; import { CollectionFreeFormView } from '../collections/collectionFreeForm'; import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView'; import { WebBox } from '../nodes/WebBox'; import { RichTextMenu } from '../nodes/formattedText/RichTextMenu'; import { DocumentType } from '../../documents/DocumentTypes'; +import { DocumentView } from '../nodes/DocumentView'; ScriptingGlobals.add(function IsNoneSelected() { return SelectionManager.Views().length <= 0; @@ -46,6 +47,7 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b const contentFrameNumber = Cast(selView.rootDoc?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed return CollectionFreeFormDocumentView.getStringValues(selView?.rootDoc, contentFrameNumber)[fieldKey] ?? 'transparent'; } + selectedViews.some(dv => dv.ComponentView instanceof InkingStroke) && SetActiveFillColor(color ?? 'transparent'); selectedViews.forEach(dv => { const fieldKey = dv.rootDoc.type === DocumentType.INK ? 'fillColor' : 'backgroundColor'; const layoutFrameNumber = Cast(dv.props.docViewPath().lastElement()?.rootDoc?._currentFrame, 'number'); // frame number that container is at which determines layout frame values @@ -61,6 +63,7 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b if (checkResult) { return selected.lastElement()?._backgroundColor ?? 'transparent'; } + SetActiveFillColor(color ?? 'transparent'); selected.forEach(doc => (doc._backgroundColor = color)); } }); @@ -92,43 +95,43 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) { selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log('[FontIconBox.tsx] toggleOverlay failed'); }); -ScriptingGlobals.add(function showFreeform(attr: 'flashcards' | 'center' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll' | 'viewAllPersist', checkResult?: boolean) { +ScriptingGlobals.add(function showFreeform(attr: 'flashcards' | 'center' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll' | 'fitOnce', checkResult?: boolean) { const selected = SelectionManager.Docs().lastElement(); // prettier-ignore - const map: Map<'flashcards' | 'center' |'grid' | 'snaplines' | 'clusters' | 'arrange'| 'viewAll' | 'viewAllPersist', { waitForRender?: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([ + const map: Map<'flashcards' | 'center' |'grid' | 'snaplines' | 'clusters' | 'arrange'| 'viewAll' | 'fitOnce', { waitForRender?: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc, dv:DocumentView) => void;}> = new Map([ ['grid', { checkResult: (doc:Doc) => BoolCast(doc._freeform_backgroundGrid, false), - setDoc: (doc:Doc) => doc._freeform_backgroundGrid = !doc._freeform_backgroundGrid, + setDoc: (doc:Doc,dv:DocumentView) => doc._freeform_backgroundGrid = !doc._freeform_backgroundGrid, }], ['snaplines', { checkResult: (doc:Doc) => BoolCast(doc._freeform_snapLines, false), - setDoc: (doc:Doc) => doc._freeform_snapLines = !doc._freeform_snapLines, + setDoc: (doc:Doc, dv:DocumentView) => doc._freeform_snapLines = !doc._freeform_snapLines, }], ['viewAll', { checkResult: (doc:Doc) => BoolCast(doc._freeform_fitContentsToBox, false), - setDoc: (doc:Doc) => doc._freeform_fitContentsToBox = !doc._freeform_fitContentsToBox, + setDoc: (doc:Doc,dv:DocumentView) => doc._freeform_fitContentsToBox = !doc._freeform_fitContentsToBox, }], ['center', { checkResult: (doc:Doc) => BoolCast(doc._stacking_alignCenter, false), - setDoc: (doc:Doc) => doc._stacking_alignCenter = !doc._stacking_alignCenter, + setDoc: (doc:Doc,dv:DocumentView) => doc._stacking_alignCenter = !doc._stacking_alignCenter, }], - ['viewAllPersist', { + ['fitOnce', { checkResult: (doc:Doc) => false, - setDoc: (doc:Doc) => doc.fitContentOnce = true + setDoc: (doc:Doc, dv:DocumentView) => (dv.ComponentView as CollectionFreeFormView)?.fitContentOnce() }], ['clusters', { waitForRender: true, // flags that undo batch should terminate after a re-render giving the script the chance to fire checkResult: (doc:Doc) => BoolCast(doc._freeform_useClusters, false), - setDoc: (doc:Doc) => doc._freeform_useClusters = !doc._freeform_useClusters, + setDoc: (doc:Doc,dv:DocumentView) => doc._freeform_useClusters = !doc._freeform_useClusters, }], ['arrange', { waitForRender: true, // flags that undo batch should terminate after a re-render giving the script the chance to fire checkResult: (doc:Doc) => BoolCast(doc._autoArrange, false), - setDoc: (doc:Doc) => doc._autoArrange = !doc._autoArrange, + setDoc: (doc:Doc,dv:DocumentView) => doc._autoArrange = !doc._autoArrange, }], ['flashcards', { checkResult: (doc:Doc) => BoolCast(Doc.UserDoc().defaultToFlashcards, false), - setDoc: (doc:Doc) => Doc.UserDoc().defaultToFlashcards = !Doc.UserDoc().defaultToFlashcards, + setDoc: (doc:Doc,dv:DocumentView) => Doc.UserDoc().defaultToFlashcards = !Doc.UserDoc().defaultToFlashcards, }], ]); @@ -136,7 +139,7 @@ ScriptingGlobals.add(function showFreeform(attr: 'flashcards' | 'center' | 'grid return map.get(attr)?.checkResult(selected); } const batch = map.get(attr)?.waitForRender ? UndoManager.StartBatch('set freeform attribute') : { end: () => {} }; - SelectionManager.Docs().map(dv => map.get(attr)?.setDoc(dv)); + SelectionManager.Views().map(dv => map.get(attr)?.setDoc(dv.rootDoc, dv)); setTimeout(() => batch.end(), 100); }); @@ -328,7 +331,7 @@ ScriptingGlobals.add(setActiveTool, 'sets the active ink tool mode'); // toggle: Set overlay status of selected document ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'fillColor' | 'strokeWidth' | 'strokeColor', value: any, checkResult?: boolean) { - const selected = SelectionManager.Docs().lastElement(); + const selected = SelectionManager.Docs().lastElement() ?? Doc.UserDoc(); // prettier-ignore const map: Map<'inkMask' | 'fillColor' | 'strokeWidth' | 'strokeColor', { checkResult: () => any; setInk: (doc: Doc) => void; setMode: () => void }> = new Map([ ['inkMask', { @@ -344,12 +347,12 @@ ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'fillColor' | ' [ 'strokeWidth', { checkResult: () => (selected?.type === DocumentType.INK ? NumCast(selected.stroke_width) : ActiveInkWidth()), setInk: (doc: Doc) => (doc.stroke_width = NumCast(value)), - setMode: () => SetActiveInkWidth(value.toString()), + setMode: () => { SetActiveInkWidth(value.toString()); setActiveTool( GestureOverlay.Instance.InkShape ?? InkTool.Pen, true, false);}, }], ['strokeColor', { checkResult: () => (selected?.type === DocumentType.INK ? StrCast(selected.color) : ActiveInkColor()), setInk: (doc: Doc) => (doc.color = String(value)), - setMode: () => SetActiveInkColor(StrCast(value)), + setMode: () => { SetActiveInkColor(StrCast(value)); setActiveTool(GestureOverlay.Instance.InkShape ?? InkTool.Pen, true, false);}, }], ]); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a2a084200..3d6b53ccc 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -120,7 +120,6 @@ export interface DocComponentView { addDocTab?: (doc: Doc, where: OpenWhere) => boolean; // determines how to add a document - used in following links to open the target ina local lightbox addDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean; // add a document (used only by collections) reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height. However, some content views (e.g., FreeFormView w/ fitContentsToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling. - shrinkWrap?: () => void; // requests a document to display all of its contents with no white space. currently only implemented (needed?) for freeform views select?: (ctrlKey: boolean, shiftKey: boolean) => void; focus?: (textAnchor: Doc, options: DocFocusOptions) => Opt; menuControls?: () => JSX.Element; // controls to display in the top menu bar when the document is selected. @@ -498,7 +497,7 @@ export class DocumentViewInternal extends DocComponent (sendToBack ? this.props.DocumentView().props.bringToFront(this.rootDoc, true) : this._componentView?.select?.(e.ctrlKey || e.metaKey, e.shiftKey) ?? - this.props.select(e.ctrlKey || e.metaKey || e.shiftKey))); + this.props.select(e.ctrlKey||e.shiftKey, e.metaKey))); const waitFordblclick = this.props.waitForDoubleClickToClick?.() ?? this.Document.waitForDoubleClickToClick; if ((clickFunc && waitFordblclick !== 'never') || waitFordblclick === 'always') { this._doubleClickTimeout && clearTimeout(this._doubleClickTimeout); @@ -1559,7 +1558,16 @@ export class DocumentView extends React.Component { scaleToScreenSpace = () => (1 / (this.props.NativeDimScaling?.() || 1)) * this.screenToLocalTransform().Scale; docViewPathFunc = () => this.docViewPath; isSelected = (outsideReaction?: boolean) => SelectionManager.IsSelected(this, outsideReaction); - select = (extendSelection: boolean) => SelectionManager.SelectView(this, extendSelection); + select = (extendSelection: boolean, focusSelection?: boolean) => { + SelectionManager.SelectView(this, extendSelection); + if (focusSelection) { + DocumentManager.Instance.showDocument(this.rootDoc, { + willZoomCentered: true, + zoomScale: 0.9, + zoomTime: 500, + }); + } + }; NativeWidth = () => this.effectiveNativeWidth; NativeHeight = () => this.effectiveNativeHeight; PanelWidth = () => this.panelWidth; diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx index 1ec6d6e3f..3093287e9 100644 --- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx +++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx @@ -20,6 +20,8 @@ import { CursorData, ImageDimensions, Point } from './generativeFillUtils/genera import { APISuccess, ImageUtility } from './generativeFillUtils/ImageHandler'; import { PointerHandler } from './generativeFillUtils/PointerHandler'; import React = require('react'); +import { DocumentManager } from '../../../util/DocumentManager'; +import { CollectionFreeFormView } from '../../collections/collectionFreeForm'; enum BrushStyle { ADD, @@ -303,19 +305,21 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD originalImg.current = currImg.current; originalDoc.current = parentDoc.current; const { urls } = res as APISuccess; - const imgUrls = await Promise.all(urls.map(url => ImageUtility.convertImgToCanvasUrl(url, canvasDims.width, canvasDims.height))); - const imgRes = await Promise.all( - imgUrls.map(async url => { - const saveRes = await onSave(url); - return [url, saveRes as Doc]; - }) - ); - setEdits(imgRes); - const image = new Image(); - image.src = imgUrls[0]; - ImageUtility.drawImgToCanvas(image, canvasRef, canvasDims.width, canvasDims.height); - currImg.current = image; - parentDoc.current = imgRes[0][1] as Doc; + if (res.status !== 'error') { + const imgUrls = await Promise.all(urls.map(url => ImageUtility.convertImgToCanvasUrl(url, canvasDims.width, canvasDims.height))); + const imgRes = await Promise.all( + imgUrls.map(async url => { + const saveRes = await onSave(url); + return [url, saveRes as Doc]; + }) + ); + setEdits(imgRes); + const image = new Image(); + image.src = imgUrls[0]; + ImageUtility.drawImgToCanvas(image, canvasRef, canvasDims.width, canvasDims.height); + currImg.current = image; + parentDoc.current = imgRes[0][1] as Doc; + } } catch (err) { console.log(err); } @@ -418,7 +422,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD ImageBox.setImageEditorOpen(false); ImageBox.setImageEditorSource(''); if (newCollectionRef.current) { - newCollectionRef.current.fitContentOnce = true; + DocumentManager.Instance.AddViewRenderedCb(newCollectionRef.current, dv => (dv.ComponentView as CollectionFreeFormView)?.fitContentOnce()); } setEdits([]); }; diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 6493117b0..54249a975 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -526,11 +526,11 @@ export class PresBox extends ViewBoxBaseComponent() { } if (pinDataTypes?.inkable || (!pinDataTypes && (activeItem.config_fillColor !== undefined || activeItem.color !== undefined))) { if (bestTarget.fillColor !== activeItem.config_fillColor) { - Doc.GetProto(bestTarget).fillColor = activeItem.config_fillColor; + Doc.GetProto(bestTarget).fillColor = StrCast(activeItem.config_fillColor, StrCast(bestTarget.fillColor)); changed = true; } if (bestTarget.color !== activeItem.config_color) { - Doc.GetProto(bestTarget).color = activeItem.config_color; + Doc.GetProto(bestTarget).color = StrCast(activeItem.config_color, StrCast(bestTarget.color)); changed = true; } if (bestTarget.width !== activeItem.width) { -- cgit v1.2.3-70-g09d2 From 84c15417f2247fc650a9f7b2c959479519bd3ebb Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 1 Nov 2023 23:54:49 -0400 Subject: fixes to snapping lines when dragging/resizing (lines are created for doc not being dragged, snapping lines are created for documents in groups). cleanup of pres path code. --- src/Utils.ts | 2 +- src/client/util/DragManager.ts | 22 +---- src/client/util/SnappingManager.ts | 23 +++-- src/client/views/DocumentDecorations.tsx | 9 +- src/client/views/MainView.tsx | 3 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 98 +++++++++------------- .../views/nodes/CollectionFreeFormDocumentView.tsx | 3 + src/client/views/nodes/trails/PresBox.tsx | 46 ++++++---- 8 files changed, 92 insertions(+), 114 deletions(-) (limited to 'src') diff --git a/src/Utils.ts b/src/Utils.ts index 34419f665..330ca59f9 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -758,7 +758,7 @@ export function DashColor(color: string) { } export function lightOrDark(color: any) { - if (color === 'transparent') return Colors.DARK_GRAY; + if (color === 'transparent' || !color) return Colors.DARK_GRAY; if (color.startsWith?.('linear')) return Colors.BLACK; const nonAlphaColor = color.startsWith('#') ? (color as string).substring(0, 7) : color.startsWith('rgba') ? color.replace(/,.[^,]*\)/, ')').replace('rgba', 'rgb') : color; const col = DashColor(nonAlphaColor).rgb(); diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 4f30e92ce..ea13eaa5b 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -191,13 +191,6 @@ export namespace DragManager { // drag a document and drop it (or make an embed/copy on drop) export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions, onDropCompleted?: (e?: DragCompleteEvent) => any) { - dragData.draggedViews.forEach( - action(view => { - const ffview = view.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView; - ffview && (ffview.GroupChildDrag = BoolCast(ffview.Document._isGroup)); - ffview?.setupDragLines(false); - }) - ); const addAudioTag = (dropDoc: any) => { dropDoc && !dropDoc.author_date && (dropDoc.author_date = new DateField()); dropDoc instanceof Doc && DocUtils.MakeLinkToActiveAudio(() => dropDoc); @@ -205,14 +198,7 @@ export namespace DragManager { }; const finishDrag = async (e: DragCompleteEvent) => { const docDragData = e.docDragData; - setTimeout(() => - dragData.draggedViews.forEach( - action(view => { - const ffview = view.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView; - ffview && (ffview.GroupChildDrag = false); - }) - ) - ); + setTimeout(() => dragData.draggedViews.forEach(view => view.props.CollectionFreeFormDocumentView?.().dragEnding())); onDropCompleted?.(e); // glr: optional additional function to be called - in this case with presentation trails if (docDragData && !docDragData.droppedDocuments.length) { docDragData.dropAction = dragData.userDropAction || dragData.dropAction; @@ -248,6 +234,7 @@ export namespace DragManager { }; dragData.draggedDocuments.map(d => d.dragFactory); // does this help? trying to make sure the dragFactory Doc is loaded StartDrag(eles, dragData, downX, downY, options, finishDrag); + dragData.draggedViews.forEach(view => view.props.CollectionFreeFormDocumentView?.().dragStarting()); return true; } @@ -281,9 +268,6 @@ export namespace DragManager { StartDrag(ele, dragData, downX, downY, options, undefined, 'Drag Column'); } - export function SetSnapLines(horizLines: number[], vertLines: number[]) { - SnappingManager.setSnapLines(horizLines, vertLines); - } export function snapDragAspect(dragPt: number[], snapAspect: number) { let closest = Utils.SNAP_THRESHOLD; let near = dragPt; @@ -491,11 +475,11 @@ export namespace DragManager { }; const cleanupDrag = action((undo: boolean) => { + (dragData as DocumentDragData).draggedViews?.forEach(view => view.props.CollectionFreeFormDocumentView?.().dragEnding()); hideDragShowOriginalElements(false); document.removeEventListener('pointermove', moveHandler, true); document.removeEventListener('pointerup', upHandler, true); SnappingManager.SetIsDragging(false); - SnappingManager.clearSnapLines(); if (batch.end() && undo) UndoManager.Undo(); docsBeingDragged.length = 0; }); diff --git a/src/client/util/SnappingManager.ts b/src/client/util/SnappingManager.ts index ed9819fc0..3cb41ab4d 100644 --- a/src/client/util/SnappingManager.ts +++ b/src/client/util/SnappingManager.ts @@ -1,19 +1,19 @@ import { observable, action, runInAction } from 'mobx'; -import { computedFn } from 'mobx-utils'; import { Doc } from '../../fields/Doc'; export namespace SnappingManager { class Manager { @observable IsDragging: boolean = false; + @observable IsResizing: Doc | undefined; @observable public horizSnapLines: number[] = []; @observable public vertSnapLines: number[] = []; @action public clearSnapLines() { this.vertSnapLines = []; this.horizSnapLines = []; } - @action public setSnapLines(horizLines: number[], vertLines: number[]) { - this.horizSnapLines = horizLines; - this.vertSnapLines = vertLines; + @action public addSnapLines(horizLines: number[], vertLines: number[]) { + this.horizSnapLines.push(...horizLines); + this.vertSnapLines.push(...vertLines); } } @@ -22,8 +22,8 @@ export namespace SnappingManager { export function clearSnapLines() { manager.clearSnapLines(); } - export function setSnapLines(horizLines: number[], vertLines: number[]) { - manager.setSnapLines(horizLines, vertLines); + export function addSnapLines(horizLines: number[], vertLines: number[]) { + manager.addSnapLines(horizLines, vertLines); } export function horizSnapLines() { return manager.horizSnapLines; @@ -35,14 +35,13 @@ export namespace SnappingManager { export function SetIsDragging(dragging: boolean) { runInAction(() => (manager.IsDragging = dragging)); } + export function SetIsResizing(doc: Doc | undefined) { + runInAction(() => (manager.IsResizing = doc)); + } export function GetIsDragging() { return manager.IsDragging; } - - export function SetShowSnapLines(show: boolean) { - runInAction(() => (Doc.UserDoc().freeform_snapLines = show)); - } - export function GetShowSnapLines() { - return Doc.UserDoc().freeform_snapLines; + export function GetIsResizing() { + return manager.IsResizing; } } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 65f7c7a70..5a145e94a 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -63,7 +63,6 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P @observable public pullColor: string = 'white'; @observable private _isRotating: boolean = false; @observable private _isRounding: boolean = false; - @observable private _isResizing: boolean = false; @observable private showLayoutAcl: boolean = false; constructor(props: any) { @@ -475,7 +474,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P @action onPointerDown = (e: React.PointerEvent): void => { - this._isResizing = true; + SnappingManager.SetIsResizing(SelectionManager.Docs().lastElement()); setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction); DocumentView.Interacting = true; // turns off pointer events on things like youtube videos and web pages so that dragging doesn't get "stuck" when cursor moves over them this._resizeHdlId = e.currentTarget.className; @@ -491,7 +490,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P ffview && ffviewSet.add(ffview); this._dragHeights.set(docView.layoutDoc, { start: NumCast(docView.rootDoc._height), lowest: NumCast(docView.rootDoc._height) }); }); - Array.from(ffviewSet).map(ffview => ffview.setupDragLines(false)); + Array.from(ffviewSet).map(ffview => ffview.dragStarting(false, false)); }; onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => { @@ -686,7 +685,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P @action onPointerUp = (e: PointerEvent): void => { - this._isResizing = false; + SnappingManager.SetIsResizing(undefined); this._resizeHdlId = ''; DocumentView.Interacting = false; this._resizeUndo?.end(); @@ -774,7 +773,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P var shareSymbolIcon = ReverseHierarchyMap.get(shareMode)?.image; // hide the decorations if the parent chooses to hide it or if the document itself hides it - const hideDecorations = this._isResizing || seldocview.props.hideDecorations || seldocview.rootDoc.layout_hideDecorations; + const hideDecorations = SnappingManager.GetIsResizing() || seldocview.props.hideDecorations || seldocview.rootDoc.layout_hideDecorations; const hideResizers = ![AclAdmin, AclEdit, AclAugment].includes(GetEffectiveAcl(seldocview.rootDoc)) || hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.layout_hideResizeHandles || this._isRounding || this._isRotating; const hideTitle = this._showNothing || hideDecorations || seldocview.props.hideDecorationTitle || seldocview.rootDoc.layout_hideDecorationTitle || this._isRounding || this._isRotating; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 0a3389fc2..da5e4f966 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -918,7 +918,8 @@ export class MainView extends React.Component { } @computed get snapLines() { SnappingManager.GetIsDragging(); - const dragged = DragManager.docsBeingDragged.lastElement(); + SnappingManager.GetIsResizing(); + const dragged = DragManager.docsBeingDragged.lastElement() ?? SelectionManager.Docs().lastElement(); const dragPar = dragged ? DocumentManager.Instance.getDocumentView(dragged)?.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView : undefined; return !dragPar?.rootDoc.freeform_snapLines ? null : (
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index a8b743896..0c3033579 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -16,7 +16,7 @@ import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } fro import { ImageField } from '../../../../fields/URLField'; import { TraceMobx } from '../../../../fields/util'; import { GestureUtils } from '../../../../pen-gestures/GestureUtils'; -import { aggregateBounds, DashColor, emptyFunction, intersectRect, lightOrDark, returnFalse, returnNone, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils'; +import { aggregateBounds, DashColor, emptyFunction, intersectRect, lightOrDark, returnFalse, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils'; import { CognitiveServices } from '../../../cognitive_services/CognitiveServices'; import { Docs, DocUtils } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; @@ -118,8 +118,6 @@ export class CollectionFreeFormView extends CollectionSubView { - return (pt => super.onExternalDrop(e, { x: pt[0], y: pt[1] }))(this.getTransform().transformPoint(e.pageX, e.pageY)); - }; + onExternalDrop = (e: React.DragEvent) => (([x, y]) => super.onExternalDrop(e, { x, y }))(this.getTransform().transformPoint(e.pageX, e.pageY)); pickCluster(probe: number[]) { return this.childLayoutPairs @@ -1820,9 +1816,6 @@ export class CollectionFreeFormView extends CollectionSubView SnappingManager.SetShowSnapLines(!SnappingManager.GetShowSnapLines()), icon: 'compress-arrows-alt' }) - : null; !Doc.noviceMode ? viewCtrlItems.push({ description: (this.Document._freeform_useClusters ? 'Hide' : 'Show') + ' Clusters', event: () => this.updateClusters(!this.Document._freeform_useClusters), icon: 'braille' }) : null; !viewctrls && ContextMenu.Instance.addItem({ description: 'UI Controls...', subitems: viewCtrlItems, icon: 'eye' }); @@ -1858,23 +1851,38 @@ export class CollectionFreeFormView extends CollectionSubView { + dragEnding = () => { + this.GroupChildDrag = false; + SnappingManager.clearSnapLines(); + }; + @action + dragStarting = (snapToDraggedDoc: boolean = false, showGroupDragTarget: boolean, visited = new Set()) => { + if (visited.has(this.rootDoc)) return; + visited.add(this.rootDoc); + showGroupDragTarget && (this.GroupChildDrag = BoolCast(this.Document._isGroup)); + if (this.rootDoc._isGroup && this.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView) { + this.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView.dragStarting(snapToDraggedDoc, false, visited); + } const activeDocs = this.getActiveDocuments(); const size = this.getTransform().transformDirection(this.props.PanelWidth(), this.props.PanelHeight()); const selRect = { left: this.panX() - size[0] / 2, top: this.panY() - size[1] / 2, width: size[0], height: size[1] }; const docDims = (doc: Doc) => ({ left: NumCast(doc.x), top: NumCast(doc.y), width: NumCast(doc._width), height: NumCast(doc._height) }); const isDocInView = (doc: Doc, rect: { left: number; top: number; width: number; height: number }) => intersectRect(docDims(doc), rect); - const otherBounds = { left: this.panX(), top: this.panY(), width: Math.abs(size[0]), height: Math.abs(size[1]) }; - let snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect)); // first see if there are any foreground docs to snap to - !snappableDocs.length && (snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect))); // if not, see if there are background docs to snap to - !snappableDocs.length && (snappableDocs = activeDocs.filter(doc => doc.z !== undefined && isDocInView(doc, otherBounds))); // if not, then why not snap to floating docs + const snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect)); // first see if there are any foreground docs to snap to + activeDocs.forEach( + doc => + doc._isGroup && + SnappingManager.GetIsResizing() !== doc && + !DragManager.docsBeingDragged.includes(doc) && + (DocumentManager.Instance.getDocumentView(doc)?.ComponentView as CollectionFreeFormView)?.dragStarting(snapToDraggedDoc, false, visited) + ); const horizLines: number[] = []; const vertLines: number[] = []; const invXf = this.getTransform().inverse(); snappableDocs - .filter(doc => snapToDraggedDoc || !DragManager.docsBeingDragged.includes(Cast(doc.rootDocument, Doc, null) || doc)) + .filter(doc => !doc._isGroup && (snapToDraggedDoc || (SnappingManager.GetIsResizing() !== doc && !DragManager.docsBeingDragged.includes(doc)))) .forEach(doc => { const { left, top, width, height } = docDims(doc); const topLeftInScreen = invXf.transformPoint(left, top); @@ -1883,7 +1891,7 @@ export class CollectionFreeFormView extends CollectionSubView this.childDocs.filter(doc => !this._renderCutoffData.get(doc[Id])).length !== 0; @@ -1913,7 +1921,6 @@ export class CollectionFreeFormView extends CollectionSubView (CollectionFreeFormView.ShowPresPaths ? PresBox.Instance.getPaths(this.rootDoc) : null); brushedView = () => this._brushedView; gridColor = () => DashColor(lightOrDark(this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor))) @@ -1921,7 +1928,9 @@ export class CollectionFreeFormView extends CollectionSubView {this.children} @@ -2028,7 +2036,7 @@ export class CollectionFreeFormView extends CollectionSubView e.preventDefault()} onContextMenu={this.onContextMenu} style={{ @@ -2065,18 +2073,9 @@ export class CollectionFreeFormView extends CollectionSubView ) : ( <> - {this._firstRender ? this.placeholder : this.marqueeView} + {this.marqueeView} {this.props.noOverlay ? null : } - -
- - {(this._hLines ?? []) - .map(l => ) // - .concat((this._vLines ?? []).map(l => )) ?? []} - -
- - {this.GroupChildDrag ?
: null} + {!this.GroupChildDrag ? null :
} )}
@@ -2104,7 +2103,6 @@ interface CollectionFreeFormViewPannableContentsProps { children?: React.ReactNode | undefined; transition?: string; isAnnotationOverlay: boolean | undefined; - presPaths: () => JSX.Element | null; transform: () => string; brushedView: () => { panX: number; panY: number; width: number; height: number } | undefined; } @@ -2112,41 +2110,21 @@ interface CollectionFreeFormViewPannableContentsProps { @observer class CollectionFreeFormViewPannableContents extends React.Component { @computed get presPaths() { - return !this.props.presPaths() ? null : ( - <> -
{PresBox.Instance?.orderedPathLabels(this.props.rootDoc)}
- - - - - - - - - - - - - {this.props.presPaths()} - - - ); + return CollectionFreeFormView.ShowPresPaths ? PresBox.Instance.pathLines(this.props.rootDoc) : null; } // rectangle highlight used when following trail/link to a region of a collection that isn't a document - @computed get brushedView() { - const brushedView = this.props.brushedView(); - return !brushedView ? null : ( + showViewport = (viewport: { panX: number; panY: number; width: number; height: number } | undefined) => + !viewport ? null : (
); - } render() { return ( @@ -2165,7 +2143,7 @@ class CollectionFreeFormViewPannableContents extends React.Component {this.props.children} {this.presPaths} - {this.brushedView} + {this.showViewport(this.props.brushedView())}
); } diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 9f7ebc5d9..f9afe4d53 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -182,6 +182,9 @@ export class CollectionFreeFormDocumentView extends DocComponent this.props.CollectionFreeFormView?.dragEnding(); + dragStarting = () => this.props.CollectionFreeFormView?.dragStarting(false, true); + nudge = (x: number, y: number) => { this.props.Document.x = NumCast(this.props.Document.x) + x; this.props.Document.y = NumCast(this.props.Document.y) + y; diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 54249a975..05810b63a 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -1384,24 +1384,38 @@ export class PresBox extends ViewBoxBaseComponent() { } }); return ( - + <> +
{PresBox.Instance?.orderedPathLabels(collection)}
+ + + + + + + + + + + + + + + ); }; - getPaths = (collection: Doc) => this.pathLines(collection); // needs to be smarter and figure out the paths to draw for this specific collection. or better yet, draw everything in an overlay layer instad of within a collection - // Converts seconds to ms and updates presentation_transition public static SetTransitionTime = (number: String, setter: (timeInMS: number) => void, change?: number) => { let timeInMS = Number(number) * 1000; -- cgit v1.2.3-70-g09d2 From 1bba63b1d15cfe76393424a768d2dbc0f0b8cffb Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 2 Nov 2023 10:55:43 -0400 Subject: cleaned up brushView to only apply to freeformviews that aren't overlays (wasn't being used properly. before anyway). cleaned up marquee view divs. --- src/client/views/collections/CollectionView.tsx | 1 - .../collectionFreeForm/CollectionFreeFormView.tsx | 108 ++++++++------------- .../collections/collectionFreeForm/MarqueeView.tsx | 28 +++++- src/client/views/nodes/PDFBox.tsx | 2 - src/client/views/nodes/WebBox.tsx | 4 - src/client/views/nodes/trails/PresBox.tsx | 5 - src/client/views/pdf/PDFViewer.tsx | 4 - 7 files changed, 67 insertions(+), 85 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index ce19b3f9b..493f40d77 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -41,7 +41,6 @@ interface CollectionViewProps_ extends FieldViewProps { isAnnotationOverlayScrollable?: boolean; // whether the annotation overlay can be vertically scrolled (just for tree views, currently) layoutEngine?: () => string; setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean, doc: Opt) => void) => void; - setBrushViewer?: (func?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void) => void; ignoreUnrendered?: boolean; // property overrides for child documents diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 0c3033579..d80ea2cb2 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -64,8 +64,6 @@ export type collectionFreeformViewProps = { noOverlay?: boolean; // used to suppress docs in the overlay (z) layer (ie, for minimap since overlay doesn't scale) engineProps?: any; getScrollHeight?: () => number | undefined; - dontRenderDocuments?: boolean; // used for annotation overlays which need to distribute documents into different freeformviews with different mixBlendModes depending on whether they are transparent or not. - // However, this screws up interactions since only the top layer gets events. so we render the freeformview a 3rd time with all documents in order to get interaction events (eg., marquee) but we don't actually want to display the documents. }; @observer @@ -123,7 +121,6 @@ export class CollectionFreeFormView extends CollectionSubView(); - @observable _marqueeRef: HTMLDivElement | null = null; @observable _marqueeViewRef = React.createRef(); @observable GroupChildDrag: boolean = false; // child document view being dragged. needed to update drop areas of groups when a group item is dragged. @observable _brushedView: { width: number; height: number; panX: number; panY: number } | undefined; // highlighted region of freeform canvas used by presentations to indicate a region @@ -1335,7 +1332,7 @@ export class CollectionFreeFormView extends CollectionSubView @@ -1571,11 +1568,9 @@ export class CollectionFreeFormView extends CollectionSubView { this._firstRender = false; @@ -1612,9 +1607,7 @@ export class CollectionFreeFormView extends CollectionSubView this.doLayoutComputation, - elements => { - if (elements !== undefined) this._layoutElements = elements || []; - }, + elements => elements !== undefined && (this._layoutElements = elements || []), { fireImmediately: true, name: 'doLayout' } ); @@ -1709,7 +1702,6 @@ export class CollectionFreeFormView extends CollectionSubView disposer?.()); - this._marqueeRef?.removeEventListener('dashDragAutoScroll', this.onDragAutoScroll as any); } @action @@ -1717,26 +1709,6 @@ export class CollectionFreeFormView extends CollectionSubView) => { - if ((e as any).handlePan || this.props.isAnnotationOverlay) return; - (e as any).handlePan = true; - - if (!this.layoutDoc._freeform_noAutoPan && !this.props.renderDepth && this._marqueeRef) { - const dragX = e.detail.clientX; - const dragY = e.detail.clientY; - const bounds = this._marqueeRef?.getBoundingClientRect(); - - const deltaX = dragX - bounds.left < 25 ? -(25 + (bounds.left - dragX)) : bounds.right - dragX < 25 ? 25 - (bounds.right - dragX) : 0; - const deltaY = dragY - bounds.top < 25 ? -(25 + (bounds.top - dragY)) : bounds.bottom - dragY < 25 ? 25 - (bounds.bottom - dragY) : 0; - if (deltaX !== 0 || deltaY !== 0) { - this.Document[this.panYFieldKey] = NumCast(this.Document[this.panYFieldKey]) + deltaY / 2; - this.Document[this.panXFieldKey] = NumCast(this.Document[this.panXFieldKey]) + deltaX / 2; - } - } - e.stopPropagation(); - }; - @undoBatch promoteCollection = () => { const childDocs = this.childDocs.slice(); @@ -1926,11 +1898,41 @@ export class CollectionFreeFormView extends CollectionSubView + +
+ ); + } + @computed get pannableContents() { + return ( + + {this.children} + + ); + } @computed get marqueeView() { TraceMobx(); - return this._firstRender ? ( - this.placeholder - ) : ( + return ( -
{ - this._marqueeRef = r; - r?.addEventListener('dashDragAutoScroll', this.onDragAutoScroll as any); - }} - style={{ opacity: this.props.dontRenderDocuments ? 0.7 : undefined }}> - {this.layoutDoc._freeform_backgroundGrid ? ( -
- -
- ) : null} - - {this.children} - -
+ {this.layoutDoc._freeform_backgroundGrid ? this.backgroundGrid : null} + {this.pannableContents} {this._showAnimTimeline ? : null}
); @@ -2073,7 +2045,7 @@ export class CollectionFreeFormView extends CollectionSubView ) : ( <> - {this.marqueeView} + {this._firstRender ? this.placeholder : this.marqueeView} {this.props.noOverlay ? null : } {!this.GroupChildDrag ? null :
} diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index a30ec5302..edcc17afd 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -7,7 +7,7 @@ import { InkData, InkField, InkTool } from '../../../../fields/InkField'; import { List } from '../../../../fields/List'; import { RichTextField } from '../../../../fields/RichTextField'; import { Cast, FieldValue, NumCast, StrCast } from '../../../../fields/Types'; -import { ImageField } from '../../../../fields/URLField'; +import { ImageField, nullAudio } from '../../../../fields/URLField'; import { GetEffectiveAcl } from '../../../../fields/util'; import { intersectRect, lightOrDark, returnFalse, Utils } from '../../../../Utils'; import { CognitiveServices } from '../../../cognitive_services/CognitiveServices'; @@ -35,6 +35,8 @@ interface MarqueeViewProps { selectDocuments: (docs: Doc[]) => void; addLiveTextDocument: (doc: Doc) => void; isSelected: () => boolean; + panXFieldKey: string; + panYFieldKey: string; trySelectCluster: (addToSel: boolean) => boolean; nudge?: (x: number, y: number, nudgeTime?: number) => boolean; ungroup?: () => void; @@ -651,11 +653,35 @@ export class MarqueeView extends React.Component ); } + MarqueeRef: HTMLDivElement | null = null; + @action + onDragAutoScroll = (e: CustomEvent) => { + if ((e as any).handlePan || this.props.isAnnotationOverlay) return; + (e as any).handlePan = true; + + const bounds = this.MarqueeRef?.getBoundingClientRect(); + if (!this.props.Document._freeform_noAutoPan && !this.props.renderDepth && bounds) { + const dragX = e.detail.clientX; + const dragY = e.detail.clientY; + + const deltaX = dragX - bounds.left < 25 ? -(25 + (bounds.left - dragX)) : bounds.right - dragX < 25 ? 25 - (bounds.right - dragX) : 0; + const deltaY = dragY - bounds.top < 25 ? -(25 + (bounds.top - dragY)) : bounds.bottom - dragY < 25 ? 25 - (bounds.bottom - dragY) : 0; + if (deltaX !== 0 || deltaY !== 0) { + this.props.Document[this.props.panYFieldKey] = NumCast(this.props.Document[this.props.panYFieldKey]) + deltaY / 2; + this.props.Document[this.props.panXFieldKey] = NumCast(this.props.Document[this.props.panXFieldKey]) + deltaX / 2; + } + } + e.stopPropagation(); + }; render() { return (
{ + r?.addEventListener('dashDragAutoScroll', this.onDragAutoScroll as any); + this.MarqueeRef = r; + }} style={{ overflow: StrCast(this.props.Document._overflow), cursor: [InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool) || this._visible ? 'crosshair' : 'pointer', diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 537da5055..c068d9dd7 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -212,8 +212,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent this._pdfViewer?.brushView(view, transTime); - sidebarAddDocTab = (doc: Doc, where: OpenWhere) => { if (DocListCast(this.props.Document[this.props.fieldKey + '_sidebar']).includes(doc) && !this.SidebarShown) { this.toggleSidebar(false); diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 58a765d61..66e0ed21f 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -51,7 +51,6 @@ export class WebBox extends ViewBoxAnnotatableComponent) => void); - private _setBrushViewer: undefined | ((view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void); private _mainCont: React.RefObject = React.createRef(); private _outerRef: React.RefObject = React.createRef(); private _disposers: { [name: string]: IReactionDisposer } = {}; @@ -275,8 +274,6 @@ export class WebBox extends ViewBoxAnnotatableComponent void) => (this._setBrushViewer = func); - brushView = (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => this._setBrushViewer?.(view, transTime); focus = (anchor: Doc, options: DocFocusOptions) => { if (anchor !== this.rootDoc && this._outerRef.current) { const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); @@ -971,7 +968,6 @@ export class WebBox extends ViewBoxAnnotatableComponent() { if (bestTarget._layout_scrollTop !== activeItem.config_scrollTop) { bestTarget._layout_scrollTop = activeItem.config_scrollTop; changed = true; - const contentBounds = Cast(activeItem.config_viewBounds, listSpec('number')); - if (contentBounds) { - const dv = DocumentManager.Instance.getDocumentView(bestTarget)?.ComponentView; - dv?.brushView?.({ panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] }, transTime); - } } } if (pinDataTypes?.dataannos || (!pinDataTypes && activeItem.config_annotations !== undefined)) { diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 939928c1c..2e494aa45 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -68,7 +68,6 @@ export class PDFViewer extends React.Component { private _styleRule: any; // stylesheet rule for making hyperlinks clickable private _retries = 0; // number of times tried to create the PDF viewer private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean, doc: Opt) => void); - private _setBrushViewer: undefined | ((view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void); private _annotationLayer: React.RefObject = React.createRef(); private _disposers: { [name: string]: IReactionDisposer } = {}; private _viewer: React.RefObject = React.createRef(); @@ -193,7 +192,6 @@ export class PDFViewer extends React.Component { return focusSpeed; }; crop = (region: Doc | undefined, addCrop?: boolean) => this.props.crop(region, addCrop); - brushView = (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => this._setBrushViewer?.(view, transTime); @action setupPdfJsViewer = async () => { @@ -469,7 +467,6 @@ export class PDFViewer extends React.Component { }; setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean, doc: Opt) => void) => (this._setPreviewCursor = func); - setBrushViewer = (func?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void) => (this._setBrushViewer = func); @action onZoomWheel = (e: React.WheelEvent) => { @@ -546,7 +543,6 @@ export class PDFViewer extends React.Component { fieldKey={this.props.fieldKey + '_annotations'} getScrollHeight={this.getScrollHeight} setPreviewCursor={this.setPreviewCursor} - setBrushViewer={this.setBrushViewer} PanelHeight={this.panelHeight} PanelWidth={this.panelWidth} ScreenToLocalTransform={this.overlayTransform} -- cgit v1.2.3-70-g09d2 From eec81f7e0b53395e3e2ea25663a9ea06ec83085d Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 2 Nov 2023 19:30:19 -0400 Subject: performance fixes - don't invalidate as much by using reactions in place of computd values; don't make things active when things are dragged unless CanEmbed; fix for linkBox to use reaction. --- src/client/util/DragManager.ts | 7 +- src/client/util/SnappingManager.ts | 7 ++ src/client/views/collections/CollectionSubView.tsx | 18 ++-- src/client/views/collections/CollectionView.tsx | 21 +++- .../collectionFreeForm/CollectionFreeFormView.tsx | 107 +++++++++++++-------- src/client/views/nodes/DocumentLinksButton.tsx | 2 + src/client/views/nodes/DocumentView.tsx | 62 ++++++++---- src/client/views/nodes/LinkBox.tsx | 90 ++++++++++------- src/client/views/nodes/WebBox.tsx | 77 ++++++++------- src/client/views/pdf/PDFViewer.tsx | 4 +- 10 files changed, 248 insertions(+), 147 deletions(-) (limited to 'src') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index ea13eaa5b..9d6bd4f60 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -329,7 +329,7 @@ export namespace DragManager { DocDragData = dragData as DocumentDragData; const batch = UndoManager.StartBatch(dragUndoName ?? 'document drag'); eles = eles.filter(e => e); - CanEmbed = dragData.canEmbed || false; + SnappingManager.SetCanEmbed(dragData.canEmbed || false); if (!dragDiv) { dragDiv = document.createElement('div'); dragDiv.className = 'dragManager-dragDiv'; @@ -455,7 +455,7 @@ export namespace DragManager { runInAction(() => docsBeingDragged.push(...docsToDrag)); const hideDragShowOriginalElements = (hide: boolean) => { - dragLabel.style.display = hide && !CanEmbed ? '' : 'none'; + dragLabel.style.display = hide && !SnappingManager.GetCanEmbed() ? '' : 'none'; !hide && dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement)); setTimeout(() => eles.forEach(ele => (ele.hidden = hide))); }; @@ -482,6 +482,7 @@ export namespace DragManager { SnappingManager.SetIsDragging(false); if (batch.end() && undo) UndoManager.Undo(); docsBeingDragged.length = 0; + SnappingManager.SetCanEmbed(false); }); var startWindowDragTimer: any; const moveHandler = (e: PointerEvent) => { @@ -588,7 +589,7 @@ export namespace DragManager { altKey: e.altKey, metaKey: e.metaKey, ctrlKey: e.ctrlKey, - embedKey: CanEmbed, + embedKey: SnappingManager.GetCanEmbed(), }, }; target.dispatchEvent(new CustomEvent('dashPreDrop', dropArgs)); diff --git a/src/client/util/SnappingManager.ts b/src/client/util/SnappingManager.ts index 3cb41ab4d..c0cd94067 100644 --- a/src/client/util/SnappingManager.ts +++ b/src/client/util/SnappingManager.ts @@ -5,6 +5,7 @@ export namespace SnappingManager { class Manager { @observable IsDragging: boolean = false; @observable IsResizing: Doc | undefined; + @observable CanEmbed: boolean = false; @observable public horizSnapLines: number[] = []; @observable public vertSnapLines: number[] = []; @action public clearSnapLines() { @@ -38,10 +39,16 @@ export namespace SnappingManager { export function SetIsResizing(doc: Doc | undefined) { runInAction(() => (manager.IsResizing = doc)); } + export function SetCanEmbed(canEmbed: boolean) { + runInAction(() => (manager.CanEmbed = canEmbed)); + } export function GetIsDragging() { return manager.IsDragging; } export function GetIsResizing() { return manager.IsResizing; } + export function GetCanEmbed() { + return manager.CanEmbed; + } } diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 09e7cdb32..8a1ba0df1 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -8,14 +8,22 @@ import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; import { Cast, ScriptCast, StrCast } from '../../../fields/Types'; import { WebField } from '../../../fields/URLField'; +import { GetEffectiveAcl, TraceMobx } from '../../../fields/util'; import { GestureUtils } from '../../../pen-gestures/GestureUtils'; import { returnFalse, Utils } from '../../../Utils'; import { DocServer } from '../../DocServer'; +import { Docs, DocumentOptions, DocUtils } from '../../documents/Documents'; +import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; import { Networking } from '../../Network'; +import { DragManager, dropActionType } from '../../util/DragManager'; import { ImageUtils } from '../../util/Import & Export/ImageUtils'; import { InteractionUtils } from '../../util/InteractionUtils'; +import { SelectionManager } from '../../util/SelectionManager'; +import { SnappingManager } from '../../util/SnappingManager'; import { undoBatch, UndoManager } from '../../util/UndoManager'; import { DocComponent } from '../DocComponent'; +import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; +import { CollectionView, CollectionViewProps } from './CollectionView'; import React = require('react'); export interface SubCollectionViewProps extends CollectionViewProps { @@ -118,7 +126,7 @@ export function CollectionSubView(moreProps?: X) { childDocs.forEach(d => { // dragging facets const dragged = this.props.childFilters?.().some(f => f.includes(Utils.noDragsDocFilter)); - if (dragged && DragManager.docsBeingDragged.includes(d)) return false; + if (dragged && SnappingManager.GetCanEmbed() && DragManager.docsBeingDragged.includes(d)) return false; let notFiltered = d.z || Doc.IsSystem(d) || DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), childFiltersByRanges, this.props.Document).length > 0; if (notFiltered) { notFiltered = (!searchDocs.length || searchDocs.includes(d)) && DocUtils.FilterDocs([d], childDocFilters, childFiltersByRanges, this.props.Document).length > 0; @@ -486,11 +494,3 @@ export function CollectionSubView(moreProps?: X) { return CollectionSubView; } - -import { GetEffectiveAcl, TraceMobx } from '../../../fields/util'; -import { Docs, DocumentOptions, DocUtils } from '../../documents/Documents'; -import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; -import { DragManager, dropActionType } from '../../util/DragManager'; -import { SelectionManager } from '../../util/SelectionManager'; -import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; -import { CollectionView, CollectionViewProps } from './CollectionView'; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 493f40d77..6e4b9ec07 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -1,4 +1,4 @@ -import { computed, observable, runInAction } from 'mobx'; +import { computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast, Opt } from '../../../fields/Doc'; @@ -78,6 +78,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent (this._annotationKeySuffix = returnEmptyString)); } + componentDidMount() { + // we use a reaction/observable instead of a computed value to reduce invalidations. + // There are many variables that aggregate into this boolean output - a change in any of them + // will cause downstream invalidations even if the computed value doesn't change. By making + // this a reaction, downstream invalidations only occur when the reaction value actually changes. + this.reactionDisposer = reaction( + () => (this.isAnyChildContentActive() ? true : this.props.isContentActive()), + active => (this._isContentActive = active), + { fireImmediately: true } + ); + } + componentWillUnmount() { + this.reactionDisposer?.(); + } + get collectionViewType(): CollectionViewType | undefined { const viewField = StrCast(this.layoutDoc._type_collection); if (CollectionView._safeMode) { @@ -220,7 +237,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent this.props.childHideResizeHandles?.() ?? BoolCast(this.Document.childHideResizeHandles); childHideDecorationTitle = () => this.props.childHideDecorationTitle?.() ?? BoolCast(this.Document.childHideDecorationTitle); childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.rootDoc.childLayoutTemplate, Doc, null); - isContentActive = (outsideReaction?: boolean) => (this.isAnyChildContentActive() ? true : this.props.isContentActive()); + isContentActive = (outsideReaction?: boolean) => this._isContentActive; render() { TraceMobx(); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index d80ea2cb2..2897cac0e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1280,12 +1280,20 @@ export class CollectionFreeFormView extends CollectionSubView this.childPointerEvents; + + @observable _childPointerEvents: 'none' | 'all' | 'visiblepainted' | undefined; + childPointerEventsFunc = () => this._childPointerEvents; childContentsActive = () => (this.props.childContentsActive ?? this.isContentActive() === false ? returnFalse : emptyFunction)(); getChildDocView(entry: PoolData) { const childLayout = entry.pair.layout; @@ -1372,9 +1380,9 @@ export class CollectionFreeFormView extends CollectionSubView; - getCalculatedPositions(params: { pair: { layout: Doc; data?: Doc }; index: number; collection: Doc }): PoolData { + getCalculatedPositions(pair: { layout: Doc; data?: Doc }): PoolData { const random = (min: number, max: number, x: number, y: number) => /* min should not be equal to max */ min + (((Math.abs(x * y) * 9301 + 49297) % 233280) / 233280) * (max - min); - const childDoc = params.pair.layout; + const childDoc = pair.layout; const childDocLayout = Doc.Layout(childDoc); const layoutFrameNumber = Cast(this.Document._currentFrame, 'number'); // frame number that container is at which determines layout frame values const contentFrameNumber = Cast(childDocLayout._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed @@ -1400,7 +1408,7 @@ export class CollectionFreeFormView extends CollectionSubView) { - this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => poolData.set(pair.layout[Id], this.getCalculatedPositions({ pair, index: i, collection: this.Document }))); + this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => poolData.set(pair.layout[Id], this.getCalculatedPositions(pair))); return [] as ViewDefResult[]; } @@ -1502,38 +1510,39 @@ export class CollectionFreeFormView extends CollectionSubView, computedElementData: ViewDefResult[]) => { const array = Array.from(newPool.entries()); - let somethingChanged = array.length !== this._lastPoolSize; this._lastPoolSize = array.length; - runInAction(() => { - for (const entry of array) { - const lastPos = this._cachedPool.get(entry[0]); // last computed pos - const newPos = entry[1]; - if ( - !lastPos || - newPos.color !== lastPos.color || - newPos.backgroundColor !== lastPos.backgroundColor || - newPos.opacity !== lastPos.opacity || - newPos.x !== lastPos.x || - newPos.y !== lastPos.y || - newPos.z !== lastPos.z || - newPos.rotation !== lastPos.rotation || - newPos.zIndex !== lastPos.zIndex || - newPos.transition !== lastPos.transition || - newPos.pointerEvents !== lastPos.pointerEvents - ) { - this._layoutPoolData.set(entry[0], newPos); - somethingChanged = true; - } - if (!lastPos || newPos.height !== lastPos.height || newPos.width !== lastPos.width) { - this._layoutSizeData.set(entry[0], { width: newPos.width, height: newPos.height }); - somethingChanged = true; - } + for (const entry of array) { + const lastPos = this._cachedPool.get(entry[0]); // last computed pos + const newPos = entry[1]; + if ( + !lastPos || + newPos.color !== lastPos.color || + newPos.backgroundColor !== lastPos.backgroundColor || + newPos.opacity !== lastPos.opacity || + newPos.x !== lastPos.x || + newPos.y !== lastPos.y || + newPos.z !== lastPos.z || + newPos.rotation !== lastPos.rotation || + newPos.zIndex !== lastPos.zIndex || + newPos.transition !== lastPos.transition || + newPos.pointerEvents !== lastPos.pointerEvents + ) { + this._layoutPoolData.set(entry[0], newPos); } - }); - if (!somethingChanged) return undefined; + if (!lastPos || newPos.height !== lastPos.height || newPos.width !== lastPos.width) { + this._layoutSizeData.set(entry[0], { width: newPos.width, height: newPos.height }); + } + } + // by returning undefined, we prevent an edit being made to layoutElements when nothing has happened + // this short circuit, prevents lots of downstream mobx invalidations which would have no effect but cause + // a distinct lag at the start of dragging. + // The reason we're here in the first place without a change is that when dragging a document, + // filters are changed on the annotation layers (eg. WebBox) which invalidate the childDoc list + // for the overlay views -- however, in many cases, this filter change doesn't actually affect anything + // (e.g, no annotations, or only opaque annotations). this._cachedPool.clear(); Array.from(newPool.entries()).forEach(k => this._cachedPool.set(k[0], k[1])); const elements = computedElementData.slice(); @@ -1551,7 +1560,7 @@ export class CollectionFreeFormView extends CollectionSubView { // create an anchor that saves information about the current state of the freeform view (pan, zoom, view type) @@ -1605,10 +1614,26 @@ export class CollectionFreeFormView extends CollectionSubView { + const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine); + return DocumentView.Interacting + ? 'none' + : this.props.childPointerEvents?.() ?? + (this.props.viewDefDivClick || // + (engine === computePassLayout.name && !this.props.isSelected(true)) || + this.isContentActive() === false + ? 'none' + : this.props.pointerEvents?.()); + }, + pointerevents => (this._childPointerEvents = pointerevents as any), + { fireImmediately: true } + ); + this._disposers.layoutComputation = reaction( - () => this.doLayoutComputation, - elements => elements !== undefined && (this._layoutElements = elements || []), - { fireImmediately: true, name: 'doLayout' } + () => this.doInternalLayoutComputation, + ({ newPool, computedElementData }) => (this._layoutElements = this.doLayoutComputation(newPool, computedElementData)), + { fireImmediately: true, name: 'layoutComputationReaction' } ); this._disposers.active = reaction( diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index 4db0bf5fa..a26d2e9f3 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -27,6 +27,7 @@ interface DocumentLinksButtonProps { StartLink?: boolean; //whether the link HAS been started (i.e. now needs to be completed) ShowCount?: boolean; scaling?: () => number; // how uch doc is scaled so that link buttons can invert it + hideCount?: () => boolean; } @observer export class DocumentLinksButton extends React.Component { @@ -239,6 +240,7 @@ export class DocumentLinksButton extends React.Component JSX.Element | null; incrementalRendering?: () => void; layout_fitWidth?: () => boolean; // whether the component always fits width (eg, KeyValueBox) - overridePointerEvents?: () => 'all' | 'none' | undefined; // if the conmponent overrides the pointer events for the document + overridePointerEvents?: () => 'all' | 'none' | undefined; // if the conmponent overrides the pointer events for the document (e.g, KeyValueBox always allows pointer events) fieldKey?: string; annotationKey?: string; getTitle?: () => string; @@ -272,8 +272,9 @@ export class DocumentViewInternal extends DocComponent { + // true - if the document has been activated directly or indirectly (by having its children selected) + // false - if its pointer events are explicitly turned off or if it's container tells it that it's inactive + // undefined - it is not active, but it should be responsive to actions that might activate it or its contents (eg clicking) + return this.props.isContentActive() === false || this.props.pointerEvents?.() === 'none' + ? false + : Doc.ActiveTool !== InkTool.None || SnappingManager.GetCanEmbed() || this.rootSelected() || this.rootDoc.forceActive || this._componentView?.isAnyChildContentActive?.() || this.props.isContentActive() + ? true + : undefined; + }, + active => (this._isContentActive = active), + { fireImmediately: true } + ); + this._disposers.pointerevents = reaction( + () => this.props.styleProvider?.(this.Document, this.props, StyleProp.PointerEvents), + pointerevents => (this._pointerEvents = pointerevents), + { fireImmediately: true } + ); } preDropFunc = (e: Event, de: DragManager.DropEvent) => { const dropAction = this.layoutDoc.dropAction as dropActionType; @@ -883,23 +906,22 @@ export class DocumentViewInternal extends DocComponent (this.disableClickScriptFunc ? undefined : this.onClickHandler); setHeight = (height: number) => (this.layoutDoc._height = height); setContentView = action((view: { getAnchor?: (addAsAnnotation: boolean) => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view)); - @computed get _isContentActive() { - // true - if the document has been activated directly or indirectly (by having its children selected) - // false - if its pointer events are explicitly turned off or if it's container tells it that it's inactive - // undefined - it is not active, but it should be responsive to actions that might active it or its contents (eg clicking) - return this.props.isContentActive() === false || this.props.pointerEvents?.() === 'none' - ? false - : Doc.ActiveTool !== InkTool.None || SnappingManager.GetIsDragging() || this.rootSelected() || this.rootDoc.forceActive || this._componentView?.isAnyChildContentActive?.() || this.props.isContentActive() - ? true - : undefined; - } + @observable _isContentActive: boolean | undefined; + isContentActive = (): boolean | undefined => this._isContentActive; childFilters = () => [...this.props.childFilters(), ...StrListCast(this.layoutDoc.childFilters)]; /// disable pointer events on content when there's an enabled onClick script (but not the browse script) and the contents aren't forced active, or if contents are marked inactive @computed get _contentPointerEvents() { - if (this.props.contentPointerEvents) return this.props.contentPointerEvents; - return (!this.disableClickScriptFunc && this.onClickHandler && !this.props.onBrowseClick?.() && this.isContentActive() !== true) || this.isContentActive() === false ? 'none' : this.pointerEvents; + TraceMobx(); + return this.props.contentPointerEvents ?? + ((!this.disableClickScriptFunc && // + this.onClickHandler && + !this.props.onBrowseClick?.() && + this.isContentActive() !== true) || + this.isContentActive() === false) + ? 'none' + : this.pointerEvents; } contentPointerEvents = () => this._contentPointerEvents; @computed get contents() { @@ -1304,8 +1326,8 @@ export class DocumentViewInternal extends DocComponent (!SnappingManager.GetIsDragging() || DragManager.CanEmbed) && Doc.BrushDoc(this.rootDoc)} - onPointerOver={e => (!SnappingManager.GetIsDragging() || DragManager.CanEmbed) && Doc.BrushDoc(this.rootDoc)} + onPointerEnter={e => (!SnappingManager.GetIsDragging() || SnappingManager.GetCanEmbed()) && Doc.BrushDoc(this.rootDoc)} + onPointerOver={e => (!SnappingManager.GetIsDragging() || SnappingManager.GetCanEmbed()) && Doc.BrushDoc(this.rootDoc)} onPointerLeave={e => !isParentOf(this.ContentDiv, document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y)) && Doc.UnBrushDoc(this.rootDoc)} style={{ borderRadius: this.borderRounding, @@ -1426,9 +1448,9 @@ export class DocumentView extends React.Component { @computed get hideLinkButton() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HideLinkBtn + (this.isSelected() ? ':selected' : '')); } + hideLinkCount = () => this.props.renderDepth === -1 || (this.isSelected() && this.props.renderDepth) || !this._isHovering || this.hideLinkButton; @computed get linkCountView() { - const hideCount = this.props.renderDepth === -1 || SnappingManager.GetIsDragging() || (this.isSelected() && this.props.renderDepth) || !this._isHovering || this.hideLinkButton; - return hideCount ? null : ; + return ; } @computed get docViewPath(): DocumentView[] { return this.props.docViewPath ? [...this.props.docViewPath(), this] : [this]; diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index 38ff21209..e66fed84b 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -1,19 +1,19 @@ import React = require('react'); import { Bezier } from 'bezier-js'; -import { computed, action } from 'mobx'; +import { computed, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import { Height, Width } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { DocCast, NumCast, StrCast } from '../../../fields/Types'; import { aggregateBounds, emptyFunction, returnAlways, returnFalse, Utils } from '../../../Utils'; import { DocumentManager } from '../../util/DocumentManager'; +import { Transform } from '../../util/Transform'; +import { CollectionFreeFormView } from '../collections/collectionFreeForm'; import { ViewBoxBaseComponent } from '../DocComponent'; import { StyleProp } from '../StyleProvider'; import { ComparisonBox } from './ComparisonBox'; import { FieldView, FieldViewProps } from './FieldView'; import './LinkBox.scss'; -import { CollectionFreeFormView } from '../collections/collectionFreeForm'; -import { Transform } from '../../util/Transform'; @observer export class LinkBox extends ViewBoxBaseComponent() { @@ -22,9 +22,6 @@ export class LinkBox extends ViewBoxBaseComponent() { } onClickScriptDisable = returnAlways; - componentDidMount() { - this.props.setContentView?.(this); - } @computed get anchor1() { const anchor1 = DocCast(this.rootDoc.link_anchor_1); const anchor_1 = anchor1?.layout_unrendered ? DocCast(anchor1.annotationOn) : anchor1; @@ -56,45 +53,70 @@ export class LinkBox extends ViewBoxBaseComponent() { } return { left: 0, top: 0, right: 0, bottom: 0, center: undefined }; }; - render() { - if (this.layoutDoc._layout_isSvg && (this.anchor1 || this.anchor2)?.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView) { - const a = (this.anchor1 ?? this.anchor2)!; - const b = (this.anchor2 ?? this.anchor1)!; - - const parxf = this.props.docViewPath()[this.props.docViewPath().length - 2].ComponentView as CollectionFreeFormView; - const this_xf = parxf?.getTransform() ?? Transform.Identity; //this.props.ScreenToLocalTransform(); - const a_invXf = a.props.ScreenToLocalTransform().inverse(); - const b_invXf = b.props.ScreenToLocalTransform().inverse(); - const a_scrBds = { tl: a_invXf.transformPoint(0, 0), br: a_invXf.transformPoint(a.rootDoc[Width](), a.rootDoc[Height]()) }; - const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(b.rootDoc[Width](), b.rootDoc[Height]()) }; - const a_bds = { tl: this_xf.transformPoint(a_scrBds.tl[0], a_scrBds.tl[1]), br: this_xf.transformPoint(a_scrBds.br[0], a_scrBds.br[1]) }; - const b_bds = { tl: this_xf.transformPoint(b_scrBds.tl[0], b_scrBds.tl[1]), br: this_xf.transformPoint(b_scrBds.br[0], b_scrBds.br[1]) }; + disposer: IReactionDisposer | undefined; + componentDidMount() { + this.props.setContentView?.(this); + this.disposer = reaction( + () => { + if (this.layoutDoc._layout_isSvg && (this.anchor1 || this.anchor2)?.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView) { + const a = (this.anchor1 ?? this.anchor2)!; + const b = (this.anchor2 ?? this.anchor1)!; - const ppt1 = [(a_bds.tl[0] + a_bds.br[0]) / 2, (a_bds.tl[1] + a_bds.br[1]) / 2]; - const pt1 = Utils.getNearestPointInPerimeter(a_bds.tl[0], a_bds.tl[1], a_bds.br[0] - a_bds.tl[0], a_bds.br[1] - a_bds.tl[1], (b_bds.tl[0] + b_bds.br[0]) / 2, (b_bds.tl[1] + b_bds.br[1]) / 2); - const pt2 = Utils.getNearestPointInPerimeter(b_bds.tl[0], b_bds.tl[1], b_bds.br[0] - b_bds.tl[0], b_bds.br[1] - b_bds.tl[1], (a_bds.tl[0] + a_bds.br[0]) / 2, (a_bds.tl[1] + a_bds.br[1]) / 2); - const ppt2 = [(b_bds.tl[0] + b_bds.br[0]) / 2, (b_bds.tl[1] + b_bds.br[1]) / 2]; + const parxf = this.props.docViewPath()[this.props.docViewPath().length - 2].ComponentView as CollectionFreeFormView; + const this_xf = parxf?.getTransform() ?? Transform.Identity; //this.props.ScreenToLocalTransform(); + const a_invXf = a.props.ScreenToLocalTransform().inverse(); + const b_invXf = b.props.ScreenToLocalTransform().inverse(); + const a_scrBds = { tl: a_invXf.transformPoint(0, 0), br: a_invXf.transformPoint(a.rootDoc[Width](), a.rootDoc[Height]()) }; + const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(b.rootDoc[Width](), b.rootDoc[Height]()) }; + const a_bds = { tl: this_xf.transformPoint(a_scrBds.tl[0], a_scrBds.tl[1]), br: this_xf.transformPoint(a_scrBds.br[0], a_scrBds.br[1]) }; + const b_bds = { tl: this_xf.transformPoint(b_scrBds.tl[0], b_scrBds.tl[1]), br: this_xf.transformPoint(b_scrBds.br[0], b_scrBds.br[1]) }; - const pts = [ppt1, pt1, pt2, ppt2].map(pt => [pt[0], pt[1]]); - const [lx, rx, ty, by] = [Math.min(pt1[0], pt2[0]), Math.max(pt1[0], pt2[0]), Math.min(pt1[1], pt2[1]), Math.max(pt1[1], pt2[1])]; - setTimeout( - action(() => { - this.layoutDoc.x = lx; - this.layoutDoc.y = ty; - this.layoutDoc._width = rx - lx; - this.layoutDoc._height = by - ty; - }) - ); + const ppt1 = [(a_bds.tl[0] + a_bds.br[0]) / 2, (a_bds.tl[1] + a_bds.br[1]) / 2]; + const pt1 = Utils.getNearestPointInPerimeter(a_bds.tl[0], a_bds.tl[1], a_bds.br[0] - a_bds.tl[0], a_bds.br[1] - a_bds.tl[1], (b_bds.tl[0] + b_bds.br[0]) / 2, (b_bds.tl[1] + b_bds.br[1]) / 2); + const pt2 = Utils.getNearestPointInPerimeter(b_bds.tl[0], b_bds.tl[1], b_bds.br[0] - b_bds.tl[0], b_bds.br[1] - b_bds.tl[1], (a_bds.tl[0] + a_bds.br[0]) / 2, (a_bds.tl[1] + a_bds.br[1]) / 2); + const ppt2 = [(b_bds.tl[0] + b_bds.br[0]) / 2, (b_bds.tl[1] + b_bds.br[1]) / 2]; + const pts = [ppt1, pt1, pt2, ppt2].map(pt => [pt[0], pt[1]]); + const [lx, rx, ty, by] = [Math.min(pt1[0], pt2[0]), Math.max(pt1[0], pt2[0]), Math.min(pt1[1], pt2[1]), Math.max(pt1[1], pt2[1])]; + return { pts, lx, rx, ty, by }; + } + return undefined; + }, + params => { + this.renderProps = params; + if (params) { + if ( + Math.abs(params.lx - NumCast(this.layoutDoc.x)) > 1e-5 || + Math.abs(params.ty - NumCast(this.layoutDoc.y)) > 1e-5 || + Math.abs(params.rx - params.lx - NumCast(this.layoutDoc._width)) > 1e-5 || + Math.abs(params.by - params.ty - NumCast(this.layoutDoc._height)) > 1e-5 + ) { + this.layoutDoc.x = params?.lx; + this.layoutDoc.y = params?.ty; + this.layoutDoc._width = params.rx - params?.lx; + this.layoutDoc._height = params?.by - params?.ty; + } + } + }, + { fireImmediately: true } + ); + } + componentWillUnmount(): void { + this.disposer?.(); + } + @observable renderProps: { lx: number; rx: number; ty: number; by: number; pts: number[][] } | undefined; + render() { + if (this.renderProps) { const highlight = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Highlighting); const highlightColor = highlight?.highlightIndex ? highlight?.highlightColor : undefined; - const bez = new Bezier(pts.map(p => ({ x: p[0], y: p[1] }))); + const bez = new Bezier(this.renderProps.pts.map(p => ({ x: p[0], y: p[1] }))); const text = bez.get(0.5); const linkDesc = StrCast(this.rootDoc.link_description) || 'description'; const strokeWidth = NumCast(this.rootDoc.stroke_width, 4); const dash = StrCast(this.rootDoc.stroke_dash); const strokeDasharray = dash && Number(dash) ? String(strokeWidth * Number(dash)) : undefined; + const { pts, lx, ty, rx, by } = this.renderProps; return (
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 66e0ed21f..a94279e88 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -861,7 +861,6 @@ export class WebBox extends ViewBoxAnnotatableComponent string[]) => ( + + ); + @computed get renderOpaqueAnnotations() { + return this.renderAnnotations(this.opaqueFilter); + } + @computed get renderTransparentAnnotations() { + return this.renderAnnotations(this.transparentFilter); + } childPointerEvents = () => (this.props.isContentActive() ? 'all' : undefined); @computed get webpage() { const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1; const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any); const scale = previewScale * (this.props.NativeDimScaling?.() || 1); - const renderAnnotations = (childFilters: () => string[]) => ( - - ); return (
{this.content} - {
{renderAnnotations(this.transparentFilter)}
} - {renderAnnotations(this.opaqueFilter)} +
{this.renderTransparentAnnotations}
+ {this.renderOpaqueAnnotations} {this.annotationLayer}
@@ -1050,7 +1055,7 @@ export class WebBox extends ViewBoxAnnotatableComponent this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop)); anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick; transparentFilter = () => [...this.props.childFilters(), Utils.IsTransparentFilter()]; - opaqueFilter = () => [...this.props.childFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length ? [] : [Utils.IsOpaqueFilter()])]; + opaqueFilter = () => [...this.props.childFilters(), Utils.noDragsDocFilter, ...(SnappingManager.GetCanEmbed() ? [] : [Utils.IsOpaqueFilter()])]; childStyleProvider = (doc: Doc | undefined, props: Opt, property: string): any => { if (doc instanceof Doc && property === StyleProp.PointerEvents) { if (this.inlineTextAnnotations.includes(doc)) return 'none'; @@ -1131,8 +1136,8 @@ export class WebBox extends ViewBoxAnnotatableComponent
- {this.sidebarHandle} - {!this.props.isContentActive() ? null : this.searchUI} + {!this.props.isContentActive() || SnappingManager.GetIsDragging() ? null : this.sidebarHandle} + {!this.props.isContentActive() || SnappingManager.GetIsDragging() ? null : this.searchUI}
); } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 2e494aa45..712a5e92f 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -512,7 +512,7 @@ export class PDFViewer extends React.Component { panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1); panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); transparentFilter = () => [...this.props.childFilters(), Utils.IsTransparentFilter()]; - opaqueFilter = () => [...this.props.childFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length && this.props.isContentActive() ? [] : [Utils.IsOpaqueFilter()])]; + opaqueFilter = () => [...this.props.childFilters(), Utils.noDragsDocFilter, ...(SnappingManager.GetCanEmbed() && this.props.isContentActive() ? [] : [Utils.IsOpaqueFilter()])]; childStyleProvider = (doc: Doc | undefined, props: Opt, property: string): any => { if (doc instanceof Doc && property === StyleProp.PointerEvents) { if (this.inlineTextAnnotations.includes(doc) || this.props.isContentActive() === false) return 'none'; @@ -557,7 +557,7 @@ export class PDFViewer extends React.Component { ); @computed get overlayTransparentAnnotations() { const transparentChildren = DocUtils.FilterDocs(DocListCast(this.props.dataDoc[this.props.fieldKey + '_annotations']), this.transparentFilter(), []); - return !transparentChildren.length ? null : this.renderAnnotations(this.transparentFilter, 'multiply', DragManager.docsBeingDragged.length && this.props.isContentActive() ? 'none' : undefined); + return !transparentChildren.length ? null : this.renderAnnotations(this.transparentFilter, 'multiply', SnappingManager.GetCanEmbed() && this.props.isContentActive() ? 'none' : undefined); } @computed get overlayOpaqueAnnotations() { return this.renderAnnotations(this.opaqueFilter, this.allAnnotations.some(anno => anno.mixBlendMode) ? 'hard-light' : undefined); -- cgit v1.2.3-70-g09d2 From a4e3b645317c4589cf49f8007f6e6b57cf2c12d3 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 4 Nov 2023 23:29:56 -0400 Subject: cleanup --- src/client/util/DragManager.ts | 2 +- src/client/views/collections/CollectionMenu.tsx | 16 ++++++++-------- src/client/views/collections/CollectionView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 2 +- src/client/views/nodes/DocumentLinksButton.tsx | 6 +++--- src/client/views/nodes/DocumentView.tsx | 6 +++--- src/client/views/nodes/KeyValueBox.tsx | 2 +- src/client/views/nodes/WebBox.tsx | 3 +-- src/client/views/nodes/generativeFill/GenerativeFill.tsx | 4 ++-- src/client/views/pdf/PDFViewer.tsx | 6 ++---- 11 files changed, 24 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 9d6bd4f60..8d8975763 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -4,7 +4,7 @@ import { Doc, Field, Opt, StrListCast } from '../../fields/Doc'; import { List } from '../../fields/List'; import { PrefetchProxy } from '../../fields/Proxy'; import { ScriptField } from '../../fields/ScriptField'; -import { BoolCast, ScriptCast, StrCast } from '../../fields/Types'; +import { ScriptCast, StrCast } from '../../fields/Types'; import { emptyFunction, Utils } from '../../Utils'; import { Docs, DocUtils } from '../documents/Documents'; import * as globalCssVariables from '../views/global/globalCssVariables.scss'; diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index ec9d86c1a..52cf40635 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -3,11 +3,11 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon, FontAwesomeIconProps } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; import { Toggle, ToggleType, Type } from 'browndash-components'; -import { Lambda, action, computed, observable, reaction, runInAction } from 'mobx'; +import { action, computed, Lambda, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { ColorState } from 'react-color'; -import { Utils, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../../Utils'; import { Doc, DocListCast, Opt } from '../../../fields/Doc'; +import { Document } from '../../../fields/documentSchemas'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; @@ -15,10 +15,10 @@ import { ObjectField } from '../../../fields/ObjectField'; import { RichTextField } from '../../../fields/RichTextField'; import { listSpec } from '../../../fields/Schema'; import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../fields/Types'; -import { Document } from '../../../fields/documentSchemas'; import { GestureUtils } from '../../../pen-gestures/GestureUtils'; -import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../../Utils'; import { Docs } from '../../documents/Documents'; +import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; import { DragManager } from '../../util/DragManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SelectionManager } from '../../util/SelectionManager'; @@ -31,17 +31,17 @@ import { GestureOverlay } from '../GestureOverlay'; import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from '../InkingStroke'; import { LightboxView } from '../LightboxView'; import { MainView } from '../MainView'; -import { DefaultStyleProvider } from '../StyleProvider'; +import { media_state } from '../nodes/AudioBox'; import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView'; import { DocumentView, DocumentViewInternal, OpenWhereMod } from '../nodes/DocumentView'; import { RichTextMenu } from '../nodes/formattedText/RichTextMenu'; +import { DefaultStyleProvider } from '../StyleProvider'; import { CollectionDockingView } from './CollectionDockingView'; +import { CollectionFreeFormView } from './collectionFreeForm'; +import { CollectionLinearView } from './collectionLinear'; import './CollectionMenu.scss'; import { COLLECTION_BORDER_WIDTH } from './CollectionView'; import { TabDocView } from './TabDocView'; -import { CollectionFreeFormView } from './collectionFreeForm'; -import { CollectionLinearView } from './collectionLinear'; -import { media_state } from '../nodes/AudioBox'; interface CollectionMenuProps { panelHeight: () => number; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 6e4b9ec07..c2062e8ab 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -1,4 +1,4 @@ -import { computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; +import { IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast, Opt } from '../../../fields/Doc'; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 2897cac0e..da0f7c893 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -241,7 +241,7 @@ export class CollectionFreeFormView extends CollectionSubView (!this._firstRender && (this.fitContentsToBox || force) ? this.fitToContentVals : undefined); - reverseNativeScaling = () => (this.fitContentsToBox ? true : false); + ignoreNativeDimScaling = () => (this.fitContentsToBox ? true : false); // freeform_panx, freeform_pany, freeform_scale all attempt to get values first from the layout controller, then from the layout/dataDoc (or template layout doc), and finally from the resolved template data document. // this search order, for example, allows icons of cropped images to find the panx/pany/zoom on the cropped image's data doc instead of the usual layout doc because the zoom/panX/panY define the cropped image panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.freeform_panX, 1)); diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index f9afe4d53..085f9f023 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -6,7 +6,7 @@ import { listSpec } from '../../../fields/Schema'; import { ComputedField } from '../../../fields/ScriptField'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { numberRange, OmitKeys } from '../../../Utils'; +import { numberRange } from '../../../Utils'; import { DocumentManager } from '../../util/DocumentManager'; import { SelectionManager } from '../../util/SelectionManager'; import { Transform } from '../../util/Transform'; diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index a26d2e9f3..50a7f5d7b 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -2,21 +2,21 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; import { action, computed, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; -import { Doc, Opt } from '../../../fields/Doc'; +import { Doc } from '../../../fields/Doc'; import { StrCast } from '../../../fields/Types'; import { emptyFunction, returnFalse, setupMoveUpEvents, StopEvent } from '../../../Utils'; import { DocUtils } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { Hypothesis } from '../../util/HypothesisUtils'; import { LinkManager } from '../../util/LinkManager'; -import { undoable, undoBatch, UndoManager } from '../../util/UndoManager'; +import { undoBatch, UndoManager } from '../../util/UndoManager'; import './DocumentLinksButton.scss'; import { DocumentView } from './DocumentView'; import { LinkDescriptionPopup } from './LinkDescriptionPopup'; import { TaskCompletionBox } from './TaskCompletedBox'; +import { PinProps } from './trails'; import React = require('react'); import _ = require('lodash'); -import { PinProps } from './trails'; interface DocumentLinksButtonProps { View: DocumentView; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 30d5a5184..98b13f90f 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -119,7 +119,7 @@ export interface DocComponentView { getView?: (doc: Doc) => Promise>; // returns a nested DocumentView for the specified doc or undefined addDocTab?: (doc: Doc, where: OpenWhere) => boolean; // determines how to add a document - used in following links to open the target ina local lightbox addDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean; // add a document (used only by collections) - reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height. However, some content views (e.g., FreeFormView w/ fitContentsToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling. + ignoreNativeDimScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height. However, some content views (e.g., FreeFormView w/ fitContentsToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling. select?: (ctrlKey: boolean, shiftKey: boolean) => void; focus?: (textAnchor: Doc, options: DocFocusOptions) => Opt; menuControls?: () => JSX.Element; // controls to display in the top menu bar when the document is selected. @@ -1459,10 +1459,10 @@ export class DocumentView extends React.Component { return Doc.Layout(this.Document, this.props.LayoutTemplate?.()); } @computed get nativeWidth() { - return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, !this.layout_fitWidth)); + return this.docView?._componentView?.ignoreNativeDimScaling?.() ? 0 : returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, !this.layout_fitWidth)); } @computed get nativeHeight() { - return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, !this.layout_fitWidth)); + return this.docView?._componentView?.ignoreNativeDimScaling?.() ? 0 : returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, !this.layout_fitWidth)); } @computed get shouldNotScale() { return this.props.shouldNotScale?.() || (this.layout_fitWidth && !this.nativeWidth) || [CollectionViewType.Docking].includes(this.Document._type_collection as any); diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 673f711be..95aeb5331 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -41,7 +41,7 @@ export class KeyValueBox extends React.Component { componentDidMount() { this.props.setContentView?.(this); } - reverseNativeScaling = returnTrue; + ignoreNativeDimScaling = returnTrue; able = returnAlways; layout_fitWidth = returnTrue; overridePointerEvents = returnAll; diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index a94279e88..2aca314da 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -10,13 +10,12 @@ import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; import { RefField } from '../../../fields/RefField'; import { listSpec } from '../../../fields/Schema'; -import { Cast, ImageCast, NumCast, StrCast, WebCast } from '../../../fields/Types'; +import { Cast, NumCast, StrCast, WebCast } from '../../../fields/Types'; import { ImageField, WebField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, getWordAtPoint, lightOrDark, returnFalse, returnOne, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentManager } from '../../util/DocumentManager'; -import { DragManager } from '../../util/DragManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SnappingManager } from '../../util/SnappingManager'; import { undoBatch, UndoManager } from '../../util/UndoManager'; diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx index 3093287e9..2b0927148 100644 --- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx +++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx @@ -9,7 +9,9 @@ import { NumCast } from '../../../../fields/Types'; import { Utils } from '../../../../Utils'; import { Docs, DocUtils } from '../../../documents/Documents'; import { Networking } from '../../../Network'; +import { DocumentManager } from '../../../util/DocumentManager'; import { CollectionDockingView } from '../../collections/CollectionDockingView'; +import { CollectionFreeFormView } from '../../collections/collectionFreeForm'; import { OpenWhereMod } from '../DocumentView'; import { ImageBox } from '../ImageBox'; import './GenerativeFill.scss'; @@ -20,8 +22,6 @@ import { CursorData, ImageDimensions, Point } from './generativeFillUtils/genera import { APISuccess, ImageUtility } from './generativeFillUtils/ImageHandler'; import { PointerHandler } from './generativeFillUtils/PointerHandler'; import React = require('react'); -import { DocumentManager } from '../../../util/DocumentManager'; -import { CollectionFreeFormView } from '../../collections/collectionFreeForm'; enum BrushStyle { ADD, diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 712a5e92f..43b662f0f 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -8,9 +8,8 @@ import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, returnAll, returnFalse, returnNone, returnTrue, returnZero, smoothScroll, Utils } from '../../../Utils'; +import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, returnAll, returnFalse, returnNone, returnZero, smoothScroll, Utils } from '../../../Utils'; import { DocUtils } from '../../documents/Documents'; -import { DragManager } from '../../util/DragManager'; import { SelectionManager } from '../../util/SelectionManager'; import { SharingManager } from '../../util/SharingManager'; import { SnappingManager } from '../../util/SnappingManager'; @@ -23,10 +22,9 @@ import { LinkDocPreview } from '../nodes/LinkDocPreview'; import { StyleProp } from '../StyleProvider'; import { AnchorMenu } from './AnchorMenu'; import { Annotation } from './Annotation'; +import { GPTPopup } from './GPTPopup/GPTPopup'; import './PDFViewer.scss'; import React = require('react'); -import { GPTPopup } from './GPTPopup/GPTPopup'; -import { InkingStroke } from '../InkingStroke'; const PDFJSViewer = require('pdfjs-dist/web/pdf_viewer'); const pdfjsLib = require('pdfjs-dist'); const _global = (window /* browser */ || global) /* node */ as any; -- cgit v1.2.3-70-g09d2