Popover
Popover 用于在触发元素附近展示浮动内容。它支持多种定位方式、自定义内容、不同触发方式以及自动关闭行为,适合用来实现提示卡片、上下文菜单、小表单和局部操作面板。
#导入
use gpui_kit::component::popover::{Popover};
#用法
#基础 Popover
use gpui_kit::ParentElement as _;
use gpui_kit::component::{button::Button, popover::Popover};
Popover::new("basic-popover")
.trigger(Button::new("trigger").label("Click me").outline())
.child("Hello, this is a popover!")
.child("It appears when you click the button.")
#自定义定位
anchor 命名的是 Popover 自身的锚点,不是触发器的角点。
Top* 表示弹层在触发器下方,Bottom* 表示在上方;
LeftCenter 表示在右侧,RightCenter 表示在左侧。
弹层只限制在窗口内,不改变 anchor,也不自动翻转。
use gpui_kit::{Anchor, px};
use gpui_kit::component::popover::Popover;
Popover::new("anchored")
.anchor(Anchor::TopCenter)
.offset(px(8.))
.arrow(true)
.trigger(Button::new("details").label("详情"))
.child("上下文详情")
| 选项 | 含义 | 默认值 |
|---|---|---|
anchor(Anchor) | 弹层锚点,支持 TopCenter、BottomCenter 等八种位置 | TopLeft |
offset(Pixels) | 触发器到弹层的间距;有箭头时为到箭头尖端的间距 | 0.25rem |
arrow(bool) | 在 anchor 对应边显示箭头 | false |
箭头跟随 anchor 的起始、居中或末端对齐,并向内避开圆角。
箭头额外占用 0.375rem,使用弹层背景色(未设置时使用主题的 popover 色)。
offset 和 arrow 都不会切换定位策略。
例如 Anchor::TopLeft 会让 Popover 出现在触发器正下方,并与其左对齐:
[ Trigger ]
┌──────────────┐
│ Popover │
└──────────────┘
use gpui_kit::component::Anchor;
Popover::new("top-center")
.anchor(Anchor::TopCenter)
.trigger(Button::new("btn").label("Top Center").outline())
.child("在触发器下方居中")
#在 Popover 中渲染 View
你也可以把实现了 Render 的 Entity<T> 作为 Popover 内容:
let view = cx.new(|_| MyView::new());
Popover::new("form-popover")
.anchor(Anchor::BottomLeft)
.trigger(Button::new("show-form").label("Open Form").outline())
.child(view.clone())
#使用 content 构造动态内容
如果你需要根据状态动态构造内容,或者希望在闭包中拿到 Popover 的上下文,可以使用 content:
use gpui_kit::ParentElement as _;
use gpui_kit::component::popover::Popover;
Popover::new("complex-popover")
.anchor(Anchor::BottomLeft)
.trigger(Button::new("complex").label("Complex Content").outline())
.content(|_, _, _| {
div()
.child("This popover has complex content.")
.child(
Button::new("action-btn")
.label("Perform Action")
.outline()
)
})
#右键触发
如果你想把 Popover 当作自定义上下文菜单来用,可以指定鼠标按键:
use gpui_kit::MouseButton;
Popover::new("context-menu")
.anchor(Anchor::BottomRight)
.mouse_button(MouseButton::Right)
.trigger(Button::new("right-click").label("Right Click Me").outline())
.child("Context Menu")
.child(Separator::horizontal())
.child("This is a custom context menu.")
#手动关闭
如果你希望在内容内部主动关闭 Popover,可以发出 DismissEvent:
use gpui_kit::component::{DismissEvent, popover::Popover};
Popover::new("dismiss-popover")
.trigger(Button::new("dismiss").label("Dismiss Popover").outline())
.content(|_, cx| {
div()
.child("Click the button below to dismiss this popover.")
.child(
Button::new("close-btn")
.label("Close Popover")
.on_click(cx.listener(|_, _, _, cx| {
cx.emit(DismissEvent);
}))
)
})
#自定义样式
Popover 同样支持 appearance(false) 来关闭默认样式,并通过 Styled trait 完整自定义外观:
Popover::new("custom-popover")
.appearance(false)
.trigger(Button::new("custom").label("Custom Style"))
.bg(cx.theme().accent)
.text_color(cx.theme().accent_foreground)
.p_6()
.rounded_xl()
.shadow_2xl()
.child("Fully custom styled popover")
#受控打开状态
通过 open 和 on_open_change,你可以把 Popover 的开关状态交给外部状态管理:
use gpui_kit::component::popover::Popover;
struct MyView {
popover_open: bool,
}
Popover::new("controlled-popover")
.open(self.open)
.on_open_change(cx.listener(|this, open: &bool, _, cx| {
this.popover_open = *open;
cx.notify();
}))
.trigger(Button::new("control-btn").label("Control Popover").outline())
.child("This popover's open state is controlled programmatically.")
#默认打开
如果只想设置首次渲染时默认打开,可以使用 default_open(true):
use gpui_kit::component::popover::Popover;
Popover::new("default-open-popover")
.default_open(true)
.trigger(Button::new("default-open-btn").label("Default Open").outline())
.child("This popover is open by default when first rendered.")
#自定义触发器
触发器是任意实现了 Selectable 的元素。Popover 打开期间会对触发器调用
open(true),而不是 selected(true),这样触发器可以区分「我的 Popover 正开着」
和「我是当前选中项」这两件事。
open 与 is_open 默认落到 selected 和 is_selected,因此只实现了选中态的
触发器行为不变,Button 触发器打开时的外观也与选中态一致。如果你的元素已经
用 selected 表达别的含义(例如侧栏行选中表示当前视图),就覆盖这两个方法:
use gpui_kit::component::Selectable;
struct SidebarRow {
/// 这一行是当前视图。
selected: bool,
/// 这一行的账户 Popover 正开着。
open: bool,
}
impl Selectable for SidebarRow {
fn selected(mut self, selected: bool) -> Self {
self.selected = selected;
self
}
fn is_selected(&self) -> bool {
self.selected
}
fn open(mut self, open: bool) -> Self {
self.open = open;
self
}
fn is_open(&self) -> bool {
self.open
}
}