IME: Handle composition events to show suggestion on web (#278)
* Handle composition message to show suggestion. * CI check * Apply suggestions from code review Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com> * Some minor changes Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
parent
0353f40dd5
commit
20bf09560e
3 changed files with 59 additions and 8 deletions
|
@ -116,6 +116,13 @@ pub enum Event {
|
|||
///
|
||||
/// On touch-up first send `PointerButton{pressed: false, …}` followed by `PointerLeft`.
|
||||
PointerGone,
|
||||
|
||||
/// IME composition start.
|
||||
CompositionStart,
|
||||
/// A new IME candidate is being suggested.
|
||||
CompositionUpdate(String),
|
||||
/// IME composition ended with this final result.
|
||||
CompositionEnd(String),
|
||||
}
|
||||
|
||||
/// Mouse button (or similar for touch input)
|
||||
|
|
|
@ -9,6 +9,10 @@ pub(crate) struct State {
|
|||
|
||||
#[cfg_attr(feature = "persistence", serde(skip))]
|
||||
undoer: Undoer<(CCursorPair, String)>,
|
||||
|
||||
// If IME candidate window is shown on this text edit.
|
||||
#[cfg_attr(feature = "persistence", serde(skip))]
|
||||
has_ime: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
|
@ -503,6 +507,41 @@ impl<'t> TextEdit<'t> {
|
|||
modifiers,
|
||||
} => on_key_press(&mut cursorp, text, &galley, *key, modifiers),
|
||||
|
||||
Event::CompositionStart => {
|
||||
state.has_ime = true;
|
||||
None
|
||||
}
|
||||
|
||||
Event::CompositionUpdate(text_mark) => {
|
||||
if !text_mark.is_empty()
|
||||
&& text_mark != "\n"
|
||||
&& text_mark != "\r"
|
||||
&& state.has_ime
|
||||
{
|
||||
let mut ccursor = delete_selected(text, &cursorp);
|
||||
let start_cursor = ccursor;
|
||||
insert_text(&mut ccursor, text, text_mark);
|
||||
Some(CCursorPair::two(start_cursor, ccursor))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
Event::CompositionEnd(prediction) => {
|
||||
if !prediction.is_empty()
|
||||
&& prediction != "\n"
|
||||
&& prediction != "\r"
|
||||
&& state.has_ime
|
||||
{
|
||||
state.has_ime = false;
|
||||
let mut ccursor = delete_selected(text, &cursorp);
|
||||
insert_text(&mut ccursor, text, prediction);
|
||||
Some(CCursorPair::one(ccursor))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
_ => None,
|
||||
};
|
||||
|
||||
|
|
|
@ -708,22 +708,27 @@ fn install_text_agent(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
|
|||
let input_clone = input.clone();
|
||||
let runner_ref = runner_ref.clone();
|
||||
let on_compositionend = Closure::wrap(Box::new(move |event: web_sys::CompositionEvent| {
|
||||
match event.type_().as_ref() {
|
||||
let mut runner_lock = runner_ref.0.lock();
|
||||
let opt_event = match event.type_().as_ref() {
|
||||
"compositionstart" => {
|
||||
is_composing.set(true);
|
||||
input_clone.set_value("");
|
||||
Some(egui::Event::CompositionStart)
|
||||
}
|
||||
"compositionend" => {
|
||||
is_composing.set(false);
|
||||
input_clone.set_value("");
|
||||
if let Some(text) = event.data() {
|
||||
let mut runner_lock = runner_ref.0.lock();
|
||||
runner_lock.input.raw.events.push(egui::Event::Text(text));
|
||||
runner_lock.needs_repaint.set_true();
|
||||
}
|
||||
event.data().map(egui::Event::CompositionEnd)
|
||||
}
|
||||
"compositionupdate" => {}
|
||||
_s => panic!("Unknown type"),
|
||||
"compositionupdate" => event.data().map(egui::Event::CompositionUpdate),
|
||||
s => {
|
||||
console_error(format!("Unknown composition event type: {:?}", s));
|
||||
None
|
||||
}
|
||||
};
|
||||
if let Some(event) = opt_event {
|
||||
runner_lock.input.raw.events.push(event);
|
||||
runner_lock.needs_repaint.set_true();
|
||||
}
|
||||
}) as Box<dyn FnMut(_)>);
|
||||
let f = on_compositionend.as_ref().unchecked_ref();
|
||||
|
|
Loading…
Reference in a new issue