From 6c7101d4f69dd79a83a48d04356748213c38a435 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 11 Apr 2022 13:31:33 -0400
Subject: making layout icons work better.

---
 src/client/views/DocumentDecorations.tsx | 20 ++++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index d7ead713a..1487d5bb0 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -144,10 +144,17 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         return true;
     }
 
+    _iconifyTimeout: NodeJS.Timeout | undefined;
     onCloseClick = () => {
-        const selected = SelectionManager.Views().slice();
-        SelectionManager.DeselectAll();
-        selected.map(dv => dv.props.removeDocument?.(dv.props.Document));
+        const views = SelectionManager.Views().slice().filter(v => v);
+        const icons = this._iconifyTimeout ? views : views.filter(view => view.rootDoc.layoutKey === "layout_icon");
+        const others = this._iconifyTimeout ? [] : views.filter(view => view.rootDoc.layoutKey !== "layout_icon");
+        icons.forEach(iconView => iconView.props.removeDocument?.(iconView.props.Document));
+        others.forEach(dv => dv.iconify());
+        others.length && (this._iconifyTimeout = setTimeout(() => {
+            views.forEach(view => SelectionManager.DeselectView(view));
+            this._iconifyTimeout = undefined;
+        }, 1000));
     }
     onMaximizeDown = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, () => {
@@ -171,7 +178,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
             } else if (e.altKey) {    // open same document in new tab
                 CollectionDockingView.ToggleSplit(selectedDocs[0].props.Document, "right");
             } else {
-                LightboxView.SetLightboxDoc(selectedDocs[0].props.Document, undefined, selectedDocs.slice(1).map(view => view.props.Document));
+                var openDoc = selectedDocs[0].props.Document;
+                if (openDoc.layoutKey === "layout_icon") {
+                    openDoc = DocListCast(openDoc.aliases).find(alias => !alias.context) ?? Doc.MakeAlias(openDoc);
+                    Doc.deiconifyView(openDoc);
+                }
+                LightboxView.SetLightboxDoc(openDoc, undefined, selectedDocs.slice(1).map(view => view.props.Document));
             }
         }
         SelectionManager.DeselectAll();
-- 
cgit v1.2.3-70-g09d2


From 49437d51b277206f31cdcdf0106944bab8330f4d Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 11 Apr 2022 16:06:15 -0400
Subject: lots of layout fixes to groups, labels, ink to support iconification
 better.  simpliifed documentdecorations.  fixed display artifacts related to
 things not showing up when dragging, or otherwise not getting a halo of
 nested freeform colletions.

---
 src/client/util/CurrentUserUtils.ts                |   8 +-
 src/client/util/DragManager.ts                     |   2 +-
 src/client/views/DocumentDecorations.scss          | 496 ++++++++++-----------
 src/client/views/DocumentDecorations.tsx           |  10 +-
 src/client/views/InkingStroke.tsx                  |   6 +-
 src/client/views/StyleProvider.tsx                 |   6 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  11 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |   5 +-
 src/client/views/nodes/DocumentIcon.tsx            |   1 +
 src/client/views/nodes/DocumentView.scss           |   3 +
 src/client/views/nodes/DocumentView.tsx            |   8 +-
 src/client/views/nodes/LabelBigText.js             |   1 +
 src/client/views/nodes/LabelBox.scss               |   2 +-
 src/client/views/nodes/LabelBox.tsx                |  31 +-
 14 files changed, 275 insertions(+), 315 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index bc4dbcb2e..6d8e2d30c 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -320,17 +320,17 @@ export class CurrentUserUtils {
         }
         if (doc["template-icon-view-img"] === undefined) {
             const iconImageView = Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", {
-                title: "data", _width: 50, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true
+                title: "data", _width: 150, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true
             });
             iconImageView.isTemplateDoc = makeTemplate(iconImageView, true, "icon_" + DocumentType.IMG);
             doc["template-icon-view-img"] = new PrefetchProxy(iconImageView);
         }
         if (doc["template-icon-view-col"] === undefined) {
-            const iconColView = Docs.Create.ImageDocument("", { title: "icon", _width: 180 / 4, _height: 135 / 4, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true });
+            const iconColView = Docs.Create.ImageDocument("", { title: "icon", _width: 360 / 4, _height: 270 / 4, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true });
             iconColView.isTemplateDoc = makeTemplate(iconColView, true, "icon_" + DocumentType.COL);
             const proto = iconColView.proto as Doc;
-            proto["icon-nativeWidth"] = 180 / 4;
-            proto["icon-nativeHeight"] = 135 / 4;
+            proto["icon-nativeWidth"] = 360 / 4;
+            proto["icon-nativeHeight"] = 270 / 4;
             proto.icon = new ImageField("http://www.cs.brown.edu/~bcz/noImage.png");
             doc["template-icon-view-col"] = new PrefetchProxy(iconColView);
         }
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index d6d04db9a..94a09eac4 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -424,7 +424,7 @@ export namespace DragManager {
 
         const hideDragShowOriginalElements = (hide: boolean) => {
             dragLabel.style.display = hide ? "" : "none";
-            //!hide && dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement));
+            !hide && dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement));
             eles.forEach(ele => ele.hidden = hide);
         };
         options?.hideSource && hideDragShowOriginalElements(true);
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 82dca1287..35e37a2cd 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -4,8 +4,8 @@ $linkGap: 3px;
 
 .documentDecorations-Dark,
 .documentDecorations {
-  position: absolute;
-  z-index: 2000;
+    position: absolute;
+    z-index: 2000;
 }
 .documentDecorations-Dark {
     background: dimgray;
@@ -17,11 +17,11 @@ $linkGap: 3px;
     left: 0;
     display: grid;
     grid-template-rows: 20px 8px 1fr 8px;
-    grid-template-columns: 8px 16px 1fr 8px 8px;
+    grid-template-columns: 8px 1fr 8px;
     pointer-events: none;
 
     .documentDecorations-centerCont {
-        grid-column: 3;
+        grid-column: 2;
         background: none;
     }
 
@@ -41,6 +41,7 @@ $linkGap: 3px;
         opacity: 1;
         transform: translate(10px, 10px);
         grid-row: 4;
+        grid-column: 3
     }
 
     .documentDecorations-topLeftResizer,
@@ -73,20 +74,18 @@ $linkGap: 3px;
 
     .documentDecorations-topResizer,
     .documentDecorations-bottomResizer {
-        grid-column-start: 2;
-        grid-column-end: 5;
+        grid-column: 2;
     }
 
     .documentDecorations-bottomRightResizer,
     .documentDecorations-topRightResizer,
     .documentDecorations-rightResizer {
-        grid-column-start: 5;
-        grid-column-end: 7;
+        grid-column: 3;
     }
 
     .documentDecorations-rotation,
     .documentDecorations-borderRadius {
-        grid-column: 5;
+        grid-column: 3;
         grid-row: 4;
         border-radius: 100%;
         background: black;
@@ -132,114 +131,113 @@ $linkGap: 3px;
         opacity: 1;
     }
 
-  .documentDecorations-topLeftResizer,
-  .documentDecorations-leftResizer,
-  .documentDecorations-bottomLeftResizer {
-    grid-column: 1;
-  }
-
-  .documentDecorations-topResizer,
-  .documentDecorations-bottomResizer {
-    grid-column-start: 2;
-    grid-column-end: 5;
-  }
-
-  .documentDecorations-bottomRightResizer,
-  .documentDecorations-topRightResizer,
-  .documentDecorations-rightResizer {
-    grid-column-start: 5;
-    grid-column-end: 7;
-  }
-
-  .documentDecorations-rotation,
-  .documentDecorations-borderRadius {
-    grid-column: 5;
-    grid-row: 4;
-    border-radius: 100%;
-    background: black;
-    height: 8;
-    right: -12;
-    top: 12;
-    position: relative;
-    pointer-events: all;
-    cursor: nwse-resize;
-
-    .borderRadiusTooltip {
-      width: 10px;
-      height: 10px;
-      position: absolute;
-    }
-  }
-  .documentDecorations-rotation {
-    background: transparent;
-    right: -15;
-  }
-
-  .documentDecorations-topLeftResizer,
-  .documentDecorations-bottomRightResizer {
-    cursor: nwse-resize;
-    background: unset;
-    opacity: 1;
-  }
+    .documentDecorations-topLeftResizer,
+    .documentDecorations-leftResizer,
+    .documentDecorations-bottomLeftResizer {
+        grid-column: 1;
+    }
 
-  .documentDecorations-topLeftResizer {
-    border-left: 2px solid;
-    border-top: solid 2px;
-  }
+    .documentDecorations-topResizer,
+    .documentDecorations-bottomResizer {
+        grid-column: 2;
+    }
 
-  .documentDecorations-bottomRightResizer {
-    border-right: 2px solid;
-    border-bottom: solid 2px;
-  }
+    .documentDecorations-bottomRightResizer,
+    .documentDecorations-topRightResizer,
+    .documentDecorations-rightResizer {
+        grid-column: 3
+    }
 
-  .documentDecorations-topLeftResizer:hover,
-  .documentDecorations-bottomRightResizer:hover {
-    opacity: 1;
-  }
+    .documentDecorations-rotation,
+    .documentDecorations-borderRadius {
+        grid-column: 3;
+        grid-row: 4;
+        border-radius: 100%;
+        background: black;
+        height: 8;
+        right: -12;
+        top: 12;
+        position: relative;
+        pointer-events: all;
+        cursor: nwse-resize;
 
-  .documentDecorations-bottomRightResizer {
-    grid-row: 4;
-  }
+        .borderRadiusTooltip {
+            width: 10px;
+            height: 10px;
+            position: absolute;
+        }
+    }
+    .documentDecorations-rotation {
+        background: transparent;
+        right: -15;
+    }
 
-  .documentDecorations-topRightResizer,
-  .documentDecorations-bottomLeftResizer {
-    cursor: nesw-resize;
-    background: unset;
-    opacity: 1;
-  }
+    .documentDecorations-topLeftResizer,
+    .documentDecorations-bottomRightResizer {
+        cursor: nwse-resize;
+        background: unset;
+        opacity: 1;
+    }
+
+    .documentDecorations-topLeftResizer {
+        border-left: 2px solid;
+        border-top: solid 2px;
+    }
+
+    .documentDecorations-bottomRightResizer {
+        border-right: 2px solid;
+        border-bottom: solid 2px;
+    }
+
+    .documentDecorations-topLeftResizer:hover,
+    .documentDecorations-bottomRightResizer:hover {
+        opacity: 1;
+    }
+
+    .documentDecorations-bottomRightResizer {
+        grid-row: 4;
+    }
+
+    .documentDecorations-topRightResizer,
+    .documentDecorations-bottomLeftResizer {
+        cursor: nesw-resize;
+        background: unset;
+        opacity: 1;
+    }
 
