feat(toolbar): new Toolbar to enable reactive state synchronization (#3983)
This commit is contained in:
1 parent
79d5c36bda
commit
566b25a544
155 files changed
+6311
-6577
No files matched your search
@@ -21,6 +21,12 @@ import toggleImageSliceSync from './utils/imageSliceSync/toggleImageSliceSync';
|
||||
import { getFirstAnnotationSelected } from './utils/measurementServiceMappings/utils/selection';
|
||||
import getActiveViewportEnabledElement from './utils/getActiveViewportEnabledElement';
|
||||
import { CornerstoneServices } from './types';
|
||||
import toggleVOISliceSync from './utils/toggleVOISliceSync';
|
||||
|
||||
const toggleSyncFunctions = {
|
||||
imageSlice: toggleImageSliceSync,
|
||||
voi: toggleVOISliceSync,
|
||||
};
|
||||
|
||||
function commandsModule({
|
||||
servicesManager,
|
||||
@@ -30,11 +36,11 @@ function commandsModule({
|
||||
viewportGridService,
|
||||
toolGroupService,
|
||||
cineService,
|
||||
toolbarService,
|
||||
uiDialogService,
|
||||
cornerstoneViewportService,
|
||||
uiNotificationService,
|
||||
measurementService,
|
||||
syncGroupService,
|
||||
} = servicesManager.services as CornerstoneServices;
|
||||
|
||||
const { measurementServiceSource } = this;
|
||||
@@ -227,30 +233,10 @@ function commandsModule({
|
||||
arrowTextCallback: ({ callback, data }) => {
|
||||
callInputDialog(uiDialogService, data, callback);
|
||||
},
|
||||
cleanUpCrosshairs: () => {
|
||||
// if the crosshairs tool is active, deactivate it and set window level active
|
||||
// since we are going back to main non-mpr HP
|
||||
const activeViewportToolGroup = toolGroupService.getToolGroup(null);
|
||||
|
||||
if (activeViewportToolGroup._toolInstances?.Crosshairs?.mode === Enums.ToolModes.Active) {
|
||||
actions.toolbarServiceRecordInteraction({
|
||||
interactionType: 'tool',
|
||||
commands: [
|
||||
{
|
||||
commandOptions: {
|
||||
toolName: 'WindowLevel',
|
||||
},
|
||||
context: 'CORNERSTONE',
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
},
|
||||
toggleCine: () => {
|
||||
const { viewports } = viewportGridService.getState();
|
||||
const { isCineEnabled } = cineService.getState();
|
||||
cineService.setIsCineEnabled(!isCineEnabled);
|
||||
toolbarService.setButton('Cine', { props: { isActive: !isCineEnabled } });
|
||||
viewports.forEach((_, index) => cineService.setCine({ id: index, isPlaying: false }));
|
||||
},
|
||||
setWindowLevel({ window, level, toolGroupId }) {
|
||||
@@ -279,35 +265,40 @@ function commandsModule({
|
||||
});
|
||||
viewport.render();
|
||||
},
|
||||
setToolEnabled: ({ toolName, toggle }) => {
|
||||
const { viewports } = viewportGridService.getState();
|
||||
|
||||
// Just call the toolbar service record interaction - allows
|
||||
// executing a toolbar command as a full toolbar command with side affects
|
||||
// coming from the ToolbarService itself.
|
||||
toolbarServiceRecordInteraction: props => {
|
||||
toolbarService.recordInteraction(props);
|
||||
},
|
||||
// Enable or disable a toggleable command, without calling the activation
|
||||
// Used to setup already active tools from hanging protocols
|
||||
setToolbarToggled: props => {
|
||||
toolbarService.setToggled(props.toolId, props.isActive ?? true);
|
||||
},
|
||||
setToolActive: ({ toolName, toolGroupId = null, toggledState }) => {
|
||||
if (toolName === 'Crosshairs') {
|
||||
const activeViewportToolGroup = toolGroupService.getToolGroup(null);
|
||||
|
||||
if (!activeViewportToolGroup._toolInstances.Crosshairs) {
|
||||
uiNotificationService.show({
|
||||
title: 'Crosshairs',
|
||||
message:
|
||||
'You need to be in a MPR view to use Crosshairs. Click on MPR button in the toolbar to activate it.',
|
||||
type: 'info',
|
||||
duration: 3000,
|
||||
});
|
||||
|
||||
throw new Error('Crosshairs tool is not available in this viewport');
|
||||
}
|
||||
if (!viewports.size) {
|
||||
return;
|
||||
}
|
||||
|
||||
const toolGroup = toolGroupService.getToolGroup(null);
|
||||
|
||||
if (!toolGroup) {
|
||||
return;
|
||||
}
|
||||
|
||||
const toolIsEnabled = toolGroup.getToolOptions(toolName).mode === Enums.ToolModes.Enabled;
|
||||
|
||||
// Toggle the tool's state only if the toggle is true
|
||||
if (toggle) {
|
||||
toolIsEnabled ? toolGroup.setToolDisabled(toolName) : toolGroup.setToolEnabled(toolName);
|
||||
}
|
||||
|
||||
const renderingEngine = cornerstoneViewportService.getRenderingEngine();
|
||||
renderingEngine.render();
|
||||
},
|
||||
setToolActiveToolbar: ({ value, itemId, toolGroupIds = [] }) => {
|
||||
// Sometimes it is passed as value (tools with options), sometimes as itemId (toolbar buttons)
|
||||
const toolName = itemId || value;
|
||||
|
||||
toolGroupIds = toolGroupIds.length ? toolGroupIds : toolGroupService.getToolGroupIds();
|
||||
|
||||
toolGroupIds.forEach(toolGroupId => {
|
||||
actions.setToolActive({ toolName, toolGroupId });
|
||||
});
|
||||
},
|
||||
setToolActive: ({ toolName, toolGroupId = null }) => {
|
||||
const { viewports } = viewportGridService.getState();
|
||||
|
||||
if (!viewports.size) {
|
||||
@@ -320,34 +311,13 @@ function commandsModule({
|
||||
return;
|
||||
}
|
||||
|
||||
if (!toolGroup.getToolInstance(toolName)) {
|
||||
uiNotificationService.show({
|
||||
title: `${toolName} tool`,
|
||||
message: `The ${toolName} tool is not available in this viewport.`,
|
||||
type: 'info',
|
||||
duration: 3000,
|
||||
});
|
||||
|
||||
throw new Error(`ToolGroup ${toolGroup.id} does not have this tool.`);
|
||||
}
|
||||
|
||||
const activeToolName = toolGroup.getActivePrimaryMouseButtonTool();
|
||||
|
||||
if (activeToolName) {
|
||||
// Todo: this is a hack to prevent the crosshairs to stick around
|
||||
// after another tool is selected. We should find a better way to do this
|
||||
if (activeToolName === 'Crosshairs') {
|
||||
toolGroup.setToolDisabled(activeToolName);
|
||||
} else {
|
||||
toolGroup.setToolPassive(activeToolName);
|
||||
}
|
||||
}
|
||||
|
||||
// If there is a toggle state, then simply set the enabled/disabled state without
|
||||
// setting the tool active.
|
||||
if (toggledState != null) {
|
||||
toggledState ? toolGroup.setToolEnabled(toolName) : toolGroup.setToolDisabled(toolName);
|
||||
return;
|
||||
const activeToolOptions = toolGroup.getToolConfiguration(activeToolName);
|
||||
activeToolOptions?.disableOnPassive
|
||||
? toolGroup.setToolDisabled(activeToolName)
|
||||
: toolGroup.setToolPassive(activeToolName);
|
||||
}
|
||||
|
||||
// Set the new toolName to be active
|
||||
@@ -565,33 +535,59 @@ function commandsModule({
|
||||
(currentIndex + direction + viewportIds.length) % viewportIds.length;
|
||||
viewportGridService.setActiveViewportId(viewportIds[nextViewportIndex] as string);
|
||||
},
|
||||
/**
|
||||
* If the syncId is given and a synchronizer with that ID already exists, it will
|
||||
* toggle it on/off for the provided viewports. If not, it will attempt to create
|
||||
* a new synchronizer using the given syncId and type for the specified viewports.
|
||||
* If no viewports are provided, you may notice some default behavior.
|
||||
* - 'voi' type, we will aim to synchronize all viewports with the same modality
|
||||
* -'imageSlice' type, we will aim to synchronize all viewports with the same orientation.
|
||||
*
|
||||
* @param options
|
||||
* @param options.viewports - The viewports to synchronize
|
||||
* @param options.syncId - The synchronization group ID
|
||||
* @param options.type - The type of synchronization to perform
|
||||
*/
|
||||
toggleSynchronizer: ({ type, viewports, syncId }) => {
|
||||
const synchronizer = syncGroupService.getSynchronizer(syncId);
|
||||
|
||||
toggleImageSliceSync: ({ toggledState }) => {
|
||||
toggleImageSliceSync({
|
||||
servicesManager,
|
||||
toggledState,
|
||||
});
|
||||
if (synchronizer) {
|
||||
synchronizer.isDisabled() ? synchronizer.setEnabled(true) : synchronizer.setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const fn = toggleSyncFunctions[type];
|
||||
|
||||
if (fn) {
|
||||
fn({
|
||||
servicesManager,
|
||||
viewports,
|
||||
syncId,
|
||||
});
|
||||
}
|
||||
},
|
||||
setSourceViewportForReferenceLinesTool: ({ toggledState, viewportId }) => {
|
||||
setSourceViewportForReferenceLinesTool: ({ viewportId }) => {
|
||||
if (!viewportId) {
|
||||
const { activeViewportId } = viewportGridService.getState();
|
||||
viewportId = activeViewportId;
|
||||
viewportId = activeViewportId ?? 'default';
|
||||
}
|
||||
|
||||
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
|
||||
|
||||
toolGroup.setToolConfiguration(
|
||||
toolGroup?.setToolConfiguration(
|
||||
ReferenceLinesTool.toolName,
|
||||
{
|
||||
sourceViewportId: viewportId,
|
||||
},
|
||||
true // overwrite
|
||||
);
|
||||
|
||||
const renderingEngine = cornerstoneViewportService.getRenderingEngine();
|
||||
renderingEngine.render();
|
||||
},
|
||||
storePresentation: ({ viewportId }) => {
|
||||
cornerstoneViewportService.storePresentation({ viewportId });
|
||||
},
|
||||
|
||||
attachProtocolViewportDataListener: ({ protocol, stageIndex }) => {
|
||||
const EVENT = cornerstoneViewportService.EVENTS.VIEWPORT_DATA_CHANGED;
|
||||
const command = protocol.callbacks.onViewportDataInitialized;
|
||||
@@ -608,6 +604,26 @@ function commandsModule({
|
||||
}
|
||||
});
|
||||
},
|
||||
resetCrosshairs: ({ viewportId }) => {
|
||||
const crosshairInstances = [];
|
||||
|
||||
const getCrosshairInstances = toolGroupId => {
|
||||
const toolGroup = toolGroupService.getToolGroup(toolGroupId);
|
||||
crosshairInstances.push(toolGroup.getToolInstance('Crosshairs'));
|
||||
};
|
||||
|
||||
if (!viewportId) {
|
||||
const toolGroupIds = toolGroupService.getToolGroupIds();
|
||||
toolGroupIds.forEach(getCrosshairInstances);
|
||||
} else {
|
||||
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
|
||||
getCrosshairInstances(toolGroup.id);
|
||||
}
|
||||
|
||||
crosshairInstances.forEach(ins => {
|
||||
ins.resetCrosshairs();
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
const definitions = {
|
||||
@@ -615,7 +631,6 @@ function commandsModule({
|
||||
// context menu
|
||||
showCornerstoneContextMenu: {
|
||||
commandFn: actions.showCornerstoneContextMenu,
|
||||
storeContexts: [],
|
||||
options: {
|
||||
menuCustomizationId: 'measurementsContextMenu',
|
||||
commands: [
|
||||
@@ -631,8 +646,6 @@ function commandsModule({
|
||||
},
|
||||
getNearbyAnnotation: {
|
||||
commandFn: actions.getNearbyAnnotation,
|
||||
storeContexts: [],
|
||||
options: {},
|
||||
},
|
||||
deleteMeasurement: {
|
||||
commandFn: actions.deleteMeasurement,
|
||||
@@ -643,16 +656,18 @@ function commandsModule({
|
||||
updateMeasurement: {
|
||||
commandFn: actions.updateMeasurement,
|
||||
},
|
||||
|
||||
setWindowLevel: {
|
||||
commandFn: actions.setWindowLevel,
|
||||
},
|
||||
toolbarServiceRecordInteraction: {
|
||||
commandFn: actions.toolbarServiceRecordInteraction,
|
||||
},
|
||||
setToolActive: {
|
||||
commandFn: actions.setToolActive,
|
||||
},
|
||||
setToolActiveToolbar: {
|
||||
commandFn: actions.setToolActiveToolbar,
|
||||
},
|
||||
setToolEnabled: {
|
||||
commandFn: actions.setToolEnabled,
|
||||
},
|
||||
rotateViewportCW: {
|
||||
commandFn: actions.rotateViewport,
|
||||
options: { rotation: 90 },
|
||||
@@ -735,15 +750,15 @@ function commandsModule({
|
||||
storePresentation: {
|
||||
commandFn: actions.storePresentation,
|
||||
},
|
||||
setToolbarToggled: {
|
||||
commandFn: actions.setToolbarToggled,
|
||||
},
|
||||
cleanUpCrosshairs: {
|
||||
commandFn: actions.cleanUpCrosshairs,
|
||||
},
|
||||
attachProtocolViewportDataListener: {
|
||||
commandFn: actions.attachProtocolViewportDataListener,
|
||||
},
|
||||
resetCrosshairs: {
|
||||
commandFn: actions.resetCrosshairs,
|
||||
},
|
||||
toggleSynchronizer: {
|
||||
commandFn: actions.toggleSynchronizer,
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
Reference in new issue
Block a user