【Tauri2】010——菜单menu(1)
目录
前言
直接创建
菜单项——MenuItem
使用new方法
with_id方法
子菜单——SubMenu
new方法
with_id方法
with_items方法
菜单——Menu
间接创建
在前端创建菜单
总结
前言
前面介绍了setup这个hook。也提到了菜单,这篇来试试创建菜单以及菜单的动作(action)
在Rust中创建菜单/
直接创建
在创建之前,可以进行一些其他操作。
首先,将lib.rs中的run函数的内容复制到main.rs,其他的东西也复制,删除lib.rs
再删除Cargo.toml下的表lib,创建menu.rs
实际上,可以不分开,但是,所有代码都写在一起,不好看。
从小到大创建。
菜单项——MenuItem
MenuItem in tauri::menu - Rusthttps://docs.rs/tauri/2.4.0/tauri/menu/struct.MenuItem.html
使用new方法
可以使用new方法,看看函数签名
pub fn new<M, T, A>(
manager: &M,
text: T,
enabled: bool,
accelerator: Option<A>,
) -> crate::Result<Self>
这个签名总体上看,四个参数,三个泛型,返回Result<Self>
Manager:&App或者&Apphandle
Text:文本。
enabled:能否使用
accelerator:快捷键
笔者以前还没发现,这个Result,居然是别名
pub type Result<T> = std::result::Result<T, Error>;
怪不得,用起来有点怪。
因此,创建菜单栏的代码如下
let start=MenuItem::new(app, "start", true, Some("q"))?;
或者没有快捷键
let start=MenuItem::new(app, "start", true, None::<&str>)?;
需要标注泛型的类型,直接写None,会报错
Type annotations needed
with_id方法
函数签名
pub fn with_id<M, I, T, A>(
manager: &M,
id: I,
text: T,
enabled: bool,
accelerator: Option<A>,
) -> crate::Result<Self>
就多了个id参数,比如
let hello_item = MenuItem::with_id(app, "hello", "Hello", true,None::<&str>)?;
子菜单——SubMenu
Submenu in tauri::menu - Rusthttps://docs.rs/tauri/2.4.0/tauri/menu/struct.Submenu.html这个简单来说,就是放前面的菜单栏,也可以不用子菜单。这里就使用了。
这个也可以使用new方法,也有with_id,或者使用with_items,写法很多种。‘
new方法
ub fn new<M: Manager<R>, S: AsRef<str>>(
manager: &M,
text: S,
enabled: bool,
) -> crate::Result<Self>
let first_submenu = Submenu::new(
app, //
"first",
true
)?;
first_submenu.append(&hello_item)?;
with_id方法
pub fn with_id<M: Manager<R>, I: Into<MenuId>, S: AsRef<str>>(
manager: &M,
id: I,
text: S,
enabled: bool,
) -> crate::Result<Self>
let first_submenu = Submenu::with_id(
app,
"first_submenu",
"First Submenu",
true,
)?.append(
&hello_item,
)?;
with_items方法
pub fn with_items<M: Manager<R>, S: AsRef<str>>(
manager: &M,
text: S,
enabled: bool,
items: &[&dyn IsMenuItem<R>],
) -> crate::Result<Self>
这个items的类型 &[&dyn IsMenuItem<R>],
首先&[] ,表示引用切片
dyn是Rust中的关键字,
dyn表示动态分发(dynamic dispatch)的 trait 对象,用于需要存储或操作多种实现同一 trait 的类型
1、在运行时(而非编译时)确定具体类型
2、通过统一接口处理不同的类型
dyn trait
对于&[&dyn IsMenuItem<R>],
简单的说,首先,是个切片。其次,包含多个实现了IsMenuItem这个trait 的不同具体类型的引用
更明白的说,里面可以写如下的东西
IsMenuItem in tauri::menu - Rusthttps://docs.rs/tauri/2.4.0/tauri/menu/trait.IsMenuItem.html
因此,代码如下
let first_submenu = Submenu::with_items(
app,
"first_submenu",
true,
&[&hello_item, &world_item],
)?;
无论用那种方法,总之,差不多。
菜单——Menu
Menu in tauri::menu - Rusthttps://docs.rs/tauri/2.4.0/tauri/menu/struct.Menu.html
根上面差不了多少,直接给出代码
let menu=Menu::with_id_and_items(
app,
"menu_id",
&[&first_submenu]
)?;
使用了with_id_and_items,既要写id,也要加item。
最终menu.rs的代码如下
use tauri::menu::{Menu, MenuItem, Submenu,IconMenuItem};
use tauri::{AppHandle, Wry,Result};
use tauri::image::Image;
pub fn create_menu(app: &AppHandle) ->Result<Menu<Wry>> {
let hello_item = MenuItem::with_id(app, "hello", "Hello", true,None::<&str>)?;
let world_item = MenuItem::with_id(app, "world", "World", true,None::<&str>)?;
let icon_item=IconMenuItem::with_id(
app,
"icon",
"Icon",
true,
Some(Image::from_path("icons/icon.png")?),
None::<&str>,
)?;
let first_submenu = Submenu::with_items(
app,
"first_submenu",
true,
&[&hello_item, &world_item,&icon_item],
)?;
let menu=Menu::with_id_and_items(
app,
"menu_id",
&[&first_submenu]
)?;
Ok(menu)
}
main.rs的关键代码
.setup(|app| {
let app_handle = app.handle();
let menu= menu::create_menu(app_handle)?;
app.set_menu(menu)?;
Ok(())
})
笔者还使用了IconMenuItem,实际上和前面差不多,只是多了图片。
IconMenuItem in tauri::menu - Rusthttps://docs.rs/tauri/2.4.0/tauri/menu/struct.IconMenuItem.html
cargo run
结果如下
看起来文字显示不完全,笔者也没有解决。
间接创建
感觉类似于工厂模式,先要有给工厂,在工厂里面生产菜单,而这个工厂就是MenuBuilder
use tauri::menu::MenuBuilder
笔者看官网是这么写的。
还有其他工厂。
因此,直接复制了,
use tauri::menu::{Menu, MenuBuilder,SubmenuBuilder,CheckMenuItemBuilder,IconMenuItemBuilder};
use tauri::{AppHandle, Wry,Result};
use tauri::image::Image;
pub fn create_menu(app: &AppHandle) ->Result<Menu<Wry>> {
let text_menu = SubmenuBuilder::new(app, "File")
.text("open", "Open")
.text("quit", "Quit")
.build()?;
let lang_str = "en";
let check_sub_item_1 = CheckMenuItemBuilder::new("English")
.id("en")
.checked(lang_str == "en")
.build(app)?;
let check_sub_item_2 = CheckMenuItemBuilder::new("Chinese")
.id("en")
.checked(lang_str == "en")
.enabled(false)
.build(app)?;
let icon_image = Image::from_path("icons/icon.png")
.expect("failed to load icon");
let icon_item = IconMenuItemBuilder::new("icon")
.icon(icon_image)
.build(app)?;
let check_menus = SubmenuBuilder::new(app, "language")
.item(&check_sub_item_1)
.item(&check_sub_item_2)
.build()?;
let menu = MenuBuilder::new(app)
.items(&[&text_menu, &check_menus, &icon_item])
.build()?;
Ok(menu)
}
在前端创建菜单
感觉在后端写菜单,不是很好看,在前端试试。
下面这个链接是一位大佬的模板
jbolda/personal-tray-app: Looking to create a Tauri app that runs in the background and is accessible through the system tray or a keyboard? Use this template as a way to jump start creating your app.https://github.com/jbolda/personal-tray-app/以后文章中经常使用这个模板。这个模板封装的可以。
项目的关键结构如下
写了这么久,很少写前端。哈哈哈哈
如下是前端menu的参考
菜单 | Tauri - Tauri 框架https://v2.tauri.org.cn/reference/javascript/api/namespacemenu/setup.ts中代码
import { useEffect, useState } from "react";
import { Menu,MenuOptions} from "@tauri-apps/api/menu";
export function useTauri(menuItems: MenuOptions) {
const [menu, setMenu] = useState<null | Menu>(null);
useEffect(() => {
async function init() {
const initMenu = await Menu.new(menuItems);
await initMenu.setAsWindowMenu()
setMenu(initMenu);
}
init()
.then(() => console.log("初始化菜单成功"))
.catch((error) => console.error("初始化菜单失败:", error));
}, []);
return { menu };
}
context.ts
import { Menu } from "@tauri-apps/api/menu";
import { createContext } from "react";
export const SystemMenuContext = createContext<{
menu: null | Menu;
}>({
menu: null,
});
App.tsx
import {SystemMenuContext} from './context.ts'
import {useTauri} from "./hook/setup.ts";
import {MenuOptions} from "@tauri-apps/api/menu";
import "./App.css";
function App() {
const menuItems = {
id: "first",
items: [
{
id: "first-item",
text: "First",
},
{
id: "second-item",
text: "Second",
items: [
{
id: "sub-item-1",
text: "Sub Item 1",
},
{
id:'separator',
item:"Separator"
},
{
id: "sub-item-2",
text: "Sub Item 2",
},
{
id: "icon-item",
text: "Icon Item",
icon: "icons/icon.png" ,
}
]
},
// icon menu item
{
id: "icon-item",
text: "Icon Item",
icon: "icons/icon.png" ,
}
]
} as MenuOptions;
return (
<SystemMenuContext.Provider value={useTauri(menuItems)}>
<header className="App-header">
<h1>My Tauri App</h1>
</header>
</SystemMenuContext.Provider>
);
}
export default App;
参数是差不多,都有上面id,text之的类,
感觉差不多,。
总结
简单的在前端和后端创建了menu,差不多,只是创建了,还有其他东西,比如action。后面再说。