Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix cursor clipping in TextEdit inside a ScrollArea #3660

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion crates/egui/src/containers/scroll_area.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,11 @@ struct Prepared {

scrolling_enabled: bool,
stick_to_end: Vec2b,

/// If there was a scroll target before the ScrollArea was added this frame, it's
/// not for us to handle so we save it and restore it after this ScrollArea is done.
saved_scroll_target: [Option<pass_state::ScrollTarget>; 2],
Comment on lines +503 to +505
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is also FrameState::scroll_delta, I think we should do the same with that?


animated: bool,
}

Expand Down Expand Up @@ -693,6 +698,10 @@ impl ScrollArea {
}
}

let saved_scroll_target = content_ui
.ctx()
.pass_state_mut(|state| std::mem::take(&mut state.scroll_target));

Prepared {
id,
state,
Expand All @@ -707,6 +716,7 @@ impl ScrollArea {
viewport,
scrolling_enabled,
stick_to_end,
saved_scroll_target,
animated,
}
}
Expand Down Expand Up @@ -820,6 +830,7 @@ impl Prepared {
viewport: _,
scrolling_enabled,
stick_to_end,
saved_scroll_target,
animated,
} = self;

Expand Down Expand Up @@ -853,7 +864,7 @@ impl Prepared {
let (start, end) = (range.min, range.max);
let clip_start = clip_rect.min[d];
let clip_end = clip_rect.max[d];
let mut spacing = ui.spacing().item_spacing[d];
let mut spacing = content_ui.spacing().item_spacing[d];

let delta_update = if let Some(align) = align {
let center_factor = align.to_factor();
Expand Down Expand Up @@ -902,6 +913,15 @@ impl Prepared {
}
}

// Restore scroll target meant for ScrollAreas up the stack (if any)
ui.ctx().pass_state_mut(|state| {
for d in 0..2 {
if saved_scroll_target[d].is_some() {
state.scroll_target[d] = saved_scroll_target[d].clone();
};
}
});

let inner_rect = {
// At this point this is the available size for the inner rect.
let mut inner_size = inner_rect.size();
Expand Down
16 changes: 13 additions & 3 deletions crates/egui/src/widgets/text_edit/builder.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::sync::Arc;

use emath::Rect;
use epaint::text::{cursor::CCursor, Galley, LayoutJob};

use crate::{
Expand Down Expand Up @@ -719,17 +720,26 @@ impl<'t> TextEdit<'t> {
}
}

// Allocate additional space if edits were made this frame that changed the size. This is important so that,
// if there's a ScrollArea, it can properly scroll to the cursor.
let extra_size = galley.size() - rect.size();
if extra_size.x > 0.0 || extra_size.y > 0.0 {
ui.allocate_rect(
Rect::from_min_size(outer_rect.max, extra_size),
Sense::hover(),
);
}

painter.galley(galley_pos, galley.clone(), text_color);

if has_focus {
if let Some(cursor_range) = state.cursor.range(&galley) {
let primary_cursor_rect =
cursor_rect(galley_pos, &galley, &cursor_range.primary, row_height);

let is_fully_visible = ui.clip_rect().contains_rect(rect); // TODO(emilk): remove this HACK workaround for https://github.com/emilk/egui/issues/1531
if (response.changed || selection_changed) && !is_fully_visible {
if response.changed || selection_changed {
juancampa marked this conversation as resolved.
Show resolved Hide resolved
// Scroll to keep primary cursor in view:
ui.scroll_to_rect(primary_cursor_rect, None);
ui.scroll_to_rect(primary_cursor_rect + margin, None);
}

if text.is_mutable() && interactive {
Expand Down
Loading