diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index c36cdf7fe6..507b04bd1a 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -313,8 +313,8 @@ struct WindowState { previous_modifiers_changed_event: Option, //State tracking what the IME did after the last request ime_state: ImeState, - //Retains the last IME keystroke that was in the 'Acted' state - last_ime_key: Option, + //Retains the last IME Text + ime_text: Option, } struct InsertText { @@ -411,6 +411,8 @@ impl Window { layer, traffic_light_position: options.traffic_light_position, previous_modifiers_changed_event: None, + ime_state: ImeState::None, + ime_text: None, }))); (*native_window).set_ivar( @@ -774,6 +776,9 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent: let mut window_state_borrow = window_state.as_ref().borrow_mut(); let event = unsafe { Event::from_native(native_event, Some(window_state_borrow.size().y())) }; + + println!("Handle key event! {:?}", event); + if let Some(event) = event { if key_equivalent { window_state_borrow.performed_key_equivalent = true; @@ -811,7 +816,9 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent: let mut handled = false; let mut window_state_borrow = window_state.borrow_mut(); + let ime_text = window_state_borrow.ime_text.clone(); if let Some((event, insert_text)) = window_state_borrow.pending_key_down.take() { + let is_held = event.is_held; if let Some(mut callback) = window_state_borrow.event_callback.take() { drop(window_state_borrow); @@ -830,6 +837,18 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent: input_handler .replace_text_in_range(insert.replacement_range, &insert.text) }); + } else if !is_composing && is_held { + if let Some(last_insert_text) = ime_text { + //MacOS IME is a bit funky, and even when you've told it there's nothing to + //inter 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() { + handled = true; + input_handler.replace_text_in_range(None, &last_insert_text) + } + }); + } } } @@ -1180,11 +1199,24 @@ extern "C" fn insert_text(this: &Object, _: Sel, text: id, replacement_range: NS .unwrap(); let replacement_range = replacement_range.to_range(); + window_state.borrow_mut().ime_text = Some(text.to_string()); + window_state.borrow_mut().ime_state = ImeState::Acted; + + //Conceptually incorrect let is_composing = with_input_handler(this, |input_handler| input_handler.marked_text_range()) .flatten() .is_some(); + println!( + "Insert text, is_composing {}, text {}, have pending key down? {:?}", + is_composing, + &text, + &pending_key_down.is_some() + ); + + //Handle key event is going to be returned to soon! Make sure it + //has the new text from the IME match pending_key_down { None | Some(_) if is_composing || text.chars().count() > 1 => { with_input_handler(this, |input_handler| { @@ -1202,6 +1234,30 @@ extern "C" fn insert_text(this: &Object, _: Sel, text: id, replacement_range: NS _ => unreachable!(), } + + // if let Some(mut pending_key_down) = pending_key_down { + // pending_key_down.1 = Some(InsertText { + // replacement_range, + // text: text.to_string(), + // }); + // window_state.borrow_mut().pending_key_down = Some(pending_key_down); + + // //Modifier key combos (˜ˆ) (success / failure) + // //Press-and-hold replacements ((hold i) 1 -> î) (success / failure) + // //Pop down chinese composition menu + // //Handwriting menu + // //Emoji Picker (😤) + + // //Regression -> alt-i l only causes 'l' to be placed, instead of ˆl + // // + // } + // //If no pending key down, then handle_key_event isn't going to + // //insert the text for us. Do it ourselves + // else { + // with_input_handler(this, |input_handler| { + // input_handler.replace_text_in_range(replacement_range, text) + // }); + // } } } @@ -1213,7 +1269,8 @@ extern "C" fn set_marked_text( replacement_range: NSRange, ) { unsafe { - get_window_state(this).borrow_mut().pending_key_down.take(); + let window_state = get_window_state(this); + window_state.borrow_mut().pending_key_down.take(); let is_attributed_string: BOOL = msg_send![text, isKindOfClass: [class!(NSAttributedString)]]; @@ -1228,6 +1285,11 @@ extern "C" fn set_marked_text( .to_str() .unwrap(); + window_state.borrow_mut().ime_state = ImeState::Acted; + window_state.borrow_mut().ime_text = Some(text.to_string()); + + println!("set_marked_text({selected_range:?}, {replacement_range:?}, {text:?})"); + with_input_handler(this, |input_handler| { input_handler.replace_and_mark_text_in_range(replacement_range, text, selected_range); }); @@ -1235,6 +1297,15 @@ extern "C" fn set_marked_text( } extern "C" fn unmark_text(this: &Object, _: Sel) { + unsafe { + let state = get_window_state(this); + let mut borrow = state.borrow_mut(); + borrow.ime_state = ImeState::Acted; + borrow.ime_text.take(); + } + + println!("unmark_text()"); + with_input_handler(this, |input_handler| input_handler.unmark_text()); } @@ -1261,7 +1332,14 @@ extern "C" fn attributed_substring_for_proposed_range( .unwrap_or(nil) } -extern "C" fn do_command_by_selector(_: &Object, _: Sel, _: Sel) {} +extern "C" fn do_command_by_selector(this: &Object, _: Sel, _: Sel) { + unsafe { + let state = get_window_state(this); + let mut borrow = state.borrow_mut(); + borrow.ime_state = ImeState::Continue; + borrow.ime_text.take(); + } +} async fn synthetic_drag( window_state: Weak>,