From 6a3e0db53ee8e2623a2cd0ae888932a736321fb9 Mon Sep 17 00:00:00 2001 From: "James A. Petts" Date: Tue, 19 May 2020 10:01:44 +0100 Subject: [PATCH 1/2] ToolBarService --- extensions/default/src/ViewerLayout/index.jsx | 45 ++++++++------- modes/example/src/index.js | 8 ++- platform/core/src/ToolBarManager.js | 46 ---------------- platform/core/src/index.js | 6 +- .../DisplaySetService/DisplaySetService.js | 6 +- .../services/ToolBarService/ToolBarService.js | 25 ++++++++- .../core/src/services/ToolBarService/index.js | 8 +++ platform/core/src/services/index.js | 2 + platform/viewer/src/appInit.js | 2 + .../src/routes/Mode/DisplaySetCreator.jsx | 55 ------------------- platform/viewer/src/routes/Mode/Mode.jsx | 6 +- 11 files changed, 73 insertions(+), 136 deletions(-) delete mode 100644 platform/core/src/ToolBarManager.js create mode 100644 platform/core/src/services/ToolBarService/index.js delete mode 100644 platform/viewer/src/routes/Mode/DisplaySetCreator.jsx diff --git a/extensions/default/src/ViewerLayout/index.jsx b/extensions/default/src/ViewerLayout/index.jsx index 83f8c2475..6896ad3e3 100644 --- a/extensions/default/src/ViewerLayout/index.jsx +++ b/extensions/default/src/ViewerLayout/index.jsx @@ -28,12 +28,9 @@ function ViewerLayout({ rightPanels, viewports, children, - ViewportGridComp + ViewportGridComp, }) { - const [displaySets, setDisplaySets] = useState({}); - - //const ViewportGrid = React.Children.only(children); - + const { ToolBarService } = servicesManager.services; /** * Set body classes (tailwindcss) that don't allow vertical * or horizontal overflow (no scrolling). Also guarantee window @@ -47,6 +44,7 @@ function ViewerLayout({ document.body.classList.remove('overflow-hidden'); }; }, []); + const getPanelData = id => { const entry = extensionManager.getModuleEntry(id); // TODO, not sure why sidepanel content has to be JSX, and not a children prop? @@ -70,27 +68,34 @@ function ViewerLayout({ }; }; + const handleToolBarSubscription = newToolBarLayout => { + debugger; + setToolBarLayout(newToolBarLayout); + }; + + const [toolBarLayout, setToolBarLayout] = useState([ + { tools: [], moreTools: [] }, + { tools: [] }, + ]); + + useEffect(() => { + const { unsubscribe } = ToolBarService.subscribe( + ToolBarService.EVENTS.TOOL_BAR_MODIFIED, + handleToolBarSubscription + ); + + return unsubscribe; + }, []); + const leftPanelComponents = leftPanels.map(getPanelData); const rightPanelComponents = rightPanels.map(getPanelData); const viewportComponents = viewports.map(getViewportComponentData); - // let { toolBarLayout } = useToolbarLayout(); - // if (!toolBarLayout.length) { - // return null; - // } - - // TODO -> make toolbar service - const toolBarLayout = { tools: [], moreTools: [] }; - - //const [primaryToolBarLayout, secondaryToolBarLayout] = toolBarLayout; - const primaryToolBarLayout = toolBarLayout; - const secondaryToolBarLayout = toolBarLayout; - return (
- +
{ - toolBarManager.addButtons([ + init: ({ servicesManager, extensionManager }) => { + const { ToolBarService } = servicesManager.services; + ToolBarService.init(extensionManager); + ToolBarService.addButtons([ { id: 'Zoom', namespace: 'org.ohif.cornerstone.toolbarModule.Zoom', @@ -53,7 +55,7 @@ export default function mode({ modeConfiguration }) { ]); // Could import layout selector here from org.ohif.default (when it exists!) - toolBarManager.setToolBarLayout([ + ToolBarService.setToolBarLayout([ // Primary { tools: ['Zoom', 'Levels', 'Pan', 'Capture', 'Layout'], diff --git a/platform/core/src/ToolBarManager.js b/platform/core/src/ToolBarManager.js deleted file mode 100644 index 0c585d8d0..000000000 --- a/platform/core/src/ToolBarManager.js +++ /dev/null @@ -1,46 +0,0 @@ -export default class toolBarManager { - constructor(extensionManager) { - this.buttons = {}; - this.extensionManager = extensionManager; - } - - addButtons(buttons) { - buttons.forEach(button => { - const buttonDefinition = this.extensionManager.getModuleEntry( - button.namespace - ); - - const id = button.id || buttonDefinition.id; - - this.buttons[id] = buttonDefinition; - }); - } - - setToolBarLayout(layouts) { - const toolBarLayout = []; - - layouts.forEach(layout => { - const toolBarDefinitions = { tools: [], moreTools: [] }; - - const { tools, moreTools } = layout; - - tools && - tools.forEach(element => { - const button = this.buttons[element]; - - toolBarDefinitions.tools.push(button); - }); - - moreTools && - moreTools.forEach(element => { - const button = this.buttons[element]; - - toolBarDefinitions.moreTools.push(button); - }); - - toolBarLayout.push(toolBarDefinitions); - }); - - // TODO -> Change this to a service. => emit an event to subscribers to update the toolbar layout. - } -} diff --git a/platform/core/src/index.js b/platform/core/src/index.js index a00f76d99..57705c424 100644 --- a/platform/core/src/index.js +++ b/platform/core/src/index.js @@ -20,7 +20,6 @@ import studies from './studies/'; import ui from './ui'; import user from './user.js'; import dicomMetadataStore from './dicomMetadataStore'; -import ToolBarManager from './ToolBarManager'; import { ViewModelProvider, useViewModel } from './ViewModelContext'; import { ToolbarLayoutProvider, @@ -35,6 +34,7 @@ import { UINotificationService, UIViewportDialogService, DisplaySetService, + ToolBarSerivce, } from './services'; import IWebApiDataSource from './DataSources/IWebApiDataSource'; @@ -73,10 +73,10 @@ const OHIF = { UIViewportDialogService, DisplaySetService, MeasurementService, + ToolBarSerivce, IWebApiDataSource, dicomMetadataStore, // - ToolBarManager, ViewModelProvider, useViewModel, }; @@ -114,9 +114,9 @@ export { UIViewportDialogService, DisplaySetService, MeasurementService, + ToolBarSerivce, IWebApiDataSource, dicomMetadataStore, - ToolBarManager, ViewModelProvider, useViewModel, ToolbarLayoutProvider, diff --git a/platform/core/src/services/DisplaySetService/DisplaySetService.js b/platform/core/src/services/DisplaySetService/DisplaySetService.js index 231c16c67..ceb69aa00 100644 --- a/platform/core/src/services/DisplaySetService/DisplaySetService.js +++ b/platform/core/src/services/DisplaySetService/DisplaySetService.js @@ -98,9 +98,9 @@ export default class DisplaySetService { displaySetsAdded = displaySets; } - // If array of array of instances - - this._broadcastChange(EVENTS.DISPLAY_SETS_ADDED, displaySetsAdded); + if (displaySetsAdded.length) { + this._broadcastChange(EVENTS.DISPLAY_SETS_ADDED, displaySetsAdded); + } }; makeDisplaySetForInstances(instances) { diff --git a/platform/core/src/services/ToolBarService/ToolBarService.js b/platform/core/src/services/ToolBarService/ToolBarService.js index 00650b066..0eb0056ef 100644 --- a/platform/core/src/services/ToolBarService/ToolBarService.js +++ b/platform/core/src/services/ToolBarService/ToolBarService.js @@ -1,5 +1,9 @@ import pubSubServiceInterface from '../pubSubServiceInterface'; +const EVENTS = { + TOOL_BAR_MODIFIED: 'event::toolBarService:toolBarModified', +}; + export default class ToolBarService { constructor() { this.displaySets = {}; @@ -26,6 +30,23 @@ export default class ToolBarService { }); } + /** + * Broadcasts displaySetService changes. + * + * @param {string} eventName The event name + * @return void + */ + _broadcastChange = (eventName, callbackProps) => { + const hasListeners = Object.keys(this.listeners).length > 0; + const hasCallbacks = Array.isArray(this.listeners[eventName]); + + if (hasListeners && hasCallbacks) { + this.listeners[eventName].forEach(listener => { + listener.callback(callbackProps); + }); + } + }; + setToolBarLayout(layouts) { const toolBarLayout = []; @@ -51,6 +72,8 @@ export default class ToolBarService { toolBarLayout.push(toolBarDefinitions); }); - // TODO -> Change this to a service. => emit an event to subscribers to update the toolbar layout. + this.toolBarLayout = toolBarLayout; + + this._broadcastChange(this.EVENTS.TOOL_BAR_MODIFIED, toolBarLayout); } } diff --git a/platform/core/src/services/ToolBarService/index.js b/platform/core/src/services/ToolBarService/index.js new file mode 100644 index 000000000..bbe3dd317 --- /dev/null +++ b/platform/core/src/services/ToolBarService/index.js @@ -0,0 +1,8 @@ +import ToolBarService from './ToolBarService'; + +export default { + name: 'ToolBarService', + create: ({ configuration = {} }) => { + return new ToolBarService(); + }, +}; diff --git a/platform/core/src/services/index.js b/platform/core/src/services/index.js index 6487ed1c3..70eadd0d7 100644 --- a/platform/core/src/services/index.js +++ b/platform/core/src/services/index.js @@ -5,6 +5,7 @@ import UIModalService from './UIModalService'; import UINotificationService from './UINotificationService'; import UIViewportDialogService from './UIViewportDialogService'; import DisplaySetService from './DisplaySetService'; +import ToolBarSerivce from './ToolBarService'; export { MeasurementService, @@ -14,4 +15,5 @@ export { UINotificationService, UIViewportDialogService, DisplaySetService, + ToolBarSerivce, }; diff --git a/platform/viewer/src/appInit.js b/platform/viewer/src/appInit.js index e47abcce5..62fcf2c51 100644 --- a/platform/viewer/src/appInit.js +++ b/platform/viewer/src/appInit.js @@ -8,6 +8,7 @@ import { UIDialogService, MeasurementService, DisplaySetService, + ToolBarSerivce, // utils, // redux as reduxOHIF, } from '@ohif/core'; @@ -45,6 +46,7 @@ function appInit(appConfigOrFunc, defaultExtensions) { UIDialogService, MeasurementService, DisplaySetService, + ToolBarSerivce, ]); /** diff --git a/platform/viewer/src/routes/Mode/DisplaySetCreator.jsx b/platform/viewer/src/routes/Mode/DisplaySetCreator.jsx deleted file mode 100644 index 19b24647b..000000000 --- a/platform/viewer/src/routes/Mode/DisplaySetCreator.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import { useEffect, useCallback } from 'react'; -import { - displaySetManager, - ToolBarManager, - useViewModel, - useToolbarLayout, -} from '@ohif/core'; - -export default function DisplaySetCreator({ - location, - mode, - dataSourceName, - extensionManager, - DisplaySetService, -}) { - console.warn('DisplaySetCreator rerendering'); - const { routes, sopClassHandlers } = mode; - const dataSources = extensionManager.getDataSources(dataSourceName); - // TODO: For now assume one unique datasource. - - const dataSource = dataSources[0]; - const route = routes[0]; - - // Add toolbar state to the view model context? - const { displaySetInstanceUIDs, setDisplaySetInstanceUIDs } = useViewModel(); - - const { toolBarLayout, setToolBarLayout } = useToolbarLayout(); - - useEffect(() => { - let toolBarManager = new ToolBarManager(extensionManager, setToolBarLayout); - route.init({ toolBarManager }); - }, [mode, dataSourceName, location]); - - const createDisplaySets = useCallback(() => { - // Add SOPClassHandlers to a new SOPClassManager. - displaySetManager.init(extensionManager, sopClassHandlers, { - displaySetInstanceUIDs, - setDisplaySetInstanceUIDs, - }); - - const queryParams = location.search; - - // Call the data source to start building the view model? - dataSource.retrieve.series.metadata( - queryParams, - displaySetManager.makeDisplaySets - ); - }, [displaySetInstanceUIDs, location]); - - useEffect(() => { - createDisplaySets(); - }, [mode, dataSourceName, location]); - - return null; -} diff --git a/platform/viewer/src/routes/Mode/Mode.jsx b/platform/viewer/src/routes/Mode/Mode.jsx index b8499f682..9b6a1d583 100644 --- a/platform/viewer/src/routes/Mode/Mode.jsx +++ b/platform/viewer/src/routes/Mode/Mode.jsx @@ -1,12 +1,10 @@ import React, { useEffect, useCallback } from 'react'; import PropTypes from 'prop-types'; // -import { ToolBarManager } from '@ohif/core'; import { DragAndDropProvider } from '@ohif/ui'; // import ViewportGrid from '@components/ViewportGrid'; import Compose from './Compose'; -//import DisplaySetCreator from './DisplaySetCreator'; export default function ModeRoute({ location, @@ -58,9 +56,7 @@ export default function ModeRoute({ } useEffect(() => { - // TODO -> Make this into a service - let toolBarManager = new ToolBarManager(extensionManager); //, setToolBarLayout); - route.init({ toolBarManager }); + route.init({ servicesManager, extensionManager }); }, [mode, dataSourceName, location]); const createDisplaySets = useCallback(() => { From f7ae544716d9000f536423701864b8d18cdc5892 Mon Sep 17 00:00:00 2001 From: "James A. Petts" Date: Tue, 19 May 2020 13:01:39 +0100 Subject: [PATCH 2/2] WIP, wrap command manager controls so we can pass these to the UI component. --- extensions/default/src/ViewerLayout/index.jsx | 74 +++++++++++++------ .../default/src/getLayoutTemplateModule.js | 13 +++- platform/core/src/ToolbarLayoutContext.js | 39 ---------- platform/core/src/index.js | 6 -- 4 files changed, 62 insertions(+), 70 deletions(-) delete mode 100644 platform/core/src/ToolbarLayoutContext.js diff --git a/extensions/default/src/ViewerLayout/index.jsx b/extensions/default/src/ViewerLayout/index.jsx index 6896ad3e3..caba65b44 100644 --- a/extensions/default/src/ViewerLayout/index.jsx +++ b/extensions/default/src/ViewerLayout/index.jsx @@ -1,28 +1,14 @@ import React, { useEffect, useState, useCallback } from 'react'; import PropTypes from 'prop-types'; import { SidePanel, Toolbar } from '@ohif/ui'; -import { useToolbarLayout } from '@ohif/core'; // import Header from './Header.jsx'; -import { displaySetManager } from '@ohif/core'; - -// function ViewportDataCreator({ setViewportData }) { -// const { displaySetInstanceUIDs } = useViewModel(); -// console.log(displaySetInstanceUIDs); - -// useEffect(() => { -// setViewportData([ -// displaySetManager.getDisplaySetByUID(displaySetInstanceUIDs[0]), -// ]); -// }, [displaySetInstanceUIDs, setViewportData]); - -// return null; -// } function ViewerLayout({ // From Extension Module Params extensionManager, servicesManager, + commandsManager, // From Modes leftPanels, rightPanels, @@ -69,8 +55,56 @@ function ViewerLayout({ }; const handleToolBarSubscription = newToolBarLayout => { + // Get buttons to pass to toolbars. + console.log(commandsManager); + + const firstTool = newToolBarLayout[0].tools[0]; + + const toolBarLayout = []; + + newToolBarLayout.forEach(newToolBar => { + const toolBar = { tools: [], moreTools: [] }; + + Object.keys(newToolBar).forEach(key => { + if (newToolBar[key].length) { + newToolBar[key].forEach(tool => { + const commandOptions = tool.commandOptions || {}; + + toolBar[key].push({ + context: tool.context, + icon: tool.icon, + id: tool.id, + label: tool.label, + type: 'setToolActive', + onClick: () => + commandsManager.runCommand(tool.commandName, commandOptions), + }); + }); + } + }); + + toolBarLayout.push(toolBar); + + // if (newToolBar.moreTools && newToolBar.moreTools.length) { + // newToolBar.moreTools.forEach(tool => { + // const commandOptions = tool.commandOptions || {}; + + // toolBar.push({ + // context: tool.context, + // icon: tool.icon, + // id: tool.id, + // label: tool.label, + // type: 'setToolActive', + // command: () => + // commandsManager.runCommand(tool.commandName, commandOptions), + // }); + // }); + // } + }); + debugger; - setToolBarLayout(newToolBarLayout); + + setToolBarLayout(toolBarLayout); }; const [toolBarLayout, setToolBarLayout] = useState([ @@ -188,6 +222,7 @@ ViewerLayout.propTypes = { extensionManager: PropTypes.shape({ getModuleEntry: PropTypes.func.isRequired, }).isRequired, + commandsManager: PropTypes.object, // From modes // TODO: Not in love with this shape, toolBarLayout: PropTypes.arrayOf( @@ -203,11 +238,4 @@ ViewerLayout.propTypes = { children: PropTypes.oneOfType(PropTypes.node, PropTypes.func).isRequired, }; -ViewerLayout.defaultProps = { - toolBarLayout: [ - { tools: [], moreTools: [] }, - { tools: [], moreTools: [] }, - ], -}; - export default ViewerLayout; diff --git a/extensions/default/src/getLayoutTemplateModule.js b/extensions/default/src/getLayoutTemplateModule.js index 595f83474..e70d961c1 100644 --- a/extensions/default/src/getLayoutTemplateModule.js +++ b/extensions/default/src/getLayoutTemplateModule.js @@ -5,9 +5,18 @@ import ViewerLayout from './ViewerLayout'; - Init layout based on the displaySets and the objects. */ -export default function({ servicesManager, extensionManager }) { +export default function({ + servicesManager, + extensionManager, + commandsManager, +}) { function ViewerLayoutWithServices(props) { - return ViewerLayout({ servicesManager, extensionManager, ...props }); + return ViewerLayout({ + servicesManager, + extensionManager, + commandsManager, + ...props, + }); } return [ diff --git a/platform/core/src/ToolbarLayoutContext.js b/platform/core/src/ToolbarLayoutContext.js deleted file mode 100644 index c88419831..000000000 --- a/platform/core/src/ToolbarLayoutContext.js +++ /dev/null @@ -1,39 +0,0 @@ -import React, { Component, useContext } from 'react'; - -/// TODO MAKE THIS PRETTY DANNY - -const ToolbarLayoutContext = React.createContext({ - toolBarLayout: [], - setToolBarLayout: () => {}, -}); - -ToolbarLayoutContext.displayName = 'ToolbarLayoutContext'; - -class ToolbarLayoutProvider extends Component { - state = { - toolBarLayout: [], - }; - - render() { - const setToolBarLayout = toolBarLayout => { - this.setState({ toolBarLayout }); - }; - - return ( - - {this.props.children} - - ); - } -} - -const useToolbarLayout = () => useContext(ToolbarLayoutContext); - -export default ToolbarLayoutContext; - -export { ToolbarLayoutProvider, useToolbarLayout }; diff --git a/platform/core/src/index.js b/platform/core/src/index.js index 57705c424..09067349b 100644 --- a/platform/core/src/index.js +++ b/platform/core/src/index.js @@ -21,10 +21,6 @@ import ui from './ui'; import user from './user.js'; import dicomMetadataStore from './dicomMetadataStore'; import { ViewModelProvider, useViewModel } from './ViewModelContext'; -import { - ToolbarLayoutProvider, - useToolbarLayout, -} from './ToolbarLayoutContext'; import utils, { hotkeys } from './utils/'; import { @@ -119,8 +115,6 @@ export { dicomMetadataStore, ViewModelProvider, useViewModel, - ToolbarLayoutProvider, - useToolbarLayout, }; export { OHIF };