From dbf21b19ecc70047a580023104d05aed0d43c946 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 4 Aug 2022 11:01:37 -0400 Subject: fixed notetaking view to have an 'unset' category for unassigned notes. fixed pivot view to not fail when docs have no width. --- .../collectionFreeForm/CollectionFreeFormLayoutEngines.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index a0ebe4cdc..3d85d32a0 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -393,8 +393,10 @@ function normalizeResults( 0 ); aggBounds.r = aggBounds.x + Math.max(minWidth, aggBounds.r - aggBounds.x); - const wscale = panelDim[0] / (aggBounds.r - aggBounds.x); - let scale = wscale * (aggBounds.b - aggBounds.y) > panelDim[1] ? panelDim[1] / (aggBounds.b - aggBounds.y) : wscale; + const width = aggBounds.r - aggBounds.x === 0 ? 1 : aggBounds.r - aggBounds.x; + const height = aggBounds.b - aggBounds.y === 0 ? 1 : aggBounds.b - aggBounds.y; + const wscale = panelDim[0] / width; + let scale = wscale * height > panelDim[1] ? panelDim[1] / height : wscale; if (Number.isNaN(scale)) scale = 1; Array.from(docMap.entries()) -- cgit v1.2.3-70-g09d2 From 081632757af3c2ec2e4482c1e5fe710c8ee6cad8 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 11 Aug 2022 19:01:37 -0400 Subject: added better ui and backend support for mask ink strokes. added frame #'s ui for seeing current animation frame. --- src/client/documents/Documents.ts | 19 ++++++++-- src/client/util/CurrentUserUtils.ts | 6 ++-- src/client/util/DragManager.ts | 14 ++++---- src/client/views/InkingStroke.tsx | 28 +++++++++++---- src/client/views/MainView.tsx | 1 + src/client/views/collections/CollectionMenu.tsx | 2 +- src/client/views/collections/TabDocView.tsx | 10 +++--- .../CollectionFreeFormLayoutEngines.tsx | 1 + .../collectionFreeForm/CollectionFreeFormView.scss | 23 +++++++++---- .../collectionFreeForm/CollectionFreeFormView.tsx | 9 +++-- src/client/views/global/globalCssVariables.scss | 13 +++---- .../views/global/globalCssVariables.scss.d.ts | 6 ++-- .../views/nodes/CollectionFreeFormDocumentView.tsx | 7 ++-- src/client/views/nodes/DocumentView.tsx | 6 ++-- src/client/views/nodes/button/FontIconBox.tsx | 34 +++++++++++++----- src/client/views/nodes/trails/PresBox.tsx | 36 +++++++++++-------- src/fields/InkField.ts | 40 +++++++++++----------- 17 files changed, 162 insertions(+), 93 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index c7ea04839..cf9ed43e1 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -31,7 +31,7 @@ import { CollectionView } from '../views/collections/CollectionView'; import { ContextMenu } from '../views/ContextMenu'; import { ContextMenuProps } from '../views/ContextMenuItem'; import { DFLT_IMAGE_NATIVE_DIM } from '../views/global/globalCssVariables.scss'; -import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, InkingStroke } from '../views/InkingStroke'; +import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, InkingStroke } from '../views/InkingStroke'; import { AudioBox } from '../views/nodes/AudioBox'; import { FontIconBox } from '../views/nodes/button/FontIconBox'; import { ColorBox } from '../views/nodes/ColorBox'; @@ -918,9 +918,22 @@ export namespace Docs { return linkDoc; } - export function InkDocument(color: string, tool: string, strokeWidth: number, strokeBezier: string, fillColor: string, arrowStart: string, arrowEnd: string, dash: string, points: PointData[], options: DocumentOptions = {}) { + export function InkDocument( + color: string, + tool: string, + strokeWidth: number, + strokeBezier: string, + fillColor: string, + arrowStart: string, + arrowEnd: string, + dash: string, + points: PointData[], + isInkMask: boolean, + options: DocumentOptions = {} + ) { const I = new Doc(); I[Initializing] = true; + I.isInkMask = isInkMask; I.type = DocumentType.INK; I.layout = InkingStroke.LayoutString('data'); I.color = color; @@ -1424,7 +1437,7 @@ export namespace DocUtils { created = Docs.Create.RecordingDocument(field.url.href, resolved); layout = RecordingBox.LayoutString; } else if (field instanceof InkField) { - created = Docs.Create.InkDocument(ActiveInkColor(), Doc.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), field.inkData, resolved); + created = Docs.Create.InkDocument(ActiveInkColor(), Doc.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), field.inkData, ActiveIsInkMask(), resolved); layout = InkingStroke.LayoutString; } else if (field instanceof List && field[0] instanceof Doc) { created = Docs.Create.StackingDocument(DocListCast(field), resolved); diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index d19874720..dcf4a71c8 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -645,8 +645,9 @@ export class CurrentUserUtils { // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", scripts:{onClick: 'setActiveTool("highlighter")'} }, { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", scripts: {onClick:'{ return setActiveTool("circle", _readOnly_);}'} }, // { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveTool("square")' }, - { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", scripts: {onClick: '{ return setActiveTool("line", _readOnly_);}' }}, - { title: "Fill", toolTip: "Fill color", btnType: ButtonType.ColorButton, icon: "fill-drip",ignoreClick: true, scripts: {script: "{ return setFillColor(value, _readOnly_);}"} }, + { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", scripts: {onClick:'{ return setActiveTool("line", _readOnly_);}' }}, + { title: "Mask", toolTip: "Mask", btnType: ButtonType.ToggleButton, icon: "user-circle", scripts: {onClick:'{ return setIsInkMask(_readOnly_);}'} }, + { title: "Fill", toolTip: "Fill color", btnType: ButtonType.ColorButton, icon: "fill-drip",ignoreClick: true, scripts: {script: '{ return setFillColor(value, _readOnly_);}'} }, { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, ignoreClick: true, scripts: {script: '{ return setStrokeWidth(value, _readOnly_);}'}, numBtnType: NumButtonType.Slider, numBtnMin: 1}, { title: "Color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, scripts: {script: '{ return setStrokeColor(value, _readOnly_);}'} }, ]; @@ -674,6 +675,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: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()'}, width: 20, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}}, + { title: "Num", icon: "", toolTip: "Frame Number", btnType: ButtonType.TextButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()', buttonText: 'selectedDocs()?.lastElement().currentFrame.toString()'}, width: 20, scripts: {}}, { title: "Fwd", icon: "chevron-right", toolTip: "Next Animation Frame", btnType: ButtonType.ClickButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()'}, width: 20, scripts: { onClick: 'nextKeyFrame(_readOnly_)'}}, { title: "Fill", icon: "fill-drip", toolTip: "Background Fill Color",btnType: ButtonType.ColorButton, funcs: {hidden: '!SelectionManager_selectedDocType()'}, ignoreClick: true, width: 20, scripts: { script: 'return setBackgroundColor(value, _readOnly_)'}}, // Only when a document is selected { title: "Header", icon: "heading", toolTip: "Header Color", btnType: ButtonType.ColorButton, funcs: {hidden: '!SelectionManager_selectedDocType()'}, ignoreClick: true, scripts: { script: 'return setHeaderColor(value, _readOnly_)'}}, diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index ccd94c56e..d781a87ab 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -377,14 +377,14 @@ export namespace DragManager { } const rect = ele.getBoundingClientRect(); const scaleX = rect.width / (ele.offsetWidth || rect.width); - const scaleY = ele.offsetHeight ? rect.height / (ele.offsetHeight || rect.height) : scaleX; + const scaleY = scaleX; //ele.offsetHeight ? rect.height / (ele.offsetHeight || rect.height) : scaleX; elesCont.left = Math.min(rect.left, elesCont.left); elesCont.top = Math.min(rect.top, elesCont.top); elesCont.right = Math.max(rect.right, elesCont.right); elesCont.bottom = Math.max(rect.bottom, elesCont.bottom); - xs.push(rect.left); - ys.push(rect.top); + xs.push(rect.left + (options?.offsetX || 0)); + ys.push(rect.top + (options?.offsetY || 0)); scaleXs.push(scaleX); scaleYs.push(scaleY); Object.assign(dragElement.style, { @@ -401,9 +401,9 @@ export namespace DragManager { transformOrigin: '0 0', width: `${rect.width / scaleX}px`, height: `${rect.height / scaleY}px`, - transform: `translate(${rect.left + (options?.offsetX || 0)}px, ${rect.top + (options?.offsetY || 0)}px) scale(${scaleX}, ${scaleY})`, + transform: `translate(${xs[0]}px, ${ys[0]}px) scale(${scaleX}, ${scaleY})`, }); - dragLabel.style.transform = `translate(${rect.left + (options?.offsetX || 0)}px, ${rect.top + (options?.offsetY || 0) - 20}px)`; + dragLabel.style.transform = `translate(${xs[0]}px, ${ys[0] - 20}px)`; if (docsToDrag.length) { const pdfBox = dragElement.getElementsByTagName('canvas'); @@ -543,8 +543,8 @@ export namespace DragManager { const moveVec = { x: x - lastPt.x, y: y - lastPt.y }; lastPt = { x, y }; - dragLabel.style.transform = `translate(${xs[0] + moveVec.x + (options?.offsetX || 0)}px, ${ys[0] + moveVec.y + (options?.offsetY || 0) - 20}px)`; - dragElements.map((dragElement, i) => (dragElement.style.transform = `translate(${(xs[i] += moveVec.x) + (options?.offsetX || 0)}px, ${(ys[i] += moveVec.y) + (options?.offsetY || 0)}px) scale(${scaleXs[i]}, ${scaleYs[i]})`)); + dragElements.map((dragElement, i) => (dragElement.style.transform = `translate(${(xs[i] += moveVec.x)}px, ${(ys[i] += moveVec.y)}px) scale(${scaleXs[i]}, ${scaleYs[i]})`)); + dragLabel.style.transform = `translate(${xs[0]}px, ${ys[0] - 20}px)`; }; const upHandler = (e: PointerEvent) => { clearTimeout(startWindowDragTimer); diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index e5de7a0c5..2671aea56 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -23,9 +23,9 @@ import React = require('react'); import { action, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; -import { Doc, WidthSym } from '../../fields/Doc'; +import { Doc, HeightSym, WidthSym } from '../../fields/Doc'; import { InkData, InkField, InkTool } from '../../fields/InkField'; -import { Cast, NumCast, RTFCast, StrCast } from '../../fields/Types'; +import { BoolCast, Cast, NumCast, RTFCast, StrCast } from '../../fields/Types'; import { TraceMobx } from '../../fields/util'; import { OmitKeys, returnFalse, setupMoveUpEvents } from '../../Utils'; import { CognitiveServices } from '../cognitive_services/CognitiveServices'; @@ -42,12 +42,13 @@ import { InkTangentHandles } from './InkTangentHandles'; import { DocComponentView } from './nodes/DocumentView'; import { FieldView, FieldViewProps } from './nodes/FieldView'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; +import { INK_MASK_SIZE } from './global/globalCssVariables.scss'; import './InkStroke.scss'; import Color = require('color'); @observer export class InkingStroke extends ViewBoxBaseComponent() { - static readonly MaskDim = 50000; // choose a really big number to make sure mask fits over container (which in theory can be arbitrarily big) + static readonly MaskDim = INK_MASK_SIZE; // choose a really big number to make sure mask fits over container (which in theory can be arbitrarily big) public static LayoutString(fieldStr: string) { return FieldView.LayoutString(InkingStroke, fieldStr); } @@ -353,9 +354,18 @@ export class InkingStroke extends ViewBoxBaseComponent() { const endMarker = StrCast(this.layoutDoc.strokeEndMarker); const markerScale = NumCast(this.layoutDoc.strokeMarkerScale, 1); const closed = InkingStroke.IsClosed(inkData); - const fillColor = StrCast(this.layoutDoc.fillColor, 'transparent'); + const isInkMask = BoolCast(this.layoutDoc.isInkMask); + const fillColor = isInkMask ? '#aaaaaa' : StrCast(this.layoutDoc.fillColor, 'transparent'); const strokeColor = !closed && fillColor && fillColor !== 'transparent' ? fillColor : StrCast(this.layoutDoc.color); + // bcz: Hack!! Not really sure why, but having fractional values for width/height of mask ink strokes causes the dragging clone (see DragManager) to be offset from where it should be. + if (isInkMask && (this.layoutDoc[WidthSym]() !== Math.round(this.layoutDoc[WidthSym]()) || this.layoutDoc[HeightSym]() !== Math.round(this.layoutDoc[HeightSym]()))) { + setTimeout(() => { + this.layoutDoc._width = Math.round(NumCast(this.layoutDoc[WidthSym]())); + this.layoutDoc._height = Math.round(NumCast(this.layoutDoc[HeightSym]())); + }); + } + // Visually renders the polygonal line made by the user. const inkLine = InteractionUtils.CreatePolyline( inkData, @@ -430,7 +440,7 @@ export class InkingStroke extends ViewBoxBaseComponent() { className="inkStroke" style={{ transform: this.props.Document.isInkMask ? `translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined, - mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? 'multiply' : 'unset', + // mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? 'multiply' : 'unset', cursor: this.props.isSelected() ? 'default' : undefined, }} {...(!closed ? interactions : {})}> @@ -467,7 +477,7 @@ export class InkingStroke extends ViewBoxBaseComponent() { className="inkStroke" style={{ transform: this.props.Document.isInkMask ? `translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined, - mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? 'multiply' : 'unset', + mixBlendMode: 'unset', cursor: this.props.isSelected() ? 'default' : undefined, position: 'absolute', }} @@ -489,6 +499,9 @@ export function SetActiveBezierApprox(bezier: string): void { export function SetActiveInkColor(value: string) { ActiveInkPen() && (ActiveInkPen().activeInkColor = value); } +export function SetActiveIsInkMask(value: boolean) { + ActiveInkPen() && (ActiveInkPen().activeIsInkMask = value); +} export function SetActiveFillColor(value: string) { ActiveInkPen() && (ActiveInkPen().activeFillColor = value); } @@ -513,6 +526,9 @@ export function ActiveInkColor(): string { export function ActiveFillColor(): string { return StrCast(ActiveInkPen()?.activeFillColor, ''); } +export function ActiveIsInkMask(): boolean { + return BoolCast(ActiveInkPen()?.activeIsInkMask, false); +} export function ActiveArrowStart(): string { return StrCast(ActiveInkPen()?.activeArrowStart, ''); } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index e96f65548..c166594e5 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -457,6 +457,7 @@ export class MainView extends React.Component { fa.faVolumeDown, fa.faSquareRootAlt, fa.faVolumeMute, + fa.faUserCircle, ] ); this.initAuthenticationRouters(); diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 8432619de..eb55650e4 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -1025,7 +1025,7 @@ export class CollectionFreeFormViewChrome extends React.Component - Toggle View All} placement="bottom"> + Frame number} placement="bottom">
{ pinDoc.presActiveFrame = pinProps?.activeFrame; pinDoc.title = doc.title + ' (move)'; pinDoc.presMovement = PresMovement.Pan; - if (pinDoc.isInkMask) { - pinDoc.presHideAfter = true; - pinDoc.presHideBefore = true; - pinDoc.presMovement = PresMovement.None; - } + } + if (pinDoc.isInkMask) { + pinDoc.presHideAfter = true; + pinDoc.presHideBefore = true; + pinDoc.presMovement = PresMovement.None; } if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true; PresBox.Instance?._selectedArray.clear(); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 3d85d32a0..ee01c341b 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -45,6 +45,7 @@ export interface PoolData { export interface ViewDefResult { ele: JSX.Element; bounds?: ViewDefBounds; + inkMask?: boolean; } function toLabel(target: FieldResult) { if (typeof target === 'number' || Number(target)) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss index 79e063f7f..010132aa5 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss @@ -1,4 +1,4 @@ -@import "../../global/globalCssVariables"; +@import '../../global/globalCssVariables'; .collectionfreeformview-none { position: inherit; @@ -20,8 +20,19 @@ pointer-events: none; } +.collectionfreeformview-mask { + mix-blend-mode: multiply; + z-index: 5000; + width: $INK_MASK_SIZE; + height: $INK_MASK_SIZE; + transform: translate($INK_MASK_SIZE_HALF, $INK_MASK_SIZE_HALF); + background-color: rgba(0, 0, 0, 0.7); + pointer-events: none; + position: absolute; +} + .collectionfreeformview-viewdef { - >.collectionFreeFormDocumentView-container { + > .collectionFreeFormDocumentView-container { pointer-events: none; .contentFittingDocumentDocumentView-previewDoc { @@ -210,13 +221,13 @@ } } - .collectionfreeformview>.jsx-parser { + .collectionfreeformview > .jsx-parser { position: inherit; height: 100%; width: 100%; } - >.jsx-parser { + > .jsx-parser { z-index: 0; } @@ -268,6 +279,6 @@ .pullpane-indicator { z-index: 99999; - background-color: rgba($color: #000000, $alpha: .4); + background-color: rgba($color: #000000, $alpha: 0.4); position: absolute; -} \ No newline at end of file +} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 82b377dfa..f3074543b 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -34,7 +34,7 @@ import { COLLECTION_BORDER_WIDTH } from '../../../views/global/globalCssVariable import { Timeline } from '../../animationtimeline/Timeline'; import { ContextMenu } from '../../ContextMenu'; import { GestureOverlay } from '../../GestureOverlay'; -import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke'; +import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke'; import { LightboxView } from '../../LightboxView'; import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView'; import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment, ViewSpecPrefix } from '../../nodes/DocumentView'; @@ -121,7 +121,10 @@ export class CollectionFreeFormView extends CollectionSubView ele.bounds && !ele.bounds.z).map(ele => ele.ele); + const viewsMask = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && ele.inkMask).map(ele => ele.ele); + const renderableEles = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && !ele.inkMask).map(ele => ele.ele); + if (viewsMask.length) renderableEles.push(
{viewsMask}
); + return renderableEles; } @computed get fitToContentVals() { return { @@ -579,6 +582,7 @@ export class CollectionFreeFormView extends CollectionSubView { render() { TraceMobx(); - const xshift = () => (this.props.Document.isInkMask && !this.props.LayoutTemplateString && !this.props.LayoutTemplate?.() ? InkingStroke.MaskDim : Math.abs(this.Xshift) <= 0.001 ? this.props.PanelWidth() : undefined); - const yshift = () => (this.props.Document.isInkMask && !this.props.LayoutTemplateString && !this.props.LayoutTemplate?.() ? InkingStroke.MaskDim : Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined); + const xshift = () => (Math.abs(this.Xshift) <= 0.001 ? this.props.PanelWidth() : undefined); + const yshift = () => (Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined); const isPresTreeElement: boolean = this.props.treeViewDoc?.type === DocumentType.PRES; const isButton: boolean = this.props.Document.type === DocumentType.FONTICON || this.props.Document._viewType === CollectionViewType.Linear; return ( @@ -1652,7 +1652,7 @@ export class DocumentView extends React.Component { ref={this.ContentRef} style={{ transition: this.props.dataTransition, - position: this.props.Document.isInkMask ? 'absolute' : undefined, + //position: this.props.Document.isInkMask ? 'absolute' : undefined, transform: isButton ? undefined : `translate(${this.centeringX}px, ${this.centeringY}px)`, width: isButton || isPresTreeElement ? '100%' : xshift() ?? `${(100 * (this.props.PanelWidth() - this.Xshift * 2)) / this.props.PanelWidth()}%`, height: diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index d3b95e25a..cb68c1ac3 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -1,6 +1,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; +import Color from 'color'; import { action, computed, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -21,7 +22,7 @@ import { DocComponent } from '../../DocComponent'; import { EditableView } from '../../EditableView'; import { GestureOverlay } from '../../GestureOverlay'; import { Colors } from '../../global/globalEnums'; -import { ActiveFillColor, ActiveInkColor, ActiveInkWidth, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke'; +import { ActiveFillColor, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth, SetActiveIsInkMask } from '../../InkingStroke'; import { InkTranscription } from '../../InkTranscription'; import { StyleProp } from '../../StyleProvider'; import { FieldView, FieldViewProps } from '.././FieldView'; @@ -498,8 +499,6 @@ export class FontIconBox extends DocComponent() {
); - const buttonText = StrCast(this.rootDoc.buttonText); - // TODO:glr Add label of button type let button: JSX.Element | null = this.defaultButton; @@ -508,7 +507,7 @@ export class FontIconBox extends DocComponent() { button = (
- {buttonText ?
{buttonText}
: null} + {StrCast(this.rootDoc.buttonText) ?
{StrCast(this.rootDoc.buttonText)}
: null} {label}
); @@ -563,11 +562,13 @@ export class FontIconBox extends DocComponent() { break; } - return !this.layoutDoc.toolTip || this.type === ButtonType.DropdownList || this.type === ButtonType.ColorButton || this.type === ButtonType.NumberButton || this.type === ButtonType.EditableText ? ( - button - ) : button !== null ? ( - {StrCast(this.layoutDoc.toolTip)}}>{button} - ) : null; + const retval = + !this.layoutDoc.toolTip || this.type === ButtonType.DropdownList || this.type === ButtonType.ColorButton || this.type === ButtonType.NumberButton || this.type === ButtonType.EditableText ? ( + button + ) : button !== null ? ( + {StrCast(this.layoutDoc.toolTip)}}>{button} + ) : null; + return retval; } } @@ -860,6 +861,21 @@ ScriptingGlobals.add(function setActiveTool(tool: string, checkResult?: boolean) } }); +// toggle: Set overlay status of selected document +ScriptingGlobals.add(function setIsInkMask(checkResult?: boolean) { + const selected = SelectionManager.Docs().lastElement(); + if (checkResult) { + if (selected?.type === DocumentType.INK) { + return BoolCast(selected.isInkMask) ? Colors.MEDIUM_BLUE : 'transparent'; + } + return ActiveIsInkMask() ? Colors.MEDIUM_BLUE : 'transparent'; + } + SetActiveIsInkMask(!ActiveIsInkMask()); + SelectionManager.Docs() + .filter(doc => doc.type === DocumentType.INK) + .map(doc => (doc.isInkMask = !doc.isInkMask)); +}); + // toggle: Set overlay status of selected document ScriptingGlobals.add(function setFillColor(color?: string, checkResult?: boolean) { const selected = SelectionManager.Docs().lastElement(); diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 3bbdce1e4..627f35e71 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -313,10 +313,15 @@ export class PresBox extends ViewBoxBaseComponent() { //it'll also execute the necessary actions if presentation is playing. public gotoDocument = action((index: number, from?: Doc, group?: boolean) => { Doc.UnBrushAllDocs(); + if (index >= 0 && index < this.childDocs.length) { this.rootDoc._itemIndex = index; const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; + if (activeItem.presActiveFrame !== undefined) { + const context = DocCast(DocCast(activeItem.presentationTargetDoc).context); + context && CollectionFreeFormViewChrome.gotoKeyFrame(context, NumCast(activeItem.presActiveFrame)); + } if (from?.mediaStopTriggerList && this.layoutDoc.presStatus !== PresStatus.Edit) { DocListCast(from.mediaStopTriggerList).forEach(this.stopTempMedia); } @@ -516,20 +521,26 @@ export class PresBox extends ViewBoxBaseComponent() { if (tagDoc === this.layoutDoc.presCollection) { tagDoc.opacity = 1; } else { - if (itemIndexes.length > 1 && curDoc.presHideBefore && curInd !== 0) { - } else if (curDoc.presHideBefore) { - if (index > this.itemIndex) { - tagDoc.opacity = 0; - } else if (index === this.itemIndex || !curDoc.presHideAfter) { + if (curDoc.presHideBefore) { + if (itemIndexes.length > 1 && curInd !== 0) { tagDoc.opacity = 1; + } else { + if (index > this.itemIndex) { + tagDoc.opacity = 0; + } else if (index === this.itemIndex || !curDoc.presHideAfter) { + tagDoc.opacity = 1; + } } } - if (itemIndexes.length > 1 && curDoc.presHideAfter && curInd !== itemIndexes.length - 1) { - } else if (curDoc.presHideAfter) { - if (index < this.itemIndex) { - tagDoc.opacity = 0; - } else if (index === this.itemIndex || !curDoc.presHideBefore) { + if (curDoc.presHideAfter) { + if (itemIndexes.length > 1 && curInd !== itemIndexes.length - 1) { tagDoc.opacity = 1; + } else { + if (index < this.itemIndex) { + tagDoc.opacity = 0; + } else if (index === this.itemIndex || !curDoc.presHideBefore) { + tagDoc.opacity = 1; + } } } } @@ -821,11 +832,6 @@ export class PresBox extends ViewBoxBaseComponent() { this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem); if (doc.presPinView || doc.presentationTargetDoc === this.layoutDoc.presCollection) setTimeout(() => this.updateCurrentPresentation(context), 0); else this.updateCurrentPresentation(context); - - if (this.activeItem.presActiveFrame !== undefined) { - const context = DocCast(DocCast(this.activeItem.presentationTargetDoc).context); - context && CollectionFreeFormViewChrome.gotoKeyFrame(context, NumCast(this.activeItem.presActiveFrame)); - } }; //Command click diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts index 114d5fc2f..a074098c1 100644 --- a/src/fields/InkField.ts +++ b/src/fields/InkField.ts @@ -1,22 +1,21 @@ -import { Bezier } from "bezier-js"; -import { createSimpleSchema, list, object, serializable } from "serializr"; -import { ScriptingGlobals } from "../client/util/ScriptingGlobals"; -import { Deserializable } from "../client/util/SerializationHelper"; -import { Copy, ToScriptString, ToString } from "./FieldSymbols"; -import { ObjectField } from "./ObjectField"; +import { Bezier } from 'bezier-js'; +import { createSimpleSchema, list, object, serializable } from 'serializr'; +import { ScriptingGlobals } from '../client/util/ScriptingGlobals'; +import { Deserializable } from '../client/util/SerializationHelper'; +import { Copy, ToScriptString, ToString } from './FieldSymbols'; +import { ObjectField } from './ObjectField'; // Helps keep track of the current ink tool in use. export enum InkTool { - None = "none", - Pen = "pen", - Highlighter = "highlighter", - Eraser = "eraser", - Stamp = "stamp", - Write = "write", - PresentationPin = 'presentationpin' + None = 'none', + Pen = 'pen', + Highlighter = 'highlighter', + Eraser = 'eraser', + Stamp = 'stamp', + Write = 'write', + PresentationPin = 'presentationpin', } - // Defines a point in an ink as a pair of x- and y-coordinates. export interface PointData { X: number; @@ -54,15 +53,16 @@ export interface HandleLine { } const pointSchema = createSimpleSchema({ - X: true, Y: true + X: true, + Y: true, }); const strokeDataSchema = createSimpleSchema({ pathData: list(object(pointSchema)), - "*": true + '*': true, }); -@Deserializable("ink") +@Deserializable('ink') export class InkField extends ObjectField { @serializable(list(object(strokeDataSchema))) readonly inkData: InkData; @@ -85,11 +85,11 @@ export class InkField extends ObjectField { } [ToScriptString]() { - return "new InkField([" + this.inkData.map(i => `{X: ${i.X}, Y: ${i.Y}`) + "])"; + return 'new InkField([' + this.inkData.map(i => `{X: ${i.X}, Y: ${i.Y}}`) + '])'; } [ToString]() { - return "InkField"; + return 'InkField'; } } -ScriptingGlobals.add("InkField", InkField); \ No newline at end of file +ScriptingGlobals.add('InkField', InkField); -- cgit v1.2.3-70-g09d2 From d8f2a45a95a14151d3484c3c14f72d217a818786 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 22 Aug 2022 12:50:52 -0400 Subject: made inkMasks animate when they are turned on /off. fixed inkStrokes to be interpolated when they have an activeFrame set. --- src/client/util/CurrentUserUtils.ts | 4 ++-- src/client/views/DocumentDecorations.tsx | 4 ++-- src/client/views/InkStrokeProperties.ts | 1 - src/client/views/InkingStroke.tsx | 22 +++++++++++++++---- .../CollectionFreeFormLayoutEngines.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.scss | 9 ++++++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 9 ++++---- src/client/views/nodes/DocumentView.tsx | 1 - src/client/views/nodes/button/FontIconBox.tsx | 25 +++++++++++----------- 9 files changed, 47 insertions(+), 30 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index f7d072d80..20c57c617 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -633,7 +633,7 @@ export class CurrentUserUtils { { title: "Left", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", scripts: {onClick:'{ return setAlignment("left", _readOnly_);}' }}, { title: "Center", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", scripts: {onClick:'{ return setAlignment("center", _readOnly_);}'} }, { title: "Right", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", scripts: {onClick:'{ return setAlignment("right", _readOnly_);}'} }, - { title: "NoLink", toolTip: "Auto Link", btnType: ButtonType.ToggleButton, icon: "link", scripts: {onClick:'{ return toggleNoAutoLinkAnchor(_readOnly_);}'}}, + { title: "NoLink", toolTip: "Auto Link", btnType: ButtonType.ToggleButton, icon: "link", scripts: {onClick:'{ return toggleNoAutoLinkAnchor(_readOnly_);}'}, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()'}}, { title: "Dictate",toolTip: "Dictate", btnType: ButtonType.ToggleButton, icon: "microphone", scripts: {onClick:'{ return toggleDictation(_readOnly_);}'}}, ]; } @@ -676,7 +676,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: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()'}, width: 20, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}}, - { title: "Num", icon: "", toolTip: "Frame Number", btnType: ButtonType.TextButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()', buttonText: 'selectedDocs()?.lastElement().currentFrame.toString()'}, width: 20, scripts: {}}, + { title: "Num", icon: "", toolTip: "Frame Number", btnType: ButtonType.TextButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()', buttonText: 'selectedDocs()?.lastElement().currentFrame.toString()'}, width: 20, scripts: {}}, { title: "Fwd", icon: "chevron-right", toolTip: "Next Animation Frame", btnType: ButtonType.ClickButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()'}, width: 20, scripts: { onClick: 'nextKeyFrame(_readOnly_)'}}, { title: "Fill", icon: "fill-drip", toolTip: "Background Fill Color",btnType: ButtonType.ColorButton, funcs: {hidden: '!SelectionManager_selectedDocType()'}, ignoreClick: true, width: 20, scripts: { script: 'return setBackgroundColor(value, _readOnly_)'}}, // Only when a document is selected { title: "Header", icon: "heading", toolTip: "Header Color", btnType: ButtonType.ColorButton, funcs: {hidden: '!SelectionManager_selectedDocType()'}, ignoreClick: true, scripts: { script: 'return setHeaderColor(value, _readOnly_)'}}, diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 6d1397395..ab77af0f4 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -203,8 +203,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P if (this._deleteAfterIconify) { views.forEach(iconView => { Doc.setNativeView(iconView.props.Document); - if (iconView.props.Document.isInkMask && iconView.props.Document.activeFrame !== undefined) { - iconView.props.Document.opacity = 0; // bcz: hacky ... allows inkMaks to be "turned off" without removing them from the collection which allows them to function properly in a presenation. + if (iconView.props.Document.activeFrame) { + iconView.props.Document.opacity = 0; // bcz: hacky ... allows inkMasks and other documents to be "turned off" without removing them from the animated collection which allows them to function properly in a presenation. } else { iconView.props.removeDocument?.(iconView.props.Document); } diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index b32c9d54c..1f5f16592 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -68,7 +68,6 @@ export class InkStrokeProperties { doc.x = oldXrange.coord + (newXrange.min - oldXrange.min) * ptsXscale; doc.y = oldYrange.coord + (newYrange.min - oldYrange.min) * ptsYscale; if (doc.activeFrame !== undefined) { - doc.data = ComputedField.MakeInterpolated('data', 'activeFrame', doc, NumCast(doc.activeFrame)); const findexed = Cast(doc[`data-indexed`], listSpec(InkField), []).slice(); findexed[NumCast(doc.activeFrame)] = new InkField(newPoints); doc[`data-indexed`] = new List(findexed); diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 52efbdfd7..520d40abf 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -25,7 +25,7 @@ import { action, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import { Doc, HeightSym, WidthSym } from '../../fields/Doc'; import { InkData, InkField, InkTool } from '../../fields/InkField'; -import { BoolCast, Cast, NumCast, RTFCast, StrCast } from '../../fields/Types'; +import { BoolCast, Cast, FieldValue, NumCast, RTFCast, StrCast } from '../../fields/Types'; import { TraceMobx } from '../../fields/util'; import { OmitKeys, returnFalse, setupMoveUpEvents } from '../../Utils'; import { CognitiveServices } from '../cognitive_services/CognitiveServices'; @@ -45,6 +45,9 @@ import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { INK_MASK_SIZE } from './global/globalCssVariables.scss'; import './InkStroke.scss'; import Color = require('color'); +import { ComputedField } from '../../fields/ScriptField'; +import { listSpec } from '../../fields/Schema'; +import { List } from '../../fields/List'; @observer export class InkingStroke extends ViewBoxBaseComponent() { @@ -56,7 +59,7 @@ export class InkingStroke extends ViewBoxBaseComponent() { return inkData && inkData.lastElement().X === inkData[0].X && inkData.lastElement().Y === inkData[0].Y; } private _handledClick = false; // flag denoting whether ink stroke has handled a psuedo-click onPointerUp so that the real onClick event can be stopPropagated - private _selDisposer?: IReactionDisposer; + private _disposers: { [key: string]: IReactionDisposer } = {}; @observable _nearestSeg?: number; // nearest Bezier segment along the ink stroke to the cursor (used for displaying the Add Point highlight) @observable _nearestT?: number; // nearest t value within the nearest Bezier segment " @@ -64,13 +67,24 @@ export class InkingStroke extends ViewBoxBaseComponent() { componentDidMount() { this.props.setContentView?.(this); - this._selDisposer = reaction( + this._disposers.activeFrame = reaction( + () => this.rootDoc.activeFrame !== undefined && !(ComputedField.WithoutComputed(() => FieldValue(this.rootDoc[this.fieldKey])) instanceof ComputedField), + () => { + const newPoints = Cast(this.rootDoc[this.fieldKey], InkField, null).inkData; + this.rootDoc[this.fieldKey] = ComputedField.MakeInterpolated(this.fieldKey, 'activeFrame', this.rootDoc, NumCast(this.rootDoc.activeFrame)); + const findexed = Cast(this.rootDoc[`data-indexed`], listSpec(InkField), []).slice(); + findexed[NumCast(this.rootDoc.activeFrame)] = new InkField(newPoints); + this.rootDoc[this.fieldKey + '-indexed'] = new List(findexed); + }, + { fireImmediately: true } + ); + this._disposers.selfDisper = reaction( () => this.props.isSelected(), // react to stroke being deselected by turning off ink handles selected => !selected && (InkStrokeProperties.Instance._controlButton = false) ); } componentWillUnmount() { - this._selDisposer?.(); + Object.keys(this._disposers).forEach(key => this._disposers[key]()); } // transform is the inherited screentolocal xf plus any scaling that was done to make the stroke diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index ee01c341b..b210e7d9a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -45,7 +45,7 @@ export interface PoolData { export interface ViewDefResult { ele: JSX.Element; bounds?: ViewDefBounds; - inkMask?: boolean; + inkMask?: number; //sort elements into either the mask layer (which has a mixedBlendMode appropriate for transparent masks), or the regular documents layer; -1 = no mask, 0 = mask layer but stroke is transprent (hidden), >0 = mask layer and not hidden } function toLabel(target: FieldResult) { if (typeof target === 'number' || Number(target)) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss index 010132aa5..d80fcdfc3 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss @@ -20,15 +20,20 @@ pointer-events: none; } +.collectionfreeformview-mask-empty, .collectionfreeformview-mask { - mix-blend-mode: multiply; z-index: 5000; width: $INK_MASK_SIZE; height: $INK_MASK_SIZE; transform: translate($INK_MASK_SIZE_HALF, $INK_MASK_SIZE_HALF); - background-color: rgba(0, 0, 0, 0.7); pointer-events: none; position: absolute; + background-color: transparent; + transition: background-color 1s ease 0s; +} +.collectionfreeformview-mask { + mix-blend-mode: multiply; + background-color: rgba(0, 0, 0, 0.7); } .collectionfreeformview-viewdef { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 0fd326091..1d518076a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -119,9 +119,9 @@ export class CollectionFreeFormView extends CollectionSubView ele.bounds && !ele.bounds.z && ele.inkMask).map(ele => ele.ele); - const renderableEles = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && !ele.inkMask).map(ele => ele.ele); - if (viewsMask.length) renderableEles.push(
{viewsMask}
); + const viewsMask = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && ele.inkMask !== -1).map(ele => ele.ele); + const renderableEles = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && ele.inkMask === -1).map(ele => ele.ele); + if (viewsMask.length) renderableEles.push(
(ele.inkMask ?? 0) > 0) ? '' : '-empty'}`}>{viewsMask}
); return renderableEles; } @computed get fitToContentVals() { @@ -251,7 +251,6 @@ export class CollectionFreeFormView extends CollectionSubView { ref={this.ContentRef} style={{ transition: this.props.dataTransition, - //position: this.props.Document.isInkMask ? 'absolute' : undefined, transform: isButton ? undefined : `translate(${this.centeringX}px, ${this.centeringY}px)`, width: isButton || isPresTreeElement ? '100%' : xshift() ?? `${(100 * (this.props.PanelWidth() - this.Xshift * 2)) / this.props.PanelWidth()}%`, height: diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index cb68c1ac3..fc5bf86f4 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -90,9 +90,11 @@ export class FontIconBox extends DocComponent() { @computed get label() { return StrCast(this.rootDoc.label, StrCast(this.rootDoc.title)); } - @computed get icon() { - return StrCast(this.dataDoc.icon, 'user') as any; - } + Icon = (color: string) => { + const icon = StrCast(this.dataDoc.icon, 'user') as any; + const trailsIcon = () => ; + return !icon ? null : icon === 'pres-trail' ? trailsIcon() : ; + }; @computed get dropdown() { return BoolCast(this.rootDoc.dropDownOpen); } @@ -228,7 +230,7 @@ export class FontIconBox extends DocComponent() { className={`menuButton ${this.type} ${active}`} style={{ color: color, backgroundColor: backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }} onClick={action(() => (this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen))}> - + {this.Icon(color)} {!this.label || !FontIconBox.GetShowLabels() ? null : (
{' '} @@ -384,7 +386,7 @@ export class FontIconBox extends DocComponent() { style={{ color: color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }} onClick={action(() => (this.colorPickerClosed = !this.colorPickerClosed))} onPointerDown={e => e.stopPropagation()}> - + {this.Icon(color)}
{label} {/* {dropdownCaret} */} @@ -436,7 +438,7 @@ export class FontIconBox extends DocComponent() { } else { return (
- + {this.Icon(color)} {label}
); @@ -453,7 +455,7 @@ export class FontIconBox extends DocComponent() { return (
- + {this.Icon(color)} {!this.label || !FontIconBox.GetShowLabels() ? null : (
{' '} @@ -506,7 +508,7 @@ export class FontIconBox extends DocComponent() { case ButtonType.TextButton: button = (
- + {this.Icon(color)} {StrCast(this.rootDoc.buttonText) ?
{StrCast(this.rootDoc.buttonText)}
: null} {label}
@@ -531,7 +533,7 @@ export class FontIconBox extends DocComponent() { case ButtonType.ToolButton: button = (
- + {this.Icon(color)} {label}
); @@ -543,16 +545,15 @@ export class FontIconBox extends DocComponent() { case ButtonType.ClickButton: button = (
- + {this.Icon(color)} {label}
); break; case ButtonType.MenuButton: - const trailsIcon = ; button = (
- {this.icon === 'pres-trail' ? trailsIcon : } + {this.Icon(color)} {menuLabel}
-- cgit v1.2.3-70-g09d2 From 440f5c296a599d5c3f9ed781b58c5b00da6edb5a Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 22 Aug 2022 17:18:10 -0400 Subject: added animation of background color, and streamlined/extended a variety of props-related things. --- src/client/util/CurrentUserUtils.ts | 6 +-- src/client/views/DocComponent.tsx | 14 ------- src/client/views/StyleProvider.tsx | 9 +++-- .../CollectionFreeFormLayoutEngines.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 33 ++++++++-------- .../views/nodes/CollectionFreeFormDocumentView.tsx | 30 ++++++++++++--- src/client/views/nodes/FieldView.tsx | 11 ++++-- .../views/nodes/formattedText/FormattedTextBox.tsx | 45 ++++++++++------------ src/fields/Doc.ts | 14 +++++++ src/fields/ScriptField.ts | 12 +++++- 10 files changed, 102 insertions(+), 74 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 20c57c617..17d58595c 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -235,9 +235,9 @@ export class CurrentUserUtils { const header = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { ...opts, title: "text", layout: "" + - ` ` + - " " + - ` Metadata` + + ` ` + + " " + + ` Metadata` + "" }, "header"); diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 280ca8a8c..886dd974b 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -132,20 +132,6 @@ export function ViewBoxAnnotatableComponent

() lookupField = (field: string) => ScriptCast((this.layoutDoc as any).lookupField)?.script.run({ self: this.layoutDoc, data: this.rootDoc, field: field }).result; - styleFromLayoutString = (scale: number) => { - const style: { [key: string]: any } = {}; - const divKeys = ['width', 'height', 'fontSize', 'transform', 'left', 'background', 'left', 'right', 'top', 'bottom', 'pointerEvents', 'position']; - const replacer = (match: any, expr: string, offset: any, string: any) => { - // bcz: this executes a script to convert a property expression string: { script } into a value - return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: 'number' })?.script.run({ self: this.rootDoc, this: this.layoutDoc, scale }).result?.toString() ?? ''; - }; - divKeys.map((prop: string) => { - const p = (this.props as any)[prop]; - typeof p === 'string' && (style[prop] = p?.replace(/{([^.'][^}']+)}/g, replacer)); - }); - return style; - }; - protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; @computed public get annotationKey() { diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 3bd4f5152..c0ba170c6 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -40,7 +40,8 @@ export enum StyleProp { JitterRotation = 'jitterRotation', // whether documents should be randomly rotated BorderPath = 'customBorder', // border path for document view FontSize = 'fontSize', // size of text font - FontFamily = 'fontFamily', // size of text font + FontFamily = 'fontFamily', // font family of text + FontWeight = 'fontWeight', // font weight of text } function darkScheme() { @@ -117,9 +118,11 @@ export function DefaultStyleProvider(doc: Opt, props: Opt(); @observable _marqueeRef = React.createRef(); @observable _marqueeViewRef = React.createRef(); - @observable _keyframeEditing = false; @observable ChildDrag: DocumentView | undefined; // child document view being dragged. needed to update drop areas of groups when a group item is dragged. @computed get views() { @@ -185,8 +184,6 @@ export class CollectionFreeFormView extends CollectionSubView (this._keyframeEditing = set); - getKeyFrameEditing = () => this._keyframeEditing; onBrowseClickHandler = () => this.props.onBrowseClick?.() || ScriptCast(this.layoutDoc.onBrowseClick); onChildClickHandler = () => this.props.childClickScript || ScriptCast(this.Document.onChildClick); onChildDoubleClickHandler = () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick); @@ -272,15 +269,15 @@ export class CollectionFreeFormView extends CollectionSubView NumCast(doc1.zIndex) - NumCast(doc2.zIndex)); zsorted.forEach((doc, index) => (doc.zIndex = doc.isInkMask ? 5000 : index + 1)); const dvals = CollectionFreeFormDocumentView.getValues(refDoc, NumCast(refDoc.activeFrame, 1000)); - const dropPos = this.Document._currentFrame !== undefined ? [dvals.x || 0, dvals.y || 0] : [NumCast(refDoc.x), NumCast(refDoc.y)]; + const dropPos = this.Document._currentFrame !== undefined ? [NumCast(dvals.x), NumCast(dvals.y)] : [NumCast(refDoc.x), NumCast(refDoc.y)]; for (let i = 0; i < docDragData.droppedDocuments.length; i++) { const d = docDragData.droppedDocuments[i]; const layoutDoc = Doc.Layout(d); if (this.Document._currentFrame !== undefined) { CollectionFreeFormDocumentView.setupKeyframes([d], NumCast(this.Document._currentFrame), false); const vals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.activeFrame, 1000)); - vals.x = x + (vals.x || 0) - dropPos[0]; - vals.y = y + (vals.y || 0) - dropPos[1]; + vals.x = x + NumCast(vals.x) - dropPos[0]; + vals.y = y + NumCast(vals.y) - dropPos[1]; vals._scrollTop = this.Document.editScrollProgressivize ? vals._scrollTop : undefined; CollectionFreeFormDocumentView.setValues(NumCast(this.Document._currentFrame), d, vals); } else { @@ -1317,22 +1314,22 @@ export class CollectionFreeFormView extends CollectionSubView { x: number; y: number; zIndex?: number; opacity?: number; highlight?: boolean; z: number; transition?: string } | undefined; + dataProvider?: (doc: Doc, replica: string) => { x: number; y: number; zIndex?: number; backgroundColor?: string; opacity?: number; highlight?: boolean; z: number; transition?: string } | undefined; sizeProvider?: (doc: Doc, replica: string) => { width: number; height: number } | undefined; renderCutoffProvider: (doc: Doc) => boolean; zIndex?: number; @@ -32,6 +32,7 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { @observer export class CollectionFreeFormDocumentView extends DocComponent() { public static animFields = ['_height', '_width', 'x', 'y', '_scrollTop', 'opacity']; // fields that are configured to be animatable using animation frames + public static animStringFields = ['backgroundColor']; // fields that are configured to be animatable using animation frames @observable _animPos: number[] | undefined = undefined; @observable _contentView: DocumentView | undefined | null; get displayName() { @@ -41,16 +42,19 @@ export class CollectionFreeFormDocumentView extends DocComponent, property: string) => { if (property === StyleProp.Opacity && doc === this.layoutDoc) return this.Opacity; // only change the opacity for this specific document, not its children + if (property === StyleProp.BackgroundColor) { + return this.BackgroundColor; // only change the opacity for this specific document, not its children + } return this.props.styleProvider?.(doc, props, property); }; @@ -77,6 +84,13 @@ export class CollectionFreeFormDocumentView extends DocComponent }); } + public static getStringValues(doc: Doc, time: number) { + return CollectionFreeFormDocumentView.animStringFields.reduce((p, val) => { + p[val] = Cast(`${val}-indexed`, listSpec('string'), [StrCast(doc[val])]).reduce((p, v, i) => ((i <= Math.round(time) && v !== undefined) || p === undefined ? v : p), undefined as any as string); + return p; + }, {} as { [val: string]: Opt }); + } + public static setValues(time: number, d: Doc, vals: { [val: string]: Opt }) { const timecode = Math.round(time); Object.keys(vals).forEach(val => { @@ -95,6 +109,10 @@ export class CollectionFreeFormDocumentView extends DocComponent { + const findexed = Cast(doc[`${val}-indexed`], listSpec('string'), null); + findexed?.length <= timecode + 1 && findexed.push(undefined as any as string); + }); }) ); setTimeout( @@ -142,6 +160,7 @@ export class CollectionFreeFormDocumentView extends DocComponent(numberRange(currTimecode + 1).map(t => (!doc.z && makeAppear && t < NumCast(doc.appearFrame) ? 0 : 1))); } CollectionFreeFormDocumentView.animFields.forEach(val => (doc[val] = ComputedField.MakeInterpolated(val, 'activeFrame', doc, currTimecode))); + CollectionFreeFormDocumentView.animStringFields.forEach(val => (doc[val] = ComputedField.MakeInterpolatedString(val, 'activeFrame', doc, currTimecode))); doc.activeFrame = ComputedField.MakeFunction('self.context?._currentFrame||0'); doc.dataTransition = 'inherit'; }); @@ -189,7 +208,6 @@ export class CollectionFreeFormDocumentView extends DocComponent number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal NOTE: Must also be added to DocumentViewInternalsProps onBrowseClick?: () => ScriptField | undefined; onKey?: (e: React.KeyboardEvent, fieldProps: FieldViewProps) => boolean | undefined; + pointerEvents?: () => Opt; // properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React) - pointerEvents?: () => Opt; + // See currentUserUtils headerTemplate for examples of creating text boxes from html which set some of these fields + // Also, see InkingStroke for examples of creating text boxes from render() methods which set some of these fields + backgroundColor?: string; + color?: string; fontSize?: number; height?: number; width?: number; - background?: string; - color?: string; xPadding?: number; yPadding?: number; + noSidebar?: boolean; + dontScale?: boolean; + dontSelectOnLoad?: boolean; // suppress selecting (e.g.,. text box) when loaded (and mark as not being associated with scrollTop document field) } @observer diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index f61533619..81ac45521 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -11,7 +11,7 @@ import { Fragment, Mark, Node, Slice } from 'prosemirror-model'; import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from 'prosemirror-state'; import { EditorView } from 'prosemirror-view'; import { DateField } from '../../../../fields/DateField'; -import { AclAdmin, AclAugment, AclEdit, AclReadonly, AclSelfEdit, DataSym, Doc, DocListCast, DocListCastAsync, Field, ForceServerWrite, HeightSym, Opt, UpdatingFromServer, WidthSym } from '../../../../fields/Doc'; +import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DataSym, Doc, DocListCast, DocListCastAsync, Field, ForceServerWrite, HeightSym, Opt, UpdatingFromServer, WidthSym } from '../../../../fields/Doc'; import { Id } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; import { PrefetchProxy } from '../../../../fields/Proxy'; @@ -35,6 +35,7 @@ import { SnappingManager } from '../../../util/SnappingManager'; import { undoBatch, UndoManager } from '../../../util/UndoManager'; import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView'; import { CollectionStackingView } from '../../collections/CollectionStackingView'; +import { CollectionTreeView } from '../../collections/CollectionTreeView'; import { ContextMenu } from '../../ContextMenu'; import { ContextMenuProps } from '../../ContextMenuItem'; import { ViewBoxAnnotatableComponent } from '../../DocComponent'; @@ -44,6 +45,7 @@ import { LightboxView } from '../../LightboxView'; import { AnchorMenu } from '../../pdf/AnchorMenu'; import { SidebarAnnos } from '../../SidebarAnnos'; import { StyleProp } from '../../StyleProvider'; +import { DocumentViewInternal } from '../DocumentView'; import { FieldView, FieldViewProps } from '../FieldView'; import { LinkDocPreview } from '../LinkDocPreview'; import { DashDocCommentView } from './DashDocCommentView'; @@ -61,25 +63,14 @@ import { schema } from './schema_rts'; import { SummaryView } from './SummaryView'; import applyDevTools = require('prosemirror-dev-tools'); import React = require('react'); -import { text } from 'body-parser'; -import { CollectionTreeView } from '../../collections/CollectionTreeView'; -import { DocumentViewInternal } from '../DocumentView'; const translateGoogleApi = require('translate-google-api'); -export interface FormattedTextBoxProps { - makeLink?: () => Opt; // bcz: hack: notifies the text document when the container has made a link. allows the text doc to react and setup a hyeprlink for any selected text - xPadding?: number; // used to override document's settings for xMargin --- see CollectionCarouselView - yPadding?: number; - noSidebar?: boolean; - dontScale?: boolean; - dontSelectOnLoad?: boolean; // suppress selecting the text box when loaded (and mark as not being associated with scrollTop document field) -} export const GoogleRef = 'googleDocId'; type PullHandler = (exportState: Opt, dataDoc: Doc) => void; @observer -export class FormattedTextBox extends ViewBoxAnnotatableComponent() { +export class FormattedTextBox extends ViewBoxAnnotatableComponent() { public static LayoutString(fieldStr: string) { return FieldView.LayoutString(FormattedTextBox, fieldStr); } @@ -1843,18 +1834,27 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent= 10 ? '-selected' : ''; - const styleFromString = this.styleFromLayoutString(scale); // this converts any expressions in the format string to style props. e.g., - return styleFromString?.height === '0px' ? null : ( + const styleFromLayoutString = Doc.styleFromLayoutString(this.rootDoc, this.layoutDoc, this.props, scale); // this converts any expressions in the format string to style props. e.g., + return styleFromLayoutString?.height === '0px' ? null : (

this.props.isContentActive() && e.stopPropagation()} style={{ - transform: this.props.dontScale ? undefined : `scale(${scale})`, - transformOrigin: this.props.dontScale ? undefined : 'top left', - width: this.props.dontScale ? undefined : `${100 / scale}%`, - height: this.props.dontScale ? undefined : `${100 / scale}%`, + ...(this.props.dontScale + ? {} + : { + transform: `scale(${scale})`, + transformOrigin: 'top left', + width: `${100 / scale}%`, + height: `${100 / scale}%`, + }), + transition: 'inherit', // overflowY: this.layoutDoc._autoHeight ? "hidden" : undefined, - ...styleFromString, + color: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color), + fontSize: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontSize), + fontFamily: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontFamily), + fontWeight: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontWeight), + ...styleFromLayoutString, }}>
Doc.AreProtosEqual(val, doc)) !== -1; } + export function styleFromLayoutString(rootDoc: Doc, layoutDoc: Doc, props: any, scale: number) { + const style: { [key: string]: any } = {}; + const divKeys = ['width', 'height', 'fontSize', 'transform', 'left', 'backgroundColor', 'left', 'right', 'top', 'bottom', 'pointerEvents', 'position']; + const replacer = (match: any, expr: string, offset: any, string: any) => { + // bcz: this executes a script to convert a property expression string: { script } into a value + return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: 'number' })?.script.run({ self: rootDoc, this: layoutDoc, scale }).result?.toString() ?? ''; + }; + divKeys.map((prop: string) => { + const p = props[prop]; + typeof p === 'string' && (style[prop] = p?.replace(/{([^.'][^}']+)}/g, replacer)); + }); + return style; + } + // prettier-ignore export function toIcon(doc?: Doc, isOpen?: boolean) { switch (StrCast(doc?.type)) { diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts index 68fb45987..0fd992d3b 100644 --- a/src/fields/ScriptField.ts +++ b/src/fields/ScriptField.ts @@ -9,7 +9,7 @@ import { Doc, Field, Opt } from './Doc'; import { Copy, Id, ToScriptString, ToString } from './FieldSymbols'; import { List } from './List'; import { ObjectField } from './ObjectField'; -import { Cast, NumCast } from './Types'; +import { Cast, NumCast, StrCast } from './Types'; import { Plugins } from './util'; function optional(propSchema: PropSchema) { @@ -199,6 +199,16 @@ export class ComputedField extends ScriptField { const setField = ScriptField.CompileScript(`setIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey}, value)`, { value: 'any' }, true, {}); return getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined; } + public static MakeInterpolatedString(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number) { + if (!doc[`${fieldKey}-indexed`]) { + const flist = new List(numberRange(curTimecode + 1).map(i => undefined) as any as string[]); + flist[curTimecode] = StrCast(doc[fieldKey]); + doc[`${fieldKey}-indexed`] = flist; + } + const getField = ScriptField.CompileScript(`getIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey})`, {}, true, {}); + const setField = ScriptField.CompileScript(`setIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey}, value)`, { value: 'any' }, true, {}); + return getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined; + } } export namespace ComputedField { let useComputed = true; -- cgit v1.2.3-70-g09d2 From 267bb35a555ac0f8b67041346213d9a06386785f Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 23 Aug 2022 11:57:58 -0400 Subject: added color to animated properties. changed doc decorations to stop before menu bar. changed color of tab bar so that doc decorations is visible when overlapping. --- src/client/views/InkingStroke.tsx | 3 ++- src/client/views/MainView.tsx | 4 ++-- src/client/views/_nodeModuleOverrides.scss | 3 ++- .../CollectionFreeFormLayoutEngines.tsx | 1 + .../collectionFreeForm/CollectionFreeFormView.tsx | 14 ++++++++++-- .../views/nodes/CollectionFreeFormDocumentView.tsx | 25 +++++++++++++--------- 6 files changed, 34 insertions(+), 16 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx') diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 520d40abf..ceaabd0e1 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -48,6 +48,7 @@ import Color = require('color'); import { ComputedField } from '../../fields/ScriptField'; import { listSpec } from '../../fields/Schema'; import { List } from '../../fields/List'; +import { StyleProp } from './StyleProvider'; @observer export class InkingStroke extends ViewBoxBaseComponent() { @@ -370,7 +371,7 @@ export class InkingStroke extends ViewBoxBaseComponent() { const closed = InkingStroke.IsClosed(inkData); const isInkMask = BoolCast(this.layoutDoc.isInkMask); const fillColor = isInkMask ? '#aaaaaa' : StrCast(this.layoutDoc.fillColor, 'transparent'); - const strokeColor = !closed && fillColor && fillColor !== 'transparent' ? fillColor : StrCast(this.layoutDoc.color); + const strokeColor = !closed && fillColor && fillColor !== 'transparent' ? fillColor : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color) ?? StrCast(this.layoutDoc.color); // bcz: Hack!! Not really sure why, but having fractional values for width/height of mask ink strokes causes the dragging clone (see DragManager) to be offset from where it should be. if (isInkMask && (this.layoutDoc[WidthSym]() !== Math.round(this.layoutDoc[WidthSym]()) || this.layoutDoc[HeightSym]() !== Math.round(this.layoutDoc[HeightSym]()))) { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index d2b0e10c1..06be4d194 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -981,7 +981,7 @@ export class MainView extends React.Component { - + {this._hideUI ? null : } {LinkDescriptionPopup.descriptionPopup ? : null} @@ -994,7 +994,7 @@ export class MainView extends React.Component { default: return ( <> -
+
{this.mainDashboardArea} diff --git a/src/client/views/_nodeModuleOverrides.scss b/src/client/views/_nodeModuleOverrides.scss index 17eff022f..b1cce8705 100644 --- a/src/client/views/_nodeModuleOverrides.scss +++ b/src/client/views/_nodeModuleOverrides.scss @@ -44,7 +44,8 @@ div .lm_header { position: absolute; width: calc(100% - 60px); overflow: scroll; - background: $dark-gray; + background: #6b6b6b6b; //$dark-gray; + border-radius: 5px; } .lm_tab { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index d84717b95..89cc22d07 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -35,6 +35,7 @@ export interface PoolData { width?: number; height?: number; backgroundColor?: string; + color?: string; opacity?: number; transition?: string; highlight?: boolean; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 0234d303f..03beaf65e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1318,12 +1318,13 @@ export class CollectionFreeFormView extends CollectionSubView { x: number; y: number; zIndex?: number; backgroundColor?: string; opacity?: number; highlight?: boolean; z: number; transition?: string } | undefined; + dataProvider?: (doc: Doc, replica: string) => { x: number; y: number; zIndex?: number; color?: string; backgroundColor?: string; opacity?: number; highlight?: boolean; z: number; transition?: string } | undefined; sizeProvider?: (doc: Doc, replica: string) => { width: number; height: number } | undefined; renderCutoffProvider: (doc: Doc) => boolean; zIndex?: number; @@ -32,12 +32,14 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { @observer export class CollectionFreeFormDocumentView extends DocComponent() { public static animFields = ['_height', '_width', 'x', 'y', '_scrollTop', 'opacity']; // fields that are configured to be animatable using animation frames - public static animStringFields = ['backgroundColor']; // fields that are configured to be animatable using animation frames + public static animStringFields = ['backgroundColor', 'color']; // fields that are configured to be animatable using animation frames @observable _animPos: number[] | undefined = undefined; @observable _contentView: DocumentView | undefined | null; get displayName() { + // this makes mobx trace() statements more descriptive return 'CollectionFreeFormDocumentView(' + this.rootDoc.title + ')'; - } // this makes mobx trace() statements more descriptive + } + get transform() { return `translate(${this.X}px, ${this.Y}px) rotate(${NumCast(this.Document.jitterRotation, this.props.jitterRotation)}deg)`; } @@ -56,12 +58,12 @@ export class CollectionFreeFormDocumentView extends DocComponent; - } @computed get dataProvider() { return this.props.dataProvider?.(this.props.Document, this.props.replica); } @@ -70,10 +72,13 @@ export class CollectionFreeFormDocumentView extends DocComponent, property: string) => { - if (property === StyleProp.Opacity && doc === this.layoutDoc) return this.Opacity; // only change the opacity for this specific document, not its children - if (property === StyleProp.BackgroundColor) { - return this.BackgroundColor; // only change the opacity for this specific document, not its children - } + if (doc === this.layoutDoc) + // prettier-ignore + switch (property) { + case StyleProp.Opacity: return this.Opacity; // only change the opacity for this specific document, not its children + case StyleProp.BackgroundColor: return this.BackgroundColor; + case StyleProp.Color: return this.Color; + } return this.props.styleProvider?.(doc, props, property); }; -- cgit v1.2.3-70-g09d2