fix: exclude scroll area padding from pane sizing
This commit is contained in:
@@ -222,6 +222,9 @@ pub(crate) fn thumb_geometry(
|
||||
|
||||
impl RenderOnce for ScrollArea {
|
||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let mut pane_style = self.style.clone();
|
||||
pane_style.padding = Default::default();
|
||||
|
||||
let mut pane = div()
|
||||
.id(self.id.clone())
|
||||
.overflow_hidden()
|
||||
@@ -239,7 +242,7 @@ impl RenderOnce for ScrollArea {
|
||||
// Keep wheel events from scrolling an outer scroll area too.
|
||||
cx.stop_propagation();
|
||||
})
|
||||
.refine_style(&self.style);
|
||||
.refine_style(&pane_style);
|
||||
|
||||
// Extend with children
|
||||
pane.extend(self.children);
|
||||
@@ -248,17 +251,18 @@ impl RenderOnce for ScrollArea {
|
||||
wrapper.extend(std::iter::once(pane.into_any_element()));
|
||||
|
||||
if let Some(handle) = self.handle {
|
||||
let id = self.id.clone();
|
||||
let autohide = match self.visibility {
|
||||
ScrollbarVisibility::Platform => cx.theme().scrollbar.autohide,
|
||||
ScrollbarVisibility::Always => false,
|
||||
ScrollbarVisibility::Autohide => true,
|
||||
};
|
||||
let id = self.id.clone();
|
||||
|
||||
wrapper.extend(std::iter::once(
|
||||
canvas(|_, _, _| {}, {
|
||||
canvas(|bounds, _, _| bounds, {
|
||||
let handle = handle.clone();
|
||||
move |_, _, window, cx| {
|
||||
paint_scrollbar(&handle, &id, autohide, window, cx);
|
||||
move |overlay, _, window, cx| {
|
||||
paint_scrollbar(&handle, &id, autohide, overlay, window, cx);
|
||||
}
|
||||
})
|
||||
.absolute()
|
||||
@@ -276,6 +280,7 @@ fn paint_scrollbar(
|
||||
handle: &ScrollHandle,
|
||||
key: &ElementId,
|
||||
autohide: bool,
|
||||
overlay: Bounds<Pixels>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) {
|
||||
@@ -287,10 +292,10 @@ fn paint_scrollbar(
|
||||
let inset = theme.scrollbar.inset;
|
||||
let min_thumb = theme.scrollbar.min_thumb;
|
||||
|
||||
let viewport = handle.bounds();
|
||||
let max_offset = handle.max_offset().y;
|
||||
let bounds = handle.bounds();
|
||||
|
||||
if max_offset <= Pixels::ZERO || bounds.size.height <= Pixels::ZERO {
|
||||
if max_offset <= Pixels::ZERO || viewport.size.height <= Pixels::ZERO {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -306,21 +311,21 @@ fn paint_scrollbar(
|
||||
_ => 1.0,
|
||||
};
|
||||
|
||||
let track = bounds.size.height - inset * 2.0;
|
||||
let track = overlay.size.height - inset * 2.0;
|
||||
let (thumb_length, thumb_top) = thumb_geometry(
|
||||
bounds.size.height,
|
||||
bounds.size.height + max_offset,
|
||||
viewport.size.height,
|
||||
viewport.size.height + max_offset,
|
||||
handle.offset().y,
|
||||
track,
|
||||
min_thumb,
|
||||
);
|
||||
|
||||
let thumb_x = bounds.right() - width - inset;
|
||||
let thumb_x = overlay.right() - width - inset;
|
||||
let thumb_bounds = Bounds::from_corners(
|
||||
point(thumb_x, bounds.top() + inset + thumb_top),
|
||||
point(thumb_x, overlay.top() + inset + thumb_top),
|
||||
point(
|
||||
thumb_x + width,
|
||||
bounds.top() + inset + thumb_top + thumb_length,
|
||||
overlay.top() + inset + thumb_top + thumb_length,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -332,7 +337,7 @@ fn paint_scrollbar(
|
||||
window.on_mouse_event({
|
||||
let state = state.clone();
|
||||
move |event: &ScrollEvent, phase, window, cx| {
|
||||
if phase != DispatchPhase::Bubble || !bounds.contains(&event.position) {
|
||||
if phase != DispatchPhase::Bubble || !overlay.contains(&event.position) {
|
||||
return;
|
||||
}
|
||||
state.touch(window, cx);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use gpui::{
|
||||
Context, IntoElement, Modifiers, ParentElement as _, Render, ScrollDelta, ScrollEvent,
|
||||
ScrollHandle, SharedString, Styled as _, TestAppContext, VisualTestContext, Window, div, point,
|
||||
px,
|
||||
Context, InteractiveElement as _, IntoElement, Modifiers, ParentElement as _, Render,
|
||||
ScrollDelta, ScrollEvent, ScrollHandle, SharedString, Styled as _, TestAppContext,
|
||||
VisualTestContext, Window, div, point, px,
|
||||
};
|
||||
use gpui_native_core::scroll_area::ScrollArea;
|
||||
use gpui_native_theme::Theme;
|
||||
@@ -116,6 +116,53 @@ impl Render for GalleryShell {
|
||||
}
|
||||
}
|
||||
|
||||
/// A window-filling scroll area with `p_4`, over one full-width row.
|
||||
struct PaddedShell {
|
||||
scroll: ScrollHandle,
|
||||
}
|
||||
|
||||
impl Render for PaddedShell {
|
||||
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
||||
div()
|
||||
.size_full()
|
||||
.debug_selector(|| "padded-root".to_string())
|
||||
.child(
|
||||
ScrollArea::new("padded-scroll")
|
||||
.handle(self.scroll.clone())
|
||||
.size_full()
|
||||
.p_4()
|
||||
.child(
|
||||
div()
|
||||
.debug_selector(|| "padded-row".to_string())
|
||||
.h(px(20.))
|
||||
.w_full(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn padding_insets_the_content_exactly_once() {
|
||||
let mut cx = TestAppContext::single();
|
||||
cx.update(Theme::init);
|
||||
let handle = ScrollHandle::new();
|
||||
let handle_for_view = handle.clone();
|
||||
let window = cx.add_window(move |_, _| PaddedShell {
|
||||
scroll: handle_for_view,
|
||||
});
|
||||
let mut visual = VisualTestContext::from_window(window.into(), &cx);
|
||||
visual.update(|window, cx| {
|
||||
window.draw(cx).clear(cx);
|
||||
});
|
||||
|
||||
let root = visual.debug_bounds("padded-root").expect("root rendered");
|
||||
let row = visual.debug_bounds("padded-row").expect("row rendered");
|
||||
// p_4 = 16px: the padding reaches only the wrapper, so the content is
|
||||
// inset once, not once per wrapper and scroll pane.
|
||||
assert_eq!(row.origin.x - root.origin.x, px(16.));
|
||||
assert_eq!(root.size.width - row.size.width, px(32.));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_gallery_style_window_fill_scroll_area_scrolls_tall_flex_content() {
|
||||
let mut cx = TestAppContext::single();
|
||||
|
||||
Reference in New Issue
Block a user