mirror of
https://github.com/purocean/yn.git
synced 2026-10-16 07:26:07 +08:00
fix: sync native titlebar window state
This commit is contained in:
@@ -6,8 +6,15 @@ const mocks = vi.hoisted(() => ({
|
||||
isSaved: undefined as any,
|
||||
win: undefined as any,
|
||||
listeners: new Map<string, Function>(),
|
||||
hooks: new Map<string, Function>(),
|
||||
isElectron: true,
|
||||
isMacOS: true,
|
||||
colorScheme: 'light',
|
||||
}))
|
||||
|
||||
vi.mock('@fe/core/hook', () => ({
|
||||
registerHook: (name: string, handler: Function) => mocks.hooks.set(name, handler),
|
||||
removeHook: (name: string) => mocks.hooks.delete(name),
|
||||
}))
|
||||
|
||||
vi.mock('@fe/support/store', () => ({
|
||||
@@ -32,6 +39,10 @@ vi.mock('@fe/services/i18n', () => ({
|
||||
useI18n: () => ({ t: (key: string) => key }),
|
||||
}))
|
||||
|
||||
vi.mock('@fe/services/theme', () => ({
|
||||
getColorScheme: () => mocks.colorScheme,
|
||||
}))
|
||||
|
||||
import { isWindowAlwaysOnTop, toggleWindowAlwaysOnTop, useWindowState } from '../window-state'
|
||||
|
||||
const TestComponent = defineComponent({
|
||||
@@ -48,11 +59,14 @@ beforeEach(() => {
|
||||
})
|
||||
mocks.isSaved = ref(false)
|
||||
mocks.listeners.clear()
|
||||
mocks.hooks.clear()
|
||||
mocks.isElectron = true
|
||||
mocks.isMacOS = true
|
||||
mocks.colorScheme = 'light'
|
||||
mocks.win = {
|
||||
isAlwaysOnTop: vi.fn(() => false),
|
||||
setAlwaysOnTop: vi.fn(),
|
||||
setTitleBarOverlay: vi.fn(),
|
||||
setDocumentEdited: vi.fn(),
|
||||
on: vi.fn((name: string, handler: Function) => mocks.listeners.set(name, handler)),
|
||||
removeListener: vi.fn((name: string) => mocks.listeners.delete(name)),
|
||||
@@ -81,6 +95,7 @@ describe('window state', () => {
|
||||
|
||||
mocks.listeners.get('enter-full-screen')?.()
|
||||
expect(mocks.storeState.isFullscreen).toBe(true)
|
||||
expect(mocks.win.setAlwaysOnTop).toHaveBeenLastCalledWith(false)
|
||||
mocks.listeners.get('leave-full-screen')?.()
|
||||
expect(mocks.storeState.isFullscreen).toBe(false)
|
||||
|
||||
@@ -94,6 +109,28 @@ describe('window state', () => {
|
||||
expect(mocks.win.removeListener).toHaveBeenCalledWith('leave-full-screen', expect.any(Function))
|
||||
})
|
||||
|
||||
test('keeps native window controls legible when the app theme changes', () => {
|
||||
mocks.isMacOS = false
|
||||
const wrapper = mount(TestComponent)
|
||||
|
||||
expect(mocks.win.setTitleBarOverlay).toHaveBeenLastCalledWith({
|
||||
color: '#00000000',
|
||||
symbolColor: '#151518',
|
||||
height: 30,
|
||||
})
|
||||
|
||||
mocks.colorScheme = 'dark'
|
||||
mocks.hooks.get('THEME_CHANGE')?.()
|
||||
expect(mocks.win.setTitleBarOverlay).toHaveBeenLastCalledWith({
|
||||
color: '#00000000',
|
||||
symbolColor: '#e6e6e6',
|
||||
height: 30,
|
||||
})
|
||||
|
||||
wrapper.unmount()
|
||||
expect(mocks.hooks.has('THEME_CHANGE')).toBe(false)
|
||||
})
|
||||
|
||||
test('keeps the browser unsaved-document prompt', () => {
|
||||
mocks.isElectron = false
|
||||
const wrapper = mount(TestComponent)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { computed, onBeforeUnmount, onMounted, ref, toRefs, watch } from 'vue'
|
||||
import { registerHook, removeHook } from '@fe/core/hook'
|
||||
import { getElectronRemote, isElectron, isMacOS, nodeRequire } from '@fe/support/env'
|
||||
import store from '@fe/support/store'
|
||||
import { useI18n } from '@fe/services/i18n'
|
||||
import { getColorScheme } from '@fe/services/theme'
|
||||
|
||||
let win: ReturnType<ReturnType<typeof getElectronRemote>['getCurrentWindow']> | null = null
|
||||
|
||||
@@ -11,6 +13,23 @@ function updateAlwaysOnTop () {
|
||||
isWindowAlwaysOnTop.value = !!win?.isAlwaysOnTop()
|
||||
}
|
||||
|
||||
function clearAlwaysOnTop () {
|
||||
if (win?.isAlwaysOnTop()) {
|
||||
win.setAlwaysOnTop(false)
|
||||
}
|
||||
updateAlwaysOnTop()
|
||||
}
|
||||
|
||||
function updateTitleBarOverlay () {
|
||||
if (win && !isMacOS) {
|
||||
win.setTitleBarOverlay({
|
||||
color: '#00000000',
|
||||
symbolColor: getColorScheme() === 'dark' ? '#e6e6e6' : '#151518',
|
||||
height: 30,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function toggleWindowAlwaysOnTop () {
|
||||
if (!win) {
|
||||
return
|
||||
@@ -27,6 +46,7 @@ export function useWindowState () {
|
||||
|
||||
function handleFullscreenEnter () {
|
||||
store.state.isFullscreen = true
|
||||
clearAlwaysOnTop()
|
||||
}
|
||||
|
||||
function handleFullscreenLeave () {
|
||||
@@ -34,10 +54,7 @@ export function useWindowState () {
|
||||
}
|
||||
|
||||
function handleMaximize () {
|
||||
if (win?.isAlwaysOnTop()) {
|
||||
win.setAlwaysOnTop(false)
|
||||
}
|
||||
updateAlwaysOnTop()
|
||||
clearAlwaysOnTop()
|
||||
}
|
||||
|
||||
function clean () {
|
||||
@@ -54,6 +71,8 @@ export function useWindowState () {
|
||||
win.removeListener('leave-full-screen', handleFullscreenLeave)
|
||||
}
|
||||
|
||||
removeHook('THEME_CHANGE', updateTitleBarOverlay)
|
||||
|
||||
win = null
|
||||
isWindowAlwaysOnTop.value = false
|
||||
}
|
||||
@@ -67,6 +86,8 @@ export function useWindowState () {
|
||||
if (nodeRequire) {
|
||||
win = getElectronRemote().getCurrentWindow()
|
||||
updateAlwaysOnTop()
|
||||
updateTitleBarOverlay()
|
||||
registerHook('THEME_CHANGE', updateTitleBarOverlay)
|
||||
win.on('maximize', handleMaximize)
|
||||
win.on('always-on-top-changed', updateAlwaysOnTop)
|
||||
win.on('enter-full-screen', handleFullscreenEnter)
|
||||
|
||||
Reference in New Issue
Block a user