iT邦幫忙

2025 iThome 鐵人賽

DAY 13
0
Rust

用 Tauri 打造你的應用程式系列 第 13

[Day 13] 視窗管理 (二)

  • 分享至 

  • xImage
  •  

現代桌面應用程式常常需要透過多個視窗來劃分功能,例如主視窗、設定面板、工具視窗等。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 系統,就可以做到視窗之間的溝通。

從視窗 A 發送事件到視窗 B

假設我們有一個主視窗和一個聊天視窗,當主視窗中再次創建聊天視窗時,會直接修改現有的視窗:

主視窗(發送方):

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

Yes
(這是影片)

小結

多視窗管理是現代桌面應用的重要特性,Tauri 提供了:

  1. 動態視窗創建:透過 WindowBuilder 靈活創建視窗
  2. 事件通訊機制:使用 emitlisten 實現視窗間溝通
  3. 生命週期管理:完整的視窗開啟、關閉、狀態管理
  4. 實務整合:與 Rust 後端的無縫整合

透過這些機制,我們可以建構出功能豐富、使用者體驗良好的多視窗桌面應用程式。下一篇我們將探討如何建立應用程式選單系統。


上一篇
[Day 12] 視窗管理 (一)
下一篇
[Day 14] Menu 選單
系列文
用 Tauri 打造你的應用程式14
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言