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