From 707038795f7129d30a1d4ed5f2311f18a282fecb Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 24 Aug 2021 02:30:06 -0400
Subject: fixed following link from ink stroke.  made interacting with
 annotatable documents consistent when selections are cleared by clicking. 
 fixed undo for webboxes.  fixed limiting size of links button.

---
 src/client/views/nodes/WebBox.tsx | 40 ++++++++++++++++-----------------------
 1 file changed, 16 insertions(+), 24 deletions(-)

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

diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 2ff41c73a..0c32a30db 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -44,7 +44,7 @@ const WebDocument = makeInterface(documentSchema);
 export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, WebDocument>(WebDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); }
     public static openSidebarWidth = 250;
-    private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void);
+    private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
     private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
     private _outerRef: React.RefObject<HTMLDivElement> = React.createRef();
     private _disposers: { [name: string]: IReactionDisposer } = {};
@@ -52,16 +52,16 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     private _keyInput = React.createRef<HTMLInputElement>();
     private _initialScroll: Opt<number>;
     private _sidebarRef = React.createRef<SidebarAnnos>();
-    @observable private _urlHash: string = "";
     @observable private _scrollTimer: any;
     @observable private _overlayAnnoInfo: Opt<Doc>;
     @observable private _marqueeing: number[] | undefined;
-    @observable private _url: string = "hello";
     @observable private _isAnnotating = false;
     @observable private _iframeClick: HTMLIFrameElement | undefined = undefined;
     @observable private _iframe: HTMLIFrameElement | null = null;
     @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
     @observable private _scrollHeight = 1500;
+    @computed get _url() { return this.webField?.toString() || ""; }
+    @computed get _urlHash() { return this._url ? WebBox.urlHash(this._url) + "" : ""; }
     @computed get scrollHeight() { return this._scrollHeight; }
     @computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); }
     @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.textInlineAnnotations); }
@@ -82,9 +82,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
 
         runInAction(() => {
-            this._url = this.webField?.toString() || "";
-            this._urlHash = WebBox.urlHash(this._url) + "";
-            this._annotationKey = this._urlHash + "-annotations";
+            this._annotationKeySuffix = () => this._urlHash + "-annotations";
             // bcz: need to make sure that doc.data-annotations points to the currently active web page's annotations (this could/should be when the doc is created)
             this.dataDoc[this.fieldKey + "-annotations"] = ComputedField.MakeFunction(`copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"`);
             this.dataDoc[this.fieldKey + "-sidebar"] = ComputedField.MakeFunction(`copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"`);
@@ -214,6 +212,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!);
         const scale = (this.props.scaling?.() || 1) * mainContBounds.scale;
         const word = getWordAtPoint(e.target, e.clientX, e.clientY);
+        this._setPreviewCursor?.(e.clientX, e.clientY, false, true);
+        MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
         this._marqueeing = [e.clientX * scale + mainContBounds.translateX,
         e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale];
         if (word) {
@@ -306,9 +306,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), []);
         if (future.length) {
             this.dataDoc[this.fieldKey + "-history"] = new List<string>([...history, this._url]);
-            this.dataDoc[this.fieldKey] = new WebField(new URL(this._url = future.pop()!));
-            this._urlHash = WebBox.urlHash(this._url) + "";
-            this._annotationKey = this._urlHash + "-annotations";
+            this.dataDoc[this.fieldKey] = new WebField(new URL(future.pop()!));
             return true;
         }
         return false;
@@ -321,9 +319,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (history.length) {
             if (future === undefined) this.dataDoc[this.fieldKey + "-future"] = new List<string>([this._url]);
             else this.dataDoc[this.fieldKey + "-future"] = new List<string>([...future, this._url]);
-            this.dataDoc[this.fieldKey] = new WebField(new URL(this._url = history.pop()!));
-            this._urlHash = WebBox.urlHash(this._url) + "";
-            this._annotationKey = this._urlHash + "-annotations";
+            this.dataDoc[this.fieldKey] = new WebField(new URL(history.pop()!));
             return true;
         }
         return false;
@@ -342,17 +338,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"));
             const url = this.webField?.toString();
             if (url && !preview) {
-                if (history === undefined) {
-                    this.dataDoc[this.fieldKey + "-history"] = new List<string>([url]);
-                } else {
-                    this.dataDoc[this.fieldKey + "-history"] = new List<string>([...history, url]);
-                }
+                this.dataDoc[this.fieldKey + "-history"] = new List<string>([...(history || []), url]);
                 this.layoutDoc._scrollTop = 0;
                 future && (future.length = 0);
             }
