Skip to content

Commit 30e8e95

Browse files
authored
🐛 修复 Windows 上亚克力材质造成的窗口拖动性能问题 (#204)
1 parent 11c7915 commit 30e8e95

File tree

8 files changed

+128
-7
lines changed

8 files changed

+128
-7
lines changed

src-tauri/tauri.conf.json

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"$schema": "https://schema.tauri.app/config/2",
23
"productName": "matcha",
34
"version": "../package.json",
45
"identifier": "com.akirami.matcha",
@@ -19,11 +20,7 @@
1920
"minWidth": 620,
2021
"center": true,
2122
"decorations": false,
22-
"transparent": true,
23-
"windowEffects": {
24-
"effects": ["acrylic", "blur", "hudWindow"],
25-
"state": "active"
26-
}
23+
"transparent": true
2724
}
2825
],
2926
"security": {

src/App.vue

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ provide('themeMode', themeMode)
2525
2626
onBeforeMount(async () => {
2727
await attachConsole()
28+
await setWindowEffect()
29+
await setAcrylicWindowEffect(general.applyAcrylicWindowEffects)
2830
})
2931
</script>
3032

src/auto-imports.d.ts

+12
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ declare global {
7272
const isReactive: (typeof import('vue'))['isReactive']
7373
const isReadonly: (typeof import('vue'))['isReadonly']
7474
const isRef: (typeof import('vue'))['isRef']
75+
const isWindows11: (typeof import('./utils/utils'))['isWindows11']
7576
const logger: typeof import('@tauri-apps/plugin-log')
7677
const makeDestructurable: (typeof import('@vueuse/core'))['makeDestructurable']
7778
const mapActions: (typeof import('pinia'))['mapActions']
@@ -122,8 +123,11 @@ declare global {
122123
const resolveRef: (typeof import('@vueuse/core'))['resolveRef']
123124
const resolveUnref: (typeof import('@vueuse/core'))['resolveUnref']
124125
const roleCheck: (typeof import('./utils/chat'))['roleCheck']
126+
const setAcrylicWindowEffect: (typeof import('./utils/window'))['setAcrylicWindowEffect']
125127
const setActivePinia: (typeof import('pinia'))['setActivePinia']
126128
const setMapStoreSuffix: (typeof import('pinia'))['setMapStoreSuffix']
129+
const setTransparentBackground: (typeof import('./utils/utils'))['setTransparentBackground']
130+
const setWindowEffect: (typeof import('./utils/window'))['setWindowEffect']
127131
const shallowReactive: (typeof import('vue'))['shallowReactive']
128132
const shallowReadonly: (typeof import('vue'))['shallowReadonly']
129133
const shallowRef: (typeof import('vue'))['shallowRef']
@@ -426,6 +430,7 @@ declare module 'vue' {
426430
readonly isReactive: UnwrapRef<(typeof import('vue'))['isReactive']>
427431
readonly isReadonly: UnwrapRef<(typeof import('vue'))['isReadonly']>
428432
readonly isRef: UnwrapRef<(typeof import('vue'))['isRef']>
433+
readonly isWindows11: UnwrapRef<(typeof import('./utils/utils'))['isWindows11']>
429434
readonly logger: UnwrapRef<typeof import('@tauri-apps/plugin-log')>
430435
readonly makeDestructurable: UnwrapRef<(typeof import('@vueuse/core'))['makeDestructurable']>
431436
readonly mapActions: UnwrapRef<(typeof import('pinia'))['mapActions']>
@@ -476,8 +481,11 @@ declare module 'vue' {
476481
readonly resolveRef: UnwrapRef<(typeof import('@vueuse/core'))['resolveRef']>
477482
readonly resolveUnref: UnwrapRef<(typeof import('@vueuse/core'))['resolveUnref']>
478483
readonly roleCheck: UnwrapRef<(typeof import('./utils/chat'))['roleCheck']>
484+
readonly setAcrylicWindowEffect: UnwrapRef<(typeof import('./utils/window'))['setAcrylicWindowEffect']>
479485
readonly setActivePinia: UnwrapRef<(typeof import('pinia'))['setActivePinia']>
480486
readonly setMapStoreSuffix: UnwrapRef<(typeof import('pinia'))['setMapStoreSuffix']>
487+
readonly setTransparentBackground: UnwrapRef<(typeof import('./utils/utils'))['setTransparentBackground']>
488+
readonly setWindowEffect: UnwrapRef<(typeof import('./utils/window'))['setWindowEffect']>
481489
readonly shallowReactive: UnwrapRef<(typeof import('vue'))['shallowReactive']>
482490
readonly shallowReadonly: UnwrapRef<(typeof import('vue'))['shallowReadonly']>
483491
readonly shallowRef: UnwrapRef<(typeof import('vue'))['shallowRef']>
@@ -758,6 +766,7 @@ declare module '@vue/runtime-core' {
758766
readonly isReactive: UnwrapRef<(typeof import('vue'))['isReactive']>
759767
readonly isReadonly: UnwrapRef<(typeof import('vue'))['isReadonly']>
760768
readonly isRef: UnwrapRef<(typeof import('vue'))['isRef']>
769+
readonly isWindows11: UnwrapRef<(typeof import('./utils/utils'))['isWindows11']>
761770
readonly logger: UnwrapRef<typeof import('@tauri-apps/plugin-log')>
762771
readonly makeDestructurable: UnwrapRef<(typeof import('@vueuse/core'))['makeDestructurable']>
763772
readonly mapActions: UnwrapRef<(typeof import('pinia'))['mapActions']>
@@ -808,8 +817,11 @@ declare module '@vue/runtime-core' {
808817
readonly resolveRef: UnwrapRef<(typeof import('@vueuse/core'))['resolveRef']>
809818
readonly resolveUnref: UnwrapRef<(typeof import('@vueuse/core'))['resolveUnref']>
810819
readonly roleCheck: UnwrapRef<(typeof import('./utils/chat'))['roleCheck']>
820+
readonly setAcrylicWindowEffect: UnwrapRef<(typeof import('./utils/window'))['setAcrylicWindowEffect']>
811821
readonly setActivePinia: UnwrapRef<(typeof import('pinia'))['setActivePinia']>
812822
readonly setMapStoreSuffix: UnwrapRef<(typeof import('pinia'))['setMapStoreSuffix']>
823+
readonly setTransparentBackground: UnwrapRef<(typeof import('./utils/utils'))['setTransparentBackground']>
824+
readonly setWindowEffect: UnwrapRef<(typeof import('./utils/window'))['setWindowEffect']>
813825
readonly shallowReactive: UnwrapRef<(typeof import('vue'))['shallowReactive']>
814826
readonly shallowReadonly: UnwrapRef<(typeof import('vue'))['shallowReadonly']>
815827
readonly shallowRef: UnwrapRef<(typeof import('vue'))['shallowRef']>

src/stores/general-settings.ts

+7
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ interface GeneralSettings {
66
autoUpdate: boolean
77
enbaleSuperUser: boolean
88
showRecallMessage: boolean
9+
applyAcrylicWindowEffects: boolean
910
}
1011

1112
export const useGeneralSettingsStore = defineStore(
@@ -17,13 +18,19 @@ export const useGeneralSettingsStore = defineStore(
1718
const autoUpdate = ref<GeneralSettings['autoUpdate']>(true)
1819
const enbaleSuperUser = ref<GeneralSettings['enbaleSuperUser']>(false)
1920
const showRecallMessage = ref<GeneralSettings['showRecallMessage']>(true)
21+
const applyAcrylicWindowEffects = ref<GeneralSettings['applyAcrylicWindowEffects']>(false)
22+
23+
watch(applyAcrylicWindowEffects, async (enable) => {
24+
await setAcrylicWindowEffect(enable)
25+
})
2026

2127
return {
2228
theme,
2329
autoUpdate,
2430
sendMessageShortcut,
2531
enbaleSuperUser,
2632
showRecallMessage,
33+
applyAcrylicWindowEffects,
2734
}
2835
},
2936

src/styles/main.css

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
html, body {
1313
font-family: "SourceHanSans", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", "Arial", "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
1414

15-
@apply m-0 select-none bg-transparent p-0 antialiased overflow-hidden;
15+
@apply m-0 select-none p-0 antialiased overflow-hidden;
1616
}
1717

1818
::selection {

src/utils/utils.ts

+38
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,41 @@ export function formatBytes(bytes: number, decimals: number = 2): string {
9090
i = Math.floor(Math.log(bytes) / Math.log(k))
9191
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]
9292
}
93+
94+
interface NavigatorUAData {
95+
getHighEntropyValues(hints: string[]): Promise<{ platformVersion: string }>
96+
platform: string
97+
}
98+
99+
interface NavigatorUA extends Navigator {
100+
readonly userAgentData?: NavigatorUAData
101+
}
102+
103+
// https://learn.microsoft.com/en-us/microsoft-edge/web-platform/how-to-detect-win11#sample-code-for-detecting-windows-11
104+
export async function isWindows11(): Promise<boolean> {
105+
const { userAgentData } = navigator as NavigatorUA
106+
107+
if (!userAgentData) {
108+
return false
109+
}
110+
111+
try {
112+
const ua = await userAgentData.getHighEntropyValues(['platformVersion'])
113+
if (userAgentData.platform === 'Windows') {
114+
const majorPlatformVersion = parseInt(ua.platformVersion.split('.')[0])
115+
return majorPlatformVersion >= 13
116+
}
117+
} catch {
118+
return false
119+
}
120+
121+
return false
122+
}
123+
124+
export function setTransparentBackground(enable: boolean = true): void {
125+
if (enable) {
126+
document.body.classList.add('bg-transparent')
127+
} else {
128+
document.body.classList.remove('bg-transparent')
129+
}
130+
}

src/utils/window.ts

+45-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { WebviewWindow } from '@tauri-apps/api/webviewWindow'
1+
import { WebviewWindow, getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'
2+
import { Effect, EffectState } from '@tauri-apps/api/window'
3+
import { type as getOsType } from '@tauri-apps/plugin-os'
4+
5+
import type { Effects } from '@tauri-apps/api/window'
26

37
const screenWidth = window.screen.width
48
const screenHeight = window.screen.height
@@ -44,3 +48,43 @@ export async function createPreviewWindow(
4448
...getWindowSize(width, height),
4549
})
4650
}
51+
52+
const osEffects = {
53+
windows: { effects: [Effect.Acrylic, Effect.Blur] },
54+
macos: { effects: [Effect.HudWindow], state: EffectState.Active },
55+
} as { 'windows': Effects; 'macos': Effects; [key: string]: Effects | undefined }
56+
57+
export async function setWindowEffect(enable: boolean = true): Promise<void> {
58+
const osType = getOsType()
59+
const appWindow = getCurrentWebviewWindow()
60+
61+
const effects = osEffects[osType]
62+
if (enable) {
63+
if (effects) {
64+
await appWindow.setEffects(effects)
65+
}
66+
} else {
67+
await appWindow.clearEffects()
68+
}
69+
if (osType !== 'linux') {
70+
setTransparentBackground(enable)
71+
}
72+
}
73+
74+
export async function setAcrylicWindowEffect(enable: boolean = true): Promise<void> {
75+
const osType = getOsType()
76+
if (osType !== 'windows') {
77+
return
78+
}
79+
const appWindow = getCurrentWebviewWindow()
80+
if (enable) {
81+
await appWindow.setEffects(osEffects.windows)
82+
setTransparentBackground(true)
83+
} else if (await isWindows11()) {
84+
await appWindow.setEffects({ effects: [Effect.Mica] })
85+
setTransparentBackground(true)
86+
} else {
87+
await appWindow.clearEffects()
88+
setTransparentBackground(false)
89+
}
90+
}

src/views/settings/general.vue

+21
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ meta:
77
</route>
88

99
<script setup lang="ts">
10+
import { type as getOsType } from '@tauri-apps/plugin-os'
1011
import { toTypedSchema } from '@vee-validate/zod'
12+
import { TriangleAlert } from 'lucide-vue-next'
1113
import { useForm } from 'vee-validate'
1214
import * as z from 'zod'
1315
@@ -24,6 +26,7 @@ const generalSettingsSchema = toTypedSchema(
2426
autoUpdate: z.boolean(),
2527
enbaleSuperUser: z.boolean(),
2628
showRecallMessage: z.boolean(),
29+
applyAcrylicWindowEffects: z.boolean(),
2730
})
2831
)
2932
@@ -53,6 +56,8 @@ const themeOptions = [
5356
let themeMode = inject('themeMode')
5457
5558
defineExpose({ resetForm })
59+
60+
const osType = getOsType()
5661
</script>
5762

5863
<template>
@@ -75,6 +80,22 @@ defineExpose({ resetForm })
7580
</RadioGroup>
7681
</FormItem>
7782
</FormField>
83+
<FormField v-if="osType === 'windows'" v-slot="{ value, handleChange }" name="applyAcrylicWindowEffects">
84+
<FormItem class="max-w-120 flex flex-row items-center justify-between rounded-lg">
85+
<div class="space-y-0.5">
86+
<FormLabel>亚力克模糊效果</FormLabel>
87+
<FormDescription v-auto-animate>
88+
是否为窗口应用亚力克模糊效果
89+
<p v-if="value" class="mt-2 flex flex-row text-xs text-red-400">
90+
<TriangleAlert class="mr-1 size-4" />在部分设备上可能导致调整窗口大小或拖动窗口时的性能问题
91+
</p>
92+
</FormDescription>
93+
</div>
94+
<FormControl>
95+
<Switch :checked="value" aria-readonly @update:checked="handleChange" />
96+
</FormControl>
97+
</FormItem>
98+
</FormField>
7899
<FormField v-slot="{ componentField }" name="sendMessageShortcut">
79100
<FormItem>
80101
<FormLabel>发送消息</FormLabel>

0 commit comments

Comments
 (0)