From 4c768162e0436115a05b9c8b0e4d837d626d45ba Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 30 Oct 2024 18:54:52 -0400
Subject: reworked how context menu buttons for ink and text work. added
 disableMixBlend for making transparent docs not use 'multiply'.

---
 src/client/views/nodes/FontIconBox/FontIconBox.tsx | 186 ++++++++++++---------
 1 file changed, 104 insertions(+), 82 deletions(-)

(limited to 'src/client/views/nodes/FontIconBox/FontIconBox.tsx')

diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index b45774a75..8c138c2ee 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -4,7 +4,7 @@ import { Button, ColorPicker, Dropdown, DropdownType, IconButton, IListItemProps
 import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { ClientUtils, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../ClientUtils';
+import { ClientUtils, DashColor, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../ClientUtils';
 import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc';
 import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { emptyFunction } from '../../../../Utils';
@@ -21,6 +21,8 @@ import { FieldView, FieldViewProps } from '../FieldView';
 import { OpenWhere } from '../OpenWhere';
 import './FontIconBox.scss';
 import TrailsIcon from './TrailsIcon';
+import { InkTool } from '../../../../fields/InkField';
+import { ScriptField } from '../../../../fields/ScriptField';
 
 export enum ButtonType {
     TextButton = 'textBtn',
@@ -126,7 +128,8 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
                 background={SnappingManager.userBackgroundColor}
                 numberDropdownType={type}
                 showPlusMinus={false}
-                tooltip={this.label}
+                formLabel={(StrCast(this.Document.title).startsWith(' ') ? '\u00A0' : '') + StrCast(this.Document.title)}
+                tooltip={StrCast(this.Document.toolTip, this.label)}
                 type={Type.PRIM}
                 min={NumCast(this.dataDoc.numBtnMin, 0)}
                 max={NumCast(this.dataDoc.numBtnMax, 100)}
@@ -148,65 +151,80 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
         return false;
     };
 
+    /**
+     * Displays custom dropdown menu for fonts -- this is a HACK -- fix for generality, don't copy
+     */
+    handleFontDropdown = (script: () => string, buttonList: string[]) => {
+        // text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
+        return {
+            buttonList,
+            jsx: undefined,
+            selectedVal: script(),
+            getStyle: (val: string) => ({ fontFamily: val }),
+        };
+    };
+    /**
+     * Displays custom dropdown menu for view selection -- this is a HACK -- fix for generality, don't copy
+     */
+    handleViewDropdown = (script: ScriptField, buttonList: string[]) => {
+        const selected = Array.from(script?.script.run({ _readOnly_: true }).result as Doc[]);
+        const noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Card, CollectionViewType.Carousel3D, CollectionViewType.Carousel, CollectionViewType.Stacking, CollectionViewType.NoteTaking];
+        return selected.length === 1 && selected[0].type === DocumentType.COL
+            ? {
+                  buttonList: buttonList.filter(value => !Doc.noviceMode || !noviceList.length || noviceList.includes(value as CollectionViewType)),
+                  getStyle: undefined,
+                  selectedVal: StrCast(selected[0]._type_collection),
+              }
+            : {
+                  jsx: selected.length ? (
+                      <Popup
+                          icon={<FontAwesomeIcon size="1x" icon={selected.length > 1 ? 'caret-down' : (Doc.toIcon(selected.lastElement()) as IconProp)} />}
+                          text={selected.length === 1 ? ClientUtils.cleanDocumentType(StrCast(selected[0].type) as DocumentType) : selected.length + ' selected'}
+                          type={Type.TERT}
+                          color={SnappingManager.userColor}
+                          background={SnappingManager.userVariantColor}
+                          popup={<SelectedDocView selectedDocs={selected} />}
+                          fillWidth
+                      />
+                  ) : (
+                      <Button
+                          text={`${Doc.ActiveTool === InkTool.None ? 'Text box' : Doc.ActiveInk} defaults`} //
+                          type={Type.TERT}
+                          color={SnappingManager.userColor}
+                          background={SnappingManager.userVariantColor}
+                          fillWidth
+                          inactive
+                      />
+                  ),
+              };
+    };
+
     /**
      * Dropdown list
      */
     @computed get dropdownListButton() {
         const script = ScriptCast(this.Document.script);
-
-        let noviceList: string[] = [];
-        let text: string | undefined;
-        let getStyle: (val: string) => { [key: string]: string } = () => ({});
-        let icon: IconProp = 'caret-down';
-        const isViewDropdown = script?.script.originalScript.startsWith('{ return setView');
-        if (isViewDropdown) {
-            const selected = Array.from(script?.script.run({ _readOnly_: true }).result as Doc[]);
-            // const selected = DocumentView.SelectedDocs();
-            if (selected.lastElement()) {
-                if (StrCast(selected.lastElement().type) === DocumentType.COL) {
-                    text = StrCast(selected.lastElement()._type_collection);
-                } else {
-                    if (selected.length > 1) {
-                        text = selected.length + ' selected';
-                    } else {
-                        text = ClientUtils.cleanDocumentType(StrCast(selected.lastElement().type) as DocumentType, '' as CollectionViewType);
-                        icon = Doc.toIcon(selected.lastElement());
-                    }
-                    return (
-                        <Popup
-                            icon={<FontAwesomeIcon size="1x" icon={icon} />}
-                            text={text}
-                            type={Type.TERT}
-                            color={SnappingManager.userColor}
-                            background={SnappingManager.userVariantColor}
-                            popup={<SelectedDocView selectedDocs={selected} />}
-                            fillWidth
-                        />
-                    );
-                }
-            } else {
-                return <Button text="None Selected" type={Type.TERT} color={SnappingManager.userColor} background={SnappingManager.userVariantColor} fillWidth inactive />;
-            }
-            noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Card, CollectionViewType.Carousel3D, CollectionViewType.Carousel, CollectionViewType.Stacking, CollectionViewType.NoteTaking];
-        } else {
-            text = script?.script.run({ this: this.Document, value: '', _readOnly_: true }).result as string;
-            // text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
-            if (this.Document.title === 'Font') getStyle = (val: string) => ({ fontFamily: val }); // bcz: major hack to style the font dropdown items --- needs to become part of the dropdown's metadata
-        }
+        const selectedFunc = () => script?.script.run({ this: this.Document, value: '', _readOnly_: true }).result as string;
+        const { buttonList, selectedVal, getStyle, jsx } = (() => {
+            switch (this.Document.title) {
+                case 'Font':         return this.handleFontDropdown(selectedFunc, this.buttonList);
+                case 'Perspective':  return this.handleViewDropdown(script, this.buttonList);
+                default:             return { buttonList: this.buttonList, selectedVal: selectedFunc(), jsx: undefined, getStyle: undefined };
+            } // prettier-ignore
+        })();
+        if (jsx) return jsx;
 
         // Get items to place into the list
-        const list: IListItemProps[] = this.buttonList
-            .filter(value => !Doc.noviceMode || !noviceList.length || noviceList.includes(value))
-            .map(value => ({
-                text: typeof value === 'string' ? value.charAt(0).toUpperCase() + value.slice(1) : StrCast(DocCast(value)?.title),
-                val: value,
-                style: getStyle(value),
-                // shortcut: '#',
-            }));
+        const list: IListItemProps[] = buttonList.map(value => ({
+            text: typeof value === 'string' ? value.charAt(0).toUpperCase() + value.slice(1) : StrCast(DocCast(value)?.title),
+            val: value,
+            style: getStyle?.(value),
+            // shortcut: '#',
+        }));
 
         return (
             <Dropdown
-                selectedVal={text}
+                selectedVal={selectedVal}
                 setSelectedVal={undoable(value => script.script.run({ this: this.Document, value }), `dropdown select ${this.label}`)}
                 color={SnappingManager.userColor}
                 background={SnappingManager.userVariantColor}
@@ -215,7 +233,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
                 dropdownType={DropdownType.SELECT}
                 onItemDown={this.dropdownItemDown}
                 items={list}
-                tooltip={this.label}
+                tooltip={StrCast(this.Document.toolTip, this.label)}
                 fillWidth
             />
         );
@@ -235,51 +253,53 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
         const tooltip: string = StrCast(this.Document.toolTip);
 
         return (
-            <ColorPicker
-                setSelectedColor={value => {
-                    if (!this.colorBatch) this.colorBatch = UndoManager.StartBatch(`Set ${tooltip} color`);
-                    this.colorScript?.script.run({ this: this.Document, value: value, _readOnly_: false });
-                }}
-                setFinalColor={value => {
-                    this.colorScript?.script.run({ this: this.Document, value: value, _readOnly_: false });
-                    this.colorBatch?.end();
-                    this.colorBatch = undefined;
-                }}
-                defaultPickerType="Classic"
-                selectedColor={curColor}
-                type={Type.PRIM}
-                color={color}
-                background={SnappingManager.userBackgroundColor}
-                icon={this.Icon(color) ?? undefined}
-                tooltip={tooltip}
-                label={this.label}
-            />
+            <div
+                onPointerDown={e => {
+                    e.stopPropagation();
+                }}>
+                <ColorPicker
+                    setSelectedColor={value => {
+                        if (!this.colorBatch) this.colorBatch = UndoManager.StartBatch(`Set ${tooltip} color`);
+                        this.colorScript?.script.run({ this: this.Document, value: value, _readOnly_: false });
+                    }}
+                    setFinalColor={value => {
+                        this.colorScript?.script.run({ this: this.Document, value: value, _readOnly_: false });
+                        this.colorBatch?.end();
+                        this.colorBatch = undefined;
+                    }}
+                    defaultPickerType="Classic"
+                    selectedColor={curColor}
+                    type={Type.PRIM}
+                    color={color}
+                    background={SnappingManager.userBackgroundColor}
+                    icon={this.Icon(color) ?? undefined}
+                    tooltip={tooltip}
+                    label={this.label}
+                />
+            </div>
         );
     }
     @computed get multiToggleButton() {
-        // Determine the type of toggle button
-        const tooltip: string = StrCast(this.Document.toolTip);
+        const tooltip = StrCast(this.Document.toolTip);
 
         const script = ScriptCast(this.Document.onClick)?.script;
         const toggleStatus = script?.run({ this: this.Document, value: undefined, _readOnly_: true }).result as boolean;
 
-        // Colors
         const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color) as string;
-        const background = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor) as string;
         const items = DocListCast(this.dataDoc.data);
         const selectedItems = items.filter(itemDoc => ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, value: undefined, _readOnly_: true }).result).map(item => StrCast(item.toolType));
 
         return (
             <MultiToggle
-                tooltip={`Toggle ${tooltip}`}
+                tooltip={`Click to Toggle ${tooltip} or select new option`}
                 type={Type.PRIM}
                 color={color}
-                background={background === SnappingManager.userBackgroundColor ? undefined : background}
+                background={undefined}
                 multiSelect={true}
                 onPointerDown={e => script && !toggleStatus && setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => script.run({ this: this.Document, value: undefined, _readOnly_: false }))}
                 isToggle={false}
                 toggleStatus={toggleStatus}
