tauri 2.0 创建系统托盘

tauri 提供了两种系统托盘的创建方式,托盘 API 在 JavaScript 和 Rust 中均可用。

通用基本配置(不管是 JavaScript 还是 Rust):

我们需要在 src-tauri/Cargo.toml 中写入包含系统托盘的必要功能 tray-iconimage-png

[package]
name = "tauri-app"
version = "0.1.0"
description = "A Tauri App"
authors = ["you"]
edition = "2021"

[lib]
name = "tauri_app_lib"
crate-type = ["staticlib", "cdylib", "rlib"]

[build-dependencies]
tauri-build = { version = "2", features = [] }

[dependencies]
# 为 tauri 的 features 数组里加入 "tray-icon",保存时自动更新依赖。
# "image-png" 是为了支持 Image api 读取文件存在的,详细请参考源码:
# import { Image } from '@tauri-apps/api/image'; static fromPath 函数的注释
# 如果没有添加 "image-png",则在读取 png 时将会出现该错误:"Uncaught (in promise) expected RGBA image data, found a file path"
tauri = { version = "2", features = ["tray-icon", "image-png"] }
tauri-plugin-shell = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"

一、使用 JavaScript 创建系统托盘

一. 使用 JavaScript 创建系统托盘:

// 导入系统托盘, 
import { TrayIcon } from '@tauri-apps/api/tray';

/**
 * 在这里你可以添加一个托盘菜单,标题,工具提示,事件处理程序等
 */
const options = {
	// icon 的相对路径基于:项目根目录/src-tauri/
    icon : "icons/32x32.png",
    // 托盘提示,悬浮在托盘图标上可以显示 tauri-app
    tooltip: 'tauri-app',
    // 是否在左键点击时显示托盘菜单,默认为 true。当然不能为 true 啦,程序进入后台不得左键点击图标显示窗口啊。
    menuOnLeftClick: false,
    // 托盘菜单,后续创建.....
    menu: undefined,
    // 托盘图标上事件的处理程序。这个下面需要详细说一下
    action: (event) => {}
}
/**
 * 创建系统托盘
 */
export async function createTray() {
    const tray = await TrayIcon.new(options);
}

二. 设置 JavaScript 系统托盘事件

在上面的代码中,我们通过运行 createTray 函数就可以得到一个系统托盘图标。但是光有一个图标还是不够的,所以我们还得有相对应的事件,通过 Ctrl+左键 进入源码部分,我们可以看到 options.action 函数传入了一个 TrayIconEvent 联合类型事件:

export type TrayIconEvent = (TrayIconEventBase<'Click'> & TrayIconClickEvent) | (TrayIconEventBase<'DoubleClick'> & Omit<TrayIconClickEvent, 'buttonState'>) | TrayIconEventBase<'Enter'> | TrayIconEventBase<'Move'> | TrayIconEventBase<'Leave'>;

在这里,它提供了 ClickDoubleClickEnterMoveLeave(单击,双击,鼠标移入,鼠标拖动,鼠标移出)这五个托盘事件。
action 属性函数的 event 参数我们需要使用 switch 或者是 if 把它的 type 筛选出来,在 case 选项中执行我们的事件:

action: (event) => {
        switch(event.type) {
            case 'Click': 
                console.log('单击事件');
                console.log('Click event:', event);
                console.log('Mouse button:', event.button);
                console.log('Button state:', event.buttonState);
                break;
            case 'DoubleClick':
                console.log('双击事件');
                console.log('DoubleClick event:', event);
                break;
            case 'Enter':
                console.log('鼠标进入托盘图标');
                console.log('Mouse entered tray icon area:', event);
                break;
            case 'Move':
                console.log('鼠标移动托盘图标');
                console.log('Mouse moved over tray icon:', event);
                break;
            case 'Leave':
                console.log('鼠标离开托盘图标');
                console.log('Mouse left tray icon area:', event);
                break;    
            default: 
                console.log(`未定义事件类型: ${event.type}`);
        }
    }

对于我们来说,常用的也就只有第一个 Click 事件,其他的用的不多,所以别的我也就不说了,大家自行选择就好。

那么接下来,我们来试一试单击托盘图标会怎么样:
在这里插入图片描述

看,它单击事件触发了两次,一次是鼠标按下(Button state: Down),一次是鼠标松开(Button state: Up)。 对于我们来说,我们只需要一次就可以,通过简单的 if 判断可以过滤掉一次事件:

if(event.buttonState === 'Down')
或者
if(event.buttonState === 'Up')

在这里插入图片描述

