属性继承

继承的组件实例可以:

  1. 覆盖父组件的属性
  2. 添加新的属性
  3. 扩展新的子组件实例
1// 基础卡片组件
2Card = {{MyView}} {
3    // 基础属性
4    background_color: #fff,
5    corner_radius: 4.0,
6    shadow_color: #0007,
7
8    // 基础布局
9    flow: Down,
10    spacing: 10
11}
12
13// 产品卡片扩展
14ProductCard = <Card> {
15    // 1. 覆盖父组件属性
16    background_color: #f5f5f5,  // 覆盖背景色
17    corner_radius: 8.0,         // 覆盖圆角
18
19    // 2. 添加新的属性
20    width: 200,
21    height: 300,
22    border_width: 1.0,         // 新增边框
23
24    // 3. 添加子组件
25    image_container = <View> {
26        height: 160
27    }
28
29    title = <Label> {
30        text_wrap: Word,
31        color: #333
32    }
33
34    price = <Label> {
35        color: #f00,
36        font_size: 18
37    }
38
39    buy_button = <Button> {
40        width: Fill,
41        label: "Buy Now"
42    }
43}
44
45// 可以进一步继承和扩展
46FeaturedProductCard = <ProductCard> {
47    // 覆盖属性
48    height: 350,
49    background_color: #fff0f0,
50
51    // 添加新组件
52    badge = <Label> {
53        text: "Featured",
54        background_color: #f00,
55        color: #fff
56    }
57}

在 Live DSL 里 ProductCard = <Card> 这种方法一般是代表「引用」一个组件。

Live 继承系统的特点:

继承方式

1. 属性继承

1ParentView = {{View}} {
2    color: #f00,
3    spacing: 10
4}
5
6ChildView = <ParentView> {
7    // 继承 color 和 spacing
8    // 覆盖 color
9    color: #00f,
10    // 添加新属性
11    margin: 20
12}

2. 布局继承

1CardBase = {{View}} {
2    flow: Down,
3    spacing: 10,
4
5    header = <View> {
6        height: 50
7    }
8}
9
10CustomCard = <CardBase> {
11    // 继承布局属性
12    // 修改子组件
13    header = <View> {
14        height: 60,
15        background_color: #f00
16    }
17}

3. 动画继承

1ButtonBase = {{Button}} {
2    animator: {
3        hover = {
4            default: off
5            on = {
6                apply: {color: #f00}
7            }
8        }
9    }
10}
11
12CustomButton = <ButtonBase> {
13    // 继承动画
14    animator: {
15        // 添加新的动画状态
16        pressed = {
17            default: off
18            on = {
19                apply: {scale: 0.9}
20            }
21        }
22    }
23}

4. 事件处理继承

1ClickableView = {{View}} {
2    cursor: Pointer,
3    grab_key_focus: true
4}
5
6InteractiveCard = <ClickableView> {
7    // 继承交互行为
8    // 添加视觉反馈
9    animator: {
10        pressed = {
11            default: off
12            on = {
13                apply: {
14                    background_color: #eee
15                }
16            }
17        }
18    }
19}

继承式创建新组件

1live_design!{
2	// 定义一个通用的按钮样式
3    // 继承自 Button
4    MyButton = {{MyButton}} <Button> {
5        width: 200,
6        height: 50,
7        margin: {left: 20, right: 20},
8
9        text: "My Button",
10        draw_text: {
11            color: #ffffff
12        },
13    }
14}
15
16#[derive(Live,Widget)]
17pub struct MyButton {
18    // 继承 Button 的所有功能
19    #[deref]
20    button: Button,
21    #[rust]
22    initialized: bool,
23}

注意这种写法: MyButton = {{MyButton}} <Button> ,是基于已有 Button Widget 进行继承性创建自己的 MyButton 组件。

这种方式适合需要在扩展现有组件时使用。

继承的建议

  1. 保持继承层次浅。避免过深的继承链,让组件关系容易理解。
  2. 明确命名约定。基础组件使用 Base/Common 前缀,变体使用功能相关的名称。
  3. 适度使用继承。不是所有组件都需要继承,有时直接创建更合适。

Rust 结构体的继承

Rust 结构体中的 #[deref] 属性宏:

1struct DrawWave {
2    #[deref] draw_super: DrawQuad,  // 继承 DrawQuad 的字段和方法
3    #[live] gain: f32,
4    #[live] vu_left: f32,
5}

这是一个编译期的 Rust trait 实现机制:

  • 通过 makepad_derive_widget 宏生成对 DrawQuadDeref/DerefMut trait 实现
  • 允许 DrawWave 直接访问 DrawQuad 的字段和方法
  • 是 Rust 语言层面的“委托/继承”模式(用 Deref 模拟继承是个反模式,但用在 Makepad 框架中正好)

两者的配合使用:

1// Rust 层的基础组件实现
2struct Button {
3    #[deref] draw_super: DrawQuad, // 继承绘制能力
4    #[live] color: Vec4
5}
6
7// Live DSL 层的组件定制
8DefaultButton = <Button> {  // 引用并覆盖属性
9    color: #f00
10}

#[deref] 与 Live DSL 中引用组件实例的关键区别:

  1. #[deref] 是 Rust 编译期的类型系统特性,用于实现组件的基础功能继承,并且可以委托调用相关的方法。
  2. Live DSL 中引用组件主要是用于创建组件结构。