diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 9962a50175..a905254a8b 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -10879,7 +10879,7 @@ impl Editor { let snapshot = buffer.read(cx).snapshot(); let range = self - .selected_text_range(cx) + .selected_text_range(false, cx) .and_then(|(selected_range, _)| { if selected_range.is_empty() { None @@ -11619,10 +11619,14 @@ impl ViewInputHandler for Editor { ) } - fn selected_text_range(&mut self, cx: &mut ViewContext) -> Option<(Range, bool)> { + fn selected_text_range( + &mut self, + ignore_disabled_input: bool, + cx: &mut ViewContext, + ) -> Option<(Range, bool)> { // Prevent the IME menu from appearing when holding down an alphabetic key // while input is disabled. - if !self.input_enabled { + if !ignore_disabled_input && !self.input_enabled { return None; } diff --git a/crates/gpui/src/input.rs b/crates/gpui/src/input.rs index 09eef04119..7074529a7d 100644 --- a/crates/gpui/src/input.rs +++ b/crates/gpui/src/input.rs @@ -13,7 +13,11 @@ pub trait ViewInputHandler: 'static + Sized { -> Option; /// See [`InputHandler::selected_text_range`] for details - fn selected_text_range(&mut self, cx: &mut ViewContext) -> Option<(Range, bool)>; + fn selected_text_range( + &mut self, + ignore_disabled_input: bool, + cx: &mut ViewContext, + ) -> Option<(Range, bool)>; /// See [`InputHandler::marked_text_range`] for details fn marked_text_range(&self, cx: &mut ViewContext) -> Option>; @@ -68,9 +72,14 @@ impl ElementInputHandler { } impl InputHandler for ElementInputHandler { - fn selected_text_range(&mut self, cx: &mut WindowContext) -> Option<(Range, bool)> { - self.view - .update(cx, |view, cx| view.selected_text_range(cx)) + fn selected_text_range( + &mut self, + ignore_disabled_input: bool, + cx: &mut WindowContext, + ) -> Option<(Range, bool)> { + self.view.update(cx, |view, cx| { + view.selected_text_range(ignore_disabled_input, cx) + }) } fn marked_text_range(&mut self, cx: &mut WindowContext) -> Option> { diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 32111ba32d..0a9b1290a0 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -247,7 +247,7 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle { fn start_system_move(&self); fn should_render_window_controls(&self) -> bool; - fn update_ime_position(&self) {} + fn update_ime_position(&self, _bounds: Bounds) {} #[cfg(any(test, feature = "test-support"))] fn as_test(&mut self) -> Option<&mut TestWindow> { @@ -403,9 +403,9 @@ impl PlatformInputHandler { Self { cx, handler } } - fn selected_text_range(&mut self) -> Option<(Range, bool)> { + fn selected_text_range(&mut self, ignore_disabled_input: bool) -> Option<(Range, bool)> { self.cx - .update(|cx| self.handler.selected_text_range(cx)) + .update(|cx| self.handler.selected_text_range(ignore_disabled_input, cx)) .ok() .flatten() } @@ -466,6 +466,20 @@ impl PlatformInputHandler { pub(crate) fn dispatch_input(&mut self, input: &str, cx: &mut WindowContext) { self.handler.replace_text_in_range(None, input, cx); } + + pub fn bounds(&mut self, cx: &mut WindowContext) -> Option> { + let Some((pos, rev)) = self.handler.selected_text_range(true, cx) else { + return None; + }; + self.handler.bounds_for_range( + if rev { + pos.start..pos.start + } else { + pos.end..pos.end + }, + cx, + ) + } } /// Zed's interface for handling text input from the platform's IME system @@ -477,7 +491,11 @@ pub trait InputHandler: 'static { /// Corresponds to [selectedRange()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438242-selectedrange) /// /// Return value is in terms of UTF-16 characters, from 0 to the length of the document - fn selected_text_range(&mut self, cx: &mut WindowContext) -> Option<(Range, bool)>; + fn selected_text_range( + &mut self, + ignore_disabled_input: bool, + cx: &mut WindowContext, + ) -> Option<(Range, bool)>; /// Get the range of the currently marked text, if any /// Corresponds to [markedRange()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438250-markedrange) diff --git a/crates/gpui/src/platform/linux/wayland/client.rs b/crates/gpui/src/platform/linux/wayland/client.rs index 8cc32bfa1c..ae8bbaa52b 100644 --- a/crates/gpui/src/platform/linux/wayland/client.rs +++ b/crates/gpui/src/platform/linux/wayland/client.rs @@ -283,28 +283,21 @@ impl WaylandClientStatePtr { } } - pub fn update_ime_position(&self) { + pub fn update_ime_position(&self, bounds: Bounds) { let client = self.get_client(); let mut state = client.borrow_mut(); if state.composing || state.text_input.is_none() { return; } - let Some(window) = state.keyboard_focused_window.clone() else { - return; - }; - let text_input = state.text_input.take().unwrap(); - drop(state); - if let Some(bounds) = window.get_ime_area() { - text_input.set_cursor_rectangle( - bounds.origin.x.0 as i32, - bounds.origin.y.0 as i32, - bounds.size.width.0 as i32, - bounds.size.height.0 as i32, - ); - text_input.commit(); - } - client.borrow_mut().text_input = Some(text_input); + let text_input = state.text_input.as_ref().unwrap(); + text_input.set_cursor_rectangle( + bounds.origin.x.0 as i32, + bounds.origin.y.0 as i32, + bounds.size.width.0 as i32, + bounds.size.height.0 as i32, + ); + text_input.commit(); } pub fn drop_window(&self, surface_id: &ObjectId) { diff --git a/crates/gpui/src/platform/linux/wayland/window.rs b/crates/gpui/src/platform/linux/wayland/window.rs index 1f25514a6e..a37c54f25e 100644 --- a/crates/gpui/src/platform/linux/wayland/window.rs +++ b/crates/gpui/src/platform/linux/wayland/window.rs @@ -475,7 +475,7 @@ impl WaylandWindowStatePtr { let mut bounds: Option> = None; if let Some(mut input_handler) = state.input_handler.take() { drop(state); - if let Some((range, reversed)) = input_handler.selected_text_range() { + if let Some((range, reversed)) = input_handler.selected_text_range(true) { bounds = input_handler.bounds_for_range(if reversed { range.start..range.start } else { @@ -863,16 +863,11 @@ impl PlatformWindow for WaylandWindow { self.borrow().decoration_state == WaylandDecorationState::Client } - fn update_ime_position(&self) { + fn update_ime_position(&self, bounds: Bounds) { let state = self.borrow(); let client = state.client.clone(); - state - .globals - .executor - .spawn(async move { - client.update_ime_position(); - }) - .detach(); + drop(state); + client.update_ime_position(bounds); } } diff --git a/crates/gpui/src/platform/linux/x11/client.rs b/crates/gpui/src/platform/linux/x11/client.rs index 33c8fd1c08..74c0d3c913 100644 --- a/crates/gpui/src/platform/linux/x11/client.rs +++ b/crates/gpui/src/platform/linux/x11/client.rs @@ -156,46 +156,38 @@ impl X11ClientStatePtr { } } - pub fn update_ime_position(&self) { + pub fn update_ime_position(&self, bounds: Bounds) { let client = self.get_client(); let mut state = client.0.borrow_mut(); if state.composing || state.ximc.is_none() { return; } - let Some(window_id) = state.focused_window else { - return; - }; let mut ximc = state.ximc.take().unwrap(); let xim_handler = state.xim_handler.take().unwrap(); - drop(state); - let window = client.get_window(window_id).unwrap(); - if let Some(area) = window.get_ime_area() { - let ic_attributes = ximc - .build_ic_attributes() - .push( - xim::AttributeName::InputStyle, - xim::InputStyle::PREEDIT_CALLBACKS - | xim::InputStyle::STATUS_NOTHING - | xim::InputStyle::PREEDIT_POSITION, - ) - .push(xim::AttributeName::ClientWindow, xim_handler.window) - .push(xim::AttributeName::FocusWindow, xim_handler.window) - .nested_list(xim::AttributeName::PreeditAttributes, |b| { - b.push( - xim::AttributeName::SpotLocation, - xim::Point { - x: u32::from(area.origin.x + area.size.width) as i16, - y: u32::from(area.origin.y + area.size.height) as i16, - }, - ); - }) - .build(); - let _ = ximc - .set_ic_values(xim_handler.im_id, xim_handler.ic_id, ic_attributes) - .log_err(); - } - state = client.0.borrow_mut(); + let ic_attributes = ximc + .build_ic_attributes() + .push( + xim::AttributeName::InputStyle, + xim::InputStyle::PREEDIT_CALLBACKS + | xim::InputStyle::STATUS_NOTHING + | xim::InputStyle::PREEDIT_POSITION, + ) + .push(xim::AttributeName::ClientWindow, xim_handler.window) + .push(xim::AttributeName::FocusWindow, xim_handler.window) + .nested_list(xim::AttributeName::PreeditAttributes, |b| { + b.push( + xim::AttributeName::SpotLocation, + xim::Point { + x: u32::from(bounds.origin.x + bounds.size.width) as i16, + y: u32::from(bounds.origin.y + bounds.size.height) as i16, + }, + ); + }) + .build(); + let _ = ximc + .set_ic_values(xim_handler.im_id, xim_handler.ic_id, ic_attributes) + .log_err(); state.ximc = Some(ximc); state.xim_handler = Some(xim_handler); } diff --git a/crates/gpui/src/platform/linux/x11/window.rs b/crates/gpui/src/platform/linux/x11/window.rs index 87d82dd376..f59b7102b2 100644 --- a/crates/gpui/src/platform/linux/x11/window.rs +++ b/crates/gpui/src/platform/linux/x11/window.rs @@ -596,7 +596,7 @@ impl X11WindowStatePtr { let mut bounds: Option> = None; if let Some(mut input_handler) = state.input_handler.take() { drop(state); - if let Some((range, _)) = input_handler.selected_text_range() { + if let Some((range, _)) = input_handler.selected_text_range(true) { bounds = input_handler.bounds_for_range(range); } let mut state = self.state.borrow_mut(); @@ -959,14 +959,10 @@ impl PlatformWindow for X11Window { false } - fn update_ime_position(&self) { + fn update_ime_position(&self, bounds: Bounds) { let mut state = self.0.state.borrow_mut(); let client = state.client.clone(); - state - .executor - .spawn(async move { - client.update_ime_position(); - }) - .detach(); + drop(state); + client.update_ime_position(bounds); } } diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index 6f9330607f..c3bb51bb7c 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -1303,7 +1303,7 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent: // enter it will still swallow certain keys (e.g. 'f', 'j') and not others // (e.g. 'n'). This is a problem for certain kinds of views, like the terminal. with_input_handler(this, |input_handler| { - if input_handler.selected_text_range().is_none() { + if input_handler.selected_text_range(false).is_none() { handled = true; input_handler.replace_text_in_range(None, &text) } diff --git a/crates/gpui/src/platform/test/window.rs b/crates/gpui/src/platform/test/window.rs index bdbc87e2f5..fa0f68426b 100644 --- a/crates/gpui/src/platform/test/window.rs +++ b/crates/gpui/src/platform/test/window.rs @@ -270,7 +270,7 @@ impl PlatformWindow for TestWindow { false } - fn update_ime_position(&self) {} + fn update_ime_position(&mut self) {} } pub(crate) struct TestAtlasState { diff --git a/crates/gpui/src/platform/windows/events.rs b/crates/gpui/src/platform/windows/events.rs index 21afa96848..c3ad28617e 100644 --- a/crates/gpui/src/platform/windows/events.rs +++ b/crates/gpui/src/platform/windows/events.rs @@ -578,7 +578,7 @@ fn handle_ime_position(handle: HWND, state_ptr: Rc) -> Op let scale_factor = lock.scale_factor; drop(lock); - let Some((caret_range, _)) = input_handler.selected_text_range() else { + let Some((caret_range, _)) = input_handler.selected_text_range(false) else { state_ptr.state.borrow_mut().input_handler = Some(input_handler); return Some(0); }; diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 9afb6e5113..cdc453ff9a 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -3407,8 +3407,15 @@ impl<'a> WindowContext<'a> { } /// Updates the IME panel position suggestions for languages like japanese, chinese. - pub fn update_ime_position(&self) { - self.window.platform_window.update_ime_position(); + pub fn update_ime_position(&mut self) { + self.on_next_frame(|cx| { + if let Some(mut input_handler) = cx.window.platform_window.take_input_handler() { + if let Some(bounds) = input_handler.bounds(cx) { + cx.window.platform_window.update_ime_position(bounds); + } + cx.window.platform_window.set_input_handler(input_handler); + } + }); } /// Present a platform dialog. diff --git a/crates/terminal_view/src/terminal_element.rs b/crates/terminal_view/src/terminal_element.rs index c334ea77f2..703dac7313 100644 --- a/crates/terminal_view/src/terminal_element.rs +++ b/crates/terminal_view/src/terminal_element.rs @@ -889,6 +889,7 @@ struct TerminalInputHandler { impl InputHandler for TerminalInputHandler { fn selected_text_range( &mut self, + _ignore_disabled_input: bool, cx: &mut WindowContext, ) -> Option<(std::ops::Range, bool)> { if self diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index cdc40c0e65..0d79d399d7 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -2554,7 +2554,7 @@ impl Workspace { } pane::Event::Remove => self.remove_pane(pane, cx), pane::Event::ActivateItem { local } => { - cx.window_context().on_next_frame(|cx| { + cx.on_next_frame(|_, cx| { cx.update_ime_position(); }); pane.model.update(cx, |pane, _| { @@ -2584,7 +2584,7 @@ impl Workspace { } } pane::Event::Focus => { - cx.window_context().on_next_frame(|cx| { + cx.on_next_frame(|_, cx| { cx.update_ime_position(); }); self.handle_pane_focused(pane.clone(), cx);