-                label={this.label}
+                label={selectedItems.length === 1 ? selectedItems[0] : this.label}
                 items={items.map(item => ({
                     icon: <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={StrCast(item.icon) as IconProp} color={color} />,
                     tooltip: StrCast(item.toolTip),
@@ -292,7 +312,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
                     //   it would be better to pas the 'added' flag to the callback script, but our script generator from currentUserUtils makes it hard to define
                     //   arbitrary parameter variables (but it could be done as a special case or with additional effort when creating the sript)
                     const itemsChanged = items.filter(item => (val instanceof Array ? val.includes(item.toolType as string | number) : item.toolType === val));
-                    itemsChanged.forEach(itemDoc => ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, _added_: added, itemDoc, _readOnly_: false }));
+                    itemsChanged.forEach(itemDoc => ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, _added_: added, value: toggleStatus, itemDoc, _readOnly_: false }));
                 }}
             />
         );
@@ -310,17 +330,19 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
         const toggleStatus = (script?.script.run({ this: this.Document, value: undefined, _readOnly_: true }).result as boolean) ?? false;
         // Colors
         const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color) as string;
-        // const backgroundColor = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor);
 
+        // bcz: ink shapes are tri-state - off, one-shot, and on.  Need to update Toggle buttons to allow this and update currentUserUtils to set the tri-state on the Doc
+        // in the meantime, if the button matches a tool type that is not locked, we want to set the background color to something distinct.
+        const inkShapeHack = ((this.Document.toolType && this.Document.toolType === SnappingManager.InkShape) || this.Document.toolType === Doc.ActiveTool) && !SnappingManager.KeepGestureMode;
         return (
             <Toggle
                 tooltip={`Toggle ${tooltip}`}
                 toggleType={ToggleType.BUTTON}
-                type={Type.PRIM}
+                type={inkShapeHack ? Type.TERT : Type.PRIM}
                 toggleStatus={toggleStatus}
                 text={buttonText}
                 color={color}
-                // background={SnappingManager.userBackgroundColor}
+                background={inkShapeHack ? DashColor(SnappingManager.userBackgroundColor).darken(0.05).toString() : undefined}
                 icon={this.Icon(color)!}
                 label={this.label}
                 onPointerDown={e =>
-- 
cgit v1.2.3-70-g09d2

