diff --git a/.gitignore b/.gitignore
index 50fcc92b8..c4a7cf78c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,7 @@ docker/dcm4che/dcm4che-arc
# Cypress test results
videos/
+screenshots/
# Locize settings
.locize
diff --git a/extensions/cornerstone/src/commandsModule.js b/extensions/cornerstone/src/commandsModule.js
index 49b856c81..db2627c34 100644
--- a/extensions/cornerstone/src/commandsModule.js
+++ b/extensions/cornerstone/src/commandsModule.js
@@ -2,6 +2,7 @@ import cornerstone from 'cornerstone-core';
import cornerstoneTools from 'cornerstone-tools';
import OHIF from '@ohif/core';
+import setCornerstoneLayout from './utils/setCornerstoneLayout.js';
import { getEnabledElement } from './state';
import CornerstoneViewportDownloadForm from './CornerstoneViewportDownloadForm';
const scroll = cornerstoneTools.import('util/scroll');
@@ -242,6 +243,9 @@ const commandsModule = ({ servicesManager }) => {
cornerstoneTools.removeToolState(element, toolType, tool);
cornerstone.updateImage(element);
},
+ setCornerstoneLayout: () => {
+ setCornerstoneLayout();
+ }
};
const definitions = {
@@ -348,6 +352,12 @@ const commandsModule = ({ servicesManager }) => {
storeContexts: [],
options: {},
},
+ setCornerstoneLayout: {
+ commandFn: actions.setCornerstoneLayout,
+ storeContexts: [],
+ options: {},
+ context: 'VIEWER',
+ },
};
return {
diff --git a/extensions/cornerstone/src/toolbarModule.js b/extensions/cornerstone/src/toolbarModule.js
index f32cdfbf1..283d2e926 100644
--- a/extensions/cornerstone/src/toolbarModule.js
+++ b/extensions/cornerstone/src/toolbarModule.js
@@ -237,6 +237,15 @@ const definitions = [
},
],
},
+ {
+ id: 'Exit2DMPR',
+ label: 'Exit 2D MPR',
+ icon: 'times',
+ //
+ type: TOOLBAR_BUTTON_TYPES.COMMAND,
+ commandName: 'setCornerstoneLayout',
+ context: 'ACTIVE_VIEWPORT::VTK',
+ }
];
export default {
diff --git a/extensions/cornerstone/src/utils/setCornerstoneLayout.js b/extensions/cornerstone/src/utils/setCornerstoneLayout.js
new file mode 100644
index 000000000..e308c0d4a
--- /dev/null
+++ b/extensions/cornerstone/src/utils/setCornerstoneLayout.js
@@ -0,0 +1,22 @@
+import { redux } from '@ohif/core';
+
+const { setLayout } = redux.actions;
+
+/**
+ * Update the current layout with a simple Cornerstone one
+ *
+ * @return void
+ */
+const setCornerstoneLayout = () => {
+ const layout = {
+ numRows: 1,
+ numColumns: 1,
+ viewports: [{ plugin: 'cornerstone' }],
+ };
+
+ const action = setLayout(layout);
+
+ window.store.dispatch(action);
+}
+
+export default setCornerstoneLayout;
diff --git a/extensions/vtk/src/toolbarComponents/VTKMPRToolbarButton.js b/extensions/vtk/src/toolbarComponents/VTKMPRToolbarButton.js
new file mode 100644
index 000000000..7ec4758c5
--- /dev/null
+++ b/extensions/vtk/src/toolbarComponents/VTKMPRToolbarButton.js
@@ -0,0 +1,87 @@
+import React from 'react';
+import { useSelector } from 'react-redux';
+import PropTypes from 'prop-types';
+import { ToolbarButton } from '@ohif/ui';
+import { utils } from '@ohif/core';
+
+const { studyMetadataManager } = utils;
+
+let isVisible = true;
+
+const _isDisplaySetReconstructable = (viewportSpecificData = {}, activeViewportIndex) => {
+ if (!viewportSpecificData[activeViewportIndex]) {
+ return false;
+ };
+
+ const { displaySetInstanceUid, studyInstanceUid } = viewportSpecificData[
+ activeViewportIndex
+ ];
+
+ const studies = studyMetadataManager.all();
+
+ const study = studies.find(
+ study => study.studyInstanceUID === studyInstanceUid
+ );
+
+ if (!study) {
+ return false;
+ }
+
+ const displaySet = study._displaySets.find(set => set.displaySetInstanceUid === displaySetInstanceUid);
+
+ if (!displaySet) {
+ return false;
+ };
+
+ return displaySet.isReconstructable;
+};
+
+function VTKMPRToolbarButton({
+ parentContext,
+ toolbarClickCallback,
+ button,
+ activeButtons,
+ isActive,
+ className,
+}) {
+ const { id, label, icon } = button;
+ const { viewportSpecificData, activeViewportIndex } = useSelector(state => {
+ const { viewports = {} } = state;
+ const { viewportSpecificData, activeViewportIndex } = viewports;
+
+ return {
+ viewportSpecificData,
+ activeViewportIndex,
+ }
+ });
+
+ isVisible = _isDisplaySetReconstructable(
+ viewportSpecificData,
+ activeViewportIndex,
+ );
+
+ return (
+
+ {isVisible && (
+ toolbarClickCallback(button, evt)}
+ isActive={isActive}
+ />
+ )}
+
+ );
+}
+
+VTKMPRToolbarButton.propTypes = {
+ parentContext: PropTypes.object.isRequired,
+ toolbarClickCallback: PropTypes.func.isRequired,
+ button: PropTypes.object.isRequired,
+ activeButtons: PropTypes.array.isRequired,
+ isActive: PropTypes.bool,
+ className: PropTypes.string,
+};
+
+export default VTKMPRToolbarButton;
diff --git a/extensions/vtk/src/toolbarModule.js b/extensions/vtk/src/toolbarModule.js
index 805644dd2..9cfa49f63 100644
--- a/extensions/vtk/src/toolbarModule.js
+++ b/extensions/vtk/src/toolbarModule.js
@@ -1,4 +1,5 @@
import SlabThicknessToolbarComponent from './toolbarComponents/SlabThicknessToolbarComponent';
+import VTKMPRToolbarButton from './toolbarComponents/VTKMPRToolbarButton';
const TOOLBAR_BUTTON_TYPES = {
COMMAND: 'command',
@@ -115,9 +116,19 @@ const definitions = [
},
],
},
+ {
+ id: '2DMPR',
+ label: '2D MPR',
+ icon: 'cube',
+ //
+ CustomComponent: VTKMPRToolbarButton,
+ type: TOOLBAR_BUTTON_TYPES.COMMAND,
+ commandName: 'mpr2d',
+ context: 'ACTIVE_VIEWPORT::CORNERSTONE',
+ },
];
export default {
definitions,
defaultContext: 'ACTIVE_VIEWPORT::VTK',
-};
+}
diff --git a/platform/core/src/extensions/ExtensionManager.test.js b/platform/core/src/extensions/ExtensionManager.test.js
index 5ed3fad8d..42965c4a1 100644
--- a/platform/core/src/extensions/ExtensionManager.test.js
+++ b/platform/core/src/extensions/ExtensionManager.test.js
@@ -226,7 +226,7 @@ describe('ExtensionManager.js', () => {
return {
definitions: {
exampleDefinition: {
- commandFn: () => {},
+ commandFn: () => { },
storeContexts: [],
options: {},
},
diff --git a/platform/viewer/cypress/support/aliases.js b/platform/viewer/cypress/support/aliases.js
index 84aadbd1c..0547e99d1 100644
--- a/platform/viewer/cypress/support/aliases.js
+++ b/platform/viewer/cypress/support/aliases.js
@@ -44,6 +44,7 @@ export function initRouteAliases() {
//Creating aliases for VTK tools buttons
export function initVTKToolsAliases() {
+ cy.get('[data-cy="exit 2d mpr"]').as('exit2dmprBtn');
cy.get('[data-cy="crosshairs"]').as('crosshairsBtn');
cy.get('[data-cy="wwwc"]').as('wwwcBtn');
cy.get('[data-cy="rotate"]').as('rotateBtn');
diff --git a/platform/viewer/src/connectedComponents/ConnectedPluginSwitch.js b/platform/viewer/src/connectedComponents/ConnectedPluginSwitch.js
deleted file mode 100644
index 130bd0eba..000000000
--- a/platform/viewer/src/connectedComponents/ConnectedPluginSwitch.js
+++ /dev/null
@@ -1,72 +0,0 @@
-import OHIF from '@ohif/core';
-import React from 'react';
-import PluginSwitch from './PluginSwitch.js';
-import { commandsManager } from './../App.js';
-import { connect } from 'react-redux';
-
-const { setLayout } = OHIF.redux.actions;
-
-const ConnectedPluginSwitch = props => {
- return ;
-};
-
-const mapStateToProps = state => {
- const { activeViewportIndex, layout, viewportSpecificData } = state.viewports;
-
- return {
- activeViewportIndex,
- viewportSpecificData,
- layout,
- };
-};
-
-const mapDispatchToProps = dispatch => {
- return {
- setLayout: data => {
- dispatch(setLayout(data));
- },
- };
-};
-
-/*function setSingleLayoutData(originalArray, viewportIndex, data) {
- const viewports = originalArray.slice();
- const layoutData = Object.assign({}, viewports[viewportIndex], data);
-
- viewports[viewportIndex] = layoutData;
-
- return viewports;
-}*/
-
-const mergeProps = (propsFromState, propsFromDispatch, ownProps) => {
- const { activeViewportIndex, viewportSpecificData } = propsFromState;
- const { studies } = ownProps;
- const { setLayout } = propsFromDispatch;
-
- const mpr = () => {
- commandsManager.runCommand('mpr2d');
- };
-
- const exitMpr = () => {
- const layout = {
- numRows: 1,
- numColumns: 1,
- viewports: [{ plugin: 'cornerstone' }],
- };
-
- setLayout(layout);
- };
-
- return {
- mpr,
- exitMpr,
- activeViewportIndex,
- viewportSpecificData,
- studies,
- };
-};
-
-export default connect(
- mapStateToProps,
- mapDispatchToProps,
- mergeProps
-)(ConnectedPluginSwitch);
diff --git a/platform/viewer/src/connectedComponents/PluginSwitch.css b/platform/viewer/src/connectedComponents/PluginSwitch.css
deleted file mode 100644
index 1f61645d5..000000000
--- a/platform/viewer/src/connectedComponents/PluginSwitch.css
+++ /dev/null
@@ -1,3 +0,0 @@
-.icon-rotate-120 {
- transform: rotate(120deg);
-}
diff --git a/platform/viewer/src/connectedComponents/PluginSwitch.js b/platform/viewer/src/connectedComponents/PluginSwitch.js
deleted file mode 100644
index 12074625e..000000000
--- a/platform/viewer/src/connectedComponents/PluginSwitch.js
+++ /dev/null
@@ -1,106 +0,0 @@
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import { ToolbarButton } from '@ohif/ui';
-import './PluginSwitch.css';
-
-class PluginSwitch extends Component {
- static propTypes = {
- mpr: PropTypes.func,
- activeViewportIndex: PropTypes.number,
- viewportSpecificData: PropTypes.object,
- studies: PropTypes.array,
- exitMpr: PropTypes.func,
- };
-
- static defaultProps = {};
- constructor(props) {
- super(props);
- this.state = {
- isPlugSwitchOn: false,
- label: '2D MPR',
- icon: 'cube',
- };
- }
-
- handleClick = () => {
- if (this.state.isPlugSwitchOn) {
- this.setState({
- isPlugSwitchOn: false,
- label: '2D MPR',
- icon: 'cube',
- });
- this.props.exitMpr();
- } else {
- this.setState({
- isPlugSwitchOn: true,
- label: 'Exit 2D MPR',
- icon: 'times',
- });
- this.props.mpr();
- }
- };
-
- render() {
- const { label, icon } = this.state;
-
- // Render exit mpr if switched on, otherwise check if mpr button should be displayed.
- const shouldRender =
- this.state.isPlugSwitchOn || _shouldRenderMpr2DButton.call(this);
-
- return (
- <>
- {shouldRender && (
-
-
-
- )}
- >
- );
- }
-}
-
-function _shouldRenderMpr2DButton() {
- const { viewportSpecificData, studies, activeViewportIndex } = this.props;
-
- if (!viewportSpecificData[activeViewportIndex]) {
- return;
- }
-
- const { displaySetInstanceUid, studyInstanceUid } = viewportSpecificData[
- activeViewportIndex
- ];
-
- const displaySet = _getDisplaySet(
- studies,
- studyInstanceUid,
- displaySetInstanceUid
- );
-
- if (!displaySet) {
- return;
- }
-
- return displaySet.isReconstructable;
-}
-
-function _getDisplaySet(studies, studyInstanceUid, displaySetInstanceUid) {
- const study = studies.find(
- study => study.studyInstanceUid === studyInstanceUid
- );
-
- if (!study) {
- return;
- }
-
- const displaySet = study.displaySets.find(set => {
- return set.displaySetInstanceUid === displaySetInstanceUid;
- });
-
- return displaySet;
-}
-
-export default PluginSwitch;
diff --git a/platform/viewer/src/connectedComponents/ToolbarRow.js b/platform/viewer/src/connectedComponents/ToolbarRow.js
index 4957d7148..29ba3798f 100644
--- a/platform/viewer/src/connectedComponents/ToolbarRow.js
+++ b/platform/viewer/src/connectedComponents/ToolbarRow.js
@@ -16,7 +16,6 @@ import { commandsManager, extensionManager } from './../App.js';
import ConnectedCineDialog from './ConnectedCineDialog';
import ConnectedLayoutButton from './ConnectedLayoutButton';
-import ConnectedPluginSwitch from './ConnectedPluginSwitch.js';
class ToolbarRow extends Component {
// TODO: Simplify these? isOpen can be computed if we say "any" value for selected,
@@ -124,7 +123,6 @@ class ToolbarRow extends Component {
{buttonComponents}
-