Show embedded scrollbars only when necessary in scrollable

This commit is contained in:
Héctor Ramón Jiménez 2025-05-03 04:15:18 +02:00
parent 7a81e638a3
commit f78a87c409
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
3 changed files with 100 additions and 50 deletions

View file

@ -426,55 +426,104 @@ where
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
let (right_padding, bottom_padding) = match self.direction {
let mut layout = |right_padding, bottom_padding| {
layout::padded(
limits,
self.width,
self.height,
Padding {
right: right_padding,
bottom: bottom_padding,
..Padding::ZERO
},
|limits| {
let child_limits = layout::Limits::new(
Size::new(limits.min().width, limits.min().height),
Size::new(
if self.direction.horizontal().is_some() {
f32::INFINITY
} else {
limits.max().width
},
if self.direction.vertical().is_some() {
f32::INFINITY
} else {
limits.max().height
},
),
);
self.content.as_widget().layout(
&mut tree.children[0],
renderer,
&child_limits,
)
},
)
};
match self.direction {
Direction::Vertical(Scrollbar {
width,
margin,
spacing: Some(spacing),
..
}) => (width + margin * 2.0 + spacing, 0.0),
Direction::Horizontal(Scrollbar {
})
| Direction::Horizontal(Scrollbar {
width,
margin,
spacing: Some(spacing),
..
}) => (0.0, width + margin * 2.0 + spacing),
_ => (0.0, 0.0),
};
}) => {
let is_vertical =
matches!(self.direction, Direction::Vertical(_));
layout::padded(
limits,
self.width,
self.height,
Padding {
right: right_padding,
bottom: bottom_padding,
..Padding::ZERO
},
|limits| {
let child_limits = layout::Limits::new(
Size::new(limits.min().width, limits.min().height),
Size::new(
if self.direction.horizontal().is_some() {
f32::INFINITY
} else {
limits.max().width
},
if self.direction.vertical().is_some() {
f32::INFINITY
} else {
limits.max().height
},
),
let padding = width + margin * 2.0 + spacing;
let state = tree.state.downcast_mut::<State>();
let status_quo = layout(
if is_vertical && state.is_scrollbar_visible {
padding
} else {
0.0
},
if !is_vertical && state.is_scrollbar_visible {
padding
} else {
0.0
},
);
self.content.as_widget().layout(
&mut tree.children[0],
renderer,
&child_limits,
)
},
)
let is_scrollbar_visible = if is_vertical {
status_quo.children()[0].size().height
> status_quo.size().height
} else {
status_quo.children()[0].size().width
> status_quo.size().width
};
if state.is_scrollbar_visible == is_scrollbar_visible {
status_quo
} else {
log::trace!("Scrollbar status quo has changed");
state.is_scrollbar_visible = is_scrollbar_visible;
layout(
if is_vertical && state.is_scrollbar_visible {
padding
} else {
0.0
},
if !is_vertical && state.is_scrollbar_visible {
padding
} else {
0.0
},
)
}
}
_ => layout(0.0, 0.0),
}
}
fn operate(
@ -1354,6 +1403,7 @@ struct State {
keyboard_modifiers: keyboard::Modifiers,
last_notified: Option<Viewport>,
last_scrolled: Option<Instant>,
is_scrollbar_visible: bool,
}
impl Default for State {
@ -1367,6 +1417,7 @@ impl Default for State {
keyboard_modifiers: keyboard::Modifiers::default(),
last_notified: None,
last_scrolled: None,
is_scrollbar_visible: false,
}
}
}
@ -1626,13 +1677,13 @@ impl Scrollbars {
) -> Self {
let translation = state.translation(direction, bounds, content_bounds);
let show_scrollbar_x = direction.horizontal().filter(|scrollbar| {
scrollbar.spacing.is_some() || content_bounds.width > bounds.width
});
let show_scrollbar_x = direction
.horizontal()
.filter(|_scrollbar| content_bounds.width > bounds.width);
let show_scrollbar_y = direction.vertical().filter(|scrollbar| {
scrollbar.spacing.is_some() || content_bounds.height > bounds.height
});
let show_scrollbar_y = direction
.vertical()
.filter(|_scrollbar| content_bounds.height > bounds.height);
let y_scrollbar = if let Some(vertical) = show_scrollbar_y {
let Scrollbar {