概念
GUI有两种菜单类型, 一种是菜单栏,另一种是右键菜单列表。
在Druid中这两种菜单都由Menu
类型来指定。
窗口菜单栏通过WindowDesc::menu() 方法来绑定。
右键菜单栏通过Controller中的右键事件EventCtx::show_context_menu()来展示。
Menu的事件触发使用 on_activate() 来绑定事件逻辑处理, 可用在该函数中操作数据对象或者向其他组件发送消息。
Menu 中还有:
pub fn refresh_on(mut self, refresh: impl FnMut(&T, &T, &Env) -> bool 'static);
pub fn rebuild_on(mut self, rebuild: impl FnMut(&T, &T, &Env) -> bool 'static);
用于菜单栏变动的更新。
实践
通过一个菜单栏读入文件到TextBox,通过右键菜单来清空TextBox读入的内容。
Cargo.toml
[dependencies]
druid = { git = "https://github.com/linebender/druid.git", features=["image", "png"]}
代码语言:javascript复制use std::fs::File;
use std::io::Read;
use druid::Event;
use druid::LocalizedString;
use druid::WidgetExt;
use druid::widget::Controller;
use druid::{lens, Env, FileDialogOptions, MenuItem, WindowId};
use druid::{widget::TextBox, AppLauncher, Menu, Widget, WindowDesc};
use druid::{AppDelegate, Command, Data, FileSpec};
#[derive(Data, Clone)]
struct AppData {
data: String,
}
struct MenuDelegate;
// 处理菜单栏传来的打开文件的Command命令
impl AppDelegate<AppData> for MenuDelegate {
fn command(
&mut self,
ctx: &mut druid::DelegateCtx,
target: druid::Target,
cmd: &druid::Command,
data: &mut AppData,
env: &Env,
) -> druid::Handled
{
if let Some(e) = cmd.get(druid::commands::OPEN_FILE) {
let mut f = File::open(e.path()).expect("Open File Fail");
f.read_to_string(&mut data.data).expect("Read Fail");
return druid::Handled::Yes;
}
return druid::Handled::No;
}
}
// 处理右键菜单事件
struct EditController;
impl <W:Widget<AppData>> Controller<AppData, W> for EditController {
fn event(&mut self, child: &mut W, ctx: &mut druid::EventCtx, event: &Event, data: &mut AppData, env: &Env) {
if let Event::MouseDown(e) = event {
if e.button.is_right() {
// 展示菜单
ctx.show_context_menu(make_context_menu(), e.pos);
}
}
child.event(ctx, event, data, env)
}
}
fn make_window() -> impl Widget<AppData> {
let data = lens!(AppData, data);
let text = TextBox::new().lens(data).expand().controller(EditController{});
text
}
// 菜单栏
fn make_menu(_: Option<WindowId>, data: &AppData, _: &Env) -> Menu<AppData> {
let mut menu = Menu::empty();
let mut menu_file = Menu::new("File");
let txt = FileSpec::new("Text file", &["txt"]);
let save_dialog_options = FileDialogOptions::new()
.allowed_types(vec![txt])
.default_type(txt)
.name_label("Target")
.title("OpenFile")
.button_text("Open");
// 菜单处理事件,这里是发送打开文件的Command
let menu_open_act = MenuItem::new("Open").on_activate(move |ctx, _data: &mut AppData, _env| {
ctx.submit_command(druid::commands::SHOW_OPEN_PANEL.with(save_dialog_options.clone()));
});
menu_file = menu_file.entry(menu_open_act);
menu = menu.entry(menu_file);
menu
}
// 右键菜单
fn make_context_menu()->Menu<AppData>{
Menu::empty()
.entry(MenuItem::new(LocalizedString::new("clear")).on_activate(|_ctx, data:&mut AppData, _env|{
data.data.clear();
}))
}
fn main() {
// window 绑定菜单栏
let window = WindowDesc::new(make_window()).menu(make_menu);
AppLauncher::with_window(window)
.log_to_console()
.delegate(MenuDelegate{})
.launch(AppData {
data: String::new(),
})
.unwrap();
}