Rust Druid 之Menu菜单栏

2021-05-28 14:36:14 浏览数 (1)

概念

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();
}

菜单栏菜单栏

0 人点赞