Skip to content

Commit 6b46e10

Browse files
authored
fix: 修复 Windows 平台窗口被任务栏遮挡的问题 (#930)
1 parent 1e241f5 commit 6b46e10

8 files changed

Lines changed: 198 additions & 101 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/src/plugins/window/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,6 @@ tauri-plugin.workspace = true
1616

1717
[target."cfg(target_os = \"macos\")".dependencies]
1818
tauri-nspanel.workspace = true
19+
20+
[target."cfg(target_os = \"windows\")".dependencies]
21+
windows = { version = "0.61", features = ["Win32_UI_WindowsAndMessaging", "Win32_Foundation"] }

src-tauri/src/plugins/window/src/commands/common.rs

Lines changed: 0 additions & 26 deletions
This file was deleted.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
use tauri::{AppHandle, Runtime, WebviewWindow, command};
2+
3+
#[command]
4+
pub async fn show_window<R: Runtime>(_app_handle: AppHandle<R>, window: WebviewWindow<R>) {
5+
let _ = window.show();
6+
let _ = window.unminimize();
7+
let _ = window.set_focus();
8+
}
9+
10+
#[command]
11+
pub async fn hide_window<R: Runtime>(_app_handle: AppHandle<R>, window: WebviewWindow<R>) {
12+
let _ = window.hide();
13+
}
14+
15+
#[command]
16+
pub async fn set_always_on_top<R: Runtime>(
17+
_app_handle: AppHandle<R>,
18+
window: WebviewWindow<R>,
19+
always_on_top: bool,
20+
) {
21+
if always_on_top {
22+
let _ = window.set_always_on_bottom(false);
23+
let _ = window.set_always_on_top(true);
24+
} else {
25+
let _ = window.set_always_on_top(false);
26+
let _ = window.set_always_on_bottom(true);
27+
}
28+
}
29+
30+
#[command]
31+
pub async fn set_taskbar_visibility<R: Runtime>(window: WebviewWindow<R>, visible: bool) {
32+
let _ = window.set_skip_taskbar(!visible);
33+
}

src-tauri/src/plugins/window/src/commands/macos.rs

Lines changed: 47 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,19 @@
11
#![allow(deprecated)]
2-
use super::{is_main_window, shared_hide_window, shared_set_always_on_top, shared_show_window};
32
use crate::MAIN_WINDOW_LABEL;
43
use tauri::{AppHandle, Runtime, WebviewWindow, command};
54
use tauri_nspanel::{CollectionBehavior, ManagerExt, PanelLevel};
65

7-
pub enum MacOSPanelStatus {
6+
enum MacOSPanelStatus {
87
Show,
98
Hide,
109
SetAlwaysOnTop(bool),
1110
}
1211

13-
#[command]
14-
pub async fn show_window<R: Runtime>(app_handle: AppHandle<R>, window: WebviewWindow<R>) {
15-
if is_main_window(&window) {
16-
set_macos_panel(&app_handle, &window, MacOSPanelStatus::Show);
17-
} else {
18-
shared_show_window(&app_handle, &window);
19-
}
20-
}
21-
22-
#[command]
23-
pub async fn hide_window<R: Runtime>(app_handle: AppHandle<R>, window: WebviewWindow<R>) {
24-
if is_main_window(&window) {
25-
set_macos_panel(&app_handle, &window, MacOSPanelStatus::Hide);
26-
} else {
27-
shared_hide_window(&app_handle, &window);
28-
}
12+
fn is_main_window<R: Runtime>(window: &WebviewWindow<R>) -> bool {
13+
window.label() == MAIN_WINDOW_LABEL
2914
}
3015

31-
#[command]
32-
pub async fn set_always_on_top<R: Runtime>(
33-
app_handle: AppHandle<R>,
34-
window: WebviewWindow<R>,
35-
always_on_top: bool,
36-
) {
37-
if is_main_window(&window) {
38-
set_macos_panel(
39-
&app_handle,
40-
&window,
41-
MacOSPanelStatus::SetAlwaysOnTop(always_on_top),
42-
);
43-
} else {
44-
shared_set_always_on_top(&app_handle, &window, always_on_top);
45-
}
46-
}
47-
48-
pub fn set_macos_panel<R: Runtime>(
16+
fn set_macos_panel<R: Runtime>(
4917
app_handle: &AppHandle<R>,
5018
window: &WebviewWindow<R>,
5119
status: MacOSPanelStatus,
@@ -91,6 +59,49 @@ pub fn set_macos_panel<R: Runtime>(
9159
}
9260
}
9361

62+
#[command]
63+
pub async fn show_window<R: Runtime>(app_handle: AppHandle<R>, window: WebviewWindow<R>) {
64+
if is_main_window(&window) {
65+
set_macos_panel(&app_handle, &window, MacOSPanelStatus::Show);
66+
} else {
67+
let _ = window.show();
68+
let _ = window.unminimize();
69+
let _ = window.set_focus();
70+
}
71+
}
72+
73+
#[command]
74+
pub async fn hide_window<R: Runtime>(app_handle: AppHandle<R>, window: WebviewWindow<R>) {
75+
if is_main_window(&window) {
76+
set_macos_panel(&app_handle, &window, MacOSPanelStatus::Hide);
77+
} else {
78+
let _ = window.hide();
79+
}
80+
}
81+
82+
#[command]
83+
pub async fn set_always_on_top<R: Runtime>(
84+
app_handle: AppHandle<R>,
85+
window: WebviewWindow<R>,
86+
always_on_top: bool,
87+
) {
88+
if is_main_window(&window) {
89+
return set_macos_panel(
90+
&app_handle,
91+
&window,
92+
MacOSPanelStatus::SetAlwaysOnTop(always_on_top),
93+
);
94+
}
95+
96+
if always_on_top {
97+
let _ = window.set_always_on_bottom(false);
98+
let _ = window.set_always_on_top(true);
99+
} else {
100+
let _ = window.set_always_on_top(false);
101+
let _ = window.set_always_on_bottom(true);
102+
}
103+
}
104+
94105
#[command]
95106
pub async fn set_taskbar_visibility<R: Runtime>(app_handle: AppHandle<R>, visible: bool) {
96107
let _ = app_handle.set_dock_visibility(visible);
Lines changed: 18 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,25 @@
1-
use tauri::{AppHandle, Manager, Runtime, WebviewWindow, async_runtime::spawn};
1+
use tauri::{AppHandle, Manager, async_runtime::spawn};
22

33
pub static MAIN_WINDOW_LABEL: &str = "main";
44
pub static PREFERENCE_WINDOW_LABEL: &str = "preference";
55

66
#[cfg(target_os = "macos")]
77
mod macos;
88

9-
#[cfg(not(target_os = "macos"))]
10-
mod common;
9+
#[cfg(target_os = "windows")]
10+
mod windows;
11+
12+
#[cfg(target_os = "linux")]
13+
mod linux;
1114

1215
#[cfg(target_os = "macos")]
1316
pub use macos::*;
1417

15-
#[cfg(not(target_os = "macos"))]
16-
pub use common::*;
18+
#[cfg(target_os = "windows")]
19+
pub use windows::*;
1720

18-
pub fn is_main_window<R: Runtime>(window: &WebviewWindow<R>) -> bool {
19-
window.label() == MAIN_WINDOW_LABEL
20-
}
21-
22-
fn shared_show_window<R: Runtime>(_app_handle: &AppHandle<R>, window: &WebviewWindow<R>) {
23-
let _ = window.show();
24-
let _ = window.unminimize();
25-
let _ = window.set_focus();
26-
}
27-
28-
fn shared_hide_window<R: Runtime>(_app_handle: &AppHandle<R>, window: &WebviewWindow<R>) {
29-
let _ = window.hide();
30-
}
31-
32-
fn shared_set_always_on_top<R: Runtime>(
33-
_app_handle: &AppHandle<R>,
34-
window: &WebviewWindow<R>,
35-
always_on_top: bool,
36-
) {
37-
if always_on_top {
38-
let _ = window.set_always_on_bottom(false);
39-
let _ = window.set_always_on_top(true);
40-
} else {
41-
let _ = window.set_always_on_top(false);
42-
let _ = window.set_always_on_bottom(true);
43-
}
44-
}
21+
#[cfg(target_os = "linux")]
22+
pub use linux::*;
4523

4624
pub fn show_main_window(app_handle: &AppHandle) {
4725
show_window_by_label(app_handle, MAIN_WINDOW_LABEL);
@@ -52,11 +30,13 @@ pub fn show_preference_window(app_handle: &AppHandle) {
5230
}
5331

5432
fn show_window_by_label(app_handle: &AppHandle, label: &str) {
55-
if let Some(window) = app_handle.get_webview_window(label) {
56-
let app_handle_clone = app_handle.clone();
33+
let Some(window) = app_handle.get_webview_window(label) else {
34+
return;
35+
};
36+
37+
let app_handle = app_handle.clone();
5738

58-
spawn(async move {
59-
show_window(app_handle_clone, window).await;
60-
});
61-
}
39+
spawn(async move {
40+
show_window(app_handle, window).await;
41+
});
6242
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
use std::sync::atomic::{AtomicBool, Ordering};
2+
use std::sync::{Arc, OnceLock};
3+
use std::thread;
4+
use std::time::Duration;
5+
use tauri::{AppHandle, Runtime, WebviewWindow, command};
6+
use windows::Win32::Foundation::HWND;
7+
use windows::Win32::UI::WindowsAndMessaging::{
8+
HWND_NOTOPMOST, HWND_TOPMOST, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SetWindowPos,
9+
};
10+
11+
static TOPMOST_RUNNING: OnceLock<Arc<AtomicBool>> = OnceLock::new();
12+
13+
#[command]
14+
pub async fn show_window<R: Runtime>(_app_handle: AppHandle<R>, window: WebviewWindow<R>) {
15+
let _ = window.show();
16+
let _ = window.unminimize();
17+
let _ = window.set_focus();
18+
}
19+
20+
#[command]
21+
pub async fn hide_window<R: Runtime>(_app_handle: AppHandle<R>, window: WebviewWindow<R>) {
22+
let _ = window.hide();
23+
}
24+
25+
#[command]
26+
pub async fn set_always_on_top<R: Runtime>(
27+
_app_handle: AppHandle<R>,
28+
window: WebviewWindow<R>,
29+
always_on_top: bool,
30+
) {
31+
let running = TOPMOST_RUNNING.get_or_init(|| Arc::new(AtomicBool::new(false)));
32+
33+
let Ok(hwnd) = window.hwnd() else { return };
34+
let raw_hwnd = hwnd.0 as isize;
35+
36+
if always_on_top {
37+
let Ok(_) = running.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
38+
else {
39+
return;
40+
};
41+
42+
let running = Arc::clone(running);
43+
44+
thread::spawn(move || {
45+
let hwnd = HWND(raw_hwnd as *mut _);
46+
47+
while running.load(Ordering::SeqCst) {
48+
unsafe {
49+
let _ = SetWindowPos(
50+
hwnd,
51+
Some(HWND_TOPMOST),
52+
0,
53+
0,
54+
0,
55+
0,
56+
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE,
57+
);
58+
}
59+
thread::sleep(Duration::from_millis(16));
60+
}
61+
});
62+
} else {
63+
running.store(false, Ordering::SeqCst);
64+
65+
let hwnd = HWND(raw_hwnd as *mut _);
66+
67+
unsafe {
68+
let _ = SetWindowPos(
69+
hwnd,
70+
Some(HWND_NOTOPMOST),
71+
0,
72+
0,
73+
0,
74+
0,
75+
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE,
76+
);
77+
}
78+
}
79+
}
80+
81+
#[command]
82+
pub async fn set_taskbar_visibility<R: Runtime>(window: WebviewWindow<R>, visible: bool) {
83+
let _ = window.set_skip_taskbar(!visible);
84+
}

src/pages/main/index.vue

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { useModelStore } from '@/stores/model'
2525
import { isImage } from '@/utils/is'
2626
import live2d from '@/utils/live2d'
2727
import { join } from '@/utils/path'
28+
import { isWindows } from '@/utils/platform'
2829
import { clearObject } from '@/utils/shared'
2930
3031
const { startListening } = useDevice()
@@ -151,7 +152,17 @@ async function handleContextmenu(event: MouseEvent) {
151152
],
152153
})
153154
154-
menu.popup()
155+
// Temporarily disable always-on-top on Windows so the context menu is not covered
156+
if (isWindows && catStore.window.alwaysOnTop) {
157+
setAlwaysOnTop(false)
158+
}
159+
160+
await menu.popup()
161+
162+
// Restore always-on-top after the menu is closed
163+
if (!isWindows || !catStore.window.alwaysOnTop) return
164+
165+
setAlwaysOnTop(true)
155166
}
156167
157168
function handleMouseMove(event: MouseEvent) {

0 commit comments

Comments
 (0)