当然还有一件事,我们多次修改了文件,vite 在多次的重载中难免会造成多个系统托盘图标,但是没关系,重启应用后,我还是一个 o( ̄▽ ̄)o
在这里插入图片描述

三. JavaScript 设置窗口关闭后,应用进入后台运行:

其实吧,我们只需要隐藏掉窗口就好,至于怎么隐藏呢,我在这里说一下步骤:
tauri 提供了 import { getCurrentWindow } from '@tauri-apps/api/window'; api,执行 getCurrentWindow 函数可以获取到一个 Window 当前窗体实例,我们可以通过这个实例的 hide() 函数隐藏窗口。

  1. 关闭系统自带的拖拽栏:设置 tauri.conf.json 属性:app.windows.decorations = false
    在这里插入图片描述

  2. 使用 HTML+CSS+JS 替代系统拖拽栏,重点是这个关闭按钮得换成 hide()

  3. 下一步就是参考我的上一篇文档,看看这两步是怎么做的:tauri 2.0创建项目 https://blog.csdn.net/xiaoyuanbaobao/article/details/143751093

在干完上面的操作后,我们就可以关闭窗口,在创建托盘的页面引入 getCurrentWindow 函数在单击事件中调用它的 show() 方法把进入后台运行的程序窗口打开。

只是把窗口显示出来就够了?当然不是,我们还得在它窗口没隐藏的情况下把窗口置顶聚焦,这才符合我们的程序使用习惯。

  • 通过 isVisible 方法可以检查窗口是否是可视状态

  • 通过 show 方法可以把窗口显示出来

  • 通过 setFocus 方法可以聚焦窗口,让窗口置顶显示

  • 通过 unminimize 方法可以解除最小化,怎么好像混进去什么奇怪的东西?
    这 TM 是个大坑!!!!!!

    在进入最小化的情况下,聚焦无法将页面置顶只有解除最小化之后聚焦才是有效的
    这个在 rustapi 中并没有这个问题,但是在 Javascriptapi 是这样的。!!!需要特别注意!!!

tauri 遵循最小权限原则,在运行中可能会碰到如下权限不足的问题:
在这里插入图片描述
src-tauri/capabilities/default.json 文件的 permissions 属性数组中加入这几个权限:
"core:window:allow-set-focus""core:window:allow-close
"core:window:allow-show""core:window:allow-is-visible"
"core:window:allow-unminimize""core:window:allow-is-minimized"
在这里插入图片描述

写到现在给大家再看看现在的 Tray 代码吧,目前它任然是不完整的,我还没挂托盘菜单 Menu 呢:

// 导入系统托盘
import { TrayIcon } from '@tauri-apps/api/tray';
import { getCurrentWindow } from '@tauri-apps/api/window';

/**
 * 在这里你可以添加一个托盘菜单,标题,工具提示,事件处理程序等
 */
const options = {
    // icon 的相对路径基于:项目根目录/src-tauri/
    icon : "icons/32x32.png",
    // 托盘提示,悬浮在托盘图标上可以显示 tauri-app
    tooltip: 'tauri-app',
    // 是否在左键点击时显示托盘菜单,默认为 true。当然不能为 true 啦,程序进入后台不得左键点击图标显示窗口啊。
    menuOnLeftClick: false,
    // 托盘图标上事件的处理程序。
    action: (event) => {
        // 左键点击事件
        if(event.type === 'Click' && event.button === "Left" && event.buttonState === 'Down') {
            console.log('单击事件');
            // 显示窗口
            winShowFocus();
        }
    }
}
/**
 * 窗口置顶显示
 */
async function winShowFocus() {
    // 获取窗体实例
    const win = getCurrentWindow();
    // 检查窗口是否见,如果不可见则显示出来
    if(!(await win.isVisible())) {
        win.show();
    }else {
        // 检查是否处于最小化状态,如果处于最小化状态则解除最小化
        if(await win.isMinimized()) {
            await win.unminimize();
        }
        // 窗口置顶
        await win.setFocus();
    }
}

/**
 * 创建系统托盘
 */
export async function createTray() {
    const tray = await TrayIcon.new(options);
    console.log(tray)
}

四. Javascript 前端添加托盘菜单 Menu

创建系统托盘菜单:

import { Menu } from '@tauri-apps/api/menu';
/**
 * 创建托盘菜单
 */
async function createMenu() {
    return await Menu.new({
        // items 的显示顺序是倒过来的
        items: [
            {
                id: 'show',
                text: '显示窗口',
                action: () => {
                    winShowFocus();
                }
            },
            {
                // 菜单 id
                id: 'quit',
                // 菜单文本
                text: '退出',
                // 菜单事件处理程序
                action: () => {
                	 // 退出应用还需要引入别的依赖
                    console.log('退出应用');
                }
            }
        ]
    })
}

