mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
gpui: Fix anchored element size calculation with negative coordinates (#55124)
Closes #53202 `Anchored::prepaint` computes the bounding box of its children to determine the size used for fitting the anchored element in the window. Previously, this calculation manually tracked the minimum origin and maximum bottom-right point, initializing the maximum point to `(0, 0)`. If child bounds were in negative coordinates, the maximum point could be clamped to `(0, 0)`, inflating the computed size. This replaces the manual min/max accumulation with `Bounds::union`, starting from the actual child bounds instead of sentinel values. This computes the child bounding box correctly regardless of coordinate sign. --- Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Fixed a bug where the context menu in the agent panel (and other scrollable surfaces) would appear at the wrong location
This commit is contained in:
parent
a38fc8c8de
commit
b38194198b
1 changed files with 118 additions and 11 deletions
|
|
@ -132,19 +132,17 @@ impl Element for Anchored {
|
|||
return;
|
||||
}
|
||||
|
||||
let mut child_min = point(Pixels::MAX, Pixels::MAX);
|
||||
let mut child_max = Point::default();
|
||||
for child_layout_id in &request_layout.child_layout_ids {
|
||||
let child_bounds = window.layout_bounds(*child_layout_id);
|
||||
child_min = child_min.min(&child_bounds.origin);
|
||||
child_max = child_max.max(&child_bounds.bottom_right());
|
||||
}
|
||||
let size: Size<Pixels> = (child_max - child_min).into();
|
||||
let children_bounds = request_layout
|
||||
.child_layout_ids
|
||||
.iter()
|
||||
.map(|id| window.layout_bounds(*id))
|
||||
.reduce(|acc, bounds| acc.union(&bounds))
|
||||
.unwrap();
|
||||
|
||||
let (origin, mut desired) = self.position_mode.get_position_and_bounds(
|
||||
self.anchor_position,
|
||||
self.anchor,
|
||||
size,
|
||||
children_bounds.size,
|
||||
bounds,
|
||||
self.offset,
|
||||
);
|
||||
|
|
@ -161,7 +159,7 @@ impl Element for Anchored {
|
|||
let switched = Bounds::from_anchor_and_size(
|
||||
anchor.other_side_along(Axis::Horizontal),
|
||||
origin,
|
||||
size,
|
||||
children_bounds.size,
|
||||
);
|
||||
if !(switched.left() < limits.left() || switched.right() > limits.right()) {
|
||||
anchor = anchor.other_side_along(Axis::Horizontal);
|
||||
|
|
@ -173,7 +171,7 @@ impl Element for Anchored {
|
|||
let switched = Bounds::from_anchor_and_size(
|
||||
anchor.other_side_along(Axis::Vertical),
|
||||
origin,
|
||||
size,
|
||||
children_bounds.size,
|
||||
);
|
||||
if !(switched.top() < limits.top() || switched.bottom() > limits.bottom()) {
|
||||
desired = switched;
|
||||
|
|
@ -289,3 +287,112 @@ impl AnchoredPositionMode {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
Context, Pixels, PlatformInput, Point, TestAppContext, Window, deferred, div, point,
|
||||
prelude::*, px, size,
|
||||
};
|
||||
|
||||
struct AnchoredTestView {
|
||||
position: Point<Pixels>,
|
||||
}
|
||||
|
||||
impl Render for AnchoredTestView {
|
||||
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
||||
div().size_full().child(
|
||||
div()
|
||||
.id("scroll-container")
|
||||
.overflow_y_scroll()
|
||||
.size_full()
|
||||
.child(div().h(px(2000.)).w_full())
|
||||
.child(
|
||||
deferred(
|
||||
super::anchored()
|
||||
.snap_to_window()
|
||||
.position(self.position)
|
||||
.child(
|
||||
div()
|
||||
.id("menu")
|
||||
.debug_selector(|| "MENU".into())
|
||||
.w(px(200.))
|
||||
.h(px(300.)),
|
||||
),
|
||||
)
|
||||
.with_priority(1),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_anchored_position_without_scroll(cx: &mut TestAppContext) {
|
||||
let window = cx.open_window(size(px(800.), px(600.)), |_, _| AnchoredTestView {
|
||||
position: point(px(100.), px(100.)),
|
||||
});
|
||||
|
||||
cx.run_until_parked();
|
||||
|
||||
let menu_bounds = window
|
||||
.update(cx, |_, window, _| {
|
||||
window.rendered_frame.debug_bounds.get("MENU").copied()
|
||||
})
|
||||
.unwrap()
|
||||
.expect("MENU debug bounds not found");
|
||||
|
||||
assert_eq!(menu_bounds.origin, point(px(100.), px(100.)));
|
||||
assert_eq!(menu_bounds.size, size(px(200.), px(300.)));
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_anchored_position_when_scrolled(cx: &mut TestAppContext) {
|
||||
let window = cx.open_window(size(px(800.), px(600.)), |_, _| AnchoredTestView {
|
||||
position: point(px(100.), px(100.)),
|
||||
});
|
||||
|
||||
cx.run_until_parked();
|
||||
|
||||
window
|
||||
.update(cx, |_, window, cx| {
|
||||
let event = gpui::ScrollWheelEvent {
|
||||
position: point(px(400.), px(300.)),
|
||||
delta: gpui::ScrollDelta::Pixels(point(px(0.), px(-1000.))),
|
||||
..Default::default()
|
||||
};
|
||||
window.dispatch_event(PlatformInput::ScrollWheel(event), cx);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
cx.run_until_parked();
|
||||
|
||||
let menu_bounds = window
|
||||
.update(cx, |_, window, _| {
|
||||
window.rendered_frame.debug_bounds.get("MENU").copied()
|
||||
})
|
||||
.unwrap()
|
||||
.expect("MENU debug bounds not found");
|
||||
|
||||
assert_eq!(menu_bounds.origin, point(px(100.), px(100.)));
|
||||
assert_eq!(menu_bounds.size, size(px(200.), px(300.)));
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_anchored_snaps_to_window(cx: &mut TestAppContext) {
|
||||
let window = cx.open_window(size(px(800.), px(600.)), |_, _| AnchoredTestView {
|
||||
position: point(px(100.), px(500.)),
|
||||
});
|
||||
|
||||
cx.run_until_parked();
|
||||
|
||||
let menu_bounds = window
|
||||
.update(cx, |_, window, _| {
|
||||
window.rendered_frame.debug_bounds.get("MENU").copied()
|
||||
})
|
||||
.unwrap()
|
||||
.expect("MENU debug bounds not found");
|
||||
|
||||
assert_eq!(menu_bounds.origin, point(px(100.), px(300.)));
|
||||
assert_eq!(menu_bounds.size, size(px(200.), px(300.)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue