Disable the IME on Windows when text input is unexpected (#51041)
Fixes #42444 - Changed `accepts_text_input` on the editor to be more precise. Previously, it returned `true` only in insert mode. Now it also returns `true` when an operator is pending. - On Windows, we disable the IME whenever there is no input handler which `accepts_text_input`. - How this improves Vim mode: in insert mode, the IME is enabled; in normal mode, it is disabled (command keys are not intercepted); when an operator is pending, the IME is re-enabled. Release Notes: - On Windows, the IME is disabled in Vim normal and visual modes.
This commit is contained in:
1 parent
b89152e516
commit
850943f89e
5 files changed
+111
-22
No files matched your search
@@ -1233,6 +1233,7 @@ pub struct Editor {
|
||||
autoindent_mode: Option<AutoindentMode>,
|
||||
workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
|
||||
input_enabled: bool,
|
||||
expects_character_input: bool,
|
||||
use_modal_editing: bool,
|
||||
read_only: bool,
|
||||
leader_id: Option<CollaboratorId>,
|
||||
@@ -2469,6 +2470,7 @@ impl Editor {
|
||||
collapse_matches: false,
|
||||
workspace: None,
|
||||
input_enabled: !is_minimap,
|
||||
expects_character_input: !is_minimap,
|
||||
use_modal_editing: full_mode,
|
||||
read_only: is_minimap,
|
||||
use_autoclose: true,
|
||||
@@ -3365,6 +3367,10 @@ impl Editor {
|
||||
self.input_enabled = input_enabled;
|
||||
}
|
||||
|
||||
pub fn set_expects_character_input(&mut self, expects_character_input: bool) {
|
||||
self.expects_character_input = expects_character_input;
|
||||
}
|
||||
|
||||
pub fn set_edit_predictions_hidden_for_vim_mode(
|
||||
&mut self,
|
||||
hidden: bool,
|
||||
@@ -28409,7 +28415,7 @@ impl EntityInputHandler for Editor {
|
||||
}
|
||||
|
||||
fn accepts_text_input(&self, _window: &mut Window, _cx: &mut Context<Self>) -> bool {
|
||||
self.input_enabled
|
||||
self.expects_character_input
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1062,6 +1062,13 @@ impl PlatformInputHandler {
|
||||
pub fn accepts_text_input(&mut self, window: &mut Window, cx: &mut App) -> bool {
|
||||
self.handler.accepts_text_input(window, cx)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn query_accepts_text_input(&mut self) -> bool {
|
||||
self.cx
|
||||
.update(|window, cx| self.handler.accepts_text_input(window, cx))
|
||||
.unwrap_or(true)
|
||||
}
|
||||
}
|
||||
|
||||
/// A struct representing a selection in a text buffer, in UTF16 characters.
|
||||
|
||||
@@ -593,33 +593,63 @@ impl WindowsWindowInner {
|
||||
}
|
||||
|
||||
pub(crate) fn update_ime_position(&self, handle: HWND, caret_position: POINT) {
|
||||
let Some(ctx) = ImeContext::get(handle) else {
|
||||
return;
|
||||
};
|
||||
unsafe {
|
||||
let ctx = ImmGetContext(handle);
|
||||
if ctx.is_invalid() {
|
||||
return;
|
||||
}
|
||||
ImmSetCompositionWindow(
|
||||
*ctx,
|
||||
&COMPOSITIONFORM {
|
||||
dwStyle: CFS_POINT,
|
||||
ptCurrentPos: caret_position,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.ok()
|
||||
.log_err();
|
||||
|
||||
let config = COMPOSITIONFORM {
|
||||
dwStyle: CFS_POINT,
|
||||
ptCurrentPos: caret_position,
|
||||
..Default::default()
|
||||
};
|
||||
ImmSetCompositionWindow(ctx, &config).ok().log_err();
|
||||
let config = CANDIDATEFORM {
|
||||
dwStyle: CFS_CANDIDATEPOS,
|
||||
ptCurrentPos: caret_position,
|
||||
..Default::default()
|
||||
};
|
||||
ImmSetCandidateWindow(ctx, &config).ok().log_err();
|
||||
ImmReleaseContext(handle, ctx).ok().log_err();
|
||||
ImmSetCandidateWindow(
|
||||
*ctx,
|
||||
&CANDIDATEFORM {
|
||||
dwStyle: CFS_CANDIDATEPOS,
|
||||
ptCurrentPos: caret_position,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.ok()
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
|
||||
fn update_ime_enabled(&self, handle: HWND) {
|
||||
let ime_enabled = self
|
||||
.with_input_handler(|input_handler| input_handler.query_accepts_text_input())
|
||||
.unwrap_or(false);
|
||||
if ime_enabled == self.state.ime_enabled.get() {
|
||||
return;
|
||||
}
|
||||
self.state.ime_enabled.set(ime_enabled);
|
||||
unsafe {
|
||||
if ime_enabled {
|
||||
ImmAssociateContextEx(handle, HIMC::default(), IACE_DEFAULT)
|
||||
.ok()
|
||||
.log_err();
|
||||
} else {
|
||||
if let Some(ctx) = ImeContext::get(handle) {
|
||||
ImmNotifyIME(*ctx, NI_COMPOSITIONSTR, CPS_COMPLETE, 0)
|
||||
.ok()
|
||||
.log_err();
|
||||
}
|
||||
ImmAssociateContextEx(handle, HIMC::default(), 0)
|
||||
.ok()
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_ime_composition(&self, handle: HWND, lparam: LPARAM) -> Option<isize> {
|
||||
let ctx = unsafe { ImmGetContext(handle) };
|
||||
let result = self.handle_ime_composition_inner(ctx, lparam);
|
||||
unsafe { ImmReleaseContext(handle, ctx).ok().log_err() };
|
||||
result
|
||||
let ctx = ImeContext::get(handle)?;
|
||||
self.handle_ime_composition_inner(*ctx, lparam)
|
||||
}
|
||||
|
||||
fn handle_ime_composition_inner(&self, ctx: HIMC, lparam: LPARAM) -> Option<isize> {
|
||||
@@ -1123,6 +1153,7 @@ impl WindowsWindowInner {
|
||||
});
|
||||
|
||||
self.state.callbacks.request_frame.set(Some(request_frame));
|
||||
self.update_ime_enabled(handle);
|
||||
unsafe { ValidateRect(Some(handle), None).ok().log_err() };
|
||||
|
||||
Some(0)
|
||||
@@ -1205,6 +1236,36 @@ impl WindowsWindowInner {
|
||||
}
|
||||
}
|
||||
|
||||
struct ImeContext {
|
||||
hwnd: HWND,
|
||||
himc: HIMC,
|
||||
}
|
||||
|
||||
impl ImeContext {
|
||||
fn get(hwnd: HWND) -> Option<Self> {
|
||||
let himc = unsafe { ImmGetContext(hwnd) };
|
||||
if himc.is_invalid() {
|
||||
return None;
|
||||
}
|
||||
Some(Self { hwnd, himc })
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for ImeContext {
|
||||
type Target = HIMC;
|
||||
fn deref(&self) -> &HIMC {
|
||||
&self.himc
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ImeContext {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
ImmReleaseContext(self.hwnd, self.himc).ok().log_err();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_key_event<F>(
|
||||
wparam: WPARAM,
|
||||
lparam: LPARAM,
|
||||
|
||||
@@ -52,6 +52,7 @@ pub struct WindowsWindowState {
|
||||
|
||||
pub callbacks: Callbacks,
|
||||
pub input_handler: Cell<Option<PlatformInputHandler>>,
|
||||
pub ime_enabled: Cell<bool>,
|
||||
pub pending_surrogate: Cell<Option<u16>>,
|
||||
pub last_reported_modifiers: Cell<Option<Modifiers>>,
|
||||
pub last_reported_capslock: Cell<Option<Capslock>>,
|
||||
@@ -142,6 +143,7 @@ impl WindowsWindowState {
|
||||
min_size,
|
||||
callbacks,
|
||||
input_handler: Cell::new(input_handler),
|
||||
ime_enabled: Cell::new(true),
|
||||
pending_surrogate: Cell::new(pending_surrogate),
|
||||
last_reported_modifiers: Cell::new(last_reported_modifiers),
|
||||
last_reported_capslock: Cell::new(last_reported_capslock),
|
||||
|
||||
@@ -978,6 +978,7 @@ impl Vim {
|
||||
editor.set_clip_at_line_ends(false, cx);
|
||||
editor.set_collapse_matches(false);
|
||||
editor.set_input_enabled(true);
|
||||
editor.set_expects_character_input(true);
|
||||
editor.set_autoindent(true);
|
||||
editor.selections.set_line_mode(false);
|
||||
editor.unregister_addon::<VimAddon>();
|
||||
@@ -1346,6 +1347,15 @@ impl Vim {
|
||||
}
|
||||
}
|
||||
|
||||
fn expects_character_input(&self) -> bool {
|
||||
if let Some(operator) = self.operator_stack.last() {
|
||||
if operator.is_waiting(self.mode) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
self.editor_input_enabled()
|
||||
}
|
||||
|
||||
pub fn editor_input_enabled(&self) -> bool {
|
||||
match self.mode {
|
||||
Mode::Insert => {
|
||||
@@ -2058,6 +2068,7 @@ impl Vim {
|
||||
clip_at_line_ends: self.clip_at_line_ends(),
|
||||
collapse_matches: !HelixModeSetting::get_global(cx).0,
|
||||
input_enabled: self.editor_input_enabled(),
|
||||
expects_character_input: self.expects_character_input(),
|
||||
autoindent: self.should_autoindent(),
|
||||
cursor_offset_on_selection: self.mode.is_visual(),
|
||||
line_mode: matches!(self.mode, Mode::VisualLine),
|
||||
@@ -2075,6 +2086,7 @@ impl Vim {
|
||||
editor.set_clip_at_line_ends(state.clip_at_line_ends, cx);
|
||||
editor.set_collapse_matches(state.collapse_matches);
|
||||
editor.set_input_enabled(state.input_enabled);
|
||||
editor.set_expects_character_input(state.expects_character_input);
|
||||
editor.set_autoindent(state.autoindent);
|
||||
editor.set_cursor_offset_on_selection(state.cursor_offset_on_selection);
|
||||
editor.selections.set_line_mode(state.line_mode);
|
||||
@@ -2087,6 +2099,7 @@ struct VimEditorSettingsState {
|
||||
clip_at_line_ends: bool,
|
||||
collapse_matches: bool,
|
||||
input_enabled: bool,
|
||||
expects_character_input: bool,
|
||||
autoindent: bool,
|
||||
cursor_offset_on_selection: bool,
|
||||
line_mode: bool,
|
||||
|
||||
Reference in new issue
Block a user