-
Notifications
You must be signed in to change notification settings - Fork 28
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
Implement IME primitives for PlainEditor. #136
base: main
Are you sure you want to change the base?
Conversation
#[derive(Clone, Default)] | ||
struct ComposeState { | ||
selection: Option<(usize, usize)>, | ||
text: Arc<str>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like this should be a String
. Then:
- We could mutate it and re-use the allocation when updating the pre-edit
- Setting it to empty would be allocation free
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Setting it to empty would be allocation free
it may not matter, but it's never empty
examples/vello_editor/src/text.rs
Outdated
// Winit on some platforms delivers an empty Preedit after Commit | ||
// so don't lock into compose when preedit is empty. | ||
Ime::Preedit(text, None) if text.is_empty() => vec![ | ||
PlainEditorOp::SetCompose("".into(), None), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A dedicated ClearCompose
Op for setting to empty might be nice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fair, and definitely the ergonomic choice when moving to methods instead of ops.
9a6f8f6
to
d400210
Compare
parley/src/layout/editor.rs
Outdated
CommitCompose => { | ||
if let Some(ComposeState { text, .. }) = self.compose.clone() { | ||
let new_insert = self.selection.insertion_index() + text.len(); | ||
self.selection = if new_insert == self.buffer.len() { | ||
Selection::from_index( | ||
&self.layout, | ||
new_insert.saturating_sub(1), | ||
Affinity::Upstream, | ||
) | ||
} else { | ||
Selection::from_index(&self.layout, new_insert, Affinity::Downstream) | ||
}; | ||
} | ||
self.compose = None; | ||
layout_after = true; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It may not matter too much, but this does some additional work on each commit, as Winit delivers a Preedit("", None)
before sending Commit
. On commit, vello_editor in this PR currently clears the compose state, to immediately set it again, to then commit it. If CommitCompose
takes the committed text, some of those buffer operations become unnecessary.
There also appear to be some IMEs that commit without going into compose first, so perhaps it makes sense to have SetCompose(Arc<str>, Option<(usize, usize)>)
, ClearCompose
, and Commit(Arc<str>)
.
Commit(Arc<str>)
would guarantee it also clears the compose state. Otherwise it's effectively the same as InsertOrReplaceSelection(Arc<str>)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, that will be the final command set as it has been requested by both you and Nico, and it clearly makes sense.
b8c7909
to
3ce25f2
Compare
3ce25f2
to
c151875
Compare
This will be updated on the new method interface, and again once #170 enters. |
This adds IME support back into Masonry. This sticks close to linebender/parley#111, except that during IME compose, this version doesn't allow changing the selection anchor, making the code simpler. For reference, there's also linebender/parley#136. This tweaks the focus update pass: when a widget with IME is unfocused, Masonry sends the platform's IME disable event to the newly focused widget. As a workaround, we synthesize an IME disable event in the focus pass and send it to the widget that is about to be unfocused. A complication is that the handling of that event can request focus to a different widget, and in particular, can request itself to be focused again. This handles that case, too. Remaining work is setting the IME candidate region to be near the current selection and to make a decision on cursor/selection hiding when the platform sends a `None` cursor. --------- Co-authored-by: Daniel McNab <[email protected]>
No description provided.