editor: Prevent overlapping of signature/hover popovers and context menus (#31090)

Closes #29358

If hover popovers or signature popovers ever clash with the context menu
(like code completion or code actions), they find the best spot by
trying different directions around the context menu to show the popover.
If they can’t find a good spot, they just overlap with the context menu.

Not overlapping state:
<img width="350" alt="image"
src="https://github.com/user-attachments/assets/2f1bdc4c-eb01-405c-b5fb-eb28eadc9957"
/>

Overlapping case, moves popover to bottom of context menu:
<img width="350" alt="image"
src="https://github.com/user-attachments/assets/3ce4be23-7701-4711-b604-5e29682360e1"
/>

Overlapping case, moves popover to right of context menu:
<img width="350" alt="image"
src="https://github.com/user-attachments/assets/60d47518-e412-4d64-9d17-a69a17248bdf"
/> <img width="350" alt="image"
src="https://github.com/user-attachments/assets/2a3de176-7443-46d8-99d1-b2973a0ffaa6"
/>

Overlapping case, moves popover to left of context menu:
<img width="350" alt="image"
src="https://github.com/user-attachments/assets/015b799b-8c6e-4405-aee6-e205d4caebec"
/>

Overlapping case, moves popover to top of context menu:
<img width="350" alt="image"
src="https://github.com/user-attachments/assets/fbd03d84-9a49-44eb-846b-a9852d2ff43e"
/>

Release Notes:

- Fixed an issue where hover popovers or signature popovers would
overlap with existing opened completion or code actions context menus.
This commit is contained in:
smit 2025-05-21 18:45:00 +05:30 committed by GitHub
parent 0c03519393
commit 7450b788f3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 322 additions and 71 deletions

View file

@ -1388,6 +1388,44 @@ where
&& point.y <= self.origin.y.clone() + self.size.height.clone()
}
/// Checks if this bounds is completely contained within another bounds.
///
/// This method determines whether the current bounds is entirely enclosed by the given bounds.
/// A bounds is considered to be contained within another if its origin (top-left corner) and
/// its bottom-right corner are both contained within the other bounds.
///
/// # Arguments
///
/// * `other` - A reference to another `Bounds` that might contain this bounds.
///
/// # Returns
///
/// Returns `true` if this bounds is completely inside the other bounds, `false` otherwise.
///
/// # Examples
///
/// ```
/// # use gpui::{Bounds, Point, Size};
/// let outer_bounds = Bounds {
/// origin: Point { x: 0, y: 0 },
/// size: Size { width: 20, height: 20 },
/// };
/// let inner_bounds = Bounds {
/// origin: Point { x: 5, y: 5 },
/// size: Size { width: 10, height: 10 },
/// };
/// let overlapping_bounds = Bounds {
/// origin: Point { x: 15, y: 15 },
/// size: Size { width: 10, height: 10 },
/// };
///
/// assert!(inner_bounds.is_contained_within(&outer_bounds));
/// assert!(!overlapping_bounds.is_contained_within(&outer_bounds));
/// ```
pub fn is_contained_within(&self, other: &Self) -> bool {
other.contains(&self.origin) && other.contains(&self.bottom_right())
}
/// Applies a function to the origin and size of the bounds, producing a new `Bounds<U>`.
///
/// This method allows for converting a `Bounds<T>` to a `Bounds<U>` by specifying a closure