Fix/update branch cherry pick (#2039)
* Update all the icons * Turn off global tool sync; watch as the world burns * Shift a bunch of things around so we can start tracking/setting per element * ToolBarService to initiate one of three different calls; callback passed to all button types * SplitButton and Toolbar Button to use new `onInteraction` prop and new toolbar state * Changes to toolbar button interface and config * Fix broken layout selector * Update SR viewport to activate tools in viewport component * Duplicate activation logic in measurement tracking extension * Add alternative/dashed variants for SR Viewport * pass through "setToolActive" commands for other viewport types * fix small overlay bugs (no wwwc or scale info) * Show SpacingBetweenSlices instead of PixelSpacing in patient information dialog * Fix prop-types * Update tracked viewport to have alternative tracked styling * alt styling for SR viewports * Update to support isLocked + isRehydratable * fix broken logic * fix broken logic * switch icon style * fix icon styles * hover and click to start flow * expedited workflow when data is not dirty (just after SR hydration) * fix: setting elliptical roi tool * fix arrow annotate dialog * fix: do not show learn more button for now * remove dead code * simpler cache invalidation * simpler cache invalidation part 2 * Fix for unable to spand study cards on separate pages
This commit is contained in:
1 parent
365ba2fa72
commit
cae54b0779
78 files changed
+1678
-1263
No files matched your search
@@ -13,7 +13,9 @@ const DEFAULT_SIZE = 512;
|
||||
const MAX_TEXTURE_SIZE = 10000;
|
||||
|
||||
const CornerstoneViewportDownloadForm = ({ onClose, activeViewportIndex }) => {
|
||||
const activeEnabledElement = getEnabledElement(activeViewportIndex);
|
||||
const { enabledElement: activeEnabledElement } = getEnabledElement(
|
||||
activeViewportIndex
|
||||
);
|
||||
|
||||
const enableViewport = viewportElement => {
|
||||
if (viewportElement) {
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import React, { Component } from 'react';
|
||||
import CornerstoneViewport from 'react-cornerstone-viewport';
|
||||
import OHIF from '@ohif/core';
|
||||
import csTools from 'cornerstone-tools';
|
||||
import PropTypes from 'prop-types';
|
||||
import debounce from 'lodash.debounce';
|
||||
import getTools from './utils/getTools.js';
|
||||
import setActiveAndPassiveToolsForElement from './utils/setActiveAndPassiveToolsForElement';
|
||||
|
||||
import { setEnabledElement } from './state';
|
||||
|
||||
@@ -23,6 +26,7 @@ class OHIFCornerstoneViewport extends Component {
|
||||
dataSource: PropTypes.object,
|
||||
children: PropTypes.node,
|
||||
customProps: PropTypes.object,
|
||||
ToolBarService: PropTypes.object,
|
||||
};
|
||||
|
||||
static name = 'OHIFCornerstoneViewport';
|
||||
@@ -131,7 +135,7 @@ class OHIFCornerstoneViewport extends Component {
|
||||
|
||||
if (
|
||||
displaySet.displaySetInstanceUID !==
|
||||
prevDisplaySet.displaySetInstanceUID ||
|
||||
prevDisplaySet.displaySetInstanceUID ||
|
||||
displaySet.SOPInstanceUID !== prevDisplaySet.SOPInstanceUID ||
|
||||
displaySet.imageIndex !== prevDisplaySet.imageIndex
|
||||
) {
|
||||
@@ -203,7 +207,14 @@ class OHIFCornerstoneViewport extends Component {
|
||||
// Need to expose viewportGrid as a "UI Service"
|
||||
onElementEnabled={evt => {
|
||||
const enabledElement = evt.detail.element;
|
||||
const tools = getTools();
|
||||
const toolAlias = ToolBarService.state.primaryToolId;
|
||||
|
||||
setEnabledElement(viewportIndex, enabledElement);
|
||||
setActiveAndPassiveToolsForElement(enabledElement, tools);
|
||||
csTools.setToolActiveForElement(enabledElement, toolAlias, {
|
||||
mouseButtonMask: 1,
|
||||
});
|
||||
}}
|
||||
// Sync resize throttle w/ sidepanel animation duration to prevent
|
||||
// seizure inducing strobe blinking effect
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import React from 'react';
|
||||
import { Input, Dialog } from '@ohif/ui';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} data
|
||||
* @param {*} data.text
|
||||
* @param {*} data.label
|
||||
* @param {*} event
|
||||
* @param {*} callback
|
||||
* @param {*} isArrowAnnotateInputDialog
|
||||
*/
|
||||
function callInputDialog(
|
||||
UIDialogService,
|
||||
data,
|
||||
callback,
|
||||
isArrowAnnotateInputDialog = true
|
||||
) {
|
||||
const dialogId = 'enter-annotation';
|
||||
const label = data
|
||||
? isArrowAnnotateInputDialog
|
||||
? data.text
|
||||
: data.label
|
||||
: '';
|
||||
|
||||
const onSubmitHandler = ({ action, value }) => {
|
||||
switch (action.id) {
|
||||
case 'save':
|
||||
callback(value.label, action.id);
|
||||
break;
|
||||
case 'cancel':
|
||||
callback('', action.id);
|
||||
break;
|
||||
}
|
||||
UIDialogService.dismiss({ id: dialogId });
|
||||
};
|
||||
|
||||
if (UIDialogService) {
|
||||
UIDialogService.create({
|
||||
id: dialogId,
|
||||
centralize: true,
|
||||
isDraggable: false,
|
||||
showOverlay: true,
|
||||
content: Dialog,
|
||||
contentProps: {
|
||||
title: 'Enter your annotation',
|
||||
value: { label },
|
||||
noCloseButton: true,
|
||||
onClose: () => UIDialogService.dismiss({ id: dialogId }),
|
||||
actions: [
|
||||
{ id: 'cancel', text: 'Cancel', type: 'secondary' },
|
||||
{ id: 'save', text: 'Save', type: 'primary' },
|
||||
],
|
||||
onSubmit: onSubmitHandler,
|
||||
body: ({ value, setValue }) => {
|
||||
return (
|
||||
<div className="p-4 bg-primary-dark">
|
||||
<Input
|
||||
autoFocus
|
||||
className="mt-2 bg-black border-primary-main"
|
||||
type="text"
|
||||
containerClassName="mr-2"
|
||||
value={value.label}
|
||||
onChange={event => {
|
||||
event.persist();
|
||||
setValue(value => ({ ...value, label: event.target.value }));
|
||||
}}
|
||||
onKeyPress={event => {
|
||||
if (event.key === 'Enter') {
|
||||
onSubmitHandler({ value, action: { id: 'save' } });
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default callInputDialog;
|
||||
@@ -10,12 +10,13 @@ const scroll = cornerstoneTools.import('util/scroll');
|
||||
const { studyMetadataManager } = OHIF.utils;
|
||||
const { setViewportSpecificData } = OHIF.redux.actions;
|
||||
|
||||
const commandsModule = ({ servicesManager }) => {
|
||||
const commandsModule = ({ servicesManager, commandsManager }) => {
|
||||
const { ViewportGridService } = servicesManager.services;
|
||||
|
||||
function _getActiveViewportsEnabledElement() {
|
||||
const { activeViewportIndex } = ViewportGridService.getState();
|
||||
return getEnabledElement(activeViewportIndex);
|
||||
const { element } = getEnabledElement(activeViewportIndex) || {};
|
||||
return element;
|
||||
}
|
||||
|
||||
const actions = {
|
||||
@@ -98,7 +99,38 @@ const commandsModule = ({ servicesManager }) => {
|
||||
if (!toolName) {
|
||||
console.warn('No toolname provided to setToolActive command');
|
||||
}
|
||||
cornerstoneTools.setToolActive(toolName, { mouseButtonMask: 1 });
|
||||
|
||||
// Find total number of tool indexes
|
||||
const { viewports } = ViewportGridService.getState();
|
||||
for (let i = 0; i < viewports.length; i++) {
|
||||
const viewport = viewports[i];
|
||||
const hasDisplaySet = viewport.displaySetInstanceUID !== undefined;
|
||||
|
||||
if (!hasDisplaySet) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const viewportInfo = getEnabledElement(i);
|
||||
const hasCornerstoneContext =
|
||||
viewportInfo.context == 'ACTIVE_VIEWPORT::CORNERSTONE';
|
||||
|
||||
if (hasCornerstoneContext) {
|
||||
cornerstoneTools.setToolActiveForElement(
|
||||
viewportInfo.enabledElement,
|
||||
toolName,
|
||||
{ mouseButtonMask: 1 }
|
||||
);
|
||||
} else {
|
||||
commandsManager.runCommand(
|
||||
'setToolActive',
|
||||
{
|
||||
element: viewportInfo.element,
|
||||
toolName,
|
||||
},
|
||||
viewportInfo.context
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
clearAnnotations: () => {
|
||||
const element = _getActiveViewportsEnabledElement();
|
||||
@@ -106,7 +138,7 @@ const commandsModule = ({ servicesManager }) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const enabledElement = cornerstone.getEnabledElement(element);
|
||||
const { enabledElement } = cornerstone.getEnabledElement(element) || {};
|
||||
if (!enabledElement || !enabledElement.image) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -33,13 +33,19 @@ export default {
|
||||
preRegistration({ servicesManager, commandsManager, configuration = {} }) {
|
||||
init({ servicesManager, commandsManager, configuration });
|
||||
},
|
||||
getViewportModule({ commandsManager }) {
|
||||
getViewportModule({ servicesManager, commandsManager }) {
|
||||
const ExtendedOHIFCornerstoneViewport = props => {
|
||||
const onNewImageHandler = jumpData => {
|
||||
commandsManager.runCommand('jumpToImage', jumpData);
|
||||
};
|
||||
const { ToolBarService } = servicesManager;
|
||||
|
||||
return (
|
||||
<OHIFCornerstoneViewport {...props} onNewImage={onNewImageHandler} />
|
||||
<OHIFCornerstoneViewport
|
||||
{...props}
|
||||
ToolBarService={ToolBarService}
|
||||
onNewImage={onNewImageHandler}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -47,8 +53,8 @@ export default {
|
||||
{ name: 'cornerstone', component: ExtendedOHIFCornerstoneViewport },
|
||||
];
|
||||
},
|
||||
getCommandsModule({ servicesManager }) {
|
||||
return commandsModule({ servicesManager });
|
||||
getCommandsModule({ servicesManager, commandsManager }) {
|
||||
return commandsModule({ servicesManager, commandsManager });
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
+104
-200
@@ -1,19 +1,19 @@
|
||||
import React from 'react';
|
||||
import OHIF from '@ohif/core';
|
||||
import { Input, Dialog, ContextMenuMeasurements } from '@ohif/ui';
|
||||
import { ContextMenuMeasurements } from '@ohif/ui';
|
||||
import cs from 'cornerstone-core';
|
||||
import csTools from 'cornerstone-tools';
|
||||
import merge from 'lodash.merge';
|
||||
import getTools, { toolsGroupedByType } from './utils/getTools.js';
|
||||
import initCornerstoneTools from './initCornerstoneTools.js';
|
||||
import './initWADOImageLoader.js';
|
||||
import getCornerstoneMeasurementById from './utils/getCornerstoneMeasurementById';
|
||||
import measurementServiceMappingsFactory from './utils/measurementServiceMappings/measurementServiceMappingsFactory';
|
||||
import { setEnabledElement } from './state';
|
||||
import callInputDialog from './callInputDialog.js';
|
||||
|
||||
// TODO -> Global "context menu open state", or lots of expensive searches on drag?
|
||||
|
||||
let CONTEXT_MENU_OPEN = false;
|
||||
|
||||
const { globalImageIdSpecificToolStateManager } = csTools;
|
||||
|
||||
const TOOL_TYPES_WITH_CONTEXT_MENU = [
|
||||
@@ -30,6 +30,24 @@ const TOOL_TYPES_WITH_CONTEXT_MENU = [
|
||||
const _refreshViewports = () =>
|
||||
cs.getEnabledElements().forEach(({ element }) => cs.updateImage(element));
|
||||
|
||||
/* Add extension tools configuration here. */
|
||||
const _createInternalToolsConfig = UIDialogService => {
|
||||
return {
|
||||
ArrowAnnotate: {
|
||||
configuration: {
|
||||
getTextCallback: (callback, eventDetails) =>
|
||||
callInputDialog(UIDialogService, null, callback),
|
||||
changeTextCallback: (data, eventDetails, callback) =>
|
||||
callInputDialog(UIDialogService, data, callback),
|
||||
allowEmptyLabel: true,
|
||||
},
|
||||
},
|
||||
DragProbe: {
|
||||
defaultStrategy: 'minimal',
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object} servicesManager
|
||||
@@ -45,7 +63,11 @@ export default function init({
|
||||
UIDialogService,
|
||||
MeasurementService,
|
||||
DisplaySetService,
|
||||
ToolBarService,
|
||||
} = servicesManager.services;
|
||||
const tools = getTools();
|
||||
|
||||
console.log(servicesManager.services);
|
||||
|
||||
/* Measurement Service */
|
||||
const measurementServiceSource = _connectToolsToMeasurementService(
|
||||
@@ -112,6 +134,7 @@ export default function init({
|
||||
);
|
||||
|
||||
callInputDialog(
|
||||
UIDialogService,
|
||||
measurement,
|
||||
(label, actionId) => {
|
||||
if (actionId === 'cancel') {
|
||||
@@ -180,8 +203,18 @@ export default function init({
|
||||
}
|
||||
};
|
||||
|
||||
function elementEnabledHandler(evt) {
|
||||
// TODO: This is the handler for ALL ENABLED ELEMENT EVENTS
|
||||
// ... Activation logic should take place per element, not for all (diff behavior per ext)
|
||||
function elementEnabledHandler(tools, evt) {
|
||||
const element = evt.detail.element;
|
||||
|
||||
_addConfiguredToolsForElement(
|
||||
UIDialogService,
|
||||
element,
|
||||
tools,
|
||||
configuration
|
||||
);
|
||||
|
||||
element.addEventListener(csTools.EVENTS.TOUCH_PRESS, onTouchPress);
|
||||
element.addEventListener(
|
||||
csTools.EVENTS.MOUSE_CLICK,
|
||||
@@ -200,85 +233,6 @@ export default function init({
|
||||
element.removeEventListener(cs.EVENTS.NEW_IMAGE, cancelContextMenuIfOpen);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} data
|
||||
* @param {*} event
|
||||
* @param {*} callback
|
||||
* @param {*} isArrowAnnotateInputDialog
|
||||
*/
|
||||
const callInputDialog = (
|
||||
data,
|
||||
callback,
|
||||
isArrowAnnotateInputDialog = true
|
||||
) => {
|
||||
const dialogId = 'enter-annotation';
|
||||
const label = data
|
||||
? isArrowAnnotateInputDialog
|
||||
? data.text
|
||||
: data.label
|
||||
: '';
|
||||
|
||||
const onSubmitHandler = ({ action, value }) => {
|
||||
switch (action.id) {
|
||||
case 'save':
|
||||
callback(value.label, action.id);
|
||||
break;
|
||||
case 'cancel':
|
||||
callback('', action.id);
|
||||
break;
|
||||
}
|
||||
UIDialogService.dismiss({ id: dialogId });
|
||||
};
|
||||
|
||||
if (UIDialogService) {
|
||||
UIDialogService.create({
|
||||
id: dialogId,
|
||||
centralize: true,
|
||||
isDraggable: false,
|
||||
showOverlay: true,
|
||||
content: Dialog,
|
||||
contentProps: {
|
||||
title: 'Enter your annotation',
|
||||
value: { label },
|
||||
noCloseButton: true,
|
||||
onClose: () => UIDialogService.dismiss({ id: dialogId }),
|
||||
actions: [
|
||||
{ id: 'cancel', text: 'Cancel', type: 'secondary' },
|
||||
{ id: 'save', text: 'Save', type: 'primary' },
|
||||
],
|
||||
onSubmit: onSubmitHandler,
|
||||
body: ({ value, setValue }) => {
|
||||
const onChangeHandler = event => {
|
||||
event.persist();
|
||||
setValue(value => ({ ...value, label: event.target.value }));
|
||||
};
|
||||
|
||||
const onKeyPressHandler = event => {
|
||||
if (event.key === 'Enter') {
|
||||
onSubmitHandler({ value, action: { id: 'save' } });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-4 bg-primary-dark">
|
||||
<Input
|
||||
autoFocus
|
||||
className="mt-2 bg-black border-primary-main"
|
||||
type="text"
|
||||
containerClassName="mr-2"
|
||||
value={value.label}
|
||||
onChange={onChangeHandler}
|
||||
onKeyPress={onKeyPressHandler}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const { csToolsConfig } = configuration;
|
||||
const metadataProvider = OHIF.cornerstone.metadataProvider;
|
||||
|
||||
@@ -286,7 +240,7 @@ export default function init({
|
||||
|
||||
// ~~
|
||||
const defaultCsToolsConfig = csToolsConfig || {
|
||||
globalToolSyncEnabled: true,
|
||||
globalToolSyncEnabled: false, // hold on to your pants!
|
||||
showSVGCursors: false,
|
||||
autoResizeViewports: false,
|
||||
};
|
||||
@@ -298,10 +252,10 @@ export default function init({
|
||||
// THIS
|
||||
// is a way for extensions that "depend" on this extension to notify it of
|
||||
// new cornerstone enabled elements so it's commands continue to work.
|
||||
const handleOhifCornerstoneEnabledElementEvent = function (evt) {
|
||||
const { viewportIndex, enabledElement } = evt.detail;
|
||||
const handleOhifCornerstoneEnabledElementEvent = function(evt) {
|
||||
const { context, viewportIndex, enabledElement } = evt.detail;
|
||||
|
||||
setEnabledElement(viewportIndex, enabledElement);
|
||||
setEnabledElement(viewportIndex, enabledElement, context);
|
||||
};
|
||||
|
||||
document.addEventListener(
|
||||
@@ -309,120 +263,10 @@ export default function init({
|
||||
handleOhifCornerstoneEnabledElementEvent
|
||||
);
|
||||
|
||||
const toolsGroupedByType = {
|
||||
touch: [csTools.PanMultiTouchTool, csTools.ZoomTouchPinchTool],
|
||||
annotations: [
|
||||
csTools.ArrowAnnotateTool,
|
||||
csTools.BidirectionalTool,
|
||||
csTools.LengthTool,
|
||||
csTools.AngleTool,
|
||||
csTools.FreehandRoiTool,
|
||||
csTools.EllipticalRoiTool,
|
||||
csTools.DragProbeTool,
|
||||
csTools.RectangleRoiTool,
|
||||
],
|
||||
other: [
|
||||
csTools.PanTool,
|
||||
csTools.ZoomTool,
|
||||
csTools.WwwcTool,
|
||||
csTools.WwwcRegionTool,
|
||||
csTools.MagnifyTool,
|
||||
csTools.StackScrollTool,
|
||||
csTools.StackScrollMouseWheelTool,
|
||||
csTools.OverlayTool,
|
||||
],
|
||||
};
|
||||
|
||||
let tools = [];
|
||||
Object.keys(toolsGroupedByType).forEach(toolsGroup =>
|
||||
tools.push(...toolsGroupedByType[toolsGroup])
|
||||
cs.events.addEventListener(
|
||||
cs.EVENTS.ELEMENT_ENABLED,
|
||||
elementEnabledHandler.bind(null, tools)
|
||||
);
|
||||
|
||||
/* Add extension tools configuration here. */
|
||||
const internalToolsConfig = {
|
||||
ArrowAnnotate: {
|
||||
configuration: {
|
||||
getTextCallback: (callback, eventDetails) =>
|
||||
callInputDialog(null, callback),
|
||||
changeTextCallback: (data, eventDetails, callback) =>
|
||||
callInputDialog(data, callback),
|
||||
allowEmptyLabel: true,
|
||||
},
|
||||
},
|
||||
DragProbe: {
|
||||
defaultStrategy: 'minimal',
|
||||
},
|
||||
};
|
||||
|
||||
/* Abstract tools configuration using extension configuration. */
|
||||
const parseToolProps = (props, tool) => {
|
||||
const { annotations } = toolsGroupedByType;
|
||||
// An alternative approach would be to remove the `drawHandlesOnHover` config
|
||||
// from the supported configuration properties in `cornerstone-tools`
|
||||
const toolsWithHideableHandles = annotations.filter(
|
||||
tool => !['RectangleRoiTool', 'EllipticalRoiTool'].includes(tool.name)
|
||||
);
|
||||
|
||||
let parsedProps = { ...props };
|
||||
|
||||
/**
|
||||
* drawHandles - Never/Always show handles
|
||||
* drawHandlesOnHover - Only show handles on handle hover (pointNearHandle)
|
||||
* hideHandlesIfMoving - Hides the handles whilst you are moving them, for better visibility.
|
||||
*
|
||||
* Does not apply to tools where handles aren't placed in predictable
|
||||
* locations.
|
||||
*/
|
||||
if (
|
||||
configuration.hideHandles !== false &&
|
||||
toolsWithHideableHandles.includes(tool)
|
||||
) {
|
||||
if (props.configuration) {
|
||||
parsedProps.configuration.drawHandlesOnHover = true;
|
||||
parsedProps.configuration.hideHandlesIfMoving = true;
|
||||
} else {
|
||||
parsedProps.configuration = {
|
||||
drawHandlesOnHover: true,
|
||||
hideHandlesIfMoving: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return parsedProps;
|
||||
};
|
||||
|
||||
/* Add tools with its custom props through extension configuration. */
|
||||
tools.forEach(tool => {
|
||||
const toolName = new tool().name;
|
||||
const externalToolsConfig = configuration.tools || {};
|
||||
const externalToolProps = externalToolsConfig[toolName] || {};
|
||||
const internalToolProps = internalToolsConfig[toolName] || {};
|
||||
const props = merge(
|
||||
internalToolProps,
|
||||
parseToolProps(externalToolProps, tool)
|
||||
);
|
||||
csTools.addTool(tool, props);
|
||||
});
|
||||
|
||||
// TODO -> We need a better way to do this with maybe global tool state setting all tools passive.
|
||||
const BaseAnnotationTool = csTools.importInternal('base/BaseAnnotationTool');
|
||||
tools.forEach(tool => {
|
||||
if (tool.prototype instanceof BaseAnnotationTool) {
|
||||
// BaseAnnotationTool would likely come from csTools lib exports
|
||||
const toolName = new tool().name;
|
||||
csTools.setToolPassive(toolName); // there may be a better place to determine name; may not be on uninstantiated class
|
||||
}
|
||||
});
|
||||
|
||||
csTools.setToolActive('Pan', { mouseButtonMask: 4 });
|
||||
csTools.setToolActive('Zoom', { mouseButtonMask: 2 });
|
||||
csTools.setToolActive('Wwwc', { mouseButtonMask: 1 });
|
||||
csTools.setToolActive('StackScrollMouseWheel', {}); // TODO: Empty options should not be required
|
||||
csTools.setToolActive('PanMultiTouch', { pointers: 2 }); // TODO: Better error if no options
|
||||
csTools.setToolActive('ZoomTouchPinch', {});
|
||||
csTools.setToolEnabled('Overlay', {});
|
||||
|
||||
cs.events.addEventListener(cs.EVENTS.ELEMENT_ENABLED, elementEnabledHandler);
|
||||
cs.events.addEventListener(
|
||||
cs.EVENTS.ELEMENT_DISABLED,
|
||||
elementDisabledHandler
|
||||
@@ -646,3 +490,63 @@ const _getDefaultPosition = event => ({
|
||||
x: (event && event.currentPoints.client.x) || 0,
|
||||
y: (event && event.currentPoints.client.y) || 0,
|
||||
});
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function _addConfiguredToolsForElement(
|
||||
UIDialogService,
|
||||
element,
|
||||
tools,
|
||||
configuration
|
||||
) {
|
||||
const internalToolsConfig = _createInternalToolsConfig(UIDialogService);
|
||||
/* Add tools with its custom props through extension configuration. */
|
||||
tools.forEach(tool => {
|
||||
const toolName = new tool().name;
|
||||
const externalToolsConfig = configuration.tools || {};
|
||||
const externalToolProps = externalToolsConfig[toolName] || {};
|
||||
const internalToolProps = internalToolsConfig[toolName] || {};
|
||||
const props = merge(
|
||||
internalToolProps,
|
||||
_parseToolProps(configuration, externalToolProps, tool)
|
||||
);
|
||||
csTools.addToolForElement(element, tool, props);
|
||||
});
|
||||
}
|
||||
/* Abstract tools configuration using extension configuration. */
|
||||
function _parseToolProps(configuration, props, tool) {
|
||||
const { annotations } = toolsGroupedByType;
|
||||
// An alternative approach would be to remove the `drawHandlesOnHover` config
|
||||
// from the supported configuration properties in `cornerstone-tools`
|
||||
const toolsWithHideableHandles = annotations.filter(
|
||||
tool => !['RectangleRoiTool', 'EllipticalRoiTool'].includes(tool.name)
|
||||
);
|
||||
|
||||
let parsedProps = { ...props };
|
||||
|
||||
/**
|
||||
* drawHandles - Never/Always show handles
|
||||
* drawHandlesOnHover - Only show handles on handle hover (pointNearHandle)
|
||||
* hideHandlesIfMoving - Hides the handles whilst you are moving them, for better visibility.
|
||||
*
|
||||
* Does not apply to tools where handles aren't placed in predictable
|
||||
* locations.
|
||||
*/
|
||||
if (
|
||||
configuration.hideHandles !== false &&
|
||||
toolsWithHideableHandles.includes(tool)
|
||||
) {
|
||||
if (props.configuration) {
|
||||
parsedProps.configuration.drawHandlesOnHover = true;
|
||||
parsedProps.configuration.hideHandlesIfMoving = true;
|
||||
} else {
|
||||
parsedProps.configuration = {
|
||||
drawHandlesOnHover: true,
|
||||
hideHandlesIfMoving: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return parsedProps;
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
const state = {
|
||||
// The `defaultContext` of an extension's commandsModule
|
||||
DEFAULT_CONTEXT: 'ACTIVE_VIEWPORT::CORNERSTONE',
|
||||
enabledElements: {},
|
||||
};
|
||||
|
||||
@@ -7,14 +9,22 @@ const state = {
|
||||
* @param {HTMLElement} dom Active viewport element.
|
||||
* @return void
|
||||
*/
|
||||
const setEnabledElement = (viewportIndex, element) =>
|
||||
(state.enabledElements[viewportIndex] = element);
|
||||
const setEnabledElement = (viewportIndex, element, context) => {
|
||||
const targetContext = context || DEFAULT_CONTEXT;
|
||||
|
||||
state.enabledElements[viewportIndex] = {
|
||||
element,
|
||||
context: targetContext,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Grabs the enabled element `dom` reference of an active viewport.
|
||||
* Grabs the enabled element `dom` reference of an adective viewport.
|
||||
*
|
||||
* @return {HTMLElement} Active viewport element.
|
||||
*/
|
||||
const getEnabledElement = viewportIndex => state.enabledElements[viewportIndex];
|
||||
const getEnabledElement = viewportIndex => {
|
||||
return state.enabledElements[viewportIndex];
|
||||
};
|
||||
|
||||
export { setEnabledElement, getEnabledElement };
|
||||
@@ -0,0 +1,36 @@
|
||||
import csTools from 'cornerstone-tools';
|
||||
|
||||
const toolsGroupedByType = {
|
||||
touch: [csTools.PanMultiTouchTool, csTools.ZoomTouchPinchTool],
|
||||
annotations: [
|
||||
csTools.ArrowAnnotateTool,
|
||||
csTools.BidirectionalTool,
|
||||
csTools.LengthTool,
|
||||
csTools.AngleTool,
|
||||
csTools.FreehandRoiTool,
|
||||
csTools.EllipticalRoiTool,
|
||||
csTools.DragProbeTool,
|
||||
csTools.RectangleRoiTool,
|
||||
],
|
||||
other: [
|
||||
csTools.PanTool,
|
||||
csTools.ZoomTool,
|
||||
csTools.WwwcTool,
|
||||
csTools.WwwcRegionTool,
|
||||
csTools.MagnifyTool,
|
||||
csTools.StackScrollTool,
|
||||
csTools.StackScrollMouseWheelTool,
|
||||
csTools.OverlayTool,
|
||||
],
|
||||
};
|
||||
|
||||
export default function getTools() {
|
||||
const tools = [];
|
||||
Object.keys(toolsGroupedByType).forEach(toolsGroup =>
|
||||
tools.push(...toolsGroupedByType[toolsGroup])
|
||||
);
|
||||
|
||||
return tools;
|
||||
}
|
||||
|
||||
export { toolsGroupedByType };
|
||||
@@ -0,0 +1,21 @@
|
||||
import csTools from 'cornerstone-tools';
|
||||
|
||||
export default function _setActiveAndPassiveToolsForElement(element, tools) {
|
||||
const BaseAnnotationTool = csTools.importInternal('base/BaseAnnotationTool');
|
||||
|
||||
tools.forEach(tool => {
|
||||
if (tool.prototype instanceof BaseAnnotationTool) {
|
||||
// BaseAnnotationTool would likely come from csTools lib exports
|
||||
const toolName = new tool().name;
|
||||
csTools.setToolPassiveForElement(element, toolName); // there may be a better place to determine name; may not be on uninstantiated class
|
||||
}
|
||||
});
|
||||
|
||||
csTools.setToolActiveForElement(element, 'Pan', { mouseButtonMask: 4 });
|
||||
csTools.setToolActiveForElement(element, 'Zoom', { mouseButtonMask: 2 });
|
||||
csTools.setToolActiveForElement(element, 'Wwwc', { mouseButtonMask: 1 });
|
||||
csTools.setToolActiveForElement(element, 'StackScrollMouseWheel', {}); // TODO: Empty options should not be required
|
||||
csTools.setToolActiveForElement(element, 'PanMultiTouch', { pointers: 2 }); // TODO: Better error if no options
|
||||
csTools.setToolActiveForElement(element, 'ZoomTouchPinch', {});
|
||||
csTools.setToolEnabledForElement(element, 'Overlay', {});
|
||||
}
|
||||
Reference in new issue
Block a user