mirror of
https://github.com/emilk/egui.git
synced 2026-06-26 14:49:06 -04:00
Treat . as a word-splitter in text navigation (#7741)
When using option + arrow keys (Mac) or ctrl + arrow keys (Windows), you navigate a full word. Previously egui would ignore `.` in the text, so that `www.example.com` would be considered a full word. This is inconsistent with how the rest of macOS works. With this PR, cursor navigation in `www.example.com` will move the cursor between the dots. This makes editing code with egui a lot nicer.
This commit is contained in:
@@ -208,25 +208,33 @@ fn ccursor_previous_line(text: &str, ccursor: CCursor) -> CCursor {
|
||||
}
|
||||
}
|
||||
|
||||
fn next_word_boundary_char_index(text: &str, index: usize) -> usize {
|
||||
for word in text.split_word_bound_indices() {
|
||||
fn next_word_boundary_char_index(text: &str, cursor_ci: usize) -> usize {
|
||||
for (word_byte_index, word) in text.split_word_bound_indices() {
|
||||
let word_ci = char_index_from_byte_index(text, word_byte_index);
|
||||
|
||||
// We consider `.` a word boundary.
|
||||
// At least that's how Mac works when navigating something like `www.example.com`.
|
||||
for (dot_ci_offset, chr) in word.chars().enumerate() {
|
||||
let dot_ci = word_ci + dot_ci_offset;
|
||||
if chr == '.' && cursor_ci < dot_ci {
|
||||
return dot_ci;
|
||||
}
|
||||
}
|
||||
|
||||
// Splitting considers contiguous whitespace as one word, such words must be skipped,
|
||||
// this handles cases for example ' abc' (a space and a word), the cursor is at the beginning
|
||||
// (before space) - this jumps at the end of 'abc' (this is consistent with text editors
|
||||
// or browsers)
|
||||
let ci = char_index_from_byte_index(text, word.0);
|
||||
if ci > index && !skip_word(word.1) {
|
||||
return ci;
|
||||
if cursor_ci < word_ci && !all_word_chars(word) {
|
||||
return word_ci;
|
||||
}
|
||||
}
|
||||
|
||||
char_index_from_byte_index(text, text.len())
|
||||
}
|
||||
|
||||
fn skip_word(text: &str) -> bool {
|
||||
// skip words that contain anything other than alphanumeric characters and underscore
|
||||
// (i.e. whitespace, dashes, etc.)
|
||||
!text.chars().any(|c| !is_word_char(c))
|
||||
fn all_word_chars(text: &str) -> bool {
|
||||
text.chars().all(is_word_char)
|
||||
}
|
||||
|
||||
fn next_line_boundary_char_index(it: impl Iterator<Item = char>, mut index: usize) -> usize {
|
||||
@@ -337,6 +345,12 @@ mod test {
|
||||
assert_eq!(next_word_boundary_char_index("", 0), 0);
|
||||
assert_eq!(next_word_boundary_char_index("", 1), 0);
|
||||
|
||||
// ASCII only
|
||||
let text = "abc.def.ghi";
|
||||
assert_eq!(next_word_boundary_char_index(text, 1), 3);
|
||||
assert_eq!(next_word_boundary_char_index(text, 3), 7);
|
||||
assert_eq!(next_word_boundary_char_index(text, 7), 11);
|
||||
|
||||
// Unicode graphemes, some of which consist of multiple Unicode characters,
|
||||
// !!! Unicode character is not always what is tranditionally considered a character,
|
||||
// the values below are correct despite not seeming that way on the first look,
|
||||
|
||||
Reference in New Issue
Block a user