From 9f93f305f63ca934ee0c6e141c2f207f30e9146d Mon Sep 17 00:00:00 2001 From: poffo Date: Fri, 25 Dec 2020 13:57:34 -0300 Subject: [PATCH 01/17] Add scroll_offset to Context, add scroll_to_here() in Ui. --- egui/src/containers/scroll_area.rs | 4 ++++ egui/src/context.rs | 11 +++++++++++ egui/src/ui.rs | 8 ++++++++ 3 files changed, 23 insertions(+) diff --git a/egui/src/containers/scroll_area.rs b/egui/src/containers/scroll_area.rs index 716482c4b7e..ab0fecd4dfc 100644 --- a/egui/src/containers/scroll_area.rs +++ b/egui/src/containers/scroll_area.rs @@ -153,6 +153,10 @@ impl Prepared { let content_size = content_ui.min_size(); + if let Some(offset_y) = content_ui.ctx().frame_state().scroll_offset_y() { + state.offset.y = offset_y; + } + let inner_rect = Rect::from_min_size( inner_rect.min, vec2( diff --git a/egui/src/context.rs b/egui/src/context.rs index e7f941cf9b4..575cb6f43e6 100644 --- a/egui/src/context.rs +++ b/egui/src/context.rs @@ -39,6 +39,7 @@ pub(crate) struct FrameState { /// How much space is used by panels. used_by_panels: Rect, // TODO: move some things from `Memory` to here + scroll_offset: Option } impl Default for FrameState { @@ -47,6 +48,7 @@ impl Default for FrameState { available_rect: Rect::invalid(), unused_rect: Rect::invalid(), used_by_panels: Rect::invalid(), + scroll_offset: None } } } @@ -56,6 +58,7 @@ impl FrameState { self.available_rect = input.screen_rect(); self.unused_rect = input.screen_rect(); self.used_by_panels = Rect::nothing(); + self.scroll_offset = None; } /// How much space is still available after panels has been added. @@ -97,6 +100,14 @@ impl FrameState { self.unused_rect = Rect::nothing(); // Nothing left unused after this self.used_by_panels = self.used_by_panels.union(panel_rect); } + + pub(crate) fn set_scroll_offset_y(&mut self, offset: f32) { + self.scroll_offset = Some(offset); + } + + pub(crate) fn scroll_offset_y(&self) -> Option { + self.scroll_offset + } } // ---------------------------------------------------------------------------- diff --git a/egui/src/ui.rs b/egui/src/ui.rs index be4da4c2173..31a217c6e1e 100644 --- a/egui/src/ui.rs +++ b/egui/src/ui.rs @@ -526,6 +526,14 @@ impl Ui { let clip_rect = self.clip_rect().intersect(rect); // Make sure we don't paint out of bounds Painter::new(self.ctx().clone(), self.layer_id(), clip_rect) } + + pub fn scroll_to_here(&mut self, center_ratio: f32) { + // This is the imgui aproach to set the scroll relative to the position + // 0.0: top of widget / 0.5: midle of widget / 1.0: bottom of widget + assert!(center_ratio >= 0.0 && center_ratio <= 1.0); + let offset = self.min_rect().height() - self.clip_rect().height() * center_ratio; + self.ctx().frame_state().set_scroll_offset_y(offset); + } } /// # Adding widgets From cd11d6d39b152bcee702407ecb8fde00b4bf4615 Mon Sep 17 00:00:00 2001 From: poffo Date: Fri, 25 Dec 2020 13:57:54 -0300 Subject: [PATCH 02/17] Update Scroll Window demo. --- egui/src/demos/demo_window.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/egui/src/demos/demo_window.rs b/egui/src/demos/demo_window.rs index ae129e63358..bf74e0f1ddc 100644 --- a/egui/src/demos/demo_window.rs +++ b/egui/src/demos/demo_window.rs @@ -11,12 +11,14 @@ pub struct DemoWindow { layout: LayoutDemo, tree: Tree, box_painting: BoxPainting, + ratio: f32, } impl Default for DemoWindow { fn default() -> DemoWindow { DemoWindow { num_columns: 2, + ratio: 0.0, widgets: Default::default(), colors: Default::default(), @@ -69,7 +71,28 @@ impl DemoWindow { .default_open(false) .show(ui, |ui| { ScrollArea::from_max_height(200.0).show(ui, |ui| { + let middle = ui.button("Go middle").clicked; + let bottom = ui.button("Go bottom").clicked; + let not_working = ui.button("Go middle (not working)").clicked; + ui.add(Slider::f32(&mut self.ratio, 0.0..=1.0).text("ratio")); ui.label(LOREM_IPSUM_LONG); + ui.label(LOREM_IPSUM_LONG); + if middle { + ui.scroll_to_here(self.ratio); + } + + // FIXME: inside the children Ui, scroll_to_here does not work. + ui.horizontal(|ui| { + if not_working { + ui.scroll_to_here(0.5); + } + }); + ui.label("Middle"); + ui.label(LOREM_IPSUM_LONG); + ui.label(LOREM_IPSUM_LONG); + if bottom { + ui.scroll_to_here(1.0); + } }); }); From cf1ae0bdfed10b1940d9c08055da2b6975ce45ce Mon Sep 17 00:00:00 2001 From: poffo Date: Sat, 26 Dec 2020 20:38:01 -0300 Subject: [PATCH 03/17] Add scroll_target_center_ratio to Context Now the center ratio is used inside the ScrollArea --- egui/src/containers/scroll_area.rs | 13 ++++++++++++- egui/src/context.rs | 29 ++++++++++++++++++++++------- egui/src/ui.rs | 10 +++++----- 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/egui/src/containers/scroll_area.rs b/egui/src/containers/scroll_area.rs index ab0fecd4dfc..a8f625a50b9 100644 --- a/egui/src/containers/scroll_area.rs +++ b/egui/src/containers/scroll_area.rs @@ -153,8 +153,19 @@ impl Prepared { let content_size = content_ui.min_size(); - if let Some(offset_y) = content_ui.ctx().frame_state().scroll_offset_y() { + let scroll_target = content_ui.ctx().frame_state().scroll_target(); + if let Some(scroll_target) = scroll_target { + let center_ratio = content_ui.ctx().frame_state().scroll_target_center_ratio(); + let height_offset = content_ui.clip_rect().height() * center_ratio; + let top = content_ui.min_rect().top(); + let offset_y = scroll_target - top - height_offset; state.offset.y = offset_y; + + // We need to clear/consume the offset + // or else all the ScrollAreas are gonna try to use this offset, + // this way only the innermost will use it. + // TODO: Is this ideal? How to set outer scrolls when inside another? + content_ui.ctx().frame_state().set_scroll_target(None); } let inner_rect = Rect::from_min_size( diff --git a/egui/src/context.rs b/egui/src/context.rs index 575cb6f43e6..93dc77e72e0 100644 --- a/egui/src/context.rs +++ b/egui/src/context.rs @@ -38,8 +38,10 @@ pub(crate) struct FrameState { /// How much space is used by panels. used_by_panels: Rect, + // TODO: Maybe turn this into an struct + scroll_target: Option, + scroll_target_center_ratio: f32, // TODO: move some things from `Memory` to here - scroll_offset: Option } impl Default for FrameState { @@ -48,7 +50,8 @@ impl Default for FrameState { available_rect: Rect::invalid(), unused_rect: Rect::invalid(), used_by_panels: Rect::invalid(), - scroll_offset: None + scroll_target: None, + scroll_target_center_ratio: 0.0, } } } @@ -58,7 +61,8 @@ impl FrameState { self.available_rect = input.screen_rect(); self.unused_rect = input.screen_rect(); self.used_by_panels = Rect::nothing(); - self.scroll_offset = None; + self.scroll_target = None; + self.scroll_target_center_ratio = 0.0; } /// How much space is still available after panels has been added. @@ -101,12 +105,23 @@ impl FrameState { self.used_by_panels = self.used_by_panels.union(panel_rect); } - pub(crate) fn set_scroll_offset_y(&mut self, offset: f32) { - self.scroll_offset = Some(offset); + pub(crate) fn set_scroll_target(&mut self, scroll_target: Option) { + self.scroll_target = scroll_target; } - pub(crate) fn scroll_offset_y(&self) -> Option { - self.scroll_offset + pub(crate) fn scroll_target(&self) -> Option { + self.scroll_target + } + + pub(crate) fn set_scroll_target_center_ratio(&mut self, center_ratio: f32) { + // This is the imgui aproach to set the scroll relative to the position + // 0.0: top of widget / 0.5: midle of widget / 1.0: bottom of widget + assert!(center_ratio >= 0.0 && center_ratio <= 1.0); + self.scroll_target_center_ratio = center_ratio; + } + + pub(crate) fn scroll_target_center_ratio(&self) -> f32 { + self.scroll_target_center_ratio } } diff --git a/egui/src/ui.rs b/egui/src/ui.rs index 31a217c6e1e..3f5a868e9e8 100644 --- a/egui/src/ui.rs +++ b/egui/src/ui.rs @@ -528,11 +528,11 @@ impl Ui { } pub fn scroll_to_here(&mut self, center_ratio: f32) { - // This is the imgui aproach to set the scroll relative to the position - // 0.0: top of widget / 0.5: midle of widget / 1.0: bottom of widget - assert!(center_ratio >= 0.0 && center_ratio <= 1.0); - let offset = self.min_rect().height() - self.clip_rect().height() * center_ratio; - self.ctx().frame_state().set_scroll_offset_y(offset); + let scroll_target = self.min_rect().bottom(); + + let mut frame_state = self.ctx().frame_state(); + frame_state.set_scroll_target(Some(scroll_target)); + frame_state.set_scroll_target_center_ratio(center_ratio); } } From 76d6eaccaa7e33333c37199ac0659bd43ff2ab72 Mon Sep 17 00:00:00 2001 From: poffo Date: Sat, 26 Dec 2020 20:38:32 -0300 Subject: [PATCH 04/17] Add scroll_to_me method to Ui --- egui/src/types.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/egui/src/types.rs b/egui/src/types.rs index a7ed462fcdc..da06764775d 100644 --- a/egui/src/types.rs +++ b/egui/src/types.rs @@ -1,4 +1,4 @@ -use crate::{math::Rect, CtxRef, Ui}; +use crate::{lerp, math::Rect, CtxRef, Ui}; // ---------------------------------------------------------------------------- @@ -135,6 +135,14 @@ impl Response { pub fn tooltip_text(self, text: impl Into) -> Self { self.on_hover_text(text) } + + pub fn scroll_to_me(&self, center_ratio: f32) { + let scroll_target = lerp(self.rect.y_range(), center_ratio); + + let mut frame_state = self.ctx.frame_state(); + frame_state.set_scroll_target(Some(scroll_target)); + frame_state.set_scroll_target_center_ratio(center_ratio); + } } impl Response { From d9d1066cc9ff8c90fe5a0da178a7603d89cc8018 Mon Sep 17 00:00:00 2001 From: poffo Date: Sat, 26 Dec 2020 20:39:04 -0300 Subject: [PATCH 05/17] Update scroll window demo --- egui/src/demos/demo_window.rs | 37 ++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/egui/src/demos/demo_window.rs b/egui/src/demos/demo_window.rs index bf74e0f1ddc..087db270c4c 100644 --- a/egui/src/demos/demo_window.rs +++ b/egui/src/demos/demo_window.rs @@ -11,14 +11,14 @@ pub struct DemoWindow { layout: LayoutDemo, tree: Tree, box_painting: BoxPainting, - ratio: f32, + center_ratio: f32, } impl Default for DemoWindow { fn default() -> DemoWindow { DemoWindow { num_columns: 2, - ratio: 0.0, + center_ratio: 0.0, widgets: Default::default(), colors: Default::default(), @@ -71,23 +71,38 @@ impl DemoWindow { .default_open(false) .show(ui, |ui| { ScrollArea::from_max_height(200.0).show(ui, |ui| { - let middle = ui.button("Go middle").clicked; let bottom = ui.button("Go bottom").clicked; - let not_working = ui.button("Go middle (not working)").clicked; - ui.add(Slider::f32(&mut self.ratio, 0.0..=1.0).text("ratio")); + let scroll_to_ui = ui.button("Scroll to Ui").clicked; + + let somewhere = ui.button("Scroll to Somewhere").clicked; + + ui.add(Slider::f32(&mut self.center_ratio, 0.0..=1.0).text("ratio")); ui.label(LOREM_IPSUM_LONG); ui.label(LOREM_IPSUM_LONG); - if middle { - ui.scroll_to_here(self.ratio); + + let (_, response) = ui.vertical(|ui| { + ui.monospace("Ui Start ========="); + ui.monospace(" ========="); + ui.monospace(" ========="); + ui.monospace(" ========="); + ui.monospace("Ui Middle ========="); + ui.monospace(" ========="); + ui.monospace(" ========="); + ui.monospace(" ========="); + ui.monospace("Ui End ========="); + }); + + if scroll_to_ui { + response.scroll_to_me(self.center_ratio); } - // FIXME: inside the children Ui, scroll_to_here does not work. ui.horizontal(|ui| { - if not_working { - ui.scroll_to_here(0.5); + if somewhere { + ui.scroll_to_here(self.center_ratio); } + ui.label("Somewhere"); }); - ui.label("Middle"); + ui.label(LOREM_IPSUM_LONG); ui.label(LOREM_IPSUM_LONG); if bottom { From e9f1e9f591525528e6a1c6b7e8bc69cd49a9842d Mon Sep 17 00:00:00 2001 From: poffo Date: Sat, 26 Dec 2020 23:15:55 -0300 Subject: [PATCH 06/17] Add initial offset when creating ScrollArea. --- egui/src/containers/scroll_area.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/egui/src/containers/scroll_area.rs b/egui/src/containers/scroll_area.rs index a8f625a50b9..f8043f33c30 100644 --- a/egui/src/containers/scroll_area.rs +++ b/egui/src/containers/scroll_area.rs @@ -31,6 +31,7 @@ pub struct ScrollArea { max_height: f32, always_show_scroll: bool, id_source: Option, + offset: Option, } impl ScrollArea { @@ -45,6 +46,7 @@ impl ScrollArea { max_height, always_show_scroll: false, id_source: None, + offset: None, } } @@ -60,6 +62,11 @@ impl ScrollArea { self.id_source = Some(Id::new(id_source)); self } + + pub fn scroll_offset(mut self, offset: Vec2) -> Self { + self.offset = Some(offset); + self + } } struct Prepared { @@ -77,19 +84,24 @@ impl ScrollArea { max_height, always_show_scroll, id_source, + offset, } = self; let ctx = ui.ctx().clone(); let id_source = id_source.unwrap_or_else(|| Id::new("scroll_area")); let id = ui.make_persistent_id(id_source); - let state = ctx + let mut state = ctx .memory() .scroll_areas .get(&id) .cloned() .unwrap_or_default(); + if let Some(offset) = offset { + state.offset = offset; + } + // 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. From 2c3c6e501db095c84f44bb606430f471b6eaf1a3 Mon Sep 17 00:00:00 2001 From: poffo Date: Sat, 26 Dec 2020 23:16:12 -0300 Subject: [PATCH 07/17] Update Scroll demo --- egui/src/demos/demo_window.rs | 44 ++-------------------- egui/src/demos/mod.rs | 3 +- egui/src/demos/scrolls.rs | 71 +++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 42 deletions(-) create mode 100644 egui/src/demos/scrolls.rs diff --git a/egui/src/demos/demo_window.rs b/egui/src/demos/demo_window.rs index 087db270c4c..35f8cab50f5 100644 --- a/egui/src/demos/demo_window.rs +++ b/egui/src/demos/demo_window.rs @@ -7,19 +7,19 @@ pub struct DemoWindow { num_columns: usize, widgets: Widgets, + scrolls: Scrolls, colors: ColorWidgets, layout: LayoutDemo, tree: Tree, box_painting: BoxPainting, - center_ratio: f32, } impl Default for DemoWindow { fn default() -> DemoWindow { DemoWindow { num_columns: 2, - center_ratio: 0.0, + scrolls: Default::default(), widgets: Default::default(), colors: Default::default(), layout: Default::default(), @@ -70,45 +70,7 @@ impl DemoWindow { CollapsingHeader::new("Scroll area") .default_open(false) .show(ui, |ui| { - ScrollArea::from_max_height(200.0).show(ui, |ui| { - let bottom = ui.button("Go bottom").clicked; - let scroll_to_ui = ui.button("Scroll to Ui").clicked; - - let somewhere = ui.button("Scroll to Somewhere").clicked; - - ui.add(Slider::f32(&mut self.center_ratio, 0.0..=1.0).text("ratio")); - ui.label(LOREM_IPSUM_LONG); - ui.label(LOREM_IPSUM_LONG); - - let (_, response) = ui.vertical(|ui| { - ui.monospace("Ui Start ========="); - ui.monospace(" ========="); - ui.monospace(" ========="); - ui.monospace(" ========="); - ui.monospace("Ui Middle ========="); - ui.monospace(" ========="); - ui.monospace(" ========="); - ui.monospace(" ========="); - ui.monospace("Ui End ========="); - }); - - if scroll_to_ui { - response.scroll_to_me(self.center_ratio); - } - - ui.horizontal(|ui| { - if somewhere { - ui.scroll_to_here(self.center_ratio); - } - ui.label("Somewhere"); - }); - - ui.label(LOREM_IPSUM_LONG); - ui.label(LOREM_IPSUM_LONG); - if bottom { - ui.scroll_to_here(1.0); - } - }); + self.scrolls.ui(ui); }); CollapsingHeader::new("Resize") diff --git a/egui/src/demos/mod.rs b/egui/src/demos/mod.rs index 442ec7bc345..5471c275d84 100644 --- a/egui/src/demos/mod.rs +++ b/egui/src/demos/mod.rs @@ -16,11 +16,12 @@ mod sliders; mod tests; pub mod toggle_switch; mod widgets; +mod scrolls; pub use { app::*, color_test::ColorTest, dancing_strings::DancingStrings, demo_window::DemoWindow, demo_windows::*, drag_and_drop::*, font_book::FontBook, fractal_clock::FractalClock, - painting::Painting, sliders::Sliders, tests::Tests, widgets::Widgets, + painting::Painting, sliders::Sliders, tests::Tests, widgets::Widgets, scrolls::Scrolls }; pub const LOREM_IPSUM: &str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; diff --git a/egui/src/demos/scrolls.rs b/egui/src/demos/scrolls.rs new file mode 100644 index 00000000000..4f15526c34d --- /dev/null +++ b/egui/src/demos/scrolls.rs @@ -0,0 +1,71 @@ +use crate::{color::*, *}; + +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", serde(default))] +pub struct Scrolls { + track_item: usize, + tracking: bool, + offset: f32, + center_ratio: f32, +} + +impl Default for Scrolls { + fn default() -> Self { + Self { + track_item: 25, + tracking: true, + offset: 0.0, + center_ratio: 0.3, + } + } +} + +impl Scrolls { + pub fn ui(&mut self, ui: &mut Ui) { + ui.horizontal(|ui| { + ui.checkbox(&mut self.tracking, "Track"); + ui.add(Slider::usize(&mut self.track_item, 1..=50).text("Track Item")); + }); + ui.add(Slider::f32(&mut self.center_ratio, 0.0..=1.0).text("Custom scroll center ratio")); + let (scroll_offset, _) = ui.horizontal(|ui| { + let scroll_offset = ui.small_button("Scroll Offset").clicked; + ui.add(DragValue::f32(&mut self.offset).speed(1.0).suffix("px")); + scroll_offset + }); + + let titles = ["Top", "25%", "Middle", "75%", "Bottom", "Custom"]; + ui.columns(6, |cols| { + for (i, col) in cols.iter_mut().enumerate() { + col.label(titles[i]); + let mut scroll_area = ScrollArea::from_max_height(200.0).id_source(i); + if scroll_offset { + self.tracking = false; + scroll_area = scroll_area.scroll_offset(Vec2::new(0.0, self.offset)); + } + + let (current_scroll, max_scroll) = scroll_area.show(col, |ui| { + ui.vertical(|ui| { + for item in 1..=50 { + if self.tracking && item == self.track_item { + let response = ui.colored_label(YELLOW, format!("Item {}", item)); + let scroll_center_ratio = if i == 5 { + self.center_ratio + } else { + 0.25 * i as f32 + }; + response.scroll_to_me(scroll_center_ratio); + } else { + ui.label(format!("Item {}", item)); + } + } + }); + ( + ui.clip_rect().top() - ui.min_rect().top(), + ui.min_rect().height() - ui.clip_rect().height(), + ) + }); + col.label(format!("{:.0}/{:.0}", current_scroll, max_scroll)); + } + }); + } +} From 5816dbfa61cdce027e02a4808f0f5cbc1c6ccef2 Mon Sep 17 00:00:00 2001 From: poffo Date: Sat, 26 Dec 2020 23:42:16 -0300 Subject: [PATCH 08/17] Consider clip_rect_margin when calculating current_scroll and scroll_max in the demo. --- egui/src/demos/scrolls.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/egui/src/demos/scrolls.rs b/egui/src/demos/scrolls.rs index 4f15526c34d..5055476ca63 100644 --- a/egui/src/demos/scrolls.rs +++ b/egui/src/demos/scrolls.rs @@ -59,9 +59,11 @@ impl Scrolls { } } }); + + let margin = ui.style().visuals.clip_rect_margin; ( - ui.clip_rect().top() - ui.min_rect().top(), - ui.min_rect().height() - ui.clip_rect().height(), + ui.clip_rect().top() - ui.min_rect().top() + margin, + ui.min_rect().height() - ui.clip_rect().height() + 2.0 * margin, ) }); col.label(format!("{:.0}/{:.0}", current_scroll, max_scroll)); From 5c346e06bbe274c14e162d539f216657de1a70d0 Mon Sep 17 00:00:00 2001 From: poffo Date: Sun, 27 Dec 2020 00:38:51 -0300 Subject: [PATCH 09/17] Rename center_ratio to center_factor, add documentation to scroll methods. --- egui/src/containers/scroll_area.rs | 6 +++++- egui/src/context.rs | 14 +++++++------- egui/src/demos/scrolls.rs | 14 +++++++------- egui/src/types.rs | 9 ++++++--- egui/src/ui.rs | 7 +++++-- 5 files changed, 30 insertions(+), 20 deletions(-) diff --git a/egui/src/containers/scroll_area.rs b/egui/src/containers/scroll_area.rs index f8043f33c30..48b0aa358d8 100644 --- a/egui/src/containers/scroll_area.rs +++ b/egui/src/containers/scroll_area.rs @@ -63,6 +63,10 @@ impl ScrollArea { self } + /// Set the position where the scroll should be. + /// See also: + /// [scroll_to_here](crate::ui::Ui::scroll_to_here) + /// [scroll_to_me](crate::types::Response::scroll_to_me) pub fn scroll_offset(mut self, offset: Vec2) -> Self { self.offset = Some(offset); self @@ -176,7 +180,7 @@ impl Prepared { // We need to clear/consume the offset // or else all the ScrollAreas are gonna try to use this offset, // this way only the innermost will use it. - // TODO: Is this ideal? How to set outer scrolls when inside another? + // TODO: How to set outer ScrollArea when inside another? content_ui.ctx().frame_state().set_scroll_target(None); } diff --git a/egui/src/context.rs b/egui/src/context.rs index 93dc77e72e0..79ad6af81dc 100644 --- a/egui/src/context.rs +++ b/egui/src/context.rs @@ -40,7 +40,7 @@ pub(crate) struct FrameState { used_by_panels: Rect, // TODO: Maybe turn this into an struct scroll_target: Option, - scroll_target_center_ratio: f32, + scroll_target_center_factor: f32, // TODO: move some things from `Memory` to here } @@ -51,7 +51,7 @@ impl Default for FrameState { unused_rect: Rect::invalid(), used_by_panels: Rect::invalid(), scroll_target: None, - scroll_target_center_ratio: 0.0, + scroll_target_center_factor: 0.0, } } } @@ -62,7 +62,7 @@ impl FrameState { self.unused_rect = input.screen_rect(); self.used_by_panels = Rect::nothing(); self.scroll_target = None; - self.scroll_target_center_ratio = 0.0; + self.scroll_target_center_factor = 0.0; } /// How much space is still available after panels has been added. @@ -113,15 +113,15 @@ impl FrameState { self.scroll_target } - pub(crate) fn set_scroll_target_center_ratio(&mut self, center_ratio: f32) { + pub(crate) fn set_scroll_target_center_factor(&mut self, center_factor: f32) { // This is the imgui aproach to set the scroll relative to the position // 0.0: top of widget / 0.5: midle of widget / 1.0: bottom of widget - assert!(center_ratio >= 0.0 && center_ratio <= 1.0); - self.scroll_target_center_ratio = center_ratio; + assert!(center_factor >= 0.0 && center_factor <= 1.0); + self.scroll_target_center_factor = center_factor; } pub(crate) fn scroll_target_center_ratio(&self) -> f32 { - self.scroll_target_center_ratio + self.scroll_target_center_factor } } diff --git a/egui/src/demos/scrolls.rs b/egui/src/demos/scrolls.rs index 5055476ca63..55bbe1e76e7 100644 --- a/egui/src/demos/scrolls.rs +++ b/egui/src/demos/scrolls.rs @@ -6,7 +6,7 @@ pub struct Scrolls { track_item: usize, tracking: bool, offset: f32, - center_ratio: f32, + center_factor: f32, } impl Default for Scrolls { @@ -15,7 +15,7 @@ impl Default for Scrolls { track_item: 25, tracking: true, offset: 0.0, - center_ratio: 0.3, + center_factor: 0.3, } } } @@ -23,10 +23,10 @@ impl Default for Scrolls { impl Scrolls { pub fn ui(&mut self, ui: &mut Ui) { ui.horizontal(|ui| { - ui.checkbox(&mut self.tracking, "Track"); + ui.checkbox(&mut self.tracking, "Track").on_hover_text("The scroll position will track the selected item"); ui.add(Slider::usize(&mut self.track_item, 1..=50).text("Track Item")); }); - ui.add(Slider::f32(&mut self.center_ratio, 0.0..=1.0).text("Custom scroll center ratio")); + ui.add(Slider::f32(&mut self.center_factor, 0.0..=1.0).text("Custom scroll center factor")); let (scroll_offset, _) = ui.horizontal(|ui| { let scroll_offset = ui.small_button("Scroll Offset").clicked; ui.add(DragValue::f32(&mut self.offset).speed(1.0).suffix("px")); @@ -48,12 +48,12 @@ impl Scrolls { for item in 1..=50 { if self.tracking && item == self.track_item { let response = ui.colored_label(YELLOW, format!("Item {}", item)); - let scroll_center_ratio = if i == 5 { - self.center_ratio + let scroll_center_factor = if i == 5 { + self.center_factor } else { 0.25 * i as f32 }; - response.scroll_to_me(scroll_center_ratio); + response.scroll_to_me(scroll_center_factor); } else { ui.label(format!("Item {}", item)); } diff --git a/egui/src/types.rs b/egui/src/types.rs index da06764775d..9418cfb8d3b 100644 --- a/egui/src/types.rs +++ b/egui/src/types.rs @@ -136,12 +136,15 @@ impl Response { self.on_hover_text(text) } - pub fn scroll_to_me(&self, center_ratio: f32) { - let scroll_target = lerp(self.rect.y_range(), center_ratio); + /// Move the scroll to this UI. + /// The scroll centering is based on the `center_factor`: + /// 0.0f - at the top, 0.5f - at the middle, 1.0f - at the bottom. + pub fn scroll_to_me(&self, center_factor: f32) { + let scroll_target = lerp(self.rect.y_range(), center_factor); let mut frame_state = self.ctx.frame_state(); frame_state.set_scroll_target(Some(scroll_target)); - frame_state.set_scroll_target_center_ratio(center_ratio); + frame_state.set_scroll_target_center_factor(center_factor); } } diff --git a/egui/src/ui.rs b/egui/src/ui.rs index 3f5a868e9e8..b907d025682 100644 --- a/egui/src/ui.rs +++ b/egui/src/ui.rs @@ -527,12 +527,15 @@ impl Ui { Painter::new(self.ctx().clone(), self.layer_id(), clip_rect) } - pub fn scroll_to_here(&mut self, center_ratio: f32) { + /// Move the scroll to this position. + /// The scroll centering is based on the `center_factor`: + /// 0.0f - at the top, 0.5f - at the middle, 1.0f - at the bottom. + pub fn scroll_to_here(&mut self, center_factor: f32) { let scroll_target = self.min_rect().bottom(); let mut frame_state = self.ctx().frame_state(); frame_state.set_scroll_target(Some(scroll_target)); - frame_state.set_scroll_target_center_ratio(center_ratio); + frame_state.set_scroll_target_center_factor(center_factor); } } From c581ac04dd3e680770253dc3a74bb7a539555874 Mon Sep 17 00:00:00 2001 From: poffo Date: Sun, 27 Dec 2020 01:31:25 -0300 Subject: [PATCH 10/17] Update scroll documentation --- egui/src/containers/scroll_area.rs | 18 +++++++++--------- egui/src/types.rs | 6 ++++-- egui/src/ui.rs | 4 +++- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/egui/src/containers/scroll_area.rs b/egui/src/containers/scroll_area.rs index 23818d9adbf..d660088dd03 100644 --- a/egui/src/containers/scroll_area.rs +++ b/egui/src/containers/scroll_area.rs @@ -63,13 +63,13 @@ impl ScrollArea { self } - /// Set the position where the scroll should be. - /// See also: - /// [scroll_to_here](crate::ui::Ui::scroll_to_here) - /// [scroll_to_me](crate::types::Response::scroll_to_me) + /// Set the scroll offset position. + /// + /// See also: [Ui::scroll_to_here](crate::ui::Ui::scroll_to_here) and + /// [Response::scroll_to_me](crate::types::Response::scroll_to_me) pub fn scroll_offset(mut self, offset: Vec2) -> Self { - self.offset = Some(offset); - self + self.offset = Some(offset); + self } } @@ -105,7 +105,7 @@ impl ScrollArea { if let Some(offset) = offset { state.offset = offset; } - + // 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. @@ -170,7 +170,7 @@ impl Prepared { } = self; let content_size = content_ui.min_size(); - + let scroll_target = content_ui.ctx().frame_state().scroll_target(); if let Some(scroll_target) = scroll_target { let center_ratio = content_ui.ctx().frame_state().scroll_target_center_ratio(); @@ -193,7 +193,7 @@ impl Prepared { inner_rect.height(), ), ); - + let width = if inner_rect.width().is_finite() { inner_rect.width().max(content_size.x) // Expand width to fit content } else { diff --git a/egui/src/types.rs b/egui/src/types.rs index 8ce5f0367ba..7a4f43ae0ad 100644 --- a/egui/src/types.rs +++ b/egui/src/types.rs @@ -160,9 +160,11 @@ impl Response { .interact_with_hovered(self.layer_id, self.id, self.rect, sense, self.hovered) } - /// Move the scroll to this UI. + /// Move the scroll to this UI. /// The scroll centering is based on the `center_factor`: - /// 0.0f - at the top, 0.5f - at the middle, 1.0f - at the bottom. + /// * 0.0 - top + /// * 0.5 - middle + /// * 1.0 - bottom pub fn scroll_to_me(&self, center_factor: f32) { let scroll_target = lerp(self.rect.y_range(), center_factor); diff --git a/egui/src/ui.rs b/egui/src/ui.rs index 8a6c830d6ce..1f15ab4ccae 100644 --- a/egui/src/ui.rs +++ b/egui/src/ui.rs @@ -587,7 +587,9 @@ impl Ui { /// Move the scroll to this position. /// The scroll centering is based on the `center_factor`: - /// 0.0f - at the top, 0.5f - at the middle, 1.0f - at the bottom. + /// * 0.0 - top + /// * 0.5 - middle + /// * 1.0 - bottom pub fn scroll_to_here(&mut self, center_factor: f32) { let scroll_target = self.min_rect().bottom(); From bc4556b393ff3acbb1e73d9335ca4f43d56469db Mon Sep 17 00:00:00 2001 From: poffo Date: Sun, 27 Dec 2020 01:33:27 -0300 Subject: [PATCH 11/17] Formatting (RustFmt) --- egui/src/demos/scrolls.rs | 3 ++- egui/src/types.rs | 6 +++--- egui/src/ui.rs | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/egui/src/demos/scrolls.rs b/egui/src/demos/scrolls.rs index 55bbe1e76e7..cf8d5fe3d52 100644 --- a/egui/src/demos/scrolls.rs +++ b/egui/src/demos/scrolls.rs @@ -23,7 +23,8 @@ impl Default for Scrolls { impl Scrolls { pub fn ui(&mut self, ui: &mut Ui) { ui.horizontal(|ui| { - ui.checkbox(&mut self.tracking, "Track").on_hover_text("The scroll position will track the selected item"); + ui.checkbox(&mut self.tracking, "Track") + .on_hover_text("The scroll position will track the selected item"); ui.add(Slider::usize(&mut self.track_item, 1..=50).text("Track Item")); }); ui.add(Slider::f32(&mut self.center_factor, 0.0..=1.0).text("Custom scroll center factor")); diff --git a/egui/src/types.rs b/egui/src/types.rs index 7a4f43ae0ad..9c38723847a 100644 --- a/egui/src/types.rs +++ b/egui/src/types.rs @@ -1,4 +1,4 @@ -use crate::{math::Rect, CtxRef, Id, LayerId, Ui, lerp}; +use crate::{lerp, math::Rect, CtxRef, Id, LayerId, Ui}; // ---------------------------------------------------------------------------- @@ -160,10 +160,10 @@ impl Response { .interact_with_hovered(self.layer_id, self.id, self.rect, sense, self.hovered) } - /// Move the scroll to this UI. + /// Move the scroll to this UI. /// The scroll centering is based on the `center_factor`: /// * 0.0 - top - /// * 0.5 - middle + /// * 0.5 - middle /// * 1.0 - bottom pub fn scroll_to_me(&self, center_factor: f32) { let scroll_target = lerp(self.rect.y_range(), center_factor); diff --git a/egui/src/ui.rs b/egui/src/ui.rs index 1f15ab4ccae..48d98f24338 100644 --- a/egui/src/ui.rs +++ b/egui/src/ui.rs @@ -588,7 +588,7 @@ impl Ui { /// Move the scroll to this position. /// The scroll centering is based on the `center_factor`: /// * 0.0 - top - /// * 0.5 - middle + /// * 0.5 - middle /// * 1.0 - bottom pub fn scroll_to_here(&mut self, center_factor: f32) { let scroll_target = self.min_rect().bottom(); From 9e20b400e18157000e0c33fb15334329003afa03 Mon Sep 17 00:00:00 2001 From: poffo Date: Sun, 27 Dec 2020 01:35:16 -0300 Subject: [PATCH 12/17] Formatting (RustFmt) --- egui/src/demos/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/egui/src/demos/mod.rs b/egui/src/demos/mod.rs index 5471c275d84..ffb3f576525 100644 --- a/egui/src/demos/mod.rs +++ b/egui/src/demos/mod.rs @@ -12,16 +12,16 @@ pub mod font_contents_emoji; pub mod font_contents_ubuntu; mod fractal_clock; mod painting; +mod scrolls; mod sliders; mod tests; pub mod toggle_switch; mod widgets; -mod scrolls; pub use { app::*, color_test::ColorTest, dancing_strings::DancingStrings, demo_window::DemoWindow, demo_windows::*, drag_and_drop::*, font_book::FontBook, fractal_clock::FractalClock, - painting::Painting, sliders::Sliders, tests::Tests, widgets::Widgets, scrolls::Scrolls + painting::Painting, scrolls::Scrolls, sliders::Sliders, tests::Tests, widgets::Widgets, }; pub const LOREM_IPSUM: &str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; From 6e848365e8ca71d34c32600e1ed1ece222526ae7 Mon Sep 17 00:00:00 2001 From: poffo Date: Sun, 27 Dec 2020 01:40:49 -0300 Subject: [PATCH 13/17] Formatting (Clippy) --- egui/src/containers/scroll_area.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/egui/src/containers/scroll_area.rs b/egui/src/containers/scroll_area.rs index d660088dd03..98a9f09625c 100644 --- a/egui/src/containers/scroll_area.rs +++ b/egui/src/containers/scroll_area.rs @@ -65,8 +65,8 @@ impl ScrollArea { /// Set the scroll offset position. /// - /// See also: [Ui::scroll_to_here](crate::ui::Ui::scroll_to_here) and - /// [Response::scroll_to_me](crate::types::Response::scroll_to_me) + /// See also: [`Ui::scroll_to_here`](crate::ui::Ui::scroll_to_here) and + /// [`Response::scroll_to_me`](crate::types::Response::scroll_to_me) pub fn scroll_offset(mut self, offset: Vec2) -> Self { self.offset = Some(offset); self From ee2d76a9a9c1fdbd0bcfba179c0fae23c976fe85 Mon Sep 17 00:00:00 2001 From: poffo Date: Sun, 27 Dec 2020 02:07:32 -0300 Subject: [PATCH 14/17] Use const for the columns title --- egui/src/demos/scrolls.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/egui/src/demos/scrolls.rs b/egui/src/demos/scrolls.rs index cf8d5fe3d52..5319295203a 100644 --- a/egui/src/demos/scrolls.rs +++ b/egui/src/demos/scrolls.rs @@ -34,10 +34,10 @@ impl Scrolls { scroll_offset }); - let titles = ["Top", "25%", "Middle", "75%", "Bottom", "Custom"]; + const TITLES: [&str; 6] = ["Top", "25%", "Middle", "75%", "Bottom", "Custom"]; ui.columns(6, |cols| { for (i, col) in cols.iter_mut().enumerate() { - col.label(titles[i]); + col.colored_label(WHITE, TITLES[i]); let mut scroll_area = ScrollArea::from_max_height(200.0).id_source(i); if scroll_offset { self.tracking = false; @@ -67,7 +67,7 @@ impl Scrolls { ui.min_rect().height() - ui.clip_rect().height() + 2.0 * margin, ) }); - col.label(format!("{:.0}/{:.0}", current_scroll, max_scroll)); + col.colored_label(WHITE, format!("{:.0}/{:.0}", current_scroll, max_scroll)); } }); } From bb5148c5de459a9a52c7bc2136abb3672a1a8e32 Mon Sep 17 00:00:00 2001 From: poffo Date: Sun, 27 Dec 2020 14:41:11 -0300 Subject: [PATCH 15/17] Clean up comments --- egui/src/containers/scroll_area.rs | 6 ++---- egui/src/context.rs | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/egui/src/containers/scroll_area.rs b/egui/src/containers/scroll_area.rs index 98a9f09625c..e17d52d2e19 100644 --- a/egui/src/containers/scroll_area.rs +++ b/egui/src/containers/scroll_area.rs @@ -179,10 +179,8 @@ impl Prepared { let offset_y = scroll_target - top - height_offset; state.offset.y = offset_y; - // We need to clear/consume the offset - // or else all the ScrollAreas are gonna try to use this offset, - // this way only the innermost will use it. - // TODO: How to set outer ScrollArea when inside another? + // We need to clear the offset + // or else all the ScrollAreas will use this offset content_ui.ctx().frame_state().set_scroll_target(None); } diff --git a/egui/src/context.rs b/egui/src/context.rs index eb0f69fa761..3d8abc228cf 100644 --- a/egui/src/context.rs +++ b/egui/src/context.rs @@ -38,7 +38,6 @@ pub(crate) struct FrameState { /// How much space is used by panels. used_by_panels: Rect, - // TODO: Maybe turn this into an struct scroll_target: Option, scroll_target_center_factor: f32, // TODO: move some things from `Memory` to here From 0149258e324a09b4f1c59178fe234a40f0a5e6a2 Mon Sep 17 00:00:00 2001 From: poffo Date: Mon, 28 Dec 2020 16:02:11 -0300 Subject: [PATCH 16/17] Add suggestions --- egui/src/align.rs | 8 ++++++ egui/src/containers/scroll_area.rs | 38 +++++++++++++---------------- egui/src/context.rs | 24 +----------------- egui/src/demos/scrolls.rs | 39 ++++++++++++++++++++---------- egui/src/types.rs | 29 +++++++++++++--------- egui/src/ui.rs | 30 ++++++++++++++--------- 6 files changed, 88 insertions(+), 80 deletions(-) diff --git a/egui/src/align.rs b/egui/src/align.rs index 25cbe5a0f3c..17f16729190 100644 --- a/egui/src/align.rs +++ b/egui/src/align.rs @@ -29,6 +29,14 @@ impl Align { pub fn bottom() -> Self { Self::Max } + + pub(crate) fn scroll_center_factor(&self) -> f32 { + match self { + Self::Min => 0.0, + Self::Center => 0.5, + Self::Max => 1.0, + } + } } impl Default for Align { diff --git a/egui/src/containers/scroll_area.rs b/egui/src/containers/scroll_area.rs index e17d52d2e19..270ff115da4 100644 --- a/egui/src/containers/scroll_area.rs +++ b/egui/src/containers/scroll_area.rs @@ -63,12 +63,12 @@ impl ScrollArea { self } - /// Set the scroll offset position. + /// Set the vertical scroll offset position. /// - /// See also: [`Ui::scroll_to_here`](crate::ui::Ui::scroll_to_here) and + /// See also: [`Ui::scroll_to_cursor`](crate::ui::Ui::scroll_to_cursor) and /// [`Response::scroll_to_me`](crate::types::Response::scroll_to_me) - pub fn scroll_offset(mut self, offset: Vec2) -> Self { - self.offset = Some(offset); + pub fn scroll_offset(mut self, offset: f32) -> Self { + self.offset = Some(Vec2::new(0.0, offset)); self } } @@ -171,26 +171,22 @@ impl Prepared { let content_size = content_ui.min_size(); - let scroll_target = content_ui.ctx().frame_state().scroll_target(); - if let Some(scroll_target) = scroll_target { - let center_ratio = content_ui.ctx().frame_state().scroll_target_center_ratio(); - let height_offset = content_ui.clip_rect().height() * center_ratio; + // We take the scroll target so only this ScrollArea will use it. + let scroll_target = content_ui.ctx().frame_state().scroll_target.take(); + if let Some((scroll_y, align)) = scroll_target { + let center_factor = align.scroll_center_factor(); + let top = content_ui.min_rect().top(); - let offset_y = scroll_target - top - height_offset; - state.offset.y = offset_y; + let visible_range = top..=top + content_ui.clip_rect().height(); + let offset_y = scroll_y - lerp(visible_range, center_factor); - // We need to clear the offset - // or else all the ScrollAreas will use this offset - content_ui.ctx().frame_state().set_scroll_target(None); - } + let mut spacing = ui.style().spacing.item_spacing.y; - let inner_rect = Rect::from_min_size( - inner_rect.min, - vec2( - inner_rect.width().max(content_size.x), // Expand width to fit content - inner_rect.height(), - ), - ); + // Depending on the alignment we need to add or subtract the spacing + spacing *= remap(center_factor, 0.0..=1.0, -1.0..=1.0); + + state.offset.y = offset_y + spacing; + } let width = if inner_rect.width().is_finite() { inner_rect.width().max(content_size.x) // Expand width to fit content diff --git a/egui/src/context.rs b/egui/src/context.rs index 3d8abc228cf..189f46272da 100644 --- a/egui/src/context.rs +++ b/egui/src/context.rs @@ -38,8 +38,7 @@ pub(crate) struct FrameState { /// How much space is used by panels. used_by_panels: Rect, - scroll_target: Option, - scroll_target_center_factor: f32, + pub(crate) scroll_target: Option<(f32, Align)>, // TODO: move some things from `Memory` to here } @@ -50,7 +49,6 @@ impl Default for FrameState { unused_rect: Rect::invalid(), used_by_panels: Rect::invalid(), scroll_target: None, - scroll_target_center_factor: 0.0, } } } @@ -61,7 +59,6 @@ impl FrameState { self.unused_rect = input.screen_rect(); self.used_by_panels = Rect::nothing(); self.scroll_target = None; - self.scroll_target_center_factor = 0.0; } /// How much space is still available after panels has been added. @@ -103,25 +100,6 @@ impl FrameState { self.unused_rect = Rect::nothing(); // Nothing left unused after this self.used_by_panels = self.used_by_panels.union(panel_rect); } - - pub(crate) fn set_scroll_target(&mut self, scroll_target: Option) { - self.scroll_target = scroll_target; - } - - pub(crate) fn scroll_target(&self) -> Option { - self.scroll_target - } - - pub(crate) fn set_scroll_target_center_factor(&mut self, center_factor: f32) { - // This is the imgui aproach to set the scroll relative to the position - // 0.0: top of widget / 0.5: midle of widget / 1.0: bottom of widget - assert!(center_factor >= 0.0 && center_factor <= 1.0); - self.scroll_target_center_factor = center_factor; - } - - pub(crate) fn scroll_target_center_ratio(&self) -> f32 { - self.scroll_target_center_factor - } } // ---------------------------------------------------------------------------- diff --git a/egui/src/demos/scrolls.rs b/egui/src/demos/scrolls.rs index 5319295203a..3451dacb946 100644 --- a/egui/src/demos/scrolls.rs +++ b/egui/src/demos/scrolls.rs @@ -1,4 +1,4 @@ -use crate::{color::*, *}; +use crate::{color::*, demos::LOREM_IPSUM_LONG, *}; #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "serde", serde(default))] @@ -6,7 +6,6 @@ pub struct Scrolls { track_item: usize, tracking: bool, offset: f32, - center_factor: f32, } impl Default for Scrolls { @@ -15,52 +14,66 @@ impl Default for Scrolls { track_item: 25, tracking: true, offset: 0.0, - center_factor: 0.3, } } } impl Scrolls { pub fn ui(&mut self, ui: &mut Ui) { + ScrollArea::from_max_height(200.0).show(ui, |ui| { + ui.label(LOREM_IPSUM_LONG); + ui.label(LOREM_IPSUM_LONG); + }); + + ui.separator(); + ui.horizontal(|ui| { ui.checkbox(&mut self.tracking, "Track") .on_hover_text("The scroll position will track the selected item"); ui.add(Slider::usize(&mut self.track_item, 1..=50).text("Track Item")); }); - ui.add(Slider::f32(&mut self.center_factor, 0.0..=1.0).text("Custom scroll center factor")); let (scroll_offset, _) = ui.horizontal(|ui| { let scroll_offset = ui.small_button("Scroll Offset").clicked; ui.add(DragValue::f32(&mut self.offset).speed(1.0).suffix("px")); scroll_offset }); - const TITLES: [&str; 6] = ["Top", "25%", "Middle", "75%", "Bottom", "Custom"]; - ui.columns(6, |cols| { + let scroll_top = ui.button("Scroll to top").clicked; + let scroll_bottom = ui.button("Scroll to bottom").clicked; + if scroll_bottom || scroll_top { + self.tracking = false; + } + + const TITLES: [&str; 3] = ["Top", "Middle", "Bottom"]; + const ALIGNS: [Align; 3] = [Align::Min, Align::Center, Align::Max]; + ui.columns(3, |cols| { for (i, col) in cols.iter_mut().enumerate() { col.colored_label(WHITE, TITLES[i]); let mut scroll_area = ScrollArea::from_max_height(200.0).id_source(i); if scroll_offset { self.tracking = false; - scroll_area = scroll_area.scroll_offset(Vec2::new(0.0, self.offset)); + scroll_area = scroll_area.scroll_offset(self.offset); } let (current_scroll, max_scroll) = scroll_area.show(col, |ui| { + if scroll_top { + ui.scroll_to_cursor(Align::top()); + } ui.vertical(|ui| { for item in 1..=50 { if self.tracking && item == self.track_item { let response = ui.colored_label(YELLOW, format!("Item {}", item)); - let scroll_center_factor = if i == 5 { - self.center_factor - } else { - 0.25 * i as f32 - }; - response.scroll_to_me(scroll_center_factor); + response.scroll_to_me(ALIGNS[i]); } else { ui.label(format!("Item {}", item)); } } }); + if scroll_bottom { + ui.scroll_to_cursor(Align::bottom()); + } + let margin = ui.style().visuals.clip_rect_margin; ( ui.clip_rect().top() - ui.min_rect().top() + margin, diff --git a/egui/src/types.rs b/egui/src/types.rs index 9c38723847a..d1d08880f0c 100644 --- a/egui/src/types.rs +++ b/egui/src/types.rs @@ -1,4 +1,4 @@ -use crate::{lerp, math::Rect, CtxRef, Id, LayerId, Ui}; +use crate::{lerp, math::Rect, Align, CtxRef, Id, LayerId, Ui}; // ---------------------------------------------------------------------------- @@ -160,17 +160,22 @@ impl Response { .interact_with_hovered(self.layer_id, self.id, self.rect, sense, self.hovered) } - /// Move the scroll to this UI. - /// The scroll centering is based on the `center_factor`: - /// * 0.0 - top - /// * 0.5 - middle - /// * 1.0 - bottom - pub fn scroll_to_me(&self, center_factor: f32) { - let scroll_target = lerp(self.rect.y_range(), center_factor); - - let mut frame_state = self.ctx.frame_state(); - frame_state.set_scroll_target(Some(scroll_target)); - frame_state.set_scroll_target_center_factor(center_factor); + /// Move the scroll to this UI with the specified alignment. + /// + /// ``` + /// # let mut ui = egui::Ui::__test(); + /// egui::ScrollArea::auto_sized().show(ui, |ui| { + /// for i in 0..1000 { + /// let response = ui.button(format!("Button {}", i)); + /// if response.clicked { + /// response.scroll_to_me(Align::Center); + /// } + /// } + /// }); + /// ``` + pub fn scroll_to_me(&self, align: Align) { + let scroll_target = lerp(self.rect.y_range(), align.scroll_center_factor()); + self.ctx.frame_state().scroll_target = Some((scroll_target, align)); } } diff --git a/egui/src/ui.rs b/egui/src/ui.rs index 48d98f24338..8a3b3ec37e1 100644 --- a/egui/src/ui.rs +++ b/egui/src/ui.rs @@ -585,17 +585,25 @@ impl Ui { (response, painter) } - /// Move the scroll to this position. - /// The scroll centering is based on the `center_factor`: - /// * 0.0 - top - /// * 0.5 - middle - /// * 1.0 - bottom - pub fn scroll_to_here(&mut self, center_factor: f32) { - let scroll_target = self.min_rect().bottom(); - - let mut frame_state = self.ctx().frame_state(); - frame_state.set_scroll_target(Some(scroll_target)); - frame_state.set_scroll_target_center_factor(center_factor); + /// Move the scroll to this cursor position with the specified alignment. + /// + /// ``` + /// # let mut ui = egui::Ui::__test(); + /// egui::ScrollArea::auto_sized().show(ui, |ui| { + /// let scroll_bottom = ui.button("Scroll to bottom.").clicked; + /// for i in 0..1000 { + /// ui.label(format!("Item {}", i)); + /// } + /// + /// if scroll_bottom { + /// ui.scroll_to_cursor(Align::bottom()); + /// } + /// }); + /// ``` + pub fn scroll_to_cursor(&mut self, align: Align) { + let scroll_y = self.region.cursor.y; + + self.ctx().frame_state().scroll_target = Some((scroll_y, align)); } } From 305d99fc9c40090db36d90f0a3e40bfb2507748e Mon Sep 17 00:00:00 2001 From: poffo Date: Mon, 28 Dec 2020 16:26:41 -0300 Subject: [PATCH 17/17] Fix doc test --- egui/src/types.rs | 3 ++- egui/src/ui.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/egui/src/types.rs b/egui/src/types.rs index d1d08880f0c..030d0e75e78 100644 --- a/egui/src/types.rs +++ b/egui/src/types.rs @@ -163,7 +163,8 @@ impl Response { /// Move the scroll to this UI with the specified alignment. /// /// ``` - /// # let mut ui = egui::Ui::__test(); + /// # use egui::Align; + /// # let mut ui = &mut egui::Ui::__test(); /// egui::ScrollArea::auto_sized().show(ui, |ui| { /// for i in 0..1000 { /// let response = ui.button(format!("Button {}", i)); diff --git a/egui/src/ui.rs b/egui/src/ui.rs index 8a3b3ec37e1..127b52b3f9c 100644 --- a/egui/src/ui.rs +++ b/egui/src/ui.rs @@ -588,7 +588,8 @@ impl Ui { /// Move the scroll to this cursor position with the specified alignment. /// /// ``` - /// # let mut ui = egui::Ui::__test(); + /// # use egui::Align; + /// # let mut ui = &mut egui::Ui::__test(); /// egui::ScrollArea::auto_sized().show(ui, |ui| { /// let scroll_bottom = ui.button("Scroll to bottom.").clicked; /// for i in 0..1000 {