-  .documentDecorations-topRightResizer {
-    border-right: 2px solid;
-    border-top: 2px solid;
-  }
+    .documentDecorations-topRightResizer {
+        border-right: 2px solid;
+        border-top: 2px solid;
+    }
 
-  .documentDecorations-bottomLeftResizer {
-    border-left: 2px solid;
-    border-bottom: 2px solid;
-  }
+    .documentDecorations-bottomLeftResizer {
+        border-left: 2px solid;
+        border-bottom: 2px solid;
+    }
 
-  .documentDecorations-topRightResizer:hover,
-  .documentDecorations-bottomLeftResizer:hover {
+.documentDecorations-topRightResizer:hover,
+.documentDecorations-bottomLeftResizer:hover {
     cursor: nesw-resize;
     background: black;
     opacity: 1;
-  }
+}
 
-  .documentDecorations-topResizer,
-  .documentDecorations-bottomResizer {
+.documentDecorations-topResizer,
+.documentDecorations-bottomResizer {
     cursor: ns-resize;
-  }
+}
 
-    .documentDecorations-title-Dark,
-    .documentDecorations-title {
+.documentDecorations-title-Dark,
+.documentDecorations-title {
         opacity: 1;
-        grid-column-start: 2;
-        grid-column-end: 4;
+        width: 100%;
+        grid-column: 2;
         pointer-events: auto;
         overflow: hidden;
         text-align: center;
         display: flex;
-        margin-left: 5px;
+        padding-left: 5px;
+        padding-right: 12px;
         height: 20px;
         position: absolute;
         border-radius: 8px;
@@ -247,7 +245,7 @@ $linkGap: 3px;
 
         .documentDecorations-titleSpan, 
         .documentDecorations-titleSpan-Dark  {
-            width: 100%;
+            width: calc(100% - 17px); // = padding-left + padding-right
             border-radius: 8px;
             background: #ffffffa0;
             position: absolute;
@@ -263,105 +261,61 @@ $linkGap: 3px;
         background: black;
     }
 
-  .documentDecorations-contextMenu {
-    width: 25px;
-    height: calc(100% + 8px); // 8px for the height of the top resizer bar
-    grid-column-start: 2;
-    grid-column-end: 2;
-    pointer-events: all;
-    padding-left: 5px;
-    cursor: pointer;
-  }
-
-  .documentDecorations-titleBackground {
-    background: #ffffffcf;
-    border-radius: 8px;
-    width: 100%;
-    height: 100%;
-    position: absolute;
-  }
-
-  .documentDecorations-title {
-    opacity: 1;
-    grid-column-start: 2;
-    grid-column-end: 4;
-    pointer-events: auto;
-    overflow: hidden;
-    text-align: center;
-    display: flex;
-    margin-left: 5px;
-    height: 20px;
-    position: absolute;
-    .documentDecorations-titleSpan {
-      width: 100%;
-      border-radius: 8px;
-      background: #ffffffcf;
-      position: absolute;
-      display: inline-block;
-      cursor: move;
-    }
-  }
-
-  .focus-visible {
-    margin-left: 0px;
-  }
-}
+    .documentDecorations-titleBackground {
+        background: #ffffffcf;
+        border-radius: 8px;
+        width: 100%;
+        height: 100%;
+        position: absolute;
+    }
 
-.documentDecorations-iconifyButton {
-  opacity: 1;
-  grid-column-start: 4;
-  grid-column-end: 4;
-  pointer-events: all;
-  right: 0;
-  cursor: pointer;
-  position: absolute;
-  width: 20px;
+    .focus-visible {
+        margin-left: 0px;
+    }
 }
 
 .documentDecorations-openButton {
-  display: flex;
-  align-items: center;
-  opacity: 1;
-  grid-column-start: 5;
-  grid-column-end: 5;
-  pointer-events: all;
-  cursor: pointer;
+    display: flex;
+    align-items: center;
+    opacity: 1;
+    grid-column-start: 3;
+    pointer-events: all;
+    cursor: pointer;
 }
 
 .documentDecorations-closeButton {
-  display: flex;
-  align-items: center;
-  opacity: 1;
-  grid-column-start: 1;
-  grid-column-end: 3;
-  pointer-events: all;
-  cursor: pointer;
-
-  > svg {
-    margin: 0;
-  }
+    display: flex;
+    align-items: center;
+    opacity: 1;
+    grid-column: 1;
+    pointer-events: all;
+    cursor: pointer;
+
+    > svg {
+        margin: 0;
+    }
 }
 
 .documentDecorations-background {
-  background: lightblue;
-  position: absolute;
-  opacity: 0.1;
+    background: lightblue;
+    position: absolute;
+    opacity: 0.1;
 }
 
 .linkFlyout {
-  grid-column: 2/4;
+    grid-column: 2/4;
 }
 
 .linkButton-empty:hover {
-  background: $medium-gray;
-  transform: scale(1.05);
-  cursor: pointer;
+    background: $medium-gray;
+    transform: scale(1.05);
+    cursor: pointer;
 }
 
 .linkButton-nonempty:hover {
-  background: $medium-gray;
-  transform: scale(1.05);
-  cursor: pointer;
+    background: $medium-gray;
+    transform: scale(1.05);
+    cursor: pointer;
 }
 
 .link-button-container {
@@ -379,132 +333,132 @@ $linkGap: 3px;
 }
 
 .linkButtonWrapper {
-  pointer-events: auto;
-  padding-right: 5px;
-  width: 25px;
+    pointer-events: auto;
+    padding-right: 5px;
+    width: 25px;
 }
 
 .linkButton-linker {
-  height: 20px;
-  width: 20px;
-  text-align: center;
-  border-radius: 50%;
-  pointer-events: auto;
-  color: $dark-gray;
-  border: $dark-gray 1px solid;
+    height: 20px;
+    width: 20px;
+    text-align: center;
+    border-radius: 50%;
+    pointer-events: auto;
+    color: $dark-gray;
+    border: $dark-gray 1px solid;
 }
 
 .linkButton-linker:hover {
-  cursor: pointer;
-  transform: scale(1.05);
+    cursor: pointer;
+    transform: scale(1.05);
 }
 
 .linkButton-empty,
 .linkButton-nonempty {
-  height: 20px;
-  width: 20px;
-  border-radius: 50%;
-  opacity: 0.9;
-  pointer-events: auto;
-  background-color: $dark-gray;
-  color: $white;
-  text-transform: uppercase;
-  letter-spacing: 2px;
-  font-size: 75%;
-  transition: transform 0.2s;
-  text-align: center;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-
-  &:hover {
-    background: $medium-gray;
-    transform: scale(1.05);
-    cursor: pointer;
-  }
+    height: 20px;
+    width: 20px;
+    border-radius: 50%;
+    opacity: 0.9;
+    pointer-events: auto;
+    background-color: $dark-gray;
+    color: $white;
+    text-transform: uppercase;
+    letter-spacing: 2px;
+    font-size: 75%;
+    transition: transform 0.2s;
+    text-align: center;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+
+    &:hover {
+        background: $medium-gray;
+        transform: scale(1.05);
+        cursor: pointer;
+    }
 }
 
 .templating-menu {
-  position: absolute;
-  pointer-events: auto;
-  text-transform: uppercase;
-  letter-spacing: 2px;
-  font-size: 75%;
-  transition: transform 0.2s;
-  text-align: center;
-  display: flex;
-  justify-content: center;
-  align-items: center;
+    position: absolute;
+    pointer-events: auto;
+    text-transform: uppercase;
+    letter-spacing: 2px;
+    font-size: 75%;
+    transition: transform 0.2s;
+    text-align: center;
+    display: flex;
+    justify-content: center;
+    align-items: center;
 }
 
 .documentdecorations-icon {
-  margin: 0px;
+    margin: 0px;
 }
 
 .templating-button,
 .docDecs-tagButton {
-  width: 20px;
-  height: 20px;
-  border-radius: 50%;
-  opacity: 0.9;
-  font-size: 14;
-  background-color: $dark-gray;
-  color: $white;
-  text-align: center;
-  cursor: pointer;
-
-  &:hover {
-    background: $medium-gray;
-    transform: scale(1.05);
-  }
+    width: 20px;
+    height: 20px;
+    border-radius: 50%;
+    opacity: 0.9;
+    font-size: 14;
+    background-color: $dark-gray;
+    color: $white;
+    text-align: center;
+    cursor: pointer;
+
+    &:hover {
+        background: $medium-gray;
+        transform: scale(1.05);
+    }
 }
 
 #template-list {
-  position: absolute;
-  top: 25px;
-  left: 0px;
-  width: max-content;
-  font-family: $sans-serif;
-  font-size: 12px;
-  background-color: $light-gray;
-  padding: 2px 12px;
-  list-style: none;
-
-  .templateToggle,
-  .chromeToggle {
-    text-align: left;
-  }
-
-  input {
-    margin-right: 10px;
-  }
+    position: absolute;
+    top: 25px;
+    left: 0px;
+    width: max-content;
+    font-family: $sans-serif;
+    font-size: 12px;
+    background-color: $light-gray;
+    padding: 2px 12px;
+    list-style: none;
+
+    .templateToggle,
+    .chromeToggle {
+        text-align: left;
+    }
+
+    input {
+        margin-right: 10px;
+    }
 }
 
 @-moz-keyframes spin {
-  100% {
-    -moz-transform: rotate(360deg);
-  }
+    100% {
+        -moz-transform: rotate(360deg);
+    }
 }
 
 @-webkit-keyframes spin {
-  100% {
-    -webkit-transform: rotate(360deg);
-  }
+    100% {
+        -webkit-transform: rotate(360deg);
+    }
 }
 
 @keyframes spin {
-  100% {
-    -webkit-transform: rotate(360deg);
-    transform: rotate(360deg);
-  }
+    100% {
+        -webkit-transform: rotate(360deg);
+        transform: rotate(360deg);
+    }
 }
 
 @keyframes shadow-pulse {
-  0% {
-    box-shadow: 0 0 0 0px rgba(0, 0, 0, 0.8);
-  }
+    0% {
+        box-shadow: 0 0 0 0px rgba(0, 0, 0, 0.8);
+    }
 
-  100% {
-    box-shadow: 0 0 0 10px rgba(0, 255, 0, 0);
-  }
+    100% {
+        box-shadow: 0 0 0 10px rgba(0, 255, 0, 0);
+    }
 }
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 1487d5bb0..353843b8d 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -449,7 +449,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
             return (null);
         }
