popover: Fix click trigger to close popover. (#1559)

This commit is contained in:
Jason Lee 2025-11-11 21:14:56 +08:00 committed by GitHub
parent eef8abaa15
commit 3cd94ea0be
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 55 additions and 11 deletions

View file

@ -121,7 +121,10 @@ impl Popover {
self
}
/// Set the content of the popover.
/// Set the content builder for content of the Popover.
///
/// This callback will called every time on render the popover.
/// So, you should avoid creating new elements or entities in the content closure.
pub fn content<F, E>(mut self, content: F) -> Self
where
E: IntoElement,
@ -144,7 +147,8 @@ impl Popover {
self
}
/// Bind the focus handle to track focus inside the popover.
/// Bind the focus handle to receive focus when the popover is opened.
/// If you not set this, a new focus handle will be created for the popover to
///
/// If popover is opened, the focus will be moved to the focus handle.
pub fn track_focus(mut self, handle: &FocusHandle) -> Self {
@ -225,7 +229,16 @@ impl PopoverState {
if self.open {
let state = cx.entity();
self.previous_focus = window.focused(cx);
self.focus_handle(cx).focus(window);
let focus_handle = if let Some(tracked_focus_handle) = self.tracked_focus_handle.clone()
{
tracked_focus_handle
} else {
self.focus_handle.clone()
};
cx.defer_in(window, move |_, window, _| {
focus_handle.focus(window);
});
self._dismiss_subscription =
Some(
@ -246,9 +259,7 @@ impl PopoverState {
if let Some(callback) = self.on_open_change.as_ref() {
callback(&self.open, window, cx);
}
cx.notify();
window.refresh();
}
fn on_action_cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
@ -258,11 +269,7 @@ impl PopoverState {
impl Focusable for PopoverState {
fn focus_handle(&self, _: &App) -> FocusHandle {
if let Some(tracked_focus_handle) = &self.tracked_focus_handle {
tracked_focus_handle.clone()
} else {
self.focus_handle.clone()
}
self.focus_handle.clone()
}
}
@ -294,7 +301,7 @@ impl RenderOnce for Popover {
});
let open = state.read(cx).open;
let focus_handle = state.read(cx).focus_handle.clone();
let focus_handle = state.focus_handle(cx);
let trigger_bounds = state.read(cx).trigger_bounds;
let Some(trigger) = self.trigger else {
@ -310,6 +317,9 @@ impl RenderOnce for Popover {
let state = state.clone();
move |_, window, cx| {
state.update(cx, |state, cx| {
// We force set open to false to toggle it correctly.
// Because if the mouse down out will toggle open first.
state.open = open;
state.toggle_open(window, cx);
});
cx.notify(parent_view_id);

View file

@ -59,6 +59,40 @@ Popover::new("form-popover")
.child(view.clone())
```
### Add content by `content` method
The `content` method allows you to create more complex popover content using a closure. This is useful when
you need to build dynamic content or need access to the popover's context.
This method will let us to have `&mut PopoverState`, `&mut Window` and `&mut Context<PopoverState>` parameters in the
closure is to allow you to interact with the popover's state and the overall application context if needed.
:::warning
This `content` callback will called every time on render the popover.
So, you should avoid creating new elements or entities in the content closure
or other heavy operations that may impact performance.
:::
And `content` will works with `child`, `children` methods together.
```rust
use gpui::ParentElement as _;
use gpui_component::popover::Popover;
Popover::new("complex-popover")
.anchor(Corner::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()
)
})
```
### Right-Click Popover
Sometimes you may want to show a popover on right-click, for example, to create a special your ownen context menu. The `mouse_button` method allows you to specify which mouse button triggers the popover.