Skip to content

Commit

Permalink
feat(multimonitor): Add simple multi-monitor support to open another …
Browse files Browse the repository at this point in the history
…study(#4178)
  • Loading branch information
wayfarer3130 authored Jan 13, 2025
1 parent a4a6de0 commit 07c628e
Show file tree
Hide file tree
Showing 49 changed files with 1,473 additions and 364 deletions.
7 changes: 1 addition & 6 deletions extensions/cornerstone/src/panels/PanelMeasurement.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import React, { useEffect, useRef, useState } from 'react';
import { utils } from '@ohif/core';
import { useViewportGrid } from '@ohif/ui-next';
import { MeasurementTable } from '@ohif/ui-next';
import debounce from 'lodash.debounce';
import { useMeasurements } from '../hooks/useMeasurements';

const {
filterAdditionalFindings: filterAdditionalFinding,
filterOr,
filterAny,
} = utils.MeasurementFilters;
const { filterAdditionalFindings: filterAdditionalFinding, filterAny } = utils.MeasurementFilters;

export type withAppAndFilters = withAppTypes & {
measurementFilter: (item) => boolean;
Expand Down
91 changes: 91 additions & 0 deletions extensions/default/src/Components/MoreDropdownMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import React from 'react';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuTrigger,
Icons,
Button,
} from '@ohif/ui-next';

/**
* The default sub-menu appearance and setup is defined here, but this can be
* replaced by
*/
const getMenuItemsDefault = ({ commandsManager, items, servicesManager, ...props }) => {
const { customizationService } = servicesManager.services;

// This allows replacing the default child item for menus, whereas the entire
// getMenuItems can also be replaced by providing it to the MoreDropdownMenu
const menuContent = customizationService.getCustomization('ohif.menuContent');

return (
<DropdownMenuContent
hideWhenDetached
align="start"
onClick={e => {
e.stopPropagation();
e.preventDefault();
}}
>
{items?.map(item =>
menuContent.content({
key: item.id,
item,
commandsManager,
servicesManager,
...props,
})
)}
</DropdownMenuContent>
);
};

/**
* The component provides a ... sub-menu for various components which appears
* on hover over the main component.
*
* @param bindProps - properties to define the sub-menu
* @returns Component bound to the bindProps
*/
export default function MoreDropdownMenu(bindProps) {
const {
menuItemsKey,
getMenuItems = getMenuItemsDefault,
commandsManager,
servicesManager,
} = bindProps;
const { customizationService } = servicesManager.services;

const items = customizationService.getCustomization(menuItemsKey)?.value;

if (!items) {
return null;
}

function BoundMoreDropdownMenu(props) {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className="hidden group-hover:inline-flex data-[state=open]:inline-flex"
onClick={e => {
e.preventDefault();
e.stopPropagation();
}}
>
<Icons.More />
</Button>
</DropdownMenuTrigger>
{getMenuItems({
...props,
commandsManager: commandsManager,
servicesManager: servicesManager,
items,
})}
</DropdownMenu>
);
}
return BoundMoreDropdownMenu;
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,22 @@ export default class ContextMenuController {
}

const { event, subMenu, menuId, menus, selectorProps } = contextMenuProps;
if (!menus) {
console.warn('No menus found for', menuId);
return;
}

const { locking, visibility } = CsAnnotation;
const targetAnnotationId = selectorProps?.nearbyToolData?.annotationUID as string;
const isLocked = locking.isAnnotationLocked(targetAnnotationId);
const isVisible = visibility.isAnnotationVisible(targetAnnotationId);

if (isLocked || !isVisible) {
console.warn(`Annotation is ${isLocked ? 'locked' : 'not visible'}.`);
return;
if (targetAnnotationId) {
const isLocked = locking.isAnnotationLocked(targetAnnotationId);
const isVisible = visibility.isAnnotationVisible(targetAnnotationId);

if (isLocked || !isVisible) {
console.warn(`Annotation is ${isLocked ? 'locked' : 'not visible'}.`);
return;
}
}

const items = ContextMenuItemsBuilder.getMenuItems(
Expand All @@ -73,7 +80,7 @@ export default class ContextMenuController {
preventCutOf: true,
defaultPosition: ContextMenuController._getDefaultPosition(
defaultPointsPosition,
event?.detail,
event?.detail || event,
viewportElement
),
event,
Expand All @@ -89,7 +96,7 @@ export default class ContextMenuController {
menus,
event,
subMenu,
eventData: event?.detail,
eventData: event?.detail || event,

onClose: () => {
this.services.uiDialogService.dismiss({ id: 'context-menu' });
Expand Down Expand Up @@ -136,8 +143,8 @@ export default class ContextMenuController {
};

static _getEventDefaultPosition = eventDetail => ({
x: eventDetail && eventDetail.currentPoints.client[0],
y: eventDetail && eventDetail.currentPoints.client[1],
x: eventDetail?.currentPoints?.client[0] ?? eventDetail?.pageX,
y: eventDetail?.currentPoints?.client[1] ?? eventDetail?.pageY,
});

static _getElementDefaultPosition = element => {
Expand Down
21 changes: 14 additions & 7 deletions extensions/default/src/Panels/StudyBrowser/PanelStudyBrowser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useNavigate } from 'react-router-dom';
import { Separator } from '@ohif/ui-next';
import { PanelStudyBrowserHeader } from './PanelStudyBrowserHeader';
import { defaultActionIcons, defaultViewPresets } from './constants';
import MoreDropdownMenu from '../../Components/MoreDropdownMenu';

const { sortStudyInstances, formatDate, createStudyBrowserTabs } = utils;

Expand Down Expand Up @@ -280,10 +281,6 @@ function PanelStudyBrowser({

const activeDisplaySetInstanceUIDs = viewports.get(activeViewportId)?.displaySetInstanceUIDs;

const onThumbnailContextMenu = (commandName, options) => {
commandsManager.runCommand(commandName, options);
};

return (
<>
<>
Expand All @@ -304,16 +301,26 @@ function PanelStudyBrowser({
tabs={tabs}
servicesManager={servicesManager}
activeTabName={activeTabName}
onDoubleClickThumbnail={onDoubleClickThumbnailHandler}
activeDisplaySetInstanceUIDs={activeDisplaySetInstanceUIDs}
expandedStudyInstanceUIDs={expandedStudyInstanceUIDs}
onClickStudy={_handleStudyClick}
onClickTab={clickedTabName => {
setActiveTabName(clickedTabName);
}}
onClickThumbnail={() => {}}
onDoubleClickThumbnail={onDoubleClickThumbnailHandler}
activeDisplaySetInstanceUIDs={activeDisplaySetInstanceUIDs}
showSettings={actionIcons.find(icon => icon.id === 'settings').value}
viewPresets={viewPresets}
onThumbnailContextMenu={onThumbnailContextMenu}
ThumbnailMenuItems={MoreDropdownMenu({
commandsManager,
servicesManager,
menuItemsKey: 'studyBrowser.thumbnailMenuItems',
})}
StudyMenuItems={MoreDropdownMenu({
commandsManager,
servicesManager,
menuItemsKey: 'studyBrowser.studyMenuItems',
})}
/>
</>
);
Expand Down
4 changes: 2 additions & 2 deletions extensions/default/src/Panels/WrappedPanelStudyBrowser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ import requestDisplaySetCreationForStudy from './requestDisplaySetCreationForStu
* @param {object} commandsManager
* @param {object} extensionManager
*/
function WrappedPanelStudyBrowser({ commandsManager, extensionManager, servicesManager }) {
function WrappedPanelStudyBrowser({ extensionManager, servicesManager }) {
// TODO: This should be made available a different way; route should have
// already determined our datasource
const dataSource = extensionManager.getDataSources()[0];
const [dataSource] = extensionManager.getActiveDataSource();
const _getStudiesForPatientByMRN = getStudiesForPatientByMRN.bind(null, dataSource);
const _getImageSrcFromImageId = useCallback(
_createGetImageSrcFromImageIdFn(extensionManager),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function requestDisplaySetCreationForStudy(
return;
}

dataSource.retrieve.series.metadata({ StudyInstanceUID, madeInClient });
return dataSource.retrieve.series.metadata({ StudyInstanceUID, madeInClient });
}

export default requestDisplaySetCreationForStudy;
7 changes: 1 addition & 6 deletions extensions/default/src/ViewerLayout/ViewerHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ function ViewerHeader({
const onClickReturnButton = () => {
const { pathname } = location;
const dataSourceIdx = pathname.indexOf('/', 1);
const query = new URLSearchParams(window.location.search);
const configUrl = query.get('configUrl');

const dataSourceName = pathname.substring(dataSourceIdx + 1);
const existingDataSource = extensionManager.getDataSources(dataSourceName);
Expand All @@ -35,10 +33,7 @@ function ViewerHeader({
if (dataSourceIdx !== -1 && existingDataSource) {
searchQuery.append('datasources', pathname.substring(dataSourceIdx + 1));
}

if (configUrl) {
searchQuery.append('configUrl', configUrl);
}
preserveQueryParameters(searchQuery);

navigate({
pathname: publicUrl,
Expand Down
Loading

0 comments on commit 07c628e

Please sign in to comment.