-        const hideResizers = seldoc.props.hideResizeHandles || seldoc.rootDoc.hideResizeHandles;
+        const hideResizers = seldoc.props.hideResizeHandles || seldoc.rootDoc.hideResizeHandles || seldoc.rootDoc.isGroup;
         const hideTitle = seldoc.props.hideDecorationTitle || seldoc.rootDoc.hideDecorationTitle;
         const canOpen = SelectionManager.Views().some(docView => !docView.props.Document._stayInCollection && !docView.props.Document.isGroup && !docView.props.Document.hideOpenButton);
         const canDelete = SelectionManager.Views().some(docView => {
@@ -466,16 +466,16 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
             </Tooltip>);
 
         const colorScheme = StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme);
-        const titleArea = hideTitle ? <div className="documentDecorations-title" onPointerDown={this.onTitleDown} style={{ width: "100%" }} key="title" /> :
+        const titleArea = hideTitle ? <div className="documentDecorations-title" onPointerDown={this.onTitleDown} key="title" /> :
             this._edtingTitle ?
                 <input ref={this._keyinput} className={`documentDecorations-title${colorScheme}`}
-                    style={{ width: `calc(100% - ${hideResizers ? 0 : 20}px` }}
+                    style={{ width: `100%` }}
                     type="text" name="dynbox" autoComplete="on"
                     value={this._accumulatedTitle}
                     onBlur={e => this.titleBlur()}
                     onChange={action(e => this._accumulatedTitle = e.target.value)}
                     onKeyPress={this.titleEntered} /> :
-                <div className="documentDecorations-title" style={{ width: `calc(100% - ${hideResizers ? 0 : 20}px` }} key="title" onPointerDown={this.onTitleDown} >
+                <div className="documentDecorations-title" style={{ width: `100%` }} key="title" onPointerDown={this.onTitleDown} >
                     <span className={`documentDecorations-titleSpan${colorScheme}`}>{`${this.selectionTitle}`}</span>
                 </div>;
 
