@@ -48,7 +48,12 @@ import { isWithinSelectionPanel } from "#src/ui/selection_details.js";
4848import type { Uint64Map } from "#src/uint64_map.js" ;
4949import { wrapSigned32BitIntegerToUint64 } from "#src/util/bigint.js" ;
5050import { setClipboard } from "#src/util/clipboard.js" ;
51- import { useWhiteBackground } from "#src/util/color.js" ;
51+ import {
52+ packColor ,
53+ parseRGBColorSpecification ,
54+ serializeColor ,
55+ useWhiteBackground ,
56+ } from "#src/util/color.js" ;
5257import { RefCounted } from "#src/util/disposable.js" ;
5358import { measureElementClone } from "#src/util/dom.js" ;
5459import type { vec3 } from "#src/util/geom.js" ;
@@ -61,6 +66,11 @@ import { makeEyeButton } from "#src/widget/eye_button.js";
6166import { makeFilterButton } from "#src/widget/filter_button.js" ;
6267import { makeStarButton } from "#src/widget/star_button.js" ;
6368
69+ declare const NEUROGLANCER_SEGMENT_LIST_COLOR_WIDGET : boolean | undefined ;
70+ const SEGMENT_LIST_COLOR_WIDGET_ENABLED =
71+ typeof NEUROGLANCER_SEGMENT_LIST_COLOR_WIDGET !== "undefined" &&
72+ NEUROGLANCER_SEGMENT_LIST_COLOR_WIDGET === true ;
73+
6474export class Uint64MapEntry {
6575 constructor (
6676 public key : bigint ,
@@ -293,6 +303,8 @@ export function bindSegmentListWidth(
293303const segmentWidgetTemplate = ( ( ) => {
294304 const template = document . createElement ( "div" ) ;
295305 template . classList . add ( "neuroglancer-segment-list-entry" ) ;
306+ template . title =
307+ "Right click to move to segment, alt+click to set color, alt+shift+click to unset color" ;
296308 const stickyContainer = document . createElement ( "div" ) ;
297309 stickyContainer . classList . add ( "neuroglancer-segment-list-entry-sticky" ) ;
298310 template . appendChild ( stickyContainer ) ;
@@ -337,6 +349,18 @@ const segmentWidgetTemplate = (() => {
337349 filterElement . classList . add ( "neuroglancer-segment-list-entry-filter" ) ;
338350 const filterIndex = template . childElementCount ;
339351 template . appendChild ( filterElement ) ;
352+ const colorInputIndex = template . childElementCount ;
353+ const colorInputElement = document . createElement ( "input" ) ;
354+ colorInputElement . type = "color" ;
355+ colorInputElement . classList . add (
356+ "neuroglancer-segment-list-entry-color-input" ,
357+ ) ;
358+ colorInputElement . classList . toggle (
359+ "hidden" ,
360+ ! SEGMENT_LIST_COLOR_WIDGET_ENABLED ,
361+ ) ;
362+ colorInputElement . title = "Set segment color (right click to unset)" ;
363+ template . appendChild ( colorInputElement ) ;
340364 return {
341365 template,
342366 copyContainerIndex,
@@ -347,6 +371,7 @@ const segmentWidgetTemplate = (() => {
347371 labelIndex,
348372 filterIndex,
349373 starIndex,
374+ colorInputIndex,
350375 unmappedIdIndex : - 1 ,
351376 unmappedCopyIndex : - 1 ,
352377 } ;
@@ -478,26 +503,65 @@ function makeRegisterSegmentWidgetEventHandlers(
478503 event . stopPropagation ( ) ;
479504 } ;
480505
481- const onMousedown = ( event : MouseEvent ) => {
506+ const onMousedown = ( event : MouseEvent , template : SegmentWidgetTemplate ) => {
507+ const entryElement = event . currentTarget as HTMLElement ;
508+ const idString = entryElement . dataset . id ! ;
509+ const id = BigInt ( idString ) ;
510+ if ( event . button === 0 && event . altKey ) {
511+ if ( event . shiftKey ) {
512+ displayState . segmentStatedColors . value . delete ( id ) ;
513+ } else {
514+ const colorInputElement = entryElement . children [
515+ template . colorInputIndex
516+ ] as HTMLInputElement ;
517+ const color = getBaseObjectColor ( displayState , id ) as vec3 ;
518+ setColorInputColor (
519+ colorInputElement ,
520+ color ,
521+ displayState . segmentStatedColors . value . has ( id ) ,
522+ ) ;
523+ colorInputElement . showPicker ( ) ;
524+ }
525+ }
482526 if (
483- event . button !== 2 ||
484- event . ctrlKey ||
485- event . altKey ||
486- event . metaKey ||
487- event . shiftKey
527+ event . button === 2 &&
528+ ! event . ctrlKey &&
529+ ! event . altKey &&
530+ ! event . metaKey &&
531+ ! event . shiftKey
488532 ) {
489- return ;
533+ displayState . moveToSegment ( id ) ;
490534 }
491- const entryElement = event . currentTarget as HTMLElement ;
535+ } ;
536+
537+ const colorHandler = ( event : Event ) => {
538+ const colorInputElement = event . currentTarget as HTMLInputElement ;
539+ const entryElement = getEntryElement ( event ) ;
492540 const idString = entryElement . dataset . id ! ;
493541 const id = BigInt ( idString ) ;
494- displayState . moveToSegment ( id ) ;
542+ const color = BigInt (
543+ packColor ( parseRGBColorSpecification ( colorInputElement . value ) ) ,
544+ ) ;
545+ displayState . segmentStatedColors . value . delete ( id ) ;
546+ displayState . segmentStatedColors . value . set ( id , color ) ;
547+ } ;
548+
549+ const onColorInputMousedown = ( event : MouseEvent ) => {
550+ if ( event . button === 2 ) {
551+ event . stopPropagation ( ) ;
552+ const entryElement = getEntryElement ( event ) ;
553+ const idString = entryElement . dataset . id ! ;
554+ const id = BigInt ( idString ) ;
555+ displayState . segmentStatedColors . value . delete ( id ) ;
556+ }
495557 } ;
496558
497559 return ( element : HTMLElement , template : SegmentWidgetTemplate ) => {
498560 const { children } = element ;
499561 const stickyChildren = children [ 0 ] . children ;
500- element . addEventListener ( "mousedown" , onMousedown ) ;
562+ element . addEventListener ( "mousedown" , ( event : MouseEvent ) =>
563+ onMousedown ( event , template ) ,
564+ ) ;
501565 const copyContainer = stickyChildren [
502566 template . copyContainerIndex
503567 ] as HTMLElement ;
@@ -528,6 +592,14 @@ function makeRegisterSegmentWidgetEventHandlers(
528592 const { selectedSegments } = displayState . segmentationGroupState . value ;
529593 selectedSegments . set ( id , ! selectedSegments . has ( id ) ) ;
530594 } ) ;
595+ const colorInputElement = children [
596+ template . colorInputIndex
597+ ] as HTMLInputElement ;
598+ colorInputElement . addEventListener ( "input" , colorHandler ) ;
599+ colorInputElement . addEventListener ( "change" , colorHandler ) ;
600+ if ( SEGMENT_LIST_COLOR_WIDGET_ENABLED ) {
601+ colorInputElement . addEventListener ( "mousedown" , onColorInputMousedown ) ;
602+ }
531603 } ;
532604}
533605
@@ -653,14 +725,24 @@ export class SegmentWidgetFactory<Template extends SegmentWidgetTemplate> {
653725 const idContainer = stickyChildren [
654726 template . idContainerIndex
655727 ] as HTMLElement ;
728+ let color = getBaseObjectColor ( this . displayState , mapped ) as vec3 ;
656729 setSegmentIdElementStyle (
657730 idContainer . children [ template . idIndex ] as HTMLElement ,
658- getBaseObjectColor ( this . displayState , mapped ) as vec3 ,
731+ color ,
732+ ! SEGMENT_LIST_COLOR_WIDGET_ENABLED &&
733+ ! ! this . displayState ?. segmentStatedColors . value . has ( mapped ) ,
659734 ) ;
735+ children [ template . colorInputIndex ] as HTMLInputElement ;
736+ if ( SEGMENT_LIST_COLOR_WIDGET_ENABLED ) {
737+ setColorInputColor (
738+ children [ template . colorInputIndex ] as HTMLInputElement ,
739+ color ,
740+ ! ! this . displayState ?. segmentStatedColors . value . has ( mapped ) ,
741+ ) ;
742+ }
660743 const { unmappedIdIndex } = template ;
661744 if ( unmappedIdIndex !== - 1 ) {
662745 let unmappedIdString : string | undefined ;
663- let color : vec3 ;
664746 if (
665747 displayState ! . baseSegmentColoring . value &&
666748 ( unmappedIdString = container . dataset . unmappedId ) !== undefined
@@ -678,9 +760,23 @@ export class SegmentWidgetFactory<Template extends SegmentWidgetTemplate> {
678760 }
679761}
680762
681- function setSegmentIdElementStyle ( element : HTMLElement , color : vec3 ) {
763+ function setSegmentIdElementStyle (
764+ element : HTMLElement ,
765+ color : vec3 ,
766+ stated = false ,
767+ ) {
682768 element . style . backgroundColor = getCssColor ( color ) ;
683769 element . style . color = useWhiteBackground ( color ) ? "white" : "black" ;
770+ element . classList . toggle ( "stated-color" , stated ) ;
771+ }
772+
773+ function setColorInputColor (
774+ element : HTMLInputElement ,
775+ color : vec3 ,
776+ stated : boolean ,
777+ ) {
778+ element . value = serializeColor ( color . subarray ( 0 , 3 ) as vec3 ) ;
779+ element . classList . toggle ( "stated-color" , stated ) ;
684780}
685781
686782export class SegmentWidgetWithExtraColumnsFactory extends SegmentWidgetFactory < SegmentWidgetWithExtraColumnsTemplate > {
@@ -867,6 +963,9 @@ export function registerCallbackWhenSegmentationDisplayStateChanged(
867963 displayState . baseSegmentColoring . changed . add ( callback ) ,
868964 ) ;
869965 context . registerDisposer ( displayState . hoverHighlight . changed . add ( callback ) ) ;
966+ context . registerDisposer (
967+ displayState . segmentStatedColors . changed . add ( callback ) ,
968+ ) ;
870969}
871970
872971export function registerRedrawWhenSegmentationDisplayStateChanged (
0 commit comments