让我们创建一个自定义 Button 组件。
完整代码如下,可查看代码中的注释:
1use makepad_widgets::*;
2
3live_design! {
4 use link::theme::*;
5 use link::shaders::*;
6 use link::widgets::*;
7
8 // 定义一个通用的按钮样式
9 // 继承自 Button
10 pub MyButton = {{MyButton}} <Button> {
11 width: 200, // 按钮宽度
12 height: 50, // 按钮高度
13 margin: {left: 20, right: 20}, // 按钮左右外边距
14
15 text: "My Button", // 按钮文字
16 draw_text: {
17 color: #ffffff // 文字颜色为白色
18 },
19
20 draw_bg: {
21 // 这里最多定义 6 个 instance,否则报错 subtract with overflow
22 instance background_color: #0000ff, // 背景色
23 instance hover_color: #0055ff, // 鼠标悬停时的颜色
24 instance pressed_color: #00008B, // 鼠标按下时的颜色
25
26 instance border_width: 1.0, // 边框宽度
27 instance border_color: #00f3ff, // 边框颜色
28
29 instance glow: 0.0, // 发光效果控制
30 instance hover: 0.0, // 控制鼠标悬停效果
31 instance pressed: 0.0, // 控制鼠标按下效果
32
33 fn pixel(self) -> vec4 {
34 let sdf = Sdf2d::viewport(self.pos * self.rect_size);
35 // sdf.box(0.0, 0.0, self.rect_size.x, self.rect_size.y, 8.0);
36
37 // 计算缩放和位移
38 let scale = 1.0 - self.pressed * 0.04; // 按下时稍微缩小
39 let size = self.rect_size * scale;
40 let offset = (self.rect_size - size) * 0.5; // 居中
41
42 // 绘制外层发光
43 sdf.box(
44 offset.x , // 向外扩展4个像素
45 offset.y ,
46 size.x , // 两边各扩展4个像素
47 size.y ,
48 9.0 // 稍大的圆角
49 );
50
51 // 发光效果 - 使用半透明的边框颜色
52 let glow_alpha = self.glow * 0.5; // 控制发光强度
53 sdf.fill_keep(vec4(self.border_color.xyz, glow_alpha));
54
55
56 // 简化绘制,只保留主体
57 sdf.box(
58 offset.x,
59 offset.y,
60 size.x,
61 size.y,
62 8.0
63 );
64
65 // 未按下时显示阴影,按下时减弱阴影
66 let shadow_alpha = (1.0 - self.pressed) * 0.2;
67 sdf.fill_keep(vec4(0.,0.,0.,shadow_alpha));
68
69 // 基础颜色
70 let base_color = self.background_color;
71
72 // hover效果通过降低透明度来实现,不直接修改颜色
73 let hover_alpha = self.hover * 0.2;
74 let color_with_hover = mix(
75 base_color,
76 vec4(1.0, 1.0, 1.0, 1.0),
77 hover_alpha
78 );
79
80 // pressed效果
81 let final_color = mix(
82 color_with_hover,
83 self.pressed_color,
84 self.pressed
85 );
86
87
88 // 先填充主体颜色
89 sdf.fill_keep(final_color);
90
91 // 边框发光效果
92 let border_glow = max(self.hover * 0.5, self.glow);
93 let border_color = mix(
94 self.border_color,
95 vec4(1.0, 1.0, 1.0, 0.8),
96 border_glow
97 );
98 sdf.stroke(border_color, self.border_width);
99
100 return sdf.result
101 }
102 }
103
104 }
105}
106
107
108// 定义组件结构体
109#[derive(Live,Widget)]
110pub struct MyButton {
111 // 继承 Button 的所有功能
112 #[deref]
113 button: Button,
114 #[rust]
115 initialized: bool, // 标记是否已初始化
116}
117
118impl LiveHook for MyButton {
119 fn after_new_from_doc(&mut self, cx: &mut Cx) {
120 log!("MyButton: after_new_from_doc");
121 self.initialized = true; // 在创建后就将其标记为已初始化
122 self.button.after_new_from_doc(cx);
123 log!("button text is empty? {:?}", self.button.text.as_ref())
124 }
125
126 fn before_apply(&mut self, cx: &mut Cx, apply: &mut Apply, index: usize, nodes: &[LiveNode]) {
127 log!("MyButton: before_apply");
128 self.button.before_apply(cx, apply, index, nodes);
129 }
130
131 fn after_apply(&mut self, cx: &mut Cx, apply: &mut Apply, index: usize, nodes: &[LiveNode]) {
132 log!("MyButton: after_apply");
133 self.button.after_apply(cx, apply, index, nodes);
134 }
135}
136
137
138impl Widget for MyButton {
139 fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
140 log!("MyButton handle_event");
141 log!("MyButton not initialized!");
142 self.button.handle_event(cx, event, scope);
143 }
144
145 fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
146 log!("MyButton draw_walk");
147 self.initialized = true;
148 log!("MyButton initialized!");
149 self.button.draw_walk(cx, scope, walk)
150 }
151}
152
153impl MyButtonRef {
154 pub fn clicked(&self, actions: &Actions) -> bool {
155 self.borrow().map(|button| button.button.clicked(actions)).unwrap_or(false)
156 }
157
158 pub fn apply_over(&self, cx: &mut Cx, nodes: LiveNodeSlice) {
159 if let Some(mut inner) = self.borrow_mut() {
160 log!("Applying style to MyButton");
161 // 应用样式到内部按钮
162 inner.button.apply_over(cx, nodes);
163 // 确保重绘
164 inner.button.redraw(cx);
165 } else {
166 log!("Failed to borrow MyButton - this may indicate an initialization problem");
167 }
168 }
169
170 pub fn set_text_and_redraw(&self, cx: &mut Cx, text: &str) {
171 if let Some(mut inner) = self.borrow_mut() {
172 inner.button.set_text_and_redraw(cx, text);
173 inner.button.redraw(cx);
174 }
175 }
176
177 // 添加检查方法
178 pub fn is_some(&self) -> bool {
179 self.borrow().is_some()
180 }
181}
总的来说,这段代码定义了一个高度可定制的按钮组件 MyButton
,具有丰富的视觉效果如发光、阴影、悬停变色等。它继承了 Button
的功能,还添加了初始化检查和一些辅助方法。通过 live_design!
宏可以方便地定义按钮的样式。
initialized
字段来标记是否已初始化。LiveHook
trait,定义了组件在不同生命周期的行为:
after_new_from_doc()
: 创建后将 initialized
设为 true,表示已初始化before_apply()
和 after_apply()
: 在应用属性前后执行一些操作handle_event()
: 处理事件draw_walk()
: 绘制组件MyButtonRef
实现了一些方法:
clicked()
: 检查按钮是否被点击apply_over()
: 应用样式到按钮set_text_and_redraw()
: 设置按钮文字并重绘is_some()
: 检查按钮是否存在特别说明:定义 Widget 主要的 Live 属性标记如下。
#[live]
- 表示此属性可在DSL中访问和修改#[rust]
- 表示此属性只在Rust代码中使用#[calc]
- 表示这是一个计算属性#[live(default)]
- 带默认值的属性#[deref]
- 表示继承另一个组件的属性