mirror of
https://github.com/Boof2015/prism.git
synced 2026-08-18 19:44:16 +02:00
ui/ux QoL improvements
This commit is contained in:
@@ -1895,6 +1895,7 @@ function createScopePopoutWindow(kind: ScopeKind, rawBounds?: WindowBounds): Bro
|
||||
autoHideMenuBar: true,
|
||||
title: `Prism ${SCOPE_LABELS[kind]}`,
|
||||
alwaysOnTop: getWindowStateStore().getPopoutAlwaysOnTop(kind),
|
||||
...(process.platform === 'darwin' ? { acceptFirstMouse: true } : {}),
|
||||
show: false,
|
||||
...getStaticWindowIconOptions(),
|
||||
webPreferences: {
|
||||
|
||||
@@ -20,6 +20,7 @@ import { getRendererWindowCapabilities } from './windowCapabilities'
|
||||
|
||||
export default function App(): JSX.Element {
|
||||
const [toolbarVisible, setToolbarVisible] = useState(false)
|
||||
const [measurementActive, setMeasurementActive] = useState(false)
|
||||
const [settingsPanelHeight, setSettingsPanelHeight] = useState(0)
|
||||
const [bottomBarHeight, setBottomBarHeight] = useState(0)
|
||||
const [trayReady, setTrayReady] = useState(false)
|
||||
@@ -302,13 +303,13 @@ export default function App(): JSX.Element {
|
||||
onMouseUp={useNativeDragRegions ? undefined : handleAltDragEnd}
|
||||
>
|
||||
<div
|
||||
className={`prism-toolbar-layer ${toolbarVisible ? 'is-visible' : ''}`.trim()}
|
||||
className={`prism-toolbar-layer ${toolbarVisible && !measurementActive ? 'is-visible' : ''}`.trim()}
|
||||
>
|
||||
<Toolbar onOpenSettings={handleToggleSettings} settingsOpen={settingsOpen} />
|
||||
</div>
|
||||
|
||||
<div className="prism-strip-region">
|
||||
<Strip />
|
||||
<Strip onMeasurementActiveChange={setMeasurementActive} />
|
||||
</div>
|
||||
|
||||
<ScopePopoutBridge />
|
||||
|
||||
@@ -55,6 +55,7 @@ interface ScopeModuleProps {
|
||||
theme?: ScopeModuleTheme
|
||||
settings?: ScopeSettings[ScopeKind]
|
||||
frameScheduler?: FrameScheduler
|
||||
onMeasurementActiveChange?: (active: boolean) => void
|
||||
dataSource?:
|
||||
| SpectrumAnalyzerDataSource
|
||||
| OscilloscopeDataSource
|
||||
@@ -400,6 +401,7 @@ export default function ScopeModule({
|
||||
theme,
|
||||
settings,
|
||||
frameScheduler,
|
||||
onMeasurementActiveChange,
|
||||
dataSource,
|
||||
}: ScopeModuleProps): JSX.Element {
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
@@ -447,6 +449,7 @@ export default function ScopeModule({
|
||||
? visualizer as ScopeMeasurementSource
|
||||
: null
|
||||
},
|
||||
onActiveChange: onMeasurementActiveChange,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -9,7 +9,11 @@ import { usePerformanceStore } from '../stores/performanceStore'
|
||||
import { FrameScheduler } from '../visualizers/frameScheduler'
|
||||
import { getRendererWindowCapabilities } from '../windowCapabilities'
|
||||
|
||||
export default function Strip(): JSX.Element {
|
||||
interface StripProps {
|
||||
onMeasurementActiveChange?: (active: boolean) => void
|
||||
}
|
||||
|
||||
export default function Strip({ onMeasurementActiveChange }: StripProps): JSX.Element {
|
||||
const scopeOrder = useSettingsStore((s) => s.scopeOrder)
|
||||
const hiddenScopes = useSettingsStore((s) => s.hiddenScopes)
|
||||
const scopePopouts = useSettingsStore((s) => s.scopePopouts)
|
||||
@@ -268,6 +272,7 @@ export default function Strip(): JSX.Element {
|
||||
<ScopeModule
|
||||
scopeKind={kind}
|
||||
frameScheduler={frameScheduler}
|
||||
onMeasurementActiveChange={onMeasurementActiveChange}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -62,6 +62,7 @@ export default function ScopePopoutWindow({ scopeKind }: ScopePopoutWindowProps)
|
||||
const [snapshot, setSnapshot] = useState<ScopePopoutSnapshot<ScopeKind> | null>(null)
|
||||
const [isAlwaysOnTop, setIsAlwaysOnTop] = useState(false)
|
||||
const [cursorInsideWindow, setCursorInsideWindow] = useState(false)
|
||||
const [measurementActive, setMeasurementActive] = useState(false)
|
||||
const prevMiniSettingsOpenRef = useRef(false)
|
||||
const frameTarget = usePerformanceStore((s) => s.frameTarget)
|
||||
const miniSettingsOpen = useUiStore((s) => s.settingsOpen)
|
||||
@@ -230,6 +231,7 @@ export default function ScopePopoutWindow({ scopeKind }: ScopePopoutWindowProps)
|
||||
'scope-popout__chrome',
|
||||
miniSettingsOpen ? 'is-expanded' : '',
|
||||
cursorInsideWindow ? 'is-cursor-inside' : '',
|
||||
measurementActive ? 'is-measuring' : '',
|
||||
].join(' ').trim()}
|
||||
>
|
||||
<header className={`scope-popout__header ${useWindowManagerDragRegions ? 'is-native-drag' : ''}`.trim()}>
|
||||
@@ -294,6 +296,7 @@ export default function ScopePopoutWindow({ scopeKind }: ScopePopoutWindowProps)
|
||||
settings={effectiveSettings}
|
||||
frameScheduler={frameScheduler}
|
||||
dataSource={dataSource}
|
||||
onMeasurementActiveChange={setMeasurementActive}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2284,7 +2284,7 @@ button.toolbar__version:hover {
|
||||
|
||||
.scope-popout__viewport:hover .scope-popout__chrome,
|
||||
.scope-popout__chrome:hover,
|
||||
.scope-popout__chrome:focus-within,
|
||||
.scope-popout__chrome:has(:focus-visible),
|
||||
.scope-popout__chrome.is-cursor-inside,
|
||||
.scope-popout__chrome.is-expanded {
|
||||
max-height: 58px;
|
||||
@@ -2293,6 +2293,13 @@ button.toolbar__version:hover {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.scope-popout__viewport .scope-popout__chrome.is-measuring {
|
||||
max-height: 0;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
|
||||
.scope-popout__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -4212,6 +4212,38 @@ test('main and detached windows keep frameless Prism chrome while enabling snap-
|
||||
assert.doesNotMatch(nowPlayingSource, /WindowResizeOverlay/)
|
||||
})
|
||||
|
||||
test('detached scope interactions accept the first macOS click and hide chrome during measurement', async () => {
|
||||
const mainSource = await readFile(join(process.cwd(), 'src', 'main', 'index.ts'), 'utf8')
|
||||
const scopeModuleSource = await readFile(join(process.cwd(), 'src', 'renderer', 'components', 'ScopeModule.tsx'), 'utf8')
|
||||
const popoutSource = await readFile(join(process.cwd(), 'src', 'renderer', 'popouts', 'ScopePopoutWindow.tsx'), 'utf8')
|
||||
const stylesSource = await readFile(join(process.cwd(), 'src', 'renderer', 'styles', 'globals.css'), 'utf8')
|
||||
const mainWindowFactorySource = mainSource.slice(
|
||||
mainSource.indexOf('function createMainWindow'),
|
||||
mainSource.indexOf('function createScopePopoutWindow'),
|
||||
)
|
||||
|
||||
assert.match(mainSource, /function createScopePopoutWindow\([\s\S]*?process\.platform === 'darwin' \? \{ acceptFirstMouse: true \} : \{\}/)
|
||||
assert.doesNotMatch(mainWindowFactorySource, /acceptFirstMouse/)
|
||||
assert.match(scopeModuleSource, /onMeasurementActiveChange\?: \(active: boolean\) => void/)
|
||||
assert.match(scopeModuleSource, /onActiveChange: onMeasurementActiveChange/)
|
||||
assert.match(popoutSource, /measurementActive \? 'is-measuring' : ''/)
|
||||
assert.match(popoutSource, /onMeasurementActiveChange=\{setMeasurementActive\}/)
|
||||
assert.match(stylesSource, /\.scope-popout__chrome:has\(:focus-visible\)/)
|
||||
assert.doesNotMatch(stylesSource, /\.scope-popout__chrome:focus-within/)
|
||||
assert.match(stylesSource, /\.scope-popout__viewport \.scope-popout__chrome\.is-measuring \{[\s\S]*?max-height: 0;[\s\S]*?opacity: 0;[\s\S]*?pointer-events: none;[\s\S]*?transform: translateY\(-8px\);[\s\S]*?\}/)
|
||||
})
|
||||
|
||||
test('main window hides its toolbar while a docked scope measurement is active', async () => {
|
||||
const appSource = await readFile(join(process.cwd(), 'src', 'renderer', 'App.tsx'), 'utf8')
|
||||
const stripSource = await readFile(join(process.cwd(), 'src', 'renderer', 'components', 'Strip.tsx'), 'utf8')
|
||||
|
||||
assert.match(stripSource, /onMeasurementActiveChange\?: \(active: boolean\) => void/)
|
||||
assert.match(stripSource, /onMeasurementActiveChange=\{onMeasurementActiveChange\}/)
|
||||
assert.match(appSource, /const \[measurementActive, setMeasurementActive\] = useState\(false\)/)
|
||||
assert.match(appSource, /toolbarVisible && !measurementActive \? 'is-visible' : ''/)
|
||||
assert.match(appSource, /<Strip onMeasurementActiveChange=\{setMeasurementActive\} \/>/)
|
||||
})
|
||||
|
||||
test('programmatic top/bottom reposition flushes fresh bounds through persistence channels', async () => {
|
||||
const mainSource = await readFile(join(process.cwd(), 'src', 'main', 'index.ts'), 'utf8')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user