diff --git a/src/composables/annotationTool.ts b/src/composables/annotationTool.ts index 465f9d629..884513578 100644 --- a/src/composables/annotationTool.ts +++ b/src/composables/annotationTool.ts @@ -1,4 +1,5 @@ import { Ref, computed, ref, watch } from 'vue'; +import { useDebounceFn } from '@vueuse/core'; import { Vector2 } from '@kitware/vtk.js/types'; import { useCurrentImage } from '@/src/composables/useCurrentImage'; import { frameOfReferenceToImageSliceAndAxis } from '@/src/utils/frameOfReference'; @@ -9,6 +10,8 @@ import { AnnotationTool, ContextMenuEvent } from '../types/annotation-tool'; import { AnnotationToolStore } from '../store/tools/useAnnotationTool'; import { getCSSCoordinatesFromEvent } from '../utils/vtk-helpers'; +const SHOW_OVERLAY_DELAY = 500; // milliseconds + // does the tools's frame of reference match // the view's axis const useDoesToolFrameMatchViewAxis = < @@ -107,6 +110,8 @@ export type OverlayInfo = displayXY: Vector2; }; +// Maintains list of tools' hover states. +// If one tool hovered, overlayInfo.visible === true with toolID and displayXY. export const useHover = ( tools: Ref>>, currentSlice: Ref @@ -114,16 +119,6 @@ export const useHover = ( type Info = OverlayInfo; const toolHoverState = ref({}) as Ref>; - const overlayInfo = computed(() => { - const visibleToolID = Object.keys(toolHoverState.value).find( - (toolID) => toolHoverState.value[toolID as ToolID].visible - ) as ToolID | undefined; - - return visibleToolID - ? toolHoverState.value[visibleToolID] - : ({ visible: false } as Info); - }); - const toolsOnCurrentSlice = computed(() => tools.value.filter((tool) => tool.slice === currentSlice.value) ); @@ -154,5 +149,37 @@ export const useHover = ( visible: false, }; }; + + // If hovering true, debounce showing overlay. + // Immediately hide overlay if hovering false. + const synchronousOverlayInfo = computed(() => { + const visibleToolID = Object.keys(toolHoverState.value).find( + (toolID) => toolHoverState.value[toolID as ToolID].visible + ) as ToolID | undefined; + + return visibleToolID + ? toolHoverState.value[visibleToolID] + : ({ visible: false } as Info); + }); + + // Debounced output + const overlayInfo = ref(synchronousOverlayInfo.value) as Ref; + + const debouncedOverlayInfo = useDebounceFn((info: Info) => { + // if we moved off the tool (syncOverlay.visible === false), don't show overlay + if (synchronousOverlayInfo.value.visible) overlayInfo.value = info; + }, SHOW_OVERLAY_DELAY); + + watch(synchronousOverlayInfo, () => { + if (!synchronousOverlayInfo.value.visible) + overlayInfo.value = synchronousOverlayInfo.value; + else { + // Immediately set visible = false to hide overlay on mouse move, even if hovering true. + // Depends on widget sending hover events with every mouse move. + overlayInfo.value = { visible: false }; + debouncedOverlayInfo({ ...synchronousOverlayInfo.value }); + } + }); + return { overlayInfo, onHover }; };