Style
GPUI 在构建 Element 时设置样式。Styled trait 提供布局、间距、颜色、边框和文字的链式方法。许多名称有意对应 Tailwind CSS utility:flex items-center gap-2 px-3 在 Rust 中写成 .flex().items_center().gap_2().px_3()。可以借助这套词汇阅读和编写 GPUI 布局,但传入的是带类型的 Rust 值,而非 CSS class。
use gpui_kit::*;
use gpui_kit::component::ActiveTheme as _;
div()
.flex()
.items_center()
.gap_2()
.px_3()
.py_2()
.bg(cx.theme().background)
.text_color(cx.theme().foreground)
.child("Search results")
链上的每次调用都会取得元素并返回元素。render 可以根据当前状态构建新树;需要长期保存的应用状态应放在 Entity 或带 key 的元素状态中。样式链描述本帧的外观,并非样式表或长期保存的组件实例。逐帧构建组件的方式见 RenderOnce。
#从第一个布局开始
先找出负责可用空间的区域,再决定哪个子元素宽度固定、哪个可以伸展。用下面的完整程序替换 examples/hello_world/src/main.rs,然后在仓库根目录运行 cargo run -p hello_world:
use gpui_kit::*;
use gpui_kit::component::{h_flex, v_flex, ActiveTheme as _};
use gpui_kit::prelude::FluentBuilder as _;
struct StyleExample;
impl Render for StyleExample {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let compact = window.viewport_size().width < px(600.);
let layout = if compact { v_flex() } else { h_flex() };
let documents = (1..=60).map(|number| {
div().p_2().child(format!("Document {number:02}"))
});
layout
.items_stretch()
.size_full()
.bg(cx.theme().background)
.text_color(cx.theme().foreground)
.child(
v_flex()
.when(!compact, |nav| nav.w_64().flex_shrink_0())
.p_3()
.gap_2()
.child("Navigation")
.child("Overview · Documents"),
)
.child(
v_flex()
.flex_1()
.min_w_0()
.min_h_0()
.child(h_flex().p_3().child("Documents"))
.child(
v_flex()
.id("document-list")
.flex_1()
.min_h_0()
.overflow_y_scroll()
.child(v_flex().p_3().gap_2().children(documents)),
),
)
}
}
fn main() {
gpui_kit::application().run(|cx| {
gpui_kit::init(cx);
gpui_kit::open_window(WindowOptions::default(), cx, |_, cx| {
cx.new(|_| StyleExample)
})
.expect("failed to open window");
});
}
窗口较宽时,导航区域以固定的 w_64() 宽度位于左侧,文档面板占用剩余空间。把内容区域缩小到 600 逻辑像素以下:同一个导航区域会移到文档面板上方,而文档列表仍可独立滚动。滚到 Document 60,再向两个方向调整窗口宽度;列表滚动时,标题栏应保持在原位。这里的阈值是在视图 render 时求值的 Rust 条件,不是 Tailwind 响应式前缀。这个小练习中的导航只是示意文本;真实应用应提供可操作的导航控件。
h_flex() 建立横向布局,并默认让子元素沿交叉轴居中。.items_stretch() 覆盖这个默认值,让两个面板占满整行高度。v_flex() 建立纵向布局,子元素默认沿宽度方向拉伸。宽窗口中的导航面板保持固定宽度,文档面板占用剩余宽度。滚动区域占据标题栏下方的剩余高度。窗口大小为最外层的 .size_full() 提供确定高度,min_h_0() 则允许内部弹性区域收缩成滚动视口。
#确定尺寸该由谁负责
| 需求 | 设置位置 | 原因 |
|---|---|---|
| 同级元素之间的距离 | 在父元素上设置 .gap_3() | gap 分隔子元素,不会在容器外边缘增加内边距。 |
| 面板内部的留白 | 在面板上设置 .p_3() | padding 把内容向内推,并参与面板的布局尺寸。 |
| 固定侧栏与弹性内容 | 侧栏 .w_64().flex_shrink_0();内容 .flex_1().min_w_0() | 侧栏保持宽度,内容可收缩到小于文字自然宽度。 |
| 标题栏下方的滚动内容 | 有确定高度的纵向容器;滚动子元素 .flex_1().min_h_0() | 子元素可缩进剩余高度,从而形成真正的滚动视口。 |
| 父元素宽度的一半 | 在子元素上设置 .w(relative(0.5)) | 布局时,比例按相关父尺寸解析。 |
w_full() 和 h_full() 表示填满可用尺寸。百分比高度仍要求上层有确定高度。最小值和最大值限制最终尺寸;它们不会让没有高度约束的滚动区域自动形成视口。横向布局中的单行长标题可在标题容器上组合 .flex_1().min_w_0().truncate()。.truncate() 只改变文字溢出方式,无法让不肯收缩的同级元素腾出宽度。
#选择裁剪、滚动或定位
.overflow_hidden() 裁剪内容,但不会让内容滚动。在有状态的元素上,.overflow_y_scroll() 可在高度受限后启用纵向滚动。GPUI Kit 的 .overflow_y_scrollbar() 会加入可见滚动条,并以原元素作为滚动区域;它来自 ScrollableElement 扩展,而非 Styled 方法。每个滚动区域应有一个明确的负责元素。如果滚动条要贴着面板边缘,内容 padding 应放在滚动区域内部。滚动归属与测量方式见编码指南。
普通 Flex 子元素占用布局空间。要让角标覆盖内容而不占一行或一列,可在容器上设置 .relative(),在角标上设置 .absolute().top_0().right_0()。偏移 setter 用于定位绝对子元素;它们不会自动将普通 Flex 子元素变成绝对定位。后面的同级元素通常绘制在前面的同级元素之上;通用 Styled 没有 z_index(...) 方法。
#主题与尺寸尺度
应用界面的颜色和圆角应从 cx.theme() 读取语义值。GPUI Kit 组件已应用正常的主题外观;实例样式可用于局部布局或有意的细化。具名间距和尺寸方法使用 rem 尺度:_1 为 0.25rem、_2 为 0.5rem、_3 为 0.75rem、_4 为 1rem。在 GPUI Kit 的 Root 中,当前主题的基础字号决定窗口的 rem 大小,因此字号或缩放变化也会改变基于 rem 的几何尺寸。命名尺度不合适时用 .gap(rems(0.625)) 等带类型的 setter;只有确实需要像素尺寸时才使用 px(...)。长度类型见几何,主题与 rem 设置见字体。
#排查布局结果
| 现象 | 检查方向 |
|---|---|
| 窗口缩窄后导航没有移到文档上方 | 把可绘制内容区域缩到 600 逻辑像素以下。条件在 render 中读取 window.viewport_size().width;仅改变显示器缩放倍率不会跨过这个逻辑像素阈值。 |
无法滚到 Document 60 | 把指针移到文档列表上并在该区域滚动。让有高度约束的列表区域拥有 .id("document-list").overflow_y_scroll(),并在该区域及外层纵向容器上保留 .flex_1().min_h_0()。 |
| 滚动时标题栏也跟着移动 | 确认标题栏与列表视口是同级元素,不要把标题栏放进滚动元素内部。 |
| 面板标题在顶部消失 | h_flex() 默认让子元素居中;让整行子元素拉伸,或让该面板占满高度。 |
| 标题溢出而没有截断 | 在弹性子元素上用 .min_w_0() 解除最小宽度约束,并限制文字宽度。 |
| 列表越过窗口而没有滚动 | 让祖先拥有确定高度,用 .min_h_0() 允许弹性子元素收缩,并在预期的视口上设置滚动。 |
| 滚动条缩在面板边缘以内 | 检查哪个元素负责滚动,以及 padding 是否包在滚动元素外面。 |
| 主题缩放后布局变化 | 重新检查基于 rem 的尺寸,以及仍沿用旧 rem 大小的测量缓存。 |
#常用 Styled 方法
Tailwind 名称中的连字符在 GPUI 方法中写成下划线。有对应样式概念时,第一列链接到相应的 Tailwind CSS 官方参考页,并在新窗口打开。表中列的是 GPUI 方法名;可在实现 Styled 的值上调用,例如 div().gap_2()。这些表格覆盖 Styled 中各类独立操作,以及宏生成的通用 setter;大量数字变体按下文所述的方法族归纳,不逐个占用数千行。链接说明对应的样式概念,不表示 GPUI 与浏览器的行为完全相同。
#显示与可见性
| 方法 | 说明 |
|---|---|
block | 使用块布局。 |
flex | 使用 Flexbox 布局。 |
grid | 使用 Grid 布局。 |
hidden | 从布局和绘制中移除元素。 |
invisible | 保留布局空间,但不绘制元素。 |
visible | 保留布局,并恢复绘制。 |
#Flexbox 与 Grid
| 方法 | 说明 |
|---|---|
flex_row | 沿横向排列 Flex 子项。 |
flex_col | 沿纵向排列 Flex 子项。 |
flex_wrap | 允许 Flex 子项换行。 |
flex_nowrap | 让 Flex 子项保持在同一行。 |
items_start | 让子项沿交叉轴向起点对齐。 |
items_center | 让子项沿交叉轴居中。 |
items_stretch | 让子项沿交叉轴拉伸。 |
self_center | 让当前子项沿父容器的交叉轴居中。 |
self_stretch | 让当前子项沿父容器的交叉轴拉伸。 |
justify_center | 让子项沿主轴居中。 |
justify_between | 把主轴剩余空间分配到子项之间。 |
content_between | 沿交叉轴分配换行后的各行。 |
flex_basis | 设置带类型的主轴初始尺寸。 |
flex_1 | 以零 Flex basis 伸长或收缩。 |
flex_auto | 以自动 basis 为起点伸长或收缩。 |
flex_grow_1 | 允许 Flex 子项伸长。 |
flex_shrink_0 | 阻止 Flex 子项收缩。 |
grid_cols | 设置指定列数的 Grid 模板。 |
grid_rows | 设置指定行数的 Grid 模板。 |
col_span | 跨越指定数量的 Grid 列。 |
row_span | 跨越指定数量的 Grid 行。 |
flex_row_reverse | 反向排列横向子项。 |
flex_col_reverse | 反向排列纵向子项。 |
flex_wrap_reverse | 反向排列换行后的 Flex 行。 |
items_end | 让子项沿交叉轴向终点对齐。 |
items_baseline | 按文字基线对齐子项。 |
self_start | 让当前子项沿交叉轴向起点对齐。 |
self_end | 让当前子项沿交叉轴向终点对齐。 |
self_flex_start | 让当前子项向 Flex 起点对齐。 |
self_flex_end | 让当前子项向 Flex 终点对齐。 |
self_baseline | 按文字基线对齐当前子项。 |
justify_start | 让子项向主轴起点聚集。 |
justify_end | 让子项向主轴终点聚集。 |
justify_around | 在子项周围分配空间。 |
justify_evenly | 沿主轴均匀分配间距。 |
content_normal | 使用默认的交叉轴行排列。 |
content_start | 让换行后的各行向交叉轴起点聚集。 |
content_center | 让换行后的各行沿交叉轴居中。 |
content_end | 让换行后的各行向交叉轴终点聚集。 |
content_around | 在换行后的各行周围分配空间。 |
content_evenly | 在换行后的各行之间均匀分配空间。 |
content_stretch | 沿交叉轴拉伸换行后的各行。 |
flex_initial | 使用自动 basis,可收缩但不伸长。 |
flex_none | 同时禁止 Flex 伸长与收缩。 |
flex_grow | 设置数字形式的 Flex 伸长系数。 |
flex_grow_0 | 禁止 Flex 伸长。 |
flex_shrink | 设置数字形式的 Flex 收缩系数。 |
flex_shrink_1 | 允许 Flex 收缩。 |
grid_cols_min_content | 以 min-content 为最小值创建列。 |
grid_cols_max_content | 以 max-content 为约束创建列。 |
grid_rows_min_content | 以 min-content 为最小值创建行。 |
grid_rows_max_content | 以 max-content 为约束创建行。 |
col_start | 设置 Grid 列起始线。 |
col_start_auto | 自动选择列起始位置。 |
col_end | 设置 Grid 列结束线。 |
col_end_auto | 自动选择列结束位置。 |
col_span_full | 跨越完整的 Grid 列范围。 |
row_start | 设置 Grid 行起始线。 |
row_start_auto | 自动选择行起始位置。 |
row_end | 设置 Grid 行结束线。 |
row_end_auto | 自动选择行结束位置。 |
row_span_full | 跨越完整的 Grid 行范围。 |
#间距与尺寸
| 方法 | 说明 |
|---|---|
gap_2 | 行间隙和列间隙均设为 0.5rem。 |
gap | 在双轴设置带类型的间隙。 |
gap_x_2 | 列间隙设为 0.5rem。 |
gap_y_2 | 行间隙设为 0.5rem。 |
p_4 | 四边内边距设为 1rem。 |
p | 四边设置带类型的内边距。 |
px_3 | 水平内边距设为 0.75rem。 |
py_2 | 垂直内边距设为 0.5rem。 |
mt_4 | 上外边距设为 1rem。 |
m | 四边设置带类型的外边距。 |
m_auto | 四边外边距设为自动。 |
w | 设置带类型的宽度。 |
w_full | 填满可用宽度。 |
h | 设置带类型的高度。 |
h_full | 填满可用高度。 |
min_w | 设置带类型的最小宽度。 |
min_w_0 | 允许宽度收缩到零。 |
max_w | 设置带类型的最大宽度。 |
size | 同时设置带类型的宽度与高度。 |
size_4 | 宽度和高度均设为 1rem。 |
aspect_square | 保持 1:1 的宽高比。 |
mt | 设置带类型的上外边距。 |
mb | 设置带类型的下外边距。 |
mx | 设置带类型的水平外边距。 |
my | 设置带类型的垂直外边距。 |
ml | 设置带类型的左外边距。 |
mr | 设置带类型的右外边距。 |
px | 设置带类型的水平内边距。 |
py | 设置带类型的垂直内边距。 |
pt | 设置带类型的上内边距。 |
pb | 设置带类型的下内边距。 |
pl | 设置带类型的左内边距。 |
pr | 设置带类型的右内边距。 |
gap_x | 设置带类型的列间隙。 |
gap_y | 设置带类型的行间隙。 |
min_h | 设置带类型的最小高度。 |
max_h | 设置带类型的最大高度。 |
aspect_ratio | 设置数字形式的宽高比。 |
#定位与溢出
| 方法 | 说明 |
|---|---|
relative | 保持正常布局位置,并作为定位祖先。 |
absolute | 用 inset 偏移,相对祖先定位。 |
inset | 四边设置带类型的偏移。 |
inset_0 | 上、右、下、左偏移均设为零。 |
top | 设置带类型的上方偏移。 |
top_0 | 上方偏移设为零。 |
overflow_hidden | 在双轴裁剪溢出的内容。 |
overflow_x_hidden | 只裁剪水平溢出。 |
overflow_y_hidden | 只裁剪垂直溢出。 |
bottom | 设置带类型的下方偏移。 |
left | 设置带类型的左方偏移。 |
right | 设置带类型的右方偏移。 |
scrollbar_width | 在滚动布局中预留带类型的滚动条宽度。 |
#颜色与边框
| 方法 | 说明 |
|---|---|
bg | 设置带类型的背景填充。 |
text_color | 设置带类型的文字颜色。 |
border_1 | 四边设置一像素边框。 |
border_t_1 | 上边设置一像素边框。 |
border_color | 设置带类型的边框颜色。 |
border_dashed | 绘制虚线边框。 |
rounded | 设置带类型的圆角半径。 |
rounded_lg | 使用命名的大圆角半径。 |
rounded_full | 让圆角半径尽可能适应该元素的尺寸。 |
border | 四边设置带类型的边框宽度。 |
border_t | 设置带类型的上边框宽度。 |
border_b | 设置带类型的下边框宽度。 |
border_l | 设置带类型的左边框宽度。 |
border_r | 设置带类型的右边框宽度。 |
border_x | 设置带类型的左右边框宽度。 |
border_y | 设置带类型的上下边框宽度。 |
rounded_t | 设置带类型的上侧圆角。 |
rounded_b | 设置带类型的下侧圆角。 |
rounded_l | 设置带类型的左侧圆角。 |
rounded_r | 设置带类型的右侧圆角。 |
rounded_tl | 设置带类型的左上圆角。 |
rounded_tr | 设置带类型的右上圆角。 |
rounded_bl | 设置带类型的左下圆角。 |
rounded_br | 设置带类型的右下圆角。 |
#排版
| 方法 | 说明 |
|---|---|
font_family | 按名称设置字体族。 |
font_weight | 设置带类型的字重。 |
italic | 使用斜体文字。 |
text_size | 设置带类型的字号。 |
text_xs | 使用特小字号。 |
text_sm | 使用小字号。 |
text_lg | 使用大字号。 |
text_left | 左对齐文字。 |
text_center | 让一行中的文字居中。 |
text_right | 右对齐文字。 |
line_height | 设置带类型的行高。 |
whitespace_normal | 允许文字正常换行。 |
whitespace_nowrap | 阻止文字换行。 |
text_ellipsis | 在溢出文字末尾添加省略号。 |
truncate | 裁剪单行文字并添加省略号。 |
line_clamp | 将文字限制为指定行数。 |
text_base | 使用基础字号。 |
text_xl | 使用特大字号。 |
text_2xl | 使用 2× 大字号。 |
text_3xl | 使用 3× 大字号。 |
text_align | 设置带类型的文字对齐方式。 |
not_italic | 使用非斜体文字。 |
underline | 添加文字下划线。 |
line_through | 添加文字删除线。 |
text_decoration_none | 移除文字装饰线。 |
text_decoration_color | 设置文字装饰线颜色。 |
text_decoration_solid | 使用实线文字装饰。 |
text_decoration_wavy | 使用波浪线文字装饰。 |
text_decoration_0 | 把文字装饰线宽度设为零。 |
text_decoration_1 | 把文字装饰线宽度设为一像素。 |
text_decoration_2 | 把文字装饰线宽度设为两像素。 |
text_decoration_4 | 把文字装饰线宽度设为四像素。 |
text_decoration_8 | 把文字装饰线宽度设为八像素。 |
text_overflow | 设置带类型的文字溢出行为。 |
font_features | 设置 OpenType 字体特性。 |
#效果与光标
| 方法 | 说明 |
|---|---|
shadow_none | 移除盒阴影。 |
shadow_sm | 使用命名的小盒阴影。 |
shadow_md | 使用命名的中等盒阴影。 |
opacity | 用浮点值设置不透明度。 |
cursor_pointer | 悬停时使用指向手形光标。 |
cursor_text | 悬停时使用文本插入光标。 |
shadow | 设置带类型的盒阴影列表。 |
shadow_2xs | 使用命名的 2× 特小盒阴影。 |
shadow_xs | 使用命名的特小盒阴影。 |
shadow_lg | 使用命名的大盒阴影。 |
shadow_xl | 使用命名的特大盒阴影。 |
shadow_2xl | 使用命名的 2× 特大盒阴影。 |
cursor | 设置带类型的鼠标光标。 |
cursor_default | 使用默认光标。 |
cursor_move | 使用移动光标。 |
cursor_not_allowed | 使用禁止操作光标。 |
cursor_context_menu | 使用上下文菜单光标。 |
cursor_crosshair | 使用十字光标。 |
cursor_vertical_text | 使用竖排文字光标。 |
cursor_alias | 使用别名光标。 |
cursor_copy | 使用复制光标。 |
cursor_no_drop | 使用禁止拖放光标。 |
cursor_grab | 使用抓取光标。 |
cursor_grabbing | 使用正在抓取的光标。 |
cursor_ew_resize | 使用水平调整尺寸光标。 |
cursor_ns_resize | 使用垂直调整尺寸光标。 |
cursor_nesw_resize | 使用东北至西南方向调整尺寸光标。 |
cursor_nwse_resize | 使用西北至东南方向调整尺寸光标。 |
cursor_col_resize | 使用列宽调整光标。 |
cursor_row_resize | 使用行高调整光标。 |
cursor_n_resize | 使用向上调整尺寸光标。 |
cursor_e_resize | 使用向右调整尺寸光标。 |
cursor_s_resize | 使用向下调整尺寸光标。 |
cursor_w_resize | 使用向左调整尺寸光标。 |
#GPUI 特有方法
这些 API 没有直接对应的 Tailwind utility。方法名不加链接,以免暗示不存在的对应关系。
| 方法 | 说明 |
|---|---|
font | 用带类型的 GPUI Font 替换文字字体。 |
min_size | 同时设置带类型的最小宽度与高度。 |
max_size | 同时设置带类型的最大宽度与高度。 |
text_bg | 设置文字片段而非元素盒的背景颜色。 |
text_ellipsis_start | 在开头截断,保留文字末尾。 |
text_ellipsis_middle | 在中间截断,保留文字两端。 |
debug | 在 debug 构建中绘制调试轮廓。 |
debug_below | 在 debug 构建中为当前元素及符合条件的后代绘制调试轮廓。 |
style | 返回元素使用的可变 StyleRefinement;这是 trait 的底层访问方法。 |
text_style | 返回元素样式中的可变文字 refinement。 |
grid_location_mut | 访问 StyleRefinement 中可变的 Grid 位置。 |
宏还为尺寸(w、h、size、min_size、min_w、min_h、max_size、max_w、max_h)、间隙(gap、gap_x、gap_y)、外边距(m、mt、mb、mx、my、ml、mr)、内边距(p、pt、pb、px、py、pl、pr)和偏移(inset、top、bottom、left、right)生成方法。例如 w_64、px_3 和 top_0 都是真实存在的方法。共用的数字后缀包括 0、0p5、1、1p5、2、2p5、3、3p5、4 到 12 的每个整数,以及 16、20、24、32、40、48、56、64、72、80、96、112、128。此外还有 _px、_full 和 _1_2 等分数后缀;只有接受 auto 的方法族才生成 _auto。非 auto 值还生成 mt_neg_2 这样的 _neg_ 形式。边框各侧(border、border_t、border_b、border_l、border_r、border_x、border_y)有 0 到 12,以及 16、20、24、32 的像素宽度后缀。各侧和各角的圆角有 none、xs、sm、md、lg、xl、2xl、3xl、full 这些后缀。只有在生成的属性符合布局需要时才使用它们。
间距便捷方法使用基于 rem 的尺度:_1 为 0.25rem,_2 为 0.5rem,_3 为 0.75rem,_4 为 1rem。命名方法没有覆盖所需数值时,可使用 .gap(rems(0.625))、.w(px(240.)) 或 .w(relative(0.5)) 这类带类型的 setter。relative(0.5) 表示可用相对尺寸的一半;px(...) 表示像素。命名尺度和方法范围以 GPUI 的实现为准,不要假定每个 Tailwind class 都有对应方法。
#样式调用改动了什么
每个实现 Styled 的元素都提供 fn style(&mut self) -> &mut StyleRefinement。例如,.px_3() 写入相应的可选 padding 字段,.bg(...) 写入 background 字段。合并样式时,未设置的字段不会覆盖已有值。解析后的 Style 同时包含布局数据与外观数据。
Styled calls → StyleRefinement → resolved Style
├─ layout fields → Taffy → bounds
└─ color, text, shadow, cursor → GPUI paint and interaction
在元素的 request_layout 阶段,GPUI 将 display、size、padding、gap、Flex 对齐、position 和 Grid 位置等布局字段连同子元素的布局 ID 交给 Taffy。Taffy 计算几何尺寸与位置。GPUI 随后在 prepaint 和 Paint 阶段使用所得 bounds 绘制并进行命中测试。Taffy 不实现 GPUI 的文字 shaping、hover listener、Action 或绘制。
StyleRefinement 本身也实现了 Styled,因此状态样式闭包可以使用相同的 utility 方法。.hover(|style| style.bg(...)) 这样的交互变体属于 InteractiveElement,需要交互型元素。条件构建方法有所不同:.when(...) 在构建本帧时决定是否追加链式步骤。
#Fluent 组合与 trait 边界
链式 API 由多个 trait 共同提供。Styled 提供样式方法,ParentElement 提供 .child(...)。InteractiveElement 提供 .id(...),返回支持 .overflow_y_scroll() 等依赖身份的方法的 Stateful<Div>;它还提供 .hover(...) 等状态样式细化。每个 IntoElement 都实现 FluentBuilder;只实现 IntoElement 不会获得样式、子元素或交互能力。缺少某个方法时,检查接收者实现了哪个 trait,以及之前的调用是否改变了其类型。
FluentBuilder 方法 | 作用 |
|---|---|
map | 转换当前值,返回类型也可以改变。 |
when | 布尔条件为真时追加构建步骤。 |
when_else | 在两个返回相同 builder 类型的步骤间选择。 |
when_some | 可选值存在时追加步骤,并把值传给闭包。 |
when_none | 引用的可选值为空时追加步骤。 |
use gpui_kit::*;
use gpui_kit::component::ActiveTheme as _;
div()
.id("result-row")
.px_3()
.py_2()
.when(selected, |row| row.bg(cx.theme().selection))
.when_some(subtitle, |row, text| row.child(text))
.child(title)
条件闭包取得 builder 并返回 builder。条件在本次 render 中求值,并非订阅。.map(...) 可以返回不同类型。跨帧变化的状态应由 Entity 持有。
例如,.when(selected, ...) 在构建本帧时判断 selected;.hover(|style| ...) 则安装指针悬停时的样式细化。滚动方法需要有状态的元素,所以应先给预期的滚动负责元素设置 .id(...)。不能假定任意实现 IntoElement 的组件都接受 .child(...) 或 .bg(...);应检查该组件自己的 builder API,或用负责这些样式的 div() 包裹它。
GPUI Kit 另有 StyledExt,提供 h_flex、v_flex 和 refine_style 等不依赖主题的辅助方法;ThemeStyled 则提供 popover_style(cx) 等依赖主题的外观方法。它们是在 GPUI Styled 之上的扩展,不属于 Tailwind utility。自行实现元素时,应从 style() 返回元素的 StyleRefinement,并在布局与绘制阶段应用解析后的样式。只实现元素确实支持的子元素与交互 trait。
Tailwind 对应关系有明确范围:这里没有 CSS 选择器、层叠、响应式前缀,也不保证每个 Tailwind utility 都存在对应的 GPUI 方法。几何布局使用 GPUI 带类型的辅助方法,产品外观使用 GPUI Kit 主题 token,行为则使用明确的元素或实体 API。