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

feat: Add simple multi-monitor support #4178

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions .webpack/webpack.base.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ module.exports = (env, argv, { SRC_DIR, ENTRY }) => {
config.optimization.minimizer = [
new TerserJSPlugin({
parallel: true,
exclude: /app-config.js/,
terserOptions: {},
}),
];
Expand Down
2 changes: 1 addition & 1 deletion extensions/default/src/ViewerLayout/ViewerHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function ViewerHeader({
if (dataSourceIdx !== -1 && existingDataSource) {
searchQuery.append('datasources', pathname.substring(dataSourceIdx + 1));
}

servicesManager.services.multiMonitorService.appendQuery(searchQuery);
if (configUrl) {
searchQuery.append('configUrl', configUrl);
}
Expand Down
34 changes: 22 additions & 12 deletions extensions/default/src/hangingprotocols/hpMNGrid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,24 @@ const hpMN: Types.HangingProtocol.Protocol = {
],
},
},
screens: [
// This hanging protocol will match screens primary and secondary
{
matches: ['primary', 'secondary'],
primary: {
x: 0.5,
y: 0,
width: 0.5,
height: 1,
},
secondary: {
x: 0.5,
y: 0,
width: 0.5,
height: 1,
},
},
],
defaultViewport: {
viewportOptions: {
viewportType: 'stack',
Expand All @@ -58,7 +76,7 @@ const hpMN: Types.HangingProtocol.Protocol = {
},
stages: [
{
id: '2x2',
name: '2x2',
stageActivation: {
enabled: {
minViewportsMatched: 4,
Expand Down Expand Up @@ -124,11 +142,7 @@ const hpMN: Types.HangingProtocol.Protocol = {

// 3x1 stage
{
id: '3x1',
// Obsolete settings:
requiredViewports: 1,
preferredViewports: 3,
// New equivalent:
name: '3x1',
stageActivation: {
enabled: {
minViewportsMatched: 3,
Expand Down Expand Up @@ -182,9 +196,7 @@ const hpMN: Types.HangingProtocol.Protocol = {

// A 2x1 stage
{
id: '2x1',
requiredViewports: 1,
preferredViewports: 2,
name: '2x1',
stageActivation: {
enabled: {
minViewportsMatched: 2,
Expand Down Expand Up @@ -226,9 +238,7 @@ const hpMN: Types.HangingProtocol.Protocol = {

// A 1x1 stage - should be automatically activated if there is only 1 viewable instance
{
id: '1x1',
requiredViewports: 1,
preferredViewports: 1,
name: '1x1',
stageActivation: {
enabled: {
minViewportsMatched: 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ function PanelStudyBrowserTracking({
uiNotificationService,
measurementService,
studyPrefetcherService,
multiMonitorService,
} = servicesManager.services;
const navigate = useNavigate();

Expand Down Expand Up @@ -320,18 +321,25 @@ function PanelStudyBrowserTracking({

const tabs = createStudyBrowserTabs(StudyInstanceUIDs, studyDisplayList, displaySets);

const _launchMultiMonitor =
multiMonitorService.numberOfScreens > 1
? (studyInstanceUID, screenDelta) => {
multiMonitorService.launchStudy(studyInstanceUID, screenDelta);
}
: null;

// TODO: Should not fire this on "close"
function _handleStudyClick(StudyInstanceUID) {
const shouldCollapseStudy = expandedStudyInstanceUIDs.includes(StudyInstanceUID);
function _handleStudyClick(studyInstanceUID) {
const shouldCollapseStudy = expandedStudyInstanceUIDs.includes(studyInstanceUID);
const updatedExpandedStudyInstanceUIDs = shouldCollapseStudy
? [...expandedStudyInstanceUIDs.filter(stdyUid => stdyUid !== StudyInstanceUID)]
: [...expandedStudyInstanceUIDs, StudyInstanceUID];
? [...expandedStudyInstanceUIDs.filter(studyUid => studyUid !== studyInstanceUID)]
: [...expandedStudyInstanceUIDs, studyInstanceUID];

setExpandedStudyInstanceUIDs(updatedExpandedStudyInstanceUIDs);

if (!shouldCollapseStudy) {
const madeInClient = true;
requestDisplaySetCreationForStudy(displaySetService, StudyInstanceUID, madeInClient);
requestDisplaySetCreationForStudy(displaySetService, studyInstanceUID, madeInClient);
}
}

Expand Down Expand Up @@ -438,6 +446,7 @@ function PanelStudyBrowserTracking({
activeTabName={activeTabName}
expandedStudyInstanceUIDs={expandedStudyInstanceUIDs}
onClickStudy={_handleStudyClick}
onClickLaunch={_launchMultiMonitor}
onClickTab={clickedTabName => {
setActiveTabName(clickedTabName);
}}
Expand Down
65 changes: 65 additions & 0 deletions platform/app/public/config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,71 @@ window.config = {
prefetch: 25,
},
// filterQueryParam: false,
// Defines multi-monitor layouts
multimonitor: [
{
id: 'split',
test: ({ multimonitor }) => multimonitor === 'split',
screens: [
{
id: 'primary',
// This is the primary screen, so don't launch is separately, but use primary
launch: 'primary',
screen: null,
location: {
screen: 0,
width: 0.5,
height: 1,
left: 0,
top: 0,
},
},
{
id: 'secondary',
// This is a window instance, so launch as a url
launch: 'url',
screen: null,
location: {
width: 0.48,
height: 1,
left: 0.52,
top: 0,
},
},
],
},

{
id: '2',
test: ({ multimonitor }) => multimonitor === '2',
screens: [
{
id: 'primary',
// This is the primary screen, so don't launch is separately, but use primary
launch: 'primary',
screen: 0,
location: {
width: 100,
height: 100,
left: 0,
top: 0,
},
},
{
id: 'secondary',
// This is a window instance, so launch as a url
launch: 'url',
screen: 1,
location: {
width: 100,
height: 100,
left: 0,
top: 0,
},
},
],
},
],
defaultDataSourceName: 'dicomweb',
/* Dynamic config allows user to pass "configUrl" query string this allows to load config without recompiling application. The regex will ensure valid configuration source */
// dangerouslyUseDynamicConfig: {
Expand Down
65 changes: 65 additions & 0 deletions platform/app/public/config/e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,71 @@ window.config = {
investigationalUseDialog: {
option: 'never',
},
// Defines multi-monitor layouts
multimonitor: [
{
id: 'split',
test: ({ multimonitor }) => multimonitor === 'split',
screens: [
{
id: 'primary',
// This is the primary screen, so don't launch is separately, but use primary
launch: 'primary',
screen: null,
location: {
screen: 0,
width: 0.5,
height: 1,
left: 0,
top: 0,
},
},
{
id: 'secondary',
// This is a window instance, so launch as a url
launch: 'url',
screen: null,
location: {
width: 0.48,
height: 1,
left: 0.52,
top: 0,
},
},
],
},

{
id: '2',
test: ({ multimonitor }) => multimonitor === '2',
screens: [
{
id: 'primary',
// This is the primary screen, so don't launch is separately, but use primary
launch: 'primary',
screen: 0,
location: {
width: 100,
height: 100,
left: 0,
top: 0,
},
},
{
id: 'secondary',
// This is a window instance, so launch as a url
launch: 'url',
screen: 1,
location: {
width: 100,
height: 100,
left: 0,
top: 0,
},
},
],
},
],
dataSources: [
{
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
Expand Down
2 changes: 2 additions & 0 deletions platform/app/src/appInit.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
PanelService,
WorkflowStepsService,
StudyPrefetcherService,
MultiMonitorService,
// utils,
} from '@ohif/core';

Expand Down Expand Up @@ -59,6 +60,7 @@ async function appInit(appConfigOrFunc, defaultExtensions, defaultModes) {
servicesManager.setExtensionManager(extensionManager);

servicesManager.registerServices([
[MultiMonitorService.REGISTRATION, appConfig.multimonitor],
UINotificationService.REGISTRATION,
UIModalService.REGISTRATION,
UIDialogService.REGISTRATION,
Expand Down
5 changes: 4 additions & 1 deletion platform/app/src/routes/WorkList/WorkList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@ function WorkList({
if (filterValues.configUrl) {
query.append('configUrl', filterValues.configUrl);
}
servicesManager.services.multiMonitorService.appendQuery(query);
query.append('StudyInstanceUIDs', studyInstanceUid);
return (
mode.displayName && (
Expand Down Expand Up @@ -495,7 +496,7 @@ function WorkList({
});
}

const { customizationService } = servicesManager.services;
const { customizationService, multiMonitorService } = servicesManager.services;
const { component: dicomUploadComponent } =
customizationService.get('dicomUploadComponent') ?? {};
const uploadProps =
Expand Down Expand Up @@ -605,6 +606,7 @@ const defaultFilterValues = {
resultsPerPage: 25,
datasources: '',
configUrl: null,
multimonitor: null,
};

function _tryParseInt(str, defaultValue) {
Expand Down Expand Up @@ -640,6 +642,7 @@ function _getQueryFilterValues(params) {
resultsPerPage: _tryParseInt(params.get('resultsperpage'), undefined),
datasources: params.get('datasources'),
configUrl: params.get('configurl'),
multimonitor: params.get('multimonitor'),
};

// Delete null/undefined keys
Expand Down
1 change: 1 addition & 0 deletions platform/core/src/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ describe('Top level exports', () => {
'UIViewportDialogService',
'DisplaySetService',
'MeasurementService',
'MultiMonitorService',
'ToolbarService',
'Types',
'ViewportGridService',
Expand Down
3 changes: 3 additions & 0 deletions platform/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
PanelService,
WorkflowStepsService,
StudyPrefetcherService,
MultiMonitorService,
} from './services';

import { DisplaySetMessage, DisplaySetMessageList } from './services/DisplaySetService';
Expand Down Expand Up @@ -79,6 +80,7 @@ const OHIF = {
ViewportGridService,
HangingProtocolService,
UserAuthenticationService,
MultiMonitorService,
IWebApiDataSource,
DicomMetadataStore,
pubSubServiceInterface,
Expand Down Expand Up @@ -120,6 +122,7 @@ export {
DisplaySetMessage,
DisplaySetMessageList,
MeasurementService,
MultiMonitorService,
ToolbarService,
ViewportGridService,
HangingProtocolService,
Expand Down
Loading