現代桌面應用程式常常需要透過多個視窗來劃分功能,例如主視窗、設定面板、工具視窗等。Tauri 不僅支援靜態設定多視窗,更強大的是它能在執行期間動態創建和管理視窗,並提供了完善的機制讓這些視窗之間進行溝通。
除了在 tauri.conf.json
中靜態定義,我們更多時候需要在使用者互動時(例如點擊按鈕)才創建新視窗。在 Tauri,我們可以直接在前端用 JS 來建立新視窗。
import { WebviewWindow } from "@tauri-apps/api/webviewWindow";
const name = "王小明"
const windowLabel = `chat-${Date.now()}`;
const chatWindow = new WebviewWindow(windowLabel, {
url: `/chat?partner=${encodeURIComponent(name)}`,
title: `與 ${name} 聊天`,
width: 400,
height: 600,
closable: true,
center: true,
});
或是也可以用在後端用 Rust 建立:
use tauri::{Manager, WebviewUrl, WebviewWindowBuilder};
#[tauri::command]
async fn open_chat_window(
app: tauri::AppHandle,
partner_name: String,
) -> Result<(), String> {
let window_label = format!("chat-{}", chrono::Utc::now().timestamp());
let url = format!("/chat?partner={}", urlencoding::encode(&partner_name));
let title = format!("與 {} 聊天", partner_name);
let window = WebviewWindowBuilder::new(
&app,
&window_label,
WebviewUrl::App(url.parse().unwrap())
)
.title(&title)
.inner_size(400.0, 600.0)
.center()
.closable(true)
.build()
.map_err(|e| format!("無法創建視窗: {}", e))?;
println!("成功創建聊天視窗: {}", window_label);
Ok(())
}
// 在 main.rs 中註冊 command
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![open_chat_window])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
前端只需 invoke
這個 command 即可觸發新視窗的創建:
import { invoke } from "@tauri-apps/api/core";
const openChatWindow = async (partnerName: string) => {
try {
await invoke('open_chat_window', { partnerName });
console.log('聊天視窗已開啟');
} catch (error) {
console.error('開啟聊天視窗失敗:', error);
}
};
多視窗應用的最大挑戰在於如何讓它們互相溝通。例如,如何在設定視窗(A 視窗)中點擊按鈕,來改變主視窗(B 視窗)的內容?
其實只要使用先前在 Day09 介紹的 Event 系統,就可以做到視窗之間的溝通。
假設我們有一個主視窗和一個聊天視窗,當主視窗中再次創建聊天視窗時,會直接修改現有的視窗:
主視窗(發送方):
import { ref } from "vue";
import { emit } from '@tauri-apps/api/event';
const name = ref("");
async function createChatWindow() {
const existingWindow = await WebviewWindow.getByLabel('chat');
if (existingWindow) {
// 如果窗口已存在,發送事件通知更新聊天對象
await existingWindow.emit('change-chat-partner', {
partner: name.value
});
// 聚焦到已存在的窗口
await existingWindow.setFocus();
console.log('Updated existing chat window with new partner:', name.value);
} else {
// 如果窗口不存在,創建新的聊天窗口
const chatWindow = new WebviewWindow('chat', {
url: `/chat?partner=${encodeURIComponent(name.value)}`,
title: `與 ${name.value} 聊天`,
width: 400,
height: 600,
resizable: true,
minimizable: true,
maximizable: true,
closable: true,
center: true,
decorations: false, // 使用自定義標題欄
});
}
}
聊天視窗(接收方):
import { ref, onMounted } from "vue";
import { listen } from '@tauri-apps/api/event';
const chatPartner = ref("");
// 初始化聊天對象並監聽事件
onMounted(() => {
const urlParams = new URLSearchParams(window.location.search);
chatPartner.value = urlParams.get('partner') || '未知用戶';
// 監聽切換聊天對象事件
const currentWindow = WebviewWindow.getCurrent();
currentWindow.listen('change-chat-partner', (event) => {
const payload = event.payload as { partner: string };
const newPartner = payload.partner;
if (newPartner && newPartner !== chatPartner.value) {
changeChatPartner(newPartner);
}
});
});
// 記得在適當時機清理監聽器
// unlisten();
最後,來看一下 Demo
多視窗管理是現代桌面應用的重要特性,Tauri 提供了:
WindowBuilder
靈活創建視窗emit
和 listen
實現視窗間溝通透過這些機制,我們可以建構出功能豐富、使用者體驗良好的多視窗桌面應用程式。下一篇我們將探討如何建立應用程式選單系統。