Context Menu
Displays a menu of actions triggered by a right click.
Overview
在虚线框里右键 —— 菜单在指针处弹出,点击外部或 Escape 关闭。
Installation
Context Menu 是单个 Rust 文件(src/context_menu.rs)—— 浮层走 deferred() overlay pass,键盘导航用 FocusHandle + on_key_down,图标和 ⌘/⇧/⌥/⌃ 修饰键全部 PathBuilder 手绘(Geist 没有这些码位)。
[dependencies]gpui-kit = "0.6.6"# context_menu.rs is self-contained: FocusHandle for keyboard# nav, deferred() for the popup pass, PathBuilder glyphs — no# extra deps.# Download the runnable example crate, then:cargo run --locked -- --context-menu# Other demos:cargo run --locked -- --context-menu submenucargo run --locked -- --context-menu selectioncargo run --locked -- --context-menu destructiveUsage
ContextMenu 是实体:.trigger(..) 接收一个元素工厂作为右键区域,菜单项用 ContextMenuEntry 枚举声明(Item / Label / Separator)。右键在指针处弹出,点击行发出 ContextMenuSelectEvent。
use justdo_command::context_menu::{ ContextMenu, ContextMenuEntry, ContextMenuItem, ContextMenuSelectEvent,};let menu = cx.new(|cx| { ContextMenu::new( // Trigger area — any renderable element factory. move || { div() .p_4() .border_1() .border_dashed() .child("Right click here") .into_any_element() }, vec![ ContextMenuEntry::Item(ContextMenuItem::new("Profile")), ContextMenuEntry::Item(ContextMenuItem::new("Billing")), ContextMenuEntry::Item( ContextMenuItem::new("Settings").shortcut("⌘S"), ), ContextMenuEntry::Separator, ContextMenuEntry::Item( ContextMenuItem::new("Delete").destructive(true), ), ], cx, )});cx.subscribe(&menu, |view, _, ev: &ContextMenuSelectEvent, cx| { view.handle(ev.value.clone()); cx.notify();});div().size_full().child(menu)网页预览与桌面端运行同一份 Rust / GPUI 组件。
Composition
结构对应 ContextMenuTrigger + ContextMenuContent → Item / Label / Separator / Sub / CheckboxItem / RadioItem / Shortcut。子菜单用 .submenu(vec![...]) 内嵌,复选与单选行提交后菜单保持打开。
ContextMenu entity — trigger + popup + nav state├── ContextMenuTrigger .trigger(factory) — right-click surface│ (secondary-button MouseDown → open at pointer)├── ContextMenuContent floating panel — deferred pass, viewport-clamped│ ├── ContextMenuGroup ContextMenuEntry::Label — muted heading│ ├── ContextMenuItem icon + label + shortcut + ▸│ │ .value(..) .icon(..) .shortcut(..)│ │ .disabled(..) .destructive(..)│ ├── ContextMenuCheckboxItem .checkbox(bool) — stays open on toggle│ ├── ContextMenuRadioItem .radio("group") — exclusive per group│ ├── ContextMenuSeparator ContextMenuEntry::Separator│ └── ContextMenuSub ContextMenuItem::submenu(vec![...])│ └── SubContent second panel at the trigger row└── emits ContextMenuSelectEvent { value, checked, group }// Pointer coordinates arrive in window space; the component// captures its root bounds each prepaint and converts to local// space, so the menu anchors correctly wherever the trigger// sits. Panels clamp inside the root bounds (MENU_W = 208px).//// Right-clicking elsewhere while open re-anchors the menu —// same as the OS menu.Keyboard
菜单打开时焦点移到浮层:↑/↓ 移动高亮(跳过禁用行),→ 打开子菜单,← 收起子菜单,Enter 执行,Escape 关闭。鼠标 hover 同样驱动高亮与子菜单开合。
// When the menu opens, a FocusHandle on the overlay takes// keyboard focus://// ↑ / ↓ move the active row (disabled rows are skipped)// → open the submenu under the active row// ← close the submenu (or the menu)// Enter activate the active row// Escape close//// Hovering a row also moves the highlight and opens its// submenu, mirroring OS menus.Accessibility
Semantics
禁用行半透明且不进入高亮序列;destructive 项红色双重标识;checkbox 行渲染 ✓、radio 行渲染圆点,状态可从实体读取(checked(..) / radio_value(..))。
Dismissal
点击外部 / Escape / 选择普通项关闭;右键点击别处把菜单重新锚定到新的指针位置,与系统菜单一致。
API Reference
实体暴露的构造器、builder 方法与事件。
| API | Signature | Notes |
|---|---|---|
ContextMenu::new(..) | trigger factory, Vec<ContextMenuEntry> | Trigger renders via a factory closure. |
ContextMenuItem::new(..) | &str label | .value(..) .icon(..) .shortcut(..) .disabled(..) .destructive(..) |
.checkbox(..) | bool | Toggle row; menu stays open. |
.radio(..) | &str group | Exclusive within the group. |
.submenu(..) | Vec<ContextMenuEntry> | Nested panel (one level deep). |
.set_open(..) | bool, Window, cx | Controlled open at trigger center. |
.checked(..) | -> Option<bool> | Checkbox state by value. |
.radio_value(..) | -> Option<SharedString> | Current value of a radio group. |
ContextMenuSelectEvent | { value, checked, group } | Emitted on every activation. |
Source & inspiration
结构与交互参考 shadcn/ui Context Menu (React Aria) 。本页交互由 Rust / GPUI 绘制,基础原语来自 gpui-kit 。