From 72220186e34878ac8ac8097627a8592120a98172 Mon Sep 17 00:00:00 2001 From: aranega Date: Mon, 4 Nov 2024 09:26:45 -0600 Subject: [PATCH 01/18] Add option to hide/show code block for image layer --- src/layer/image/index.ts | 37 +++++++++++++++++++++++++++++++++++++ src/layer/index.ts | 14 ++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/src/layer/image/index.ts b/src/layer/image/index.ts index 256050060c..1bc7f048e8 100644 --- a/src/layer/image/index.ts +++ b/src/layer/image/index.ts @@ -15,6 +15,8 @@ */ import "#src/layer/image/style.css"; +import svgClosedEye from "ikonate/icons/eye-closed.svg?raw"; +import svgOpenedEye from "ikonate/icons/eye.svg?raw"; import type { CoordinateSpace } from "#src/coordinate_transform.js"; import { @@ -82,6 +84,7 @@ import { ChannelDimensionsWidget } from "#src/widget/channel_dimensions_widget.j import { makeCopyButton } from "#src/widget/copy_button.js"; import type { DependentViewContext } from "#src/widget/dependent_view_widget.js"; import { makeHelpButton } from "#src/widget/help_button.js"; +import { makeIcon } from "#src/widget/icon.js"; import type { LayerControlDefinition } from "#src/widget/layer_control.js"; import { addLayerControlToOptionsTab, @@ -540,6 +543,38 @@ class RenderingOptionsTab extends Tab { topRow.className = "neuroglancer-image-dropdown-top-row"; topRow.appendChild(document.createTextNode("Shader")); topRow.appendChild(spacer); + + const managedLayer = this.layer.managedLayer; + const codeVisibilityControl = makeIcon({ + title: managedLayer.codeVisible ? "Hide code": "Show code", + svg: managedLayer.codeVisible ? svgOpenedEye : svgClosedEye, + onClick: () => { + const button = codeVisibilityControl as HTMLDivElement; + managedLayer.setCodeVisible(!managedLayer.codeVisible) + if (managedLayer.codeVisible) { + button.title = "Hide code"; + button.innerHTML = svgOpenedEye + this.codeWidget.element.style.display = "block"; + } else { + button.title = "Show code"; + button.innerHTML = svgClosedEye + this.codeWidget.element.style.display = "none"; + } + }}); + // managedLayer.layerChanged.add(() => { + // const button = codeVisibilityControl as HTMLDivElement; + // if (managedLayer.codeVisible) { + // button.title = "Hide code"; + // button.innerHTML = svgOpenedEye + // this.codeWidget.element.style.display = "block"; + // } else { + // button.title = "Show code"; + // button.innerHTML = svgClosedEye + // this.codeWidget.element.style.display = "none"; + // } + // }); + + topRow.appendChild(codeVisibilityControl); topRow.appendChild( makeMaximizeButton({ title: "Show larger editor view", @@ -561,6 +596,8 @@ class RenderingOptionsTab extends Tab { new ChannelDimensionsWidget(layer.channelCoordinateSpaceCombiner), ).element, ); + + this.codeWidget.element.style.display = managedLayer.codeVisible ? "block" : "none"; element.appendChild(this.codeWidget.element); element.appendChild( this.registerDisposer( diff --git a/src/layer/index.ts b/src/layer/index.ts index ff3f7d8d29..0624af296f 100644 --- a/src/layer/index.ts +++ b/src/layer/index.ts @@ -710,6 +710,7 @@ export class ManagedUserLayer extends RefCounted { } visible = true; + codeVisible = true; archived = false; get supportsPickOption() { @@ -764,6 +765,7 @@ export class ManagedUserLayer extends RefCounted { } const layerSpec = userLayer.toJSON(); layerSpec.name = this.name; + layerSpec.codeVisible = this.codeVisible; if (!this.visible) { if (this.archived) { layerSpec.archived = true; @@ -774,6 +776,12 @@ export class ManagedUserLayer extends RefCounted { return layerSpec; } + setCodeVisible(value: boolean) { + if (value === this.codeVisible) return; + this.codeVisible = value; + this.layerChanged.dispatch(); + } + setVisible(value: boolean) { if (value === this.visible) return; if (value && this.archived) { @@ -2018,6 +2026,12 @@ function initializeLayerFromSpecNoRestoreState( } else { managedLayer.visible = false; } + managedLayer.codeVisible = verifyOptionalObjectProperty( + spec, + "codeVisible", + verifyBoolean, + true, + ) const layerConstructor = layerTypes.get(layerType) || NewUserLayer; managedLayer.layer = new layerConstructor(managedLayer); return spec; From a783ebfdb00ff86020ca2ef747789374c5731023 Mon Sep 17 00:00:00 2001 From: aranega Date: Fri, 8 Nov 2024 05:29:31 -0600 Subject: [PATCH 02/18] CC-122 Clean code for code-box visibility --- src/layer/image/index.ts | 23 ++++++----------------- src/widget/shader_code_widget.ts | 8 ++++++++ 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/layer/image/index.ts b/src/layer/image/index.ts index 1bc7f048e8..61dae218c4 100644 --- a/src/layer/image/index.ts +++ b/src/layer/image/index.ts @@ -545,34 +545,24 @@ class RenderingOptionsTab extends Tab { topRow.appendChild(spacer); const managedLayer = this.layer.managedLayer; + const codeVisible = managedLayer.codeVisible; + this.codeWidget.element.style.display = managedLayer.codeVisible ? "block" : "none"; + this.codeWidget.setVisible(codeVisible); const codeVisibilityControl = makeIcon({ - title: managedLayer.codeVisible ? "Hide code": "Show code", - svg: managedLayer.codeVisible ? svgOpenedEye : svgClosedEye, + title: codeVisible ? "Hide code": "Show code", + svg: codeVisible ? svgOpenedEye : svgClosedEye, onClick: () => { const button = codeVisibilityControl as HTMLDivElement; managedLayer.setCodeVisible(!managedLayer.codeVisible) if (managedLayer.codeVisible) { button.title = "Hide code"; button.innerHTML = svgOpenedEye - this.codeWidget.element.style.display = "block"; } else { button.title = "Show code"; button.innerHTML = svgClosedEye - this.codeWidget.element.style.display = "none"; } + this.codeWidget.setVisible(managedLayer.codeVisible); }}); - // managedLayer.layerChanged.add(() => { - // const button = codeVisibilityControl as HTMLDivElement; - // if (managedLayer.codeVisible) { - // button.title = "Hide code"; - // button.innerHTML = svgOpenedEye - // this.codeWidget.element.style.display = "block"; - // } else { - // button.title = "Show code"; - // button.innerHTML = svgClosedEye - // this.codeWidget.element.style.display = "none"; - // } - // }); topRow.appendChild(codeVisibilityControl); topRow.appendChild( @@ -597,7 +587,6 @@ class RenderingOptionsTab extends Tab { ).element, ); - this.codeWidget.element.style.display = managedLayer.codeVisible ? "block" : "none"; element.appendChild(this.codeWidget.element); element.appendChild( this.registerDisposer( diff --git a/src/widget/shader_code_widget.ts b/src/widget/shader_code_widget.ts index c86fae81cf..e6e3d0401d 100644 --- a/src/widget/shader_code_widget.ts +++ b/src/widget/shader_code_widget.ts @@ -189,4 +189,12 @@ export class ShaderCodeWidget extends RefCounted { this.textEditor = undefined; super.disposed(); } + + setVisible(visible: boolean) { + this.element.style.display = visible ? "block" : "none"; + } + + isVisible() { + return this.element.style.display === "block"; + } } From e3ebbdf0b8e4e7bae3cd8037e1ec22a49ed57f72 Mon Sep 17 00:00:00 2001 From: aranega Date: Fri, 8 Nov 2024 05:44:38 -0600 Subject: [PATCH 03/18] CC-154 Add code block visibility control for annotation layer --- src/layer/annotation/index.ts | 25 +++++++++++++++++++++++++ src/layer/image/index.ts | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/layer/annotation/index.ts b/src/layer/annotation/index.ts index 35c1a4e430..76d4375e54 100644 --- a/src/layer/annotation/index.ts +++ b/src/layer/annotation/index.ts @@ -15,6 +15,8 @@ */ import "#src/layer/annotation/style.css"; +import svgClosedEye from "ikonate/icons/eye-closed.svg?raw"; +import svgOpenedEye from "ikonate/icons/eye.svg?raw"; import type { AnnotationDisplayState } from "#src/annotation/annotation_layer_state.js"; import { AnnotationLayerState } from "#src/annotation/annotation_layer_state.js"; @@ -69,6 +71,7 @@ import { import { NullarySignal } from "#src/util/signal.js"; import { DependentViewWidget } from "#src/widget/dependent_view_widget.js"; import { makeHelpButton } from "#src/widget/help_button.js"; +import { makeIcon } from "#src/widget/icon.js"; import { LayerReferenceWidget } from "#src/widget/layer_reference.js"; import { makeMaximizeButton } from "#src/widget/maximize_button.js"; import { RenderScaleWidget } from "#src/widget/render_scale_widget.js"; @@ -783,6 +786,28 @@ class RenderingOptionsTab extends Tab { label.style.flex = "1"; label.textContent = "Annotation shader:"; topRow.appendChild(label); + + const managedLayer = this.layer.managedLayer; + const codeVisible = managedLayer.codeVisible; + this.codeWidget.element.style.display = managedLayer.codeVisible ? "block" : "none"; + this.codeWidget.setVisible(codeVisible); + const codeVisibilityControl = makeIcon({ + title: codeVisible ? "Hide code": "Show code", + svg: codeVisible ? svgOpenedEye : svgClosedEye, + onClick: () => { + const button = codeVisibilityControl as HTMLDivElement; + managedLayer.setCodeVisible(!managedLayer.codeVisible) + if (managedLayer.codeVisible) { + button.title = "Hide code"; + button.innerHTML = svgOpenedEye + } else { + button.title = "Show code"; + button.innerHTML = svgClosedEye + } + this.codeWidget.setVisible(managedLayer.codeVisible); + }}); + topRow.appendChild(codeVisibilityControl); + topRow.appendChild( makeMaximizeButton({ title: "Show larger editor view", diff --git a/src/layer/image/index.ts b/src/layer/image/index.ts index 61dae218c4..5a338a10b9 100644 --- a/src/layer/image/index.ts +++ b/src/layer/image/index.ts @@ -563,8 +563,8 @@ class RenderingOptionsTab extends Tab { } this.codeWidget.setVisible(managedLayer.codeVisible); }}); - topRow.appendChild(codeVisibilityControl); + topRow.appendChild( makeMaximizeButton({ title: "Show larger editor view", From 77532fbbdbf07b71e0f0a7c8fae4c998a8b77f95 Mon Sep 17 00:00:00 2001 From: aranega Date: Fri, 8 Nov 2024 06:00:21 -0600 Subject: [PATCH 04/18] CC-156 Add code visibility control for single mesh layer --- src/layer/single_mesh/index.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/layer/single_mesh/index.ts b/src/layer/single_mesh/index.ts index 6e2b686943..a8cc1c9564 100644 --- a/src/layer/single_mesh/index.ts +++ b/src/layer/single_mesh/index.ts @@ -15,6 +15,8 @@ */ import "#src/layer/single_mesh/style.css"; +import svgClosedEye from "ikonate/icons/eye-closed.svg?raw"; +import svgOpenedEye from "ikonate/icons/eye.svg?raw"; import type { ManagedUserLayer } from "#src/layer/index.js"; import { @@ -37,6 +39,7 @@ import type { Borrowed } from "#src/util/disposable.js"; import { RefCounted } from "#src/util/disposable.js"; import { removeChildren, removeFromParent } from "#src/util/dom.js"; import { makeHelpButton } from "#src/widget/help_button.js"; +import { makeIcon } from "#src/widget/icon.js"; import { makeMaximizeButton } from "#src/widget/maximize_button.js"; import { ShaderCodeWidget } from "#src/widget/shader_code_widget.js"; import { @@ -207,6 +210,28 @@ class DisplayOptionsTab extends Tab { spacer.style.flex = "1"; topRow.appendChild(spacer); + + const managedLayer = this.layer.managedLayer; + const codeVisible = managedLayer.codeVisible; + this.codeWidget.element.style.display = managedLayer.codeVisible ? "block" : "none"; + this.codeWidget.setVisible(codeVisible); + const codeVisibilityControl = makeIcon({ + title: codeVisible ? "Hide code": "Show code", + svg: codeVisible ? svgOpenedEye : svgClosedEye, + onClick: () => { + const button = codeVisibilityControl as HTMLDivElement; + managedLayer.setCodeVisible(!managedLayer.codeVisible) + if (managedLayer.codeVisible) { + button.title = "Hide code"; + button.innerHTML = svgOpenedEye + } else { + button.title = "Show code"; + button.innerHTML = svgClosedEye + } + this.codeWidget.setVisible(managedLayer.codeVisible); + }}); + topRow.appendChild(codeVisibilityControl); + topRow.appendChild( makeMaximizeButton({ title: "Show larger editor view", From 87d9433202cf4e5950a41416723536f2871b6a70 Mon Sep 17 00:00:00 2001 From: aranega Date: Fri, 8 Nov 2024 06:10:47 -0600 Subject: [PATCH 05/18] CC-157 Improve state handling for code box hidden --- src/layer/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/layer/index.ts b/src/layer/index.ts index 0624af296f..fb894cd0f7 100644 --- a/src/layer/index.ts +++ b/src/layer/index.ts @@ -765,7 +765,9 @@ export class ManagedUserLayer extends RefCounted { } const layerSpec = userLayer.toJSON(); layerSpec.name = this.name; - layerSpec.codeVisible = this.codeVisible; + if (!this.codeVisible) { + layerSpec.codeVisible = false; + } if (!this.visible) { if (this.archived) { layerSpec.archived = true; From 684807c427d9b7071d9161b04cff235eb4bd6315 Mon Sep 17 00:00:00 2001 From: aranega Date: Thu, 14 Nov 2024 07:14:19 -0600 Subject: [PATCH 06/18] Hide shader properties in annotation when code is not displayed --- src/layer/annotation/index.ts | 77 ++++++++++++++++++----------------- src/layer/image/index.ts | 1 - 2 files changed, 40 insertions(+), 38 deletions(-) diff --git a/src/layer/annotation/index.ts b/src/layer/annotation/index.ts index 76d4375e54..d0ecf4c432 100644 --- a/src/layer/annotation/index.ts +++ b/src/layer/annotation/index.ts @@ -743,42 +743,42 @@ class RenderingOptionsTab extends Tab { super(); const { element } = this; element.classList.add("neuroglancer-annotation-rendering-tab"); - element.appendChild( - this.registerDisposer( - new DependentViewWidget( - layer.annotationDisplayState.annotationProperties, - (properties, parent) => { - if (properties === undefined || properties.length === 0) return; - const propertyList = document.createElement("div"); - parent.appendChild(propertyList); - propertyList.classList.add( - "neuroglancer-annotation-shader-property-list", + const shaderProperties = this.registerDisposer( + new DependentViewWidget( + layer.annotationDisplayState.annotationProperties, + (properties, parent) => { + if (properties === undefined || properties.length === 0) return; + const propertyList = document.createElement("div"); + parent.appendChild(propertyList); + propertyList.classList.add( + "neuroglancer-annotation-shader-property-list", + ); + for (const property of properties) { + const div = document.createElement("div"); + div.classList.add("neuroglancer-annotation-shader-property"); + const typeElement = document.createElement("span"); + typeElement.classList.add( + "neuroglancer-annotation-shader-property-type", ); - for (const property of properties) { - const div = document.createElement("div"); - div.classList.add("neuroglancer-annotation-shader-property"); - const typeElement = document.createElement("span"); - typeElement.classList.add( - "neuroglancer-annotation-shader-property-type", - ); - typeElement.textContent = property.type; - const nameElement = document.createElement("span"); - nameElement.classList.add( - "neuroglancer-annotation-shader-property-identifier", - ); - nameElement.textContent = `prop_${property.identifier}`; - div.appendChild(typeElement); - div.appendChild(nameElement); - const { description } = property; - if (description !== undefined) { - div.title = description; - } - propertyList.appendChild(div); + typeElement.textContent = property.type; + const nameElement = document.createElement("span"); + nameElement.classList.add( + "neuroglancer-annotation-shader-property-identifier", + ); + nameElement.textContent = `prop_${property.identifier}`; + div.appendChild(typeElement); + div.appendChild(nameElement); + const { description } = property; + if (description !== undefined) { + div.title = description; } - }, - ), - ).element, - ); + propertyList.appendChild(div); + } + }, + ), + ).element; + + element.appendChild(shaderProperties); const topRow = document.createElement("div"); topRow.className = "neuroglancer-segmentation-dropdown-skeleton-shader-header"; @@ -788,9 +788,10 @@ class RenderingOptionsTab extends Tab { topRow.appendChild(label); const managedLayer = this.layer.managedLayer; + shaderProperties.style.display = managedLayer.codeVisible ? "block" : "none"; const codeVisible = managedLayer.codeVisible; - this.codeWidget.element.style.display = managedLayer.codeVisible ? "block" : "none"; this.codeWidget.setVisible(codeVisible); + const codeVisibilityControl = makeIcon({ title: codeVisible ? "Hide code": "Show code", svg: codeVisible ? svgOpenedEye : svgClosedEye, @@ -799,10 +800,12 @@ class RenderingOptionsTab extends Tab { managedLayer.setCodeVisible(!managedLayer.codeVisible) if (managedLayer.codeVisible) { button.title = "Hide code"; - button.innerHTML = svgOpenedEye + button.innerHTML = svgOpenedEye; + shaderProperties.style.display = "block"; } else { button.title = "Show code"; - button.innerHTML = svgClosedEye + button.innerHTML = svgClosedEye; + shaderProperties.style.display = "none"; } this.codeWidget.setVisible(managedLayer.codeVisible); }}); diff --git a/src/layer/image/index.ts b/src/layer/image/index.ts index 5a338a10b9..72c9b32d64 100644 --- a/src/layer/image/index.ts +++ b/src/layer/image/index.ts @@ -546,7 +546,6 @@ class RenderingOptionsTab extends Tab { const managedLayer = this.layer.managedLayer; const codeVisible = managedLayer.codeVisible; - this.codeWidget.element.style.display = managedLayer.codeVisible ? "block" : "none"; this.codeWidget.setVisible(codeVisible); const codeVisibilityControl = makeIcon({ title: codeVisible ? "Hide code": "Show code", From d5fe172436ca19be0cb9a315ac4b90490ef02957 Mon Sep 17 00:00:00 2001 From: aranega Date: Thu, 21 Nov 2024 06:17:53 -0600 Subject: [PATCH 07/18] Change button icon and background when clicked --- src/layer/annotation/index.ts | 10 +++++----- src/layer/image/index.ts | 10 +++++----- src/layer/single_mesh/index.ts | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/layer/annotation/index.ts b/src/layer/annotation/index.ts index d0ecf4c432..c8647a2b6e 100644 --- a/src/layer/annotation/index.ts +++ b/src/layer/annotation/index.ts @@ -15,8 +15,7 @@ */ import "#src/layer/annotation/style.css"; -import svgClosedEye from "ikonate/icons/eye-closed.svg?raw"; -import svgOpenedEye from "ikonate/icons/eye.svg?raw"; +import svgCode from "ikonate/icons/code.svg?raw"; import type { AnnotationDisplayState } from "#src/annotation/annotation_layer_state.js"; import { AnnotationLayerState } from "#src/annotation/annotation_layer_state.js"; @@ -794,21 +793,22 @@ class RenderingOptionsTab extends Tab { const codeVisibilityControl = makeIcon({ title: codeVisible ? "Hide code": "Show code", - svg: codeVisible ? svgOpenedEye : svgClosedEye, + svg: svgCode, onClick: () => { const button = codeVisibilityControl as HTMLDivElement; managedLayer.setCodeVisible(!managedLayer.codeVisible) if (managedLayer.codeVisible) { button.title = "Hide code"; - button.innerHTML = svgOpenedEye; + button.style.backgroundColor = "rgba(255, 255, 255, 0.2)"; shaderProperties.style.display = "block"; } else { button.title = "Show code"; - button.innerHTML = svgClosedEye; + button.style.backgroundColor = ""; shaderProperties.style.display = "none"; } this.codeWidget.setVisible(managedLayer.codeVisible); }}); + codeVisibilityControl.style.backgroundColor = codeVisible ? "rgba(255, 255, 255, 0.2)" : ""; topRow.appendChild(codeVisibilityControl); topRow.appendChild( diff --git a/src/layer/image/index.ts b/src/layer/image/index.ts index 72c9b32d64..35c20b3a72 100644 --- a/src/layer/image/index.ts +++ b/src/layer/image/index.ts @@ -15,8 +15,7 @@ */ import "#src/layer/image/style.css"; -import svgClosedEye from "ikonate/icons/eye-closed.svg?raw"; -import svgOpenedEye from "ikonate/icons/eye.svg?raw"; +import svgCode from "ikonate/icons/code.svg?raw"; import type { CoordinateSpace } from "#src/coordinate_transform.js"; import { @@ -549,19 +548,20 @@ class RenderingOptionsTab extends Tab { this.codeWidget.setVisible(codeVisible); const codeVisibilityControl = makeIcon({ title: codeVisible ? "Hide code": "Show code", - svg: codeVisible ? svgOpenedEye : svgClosedEye, + svg: svgCode, onClick: () => { const button = codeVisibilityControl as HTMLDivElement; managedLayer.setCodeVisible(!managedLayer.codeVisible) if (managedLayer.codeVisible) { button.title = "Hide code"; - button.innerHTML = svgOpenedEye + button.style.backgroundColor = "rgba(255, 255, 255, 0.2)"; } else { button.title = "Show code"; - button.innerHTML = svgClosedEye + button.style.backgroundColor = ""; } this.codeWidget.setVisible(managedLayer.codeVisible); }}); + codeVisibilityControl.style.backgroundColor = codeVisible ? "rgba(255, 255, 255, 0.2)" : ""; topRow.appendChild(codeVisibilityControl); topRow.appendChild( diff --git a/src/layer/single_mesh/index.ts b/src/layer/single_mesh/index.ts index a8cc1c9564..b7b4d06377 100644 --- a/src/layer/single_mesh/index.ts +++ b/src/layer/single_mesh/index.ts @@ -15,8 +15,7 @@ */ import "#src/layer/single_mesh/style.css"; -import svgClosedEye from "ikonate/icons/eye-closed.svg?raw"; -import svgOpenedEye from "ikonate/icons/eye.svg?raw"; +import svgCode from "ikonate/icons/code.svg?raw"; import type { ManagedUserLayer } from "#src/layer/index.js"; import { @@ -217,19 +216,20 @@ class DisplayOptionsTab extends Tab { this.codeWidget.setVisible(codeVisible); const codeVisibilityControl = makeIcon({ title: codeVisible ? "Hide code": "Show code", - svg: codeVisible ? svgOpenedEye : svgClosedEye, + svg: svgCode, onClick: () => { const button = codeVisibilityControl as HTMLDivElement; managedLayer.setCodeVisible(!managedLayer.codeVisible) if (managedLayer.codeVisible) { button.title = "Hide code"; - button.innerHTML = svgOpenedEye + button.style.backgroundColor = "rgba(255, 255, 255, 0.2)"; } else { button.title = "Show code"; - button.innerHTML = svgClosedEye + button.style.backgroundColor = ""; } this.codeWidget.setVisible(managedLayer.codeVisible); }}); + codeVisibilityControl.style.backgroundColor = codeVisible ? "rgba(255, 255, 255, 0.2)" : ""; topRow.appendChild(codeVisibilityControl); topRow.appendChild( From 635f9b9b5edf65d364b902aa9dafbe6f4095d5db Mon Sep 17 00:00:00 2001 From: aranega Date: Thu, 21 Nov 2024 06:21:27 -0600 Subject: [PATCH 08/18] Change icon from code to code-alt --- src/layer/annotation/index.ts | 2 +- src/layer/image/index.ts | 2 +- src/layer/single_mesh/index.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/layer/annotation/index.ts b/src/layer/annotation/index.ts index c8647a2b6e..7825dd373a 100644 --- a/src/layer/annotation/index.ts +++ b/src/layer/annotation/index.ts @@ -15,7 +15,7 @@ */ import "#src/layer/annotation/style.css"; -import svgCode from "ikonate/icons/code.svg?raw"; +import svgCode from "ikonate/icons/code-alt.svg?raw"; import type { AnnotationDisplayState } from "#src/annotation/annotation_layer_state.js"; import { AnnotationLayerState } from "#src/annotation/annotation_layer_state.js"; diff --git a/src/layer/image/index.ts b/src/layer/image/index.ts index 35c20b3a72..2d9e616480 100644 --- a/src/layer/image/index.ts +++ b/src/layer/image/index.ts @@ -15,7 +15,7 @@ */ import "#src/layer/image/style.css"; -import svgCode from "ikonate/icons/code.svg?raw"; +import svgCode from "ikonate/icons/code-alt.svg?raw"; import type { CoordinateSpace } from "#src/coordinate_transform.js"; import { diff --git a/src/layer/single_mesh/index.ts b/src/layer/single_mesh/index.ts index b7b4d06377..ca750f8575 100644 --- a/src/layer/single_mesh/index.ts +++ b/src/layer/single_mesh/index.ts @@ -15,7 +15,7 @@ */ import "#src/layer/single_mesh/style.css"; -import svgCode from "ikonate/icons/code.svg?raw"; +import svgCode from "ikonate/icons/code-alt.svg?raw"; import type { ManagedUserLayer } from "#src/layer/index.js"; import { From 80d4256feac6a69a4ffc8d3087e41e7998e74fdd Mon Sep 17 00:00:00 2001 From: aranega Date: Tue, 26 Nov 2024 09:11:44 -0600 Subject: [PATCH 09/18] Refactor layer code visibility feature Use a trackable boolean instead of a simple value in the layer, and use CheckBoxIcon instead of a hand-crafted one in each layer that implements the feature. --- src/layer/annotation/index.ts | 41 ++++++++++++++-------------------- src/layer/image/index.ts | 33 ++++++++++++--------------- src/layer/index.ts | 28 ++++++++++++----------- src/layer/single_mesh/index.ts | 35 ++++++++++++----------------- 4 files changed, 60 insertions(+), 77 deletions(-) diff --git a/src/layer/annotation/index.ts b/src/layer/annotation/index.ts index 7825dd373a..11ab9447de 100644 --- a/src/layer/annotation/index.ts +++ b/src/layer/annotation/index.ts @@ -46,7 +46,10 @@ import { RenderLayerRole } from "#src/renderlayer.js"; import type { SegmentationDisplayState } from "#src/segmentation_display_state/frontend.js"; import type { TrackableBoolean } from "#src/trackable_boolean.js"; import { TrackableBooleanCheckbox } from "#src/trackable_boolean.js"; -import { makeCachedLazyDerivedWatchableValue } from "#src/trackable_value.js"; +import { + makeCachedLazyDerivedWatchableValue, + observeWatchable, +} from "#src/trackable_value.js"; import type { AnnotationLayerView, MergedAnnotationStates, @@ -68,9 +71,9 @@ import { verifyStringArray, } from "#src/util/json.js"; import { NullarySignal } from "#src/util/signal.js"; +import { CheckboxIcon } from "#src/widget/checkbox_icon.js"; import { DependentViewWidget } from "#src/widget/dependent_view_widget.js"; import { makeHelpButton } from "#src/widget/help_button.js"; -import { makeIcon } from "#src/widget/icon.js"; import { LayerReferenceWidget } from "#src/widget/layer_reference.js"; import { makeMaximizeButton } from "#src/widget/maximize_button.js"; import { RenderScaleWidget } from "#src/widget/render_scale_widget.js"; @@ -787,29 +790,19 @@ class RenderingOptionsTab extends Tab { topRow.appendChild(label); const managedLayer = this.layer.managedLayer; - shaderProperties.style.display = managedLayer.codeVisible ? "block" : "none"; - const codeVisible = managedLayer.codeVisible; - this.codeWidget.setVisible(codeVisible); - - const codeVisibilityControl = makeIcon({ - title: codeVisible ? "Hide code": "Show code", + this.registerDisposer( + observeWatchable((visible) => { + shaderProperties.style.display = visible ? "block" : "none"; + this.codeWidget.setVisible(visible); + }, managedLayer.codeVisible), + ); + const codeVisibilityControl = new CheckboxIcon(managedLayer.codeVisible, { + enableTitle: "Hide code", + disableTitle: "Show code", + backgroundScheme: "dark", svg: svgCode, - onClick: () => { - const button = codeVisibilityControl as HTMLDivElement; - managedLayer.setCodeVisible(!managedLayer.codeVisible) - if (managedLayer.codeVisible) { - button.title = "Hide code"; - button.style.backgroundColor = "rgba(255, 255, 255, 0.2)"; - shaderProperties.style.display = "block"; - } else { - button.title = "Show code"; - button.style.backgroundColor = ""; - shaderProperties.style.display = "none"; - } - this.codeWidget.setVisible(managedLayer.codeVisible); - }}); - codeVisibilityControl.style.backgroundColor = codeVisible ? "rgba(255, 255, 255, 0.2)" : ""; - topRow.appendChild(codeVisibilityControl); + }); + topRow.appendChild(codeVisibilityControl.element); topRow.appendChild( makeMaximizeButton({ diff --git a/src/layer/image/index.ts b/src/layer/image/index.ts index 2d9e616480..ab89ca3474 100644 --- a/src/layer/image/index.ts +++ b/src/layer/image/index.ts @@ -55,6 +55,7 @@ import type { WatchableValueInterface } from "#src/trackable_value.js"; import { makeCachedDerivedWatchableValue, makeCachedLazyDerivedWatchableValue, + observeWatchable, registerNested, WatchableValue, } from "#src/trackable_value.js"; @@ -80,10 +81,10 @@ import { ShaderControlState, } from "#src/webgl/shader_ui_controls.js"; import { ChannelDimensionsWidget } from "#src/widget/channel_dimensions_widget.js"; +import { CheckboxIcon } from "#src/widget/checkbox_icon.js"; import { makeCopyButton } from "#src/widget/copy_button.js"; import type { DependentViewContext } from "#src/widget/dependent_view_widget.js"; import { makeHelpButton } from "#src/widget/help_button.js"; -import { makeIcon } from "#src/widget/icon.js"; import type { LayerControlDefinition } from "#src/widget/layer_control.js"; import { addLayerControlToOptionsTab, @@ -544,25 +545,19 @@ class RenderingOptionsTab extends Tab { topRow.appendChild(spacer); const managedLayer = this.layer.managedLayer; - const codeVisible = managedLayer.codeVisible; - this.codeWidget.setVisible(codeVisible); - const codeVisibilityControl = makeIcon({ - title: codeVisible ? "Hide code": "Show code", + this.registerDisposer( + observeWatchable((visible) => { + this.codeWidget.setVisible(visible); + }, managedLayer.codeVisible), + ); + + const codeVisibilityControl = new CheckboxIcon(managedLayer.codeVisible, { + enableTitle: "Hide code", + disableTitle: "Show code", + backgroundScheme: "dark", svg: svgCode, - onClick: () => { - const button = codeVisibilityControl as HTMLDivElement; - managedLayer.setCodeVisible(!managedLayer.codeVisible) - if (managedLayer.codeVisible) { - button.title = "Hide code"; - button.style.backgroundColor = "rgba(255, 255, 255, 0.2)"; - } else { - button.title = "Show code"; - button.style.backgroundColor = ""; - } - this.codeWidget.setVisible(managedLayer.codeVisible); - }}); - codeVisibilityControl.style.backgroundColor = codeVisible ? "rgba(255, 255, 255, 0.2)" : ""; - topRow.appendChild(codeVisibilityControl); + }); + topRow.appendChild(codeVisibilityControl.element); topRow.appendChild( makeMaximizeButton({ diff --git a/src/layer/index.ts b/src/layer/index.ts index fb894cd0f7..16927d667b 100644 --- a/src/layer/index.ts +++ b/src/layer/index.ts @@ -705,12 +705,11 @@ export class ManagedUserLayer extends RefCounted { set name(value: string) { if (value !== this.name_) { this.name_ = value; - this.layerChanged.dispatch(); } } visible = true; - codeVisible = true; + codeVisible = new TrackableBoolean(true, true); archived = false; get supportsPickOption() { @@ -756,6 +755,7 @@ export class ManagedUserLayer extends RefCounted { this.localVelocity, ), ); + this.codeVisible.changed.add(this.layerChanged.dispatch); } toJSON() { @@ -765,9 +765,7 @@ export class ManagedUserLayer extends RefCounted { } const layerSpec = userLayer.toJSON(); layerSpec.name = this.name; - if (!this.codeVisible) { - layerSpec.codeVisible = false; - } + layerSpec.codeVisible = this.codeVisible.toJSON(); if (!this.visible) { if (this.archived) { layerSpec.archived = true; @@ -779,8 +777,9 @@ export class ManagedUserLayer extends RefCounted { } setCodeVisible(value: boolean) { - if (value === this.codeVisible) return; - this.codeVisible = value; + this.codeVisible.value = value; + // if (value === this.codeVisible) return; + // this.codeVisible = value; this.layerChanged.dispatch(); } @@ -2028,12 +2027,15 @@ function initializeLayerFromSpecNoRestoreState( } else { managedLayer.visible = false; } - managedLayer.codeVisible = verifyOptionalObjectProperty( - spec, - "codeVisible", - verifyBoolean, - true, - ) + managedLayer.codeVisible.restoreState( + verifyOptionalObjectProperty(spec, "codeVisible", verifyBoolean, true), + ); + // managedLayer.codeVisible.value = verifyOptionalObjectProperty( + // spec, + // "codeVisible", + // verifyBoolean, + // true, + // ); const layerConstructor = layerTypes.get(layerType) || NewUserLayer; managedLayer.layer = new layerConstructor(managedLayer); return spec; diff --git a/src/layer/single_mesh/index.ts b/src/layer/single_mesh/index.ts index ca750f8575..dcb5c0af56 100644 --- a/src/layer/single_mesh/index.ts +++ b/src/layer/single_mesh/index.ts @@ -33,12 +33,12 @@ import { SingleMeshLayer, } from "#src/single_mesh/frontend.js"; import type { WatchableValueInterface } from "#src/trackable_value.js"; -import { WatchableValue } from "#src/trackable_value.js"; +import { observeWatchable, WatchableValue } from "#src/trackable_value.js"; import type { Borrowed } from "#src/util/disposable.js"; import { RefCounted } from "#src/util/disposable.js"; import { removeChildren, removeFromParent } from "#src/util/dom.js"; +import { CheckboxIcon } from "#src/widget/checkbox_icon.js"; import { makeHelpButton } from "#src/widget/help_button.js"; -import { makeIcon } from "#src/widget/icon.js"; import { makeMaximizeButton } from "#src/widget/maximize_button.js"; import { ShaderCodeWidget } from "#src/widget/shader_code_widget.js"; import { @@ -211,26 +211,19 @@ class DisplayOptionsTab extends Tab { topRow.appendChild(spacer); const managedLayer = this.layer.managedLayer; - const codeVisible = managedLayer.codeVisible; - this.codeWidget.element.style.display = managedLayer.codeVisible ? "block" : "none"; - this.codeWidget.setVisible(codeVisible); - const codeVisibilityControl = makeIcon({ - title: codeVisible ? "Hide code": "Show code", + this.registerDisposer( + observeWatchable((visible) => { + this.codeWidget.setVisible(visible); + }, managedLayer.codeVisible), + ); + + const codeVisibilityControl = new CheckboxIcon(managedLayer.codeVisible, { + enableTitle: "Hide code", + disableTitle: "Show code", + backgroundScheme: "dark", svg: svgCode, - onClick: () => { - const button = codeVisibilityControl as HTMLDivElement; - managedLayer.setCodeVisible(!managedLayer.codeVisible) - if (managedLayer.codeVisible) { - button.title = "Hide code"; - button.style.backgroundColor = "rgba(255, 255, 255, 0.2)"; - } else { - button.title = "Show code"; - button.style.backgroundColor = ""; - } - this.codeWidget.setVisible(managedLayer.codeVisible); - }}); - codeVisibilityControl.style.backgroundColor = codeVisible ? "rgba(255, 255, 255, 0.2)" : ""; - topRow.appendChild(codeVisibilityControl); + }); + topRow.appendChild(codeVisibilityControl.element); topRow.appendChild( makeMaximizeButton({ From 5b46a3f7ea0ef8bb794b1922490e7d2de214713b Mon Sep 17 00:00:00 2001 From: aranega Date: Thu, 5 Dec 2024 06:41:11 -0600 Subject: [PATCH 10/18] Fix tooltip swap for code visibility --- src/layer/annotation/index.ts | 4 ++-- src/layer/image/index.ts | 4 ++-- src/layer/index.ts | 6 ------ src/layer/single_mesh/index.ts | 4 ++-- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/layer/annotation/index.ts b/src/layer/annotation/index.ts index 11ab9447de..24494cae50 100644 --- a/src/layer/annotation/index.ts +++ b/src/layer/annotation/index.ts @@ -797,8 +797,8 @@ class RenderingOptionsTab extends Tab { }, managedLayer.codeVisible), ); const codeVisibilityControl = new CheckboxIcon(managedLayer.codeVisible, { - enableTitle: "Hide code", - disableTitle: "Show code", + enableTitle: "Show code", + disableTitle: "Hide code", backgroundScheme: "dark", svg: svgCode, }); diff --git a/src/layer/image/index.ts b/src/layer/image/index.ts index ab89ca3474..265509a743 100644 --- a/src/layer/image/index.ts +++ b/src/layer/image/index.ts @@ -552,8 +552,8 @@ class RenderingOptionsTab extends Tab { ); const codeVisibilityControl = new CheckboxIcon(managedLayer.codeVisible, { - enableTitle: "Hide code", - disableTitle: "Show code", + enableTitle: "Show code", + disableTitle: "Hide code", backgroundScheme: "dark", svg: svgCode, }); diff --git a/src/layer/index.ts b/src/layer/index.ts index 16927d667b..50eece844e 100644 --- a/src/layer/index.ts +++ b/src/layer/index.ts @@ -2030,12 +2030,6 @@ function initializeLayerFromSpecNoRestoreState( managedLayer.codeVisible.restoreState( verifyOptionalObjectProperty(spec, "codeVisible", verifyBoolean, true), ); - // managedLayer.codeVisible.value = verifyOptionalObjectProperty( - // spec, - // "codeVisible", - // verifyBoolean, - // true, - // ); const layerConstructor = layerTypes.get(layerType) || NewUserLayer; managedLayer.layer = new layerConstructor(managedLayer); return spec; diff --git a/src/layer/single_mesh/index.ts b/src/layer/single_mesh/index.ts index dcb5c0af56..8a46d69520 100644 --- a/src/layer/single_mesh/index.ts +++ b/src/layer/single_mesh/index.ts @@ -218,8 +218,8 @@ class DisplayOptionsTab extends Tab { ); const codeVisibilityControl = new CheckboxIcon(managedLayer.codeVisible, { - enableTitle: "Hide code", - disableTitle: "Show code", + enableTitle: "Show code", + disableTitle: "Hide code", backgroundScheme: "dark", svg: svgCode, }); From feeba32bb6d13e86d6d42fe847fc00b88c82bfa8 Mon Sep 17 00:00:00 2001 From: Sean Martin Date: Thu, 5 Dec 2024 14:30:30 +0100 Subject: [PATCH 11/18] refactor: remove unused commented code --- src/layer/index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/layer/index.ts b/src/layer/index.ts index 50eece844e..8cb0c3e512 100644 --- a/src/layer/index.ts +++ b/src/layer/index.ts @@ -778,8 +778,6 @@ export class ManagedUserLayer extends RefCounted { setCodeVisible(value: boolean) { this.codeVisible.value = value; - // if (value === this.codeVisible) return; - // this.codeVisible = value; this.layerChanged.dispatch(); } From 7ce500c04b104089e19543d41573d56dd816c6f2 Mon Sep 17 00:00:00 2001 From: aranega Date: Wed, 18 Dec 2024 07:02:37 -0600 Subject: [PATCH 12/18] Add back event dispatch on name change for layers --- src/layer/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/layer/index.ts b/src/layer/index.ts index 50eece844e..d8d5f85e73 100644 --- a/src/layer/index.ts +++ b/src/layer/index.ts @@ -705,6 +705,7 @@ export class ManagedUserLayer extends RefCounted { set name(value: string) { if (value !== this.name_) { this.name_ = value; + this.layerChanged.dispatch(); } } From 2a57ea59eaa32618c84e6a7410155f67c74a0bf5 Mon Sep 17 00:00:00 2001 From: Sean Martin Date: Wed, 22 Jan 2025 19:04:22 +0100 Subject: [PATCH 13/18] feat: save test on image user layer code visible --- src/layer/image/index.ts | 18 ++++++++++++------ src/layer/index.ts | 12 ++++++------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/layer/image/index.ts b/src/layer/image/index.ts index 265509a743..9e62104a75 100644 --- a/src/layer/image/index.ts +++ b/src/layer/image/index.ts @@ -50,12 +50,12 @@ import { } from "#src/sliceview/volume/image_renderlayer.js"; import { trackableAlphaValue } from "#src/trackable_alpha.js"; import { trackableBlendModeValue } from "#src/trackable_blend.js"; +import { TrackableBoolean } from "#src/trackable_boolean.js"; import { trackableFiniteFloat } from "#src/trackable_finite_float.js"; import type { WatchableValueInterface } from "#src/trackable_value.js"; import { makeCachedDerivedWatchableValue, makeCachedLazyDerivedWatchableValue, - observeWatchable, registerNested, WatchableValue, } from "#src/trackable_value.js"; @@ -108,6 +108,7 @@ import { Tab } from "#src/widget/tab_view.js"; const OPACITY_JSON_KEY = "opacity"; const BLEND_JSON_KEY = "blend"; const SHADER_JSON_KEY = "shader"; +const CODE_VISIBLE_KEY = "codeVisibleTEST"; const SHADER_CONTROLS_JSON_KEY = "shaderControls"; const CROSS_SECTION_RENDER_SCALE_JSON_KEY = "crossSectionRenderScale"; const CHANNEL_DIMENSIONS_JSON_KEY = "channelDimensions"; @@ -127,6 +128,7 @@ const [ export class ImageUserLayer extends Base { opacity = trackableAlphaValue(0.5); blendMode = trackableBlendModeValue(); + codeVisible = new TrackableBoolean(true, true); fragmentMain = getTrackableFragmentMain(); shaderError = makeWatchableShaderError(); dataType = new WatchableValue(undefined); @@ -207,6 +209,9 @@ export class ImageUserLayer extends Base { isLocalDimension; this.blendMode.changed.add(this.specificationChanged.dispatch); this.opacity.changed.add(this.specificationChanged.dispatch); + this.codeVisible.changed.add(() => { + this.specificationChanged.dispatch; + }); this.volumeRenderingGain.changed.add(this.specificationChanged.dispatch); this.fragmentMain.changed.add(this.specificationChanged.dispatch); this.shaderControlState.changed.add(this.specificationChanged.dispatch); @@ -296,6 +301,7 @@ export class ImageUserLayer extends Base { restoreState(specification: any) { super.restoreState(specification); this.opacity.restoreState(specification[OPACITY_JSON_KEY]); + this.codeVisible.restoreState(specification[CODE_VISIBLE_KEY]); verifyOptionalObjectProperty(specification, BLEND_JSON_KEY, (blendValue) => this.blendMode.restoreState(blendValue), ); @@ -341,6 +347,7 @@ export class ImageUserLayer extends Base { const x = super.toJSON(); x[OPACITY_JSON_KEY] = this.opacity.toJSON(); x[BLEND_JSON_KEY] = this.blendMode.toJSON(); + x[CODE_VISIBLE_KEY] = this.codeVisible.toJSON(); x[SHADER_JSON_KEY] = this.fragmentMain.toJSON(); x[SHADER_CONTROLS_JSON_KEY] = this.shaderControlState.toJSON(); x[CROSS_SECTION_RENDER_SCALE_JSON_KEY] = @@ -544,14 +551,13 @@ class RenderingOptionsTab extends Tab { topRow.appendChild(document.createTextNode("Shader")); topRow.appendChild(spacer); - const managedLayer = this.layer.managedLayer; this.registerDisposer( - observeWatchable((visible) => { - this.codeWidget.setVisible(visible); - }, managedLayer.codeVisible), + this.layer.codeVisible.changed.add(() => { + this.codeWidget.setVisible(this.layer.codeVisible.value); + }), ); - const codeVisibilityControl = new CheckboxIcon(managedLayer.codeVisible, { + const codeVisibilityControl = new CheckboxIcon(this.layer.codeVisible, { enableTitle: "Show code", disableTitle: "Hide code", backgroundScheme: "dark", diff --git a/src/layer/index.ts b/src/layer/index.ts index a592e1a6ec..4387cc0563 100644 --- a/src/layer/index.ts +++ b/src/layer/index.ts @@ -756,7 +756,7 @@ export class ManagedUserLayer extends RefCounted { this.localVelocity, ), ); - this.codeVisible.changed.add(this.layerChanged.dispatch); + // this.codeVisible.changed.add(this.layerChanged.dispatch); } toJSON() { @@ -766,7 +766,7 @@ export class ManagedUserLayer extends RefCounted { } const layerSpec = userLayer.toJSON(); layerSpec.name = this.name; - layerSpec.codeVisible = this.codeVisible.toJSON(); + // layerSpec.codeVisible = this.codeVisible.toJSON(); if (!this.visible) { if (this.archived) { layerSpec.archived = true; @@ -779,7 +779,7 @@ export class ManagedUserLayer extends RefCounted { setCodeVisible(value: boolean) { this.codeVisible.value = value; - this.layerChanged.dispatch(); + // this.layerChanged.dispatch(); } setVisible(value: boolean) { @@ -2026,9 +2026,9 @@ function initializeLayerFromSpecNoRestoreState( } else { managedLayer.visible = false; } - managedLayer.codeVisible.restoreState( - verifyOptionalObjectProperty(spec, "codeVisible", verifyBoolean, true), - ); + // managedLayer.codeVisible.restoreState( + // verifyOptionalObjectProperty(spec, "codeVisible", verifyBoolean, true), + // ); const layerConstructor = layerTypes.get(layerType) || NewUserLayer; managedLayer.layer = new layerConstructor(managedLayer); return spec; From 0e618b8712fcf35bb1426cd0e68211ad865f3b29 Mon Sep 17 00:00:00 2001 From: Sean Martin Date: Thu, 23 Jan 2025 11:37:12 +0100 Subject: [PATCH 14/18] fix: correct linking state and refactor code visibility --- src/layer/image/index.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/layer/image/index.ts b/src/layer/image/index.ts index 9e62104a75..375336b367 100644 --- a/src/layer/image/index.ts +++ b/src/layer/image/index.ts @@ -50,7 +50,10 @@ import { } from "#src/sliceview/volume/image_renderlayer.js"; import { trackableAlphaValue } from "#src/trackable_alpha.js"; import { trackableBlendModeValue } from "#src/trackable_blend.js"; -import { TrackableBoolean } from "#src/trackable_boolean.js"; +import { + TrackableBoolean, + ElementVisibilityFromTrackableBoolean, +} from "#src/trackable_boolean.js"; import { trackableFiniteFloat } from "#src/trackable_finite_float.js"; import type { WatchableValueInterface } from "#src/trackable_value.js"; import { @@ -108,7 +111,7 @@ import { Tab } from "#src/widget/tab_view.js"; const OPACITY_JSON_KEY = "opacity"; const BLEND_JSON_KEY = "blend"; const SHADER_JSON_KEY = "shader"; -const CODE_VISIBLE_KEY = "codeVisibleTEST"; +const CODE_VISIBLE_KEY = "codeVisible"; const SHADER_CONTROLS_JSON_KEY = "shaderControls"; const CROSS_SECTION_RENDER_SCALE_JSON_KEY = "crossSectionRenderScale"; const CHANNEL_DIMENSIONS_JSON_KEY = "channelDimensions"; @@ -128,7 +131,7 @@ const [ export class ImageUserLayer extends Base { opacity = trackableAlphaValue(0.5); blendMode = trackableBlendModeValue(); - codeVisible = new TrackableBoolean(true, true); + codeVisible = new TrackableBoolean(true); fragmentMain = getTrackableFragmentMain(); shaderError = makeWatchableShaderError(); dataType = new WatchableValue(undefined); @@ -209,9 +212,7 @@ export class ImageUserLayer extends Base { isLocalDimension; this.blendMode.changed.add(this.specificationChanged.dispatch); this.opacity.changed.add(this.specificationChanged.dispatch); - this.codeVisible.changed.add(() => { - this.specificationChanged.dispatch; - }); + this.codeVisible.changed.add(this.specificationChanged.dispatch); this.volumeRenderingGain.changed.add(this.specificationChanged.dispatch); this.fragmentMain.changed.add(this.specificationChanged.dispatch); this.shaderControlState.changed.add(this.specificationChanged.dispatch); @@ -552,9 +553,10 @@ class RenderingOptionsTab extends Tab { topRow.appendChild(spacer); this.registerDisposer( - this.layer.codeVisible.changed.add(() => { - this.codeWidget.setVisible(this.layer.codeVisible.value); - }), + new ElementVisibilityFromTrackableBoolean( + this.layer.codeVisible, + this.codeWidget.element, + ), ); const codeVisibilityControl = new CheckboxIcon(this.layer.codeVisible, { From 0a81f6aba4a6fd239d919c805e60b98790350214 Mon Sep 17 00:00:00 2001 From: aranega Date: Thu, 23 Jan 2025 08:20:12 -0600 Subject: [PATCH 15/18] Move code visibility option to user layer for mesh and annotations --- src/layer/annotation/index.ts | 16 +++++++++++----- src/layer/single_mesh/index.ts | 23 ++++++++++++++++------- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/layer/annotation/index.ts b/src/layer/annotation/index.ts index 13f257d14f..2761889a56 100644 --- a/src/layer/annotation/index.ts +++ b/src/layer/annotation/index.ts @@ -44,8 +44,10 @@ import { Overlay } from "#src/overlay.js"; import { getWatchableRenderLayerTransform } from "#src/render_coordinate_transform.js"; import { RenderLayerRole } from "#src/renderlayer.js"; import type { SegmentationDisplayState } from "#src/segmentation_display_state/frontend.js"; -import type { TrackableBoolean } from "#src/trackable_boolean.js"; -import { TrackableBooleanCheckbox } from "#src/trackable_boolean.js"; +import { + TrackableBoolean, + TrackableBooleanCheckbox, +} from "#src/trackable_boolean.js"; import { makeCachedLazyDerivedWatchableValue, observeWatchable, @@ -143,6 +145,7 @@ interface LinkedSegmentationLayer { const LINKED_SEGMENTATION_LAYER_JSON_KEY = "linkedSegmentationLayer"; const FILTER_BY_SEGMENTATION_JSON_KEY = "filterBySegmentation"; const IGNORE_NULL_SEGMENT_FILTER_JSON_KEY = "ignoreNullSegmentFilter"; +const CODE_VISIBLE_KEY = "codeVisible"; class LinkedSegmentationLayers extends RefCounted { changed = new NullarySignal(); @@ -387,6 +390,7 @@ class LinkedSegmentationLayersWidget extends RefCounted { const Base = UserLayerWithAnnotationsMixin(UserLayer); export class AnnotationUserLayer extends Base { localAnnotations: LocalAnnotationSource | undefined; + codeVisible = new TrackableBoolean(true); private localAnnotationProperties: AnnotationPropertySpec[] | undefined; private localAnnotationRelationships: string[]; private localAnnotationsJson: any = undefined; @@ -412,6 +416,7 @@ export class AnnotationUserLayer extends Base { this.linkedSegmentationLayers.changed.add( this.specificationChanged.dispatch, ); + this.codeVisible.changed.add(this.specificationChanged.dispatch); this.annotationDisplayState.ignoreNullSegmentFilter.changed.add( this.specificationChanged.dispatch, ); @@ -432,6 +437,7 @@ export class AnnotationUserLayer extends Base { restoreState(specification: any) { super.restoreState(specification); this.linkedSegmentationLayers.restoreState(specification); + this.codeVisible.restoreState(specification[CODE_VISIBLE_KEY]); this.localAnnotationsJson = specification[ANNOTATIONS_JSON_KEY]; this.localAnnotationProperties = verifyOptionalObjectProperty( specification, @@ -693,6 +699,7 @@ export class AnnotationUserLayer extends Base { const x = super.toJSON(); x[CROSS_SECTION_RENDER_SCALE_JSON_KEY] = this.annotationCrossSectionRenderScaleTarget.toJSON(); + x[CODE_VISIBLE_KEY] = this.codeVisible.toJSON(); x[PROJECTION_RENDER_SCALE_JSON_KEY] = this.annotationProjectionRenderScaleTarget.toJSON(); if (this.localAnnotations !== undefined) { @@ -791,14 +798,13 @@ class RenderingOptionsTab extends Tab { label.textContent = "Annotation shader:"; topRow.appendChild(label); - const managedLayer = this.layer.managedLayer; this.registerDisposer( observeWatchable((visible) => { shaderProperties.style.display = visible ? "block" : "none"; this.codeWidget.setVisible(visible); - }, managedLayer.codeVisible), + }, this.layer.codeVisible), ); - const codeVisibilityControl = new CheckboxIcon(managedLayer.codeVisible, { + const codeVisibilityControl = new CheckboxIcon(this.layer.codeVisible, { enableTitle: "Show code", disableTitle: "Hide code", backgroundScheme: "dark", diff --git a/src/layer/single_mesh/index.ts b/src/layer/single_mesh/index.ts index c1554921b1..094dea43ab 100644 --- a/src/layer/single_mesh/index.ts +++ b/src/layer/single_mesh/index.ts @@ -32,8 +32,12 @@ import { SingleMeshDisplayState, SingleMeshLayer, } from "#src/single_mesh/frontend.js"; +import { + ElementVisibilityFromTrackableBoolean, + TrackableBoolean, +} from "#src/trackable_boolean.js"; import type { WatchableValueInterface } from "#src/trackable_value.js"; -import { observeWatchable, WatchableValue } from "#src/trackable_value.js"; +import { WatchableValue } from "#src/trackable_value.js"; import type { Borrowed } from "#src/util/disposable.js"; import { RefCounted } from "#src/util/disposable.js"; import { removeChildren, removeFromParent } from "#src/util/dom.js"; @@ -49,14 +53,18 @@ import { Tab } from "#src/widget/tab_view.js"; const SHADER_JSON_KEY = "shader"; const SHADER_CONTROLS_JSON_KEY = "shaderControls"; +const CODE_VISIBLE_KEY = "codeVisible"; export class SingleMeshUserLayer extends UserLayer { displayState = new SingleMeshDisplayState(); + codeVisible = new TrackableBoolean(true); + vertexAttributes = new WatchableValue( undefined, ); constructor(public managedLayer: Borrowed) { super(managedLayer); + this.codeVisible.changed.add(this.specificationChanged.dispatch); this.registerDisposer( this.displayState.shaderControlState.changed.add( this.specificationChanged.dispatch, @@ -77,6 +85,7 @@ export class SingleMeshUserLayer extends UserLayer { restoreState(specification: any) { super.restoreState(specification); + this.codeVisible.restoreState(specification[CODE_VISIBLE_KEY]); this.displayState.fragmentMain.restoreState(specification[SHADER_JSON_KEY]); this.displayState.shaderControlState.restoreState( specification[SHADER_CONTROLS_JSON_KEY], @@ -118,6 +127,7 @@ export class SingleMeshUserLayer extends UserLayer { const x = super.toJSON(); x[SHADER_JSON_KEY] = this.displayState.fragmentMain.toJSON(); x[SHADER_CONTROLS_JSON_KEY] = this.displayState.shaderControlState.toJSON(); + x[CODE_VISIBLE_KEY] = this.codeVisible.toJSON(); return x; } @@ -213,14 +223,13 @@ class DisplayOptionsTab extends Tab { topRow.appendChild(spacer); - const managedLayer = this.layer.managedLayer; this.registerDisposer( - observeWatchable((visible) => { - this.codeWidget.setVisible(visible); - }, managedLayer.codeVisible), + new ElementVisibilityFromTrackableBoolean( + this.layer.codeVisible, + this.codeWidget.element, + ), ); - - const codeVisibilityControl = new CheckboxIcon(managedLayer.codeVisible, { + const codeVisibilityControl = new CheckboxIcon(this.layer.codeVisible, { enableTitle: "Show code", disableTitle: "Hide code", backgroundScheme: "dark", From 6253e2fcba8b1b0d3a6c5c13bcb6cda3081b6deb Mon Sep 17 00:00:00 2001 From: Sean Martin Date: Thu, 23 Jan 2025 15:38:07 +0100 Subject: [PATCH 16/18] refactor: remove old comments and use ElementVisibilityFromTrackableBoolean --- src/layer/annotation/index.ts | 20 ++++++++++++-------- src/layer/index.ts | 11 ----------- src/widget/shader_code_widget.ts | 8 -------- 3 files changed, 12 insertions(+), 27 deletions(-) diff --git a/src/layer/annotation/index.ts b/src/layer/annotation/index.ts index 2761889a56..eba7104c4e 100644 --- a/src/layer/annotation/index.ts +++ b/src/layer/annotation/index.ts @@ -47,11 +47,9 @@ import type { SegmentationDisplayState } from "#src/segmentation_display_state/f import { TrackableBoolean, TrackableBooleanCheckbox, + ElementVisibilityFromTrackableBoolean, } from "#src/trackable_boolean.js"; -import { - makeCachedLazyDerivedWatchableValue, - observeWatchable, -} from "#src/trackable_value.js"; +import { makeCachedLazyDerivedWatchableValue } from "#src/trackable_value.js"; import type { AnnotationLayerView, MergedAnnotationStates, @@ -799,10 +797,16 @@ class RenderingOptionsTab extends Tab { topRow.appendChild(label); this.registerDisposer( - observeWatchable((visible) => { - shaderProperties.style.display = visible ? "block" : "none"; - this.codeWidget.setVisible(visible); - }, this.layer.codeVisible), + new ElementVisibilityFromTrackableBoolean( + this.layer.codeVisible, + this.codeWidget.element, + ), + ); + this.registerDisposer( + new ElementVisibilityFromTrackableBoolean( + this.layer.codeVisible, + shaderProperties, + ), ); const codeVisibilityControl = new CheckboxIcon(this.layer.codeVisible, { enableTitle: "Show code", diff --git a/src/layer/index.ts b/src/layer/index.ts index a2c12b2e84..fea87063da 100644 --- a/src/layer/index.ts +++ b/src/layer/index.ts @@ -711,7 +711,6 @@ export class ManagedUserLayer extends RefCounted { } visible = true; - codeVisible = new TrackableBoolean(true, true); archived = false; get supportsPickOption() { @@ -757,7 +756,6 @@ export class ManagedUserLayer extends RefCounted { this.localVelocity, ), ); - // this.codeVisible.changed.add(this.layerChanged.dispatch); } toJSON() { @@ -767,7 +765,6 @@ export class ManagedUserLayer extends RefCounted { } const layerSpec = userLayer.toJSON(); layerSpec.name = this.name; - // layerSpec.codeVisible = this.codeVisible.toJSON(); if (!this.visible) { if (this.archived) { layerSpec.archived = true; @@ -778,11 +775,6 @@ export class ManagedUserLayer extends RefCounted { return layerSpec; } - setCodeVisible(value: boolean) { - this.codeVisible.value = value; - // this.layerChanged.dispatch(); - } - setVisible(value: boolean) { if (value === this.visible) return; if (value && this.archived) { @@ -2027,9 +2019,6 @@ function initializeLayerFromSpecNoRestoreState( } else { managedLayer.visible = false; } - // managedLayer.codeVisible.restoreState( - // verifyOptionalObjectProperty(spec, "codeVisible", verifyBoolean, true), - // ); const layerConstructor = layerTypes.get(layerType) || NewUserLayer; managedLayer.layer = new layerConstructor(managedLayer); return spec; diff --git a/src/widget/shader_code_widget.ts b/src/widget/shader_code_widget.ts index ea57244f0a..df08d64f1a 100644 --- a/src/widget/shader_code_widget.ts +++ b/src/widget/shader_code_widget.ts @@ -189,12 +189,4 @@ export class ShaderCodeWidget extends RefCounted { this.textEditor = undefined; super.disposed(); } - - setVisible(visible: boolean) { - this.element.style.display = visible ? "block" : "none"; - } - - isVisible() { - return this.element.style.display === "block"; - } } From 035ef488160cab881baa2a19bcc10602d0a5573a Mon Sep 17 00:00:00 2001 From: aranega Date: Thu, 27 Feb 2025 08:16:27 -0600 Subject: [PATCH 17/18] CC-122 Extract top row as a function and add shadder top row for skeleton --- src/layer/annotation/index.ts | 88 +++++++++++----------- src/layer/annotation/style.css | 6 ++ src/layer/image/index.ts | 84 +++++++++++---------- src/layer/segmentation/index.ts | 4 + src/layer/segmentation/json_keys.ts | 1 + src/layer/single_mesh/index.ts | 82 +++++++++++--------- src/ui/segmentation_display_options_tab.ts | 74 ++++++++++++------ 7 files changed, 200 insertions(+), 139 deletions(-) diff --git a/src/layer/annotation/index.ts b/src/layer/annotation/index.ts index eba7104c4e..93e3e88b43 100644 --- a/src/layer/annotation/index.ts +++ b/src/layer/annotation/index.ts @@ -735,6 +735,50 @@ function makeShaderCodeWidget(layer: AnnotationUserLayer) { }); } +function makeShaderCodeWidgetTopRow( + layer: AnnotationUserLayer, + codeWidget: ShaderCodeWidget, +) { + const spacer = document.createElement("div"); + spacer.style.flex = "1"; + + const topRow = document.createElement("div"); + topRow.className = "neuroglancer-annotation-dropdown-shader-top-row"; + topRow.appendChild(document.createTextNode("Shader")); + topRow.appendChild(spacer); + + layer.registerDisposer( + new ElementVisibilityFromTrackableBoolean( + layer.codeVisible, + codeWidget.element, + ), + ); + + const codeVisibilityControl = new CheckboxIcon(layer.codeVisible, { + enableTitle: "Show code", + disableTitle: "Hide code", + backgroundScheme: "dark", + svg: svgCode, + }); + topRow.appendChild(codeVisibilityControl.element); + + topRow.appendChild( + makeMaximizeButton({ + title: "Show larger editor view", + onClick: () => { + new ShaderCodeOverlay(layer); + }, + }), + ); + topRow.appendChild( + makeHelpButton({ + title: "Documentation on image layer rendering", + href: "https://github.com/google/neuroglancer/blob/master/src/annotation/rendering.md", + }), + ); + return topRow; +} + class ShaderCodeOverlay extends Overlay { codeWidget: ShaderCodeWidget; constructor(public layer: AnnotationUserLayer) { @@ -788,49 +832,9 @@ class RenderingOptionsTab extends Tab { ).element; element.appendChild(shaderProperties); - const topRow = document.createElement("div"); - topRow.className = - "neuroglancer-segmentation-dropdown-skeleton-shader-header"; - const label = document.createElement("div"); - label.style.flex = "1"; - label.textContent = "Annotation shader:"; - topRow.appendChild(label); - - this.registerDisposer( - new ElementVisibilityFromTrackableBoolean( - this.layer.codeVisible, - this.codeWidget.element, - ), - ); - this.registerDisposer( - new ElementVisibilityFromTrackableBoolean( - this.layer.codeVisible, - shaderProperties, - ), - ); - const codeVisibilityControl = new CheckboxIcon(this.layer.codeVisible, { - enableTitle: "Show code", - disableTitle: "Hide code", - backgroundScheme: "dark", - svg: svgCode, - }); - topRow.appendChild(codeVisibilityControl.element); - - topRow.appendChild( - makeMaximizeButton({ - title: "Show larger editor view", - onClick: () => { - new ShaderCodeOverlay(this.layer); - }, - }), - ); - topRow.appendChild( - makeHelpButton({ - title: "Documentation on annotation rendering", - href: "https://github.com/google/neuroglancer/blob/master/src/annotation/rendering.md", - }), + element.appendChild( + makeShaderCodeWidgetTopRow(this.layer, this.codeWidget), ); - element.appendChild(topRow); element.appendChild(this.codeWidget.element); element.appendChild( diff --git a/src/layer/annotation/style.css b/src/layer/annotation/style.css index 1c9238ff8f..137b1a2dd9 100644 --- a/src/layer/annotation/style.css +++ b/src/layer/annotation/style.css @@ -24,6 +24,12 @@ flex-shrink: 0; } +.neuroglancer-annotation-dropdown-shader-top-row { + display: flex; + flex-direction: row; + align-items: center; +} + .neuroglancer-annotation-shader-property-list { max-height: 8em; overflow: auto; diff --git a/src/layer/image/index.ts b/src/layer/image/index.ts index 189d7e1b14..1c452cf0cd 100644 --- a/src/layer/image/index.ts +++ b/src/layer/image/index.ts @@ -470,6 +470,50 @@ function makeShaderCodeWidget(layer: ImageUserLayer) { }); } +function makeShaderCodeWidgetTopRow( + layer: ImageUserLayer, + codeWidget: ShaderCodeWidget, +) { + const spacer = document.createElement("div"); + spacer.style.flex = "1"; + + const topRow = document.createElement("div"); + topRow.className = "neuroglancer-image-dropdown-top-row"; + topRow.appendChild(document.createTextNode("Shader")); + topRow.appendChild(spacer); + + layer.registerDisposer( + new ElementVisibilityFromTrackableBoolean( + layer.codeVisible, + codeWidget.element, + ), + ); + + const codeVisibilityControl = new CheckboxIcon(layer.codeVisible, { + enableTitle: "Show code", + disableTitle: "Hide code", + backgroundScheme: "dark", + svg: svgCode, + }); + topRow.appendChild(codeVisibilityControl.element); + + topRow.appendChild( + makeMaximizeButton({ + title: "Show larger editor view", + onClick: () => { + new ShaderCodeOverlay(layer); + }, + }), + ); + topRow.appendChild( + makeHelpButton({ + title: "Documentation on image layer rendering", + href: "https://github.com/google/neuroglancer/blob/master/src/sliceview/image_layer_rendering.md", + }), + ); + return topRow; +} + const LAYER_CONTROLS: LayerControlDefinition[] = [ { label: "Resolution (slice)", @@ -545,45 +589,9 @@ class RenderingOptionsTab extends Tab { ); } - const spacer = document.createElement("div"); - spacer.style.flex = "1"; - - const topRow = document.createElement("div"); - topRow.className = "neuroglancer-image-dropdown-top-row"; - topRow.appendChild(document.createTextNode("Shader")); - topRow.appendChild(spacer); - - this.registerDisposer( - new ElementVisibilityFromTrackableBoolean( - this.layer.codeVisible, - this.codeWidget.element, - ), - ); - - const codeVisibilityControl = new CheckboxIcon(this.layer.codeVisible, { - enableTitle: "Show code", - disableTitle: "Hide code", - backgroundScheme: "dark", - svg: svgCode, - }); - topRow.appendChild(codeVisibilityControl.element); - - topRow.appendChild( - makeMaximizeButton({ - title: "Show larger editor view", - onClick: () => { - new ShaderCodeOverlay(this.layer); - }, - }), - ); - topRow.appendChild( - makeHelpButton({ - title: "Documentation on image layer rendering", - href: "https://github.com/google/neuroglancer/blob/master/src/sliceview/image_layer_rendering.md", - }), + element.appendChild( + makeShaderCodeWidgetTopRow(this.layer, this.codeWidget), ); - - element.appendChild(topRow); element.appendChild( this.registerDisposer( new ChannelDimensionsWidget(layer.channelCoordinateSpaceCombiner), diff --git a/src/layer/segmentation/index.ts b/src/layer/segmentation/index.ts index 8551e8c576..a95480d4e2 100644 --- a/src/layer/segmentation/index.ts +++ b/src/layer/segmentation/index.ts @@ -578,6 +578,7 @@ const Base = UserLayerWithAnnotationsMixin(UserLayer); export class SegmentationUserLayer extends Base { sliceViewRenderScaleHistogram = new RenderScaleHistogram(); sliceViewRenderScaleTarget = trackableRenderScaleTarget(1); + codeVisible = new TrackableBoolean(true); graphConnection = new WatchableValue< SegmentationGraphSourceConnection | undefined @@ -622,6 +623,7 @@ export class SegmentationUserLayer extends Base { constructor(managedLayer: Borrowed) { super(managedLayer); + this.codeVisible.changed.add(this.specificationChanged.dispatch); this.registerDisposer( registerNestedSync((context, group) => { context.registerDisposer( @@ -986,6 +988,7 @@ export class SegmentationUserLayer extends Base { if (skeletonShader !== undefined) { skeletonRenderingOptions.shader.restoreState(skeletonShader); } + this.codeVisible.restoreState(json_keys.SKELETON_CODE_VISIBLE_KEY); this.displayState.renderScaleTarget.restoreState( specification[json_keys.MESH_RENDER_SCALE_JSON_KEY], ); @@ -1041,6 +1044,7 @@ export class SegmentationUserLayer extends Base { x[json_keys.ANCHOR_SEGMENT_JSON_KEY] = this.anchorSegment.toJSON(); x[json_keys.SKELETON_RENDERING_JSON_KEY] = this.displayState.skeletonRenderingOptions.toJSON(); + x[json_keys.SKELETON_CODE_VISIBLE_KEY] = this.codeVisible.toJSON(); x[json_keys.MESH_RENDER_SCALE_JSON_KEY] = this.displayState.renderScaleTarget.toJSON(); x[json_keys.CROSS_SECTION_RENDER_SCALE_JSON_KEY] = diff --git a/src/layer/segmentation/json_keys.ts b/src/layer/segmentation/json_keys.ts index 96417a61ca..47d603dab4 100644 --- a/src/layer/segmentation/json_keys.ts +++ b/src/layer/segmentation/json_keys.ts @@ -16,6 +16,7 @@ export const MESH_RENDER_SCALE_JSON_KEY = "meshRenderScale"; export const CROSS_SECTION_RENDER_SCALE_JSON_KEY = "crossSectionRenderScale"; export const SKELETON_RENDERING_JSON_KEY = "skeletonRendering"; export const SKELETON_SHADER_JSON_KEY = "skeletonShader"; +export const SKELETON_CODE_VISIBLE_KEY = "codeVisible"; export const SEGMENT_QUERY_JSON_KEY = "segmentQuery"; export const MESH_SILHOUETTE_RENDERING_JSON_KEY = "meshSilhouetteRendering"; export const LINKED_SEGMENTATION_GROUP_JSON_KEY = "linkedSegmentationGroup"; diff --git a/src/layer/single_mesh/index.ts b/src/layer/single_mesh/index.ts index 52f03dfff7..1d83b8079a 100644 --- a/src/layer/single_mesh/index.ts +++ b/src/layer/single_mesh/index.ts @@ -143,6 +143,50 @@ function makeShaderCodeWidget(layer: SingleMeshUserLayer) { }); } +function makeShaderCodeWidgetTopRow( + layer: SingleMeshUserLayer, + codeWidget: ShaderCodeWidget, +) { + const spacer = document.createElement("div"); + spacer.style.flex = "1"; + + const topRow = document.createElement("div"); + topRow.className = "neuroglancer-single-mesh-dropdown-top-row"; + topRow.appendChild(document.createTextNode("Shader")); + topRow.appendChild(spacer); + + layer.registerDisposer( + new ElementVisibilityFromTrackableBoolean( + layer.codeVisible, + codeWidget.element, + ), + ); + + const codeVisibilityControl = new CheckboxIcon(layer.codeVisible, { + enableTitle: "Show code", + disableTitle: "Hide code", + backgroundScheme: "dark", + svg: svgCode, + }); + topRow.appendChild(codeVisibilityControl.element); + + topRow.appendChild( + makeMaximizeButton({ + title: "Show larger editor view", + onClick: () => { + new ShaderCodeOverlay(layer); + }, + }), + ); + topRow.appendChild( + makeHelpButton({ + title: "Documentation on image layer rendering", + href: "https://github.com/google/neuroglancer/blob/master/src/sliceview/image_layer_rendering.md", + }), + ); + return topRow; +} + class VertexAttributeWidget extends RefCounted { element = document.createElement("div"); constructor( @@ -216,43 +260,9 @@ class DisplayOptionsTab extends Tab { ); this.codeWidget = this.registerDisposer(makeShaderCodeWidget(layer)); element.classList.add("neuroglancer-single-mesh-dropdown"); - const topRow = document.createElement("div"); - topRow.className = "neuroglancer-single-mesh-dropdown-top-row"; - const spacer = document.createElement("div"); - spacer.style.flex = "1"; - - topRow.appendChild(spacer); - - this.registerDisposer( - new ElementVisibilityFromTrackableBoolean( - this.layer.codeVisible, - this.codeWidget.element, - ), - ); - const codeVisibilityControl = new CheckboxIcon(this.layer.codeVisible, { - enableTitle: "Show code", - disableTitle: "Hide code", - backgroundScheme: "dark", - svg: svgCode, - }); - topRow.appendChild(codeVisibilityControl.element); - - topRow.appendChild( - makeMaximizeButton({ - title: "Show larger editor view", - onClick: () => { - new ShaderCodeOverlay(this.layer); - }, - }), - ); - topRow.appendChild( - makeHelpButton({ - title: "Documentation on single mesh layer rendering", - href: "https://github.com/google/neuroglancer/blob/master/src/sliceview/image_layer_rendering.md", - }), + element.appendChild( + makeShaderCodeWidgetTopRow(this.layer, this.codeWidget), ); - - element.appendChild(topRow); element.appendChild(this.attributeWidget.element); element.appendChild(this.codeWidget.element); element.appendChild( diff --git a/src/ui/segmentation_display_options_tab.ts b/src/ui/segmentation_display_options_tab.ts index b145d4a398..634bc3df41 100644 --- a/src/ui/segmentation_display_options_tab.ts +++ b/src/ui/segmentation_display_options_tab.ts @@ -14,10 +14,13 @@ * limitations under the License. */ +import svgCode from "ikonate/icons/code-alt.svg?raw"; import type { SegmentationUserLayer } from "#src/layer/segmentation/index.js"; import { SKELETON_RENDERING_SHADER_CONTROL_TOOL_ID } from "#src/layer/segmentation/json_keys.js"; import { LAYER_CONTROLS } from "#src/layer/segmentation/layer_controls.js"; import { Overlay } from "#src/overlay.js"; +import { ElementVisibilityFromTrackableBoolean } from "#src/trackable_boolean.js"; +import { CheckboxIcon } from "#src/widget/checkbox_icon.js"; import { DependentViewWidget } from "#src/widget/dependent_view_widget.js"; import { makeHelpButton } from "#src/widget/help_button.js"; import { addLayerControlToOptionsTab } from "#src/widget/layer_control.js"; @@ -36,6 +39,51 @@ function makeSkeletonShaderCodeWidget(layer: SegmentationUserLayer) { }); } +function makeShaderCodeWidgetTopRow( + layer: SegmentationUserLayer, + codeWidget: ShaderCodeWidget, +) { + const spacer = document.createElement("div"); + spacer.style.flex = "1"; + + const topRow = document.createElement("div"); + topRow.className = + "neuroglancer-segmentation-dropdown-skeleton-shader-header"; + topRow.appendChild(document.createTextNode("Shader")); + topRow.appendChild(spacer); + + layer.registerDisposer( + new ElementVisibilityFromTrackableBoolean( + layer.codeVisible, + codeWidget.element, + ), + ); + + const codeVisibilityControl = new CheckboxIcon(layer.codeVisible, { + enableTitle: "Show code", + disableTitle: "Hide code", + backgroundScheme: "dark", + svg: svgCode, + }); + topRow.appendChild(codeVisibilityControl.element); + + topRow.appendChild( + makeMaximizeButton({ + title: "Show larger editor view", + onClick: () => { + new ShaderCodeOverlay(layer); + }, + }), + ); + topRow.appendChild( + makeHelpButton({ + title: "Documentation on image layer rendering", + href: "https://github.com/google/neuroglancer/blob/master/src/sliceview/image_layer_rendering.md", + }), + ); + return topRow; +} + export class DisplayOptionsTab extends Tab { constructor(public layer: SegmentationUserLayer) { super(); @@ -73,32 +121,12 @@ export class DisplayOptionsTab extends Tab { layer.hasSkeletonsLayer, (hasSkeletonsLayer, parent, refCounted) => { if (!hasSkeletonsLayer) return; - const topRow = document.createElement("div"); - topRow.className = - "neuroglancer-segmentation-dropdown-skeleton-shader-header"; - const label = document.createElement("div"); - label.style.flex = "1"; - label.textContent = "Skeleton shader:"; - topRow.appendChild(label); - topRow.appendChild( - makeMaximizeButton({ - title: "Show larger editor view", - onClick: () => { - new ShaderCodeOverlay(this.layer); - }, - }), - ); - topRow.appendChild( - makeHelpButton({ - title: "Documentation on skeleton rendering", - href: "https://github.com/google/neuroglancer/blob/master/src/sliceview/image_layer_rendering.md", - }), - ); - parent.appendChild(topRow); - const codeWidget = refCounted.registerDisposer( makeSkeletonShaderCodeWidget(this.layer), ); + parent.appendChild( + makeShaderCodeWidgetTopRow(this.layer, codeWidget), + ); parent.appendChild(codeWidget.element); parent.appendChild( refCounted.registerDisposer( From fa8d978810bf38d43a02576abc6e2f8b2d24af3d Mon Sep 17 00:00:00 2001 From: aranega Date: Wed, 12 Mar 2025 08:02:46 -0600 Subject: [PATCH 18/18] CC-122 Introduce generic function to build the top row of ShaderCodeWidget --- src/layer/annotation/index.ts | 68 ++++++--------------- src/layer/image/index.ts | 69 +++++----------------- src/layer/single_mesh/index.ts | 69 +++++----------------- src/ui/segmentation_display_options_tab.ts | 66 +++++---------------- src/widget/shader_code_widget.ts | 62 ++++++++++++++++++- 5 files changed, 123 insertions(+), 211 deletions(-) diff --git a/src/layer/annotation/index.ts b/src/layer/annotation/index.ts index f29f9ff289..ac22dec58f 100644 --- a/src/layer/annotation/index.ts +++ b/src/layer/annotation/index.ts @@ -43,8 +43,10 @@ import { Overlay } from "#src/overlay.js"; import { getWatchableRenderLayerTransform } from "#src/render_coordinate_transform.js"; import { RenderLayerRole } from "#src/renderlayer.js"; import type { SegmentationDisplayState } from "#src/segmentation_display_state/frontend.js"; -import type { TrackableBoolean } from "#src/trackable_boolean.js"; -import { TrackableBooleanCheckbox } from "#src/trackable_boolean.js"; +import { + TrackableBoolean, + TrackableBooleanCheckbox, +} from "#src/trackable_boolean.js"; import { makeCachedLazyDerivedWatchableValue } from "#src/trackable_value.js"; import type { AnnotationLayerView, @@ -68,7 +70,6 @@ import { } from "#src/util/json.js"; import { NullarySignal } from "#src/util/signal.js"; import { DependentViewWidget } from "#src/widget/dependent_view_widget.js"; -import { makeHelpButton } from "#src/widget/help_button.js"; import { addLayerControlToOptionsTab, type LayerControlDefinition, @@ -76,9 +77,11 @@ import { } from "#src/widget/layer_control.js"; import { colorLayerControl } from "#src/widget/layer_control_color.js"; import { LayerReferenceWidget } from "#src/widget/layer_reference.js"; -import { makeMaximizeButton } from "#src/widget/maximize_button.js"; import { RenderScaleWidget } from "#src/widget/render_scale_widget.js"; -import { ShaderCodeWidget } from "#src/widget/shader_code_widget.js"; +import { + makeShaderCodeWidgetTopRow, + ShaderCodeWidget, +} from "#src/widget/shader_code_widget.js"; import { registerLayerShaderControlsTool, ShaderControls, @@ -739,50 +742,6 @@ function makeShaderCodeWidget(layer: AnnotationUserLayer) { }); } -function makeShaderCodeWidgetTopRow( - layer: AnnotationUserLayer, - codeWidget: ShaderCodeWidget, -) { - const spacer = document.createElement("div"); - spacer.style.flex = "1"; - - const topRow = document.createElement("div"); - topRow.className = "neuroglancer-annotation-dropdown-shader-top-row"; - topRow.appendChild(document.createTextNode("Shader")); - topRow.appendChild(spacer); - - layer.registerDisposer( - new ElementVisibilityFromTrackableBoolean( - layer.codeVisible, - codeWidget.element, - ), - ); - - const codeVisibilityControl = new CheckboxIcon(layer.codeVisible, { - enableTitle: "Show code", - disableTitle: "Hide code", - backgroundScheme: "dark", - svg: svgCode, - }); - topRow.appendChild(codeVisibilityControl.element); - - topRow.appendChild( - makeMaximizeButton({ - title: "Show larger editor view", - onClick: () => { - new ShaderCodeOverlay(layer); - }, - }), - ); - topRow.appendChild( - makeHelpButton({ - title: "Documentation on image layer rendering", - href: "https://github.com/google/neuroglancer/blob/master/src/annotation/rendering.md", - }), - ); - return topRow; -} - class ShaderCodeOverlay extends Overlay { codeWidget: ShaderCodeWidget; constructor(public layer: AnnotationUserLayer) { @@ -837,7 +796,16 @@ class RenderingOptionsTab extends Tab { element.appendChild(shaderProperties); element.appendChild( - makeShaderCodeWidgetTopRow(this.layer, this.codeWidget), + makeShaderCodeWidgetTopRow( + this.layer, + this.codeWidget, + ShaderCodeOverlay, + { + title: "Documentation on image layer rendering", + href: "https://github.com/google/neuroglancer/blob/master/src/annotation/rendering.md", + }, + "neuroglancer-annotation-dropdown-shader-top-row", + ), ); element.appendChild(this.codeWidget.element); diff --git a/src/layer/image/index.ts b/src/layer/image/index.ts index 1c452cf0cd..241b69853e 100644 --- a/src/layer/image/index.ts +++ b/src/layer/image/index.ts @@ -15,7 +15,6 @@ */ import "#src/layer/image/style.css"; -import svgCode from "ikonate/icons/code-alt.svg?raw"; import type { CoordinateSpace } from "#src/coordinate_transform.js"; import { @@ -50,10 +49,7 @@ import { } from "#src/sliceview/volume/image_renderlayer.js"; import { trackableAlphaValue } from "#src/trackable_alpha.js"; import { trackableBlendModeValue } from "#src/trackable_blend.js"; -import { - TrackableBoolean, - ElementVisibilityFromTrackableBoolean, -} from "#src/trackable_boolean.js"; +import { TrackableBoolean } from "#src/trackable_boolean.js"; import { trackableFiniteFloat } from "#src/trackable_finite_float.js"; import type { WatchableValueInterface } from "#src/trackable_value.js"; import { @@ -84,10 +80,8 @@ import { ShaderControlState, } from "#src/webgl/shader_ui_controls.js"; import { ChannelDimensionsWidget } from "#src/widget/channel_dimensions_widget.js"; -import { CheckboxIcon } from "#src/widget/checkbox_icon.js"; import { makeCopyButton } from "#src/widget/copy_button.js"; import type { DependentViewContext } from "#src/widget/dependent_view_widget.js"; -import { makeHelpButton } from "#src/widget/help_button.js"; import type { LayerControlDefinition } from "#src/widget/layer_control.js"; import { addLayerControlToOptionsTab, @@ -95,12 +89,14 @@ import { } from "#src/widget/layer_control.js"; import { enumLayerControl } from "#src/widget/layer_control_enum.js"; import { rangeLayerControl } from "#src/widget/layer_control_range.js"; -import { makeMaximizeButton } from "#src/widget/maximize_button.js"; import { renderScaleLayerControl, VolumeRenderingRenderScaleWidget, } from "#src/widget/render_scale_widget.js"; -import { ShaderCodeWidget } from "#src/widget/shader_code_widget.js"; +import { + makeShaderCodeWidgetTopRow, + ShaderCodeWidget, +} from "#src/widget/shader_code_widget.js"; import type { LegendShaderOptions } from "#src/widget/shader_controls.js"; import { registerLayerShaderControlsTool, @@ -470,50 +466,6 @@ function makeShaderCodeWidget(layer: ImageUserLayer) { }); } -function makeShaderCodeWidgetTopRow( - layer: ImageUserLayer, - codeWidget: ShaderCodeWidget, -) { - const spacer = document.createElement("div"); - spacer.style.flex = "1"; - - const topRow = document.createElement("div"); - topRow.className = "neuroglancer-image-dropdown-top-row"; - topRow.appendChild(document.createTextNode("Shader")); - topRow.appendChild(spacer); - - layer.registerDisposer( - new ElementVisibilityFromTrackableBoolean( - layer.codeVisible, - codeWidget.element, - ), - ); - - const codeVisibilityControl = new CheckboxIcon(layer.codeVisible, { - enableTitle: "Show code", - disableTitle: "Hide code", - backgroundScheme: "dark", - svg: svgCode, - }); - topRow.appendChild(codeVisibilityControl.element); - - topRow.appendChild( - makeMaximizeButton({ - title: "Show larger editor view", - onClick: () => { - new ShaderCodeOverlay(layer); - }, - }), - ); - topRow.appendChild( - makeHelpButton({ - title: "Documentation on image layer rendering", - href: "https://github.com/google/neuroglancer/blob/master/src/sliceview/image_layer_rendering.md", - }), - ); - return topRow; -} - const LAYER_CONTROLS: LayerControlDefinition[] = [ { label: "Resolution (slice)", @@ -590,7 +542,16 @@ class RenderingOptionsTab extends Tab { } element.appendChild( - makeShaderCodeWidgetTopRow(this.layer, this.codeWidget), + makeShaderCodeWidgetTopRow( + this.layer, + this.codeWidget, + ShaderCodeOverlay, + { + title: "Documentation on image layer rendering", + href: "https://github.com/google/neuroglancer/blob/master/src/sliceview/image_layer_rendering.md", + }, + "neuroglancer-image-dropdown-top-row", + ), ); element.appendChild( this.registerDisposer( diff --git a/src/layer/single_mesh/index.ts b/src/layer/single_mesh/index.ts index 1d83b8079a..d1e25a03d3 100644 --- a/src/layer/single_mesh/index.ts +++ b/src/layer/single_mesh/index.ts @@ -15,7 +15,6 @@ */ import "#src/layer/single_mesh/style.css"; -import svgCode from "ikonate/icons/code-alt.svg?raw"; import type { ManagedUserLayer } from "#src/layer/index.js"; import { @@ -32,19 +31,16 @@ import { SingleMeshDisplayState, SingleMeshLayer, } from "#src/single_mesh/frontend.js"; -import { - ElementVisibilityFromTrackableBoolean, - TrackableBoolean, -} from "#src/trackable_boolean.js"; +import { TrackableBoolean } from "#src/trackable_boolean.js"; import type { WatchableValueInterface } from "#src/trackable_value.js"; import { WatchableValue } from "#src/trackable_value.js"; import type { Borrowed } from "#src/util/disposable.js"; import { RefCounted } from "#src/util/disposable.js"; import { removeChildren, removeFromParent } from "#src/util/dom.js"; -import { CheckboxIcon } from "#src/widget/checkbox_icon.js"; -import { makeHelpButton } from "#src/widget/help_button.js"; -import { makeMaximizeButton } from "#src/widget/maximize_button.js"; -import { ShaderCodeWidget } from "#src/widget/shader_code_widget.js"; +import { + makeShaderCodeWidgetTopRow, + ShaderCodeWidget, +} from "#src/widget/shader_code_widget.js"; import { registerLayerShaderControlsTool, ShaderControls, @@ -143,50 +139,6 @@ function makeShaderCodeWidget(layer: SingleMeshUserLayer) { }); } -function makeShaderCodeWidgetTopRow( - layer: SingleMeshUserLayer, - codeWidget: ShaderCodeWidget, -) { - const spacer = document.createElement("div"); - spacer.style.flex = "1"; - - const topRow = document.createElement("div"); - topRow.className = "neuroglancer-single-mesh-dropdown-top-row"; - topRow.appendChild(document.createTextNode("Shader")); - topRow.appendChild(spacer); - - layer.registerDisposer( - new ElementVisibilityFromTrackableBoolean( - layer.codeVisible, - codeWidget.element, - ), - ); - - const codeVisibilityControl = new CheckboxIcon(layer.codeVisible, { - enableTitle: "Show code", - disableTitle: "Hide code", - backgroundScheme: "dark", - svg: svgCode, - }); - topRow.appendChild(codeVisibilityControl.element); - - topRow.appendChild( - makeMaximizeButton({ - title: "Show larger editor view", - onClick: () => { - new ShaderCodeOverlay(layer); - }, - }), - ); - topRow.appendChild( - makeHelpButton({ - title: "Documentation on image layer rendering", - href: "https://github.com/google/neuroglancer/blob/master/src/sliceview/image_layer_rendering.md", - }), - ); - return topRow; -} - class VertexAttributeWidget extends RefCounted { element = document.createElement("div"); constructor( @@ -261,7 +213,16 @@ class DisplayOptionsTab extends Tab { this.codeWidget = this.registerDisposer(makeShaderCodeWidget(layer)); element.classList.add("neuroglancer-single-mesh-dropdown"); element.appendChild( - makeShaderCodeWidgetTopRow(this.layer, this.codeWidget), + makeShaderCodeWidgetTopRow( + this.layer, + this.codeWidget, + ShaderCodeOverlay, + { + title: "Documentation on image layer rendering", + href: "https://github.com/google/neuroglancer/blob/master/src/sliceview/image_layer_rendering.md", + }, + "neuroglancer-single-mesh-dropdown-top-row", + ), ); element.appendChild(this.attributeWidget.element); element.appendChild(this.codeWidget.element); diff --git a/src/ui/segmentation_display_options_tab.ts b/src/ui/segmentation_display_options_tab.ts index 634bc3df41..7bb6e9fc80 100644 --- a/src/ui/segmentation_display_options_tab.ts +++ b/src/ui/segmentation_display_options_tab.ts @@ -14,19 +14,17 @@ * limitations under the License. */ -import svgCode from "ikonate/icons/code-alt.svg?raw"; import type { SegmentationUserLayer } from "#src/layer/segmentation/index.js"; import { SKELETON_RENDERING_SHADER_CONTROL_TOOL_ID } from "#src/layer/segmentation/json_keys.js"; import { LAYER_CONTROLS } from "#src/layer/segmentation/layer_controls.js"; import { Overlay } from "#src/overlay.js"; -import { ElementVisibilityFromTrackableBoolean } from "#src/trackable_boolean.js"; -import { CheckboxIcon } from "#src/widget/checkbox_icon.js"; import { DependentViewWidget } from "#src/widget/dependent_view_widget.js"; -import { makeHelpButton } from "#src/widget/help_button.js"; import { addLayerControlToOptionsTab } from "#src/widget/layer_control.js"; import { LinkedLayerGroupWidget } from "#src/widget/linked_layer.js"; -import { makeMaximizeButton } from "#src/widget/maximize_button.js"; -import { ShaderCodeWidget } from "#src/widget/shader_code_widget.js"; +import { + makeShaderCodeWidgetTopRow, + ShaderCodeWidget, +} from "#src/widget/shader_code_widget.js"; import { ShaderControls } from "#src/widget/shader_controls.js"; import { Tab } from "#src/widget/tab_view.js"; @@ -39,51 +37,6 @@ function makeSkeletonShaderCodeWidget(layer: SegmentationUserLayer) { }); } -function makeShaderCodeWidgetTopRow( - layer: SegmentationUserLayer, - codeWidget: ShaderCodeWidget, -) { - const spacer = document.createElement("div"); - spacer.style.flex = "1"; - - const topRow = document.createElement("div"); - topRow.className = - "neuroglancer-segmentation-dropdown-skeleton-shader-header"; - topRow.appendChild(document.createTextNode("Shader")); - topRow.appendChild(spacer); - - layer.registerDisposer( - new ElementVisibilityFromTrackableBoolean( - layer.codeVisible, - codeWidget.element, - ), - ); - - const codeVisibilityControl = new CheckboxIcon(layer.codeVisible, { - enableTitle: "Show code", - disableTitle: "Hide code", - backgroundScheme: "dark", - svg: svgCode, - }); - topRow.appendChild(codeVisibilityControl.element); - - topRow.appendChild( - makeMaximizeButton({ - title: "Show larger editor view", - onClick: () => { - new ShaderCodeOverlay(layer); - }, - }), - ); - topRow.appendChild( - makeHelpButton({ - title: "Documentation on image layer rendering", - href: "https://github.com/google/neuroglancer/blob/master/src/sliceview/image_layer_rendering.md", - }), - ); - return topRow; -} - export class DisplayOptionsTab extends Tab { constructor(public layer: SegmentationUserLayer) { super(); @@ -125,7 +78,16 @@ export class DisplayOptionsTab extends Tab { makeSkeletonShaderCodeWidget(this.layer), ); parent.appendChild( - makeShaderCodeWidgetTopRow(this.layer, codeWidget), + makeShaderCodeWidgetTopRow( + this.layer, + codeWidget, + ShaderCodeOverlay, + { + title: "Documentation on image layer rendering", + href: "https://github.com/google/neuroglancer/blob/master/src/sliceview/image_layer_rendering.md", + }, + "neuroglancer-segmentation-dropdown-skeleton-shader-header", + ), ); parent.appendChild(codeWidget.element); parent.appendChild( diff --git a/src/widget/shader_code_widget.ts b/src/widget/shader_code_widget.ts index df08d64f1a..003bec948a 100644 --- a/src/widget/shader_code_widget.ts +++ b/src/widget/shader_code_widget.ts @@ -14,14 +14,21 @@ * limitations under the License. */ +import CodeMirror from "codemirror"; +import svgCode from "ikonate/icons/code-alt.svg?raw"; import "codemirror/addon/lint/lint.js"; import "#src/widget/shader_code_widget.css"; import "codemirror/lib/codemirror.css"; import "codemirror/addon/lint/lint.css"; -import CodeMirror from "codemirror"; import { debounce } from "lodash-es"; +import type { UserLayer } from "#src/layer/index.js"; +import type { Overlay } from "#src/overlay.js"; import glslCodeMirror from "#src/third_party/codemirror-glsl.js"; +import { + ElementVisibilityFromTrackableBoolean, + type TrackableBoolean, +} from "#src/trackable_boolean.js"; import type { WatchableValue } from "#src/trackable_value.js"; import { RefCounted } from "#src/util/disposable.js"; import { removeFromParent } from "#src/util/dom.js"; @@ -34,6 +41,9 @@ import type { ShaderControlParseError, ShaderControlState, } from "#src/webgl/shader_ui_controls.js"; +import { CheckboxIcon } from "#src/widget/checkbox_icon.js"; +import { makeHelpButton } from "#src/widget/help_button.js"; +import { makeMaximizeButton } from "#src/widget/maximize_button.js"; // Install glsl support in CodeMirror. glslCodeMirror(CodeMirror); @@ -190,3 +200,53 @@ export class ShaderCodeWidget extends RefCounted { super.disposed(); } } + +type UserLayerWithCodeEditor = UserLayer & { codeVisible: TrackableBoolean }; +type ShaderCodeOverlayConstructor = new ( + layer: UserLayerWithCodeEditor, +) => T; + +export function makeShaderCodeWidgetTopRow( + layer: UserLayerWithCodeEditor, + codeWidget: ShaderCodeWidget, + ShaderCodeOverlay: ShaderCodeOverlayConstructor, + help: { + title: string; + href: string; + }, + className: string, +) { + const spacer = document.createElement("div"); + spacer.style.flex = "1"; + + const topRow = document.createElement("div"); + topRow.className = className; + topRow.appendChild(document.createTextNode("Shader")); + topRow.appendChild(spacer); + + layer.registerDisposer( + new ElementVisibilityFromTrackableBoolean( + layer.codeVisible, + codeWidget.element, + ), + ); + + const codeVisibilityControl = new CheckboxIcon(layer.codeVisible, { + enableTitle: "Show code", + disableTitle: "Hide code", + backgroundScheme: "dark", + svg: svgCode, + }); + topRow.appendChild(codeVisibilityControl.element); + + topRow.appendChild( + makeMaximizeButton({ + title: "Show larger editor view", + onClick: () => { + new ShaderCodeOverlay(layer); + }, + }), + ); + topRow.appendChild(makeHelpButton(help)); + return topRow; +}