diff --git a/extensions/cornerstone/src/Viewport/OHIFCornerstoneViewport.tsx b/extensions/cornerstone/src/Viewport/OHIFCornerstoneViewport.tsx index 9c867c1b6..b98cd320e 100644 --- a/extensions/cornerstone/src/Viewport/OHIFCornerstoneViewport.tsx +++ b/extensions/cornerstone/src/Viewport/OHIFCornerstoneViewport.tsx @@ -15,7 +15,6 @@ import type { Types } from '@ohif/core'; import OHIFViewportActionCorners from '../components/OHIFViewportActionCorners'; import { getWindowLevelActionMenu } from '../components/WindowLevelActionMenu/getWindowLevelActionMenu'; -import { useAppConfig } from '@state'; import { getViewportDataOverlaySettingsMenu } from '../components/ViewportDataOverlaySettingMenu'; import { getViewportPresentations } from '../utils/presentations/getViewportPresentations'; import { useSynchronizersStore } from '../stores/useSynchronizersStore'; @@ -89,7 +88,6 @@ const OHIFCornerstoneViewport = React.memo( const [scrollbarHeight, setScrollbarHeight] = useState('100px'); const [enabledVPElement, setEnabledVPElement] = useState(null); const elementRef = useRef() as React.MutableRefObject; - const [appConfig] = useAppConfig(); const { displaySetService, @@ -324,51 +322,52 @@ const OHIFCornerstoneViewport = React.memo( // Set up the window level action menu in the viewport action corners. useEffect(() => { - // Doing an === check here because the default config value when not set is true - if (appConfig.addWindowLevelActionMenu === false) { - return; + const windowLevelActionMenu = customizationService.getCustomization( + 'viewportActionMenu.windowLevelActionMenu' + ); + const segmentationOverlay = customizationService.getCustomization( + 'viewportActionMenu.segmentationOverlay' + ); + + if (windowLevelActionMenu?.enabled) { + viewportActionCornersService.addComponent({ + viewportId, + id: 'windowLevelActionMenu', + component: getWindowLevelActionMenu({ + viewportId, + element: elementRef.current, + displaySets, + servicesManager, + commandsManager, + location: windowLevelActionMenu.location, + verticalDirection: AllInOneMenu.VerticalDirection.TopToBottom, + horizontalDirection: AllInOneMenu.HorizontalDirection.RightToLeft, + }), + location: windowLevelActionMenu.location, + }); } - const location = viewportActionCornersService.LOCATIONS.topRight; - - // TODO: In the future we should consider using the customization service - // to determine if and in which corner various action components should go. - viewportActionCornersService.addComponent({ - viewportId, - id: 'windowLevelActionMenu', - component: getWindowLevelActionMenu({ + if (segmentationOverlay?.enabled) { + viewportActionCornersService.addComponent({ viewportId, - element: elementRef.current, - displaySets, - servicesManager, - commandsManager, - location, - verticalDirection: AllInOneMenu.VerticalDirection.TopToBottom, - horizontalDirection: AllInOneMenu.HorizontalDirection.RightToLeft, - }), - location, - }); - - viewportActionCornersService.addComponent({ - viewportId, - id: 'segmentation', - component: getViewportDataOverlaySettingsMenu({ - viewportId, - element: elementRef.current, - displaySets, - servicesManager, - commandsManager, - location, - }), - location, - }); + id: 'segmentation', + component: getViewportDataOverlaySettingsMenu({ + viewportId, + element: elementRef.current, + displaySets, + servicesManager, + commandsManager, + location: segmentationOverlay.location, + }), + location: segmentationOverlay.location, + }); + } }, [ displaySets, viewportId, viewportActionCornersService, servicesManager, commandsManager, - appConfig, ]); const { ref: resizeRef } = useResizeDetector({ diff --git a/extensions/cornerstone/src/customizations/viewportActionMenuCustomizations.ts b/extensions/cornerstone/src/customizations/viewportActionMenuCustomizations.ts new file mode 100644 index 000000000..0605c15be --- /dev/null +++ b/extensions/cornerstone/src/customizations/viewportActionMenuCustomizations.ts @@ -0,0 +1,12 @@ +import viewportActionCornersService from '../services/ViewportActionCornersService/ViewportActionCornersService'; + +export default { + 'viewportActionMenu.windowLevelActionMenu': { + enabled: true, + location: viewportActionCornersService.LOCATIONS.topRight, + }, + 'viewportActionMenu.segmentationOverlay': { + enabled: true, + location: viewportActionCornersService.LOCATIONS.topRight, + }, +}; diff --git a/extensions/cornerstone/src/getCustomizationModule.tsx b/extensions/cornerstone/src/getCustomizationModule.tsx index 3e2a30d3a..b37c98902 100644 --- a/extensions/cornerstone/src/getCustomizationModule.tsx +++ b/extensions/cornerstone/src/getCustomizationModule.tsx @@ -9,6 +9,7 @@ import colorbarCustomization from './customizations/colorbarCustomization'; import windowLevelPresetsCustomization from './customizations/windowLevelPresetsCustomization'; import miscCustomization from './customizations/miscCustomization'; import windowLevelActionMenuCustomization from './customizations/windowLevelActionMenuCustomization'; +import viewportActionMenuCustomizations from './customizations/viewportActionMenuCustomizations'; function getCustomizationModule({ commandsManager, servicesManager }) { return [ @@ -26,6 +27,7 @@ function getCustomizationModule({ commandsManager, servicesManager }) { ...windowLevelPresetsCustomization, ...miscCustomization, ...windowLevelActionMenuCustomization, + ...viewportActionMenuCustomizations, }, }, ]; diff --git a/platform/core/src/types/AppTypes.ts b/platform/core/src/types/AppTypes.ts index cf38365e4..826e5de13 100644 --- a/platform/core/src/types/AppTypes.ts +++ b/platform/core/src/types/AppTypes.ts @@ -129,7 +129,6 @@ declare global { showStudyList?: boolean; whiteLabeling?: Record; httpErrorHandler?: (error: Error) => void; - addWindowLevelActionMenu?: boolean; dangerouslyUseDynamicConfig?: { enabled: boolean; regex: RegExp; diff --git a/platform/docs/docs/assets/img/segmentation-overlay.png b/platform/docs/docs/assets/img/segmentation-overlay.png new file mode 100644 index 000000000..ed49baf29 Binary files /dev/null and b/platform/docs/docs/assets/img/segmentation-overlay.png differ diff --git a/platform/docs/docs/migration-guide/3p9-to-3p10/CustomizationService/index.md b/platform/docs/docs/migration-guide/3p9-to-3p10/CustomizationService/index.md index e0d3fdb0e..ad3252fc6 100644 --- a/platform/docs/docs/migration-guide/3p9-to-3p10/CustomizationService/index.md +++ b/platform/docs/docs/migration-guide/3p9-to-3p10/CustomizationService/index.md @@ -207,6 +207,86 @@ customizationService.setCustomizations({ }, }); ``` +``` +**Example: Customizing viewport action Menu** + +**Before (v3.9):** + +```javascript +// In your configuration for global customizations +window.config = { + // rest of config + addWindowLevelActionMenu: true, +}; +``` + +**After (v3.10):** + +```javascript +// you can now handle each action menu item (windowLevelActionMenu and segmentationOverlay) separately + +// cornerstone extension getCustomizationModule +function getCustomizationModule() { + return [ + { + name: 'default', + value: { + 'viewportActionMenu.windowLevelActionMenu': { + enabled: true, + location: viewportActionCornersService.LOCATIONS.topRight, + }, + 'viewportActionMenu.segmentationOverlay': { + enabled: true, + location: viewportActionCornersService.LOCATIONS.topRight, + }, + }, + }, + ]; +} + + +// Accessing customizations within your component (e.g., OHIFCornerstoneViewport.tsx) +const windowLevelActionMenu = customizationService.getCustomization('viewportActionMenu.windowLevelActionMenu'); +const segmentationOverlay = customizationService.getCustomization('viewportActionMenu.segmentationOverlay'); + +// Modifying customizations at runtime, for example, in your mode's onModeEnter +customizationService.setCustomizations({ + 'viewportActionMenu.windowLevelActionMenu': { + $set: { + enabled: false, + location: viewportActionCornersService.LOCATIONS.bottomLeft, + }, + }, + 'viewportActionMenu.segmentationOverlay': { + $set: { + enabled: true, + location: viewportActionCornersService.LOCATIONS.topLeft, + }, + }, +}); + +// Alternatively, setting global customizations via configuration +window.config = { + // rest of config + customizationService: [ + { + 'viewportActionMenu.windowLevelActionMenu': { + $set: { + enabled: false, + location: 1, + }, + }, + 'viewportActionMenu.segmentationOverlay': { + $set: { + enabled: true, + location: 1, + }, + }, + }, + ], + // rest of config +}; +``` **Note:** @@ -237,3 +317,5 @@ To keep our customization system consistent, you should be aware of a few key re | `cornerstoneOverlayTopRight` | `viewportOverlay.topRight` | Custom overlay items for the top-right corner of the viewport. | | `cornerstoneOverlayBottomLeft` | `viewportOverlay.bottomLeft` | Custom overlay items for the bottom-left corner of the viewport. | | `cornerstoneOverlayBottomRight` | `viewportOverlay.bottomRight` | Custom overlay items for the bottom-right corner of the viewport. | +| (New) | `viewportActionMenu.windowLevelActionMenu` | Controls the display and the location of the window level action menu in the viewport. | +| (New) | `viewportActionMenu.segmentationOverlay` | Controls the display and the location of segmentation overlays in the viewport. | diff --git a/platform/docs/docs/platform/services/customization-service/sampleCustomizations.tsx b/platform/docs/docs/platform/services/customization-service/sampleCustomizations.tsx index 5b3bd93f0..97060a164 100644 --- a/platform/docs/docs/platform/services/customization-service/sampleCustomizations.tsx +++ b/platform/docs/docs/platform/services/customization-service/sampleCustomizations.tsx @@ -16,6 +16,7 @@ import loadingIndicatorProgress from '../../../assets/img/loading-indicator-icon import loadingIndicatorPercent from '../../../assets/img/loading-indicator-percent.png'; import viewportActionCorners from '../../../assets/img/viewport-action-corners.png'; import contextMenu from '../../../assets/img/context-menu.jpg'; +import segmentationOverlay from '../../../assets/img/segmentation-overlay.png'; import segDisplayEditingTrue from '../../../assets/img/segDisplayEditingTrue.png'; import segDisplayEditingFalse from '../../../assets/img/segDisplayEditingFalse.png'; @@ -784,6 +785,57 @@ window.config = { }; `, }, + { + id: 'viewportActionMenu.windowLevelActionMenu', + description: + 'Configures the display and location of the window level action menu in the viewport.', + image: windowLevelActionMenu, + default: null, + configuration: ` + window.config = { + // rest of window config + customizationService: [ + { + 'viewportActionMenu.windowLevelActionMenu': { + $set: { + enabled: true, + location: 1, // Set the location of the menu in the viewport. + // 0: topLeft + // 1: topRight + // 2: bottomLeft + // 3: bottomRight + } + }, + }, + ], + }; + `, + }, + { + id: 'viewportActionMenu.segmentationOverlay', + description: 'Configures the display and location of the segmentation overlay in the viewport.', + image: segmentationOverlay, + default: null, + configuration: ` + window.config = { + // rest of window config + customizationService: [ + { + 'viewportActionMenu.segmentationOverlay': { + $set: { + enabled: true, + location: 1, // Set the location of the overlay in the viewport. + // 0: topLeft + // 1: topRight + // 2: bottomLeft + // 3: bottomRight + } + }, + }, + ], + }; + `, + }, ]; export const segmentationCustomizations = [ diff --git a/platform/docs/versioned_docs/version-3.9/configuration/configurationFiles.md b/platform/docs/versioned_docs/version-3.9/configuration/configurationFiles.md index fc0c14edc..c8dac49ba 100644 --- a/platform/docs/versioned_docs/version-3.9/configuration/configurationFiles.md +++ b/platform/docs/versioned_docs/version-3.9/configuration/configurationFiles.md @@ -192,7 +192,6 @@ if auth headers are used, a preflight request is required. - `allowMultiSelectExport`: (default to false), if set to true, the user will be able to select the datasource to export the report to. - `activateViewportBeforeInteraction`: (default to true), if set to false, tools can be used directly without the need to click and activate the viewport. - `autoPlayCine`: (default to false), if set to true, data sets with the DICOM frame time tag (i.e. (0018,1063)) will auto play when displayed -- `addWindowLevelActionMenu`: (default to true), if set to false, the window level action menu item is NOT added to the viewport action corners - `dangerouslyUseDynamicConfig`: Dynamic config allows user to pass `configUrl` query string. This allows to load config without recompiling application. If the `configUrl` query string is passed, the worklist and modes will load from the referenced json rather than the default .env config. If there is no `configUrl` path provided, the default behaviour is used and there should not be any deviation from current user experience.
Points to consider while using `dangerouslyUseDynamicConfig`:
- User have to enable this feature by setting `dangerouslyUseDynamicConfig.enabled:true`. By default it is `false`.