
关于
Robius 跨平台 Widget 模式专家,用于构建原生性能的 Rust UI 组件。
name: robius-widget-patterns description: | 关键:用于 Robius widget 模式。触发条件: apply_over, TextOrImage, modal, 可复用, 模态, collapsible, drag drop, reusable widget, widget design, pageflip, 组件设计, 组件模式 risk: unknown source: community
Robius Widget 模式技能
基于 Robrix 和 Moly 代码库模式设计可复用 Makepad widget 的最佳实践。
源代码库:
- Robrix:Matrix 聊天客户端 - Avatar、RoomsList、RoomScreen widgets
- Moly:AI 聊天应用 - Slot、ChatLine、PromptInput、AdaptiveView widgets
何时使用
在以下情况使用此技能:
- 创建可复用的 Makepad widgets
- 设计 widget 组件 API
- 实现文本/图片切换模式
- Makepad 中的动态样式
- 关键词:robrix widget、makepad 组件、可复用 widget、widget 设计模式
生产模式
生产就绪的 widget 模式,参见 _base/ 目录:
| 模式 | 描述 | |---------|-------------| | 01-widget-extension | 为 widget 引用添加辅助方法 | | 02-modal-overlay | 使用 DrawList2d 覆盖层的弹窗、对话框 | | 03-collapsible | 可展开/折叠的区域 | | 04-list-template | 使用 LivePtr 模板的动态列表 | | 05-lru-view-cache | 内存高效的视图缓存 | | 14-callout-tooltip | 带箭头定位的工具提示 | | 20-redraw-optimization | 高效重绘模式 | | 15-dock-studio-layout | IDE 风格的可调整面板 | | 16-hover-effect | 使用实例变量的悬停效果 | | 17-row-based-grid-layout | 动态网格布局 | | 18-drag-drop-reorder | 拖放 widget 重排序 | | 19-pageflip-optimization | PageFlip 切换优化,即刻销毁/缓存模式 | | 21-collapsible-row-portal-list | 在 portal 列表中使用 FoldHeader 自动分组连续项 | | 22-dropdown-overlay | 使用 DrawList2d 覆盖层的下拉弹窗(无布局推送) |
标准 Widget 结构
use makepad_widgets::*;
live_design! {
use link::theme::*;
use link::widgets::*;
pub MyWidget = {{MyWidget}} {
width: Fill, height: Fit,
flow: Down,
// 在 DSL 中定义子 widget
inner_view = <View> {
// ...
}
}
}
#[derive(Live, LiveHook, Widget)]
pub struct MyWidget {
#[deref] view: View, // 委托给内部 View
#[live] some_property: f64, // DSL 可配置属性
#[live(100.0)] default_val: f64, // 带默认值
#[rust] internal_state: State, // 仅 Rust 状态(不在 DSL 中)
#[animator] animator: Animator, // 用于动画
}
impl Widget for MyWidget {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
self.view.handle_event(cx, event, scope);
// 自定义事件处理...
}
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
self.view.draw_walk(cx, scope, walk)
}
}
文本/图片切换模式
用于显示文本或图片的 widget 常见模式(如头像):
live_design! {
pub Avatar = {{Avatar}} {
width: 36.0, height: 36.0,
align: { x: 0.5, y: 0.5 }
flow: Overlay, // 将视图堆叠在一起
text_view = <View> {
visible: true, // 默认可见
show_bg: true,
draw_bg: {
uniform background_color: #888888
fn pixel(self) -> vec4 {
let sdf = Sdf2d::viewport(self.pos * self.rect_size);
let c = self.rect_size * 0.5;
sdf.circle(c.x, c.x, c.x)
sdf.fill_keep(self.background_color);
return sdf.result
}
}
text = <Label> {
text: "?"
}
}
img_view = <View> {
visible: false, // 默认隐藏
img = <Image> {
fit: Stretch,
width: Fill, height: Fill,
}
}
}
}
#[derive(LiveHook, Live, Widget)]
pub struct Avatar {
#[deref] view: View,
#[rust] info: Option<UserInfo>,
}
impl Avatar {
/// 显示文本内容,隐藏图片
pub fn show_text<T: AsRef<str>>(
&mut self,
cx: &mut Cx,
bg_color: Option<Vec4>,
info: Option<AvatarTextInfo>,
username: T,
) {
self.info = info.map(|i| i.into());
// 获取首字符
let first_char = utils::first_letter(username.as_ref())
.unwrap_or("?").to_uppercase();
self.label(ids!(text_view.text)).set_text(cx, &first_char);
// 切换可见性
self.view(ids!(text_view)).set_visible(cx, true);
self.view(ids!(img_view)).set_visible(cx, false);
// 应用可选背景色
if let Some(color) = bg_color {
self.view(ids!(text_view)).apply_over(cx, live! {
draw_bg: { background_color: (color) }
});
}
}
}