ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

gpui-kit DropdownButton 组合按钮指南:主按钮与下拉触发的拆分式交互

gpui-kit DropdownButton 组合按钮指南:主按钮与下拉触发的拆分式交互 gpui-kit DropdownButton 组合按钮指南主按钮与下拉触发的拆分式交互【免费下载链接】gpui-kitRust GUI components for building fantastic cross-platform desktop application by using GPUI.项目地址: https://gitcode.com/GitHub_Trending/gp/gpui-kitDropdownButton 是 gpui-kit 中一个一分为二的组合按钮左侧是保留独立点击事件的普通按钮右侧是展开下拉菜单的触发按钮两半视觉上连成一体。本文基于 website/component/dropdown_button.md 及组件源码完整讲解它的导入、变体、尺寸、锚点与内层按钮配置并深入 dropdown_button.rs 的实现细节与测试用例帮助你写出可直接落地运行的分裂式按钮Split Button界面。组件定位一个按钮 一个触发按钮DropdownButton 是一种组合型按钮组件。它同时承担两种交互点击左侧主按钮时执行一个独立动作例如保存点击右侧触发按钮通常带下拉箭头时展开一个下拉菜单提供更多选项例如另存为…、存为模板…。从源码结构看DropdownButton 的核心字段如下pub struct DropdownButton { id: ElementId, style: StyleRefinement, button: OptionButton, // 左侧主按钮 menu: OptionBoxdyn Fn(PopupMenu, mut Window, mut ContextPopupMenu) - PopupMenu static, selected: bool, disabled: bool, outline: bool, variant: OptionButtonVariant, // 未设置时回退到内层按钮 size: OptionSize, // 未设置时回退到内层按钮 anchor: Anchor, // 默认 TopRight }设计上遵循共享与专属的分工原则共享属性变体、尺寸、选中/禁用状态、outline 描边直接设置在DropdownButton上同时作用于两半动作专属属性文案 label、图标 icon、tooltip、加载状态 loading、点击回调 on_click设置在内层 Button 上只影响主按钮。导入与基础用法在使用了gpui_kitcrate 的项目中按如下方式导入use gpui_kit::component::button::{Button, DropdownButton}; use gpui_kit::Anchor;最基础的用法是给一个主按钮挂上由多个菜单项组成的下拉菜单。DropdownButton::new(dropdown) .button(Button::new(btn).label(Click Me)) .dropdown_menu(|menu, _, _| { menu.menu(Option 1, Box::new(MyAction)) .menu(Option 2, Box::new(MyAction)) .separator() .menu(Option 3, Box::new(MyAction)) })几点说明DropdownButton::new(id)需要一个唯一的ElementId用于状态追踪与无障碍标识.button(...)接收一个构建好的 Button主按钮保留自己的 label、icon、tooltip 和 on_click.dropdown_menu(...)接收一个构建闭包签名固定为Fn(PopupMenu, mut Window, mut ContextPopupMenu) - PopupMenu。闭包内通过链式调用向菜单追加条目其中menu(文案, Box::new(MyAction))中的MyAction是实现了gpui::Action的动作类型参见 popup_menu.rs菜单项被点击后即派发对应 Action.separator()在菜单项之间插入分隔线渲染前会执行debug_assert!(self.button.is_some() || self.menu.is_some(), ...)dropdown_button.rs即button与dropdown_menu至少提供其一否则在调试构建下会直接断言失败——这保证组件不会渲染成一个空壳。菜单构建闭包中还支持更多条目类型参考 popup_menu.rs方法作用menu(label, action)添加标准菜单项点击派发 Actionmenu_with_disabled(label, action, disabled)添加可禁用菜单项menu_with_enable(label, action, enable)按布尔值控制可用状态menu_with_check(label, checked, action)添加带勾选状态的菜单项separator()插入分隔线label(text)插入纯文本说明条目不可交互link(label, href)添加打开链接的菜单项submenu(label, menu)添加子菜单父菜单不可滚动时支持视觉变体与 Button 完全一致与 Button 相同DropdownButton通过ButtonVariantstraitbutton.rs提供全套变体方法DropdownButton::new(dropdown) .primary() .button(Button::new(btn).label(Primary)) .dropdown_menu(|menu, _, _| { menu.menu(Option 1, Box::new(MyAction)) })ButtonVariant 枚举共 11 种取值对应的快捷方法如下变体快捷方法典型语义Default默认普通输入框风格Primary.primary()主操作强调背景色Secondary.secondary()次操作Danger.danger()危险/删除类操作Info.info()信息提示Success.success()成功确认Warning.warning()警告操作Ghost.ghost()透明底悬停才浮现Link.link()链接样式Text.text()无内边距的纯文本按钮Custom(style).custom(style)自定义配色背景/前景/悬停/按下/阴影关键行为在DropdownButton上不设置变体或尺寸时内层按钮的值会自动应用到两半。这一点由源码中的effective_variant/effective_size实现dropdown_button.rsfn effective_variant(self) - ButtonVariant { self.variant .or_else(|| self.button.as_ref().map(Button::variant)) .unwrap_or_default() } fn effective_size(self) - Size { self.size .or_else(|| self.button.as_ref().map(Button::button_size)) .unwrap_or_default() }即优先使用外层显式设置的值外层未设置则回退到内层Button的值两者都未设置时才落到默认值变体Default、尺寸Medium。尺寸设置DropdownButton实现了Sizabletrait支持链式.with_size(...)或语义化快捷方法xsmall()、small()、medium()、large()。尺寸枚举定义在 sizing.rspub enum Size { Size(Pixels), // 自定义像素尺寸 XSmall, // xs Small, // sm Medium, // md默认值 Large, // lg }尺寸同样会同步作用于左右两半若外层未设置则继承内层按钮的尺寸见上文effective_size。在 button.rs 中可以看到不同尺寸对应的实际内边距与高度例如Small为h_6().px_2()Medium为h_8().px_2p5()Large为h_8().px_3()。内层按钮选项主按钮的完整能力主按钮本身是一个完整的 Button因此所有按钮专属选项都可直接使用DropdownButton::new(dropdown) .button( Button::new(btn) .label(Save) .compact() .loading(is_saving) .tooltip(Save the current view) .on_click(|_, _, _| println!(Saved)), ) .dropdown_menu(|menu, _, _| { menu.menu(Save as…, Box::new(MyAction)) })内层Button常用构建方法定义于 button.rs方法说明.label(text)设置按钮文案不设置则进入图标按钮模式.icon(icon)设置图标与 label 组合显示.compact()紧凑模式缩小内边距.loading(bool)加载态整体透明度降为 0.8 且屏蔽点击button.rs.loading_icon(icon)自定义加载图标默认是 spinner.tooltip(text)悬停提示.tooltip_with_action(text, action, context)带快捷键提示的 tooltip.tooltip_placement(placement)指定 tooltip 偏好方位.on_click(handler)点击回调签名Fn(ClickEvent, mut Window, mut App).on_hover(handler)悬停回调bool 参数表示是否悬停.disabled(bool)禁用配合外层Disableable实现.outline()描边风格.rounded(...)自定义圆角.tab_index / .tab_stop键盘焦点管理注意.loading(true)时主按钮会保持自身外观但完全屏蔽交互鼠标按下、点击均被stop_propagation拦截这与禁用态的置灰表现不同——加载态只是看起来还能点实际上不响应。自定义锚点控制菜单弹出方位默认情况下DropdownButton的菜单锚点是Anchor::TopRight见 dropdown_button.rs。需要调整弹出方位时使用dropdown_menu_with_anchorDropdownButton::new(dropdown) .button(Button::new(btn).label(Click Me)) .dropdown_menu_with_anchor(Anchor::BottomRight, |menu, _, _| { menu.menu(Option 1, Box::new(MyAction)) })Anchor来自 gpui 核心TopLeft、TopRight、BottomLeft、BottomRight等由 dropdown_menu.rs 中的DropdownMenutrait 透传给底层的Popover。另外注意区分两个入口的默认锚点差异直接在Button上调用dropdown_menu(...)DropdownMenutrait 方法默认锚点是Anchor::TopLeftdropdown_menu.rs在DropdownButton上调用dropdown_menu(...)默认锚点是Anchor::TopRight。选中、禁用与 ghost 联动DropdownButton同时实现了Disableable与Selectable支持DropdownButton::new(dropdown) .disabled(true) // 两半同时禁用 .selected(true) // 两半同时进入选中态selected状态还会自动合并内层按钮的选中态渲染时执行let selected self.selected || self.button.as_ref().is_some_and(Selectable::is_selected);dropdown_button.rs因此从任意一层设置selected都会让整体呈现选中样式。ghost变体有一个值得注意的交互细节由于 ghost 默认透明、仅在悬停时浮现左右两半通过共享悬停组HALVES_GROUP常量dropdown-button见 dropdown_button.rs联动——悬停任意一半都会让整个控件以半透明强度浮现且菜单打开期间menu_open状态为 true触发侧会保持被按住的浮现效果使两者读起来是一个完整控件而非两个独立按钮。相关逻辑见 dropdown_button.rs 与 button.rs。源码实现剖析两半如何拼接DropdownButton的渲染逻辑在RenderOnce::renderdropdown_button.rs中核心要点左右两半共用一个水平 flex 容器外层div().h_flex()包裹左侧主按钮通过border_corners只保留左上、左下圆角border_edges保留四边Edges::all(true)并接收外层传入的selected、disabled、variant、size右侧触发按钮是内部自动构建的Button::new(popup)通过.dropdown_caret(true)显示下拉箭头只保留右上、右下圆角并隐藏左边框Edges { left: false, ... }使两半视觉无缝衔接触发按钮通过DropdownMenutrait 的dropdown_menu_with_anchor挂接菜单并通过on_open_change回调把开合状态写回menu_open状态用于 ghost 联动的hover_group_held。菜单弹出层由 DropdownMenuPopover 承载它本质上是Popover的封装有两个实现细节值得注意菜单实体只创建一次并缓存PopupMenu通过use_keyed_state存入DropdownMenuState避免每次重渲染都重建实体关闭时重建PopupMenu派发DismissEvent时popover 关闭并清空缓存的菜单实体从而支持菜单项的动态重建下次打开时用最新闭包重新生成条目订阅逻辑见 dropdown_menu.rs。菜单打开后自动聚焦并支持键盘导航。PopupMenu::initpopup_menu.rs注册了enter确认、escape取消、up/down上下选择、left/right左右选择等按键绑定因此菜单天然支持纯键盘操作。实战示例参考 Story 展示仓库自带的交互演示 dropdown_button_story.rs 提供了三个可直接借鉴的场景1. 基础分裂按钮Export主按钮 CSV/PDF 导出菜单使用primary()变体与Anchor::TopRight锚点DropdownButton::new(export) .with_size(self.size) .primary() .button(Button::new(export-default).label(Export).on_click(...)) .dropdown_menu_with_anchor(Anchor::TopRight, move |this, _, _| { this.menu(Export all rows (.csv), Box::new(ButtonAction::ExportCsv)) .menu(Download report (.pdf), Box::new(ButtonAction::ExportPdf)) })2. 内层按钮选项Save主按钮携带 tooltip、loading、on_click外层用outline()描边风格DropdownButton::new(save) .with_size(self.size) .outline() .button( Button::new(save-default) .label(Save) .tooltip(Save the current document) .loading(loading) .on_click(...), ) .dropdown_menu(move |this, _, _| { this.menu(Save as new file…, Box::new(ButtonAction::SaveCopy)) .menu(Save as template…, Box::new(ButtonAction::SaveTemplate)) })3. 样式继承内层按钮使用ghost().small()外层不设置变体与尺寸两半自动继承 ghost 小尺寸DropdownButton::new(recent) .button(Button::new(recent-default).label(Open latest).ghost().small().on_click(...)) .dropdown_menu(move |this, _, _| { this.menu(Quarterly Report.gpui, Box::new(ButtonAction::OpenQuarterlyReport)) .menu(Watchlist Layout.gpui, Box::new(ButtonAction::OpenWatchlistLayout)) })Story 中还演示了通过工具栏勾选项实时切换Disabled、Loading、Selected、Compact状态以及用menu_with_check生成带勾选标记的选项菜单适合作为交互验证模板。测试与验证dropdown_button.rs 内置了 4 个测试覆盖了组件最核心的行为契约test_dropdown_button_builder验证构建链完整设置variant、outline、size、disabled、selected、anchor等字段inner_button_keeps_its_own_variant_and_size外层不设置变体/尺寸时字段保持None证明回退到内层机制的存在inner_ghost_becomes_the_split_variant内层按钮为ghost时effective_variant()返回Ghostinner_size_becomes_the_split_size内层按钮为small时effective_size()返回Small。这四个测试恰好印证了前文介绍的外层优先、未设则继承内层的取值规则可以作为你后续扩展或排查样式问题的起点。小结DropdownButton 把主动作 更多选项两种交互浓缩进一个视觉整体通过DropdownButton::new(id).button(...).dropdown_menu(...)三件套即可快速搭建变体与尺寸共享到两半label/icon/tooltip/loading/on_click 等动作属性留在内层Button锚点可用dropdown_menu_with_anchor自由控制。结合 dropdown_button.rs 源码、dropdown_menu.rs 的弹出层实现与 dropdown_button_story.rs 的示例你可以在此基础上构造工具栏、文件导出、设置项等各类分裂按钮交互。【免费下载链接】gpui-kitRust GUI components for building fantastic cross-platform desktop application by using GPUI.项目地址: https://gitcode.com/GitHub_Trending/gp/gpui-kit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表