目的
了解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();
}
运行结果: