fix(viewport): Reset viewport state and fix CINE looping, thumbnail resolution, and dynamic tool settings (#4037)
This commit is contained in:
1 parent
a6c6fff43a
commit
f99a0bfb31
80 files changed
+1129
-801
No files matched your search
@@ -1,11 +1,19 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import Icon from '../Icon/Icon';
|
||||
|
||||
function LayoutPreset({ onSelection, title, icon, commandOptions, classNames }) {
|
||||
function LayoutPreset({
|
||||
onSelection,
|
||||
title,
|
||||
icon,
|
||||
commandOptions,
|
||||
classNames: classNameProps,
|
||||
disabled,
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={classNames}
|
||||
className={classNames(classNameProps, disabled && 'ohif-disabled')}
|
||||
onClick={() => {
|
||||
onSelection(commandOptions);
|
||||
}}
|
||||
@@ -29,6 +37,7 @@ LayoutPreset.propTypes = {
|
||||
icon: PropTypes.string.isRequired,
|
||||
commandOptions: PropTypes.object.isRequired,
|
||||
classNames: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default LayoutPreset;
|
||||
@@ -7,7 +7,7 @@ function AddSegmentRow({ onClick, onToggleSegmentationVisibility = null, segment
|
||||
return (
|
||||
<div className="flex justify-between bg-black pl-[34px] hover:cursor-pointer">
|
||||
<div
|
||||
className="group pt-[5px] pb-[5px]"
|
||||
className="group py-[5px] pb-[5px]"
|
||||
onClick={onClick}
|
||||
>
|
||||
<div className="text-primary-active group-hover:bg-secondary-dark flex items-center rounded-[4px] pr-2">
|
||||
|
||||
@@ -2,11 +2,11 @@ import React from 'react';
|
||||
import Icon from '../Icon';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
function NoSegmentationRow({ onSegmentationAdd }) {
|
||||
function NoSegmentationRow({ onSegmentationAdd, addSegmentationClassName }) {
|
||||
const { t } = useTranslation('SegmentationTable');
|
||||
return (
|
||||
<div
|
||||
className="group"
|
||||
className={`group ${addSegmentationClassName}`}
|
||||
onClick={onSegmentationAdd}
|
||||
>
|
||||
<div className="text-primary-active group-hover:bg-secondary-dark flex items-center rounded-[4px] group-hover:cursor-pointer">
|
||||
|
||||
@@ -15,6 +15,7 @@ function SegmentationDropDownRow({
|
||||
storeSegmentation,
|
||||
onSegmentationDelete,
|
||||
onSegmentationAdd,
|
||||
addSegmentationClassName,
|
||||
}) {
|
||||
const handleChange = option => {
|
||||
onActiveSegmentationChange(option.value); // Notify the parent
|
||||
@@ -41,7 +42,7 @@ function SegmentationDropDownRow({
|
||||
id="segmentation-dropdown"
|
||||
showDropdownIcon={false}
|
||||
alignment="left"
|
||||
itemsClassName="text-primary-active"
|
||||
itemsClassName={`text-primary-active ${addSegmentationClassName}`}
|
||||
showBorders={false}
|
||||
maxCharactersPerLine={30}
|
||||
list={[
|
||||
|
||||
@@ -42,6 +42,7 @@ const SegmentationGroupTable = ({
|
||||
setRenderFill,
|
||||
setRenderInactiveSegmentations,
|
||||
setRenderOutline,
|
||||
addSegmentationClassName,
|
||||
}) => {
|
||||
const [isConfigOpen, setIsConfigOpen] = useState(false);
|
||||
const [activeSegmentationId, setActiveSegmentationId] = useState(null);
|
||||
@@ -100,9 +101,12 @@ const SegmentationGroupTable = ({
|
||||
)}
|
||||
<div className="bg-primary-dark ">
|
||||
{segmentations?.length === 0 ? (
|
||||
<div className="select-none bg-black pt-[5px] pb-[5px]">
|
||||
<div className="select-none bg-black py-[3px]">
|
||||
{showAddSegmentation && !disableEditing && (
|
||||
<NoSegmentationRow onSegmentationAdd={onSegmentationAdd} />
|
||||
<NoSegmentationRow
|
||||
onSegmentationAdd={onSegmentationAdd}
|
||||
addSegmentationClassName={addSegmentationClassName}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
@@ -118,6 +122,7 @@ const SegmentationGroupTable = ({
|
||||
onSegmentationDownloadRTSS={onSegmentationDownloadRTSS}
|
||||
storeSegmentation={storeSegmentation}
|
||||
onSegmentationAdd={onSegmentationAdd}
|
||||
addSegmentationClassName={addSegmentationClassName}
|
||||
onToggleSegmentationVisibility={onToggleSegmentationVisibility}
|
||||
/>
|
||||
{!disableEditing && showAddSegment && (
|
||||
|
||||
@@ -51,7 +51,7 @@ const Thumbnail = ({
|
||||
<div ref={drag}>
|
||||
<div
|
||||
className={classnames(
|
||||
'min-h-32 flex flex-1 items-center justify-center overflow-hidden rounded-md bg-black text-base text-white',
|
||||
'flex h-32 flex-1 items-center justify-center overflow-hidden rounded-md bg-black text-base text-white',
|
||||
isActive
|
||||
? 'border-primary-light border-2'
|
||||
: 'border-secondary-light border hover:border-blue-300'
|
||||
@@ -64,7 +64,7 @@ const Thumbnail = ({
|
||||
<img
|
||||
src={imageSrc}
|
||||
alt={imageAltText}
|
||||
className="min-h-32 object-contain"
|
||||
className="h-full w-full object-contain"
|
||||
crossOrigin="anonymous"
|
||||
/>
|
||||
) : (
|
||||
|
||||
@@ -16,7 +16,7 @@ const ThumbnailList = ({
|
||||
return (
|
||||
<div
|
||||
id="ohif-thumbnail-list"
|
||||
className="ohif-scrollbar study-min-height overflow-y-hidden bg-black py-3"
|
||||
className="ohif-scrollbar study-min-height overflow-y-hidden bg-black py-5"
|
||||
>
|
||||
{thumbnails.map(
|
||||
({
|
||||
|
||||
@@ -32,10 +32,7 @@ function ThumbnailTracked({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames(
|
||||
'flex flex-1 cursor-pointer flex-row px-3 py-2 outline-none',
|
||||
className
|
||||
)}
|
||||
className={classnames('flex flex-1 cursor-pointer flex-row px-3 outline-none', className)}
|
||||
id={`thumbnail-${displaySetInstanceUID}`}
|
||||
>
|
||||
<div className="flex-2 flex flex-col items-center">
|
||||
|
||||
@@ -65,6 +65,7 @@ function Toolbox({ servicesManager, buttonSectionId, commandsManager, title, ...
|
||||
cmds.forEach(command => {
|
||||
const isString = typeof command === 'string';
|
||||
const isObject = typeof command === 'object';
|
||||
const isFunction = typeof command === 'function';
|
||||
|
||||
if (isString) {
|
||||
commandsManager.run(command, { value });
|
||||
@@ -73,6 +74,8 @@ function Toolbox({ servicesManager, buttonSectionId, commandsManager, title, ...
|
||||
...command,
|
||||
commandOptions: { ...command.commandOptions, ...option, value },
|
||||
});
|
||||
} else if (isFunction) {
|
||||
command({ value, commandsManager, servicesManager });
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@@ -92,7 +92,7 @@ export default function CineProvider({ children, service }) {
|
||||
setCine,
|
||||
setIsCineEnabled: isCineEnabled => service.setIsCineEnabled(isCineEnabled),
|
||||
playClip: (element, playClipOptions) => service.playClip(element, playClipOptions),
|
||||
stopClip: element => service.stopClip(element),
|
||||
stopClip: (element, stopClipOptions) => service.stopClip(element, stopClipOptions),
|
||||
};
|
||||
|
||||
return <CineContext.Provider value={[state, api]}>{children}</CineContext.Provider>;
|
||||
|
||||
@@ -28,6 +28,7 @@ interface Layout {
|
||||
interface DefaultState {
|
||||
activeViewportId: string | null;
|
||||
layout: Layout;
|
||||
isHangingProtocolLayout: boolean;
|
||||
viewports: Map<string, Viewport>;
|
||||
}
|
||||
|
||||
@@ -38,6 +39,16 @@ const DEFAULT_STATE: DefaultState = {
|
||||
numCols: 0,
|
||||
layoutType: 'grid',
|
||||
},
|
||||
// this flag is used to determine if the hanging protocol layout is active
|
||||
// so that we can inherit the viewport options from the previous state
|
||||
// otherwise we will not allow that. Basically the issue is that we need
|
||||
// to be able to come out of the hanging protocol layout and go back to the
|
||||
// regular layout e.g., if we are in the MPR hanging protocol, and someone use
|
||||
// 1x1 layout by custom layout selector, there is no way to drag and drop
|
||||
// a non-reconstructible series to the viewport since it will always
|
||||
// inherit the hanging protocol layout options (volume viewport),
|
||||
// so we need to be able to switch back to the regular layout.
|
||||
isHangingProtocolLayout: false,
|
||||
// Viewports structure has been changed to Map (previously it was
|
||||
// tied to the viewportIndex which caused multiple issues. Now we have
|
||||
// moved completely to viewportId which is unique for each viewport.
|
||||
@@ -152,7 +163,7 @@ export function ViewportGridProvider({ children, service }) {
|
||||
// Use the newly provide viewportOptions and display set options
|
||||
// when provided, and otherwise fall back to the previous ones.
|
||||
// That allows for easy updates of just the display set.
|
||||
const viewportOptions = merge(
|
||||
let viewportOptions = merge(
|
||||
{},
|
||||
previousViewport?.viewportOptions,
|
||||
updatedViewport?.viewportOptions
|
||||
@@ -161,18 +172,29 @@ export function ViewportGridProvider({ children, service }) {
|
||||
const displaySetOptions = updatedViewport.displaySetOptions || [];
|
||||
if (!displaySetOptions.length) {
|
||||
// Copy all the display set options, assuming a full set of displaySet UID's is provided.
|
||||
displaySetOptions.push(...previousViewport.displaySetOptions);
|
||||
if (state.isHangingProtocolLayout) {
|
||||
displaySetOptions.push(...(previousViewport.displaySetOptions || []));
|
||||
}
|
||||
if (!displaySetOptions.length) {
|
||||
displaySetOptions.push({});
|
||||
}
|
||||
}
|
||||
|
||||
// if it is not part of the hanging protocol layout, we should remove the toolGroupId
|
||||
// and viewportType from the viewportOptions so that it doesn't
|
||||
// inherit the hanging protocol layout options
|
||||
if (!state.isHangingProtocolLayout) {
|
||||
viewportOptions = {
|
||||
viewportId: viewportOptions.viewportId,
|
||||
};
|
||||
}
|
||||
|
||||
const newViewport = {
|
||||
...previousViewport,
|
||||
displaySetInstanceUIDs,
|
||||
viewportOptions,
|
||||
displaySetOptions,
|
||||
viewportLabel: getViewportLabel(viewports, viewportId),
|
||||
// viewportLabel: getViewportLabel(viewports, viewportId),
|
||||
};
|
||||
|
||||
viewportOptions.presentationIds = ViewportGridService.getPresentationIds(
|
||||
@@ -196,6 +218,7 @@ export function ViewportGridProvider({ children, service }) {
|
||||
layoutType = 'grid',
|
||||
activeViewportId,
|
||||
findOrCreateViewport,
|
||||
isHangingProtocolLayout,
|
||||
} = action.payload;
|
||||
|
||||
// If empty viewportOptions, we use numRow and numCols to calculate number of viewports
|
||||
@@ -283,6 +306,7 @@ export function ViewportGridProvider({ children, service }) {
|
||||
layoutType,
|
||||
},
|
||||
viewports,
|
||||
isHangingProtocolLayout,
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
@@ -373,6 +397,7 @@ export function ViewportGridProvider({ children, service }) {
|
||||
layoutOptions = [],
|
||||
activeViewportId,
|
||||
findOrCreateViewport,
|
||||
isHangingProtocolLayout,
|
||||
}) =>
|
||||
dispatch({
|
||||
type: 'SET_LAYOUT',
|
||||
@@ -383,6 +408,7 @@ export function ViewportGridProvider({ children, service }) {
|
||||
layoutOptions,
|
||||
activeViewportId,
|
||||
findOrCreateViewport,
|
||||
isHangingProtocolLayout,
|
||||
},
|
||||
}),
|
||||
[dispatch]
|
||||
|
||||
Reference in new issue
Block a user