From 6c2d3e6b3254a0130cafbba7bb856c7b9279c771 Mon Sep 17 00:00:00 2001 From: aranega Date: Fri, 1 Nov 2024 06:18:52 -0600 Subject: [PATCH 01/36] CC-127 Add first PoC about changing layer's title background color --- src/layer/index.ts | 46 +++++++++++++++++++++++++++++++++ src/layer/segmentation/index.ts | 19 ++++++++++++++ src/ui/layer_bar.ts | 33 ++++++++++++++++++++++- 3 files changed, 97 insertions(+), 1 deletion(-) diff --git a/src/layer/index.ts b/src/layer/index.ts index ff3f7d8d2..e0f20aae2 100644 --- a/src/layer/index.ts +++ b/src/layer/index.ts @@ -84,6 +84,7 @@ import { LocalToolBinder, SelectedLegacyTool } from "#src/ui/tool.js"; import { gatherUpdate } from "#src/util/array.js"; import type { Borrowed, Owned } from "#src/util/disposable.js"; import { invokeDisposers, RefCounted } from "#src/util/disposable.js"; +import type { vec3 } from "#src/util/geom.js"; import { emptyToUndefined, parseArray, @@ -185,6 +186,7 @@ export class UserLayer extends RefCounted { } static supportsPickOption = false; + static supportsLayerBarColorSyncOption = false; pick = new TrackableBoolean(true, true); @@ -192,6 +194,16 @@ export class UserLayer extends RefCounted { messages = new MessageList(); + layerBarColorSync = new TrackableBoolean(false, false); + + get layerBarColor() { + return "" + } + + get layerBarColorWatchableProperty(): WatchableValueInterface | undefined { + return undefined; + } + initializeSelectionState(state: this["selectionState"]) { state.generation = -1; state.localPositionValid = false; @@ -739,6 +751,40 @@ export class ManagedUserLayer extends RefCounted { } } + get layerBarColorSyncEnabled() { + const userLayer = this.layer; + return ( + userLayer !== null && + (userLayer.constructor as typeof UserLayer).supportsLayerBarColorSyncOption && + userLayer.layerBarColorSync.value + ); + } + + set layerBarColorSyncEnabled(value: boolean) { + const userLayer = this.layer + if (userLayer !== null && (userLayer.constructor as typeof UserLayer).supportsLayerBarColorSyncOption) { + userLayer.layerBarColorSync.value = value + } + } + + get layerBarColor() { + const userLayer = this.layer; + return userLayer?.layerBarColor; + } + + get layerBarColorSync() { + const userLayer = this.layer; + return userLayer?.layerBarColorSync; + } + + get supportsLayerBarColorSyncOption() { + const userLayer = this.layer; + return ( + userLayer !== null && + (userLayer.constructor as typeof UserLayer).supportsLayerBarColorSyncOption + ); + } + /** * If layer is not null, tranfers ownership of a reference. */ diff --git a/src/layer/segmentation/index.ts b/src/layer/segmentation/index.ts index 8a5981d87..d6464cf8f 100644 --- a/src/layer/segmentation/index.ts +++ b/src/layer/segmentation/index.ts @@ -1261,9 +1261,28 @@ export class SegmentationUserLayer extends Base { ); } + get layerBarColor () { + if (this.displayState.segmentDefaultColor.value) { + const [r, g, b] = this.displayState.segmentDefaultColor.value; + return `rgb(${r * 255}, ${g * 255}, ${b * 255})`; + } + + const visibleSegments = this.displayState.segmentationGroupState.value.visibleSegments; + if (visibleSegments.size === 1) { + console.log("Only 1 segment visible and random seed") + } + + return "#00FF00"; + } + + get layerBarColorWatchableProperty() { + return this.displayState.segmentDefaultColor + } + static type = "segmentation"; static typeAbbreviation = "seg"; static supportsPickOption = true; + static supportsLayerBarColorSyncOption = true; } registerLayerControls(SegmentationUserLayer); diff --git a/src/ui/layer_bar.ts b/src/ui/layer_bar.ts index cb6eadeea..c7a93cdae 100644 --- a/src/ui/layer_bar.ts +++ b/src/ui/layer_bar.ts @@ -21,7 +21,7 @@ import type { ManagedUserLayer } from "#src/layer/index.js"; import { addNewLayer, deleteLayer, makeLayer } from "#src/layer/index.js"; import type { LayerGroupViewer } from "#src/layer_group_viewer.js"; import { NavigationLinkType } from "#src/navigation_state.js"; -import type { WatchableValueInterface } from "#src/trackable_value.js"; +import { observeWatchable, type WatchableValueInterface } from "#src/trackable_value.js"; import type { DropLayers } from "#src/ui/layer_drag_and_drop.js"; import { registerLayerBarDragLeaveHandler, @@ -32,6 +32,7 @@ import { animationFrameDebounce } from "#src/util/animation_frame_debounce.js"; import { RefCounted } from "#src/util/disposable.js"; import { removeFromParent } from "#src/util/dom.js"; import { preventDrag } from "#src/util/drag_and_drop.js"; +import { CheckboxIcon } from "#src/widget/checkbox_icon.js"; import { makeCloseButton } from "#src/widget/close_button.js"; import { makeDeleteButton } from "#src/widget/delete_button.js"; import { makeIcon } from "#src/widget/icon.js"; @@ -48,6 +49,14 @@ class LayerWidget extends RefCounted { maxLength = 0; prevValueText = ""; + get backgroundColor() { + return this.element.style.backgroundColor; + } + + set backgroundColor(value: string | undefined) { + this.element.style.backgroundColor = value || ''; + } + constructor( public layer: ManagedUserLayer, public panel: LayerBar, @@ -103,9 +112,27 @@ class LayerWidget extends RefCounted { deleteLayer(this.layer); event.stopPropagation(); }); + const { element: syncColorsElement } = new CheckboxIcon(this.layer.layerBarColorSync!, { + text: "#", + backgroundScheme: "dark", + enableTitle: "Enable color sync", + disableTitle: "Disable color sync", + }); + syncColorsElement.addEventListener("click", (event: MouseEvent) => { + this.updateBackgroundColor(); + event.stopPropagation(); + }); + if (this.layer?.layer?.layerBarColorWatchableProperty) { + this.layer.registerDisposer(observeWatchable(() => { + this.updateBackgroundColor(); + }, this.layer.layer.layerBarColorWatchableProperty)) + } + + // Compose the layer's title bar element.appendChild(layerNumberElement); valueContainer.appendChild(valueElement); valueContainer.appendChild(buttonContainer); + buttonContainer.appendChild(syncColorsElement); buttonContainer.appendChild(closeElement); buttonContainer.appendChild(deleteElement); element.appendChild(labelElement); @@ -174,6 +201,10 @@ class LayerWidget extends RefCounted { this.element.remove(); super.disposed(); } + + updateBackgroundColor() { + this.backgroundColor = this.layer.layerBarColorSyncEnabled ? this.layer.layerBarColor : ""; + } } export class LayerBar extends RefCounted { From 5dfafe868e2db4feadf2ab4ad5ac04acc4c5cdb2 Mon Sep 17 00:00:00 2001 From: aranega Date: Tue, 12 Nov 2024 08:39:17 -0600 Subject: [PATCH 02/36] Add first version of layer color widget --- src/layer/index.ts | 23 ++++++++++++----- src/layer/segmentation/index.ts | 14 +++++----- src/ui/layer_bar.css | 7 +++++ src/ui/layer_bar.ts | 46 +++++++++++++++++++-------------- 4 files changed, 58 insertions(+), 32 deletions(-) diff --git a/src/layer/index.ts b/src/layer/index.ts index e0f20aae2..a87942274 100644 --- a/src/layer/index.ts +++ b/src/layer/index.ts @@ -70,7 +70,7 @@ import type { WatchableSet, WatchableValueInterface, } from "#src/trackable_value.js"; -import { registerNested, WatchableValue } from "#src/trackable_value.js"; +import { observeWatchable, registerNested, WatchableValue } from "#src/trackable_value.js"; import { SELECTED_LAYER_SIDE_PANEL_DEFAULT_LOCATION, UserLayerSidePanelsState, @@ -82,9 +82,9 @@ import { import type { GlobalToolBinder } from "#src/ui/tool.js"; import { LocalToolBinder, SelectedLegacyTool } from "#src/ui/tool.js"; import { gatherUpdate } from "#src/util/array.js"; +import { TrackableOptionalRGB } from "#src/util/color.js"; import type { Borrowed, Owned } from "#src/util/disposable.js"; import { invokeDisposers, RefCounted } from "#src/util/disposable.js"; -import type { vec3 } from "#src/util/geom.js"; import { emptyToUndefined, parseArray, @@ -194,14 +194,23 @@ export class UserLayer extends RefCounted { messages = new MessageList(); - layerBarColorSync = new TrackableBoolean(false, false); + layerBarColorSync = new TrackableBoolean(true, true); - get layerBarColor() { + layerBarUserDefinedColor = new TrackableOptionalRGB(); + + registerColorWatcher(callback: ((value: any) => void)) { + observeWatchable(callback, this.layerBarUserDefinedColor); + } + + get automaticLayerBarColor(): string | undefined { return "" } - get layerBarColorWatchableProperty(): WatchableValueInterface | undefined { - return undefined; + get layerBarColor(): string | undefined { + if (this.layerBarUserDefinedColor.value) { + return this.layerBarUserDefinedColor.toJSON() + } + return this.automaticLayerBarColor; } initializeSelectionState(state: this["selectionState"]) { @@ -767,7 +776,7 @@ export class ManagedUserLayer extends RefCounted { } } - get layerBarColor() { + get layerBarColor(): string | undefined { const userLayer = this.layer; return userLayer?.layerBarColor; } diff --git a/src/layer/segmentation/index.ts b/src/layer/segmentation/index.ts index d6464cf8f..c221c29f4 100644 --- a/src/layer/segmentation/index.ts +++ b/src/layer/segmentation/index.ts @@ -95,6 +95,7 @@ import { IndirectWatchableValue, makeCachedDerivedWatchableValue, makeCachedLazyDerivedWatchableValue, + observeWatchable, registerNestedSync, TrackableValue, WatchableValue, @@ -1261,7 +1262,12 @@ export class SegmentationUserLayer extends Base { ); } - get layerBarColor () { + registerColorWatcher(callback: ((value: any) => void)) { + super.registerColorWatcher(callback); + observeWatchable(callback, this.displayState.segmentDefaultColor); + } + + get automaticLayerBarColor () { if (this.displayState.segmentDefaultColor.value) { const [r, g, b] = this.displayState.segmentDefaultColor.value; return `rgb(${r * 255}, ${g * 255}, ${b * 255})`; @@ -1272,11 +1278,7 @@ export class SegmentationUserLayer extends Base { console.log("Only 1 segment visible and random seed") } - return "#00FF00"; - } - - get layerBarColorWatchableProperty() { - return this.displayState.segmentDefaultColor + return undefined; } static type = "segmentation"; diff --git a/src/ui/layer_bar.css b/src/ui/layer_bar.css index 7d24208df..030271f90 100644 --- a/src/ui/layer_bar.css +++ b/src/ui/layer_bar.css @@ -134,6 +134,13 @@ align-items: center; } +.neuroglancer-layer-color-value-container { + border-radius: 50%; + height: 15px; + width: 15px; + margin: 7.5px; +} + .neuroglancer-layer-item-value { grid-row: 1; grid-column: 1; diff --git a/src/ui/layer_bar.ts b/src/ui/layer_bar.ts index c7a93cdae..79f880ee2 100644 --- a/src/ui/layer_bar.ts +++ b/src/ui/layer_bar.ts @@ -21,7 +21,7 @@ import type { ManagedUserLayer } from "#src/layer/index.js"; import { addNewLayer, deleteLayer, makeLayer } from "#src/layer/index.js"; import type { LayerGroupViewer } from "#src/layer_group_viewer.js"; import { NavigationLinkType } from "#src/navigation_state.js"; -import { observeWatchable, type WatchableValueInterface } from "#src/trackable_value.js"; +import type { WatchableValueInterface } from "#src/trackable_value.js"; import type { DropLayers } from "#src/ui/layer_drag_and_drop.js"; import { registerLayerBarDragLeaveHandler, @@ -49,14 +49,6 @@ class LayerWidget extends RefCounted { maxLength = 0; prevValueText = ""; - get backgroundColor() { - return this.element.style.backgroundColor; - } - - set backgroundColor(value: string | undefined) { - this.element.style.backgroundColor = value || ''; - } - constructor( public layer: ManagedUserLayer, public panel: LayerBar, @@ -112,6 +104,24 @@ class LayerWidget extends RefCounted { deleteLayer(this.layer); event.stopPropagation(); }); + + const layerColorWidget = document.createElement("div"); + layerColorWidget.className = "neuroglancer-layer-color-value-container"; + + const updateLayerColorWidget = () => { + if (! this.layer.layerBarColorSyncEnabled) { + layerColorWidget.style.background = "none"; + layerColorWidget.style.backgroundColor = ""; + return; + } + const color = this.layer.layerBarColor; + if (color) { + layerColorWidget.style.background = "none"; + layerColorWidget.style.backgroundColor = color; + } else { + layerColorWidget.style.background = "radial-gradient(circle, red, orange, yellow, green, blue, indigo, violet)"; + } + } const { element: syncColorsElement } = new CheckboxIcon(this.layer.layerBarColorSync!, { text: "#", backgroundScheme: "dark", @@ -119,14 +129,15 @@ class LayerWidget extends RefCounted { disableTitle: "Disable color sync", }); syncColorsElement.addEventListener("click", (event: MouseEvent) => { - this.updateBackgroundColor(); + updateLayerColorWidget(); event.stopPropagation(); }); - if (this.layer?.layer?.layerBarColorWatchableProperty) { - this.layer.registerDisposer(observeWatchable(() => { - this.updateBackgroundColor(); - }, this.layer.layer.layerBarColorWatchableProperty)) - } + + this.layer?.layer?.registerColorWatcher((value) => { + console.log("Color changed", value) + console.log("Computed", this.layer.layerBarColor) + updateLayerColorWidget(); + }) // Compose the layer's title bar element.appendChild(layerNumberElement); @@ -136,6 +147,7 @@ class LayerWidget extends RefCounted { buttonContainer.appendChild(closeElement); buttonContainer.appendChild(deleteElement); element.appendChild(labelElement); + element.appendChild(layerColorWidget); element.appendChild(valueContainer); const positionWidget = this.registerDisposer( new PositionWidget( @@ -201,10 +213,6 @@ class LayerWidget extends RefCounted { this.element.remove(); super.disposed(); } - - updateBackgroundColor() { - this.backgroundColor = this.layer.layerBarColorSyncEnabled ? this.layer.layerBarColor : ""; - } } export class LayerBar extends RefCounted { From 6de87f1f669d9f5be14dfd9c041921247f379ace Mon Sep 17 00:00:00 2001 From: aranega Date: Tue, 12 Nov 2024 09:12:49 -0600 Subject: [PATCH 03/36] Add first color widget in left layer bar --- src/ui/layer_bar.css | 8 ++++---- src/ui/layer_bar.ts | 6 ++---- src/ui/layer_list_panel.ts | 27 +++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/ui/layer_bar.css b/src/ui/layer_bar.css index 030271f90..6a0e924e3 100644 --- a/src/ui/layer_bar.css +++ b/src/ui/layer_bar.css @@ -134,11 +134,11 @@ align-items: center; } -.neuroglancer-layer-color-value-container { +.neuroglancer-layer-color-value { border-radius: 50%; - height: 15px; - width: 15px; - margin: 7.5px; + height: 12px; + width: 12px; + margin: 5px; } .neuroglancer-layer-item-value { diff --git a/src/ui/layer_bar.ts b/src/ui/layer_bar.ts index 79f880ee2..7abaeb0eb 100644 --- a/src/ui/layer_bar.ts +++ b/src/ui/layer_bar.ts @@ -106,7 +106,7 @@ class LayerWidget extends RefCounted { }); const layerColorWidget = document.createElement("div"); - layerColorWidget.className = "neuroglancer-layer-color-value-container"; + layerColorWidget.className = "neuroglancer-layer-color-value"; const updateLayerColorWidget = () => { if (! this.layer.layerBarColorSyncEnabled) { @@ -133,9 +133,7 @@ class LayerWidget extends RefCounted { event.stopPropagation(); }); - this.layer?.layer?.registerColorWatcher((value) => { - console.log("Color changed", value) - console.log("Computed", this.layer.layerBarColor) + this.layer?.layer?.registerColorWatcher(() => { updateLayerColorWidget(); }) diff --git a/src/ui/layer_list_panel.ts b/src/ui/layer_list_panel.ts index d3251143b..0add3a527 100644 --- a/src/ui/layer_list_panel.ts +++ b/src/ui/layer_list_panel.ts @@ -103,6 +103,30 @@ class LayerVisibilityWidget extends RefCounted { } } +class LayerColorWidget extends RefCounted { + element = document.createElement("div"); + constructor(public layer: ManagedUserLayer) { + super(); + const { element } = this; + element.className = "neuroglancer-layer-color-value"; + + this.layer.layer?.registerColorWatcher(() => { + if (! this.layer.layerBarColorSyncEnabled) { + element.style.background = "none"; + element.style.backgroundColor = ""; + return; + } + const color = this.layer.layerBarColor; + if (color) { + element.style.background = "none"; + element.style.backgroundColor = color; + } else { + element.style.background = "radial-gradient(circle, red, orange, yellow, green, blue, indigo, violet)"; + } + }) + } +} + function makeSelectedLayerSidePanelCheckboxIcon(layer: ManagedUserLayer) { const { selectedLayer } = layer.manager.root; const icon = new CheckboxIcon( @@ -167,6 +191,9 @@ class LayerListItem extends RefCounted { element.appendChild( this.registerDisposer(new LayerVisibilityWidget(layer)).element, ); + element.appendChild( + this.registerDisposer(new LayerColorWidget(layer)).element, + ); element.appendChild( this.registerDisposer(new LayerNameWidget(layer)).element, ); From 4b79ce78edd09064b61ecd82c05ae693f126fff6 Mon Sep 17 00:00:00 2001 From: aranega Date: Tue, 12 Nov 2024 10:37:23 -0600 Subject: [PATCH 04/36] Add indicator to express that color is not visible even if active --- src/ui/layer_bar.css | 12 ++++++++++++ src/ui/layer_bar.ts | 10 +++++++++- src/ui/layer_list_panel.css | 19 +++++++++++++++++++ src/ui/layer_list_panel.ts | 12 ++++++++++-- 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/src/ui/layer_bar.css b/src/ui/layer_bar.css index 6a0e924e3..055e01acf 100644 --- a/src/ui/layer_bar.css +++ b/src/ui/layer_bar.css @@ -135,12 +135,24 @@ } .neuroglancer-layer-color-value { + position: relative; border-radius: 50%; height: 12px; width: 12px; margin: 5px; } +.neuroglancer-layer-color-value.cross::before { + content: ""; + position: absolute; + top: 50%; + left: 50%; + width: 18px; + height: 1px; + background-color: rgb(255, 255, 255); + transform: translate(-50%, -50%) rotate(135deg); +} + .neuroglancer-layer-item-value { grid-row: 1; grid-column: 1; diff --git a/src/ui/layer_bar.ts b/src/ui/layer_bar.ts index 7abaeb0eb..cd0ee47ee 100644 --- a/src/ui/layer_bar.ts +++ b/src/ui/layer_bar.ts @@ -121,7 +121,7 @@ class LayerWidget extends RefCounted { } else { layerColorWidget.style.background = "radial-gradient(circle, red, orange, yellow, green, blue, indigo, violet)"; } - } + }; const { element: syncColorsElement } = new CheckboxIcon(this.layer.layerBarColorSync!, { text: "#", backgroundScheme: "dark", @@ -137,6 +137,14 @@ class LayerWidget extends RefCounted { updateLayerColorWidget(); }) + this.registerDisposer(layer.layerChanged.add(() => { + if (!this.layer.visible) { + layerColorWidget.classList.add("cross"); + } else { + layerColorWidget.classList.remove("cross"); + } + })); + // Compose the layer's title bar element.appendChild(layerNumberElement); valueContainer.appendChild(valueElement); diff --git a/src/ui/layer_list_panel.css b/src/ui/layer_list_panel.css index ecdb04f79..795716501 100644 --- a/src/ui/layer_list_panel.css +++ b/src/ui/layer_list_panel.css @@ -45,6 +45,25 @@ display: inline-block; } +.neuroglancer-layer-list-panel-color-value { + position: relative; + border-radius: 50%; + height: 12px; + width: 12px; + margin: 3px; +} + +.neuroglancer-layer-list-panel-color-value.cross::before { + content: ""; + position: absolute; + top: 50%; + left: 50%; + width: 18px; + height: 1px; + background-color: rgb(255, 255, 255); + transform: translate(-50%, -50%) rotate(135deg); +} + .neuroglancer-layer-list-panel-item:not(:hover) > .neuroglancer-layer-list-panel-item-delete { display: none; diff --git a/src/ui/layer_list_panel.ts b/src/ui/layer_list_panel.ts index 0add3a527..b71888a26 100644 --- a/src/ui/layer_list_panel.ts +++ b/src/ui/layer_list_panel.ts @@ -108,7 +108,7 @@ class LayerColorWidget extends RefCounted { constructor(public layer: ManagedUserLayer) { super(); const { element } = this; - element.className = "neuroglancer-layer-color-value"; + element.className = "neuroglancer-layer-list-panel-color-value"; this.layer.layer?.registerColorWatcher(() => { if (! this.layer.layerBarColorSyncEnabled) { @@ -123,7 +123,15 @@ class LayerColorWidget extends RefCounted { } else { element.style.background = "radial-gradient(circle, red, orange, yellow, green, blue, indigo, violet)"; } - }) + }); + + this.registerDisposer(this.layer.layerChanged.add(() => { + if (!this.layer.visible) { + element.classList.add("cross"); + } else { + element.classList.remove("cross"); + } + })); } } From 110b99b8ec15d562a85fad6d400749fe8d411df0 Mon Sep 17 00:00:00 2001 From: aranega Date: Wed, 13 Nov 2024 12:28:36 -0600 Subject: [PATCH 05/36] Fix disposer logic for layer color computation --- src/layer/index.ts | 12 ++++++++++-- src/layer/segmentation/index.ts | 10 +++++++--- src/ui/layer_bar.ts | 25 +++++++++++++------------ src/ui/layer_list_panel.ts | 4 ++-- 4 files changed, 32 insertions(+), 19 deletions(-) diff --git a/src/layer/index.ts b/src/layer/index.ts index a87942274..afd941d71 100644 --- a/src/layer/index.ts +++ b/src/layer/index.ts @@ -198,8 +198,8 @@ export class UserLayer extends RefCounted { layerBarUserDefinedColor = new TrackableOptionalRGB(); - registerColorWatcher(callback: ((value: any) => void)) { - observeWatchable(callback, this.layerBarUserDefinedColor); + observeLayerColor(callback: ((value: any) => void)): () => void { + return observeWatchable(callback, this.layerBarUserDefinedColor); } get automaticLayerBarColor(): string | undefined { @@ -786,6 +786,14 @@ export class ManagedUserLayer extends RefCounted { return userLayer?.layerBarColorSync; } + observeLayerColor(callback: ((value: any) => void)): () => void { + const userLayer = this.layer; + if (userLayer !== null) { + return userLayer.observeLayerColor(callback); + } + return () => {}; + } + get supportsLayerBarColorSyncOption() { const userLayer = this.layer; return ( diff --git a/src/layer/segmentation/index.ts b/src/layer/segmentation/index.ts index c221c29f4..c3782de60 100644 --- a/src/layer/segmentation/index.ts +++ b/src/layer/segmentation/index.ts @@ -1262,9 +1262,13 @@ export class SegmentationUserLayer extends Base { ); } - registerColorWatcher(callback: ((value: any) => void)) { - super.registerColorWatcher(callback); - observeWatchable(callback, this.displayState.segmentDefaultColor); + observeLayerColor(callback: ((value: any) => void)) { + const disposer = super.observeLayerColor(callback); + const subDisposer = observeWatchable(callback, this.displayState.segmentDefaultColor); + return () => { + disposer(); + subDisposer(); + } } get automaticLayerBarColor () { diff --git a/src/ui/layer_bar.ts b/src/ui/layer_bar.ts index cd0ee47ee..ca7ccc6a8 100644 --- a/src/ui/layer_bar.ts +++ b/src/ui/layer_bar.ts @@ -46,6 +46,7 @@ class LayerWidget extends RefCounted { prefetchProgress = document.createElement("div"); labelElementText = document.createTextNode(""); valueElement = document.createElement("div"); + layerColorElement = document.createElement("div"); maxLength = 0; prevValueText = ""; @@ -62,6 +63,7 @@ class LayerWidget extends RefCounted { visibleProgress, prefetchProgress, labelElementText, + layerColorElement, } = this; element.className = "neuroglancer-layer-item neuroglancer-noselect"; element.appendChild(visibleProgress); @@ -105,21 +107,20 @@ class LayerWidget extends RefCounted { event.stopPropagation(); }); - const layerColorWidget = document.createElement("div"); - layerColorWidget.className = "neuroglancer-layer-color-value"; + layerColorElement.className = "neuroglancer-layer-color-value"; const updateLayerColorWidget = () => { if (! this.layer.layerBarColorSyncEnabled) { - layerColorWidget.style.background = "none"; - layerColorWidget.style.backgroundColor = ""; + layerColorElement.style.background = "none"; + layerColorElement.style.backgroundColor = ""; return; } const color = this.layer.layerBarColor; if (color) { - layerColorWidget.style.background = "none"; - layerColorWidget.style.backgroundColor = color; + layerColorElement.style.background = "none"; + layerColorElement.style.backgroundColor = color; } else { - layerColorWidget.style.background = "radial-gradient(circle, red, orange, yellow, green, blue, indigo, violet)"; + layerColorElement.style.background = "radial-gradient(circle, red, orange, yellow, green, blue, indigo, violet)"; } }; const { element: syncColorsElement } = new CheckboxIcon(this.layer.layerBarColorSync!, { @@ -133,15 +134,15 @@ class LayerWidget extends RefCounted { event.stopPropagation(); }); - this.layer?.layer?.registerColorWatcher(() => { + this.registerDisposer(layer.observeLayerColor(() => { updateLayerColorWidget(); - }) + })); this.registerDisposer(layer.layerChanged.add(() => { if (!this.layer.visible) { - layerColorWidget.classList.add("cross"); + layerColorElement.classList.add("cross"); } else { - layerColorWidget.classList.remove("cross"); + layerColorElement.classList.remove("cross"); } })); @@ -153,7 +154,7 @@ class LayerWidget extends RefCounted { buttonContainer.appendChild(closeElement); buttonContainer.appendChild(deleteElement); element.appendChild(labelElement); - element.appendChild(layerColorWidget); + element.appendChild(layerColorElement); element.appendChild(valueContainer); const positionWidget = this.registerDisposer( new PositionWidget( diff --git a/src/ui/layer_list_panel.ts b/src/ui/layer_list_panel.ts index b71888a26..5f3b78fd1 100644 --- a/src/ui/layer_list_panel.ts +++ b/src/ui/layer_list_panel.ts @@ -110,7 +110,7 @@ class LayerColorWidget extends RefCounted { const { element } = this; element.className = "neuroglancer-layer-list-panel-color-value"; - this.layer.layer?.registerColorWatcher(() => { + this.registerDisposer(this.layer.observeLayerColor(() => { if (! this.layer.layerBarColorSyncEnabled) { element.style.background = "none"; element.style.backgroundColor = ""; @@ -123,7 +123,7 @@ class LayerColorWidget extends RefCounted { } else { element.style.background = "radial-gradient(circle, red, orange, yellow, green, blue, indigo, violet)"; } - }); + })); this.registerDisposer(this.layer.layerChanged.add(() => { if (!this.layer.visible) { From 9ea3b1ef5f2fad413bf27d16a50094a597ea759b Mon Sep 17 00:00:00 2001 From: aranega Date: Thu, 21 Nov 2024 06:47:36 -0600 Subject: [PATCH 06/36] Add support for color indicator for annotation layer --- src/layer/annotation/index.ts | 21 +++++++++++++- src/layer/single_mesh/index.ts | 2 +- src/ui/layer_bar.ts | 52 +++++++++++++++++++--------------- 3 files changed, 50 insertions(+), 25 deletions(-) diff --git a/src/layer/annotation/index.ts b/src/layer/annotation/index.ts index 35c1a4e43..316611d02 100644 --- a/src/layer/annotation/index.ts +++ b/src/layer/annotation/index.ts @@ -45,7 +45,7 @@ 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, @@ -713,8 +713,27 @@ export class AnnotationUserLayer extends Base { return x; } + observeLayerColor(callback: ((value: any) => void)) { + const disposer = super.observeLayerColor(callback); + const subDisposer = observeWatchable(callback, this.annotationDisplayState.color); + return () => { + disposer(); + subDisposer(); + } + } + + get automaticLayerBarColor () { + if (this.annotationDisplayState.color) { + const [r, g, b] = this.annotationDisplayState.color.value; + return `rgb(${r * 255}, ${g * 255}, ${b * 255})`; + } + + return undefined; + } + static type = "annotation"; static typeAbbreviation = "ann"; + static supportsLayerBarColorSyncOption = true; } function makeShaderCodeWidget(layer: AnnotationUserLayer) { diff --git a/src/layer/single_mesh/index.ts b/src/layer/single_mesh/index.ts index 6e2b68694..4ddc017e6 100644 --- a/src/layer/single_mesh/index.ts +++ b/src/layer/single_mesh/index.ts @@ -32,7 +32,7 @@ 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"; diff --git a/src/ui/layer_bar.ts b/src/ui/layer_bar.ts index ca7ccc6a8..76371a482 100644 --- a/src/ui/layer_bar.ts +++ b/src/ui/layer_bar.ts @@ -123,34 +123,40 @@ class LayerWidget extends RefCounted { layerColorElement.style.background = "radial-gradient(circle, red, orange, yellow, green, blue, indigo, violet)"; } }; - const { element: syncColorsElement } = new CheckboxIcon(this.layer.layerBarColorSync!, { - text: "#", - backgroundScheme: "dark", - enableTitle: "Enable color sync", - disableTitle: "Disable color sync", - }); - syncColorsElement.addEventListener("click", (event: MouseEvent) => { - updateLayerColorWidget(); - event.stopPropagation(); - }); - - this.registerDisposer(layer.observeLayerColor(() => { - updateLayerColorWidget(); - })); - - this.registerDisposer(layer.layerChanged.add(() => { - if (!this.layer.visible) { - layerColorElement.classList.add("cross"); - } else { - layerColorElement.classList.remove("cross"); - } - })); + let syncColorsElement = null; + if (this.layer.supportsLayerBarColorSyncOption) { + const { element } = new CheckboxIcon(this.layer.layerBarColorSync!, { + text: "#", + backgroundScheme: "dark", + enableTitle: "Enable color sync", + disableTitle: "Disable color sync", + }); + syncColorsElement = element + syncColorsElement.addEventListener("click", (event: MouseEvent) => { + updateLayerColorWidget(); + event.stopPropagation(); + }); + + this.registerDisposer(layer.observeLayerColor(() => { + updateLayerColorWidget(); + })); + + this.registerDisposer(layer.layerChanged.add(() => { + if (!this.layer.visible) { + layerColorElement.classList.add("cross"); + } else { + layerColorElement.classList.remove("cross"); + } + })); + } // Compose the layer's title bar element.appendChild(layerNumberElement); valueContainer.appendChild(valueElement); valueContainer.appendChild(buttonContainer); - buttonContainer.appendChild(syncColorsElement); + if (this.layer.supportsLayerBarColorSyncOption) { + buttonContainer.appendChild(syncColorsElement!); + } buttonContainer.appendChild(closeElement); buttonContainer.appendChild(deleteElement); element.appendChild(labelElement); From 149d030dde22f53003bc1fd3a22fd46d93675e74 Mon Sep 17 00:00:00 2001 From: aranega Date: Tue, 26 Nov 2024 08:28:10 -0600 Subject: [PATCH 07/36] Fix format --- src/layer/annotation/index.ts | 16 +++++++---- src/layer/index.ts | 30 ++++++++++++++------- src/layer/segmentation/index.ts | 18 ++++++++----- src/layer/single_mesh/index.ts | 2 +- src/ui/layer_bar.ts | 33 +++++++++++++---------- src/ui/layer_list_panel.ts | 47 ++++++++++++++++++--------------- 6 files changed, 88 insertions(+), 58 deletions(-) diff --git a/src/layer/annotation/index.ts b/src/layer/annotation/index.ts index 316611d02..9e58e70a5 100644 --- a/src/layer/annotation/index.ts +++ b/src/layer/annotation/index.ts @@ -45,7 +45,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, observeWatchable } from "#src/trackable_value.js"; +import { + makeCachedLazyDerivedWatchableValue, + observeWatchable, +} from "#src/trackable_value.js"; import type { AnnotationLayerView, MergedAnnotationStates, @@ -713,16 +716,19 @@ export class AnnotationUserLayer extends Base { return x; } - observeLayerColor(callback: ((value: any) => void)) { + observeLayerColor(callback: (value: any) => void) { const disposer = super.observeLayerColor(callback); - const subDisposer = observeWatchable(callback, this.annotationDisplayState.color); + const subDisposer = observeWatchable( + callback, + this.annotationDisplayState.color, + ); return () => { disposer(); subDisposer(); - } + }; } - get automaticLayerBarColor () { + get automaticLayerBarColor() { if (this.annotationDisplayState.color) { const [r, g, b] = this.annotationDisplayState.color.value; return `rgb(${r * 255}, ${g * 255}, ${b * 255})`; diff --git a/src/layer/index.ts b/src/layer/index.ts index afd941d71..c8ad2f10a 100644 --- a/src/layer/index.ts +++ b/src/layer/index.ts @@ -70,7 +70,11 @@ import type { WatchableSet, WatchableValueInterface, } from "#src/trackable_value.js"; -import { observeWatchable, registerNested, WatchableValue } from "#src/trackable_value.js"; +import { + observeWatchable, + registerNested, + WatchableValue, +} from "#src/trackable_value.js"; import { SELECTED_LAYER_SIDE_PANEL_DEFAULT_LOCATION, UserLayerSidePanelsState, @@ -198,17 +202,17 @@ export class UserLayer extends RefCounted { layerBarUserDefinedColor = new TrackableOptionalRGB(); - observeLayerColor(callback: ((value: any) => void)): () => void { + observeLayerColor(callback: (value: any) => void): () => void { return observeWatchable(callback, this.layerBarUserDefinedColor); } get automaticLayerBarColor(): string | undefined { - return "" + return ""; } get layerBarColor(): string | undefined { if (this.layerBarUserDefinedColor.value) { - return this.layerBarUserDefinedColor.toJSON() + return this.layerBarUserDefinedColor.toJSON(); } return this.automaticLayerBarColor; } @@ -764,15 +768,20 @@ export class ManagedUserLayer extends RefCounted { const userLayer = this.layer; return ( userLayer !== null && - (userLayer.constructor as typeof UserLayer).supportsLayerBarColorSyncOption && + (userLayer.constructor as typeof UserLayer) + .supportsLayerBarColorSyncOption && userLayer.layerBarColorSync.value ); } set layerBarColorSyncEnabled(value: boolean) { - const userLayer = this.layer - if (userLayer !== null && (userLayer.constructor as typeof UserLayer).supportsLayerBarColorSyncOption) { - userLayer.layerBarColorSync.value = value + const userLayer = this.layer; + if ( + userLayer !== null && + (userLayer.constructor as typeof UserLayer) + .supportsLayerBarColorSyncOption + ) { + userLayer.layerBarColorSync.value = value; } } @@ -786,7 +795,7 @@ export class ManagedUserLayer extends RefCounted { return userLayer?.layerBarColorSync; } - observeLayerColor(callback: ((value: any) => void)): () => void { + observeLayerColor(callback: (value: any) => void): () => void { const userLayer = this.layer; if (userLayer !== null) { return userLayer.observeLayerColor(callback); @@ -798,7 +807,8 @@ export class ManagedUserLayer extends RefCounted { const userLayer = this.layer; return ( userLayer !== null && - (userLayer.constructor as typeof UserLayer).supportsLayerBarColorSyncOption + (userLayer.constructor as typeof UserLayer) + .supportsLayerBarColorSyncOption ); } diff --git a/src/layer/segmentation/index.ts b/src/layer/segmentation/index.ts index c3782de60..553d3b100 100644 --- a/src/layer/segmentation/index.ts +++ b/src/layer/segmentation/index.ts @@ -1262,24 +1262,28 @@ export class SegmentationUserLayer extends Base { ); } - observeLayerColor(callback: ((value: any) => void)) { + observeLayerColor(callback: (value: any) => void) { const disposer = super.observeLayerColor(callback); - const subDisposer = observeWatchable(callback, this.displayState.segmentDefaultColor); + const subDisposer = observeWatchable( + callback, + this.displayState.segmentDefaultColor, + ); return () => { disposer(); subDisposer(); - } + }; } - get automaticLayerBarColor () { + get automaticLayerBarColor() { if (this.displayState.segmentDefaultColor.value) { const [r, g, b] = this.displayState.segmentDefaultColor.value; return `rgb(${r * 255}, ${g * 255}, ${b * 255})`; } - const visibleSegments = this.displayState.segmentationGroupState.value.visibleSegments; - if (visibleSegments.size === 1) { - console.log("Only 1 segment visible and random seed") + const visibleSegments = + this.displayState.segmentationGroupState.value.visibleSegments; + if (visibleSegments.value.size === 1) { + console.log("Only 1 segment visible and random seed"); } return undefined; diff --git a/src/layer/single_mesh/index.ts b/src/layer/single_mesh/index.ts index 4ddc017e6..6e2b68694 100644 --- a/src/layer/single_mesh/index.ts +++ b/src/layer/single_mesh/index.ts @@ -32,7 +32,7 @@ import { SingleMeshLayer, } from "#src/single_mesh/frontend.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"; diff --git a/src/ui/layer_bar.ts b/src/ui/layer_bar.ts index 76371a482..adbaf6bb3 100644 --- a/src/ui/layer_bar.ts +++ b/src/ui/layer_bar.ts @@ -110,7 +110,7 @@ class LayerWidget extends RefCounted { layerColorElement.className = "neuroglancer-layer-color-value"; const updateLayerColorWidget = () => { - if (! this.layer.layerBarColorSyncEnabled) { + if (!this.layer.layerBarColorSyncEnabled) { layerColorElement.style.background = "none"; layerColorElement.style.backgroundColor = ""; return; @@ -120,7 +120,8 @@ class LayerWidget extends RefCounted { layerColorElement.style.background = "none"; layerColorElement.style.backgroundColor = color; } else { - layerColorElement.style.background = "radial-gradient(circle, red, orange, yellow, green, blue, indigo, violet)"; + layerColorElement.style.background = + "radial-gradient(circle, red, orange, yellow, green, blue, indigo, violet)"; } }; let syncColorsElement = null; @@ -131,23 +132,27 @@ class LayerWidget extends RefCounted { enableTitle: "Enable color sync", disableTitle: "Disable color sync", }); - syncColorsElement = element + syncColorsElement = element; syncColorsElement.addEventListener("click", (event: MouseEvent) => { updateLayerColorWidget(); event.stopPropagation(); }); - this.registerDisposer(layer.observeLayerColor(() => { - updateLayerColorWidget(); - })); - - this.registerDisposer(layer.layerChanged.add(() => { - if (!this.layer.visible) { - layerColorElement.classList.add("cross"); - } else { - layerColorElement.classList.remove("cross"); - } - })); + this.registerDisposer( + layer.observeLayerColor(() => { + updateLayerColorWidget(); + }), + ); + + this.registerDisposer( + layer.layerChanged.add(() => { + if (!this.layer.visible) { + layerColorElement.classList.add("cross"); + } else { + layerColorElement.classList.remove("cross"); + } + }), + ); } // Compose the layer's title bar diff --git a/src/ui/layer_list_panel.ts b/src/ui/layer_list_panel.ts index 5f3b78fd1..d9c690543 100644 --- a/src/ui/layer_list_panel.ts +++ b/src/ui/layer_list_panel.ts @@ -110,28 +110,33 @@ class LayerColorWidget extends RefCounted { const { element } = this; element.className = "neuroglancer-layer-list-panel-color-value"; - this.registerDisposer(this.layer.observeLayerColor(() => { - if (! this.layer.layerBarColorSyncEnabled) { - element.style.background = "none"; - element.style.backgroundColor = ""; - return; - } - const color = this.layer.layerBarColor; - if (color) { - element.style.background = "none"; - element.style.backgroundColor = color; - } else { - element.style.background = "radial-gradient(circle, red, orange, yellow, green, blue, indigo, violet)"; - } - })); + this.registerDisposer( + this.layer.observeLayerColor(() => { + if (!this.layer.layerBarColorSyncEnabled) { + element.style.background = "none"; + element.style.backgroundColor = ""; + return; + } + const color = this.layer.layerBarColor; + if (color) { + element.style.background = "none"; + element.style.backgroundColor = color; + } else { + element.style.background = + "radial-gradient(circle, red, orange, yellow, green, blue, indigo, violet)"; + } + }), + ); - this.registerDisposer(this.layer.layerChanged.add(() => { - if (!this.layer.visible) { - element.classList.add("cross"); - } else { - element.classList.remove("cross"); - } - })); + this.registerDisposer( + this.layer.layerChanged.add(() => { + if (!this.layer.visible) { + element.classList.add("cross"); + } else { + element.classList.remove("cross"); + } + }), + ); } } From 4f8a4777c323411a25272b1a0625668461ab9dbb Mon Sep 17 00:00:00 2001 From: aranega Date: Wed, 27 Nov 2024 08:02:08 -0600 Subject: [PATCH 08/36] Add color sync setting in layer state --- src/layer/index.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/layer/index.ts b/src/layer/index.ts index c8ad2f10a..e5b328fdf 100644 --- a/src/layer/index.ts +++ b/src/layer/index.ts @@ -127,6 +127,7 @@ const LOCAL_COORDINATE_SPACE_JSON_KEY = "localDimensions"; const SOURCE_JSON_KEY = "source"; const TRANSFORM_JSON_KEY = "transform"; const PICK_JSON_KEY = "pick"; +const LAYERBAR_COLOR_JSON_KEY = "layerBarColorSync"; export interface UserLayerSelectionState { generation: number; @@ -198,7 +199,7 @@ export class UserLayer extends RefCounted { messages = new MessageList(); - layerBarColorSync = new TrackableBoolean(true, true); + layerBarColorSync = new TrackableBoolean(false, false); layerBarUserDefinedColor = new TrackableOptionalRGB(); @@ -396,6 +397,7 @@ export class UserLayer extends RefCounted { this.localPosition.changed.add(this.specificationChanged.dispatch); this.pick.changed.add(this.specificationChanged.dispatch); this.pick.changed.add(this.layersChanged.dispatch); + this.layerBarColorSync.changed.add(this.layersChanged.dispatch); this.dataSourcesChanged.add(this.specificationChanged.dispatch); this.dataSourcesChanged.add(() => this.updateDataSubsourceActivations()); this.messages.changed.add(this.layersChanged.dispatch); @@ -550,6 +552,13 @@ export class UserLayer extends RefCounted { if ((this.constructor as typeof UserLayer).supportsPickOption) { this.pick.restoreState(specification[PICK_JSON_KEY]); } + if ( + (this.constructor as typeof UserLayer).supportsLayerBarColorSyncOption + ) { + this.layerBarColorSync.restoreState( + specification[LAYERBAR_COLOR_JSON_KEY], + ); + } for (const spec of this.getDataSourceSpecifications(specification)) { this.addDataSource(spec); } @@ -623,6 +632,7 @@ export class UserLayer extends RefCounted { [LOCAL_POSITION_JSON_KEY]: this.localPosition.toJSON(), [LOCAL_VELOCITY_JSON_KEY]: this.localVelocity.toJSON(), [PICK_JSON_KEY]: this.pick.toJSON(), + [LAYERBAR_COLOR_JSON_KEY]: this.layerBarColorSync.toJSON(), ...this.panels.toJSON(), }; } From a3a2365b47cae1c6750fdd0f743d7d81ad55c2b7 Mon Sep 17 00:00:00 2001 From: aranega Date: Wed, 27 Nov 2024 09:29:06 -0600 Subject: [PATCH 09/36] Add single color sync when only 1 label is visible in segmentation layer --- src/layer/segmentation/index.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/layer/segmentation/index.ts b/src/layer/segmentation/index.ts index 553d3b100..69f13c53b 100644 --- a/src/layer/segmentation/index.ts +++ b/src/layer/segmentation/index.ts @@ -1264,13 +1264,18 @@ export class SegmentationUserLayer extends Base { observeLayerColor(callback: (value: any) => void) { const disposer = super.observeLayerColor(callback); - const subDisposer = observeWatchable( + const defaultColorDisposer = observeWatchable( callback, this.displayState.segmentDefaultColor, ); + const visibleSegmentDisposer = + this.displayState.segmentationGroupState.value.visibleSegments.changed.add( + callback, + ); return () => { disposer(); - subDisposer(); + defaultColorDisposer(); + visibleSegmentDisposer(); }; } @@ -1282,8 +1287,13 @@ export class SegmentationUserLayer extends Base { const visibleSegments = this.displayState.segmentationGroupState.value.visibleSegments; - if (visibleSegments.value.size === 1) { - console.log("Only 1 segment visible and random seed"); + if (visibleSegments.size === 1) { + const id = [...visibleSegments][0]; + const color = + this.displayState.segmentationColorGroupState.value.segmentColorHash.computeCssColor( + id, + ); + return color; } return undefined; From 61a6c8696adacee0a4159f742e1cd995938b2cb6 Mon Sep 17 00:00:00 2001 From: aranega Date: Wed, 27 Nov 2024 09:46:05 -0600 Subject: [PATCH 10/36] Fix color legend widget sync accross layerbar and layer panel --- src/layer/annotation/index.ts | 2 +- src/layer/index.ts | 16 +++++++++++++--- src/layer/segmentation/index.ts | 2 +- src/ui/layer_bar.ts | 3 ++- src/ui/layer_list_panel.ts | 2 +- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/layer/annotation/index.ts b/src/layer/annotation/index.ts index 9e58e70a5..08827f522 100644 --- a/src/layer/annotation/index.ts +++ b/src/layer/annotation/index.ts @@ -716,7 +716,7 @@ export class AnnotationUserLayer extends Base { return x; } - observeLayerColor(callback: (value: any) => void) { + observeLayerColor(callback: () => void) { const disposer = super.observeLayerColor(callback); const subDisposer = observeWatchable( callback, diff --git a/src/layer/index.ts b/src/layer/index.ts index e5b328fdf..ba434c318 100644 --- a/src/layer/index.ts +++ b/src/layer/index.ts @@ -203,8 +203,17 @@ export class UserLayer extends RefCounted { layerBarUserDefinedColor = new TrackableOptionalRGB(); - observeLayerColor(callback: (value: any) => void): () => void { - return observeWatchable(callback, this.layerBarUserDefinedColor); + observeLayerColor(callback: () => void): () => void { + const userDefinedColorDisposer = observeWatchable( + callback, + this.layerBarUserDefinedColor, + ); + const layerBarColorSyncDisposer = + this.layerBarColorSync.changed.add(callback); + return () => { + userDefinedColorDisposer(); + layerBarColorSyncDisposer(); + }; } get automaticLayerBarColor(): string | undefined { @@ -397,6 +406,7 @@ export class UserLayer extends RefCounted { this.localPosition.changed.add(this.specificationChanged.dispatch); this.pick.changed.add(this.specificationChanged.dispatch); this.pick.changed.add(this.layersChanged.dispatch); + this.layerBarColorSync.changed.add(this.specificationChanged.dispatch); this.layerBarColorSync.changed.add(this.layersChanged.dispatch); this.dataSourcesChanged.add(this.specificationChanged.dispatch); this.dataSourcesChanged.add(() => this.updateDataSubsourceActivations()); @@ -805,7 +815,7 @@ export class ManagedUserLayer extends RefCounted { return userLayer?.layerBarColorSync; } - observeLayerColor(callback: (value: any) => void): () => void { + observeLayerColor(callback: () => void): () => void { const userLayer = this.layer; if (userLayer !== null) { return userLayer.observeLayerColor(callback); diff --git a/src/layer/segmentation/index.ts b/src/layer/segmentation/index.ts index 69f13c53b..d3a75953f 100644 --- a/src/layer/segmentation/index.ts +++ b/src/layer/segmentation/index.ts @@ -1262,7 +1262,7 @@ export class SegmentationUserLayer extends Base { ); } - observeLayerColor(callback: (value: any) => void) { + observeLayerColor(callback: () => void) { const disposer = super.observeLayerColor(callback); const defaultColorDisposer = observeWatchable( callback, diff --git a/src/ui/layer_bar.ts b/src/ui/layer_bar.ts index adbaf6bb3..4877e305e 100644 --- a/src/ui/layer_bar.ts +++ b/src/ui/layer_bar.ts @@ -135,6 +135,7 @@ class LayerWidget extends RefCounted { syncColorsElement = element; syncColorsElement.addEventListener("click", (event: MouseEvent) => { updateLayerColorWidget(); + this.layer.layerChanged.dispatch(); event.stopPropagation(); }); @@ -146,7 +147,7 @@ class LayerWidget extends RefCounted { this.registerDisposer( layer.layerChanged.add(() => { - if (!this.layer.visible) { + if (!this.layer.visible && this.layer.layerBarColorSync!.value) { layerColorElement.classList.add("cross"); } else { layerColorElement.classList.remove("cross"); diff --git a/src/ui/layer_list_panel.ts b/src/ui/layer_list_panel.ts index d9c690543..a40a07ed4 100644 --- a/src/ui/layer_list_panel.ts +++ b/src/ui/layer_list_panel.ts @@ -130,7 +130,7 @@ class LayerColorWidget extends RefCounted { this.registerDisposer( this.layer.layerChanged.add(() => { - if (!this.layer.visible) { + if (!this.layer.visible && this.layer.layerBarColorSync!.value) { element.classList.add("cross"); } else { element.classList.remove("cross"); From ff96fd4a6deca39c1d94f9283c2a08caf2c90774 Mon Sep 17 00:00:00 2001 From: Aiga115 Date: Wed, 27 Nov 2024 17:02:39 +0100 Subject: [PATCH 11/36] CC-167 update rainbow circle --- src/ui/layer_bar.css | 25 +++++++++++++++++++++++-- src/ui/layer_bar.ts | 4 ++-- src/ui/layer_list_panel.css | 27 ++++++++++++++++++++++++--- src/ui/layer_list_panel.ts | 3 +-- 4 files changed, 50 insertions(+), 9 deletions(-) diff --git a/src/ui/layer_bar.css b/src/ui/layer_bar.css index 055e01acf..bc89f05af 100644 --- a/src/ui/layer_bar.css +++ b/src/ui/layer_bar.css @@ -137,11 +137,32 @@ .neuroglancer-layer-color-value { position: relative; border-radius: 50%; - height: 12px; - width: 12px; + height: 10px; + width: 10px; margin: 5px; } +.neuroglancer-layer-rainbow-color-value { + position: relative; + border-radius: 50%; + height: 10px; + width: 10px; + margin: 5px; + overflow: hidden; +} + +.neuroglancer-layer-rainbow-color-value::before { + content: ''; + position: absolute; + top: -17.5%; + left: -17.5%; + width: 135%; + height: 135%; + background: conic-gradient(from 0deg, hsl(0, 100%, 50%), hsl(60, 100%, 50%), hsl(120, 100%, 50%), hsl(180, 100%, 50%), hsl(240, 100%, 50%), hsl(300, 100%, 50%), hsl(360, 100%, 50%)); + filter: blur(2.7px); + transform: scale(1.35); +} + .neuroglancer-layer-color-value.cross::before { content: ""; position: absolute; diff --git a/src/ui/layer_bar.ts b/src/ui/layer_bar.ts index adbaf6bb3..e514d9f11 100644 --- a/src/ui/layer_bar.ts +++ b/src/ui/layer_bar.ts @@ -120,8 +120,8 @@ class LayerWidget extends RefCounted { layerColorElement.style.background = "none"; layerColorElement.style.backgroundColor = color; } else { - layerColorElement.style.background = - "radial-gradient(circle, red, orange, yellow, green, blue, indigo, violet)"; + layerColorElement.className = "neuroglancer-layer-rainbow-color-value" + } }; let syncColorsElement = null; diff --git a/src/ui/layer_list_panel.css b/src/ui/layer_list_panel.css index 795716501..9b6c9a0af 100644 --- a/src/ui/layer_list_panel.css +++ b/src/ui/layer_list_panel.css @@ -48,9 +48,30 @@ .neuroglancer-layer-list-panel-color-value { position: relative; border-radius: 50%; - height: 12px; - width: 12px; - margin: 3px; + height: 10px; + width: 10px; + margin: 5px; +} + +.neuroglancer-layer-list-panel-rainbow-color-value { + position: relative; + border-radius: 50%; + height: 10px; + width: 10px; + margin: 5px; + overflow: hidden; +} + +.neuroglancer-layer-list-panel-rainbow-color-value::before { + content: ''; + position: absolute; + top: -17.5%; + left: -17.5%; + width: 135%; + height: 135%; + background: conic-gradient(from 0deg, hsl(0, 100%, 50%), hsl(60, 100%, 50%), hsl(120, 100%, 50%), hsl(180, 100%, 50%), hsl(240, 100%, 50%), hsl(300, 100%, 50%), hsl(360, 100%, 50%)); + filter: blur(1px); + transform: scale(1.35); } .neuroglancer-layer-list-panel-color-value.cross::before { diff --git a/src/ui/layer_list_panel.ts b/src/ui/layer_list_panel.ts index d9c690543..f4270a6a7 100644 --- a/src/ui/layer_list_panel.ts +++ b/src/ui/layer_list_panel.ts @@ -122,8 +122,7 @@ class LayerColorWidget extends RefCounted { element.style.background = "none"; element.style.backgroundColor = color; } else { - element.style.background = - "radial-gradient(circle, red, orange, yellow, green, blue, indigo, violet)"; + element.className = "neuroglancer-layer-list-panel-rainbow-color-value" } }), ); From f9cae7f29e756199ed6680bebdd2f522d4603bac Mon Sep 17 00:00:00 2001 From: Aiga115 Date: Wed, 27 Nov 2024 17:05:41 +0100 Subject: [PATCH 12/36] CC-167 reduce blur on rainbow circle --- src/ui/layer_bar.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/layer_bar.css b/src/ui/layer_bar.css index bc89f05af..e53cc8363 100644 --- a/src/ui/layer_bar.css +++ b/src/ui/layer_bar.css @@ -159,7 +159,7 @@ width: 135%; height: 135%; background: conic-gradient(from 0deg, hsl(0, 100%, 50%), hsl(60, 100%, 50%), hsl(120, 100%, 50%), hsl(180, 100%, 50%), hsl(240, 100%, 50%), hsl(300, 100%, 50%), hsl(360, 100%, 50%)); - filter: blur(2.7px); + filter: blur(1px); transform: scale(1.35); } From 2229c8f05aba9a91da08ea3b57e7cc5d93ae7f2e Mon Sep 17 00:00:00 2001 From: Aiga115 Date: Thu, 28 Nov 2024 11:27:40 +0100 Subject: [PATCH 13/36] CC-167 update ui of circle labels --- src/ui/layer_bar.css | 19 +++++++++++++++---- src/ui/layer_bar.ts | 11 ++++++++--- src/ui/layer_list_panel.css | 19 +++++++++++++++---- src/ui/layer_list_panel.ts | 13 +++++++++---- 4 files changed, 47 insertions(+), 15 deletions(-) diff --git a/src/ui/layer_bar.css b/src/ui/layer_bar.css index e53cc8363..33dd676a8 100644 --- a/src/ui/layer_bar.css +++ b/src/ui/layer_bar.css @@ -134,12 +134,18 @@ align-items: center; } -.neuroglancer-layer-color-value { +.neuroglancer-layer-color-value-wrapper { position: relative; + width: 10px; + height: 10px; + padding: 5px; + margin: 0 4px; +} + +.neuroglancer-layer-color-value { border-radius: 50%; height: 10px; width: 10px; - margin: 5px; } .neuroglancer-layer-rainbow-color-value { @@ -147,7 +153,6 @@ border-radius: 50%; height: 10px; width: 10px; - margin: 5px; overflow: hidden; } @@ -163,7 +168,12 @@ transform: scale(1.35); } -.neuroglancer-layer-color-value.cross::before { +.neuroglancer-layer-color-value-wrapper.cross .neuroglancer-layer-color-value, +.neuroglancer-layer-color-value-wrapper.cross .neuroglancer-layer-rainbow-color-value::before { + opacity: 0.4; +} + +.neuroglancer-layer-color-value-wrapper.cross::before { content: ""; position: absolute; top: 50%; @@ -172,6 +182,7 @@ height: 1px; background-color: rgb(255, 255, 255); transform: translate(-50%, -50%) rotate(135deg); + z-index: 1; } .neuroglancer-layer-item-value { diff --git a/src/ui/layer_bar.ts b/src/ui/layer_bar.ts index a04cdb0fd..4edb35801 100644 --- a/src/ui/layer_bar.ts +++ b/src/ui/layer_bar.ts @@ -107,8 +107,13 @@ class LayerWidget extends RefCounted { event.stopPropagation(); }); + const layerColorElementWrapper = document.createElement("div"); + layerColorElementWrapper.className = "neuroglancer-layer-color-value-wrapper" + layerColorElement.className = "neuroglancer-layer-color-value"; + layerColorElementWrapper.appendChild(layerColorElement); + const updateLayerColorWidget = () => { if (!this.layer.layerBarColorSyncEnabled) { layerColorElement.style.background = "none"; @@ -148,9 +153,9 @@ class LayerWidget extends RefCounted { this.registerDisposer( layer.layerChanged.add(() => { if (!this.layer.visible && this.layer.layerBarColorSync!.value) { - layerColorElement.classList.add("cross"); + layerColorElementWrapper.classList.add("cross"); } else { - layerColorElement.classList.remove("cross"); + layerColorElementWrapper.classList.remove("cross"); } }), ); @@ -166,7 +171,7 @@ class LayerWidget extends RefCounted { buttonContainer.appendChild(closeElement); buttonContainer.appendChild(deleteElement); element.appendChild(labelElement); - element.appendChild(layerColorElement); + element.appendChild(layerColorElementWrapper); element.appendChild(valueContainer); const positionWidget = this.registerDisposer( new PositionWidget( diff --git a/src/ui/layer_list_panel.css b/src/ui/layer_list_panel.css index 9b6c9a0af..09bab3f2c 100644 --- a/src/ui/layer_list_panel.css +++ b/src/ui/layer_list_panel.css @@ -45,12 +45,18 @@ display: inline-block; } -.neuroglancer-layer-list-panel-color-value { +.neuroglancer-layer-list-panel-color-value-wrapper { position: relative; + width: 10px; + height: 10px; + padding: 5px; + margin: 0 4px; +} + +.neuroglancer-layer-list-panel-color-value { border-radius: 50%; height: 10px; width: 10px; - margin: 5px; } .neuroglancer-layer-list-panel-rainbow-color-value { @@ -58,7 +64,6 @@ border-radius: 50%; height: 10px; width: 10px; - margin: 5px; overflow: hidden; } @@ -74,7 +79,12 @@ transform: scale(1.35); } -.neuroglancer-layer-list-panel-color-value.cross::before { +.neuroglancer-layer-list-panel-color-value-wrapper.cross .neuroglancer-layer-list-panel-color-value, +.neuroglancer-layer-list-panel-color-value-wrapper.cross .neuroglancer-layer-list-panel-rainbow-color-value::before { + opacity: 0.4; +} + +.neuroglancer-layer-list-panel-color-value-wrapper.cross::before { content: ""; position: absolute; top: 50%; @@ -83,6 +93,7 @@ height: 1px; background-color: rgb(255, 255, 255); transform: translate(-50%, -50%) rotate(135deg); + z-index: 1; } .neuroglancer-layer-list-panel-item:not(:hover) diff --git a/src/ui/layer_list_panel.ts b/src/ui/layer_list_panel.ts index 65e917107..3c2d7d261 100644 --- a/src/ui/layer_list_panel.ts +++ b/src/ui/layer_list_panel.ts @@ -105,10 +105,15 @@ class LayerVisibilityWidget extends RefCounted { class LayerColorWidget extends RefCounted { element = document.createElement("div"); + elementWrapper = document.createElement("div"); + constructor(public layer: ManagedUserLayer) { super(); - const { element } = this; + const { element, elementWrapper } = this; element.className = "neuroglancer-layer-list-panel-color-value"; + elementWrapper.className = "neuroglancer-layer-list-panel-color-value-wrapper" + + elementWrapper.appendChild(element); this.registerDisposer( this.layer.observeLayerColor(() => { @@ -130,9 +135,9 @@ class LayerColorWidget extends RefCounted { this.registerDisposer( this.layer.layerChanged.add(() => { if (!this.layer.visible && this.layer.layerBarColorSync!.value) { - element.classList.add("cross"); + elementWrapper.classList.add("cross"); } else { - element.classList.remove("cross"); + elementWrapper.classList.remove("cross"); } }), ); @@ -204,7 +209,7 @@ class LayerListItem extends RefCounted { this.registerDisposer(new LayerVisibilityWidget(layer)).element, ); element.appendChild( - this.registerDisposer(new LayerColorWidget(layer)).element, + this.registerDisposer(new LayerColorWidget(layer)).elementWrapper, ); element.appendChild( this.registerDisposer(new LayerNameWidget(layer)).element, From 9d66b66d00ecbe6c81618f2274a265d7e1a9883f Mon Sep 17 00:00:00 2001 From: aranega Date: Fri, 6 Dec 2024 03:23:59 -0600 Subject: [PATCH 14/36] Fix rainbow color not removing when number of visible segment changes --- src/ui/layer_bar.css | 6 +++--- src/ui/layer_bar.ts | 4 ++-- src/ui/layer_list_panel.css | 6 +++--- src/ui/layer_list_panel.ts | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/ui/layer_bar.css b/src/ui/layer_bar.css index 33dd676a8..76eb0ad77 100644 --- a/src/ui/layer_bar.css +++ b/src/ui/layer_bar.css @@ -148,7 +148,7 @@ width: 10px; } -.neuroglancer-layer-rainbow-color-value { +.neuroglancer-layer-color-value.rainbow { position: relative; border-radius: 50%; height: 10px; @@ -156,7 +156,7 @@ overflow: hidden; } -.neuroglancer-layer-rainbow-color-value::before { +.neuroglancer-layer-color-value.rainbow::before { content: ''; position: absolute; top: -17.5%; @@ -169,7 +169,7 @@ } .neuroglancer-layer-color-value-wrapper.cross .neuroglancer-layer-color-value, -.neuroglancer-layer-color-value-wrapper.cross .neuroglancer-layer-rainbow-color-value::before { +.neuroglancer-layer-color-value-wrapper.cross .neuroglancer-layer-color-value.rainbow::before { opacity: 0.4; } diff --git a/src/ui/layer_bar.ts b/src/ui/layer_bar.ts index 4edb35801..fff80ad41 100644 --- a/src/ui/layer_bar.ts +++ b/src/ui/layer_bar.ts @@ -122,10 +122,10 @@ class LayerWidget extends RefCounted { } const color = this.layer.layerBarColor; if (color) { - layerColorElement.style.background = "none"; layerColorElement.style.backgroundColor = color; + layerColorElement.classList.remove("rainbow") } else { - layerColorElement.className = "neuroglancer-layer-rainbow-color-value" + layerColorElement.classList.add("rainbow") } }; diff --git a/src/ui/layer_list_panel.css b/src/ui/layer_list_panel.css index 09bab3f2c..592b92f0f 100644 --- a/src/ui/layer_list_panel.css +++ b/src/ui/layer_list_panel.css @@ -59,7 +59,7 @@ width: 10px; } -.neuroglancer-layer-list-panel-rainbow-color-value { +.neuroglancer-layer-list-panel-color-value.rainbow { position: relative; border-radius: 50%; height: 10px; @@ -67,7 +67,7 @@ overflow: hidden; } -.neuroglancer-layer-list-panel-rainbow-color-value::before { +.neuroglancer-layer-list-panel-color-value.rainbow::before { content: ''; position: absolute; top: -17.5%; @@ -80,7 +80,7 @@ } .neuroglancer-layer-list-panel-color-value-wrapper.cross .neuroglancer-layer-list-panel-color-value, -.neuroglancer-layer-list-panel-color-value-wrapper.cross .neuroglancer-layer-list-panel-rainbow-color-value::before { +.neuroglancer-layer-list-panel-color-value-wrapper.cross .neuroglancer-layer-list-panel-color-value.rainbow::before { opacity: 0.4; } diff --git a/src/ui/layer_list_panel.ts b/src/ui/layer_list_panel.ts index 3c2d7d261..0f5681d63 100644 --- a/src/ui/layer_list_panel.ts +++ b/src/ui/layer_list_panel.ts @@ -124,10 +124,10 @@ class LayerColorWidget extends RefCounted { } const color = this.layer.layerBarColor; if (color) { - element.style.background = "none"; element.style.backgroundColor = color; + element.classList.remove("rainbow") } else { - element.className = "neuroglancer-layer-list-panel-rainbow-color-value" + element.classList.add("rainbow") } }), ); From f33816a4d08be4f210c7dd57bf4a3aa871a7c765 Mon Sep 17 00:00:00 2001 From: aranega Date: Fri, 6 Dec 2024 03:40:24 -0600 Subject: [PATCH 15/36] Move color widget to the left of the title in layer bar --- src/ui/layer_bar.ts | 5 +++-- src/ui/layer_list_panel.ts | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ui/layer_bar.ts b/src/ui/layer_bar.ts index fff80ad41..4d7f5c9d9 100644 --- a/src/ui/layer_bar.ts +++ b/src/ui/layer_bar.ts @@ -116,11 +116,12 @@ class LayerWidget extends RefCounted { const updateLayerColorWidget = () => { if (!this.layer.layerBarColorSyncEnabled) { - layerColorElement.style.background = "none"; layerColorElement.style.backgroundColor = ""; + layerColorElementWrapper.style.display = "none"; return; } const color = this.layer.layerBarColor; + layerColorElementWrapper.style.display = "block"; if (color) { layerColorElement.style.backgroundColor = color; layerColorElement.classList.remove("rainbow") @@ -163,6 +164,7 @@ class LayerWidget extends RefCounted { // Compose the layer's title bar element.appendChild(layerNumberElement); + element.appendChild(layerColorElementWrapper); valueContainer.appendChild(valueElement); valueContainer.appendChild(buttonContainer); if (this.layer.supportsLayerBarColorSyncOption) { @@ -171,7 +173,6 @@ class LayerWidget extends RefCounted { buttonContainer.appendChild(closeElement); buttonContainer.appendChild(deleteElement); element.appendChild(labelElement); - element.appendChild(layerColorElementWrapper); element.appendChild(valueContainer); const positionWidget = this.registerDisposer( new PositionWidget( diff --git a/src/ui/layer_list_panel.ts b/src/ui/layer_list_panel.ts index 0f5681d63..dd0a36692 100644 --- a/src/ui/layer_list_panel.ts +++ b/src/ui/layer_list_panel.ts @@ -118,11 +118,12 @@ class LayerColorWidget extends RefCounted { this.registerDisposer( this.layer.observeLayerColor(() => { if (!this.layer.layerBarColorSyncEnabled) { - element.style.background = "none"; element.style.backgroundColor = ""; + elementWrapper.style.display = "none"; return; } const color = this.layer.layerBarColor; + elementWrapper.style.display = "block"; if (color) { element.style.backgroundColor = color; element.classList.remove("rainbow") From 340be26467517f9f270845cef9c4bbcf98178679 Mon Sep 17 00:00:00 2001 From: aranega Date: Fri, 6 Dec 2024 06:13:16 -0600 Subject: [PATCH 16/36] Add general option for enabling/disabling color widget --- src/data_panel_layout.ts | 2 ++ src/layer_group_viewer.ts | 4 +++ src/layer_groups_layout.ts | 1 + src/ui/layer_bar.ts | 57 +++++++++++++------------------------- src/ui/layer_list_panel.ts | 52 +++++++++++++++++++--------------- src/ui/side_panel.ts | 2 ++ src/ui/viewer_settings.ts | 1 + src/viewer.ts | 5 +++- src/viewer_state.ts | 1 + 9 files changed, 64 insertions(+), 61 deletions(-) diff --git a/src/data_panel_layout.ts b/src/data_panel_layout.ts index 7154a5785..4fec6dd41 100644 --- a/src/data_panel_layout.ts +++ b/src/data_panel_layout.ts @@ -100,6 +100,7 @@ export interface ViewerUIState inputEventBindings: InputEventBindings; crossSectionBackgroundColor: TrackableRGB; perspectiveViewBackgroundColor: TrackableRGB; + enableLayerColorWidget: TrackableBoolean; } export interface DataDisplayLayout extends RefCounted { @@ -180,6 +181,7 @@ export function getCommonViewerState(viewer: ViewerUIState) { selectedLayer: viewer.selectedLayer, visibility: viewer.visibility, scaleBarOptions: viewer.scaleBarOptions, + enableLayerColorWidget: viewer.enableLayerColorWidget, }; } diff --git a/src/layer_group_viewer.ts b/src/layer_group_viewer.ts index dfca69cc0..ef98881cc 100644 --- a/src/layer_group_viewer.ts +++ b/src/layer_group_viewer.ts @@ -105,6 +105,7 @@ export interface LayerGroupViewerState { visibleLayerRoles: WatchableSet; crossSectionBackgroundColor: TrackableRGB; perspectiveViewBackgroundColor: TrackableRGB; + enableLayerColorWidget: TrackableBoolean; } export interface LayerGroupViewerOptions { @@ -384,6 +385,9 @@ export class LayerGroupViewer extends RefCounted { get scaleBarOptions() { return this.viewerState.scaleBarOptions; } + get enableLayerColorWidget() { + return this.viewerState.enableLayerColorWidget; + } layerPanel: LayerBar | undefined; layout: DataPanelLayoutContainer; toolBinder: LocalToolBinder; diff --git a/src/layer_groups_layout.ts b/src/layer_groups_layout.ts index 851b3ff27..6cbd79846 100644 --- a/src/layer_groups_layout.ts +++ b/src/layer_groups_layout.ts @@ -420,6 +420,7 @@ function getCommonViewerState(viewer: Viewer) { velocity: viewer.velocity.addRef(), crossSectionBackgroundColor: viewer.crossSectionBackgroundColor, perspectiveViewBackgroundColor: viewer.perspectiveViewBackgroundColor, + enableLayerColorWidget: viewer.enableLayerColorWidget, }; } diff --git a/src/ui/layer_bar.ts b/src/ui/layer_bar.ts index 4d7f5c9d9..9f04e3773 100644 --- a/src/ui/layer_bar.ts +++ b/src/ui/layer_bar.ts @@ -21,7 +21,7 @@ import type { ManagedUserLayer } from "#src/layer/index.js"; import { addNewLayer, deleteLayer, makeLayer } from "#src/layer/index.js"; import type { LayerGroupViewer } from "#src/layer_group_viewer.js"; import { NavigationLinkType } from "#src/navigation_state.js"; -import type { WatchableValueInterface } from "#src/trackable_value.js"; +import { observeWatchable, type WatchableValueInterface } from "#src/trackable_value.js"; import type { DropLayers } from "#src/ui/layer_drag_and_drop.js"; import { registerLayerBarDragLeaveHandler, @@ -32,7 +32,6 @@ import { animationFrameDebounce } from "#src/util/animation_frame_debounce.js"; import { RefCounted } from "#src/util/disposable.js"; import { removeFromParent } from "#src/util/dom.js"; import { preventDrag } from "#src/util/drag_and_drop.js"; -import { CheckboxIcon } from "#src/widget/checkbox_icon.js"; import { makeCloseButton } from "#src/widget/close_button.js"; import { makeDeleteButton } from "#src/widget/delete_button.js"; import { makeIcon } from "#src/widget/icon.js"; @@ -109,57 +108,42 @@ class LayerWidget extends RefCounted { const layerColorElementWrapper = document.createElement("div"); layerColorElementWrapper.className = "neuroglancer-layer-color-value-wrapper" - layerColorElement.className = "neuroglancer-layer-color-value"; - layerColorElementWrapper.appendChild(layerColorElement); - const updateLayerColorWidget = () => { - if (!this.layer.layerBarColorSyncEnabled) { - layerColorElement.style.backgroundColor = ""; - layerColorElementWrapper.style.display = "none"; - return; - } - const color = this.layer.layerBarColor; - layerColorElementWrapper.style.display = "block"; - if (color) { - layerColorElement.style.backgroundColor = color; - layerColorElement.classList.remove("rainbow") - } else { - layerColorElement.classList.add("rainbow") - - } - }; - let syncColorsElement = null; if (this.layer.supportsLayerBarColorSyncOption) { - const { element } = new CheckboxIcon(this.layer.layerBarColorSync!, { - text: "#", - backgroundScheme: "dark", - enableTitle: "Enable color sync", - disableTitle: "Disable color sync", - }); - syncColorsElement = element; - syncColorsElement.addEventListener("click", (event: MouseEvent) => { - updateLayerColorWidget(); - this.layer.layerChanged.dispatch(); - event.stopPropagation(); - }); + const updateLayerColorWidget = () => { + const color = this.layer.layerBarColor; + if (color) { + layerColorElement.style.backgroundColor = color; + layerColorElement.classList.remove("rainbow") + } else { + layerColorElement.classList.add("rainbow") + } + }; this.registerDisposer( layer.observeLayerColor(() => { updateLayerColorWidget(); }), ); - + this.registerDisposer( + observeWatchable((layerColorEnabled) => { + layerColorElementWrapper.style.display = layerColorEnabled ? "block" : "none"; + }, this.panel.layerGroupViewer.viewerState.enableLayerColorWidget) + ) this.registerDisposer( layer.layerChanged.add(() => { - if (!this.layer.visible && this.layer.layerBarColorSync!.value) { + if (!this.layer.visible) { layerColorElementWrapper.classList.add("cross"); } else { layerColorElementWrapper.classList.remove("cross"); } }), ); + } else { + // If the layer do not support color sync (no color to deal with) + layerColorElement.classList.add("unsupported") } // Compose the layer's title bar @@ -167,9 +151,6 @@ class LayerWidget extends RefCounted { element.appendChild(layerColorElementWrapper); valueContainer.appendChild(valueElement); valueContainer.appendChild(buttonContainer); - if (this.layer.supportsLayerBarColorSyncOption) { - buttonContainer.appendChild(syncColorsElement!); - } buttonContainer.appendChild(closeElement); buttonContainer.appendChild(deleteElement); element.appendChild(labelElement); diff --git a/src/ui/layer_list_panel.ts b/src/ui/layer_list_panel.ts index dd0a36692..ce3eae43b 100644 --- a/src/ui/layer_list_panel.ts +++ b/src/ui/layer_list_panel.ts @@ -25,6 +25,7 @@ import type { } from "#src/layer/index.js"; import { deleteLayer } from "#src/layer/index.js"; import { TrackableBooleanCheckbox } from "#src/trackable_boolean.js"; +import { observeWatchable } from "#src/trackable_value.js"; import type { DropLayers } from "#src/ui/layer_drag_and_drop.js"; import { registerLayerBarDragLeaveHandler, @@ -107,41 +108,48 @@ class LayerColorWidget extends RefCounted { element = document.createElement("div"); elementWrapper = document.createElement("div"); - constructor(public layer: ManagedUserLayer) { + constructor(public panel: LayerListPanel, public layer: ManagedUserLayer) { super(); const { element, elementWrapper } = this; element.className = "neuroglancer-layer-list-panel-color-value"; elementWrapper.className = "neuroglancer-layer-list-panel-color-value-wrapper" - elementWrapper.appendChild(element); - this.registerDisposer( - this.layer.observeLayerColor(() => { - if (!this.layer.layerBarColorSyncEnabled) { - element.style.backgroundColor = ""; - elementWrapper.style.display = "none"; - return; - } + if (this.layer.supportsLayerBarColorSyncOption) { + const updateLayerColorWidget = () => { const color = this.layer.layerBarColor; - elementWrapper.style.display = "block"; if (color) { element.style.backgroundColor = color; element.classList.remove("rainbow") } else { element.classList.add("rainbow") - } - }), - ); - this.registerDisposer( - this.layer.layerChanged.add(() => { - if (!this.layer.visible && this.layer.layerBarColorSync!.value) { - elementWrapper.classList.add("cross"); - } else { - elementWrapper.classList.remove("cross"); } - }), - ); + }; + this.registerDisposer( + layer.observeLayerColor(() => { + updateLayerColorWidget(); + }), + ); + panel.sidePanelManager.viewerState.enableLayerColorWidget + this.registerDisposer( + observeWatchable((layerColorEnabled) => { + elementWrapper.style.display = layerColorEnabled ? "block" : "none"; + }, panel.sidePanelManager.viewerState.enableLayerColorWidget) + ) + this.registerDisposer( + layer.layerChanged.add(() => { + if (!this.layer.visible) { + elementWrapper.classList.add("cross"); + } else { + elementWrapper.classList.remove("cross"); + } + }), + ); + } else { + // If the layer do not support color sync (no color to deal with) + element.classList.add("unsupported") + } } } @@ -210,7 +218,7 @@ class LayerListItem extends RefCounted { this.registerDisposer(new LayerVisibilityWidget(layer)).element, ); element.appendChild( - this.registerDisposer(new LayerColorWidget(layer)).elementWrapper, + this.registerDisposer(new LayerColorWidget(panel, layer)).elementWrapper, ); element.appendChild( this.registerDisposer(new LayerNameWidget(layer)).element, diff --git a/src/ui/side_panel.ts b/src/ui/side_panel.ts index a0e9c285f..65ee90d42 100644 --- a/src/ui/side_panel.ts +++ b/src/ui/side_panel.ts @@ -24,6 +24,7 @@ import { RefCounted } from "#src/util/disposable.js"; import { updateChildren } from "#src/util/dom.js"; import { startRelativeMouseDrag } from "#src/util/mouse_drag.js"; import { Signal } from "#src/util/signal.js"; +import type { ViewerState } from "#src/viewer_state.js"; import { WatchableVisibilityPriority } from "#src/visibility_priority/frontend.js"; import { makeCloseButton } from "#src/widget/close_button.js"; @@ -225,6 +226,7 @@ export class SidePanelManager extends RefCounted { public visibility = new WatchableVisibilityPriority( WatchableVisibilityPriority.VISIBLE, ), + public viewerState: ViewerState ) { super(); const { element, centerColumn } = this; diff --git a/src/ui/viewer_settings.ts b/src/ui/viewer_settings.ts index a0c936f94..193c90050 100644 --- a/src/ui/viewer_settings.ts +++ b/src/ui/viewer_settings.ts @@ -129,6 +129,7 @@ export class ViewerSettingsPanel extends SidePanel { "Enable adaptive downsampling", viewer.enableAdaptiveDownsampling, ); + addCheckbox("Enable layer color widget", viewer.enableLayerColorWidget); const addColor = (label: string, value: WatchableValueInterface) => { const labelElement = document.createElement("label"); diff --git a/src/viewer.ts b/src/viewer.ts index 73a72f171..96e026432 100644 --- a/src/viewer.ts +++ b/src/viewer.ts @@ -328,6 +328,7 @@ class TrackableViewerState extends CompoundTrackable { this.add("partialViewport", viewer.partialViewport); this.add("selectedStateServer", viewer.selectedStateServer); this.add("toolBindings", viewer.toolBinder); + this.add("enableLayerColorWidget", viewer.enableLayerColorWidget); } restoreState(obj: any) { @@ -468,6 +469,8 @@ export class Viewer extends RefCounted implements ViewerState { vec3.fromValues(0.5, 0.5, 0.5), ); perspectiveViewBackgroundColor = new TrackableRGB(vec3.fromValues(0, 0, 0)); + enableLayerColorWidget = new TrackableBoolean(false, false); + scaleBarOptions = new TrackableScaleBarOptions(); partialViewport = new TrackableWindowedViewport(); statisticsDisplayState = new StatisticsDisplayState(); @@ -911,7 +914,7 @@ export class Viewer extends RefCounted implements ViewerState { new RootLayoutContainer(this, "4panel"), ); this.sidePanelManager = this.registerDisposer( - new SidePanelManager(this.display, this.layout.element, this.visibility), + new SidePanelManager(this.display, this.layout.element, this.visibility, this), ); this.registerDisposer( this.sidePanelManager.registerPanel({ diff --git a/src/viewer_state.ts b/src/viewer_state.ts index abb11b03b..1d3c30e90 100644 --- a/src/viewer_state.ts +++ b/src/viewer_state.ts @@ -36,4 +36,5 @@ export interface ViewerState extends VisibilityPrioritySpecification { layerManager: LayerManager; selectedLayer: SelectedLayerState; selectionDetailsState: TrackableDataSelectionState; + enableLayerColorWidget: TrackableBoolean; } From 5aab0c4235e49c82cdd83c2688f8ff83ea042f3f Mon Sep 17 00:00:00 2001 From: aranega Date: Fri, 6 Dec 2024 06:25:21 -0600 Subject: [PATCH 17/36] Remove unused trackable boolean for color sync inside a layer --- src/layer/index.ts | 46 +--------------------------------------------- 1 file changed, 1 insertion(+), 45 deletions(-) diff --git a/src/layer/index.ts b/src/layer/index.ts index ba434c318..9f9864165 100644 --- a/src/layer/index.ts +++ b/src/layer/index.ts @@ -127,7 +127,6 @@ const LOCAL_COORDINATE_SPACE_JSON_KEY = "localDimensions"; const SOURCE_JSON_KEY = "source"; const TRANSFORM_JSON_KEY = "transform"; const PICK_JSON_KEY = "pick"; -const LAYERBAR_COLOR_JSON_KEY = "layerBarColorSync"; export interface UserLayerSelectionState { generation: number; @@ -199,21 +198,14 @@ export class UserLayer extends RefCounted { messages = new MessageList(); - layerBarColorSync = new TrackableBoolean(false, false); layerBarUserDefinedColor = new TrackableOptionalRGB(); observeLayerColor(callback: () => void): () => void { - const userDefinedColorDisposer = observeWatchable( + return observeWatchable( callback, this.layerBarUserDefinedColor, ); - const layerBarColorSyncDisposer = - this.layerBarColorSync.changed.add(callback); - return () => { - userDefinedColorDisposer(); - layerBarColorSyncDisposer(); - }; } get automaticLayerBarColor(): string | undefined { @@ -406,8 +398,6 @@ export class UserLayer extends RefCounted { this.localPosition.changed.add(this.specificationChanged.dispatch); this.pick.changed.add(this.specificationChanged.dispatch); this.pick.changed.add(this.layersChanged.dispatch); - this.layerBarColorSync.changed.add(this.specificationChanged.dispatch); - this.layerBarColorSync.changed.add(this.layersChanged.dispatch); this.dataSourcesChanged.add(this.specificationChanged.dispatch); this.dataSourcesChanged.add(() => this.updateDataSubsourceActivations()); this.messages.changed.add(this.layersChanged.dispatch); @@ -562,13 +552,6 @@ export class UserLayer extends RefCounted { if ((this.constructor as typeof UserLayer).supportsPickOption) { this.pick.restoreState(specification[PICK_JSON_KEY]); } - if ( - (this.constructor as typeof UserLayer).supportsLayerBarColorSyncOption - ) { - this.layerBarColorSync.restoreState( - specification[LAYERBAR_COLOR_JSON_KEY], - ); - } for (const spec of this.getDataSourceSpecifications(specification)) { this.addDataSource(spec); } @@ -642,7 +625,6 @@ export class UserLayer extends RefCounted { [LOCAL_POSITION_JSON_KEY]: this.localPosition.toJSON(), [LOCAL_VELOCITY_JSON_KEY]: this.localVelocity.toJSON(), [PICK_JSON_KEY]: this.pick.toJSON(), - [LAYERBAR_COLOR_JSON_KEY]: this.layerBarColorSync.toJSON(), ...this.panels.toJSON(), }; } @@ -784,37 +766,11 @@ export class ManagedUserLayer extends RefCounted { } } - get layerBarColorSyncEnabled() { - const userLayer = this.layer; - return ( - userLayer !== null && - (userLayer.constructor as typeof UserLayer) - .supportsLayerBarColorSyncOption && - userLayer.layerBarColorSync.value - ); - } - - set layerBarColorSyncEnabled(value: boolean) { - const userLayer = this.layer; - if ( - userLayer !== null && - (userLayer.constructor as typeof UserLayer) - .supportsLayerBarColorSyncOption - ) { - userLayer.layerBarColorSync.value = value; - } - } - get layerBarColor(): string | undefined { const userLayer = this.layer; return userLayer?.layerBarColor; } - get layerBarColorSync() { - const userLayer = this.layer; - return userLayer?.layerBarColorSync; - } - observeLayerColor(callback: () => void): () => void { const userLayer = this.layer; if (userLayer !== null) { From 91dbfba18e5570f545c619a4ea9e2c5689233fc4 Mon Sep 17 00:00:00 2001 From: aranega Date: Fri, 6 Dec 2024 06:26:03 -0600 Subject: [PATCH 18/36] Fix format --- src/layer/index.ts | 6 +----- src/ui/layer_bar.css | 16 +++++++++++++--- src/ui/layer_bar.ts | 23 ++++++++++++++--------- src/ui/layer_list_panel.css | 19 +++++++++++++++---- src/ui/layer_list_panel.ts | 21 ++++++++++++--------- src/ui/side_panel.ts | 2 +- src/viewer.ts | 7 ++++++- 7 files changed, 62 insertions(+), 32 deletions(-) diff --git a/src/layer/index.ts b/src/layer/index.ts index 9f9864165..3e5a6e4f2 100644 --- a/src/layer/index.ts +++ b/src/layer/index.ts @@ -198,14 +198,10 @@ export class UserLayer extends RefCounted { messages = new MessageList(); - layerBarUserDefinedColor = new TrackableOptionalRGB(); observeLayerColor(callback: () => void): () => void { - return observeWatchable( - callback, - this.layerBarUserDefinedColor, - ); + return observeWatchable(callback, this.layerBarUserDefinedColor); } get automaticLayerBarColor(): string | undefined { diff --git a/src/ui/layer_bar.css b/src/ui/layer_bar.css index 76eb0ad77..fa31c2d7a 100644 --- a/src/ui/layer_bar.css +++ b/src/ui/layer_bar.css @@ -157,19 +157,29 @@ } .neuroglancer-layer-color-value.rainbow::before { - content: ''; + content: ""; position: absolute; top: -17.5%; left: -17.5%; width: 135%; height: 135%; - background: conic-gradient(from 0deg, hsl(0, 100%, 50%), hsl(60, 100%, 50%), hsl(120, 100%, 50%), hsl(180, 100%, 50%), hsl(240, 100%, 50%), hsl(300, 100%, 50%), hsl(360, 100%, 50%)); + background: conic-gradient( + from 0deg, + hsl(0, 100%, 50%), + hsl(60, 100%, 50%), + hsl(120, 100%, 50%), + hsl(180, 100%, 50%), + hsl(240, 100%, 50%), + hsl(300, 100%, 50%), + hsl(360, 100%, 50%) + ); filter: blur(1px); transform: scale(1.35); } .neuroglancer-layer-color-value-wrapper.cross .neuroglancer-layer-color-value, -.neuroglancer-layer-color-value-wrapper.cross .neuroglancer-layer-color-value.rainbow::before { +.neuroglancer-layer-color-value-wrapper.cross + .neuroglancer-layer-color-value.rainbow::before { opacity: 0.4; } diff --git a/src/ui/layer_bar.ts b/src/ui/layer_bar.ts index 9f04e3773..d4cdeb91e 100644 --- a/src/ui/layer_bar.ts +++ b/src/ui/layer_bar.ts @@ -21,7 +21,10 @@ import type { ManagedUserLayer } from "#src/layer/index.js"; import { addNewLayer, deleteLayer, makeLayer } from "#src/layer/index.js"; import type { LayerGroupViewer } from "#src/layer_group_viewer.js"; import { NavigationLinkType } from "#src/navigation_state.js"; -import { observeWatchable, type WatchableValueInterface } from "#src/trackable_value.js"; +import { + observeWatchable, + type WatchableValueInterface, +} from "#src/trackable_value.js"; import type { DropLayers } from "#src/ui/layer_drag_and_drop.js"; import { registerLayerBarDragLeaveHandler, @@ -107,7 +110,8 @@ class LayerWidget extends RefCounted { }); const layerColorElementWrapper = document.createElement("div"); - layerColorElementWrapper.className = "neuroglancer-layer-color-value-wrapper" + layerColorElementWrapper.className = + "neuroglancer-layer-color-value-wrapper"; layerColorElement.className = "neuroglancer-layer-color-value"; layerColorElementWrapper.appendChild(layerColorElement); @@ -116,10 +120,9 @@ class LayerWidget extends RefCounted { const color = this.layer.layerBarColor; if (color) { layerColorElement.style.backgroundColor = color; - layerColorElement.classList.remove("rainbow") + layerColorElement.classList.remove("rainbow"); } else { - layerColorElement.classList.add("rainbow") - + layerColorElement.classList.add("rainbow"); } }; this.registerDisposer( @@ -129,9 +132,11 @@ class LayerWidget extends RefCounted { ); this.registerDisposer( observeWatchable((layerColorEnabled) => { - layerColorElementWrapper.style.display = layerColorEnabled ? "block" : "none"; - }, this.panel.layerGroupViewer.viewerState.enableLayerColorWidget) - ) + layerColorElementWrapper.style.display = layerColorEnabled + ? "block" + : "none"; + }, this.panel.layerGroupViewer.viewerState.enableLayerColorWidget), + ); this.registerDisposer( layer.layerChanged.add(() => { if (!this.layer.visible) { @@ -143,7 +148,7 @@ class LayerWidget extends RefCounted { ); } else { // If the layer do not support color sync (no color to deal with) - layerColorElement.classList.add("unsupported") + layerColorElement.classList.add("unsupported"); } // Compose the layer's title bar diff --git a/src/ui/layer_list_panel.css b/src/ui/layer_list_panel.css index 592b92f0f..d03ef68c4 100644 --- a/src/ui/layer_list_panel.css +++ b/src/ui/layer_list_panel.css @@ -68,19 +68,30 @@ } .neuroglancer-layer-list-panel-color-value.rainbow::before { - content: ''; + content: ""; position: absolute; top: -17.5%; left: -17.5%; width: 135%; height: 135%; - background: conic-gradient(from 0deg, hsl(0, 100%, 50%), hsl(60, 100%, 50%), hsl(120, 100%, 50%), hsl(180, 100%, 50%), hsl(240, 100%, 50%), hsl(300, 100%, 50%), hsl(360, 100%, 50%)); + background: conic-gradient( + from 0deg, + hsl(0, 100%, 50%), + hsl(60, 100%, 50%), + hsl(120, 100%, 50%), + hsl(180, 100%, 50%), + hsl(240, 100%, 50%), + hsl(300, 100%, 50%), + hsl(360, 100%, 50%) + ); filter: blur(1px); transform: scale(1.35); } -.neuroglancer-layer-list-panel-color-value-wrapper.cross .neuroglancer-layer-list-panel-color-value, -.neuroglancer-layer-list-panel-color-value-wrapper.cross .neuroglancer-layer-list-panel-color-value.rainbow::before { +.neuroglancer-layer-list-panel-color-value-wrapper.cross + .neuroglancer-layer-list-panel-color-value, +.neuroglancer-layer-list-panel-color-value-wrapper.cross + .neuroglancer-layer-list-panel-color-value.rainbow::before { opacity: 0.4; } diff --git a/src/ui/layer_list_panel.ts b/src/ui/layer_list_panel.ts index ce3eae43b..b2dbf4b19 100644 --- a/src/ui/layer_list_panel.ts +++ b/src/ui/layer_list_panel.ts @@ -108,11 +108,15 @@ class LayerColorWidget extends RefCounted { element = document.createElement("div"); elementWrapper = document.createElement("div"); - constructor(public panel: LayerListPanel, public layer: ManagedUserLayer) { + constructor( + public panel: LayerListPanel, + public layer: ManagedUserLayer, + ) { super(); const { element, elementWrapper } = this; element.className = "neuroglancer-layer-list-panel-color-value"; - elementWrapper.className = "neuroglancer-layer-list-panel-color-value-wrapper" + elementWrapper.className = + "neuroglancer-layer-list-panel-color-value-wrapper"; elementWrapper.appendChild(element); if (this.layer.supportsLayerBarColorSyncOption) { @@ -120,10 +124,9 @@ class LayerColorWidget extends RefCounted { const color = this.layer.layerBarColor; if (color) { element.style.backgroundColor = color; - element.classList.remove("rainbow") + element.classList.remove("rainbow"); } else { - element.classList.add("rainbow") - + element.classList.add("rainbow"); } }; this.registerDisposer( @@ -131,12 +134,12 @@ class LayerColorWidget extends RefCounted { updateLayerColorWidget(); }), ); - panel.sidePanelManager.viewerState.enableLayerColorWidget + panel.sidePanelManager.viewerState.enableLayerColorWidget; this.registerDisposer( observeWatchable((layerColorEnabled) => { elementWrapper.style.display = layerColorEnabled ? "block" : "none"; - }, panel.sidePanelManager.viewerState.enableLayerColorWidget) - ) + }, panel.sidePanelManager.viewerState.enableLayerColorWidget), + ); this.registerDisposer( layer.layerChanged.add(() => { if (!this.layer.visible) { @@ -148,7 +151,7 @@ class LayerColorWidget extends RefCounted { ); } else { // If the layer do not support color sync (no color to deal with) - element.classList.add("unsupported") + element.classList.add("unsupported"); } } } diff --git a/src/ui/side_panel.ts b/src/ui/side_panel.ts index 65ee90d42..15c5d3307 100644 --- a/src/ui/side_panel.ts +++ b/src/ui/side_panel.ts @@ -226,7 +226,7 @@ export class SidePanelManager extends RefCounted { public visibility = new WatchableVisibilityPriority( WatchableVisibilityPriority.VISIBLE, ), - public viewerState: ViewerState + public viewerState: ViewerState, ) { super(); const { element, centerColumn } = this; diff --git a/src/viewer.ts b/src/viewer.ts index 96e026432..226cdc9ae 100644 --- a/src/viewer.ts +++ b/src/viewer.ts @@ -914,7 +914,12 @@ export class Viewer extends RefCounted implements ViewerState { new RootLayoutContainer(this, "4panel"), ); this.sidePanelManager = this.registerDisposer( - new SidePanelManager(this.display, this.layout.element, this.visibility, this), + new SidePanelManager( + this.display, + this.layout.element, + this.visibility, + this, + ), ); this.registerDisposer( this.sidePanelManager.registerPanel({ From 83cde4f89d75aa6e9432ebc1ee94299aaaa6e684 Mon Sep 17 00:00:00 2001 From: aranega Date: Fri, 6 Dec 2024 06:33:37 -0600 Subject: [PATCH 19/36] Add layer bar color change on color seed change --- src/layer/segmentation/index.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/layer/segmentation/index.ts b/src/layer/segmentation/index.ts index d3a75953f..d252021f3 100644 --- a/src/layer/segmentation/index.ts +++ b/src/layer/segmentation/index.ts @@ -1272,10 +1272,15 @@ export class SegmentationUserLayer extends Base { this.displayState.segmentationGroupState.value.visibleSegments.changed.add( callback, ); + const colorHashChangeDisposer = + this.displayState.segmentationColorGroupState.value.segmentColorHash.changed.add( + callback, + ); return () => { disposer(); defaultColorDisposer(); visibleSegmentDisposer(); + colorHashChangeDisposer(); }; } From 46c78e8c3dc98fbffdf2cd23e8f8e1f4be84f840 Mon Sep 17 00:00:00 2001 From: aranega Date: Mon, 9 Dec 2024 10:38:27 -0600 Subject: [PATCH 20/36] Refactor color widget in layer bar --- src/layer/annotation/index.ts | 8 +++++ src/layer/index.ts | 9 +++++ src/layer/segmentation/index.ts | 14 ++++++++ src/ui/layer_bar.css | 35 ++++++++++--------- src/ui/layer_bar.ts | 59 +++++++++++++-------------------- 5 files changed, 74 insertions(+), 51 deletions(-) diff --git a/src/layer/annotation/index.ts b/src/layer/annotation/index.ts index 08827f522..5f10663c7 100644 --- a/src/layer/annotation/index.ts +++ b/src/layer/annotation/index.ts @@ -737,6 +737,14 @@ export class AnnotationUserLayer extends Base { return undefined; } + colorWidgetTooltip(): string | undefined { + if (this.annotationDisplayState.color) { + return `The color comes from the selected shader default color`; + } + + return undefined; + } + static type = "annotation"; static typeAbbreviation = "ann"; static supportsLayerBarColorSyncOption = true; diff --git a/src/layer/index.ts b/src/layer/index.ts index 3e5a6e4f2..eb86a6d16 100644 --- a/src/layer/index.ts +++ b/src/layer/index.ts @@ -215,6 +215,10 @@ export class UserLayer extends RefCounted { return this.automaticLayerBarColor; } + colorWidgetTooltip(): string | undefined { + return undefined; + } + initializeSelectionState(state: this["selectionState"]) { state.generation = -1; state.localPositionValid = false; @@ -767,6 +771,11 @@ export class ManagedUserLayer extends RefCounted { return userLayer?.layerBarColor; } + colorWidgetTooltip(): string | undefined { + const userLayer = this.layer; + return userLayer?.colorWidgetTooltip(); + } + observeLayerColor(callback: () => void): () => void { const userLayer = this.layer; if (userLayer !== null) { diff --git a/src/layer/segmentation/index.ts b/src/layer/segmentation/index.ts index d252021f3..48e63f09f 100644 --- a/src/layer/segmentation/index.ts +++ b/src/layer/segmentation/index.ts @@ -1304,6 +1304,20 @@ export class SegmentationUserLayer extends Base { return undefined; } + colorWidgetTooltip(): string { + if (this.displayState.segmentDefaultColor.value) { + return `The color comes from the manually selected color`; + } + + const visibleSegments = + this.displayState.segmentationGroupState.value.visibleSegments; + if (visibleSegments.size === 1) { + const id = [...visibleSegments][0]; + return `The color of the visible segment with id "${id}"`; + } + return "The segmentation layer has multiple segments visible"; + } + static type = "segmentation"; static typeAbbreviation = "seg"; static supportsPickOption = true; diff --git a/src/ui/layer_bar.css b/src/ui/layer_bar.css index fa31c2d7a..692d1ae12 100644 --- a/src/ui/layer_bar.css +++ b/src/ui/layer_bar.css @@ -134,21 +134,13 @@ align-items: center; } -.neuroglancer-layer-color-value-wrapper { - position: relative; - width: 10px; - height: 10px; - padding: 5px; - margin: 0 4px; -} - -.neuroglancer-layer-color-value { +.neuroglancer-layer-item[data-color="fixed"] .neuroglancer-layer-color-value { border-radius: 50%; height: 10px; width: 10px; } -.neuroglancer-layer-color-value.rainbow { +.neuroglancer-layer-item[data-color="rainbow"] .neuroglancer-layer-color-value { position: relative; border-radius: 50%; height: 10px; @@ -156,7 +148,8 @@ overflow: hidden; } -.neuroglancer-layer-color-value.rainbow::before { +.neuroglancer-layer-item[data-color="rainbow"] + .neuroglancer-layer-color-value::before { content: ""; position: absolute; top: -17.5%; @@ -177,13 +170,25 @@ transform: scale(1.35); } -.neuroglancer-layer-color-value-wrapper.cross .neuroglancer-layer-color-value, -.neuroglancer-layer-color-value-wrapper.cross - .neuroglancer-layer-color-value.rainbow::before { +.neuroglancer-layer-color-value-wrapper { + position: relative; + width: 10px; + height: 10px; + padding: 5px; + margin: 0 4px; +} + +.neuroglancer-layer-item[data-visible="false"] + .neuroglancer-layer-color-value-wrapper + .neuroglancer-layer-color-value, +.neuroglancer-layer-item[data-visible="false"] + .neuroglancer-layer-color-value-wrapper + .neuroglancer-layer-color-value::before { opacity: 0.4; } -.neuroglancer-layer-color-value-wrapper.cross::before { +.neuroglancer-layer-item[data-visible="false"] + .neuroglancer-layer-color-value-wrapper::before { content: ""; position: absolute; top: 50%; diff --git a/src/ui/layer_bar.ts b/src/ui/layer_bar.ts index d4cdeb91e..71ce118c0 100644 --- a/src/ui/layer_bar.ts +++ b/src/ui/layer_bar.ts @@ -115,41 +115,13 @@ class LayerWidget extends RefCounted { layerColorElement.className = "neuroglancer-layer-color-value"; layerColorElementWrapper.appendChild(layerColorElement); - if (this.layer.supportsLayerBarColorSyncOption) { - const updateLayerColorWidget = () => { - const color = this.layer.layerBarColor; - if (color) { - layerColorElement.style.backgroundColor = color; - layerColorElement.classList.remove("rainbow"); - } else { - layerColorElement.classList.add("rainbow"); - } - }; - this.registerDisposer( - layer.observeLayerColor(() => { - updateLayerColorWidget(); - }), - ); - this.registerDisposer( - observeWatchable((layerColorEnabled) => { - layerColorElementWrapper.style.display = layerColorEnabled - ? "block" - : "none"; - }, this.panel.layerGroupViewer.viewerState.enableLayerColorWidget), - ); - this.registerDisposer( - layer.layerChanged.add(() => { - if (!this.layer.visible) { - layerColorElementWrapper.classList.add("cross"); - } else { - layerColorElementWrapper.classList.remove("cross"); - } - }), - ); - } else { - // If the layer do not support color sync (no color to deal with) - layerColorElement.classList.add("unsupported"); - } + this.registerDisposer( + observeWatchable((layerColorEnabled) => { + layerColorElementWrapper.style.display = layerColorEnabled + ? "block" + : "none"; + }, this.panel.layerGroupViewer.viewerState.enableLayerColorWidget), + ); // Compose the layer's title bar element.appendChild(layerNumberElement); @@ -201,7 +173,7 @@ class LayerWidget extends RefCounted { } update() { - const { layer, element } = this; + const { layer, element, panel, layerColorElement } = this; this.labelElementText.textContent = layer.name; element.dataset.visible = layer.visible.toString(); element.dataset.selected = ( @@ -218,6 +190,21 @@ class LayerWidget extends RefCounted { } title += ", drag to move, shift+drag to copy"; element.title = title; + // Color widget updates + if (panel.layerGroupViewer.viewerState.enableLayerColorWidget.value) { + if (layer.supportsLayerBarColorSyncOption) { + const color = this.layer.layerBarColor; + if (color) { + element.dataset.color = "fixed"; + layerColorElement.style.backgroundColor = color; + } else { + element.dataset.color = "rainbow"; + } + } else { + element.dataset.color = "unsupported"; + } + } + layerColorElement.title = layer.colorWidgetTooltip() || ""; } disposed() { From bddd998309c207ac83bd3f6ba8a61ec023f111d0 Mon Sep 17 00:00:00 2001 From: aranega Date: Tue, 17 Dec 2024 07:30:06 -0600 Subject: [PATCH 21/36] Fix issue with color widget not updated when activated for the first time --- src/ui/layer_bar.css | 24 ++++++++++++++++++++++++ src/ui/layer_bar.ts | 11 ++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/ui/layer_bar.css b/src/ui/layer_bar.css index 692d1ae12..8c68a011a 100644 --- a/src/ui/layer_bar.css +++ b/src/ui/layer_bar.css @@ -170,6 +170,25 @@ transform: scale(1.35); } +.neuroglancer-layer-item[data-color="unsupported"] + .neuroglancer-layer-color-value { + background-image: linear-gradient( + 45deg, + rgba(128, 128, 128, 0.5) 25%, + transparent 25% + ), + linear-gradient(-45deg, rgba(128, 128, 128, 0.5) 25%, transparent 25%), + linear-gradient(45deg, transparent 75%, rgba(128, 128, 128, 0.5) 75%), + linear-gradient(-45deg, transparent 75%, rgba(128, 128, 128, 0.5) 75%); + background-size: 4px 4px; + background-position: + 0 0, + 0 2px, + 2px -2px, + -2px 0px; + padding: 6px; +} + .neuroglancer-layer-color-value-wrapper { position: relative; width: 10px; @@ -178,6 +197,11 @@ margin: 0 4px; } +.neuroglancer-layer-item[data-color="unsupported"] + .neuroglancer-layer-color-value-wrapper { + position: relative; +} + .neuroglancer-layer-item[data-visible="false"] .neuroglancer-layer-color-value-wrapper .neuroglancer-layer-color-value, diff --git a/src/ui/layer_bar.ts b/src/ui/layer_bar.ts index 71ce118c0..fd9c07cf9 100644 --- a/src/ui/layer_bar.ts +++ b/src/ui/layer_bar.ts @@ -257,13 +257,17 @@ export class LayerBar extends RefCounted { return this.layerGroupViewer.viewerNavigationState; } + get viewerState() { + return this.layerGroupViewer.viewerState; + } + constructor( public layerGroupViewer: LayerGroupViewer, public getLayoutSpecForDrag: () => any, public showLayerHoverValues: WatchableValueInterface, ) { super(); - const { element, manager, selectedLayer } = this; + const { element, manager, selectedLayer, viewerState } = this; element.className = "neuroglancer-layer-panel"; this.registerDisposer( manager.layerSelectedValues.changed.add(() => { @@ -285,6 +289,11 @@ export class LayerBar extends RefCounted { this.handleLayerItemValueChanged(); }), ); + this.registerDisposer( + viewerState.enableLayerColorWidget.changed.add(() => { + this.handleLayersChanged(); + }), + ); this.element.dataset.showHoverValues = this.showLayerHoverValues.value.toString(); this.layerWidgetInsertionPoint.style.display = "none"; From 491a15d9d3c50b75342e4c8419fc5d03b6af65a3 Mon Sep 17 00:00:00 2001 From: aranega Date: Tue, 17 Dec 2024 09:51:12 -0600 Subject: [PATCH 22/36] Fix unsupported style activation for layer list panel --- src/ui/layer_list_panel.css | 22 ++++++++++++ src/ui/layer_list_panel.ts | 68 +++++++++++++++++++++---------------- 2 files changed, 60 insertions(+), 30 deletions(-) diff --git a/src/ui/layer_list_panel.css b/src/ui/layer_list_panel.css index d03ef68c4..6506e5514 100644 --- a/src/ui/layer_list_panel.css +++ b/src/ui/layer_list_panel.css @@ -88,6 +88,28 @@ transform: scale(1.35); } +.neuroglancer-layer-list-panel-color-value.unsupported { + background-image: linear-gradient( + 45deg, + rgba(128, 128, 128, 0.5) 25%, + transparent 25% + ), + linear-gradient(-45deg, rgba(128, 128, 128, 0.5) 25%, transparent 25%), + linear-gradient(45deg, transparent 75%, rgba(128, 128, 128, 0.5) 75%), + linear-gradient(-45deg, transparent 75%, rgba(128, 128, 128, 0.5) 75%); + background-size: 4px 4px; + background-position: + 0 0, + 0 2px, + 2px -2px, + -2px 0px; + padding: 6px; +} + +.neuroglancer-layer-list-panel-color-value-wrapper.unsupported::before { + position: relative; +} + .neuroglancer-layer-list-panel-color-value-wrapper.cross .neuroglancer-layer-list-panel-color-value, .neuroglancer-layer-list-panel-color-value-wrapper.cross diff --git a/src/ui/layer_list_panel.ts b/src/ui/layer_list_panel.ts index b2dbf4b19..d2cda0831 100644 --- a/src/ui/layer_list_panel.ts +++ b/src/ui/layer_list_panel.ts @@ -119,40 +119,48 @@ class LayerColorWidget extends RefCounted { "neuroglancer-layer-list-panel-color-value-wrapper"; elementWrapper.appendChild(element); - if (this.layer.supportsLayerBarColorSyncOption) { - const updateLayerColorWidget = () => { - const color = this.layer.layerBarColor; - if (color) { - element.style.backgroundColor = color; - element.classList.remove("rainbow"); + this.registerDisposer( + observeWatchable((layerColorEnabled) => { + elementWrapper.style.display = layerColorEnabled ? "block" : "none"; + if (this.layer.supportsLayerBarColorSyncOption) { + elementWrapper.classList.remove("unsupported"); + element.classList.remove("unsupported"); } else { - element.classList.add("rainbow"); + elementWrapper.classList.add("unsupported"); + element.classList.add("unsupported"); } - }; - this.registerDisposer( - layer.observeLayerColor(() => { - updateLayerColorWidget(); - }), - ); - panel.sidePanelManager.viewerState.enableLayerColorWidget; - this.registerDisposer( - observeWatchable((layerColorEnabled) => { - elementWrapper.style.display = layerColorEnabled ? "block" : "none"; - }, panel.sidePanelManager.viewerState.enableLayerColorWidget), - ); - this.registerDisposer( - layer.layerChanged.add(() => { - if (!this.layer.visible) { - elementWrapper.classList.add("cross"); - } else { - elementWrapper.classList.remove("cross"); - } - }), - ); - } else { - // If the layer do not support color sync (no color to deal with) + }, panel.sidePanelManager.viewerState.enableLayerColorWidget), + ); + if (!this.layer.supportsLayerBarColorSyncOption) { + elementWrapper.classList.add("unsupported"); element.classList.add("unsupported"); } + const updateLayerColorWidget = () => { + const color = this.layer.layerBarColor; + if (color) { + element.style.backgroundColor = color; + element.classList.remove("rainbow"); + } else { + const style = this.layer.supportsLayerBarColorSyncOption + ? "rainbow" + : "unsupported"; + element.classList.add(style); + } + }; + this.registerDisposer( + layer.observeLayerColor(() => { + updateLayerColorWidget(); + }), + ); + this.registerDisposer( + layer.layerChanged.add(() => { + if (!this.layer.visible) { + elementWrapper.classList.add("cross"); + } else { + elementWrapper.classList.remove("cross"); + } + }), + ); } } From b413a56d15dbe084e6a408e32639cae170227aed Mon Sep 17 00:00:00 2001 From: Aiga115 Date: Fri, 20 Dec 2024 15:30:49 +0100 Subject: [PATCH 23/36] CC-189 make changes to ui of non color state circle --- src/ui/layer_bar.css | 23 ++++++++++++++++++----- src/ui/layer_list_panel.css | 9 ++++----- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/ui/layer_bar.css b/src/ui/layer_bar.css index 8c68a011a..f5a348ff0 100644 --- a/src/ui/layer_bar.css +++ b/src/ui/layer_bar.css @@ -134,6 +134,12 @@ align-items: center; } +.neuroglancer-layer-color-value { + border-radius: 50%; + height: 10px; + width: 10px; +} + .neuroglancer-layer-item[data-color="fixed"] .neuroglancer-layer-color-value { border-radius: 50%; height: 10px; @@ -170,23 +176,30 @@ transform: scale(1.35); } +/* linear-gradient( + 45deg, + rgba(200, 200, 200, 0.3) 25%, + transparent 25% + ), + linear-gradient(-45deg, rgba(200, 200, 200, 0.3) 25%, transparent 25%), + linear-gradient(45deg, transparent 75%, rgba(200, 200, 200, 0.3) 75%), + linear-gradient(-45deg, transparent 75%, rgba(200, 200, 200, 0.3) 75%); */ .neuroglancer-layer-item[data-color="unsupported"] .neuroglancer-layer-color-value { background-image: linear-gradient( 45deg, - rgba(128, 128, 128, 0.5) 25%, + rgba(128, 128, 128, 1.5) 25%, transparent 25% ), - linear-gradient(-45deg, rgba(128, 128, 128, 0.5) 25%, transparent 25%), - linear-gradient(45deg, transparent 75%, rgba(128, 128, 128, 0.5) 75%), - linear-gradient(-45deg, transparent 75%, rgba(128, 128, 128, 0.5) 75%); + linear-gradient(-45deg, rgba(128, 128, 128, 1.5) 25%, transparent 25%), + linear-gradient(45deg, transparent 75%, rgba(128, 128, 128, 1.5) 75%), + linear-gradient(-45deg, transparent 75%, rgba(128, 128, 128, 1.5) 75%); background-size: 4px 4px; background-position: 0 0, 0 2px, 2px -2px, -2px 0px; - padding: 6px; } .neuroglancer-layer-color-value-wrapper { diff --git a/src/ui/layer_list_panel.css b/src/ui/layer_list_panel.css index 6506e5514..c150dce88 100644 --- a/src/ui/layer_list_panel.css +++ b/src/ui/layer_list_panel.css @@ -91,19 +91,18 @@ .neuroglancer-layer-list-panel-color-value.unsupported { background-image: linear-gradient( 45deg, - rgba(128, 128, 128, 0.5) 25%, + rgba(128, 128, 128, 1.5) 25%, transparent 25% ), - linear-gradient(-45deg, rgba(128, 128, 128, 0.5) 25%, transparent 25%), - linear-gradient(45deg, transparent 75%, rgba(128, 128, 128, 0.5) 75%), - linear-gradient(-45deg, transparent 75%, rgba(128, 128, 128, 0.5) 75%); + linear-gradient(-45deg, rgba(128, 128, 128, 1.5) 25%, transparent 25%), + linear-gradient(45deg, transparent 75%, rgba(128, 128, 128, 1.5) 75%), + linear-gradient(-45deg, transparent 75%, rgba(128, 128, 128, 1.5) 75%); background-size: 4px 4px; background-position: 0 0, 0 2px, 2px -2px, -2px 0px; - padding: 6px; } .neuroglancer-layer-list-panel-color-value-wrapper.unsupported::before { From 5e2208346e00244dd35362f8f2e91738b9d023d1 Mon Sep 17 00:00:00 2001 From: aranega Date: Fri, 20 Dec 2024 08:34:50 -0600 Subject: [PATCH 24/36] Display a rainbow when the default annotation color is not used in the shader --- src/layer/annotation/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/layer/annotation/index.ts b/src/layer/annotation/index.ts index 5f10663c7..66c842e15 100644 --- a/src/layer/annotation/index.ts +++ b/src/layer/annotation/index.ts @@ -729,7 +729,9 @@ export class AnnotationUserLayer extends Base { } get automaticLayerBarColor() { - if (this.annotationDisplayState.color) { + const shaderHasDefaultColor = + this.annotationDisplayState.shader.value.includes("defaultColor"); + if (shaderHasDefaultColor && this.annotationDisplayState.color.value) { const [r, g, b] = this.annotationDisplayState.color.value; return `rgb(${r * 255}, ${g * 255}, ${b * 255})`; } From cd24b91c7bd96732f236229b5eeac580f795a1db Mon Sep 17 00:00:00 2001 From: Aiga115 Date: Fri, 20 Dec 2024 15:37:53 +0100 Subject: [PATCH 25/36] CC-189 remove comment --- src/ui/layer_bar.css | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/ui/layer_bar.css b/src/ui/layer_bar.css index f5a348ff0..5b487649f 100644 --- a/src/ui/layer_bar.css +++ b/src/ui/layer_bar.css @@ -176,14 +176,6 @@ transform: scale(1.35); } -/* linear-gradient( - 45deg, - rgba(200, 200, 200, 0.3) 25%, - transparent 25% - ), - linear-gradient(-45deg, rgba(200, 200, 200, 0.3) 25%, transparent 25%), - linear-gradient(45deg, transparent 75%, rgba(200, 200, 200, 0.3) 75%), - linear-gradient(-45deg, transparent 75%, rgba(200, 200, 200, 0.3) 75%); */ .neuroglancer-layer-item[data-color="unsupported"] .neuroglancer-layer-color-value { background-image: linear-gradient( From c9f8ecfba57c0f3d159e0a5d626b33cd5d825761 Mon Sep 17 00:00:00 2001 From: aranega Date: Fri, 20 Dec 2024 08:38:51 -0600 Subject: [PATCH 26/36] Change tooltip message for annotation with rainbow --- src/layer/annotation/index.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/layer/annotation/index.ts b/src/layer/annotation/index.ts index 66c842e15..3949268a4 100644 --- a/src/layer/annotation/index.ts +++ b/src/layer/annotation/index.ts @@ -740,11 +740,13 @@ export class AnnotationUserLayer extends Base { } colorWidgetTooltip(): string | undefined { - if (this.annotationDisplayState.color) { + const shaderHasDefaultColor = + this.annotationDisplayState.shader.value.includes("defaultColor"); + if (shaderHasDefaultColor && this.annotationDisplayState.color.value) { return `The color comes from the selected shader default color`; } - return undefined; + return "Your shader code doesn't use the default color, we cannot easily determine which color you are using"; } static type = "annotation"; From 7ad72ff30117d4e48595dde73763014d879408da Mon Sep 17 00:00:00 2001 From: aranega Date: Fri, 20 Dec 2024 08:42:39 -0600 Subject: [PATCH 27/36] Fix layer widget color not updating for annotation --- src/layer/annotation/index.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/layer/annotation/index.ts b/src/layer/annotation/index.ts index 3949268a4..aa1afc8f5 100644 --- a/src/layer/annotation/index.ts +++ b/src/layer/annotation/index.ts @@ -722,9 +722,14 @@ export class AnnotationUserLayer extends Base { callback, this.annotationDisplayState.color, ); + const shaderDisposer = observeWatchable( + callback, + this.annotationDisplayState.shader, + ); return () => { disposer(); subDisposer(); + shaderDisposer(); }; } From e5fe020a82de6717051e241e868cbeb5b316e4fb Mon Sep 17 00:00:00 2001 From: Aiga115 Date: Fri, 20 Dec 2024 18:50:48 +0100 Subject: [PATCH 28/36] CC-200 initial changes made --- src/ui/layer_list_panel.css | 8 +++++++- src/ui/layer_list_panel.ts | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/ui/layer_list_panel.css b/src/ui/layer_list_panel.css index c150dce88..fec701250 100644 --- a/src/ui/layer_list_panel.css +++ b/src/ui/layer_list_panel.css @@ -14,6 +14,7 @@ padding: 2px; border: 1px solid #aaa; margin: 2px; + gap: 4px; } .neuroglancer-layer-list-panel-item[data-selected="true"] { @@ -45,12 +46,17 @@ display: inline-block; } +.neuroglancer-layer-list-panel-item input[type="checkbox"] { + width: '1rem'; + height: '1rem'; + margin: '0.25rem'; +} + .neuroglancer-layer-list-panel-color-value-wrapper { position: relative; width: 10px; height: 10px; padding: 5px; - margin: 0 4px; } .neuroglancer-layer-list-panel-color-value { diff --git a/src/ui/layer_list_panel.ts b/src/ui/layer_list_panel.ts index 6f7f3921e..5a029be07 100644 --- a/src/ui/layer_list_panel.ts +++ b/src/ui/layer_list_panel.ts @@ -92,6 +92,8 @@ export class LayerVisibilityWidget extends RefCounted { this.layer.setVisible(true); }, }); + element.style.display = "flex"; + element.style.alignItems = "center"; element.appendChild(showIcon); element.appendChild(hideIcon); const updateView = () => { From c42c86d24fbd5b8b4fd159221bc562f330c8dcac Mon Sep 17 00:00:00 2001 From: Sean Martin Date: Wed, 8 Jan 2025 11:25:51 +0100 Subject: [PATCH 29/36] chore: formatting pass --- src/ui/layer_list_panel.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ui/layer_list_panel.css b/src/ui/layer_list_panel.css index fec701250..5ffce5c41 100644 --- a/src/ui/layer_list_panel.css +++ b/src/ui/layer_list_panel.css @@ -47,9 +47,9 @@ } .neuroglancer-layer-list-panel-item input[type="checkbox"] { - width: '1rem'; - height: '1rem'; - margin: '0.25rem'; + width: "1rem"; + height: "1rem"; + margin: "0.25rem"; } .neuroglancer-layer-list-panel-color-value-wrapper { From b9ed80de5922dfca8307de73557b120d3f40e3d7 Mon Sep 17 00:00:00 2001 From: Sean Martin Date: Wed, 8 Jan 2025 11:26:13 +0100 Subject: [PATCH 30/36] feat: clarify tooltip for non-default color annotations --- src/layer/annotation/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layer/annotation/index.ts b/src/layer/annotation/index.ts index efc63bfb0..933015f0c 100644 --- a/src/layer/annotation/index.ts +++ b/src/layer/annotation/index.ts @@ -751,7 +751,7 @@ export class AnnotationUserLayer extends Base { return `The color comes from the selected shader default color`; } - return "Your shader code doesn't use the default color, we cannot easily determine which color you are using"; + return "Your shader code doesn't use the default color, we cannot determine which color you are using"; } static type = "annotation"; From 1857d191a5cba5672fe7be28d1348a69a6aa8108 Mon Sep 17 00:00:00 2001 From: aranega Date: Wed, 8 Jan 2025 06:32:20 -0600 Subject: [PATCH 31/36] CC-127 Remove grid pattern when the layer type changes --- src/ui/layer_bar.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ui/layer_bar.ts b/src/ui/layer_bar.ts index e1c4fee13..4db6ee781 100644 --- a/src/ui/layer_bar.ts +++ b/src/ui/layer_bar.ts @@ -201,6 +201,7 @@ class LayerWidget extends RefCounted { element.dataset.color = "rainbow"; } } else { + layerColorElement.style.backgroundColor = ""; element.dataset.color = "unsupported"; } } From 4fbb7b229b6cb8e01c4e875c9438690850f32ab0 Mon Sep 17 00:00:00 2001 From: aranega Date: Wed, 8 Jan 2025 06:32:43 -0600 Subject: [PATCH 32/36] CC-127 Update color widget in the layer list when layer type changes --- src/ui/layer_list_panel.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ui/layer_list_panel.ts b/src/ui/layer_list_panel.ts index 5a029be07..ab56c26a9 100644 --- a/src/ui/layer_list_panel.ts +++ b/src/ui/layer_list_panel.ts @@ -138,6 +138,8 @@ class LayerColorWidget extends RefCounted { element.classList.add("unsupported"); } const updateLayerColorWidget = () => { + element.classList.remove("rainbow"); + element.classList.remove("unsupported"); const color = this.layer.layerBarColor; if (color) { element.style.backgroundColor = color; @@ -147,6 +149,7 @@ class LayerColorWidget extends RefCounted { ? "rainbow" : "unsupported"; element.classList.add(style); + element.style.backgroundColor = ""; } }; this.registerDisposer( @@ -161,6 +164,7 @@ class LayerColorWidget extends RefCounted { } else { elementWrapper.classList.remove("cross"); } + updateLayerColorWidget(); }), ); } From 66e6d390e026c6063a47e7a532717f1baea31afb Mon Sep 17 00:00:00 2001 From: aranega Date: Wed, 8 Jan 2025 06:33:25 -0600 Subject: [PATCH 33/36] CC-127 Add tooltip when the layer doesn't support color widget --- src/ui/layer_bar.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ui/layer_bar.ts b/src/ui/layer_bar.ts index 4db6ee781..3b52fb6dc 100644 --- a/src/ui/layer_bar.ts +++ b/src/ui/layer_bar.ts @@ -205,7 +205,9 @@ class LayerWidget extends RefCounted { element.dataset.color = "unsupported"; } } - layerColorElement.title = layer.colorWidgetTooltip() || ""; + layerColorElement.title = + layer.colorWidgetTooltip() || + "The color of this layer cannot be determined"; } disposed() { From 66405e1ae897a33ab9bf19f63dcdaf2f2d7405d8 Mon Sep 17 00:00:00 2001 From: aranega Date: Wed, 8 Jan 2025 06:46:05 -0600 Subject: [PATCH 34/36] CC-127 Remove redundant code for color widget from then layer list --- src/ui/layer_list_panel.ts | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/src/ui/layer_list_panel.ts b/src/ui/layer_list_panel.ts index ab56c26a9..c1b3bd1d4 100644 --- a/src/ui/layer_list_panel.ts +++ b/src/ui/layer_list_panel.ts @@ -120,23 +120,6 @@ class LayerColorWidget extends RefCounted { elementWrapper.className = "neuroglancer-layer-list-panel-color-value-wrapper"; elementWrapper.appendChild(element); - - this.registerDisposer( - observeWatchable((layerColorEnabled) => { - elementWrapper.style.display = layerColorEnabled ? "block" : "none"; - if (this.layer.supportsLayerBarColorSyncOption) { - elementWrapper.classList.remove("unsupported"); - element.classList.remove("unsupported"); - } else { - elementWrapper.classList.add("unsupported"); - element.classList.add("unsupported"); - } - }, panel.sidePanelManager.viewerState.enableLayerColorWidget), - ); - if (!this.layer.supportsLayerBarColorSyncOption) { - elementWrapper.classList.add("unsupported"); - element.classList.add("unsupported"); - } const updateLayerColorWidget = () => { element.classList.remove("rainbow"); element.classList.remove("unsupported"); @@ -152,6 +135,11 @@ class LayerColorWidget extends RefCounted { element.style.backgroundColor = ""; } }; + this.registerDisposer( + observeWatchable((layerColorEnabled) => { + elementWrapper.style.display = layerColorEnabled ? "block" : "none"; + }, panel.sidePanelManager.viewerState.enableLayerColorWidget), + ); this.registerDisposer( layer.observeLayerColor(() => { updateLayerColorWidget(); From 4520ef256f8269da877ab2b071bd2b8cb4cee096 Mon Sep 17 00:00:00 2001 From: aranega Date: Wed, 8 Jan 2025 08:22:11 -0600 Subject: [PATCH 35/36] CC-127 Remove user-defined color for layers --- src/layer/index.ts | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/layer/index.ts b/src/layer/index.ts index ec692977a..bbe440e8f 100644 --- a/src/layer/index.ts +++ b/src/layer/index.ts @@ -70,11 +70,7 @@ import type { WatchableSet, WatchableValueInterface, } from "#src/trackable_value.js"; -import { - observeWatchable, - registerNested, - WatchableValue, -} from "#src/trackable_value.js"; +import { registerNested, WatchableValue } from "#src/trackable_value.js"; import { SELECTED_LAYER_SIDE_PANEL_DEFAULT_LOCATION, UserLayerSidePanelsState, @@ -86,7 +82,6 @@ import { import type { GlobalToolBinder } from "#src/ui/tool.js"; import { LayerToolBinder, SelectedLegacyTool } from "#src/ui/tool.js"; import { gatherUpdate } from "#src/util/array.js"; -import { TrackableOptionalRGB } from "#src/util/color.js"; import type { Borrowed, Owned } from "#src/util/disposable.js"; import { invokeDisposers, RefCounted } from "#src/util/disposable.js"; import { @@ -198,10 +193,8 @@ export class UserLayer extends RefCounted { messages = new MessageList(); - layerBarUserDefinedColor = new TrackableOptionalRGB(); - - observeLayerColor(callback: () => void): () => void { - return observeWatchable(callback, this.layerBarUserDefinedColor); + observeLayerColor(_: () => void): () => void { + return () => {}; } get automaticLayerBarColor(): string | undefined { @@ -209,9 +202,6 @@ export class UserLayer extends RefCounted { } get layerBarColor(): string | undefined { - if (this.layerBarUserDefinedColor.value) { - return this.layerBarUserDefinedColor.toJSON(); - } return this.automaticLayerBarColor; } From 166ff6fe2a541317395ad0b429ef7f6a8bf96d09 Mon Sep 17 00:00:00 2001 From: aranega Date: Wed, 8 Jan 2025 12:22:06 -0600 Subject: [PATCH 36/36] CC-127 Change setting label from color widget to color legend --- src/ui/viewer_settings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/viewer_settings.ts b/src/ui/viewer_settings.ts index 193c90050..155148352 100644 --- a/src/ui/viewer_settings.ts +++ b/src/ui/viewer_settings.ts @@ -129,7 +129,7 @@ export class ViewerSettingsPanel extends SidePanel { "Enable adaptive downsampling", viewer.enableAdaptiveDownsampling, ); - addCheckbox("Enable layer color widget", viewer.enableLayerColorWidget); + addCheckbox("Enable layer color legend", viewer.enableLayerColorWidget); const addColor = (label: string, value: WatchableValueInterface) => { const labelElement = document.createElement("label");