IME: reset editor (changed input focus)
So, in the normal case:
- A user clicks on an IME-supporting input field, thus the app calls
window.set_ime_allowed(true)and (optionally)window.set_ime_purpose(..) - The app receives
Ime::Enabled, and callswindow.set_ime_cursor_area(also causing itself to call this method again any time the area changes) - The input field handles
Ime::Preedit,ImeCommit, ... - The input field loses focus: the app calls
window.set_ime_allowed(false)and receivesIme::Disabled(or potentially the other way around)
But if instead during step 3 (while the field has IME focus), the user clicks on a different IME-supporting input field. What should the app do?
- Only call
window.set_ime_purpose(..)andwindow.set_ime_cursor_area(..)? But then the IME editor is not notified of a context switch and will presumably copy the current content into the new input field. - Call
window.set_ime_allowed(false)then immediately callwindow.set_ime_allowed(true)? This may reset the IME editor (not tested), but presents a new problem: when the app receivesIme::Disabledit does not know whether this is due to the context switch or due to some external cause cancelling all IME input. - Call
window.set_ime_allowed(false)then wait until receivingIme::Disabledbefore callingwindow.set_ime_allowed(true)?
Documentation could be improved here.
Potentially it would also be useful if I could pass a u64 identifier when enabling IME to identify the input ~~field~~ location receiving focus, with this identifier returned through all Ime events: this way my code can be certain that the correct input field is being notified that IME was enabled/disabled and is receiving IME input.
Potentially it would be useful to know whether Ime::Disabled is received due to a call to set_ime_allowed(false) or due to some external cause such as the app losing focus. (This is not really important if an identifier is included as in the previous paragraph.)
Further note: moving the text cursor within a text field should also reset the IME.
According to the wayland_protocols docs, change of focus should be handled via disable/enable and "the compositor must be able to handle consecutive series of the same request".
Also missing in winit's API is the ability to set the surrounding text, to specify the cause of change to the surrounding text, and to commit.
Wayland supports many more ContentPurpose variants than winit.
I see @kchibisov wrote a similar comment five years ago, so this is nothing new... yet the status of #1497 indicates that the API is done.
According to the wayland_protocols docs, change of focus should be handled via disable/enable and "the compositor must be able to handle consecutive series of the same request".
When you have focus in your app, you're the one doing enable/disable. This API is present.
Also missing in winit's API is the ability to set the surrounding text, to specify the cause of change to the surrounding text, and to commit.
commit is internal detail, surrounding text API is indeed missing, and someone is free to add it.
The big chunk of API is done, the rest could be added on demand. The issue is also not really closed and IIRC we have individual issues for smaller parts.
Call window.set_ime_allowed(false) then immediately call window.set_ime_allowed(true)? This may reset the IME editor (not tested), but presents a new problem: when the app receives Ime::Disabled it does not know whether this is due to the context switch or due to some external cause cancelling all IME input.
You should disable/enable.
Potentially it would be useful to know whether Ime::Disabled is received due to a call to set_ime_allowed(false) or due to some external cause such as the app losing focus. (This is not really important if an identifier is included as in the previous paragraph.)
This sounds fine. We can add Enter/Left for that reason.
Potentially it would also be useful if I could pass a u64 identifier when enabling IME to identify the input field location receiving focus, with this identifier returned through all Ime events: this way my code can be certain that the correct input field is being notified that IME was enabled/disabled and is receiving IME input.
This one is solved via disable/enable being dedicated event, you know exactly which field is targeted, since in case of change, the events will arrive sequentially.
When you have focus in your app, you're the one doing enable/disable. This API is present.
This much appears to be fine. And I seem to be having success with code like this, where old_ime_target is set when changing/clearing IME focus:
Ime(winit::event::Ime::Disabled) => {
let mut target = self.old_ime_target.take();
if target.is_none() && self.ime.is_some() {
target = self.sel_focus.clone();
self.ime = None;
}
if let Some(id) = target {
// Notify my widget that it lost IME focus
self.send_event(win.as_node(data), id, Event::LostImeFocus);
}
}
I think the main gap here is really just winit's API docs clarifying that it is expected to call set_ime_allowed(false) then set_ime_allowed(true) when the text position or input field focus changes.
Clarification wanted: are spurious calls to set_ime_allowed(true) ineffective or do they reset the IME? If these are ineffective then it should be safe to merge set_ime_purpose into the same function (i.e. fn set_ime_allowed(purpose: Option<ImePurpose>)).
So I think this issue could be closed with only doc changes.
I'd say that separate event for who closed sounds fine. That why you don't have to deal with the LostImeFocus yourself.
Clarification wanted: are spurious calls to set_ime_allowed(true) ineffective or do they reset the IME? If these are ineffective then it should be safe to merge set_ime_purpose into the same function (i.e. fn set_ime_allowed(purpose: Option<ImePurpose>)).
I think it's more like you communicate the intent, the state is send back to you eventually. In general, I'd change the API to set_ime_state and in the state you can enable/disable and set all other sort of properties to better accommodate for atomic APIs like Wayland.
Yes, those changes would be nice. A to-do list to recap:
- [ ] Let
Ime::Disableddistinguish the source: requested by the user or external - [x] Potentially merge fns
set_ime_allowedandset_ime_purposeinto a new fn,set_ime_state - [x] Add ability to set surrounding text, likely similar to wayland-protocols.
Link to my implementation: https://github.com/kas-gui/kas/pull/497
Yeah, I'd just merge everything in a builder like thing which you pass in, so it'll be atomically applied.
We've merged Window::request_ime_update that does resolve parts of the issue.
I don't think there's value in keeping this open any more: changes are mostly implemented (thanks!).
The exception is knowing the cause of Ime::Disabled, but I think it's not a problem: the application can track whether or not this has been requested.