Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(toolbar): allow customizable toolbar for active viewport and allow active tool to be deactivated via a click #3608

Merged
merged 11 commits into from
Oct 25, 2023
9 changes: 2 additions & 7 deletions extensions/cornerstone/src/commandsModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
},
Expand Down
25 changes: 20 additions & 5 deletions extensions/default/src/Toolbar/Toolbar.tsx
Original file line number Diff line number Diff line change
@@ -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),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
5 changes: 5 additions & 0 deletions modes/basic-test-mode/src/createButton.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { ToolbarService } from '@ohif/core';

export const createActionButton = ToolbarService._createButton.bind(null, 'action');
export const createToggleButton = ToolbarService._createButton.bind(null, 'toggle');
export const createToolButton = ToolbarService._createButton.bind(null, 'tool');
40 changes: 26 additions & 14 deletions modes/basic-test-mode/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,21 +77,23 @@ function modeFactory() {
]);

let unsubscribe;
toolbarService.setDefaultTool({
groupId: 'WindowLevel',
itemId: 'WindowLevel',
jbocce marked this conversation as resolved.
Show resolved Hide resolved
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.
Expand All @@ -115,9 +117,19 @@ function modeFactory() {
'Capture',
'Layout',
'MPR',
'Crosshairs',
'MoreTools',
]);
toolbarService.createButtonSection('mpr', [
jbocce marked this conversation as resolved.
Show resolved Hide resolved
'MeasurementTools',
'Zoom',
'WindowLevel',
'Pan',
'Capture',
'Layout',
'MPR',
'Crosshairs',
'MoreToolsMpr',
]);
},
onModeExit: ({ servicesManager }) => {
const {
Expand Down
229 changes: 229 additions & 0 deletions modes/basic-test-mode/src/moreTools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
import type { RunCommand } from '@ohif/core/types';
import { EVENTS } from '@cornerstonejs/core';
import { createActionButton, createToggleButton, createToolButton } from './createButton';

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: createActionButton(
'Reset',
'tool-reset',
'Reset View',
[
{
commandName: 'resetViewport',
},
],
'Reset'
),
secondary: {
icon: 'chevron-down',
label: '',
isActive: true,
tooltip: 'More Tools',
},
items: [
createActionButton(
'Reset',
'tool-reset',
'Reset View',
[
{
commandName: 'resetViewport',
},
],
'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 moreTools;
Loading