diff --git a/extensions/cornerstone/src/components/AdvancedRenderingControls/AdvancedRenderingControls.tsx b/extensions/cornerstone/src/components/AdvancedRenderingControls/AdvancedRenderingControls.tsx
index 7b638cc78..d58752a46 100644
--- a/extensions/cornerstone/src/components/AdvancedRenderingControls/AdvancedRenderingControls.tsx
+++ b/extensions/cornerstone/src/components/AdvancedRenderingControls/AdvancedRenderingControls.tsx
@@ -1,6 +1,12 @@
import { useToolbar, useViewportMousePosition } from '@ohif/core/src/hooks';
import React, { useState, useEffect, useRef } from 'react';
import { useViewportRendering } from '../../hooks';
+import { ButtonLocation } from '@ohif/core/src/services/ToolBarService/ToolbarService';
+
+const mouseNearControlsRanges = {
+ [ButtonLocation.TopMiddle]: { minX: 0, minY: 0, maxX: 1, maxY: 0.1 },
+ [ButtonLocation.BottomMiddle]: { minX: 0, minY: 0.9, maxX: 1, maxY: 1 },
+};
function AdvancedRenderingControls({
viewportId,
@@ -24,7 +30,7 @@ function AdvancedRenderingControls({
});
const mousePosition = useViewportMousePosition(viewportId);
- const [isAtBottom, setIsAtBottom] = useState(false);
+ const [isMouseNearControls, setIsMouseNearControls] = useState(false);
const [showAllIcons, setShowAllIcons] = useState(true);
const firstMountRef = useRef(true);
const { hasColorbar } = useViewportRendering(viewportId);
@@ -43,10 +49,11 @@ function AdvancedRenderingControls({
useEffect(() => {
if (!showAllIcons && mousePosition.isInViewport) {
- if (mousePosition.isInBottomPercentage(10)) {
- setIsAtBottom(true);
+ const mouseHoverLocation = mouseNearControlsRanges[location];
+ if (mousePosition.isWithinNormalizedBox(mouseHoverLocation)) {
+ setIsMouseNearControls(true);
} else {
- setIsAtBottom(false);
+ setIsMouseNearControls(false);
}
}
}, [mousePosition, showAllIcons]);
@@ -97,7 +104,7 @@ function AdvancedRenderingControls({
// Always show all icons on first mount for 3 seconds
// After that, always show Colorbar, show others only when mouse is at bottom
- const shouldBeVisible = showAllIcons || id === 'Colorbar' || isAtBottom;
+ const shouldBeVisible = showAllIcons || id === 'Colorbar' || isMouseNearControls;
return (
+ position === 'top' || position === 'bottom';
+
/**
* ViewportColorbar Component
* A React wrapper for the cornerstone ViewportColorbar that adds a close button
@@ -143,8 +146,9 @@ const ViewportColorbar = memo(function ViewportColorbar({
display: 'flex',
alignItems: 'center',
pointerEvents: 'auto',
- minWidth: position === 'bottom' ? width / 2.5 : '17px',
- minHeight: position === 'bottom' ? '20px' : numColorbars === 1 ? height / 3 : height / 4,
+ minWidth: isHorizontal(position) ? width / 2.5 : '17px',
+ minHeight: isHorizontal(position) ? '20px' : numColorbars === 1 ? height / 3 : height / 4,
+ height: '1px', // sometimes flex items with min-height need a starting point for its height calculation
...positionStylesFromConfig,
}}
>
diff --git a/extensions/cornerstone/src/components/ViewportColorbar/ViewportColorbarsContainer.tsx b/extensions/cornerstone/src/components/ViewportColorbar/ViewportColorbarsContainer.tsx
index 74eafd407..b4f0fc08b 100644
--- a/extensions/cornerstone/src/components/ViewportColorbar/ViewportColorbarsContainer.tsx
+++ b/extensions/cornerstone/src/components/ViewportColorbar/ViewportColorbarsContainer.tsx
@@ -2,7 +2,7 @@ import React, { useEffect, useState, useMemo, memo, useCallback } from 'react';
import { useSystem } from '@ohif/core';
import { ColorbarCustomization } from '../../types/Colorbar';
import type { ColorMapPreset } from '../../types/Colormap';
-import ViewportColorbar from './ViewportColorbar';
+import ViewportColorbar, { isHorizontal } from './ViewportColorbar';
import useViewportRendering from '../../hooks/useViewportRendering';
import { useViewportDisplaySets } from '../../hooks/useViewportDisplaySets';
type ViewportColorbarsContainerProps = {
@@ -74,10 +74,8 @@ const ViewportColorbarsContainer = memo(function ViewportColorbarsContainer({
return null;
}
- const isBottom = position === 'bottom';
-
const isSingleViewport = viewportDisplaySets.length === 1;
- const showFullList = isSingleViewport || !isBottom;
+ const showFullList = isSingleViewport || !isHorizontal(position);
const colorbarsToUse = showFullList
? colorbars
diff --git a/platform/core/src/hooks/useViewportMousePosition.ts b/platform/core/src/hooks/useViewportMousePosition.ts
index 60fa12c80..442cd5952 100644
--- a/platform/core/src/hooks/useViewportMousePosition.ts
+++ b/platform/core/src/hooks/useViewportMousePosition.ts
@@ -2,24 +2,33 @@ import { useState, useEffect } from 'react';
import { useViewportRef } from './useViewportRef';
import { useViewportSize } from './useViewportSize';
+interface NormalizedBox {
+ minX: number;
+ minY: number;
+ maxX: number;
+ maxY: number;
+}
+
interface MousePosition {
x: number;
y: number;
isInViewport: boolean;
+ relativeX: number;
relativeY: number; // Position as percentage from top (0-1)
- isInBottomPercentage: (percentage: number) => boolean;
+ isWithinNormalizedBox?: (normalizedBox: NormalizedBox) => boolean;
}
function useViewportMousePosition(viewportId: string): MousePosition {
const viewportRef = useViewportRef(viewportId);
- const { height, clientRect } = useViewportSize(viewportId);
+ const { width, height, clientRect } = useViewportSize(viewportId);
const [mousePosition, setMousePosition] = useState({
x: 0,
y: 0,
isInViewport: false,
relativeY: 0,
- isInBottomPercentage: (percentage: number) => false,
+ relativeX: 0,
+ isWithinNormalizedBox: (normalizedBox: NormalizedBox) => false,
});
useEffect(() => {
@@ -38,18 +47,25 @@ function useViewportMousePosition(viewportId: string): MousePosition {
const isInViewport = x >= 0 && x <= clientRect.width && y >= 0 && y <= clientRect.height;
+ const relativeX = Math.max(0, Math.min(1, x / width));
const relativeY = Math.max(0, Math.min(1, y / height));
- const isInBottomPercentage = (percentage: number) => {
- return relativeY >= 1 - percentage / 100;
+ const isWithinNormalizedBox = (normalizedBox: NormalizedBox) => {
+ return (
+ relativeX >= normalizedBox.minX &&
+ relativeX <= normalizedBox.maxX &&
+ relativeY >= normalizedBox.minY &&
+ relativeY <= normalizedBox.maxY
+ );
};
setMousePosition({
x,
y,
isInViewport,
+ relativeX,
relativeY,
- isInBottomPercentage,
+ isWithinNormalizedBox,
});
};
@@ -58,7 +74,7 @@ function useViewportMousePosition(viewportId: string): MousePosition {
return () => {
document.removeEventListener('mousemove', handleMouseMove);
};
- }, [viewportRef, height, clientRect]);
+ }, [viewportRef, height, clientRect, width]);
return mousePosition;
}
diff --git a/tests/SEGHydration.spec.ts b/tests/SEGHydration.spec.ts
index 82c18ef56..f6abea1cc 100644
--- a/tests/SEGHydration.spec.ts
+++ b/tests/SEGHydration.spec.ts
@@ -10,6 +10,8 @@ test.beforeEach(async ({ page }) => {
test('should hydrate SEG reports correctly', async ({ page }) => {
await page.getByTestId('side-panel-header-right').click();
await page.getByTestId('study-browser-thumbnail-no-image').dblclick();
+
+ await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.segHydration.segPreHydration);
await page.evaluate(() => {
@@ -32,5 +34,7 @@ test('should hydrate SEG reports correctly', async ({ page }) => {
});
await page.getByTestId('yes-hydrate-btn').click();
+
+ await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.segHydration.segPostHydration);
});
diff --git a/tests/SEGHydrationFromMPR.spec.ts b/tests/SEGHydrationFromMPR.spec.ts
index da5b19d3b..142385afc 100644
--- a/tests/SEGHydrationFromMPR.spec.ts
+++ b/tests/SEGHydrationFromMPR.spec.ts
@@ -13,19 +13,23 @@ test('should properly display MPR for MR', async ({ page }) => {
await page.getByTestId('Layout').click();
await page.getByTestId('MPR').click();
+ await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.segHydrationFromMPR.mprBeforeSEG);
await page.getByTestId('study-browser-thumbnail-no-image').dblclick();
+ await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.segHydrationFromMPR.mprAfterSEG);
await page.getByTestId('yes-hydrate-btn').click();
+ await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.segHydrationFromMPR.mprAfterSegHydrated);
await page.getByTestId('Layout').click();
await page.getByTestId('Axial Primary').click();
+ await page.waitForTimeout(5000);
await checkForScreenshot(
page,
page,