Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add composition event on macOS #1979

Merged
merged 14 commits into from Aug 9, 2021
80 changes: 71 additions & 9 deletions src/platform_impl/macos/view.rs
Expand Up @@ -57,6 +57,9 @@ pub(super) struct ViewState {
raw_characters: Option<String>,
pub(super) modifiers: ModifiersState,
tracking_rect: Option<NSInteger>,
is_ime_activated: bool,
is_preediting: bool,
marked_text: String,
}

impl ViewState {
Expand All @@ -75,6 +78,9 @@ pub fn new_view(ns_window: id) -> (IdRef, Weak<Mutex<CursorState>>) {
raw_characters: None,
modifiers: Default::default(),
tracking_rect: None,
is_ime_activated: false,
is_preediting: false,
marked_text: String::new(),
};
unsafe {
// This is free'd in `dealloc`
Expand Down Expand Up @@ -147,7 +153,10 @@ lazy_static! {
sel!(setMarkedText:selectedRange:replacementRange:),
set_marked_text as extern "C" fn(&mut Object, Sel, id, NSRange, NSRange),
);
decl.add_method(sel!(unmarkText), unmark_text as extern "C" fn(&Object, Sel));
decl.add_method(
sel!(unmarkText),
unmark_text as extern "C" fn(&mut Object, Sel),
);
decl.add_method(
sel!(validAttributesForMarkedText),
valid_attributes_for_marked_text as extern "C" fn(&Object, Sel) -> id,
Expand All @@ -159,7 +168,7 @@ lazy_static! {
);
decl.add_method(
sel!(insertText:replacementRange:),
insert_text as extern "C" fn(&Object, Sel, id, NSRange),
insert_text as extern "C" fn(&mut Object, Sel, id, NSRange),
);
decl.add_method(
sel!(characterIndexForPoint:),
Expand All @@ -174,7 +183,10 @@ lazy_static! {
sel!(doCommandBySelector:),
do_command_by_selector as extern "C" fn(&Object, Sel, Sel),
);
decl.add_method(sel!(keyDown:), key_down as extern "C" fn(&Object, Sel, id));
decl.add_method(
sel!(keyDown:),
key_down as extern "C" fn(&mut Object, Sel, id),
);
decl.add_method(sel!(keyUp:), key_up as extern "C" fn(&Object, Sel, id));
decl.add_method(
sel!(flagsChanged:),
Expand Down Expand Up @@ -423,7 +435,16 @@ extern "C" fn set_marked_text(
) {
trace!("Triggered `setMarkedText`");
unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState);

// Delete previous marked text
let previous_marked_text = state.marked_text.clone();
delete_marked_text(state, previous_marked_text.chars().count());

state.is_ime_activated = true;
let marked_text_ref: &mut id = this.get_mut_ivar("markedText");

let _: () = msg_send![(*marked_text_ref), release];
let marked_text = NSMutableAttributedString::alloc(nil);
let has_attr = msg_send![string, isKindOfClass: class!(NSAttributedString)];
Expand All @@ -433,22 +454,44 @@ extern "C" fn set_marked_text(
marked_text.initWithString(string);
};
*marked_text_ref = marked_text;

let composed_string = marked_text_ref.clone().string();
let slice = slice::from_raw_parts(
composed_string.UTF8String() as *const c_uchar,
composed_string.len(),
);
let composed_string = str::from_utf8_unchecked(slice);

state.marked_text = composed_string.to_string();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now there's a state.marked_text as well as a markedText field. They seem to store the same text. I have a specific idea of resolving this. Would you mind if I added changes to this PR?

If you would not like me to add commits to this PR, that's completely okay, but then please remove either state.marked_text or markedText. Preferrably we would only have the NSMutableAttributedString stored within the ViewState.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Welcome your patch !
I'm also confused there are duplicated information for markedText and state.marked_text.
markedText is referred in setMarkedText but I'm not sure how to remove this because I'm not familiar with Obj-c and type system between Obj-c and Rust.

I appreciate your help.


for character in composed_string.chars() {
AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::ReceivedCharacter(character),
}));
}
}
trace!("Completed `setMarkedText`");
}

extern "C" fn unmark_text(this: &Object, _sel: Sel) {
extern "C" fn unmark_text(this: &mut Object, _sel: Sel) {
trace!("Triggered `unmarkText`");
unsafe {
let marked_text: id = *this.get_ivar("markedText");
let mutable_string = marked_text.mutableString();
let _: () = msg_send![mutable_string, setString:""];
clear_marked_text(this);
let input_context: id = msg_send![this, inputContext];
let _: () = msg_send![input_context, discardMarkedText];
}
trace!("Completed `unmarkText`");
}

fn clear_marked_text(this: &mut Object) {
unsafe {
let marked_text_ref: &mut id = this.get_mut_ivar("markedText");
let _: () = msg_send![(*marked_text_ref), release];
*marked_text_ref = NSMutableAttributedString::alloc(nil);
}
}

extern "C" fn valid_attributes_for_marked_text(_this: &Object, _sel: Sel) -> id {
trace!("Triggered `validAttributesForMarkedText`");
trace!("Completed `validAttributesForMarkedText`");
Expand Down Expand Up @@ -496,12 +539,22 @@ extern "C" fn first_rect_for_character_range(
}
}

extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_range: NSRange) {
extern "C" fn insert_text(this: &mut Object, sel: Sel, string: id, _replacement_range: NSRange) {
trace!("Triggered `insertText`");
unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState);

let is_ime_activated: bool = state.is_ime_activated;
if is_ime_activated {
clear_marked_text(this);
unmark_text(this, sel);
state.is_ime_activated = false;
state.is_preediting = false;
state.marked_text = String::new();
return;
}

let has_attr = msg_send![string, isKindOfClass: class!(NSAttributedString)];
let characters = if has_attr {
// This is a *mut NSAttributedString
Expand Down Expand Up @@ -568,6 +621,15 @@ extern "C" fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) {
trace!("Completed `doCommandBySelector`");
}

fn delete_marked_text(state: &mut ViewState, count: usize) {
for _ in 0..count {
AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::ReceivedCharacter('\u{7f}'), // fire DELETE
}));
}
}

fn get_characters(event: id, ignore_modifiers: bool) -> String {
unsafe {
let characters: id = if ignore_modifiers {
Expand Down Expand Up @@ -637,7 +699,7 @@ fn update_potentially_stale_modifiers(state: &mut ViewState, event: id) {
}
}

extern "C" fn key_down(this: &Object, _sel: Sel, event: id) {
extern "C" fn key_down(this: &mut Object, _sel: Sel, event: id) {
komi1230 marked this conversation as resolved.
Show resolved Hide resolved
trace!("Triggered `keyDown`");
unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState");
Expand Down