-            this._url = newUrl;
-            this._urlHash = WebBox.urlHash(this._url) + "";
-            this._annotationKey = this._urlHash + "-annotations";
             if (!preview) this.dataDoc[this.fieldKey] = new WebField(new URL(newUrl));
         } catch (e) {
             console.log("WebBox URL error:" + this._url);
@@ -413,15 +402,18 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @action
     onMarqueeDown = (e: React.PointerEvent) => {
         if (!e.altKey && e.button === 0 && this.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
-            this._marqueeing = [e.clientX, e.clientY];
-            this.props.select(false);
+            setupMoveUpEvents(this, e, action(e => {
+                MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
+                this._marqueeing = [e.clientX, e.clientY];
+                return true;
+            }), returnFalse, () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), false);
         }
     }
     @action finishMarquee = (x?: number, y?: number) => {
         this._marqueeing = undefined;
         this._isAnnotating = false;
         this._iframeClick = undefined;
-        x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false);
+        x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false, false);
     }
 
     @computed
@@ -511,7 +503,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @computed get SidebarShown() { return this._showSidebar || this.layoutDoc._showSidebar ? true : false; }
 
     showInfo = action((anno: Opt<Doc>) => this._overlayAnnoInfo = anno);
-    setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => this._setPreviewCursor = func;
+    setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => this._setPreviewCursor = func;
     panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
     panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
     scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop));
-- 
cgit v1.2.3-70-g09d2


From 5867ca16f8a8beaf7daf754ee5c42a5cc249346f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 24 Aug 2021 14:28:13 -0400
Subject: fixed undo for dragging docs.  made separate layers for transparent
 and other annotations on pdfs/webs so that transparency will work better.

---
 src/Utils.ts                       | 10 ++++++++
 src/client/util/DragManager.ts     | 12 +++++-----
 src/client/views/nodes/WebBox.tsx  | 48 +++++++++++++++++++++-----------------
 src/client/views/pdf/PDFViewer.tsx | 18 +++++++++-----
 src/fields/Doc.ts                  |  5 ++++
 5 files changed, 60 insertions(+), 33 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index f251f776c..f90296121 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -115,6 +115,16 @@ export namespace Utils {
         return { r: r, g: g, b: b, a: a };
     }
 
