资讯动态

gpui-kit Attachment 组件实战指南:可组合的文件与媒体附件 UI 全解析

发布时间:2026/9/14 20:10:40 来源:尧图企业网站定制
gpui-kit Attachment 组件实战指南可组合的文件与媒体附件 UI 全解析【免费下载链接】gpui-kitRust GUI components for building fantastic cross-platform desktop application by using GPUI.项目地址: https://gitcode.com/GitHub_Trending/gp/gpui-kitAttachment 是 gpui-kitgpui-component中用于呈现单个文件或媒体条目的组合式组件覆盖媒体预览、元信息、生命周期状态与操作槽位同时把上传状态、选择、重试与导航逻辑留给应用层。本文以 attachment.md 为主线结合 attachment.rs 源码与 attachment_story.rs 的真实示例系统讲解其解剖结构、五个生命周期状态、状态继承机制、尺寸与轴向、整卡点击、分组滚动及主题定制并给出可直接复制的 GPUI 代码。读完本文你将能在一个聊天/文件列表产品中快速落地带预览、上传进度、失败重试与整卡交互的附件 UI。组件定位一个刻意设计的组合原语Attachment为单个文件或媒体项提供稳定的布局媒体预览、元信息与可选操作。它不持有产品级的文件模型上传状态、选择、重试与导航都由应用负责每个公开槽位都可设置样式并接受任意 GPUI 子元素。从源码结构看这种“刻意不做事”是设计的一部分attachment.rs 中的Attachment结构体只保存id、style、status、size、axis与三个可选槽位media、content、actions没有任何文件上传或状态机逻辑。AttachmentActions不发明附件专属的操作模型把Button、Link或其他语义控件直接放进去即可AttachmentGroup只负责水平间距与滚动。选择与预览行为同样归属应用。引入组件组件 crate 名为gpui-component见 Cargo.toml同时通过汇总 crategpui_kit重导出attachment_story.rs 中的用法即经gpui_kit::component导入。典型导入方式use gpui_kit::{Axis, ParentElement as _, Styled as _}; use gpui_kit::component::{ ActiveTheme as _, Colorize as _, Icon, IconName, Sizable as _, Size, attachment::{ Attachment, AttachmentActions, AttachmentContent, AttachmentDescription, AttachmentGroup, AttachmentMedia, AttachmentStatus, AttachmentTitle, }, button::{Button, ButtonVariants as _}, badge::Badge, progress::Progress, shimmer::ShimmerStyle, spinner::Spinner, };attachment模块在 lib.rs 中以pub mod attachment;公开导出因此所有类型都通过gpui_kit::component::attachment::*或gpui_component::attachment::*可达。解剖结构与基础用法Attachment 由三个命名槽位组成AttachmentMedia预览、AttachmentContent元信息、AttachmentActions操作。类型化构建器让常见的文件形状一目了然Attachment::new() .media(AttachmentMedia::new().child(Icon::new(IconName::FileText))) .content( AttachmentContent::new() .title(AttachmentTitle::new(quarterly-report.pdf)) .description(AttachmentDescription::new(PDF · 2.4 MB)), ) .actions( AttachmentActions::new().child( Button::new(remove-report) .ghost() .xsmall() .icon(IconName::Close) .label(Remove), ), )槽位全部可选。产品需要时媒体-only、元信息-only 或操作-only 的附件都是合法形态Attachment::new() .media(AttachmentMedia::new().child(Icon::new(IconName::FileText))); Attachment::new().content( AttachmentContent::new() .title(AttachmentTitle::new(notes.txt)) .description(AttachmentDescription::new(TXT · 12 KB)), )默认状态属性默认值含义状态StatusComplete条目已就绪。尺寸SizeMedium使用标准对话密度。轴向AxisHorizontal媒体、元信息与操作共享一行。媒体/内容/操作缺省只添加条目需要的槽位。表面Surfacebackground与foreground卡片表面以边框分隔类似 shadcn 的bg-card。圆角Radiusradius_2xl()XSmall时为radius_xl共享语义圆角。源码与默认表一一对应attachment.rs 的new()初始化Status::Complete、Size::Medium、Axis::Horizontal且三个槽位为None渲染逻辑 中XSmall用tokens.radius.xl其余用cx.theme().radius_2xl()底色为tokens.colors.background、文字为foreground。Attachment随内容自适应大小绝不拥有产品级文件模型。请把文件 ID 与状态保存在父视图中再将当前记录渲染进该元素。媒体与图片预览子元素用于图标式媒体槽位src(...)用于图片预览Attachment::new() .media( AttachmentMedia::new() .src(https://example.com/previews/sdk.svg) .overlay(Icon::new(IconName::Download)), ) .content( AttachmentContent::new() .title(AttachmentTitle::new(sdk-preview.svg)) .description(AttachmentDescription::new(SVG · 1280 × 720)), )图片以ObjectFit::Cover渲染在媒体边界内渲染源码子元素与overlay(...)绘制在图片之上。overlay(...)将元素居中对齐于整个媒体区域适合放 Spinner、播放图标或预览动作Attachment::new() .status(AttachmentStatus::Uploading) .axis(Axis::Vertical) .media( AttachmentMedia::new() .src(preview_url) .overlay(Spinner::new().small()), )overlay(...)的实现是把子元素包进absolute().inset_0()的全覆盖居中容器源码因此叠层天然覆盖整个媒体区域。只有源图片在Uploading、Processing、Failed时被变暗源码中dimmed_image判定为“有源且状态不是 Pending/Complete”此时图片施加opacity(0.6)叠加层与自定义子元素保持完整对比度。无源时媒体槽是主题化的弱化区域失败态下使用破坏性语义表面与前景色destructive.opacity(0.1)背景 destructive文字保证错误图标依然清晰可读源码。AttachmentMedia可独立设置样式。用with_size(...)覆盖继承的媒体尺寸或用常规 GPUI 精化方法定制预览比例与表面AttachmentMedia::new() .with_size(Size::Large) .aspect_ratio(16. / 9.) .rounded(cx.theme().radius_lg) .child(Icon::new(IconName::Image))显式媒体尺寸优先于附件尺寸源码中AttachmentMedia::layout仅在self.size.is_none()时才继承父级尺寸源码对应测试test_attachment_media_size_inherits_root_unless_explicit测试也验证了“显式with_size(XSmall)不会被父级Large覆盖”。纵向附件默认让媒体全宽且为正方形应用可改用媒体自身样式替换该几何。生命周期状态AttachmentStatus有五个显式状态父状态在渲染时传给类型化的标题、描述、媒体与操作布局枚举定义状态表面/布局行为推荐内容Pending虚线边框预览不变暗。“准备上传”与开始操作。Uploading预览变暗类型化标题显示 shimmer。进度值与取消按钮。Processing预览变暗类型化标题显示 shimmer。“处理中…”与无破坏性的等待态。Failed破坏性边框/描述有预览时变暗。错误原因 重试或移除。Complete就绪表面预览全不透明。文件元信息与常规操作。Pending的虚线边框在源码中由when(status.is_pending(), |this| this.border_dashed())实现渲染源码Failed的边框色则换成destructive.opacity(0.3)L208-L212。上传中附件的完整形态示例Attachment::new() .status(AttachmentStatus::Uploading) .media(AttachmentMedia::new().child(Icon::new(IconName::FileText))) .content( AttachmentContent::new() .title(AttachmentTitle::new(design-assets.zip)) .description(AttachmentDescription::new(Uploading · 68%)) .child(Progress::new(attachment-progress).value(68.)), ) .actions( AttachmentActions::new() .child(Button::new(cancel-upload).ghost().xsmall().label(Cancel)), )状态辅助方法在应用状态映射到呈现时很实用match status { AttachmentStatus::Pending Ready to upload, AttachmentStatus::Uploading Uploading…, AttachmentStatus::Processing Processing…, AttachmentStatus::Failed Upload failed, AttachmentStatus::Complete Ready, }is_pending()、is_uploading()、is_processing()、is_failed()、is_complete()、is_in_progress()都是纯读取器源码不会更新附件或应用的上传任务。其中is_in_progress()判定Uploading | Processing两种进行中状态测试test_attachment_defaults_and_status_helpers测试验证了各方法的行为。状态继承与覆盖通过类型化构建器添加的标题与描述自动继承父状态显式子状态优先于继承值Attachment::new() .status(AttachmentStatus::Failed) .content( AttachmentContent::new() .title(AttachmentTitle::new(archive.zip)) .description( AttachmentDescription::new(Previous upload completed) .status(AttachmentStatus::Complete), ), )继承机制在AttachmentContent::layout中实现仅当子项的status为None时才写入父状态源码。对应测试test_attachment_typed_content_inherits_status与test_attachment_explicit_child_status_overrides_parent测试分别覆盖了继承与显式覆盖两条路径。只要加载 shimmer 或失败着色应跟随附件状态就应使用类型化的.title(...)与.description(...)。通用.child(...)形式仍接受任意元素但它无法窥探被擦除子元素的状态因此不会自动继承AttachmentContent::new() .title(AttachmentTitle::new(status-aware-title)) .description(AttachmentDescription::new(status-aware-description)) .child(custom_metadata_element)AttachmentTitle在Uploading/Processing时用ShimmerText渲染文本源码依据status.is_some_and(AttachmentStatus::is_in_progress)见 L533-L554。可用可复用的 shimmer 样式定制进行中的标题AttachmentTitle::new(transcript.pdf) .with_shimmer_style( ShimmerStyle::new() .duration(std::time::Duration::from_secs(3)) .spread(0.45) .reverse(true) .once(false), )ShimmerStyle定义于 shimmer.rs测试test_attachment_title_keeps_custom_shimmer_style测试验证了自定义 shimmer 样式会随标题保留。AttachmentDescription仅在显式或继承的Failed状态下使用破坏性语义色源码中为destructive.opacity(0.8)见 L588-L606。描述中的文字仍应说明发生了什么颜色只是辅助线索。尺寸与轴向Attachment实现Sizable。便捷构建器映射到SizeAttachment::new().xsmall(); Attachment::new().small(); Attachment::new(); // medium默认 Attachment::new().large(); Attachment::new().w_72() // 需要固定尺寸时由应用设定宽度命名尺寸把间隙、排版、内边距、媒体基准与圆角作为同一刻度整体调整attachment_size_style函数按Size分发 gap/字号/内边距见 源码。Size::Size(...)是自定义密度值不是宽度设置器需要固定尺寸时使用常规 GPUI 宽度精化w_72()、w(...)或父布局。为保持主题一致优先使用命名尺寸。横向是默认轴向媒体、元信息与操作保持在一行纵向把预览移到元信息上方并把操作放在预览右上角源码中纵向时AttachmentActions变为absolute().top_3().right_3()见 L662-L664Attachment::new() .axis(Axis::Vertical) .large() .media(AttachmentMedia::new().src(preview_url)) .content( AttachmentContent::new() .title(AttachmentTitle::new(presentation.png)) .description(AttachmentDescription::new(PNG · 1920 × 1080)), ) .actions( AttachmentActions::new() .child(Button::new(remove-presentation).ghost().xsmall().label(Remove)), )纵向默认是正方形媒体源码w_full().aspect_ratio(1.)见 L368-L370。需要横向预览时设置媒体纵横比或尺寸。AttachmentContent与AttachmentActions仍是独立槽位应用可省略其一或在任一中放置额外控件。横向媒体的命名尺寸在源码中对应固定的边长size_7/size_8/size_10/size_12见 L361-L367自定义Size::Size(v)则直接取该像素边长——需要精确控制时可用自定义尺寸。内容与操作AttachmentContent把标题与描述保持在一个纵向元信息栈中也接受进度、徽章或第二行等自定义子元素渲染为v_flex().gap_0p5()见 L476-L492AttachmentContent::new() .title(AttachmentTitle::new(report.pdf)) .description(AttachmentDescription::new(PDF · 2.4 MB)) .child(Badge::new().count(3))用AttachmentActions放置一个或多个现有语义控件AttachmentActions::new() .child(Button::new(download).ghost().xsmall().label(Download)) .child(Button::new(remove).danger().xsmall().label(Remove))AttachmentActions只提供布局不会让子元素可聚焦、可点击或可禁用。工具提示是补充性的当前Button实现从.label(...)派生无障碍标签因此当操作需要命名可访问控件时请使用可见标签。仅带.tooltip(...)的纯图标按钮不能替代该标签。整卡点击设置.id(...)与.on_click(...)使整卡可激活例如打开预览。点击层绘制在AttachmentActions之下因此操作按钮保持独立可点击Attachment::new() .id(design-attachment) .on_click(|_, window, cx| { // 打开预览。 }) .content( AttachmentContent::new() .title(AttachmentTitle::new(design-mockups.png)) .description(AttachmentDescription::new(PNG · 1.8 MB)), ) .actions( AttachmentActions::new() .child(Button::new(remove).ghost().xsmall().icon(IconName::Close)), )处理器只有与.id(...)同时存在才生效点击状态需要稳定的身份标识。源码中点击层以div().id(id).absolute().inset_0()的形式插在 media/content 之后、actions 之前L237-L248因此 actions 的命中区域始终在上层。AttachmentActions还会在左键按下时stop_propagation()L665-L670确保操作按钮或按钮间隙不会同时触发整卡点击。集成测试click_dispatch::whole_card_click_stays_below_the_actions测试精确验证了两点点击操作按钮只触发操作、不触发卡片处理器点击卡片其他区域才触发整卡处理器。可点击卡片会显示弱化的 hover 表面以呈现可交互性源码中 clickable 时 hover 背景为muted.opacity(0.5)见 L216-L224。激活意味着什么——对话框、浏览器、文件查看器还是选择——由应用决定。请把破坏性与次级命令放在AttachmentActions中使其永不依赖卡片主激活同时在键盘可达的位置提供卡片主操作的Button或Link点击层本身只是指针便利不获取焦点。分组AttachmentGroup提供共享组间距的水平可滚动行。它的 ID 是必需的因为它拥有 GPUI 元素级滚动状态AttachmentGroup::new(message-attachments) .child(first_attachment) .child(second_attachment) .child(third_attachment)源码中分组渲染为h_flex().id(self.id).w_full().min_w_0().gap_3().py_1().overflow_x_scroll().lock_scroll_axis()L741-L754即全宽、最小宽度为 0、水平滚动且锁定滚动轴。分组不提供选择、吸附、重排手柄、“N 更多”溢出标签或预览对话框请在应用自有的包装器中组合这些行为。对话行的存活期内请保持 ID 稳定。自定义样式与主题令牌Attachment、AttachmentGroup与每个命名槽位都实现Styled。精化方法在组件默认样式之后应用使开发者能控制表面、间距、媒体几何、排版与操作布局Attachment::new() .w_full() .rounded(cx.theme().radius_lg) .bg(cx.theme().group_box) .border_color(cx.theme().ring) .media( AttachmentMedia::new() .rounded(cx.theme().radius_lg) .bg(cx.theme().primary.opacity(0.12)) .text_color(cx.theme().primary) .child(Icon::new(IconName::FileText)), ) .content( AttachmentContent::new() .title(AttachmentTitle::new(custom-theme.json).text_color(cx.theme().primary)) .description(AttachmentDescription::new(JSON · 16 KB)), )请优先使用cx.theme()中的语义角色background、muted、border、destructive、foreground及其前景对应项而不是原始颜色。组件的默认圆角、间距与排版遵循共享设计刻度应用特定的密度可用Size与类型化样式精化在组合边界表达。常见组合速查用AttachmentContent::title(...)与.description(...)做状态感知元信息.child(...)放任意自定义内容子项.status(...)做显式覆盖AttachmentTitle::with_shimmer_style(...)做加载动效AttachmentMedia::overlay(...)在图片之上放控件。可访问性与状态指引在文本中包含文件名与有用的类型/尺寸信息。仅图标式媒体预览不足以识别附件。将上传、重试、移除、下载与预览操作放入语义化的Button或Link控件。工具提示只是补充当前ButtonAPI 下操作需要可访问名称时请使用.label(...)。在文本或控件状态中描述Pending、Uploading、Processing与Failed。虚线边框、不透明度、shimmer 与破坏性颜色只是辅助线索。应用知道字节数或条数时保持进度确定化用Progress作为子元素不要在Attachment中重复实现进度语义。开启 reduced motion 时ShimmerText会禁用加载 shimmer。该模式下请保持可读的标题与描述可见。确保纵向叠加操作可从键盘到达它不能只通过图片 hover 可用。组件边界这些边界是刻意设计的直接使用Button而非附件专属的操作组件。这保留了 Button 的变体、尺寸、加载、禁用行为、焦点与事件处理。直接使用Progress而非附件专属的进度包装器。整卡激活用.id(...).on_click(...)。卡片只上报点击打开对话框、浏览器、文件查看器还是切换选择由应用决定。AttachmentGroup只负责共享水平行与溢出。选择、重排、吸附或自定义滚动控件请使用应用自有容器。API 参考Attachment方法默认值用途new()Complete、Medium、Horizontal、无槽位创建附件。id(ElementId)无整卡点击层的稳定身份。on_click(handler)无整卡激活需要id(...)且位于操作层之下。status(AttachmentStatus)Complete设置生命周期样式。axis(Axis)Horizontal选择横向或纵向布局。with_size(Size)Medium设置命名或自定义尺寸。xsmall()/small()/large()—Sizable 快捷方式。media(AttachmentMedia)无添加预览槽位。content(AttachmentContent)无添加元信息。actions(AttachmentActions)无添加操作控件。AttachmentMedia方法默认值用途new()无源、无子元素创建媒体槽位。src(ImageSource)无渲染图片预览。with_size(Size)继承附件尺寸覆盖媒体密度。overlay(element)无在媒体上居中放置元素。child(element)—在预览之上添加图标或自定义内容。Styled方法主题化弱化媒体精化几何、圆角、背景与排版。AttachmentContent、AttachmentTitle与AttachmentDescription方法默认值用途AttachmentContent::new()空纵向元信息栈创建内容。.title(AttachmentTitle)—添加状态感知的单行标题。.description(AttachmentDescription)—添加状态感知的单行描述。AttachmentTitle::new(text)无显式子状态创建标题。AttachmentTitle::status(status)继承父级覆盖标题加载状态。AttachmentTitle::with_shimmer_style(style)默认 shimmer定制标题动画。AttachmentDescription::new(text)无显式子状态创建描述。AttachmentDescription::status(status)继承父级覆盖描述颜色状态。.child(element)—添加进度、徽章或自定义元信息。AttachmentActions与AttachmentGroup方法默认值用途AttachmentActions::new()空操作布局创建操作槽位。.child(element)—添加 Button、Link 或其他控件。AttachmentGroup::new(id)需要稳定 ID创建水平滚动分组。AttachmentGroup::child(element)—向分组添加附件。相关类型与源码延伸AttachmentStatusPending、Uploading、Processing、Failed、Complete定义于 attachment.rs#L16-L30。SizeXSmall、Small、Medium、Large或自定义Pixels值来自gpui-component顶层Sizable/Size。Axis来自 GPUI 的Horizontal或Vertical。ShimmerStyle共享加载动画配置定义于 shimmer.rs。想看到组件在真实应用中的全部形态含五状态示例、整卡点击、缩略图、图片叠加、各尺寸、分组、方向、状态继承、超长文件名与自定义样式可直接阅读 attachment_story.rs其单元测试与点击分发集成测试位于 attachment.rs 的 tests 模块。中文版本可对照 zh-CN 文档。【免费下载链接】gpui-kitRust GUI components for building fantastic cross-platform desktop application by using GPUI.项目地址: https://gitcode.com/GitHub_Trending/gp/gpui-kit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价