Skip to content

Re-add IME support #762

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

Merged
merged 14 commits into from
Nov 30, 2024
76 changes: 54 additions & 22 deletions masonry/src/passes/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ use std::collections::HashSet;
use cursor_icon::CursorIcon;
use tracing::{info_span, trace};

use crate::passes::event::run_on_pointer_event_pass;
use crate::passes::event::{run_on_pointer_event_pass, run_on_text_event_pass};
use crate::passes::{enter_span, enter_span_if, merge_state_up, recurse_on_children};
use crate::render_root::{RenderRoot, RenderRootSignal, RenderRootState};
use crate::tree_arena::ArenaMut;
use crate::{
PointerEvent, QueryCtx, RegisterCtx, Update, UpdateCtx, Widget, WidgetId, WidgetState,
PointerEvent, QueryCtx, RegisterCtx, TextEvent, Update, UpdateCtx, Widget, WidgetId,
WidgetState,
};

// --- MARK: HELPERS ---
Expand Down Expand Up @@ -393,7 +394,7 @@ pub(crate) fn run_update_focus_chain_pass(root: &mut RenderRoot) {
// --- MARK: UPDATE FOCUS ---
pub(crate) fn run_update_focus_pass(root: &mut RenderRoot) {
let _span = info_span!("update_focus").entered();
// If the focused widget is disabled, stashed or removed, we set
// If the next-focused widget is disabled, stashed or removed, we set
// the focused id to None
if let Some(id) = root.global_state.next_focused_widget {
if !root.is_still_interactive(id) {
Expand All @@ -402,6 +403,43 @@ pub(crate) fn run_update_focus_pass(root: &mut RenderRoot) {
}

let prev_focused = root.global_state.focused_widget;
let was_ime_active = root.global_state.is_ime_active;

if was_ime_active && prev_focused != root.global_state.next_focused_widget {
// IME was active, but the next focused widget is going to receive the Ime::Disabled event
// sent by the platform. Synthesize an `Ime::Disabled` event here and send it to the widget
// about to be unfocused.
run_on_text_event_pass(root, &TextEvent::Ime(winit::event::Ime::Disabled));

// Disable the IME, which was enabled specifically for this widget. Note that if the newly
// focused widget also requires IME, we will request it again - this resets the platform's
// state, ensuring that partial IME inputs do not "travel" between widgets
root.global_state.emit_signal(RenderRootSignal::EndIme);

// Note: handling of the Ime::Disabled event sent above may have changed the next focused
// widget. In particular, focus may have changed back to the original widget we just
// disabled IME for.
//
// In this unlikely case, the rest of this handler will short-circuit, and IME would not be
// re-enabled for this widget. Re-enable IME here; the resultant `Ime::Enabled` event sent
// by the platform will be routed to this widget as it remains the focused widget. We don't
// handle this as above to avoid loops.
//
// First do the disabled, stashed or removed check again.
if let Some(id) = root.global_state.next_focused_widget {
if !root.is_still_interactive(id) {
root.global_state.next_focused_widget = None;
}
}
if prev_focused == root.global_state.next_focused_widget {
tracing::warn!(
id = prev_focused.map(|id| id.trace()),
"request_focus called whilst handling Ime::Disabled"
);
root.global_state.emit_signal(RenderRootSignal::StartIme);
}
}

let next_focused = root.global_state.next_focused_widget;

// "Focused path" means the focused widget, and all its parents.
Expand Down Expand Up @@ -458,15 +496,8 @@ pub(crate) fn run_update_focus_pass(root: &mut RenderRoot) {
}
}

// Refocus if the focused widget changed.
if prev_focused != next_focused {
let was_ime_active = root.global_state.is_ime_active;
let is_ime_active = if let Some(id) = next_focused {
root.widget_arena.get_state(id).item.accepts_text_input
} else {
false
};
root.global_state.is_ime_active = is_ime_active;

// We send FocusChange event to widget that lost and the widget that gained focus.
// We also request accessibility, because build_access_node() depends on the focus state.
if let Some(prev_focused) = prev_focused {
Expand All @@ -485,23 +516,24 @@ pub(crate) fn run_update_focus_pass(root: &mut RenderRoot) {
ctx.widget_state.request_accessibility = true;
ctx.widget_state.needs_accessibility = true;
});
}

if prev_focused.is_some() && was_ime_active {
root.global_state.emit_signal(RenderRootSignal::EndIme);
}
if next_focused.is_some() && is_ime_active {
root.global_state.emit_signal(RenderRootSignal::StartIme);
}
let widget_state = root.widget_arena.get_state(next_focused).item;

root.global_state.is_ime_active = widget_state.accepts_text_input;
if widget_state.accepts_text_input {
root.global_state.emit_signal(RenderRootSignal::StartIme);
}

if let Some(id) = next_focused {
let ime_area = root.widget_arena.get_state(id).item.get_ime_area();
root.global_state
.emit_signal(RenderRootSignal::new_ime_moved_signal(ime_area));
.emit_signal(RenderRootSignal::new_ime_moved_signal(
widget_state.get_ime_area(),
));
} else {
root.global_state.is_ime_active = false;
}
}

root.global_state.focused_widget = root.global_state.next_focused_widget;
root.global_state.focused_widget = next_focused;
root.global_state.focused_path = next_focused_path;
}

Expand Down
Loading