diff --git a/egui/src/containers/scroll_area.rs b/egui/src/containers/scroll_area.rs index daa6b83edfee..41681d958bf3 100644 --- a/egui/src/containers/scroll_area.rs +++ b/egui/src/containers/scroll_area.rs @@ -498,7 +498,7 @@ impl Prepared { let clip_end = clip_rect.max[d]; let mut spacing = ui.spacing().item_spacing[d]; - if let Some(align) = align { + let delta = if let Some(align) = align { let center_factor = align.to_factor(); let offset = @@ -507,19 +507,20 @@ impl Prepared { // 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[d] = offset + spacing; + offset + spacing } else if start < clip_start && end < clip_end { - let min_adjust = - (clip_start - start + spacing).min(clip_end - end - spacing); - state.offset[d] -= min_adjust; + -(clip_start - start + spacing).min(clip_end - end - spacing) } else if end > clip_end && start > clip_start { - let min_adjust = - (end - clip_end + spacing).min(start - clip_start - spacing); - state.offset[d] += min_adjust; + (end - clip_end + spacing).min(start - clip_start - spacing) } else { // Ui is already in view, no need to adjust scroll. - continue; + 0.0 }; + + if delta != 0.0 { + state.offset[d] += delta; + ui.ctx().request_repaint(); + } } } } diff --git a/egui/src/response.rs b/egui/src/response.rs index e85626f690bf..5478fe103531 100644 --- a/egui/src/response.rs +++ b/egui/src/response.rs @@ -443,10 +443,11 @@ impl Response { ) } - /// Adjust the scroll position until this UI becomes visible. If `align` is not provided, it'll scroll enough to - /// bring the UI into view. + /// Adjust the scroll position until this UI becomes visible. /// - /// See also [`Ui::scroll_to_cursor`] + /// If `align` is `None`, it'll scroll enough to bring the UI into view. + /// + /// See also: [`Ui::scroll_to_cursor`], [`Ui::scroll_to`]. /// /// ``` /// # egui::__run_test_ui(|ui| { diff --git a/egui/src/ui.rs b/egui/src/ui.rs index b1653a626c33..75509f29fedb 100644 --- a/egui/src/ui.rs +++ b/egui/src/ui.rs @@ -889,10 +889,36 @@ impl Ui { (response, painter) } - /// Adjust the scroll position until the cursor becomes visible. If `align` is not provided, it'll scroll enough to - /// bring the cursor into view. + /// Adjust the scroll position of any parent [`ScrollArea`] so that the given `Rect` becomes visible. /// - /// See also [`Response::scroll_to_me`] + /// If `align` is `None`, it'll scroll enough to bring the cursor into view. + /// + /// See also: [`Response::scroll_to_me`], [`Ui::scroll_to`]. + /// + /// ``` + /// # use egui::Align; + /// # egui::__run_test_ui(|ui| { + /// egui::ScrollArea::vertical().show(ui, |ui| { + /// // … + /// let response = ui.button("Center on me.").clicked(); + /// if response.clicked() { + /// ui.scroll_to_rect(response.rect, Some(Align::CENTER)); + /// } + /// }); + /// # }); + /// ``` + pub fn scroll_to_rect(&self, rect: Rect, align: Option) { + for d in 0..2 { + let range = rect.min[d]..=rect.max[d]; + self.ctx().frame_state().scroll_target[d] = Some((range, align)); + } + } + + /// Adjust the scroll position of any parent [`ScrollArea`] so that the cursor (where the next widget goes) becomes visible. + /// + /// If `align` is not provided, it'll scroll enough to bring the cursor into view. + /// + /// See also: [`Response::scroll_to_me`], [`Ui::scroll_to`]. /// /// ``` /// # use egui::Align; @@ -909,7 +935,7 @@ impl Ui { /// }); /// # }); /// ``` - pub fn scroll_to_cursor(&mut self, align: Option) { + pub fn scroll_to_cursor(&self, align: Option) { let target = self.next_widget_position(); for d in 0..2 { let target = target[d]; diff --git a/egui/src/widgets/text_edit/builder.rs b/egui/src/widgets/text_edit/builder.rs index 1c95663380e8..da0d577b6739 100644 --- a/egui/src/widgets/text_edit/builder.rs +++ b/egui/src/widgets/text_edit/builder.rs @@ -561,7 +561,7 @@ impl<'t> TextEdit<'t> { // We paint the cursor on top of the text, in case // the text galley has backgrounds (as e.g. `code` snippets in markup do). paint_cursor_selection(ui, &painter, text_draw_pos, &galley, &cursor_range); - paint_cursor_end( + let cursor_pos = paint_cursor_end( ui, row_height, &painter, @@ -570,6 +570,8 @@ impl<'t> TextEdit<'t> { &cursor_range.primary, ); + ui.scroll_to_rect(cursor_pos, None); // keep cursor in view + if interactive && text.is_mutable() { // egui_web uses `text_cursor_pos` when showing IME, // so only set it when text is editable and visible! @@ -887,7 +889,7 @@ fn paint_cursor_end( pos: Pos2, galley: &Galley, cursor: &Cursor, -) { +) -> Rect { let stroke = ui.visuals().selection.stroke; let mut cursor_pos = galley.pos_from_cursor(cursor).translate(pos.to_vec2()); @@ -915,6 +917,8 @@ fn paint_cursor_end( (width, stroke.color), ); } + + cursor_pos } // ----------------------------------------------------------------------------