将托盘菜单添加进系统托盘,修改创建系统托盘的 createTray 函数:

/**
 * 创建系统托盘
 */
export async function createTray() {
    // 获取 menu
    options.menu = await createMenu();
    const tray = await TrayIcon.new(options);
    console.log(tray)
}

在这里插入图片描述

五. JavaScript 退出应用

在窗体化应用中,通常关闭最后一个窗口,就可以退出整个应用:

getCurrentWindow().close();

但是这样可能并不一定保险,我在 getCurrentWindow 并没有发现类似于 app.exit 或者 app.quit 的函数。所以我们又得引入新的东西了:进程 process https://tauri.app/zh-cn/plugin/process/

  1. 首先使用项目的包管理器来添加依赖:

    npm run tauri add process

  2. 引入 api

    import { exit, relaunch } from ‘@tauri-apps/plugin-process’;

在进程管理中,只有两个东西,一个是 exit 退出应用函数,一个是 relaunch 重启应用函数,没了 ಠ_ಠ

用法也没啥好说的,直接调用就好:

// 退出应用
await exit(0);
// 重启应用
await relaunch();

六. JavaScript 创建系统托盘最终代码:

createTray 函数是对外导出的,外部调用跑就行,不会真的有人没调这个方法吧?(⓿_⓿)

// 导入系统托盘
import { TrayIcon } from '@tauri-apps/api/tray';
// 获取当前窗口
import { getCurrentWindow } from '@tauri-apps/api/window';
// 托盘菜单
import { Menu } from '@tauri-apps/api/menu';
// 进程管理
import { exit } from '@tauri-apps/plugin-process';

/**
 * 在这里你可以添加一个托盘菜单,标题,工具提示,事件处理程序等
 */
const options = {
    // icon 的相对路径基于:项目根目录/src-tauri/,其他 tauri api 相对路径大抵都是这个套路
    icon : "icons/32x32.png",
    // 托盘提示,悬浮在托盘图标上可以显示 tauri-app
    tooltip: 'tauri-app',
    // 是否在左键点击时显示托盘菜单,默认为 true。当然不能为 true 啦,程序进入后台不得左键点击图标显示窗口啊。
    menuOnLeftClick: false,
    // 托盘图标上事件的处理程序。
    action: (event) => {
        // 左键点击事件
        if(event.type === 'Click' && event.button === "Left" && event.buttonState === 'Down') {
            console.log('单击事件');
            // 显示窗口
            winShowFocus();
        }
    }
}

/**
 * 窗口置顶显示
 */
async function winShowFocus() {
    // 获取窗体实例
    const win = getCurrentWindow();
    // 检查窗口是否见,如果不可见则显示出来
    if(!(await win.isVisible())) {
        win.show();
    }else {
        // 检查是否处于最小化状态,如果处于最小化状态则解除最小化
        if(await win.isMinimized()) {
            await win.unminimize();
        }
        // 窗口置顶
        await win.setFocus();
    }
}

/**
 * 创建托盘菜单
 */
async function createMenu() {
    return await Menu.new({
        // items 的显示顺序是倒过来的
        items: [
            {
                id: 'show',
                text: '显示窗口',
                action: () => {
                    winShowFocus();
                }
            },
            {
                // 菜单 id
                id: 'quit',
                // 菜单文本
                text: '退出',
                //  菜单项点击事件
                action: () => {
                    // 退出应用
                    exit(0);
                }
            }
        ]
    })
}

/**
 * 创建系统托盘
 */
export async function createTray() {
    // 获取 menu
    options.menu = await createMenu();
    await TrayIcon.new(options);
}

二、在 Rust 创建系统托盘菜单:

其实吧,我觉得 JavaScript api 创建托盘菜单已经足够了,但是由于我 rust 学的一坨屎 (啥也不会!),所以在这里我不会说太多,直接将操作甩这就完了。

1. 在 src-tauri/Cargo.toml 中写入包含系统托盘的必要功能 tray-icon

2. 使用 html+css+Javascript 替换掉系统默认的拖拽栏,详情请参考:tauri 2.0创建项目 https://blog.csdn.net/xiaoyuanbaobao/article/details/143751093

3.Rust 创建系统托盘:

修改 src-tauri/lib.rs 文件,在文件中添加如下内容:

