terminal: Implement basic Japanese IME support on macOS (#29879)
## Description This PR implements basic support for Japanese Input Method Editors (IMEs) in the Zed terminal on macOS, addressing issue #9900. Previously, users had to switch input modes to confirm Japanese text, and pre-edit (marked) text was not displayed. With these changes: - **Marked Text Display:** Pre-edit text (e.g., underlined characters during Japanese composition) is now rendered directly in the terminal at the cursor's current position. - **Composition Confirmation:** Pressing Enter correctly finalizes the IME composition, clears the marked text, and sends the confirmed string to the underlying PTY process. This allows for a more natural input flow similar to other macOS applications like iTerm2. - **State Management:** IME state (marked text and its selected range within the marked text) is now managed within the `TerminalView` struct. - **Input Handling:** `TerminalInputHandler` has been updated to correctly process IME callbacks (`replace_and_mark_text_in_range`, `replace_text_in_range`, `unmark_text`, `marked_text_range`) by interacting with `TerminalView`. - **Painting Logic:** `TerminalElement::paint` now fetches the marked text and its range from `TerminalView` and renders it with an underline. The standard terminal cursor is hidden when marked text is present to avoid visual clutter. - **Candidate Window Positioning:** `TerminalInputHandler::bounds_for_range` now attempts to provide more accurate bounds for the IME candidate window by using the actual painted bounds of the pre-edit text, falling back to a cursor-based approximation if necessary. This significantly improves the usability of the Zed terminal for users who need to input Japanese characters, bringing the experience closer to system-standard IME behavior. ## Movies https://github.com/user-attachments/assets/be6c7597-7b65-49a6-b376-e1adff6da974 --- Closes #9900 Release Notes: - **Terminal:** Implemented basic support for Japanese Input Method Editors (IMEs) on macOS. Users can now see pre-edit (marked) text as they type Japanese and confirm their input with the Enter key directly in the terminal. This provides a more natural and efficient experience for Japanese language input. (Fixes #9900) --------- Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
parent
e26620d1cf
commit
c7725e31d9
2 changed files with 121 additions and 15 deletions
|
@ -52,7 +52,7 @@ use zed_actions::assistant::InlineAssist;
|
|||
|
||||
use std::{
|
||||
cmp,
|
||||
ops::RangeInclusive,
|
||||
ops::{Range, RangeInclusive},
|
||||
path::{Path, PathBuf},
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
|
@ -126,6 +126,8 @@ pub struct TerminalView {
|
|||
scroll_handle: TerminalScrollHandle,
|
||||
show_scrollbar: bool,
|
||||
hide_scrollbar_task: Option<Task<()>>,
|
||||
marked_text: Option<String>,
|
||||
marked_range_utf16: Option<Range<usize>>,
|
||||
_subscriptions: Vec<Subscription>,
|
||||
_terminal_subscriptions: Vec<Subscription>,
|
||||
}
|
||||
|
@ -218,6 +220,8 @@ impl TerminalView {
|
|||
show_scrollbar: !Self::should_autohide_scrollbar(cx),
|
||||
hide_scrollbar_task: None,
|
||||
cwd_serialized: false,
|
||||
marked_text: None,
|
||||
marked_range_utf16: None,
|
||||
_subscriptions: vec![
|
||||
focus_in,
|
||||
focus_out,
|
||||
|
@ -227,6 +231,45 @@ impl TerminalView {
|
|||
}
|
||||
}
|
||||
|
||||
/// Sets the marked (pre-edit) text from the IME.
|
||||
pub(crate) fn set_marked_text(
|
||||
&mut self,
|
||||
text: String,
|
||||
range: Range<usize>,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.marked_text = Some(text);
|
||||
self.marked_range_utf16 = Some(range);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
/// Gets the current marked range (UTF-16).
|
||||
pub(crate) fn marked_text_range(&self) -> Option<Range<usize>> {
|
||||
self.marked_range_utf16.clone()
|
||||
}
|
||||
|
||||
/// Clears the marked (pre-edit) text state.
|
||||
pub(crate) fn clear_marked_text(&mut self, cx: &mut Context<Self>) {
|
||||
if self.marked_text.is_some() {
|
||||
self.marked_text = None;
|
||||
self.marked_range_utf16 = None;
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
/// Commits (sends) the given text to the PTY. Called by InputHandler::replace_text_in_range.
|
||||
pub(crate) fn commit_text(&mut self, text: &str, cx: &mut Context<Self>) {
|
||||
if !text.is_empty() {
|
||||
self.terminal.update(cx, |term, _| {
|
||||
term.input(text.to_string());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn terminal_bounds(&self, cx: &App) -> TerminalBounds {
|
||||
self.terminal.read(cx).last_content().terminal_bounds
|
||||
}
|
||||
|
||||
pub fn entity(&self) -> &Entity<Terminal> {
|
||||
&self.terminal
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue