窗口管理
Tauri 提供了丰富的窗口管理 API,允许你创建、配置和控制应用窗口。
窗口基础
创建窗口
通过配置文件创建(tauri.conf.json):
{
"app": {
"windows": [
{
"label": "main",
"title": "我的应用",
"width": 1200,
"height": 800,
"center": true,
"resizable": true
}
]
}
}
通过代码动态创建(Rust):
use tauri::WebviewWindowBuilder;
#[tauri::command]
async fn create_settings_window(app: tauri::AppHandle) -> Result<(), String> {
let window = WebviewWindowBuilder::new(
&app,
"settings", // 窗口标签(唯一标识)
tauri::WebviewUrl::App("settings.html".into())
)
.title("设置")
.inner_size(600, 400)
.min_inner_size(400, 300)
.center()
.resizable(true)
.build()
.map_err(|e| e.to_string())?;
Ok(())
}
窗口配置选项
| 选项 | 类型 | 说明 |
|---|---|---|
label | string | 窗口唯一标识符 |
title | string | 窗口标题 |
width / height | number | 窗口尺寸 |
minWidth / minHeight | number | 最小尺寸 |
maxWidth / maxHeight | number | 最大尺寸 |
x / y | number | 窗口位置 |
center | boolean | 是否居中显示 |
resizable | boolean | 是否可调整大小 |
maximized | boolean | 是否最大化 |
fullscreen | boolean | 是否全屏 |
decorations | boolean | 是否显示边框和标题栏 |
alwaysOnTop | boolean | 是否置顶 |
visible | boolean | 初始是否可见 |
transparent | boolean | 是否透明背景 |
skipTaskbar | boolean | 是否在任务栏隐藏 |
窗口控制
获取窗口实例
在 Rust 中:
// 通过 AppHandle 获取
let window = app.get_webview_window("main").unwrap();
// 在 Command 中注入
#[tauri::command]
fn do_something(window: tauri::WebviewWindow) {
// window 就是调用此命令的窗口
}
在前端中:
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
const currentWindow = getCurrentWebviewWindow();
窗口操作
最小化、最大化、关闭:
// Rust
window.minimize().unwrap();
window.maximize().unwrap();
window.unmaximize().unwrap();
window.close().unwrap();
// 前端
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
const window = getCurrentWebviewWindow();
window.minimize();
window.maximize();
window.unmaximize();
window.close();
显示/隐藏窗口:
window.show().unwrap();
window.hide().unwrap();
设置窗口属性:
window.set_title("新标题").unwrap();
window.set_size(tauri::Size::Logical(tauri::LogicalSize {
width: 800.0,
height: 600.0
})).unwrap();
window.set_position(tauri::Position::Logical(tauri::LogicalPosition {
x: 100.0,
y: 100.0
})).unwrap();
window.set_always_on_top(true).unwrap();
window.set_decorations(false).unwrap(); // 无边框窗口
无边框窗口
创建无边框窗口可以实现自定义标题栏:
let window = WebviewWindowBuilder::new(&app, "main", tauri::WebviewUrl::App("index.html".into()))
.decorations(false) // 移除系统边框
.transparent(true) // 可选:透明背景
.build()
.unwrap();
实现自定义标题栏
前端 HTML:
<div data-tauri-drag-region class="titlebar">
<span class="title">我的应用</span>
<div class="window-controls">
<button onclick="minimize()">─</button>
<button onclick="maximize()">□</button>
<button onclick="close()">×</button>
</div>
</div>
CSS:
.titlebar {
height: 32px;
background: #2d2d2d;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 12px;
-webkit-user-select: none;
user-select: none;
}
[data-tauri-drag-region] {
app-region: drag; /* 允许拖拽窗口 */
}
.window-controls button {
app-region: no-drag; /* 按钮不可拖拽 */
}
JavaScript:
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
const window = getCurrentWebviewWindow();
function minimize() {
window.minimize();
}
function maximize() {
window.toggleMaximize();
}
function close() {
window.close();
}
窗口事件
监听窗口事件
Rust 端:
use tauri::Manager;
pub fn run() {
tauri::Builder::default()
.setup(|app| {
let window = app.get_webview_window("main").unwrap();
// 监听窗口移动
window.on_window_event(|event| {
match event {
tauri::WindowEvent::Moved(position) => {
println!("窗口移动到新位置: {:?}", position);
}
tauri::WindowEvent::Resized(size) => {
println!("窗口调整大小: {:?}", size);
}
tauri::WindowEvent::CloseRequested { .. } => {
println!("窗口关闭请求");
}
tauri::WindowEvent::Focused(focused) => {
println!("窗口焦点状态: {}", focused);
}
_ => {}
}
});
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
前端:
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
import { listen } from "@tauri-apps/api/event";
const window = getCurrentWebviewWindow();
// 监听窗口大小变化
window.listen("tauri://resize", (event) => {
console.log("窗口大小变化:", event.payload);
});
// 监听窗口移动
window.listen("tauri://move", (event) => {
console.log("窗口移动:", event.payload);
});
// 监听窗口关闭
window.listen("tauri://close-requested", (event) => {
console.log("窗口即将关闭");
});
多窗口通信
窗口间发送事件
从 Rust 发送:
use tauri::{AppHandle, Emitter};
// 向特定窗口发送
app.emit_to("settings", "theme-changed", "dark").unwrap();
// 向所有窗口广播
app.emit("config-updated", &new_config).unwrap();
从前端发送:
import { emitTo, emit } from "@tauri-apps/api/event";
// 向特定窗口发送
await emitTo("settings", "theme-changed", { theme: "dark" });
// 向所有窗口广播
await emit("config-updated", config);
窗口状态保存
使用 tauri-plugin-window-state 插件保存窗口位置和大小:
安装插件:
cargo add tauri-plugin-window-state
注册插件:
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_window_state::Builder::default().build())
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
插件会自动保存和恢复窗口状态。
实战示例
创建带加载动画的启动窗口
Rust:
use tauri::WebviewWindowBuilder;
pub fn run() {
tauri::Builder::default()
.setup(|app| {
// 创建启动窗口
let splash = WebviewWindowBuilder::new(
app,
"splash",
tauri::WebviewUrl::App("splash.html".into())
)
.title("加载中...")
.inner_size(400, 300)
.decorations(false)
.always_on_top(true)
.center()
.build()
.unwrap();
// 创建主窗口(初始隐藏)
let main = WebviewWindowBuilder::new(
app,
"main",
tauri::WebviewUrl::App("index.html".into())
)
.title("我的应用")
.inner_size(1200, 800)
.visible(false)
.build()
.unwrap();
// 模拟加载过程
let splash_clone = splash.clone();
tauri::async_runtime::spawn(async move {
tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
// 关闭启动窗口,显示主窗口
splash_clone.close().unwrap();
main.show().unwrap();
main.set_focus().unwrap();
});
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
创建模态对话框窗口
#[tauri::command]
async fn show_modal_dialog(app: tauri::AppHandle) -> Result<(), String> {
// 禁用主窗口
let main_window = app.get_webview_window("main").unwrap();
main_window.set_enabled(false).unwrap();
// 创建对话框窗口
let dialog = WebviewWindowBuilder::new(
&app,
"dialog",
tauri::WebviewUrl::App("dialog.html".into())
)
.title("确认")
.inner_size(400, 200)
.decorations(false)
.always_on_top(true)
.center()
.build()
.map_err(|e| e.to_string())?;
// 监听对话框关闭
let main_clone = main_window.clone();
dialog.on_window_event(move |event| {
if matches!(event, tauri::WindowEvent::CloseRequested { .. }) {
main_clone.set_enabled(true).unwrap();
main_clone.set_focus().unwrap();
}
});
Ok(())
}
最佳实践
- 合理使用标签:为每个窗口设置有意义的标签,便于管理
- 延迟加载:非必要窗口延迟创建,减少内存占用
- 状态保存:使用插件保存窗口状态,提升用户体验
- 错误处理:窗口操作可能失败,始终处理错误情况