资讯详情

资讯详情

GPUI Kit Alert Dialog:用 Rust 构建不可误触的破坏性操作确认对话框

GPUI Kit Alert Dialog用 Rust 构建不可误触的破坏性操作确认对话框【免费下载链接】gpui-kitRust GUI components for building fantastic cross-platform desktop application by using GPUI.项目地址: https://gitcode.com/GitHub_Trending/gp/gpui-kitAlert Dialog 是 GPUI Kit 的gpui-base层提供的一个模态确认原语当用户即将执行删除、覆盖、提交等不可逆或高风险操作时用它强制用户做出明确决策。它只负责行为与语义结构打开/关闭状态、焦点陷阱、键盘确认、无障碍语义不掺入任何产品视觉语言样式完全由你通过 GPUI 的Styled方法与可组合部件自行定义。读完本文你将掌握 Alert Dialog 的全部部件构成、受控状态管理方式、键盘/焦点行为以及如何基于官方 showcase 示例在原生与 WASM 环境运行它。定位与 Dialog 有何不同在 GPUI Base 原语目录 中Alert Dialog 与普通 Dialog 同属模态表面但职责不同Dialog是一个可组合的通用模态宿主提供焦点管理、backdrop、标题、关闭部件见 Dialog 文档可用于表单、设置、详情等场景Alert Dialog是 Dialog 的特化专为需要用户明确决策的破坏性/高风险操作设计。从源码看它的实现就是pub struct AlertDialog(Dialog)通过一行构造完成两处关键差异crates/base/src/alert_dialog.rspub fn new(cx: mut App) - Self { Self( Dialog::new(cx) .role(Role::AlertDialog) .close_on_backdrop_press(false), ) }Role::AlertDialog为无障碍树提供正确的语义角色而close_on_backdrop_press(false)意味着点击背景遮罩默认不会关闭对话框——因为需要明确决策的操作不允许被顺手点掉。这与通用 Dialog 的默认值overlay_closable: true正好相反也是单元测试backdrop_is_not_closable_by_default所断言的行为crates/base/src/alert_dialog.rs。运行官方示例在仓库根目录执行cargo run -p gpui-base-examples -- alert-dialog这条命令的运行链路是crates/base/examples/native包包名gpui-base-examplesdefault-run components见 Cargo.toml启动入口 components.rs 读取命令行第一个参数alert-dialog并通过#[path ../../../showcase/mod.rs]复用同一份 showcase 实现showcase/mod.rs 的render_page把alert-dialog路由到self.alert_dialog(cx)。同一份 showcase 还会编译成页面顶部的 WASM 预览通过run_embedded见 showcase/mod.rs因此原生窗口与浏览器预览展示的是完全相同的代码。窗口默认尺寸为 840×640showcase/mod.rs。导入在gpui-kit应用或直接依赖gpui-base中导入use gpui_kit::base::{ AlertDialog, AlertDialogAction, AlertDialogBackdrop, AlertDialogCancel, AlertDialogClose, AlertDialogDescription, AlertDialogPopup, AlertDialogTitle, AlertDialogTrigger, };gpui_kit通过pub use ::gpui_base as base;重新导出整个 base 层crates/kit/src/lib.rs而上述类型全部在 crates/base/src/lib.rs 中对外公开。直接使用gpui-basecrate 时路径写作use gpui_base::AlertDialog;等。完整部件清单见下表部件作用AlertDialog模态宿主管理打开/关闭、焦点、键盘 action 与回调顺序AlertDialogTrigger触发打开的外层包装支持DialogHandle与on_open回调AlertDialogBackdrop背景遮罩部件可独立设置样式AlertDialogPopup弹窗内容容器部件AlertDialogTitle标题槽位AlertDialogDescription描述文本槽位AlertDialogCancel取消包装激活时向窗口分发CancelactionAlertDialogClose关闭包装同Cancel分发AlertDialogAction确认包装激活时向窗口分发Confirm { secondary: false }actionAnatomy 与 API官方示例即本页面上方可运行的展示组合了AlertDialog、AlertDialogAction、AlertDialogCancel、AlertDialogDescription、AlertDialogPopup、AlertDialogTitle、AlertDialogTrigger七个部件。GPUI 的标准样式与事件 trait 负责呈现这些 base 类型只提供交互结构——这正是 gpui-base 设计原则 的体现行为内建、呈现归你、部件可组合、状态显式。权威示例文件为 crates/base/examples/showcase/components/alert_dialog.rs原生与浏览器预览编译的是同一个文件。AlertDialog的完整构造方法全部转发给底层Dialogcrates/base/src/alert_dialog.rs方法签名要点说明open(bool)初始打开状态用于受控模式传入self.alert_dialog_openhandle(DialogHandle)外部句柄与on_open_change互备可命令式open()/close()on_open_changeFn(bool, DialogChangeReason, mut Window, mut App)打开状态变化回调DialogChangeReason区分触发来源on_okFn(ClickEvent, mut Window, mut App) - bool确认 action 处理返回true才允许关闭on_cancelFn(ClickEvent, mut Window, mut App) - bool取消 action 处理返回true才允许关闭on_closeFn(ClickEvent, mut Window, mut App)已确认关闭后的事件backdrop(impl IntoElement)遮罩元素典型用法AlertDialogBackdrop::new()配样式popup(impl IntoElement)弹窗元素典型用法AlertDialogPopup::new()装内容close_on_escape(bool)键盘 Esc 开关默认开启dismiss_below_y(Pixels)遮罩点击忽略区点击 y 坐标小于该值时不会触发关闭底层Dialog默认px(0.)完整的 Rust 示例以下是官方 showcase 的完整实现与 alert_dialog.rs 一致逐段注释以便直接改写use gpui::relative; use super::*; impl BaseShowcase { pub(in super::super) fn alert_dialog(self, cx: mut ContextSelf) - impl IntoElement { let open self.alert_dialog_open; // 用弱引用把 entity 传进回调避免形成引用环 let entity cx.entity().downgrade(); let open_entity entity.clone(); let cancel_entity entity.clone(); let action_entity entity.clone(); div() // ---- 触发器一个完全由你决定样式的按钮 ---- .child( Button::new(open-alert-dialog) .h_7() .line_height(relative(1.)) .px_3() .flex() .items_center() .justify_center() .bg(gpui::black()) .text_color(gpui::white()) .on_click(move |_, _, cx| { _ open_entity.update(cx, |this, cx| { this.alert_dialog_open true; cx.notify(); // 更新受控状态并请求重绘 }); }) .child(Delete project), ) .child( // ---- 模态宿主受控 open 状态 状态变化回调 ---- AlertDialog::new(cx) .open(open) .on_open_change(move |open, _, _, cx| { _ entity.update(cx, |this, cx| { this.alert_dialog_open open; cx.notify(); }); }) // 确认分支返回值 true 表示允许关闭 .on_ok(move |_, _, cx| { _ ok_entity.update(cx, |this, cx| { this.alert_dialog_open false; cx.notify(); }); true }) // 遮罩半透明黑点击不关闭AlertDialog 默认如此 .backdrop( AlertDialogBackdrop::new() .absolute() .inset_0() .bg(super::example_rgb(0x000000)) .opacity(0.18), ) // 弹窗居中容器 你的卡片视觉 .popup( AlertDialogPopup::new() .flex() .items_center() .justify_center() .child( div() .w_72() .p_3() .bg(super::example_rgb(0xffffff)) .border_1() .border_color(super::example_rgb(0x171717)) .child(AlertDialogTitle::new().child(Delete project?)) .child( AlertDialogDescription::new() .mt_2() .text_xs() .text_color(super::example_rgb(0x525252)) .child( This permanently deletes Acme Studio and all of its data., ), ) .child( div() .mt_3() .flex() .justify_end() .gap_2() .child(AlertDialogCancel::new().child( Button::new(cancel-delete) .px_3() .h_7() .flex() .items_center() .text_xs() .border_1() .border_color(super::example_rgb(0xd4d4d4)) .on_click(move |_, _, cx| { _ cancel_entity.update(cx, |this, cx| { this.alert_dialog_open false; cx.notify(); }); }) .child(Cancel), )) .child(AlertDialogAction::new().child( Button::new(confirm-delete) .px_3() .h_7() .flex() .items_center() .text_xs() .border_1() .border_color(super::example_rgb(0x171717)) .bg(super::example_rgb(0x171717)) .text_color(super::example_rgb(0xffffff)) .on_click(move |_, _, cx| { _ action_entity.update(cx, |this, cx| { this.alert_dialog_open false; cx.notify(); }); }) .child(Delete), )), ), ), ), ) } }运行该示例的命令在前面已给出cargo run -p gpui-base-examples -- alert-dialog。该命令负责应用初始化、窗口创建840×640以及共享的BaseShowcase状态装配gpui_base::init(cx)与open_window见 showcase/mod.rs。示例中的example_rgb取自 shared/palette.rs 的ExamplePalette它会依据窗口的亮暗外观自动切换配色——真实项目中建议改用你的主题 token。状态与事件谁决定是否真的执行破坏操作打开与关闭由AlertDialog管理但提交破坏性工作的时机由你的业务回调决定。示例中的分工是点击DeleteAlertDialogAction包装的按钮→ 底层分发Confirm { secondary: false }action对话框宿主调用你注册的on_ok回调——这里才是执行删除、请求后端、写入磁盘等真实破坏操作的地方on_ok返回true表示决策已完成宿主随后将open置为false并触发on_open_change(false, DialogChangeReason::Confirm, ...)若返回false则对话框保持打开例如校验失败、需要二次确认时。取消侧同理AlertDialogCancel分发Cancelaction走on_cancel回调。遮罩点击与 Esc 键也都会进入on_cancel其中遮罩受close_on_backdrop_press(false)限制默认不生效。受控状态的放置原则原文要求源码印证把受控状态放在父渲染类型如BaseShowcase的alert_dialog_open: bool字段或一个 GPUI entity 中在回调里修改它并调用cx.notify()不要在每次渲染时重建持久 entity——示例中所有回调都通过cx.entity().downgrade()克隆出的弱引用update同一个实体正是为了避免每次渲染产生新的引用环或丢失状态。如果你更希望命令式控制可以使用DialogHandleAlertDialog::new(cx).handle(handle)之后调用handle.open(window, cx)/handle.close(window, cx)或先查询handle.is_open()。handle 与on_open_change是同一套状态机的两个入口任何入口触发的变化都会以DialogChangeReasonTriggerPress/BackdropPress/Cancel/Confirm/Imperative标注来源crates/base/src/dialog.rs。源码级原理部件如何被构造、键盘如何生效部件宏与稳定元素 IDAlertDialogBackdrop、AlertDialogPopup、AlertDialogTitle、AlertDialogDescription四个纯结构部件由alert_part!宏生成每个部件只是一个持有StyleRefinement与少量子元素的div渲染时带上稳定元素 ID如alert-dialog-backdrop、alert-dialog-title并接受Styled与ParentElementcrates/base/src/alert_dialog.rs。这些稳定 ID 正是无障碍标签与自动化测试的锚点。Cancel / Confirm action 与键盘绑定AlertDialogCancel、AlertDialogClose渲染为带on_click的div点击时执行window.dispatch_action(Box::new(crate::actions::Cancel), cx)AlertDialogAction则分发crate::actions::Confirm { secondary: false }crates/base/src/alert_dialog.rs。这两个 action 定义在 crates/base/src/actions.rsConfirm带secondary: bool载荷用于区分主/次确认Cancel是无载荷 action。键盘支持由 crates/base/src/dialog.rs 的init全局注册cx.bind_keys([ KeyBinding::new(escape, Cancel, Some(CONTEXT)), KeyBinding::new(enter, Confirm { secondary: false }, Some(CONTEXT)), ]);即对话框打开期间Enter 触发确认、Esc 触发取消。宿主在渲染时对焦点内容注册key_context(Dialog)并绑定这两个 action 的处理器crates/base/src/dialog.rs处理顺序为调用你的on_ok/on_cancel→ 若返回true则同步handle/on_open_change为关闭态附对应DialogChangeReason→ 触发on_close。焦点陷阱与层级宿主使用deferred(anchored())把整个对话框挂到窗口坐标原点并铺满视口内部通过focus_trap(format!(dialog-{}, self.layer), self.focus)建立焦点陷阱保证 Tab 循环不会逃逸到背后的应用crates/base/src/dialog.rs。layer参数默认 0#[doc(hidden)]允许同一时间存在多层模态并保持各自焦点域。topmost: true时遮罩点击关闭才可用dismiss_below_y可让标题栏区域内的点击不触发关闭。单元测试印证alert_dialog.rs 内置了一个最小测试用request_close探针 simulate_click点击遮罩断言close_requested仍为false——直接验证Alert Dialog 默认不允许点击遮罩关闭这一安全语义防止未来回归。无障碍要求构建 Alert Dialog 时必须满足提供标题与描述AlertDialogTitle与AlertDialogDescription缺一不可二者通过稳定元素 IDalert-dialog-title/alert-dialog-description关联到Role::AlertDialog宿主焦点陷阱由宿主内置的focus_trap保证无需额外实现必须提供取消途径至少保留一个AlertDialogCancel或 Esc 键默认开启恢复焦点关闭后焦点应回到打开它的触发器AlertDialogTrigger之上。示例中打开动作由Button的on_click完成焦点恢复行为来自 GPUI 的焦点管理在你的消费设计系统中需要验证该链路。注意事项与视觉验证清单元素 ID 使用稳定值能接受的地方就使用固定 ID如Button::new(open-alert-dialog)它们是焦点、无障碍与测试的锚点破坏性按钮放在 Action 一侧示例中AlertDialogCancel与AlertDialogAction并列于弹窗底部右侧justify_endgap_2取消在左、确认在右符合桌面惯例必查状态外观在你的消费设计系统中逐一验证焦点、悬停hover、激活active、选中selected、禁用disabled五态以及**减少动效reduced-motion与高对比度high-contrast**模式下的表现——这些是gpui-base交给应用层的呈现职责。最后回到核心结论Alert Dialog 是一个行为内建、呈现归你的模态确认原语。它的安全性遮罩不可误关、Enter/Esc 明确映射、焦点不逃逸全部内建于gpui-base而破坏性操作是否提交、如何提交始终由你的on_ok回调把守。把本文示例与 crates/base/examples/showcase/components/alert_dialog.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),仅供参考
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →