1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-23 21:32:13 +03:00

macos: improve ime vs dead key composing

When we translate a dead key, send the composed event immediately
and don't try to route the current key press via the IME.

Improve rendering when in the composing state: overlay the composing
text at the cursor position to show what the composing text would
look like, even though it hasn't been committed to the input stream
yet.

refs: https://github.com/wez/wezterm/issues/1504
This commit is contained in:
Wez Furlong 2022-01-05 07:50:12 -07:00
parent 71dae34b75
commit 0d6fbc1aa2
3 changed files with 65 additions and 20 deletions

View File

@ -815,6 +815,9 @@ impl TermWindow {
log::trace!("DeadKeyStatus now: {:?}", status); log::trace!("DeadKeyStatus now: {:?}", status);
self.dead_key_status = status; self.dead_key_status = status;
self.update_title(); self.update_title();
// Ensure that we repaint so that any composing
// text is updated
window.invalidate();
Ok(true) Ok(true)
} }
WindowEvent::NeedRepaint => Ok(self.do_paint(window)), WindowEvent::NeedRepaint => Ok(self.do_paint(window)),

View File

@ -1730,20 +1730,45 @@ impl super::TermWindow {
let local_shaped; let local_shaped;
let cell_clusters; let cell_clusters;
let shaped = match params.pre_shaped {
Some(s) => s, // Referencing the text being composed, but only if it belongs to this pane
None => { let composing = if params.pane.is_some()
// Break the line into clusters of cells with the same attributes && params.is_active
cell_clusters = params.line.cluster(); && params.stable_line_idx == Some(params.cursor.y)
metrics::histogram!("render_screen_line_opengl.line.cluster", start.elapsed()); {
log::trace!( if let DeadKeyStatus::Composing(composing) = &self.dead_key_status {
"cluster -> {} clusters, elapsed {:?}", Some(composing)
cell_clusters.len(), } else {
start.elapsed() None
);
local_shaped = self.cluster_and_shape(&cell_clusters, &params)?;
&local_shaped
} }
} else {
None
};
// Do we need to shape immediately, or can we use the pre-shaped data?
let to_shape = if let Some(composing) = composing {
// Create an updated line with the composition overlaid
let mut line = params.line.clone();
line.overlay_text_with_attribute(
params.cursor.x,
composing,
CellAttributes::blank(),
termwiz::surface::SEQ_ZERO,
);
cell_clusters = line.cluster();
Some(&cell_clusters)
} else if params.pre_shaped.is_none() {
cell_clusters = params.line.cluster();
Some(&cell_clusters)
} else {
None
};
let shaped = if let Some(cell_clusters) = to_shape {
local_shaped = self.cluster_and_shape(&cell_clusters, &params)?;
&local_shaped
} else {
params.pre_shaped.unwrap()
}; };
let bounding_rect = euclid::rect( let bounding_rect = euclid::rect(

View File

@ -1488,12 +1488,14 @@ impl WindowView {
modifiers: Modifiers::NONE, modifiers: Modifiers::NONE,
repeat_count: 1, repeat_count: 1,
key_is_down, key_is_down,
} };
.normalize_shift();
inner.ime_text.clear();
inner
.events
.dispatch(WindowEvent::AdviseDeadKeyStatus(DeadKeyStatus::None));
inner.ime_last_event.replace(event.clone()); inner.ime_last_event.replace(event.clone());
inner.events.dispatch(WindowEvent::KeyEvent(event)); inner.events.dispatch(WindowEvent::KeyEvent(event));
inner.ime_text.clear();
inner.ime_state = ImeDisposition::Acted; inner.ime_state = ImeDisposition::Acted;
} }
} }
@ -1849,7 +1851,6 @@ impl WindowView {
let modifier_flags = unsafe { nsevent.modifierFlags() }; let modifier_flags = unsafe { nsevent.modifierFlags() };
let modifiers = key_modifiers(modifier_flags); let modifiers = key_modifiers(modifier_flags);
let virtual_key = unsafe { nsevent.keyCode() }; let virtual_key = unsafe { nsevent.keyCode() };
let translated;
log::debug!( log::debug!(
"key_common: chars=`{}` unmod=`{}` modifiers=`{:?}` virtual_key={:?} key_is_down:{}", "key_common: chars=`{}` unmod=`{}` modifiers=`{:?}` virtual_key={:?} key_is_down:{}",
@ -1928,12 +1929,18 @@ impl WindowView {
return; return;
} }
Some(Ok(s)) => { Some(Ok(translated)) => {
inner inner
.events .events
.dispatch(WindowEvent::AdviseDeadKeyStatus(DeadKeyStatus::None)); .dispatch(WindowEvent::AdviseDeadKeyStatus(DeadKeyStatus::None));
translated = s; let event = KeyEvent {
&translated key: KeyCode::composed(&translated),
modifiers: Modifiers::NONE,
repeat_count: 1,
key_is_down,
};
inner.events.dispatch(WindowEvent::KeyEvent(event));
return;
} }
Some(Err(e)) => { Some(Err(e)) => {
log::error!("Failed to translate dead key: {}", e); log::error!("Failed to translate dead key: {}", e);
@ -1994,6 +2001,7 @@ impl WindowView {
let mut inner = myself.inner.borrow_mut(); let mut inner = myself.inner.borrow_mut();
inner.key_is_down.replace(key_is_down); inner.key_is_down.replace(key_is_down);
inner.ime_state = ImeDisposition::None; inner.ime_state = ImeDisposition::None;
inner.ime_text.clear();
} }
unsafe { unsafe {
@ -2050,8 +2058,17 @@ impl WindowView {
inner.ime_last_event.as_ref().map(|e| e.clone()) inner.ime_last_event.as_ref().map(|e| e.clone())
{ {
inner.events.dispatch(WindowEvent::KeyEvent(event)); inner.events.dispatch(WindowEvent::KeyEvent(event));
return;
} }
} }
let status = if inner.ime_text.is_empty() {
DeadKeyStatus::None
} else {
DeadKeyStatus::Composing(inner.ime_text.clone())
};
inner
.events
.dispatch(WindowEvent::AdviseDeadKeyStatus(status));
return; return;
} }
} }