mirror of
https://github.com/rust-windowing/winit.git
synced 2026-09-02 23:00:05 -04:00
fix(macos): NSTextInputClient IME commit flow
Three fixes for NSTextInputClient protocol implementation: 1. validAttributesForMarkedText now returns standard attributes (NSUnderlineStyle, NSMarkedClauseSegment) instead of an empty array. Some IMEs (e.g. LogInputMac3) require non-empty return to enter composition mode. 2. insertText:replacementRange: no longer gates commit on hasMarkedText(). Some IMEs call unmarkText before insertText, which clears the marked text and caused all commits to be silently discarded. 3. Added pending_commit flag to distinguish genuine composition commits from regular typing during IME mode (spaces, English chars), which should go through keyboard input. Fixes #3925.
This commit is contained in:
@@ -129,6 +129,9 @@ pub struct ViewState {
|
||||
ime_state: Cell<ImeState>,
|
||||
input_source: RefCell<String>,
|
||||
|
||||
/// True if this view was in a preedit session that will result in a commit.
|
||||
pending_commit: Cell<bool>,
|
||||
|
||||
/// True iff the application wants IME events.
|
||||
///
|
||||
/// Can be set using `set_ime_allowed`
|
||||
@@ -292,6 +295,7 @@ define_class!(
|
||||
// In case the preedit was cleared, set IME into the Ground state.
|
||||
self.ivars().ime_state.set(ImeState::Ground);
|
||||
}
|
||||
self.ivars().pending_commit.set(true);
|
||||
|
||||
let string = string.to_string();
|
||||
let cursor_range = if string.is_empty() {
|
||||
@@ -335,7 +339,9 @@ define_class!(
|
||||
#[unsafe(method_id(validAttributesForMarkedText))]
|
||||
fn valid_attributes_for_marked_text(&self) -> Retained<NSArray<NSAttributedStringKey>> {
|
||||
let _entered = trace_span!("validAttributesForMarkedText").entered();
|
||||
NSArray::new()
|
||||
let underline_style = NSString::from_str("NSUnderlineStyle");
|
||||
let marked_clause = NSString::from_str("NSMarkedClauseSegment");
|
||||
NSArray::from_slice(&[&*underline_style, &*marked_clause])
|
||||
}
|
||||
|
||||
#[unsafe(method_id(attributedSubstringForProposedRange:actualRange:))]
|
||||
@@ -389,10 +395,20 @@ define_class!(
|
||||
};
|
||||
|
||||
let is_control = string.chars().next().is_some_and(|c| c.is_control());
|
||||
let has_marked = self.hasMarkedText();
|
||||
let pending_commit = self.ivars().pending_commit.get();
|
||||
let ime_enabled = self.is_ime_enabled();
|
||||
|
||||
// Commit only if we have marked text.
|
||||
if self.hasMarkedText() && self.is_ime_enabled() && !is_control {
|
||||
// Clear preedit if there is marked text.
|
||||
if has_marked {
|
||||
self.queue_event(WindowEvent::Ime(Ime::Preedit(String::new(), None)));
|
||||
}
|
||||
|
||||
// Only commit via IME if there was a real composition session.
|
||||
// Some IMEs send insertText for all typing (e.g. spaces, English chars)
|
||||
// which should go through keyboard input instead of paste.
|
||||
if pending_commit && ime_enabled && !is_control {
|
||||
self.ivars().pending_commit.set(false);
|
||||
self.queue_event(WindowEvent::Ime(Ime::Commit(string)));
|
||||
self.ivars().ime_state.set(ImeState::Committed);
|
||||
}
|
||||
@@ -816,6 +832,7 @@ impl WinitView {
|
||||
phys_modifiers: Default::default(),
|
||||
ime_state: Default::default(),
|
||||
input_source: Default::default(),
|
||||
pending_commit: Default::default(),
|
||||
ime_capabilities: Default::default(),
|
||||
forward_key_to_app: Default::default(),
|
||||
marked_text: Default::default(),
|
||||
@@ -971,6 +988,7 @@ impl WinitView {
|
||||
}
|
||||
self.ivars().ime_capabilities.set(Some(capabilities));
|
||||
*self.ivars().marked_text.borrow_mut() = NSMutableAttributedString::new();
|
||||
self.ivars().pending_commit.set(false);
|
||||
}
|
||||
pub(super) fn disable_ime(&self) {
|
||||
// see above
|
||||
@@ -982,6 +1000,7 @@ impl WinitView {
|
||||
// we probably don't need to do this, but again this mirrors the prior behavior of
|
||||
// `set_ime_allowed`
|
||||
*self.ivars().marked_text.borrow_mut() = NSMutableAttributedString::new();
|
||||
self.ivars().pending_commit.set(false);
|
||||
}
|
||||
|
||||
pub(super) fn ime_capabilities(&self) -> Option<ImeCapabilities> {
|
||||
|
||||
Reference in New Issue
Block a user