网站首页 > 精选文章 / 正文
下面让我们手动实现一个自己的小部件
首先创建我们的rust项目
cargo new druid-widget
创建完成项目后添加包
druid = { git = "https://github.com/linebender/druid.git"}
在main.rs引入我们需要用到的引用
use druid::{widget::{Label, LabelText, ControllerHost, Click, Flex},RoundedRectRadii,Affine,UnitPoint,LinearGradient,Insets,theme, Data, Size, Env, WindowDesc, AppLauncher, Color, Lens};
use druid::widget::prelude::*;
我们先创建我们的小部件结构体
struct MyButton<T>{
//按钮上面的文字使用Label
label:Label<T>,
//尺寸
label_size:Size
}
给MyButton实现new 方法和form_label方法,on_click点击事件
impl<T: Data> MyButton<T> {
pub fn new(text: impl Into<LabelText<T>>) -> MyButton<T>{
//根据传入文字参数,创建按钮
MyButton::form_label(Label::new(text))
}
pub fn form_label(label:Label<T>) -> MyButton<T>{
//根据传入label部件创建按钮
MyButton
{
label,
label_size: Size::ZERO,
}
}
//按钮点击事件(也可以实现其余的事件,鼠标悬浮,鼠标移除等)
pub fn on_click(
self,
f: impl Fn(&mut EventCtx,&mut T,&Env)+ 'static,
) -> ControllerHost<Self,Click<T>> {
//controller事件处理,后面会详细讲解,Click是druid里面的部件可以直接使用,点击事件
ControllerHost::new(self, Click::new(f))
}
}
下面让我们的MyButton实现Widget,实现部件显示(里边的代码和方法我都添加了注释)
impl<T:Data> Widget<T> for MyButton<T> {
fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
match event {
Event::MouseDown(_) => {
if !ctx.is_disabled() {
ctx.set_active(true);
//请求重画
ctx.request_paint();
}
},
Event::MouseUp(_) => {
if ctx.is_active() && ctx.is_disabled() {
//请求重画
ctx.request_paint();
//trace!("Button {:?} released",ctx.widget_id());
}
ctx.set_active(false);
},
_ => {}
}
}
fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &T, env: &Env) {
if let LifeCycle::HotChanged(_) | LifeCycle::DisabledChanged(_) = event {
ctx.request_paint();
}
self.label.lifecycle(ctx, event, data, env)
}
fn update(&mut self, ctx: &mut UpdateCtx, old_data: &T, data: &T, env: &Env) {
//调用label的update方法
self.label.update(ctx, old_data, data, env)
}
fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, data: &T, env: &Env) -> Size {
//设置padding的大小
let padding = Size::new(LABEL_INSETS.x_value(), LABEL_INSETS.y_value());
//shrink 按照传入的padding设置label的边距,(给定的大小也是取整数,四舍五入)
//loosen 复制一份相同大小的尺寸
let label_bc = bc.shrink(padding).loosen();
//调用label的layout方法
self.label_size = self.label.layout(ctx, &label_bc, data, env);
//获取部件的最小宽度
let min_height = env.get(theme::BORDERED_WIDGET_HEIGHT);
//返回偏移量
let baseline = self.label.baseline_offset();
//设置偏移量
ctx.set_baseline_offset(baseline + LABEL_INSETS.y1);
//按钮的大小
let button_size = bc.constrain(Size::new(
self.label_size.width + padding.width,
(self.label_size.height + padding.height).max(min_height),
));
println!("{:?}",button_size);
button_size
}
fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env) {
//active 表示是否鼠标按下
//disabled 表示是否禁用
let is_active = ctx.is_active() && !ctx.is_disabled();
//hot 鼠标是否悬停在按钮上面
let is_hot = ctx.is_hot();
//size 布局大小
let size = ctx.size();
//获取按钮边框的宽度
let stroke_width = env.get(theme::BUTTON_BORDER_WIDTH);
let rounded_rect = size
//转换成原点格式 Rect::new(0., 0., self.width, self.height)
.to_rect()
//计算尺寸
.inset(-stroke_width / 2.0)
//拐角的半径 env.get(theme::BUTTON_BORDER_RADIUS)
.to_rounded_rect(RoundedRectRadii {
top_left:size.width/2.0,
top_right:size.width/2.0,
bottom_right:size.width/2.0,
bottom_left:size.width/2.0,
});
//
let bg_gradient = if ctx.is_disabled() {
//禁用的效果
LinearGradient::new(
UnitPoint::TOP,
UnitPoint::BOTTOM,
(
env.get(theme::DISABLED_BUTTON_LIGHT),
env.get(theme::DISABLED_BUTTON_DARK),
),
)
//鼠标按下的效果
} else if is_active {
LinearGradient::new(
UnitPoint::TOP,
UnitPoint::BOTTOM,
(Color::YELLOW,Color::WHITE)
//(env.get(theme::BUTTON_DARK), env.get(theme::BUTTON_LIGHT)),
)
} else {
//从某个位置到某个位置,可以渐变
LinearGradient::new(
//从顶部到底部 由红色渐变到白色
UnitPoint::TOP,
UnitPoint::BOTTOM,
(Color::PURPLE,Color::WHITE)
//(env.get(theme::BUTTON_LIGHT), env.get(theme::BUTTON_DARK)),
)
};
//边框的颜色
let border_color = if is_hot && !ctx.is_disabled() {
//env.get(theme::BORDER_LIGHT)
Color::RED
} else {
//env.get(theme::BORDER_DARK)
Color::WHITE
};
//
ctx.stroke(rounded_rect, &border_color, stroke_width);
ctx.fill(rounded_rect, &bg_gradient);
let label_offset = (size.to_vec2() - self.label_size.to_vec2()) / 2.0;
ctx.with_save(|ctx| {
ctx.transform(Affine::translate(label_offset));
//调用label的paint
self.label.paint(ctx, data, env);
});
}
}
下面我们创建我们的Data数据结构体
#[derive(Clone,Data,Lens)]
struct State{
//label显示的文字
text:String,
}
编写窗体部分
fn builder_ui() -> impl Widget<State> {
//MyButton创建,这里的Label使用了Data模型,MyButton添加了点击事件
let my_button = MyButton::form_label(Label::new(|state: &State,_env: &Env|{ format!("{}",state.text)}))
.on_click(|_etc,state,_env|{
println!("点击了按钮");
state.text = "你点击了".to_string();
});
//Flex布局,这个后面也会讲解一下
Flex::row()
//把我们的组件添加到布局
.with_child(my_button)
}
编写启动方法
fn main() {
let win = WindowDesc::new(builder_ui()).title("自定义按钮").window_size((300.0,300.0));
let _app = AppLauncher::with_window(win)
.log_to_console()
.launch(State { text: "按钮".to_string() });
}
运行自定义按钮出现
Tags:druid参数配置
猜你喜欢
- 2025-01-02 Java 与 MySQL 数据库连接池优化:提升数据访问效率的关键策略
- 2025-01-02 Spring Boot 入门系列(二十四)多环境配置,3分钟搞定
- 2025-01-02 搞懂Druid之连接获取和归还
- 2025-01-02 Apache Druid连接开通Kerberos认证的Kafka处理过程
- 2025-01-02 非Spring Boot 或 Spring Cloud 项目如何集成Druid数据源
- 2025-01-02 Spring Boot 2.x基础教程:默认数据源Hikari的配置详解
- 2025-01-02 连接池:别让连接池帮了倒忙
- 2025-01-02 SpringBoot事务事件监听:事务事件监听注解详细使用示例(二)
- 2025-01-02 SpringBoot实现动态数据源配置
- 2025-01-02 mybatis系列-5分钟hikaryCP、Druid、C3p0数据库连接池最全对比