feat(DoubleClick): double click a viewport to one up and back (#3285)

* feat(DoubleClick): double click a viewport to one up and back

Added a toggleOneUp command that puts the active viewport into a 1x1 grid layout
and it toggles out of 'one-up' by restoring its saved 'toggleOneUpViewportGridStore'
from the StateSyncService.
Added double click customization for the Cornerstone extension with the
default double click handling being the toggleOneUp command.
Added a cypress test for the double click functionality.

* PR feedback:
- tracked viewport measurements no longer show as dashed when toggling one up
- disallowed double clicking near a measurement
- updated cornerstone3D dependencies to fix double click of TMTV and volume viewport 3D
- created ViewportGridService.getLayoutOptionsFromState

* Updated the ViewportGridService docs.

* Switched to using 'cornerstoneViewportClickCommands' and consistency with the context menu clicks.
This commit is contained in:
Joe Boccanfuso authored and GitHub committed 2023-03-29 15:39:51 -04:00
1 parent 4734b3bac6
commit d5ff590dfc
14 files changed
+402 -40

No files matched your search

+126 -6
View File
@@ -1,4 +1,4 @@
import { ServicesManager, Types } from '@ohif/core';
import { ServicesManager, utils } from '@ohif/core';
import {
ContextMenuController,
@@ -12,6 +12,8 @@ import findViewportsByPosition, {
import { ContextMenuProps } from './CustomizeableContextMenu/types';
const { subscribeToNextViewportGridChange } = utils;
export type HangingProtocolParams = {
protocolId?: string;
stageIndex?: number;
@@ -159,12 +161,14 @@ const commandsModule = ({
* @param options.protocolId - the protocol ID to change to
* @param options.stageId - the stageId to apply
* @param options.stageIndex - the index of the stage to go to.
* @param options.reset - flag to indicate if the HP should be reset to its original and not restored to a previous state
*/
setHangingProtocol: ({
activeStudyUID = '',
protocolId,
stageId,
stageIndex,
reset = false,
}: HangingProtocolParams): boolean => {
try {
// Stores in the state the reuseID to displaySetUID mapping
@@ -210,10 +214,11 @@ const commandsModule = ({
hangingProtocolService.setActiveStudyUID(activeStudyUID);
}
const storedHanging = `${hangingProtocolService.getState().activeStudyUID
const storedHanging = `${
hangingProtocolService.getState().activeStudyUID
}:${protocolId}:${useStageIdx || 0}`;
const restoreProtocol = !!viewportGridStore[storedHanging];
const restoreProtocol = !reset && viewportGridStore[storedHanging];
if (
protocolId === hpInfo.protocolId &&
@@ -273,8 +278,9 @@ const commandsModule = ({
activeStudy,
} = hangingProtocolService.getActiveProtocol();
const { toggleHangingProtocol } = stateSyncService.getState();
const storedHanging = `${activeStudy.StudyInstanceUID
}:${protocolId}:${stageIndex | 0}`;
const storedHanging = `${
activeStudy.StudyInstanceUID
}:${protocolId}:${stageIndex | 0}`;
if (
protocol.id === protocolId &&
(stageIndex === undefined || stageIndex === desiredStageIndex)
@@ -294,7 +300,11 @@ const commandsModule = ({
},
},
});
return actions.setHangingProtocol({ protocolId, stageIndex });
return actions.setHangingProtocol({
protocolId,
stageIndex,
reset: true,
});
}
},
@@ -365,6 +375,111 @@ const commandsModule = ({
window.setTimeout(completeLayout, 0);
},
toggleOneUp() {
const viewportGridState = viewportGridService.getState();
const { activeViewportIndex, viewports, layout } = viewportGridState;
const {
displaySetInstanceUIDs,
displaySetOptions,
viewportOptions,
} = viewports[activeViewportIndex];
if (layout.numCols === 1 && layout.numRows === 1) {
// The viewer is in one-up. Check if there is a state to restore/toggle back to.
const { toggleOneUpViewportGridStore } = stateSyncService.getState();
if (!toggleOneUpViewportGridStore.layout) {
return;
}
// There is a state to toggle back to. The viewport that was
// originally toggled to one up was the former active viewport.
const viewportIndexToUpdate =
toggleOneUpViewportGridStore.activeViewportIndex;
// Determine which viewports need to be updated. This is particularly
// important when MPR is toggled to one up and a different reconstructable
// is swapped in. Note that currently HangingProtocolService.getViewportsRequireUpdate
// does not support viewport with multiple display sets.
const updatedViewports =
displaySetInstanceUIDs.length > 1
? []
: displaySetInstanceUIDs
.map(displaySetInstanceUID =>
hangingProtocolService.getViewportsRequireUpdate(
viewportIndexToUpdate,
displaySetInstanceUID
)
)
.flat();
// This findOrCreateViewport returns either one of the updatedViewports
// returned from the HP service OR if there is not one from the HP service then
// simply returns what was in the previous state.
const findOrCreateViewport = (viewportIndex: number) => {
const viewport = updatedViewports.find(
viewport => viewport.viewportIndex === viewportIndex
);
return viewport
? { viewportOptions, displaySetOptions, ...viewport }
: toggleOneUpViewportGridStore.viewports[viewportIndex];
};
const layoutOptions = viewportGridService.getLayoutOptionsFromState(
toggleOneUpViewportGridStore
);
// Restore the previous layout including the active viewport.
viewportGridService.setLayout({
numRows: toggleOneUpViewportGridStore.layout.numRows,
numCols: toggleOneUpViewportGridStore.layout.numCols,
activeViewportIndex: viewportIndexToUpdate,
layoutOptions,
findOrCreateViewport,
});
} else {
// We are not in one-up, so toggle to one up.
// Store the current viewport grid state so we can toggle it back later.
stateSyncService.store({
toggleOneUpViewportGridStore: viewportGridState,
});
// This findOrCreateViewport only return one viewport - the active
// one being toggled to one up.
const findOrCreateViewport = () => {
return {
displaySetInstanceUIDs,
displaySetOptions,
viewportOptions,
};
};
// Set the layout to be 1x1/one-up.
viewportGridService.setLayout({
numRows: 1,
numCols: 1,
findOrCreateViewport,
});
// Subscribe to ANY (i.e. manual and hanging protocol) layout changes so that
// any grid layout state to toggle to from one up is cleared. This is performed on
// a timeout to avoid clearing the state for the actual to one up change.
// Whenever the next layout change event is fired, the subscriptions are unsubscribed.
const clearToggleOneUpViewportGridStore = () => {
const toggleOneUpViewportGridStore = {};
stateSyncService.store({
toggleOneUpViewportGridStore,
});
};
subscribeToNextViewportGridChange(
viewportGridService,
clearToggleOneUpViewportGridStore
);
}
},
openDICOMTagViewer() {
const { activeViewportIndex, viewports } = viewportGridService.getState();
const activeViewportSpecificData = viewports[activeViewportIndex];
@@ -440,6 +555,11 @@ const commandsModule = ({
storeContexts: [],
options: {},
},
toggleOneUp: {
commandFn: actions.toggleOneUp,
storeContexts: [],
options: {},
},
openDICOMTagViewer: {
commandFn: actions.openDICOMTagViewer,
},