+    export function IsTransparentFilter() {
+        // bcz: isTransparent(__value__) is a hack.  it would be nice to have acual functions be parsed, but now Doc.matchFieldValue is hardwired to recognize just this one
+        return ["backgroundColor:isTransparent(__value__):check"];
+    }
+    export function IsOpaqueFilter() {
+        // bcz: isTransparent(__value__) is a hack.  it would be nice to have acual functions be parsed, but now Doc.matchFieldValue is hardwired to recognize just this one
+        return ["backgroundColor:isTransparent(__value__):x"];
+    }
+
+
     export function toRGBAstr(col: { r: number, g: number, b: number, a?: number }) {
         return "rgba(" + col.r + "," + col.g + "," + col.b + (col.a !== undefined ? "," + col.a : "") + ")";
     }
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 5e16de617..f7ef9ae6f 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -425,10 +425,10 @@ export namespace DragManager {
 
         AbortDrag = () => {
             options?.dragComplete?.(new DragCompleteEvent(true, dragData));
-            endDrag();
+            cleanupDrag();
         };
 
-        const endDrag = action(() => {
+        const cleanupDrag = action(() => {
             hideDragShowOriginalElements(false);
             document.removeEventListener("pointermove", moveHandler, true);
             document.removeEventListener("pointerup", upHandler);
@@ -518,15 +518,14 @@ export namespace DragManager {
                 `translate(${(xs[i] += moveVec.x) + (options?.offsetX || 0)}px, ${(ys[i] += moveVec.y) + (options?.offsetY || 0)}px)  scale(${scaleXs[i]}, ${scaleYs[i]})`)
             );
         };
-        const upHandler = async (e: PointerEvent) => {
-            dispatchDrag(document.elementFromPoint(e.x, e.y) || document.body, e, new DragCompleteEvent(false, dragData), snapDrag(e, xFromLeft, yFromTop, xFromRight, yFromBottom), finishDrag, options);
-            endDrag();
+        const upHandler = (e: PointerEvent) => {
+            dispatchDrag(document.elementFromPoint(e.x, e.y) || document.body, e, new DragCompleteEvent(false, dragData), snapDrag(e, xFromLeft, yFromTop, xFromRight, yFromBottom), finishDrag, options, cleanupDrag);
         };
         document.addEventListener("pointermove", moveHandler, true);
         document.addEventListener("pointerup", upHandler);
     }
 
-    async function dispatchDrag(target: Element, e: PointerEvent, complete: DragCompleteEvent, pos: { x: number, y: number }, finishDrag?: (e: DragCompleteEvent) => void, options?: DragOptions) {
+    async function dispatchDrag(target: Element, e: PointerEvent, complete: DragCompleteEvent, pos: { x: number, y: number }, finishDrag?: (e: DragCompleteEvent) => void, options?: DragOptions, endDrag?: () => void) {
         const dropArgs = {
             bubbles: true,
             detail: {
@@ -543,5 +542,6 @@ export namespace DragManager {
         await finishDrag?.(complete);
         target.dispatchEvent(new CustomEvent<DropEvent>("dashOnDrop", dropArgs));
         options?.dragComplete?.(complete);
+        endDrag?.();
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 0c32a30db..94697464a 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -512,6 +512,29 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const pointerEvents = this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined;
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
         const scale = previewScale * (this.props.scaling?.() || 1);
+        const renderAnnotations = (docFilters: () => string[]) =>
+            <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
+                renderDepth={this.props.renderDepth + 1}
+                isAnnotationOverlay={true}
+                fieldKey={this.annotationKey}
+                CollectionView={undefined}
+                setPreviewCursor={this.setPreviewCursor}
+                PanelWidth={this.panelWidth}
+                PanelHeight={this.panelHeight}
+                ScreenToLocalTransform={this.scrollXf}
+                scaling={returnOne}
+                dropAction={"alias"}
+                docFilters={docFilters}
+                select={emptyFunction}
+                isContentActive={returnFalse}
+                ContentScaling={returnOne}
+                bringToFront={emptyFunction}
+                whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                removeDocument={this.removeDocument}
+                moveDocument={this.moveDocument}
+                addDocument={this.sidebarAddDocument}
+                childPointerEvents={true}
+                pointerEvents={this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
         return (
             <div className="webBox" ref={this._mainCont} style={{ pointerEvents: this.isContentActive() ? "all" : this.isContentActive() || SnappingManager.GetIsDragging() ? undefined : "none" }} >
                 <div className={`webBox-container`} style={{ pointerEvents }} onContextMenu={this.specificContextMenu}>
@@ -529,27 +552,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     >
                         <div className={"webBox-innerContent"} style={{ height: NumCast(this.scrollHeight, 50), pointerEvents }}>
                             {this.content}
-                            <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
-                                renderDepth={this.props.renderDepth + 1}
-                                isAnnotationOverlay={true}
-                                fieldKey={this.annotationKey}
-                                CollectionView={undefined}
-                                setPreviewCursor={this.setPreviewCursor}
-                                PanelWidth={this.panelWidth}
-                                PanelHeight={this.panelHeight}
-                                ScreenToLocalTransform={this.scrollXf}
-                                scaling={returnOne}
-                                dropAction={"alias"}
-                                select={emptyFunction}
-                                isContentActive={returnFalse}
-                                ContentScaling={returnOne}
-                                bringToFront={emptyFunction}
-                                whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                                removeDocument={this.removeDocument}
-                                moveDocument={this.moveDocument}
-                                addDocument={this.sidebarAddDocument}
-                                childPointerEvents={true}
-                                pointerEvents={this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />
+                            <div style={{ mixBlendMode: "multiply" }}>
+                                {renderAnnotations(Utils.IsTransparentFilter)}
+                            </div>
+                            {renderAnnotations(Utils.IsOpaqueFilter)}
                             {this.annotationLayer}
                         </div>
                     </div>
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 41a60bedf..2c00b005d 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -507,12 +507,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
     panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
     @computed get overlayLayer() {
-        return <div className={`pdfViewerDash-overlay${CurrentUserUtils.SelectedTool !== InkTool.None || SnappingManager.GetIsDragging() ? "-inking" : ""}`}
-            style={{
-                pointerEvents: SnappingManager.GetIsDragging() ? "all" : undefined,
-                mixBlendMode: this.allAnnotations.some(anno => anno.mixBlendMode) ? "hard-light" : undefined,
-                transform: `scale(${this._zoomed})`
-            }}>
+        const renderAnnotations = (docFilters: () => string[]) =>
             <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
                 isAnnotationOverlay={true}
                 isContentActive={returnFalse}
@@ -524,10 +519,21 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 select={emptyFunction}
                 ContentScaling={this.contentZoom}
                 bringToFront={emptyFunction}
+                docFilters={doFilters}
                 CollectionView={undefined}
                 ScreenToLocalTransform={this.overlayTransform}
                 renderDepth={this.props.renderDepth + 1}
                 childPointerEvents={true} />
+        return <div className={`pdfViewerDash-overlay${CurrentUserUtils.SelectedTool !== InkTool.None || SnappingManager.GetIsDragging() ? "-inking" : ""}`}
+            style={{
+                pointerEvents: SnappingManager.GetIsDragging() ? "all" : undefined,
+                mixBlendMode: this.allAnnotations.some(anno => anno.mixBlendMode) ? "hard-light" : undefined,
+                transform: `scale(${this._zoomed})`
+            }}>
+            <div style={{ mixBlendMode: "multiply" }}>
+                {renderAnnotations(Utils.IsTransparentFilter)}
+            </div>
+            {renderAnnotations(Utils.IsOpaqueFilter)}
         </div>;
     }
     @computed get pdfViewerDiv() {
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index e4087cf43..f6efefdf9 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -25,6 +25,7 @@ import { deleteProperty, GetEffectiveAcl, getField, getter, inheritParentAcls, m
 import JSZip = require("jszip");
 import { CurrentUserUtils } from "../client/util/CurrentUserUtils";
 import { IconProp } from "@fortawesome/fontawesome-svg-core";
+import Color = require("color");
 
 export namespace Field {
     export function toKeyValueString(doc: Doc, key: string): string {
@@ -1087,6 +1088,10 @@ export namespace Doc {
     }
 
     export function matchFieldValue(doc: Doc, key: string, value: any): boolean {
+        if (value === "isTransparent(__value__)") {
+            const isTransparent = (color: string) => color !== "" && (Color(color).alpha() !== 1);
+            return isTransparent(StrCast(doc[key]));
+        }
         const fieldVal = doc[key];
         if (Cast(fieldVal, listSpec("string"), []).length) {
             const vals = Cast(fieldVal, listSpec("string"), []);
-- 
cgit v1.2.3-70-g09d2


From 66dd6788fbf46cd827d80eb97810e4d2af24eb26 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 24 Aug 2021 14:52:35 -0400
Subject: suppressed filter icon for annotations that are filtered based on
 transparency.  fixed drawing ink on webbox's.

---
 src/client/views/collections/CollectionSubView.tsx | 2 +-
 src/client/views/nodes/WebBox.tsx                  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index c7629acbe..55b1e4031 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -93,7 +93,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
         childDocFilters = () => [...this.props.docFilters(), ...this.collectionFilters()];
         childDocRangeFilters = () => [...(this.props.docRangeFilters?.() || []), ...this.collectionRangeDocFilters()];
         IsFiltered = () => this.collectionFilters().length || this.collectionRangeDocFilters().length ? "hasFilter" :
-            this.props.docFilters().length || this.props.docRangeFilters().length ? "inheritsFilter" : undefined
+            this.props.docFilters().filter(f => !f.includes("__value__")).length || this.props.docRangeFilters().length ? "inheritsFilter" : undefined
         searchFilterDocs = () => this.props.searchFilterDocs?.() ?? DocListCast(this.props.Document._searchFilterDocs);
         @computed.struct get childDocs() {
             TraceMobx();
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 94697464a..cdc79678a 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -534,7 +534,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 moveDocument={this.moveDocument}
                 addDocument={this.sidebarAddDocument}
                 childPointerEvents={true}
-                pointerEvents={this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
+                pointerEvents={CurrentUserUtils.SelectedTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
         return (
             <div className="webBox" ref={this._mainCont} style={{ pointerEvents: this.isContentActive() ? "all" : this.isContentActive() || SnappingManager.GetIsDragging() ? undefined : "none" }} >
                 <div className={`webBox-container`} style={{ pointerEvents }} onContextMenu={this.specificContextMenu}>
-- 
cgit v1.2.3-70-g09d2

