diff --git a/js/obs_store/obs_store.js b/js/obs_store/obs_store.js index 1446925ac..3fa2ed909 100644 --- a/js/obs_store/obs_store.js +++ b/js/obs_store/obs_store.js @@ -35,6 +35,7 @@ export const create_obs_store = () => { viz_edit_layer: Observable(false), landscape_view: Observable('spatial'), umap_state: Observable(false), + focused_image_layer: Observable(null), // to do utilize for setProps deck_check: Observable({ background_layer: true, diff --git a/js/ui/text_buttons.js b/js/ui/text_buttons.js index 8650be5d4..21ff57952 100644 --- a/js/ui/text_buttons.js +++ b/js/ui/text_buttons.js @@ -22,6 +22,35 @@ const set_img_layer_visible = (visible) => { img_layer_visible = visible; }; +const handle_img_layer_button_double_click = (event, text, viz_state) => { + event.preventDefault(); + event.stopPropagation(); + + const normalized_text = text; + const focused_layer = viz_state.obs_store.focused_image_layer.get(); + + const ensure_image_layers_enabled = () => { + if (!viz_state.obs_store.viz_image_layers.get()) { + viz_state.obs_store.viz_image_layers.set(true); + } + + if (!viz_state.obs_store.viz_background_layer.get()) { + viz_state.obs_store.viz_background_layer.set(true); + } + + set_img_layer_visible(true); + }; + + if (focused_layer === normalized_text) { + viz_state.obs_store.focused_image_layer.set(null); + ensure_image_layers_enabled(); + return; + } + + viz_state.obs_store.focused_image_layer.set(normalized_text); + ensure_image_layers_enabled(); +}; + const toggle_visible_button = (event) => { const current = d3.select(event.currentTarget); @@ -335,6 +364,10 @@ const make_ist_img_layer_button_callback = ( viz_state ) => { return async (event) => { + if (viz_state.obs_store.focused_image_layer.get()) { + return; + } + const inUmap = viz_state.obs_store.umap_state.get(); if (!img_layer_visible && !inUmap) { @@ -439,6 +472,12 @@ export const make_button = ( ) .on('click', callback); + if (button_class === 'img_layer_button') { + inst_button.on('dblclick', (event) => + handle_img_layer_button_double_click(event, text, viz_state) + ); + } + const button_name = text.toLowerCase(); viz_state.buttons.buttons[button_name] = inst_button; }; diff --git a/js/ui/ui_containers.js b/js/ui/ui_containers.js index 49e02b60e..628b2038e 100644 --- a/js/ui/ui_containers.js +++ b/js/ui/ui_containers.js @@ -8,7 +8,7 @@ import { calc_and_update_rgn_bar_graph, sync_region_to_model, } from '../deck-gl/layers/edit_layer'; -import { toggle_visibility_image_layers } from '../deck-gl/layers/image_layers'; +import { toggle_visibility_single_image_layer } from '../deck-gl/layers/image_layers'; import { toggle_nbhd_layer_visibility } from '../deck-gl/layers/nbhd_layer'; import { update_path_pickable_state } from '../deck-gl/layers/path_layer'; import { update_trx_pickable_state } from '../deck-gl/layers/trx_layer'; @@ -504,25 +504,71 @@ export const make_ist_ui_container = ( make_img_layer_ctrl(viz_state.img, inst_image) ); - viz_state.obs_store.viz_image_layers.subscribe((viz_image_layers) => { - d3.select(viz_state.containers.image) - .selectAll('.img_layer_button') - .style('color', viz_image_layers ? 'blue' : 'gray'); + const sync_image_layer_controls = () => { + const vizImageLayers = viz_state.obs_store.viz_image_layers.get(); + const focusedLayer = viz_state.obs_store.focused_image_layer.get(); + const focusActive = Boolean(focusedLayer); + + const buttonSelection = d3 + .select(viz_state.containers.image) + .selectAll('.img_layer_button'); + + if (!focusActive) { + buttonSelection + .style('pointer-events', 'auto') + .style('cursor', 'pointer') + .style('opacity', 1) + .style('color', vizImageLayers ? 'blue' : 'gray'); + } - viz_state.img.image_layer_sliders.map((slider) => - toggle_slider(slider, viz_image_layers) - ); + viz_state.img.image_info.forEach((inst_image) => { + const inst_name = inst_image.button_name; + const slider = get_slider_by_name(viz_state.img, inst_name)[0]; + const button = viz_state.buttons.buttons[inst_name.toLowerCase()]; + const isFocused = focusActive && inst_name === focusedLayer; + const sliderEnabled = focusActive ? isFocused : vizImageLayers; + + toggle_slider(slider, sliderEnabled); + + if (focusActive && button) { + button + .style('pointer-events', isFocused ? 'auto' : 'none') + .style('cursor', isFocused ? 'pointer' : 'default') + .style('opacity', isFocused ? 1 : 0.5) + .style('color', isFocused ? 'blue' : 'gray'); + } - toggle_visibility_image_layers(layers_obj, viz_image_layers); + const shouldBeVisible = focusActive ? isFocused : vizImageLayers; + toggle_visibility_single_image_layer( + layers_obj, + inst_name, + shouldBeVisible + ); + }); refresh_layer(viz_state, layers_obj, 'image_layers'); + }; - // move out of umap state if image is visible - if (viz_image_layers && viz_state.obs_store.umap_state.get()) { + viz_state.obs_store.viz_image_layers.subscribe((vizImageLayers) => { + if (!vizImageLayers && viz_state.obs_store.focused_image_layer.get()) { + viz_state.obs_store.focused_image_layer.set(null); + } + + sync_image_layer_controls(); + + const has_visible_image = + vizImageLayers || + Boolean(viz_state.obs_store.focused_image_layer.get()); + + if (has_visible_image && viz_state.obs_store.umap_state.get()) { viz_state.obs_store.landscape_view.set('spatial'); } }); + viz_state.obs_store.focused_image_layer.subscribe(() => { + sync_image_layer_controls(); + }); + viz_state.obs_store.viz_background_layer.subscribe((visible) => { toggle_background_layer_visibility(layers_obj, visible); refresh_layer(viz_state, layers_obj, 'background_layer');