diff --git a/extensions/cornerstone/src/commandsModule.ts b/extensions/cornerstone/src/commandsModule.ts index 9ca0dbb33..4c443c243 100644 --- a/extensions/cornerstone/src/commandsModule.ts +++ b/extensions/cornerstone/src/commandsModule.ts @@ -456,13 +456,8 @@ function commandsModule({ const { viewport } = enabledElement; - if (viewport instanceof StackViewport) { - viewport.resetProperties(); - viewport.resetCamera(); - } else { - viewport.resetProperties(); - viewport.resetCamera(); - } + viewport.resetProperties?.(); + viewport.resetCamera(); viewport.render(); }, diff --git a/extensions/default/src/Toolbar/Toolbar.tsx b/extensions/default/src/Toolbar/Toolbar.tsx index c714968a1..f3812ff5b 100644 --- a/extensions/default/src/Toolbar/Toolbar.tsx +++ b/extensions/default/src/Toolbar/Toolbar.tsx @@ -1,19 +1,34 @@ -import React, { useEffect, useState, useCallback } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import classnames from 'classnames'; +import { useViewportGrid } from '@ohif/ui'; -export default function Toolbar({ servicesManager }) { +export default function Toolbar({ + servicesManager, +}: Types.Extensions.ExtensionParams): React.ReactElement { const { toolbarService } = servicesManager.services; + + const [viewportGrid, viewportGridService] = useViewportGrid(); + const [toolbarButtons, setToolbarButtons] = useState([]); useEffect(() => { - const { unsubscribe } = toolbarService.subscribe(toolbarService.EVENTS.TOOL_BAR_MODIFIED, () => - setToolbarButtons(toolbarService.getButtonSection('primary')) + const updateToolbar = () => { + const toolGroupId = + viewportGridService.getActiveViewportOptionByKey('toolGroupId') ?? 'default'; + setToolbarButtons(toolbarService.getButtonSection(toolGroupId)); + }; + + const { unsubscribe } = toolbarService.subscribe( + toolbarService.EVENTS.TOOL_BAR_MODIFIED, + updateToolbar ); + updateToolbar(); + return () => { unsubscribe(); }; - }, [toolbarService]); + }, [toolbarService, viewportGrid]); const onInteraction = useCallback( args => toolbarService.recordInteraction(args), diff --git a/extensions/default/src/Toolbar/ToolbarButtonWithServices.tsx b/extensions/default/src/Toolbar/ToolbarButtonWithServices.tsx index 24dda712b..32b7fb51e 100644 --- a/extensions/default/src/Toolbar/ToolbarButtonWithServices.tsx +++ b/extensions/default/src/Toolbar/ToolbarButtonWithServices.tsx @@ -65,7 +65,7 @@ ToolbarButtonWithServices.propTypes = { state: PropTypes.shape({ primaryToolId: PropTypes.string, toggles: PropTypes.objectOf(PropTypes.bool), - groups: PropTypes.objectOf(PropTypes.object), + groups: PropTypes.objectOf(PropTypes.any), }).isRequired, }).isRequired, }).isRequired, diff --git a/modes/basic-test-mode/src/index.ts b/modes/basic-test-mode/src/index.ts index 3d51b06f1..1c8d5fc8b 100644 --- a/modes/basic-test-mode/src/index.ts +++ b/modes/basic-test-mode/src/index.ts @@ -2,11 +2,16 @@ import { hotkeys } from '@ohif/core'; import toolbarButtons from './toolbarButtons'; import { id } from './id'; import initToolGroups from './initToolGroups'; +import moreTools from './moreTools'; +import moreToolsMpr from './moreToolsMpr'; // Allow this mode by excluding non-imaging modalities such as SR, SEG // Also, SM is not a simple imaging modalities, so exclude it. const NON_IMAGE_MODALITIES = ['SM', 'ECG', 'SR', 'SEG']; +const DEFAULT_TOOL_GROUP_ID = 'default'; +const MPR_TOOL_GROUP_ID = 'mpr'; + const ohif = { layout: '@ohif/extension-default.layoutTemplateModule.viewerLayout', sopClassHandler: '@ohif/extension-default.sopClassHandlerModule.stack', @@ -77,21 +82,23 @@ function modeFactory() { ]); let unsubscribe; + toolbarService.setDefaultTool({ + groupId: 'WindowLevel', + itemId: 'WindowLevel', + interactionType: 'tool', + commands: [ + { + commandName: 'setToolActive', + commandOptions: { + toolName: 'WindowLevel', + }, + context: 'CORNERSTONE', + }, + ], + }); const activateTool = () => { - toolbarService.recordInteraction({ - groupId: 'WindowLevel', - interactionType: 'tool', - commands: [ - { - commandName: 'setToolActive', - commandOptions: { - toolName: 'WindowLevel', - }, - context: 'CORNERSTONE', - }, - ], - }); + toolbarService.recordInteraction(toolbarService.getDefaultTool()); // We don't need to reset the active tool whenever a viewport is getting // added to the toolGroup. @@ -106,8 +113,18 @@ function modeFactory() { )); toolbarService.init(extensionManager); - toolbarService.addButtons(toolbarButtons); - toolbarService.createButtonSection('primary', [ + toolbarService.addButtons([...toolbarButtons, ...moreTools, ...moreToolsMpr]); + toolbarService.createButtonSection(DEFAULT_TOOL_GROUP_ID, [ + 'MeasurementTools', + 'Zoom', + 'WindowLevel', + 'Pan', + 'Capture', + 'Layout', + 'MPR', + 'MoreTools', + ]); + toolbarService.createButtonSection(MPR_TOOL_GROUP_ID, [ 'MeasurementTools', 'Zoom', 'WindowLevel', @@ -116,7 +133,7 @@ function modeFactory() { 'Layout', 'MPR', 'Crosshairs', - 'MoreTools', + 'MoreToolsMpr', ]); }, onModeExit: ({ servicesManager }) => { diff --git a/modes/basic-test-mode/src/moreTools.ts b/modes/basic-test-mode/src/moreTools.ts new file mode 100644 index 000000000..1baa17118 --- /dev/null +++ b/modes/basic-test-mode/src/moreTools.ts @@ -0,0 +1,231 @@ +import type { RunCommand } from '@ohif/core/types'; +import { EVENTS } from '@cornerstonejs/core'; +import { ToolbarService } from '@ohif/core'; + +const ReferenceLinesCommands: RunCommand = [ + { + commandName: 'setSourceViewportForReferenceLinesTool', + context: 'CORNERSTONE', + }, + { + commandName: 'setToolActive', + commandOptions: { + toolName: 'ReferenceLines', + }, + context: 'CORNERSTONE', + }, +]; + +const moreTools = [ + { + id: 'MoreTools', + type: 'ohif.splitButton', + props: { + isRadio: true, // ? + groupId: 'MoreTools', + primary: ToolbarService._createActionButton( + 'Reset', + 'tool-reset', + 'Reset View', + [ + { + commandName: 'resetViewport', + }, + ], + 'Reset' + ), + secondary: { + icon: 'chevron-down', + label: '', + isActive: true, + tooltip: 'More Tools', + }, + items: [ + ToolbarService._createActionButton( + 'Reset', + 'tool-reset', + 'Reset View', + [ + { + commandName: 'resetViewport', + }, + ], + 'Reset' + ), + ToolbarService._createActionButton( + 'rotate-right', + 'tool-rotate-right', + 'Rotate Right', + [ + { + commandName: 'rotateViewportCW', + commandOptions: {}, + context: 'CORNERSTONE', + }, + ], + 'Rotate +90' + ), + ToolbarService._createActionButton( + 'flip-horizontal', + 'tool-flip-horizontal', + 'Flip Horizontally', + [ + { + commandName: 'flipViewportHorizontal', + commandOptions: {}, + context: 'CORNERSTONE', + }, + ], + 'Flip Horizontally' + ), + ToolbarService._createToggleButton( + 'StackImageSync', + 'link', + 'Stack Image Sync', + [ + { + commandName: 'toggleStackImageSync', + }, + ], + 'Enable position synchronization on stack viewports', + { + listeners: { + [EVENTS.STACK_VIEWPORT_NEW_STACK]: { + commandName: 'toggleStackImageSync', + commandOptions: { toggledState: true }, + }, + }, + } + ), + ToolbarService._createToggleButton( + 'ReferenceLines', + 'tool-referenceLines', // change this with the new icon + 'Reference Lines', + ReferenceLinesCommands, + 'Show Reference Lines', + { + listeners: { + [EVENTS.STACK_VIEWPORT_NEW_STACK]: ReferenceLinesCommands, + [EVENTS.ACTIVE_VIEWPORT_ID_CHANGED]: ReferenceLinesCommands, + }, + } + ), + ToolbarService._createToolButton( + 'StackScroll', + 'tool-stack-scroll', + 'Stack Scroll', + [ + { + commandName: 'setToolActive', + commandOptions: { + toolName: 'StackScroll', + }, + context: 'CORNERSTONE', + }, + ], + 'Stack Scroll' + ), + ToolbarService._createActionButton( + 'invert', + 'tool-invert', + 'Invert', + [ + { + commandName: 'invertViewport', + commandOptions: {}, + context: 'CORNERSTONE', + }, + ], + 'Invert Colors' + ), + ToolbarService._createToolButton( + 'Probe', + 'tool-probe', + 'Probe', + [ + { + commandName: 'setToolActive', + commandOptions: { + toolName: 'DragProbe', + }, + context: 'CORNERSTONE', + }, + ], + 'Probe' + ), + ToolbarService._createToggleButton( + 'cine', + 'tool-cine', + 'Cine', + [ + { + commandName: 'toggleCine', + context: 'CORNERSTONE', + }, + ], + 'Cine' + ), + ToolbarService._createToolButton( + 'Angle', + 'tool-angle', + 'Angle', + [ + { + commandName: 'setToolActive', + commandOptions: { + toolName: 'Angle', + }, + context: 'CORNERSTONE', + }, + ], + 'Angle' + ), + ToolbarService._createToolButton( + 'Magnify', + 'tool-magnify', + 'Magnify', + [ + { + commandName: 'setToolActive', + commandOptions: { + toolName: 'Magnify', + }, + context: 'CORNERSTONE', + }, + ], + 'Magnify' + ), + ToolbarService._createToolButton( + 'Rectangle', + 'tool-rectangle', + 'Rectangle', + [ + { + commandName: 'setToolActive', + commandOptions: { + toolName: 'RectangleROI', + }, + context: 'CORNERSTONE', + }, + ], + 'Rectangle' + ), + ToolbarService._createActionButton( + 'TagBrowser', + 'list-bullets', + 'Dicom Tag Browser', + [ + { + commandName: 'openDICOMTagViewer', + commandOptions: {}, + context: 'DEFAULT', + }, + ], + 'Dicom Tag Browser' + ), + ], + }, + }, +]; + +export default moreTools; diff --git a/modes/basic-test-mode/src/moreToolsMpr.ts b/modes/basic-test-mode/src/moreToolsMpr.ts new file mode 100644 index 000000000..0672170a3 --- /dev/null +++ b/modes/basic-test-mode/src/moreToolsMpr.ts @@ -0,0 +1,142 @@ +import { ToolbarService } from '@ohif/core'; + +const moreToolsMpr = [ + { + id: 'MoreToolsMpr', + type: 'ohif.splitButton', + props: { + isRadio: true, // ? + groupId: 'MoreTools', + primary: ToolbarService._createActionButton( + 'Reset', + 'tool-reset', + 'Reset View', + [ + { + commandName: 'resetViewport', + }, + ], + 'Reset' + ), + secondary: { + icon: 'chevron-down', + label: '', + isActive: true, + tooltip: 'More Tools', + }, + items: [ + ToolbarService._createActionButton( + 'Reset', + 'tool-reset', + 'Reset View', + [ + { + commandName: 'resetViewport', + }, + ], + 'Reset' + ), + ToolbarService._createToolButton( + 'StackScroll', + 'tool-stack-scroll', + 'Stack Scroll', + [ + { + commandName: 'setToolActive', + commandOptions: { + toolName: 'StackScroll', + }, + context: 'CORNERSTONE', + }, + ], + 'Stack Scroll' + ), + ToolbarService._createActionButton( + 'invert', + 'tool-invert', + 'Invert', + [ + { + commandName: 'invertViewport', + commandOptions: {}, + context: 'CORNERSTONE', + }, + ], + 'Invert Colors' + ), + ToolbarService._createToolButton( + 'Probe', + 'tool-probe', + 'Probe', + [ + { + commandName: 'setToolActive', + commandOptions: { + toolName: 'DragProbe', + }, + context: 'CORNERSTONE', + }, + ], + 'Probe' + ), + ToolbarService._createToggleButton( + 'cine', + 'tool-cine', + 'Cine', + [ + { + commandName: 'toggleCine', + context: 'CORNERSTONE', + }, + ], + 'Cine' + ), + ToolbarService._createToolButton( + 'Angle', + 'tool-angle', + 'Angle', + [ + { + commandName: 'setToolActive', + commandOptions: { + toolName: 'Angle', + }, + context: 'CORNERSTONE', + }, + ], + 'Angle' + ), + ToolbarService._createToolButton( + 'Rectangle', + 'tool-rectangle', + 'Rectangle', + [ + { + commandName: 'setToolActive', + commandOptions: { + toolName: 'RectangleROI', + }, + context: 'CORNERSTONE', + }, + ], + 'Rectangle' + ), + ToolbarService._createActionButton( + 'TagBrowser', + 'list-bullets', + 'Dicom Tag Browser', + [ + { + commandName: 'openDICOMTagViewer', + commandOptions: {}, + context: 'DEFAULT', + }, + ], + 'Dicom Tag Browser' + ), + ], + }, + }, +]; + +export default moreToolsMpr; diff --git a/modes/basic-test-mode/src/toolbarButtons.ts b/modes/basic-test-mode/src/toolbarButtons.ts index 5d540b39c..391aef110 100644 --- a/modes/basic-test-mode/src/toolbarButtons.ts +++ b/modes/basic-test-mode/src/toolbarButtons.ts @@ -5,16 +5,11 @@ import { // ListMenu, WindowLevelMenuItem, } from '@ohif/ui'; -import { defaults, ToolbarService } from '@ohif/core'; -import type { Button, RunCommand } from '@ohif/core/types'; -import { EVENTS } from '@cornerstonejs/core'; +import { ToolbarService, defaults } from '@ohif/core'; +import type { Button } from '@ohif/core/types'; const { windowLevelPresets } = defaults; -const _createActionButton = ToolbarService._createButton.bind(null, 'action'); -const _createToggleButton = ToolbarService._createButton.bind(null, 'toggle'); -const _createToolButton = ToolbarService._createButton.bind(null, 'tool'); - /** * * @param {*} preset - preset number (from above import) @@ -39,20 +34,6 @@ function _createWwwcPreset(preset, title, subtitle) { }; } -const ReferenceLinesCommands: RunCommand = [ - { - commandName: 'setSourceViewportForReferenceLinesTool', - context: 'CORNERSTONE', - }, - { - commandName: 'setToolActive', - commandOptions: { - toolName: 'ReferenceLines', - }, - context: 'CORNERSTONE', - }, -]; - const toolbarButtons: Button[] = [ // Measurement { @@ -62,7 +43,7 @@ const toolbarButtons: Button[] = [ groupId: 'MeasurementTools', isRadio: true, // ? // Switch? - primary: _createToolButton( + primary: ToolbarService._createToolButton( 'Length', 'tool-length', 'Length', @@ -93,7 +74,7 @@ const toolbarButtons: Button[] = [ tooltip: 'More Measure Tools', }, items: [ - _createToolButton( + ToolbarService._createToolButton( 'Length', 'tool-length', 'Length', @@ -117,7 +98,7 @@ const toolbarButtons: Button[] = [ ], 'Length Tool' ), - _createToolButton( + ToolbarService._createToolButton( 'Bidirectional', 'tool-bidirectional', 'Bidirectional', @@ -140,7 +121,7 @@ const toolbarButtons: Button[] = [ ], 'Bidirectional Tool' ), - _createToolButton( + ToolbarService._createToolButton( 'ArrowAnnotate', 'tool-annotate', 'Annotation', @@ -163,7 +144,7 @@ const toolbarButtons: Button[] = [ ], 'Arrow Annotate' ), - _createToolButton( + ToolbarService._createToolButton( 'EllipticalROI', 'tool-elipse', 'Ellipse', @@ -186,7 +167,7 @@ const toolbarButtons: Button[] = [ ], 'Ellipse Tool' ), - _createToolButton( + ToolbarService._createToolButton( 'CircleROI', 'tool-circle', 'Circle', @@ -237,7 +218,7 @@ const toolbarButtons: Button[] = [ type: 'ohif.splitButton', props: { groupId: 'WindowLevel', - primary: _createToolButton( + primary: ToolbarService._createToolButton( 'WindowLevel', 'tool-window-level', 'Window Level', @@ -411,7 +392,6 @@ const toolbarButtons: Button[] = [ commandOptions: { protocolId: 'mpr', }, - context: 'DEFAULT', }, ], }, @@ -430,225 +410,10 @@ const toolbarButtons: Button[] = [ toolGroupId: 'mpr', toolName: 'Crosshairs', }, - context: 'CORNERSTONE', }, ], }, }, - // More... - { - id: 'MoreTools', - type: 'ohif.splitButton', - props: { - isRadio: true, // ? - groupId: 'MoreTools', - primary: _createActionButton( - 'Reset', - 'tool-reset', - 'Reset View', - [ - { - commandName: 'resetViewport', - commandOptions: {}, - context: 'CORNERSTONE', - }, - ], - 'Reset' - ), - secondary: { - icon: 'chevron-down', - label: '', - isActive: true, - tooltip: 'More Tools', - }, - items: [ - _createActionButton( - 'Reset', - 'tool-reset', - 'Reset View', - [ - { - commandName: 'resetViewport', - commandOptions: {}, - context: 'CORNERSTONE', - }, - ], - 'Reset' - ), - _createActionButton( - 'rotate-right', - 'tool-rotate-right', - 'Rotate Right', - [ - { - commandName: 'rotateViewportCW', - commandOptions: {}, - context: 'CORNERSTONE', - }, - ], - 'Rotate +90' - ), - _createActionButton( - 'flip-horizontal', - 'tool-flip-horizontal', - 'Flip Horizontally', - [ - { - commandName: 'flipViewportHorizontal', - commandOptions: {}, - context: 'CORNERSTONE', - }, - ], - 'Flip Horizontally' - ), - _createToggleButton( - 'StackImageSync', - 'link', - 'Stack Image Sync', - [ - { - commandName: 'toggleStackImageSync', - }, - ], - 'Enable position synchronization on stack viewports', - { - listeners: { - [EVENTS.STACK_VIEWPORT_NEW_STACK]: { - commandName: 'toggleStackImageSync', - commandOptions: { toggledState: true }, - }, - }, - } - ), - _createToggleButton( - 'ReferenceLines', - 'tool-referenceLines', // change this with the new icon - 'Reference Lines', - ReferenceLinesCommands, - 'Show Reference Lines', - { - listeners: { - [EVENTS.STACK_VIEWPORT_NEW_STACK]: ReferenceLinesCommands, - [EVENTS.ACTIVE_VIEWPORT_ID_CHANGED]: ReferenceLinesCommands, - }, - } - ), - _createToolButton( - 'StackScroll', - 'tool-stack-scroll', - 'Stack Scroll', - [ - { - commandName: 'setToolActive', - commandOptions: { - toolName: 'StackScroll', - }, - context: 'CORNERSTONE', - }, - ], - 'Stack Scroll' - ), - _createActionButton( - 'invert', - 'tool-invert', - 'Invert', - [ - { - commandName: 'invertViewport', - commandOptions: {}, - context: 'CORNERSTONE', - }, - ], - 'Invert Colors' - ), - _createToolButton( - 'Probe', - 'tool-probe', - 'Probe', - [ - { - commandName: 'setToolActive', - commandOptions: { - toolName: 'DragProbe', - }, - context: 'CORNERSTONE', - }, - ], - 'Probe' - ), - _createToggleButton( - 'cine', - 'tool-cine', - 'Cine', - [ - { - commandName: 'toggleCine', - context: 'CORNERSTONE', - }, - ], - 'Cine' - ), - _createToolButton( - 'Angle', - 'tool-angle', - 'Angle', - [ - { - commandName: 'setToolActive', - commandOptions: { - toolName: 'Angle', - }, - context: 'CORNERSTONE', - }, - ], - 'Angle' - ), - _createToolButton( - 'Magnify', - 'tool-magnify', - 'Magnify', - [ - { - commandName: 'setToolActive', - commandOptions: { - toolName: 'Magnify', - }, - context: 'CORNERSTONE', - }, - ], - 'Magnify' - ), - _createToolButton( - 'Rectangle', - 'tool-rectangle', - 'Rectangle', - [ - { - commandName: 'setToolActive', - commandOptions: { - toolName: 'RectangleROI', - }, - context: 'CORNERSTONE', - }, - ], - 'Rectangle' - ), - _createActionButton( - 'TagBrowser', - 'list-bullets', - 'Dicom Tag Browser', - [ - { - commandName: 'openDICOMTagViewer', - commandOptions: {}, - context: 'DEFAULT', - }, - ], - 'Dicom Tag Browser' - ), - ], - }, - }, ]; export default toolbarButtons; diff --git a/modes/longitudinal/src/index.js b/modes/longitudinal/src/index.js index 25397a2cf..03f7fce9c 100644 --- a/modes/longitudinal/src/index.js +++ b/modes/longitudinal/src/index.js @@ -2,11 +2,16 @@ import { hotkeys } from '@ohif/core'; import toolbarButtons from './toolbarButtons'; import { id } from './id'; import initToolGroups from './initToolGroups'; +import moreTools from './moreTools'; +import moreToolsMpr from './moreToolsMpr'; // Allow this mode by excluding non-imaging modalities such as SR, SEG // Also, SM is not a simple imaging modalities, so exclude it. const NON_IMAGE_MODALITIES = ['SM', 'ECG', 'SR', 'SEG', 'RTSTRUCT']; +const DEFAULT_TOOL_GROUP_ID = 'default'; +const MPR_TOOL_GROUP_ID = 'mpr'; + const ohif = { layout: '@ohif/extension-default.layoutTemplateModule.viewerLayout', sopClassHandler: '@ohif/extension-default.sopClassHandlerModule.stack', @@ -83,21 +88,23 @@ function modeFactory({ modeConfiguration }) { initToolGroups(extensionManager, toolGroupService, commandsManager); let unsubscribe; + toolbarService.setDefaultTool({ + groupId: 'WindowLevel', + itemId: 'WindowLevel', + interactionType: 'tool', + commands: [ + { + commandName: 'setToolActive', + commandOptions: { + toolName: 'WindowLevel', + }, + context: 'CORNERSTONE', + }, + ], + }); const activateTool = () => { - toolbarService.recordInteraction({ - groupId: 'WindowLevel', - interactionType: 'tool', - commands: [ - { - commandName: 'setToolActive', - commandOptions: { - toolName: 'WindowLevel', - }, - context: 'CORNERSTONE', - }, - ], - }); + toolbarService.recordInteraction(toolbarService.getDefaultTool()); // We don't need to reset the active tool whenever a viewport is getting // added to the toolGroup. @@ -112,8 +119,18 @@ function modeFactory({ modeConfiguration }) { )); toolbarService.init(extensionManager); - toolbarService.addButtons(toolbarButtons); - toolbarService.createButtonSection('primary', [ + toolbarService.addButtons([...toolbarButtons, ...moreTools, ...moreToolsMpr]); + toolbarService.createButtonSection(DEFAULT_TOOL_GROUP_ID, [ + 'MeasurementTools', + 'Zoom', + 'WindowLevel', + 'Pan', + 'Capture', + 'Layout', + 'MPR', + 'MoreTools', + ]); + toolbarService.createButtonSection(MPR_TOOL_GROUP_ID, [ 'MeasurementTools', 'Zoom', 'WindowLevel', @@ -122,7 +139,7 @@ function modeFactory({ modeConfiguration }) { 'Layout', 'MPR', 'Crosshairs', - 'MoreTools', + 'MoreToolsMpr', ]); customizationService.addModeCustomizations([ diff --git a/modes/longitudinal/src/moreTools.ts b/modes/longitudinal/src/moreTools.ts new file mode 100644 index 000000000..5a20d1cbe --- /dev/null +++ b/modes/longitudinal/src/moreTools.ts @@ -0,0 +1,298 @@ +import type { RunCommand } from '@ohif/core/types'; +import { EVENTS } from '@cornerstonejs/core'; +import { ToolbarService } from '@ohif/core'; + +const ReferenceLinesCommands: RunCommand = [ + { + commandName: 'setSourceViewportForReferenceLinesTool', + context: 'CORNERSTONE', + }, + { + commandName: 'setToolActive', + commandOptions: { + toolName: 'ReferenceLines', + }, + context: 'CORNERSTONE', + }, +]; + +const moreTools = [ + { + id: 'MoreTools', + type: 'ohif.splitButton', + props: { + isRadio: true, // ? + groupId: 'MoreTools', + primary: ToolbarService._createActionButton( + 'Reset', + 'tool-reset', + 'Reset View', + [ + { + commandName: 'resetViewport', + commandOptions: {}, + context: 'CORNERSTONE', + }, + ], + 'Reset' + ), + secondary: { + icon: 'chevron-down', + label: '', + isActive: true, + tooltip: 'More Tools', + }, + items: [ + ToolbarService._createActionButton( + 'Reset', + 'tool-reset', + 'Reset View', + [ + { + commandName: 'resetViewport', + commandOptions: {}, + context: 'CORNERSTONE', + }, + ], + 'Reset' + ), + ToolbarService._createActionButton( + 'rotate-right', + 'tool-rotate-right', + 'Rotate Right', + [ + { + commandName: 'rotateViewportCW', + commandOptions: {}, + context: 'CORNERSTONE', + }, + ], + 'Rotate +90' + ), + ToolbarService._createActionButton( + 'flip-horizontal', + 'tool-flip-horizontal', + 'Flip Horizontally', + [ + { + commandName: 'flipViewportHorizontal', + commandOptions: {}, + context: 'CORNERSTONE', + }, + ], + 'Flip Horizontal' + ), + ToolbarService._createToggleButton( + 'StackImageSync', + 'link', + 'Stack Image Sync', + [ + { + commandName: 'toggleStackImageSync', + }, + ], + 'Enable position synchronization on stack viewports', + { + listeners: { + [EVENTS.STACK_VIEWPORT_NEW_STACK]: { + commandName: 'toggleStackImageSync', + commandOptions: { toggledState: true }, + }, + }, + } + ), + ToolbarService._createToggleButton( + 'ReferenceLines', + 'tool-referenceLines', // change this with the new icon + 'Reference Lines', + ReferenceLinesCommands, + 'Show Reference Lines', + { + listeners: { + [EVENTS.STACK_VIEWPORT_NEW_STACK]: ReferenceLinesCommands, + [EVENTS.ACTIVE_VIEWPORT_ID_CHANGED]: ReferenceLinesCommands, + }, + } + ), + ToolbarService._createToggleButton( + 'ImageOverlayViewer', + 'toggle-dicom-overlay', + 'Image Overlay', + [ + { + commandName: 'setToolActive', + commandOptions: { + toolName: 'ImageOverlayViewer', + }, + context: 'CORNERSTONE', + }, + ], + 'Image Overlay', + { isActive: true } + ), + ToolbarService._createToolButton( + 'StackScroll', + 'tool-stack-scroll', + 'Stack Scroll', + [ + { + commandName: 'setToolActive', + commandOptions: { + toolName: 'StackScroll', + }, + context: 'CORNERSTONE', + }, + ], + 'Stack Scroll' + ), + ToolbarService._createActionButton( + 'invert', + 'tool-invert', + 'Invert', + [ + { + commandName: 'invertViewport', + commandOptions: {}, + context: 'CORNERSTONE', + }, + ], + 'Invert Colors' + ), + ToolbarService._createToolButton( + 'Probe', + 'tool-probe', + 'Probe', + [ + { + commandName: 'setToolActive', + commandOptions: { + toolName: 'DragProbe', + }, + context: 'CORNERSTONE', + }, + ], + 'Probe' + ), + ToolbarService._createToggleButton( + 'cine', + 'tool-cine', + 'Cine', + [ + { + commandName: 'toggleCine', + context: 'CORNERSTONE', + }, + ], + 'Cine' + ), + ToolbarService._createToolButton( + 'Angle', + 'tool-angle', + 'Angle', + [ + { + commandName: 'setToolActive', + commandOptions: { + toolName: 'Angle', + }, + context: 'CORNERSTONE', + }, + ], + 'Angle' + ), + + // Next two tools can be added once icons are added + // ToolbarService._createToolButton( + // 'Cobb Angle', + // 'tool-cobb-angle', + // 'Cobb Angle', + // [ + // { + // commandName: 'setToolActive', + // commandOptions: { + // toolName: 'CobbAngle', + // }, + // context: 'CORNERSTONE', + // }, + // ], + // 'Cobb Angle' + // ), + // ToolbarService._createToolButton( + // 'Planar Freehand ROI', + // 'tool-freehand', + // 'PlanarFreehandROI', + // [ + // { + // commandName: 'setToolActive', + // commandOptions: { + // toolName: 'PlanarFreehandROI', + // }, + // context: 'CORNERSTONE', + // }, + // ], + // 'Planar Freehand ROI' + // ), + ToolbarService._createToolButton( + 'Magnify', + 'tool-magnify', + 'Magnify', + [ + { + commandName: 'setToolActive', + commandOptions: { + toolName: 'Magnify', + }, + context: 'CORNERSTONE', + }, + ], + 'Magnify' + ), + ToolbarService._createToolButton( + 'Rectangle', + 'tool-rectangle', + 'Rectangle', + [ + { + commandName: 'setToolActive', + commandOptions: { + toolName: 'RectangleROI', + }, + context: 'CORNERSTONE', + }, + ], + 'Rectangle' + ), + ToolbarService._createToolButton( + 'CalibrationLine', + 'tool-calibration', + 'Calibration', + [ + { + commandName: 'setToolActive', + commandOptions: { + toolName: 'CalibrationLine', + }, + context: 'CORNERSTONE', + }, + ], + 'Calibration Line' + ), + ToolbarService._createActionButton( + 'TagBrowser', + 'list-bullets', + 'Dicom Tag Browser', + [ + { + commandName: 'openDICOMTagViewer', + commandOptions: {}, + context: 'DEFAULT', + }, + ], + 'Dicom Tag Browser' + ), + ], + }, + }, +]; + +export default moreTools; diff --git a/modes/longitudinal/src/moreToolsMpr.ts b/modes/longitudinal/src/moreToolsMpr.ts new file mode 100644 index 000000000..7930068dc --- /dev/null +++ b/modes/longitudinal/src/moreToolsMpr.ts @@ -0,0 +1,242 @@ +import type { RunCommand } from '@ohif/core/types'; +import { EVENTS } from '@cornerstonejs/core'; +import { ToolbarService } from '@ohif/core'; + +const ReferenceLinesCommands: RunCommand = [ + { + commandName: 'setSourceViewportForReferenceLinesTool', + context: 'CORNERSTONE', + }, + { + commandName: 'setToolActive', + commandOptions: { + toolName: 'ReferenceLines', + }, + context: 'CORNERSTONE', + }, +]; + +const moreToolsMpr = [ + { + id: 'MoreToolsMpr', + type: 'ohif.splitButton', + props: { + isRadio: true, // ? + groupId: 'MoreTools', + primary: ToolbarService._createActionButton( + 'Reset', + 'tool-reset', + 'Reset View', + [ + { + commandName: 'resetViewport', + commandOptions: {}, + context: 'CORNERSTONE', + }, + ], + 'Reset' + ), + secondary: { + icon: 'chevron-down', + label: '', + isActive: true, + tooltip: 'More Tools', + }, + items: [ + ToolbarService._createActionButton( + 'Reset', + 'tool-reset', + 'Reset View', + [ + { + commandName: 'resetViewport', + commandOptions: {}, + context: 'CORNERSTONE', + }, + ], + 'Reset' + ), + ToolbarService._createToggleButton( + 'StackImageSync', + 'link', + 'Stack Image Sync', + [ + { + commandName: 'toggleStackImageSync', + }, + ], + 'Enable position synchronization on stack viewports', + { + listeners: { + [EVENTS.STACK_VIEWPORT_NEW_STACK]: { + commandName: 'toggleStackImageSync', + commandOptions: { toggledState: true }, + }, + }, + } + ), + ToolbarService._createToggleButton( + 'ReferenceLines', + 'tool-referenceLines', // change this with the new icon + 'Reference Lines', + ReferenceLinesCommands, + 'Show Reference Lines', + { + listeners: { + [EVENTS.STACK_VIEWPORT_NEW_STACK]: ReferenceLinesCommands, + [EVENTS.ACTIVE_VIEWPORT_ID_CHANGED]: ReferenceLinesCommands, + }, + } + ), + ToolbarService._createToggleButton( + 'ImageOverlayViewer', + 'toggle-dicom-overlay', + 'Image Overlay', + [ + { + commandName: 'setToolActive', + commandOptions: { + toolName: 'ImageOverlayViewer', + }, + context: 'CORNERSTONE', + }, + ], + 'Image Overlay', + { isActive: true } + ), + ToolbarService._createToolButton( + 'StackScroll', + 'tool-stack-scroll', + 'Stack Scroll', + [ + { + commandName: 'setToolActive', + commandOptions: { + toolName: 'StackScroll', + }, + context: 'CORNERSTONE', + }, + ], + 'Stack Scroll' + ), + ToolbarService._createActionButton( + 'invert', + 'tool-invert', + 'Invert', + [ + { + commandName: 'invertViewport', + commandOptions: {}, + context: 'CORNERSTONE', + }, + ], + 'Invert Colors' + ), + ToolbarService._createToolButton( + 'Probe', + 'tool-probe', + 'Probe', + [ + { + commandName: 'setToolActive', + commandOptions: { + toolName: 'DragProbe', + }, + context: 'CORNERSTONE', + }, + ], + 'Probe' + ), + ToolbarService._createToggleButton( + 'cine', + 'tool-cine', + 'Cine', + [ + { + commandName: 'toggleCine', + context: 'CORNERSTONE', + }, + ], + 'Cine' + ), + ToolbarService._createToolButton( + 'Angle', + 'tool-angle', + 'Angle', + [ + { + commandName: 'setToolActive', + commandOptions: { + toolName: 'Angle', + }, + context: 'CORNERSTONE', + }, + ], + 'Angle' + ), + + // Next two tools can be added once icons are added + // ToolbarService._createToolButton( + // 'Cobb Angle', + // 'tool-cobb-angle', + // 'Cobb Angle', + // [ + // { + // commandName: 'setToolActive', + // commandOptions: { + // toolName: 'CobbAngle', + // }, + // context: 'CORNERSTONE', + // }, + // ], + // 'Cobb Angle' + // ), + // ToolbarService._createToolButton( + // 'Planar Freehand ROI', + // 'tool-freehand', + // 'PlanarFreehandROI', + // [ + // { + // commandName: 'setToolActive', + // commandOptions: { + // toolName: 'PlanarFreehandROI', + // }, + // context: 'CORNERSTONE', + // }, + // ], + // 'Planar Freehand ROI' + // ), + ToolbarService._createToolButton( + 'Rectangle', + 'tool-rectangle', + 'Rectangle', + [ + { + commandName: 'setToolActive', + commandOptions: { + toolName: 'RectangleROI', + }, + context: 'CORNERSTONE', + }, + ], + 'Rectangle' + ), + ToolbarService._createActionButton( + 'TagBrowser', + 'list-bullets', + 'Dicom Tag Browser', + [ + { + commandName: 'openDICOMTagViewer', + commandOptions: {}, + context: 'DEFAULT', + }, + ], + 'Dicom Tag Browser' + ), + ], + }, + }, +]; + +export default moreToolsMpr; diff --git a/modes/longitudinal/src/toolbarButtons.ts b/modes/longitudinal/src/toolbarButtons.ts index e7842c565..2237edd2b 100644 --- a/modes/longitudinal/src/toolbarButtons.ts +++ b/modes/longitudinal/src/toolbarButtons.ts @@ -6,15 +6,10 @@ import { WindowLevelMenuItem, } from '@ohif/ui'; import { defaults, ToolbarService } from '@ohif/core'; -import type { Button, RunCommand } from '@ohif/core/types'; -import { EVENTS } from '@cornerstonejs/core'; +import type { Button } from '@ohif/core/types'; const { windowLevelPresets } = defaults; -const _createActionButton = ToolbarService._createButton.bind(null, 'action'); -const _createToggleButton = ToolbarService._createButton.bind(null, 'toggle'); -const _createToolButton = ToolbarService._createButton.bind(null, 'tool'); - /** * * @param {*} preset - preset number (from above import) @@ -59,20 +54,6 @@ function _createSetToolActiveCommands(toolName) { return temp; } -const ReferenceLinesCommands: RunCommand = [ - { - commandName: 'setSourceViewportForReferenceLinesTool', - context: 'CORNERSTONE', - }, - { - commandName: 'setToolActive', - commandOptions: { - toolName: 'ReferenceLines', - }, - context: 'CORNERSTONE', - }, -]; - const toolbarButtons: Button[] = [ // Measurement { @@ -82,7 +63,7 @@ const toolbarButtons: Button[] = [ groupId: 'MeasurementTools', isRadio: true, // ? // Switch? - primary: _createToolButton( + primary: ToolbarService._createToolButton( 'Length', 'tool-length', 'Length', @@ -113,7 +94,7 @@ const toolbarButtons: Button[] = [ tooltip: 'More Measure Tools', }, items: [ - _createToolButton( + ToolbarService._createToolButton( 'Length', 'tool-length', 'Length', @@ -137,7 +118,7 @@ const toolbarButtons: Button[] = [ ], 'Length Tool' ), - _createToolButton( + ToolbarService._createToolButton( 'Bidirectional', 'tool-bidirectional', 'Bidirectional', @@ -160,7 +141,7 @@ const toolbarButtons: Button[] = [ ], 'Bidirectional Tool' ), - _createToolButton( + ToolbarService._createToolButton( 'ArrowAnnotate', 'tool-annotate', 'Annotation', @@ -183,7 +164,7 @@ const toolbarButtons: Button[] = [ ], 'Arrow Annotate' ), - _createToolButton( + ToolbarService._createToolButton( 'EllipticalROI', 'tool-elipse', 'Ellipse', @@ -206,7 +187,7 @@ const toolbarButtons: Button[] = [ ], 'Ellipse Tool' ), - _createToolButton( + ToolbarService._createToolButton( 'CircleROI', 'tool-circle', 'Circle', @@ -249,7 +230,7 @@ const toolbarButtons: Button[] = [ type: 'ohif.splitButton', props: { groupId: 'WindowLevel', - primary: _createToolButton( + primary: ToolbarService._createToolButton( 'WindowLevel', 'tool-window-level', 'Window Level', @@ -354,282 +335,6 @@ const toolbarButtons: Button[] = [ }, }, // More... - { - id: 'MoreTools', - type: 'ohif.splitButton', - props: { - isRadio: true, // ? - groupId: 'MoreTools', - primary: _createActionButton( - 'Reset', - 'tool-reset', - 'Reset View', - [ - { - commandName: 'resetViewport', - commandOptions: {}, - context: 'CORNERSTONE', - }, - ], - 'Reset' - ), - secondary: { - icon: 'chevron-down', - label: '', - isActive: true, - tooltip: 'More Tools', - }, - items: [ - _createActionButton( - 'Reset', - 'tool-reset', - 'Reset View', - [ - { - commandName: 'resetViewport', - commandOptions: {}, - context: 'CORNERSTONE', - }, - ], - 'Reset' - ), - _createActionButton( - 'rotate-right', - 'tool-rotate-right', - 'Rotate Right', - [ - { - commandName: 'rotateViewportCW', - commandOptions: {}, - context: 'CORNERSTONE', - }, - ], - 'Rotate +90' - ), - _createActionButton( - 'flip-horizontal', - 'tool-flip-horizontal', - 'Flip Horizontally', - [ - { - commandName: 'flipViewportHorizontal', - commandOptions: {}, - context: 'CORNERSTONE', - }, - ], - 'Flip Horizontal' - ), - _createToggleButton( - 'StackImageSync', - 'link', - 'Stack Image Sync', - [ - { - commandName: 'toggleStackImageSync', - }, - ], - 'Enable position synchronization on stack viewports', - { - listeners: { - [EVENTS.STACK_VIEWPORT_NEW_STACK]: { - commandName: 'toggleStackImageSync', - commandOptions: { toggledState: true }, - }, - }, - } - ), - _createToggleButton( - 'ReferenceLines', - 'tool-referenceLines', // change this with the new icon - 'Reference Lines', - ReferenceLinesCommands, - 'Show Reference Lines', - { - listeners: { - [EVENTS.STACK_VIEWPORT_NEW_STACK]: ReferenceLinesCommands, - [EVENTS.ACTIVE_VIEWPORT_ID_CHANGED]: ReferenceLinesCommands, - }, - } - ), - _createToggleButton( - 'ImageOverlayViewer', - 'toggle-dicom-overlay', - 'Image Overlay', - [ - { - commandName: 'setToolActive', - commandOptions: { - toolName: 'ImageOverlayViewer', - }, - context: 'CORNERSTONE', - }, - ], - 'Image Overlay', - { isActive: true } - ), - _createToolButton( - 'StackScroll', - 'tool-stack-scroll', - 'Stack Scroll', - [ - { - commandName: 'setToolActive', - commandOptions: { - toolName: 'StackScroll', - }, - context: 'CORNERSTONE', - }, - ], - 'Stack Scroll' - ), - _createActionButton( - 'invert', - 'tool-invert', - 'Invert', - [ - { - commandName: 'invertViewport', - commandOptions: {}, - context: 'CORNERSTONE', - }, - ], - 'Invert Colors' - ), - _createToolButton( - 'Probe', - 'tool-probe', - 'Probe', - [ - { - commandName: 'setToolActive', - commandOptions: { - toolName: 'DragProbe', - }, - context: 'CORNERSTONE', - }, - ], - 'Probe' - ), - _createToggleButton( - 'cine', - 'tool-cine', - 'Cine', - [ - { - commandName: 'toggleCine', - context: 'CORNERSTONE', - }, - ], - 'Cine' - ), - _createToolButton( - 'Angle', - 'tool-angle', - 'Angle', - [ - { - commandName: 'setToolActive', - commandOptions: { - toolName: 'Angle', - }, - context: 'CORNERSTONE', - }, - ], - 'Angle' - ), - - // Next two tools can be added once icons are added - // _createToolButton( - // 'Cobb Angle', - // 'tool-cobb-angle', - // 'Cobb Angle', - // [ - // { - // commandName: 'setToolActive', - // commandOptions: { - // toolName: 'CobbAngle', - // }, - // context: 'CORNERSTONE', - // }, - // ], - // 'Cobb Angle' - // ), - // _createToolButton( - // 'Planar Freehand ROI', - // 'tool-freehand', - // 'PlanarFreehandROI', - // [ - // { - // commandName: 'setToolActive', - // commandOptions: { - // toolName: 'PlanarFreehandROI', - // }, - // context: 'CORNERSTONE', - // }, - // ], - // 'Planar Freehand ROI' - // ), - _createToolButton( - 'Magnify', - 'tool-magnify', - 'Magnify', - [ - { - commandName: 'setToolActive', - commandOptions: { - toolName: 'Magnify', - }, - context: 'CORNERSTONE', - }, - ], - 'Magnify' - ), - _createToolButton( - 'Rectangle', - 'tool-rectangle', - 'Rectangle', - [ - { - commandName: 'setToolActive', - commandOptions: { - toolName: 'RectangleROI', - }, - context: 'CORNERSTONE', - }, - ], - 'Rectangle' - ), - _createToolButton( - 'CalibrationLine', - 'tool-calibration', - 'Calibration', - [ - { - commandName: 'setToolActive', - commandOptions: { - toolName: 'CalibrationLine', - }, - context: 'CORNERSTONE', - }, - ], - 'Calibration Line' - ), - _createActionButton( - 'TagBrowser', - 'list-bullets', - 'Dicom Tag Browser', - [ - { - commandName: 'openDICOMTagViewer', - commandOptions: {}, - context: 'DEFAULT', - }, - ], - 'Dicom Tag Browser' - ), - ], - }, - }, ]; export default toolbarButtons; diff --git a/platform/app/cypress/integration/measurement-tracking/OHIFMeasurementPanel.spec.js b/platform/app/cypress/integration/measurement-tracking/OHIFMeasurementPanel.spec.js index 9f0e8b0c8..623c58ec6 100644 --- a/platform/app/cypress/integration/measurement-tracking/OHIFMeasurementPanel.spec.js +++ b/platform/app/cypress/integration/measurement-tracking/OHIFMeasurementPanel.spec.js @@ -48,6 +48,7 @@ describe('OHIF Measurement Panel', function () { cy.scrollToIndex(13); + // Reset to default tool so that the new add length works cy.addLengthMeasurement([100, 100], [200, 200]); //Adding measurement in the viewport cy.get('@viewportInfoTopRight').should('contains.text', '(14/'); diff --git a/platform/app/cypress/integration/volume/MPR.spec.js b/platform/app/cypress/integration/volume/MPR.spec.js index c2f8d94cc..2f2cb05cb 100644 --- a/platform/app/cypress/integration/volume/MPR.spec.js +++ b/platform/app/cypress/integration/volume/MPR.spec.js @@ -88,19 +88,7 @@ describe('OHIF MPR', () => { }); it('should correctly render Crosshairs for MPR', () => { - cy.wait(250); - - cy.get('[data-cy="Crosshairs"]').click(); - cy.window() - .its('cornerstoneTools') - .then(cornerstoneTools => { - const state = cornerstoneTools.annotation.state.getAnnotationManager(); - - const fORMap = state.annotations; - // it should not have crosshairs yet - expect(Object.keys(fORMap)).to.have.length(0); - }); - + cy.get('[data-cy="Crosshairs"]').should('not.exist'); cy.get(':nth-child(3) > [data-cy="study-browser-thumbnail"]').dblclick(); cy.get('[data-cy="MPR"]').click(); cy.get('[data-cy="Crosshairs"]').click(); @@ -131,4 +119,19 @@ describe('OHIF MPR', () => { ); }); }); + + it('should activate window level when the active Crosshairs tool for MPR is clicked', () => { + cy.get(':nth-child(3) > [data-cy="study-browser-thumbnail"]').dblclick(); + cy.get('[data-cy="MPR"]').click(); + cy.get('[data-cy="Crosshairs"]').click(); + + // wait for the crosshairs tool to be active + cy.get('[data-cy="Crosshairs"].active'); + + // Click the crosshairs button to deactivate it. + cy.get('[data-cy="Crosshairs"]').click(); + + // wait for the window level button to be active + cy.get('[data-cy="WindowLevel-split-button-primary"].active'); + }); }); diff --git a/platform/app/cypress/support/commands.js b/platform/app/cypress/support/commands.js index 83b2df919..f1351eb0d 100644 --- a/platform/app/cypress/support/commands.js +++ b/platform/app/cypress/support/commands.js @@ -262,7 +262,14 @@ Cypress.Commands.add( cy.get('@measurementToolsBtnPrimary').as('lengthButton'); cy.get('@lengthButton').should('have.attr', 'data-tool', 'Length'); - cy.get('@lengthButton').click(); + + cy.get('@lengthButton').then(button => { + // Only click the length tool if it is not active, in case the length tool is set up to + // toggle to inactive. + if (!button.is('.active')) { + cy.wrap(button).click(); + } + }); cy.get('@lengthButton').should('have.class', 'active'); diff --git a/platform/core/src/services/ToolBarService/ToolbarService.ts b/platform/core/src/services/ToolBarService/ToolbarService.ts index a39d3eca5..269f93da4 100644 --- a/platform/core/src/services/ToolBarService/ToolbarService.ts +++ b/platform/core/src/services/ToolBarService/ToolbarService.ts @@ -63,12 +63,17 @@ export default class ToolbarService extends PubSubService { }; } + public static _createActionButton = ToolbarService._createButton.bind(null, 'action'); + public static _createToggleButton = ToolbarService._createButton.bind(null, 'toggle'); + public static _createToolButton = ToolbarService._createButton.bind(null, 'tool'); + buttons: Record = {}; state: { primaryToolId: string; toggles: Record; groups: Record; - } = { primaryToolId: 'WindowLevel', toggles: {}, groups: {} }; + } = { primaryToolId: '', toggles: {}, groups: {} }; + buttonSections: Record = { /** * primary: ['Zoom', 'Wwwc'], @@ -78,6 +83,8 @@ export default class ToolbarService extends PubSubService { _commandsManager: CommandsManager; extensionManager: ExtensionManager; + defaultTool: Record; + constructor(commandsManager: CommandsManager) { super(EVENTS); this._commandsManager = commandsManager; @@ -103,6 +110,19 @@ export default class ToolbarService extends PubSubService { this.reset(); } + /** + * Sets the default tool that will be activated whenever the primary tool is + * deactivated without activating another/different tool. + * @param interaction the interaction command that will set the default tool active + */ + public setDefaultTool(interaction) { + this.defaultTool = interaction; + } + + public getDefaultTool() { + return this.defaultTool; + } + /** * * @param {*} interaction - can be undefined to run nothing @@ -125,25 +145,20 @@ export default class ToolbarService extends PubSubService { switch (interactionType) { case 'action': { - commands.forEach(({ commandName, commandOptions, context }) => { - if (commandName) { - commandsManager.runCommand( - commandName, - { - ...commandOptions, - ...options, - }, - context - ); - } - }); + commandsManager.run(commands, options); break; } case 'tool': { try { - commands.forEach(({ commandName = 'setToolActive', commandOptions, context }) => { - commandsManager.runCommand(commandName, commandOptions, context); - }); + const alternateInteraction = + this.state.primaryToolId === itemId && + this.defaultTool?.itemId !== itemId && + this.getDefaultTool(); + if (alternateInteraction) { + // Allow toggling the mode off + return this.recordInteraction(alternateInteraction, options); + } + commandsManager.run(commands, options); // only set the primary tool if no error was thrown. // if the itemId is not undefined use it; otherwise, set the first tool in @@ -312,21 +327,27 @@ export default class ToolbarService extends PubSubService { /** * - * Finds a button section by it's name, then maps the list of string name + * Finds a button section by it's name/tool group id, then maps the list of string name * identifiers to schema/values that can be used to render the buttons. * - * @param {string} key - * @param {*} props + * @param toolGroupId - the tool group id + * @param props - optional properties to apply to every button of the section + * @param defaultToolGroupId - the fallback section to return if the given toolGroupId section is not available */ - getButtonSection(key, props) { - const buttonSectionIds = this.buttonSections[key]; + getButtonSection( + toolGroupId: string, + props?: Record, + defaultToolGroupId = 'primary' + ) { + const buttonSectionIds = + this.buttonSections[toolGroupId] || this.buttonSections[defaultToolGroupId]; const buttonsInSection = []; if (buttonSectionIds && buttonSectionIds.length !== 0) { buttonSectionIds.forEach(btnId => { const btn = this.buttons[btnId]; const metadata = {}; - const mappedBtn = this._mapButtonToDisplay(btn, key, metadata, props); + const mappedBtn = this._mapButtonToDisplay(btn, toolGroupId, metadata, props); buttonsInSection.push(mappedBtn); }); diff --git a/platform/docs/docs/platform/services/data/ToolbarService.md b/platform/docs/docs/platform/services/data/ToolbarService.md index f92cc385f..3acfb3e2d 100644 --- a/platform/docs/docs/platform/services/data/ToolbarService.md +++ b/platform/docs/docs/platform/services/data/ToolbarService.md @@ -46,6 +46,8 @@ button is clicked by the user. - `getActiveTools`: returns the active tool + all the toggled-on tools +- `setDefaultTool`: sets the default tool that will be activated whenever the primary tool is deactivated without activating another/different tool + ## State ToolBarService has an internal state that gets updated per tool interaction and diff --git a/platform/docs/docs/platform/services/ui/viewport-grid-service.md b/platform/docs/docs/platform/services/ui/viewport-grid-service.md index ee610a98c..6f8435905 100644 --- a/platform/docs/docs/platform/services/ui/viewport-grid-service.md +++ b/platform/docs/docs/platform/services/ui/viewport-grid-service.md @@ -33,6 +33,7 @@ is expected to support, [check out it's interface in `@ohif/core`][interface] | `getNumViewportPanes()` | Gets the number of visible viewport panes | | `getLayoutOptionsFromState(gridState)` | Utility method that produces a `ViewportLayoutOptions` based on the passed in state| | `getActiveViewportId()` | Returns the viewport Id of the active viewport in the grid| +| `getActiveViewportOptionByKey(key)` | Gets the specified viewport option field (key) for the active viewport | ## Implementations diff --git a/platform/ui/src/contextProviders/ViewportGridProvider.tsx b/platform/ui/src/contextProviders/ViewportGridProvider.tsx index 67d951347..c5eee312b 100644 --- a/platform/ui/src/contextProviders/ViewportGridProvider.tsx +++ b/platform/ui/src/contextProviders/ViewportGridProvider.tsx @@ -300,6 +300,11 @@ export function ViewportGridProvider({ children, service }) { return viewportGridState; }, [viewportGridState]); + const getActiveViewportOptionByKey = (key: string) => { + const { viewports, activeViewportId } = viewportGridState; + return viewports.get(activeViewportId)?.viewportOptions?.[key]; + }; + const setActiveViewportId = useCallback( index => dispatch({ type: 'SET_ACTIVE_VIEWPORT_ID', payload: index }), [dispatch] @@ -400,6 +405,7 @@ export function ViewportGridProvider({ children, service }) { reset: () => service.reset(), set: gridLayoutState => service.setState(gridLayoutState), // run it through the service itself since we want to publish events getNumViewportPanes, + getActiveViewportOptionByKey, }; return (