@@ -515,8 +515,6 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                     {!canOpen ? (null) : topBtn("open", "external-link-alt", this.onMaximizeDown, undefined, "Open in Tab (ctrl: as alias, shift: in new collection)")}
                     {hideResizers ? (null) :
                         <>
-                            {SelectionManager.Views().length !== 1 || hideTitle ? (null) :
-                                topBtn("iconify", `window-${seldoc.finalLayoutKey.includes("icon") ? "restore" : "minimize"}`, undefined, this.onIconifyClick, `${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`)}
                             <div key="tl" className={`documentDecorations-topLeftResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
                             <div key="t" className={`documentDecorations-topResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
                             <div key="tr" className={`documentDecorations-topRightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 5e589fdea..06671961d 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -70,11 +70,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
 
     // transform is the inherited screentolocal xf plus any scaling that was done to make the stroke
     // fit within its panel (e.g., for content fitting views like Lightbox or multicolumn, etc)
-    screenToLocal = () => {
-        console.log("Scaling = " + this.props.scaling?.())
-        console.log("Stolocal = " + this.props.ScreenToLocalTransform().Scale)
-        return this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1);
-    }
+    screenToLocal = () => this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1);
 
     getAnchor = () => {
         console.log(document.activeElement);
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 739670ecf..eb6551fc8 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -98,7 +98,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         case StyleProp.ShowTitle: return (doc && !doc.presentationTargetDoc &&
             StrCast(doc._showTitle,
                 props?.showTitle?.() ||
-                (!Doc.IsSystem(doc) && [DocumentType.COL, DocumentType.RTF, DocumentType.IMG, DocumentType.VID].includes(doc.type as any) ?
+                (!Doc.IsSystem(doc) && [DocumentType.COL, DocumentType.LABEL, DocumentType.RTF, DocumentType.IMG, DocumentType.VID].includes(doc.type as any) ?
                     (doc.author === Doc.CurrentUserEmail ? StrCast(Doc.UserDoc().showTitle) :
                         "author;creationDate") : "")) || "");
         case StyleProp.Color:
@@ -114,7 +114,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         case StyleProp.BorderPath: return comicStyle() && props?.renderDepth && doc?.type !== DocumentType.INK ? { path: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0), fill: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0, .08), width: 3 } : { path: undefined, width: 0 };
         case StyleProp.JitterRotation: return comicStyle() ? random(-1, 1, NumCast(doc?.x), NumCast(doc?.y)) * ((props?.PanelWidth() || 0) > (props?.PanelHeight() || 0) ? 5 : 10) : 0;
         case StyleProp.HeaderMargin: return ([CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.Tree].includes(doc?._viewType as any) ||
-            doc?.type === DocumentType.RTF) && showTitle() && !StrCast(doc?.showTitle).includes(":hover") ? 15 : 0;
+            doc?.type === DocumentType.RTF || doc?.type === DocumentType.LABEL) && showTitle() && !StrCast(doc?.showTitle).includes(":hover") ? 15 : 0;
         case StyleProp.BackgroundColor: {
             if (MainView.Instance.LastButton === doc) return Colors.LIGHT_GRAY;
             let docColor: Opt<string> =
@@ -143,7 +143,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                 case DocumentType.COL:
                     if (StrCast(Doc.LayoutField(doc)).includes(SliderBox.name)) break;
                     docColor = docColor ||
-                        (doc?._isGroup ? "#00000004" : // very faint highlight to show bounds of group
+                        (doc?._isGroup ? "#00000010" : // very faint highlight to show bounds of group
                             (doc?._viewType === CollectionViewType.Pile || Doc.IsSystem(doc) ? (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY) : // system docs (seen in treeView) get a grayish background
                                 isBackground() ? "cyan" : // ?? is there a good default for a background collection
                                     doc.annotationOn ? "#00000015" : // faint interior for collections on PDFs, images, etc
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 7ca6dfd93..04beed541 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1014,8 +1014,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         } else if (doc.isInkMask) {
             doc.zIndex = 5000;
         } else {
-            const docs = this.childLayoutPairs.map(pair => pair.layout);
-            docs.slice().sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
+            const docs = this.childLayoutPairs.map(pair => pair.layout).slice();
+            docs.sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
             let zlast = docs.length ? Math.max(docs.length, NumCast(docs[docs.length - 1].zIndex)) : 1;
             if (zlast - docs.length > 100) {
                 for (let i = 0; i < docs.length; i++) doc.zIndex = i + 1;
@@ -1414,12 +1414,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         }));
     }
 
-    clicks = 0;
     updateIcon = () => {
-        if (this.clicks) {
-            console.log("UPDATE ICON");
-        }
-        this.clicks++;
         this.props.docViewPath().lastElement().ContentDiv!.style.width = (this.layoutDoc[WidthSym]()).toString();
         this.props.docViewPath().lastElement().ContentDiv!.style.height = (this.layoutDoc[HeightSym]()).toString();
         var htmlString = this._mainCont && new XMLSerializer().serializeToString(this.props.docViewPath().lastElement().ContentDiv!);
@@ -1431,7 +1426,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         CreateImage(
             "",
             document.styleSheets,
-            htmlString?.replace(/"marqueeView"/g, '"marqueeView marqueeView2"'),
+            htmlString?.replace(/docView-hack/g, 'documentView-hack'),
             nativeWidth,
             nativeWidth * this.props.PanelHeight() / this.props.PanelWidth(),
             NumCast(this.layoutDoc._scrollTop)
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 5149d370f..ba34d846e 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -167,7 +167,10 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
             PanelHeight: this.panelHeight,
         };
         const background = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-        const mixBlendMode = StrCast(this.layoutDoc.mixBlendMode) as any || (typeof background === "string" && DashColor(background).alpha() !== 1 ? "multiply" : undefined);
+        const mixBlendMode = "";//StrCast(this.layoutDoc.mixBlendMode) as any || (typeof background === "string" && DashColor(background).alpha() !== 1 ? "multiply" : undefined);
+        if (mixBlendMode) {
+            console.log(mixBlendMode);
+        }
         return <div className={"collectionFreeFormDocumentView-container"}
             style={{
                 outline: this.Highlight ? "orange solid 2px" : "",
diff --git a/src/client/views/nodes/DocumentIcon.tsx b/src/client/views/nodes/DocumentIcon.tsx
index 433a0bf48..a9c998757 100644
--- a/src/client/views/nodes/DocumentIcon.tsx
+++ b/src/client/views/nodes/DocumentIcon.tsx
@@ -1,3 +1,4 @@
+
 import { observer } from "mobx-react";
 import * as React from "react";
 import { DocumentView } from "./DocumentView";
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 4565f8504..5c5d2df10 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -3,6 +3,9 @@
 .documentView-effectsWrapper {
     border-radius: inherit;
 }
+.documentView-hack {
+    display: inline;
+}
 
 .documentView-customBorder {
     width:100%;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 185eafa4a..423a5edbf 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -887,10 +887,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 focus={this.focus}
                 layoutKey={this.finalLayoutKey} />
             {this.layoutDoc.hideAllLinks ? (null) : this.allLinkEndpoints}
-            {!this.props.isSelected() || this.hideLinkButton || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() ? (null) :
+            {(!this.props.isSelected() && !this._isHovering) || this.hideLinkButton || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() ? (null) :
                 <DocumentLinksButton View={this.props.DocumentView()}
                     ContentScaling={this.props.ContentScaling}
-                    Offset={[this.topMost ? 0 : -15, undefined, undefined, this.topMost ? 10 : -20]} />
+                    Offset={[this.topMost ? 0 : !this.props.isSelected() ? - 15 : -30, undefined, undefined, this.topMost ? 10 : !this.props.isSelected() ? - 15 : -30]} />
             }
             {audioView}
         </div>;
@@ -1111,7 +1111,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             this.boxShadow || (this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" : undefined);
 
         // Return surrounding highlight 
-        return <div className={DocumentView.ROOT_DIV} ref={this._mainCont}
+        return <div className={`${DocumentView.ROOT_DIV} docView-hack`} ref={this._mainCont}
             onContextMenu={this.onContextMenu}
             onKeyDown={this.onKeyDown}
             onPointerDown={this.onPointerDown}
@@ -1119,7 +1119,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             onPointerEnter={e => !SnappingManager.GetIsDragging() && Doc.BrushDoc(this.props.Document)}
             onPointerLeave={e => !hasDescendantTarget(e.nativeEvent.x, e.nativeEvent.y, this.ContentDiv) && Doc.UnBrushDoc(this.props.Document)}
             style={{
-                display: "inline",
                 borderRadius: this.borderRounding,
                 pointerEvents: this.pointerEvents,
                 outline: highlighting && !this.borderRounding ? `${highlightColor} ${highlightStyle} ${highlightIndex}px` : "solid 0px",
@@ -1244,6 +1243,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             const deiconifyLayout = Cast(this.Document.deiconifyLayout, "string", null);
             this.switchViews(deiconifyLayout ? true : false, deiconifyLayout);
             this.Document.deiconifyLayout = undefined;
+            this.props.bringToFront(this.rootDoc);
         }
     }
     @undoBatch
diff --git a/src/client/views/nodes/LabelBigText.js b/src/client/views/nodes/LabelBigText.js
index 78fe2839e..02c36c4bc 100644
--- a/src/client/views/nodes/LabelBigText.js
+++ b/src/client/views/nodes/LabelBigText.js
@@ -156,6 +156,7 @@ export default function BigText(element, options) {
         width: element.offsetWidth, //Note: This is slightly larger than the jQuery version
         height: element.offsetHeight,
     };
+    if (!box.width || !box.height) return element;
 
 
     if (options.rotateText !== null) {
diff --git a/src/client/views/nodes/LabelBox.scss b/src/client/views/nodes/LabelBox.scss
index 6a0d651d2..42e158584 100644
--- a/src/client/views/nodes/LabelBox.scss
+++ b/src/client/views/nodes/LabelBox.scss
@@ -4,7 +4,7 @@
     border-radius: inherit;
     display: flex;
     flex-direction: column;
-    position: absolute;
+    position: relative;
 }
 
 .labelBox-mainButton {
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index c689d9f40..3405a7635 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -30,6 +30,11 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro
 
     componentDidMount() {
         this.props.setContentView?.(this);
+        console.log("MOUNTING")
+    }
+
+    componentDidUpdate() {
+        console.log("UPDATING")
     }
 
     getTitle() {
@@ -73,6 +78,19 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro
     @observable _mouseOver = false;
     @computed get hoverColor() { return this._mouseOver ? StrCast(this.layoutDoc._hoverBackgroundColor) : "unset"; }
 
+    fitTextToBox = (r: any) => {
+        BigText(r, {
+            rotateText: null,
+            fontSizeFactor: 1,
+            minimumFontSize: NumCast(this.layoutDoc._minFontSize),
+            maximumFontSize: NumCast(this.layoutDoc._maxFontSize),
+            limitingDimension: "both",
+            horizontalAlign: "center",
+            verticalAlign: "center",
+            textAlign: "center",
+            whiteSpace: "nowrap"
+        })
+    }
     // (!missingParams || !missingParams.length ? "" : "(" + missingParams.map(m => m + ":").join(" ") + ")")
     render() {
         const params = Cast(this.paramsDoc["onClick-paramFieldKeys"], listSpec("string"), []);
@@ -97,17 +115,8 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro
                 }} >
                     <span ref={r => {
                         if (r) {
-                            BigText(r, {
-                                rotateText: null,
-                                fontSizeFactor: 1,
-                                minimumFontSize: NumCast(this.layoutDoc._minFontSize),
-                                maximumFontSize: NumCast(this.layoutDoc._maxFontSize),
-                                limitingDimension: "both",
-                                horizontalAlign: "center",
-                                verticalAlign: "center",
-                                textAlign: "center",
-                                whiteSpace: "nowrap"
-                            });
+                            if (!r.offsetWidth || !r.offsetHeight) setTimeout(() => this.fitTextToBox(r));
+                            else this.fitTextToBox(r);
                         }
                     }}>{label.startsWith("#") ? (null) : label}</span>
                 </div>
-- 
cgit v1.2.3-70-g09d2


From 7cd890392f79f1783af2bdb0fe86fa6a49db849a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 11 Apr 2022 23:17:07 -0400
Subject: fixes to header to be transparent for icons.  layout fixes for doc
 decoratoins title row.  groups are not semi transnparent anymore to avoid
 mixBlendMode confusion

---
 src/client/util/CurrentUserUtils.ts                       | 13 +++++--------
 src/client/views/DocumentDecorations.scss                 |  9 +++++----
 src/client/views/DocumentDecorations.tsx                  |  3 +--
 src/client/views/StyleProvider.tsx                        |  2 +-
 src/client/views/nodes/CollectionFreeFormDocumentView.tsx |  5 +++--
 src/client/views/nodes/DocumentView.tsx                   |  6 ++++--
 6 files changed, 19 insertions(+), 19 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 0e392cc85..bf722157a 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -303,7 +303,7 @@ export class CurrentUserUtils {
         }
         if (doc["template-icon-view-rtf"] === undefined) {
             const iconRtfView = Docs.Create.LabelDocument({
-                title: "icon_" + DocumentType.RTF, textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("text"),
+                title: "icon_" + DocumentType.RTF, _showTitle: "creationDate", textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("text"),
                 _singleLine: false, _minFontSize: 18, _maxFontSize: 24,
                 _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true
             });
@@ -320,13 +320,13 @@ export class CurrentUserUtils {
         }
         if (doc["template-icon-view-img"] === undefined) {
             const iconImageView = Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", {
-                title: "data", _width: 150, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true
+                title: "data", _width: 150, isTemplateDoc: true, _showTitle: "title", onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true
             });
             iconImageView.isTemplateDoc = makeTemplate(iconImageView, true, "icon_" + DocumentType.IMG);
             doc["template-icon-view-img"] = new PrefetchProxy(iconImageView);
         }
         if (doc["template-icon-view-col"] === undefined) {
-            const iconColView = Docs.Create.ImageDocument("", { title: "icon", _width: 360 / 4, _height: 270 / 4, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true });
+            const iconColView = Docs.Create.ImageDocument("", { title: "icon", _showTitle: "title", _width: 360 / 4, _height: 270 / 4, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true });
             iconColView.isTemplateDoc = makeTemplate(iconColView, true, "icon_" + DocumentType.COL);
             const proto = iconColView.proto as Doc;
             proto["icon-nativeWidth"] = 360 / 4;
@@ -335,7 +335,7 @@ export class CurrentUserUtils {
             doc["template-icon-view-col"] = new PrefetchProxy(iconColView);
         }
         if (doc["template-icon-view-video"] === undefined) {
-            const iconVidView = Docs.Create.ImageDocument("", { title: "icon", _width: 360 / 4, _height: 270 / 4, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true });
+            const iconVidView = Docs.Create.ImageDocument("", { title: "icon", _width: 360 / 4, _height: 270 / 4, _showTitle: "title", onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true });
             iconVidView.isTemplateDoc = makeTemplate(iconVidView, true, "icon_" + DocumentType.VID);
             const proto = iconVidView.proto as Doc;
             proto["icon-nativeWidth"] = 360 / 4;
@@ -344,7 +344,7 @@ export class CurrentUserUtils {
             doc["template-icon-view-video"] = new PrefetchProxy(iconVidView);
         }
         if (doc["template-icon-view-pdf"] === undefined) {
-            const iconPdfView = Docs.Create.ImageDocument("", { title: "icon", _width: 360 / 4, _height: 270 / 4, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true });
+            const iconPdfView = Docs.Create.ImageDocument("", { title: "icon", _width: 360 / 4, _height: 270 / 4, _showTitle: "title", onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true });
             iconPdfView.isTemplateDoc = makeTemplate(iconPdfView, true, "icon_" + DocumentType.PDF);
             const proto = iconPdfView.proto as Doc;
             proto["icon-nativeWidth"] = 360 / 4;
@@ -1344,9 +1344,6 @@ export class CurrentUserUtils {
             // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
             doc.activeDashboard.colorScheme = doc.activeDashboard.colorScheme === ColorScheme.Light ? undefined : doc.activeDashboard.colorScheme;
         }
-        if (doc.activeCollectionBackground === "white") { // temporary to avoid having to rebuild the databse for old accounts that have this set by default.
-            doc.activeCollectionBackground = undefined;
-        }
         return doc;
     }
 
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 7432d45bf..481b90249 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -230,15 +230,15 @@ $linkGap: 3px;
 .documentDecorations-title-Dark,
 .documentDecorations-title {
         opacity: 1;
-        width: 100%;
+        width: calc(100% - 8px); // = margin-left + margin-right
         grid-column: 2;
         grid-column-end: 2;
         pointer-events: auto;
         overflow: hidden;
         text-align: center;
         display: flex;
-        padding-left: 2px;
-        padding-right: 2px;
+        margin-left: 6px; // closeButton width (14) - leftColumn width (8)
+        margin-right: 2px;
         height: 20px;
         position: absolute;
         border-radius: 8px;
@@ -246,7 +246,7 @@ $linkGap: 3px;
 
         .documentDecorations-titleSpan, 
         .documentDecorations-titleSpan-Dark  {
-            width: calc(100% - 17px); // = padding-left + padding-right
+            width: 100% ;
             border-radius: 8px;
             background: #ffffffa0;
             position: absolute;
@@ -290,6 +290,7 @@ $linkGap: 3px;
     opacity: 1;
     grid-column: 1;
     pointer-events: all;
+    width: 14px;
     cursor: pointer;
 
     > svg {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 353843b8d..9d9505c08 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -469,13 +469,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         const titleArea = hideTitle ? <div className="documentDecorations-title" onPointerDown={this.onTitleDown} key="title" /> :
             this._edtingTitle ?
                 <input ref={this._keyinput} className={`documentDecorations-title${colorScheme}`}
-                    style={{ width: `100%` }}
                     type="text" name="dynbox" autoComplete="on"
                     value={this._accumulatedTitle}
                     onBlur={e => this.titleBlur()}
                     onChange={action(e => this._accumulatedTitle = e.target.value)}
                     onKeyPress={this.titleEntered} /> :
-                <div className="documentDecorations-title" style={{ width: `100%` }} key="title" onPointerDown={this.onTitleDown} >
+                <div className="documentDecorations-title" key="title" onPointerDown={this.onTitleDown} >
                     <span className={`documentDecorations-titleSpan${colorScheme}`}>{`${this.selectionTitle}`}</span>
                 </div>;
 
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 37ef96782..2ce78f64f 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -146,7 +146,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                         (doc?._viewType === CollectionViewType.Pile || Doc.IsSystem(doc) ? (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY) : // system docs (seen in treeView) get a grayish background
                             isBackground() ? "cyan" : // ?? is there a good default for a background collection
                                 doc.annotationOn ? "#00000015" : // faint interior for collections on PDFs, images, etc
-                                    (doc?._isGroup ? undefined : // groups get a faint background color in CollectionfreeformDocumentView
+                                    (doc?._isGroup ? undefined :
                                         StrCast((props?.renderDepth || 0) > 0 ?
                                             Doc.UserDoc().activeCollectionNestedBackground :
                                             Doc.UserDoc().activeCollectionBackground ?? (darkScheme() ? Colors.BLACK : Colors.WHITE))
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 277d6dc53..7fb2c235a 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -18,6 +18,8 @@ import "./CollectionFreeFormDocumentView.scss";
 import { DocumentView, DocumentViewProps } from "./DocumentView";
 import React = require("react");
 import { DocumentType } from "../../documents/DocumentTypes";
+import { collectionSchema } from "../../../fields/documentSchemas";
+import { CollectionViewType } from "../collections/CollectionView";
 
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, opacity?: number, highlight?: boolean, z: number, transition?: string } | undefined;
@@ -167,8 +169,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
             PanelHeight: this.panelHeight,
         };
         var background = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-        const mixBlendMode = StrCast(this.layoutDoc.mixBlendMode) as any || (typeof background === "string" && DashColor(background).alpha() !== 1 ? "multiply" : undefined);
-        if (!background && this.rootDoc.isGroup) background = "#00000004";
+        const mixBlendMode = StrCast(this.layoutDoc.mixBlendMode) as any || (typeof background === "string" && background && DashColor(background).alpha() !== 1 ? "multiply" : undefined);
         return <div className={"collectionFreeFormDocumentView-container"}
             style={{
                 outline: this.Highlight ? "orange solid 2px" : "",
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 9ec7b9a58..195f6c9e5 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -859,9 +859,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     style={{ color: [DocListCast(this.dataDoc[this.LayoutFieldKey + "-audioAnnotations"]).length ? "blue" : "gray", "green", "red"][this._mediaState] }}
                     icon={!DocListCast(this.dataDoc[this.LayoutFieldKey + "-audioAnnotations"]).length ? "microphone" : "file-audio"} size="sm" />
             </div>;
+
         return <div className="documentView-contentsView"
             style={{
-                pointerEvents: this.props.pointerEvents as any ? this.props.pointerEvents as any : (this.rootDoc.type !== DocumentType.INK && ((this.props.contentPointerEvents as any) || (this.isContentActive())) ? "all" : "none"),
+                pointerEvents: this.props.pointerEvents as any ?? this.rootDoc.layoutKey === "layout_icon" ? "none" : (this.rootDoc.type !== DocumentType.INK && ((this.props.contentPointerEvents as any) || (this.isContentActive())) ? "all" : "none"),
                 height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
             }}>
             {!this._retryThumb || !this.thumbShown() ? (null) :
@@ -1030,7 +1031,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 />
             </div>;
         const targetDoc = (showTitle?.startsWith("_") ? this.layoutDoc : this.rootDoc);
-        const background = StrCast(SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.sharingDoc.userColor, [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().userColor) : "rgba(0,0,0,0.4)");
+        const background = StrCast(SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.sharingDoc.userColor,
+            Doc.UserDoc().showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().userColor) : "rgba(0,0,0,0.4)");
         const titleView = !showTitle ? (null) :
             <div className={`documentView-titleWrapper${showTitleHover ? "-hover" : ""}`} key="title" style={{
                 position: this.headerMargin ? "relative" : "absolute",
-- 
cgit v1.2.3-70-g09d2


From 521b13a1ab34846cf91b9eceb6963ea91789620e Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 11 Apr 2022 23:33:23 -0400
Subject: changed backspace to iconify then delete

---
 src/client/views/DocumentDecorations.tsx | 35 +++++++++++++++++++-------------
 src/client/views/GlobalKeyHandler.ts     | 15 ++++++++++----
 2 files changed, 32 insertions(+), 18 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 9d9505c08..062ca1181 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -146,15 +146,17 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
 
     _iconifyTimeout: NodeJS.Timeout | undefined;
     onCloseClick = () => {
-        const views = SelectionManager.Views().slice().filter(v => v);
-        const icons = this._iconifyTimeout ? views : views.filter(view => view.rootDoc.layoutKey === "layout_icon");
-        const others = this._iconifyTimeout ? [] : views.filter(view => view.rootDoc.layoutKey !== "layout_icon");
-        icons.forEach(iconView => iconView.props.removeDocument?.(iconView.props.Document));
-        others.forEach(dv => dv.iconify());
-        others.length && (this._iconifyTimeout = setTimeout(() => {
-            views.forEach(view => SelectionManager.DeselectView(view));
-            this._iconifyTimeout = undefined;
-        }, 1000));
+        if (this.canDelete) {
+            const views = SelectionManager.Views().slice().filter(v => v);
+            const icons = this._iconifyTimeout ? views : views.filter(view => view.rootDoc.layoutKey === "layout_icon");
+            const others = this._iconifyTimeout ? [] : views.filter(view => view.rootDoc.layoutKey !== "layout_icon");
+            icons.forEach(iconView => iconView.props.removeDocument?.(iconView.props.Document));
+            others.forEach(dv => dv.iconify());
+            others.length && (this._iconifyTimeout = setTimeout(() => {
+                views.forEach(view => SelectionManager.DeselectView(view));
+                this._iconifyTimeout = undefined;
+            }, 1000));
+        }
     }
     onMaximizeDown = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, () => {
@@ -443,6 +445,15 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         return SelectionManager.Views().length > 1 ? "-multiple-" : "-unset-";
     }
 
+    @computed get canDelete() {
+        return SelectionManager.Views().some(docView => {
+            if (docView.rootDoc.stayInCollection) return false;
+            const collectionAcl = docView.props.ContainingCollectionView ? GetEffectiveAcl(docView.props.ContainingCollectionDoc?.[DataSym]) : AclEdit;
+            //return (!docView.rootDoc._stayInCollection || docView.rootDoc.isInkMask) &&
+            return (collectionAcl === AclAdmin || collectionAcl === AclEdit || GetEffectiveAcl(docView.rootDoc) === AclAdmin);
+        });
+    }
+
     render() {
         const bounds = this.Bounds;
         const seldoc = SelectionManager.Views().slice(-1)[0];
@@ -452,11 +463,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         const hideResizers = seldoc.props.hideResizeHandles || seldoc.rootDoc.hideResizeHandles || seldoc.rootDoc.isGroup;
         const hideTitle = seldoc.props.hideDecorationTitle || seldoc.rootDoc.hideDecorationTitle;
         const canOpen = SelectionManager.Views().some(docView => !docView.props.Document._stayInCollection && !docView.props.Document.isGroup && !docView.props.Document.hideOpenButton);
-        const canDelete = SelectionManager.Views().some(docView => {
-            const collectionAcl = docView.props.ContainingCollectionView ? GetEffectiveAcl(docView.props.ContainingCollectionDoc?.[DataSym]) : AclEdit;
-            //return (!docView.rootDoc._stayInCollection || docView.rootDoc.isInkMask) &&
-            return (collectionAcl === AclAdmin || collectionAcl === AclEdit || GetEffectiveAcl(docView.rootDoc) === AclAdmin);
-        });
+        const canDelete = this.canDelete;
         const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => (
             <Tooltip key={key} title={<div className="dash-tooltip">{title}</div>} placement="top">
                 <div className={`documentDecorations-${key}Button`} onContextMenu={e => e.preventDefault()}
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 4a327a842..91e0503b3 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -142,11 +142,18 @@ export class KeyManager {
             case "delete":
             case "backspace":
                 if (document.activeElement?.tagName !== "INPUT" && document.activeElement?.tagName !== "TEXTAREA") {
-                    const selected = SelectionManager.Views().filter(dv => !dv.topMost);
                     UndoManager.RunInBatch(() => {
-                        SelectionManager.DeselectAll();
-                        selected.map(dv => !dv.props.Document._stayInCollection && dv.props.removeDocument?.(dv.props.Document));
-                    }, "delete");
+                        if (LightboxView.LightboxDoc) {
+                            LightboxView.SetLightboxDoc(undefined);
+                            SelectionManager.DeselectAll();
+                        }
+                        else DocumentDecorations.Instance.onCloseClick();
+                    }, "backspace");
+                    // const selected = SelectionManager.Views().filter(dv => !dv.topMost);
+                    // UndoManager.RunInBatch(() => {
+                    //     SelectionManager.DeselectAll();
+                    //     selected.map(dv => !dv.props.Document._stayInCollection && dv.props.removeDocument?.(dv.props.Document));
+                    // }, "delete");
                     return { stopPropagation: true, preventDefault: true };
                 }
                 break;
-- 
cgit v1.2.3-70-g09d2


From 594f585b74bf6ecec3795a4b780da619c66f341d Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 13 Apr 2022 10:57:08 -0400
Subject: added images to treeView bullets for docs with icons.  fixed
 following pushpin links to prototypes to  toggle delegates when shown.  gave
 groups a default padding.  fixed creating icons to not create unnecessary
 template docs.  fixed toggling isPushpin. made summaries simple links instead
 of templates.

---
 src/client/documents/Documents.ts                  | 38 ++++++++++++----------
 src/client/util/DocumentManager.ts                 |  5 +--
 src/client/views/DocumentDecorations.tsx           |  5 ++-
 src/client/views/StyleProvider.tsx                 | 13 ++++++--
 src/client/views/collections/TreeView.scss         |  2 +-
 src/client/views/collections/TreeView.tsx          |  5 +--
 .../collections/collectionFreeForm/MarqueeView.tsx |  9 ++---
 src/client/views/nodes/DocumentView.tsx            |  8 +++--
 src/client/views/nodes/LinkDocPreview.tsx          |  9 ++++-
 9 files changed, 59 insertions(+), 35 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 652f8e7e0..465cdf3ec 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -223,6 +223,7 @@ export class DocumentOptions {
     dockingConfig?: string;
     annotationOn?: Doc;
     isPushpin?: boolean;
+    isGroup?: boolean; // whether a collection should use a grouping UI behavior
     _removeDropProperties?: List<string>; // list of properties that should be removed from a document when it is dropped.  e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document
 
     // BACKGROUND GRID
@@ -807,7 +808,7 @@ export namespace Docs {
         }
 
         export function FreeformDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
-            const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, _viewType: CollectionViewType.Freeform }, id);
+            const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _xPadding: 20, _yPadding: 20, ...options, _viewType: CollectionViewType.Freeform }, id);
             documents.map(d => d.context = inst);
             return inst;
         }
@@ -1317,22 +1318,25 @@ export namespace DocUtils {
         const _height = NumCast(doc._height);
         const options = { title: "data", backgroundColor: StrCast(doc.backgroundColor), _autoHeight: true, _width, x: -_width / 2, y: - _height / 2, _showSidebar: false };
 
-        let fieldTemplate: Opt<Doc>;
-        if (doc.data instanceof RichTextField || typeof (doc.data) === "string") {
-            fieldTemplate = Docs.Create.TextDocument("", options);
-        } else if (doc.data instanceof PdfField) {
-            fieldTemplate = Docs.Create.PdfDocument("http://www.msn.com", options);
-        } else if (doc.data instanceof VideoField) {
-            fieldTemplate = Docs.Create.VideoDocument("http://www.cs.brown.edu", options);
-        } else if (doc.data instanceof AudioField) {
-            fieldTemplate = Docs.Create.AudioDocument("http://www.cs.brown.edu", options);
-        } else if (doc.data instanceof ImageField) {
-            fieldTemplate = Docs.Create.ImageDocument("http://www.cs.brown.edu", options);
-        }
-        const docTemplate = docLayoutTemplate || creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + "(" + doc.title + ")", isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) });
-
-        fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, docTemplate ? Doc.GetProto(docTemplate) : docTemplate);
-        docTemplate && Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined);
+        if (docLayoutTemplate) {
+            Doc.ApplyTemplateTo(docLayoutTemplate, doc, customName, undefined);
+        } else {
+            let fieldTemplate: Opt<Doc>;
+            if (doc.data instanceof RichTextField || typeof (doc.data) === "string") {
+                fieldTemplate = Docs.Create.TextDocument("", options);
+            } else if (doc.data instanceof PdfField) {
+                fieldTemplate = Docs.Create.PdfDocument("http://www.msn.com", options);
+            } else if (doc.data instanceof VideoField) {
+                fieldTemplate = Docs.Create.VideoDocument("http://www.cs.brown.edu", options);
+            } else if (doc.data instanceof AudioField) {
+                fieldTemplate = Docs.Create.AudioDocument("http://www.cs.brown.edu", options);
+            } else if (doc.data instanceof ImageField) {
+                fieldTemplate = Docs.Create.ImageDocument("http://www.cs.brown.edu", options);
+            }
+            const docTemplate = creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + "(" + doc.title + ")", isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) });
+            fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, docTemplate ? Doc.GetProto(docTemplate) : docTemplate);
+            docTemplate && Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined);
+        }
     }
     export function makeCustomView(doc: Doc, custom: boolean, layout: string) {
         Doc.setNativeView(doc);
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 1d603e18f..fb50ecb4a 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -176,12 +176,13 @@ export class DocumentManager {
             });  // if the target is hidden, un-hide it here.
         }
         const focusAndFinish = (didFocus: boolean) => {
+            const finalTargetDoc = docView?.Document ?? targetDoc;
             if (originatingDoc?.isPushpin) {
                 if (!didFocus && !wasHidden) {  // don't toggle the hidden state if the doc was already un-hidden as part of this document traversal
-                    targetDoc.hidden = !targetDoc.hidden;
+                    finalTargetDoc.hidden = !finalTargetDoc.hidden;
                 }
             } else {
-                targetDoc.hidden && (targetDoc.hidden = undefined);
+                finalTargetDoc.hidden && (finalTargetDoc.hidden = undefined);
                 !noSelect && docView?.select(false);
             }
             finished?.();
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 062ca1181..a8d91e0a2 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -453,6 +453,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
             return (collectionAcl === AclAdmin || collectionAcl === AclEdit || GetEffectiveAcl(docView.rootDoc) === AclAdmin);
         });
     }
+    @computed get hasIcons() {
+        return SelectionManager.Views().some(docView => docView.rootDoc.layoutKey === "layout_icon");
+    }
 
     render() {
         const bounds = this.Bounds;
@@ -516,7 +519,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                     left: bounds.x - this._resizeBorderWidth / 2,
                     top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight,
                 }}>
-                    {!canDelete ? <div /> : topBtn("close", "times", undefined, this.onCloseClick, "Close")}
+                    {!canDelete ? <div /> : topBtn("close", this.hasIcons ? "times" : "window-maximize", undefined, this.onCloseClick, "Close")}
                     {titleArea}
                     {!canOpen ? (null) : topBtn("open", "external-link-alt", this.onMaximizeDown, undefined, "Open in Tab (ctrl: as alias, shift: in new collection)")}
                     {hideResizers ? (null) :
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 9803f9782..dba290b46 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -3,10 +3,11 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import 'golden-layout/src/css/goldenlayout-base.css';
 import 'golden-layout/src/css/goldenlayout-dark-theme.css';
 import { action, runInAction } from 'mobx';
+import { extname } from 'path';
 import { Doc, Opt, StrListCast } from "../../fields/Doc";
 import { List } from '../../fields/List';
 import { listSpec } from '../../fields/Schema';
-import { BoolCast, Cast, NumCast, StrCast } from "../../fields/Types";
+import { BoolCast, Cast, ImageCast, NumCast, StrCast } from "../../fields/Types";
 import { DashColor, lightOrDark } from '../../Utils';
 import { DocumentType } from '../documents/DocumentTypes';
 import { CurrentUserUtils } from '../util/CurrentUserUtils';
@@ -88,7 +89,15 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
     const showTitle = () => props?.styleProvider?.(doc, props, StyleProp.ShowTitle);
     const random = (min: number, max: number, x: number, y: number) => /* min should not be equal to max */ min + ((Math.abs(x * y) * 9301 + 49297) % 233280 / 233280) * (max - min);
     switch (property.split(":")[0]) {
-        case StyleProp.TreeViewIcon: return Doc.toIcon(doc, isOpen);
+        case StyleProp.TreeViewIcon:
+            const icon = Cast(doc?.layout_icon, Doc, null) ? Doc.expandTemplateLayout(Cast(doc?.layout_icon, Doc, null), doc) : undefined;
+            if (icon && ImageCast(doc?.icon, ImageCast(doc?.data))) {
+                const img = ImageCast(doc?.icon, ImageCast(doc?.data));
+                const ext = extname(img.url.href);
+                const url = doc?.icon ? img.url.href : img.url.href.replace(ext, "_s" + ext);
+                return <img src={url} width={20} height={15} style={{ margin: "auto", display: "block", objectFit: "contain" }} />;
+            }
+            return Doc.toIcon(doc, isOpen);
         case StyleProp.DocContents: return undefined;
         case StyleProp.WidgetColor: return isAnnotated ? Colors.LIGHT_BLUE : darkScheme() ? "lightgrey" : "dimgrey";
         case StyleProp.Opacity: return Cast(doc?._opacity, "number", Cast(doc?.opacity, "number", null));
diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss
index 2e33d3564..7c75810d3 100644
--- a/src/client/views/collections/TreeView.scss
+++ b/src/client/views/collections/TreeView.scss
@@ -47,7 +47,7 @@
         width: $TREE_BULLET_WIDTH;
         color: $medium-gray;
         margin-top: 3px;
-        transform: scale(1.3, 1.3);
+       // transform: scale(1.3, 1.3); // bcz: why was this here?  It makes displaying images in the treeView for documents that have icons harder.
         border: #80808030 1px solid;
         border-radius: 4px;
     }
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index ff5c4bab1..c2d0983da 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -31,6 +31,7 @@ import { CollectionTreeView } from './CollectionTreeView';
 import { CollectionView, CollectionViewType } from './CollectionView';
 import "./TreeView.scss";
 import React = require("react");
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
 
 export interface TreeViewProps {
     treeView: CollectionTreeView;
@@ -471,7 +472,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             onClick={this.bulletClick}
             style={this.props.treeView.outlineMode ? { opacity: this.titleStyleProvider?.(this.doc, this.props.treeView.props, StyleProp.Opacity) } : {
                 color: StrCast(this.doc.color, checked === "unchecked" ? "white" : "inherit"),
-                opacity: checked === "unchecked" ? undefined : 0.4
+                opacity: checked === "unchecked" || typeof iconType !== "string" ? undefined : 0.4
             }}>
             {this.props.treeView.outlineMode ?
                 !(this.doc.text as RichTextField)?.Text ? (null) :
@@ -484,7 +485,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                                     checked === "unchecked" ? "square" :
                                         !this.treeViewOpen ? "caret-right" : "caret-down"} />
                     </div>
-                    {this.onCheckedClick ? (null) : <FontAwesomeIcon icon={iconType} />}
+                    {this.onCheckedClick ? (null) : typeof iconType === "string" ? <FontAwesomeIcon icon={iconType as IconProp} /> : iconType}
                 </div>
             }
         </div>;
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index b8f687489..5405ab500 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -519,13 +519,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             d.y = NumCast(d.y) - this.Bounds.top;
             return d;
         });
-        const summary = Docs.Create.TextDocument("", { x: this.Bounds.left, y: this.Bounds.top, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, title: "overview" });
-        const portal = Doc.MakeAlias(summary);
-        Doc.GetProto(summary)[Doc.LayoutFieldKey(summary) + "-annotations"] = new List<Doc>(selected);
-        Doc.GetProto(summary).layout_portal = CollectionView.LayoutString(Doc.LayoutFieldKey(summary) + "-annotations");
-        summary._backgroundColor = "#e2ad32";
-        portal.layoutKey = "layout_portal";
-        portal.title = "document collection";
+        const summary = Docs.Create.TextDocument("", { _backgroundColor: "#e2ad32", x: this.Bounds.left, y: this.Bounds.top, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, title: "overview" });
+        const portal = Docs.Create.FreeformDocument(selected, { isGroup: true, backgroundColor: "transparent" });
         DocUtils.MakeLink({ doc: summary }, { doc: portal }, "summarizing", "");
 
         this.props.addLiveTextDocument(summary);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 195f6c9e5..8bf9348c7 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -594,8 +594,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @undoBatch @action
     toggleFollowLink = (location: Opt<string>, zoom: boolean, setPushpin: boolean): void => {
         this.Document.ignoreClick = false;
-        this.Document._isLinkButton = !this.Document._isLinkButton;
-        setPushpin && (this.Document.isPushpin = this.Document._isLinkButton);
+        if (setPushpin) {
+            this.Document.isPushpin = !this.Document.isPushpin;
+            this.Document._isLinkButton = this.Document.isPushpin || this.Document._isLinkButton;
+        } else {
+            this.Document._isLinkButton = !this.Document._isLinkButton;
+        }
         if (this.Document._isLinkButton && !this.onClickHandler) {
             this.Document.followLinkZoom = zoom;
             this.Document.followLinkLocation = location;
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index d5f3f3b4e..eca2e3cfd 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -15,6 +15,7 @@ import { DocumentView, DocumentViewSharedProps } from "./DocumentView";
 import './LinkDocPreview.scss';
 import React = require("react");
 import { DocumentType } from '../../documents/DocumentTypes';
+import { DragManager } from '../../util/DragManager';
 
 interface LinkDocPreviewProps {
     linkDoc?: Doc;
@@ -137,7 +138,13 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
     @computed get previewHeader() {
         return !this._linkDoc || !this._targetDoc || !this._linkSrc ? (null) :
             <div className="linkDocPreview-info" ref={this._infoRef}>
-                <div className="linkDocPreview-title">
+                <div className="linkDocPreview-title" style={{ pointerEvents: "all" }}
+                    onPointerDown={e => {
+                        DragManager.StartDocumentDrag([this._infoRef.current!],
+                            new DragManager.DocumentDragData([this._targetDoc!]), e.pageX, e.pageY);
+                        e.stopPropagation();
+                        LinkDocPreview.Clear();
+                    }}>
                     {StrCast(this._targetDoc.title).length > 16 ? StrCast(this._targetDoc.title).substr(0, 16) + "..." : this._targetDoc.title}
                     <p className="linkDocPreview-description"> {StrCast(this._linkDoc.description)}</p>
                 </div>
-- 
cgit v1.2.3-70-g09d2


From 74ef5f7a8c60dc68a1262077f7502d0535377b88 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 14 Apr 2022 11:49:28 -0400
Subject: fixed following links to documents in iconified collections/text to
 deiconify.  fixed ctrl-x to cut (not iconify).  changed lightboxview to add
 to future docs when stepping into.  added placeholder for indicating how many
 documents are left in lightbox future

---
 src/client/util/DocumentManager.ts                        | 14 +++++++++++++-
 src/client/views/DocumentDecorations.tsx                  |  8 ++++----
 src/client/views/GlobalKeyHandler.ts                      |  2 +-
 src/client/views/LightboxView.tsx                         | 11 ++++-------
 src/client/views/nodes/DocumentView.tsx                   | 15 +++++++++------
 src/client/views/nodes/formattedText/FormattedTextBox.tsx |  4 ++--
 6 files changed, 33 insertions(+), 21 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index fb50ecb4a..607a3d6bf 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -197,7 +197,14 @@ export class DocumentManager {
             (wasHidden && annoContainerView);// if we have an annotation container and the target was hidden, then try again because we just un-hid the document above
         const focusView = !docView && targetDoc.type === DocumentType.MARKER && annoContainerView ? annoContainerView : docView;
         if (!docView && annoContainerView) {
-            annoContainerView.focus(targetDoc);  // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below
+            if (annoContainerView.props.Document.layoutKey === "layout_icon") {
+                annoContainerView.iconify(() => this.jumpToDocument(
+                    targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc,
+                    finished, originalTarget, noSelect, presZoom));
+                return;
+            } else {
+                annoContainerView.focus(targetDoc);  // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below
+            }
         }
         if (focusView) {
             !noSelect && Doc.linkFollowHighlight(focusView.rootDoc); //TODO:glr make this a setting in PresBox
@@ -217,6 +224,11 @@ export class DocumentManager {
                     targetDocContextView.props.focus(targetDocContextView.rootDoc, {
                         willZoom, afterFocus: async () => {
                             targetDocContext._viewTransition = undefined;
+                            if (targetDocContext.layoutKey === "layout_icon") {
+                                targetDocContextView.iconify(() => this.jumpToDocument(
+                                    targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc,
+                                    finished, originalTarget, noSelect, presZoom));
+                            }
                             return ViewAdjustment.doNothing;
                         }
                     });
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index a8d91e0a2..78a8a4c7e 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -145,11 +145,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
     }
 
     _iconifyTimeout: NodeJS.Timeout | undefined;
-    onCloseClick = () => {
+    onCloseClick = (force = false) => {
         if (this.canDelete) {
             const views = SelectionManager.Views().slice().filter(v => v);
-            const icons = this._iconifyTimeout ? views : views.filter(view => view.rootDoc.layoutKey === "layout_icon");
-            const others = this._iconifyTimeout ? [] : views.filter(view => view.rootDoc.layoutKey !== "layout_icon");
+            const icons = this._iconifyTimeout || force ? views : views.filter(view => view.rootDoc.layoutKey === "layout_icon");
+            const others = this._iconifyTimeout || force ? [] : views.filter(view => view.rootDoc.layoutKey !== "layout_icon");
             icons.forEach(iconView => iconView.props.removeDocument?.(iconView.props.Document));
             others.forEach(dv => dv.iconify());
             others.length && (this._iconifyTimeout = setTimeout(() => {
@@ -519,7 +519,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                     left: bounds.x - this._resizeBorderWidth / 2,
                     top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight,
                 }}>
-                    {!canDelete ? <div /> : topBtn("close", this.hasIcons ? "times" : "window-maximize", undefined, this.onCloseClick, "Close")}
+                    {!canDelete ? <div /> : topBtn("close", this.hasIcons ? "times" : "window-maximize", undefined, e => this.onCloseClick(), "Close")}
                     {titleArea}
                     {!canOpen ? (null) : topBtn("open", "external-link-alt", this.onMaximizeDown, undefined, "Open in Tab (ctrl: as alias, shift: in new collection)")}
                     {hideResizers ? (null) :
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 91e0503b3..e3031d2c5 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -275,7 +275,7 @@ export class KeyManager {
                     const pt = SelectionManager.Views()[0].props.ScreenToLocalTransform().transformPoint(bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2);
                     const text = `__DashDocId(${pt?.[0] || 0},${pt?.[1] || 0}):` + SelectionManager.Views().map(dv => dv.Document[Id]).join(":");
                     SelectionManager.Views().length && navigator.clipboard.writeText(text);
-                    DocumentDecorations.Instance.onCloseClick();
+                    DocumentDecorations.Instance.onCloseClick(true);
                     stopPropagation = false;
                     preventDefault = false;
                 }
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index b39b5020c..59ed0dc92 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -63,7 +63,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
             }
         }
         if (future) {
-            this._future = future.slice().sort((a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)).sort((a, b) => DocListCast(a.links).length - DocListCast(b.links).length);
+            this._future = [...(this._future ?? []), ...(this.LightboxDoc ? [this.LightboxDoc] : []), ...future.slice().sort((a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)).sort((a, b) => DocListCast(a.links).length - DocListCast(b.links).length),];
         }
         this._doc = doc;
         this._layoutTemplate = layoutTemplate;
@@ -118,7 +118,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
     addDocTab = LightboxView.AddDocTab;
     @action public static Next() {
         const doc = LightboxView._doc!;
-        const target = LightboxView._docTarget = LightboxView._future?.pop();
+        const target = LightboxView._docTarget = this._future?.pop();
         const targetDocView = target && DocumentManager.Instance.getLightboxDocumentView(target);
         if (targetDocView && target) {
             const l = DocUtils.MakeLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.() || target).lastElement();
@@ -163,11 +163,8 @@ export class LightboxView extends React.Component<LightboxViewProps> {
         const docView = DocumentManager.Instance.getLightboxDocumentView(target || doc);
         if (docView) {
             LightboxView._docTarget = target;
-            const focusSpeed = 1000;
-            doc._viewTransition = `transform ${focusSpeed}ms`;
             if (!target) docView.ComponentView?.shrinkWrap?.();
             else docView.focus(target, { willZoom: true, scale: 0.9 });
-            setTimeout(() => doc._viewTransition = undefined, focusSpeed);
         }
         else {
             LightboxView.SetLightboxDoc(doc, target);
@@ -232,7 +229,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                             const doc = LightboxView._doc;
                             const targetView = target && DocumentManager.Instance.getLightboxDocumentView(target);
                             if (doc === r.props.Document && (!target || target === doc)) r.ComponentView?.shrinkWrap?.();
-                            else target && targetView?.focus(target, { willZoom: true, scale: 0.9, instant: true });
+                            //else target && targetView?.focus(target, { willZoom: true, scale: 0.9, instant: true }); // bcz: why was this here?  it breaks smooth navigation in lightbox using 'next' button
                         }));
                     })}
                         Document={LightboxView.LightboxDoc}
@@ -272,7 +269,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                     () => LightboxView.LightboxDoc && LightboxView._future?.length ? "" : "none", e => {
                         e.stopPropagation();
                         LightboxView.Next();
-                    })}
+                    }, this.future()?.length.toString())}
                 <LightboxTourBtn navBtn={this.navBtn} future={this.future} stepInto={this.stepInto} tourMap={this.tourMap} />
                 <div className="lightboxView-tabBtn" title={"open in tab"}
                     onClick={e => {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 0e22b1b3f..4380cb0bc 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -452,7 +452,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         });
         // after  a timeout, the right _componentView should have been created, so call it to update its view spec values
         setTimeout(() => this._componentView?.setViewSpec?.(anchor, LinkDocPreview.LinkInfo ? true : false));
-        const focusSpeed = this._componentView?.scrollFocus?.(anchor, !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here
+        const focusSpeed = this._componentView?.scrollFocus?.(anchor, !options?.instant || !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here
         const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => options?.afterFocus ? options?.afterFocus(true) : ViewAdjustment.doNothing;
         this.props.focus(options?.docTransform ? anchor : this.rootDoc, {
             ...options, afterFocus: (didFocus: boolean) =>
@@ -1243,15 +1243,15 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         return { left, top, right, bottom, center: this.ComponentView?.getCenter?.(xf) };
     }
 
-    public iconify() {
+    public iconify(finished?: () => void) {
         this.ComponentView?.updateIcon?.();
         const layoutKey = Cast(this.Document.layoutKey, "string", null);
         if (layoutKey !== "layout_icon") {
-            this.switchViews(true, "icon");
+            this.switchViews(true, "icon", finished);
             if (layoutKey && layoutKey !== "layout" && layoutKey !== "layout_icon") this.Document.deiconifyLayout = layoutKey.replace("layout_", "");
         } else {
             const deiconifyLayout = Cast(this.Document.deiconifyLayout, "string", null);
-            this.switchViews(deiconifyLayout ? true : false, deiconifyLayout);
+            this.switchViews(deiconifyLayout ? true : false, deiconifyLayout, finished);
             this.Document.deiconifyLayout = undefined;
             this.props.bringToFront(this.rootDoc);
         }
@@ -1262,12 +1262,15 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         Doc.setNativeView(this.props.Document);
         custom && DocUtils.makeCustomViewClicked(this.props.Document, Docs.Create.StackingDocument, layout, undefined);
     }
-    switchViews = action((custom: boolean, view: string) => {
+    switchViews = action((custom: boolean, view: string, finished?: () => void) => {
         this.docView && (this.docView._animateScalingTo = 0.1);  // shrink doc
         setTimeout(action(() => {
             this.setCustomView(custom, view);
             this.docView && (this.docView._animateScalingTo = 1); // expand it
-            setTimeout(action(() => this.docView && (this.docView._animateScalingTo = 0)), this.docView!._animateScaleTime - 10);
+            setTimeout(action(() => {
+                this.docView && (this.docView._animateScalingTo = 0);
+                finished?.();
+            }), this.docView!._animateScaleTime - 10);
         }), this.docView!._animateScaleTime - 10);
     });
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index b6713d878..ac291801b 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -758,7 +758,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             const splitter = state.schema.marks.splitter.create({ id: Utils.GenerateGuid() });
             let tr = state.tr.addMark(sel.from, sel.to, splitter);
             if (sel.from !== sel.to) {
-                const anchor = anchorDoc ?? Docs.Create.TextanchorDocument({ title: "#" + this._editorView?.state.doc.textBetween(sel.from, sel.to), unrendered: true });
+                const anchor = anchorDoc ?? Docs.Create.TextanchorDocument({ title: "#" + this._editorView?.state.doc.textBetween(sel.from, sel.to), annotationOn: this.dataDoc, unrendered: true });
                 const href = targetHref ?? Doc.localServerPath(anchor);
                 if (anchor !== anchorDoc) this.addDocument(anchor);
                 tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => {
@@ -859,7 +859,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             ({ sidebarHeight, textHeight, autoHeight, marginsHeight }) => {
                 autoHeight && this.props.setHeight?.(marginsHeight + Math.max(sidebarHeight, textHeight));
             }, { fireImmediately: true });
-        this._disposers.links = reaction(() => DocListCast(this.Document.links), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks
+        this._disposers.links = reaction(() => DocListCast(this.dataDoc.links), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks
             newLinks => {
                 this._cachedLinks.forEach(l => !newLinks.includes(l) && this.RemoveLinkFromDoc(l));
                 this._cachedLinks = newLinks;
-- 
cgit v1.2.3-70-g09d2


From a0b30f735850d4543cf9499ebd497b9548c22e47 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 14 Apr 2022 12:25:31 -0400
Subject: tweaks.  fixed groups to be resizable when iconified.

---
 src/Utils.ts                             | 1 +
 src/client/views/DocumentDecorations.tsx | 2 +-
 src/client/views/nodes/PDFBox.tsx        | 2 +-
 3 files changed, 3 insertions(+), 2 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/Utils.ts b/src/Utils.ts
index 77095005b..d1d400de7 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -604,6 +604,7 @@ export function DashColor(color: string) {
 
 export function lightOrDark(color: any) {
     if (color === "transparent") return "gray";
+    if (color.startsWith?.("linear")) return "black";
     const nonAlphaColor = color.startsWith("#") ? (color as string).substring(0, 7) :
         color.startsWith("rgba") ? color.replace(/,.[^,]*\)/, ")").replace("rgba", "rgb") : color;
     const col = DashColor(nonAlphaColor).rgb();
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 78a8a4c7e..e412d39ac 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -463,7 +463,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
             return (null);
         }
-        const hideResizers = seldoc.props.hideResizeHandles || seldoc.rootDoc.hideResizeHandles || seldoc.rootDoc.isGroup;
+        const hideResizers = seldoc.props.hideResizeHandles || seldoc.rootDoc.hideResizeHandles || seldoc.rootDoc._isGroup;
         const hideTitle = seldoc.props.hideDecorationTitle || seldoc.rootDoc.hideDecorationTitle;
         const canOpen = SelectionManager.Views().some(docView => !docView.props.Document._stayInCollection && !docView.props.Document.isGroup && !docView.props.Document.hideOpenButton);
         const canDelete = this.canDelete;
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index c73823a2b..54c4be24d 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -62,7 +62,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             }
         }
         if (oldDiv instanceof HTMLCanvasElement) {
-            const canvas = oldDiv as HTMLCanvasElement;
+            const canvas = oldDiv;
             const img = document.createElement('img'); // create a Image Element
             img.src = canvas.toDataURL();   //image source
             img.style.width = canvas.style.width;
-- 
cgit v1.2.3-70-g09d2


From c05404c741eac9288b8d8c62208b255b74a620d3 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 26 Apr 2022 11:06:37 -0400
Subject: fixed undoing closing of documents with DocumentDecorations close
 button

---
 src/client/views/DocumentDecorations.tsx | 28 +++++++++++++++++++---------
 src/client/views/GlobalKeyHandler.ts     |  2 +-
 2 files changed, 20 insertions(+), 10 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index e412d39ac..2914e5ad4 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -144,18 +144,28 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         return true;
     }
 
-    _iconifyTimeout: NodeJS.Timeout | undefined;
+    _iconifyingCount = 0;
+    _deleteAfterIconify = false;
+    _iconifyBatch: UndoManager.Batch | undefined;
     onCloseClick = (force = false) => {
         if (this.canDelete) {
             const views = SelectionManager.Views().slice().filter(v => v);
-            const icons = this._iconifyTimeout || force ? views : views.filter(view => view.rootDoc.layoutKey === "layout_icon");
-            const others = this._iconifyTimeout || force ? [] : views.filter(view => view.rootDoc.layoutKey !== "layout_icon");
-            icons.forEach(iconView => iconView.props.removeDocument?.(iconView.props.Document));
-            others.forEach(dv => dv.iconify());
-            others.length && (this._iconifyTimeout = setTimeout(() => {
-                views.forEach(view => SelectionManager.DeselectView(view));
-                this._iconifyTimeout = undefined;
-            }, 1000));
+            if (force) this._deleteAfterIconify = true;
+            if (!this._iconifyBatch) this._iconifyBatch = UndoManager.StartBatch("iconifying");
+            else this._deleteAfterIconify = true;
+            this._iconifyingCount = views.length;
+            const finished = () => {
+                if (this._deleteAfterIconify || --this._iconifyingCount <= 0) {
+                    this._iconifyingCount = 0;
+                    SelectionManager.DeselectAll();
+                    if (this._deleteAfterIconify) views.forEach(iconView => iconView.props.removeDocument?.(iconView.props.Document));
+                    this._deleteAfterIconify = false;
+                    this._iconifyBatch?.end();
+                    this._iconifyBatch = undefined;
+                }
+            }
+            if (!this._deleteAfterIconify) views.forEach(dv => dv.iconify(finished));
+            else finished();
         }
     }
     onMaximizeDown = (e: React.PointerEvent) => {
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index e3031d2c5..6120c4c9e 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -147,7 +147,7 @@ export class KeyManager {
                             LightboxView.SetLightboxDoc(undefined);
                             SelectionManager.DeselectAll();
                         }
-                        else DocumentDecorations.Instance.onCloseClick();
+                        else DocumentDecorations.Instance.onCloseClick(true);
                     }, "backspace");
                     // const selected = SelectionManager.Views().filter(dv => !dv.topMost);
                     // UndoManager.RunInBatch(() => {
-- 
cgit v1.2.3-70-g09d2


From 0fa5a34abf8dcd40784c0a9e2f89ba01937af024 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 26 Apr 2022 11:13:44 -0400
Subject: from last

---
 src/client/views/DocumentDecorations.tsx | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 2914e5ad4..def2be848 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -151,11 +151,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         if (this.canDelete) {
             const views = SelectionManager.Views().slice().filter(v => v);
             if (force) this._deleteAfterIconify = true;
-            if (!this._iconifyBatch) this._iconifyBatch = UndoManager.StartBatch("iconifying");
-            else this._deleteAfterIconify = true;
-            this._iconifyingCount = views.length;
+            if (!this._iconifyBatch) {
+                this._iconifyBatch = UndoManager.StartBatch("iconifying");
+                this._iconifyingCount = force ? 1 : views.length;
+            } else this._deleteAfterIconify = true;
             const finished = () => {
-                if (this._deleteAfterIconify || --this._iconifyingCount <= 0) {
+                if (--this._iconifyingCount <= 0) {
                     this._iconifyingCount = 0;
                     SelectionManager.DeselectAll();
                     if (this._deleteAfterIconify) views.forEach(iconView => iconView.props.removeDocument?.(iconView.props.Document));
-- 
cgit v1.2.3-70-g09d2


From 5c1da16d7711c65c1453613cdf5e557cc0775052 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 26 Apr 2022 11:16:10 -0400
Subject: again from last

---
 src/client/views/DocumentDecorations.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index def2be848..d9c206032 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -166,7 +166,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                 }
             }
             if (!this._deleteAfterIconify) views.forEach(dv => dv.iconify(finished));
-            else finished();
+            else if (force) finished();
         }
     }
     onMaximizeDown = (e: React.PointerEvent) => {
-- 
cgit v1.2.3-70-g09d2


From 690894b1b08b17205d4b14ad9150926a592e9ec4 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 26 Apr 2022 12:08:38 -0400
Subject: changed close/iconify to leave icons selected.  fixed some edge cases
 when clicking icon and typing backspace at same time.

---
 src/client/views/DocumentDecorations.tsx | 29 +++++++++++++++--------------
 1 file changed, 15 insertions(+), 14 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index d9c206032..1feb643df 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -144,29 +144,30 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         return true;
     }
 
-    _iconifyingCount = 0;
     _deleteAfterIconify = false;
     _iconifyBatch: UndoManager.Batch | undefined;
     onCloseClick = (force = false) => {
         if (this.canDelete) {
             const views = SelectionManager.Views().slice().filter(v => v);
-            if (force) this._deleteAfterIconify = true;
+            this._deleteAfterIconify = force || this._iconifyBatch ? true : false;
             if (!this._iconifyBatch) {
                 this._iconifyBatch = UndoManager.StartBatch("iconifying");
-                this._iconifyingCount = force ? 1 : views.length;
-            } else this._deleteAfterIconify = true;
-            const finished = () => {
-                if (--this._iconifyingCount <= 0) {
-                    this._iconifyingCount = 0;
-                    SelectionManager.DeselectAll();
-                    if (this._deleteAfterIconify) views.forEach(iconView => iconView.props.removeDocument?.(iconView.props.Document));
-                    this._deleteAfterIconify = false;
+            } else {
+                force = false; // can't force immediate close in the middle of iconifying -- have to wait until iconifying completes
+            }
+            var iconifyingCount = views.length;
+            const finished = action((force?: boolean) => {
+                if ((force || --iconifyingCount === 0) && this._iconifyBatch) {
+                    if (this._deleteAfterIconify) {
+                        views.forEach(iconView => iconView.props.removeDocument?.(iconView.props.Document));
+                        SelectionManager.DeselectAll();
+                    }
                     this._iconifyBatch?.end();
                     this._iconifyBatch = undefined;
                 }
-            }
-            if (!this._deleteAfterIconify) views.forEach(dv => dv.iconify(finished));
-            else if (force) finished();
+            });
+            if (force) finished(force);
+            else if (!this._deleteAfterIconify) views.forEach(dv => dv.iconify(finished));
         }
     }
     onMaximizeDown = (e: React.PointerEvent) => {
@@ -530,7 +531,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                     left: bounds.x - this._resizeBorderWidth / 2,
                     top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight,
                 }}>
-                    {!canDelete ? <div /> : topBtn("close", this.hasIcons ? "times" : "window-maximize", undefined, e => this.onCloseClick(), "Close")}
+                    {!canDelete ? <div /> : topBtn("close", this.hasIcons ? "times" : "window-maximize", undefined, e => this.onCloseClick(this.hasIcons), "Close")}
                     {titleArea}
                     {!canOpen ? (null) : topBtn("open", "external-link-alt", this.onMaximizeDown, undefined, "Open in Tab (ctrl: as alias, shift: in new collection)")}
                     {hideResizers ? (null) :
-- 
cgit v1.2.3-70-g09d2

