Rust Druid 之Controller 控制器使用

2021-05-24 10:48:27 浏览数 (1)

目的

了解Druid 中如何控制Widget的各种界面事件。

概念

Druid 做为数据驱动的GUI 框架, 它的设计分为三层:

Widget 负责界面展示渲染, Lens 负责各界面的数据传递, Controller 则负责各种事件的处理逻辑。

可以把Controller理解成一个事件拦截器,用于处理Widget的事件,它的源码实现也是使用代理模式。

当使用对一个widget对象使用 controller 方法,它会放回一个ControllerHost的代理对象, 内部会调用我们定义的Controller对象方法。

fn controller<C: Controller<T, Self>>(self, controller: C) -> ControllerHost<Self, C> {

ControllerHost::new(self, controller)

}

ControllerHost 部分源码:

代码语言:javascript复制
pub struct ControllerHost<W, C> {
    widget: W,
    controller: C,
}

impl<W, C> ControllerHost<W, C> {
    /// Create a new `ControllerHost`.
    pub fn new(widget: W, controller: C) -> ControllerHost<W, C> {
        ControllerHost { widget, controller }
    }
}

impl<T, W: Widget<T>, C: Controller<T, W>> Widget<T> for ControllerHost<W, C> {
    fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
        // 处理方法
        self.controller
            .event(&mut self.widget, ctx, event, data, env)
    }
    //... 略
}

实践

界面上有两个TextBox, 当TextBox_1 输入文本时, TextBox_2 输出 TextBox_1的反转文本。

Cargo.toml

[dependencies]

druid = { git = "https://github.com/linebender/druid.git", features=["image", "png"]}

代码语言:javascript复制
use druid::{Event, lens, text, widget::{Button, Controller, SizedBox}};
use druid::WidgetExt;
use druid::{
    widget::{Flex, TextBox},
    AppLauncher, Data, Size, Widget, WindowDesc,
};

#[derive(Data, Clone, Debug)]
pub struct DataText {
    pub data: String,	   
    pub data_rev: String,  // 保存反转
}

// 控制器声明
struct TextBoxController;

// 实现Event键盘事件拦截
impl <W:Widget<DataText>> Controller<DataText, W> for TextBoxController {
    fn event(&mut self, child: &mut W, ctx: &mut druid::EventCtx, event: &druid::Event, data: &mut DataText, env: &druid::Env) {
        if let Event::KeyUp(e) = event {
            // 反转
            data.data_rev = data.data.chars().rev().collect::<String>();
        }
        child.event(ctx, event, data, env)
    }

    fn lifecycle(
        &mut self,
        child: &mut W,
        ctx: &mut druid::LifeCycleCtx,
        event: &druid::LifeCycle,
        data: &DataText,
        env: &druid::Env,
    ) {
        child.lifecycle(ctx, event, data, env)
    }

    fn update(&mut self, child: &mut W, ctx: &mut druid::UpdateCtx, old_data: &DataText, data: &DataText, env: &druid::Env) {
        child.update(ctx, old_data, data, env)
    }
}

fn buid_root() -> impl Widget<DataText> {
    let s1 = lens!(DataText, data);
    let s2 = lens!(DataText, data_rev);
    // 设置控制器
    let text_box_up= TextBox::multiline().lens(s1).controller(TextBoxController{});
    let text_box_down = TextBox::multiline().lens(s2);
    let clear_btn = Button::new("Clear").on_click(|_ctx, data: &mut DataText, _env| data.data = "".to_string());
    Flex::column()
    .with_flex_child(text_box_up.expand(), 1.0)
    .with_flex_child(text_box_down.expand(), 1.0)
    .with_child(Flex::row().with_flex_child(SizedBox::empty().fix_height(20.0).expand_width(), 1.0).with_child(clear_btn))
}

fn main() {
    let m = DataText {
        data: "Hello".to_string(),
        data_rev: "World".to_string()
    };
    let w = WindowDesc::new(buid_root()).window_size(Size::new(400.0, 400.0));
    AppLauncher::with_window(w).log_to_console().launch(m).unwrap();
}

运行结果:

0 人点赞