diff --git a/crates/eframe/src/web/text_agent.rs b/crates/eframe/src/web/text_agent.rs index 8f0bd695d..1346f1272 100644 --- a/crates/eframe/src/web/text_agent.rs +++ b/crates/eframe/src/web/text_agent.rs @@ -105,19 +105,17 @@ impl TextAgent { active_range_chars, }); runner.input.raw.events.push(event); - runner.needs_repaint.repaint_asap(); } else { if text.is_empty() { return; } - if !event.is_composing() { - input.set_value(""); - let event = egui::Event::Text(text); - runner.input.raw.events.push(event); - runner.needs_repaint.repaint_asap(); - } + input.set_value(""); + let event = egui::Event::Text(text); + runner.input.raw.events.push(event); } + + runner.needs_repaint.repaint_asap(); } }; diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index e10aeef07..93053adc3 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -689,16 +689,25 @@ impl State { // See winit::event::Ime::Enabled | winit::event::Ime::Disabled => {} winit::event::Ime::Preedit(text, active_range_bytes) => { + let active_range_chars = match active_range_bytes.clone() { + Some((start_bytes, end_bytes)) => { + let (Some(start_chars), Some(middle_chars)) = ( + text.get(..start_bytes).map(|s| s.chars().count()), + text.get(start_bytes..end_bytes).map(|s| s.chars().count()), + ) else { + log::warn!("{ime:?} is ignored due to invalid range"); + return; + }; + Some(start_chars..start_chars + middle_chars) + } + None => None, + }; + self.egui_input .events .push(egui::Event::Ime(egui::ImeEvent::Preedit { text: text.clone(), - active_range_chars: active_range_bytes.map(|(start_bytes, end_bytes)| { - let start_char = text[..start_bytes].chars().count(); - let end_char = - start_char + text[start_bytes..end_bytes].chars().count(); - start_char..end_char - }), + active_range_chars, })); } winit::event::Ime::Commit(text) => { diff --git a/crates/egui/src/style.rs b/crates/egui/src/style.rs index 5c523c71a..1af8b590e 100644 --- a/crates/egui/src/style.rs +++ b/crates/egui/src/style.rs @@ -1641,7 +1641,7 @@ impl ImeComposition { color: active_underline_stroke.color.linear_multiply(0.5), }; Self { - active_underline_stroke: Stroke::new(2.0, Color32::from_rgb(192, 222, 255)), + active_underline_stroke, inactive_underline_stroke, } } diff --git a/crates/egui/src/text_selection/visuals.rs b/crates/egui/src/text_selection/visuals.rs index d3c0f7764..a716a7f83 100644 --- a/crates/egui/src/text_selection/visuals.rs +++ b/crates/egui/src/text_selection/visuals.rs @@ -140,10 +140,27 @@ pub(crate) fn paint_ime_preedit_text_visuals( mut relative_active_range: Option>, time_since_last_interaction: f64, ) { - if preedit_range.is_empty() { + /// Instead of implementing [`PartialOrd`] and [`Ord`] for [`CCursor`] to + /// make [`std::ops::Range::is_empty`] available, we use this helper + /// function instead. + /// + /// These traits are intentionally not implemented because + /// [`CCursor::prefer_next_row`] makes it difficult to define a clear + /// ordering between two [`CCursor`]s. + fn is_cursor_range_empty(range: &std::ops::Range) -> bool { + range.start.index == range.end.index + } + + if is_cursor_range_empty(&preedit_range) { return; } + if let Some(relative_active_range) = &mut relative_active_range + && relative_active_range.end.index > preedit_range.end.index - preedit_range.start.index + { + relative_active_range.end.index = preedit_range.end.index - preedit_range.start.index; + } + if matches!(ui.ctx().os(), crate::os::OperatingSystem::Windows) && let Some(r) = &relative_active_range && r.start.index == 0 @@ -163,7 +180,7 @@ pub(crate) fn paint_ime_preedit_text_visuals( let inactive_underline_stroke = visuals.ime_composition.inactive_underline_stroke; if let Some(relative_active_range) = &relative_active_range - && !relative_active_range.is_empty() + && !is_cursor_range_empty(relative_active_range) { if relative_active_range.start.index > 0 { paint_underlines( @@ -185,7 +202,9 @@ pub(crate) fn paint_ime_preedit_text_visuals( active_underline_stroke, ); - if relative_active_range.end < preedit_range.end - preedit_range.start.index { + if !is_cursor_range_empty( + &(relative_active_range.end..(preedit_range.end - preedit_range.start.index)), + ) { paint_underlines( pos, painter, @@ -207,7 +226,7 @@ pub(crate) fn paint_ime_preedit_text_visuals( } if let Some(relative_active_range) = relative_active_range - && relative_active_range.is_empty() + && is_cursor_range_empty(&relative_active_range) { let active_cursor = preedit_range.start + relative_active_range.start.index; let cursor_rect = cursor_rect(galley, &active_cursor, row_height);