diff --git a/crates/egui/src/containers/scroll_area.rs b/crates/egui/src/containers/scroll_area.rs index 4d952d315..5ed4c31f3 100644 --- a/crates/egui/src/containers/scroll_area.rs +++ b/crates/egui/src/containers/scroll_area.rs @@ -1,8 +1,11 @@ +//! See [`ScrollArea`] for docs. + #![allow(clippy::needless_range_loop)] use std::ops::{Add, AddAssign, BitOr, BitOrAssign}; use emath::GuiRounding as _; +use epaint::Margin; use crate::{ Context, CursorIcon, Id, NumExt as _, Pos2, Rangef, Rect, Response, Sense, Ui, UiBuilder, @@ -258,7 +261,7 @@ impl AddAssign for ScrollSource { /// ### Coordinate system /// * content: size of contents (generally large; that's why we want scroll bars) /// * outer: size of scroll area including scroll bar(s) -/// * inner: excluding scroll bar(s). The area we clip the contents to. +/// * inner: excluding scroll bar(s). The area we clip the contents to. Includes `content_margin`. /// /// If the floating scroll bars settings is turned on then `inner == outer`. /// @@ -294,6 +297,8 @@ pub struct ScrollArea { scroll_source: ScrollSource, wheel_scroll_multiplier: Vec2, + content_margin: Option, + /// If true for vertical or horizontal the scroll wheel will stick to the /// end position until user manually changes position. It will become true /// again once scroll handle makes contact with end. @@ -346,6 +351,7 @@ impl ScrollArea { on_drag_cursor: None, scroll_source: ScrollSource::default(), wheel_scroll_multiplier: Vec2::splat(1.0), + content_margin: None, stick_to_end: Vec2b::FALSE, animated: true, } @@ -593,6 +599,18 @@ impl ScrollArea { self.direction_enabled[0] || self.direction_enabled[1] } + /// Extra margin added around the contents. + /// + /// The scroll bars will be either on top of this margin, or outside of it, + /// depending on the value of [`crate::style::ScrollStyle::floating`]. + /// + /// Default: [`crate::style::ScrollStyle::content_margin`]. + #[inline] + pub fn content_margin(mut self, margin: impl Into) -> Self { + self.content_margin = Some(margin.into()); + self + } + /// The scroll handle will stick to the rightmost position even while the content size /// changes dynamically. This can be useful to simulate text scrollers coming in from right /// hand side. The scroll handle remains stuck until user manually changes position. Once "unstuck" @@ -644,7 +662,7 @@ struct Prepared { scroll_bar_visibility: ScrollBarVisibility, scroll_bar_rect: Option, - /// Where on the screen the content is (excludes scroll bars). + /// Where on the screen the content is (excludes scroll bars; includes `content_margin`). inner_rect: Rect, content_ui: Ui, @@ -683,6 +701,7 @@ impl ScrollArea { on_drag_cursor, scroll_source, wheel_scroll_multiplier, + content_margin: _, // Used elsewhere stick_to_end, animated, } = self; @@ -983,10 +1002,21 @@ impl ScrollArea { ui: &mut Ui, add_contents: Box R + 'c>, ) -> ScrollAreaOutput { + let margin = self + .content_margin + .unwrap_or_else(|| ui.spacing().scroll.content_margin); + let mut prepared = self.begin(ui); let id = prepared.id; let inner_rect = prepared.inner_rect; - let inner = add_contents(&mut prepared.content_ui, prepared.viewport); + + let inner = crate::Frame::NONE + .inner_margin(margin) + .show(&mut prepared.content_ui, |ui| { + add_contents(ui, prepared.viewport) + }) + .inner; + let (content_size, state) = prepared.end(ui); ScrollAreaOutput { inner, diff --git a/crates/egui/src/style.rs b/crates/egui/src/style.rs index 9982c05bb..b77536002 100644 --- a/crates/egui/src/style.rs +++ b/crates/egui/src/style.rs @@ -508,6 +508,12 @@ pub struct ScrollStyle { /// it more promiment. pub floating: bool, + /// Extra margin added around the contents of a [`crate::ScrollArea`]. + /// + /// The scroll bars will be either on top of this margin, or outside of it, + /// depending on the value of [`Self::floating`]. + pub content_margin: Margin, + /// The width of the scroll bars at it largest. pub bar_width: f32, @@ -591,6 +597,7 @@ impl ScrollStyle { pub fn solid() -> Self { Self { floating: false, + content_margin: Margin::ZERO, bar_width: 6.0, handle_min_length: 12.0, bar_inner_margin: 4.0, @@ -672,6 +679,9 @@ impl ScrollStyle { pub fn details_ui(&mut self, ui: &mut Ui) { let Self { floating, + + content_margin, + bar_width, handle_min_length, bar_inner_margin, @@ -695,6 +705,11 @@ impl ScrollStyle { ui.selectable_value(floating, true, "Floating"); }); + ui.horizontal(|ui| { + ui.label("Content margin:"); + content_margin.ui(ui); + }); + ui.horizontal(|ui| { ui.add(DragValue::new(bar_width).range(0.0..=32.0)); ui.label("Full bar width"); @@ -1824,6 +1839,10 @@ impl Spacing { ui.add(window_margin); ui.end_row(); + ui.label("ScrollArea margin"); + scroll.content_margin.ui(ui); + ui.end_row(); + ui.label("Menu margin"); ui.add(menu_margin); ui.end_row();