Return more info from ScrollArea::show (#1166)

This commit is contained in:
Emil Ernerfeldt 2022-01-26 22:19:32 +01:00 committed by GitHub
parent 1134258441
commit 3333d63b91
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 72 additions and 34 deletions

View file

@ -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)). * 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)). * `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)). * 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 ([#1166](https://github.com/emilk/egui/pull/1166)).
### Fixed 🐛 ### Fixed 🐛
* Context menu now respects the theme ([#1043](https://github.com/emilk/egui/pull/1043)) * Context menu now respects the theme ([#1043](https://github.com/emilk/egui/pull/1043))

View file

@ -196,6 +196,7 @@ fn combo_box_dyn<'c, R>(
ScrollArea::vertical() ScrollArea::vertical()
.max_height(ui.spacing().combo_height) .max_height(ui.spacing().combo_height)
.show(ui, menu_contents) .show(ui, menu_contents)
.inner
}); });
InnerResponse { InnerResponse {

View file

@ -10,9 +10,9 @@ use crate::*;
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(default))] #[cfg_attr(feature = "serde", serde(default))]
pub(crate) struct State { pub struct State {
/// Positive offset means scrolling down/right /// Positive offset means scrolling down/right
offset: Vec2, pub offset: Vec2,
show_scroll: [bool; 2], show_scroll: [bool; 2],
@ -51,6 +51,20 @@ impl State {
} }
} }
pub struct ScrollAreaOutput<R> {
/// 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`]. /// 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? /// width of the vertical bar, and the height of the horizontal bar?
current_bar_use: Vec2, current_bar_use: Vec2,
always_show_scroll: bool, always_show_scroll: bool,
/// Where on the screen the content is (excludes scroll bars).
inner_rect: Rect, inner_rect: Rect,
content_ui: Ui, content_ui: Ui,
/// Relative coordinates: the offset and size of the view of the inner 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. /// Show the `ScrollArea`, and add the contents to the viewport.
/// ///
/// If the inner area can be very long, consider using [`Self::show_rows`] instead. /// If the inner area can be very long, consider using [`Self::show_rows`] instead.
pub fn show<R>(self, ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> R { pub fn show<R>(
self,
ui: &mut Ui,
add_contents: impl FnOnce(&mut Ui) -> R,
) -> ScrollAreaOutput<R> {
self.show_viewport_dyn(ui, Box::new(|ui, _viewport| add_contents(ui))) self.show_viewport_dyn(ui, Box::new(|ui, _viewport| add_contents(ui)))
} }
@ -391,7 +410,7 @@ impl ScrollArea {
row_height_sans_spacing: f32, row_height_sans_spacing: f32,
total_rows: usize, total_rows: usize,
add_contents: impl FnOnce(&mut Ui, std::ops::Range<usize>) -> R, add_contents: impl FnOnce(&mut Ui, std::ops::Range<usize>) -> R,
) -> R { ) -> ScrollAreaOutput<R> {
let spacing = ui.spacing().item_spacing; let spacing = ui.spacing().item_spacing;
let row_height_with_spacing = row_height_sans_spacing + spacing.y; let row_height_with_spacing = row_height_sans_spacing + spacing.y;
self.show_viewport(ui, |ui, viewport| { 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. /// `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). /// So if the passed rect has min = zero, then show the top left content (the user has not scrolled).
pub fn show_viewport<R>(self, ui: &mut Ui, add_contents: impl FnOnce(&mut Ui, Rect) -> R) -> R { pub fn show_viewport<R>(
self,
ui: &mut Ui,
add_contents: impl FnOnce(&mut Ui, Rect) -> R,
) -> ScrollAreaOutput<R> {
self.show_viewport_dyn(ui, Box::new(add_contents)) self.show_viewport_dyn(ui, Box::new(add_contents))
} }
@ -428,16 +451,23 @@ impl ScrollArea {
self, self,
ui: &mut Ui, ui: &mut Ui,
add_contents: Box<dyn FnOnce(&mut Ui, Rect) -> R + 'c>, add_contents: Box<dyn FnOnce(&mut Ui, Rect) -> R + 'c>,
) -> R { ) -> ScrollAreaOutput<R> {
let mut prepared = self.begin(ui); let mut prepared = self.begin(ui);
let ret = add_contents(&mut prepared.content_ui, prepared.viewport); let id = prepared.id;
prepared.end(ui); let inner_rect = prepared.inner_rect;
ret let inner = add_contents(&mut prepared.content_ui, prepared.viewport);
let state = prepared.end(ui);
ScrollAreaOutput {
inner,
id,
state,
inner_rect,
}
} }
} }
impl Prepared { impl Prepared {
fn end(self, ui: &mut Ui) { fn end(self, ui: &mut Ui) -> State {
let Prepared { let Prepared {
id, id,
mut state, mut state,
@ -747,6 +777,8 @@ impl Prepared {
state.show_scroll = show_scroll_this_frame; state.show_scroll = show_scroll_this_frame;
state.store(ui.ctx(), id); state.store(ui.ctx(), id);
state
} }
} }

View file

@ -354,7 +354,7 @@ impl<'open> Window<'open> {
} }
if scroll.has_any_bar() { if scroll.has_any_bar() {
scroll.show(ui, add_contents) scroll.show(ui, add_contents).inner
} else { } else {
add_contents(ui) add_contents(ui)
} }

View file

@ -586,7 +586,9 @@ impl std::ops::BitOrAssign for Response {
/// ``` /// ```
#[derive(Debug)] #[derive(Debug)]
pub struct InnerResponse<R> { pub struct InnerResponse<R> {
/// What the user closure returned.
pub inner: R, pub inner: R,
/// The response of the area.
pub response: Response, pub response: Response,
} }

View file

@ -210,32 +210,34 @@ impl super::View for ScrollTo {
} }
ui.separator(); ui.separator();
let (current_scroll, max_scroll) = scroll_area.show(ui, |ui| { let (current_scroll, max_scroll) = scroll_area
if scroll_top { .show(ui, |ui| {
ui.scroll_to_cursor(Align::TOP); 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));
}
} }
}); 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 { if scroll_bottom {
ui.scroll_to_cursor(Align::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 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; let max_scroll = ui.min_rect().height() - ui.clip_rect().height() + 2.0 * margin;
(current_scroll, max_scroll) (current_scroll, max_scroll)
}); })
.inner;
ui.separator(); ui.separator();
ui.label(format!( ui.label(format!(