Make text cursor always appear on click (#5420)

<!--
Please read the "Making a PR" section of
[`CONTRIBUTING.md`](https://github.com/emilk/egui/blob/master/CONTRIBUTING.md)
before opening a Pull Request!

* Keep your PR:s small and focused.
* The PR title is what ends up in the changelog, so make it descriptive!
* If applicable, add a screenshot or gif.
* If it is a non-trivial addition, consider adding a demo for it to
`egui_demo_lib`, or a new example.
* Do NOT open PR:s from your `master` branch, as that makes it hard for
maintainers to test and add commits to your PR.
* Remember to run `cargo fmt` and `cargo clippy`.
* Open the PR as a draft until you have self-reviewed it and run
`./scripts/check.sh`.
* When you have addressed a PR comment, mark it as resolved.

Please be patient! I will review your PR, but my time is limited!
-->

* [x] I have followed the instructions in the PR template

### Problem
When clicking on a TextEdit sometimes the cursor doesn't appear
immediately which makes it feel like the click was not registered for a
second. This is because the start time for the blinking animation is
only reset on keyboard input, but not on mouse interaction.

It's hard to tell on the video but the cursor doesn't show immediately
after clicking if the blink timer happens to be off.


https://github.com/user-attachments/assets/9f049bd0-0375-4291-b2ef-697777fb854d


### Solution
Reset the click timer every time a `TextEdit` is clicked. 

Additionally, the cursor is now correctly painted on the pixel boundary.
IMO we should default to 1px cursor (instead of 2px) but that's not
included in this PR. Happy to make that change too.


https://github.com/user-attachments/assets/6c489414-f2c4-4dc6-85dd-f8bc457edad0
This commit is contained in:
Juan Campa 2024-12-04 08:18:49 -05:00 committed by GitHub
parent eac7ba01fa
commit cd0f5859b2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 21 additions and 8 deletions

View File

@ -96,8 +96,19 @@ pub fn paint_text_selection(
pub fn paint_cursor_end(painter: &Painter, visuals: &Visuals, cursor_rect: Rect) {
let stroke = visuals.text_cursor.stroke;
let top = cursor_rect.center_top();
let bottom = cursor_rect.center_bottom();
// Ensure the cursor is aligned to the pixel grid for whole number widths.
// See https://github.com/emilk/egui/issues/5164
let (top, bottom) = if (stroke.width as usize) % 2 == 0 {
(
painter.round_pos_to_pixels(cursor_rect.center_top()),
painter.round_pos_to_pixels(cursor_rect.center_bottom()),
)
} else {
(
painter.round_pos_to_pixel_center(cursor_rect.center_top()),
painter.round_pos_to_pixel_center(cursor_rect.center_bottom()),
)
};
painter.line_segment([top, bottom], (stroke.width, stroke.color));
@ -121,14 +132,14 @@ pub fn paint_text_cursor(
ui: &Ui,
painter: &Painter,
primary_cursor_rect: Rect,
time_since_last_edit: f64,
time_since_last_interaction: f64,
) {
if ui.visuals().text_cursor.blink {
let on_duration = ui.visuals().text_cursor.on_duration;
let off_duration = ui.visuals().text_cursor.off_duration;
let total_duration = on_duration + off_duration;
let time_in_cycle = (time_since_last_edit % (total_duration as f64)) as f32;
let time_in_cycle = (time_since_last_interaction % (total_duration as f64)) as f32;
let wake_in = if time_in_cycle < on_duration {
// Cursor is visible

View File

@ -603,6 +603,8 @@ impl<'t> TextEdit<'t> {
if did_interact || response.clicked() {
ui.memory_mut(|mem| mem.request_focus(response.id));
state.last_interaction_time = ui.ctx().input(|i| i.time);
}
}
}
@ -746,7 +748,7 @@ impl<'t> TextEdit<'t> {
if text.is_mutable() && interactive {
let now = ui.ctx().input(|i| i.time);
if response.changed || selection_changed {
state.last_edit_time = now;
state.last_interaction_time = now;
}
// Only show (and blink) cursor if the egui viewport has focus.
@ -759,7 +761,7 @@ impl<'t> TextEdit<'t> {
ui,
&painter,
primary_cursor_rect,
now - state.last_edit_time,
now - state.last_interaction_time,
);
}

View File

@ -53,10 +53,10 @@ pub struct TextEditState {
#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) singleline_offset: f32,
/// When did the user last press a key?
/// When did the user last press a key or click on the `TextEdit`.
/// Used to pause the cursor animation when typing.
#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) last_edit_time: f64,
pub(crate) last_interaction_time: f64,
}
impl TextEditState {