// 导入系统托盘所需的依赖, 导入的全都是
use tauri::{
    tray::{
        TrayIconBuilder,
        MouseButtonState,
        MouseButton,
        TrayIconEvent
    },
    menu::{
        Menu,
        MenuItem
    },
    Manager
};

#[tauri::command]
fn greet(name: &str) -> String {
    format!("Hello, {}! You've been greeted from Rust!", name)
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .plugin(tauri_plugin_process::init())
        .plugin(tauri_plugin_shell::init())
        .invoke_handler(tauri::generate_handler![greet])
        // setup 部分是系统托盘
        .setup(|app| {
            let show_i = MenuItem::with_id(app, "show", "显示", true, None::<&str>)?;
            let quit_i = MenuItem::with_id(app, "quit", "退出", true, None::<&str>)?;
            let menu = Menu::with_items(app, &[&show_i, &quit_i])?;
            // 创建系统托盘
            let _tray = TrayIconBuilder::new()
                // 添加托盘图标
                .icon(app.default_window_icon().unwrap().clone())
                // 添加菜单
                .menu(&menu)
                // 禁用鼠标左键点击图标显示托盘菜单
                .menu_on_left_click(false)
                // 监听托盘图标发出的鼠标事件
                .on_tray_icon_event(|tray, event| match event {
                    // 左键点击托盘图标显示窗口
                    TrayIconEvent::Click {
                        id: _,
                        position: _,
                        rect: _,
                        button: MouseButton::Left,
                        button_state: MouseButtonState::Up,
                    } => {
                        let win = tray
                            .app_handle()
                            .get_webview_window("main")
                            .expect("REASON");
                        match win.is_visible() {
                            Ok(visible) if !visible => {
                                win.show().unwrap();
                            }
                            Err(e) => eprintln!("{}", e),
                            _ => (),
                        };
                        // 获取窗口焦点
                        win.set_focus().unwrap();
                    }
                    _ => {}
                })
                // 监听菜单事件
                .on_menu_event(|app, event| match event.id.as_ref() {
                    "show" => {
                        let win = app.get_webview_window("main").unwrap();
                        match win.is_visible() {
                            Ok(visible) if !visible => {
                                win.show().unwrap();
                            }
                            Err(e) => eprintln!("{}", e),
                            _ => (),
                        };
                        // 获取窗口焦点
                        win.set_focus().unwrap();
                    }
                    "quit" => {
                        app.exit(0);
                    }
                    _ => {}
                })
                .build(app)?;
            Ok(())
        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

在这一大串代码中,.setup 部分是系统托盘部分的代码,注意别忘了粘贴的位置,以及导入依赖。

三、结束

tauri 2.0 创建系统托盘的教程到这里就结束了,让我想想接下来要在官方文档里薅哪一步分下来写 ヽ( ̄ω ̄( ̄ω ̄〃)ゝ

### Tauri 2.0 系统托盘功能实现与配置 Tauri 是一种用于构建安全、快速和小型二进制文件的跨平台桌面应用程序框架。其最新版本 Tauri 2.0 提供了许多新特性和改进,其中包括系统托盘(System Tray)的支持[^1]。 #### 配置 System Tray 为了启用系统的托盘支持,在 `tauri.conf.json` 文件中需要定义特定的设置项: ```json { "tauri": { "allowlist": { "tray": true } }, "plugin": { "tray": { "icon": "./assets/tray-icon.png", "tooltip": "This is a tooltip" } } } ``` 上述 JSON 中的关键字段解释如下: - **icon**: 定义托盘图标路径,该图标的大小通常建议为 16x16 或 32x32 像素。 - **tooltip**: 设置鼠标悬停在托盘图标上时显示的文字提示。 这些配置允许开发者自定义托盘的行为以及外观[^2]。 #### 实现交互逻辑 除了基本的配置外,还可以通过事件监听器来处理用户的点击操作或其他行为。以下是使用 TypeScript 编写的简单示例代码片段,展示如何响应托盘单击事件: ```typescript import { appWindow, listen } from '@tauri-apps/api/window'; import { invoke } from '@tauri-apps/api/tauri'; listen('tray-event', (event) => { const payload = event.payload as string; switch(payload){ case 'left-click': console.log('Left click on the tray icon'); break; default: console.warn(`Unhandled tray event: ${payload}`); } }); ``` 此脚本注册了一个名为 `'tray-event'` 的全局事件侦听器,并依据传入的有效载荷执行不同的动作。例如,“左键单击”会触发日志记录语句[^3]。 #### 扩展功能 如果希望进一步增强用户体验,则可以通过插件扩展更多能力。尽管并非所有官方插件均达到完全稳定性标准,但它们仍能提供丰富的附加价值。 ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值