From c6e84c21cc30e13ee6729abbf7147076594f87fc Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Wed, 26 Jan 2022 22:03:47 +0100 Subject: [PATCH] Return more info from ScrollArea::show --- CHANGELOG.md | 3 +- egui/src/containers/combo_box.rs | 1 + egui/src/containers/scroll_area.rs | 52 +++++++++++++++++++----- egui/src/containers/window.rs | 2 +- egui/src/response.rs | 2 + egui_demo_lib/src/apps/demo/scrolling.rs | 46 +++++++++++---------- 6 files changed, 72 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69ad2f99ba44..53b31d3c10a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,7 +33,8 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui_w * Replaced `Style::body_text_style` with more generic `Style::text_styles` ([#1154](https://github.com/emilk/egui/pull/1154)). * `TextStyle` is no longer `Copy` ([#1154](https://github.com/emilk/egui/pull/1154)). * Replaced `TextEdit::text_style` with `TextEdit::font` ([#1154](https://github.com/emilk/egui/pull/1154)). -* `Plot::highlight` now takes a `bool` argument ([#1159](https://github.com/emilk/egui/pull/1159)), +* `Plot::highlight` now takes a `bool` argument ([#1159](https://github.com/emilk/egui/pull/1159)). +* `ScrollArea::show` now returns a `ScrollAreaOutput`, so you might need to add `.inner` after the call to it. ### Fixed 🐛 * Context menu now respects the theme ([#1043](https://github.com/emilk/egui/pull/1043)) diff --git a/egui/src/containers/combo_box.rs b/egui/src/containers/combo_box.rs index 71c4f06358a6..adab23b3f639 100644 --- a/egui/src/containers/combo_box.rs +++ b/egui/src/containers/combo_box.rs @@ -196,6 +196,7 @@ fn combo_box_dyn<'c, R>( ScrollArea::vertical() .max_height(ui.spacing().combo_height) .show(ui, menu_contents) + .inner }); InnerResponse { diff --git a/egui/src/containers/scroll_area.rs b/egui/src/containers/scroll_area.rs index 83c7e3b4254d..78d6156bf14c 100644 --- a/egui/src/containers/scroll_area.rs +++ b/egui/src/containers/scroll_area.rs @@ -10,9 +10,9 @@ use crate::*; #[derive(Clone, Copy, Debug)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "serde", serde(default))] -pub(crate) struct State { +pub struct State { /// Positive offset means scrolling down/right - offset: Vec2, + pub offset: Vec2, show_scroll: [bool; 2], @@ -51,6 +51,20 @@ impl State { } } +pub struct ScrollAreaOutput { + /// What the user closure returned. + pub inner: R, + + /// `Id` of the `ScrollArea`. + pub id: Id, + + /// The current state of the scroll area. + pub state: State, + + /// Where on the screen the content is (excludes scroll bars). + pub inner_rect: Rect, +} + /// Add vertical and/or horizontal scrolling to a contained [`Ui`]. /// /// ``` @@ -258,6 +272,7 @@ struct Prepared { /// width of the vertical bar, and the height of the horizontal bar? current_bar_use: Vec2, always_show_scroll: bool, + /// Where on the screen the content is (excludes scroll bars). inner_rect: Rect, content_ui: Ui, /// Relative coordinates: the offset and size of the view of the inner UI. @@ -365,7 +380,11 @@ impl ScrollArea { /// Show the `ScrollArea`, and add the contents to the viewport. /// /// If the inner area can be very long, consider using [`Self::show_rows`] instead. - pub fn show(self, ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> R { + pub fn show( + self, + ui: &mut Ui, + add_contents: impl FnOnce(&mut Ui) -> R, + ) -> ScrollAreaOutput { self.show_viewport_dyn(ui, Box::new(|ui, _viewport| add_contents(ui))) } @@ -391,7 +410,7 @@ impl ScrollArea { row_height_sans_spacing: f32, total_rows: usize, add_contents: impl FnOnce(&mut Ui, std::ops::Range) -> R, - ) -> R { + ) -> ScrollAreaOutput { let spacing = ui.spacing().item_spacing; let row_height_with_spacing = row_height_sans_spacing + spacing.y; self.show_viewport(ui, |ui, viewport| { @@ -420,7 +439,11 @@ impl ScrollArea { /// /// `add_contents` is past the viewport, which is the relative view of the content. /// So if the passed rect has min = zero, then show the top left content (the user has not scrolled). - pub fn show_viewport(self, ui: &mut Ui, add_contents: impl FnOnce(&mut Ui, Rect) -> R) -> R { + pub fn show_viewport( + self, + ui: &mut Ui, + add_contents: impl FnOnce(&mut Ui, Rect) -> R, + ) -> ScrollAreaOutput { self.show_viewport_dyn(ui, Box::new(add_contents)) } @@ -428,16 +451,23 @@ impl ScrollArea { self, ui: &mut Ui, add_contents: Box R + 'c>, - ) -> R { + ) -> ScrollAreaOutput { let mut prepared = self.begin(ui); - let ret = add_contents(&mut prepared.content_ui, prepared.viewport); - prepared.end(ui); - ret + let id = prepared.id; + let inner_rect = prepared.inner_rect; + let inner = add_contents(&mut prepared.content_ui, prepared.viewport); + let state = prepared.end(ui); + ScrollAreaOutput { + inner, + id, + state, + inner_rect, + } } } impl Prepared { - fn end(self, ui: &mut Ui) { + fn end(self, ui: &mut Ui) -> State { let Prepared { id, mut state, @@ -747,6 +777,8 @@ impl Prepared { state.show_scroll = show_scroll_this_frame; state.store(ui.ctx(), id); + + state } } diff --git a/egui/src/containers/window.rs b/egui/src/containers/window.rs index c6d2cae87e26..62a620f4e279 100644 --- a/egui/src/containers/window.rs +++ b/egui/src/containers/window.rs @@ -354,7 +354,7 @@ impl<'open> Window<'open> { } if scroll.has_any_bar() { - scroll.show(ui, add_contents) + scroll.show(ui, add_contents).inner } else { add_contents(ui) } diff --git a/egui/src/response.rs b/egui/src/response.rs index 2d20d1728bb7..e20c87b33137 100644 --- a/egui/src/response.rs +++ b/egui/src/response.rs @@ -586,7 +586,9 @@ impl std::ops::BitOrAssign for Response { /// ``` #[derive(Debug)] pub struct InnerResponse { + /// What the user closure returned. pub inner: R, + /// The response of the area. pub response: Response, } diff --git a/egui_demo_lib/src/apps/demo/scrolling.rs b/egui_demo_lib/src/apps/demo/scrolling.rs index f2499bd0aaa2..0c01ef11699d 100644 --- a/egui_demo_lib/src/apps/demo/scrolling.rs +++ b/egui_demo_lib/src/apps/demo/scrolling.rs @@ -210,32 +210,34 @@ impl super::View for ScrollTo { } ui.separator(); - let (current_scroll, max_scroll) = scroll_area.show(ui, |ui| { - if scroll_top { - ui.scroll_to_cursor(Align::TOP); - } - ui.vertical(|ui| { - for item in 1..=50 { - if track_item && item == self.track_item { - let response = - ui.colored_label(Color32::YELLOW, format!("This is item {}", item)); - response.scroll_to_me(self.tack_item_align); - } else { - ui.label(format!("This is item {}", item)); - } + let (current_scroll, max_scroll) = scroll_area + .show(ui, |ui| { + if scroll_top { + ui.scroll_to_cursor(Align::TOP); } - }); + ui.vertical(|ui| { + for item in 1..=50 { + if track_item && item == self.track_item { + let response = + ui.colored_label(Color32::YELLOW, format!("This is item {}", item)); + response.scroll_to_me(self.tack_item_align); + } else { + ui.label(format!("This is item {}", item)); + } + } + }); - if scroll_bottom { - ui.scroll_to_cursor(Align::BOTTOM); - } + if scroll_bottom { + ui.scroll_to_cursor(Align::BOTTOM); + } - let margin = ui.visuals().clip_rect_margin; + let margin = ui.visuals().clip_rect_margin; - let current_scroll = ui.clip_rect().top() - ui.min_rect().top() + margin; - let max_scroll = ui.min_rect().height() - ui.clip_rect().height() + 2.0 * margin; - (current_scroll, max_scroll) - }); + let current_scroll = ui.clip_rect().top() - ui.min_rect().top() + margin; + let max_scroll = ui.min_rect().height() - ui.clip_rect().height() + 2.0 * margin; + (current_scroll, max_scroll) + }) + .inner; ui.separator(); ui.label(format!(