From 390562e90eb695906d67ce57b4374f398b5c22d7 Mon Sep 17 00:00:00 2001 From: Laurent Chauvin Date: Fri, 21 Feb 2025 14:44:38 -0500 Subject: [PATCH 1/3] feat: keyboard shortcut to delete current image and clear scene --- docs/mouse_controls.md | 6 ++++-- src/composables/actions.ts | 17 +++++++++++++++++ src/config.ts | 3 +++ src/constants.ts | 8 ++++++++ src/store/datasets.ts | 16 ++++++++++++++++ 5 files changed, 48 insertions(+), 2 deletions(-) diff --git a/docs/mouse_controls.md b/docs/mouse_controls.md index b2fe86ac3..675d88875 100644 --- a/docs/mouse_controls.md +++ b/docs/mouse_controls.md @@ -2,8 +2,10 @@ ## Data management -| Shortcut | Action | -| -------- | ------ | +| Shortcut | Action | +| -------- | --------------------- | +| Ctrl + . | Delete Current Image | +| Ctrl + / | Clear all data | ## Slice diff --git a/src/composables/actions.ts b/src/composables/actions.ts index faa91e250..a33194c62 100644 --- a/src/composables/actions.ts +++ b/src/composables/actions.ts @@ -8,6 +8,7 @@ import { Action } from '../constants'; import { useKeyboardShortcutsStore } from '../store/keyboard-shortcuts'; import { useCurrentImage } from './useCurrentImage'; import { useSliceConfig } from './useSliceConfig'; +import { useDatasetStore } from '../store/datasets'; const applyLabelOffset = (offset: number) => () => { const toolToStore = { @@ -47,6 +48,19 @@ const changeSlice = (offset: number) => () => { currentSlice.value += offset; }; +const clearScene = () => () => { + const datasetStore = useDatasetStore(); + datasetStore.removeAll(); +}; + +const deleteCurrentImage = () => () => { + const datasetStore = useDatasetStore(); + datasetStore.remove(datasetStore.primaryImageID!); + + // Automatically select next image + datasetStore.setPrimarySelection(datasetStore.idsAsSelections[0]); +}; + export const ACTION_TO_FUNC = { windowLevel: setTool(Tools.WindowLevel), pan: setTool(Tools.Pan), @@ -65,6 +79,9 @@ export const ACTION_TO_FUNC = { decrementLabel: applyLabelOffset(-1), incrementLabel: applyLabelOffset(1), + deleteCurrentImage: deleteCurrentImage(), + clearScene: clearScene(), + mergeNewPolygon: () => {}, // acts as a modifier key rather than immediate effect, so no-op showKeyboardShortcuts, diff --git a/src/config.ts b/src/config.ts index 122357101..937db97fc 100644 --- a/src/config.ts +++ b/src/config.ts @@ -287,6 +287,9 @@ export const ACTION_TO_KEY = { decrementLabel: 'q', incrementLabel: 'w', + deleteCurrentImage: 'ctrl+.', + clearScene: 'ctrl+/', + showKeyboardShortcuts: '?', } satisfies Record; diff --git a/src/constants.ts b/src/constants.ts index fd4f54d57..1edc847df 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -67,6 +67,14 @@ export const ACTIONS = { readable: 'Activate next Label', }, + deleteCurrentImage: { + readable: 'Remove current active image', + }, + + clearScene: { + readable: 'Clear scene', + }, + mergeNewPolygon: { readable: 'Hold to merge new polygons with overlapping polygons', }, diff --git a/src/store/datasets.ts b/src/store/datasets.ts index e8f7e387f..8a91dd949 100644 --- a/src/store/datasets.ts +++ b/src/store/datasets.ts @@ -12,6 +12,7 @@ import { useFileStore } from './datasets-files'; import { StateFile } from '../io/state-file/schema'; import { useErrorMessage } from '../composables/useErrorMessage'; import { useLayersStore } from './datasets-layers'; +import { useModelStore } from './datasets-models'; export const DataType = { Image: 'Image', @@ -23,6 +24,7 @@ export const useDatasetStore = defineStore('dataset', () => { const dicomStore = useDICOMStore(); const fileStore = useFileStore(); const layersStore = useLayersStore(); + const modelStore = useModelStore(); // --- state --- // @@ -81,6 +83,19 @@ export const useDatasetStore = defineStore('dataset', () => { layersStore.remove(id); }; + const removeAll = () => { + // Create a copy to avoid iteration issue while removing data + const imageIdCopy = [...imageStore.idList]; + imageIdCopy.forEach((id) => { + remove(id); + }); + + const modelIdCopy = [...modelStore.idList]; + modelIdCopy.forEach((id) => { + remove(id); + }); + }; + return { primaryImageID, primarySelection, @@ -89,5 +104,6 @@ export const useDatasetStore = defineStore('dataset', () => { setPrimarySelection, serialize, remove, + removeAll, }; }); From 9e7cb1f1402b591c6c066cf6b833d2b396617b28 Mon Sep 17 00:00:00 2001 From: Laurent Chauvin Date: Fri, 28 Feb 2025 14:27:48 -0500 Subject: [PATCH 2/3] fix: remove function handles null ids --- src/composables/actions.ts | 2 +- src/store/datasets.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/composables/actions.ts b/src/composables/actions.ts index a33194c62..98a0b9ab7 100644 --- a/src/composables/actions.ts +++ b/src/composables/actions.ts @@ -55,7 +55,7 @@ const clearScene = () => () => { const deleteCurrentImage = () => () => { const datasetStore = useDatasetStore(); - datasetStore.remove(datasetStore.primaryImageID!); + datasetStore.remove(datasetStore.primaryImageID); // Automatically select next image datasetStore.setPrimarySelection(datasetStore.idsAsSelections[0]); diff --git a/src/store/datasets.ts b/src/store/datasets.ts index 8a91dd949..793d99a65 100644 --- a/src/store/datasets.ts +++ b/src/store/datasets.ts @@ -69,7 +69,9 @@ export const useDatasetStore = defineStore('dataset', () => { } } - const remove = (id: string) => { + const remove = (id: string | null) => { + if (!id) return; + if (id === primarySelection.value) { primarySelection.value = null; } From 66e16bcb49dbc9e22460cf5350bd3de6c90b379a Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Thu, 6 Mar 2025 13:50:14 -0500 Subject: [PATCH 3/3] fix(App): just VolView in doc title after delete image --- src/components/App.vue | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/App.vue b/src/components/App.vue index 42a0c89e9..3f5e6c6c9 100644 --- a/src/components/App.vue +++ b/src/components/App.vue @@ -123,13 +123,15 @@ export default defineComponent({ const { currentImageMetadata, isImageLoading } = useCurrentImage(); const defaultImageMetadataName = defaultImageMetadata().name; watch(currentImageMetadata, (newMetadata) => { + let prefix = ''; if ( newMetadata?.name && // wait until we get a real name, but if we never do, show default name (newMetadata.name !== defaultImageMetadataName || !isImageLoading) ) { - document.title = `${newMetadata.name} - VolView`; + prefix = `${newMetadata.name} -`; } + document.title = `${prefix}VolView`; }); // --- parse URL -- //