Skip to content

Commit 178f3c9

Browse files
authored
Add ScrollArea::content_margin (#7722)
* Part of #5605 * Part of #3385
1 parent f74b7c7 commit 178f3c9

File tree

2 files changed

+52
-3
lines changed

2 files changed

+52
-3
lines changed

crates/egui/src/containers/scroll_area.rs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
//! See [`ScrollArea`] for docs.
2+
13
#![allow(clippy::needless_range_loop)]
24

35
use std::ops::{Add, AddAssign, BitOr, BitOrAssign};
46

57
use emath::GuiRounding as _;
8+
use epaint::Margin;
69

710
use crate::{
811
Context, CursorIcon, Id, NumExt as _, Pos2, Rangef, Rect, Response, Sense, Ui, UiBuilder,
@@ -258,7 +261,7 @@ impl AddAssign for ScrollSource {
258261
/// ### Coordinate system
259262
/// * content: size of contents (generally large; that's why we want scroll bars)
260263
/// * outer: size of scroll area including scroll bar(s)
261-
/// * inner: excluding scroll bar(s). The area we clip the contents to.
264+
/// * inner: excluding scroll bar(s). The area we clip the contents to. Includes `content_margin`.
262265
///
263266
/// If the floating scroll bars settings is turned on then `inner == outer`.
264267
///
@@ -294,6 +297,8 @@ pub struct ScrollArea {
294297
scroll_source: ScrollSource,
295298
wheel_scroll_multiplier: Vec2,
296299

300+
content_margin: Option<Margin>,
301+
297302
/// If true for vertical or horizontal the scroll wheel will stick to the
298303
/// end position until user manually changes position. It will become true
299304
/// again once scroll handle makes contact with end.
@@ -346,6 +351,7 @@ impl ScrollArea {
346351
on_drag_cursor: None,
347352
scroll_source: ScrollSource::default(),
348353
wheel_scroll_multiplier: Vec2::splat(1.0),
354+
content_margin: None,
349355
stick_to_end: Vec2b::FALSE,
350356
animated: true,
351357
}
@@ -593,6 +599,18 @@ impl ScrollArea {
593599
self.direction_enabled[0] || self.direction_enabled[1]
594600
}
595601

602+
/// Extra margin added around the contents.
603+
///
604+
/// The scroll bars will be either on top of this margin, or outside of it,
605+
/// depending on the value of [`crate::style::ScrollStyle::floating`].
606+
///
607+
/// Default: [`crate::style::ScrollStyle::content_margin`].
608+
#[inline]
609+
pub fn content_margin(mut self, margin: impl Into<Margin>) -> Self {
610+
self.content_margin = Some(margin.into());
611+
self
612+
}
613+
596614
/// The scroll handle will stick to the rightmost position even while the content size
597615
/// changes dynamically. This can be useful to simulate text scrollers coming in from right
598616
/// hand side. The scroll handle remains stuck until user manually changes position. Once "unstuck"
@@ -644,7 +662,7 @@ struct Prepared {
644662
scroll_bar_visibility: ScrollBarVisibility,
645663
scroll_bar_rect: Option<Rect>,
646664

647-
/// Where on the screen the content is (excludes scroll bars).
665+
/// Where on the screen the content is (excludes scroll bars; includes `content_margin`).
648666
inner_rect: Rect,
649667

650668
content_ui: Ui,
@@ -683,6 +701,7 @@ impl ScrollArea {
683701
on_drag_cursor,
684702
scroll_source,
685703
wheel_scroll_multiplier,
704+
content_margin: _, // Used elsewhere
686705
stick_to_end,
687706
animated,
688707
} = self;
@@ -983,10 +1002,21 @@ impl ScrollArea {
9831002
ui: &mut Ui,
9841003
add_contents: Box<dyn FnOnce(&mut Ui, Rect) -> R + 'c>,
9851004
) -> ScrollAreaOutput<R> {
1005+
let margin = self
1006+
.content_margin
1007+
.unwrap_or_else(|| ui.spacing().scroll.content_margin);
1008+
9861009
let mut prepared = self.begin(ui);
9871010
let id = prepared.id;
9881011
let inner_rect = prepared.inner_rect;
989-
let inner = add_contents(&mut prepared.content_ui, prepared.viewport);
1012+
1013+
let inner = crate::Frame::NONE
1014+
.inner_margin(margin)
1015+
.show(&mut prepared.content_ui, |ui| {
1016+
add_contents(ui, prepared.viewport)
1017+
})
1018+
.inner;
1019+
9901020
let (content_size, state) = prepared.end(ui);
9911021
ScrollAreaOutput {
9921022
inner,

crates/egui/src/style.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,12 @@ pub struct ScrollStyle {
508508
/// it more promiment.
509509
pub floating: bool,
510510

511+
/// Extra margin added around the contents of a [`crate::ScrollArea`].
512+
///
513+
/// The scroll bars will be either on top of this margin, or outside of it,
514+
/// depending on the value of [`Self::floating`].
515+
pub content_margin: Margin,
516+
511517
/// The width of the scroll bars at it largest.
512518
pub bar_width: f32,
513519

@@ -591,6 +597,7 @@ impl ScrollStyle {
591597
pub fn solid() -> Self {
592598
Self {
593599
floating: false,
600+
content_margin: Margin::ZERO,
594601
bar_width: 6.0,
595602
handle_min_length: 12.0,
596603
bar_inner_margin: 4.0,
@@ -672,6 +679,9 @@ impl ScrollStyle {
672679
pub fn details_ui(&mut self, ui: &mut Ui) {
673680
let Self {
674681
floating,
682+
683+
content_margin,
684+
675685
bar_width,
676686
handle_min_length,
677687
bar_inner_margin,
@@ -695,6 +705,11 @@ impl ScrollStyle {
695705
ui.selectable_value(floating, true, "Floating");
696706
});
697707

708+
ui.horizontal(|ui| {
709+
ui.label("Content margin:");
710+
content_margin.ui(ui);
711+
});
712+
698713
ui.horizontal(|ui| {
699714
ui.add(DragValue::new(bar_width).range(0.0..=32.0));
700715
ui.label("Full bar width");
@@ -1824,6 +1839,10 @@ impl Spacing {
18241839
ui.add(window_margin);
18251840
ui.end_row();
18261841

1842+
ui.label("ScrollArea margin");
1843+
scroll.content_margin.ui(ui);
1844+
ui.end_row();
1845+
18271846
ui.label("Menu margin");
18281847
ui.add(menu_margin);
18291848
ui.end_row();

0 commit comments

Comments
 (0)