-
Notifications
You must be signed in to change notification settings - Fork 567
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
macOS text input #1378
macOS text input #1378
Conversation
Looks like you're plugging away at this but if you think it needs a look or you have any other concerns I'm happy to do a review or have a chat. |
thanks! maybe ~a day more of work and it'll be ready for a first look? |
3b9bbe0
to
392205b
Compare
@cmyr added some more docs, and squashed a few bugs! want to take a peek? although I've already written a bunch of code, still happy to make bigger structural changes to how the API works if we think it's a good idea! |
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.
This is really impressive rob, and it definitely stretches my own understanding of the cocoa text system.
To address your specific questions first:
- vertical text: happy to put this on the "things to worry about in 2023" list.
- async functions on android: not sure how to think about this, but it feels ambitious, and it also doesn't need to be a major concern right now. The fact that there is prior-art for an API like this working on gtk & windows is very compelling, and enough to satisfy my general cross-platform concerns.
- complexity: I really don't see many places where this could be simplified, and I actually feel encouraged by its relative simplicity; this is going to be an API that is principally used internally, and given that I think it's pretty good? I'll have to actually see what's involved in making this work with druid proper, but I'm pretty encouraged.
- taxonomy: I think this is excellent, and very similar to how we did this in xi. I think sharing things like movement between selection and cursor movement makes total sense.
TextInput
prefixes: I would not be opposed to having atext
module in druid-shell; that's how we ended up handling it indruid
(and a lot of the druid types will end up being replaced by these new types, I think).- kill/yank: This is... I mean, these two (and the various
setMark:
commands) are just such a pain. They really don't make sense cross-platform. we could definitely consider maintaining a per-app killring indruid-shell
, and then implementing these ourselves, and I don't think this would really be that hard, but I think it's okay if this doesn't work for now. - no tests for now is okay. We will need to think about this "eventually", but... 😈
And then other things:
The most major thing that is missing here, for me, is a sort of 'guide level' explanation of how IME is supposed to work, and how a user of druid-shell is supposed to interact with it. Like:
- each widget that can accept text input must register with the window, by calling XXXX.
- each time a widget gains focus, it must set itself as active, by calling XXXX.
- when the (selection, text, layout properties) of a widget change, you must notify the IME by calling XXXX.
- when there is an active IME session, your widget will be given a chance to handle raw keyboard events. If the events are not handled, they will be passed to the IME, which will convert them to
TextInputAction
s.
I think that this sort of information will be invaluable, and will be a really important artifact of this work.
Other than that, I don't really have a lot else to say, and maybe a good next step is for me to try and integrate this into druid proper, and see how that feels? I think that would give me a better general sense of how this API works for us, and whether or not there are any kinks to work out.
druid-shell/src/text_input.rs
Outdated
/// and `extent` to be `0`. If the user holds shift and presses the right arrow key five times, we would expect the | ||
/// word `hello` to be selected, the `anchor` to still be `0`, and the `extent` to now be `5`. | ||
#[derive(Clone, Default, PartialEq, Eq, Hash)] | ||
pub struct TextSelection { |
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.
This raises a pretty big question about our various impls of these different types, and whether they should be unified. I could imagine having a 'selection' type that we only use for IME, if (for instance) we wanted to support multi-selection in our main selection type, or something, but it might also be nice to simplify and try to just have a single version of these types that we use everywhere.
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 would definitely be ideal! I think it will require we modify this to include last-horizontal-move-pixel-x positions, but perhaps an IME will some day want that information anyway. It does mean we'll have to more explicitly specify how pixel-x positions (is there a better name for that which you've seen) change in response to edits?
druid-shell/examples/edit_text.rs
Outdated
true | ||
} | ||
|
||
fn movement_goes_downstream(movement: TextMovement) -> bool { |
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.
maybe this makes sense as a provided method on TextMovement
?
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.
Definitely would be ideal (as would the next function) but right now the implementation is wrong; it doesn't take into account the context around the caret, which is necessary for knowing the direction of Left
and Right
. How would you feel about punting for a later commit, when we've figured out a little bit more of the rules for how bidi text must work?
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.
gotcha.
I think it probably makes sense even ignoring BiDi to have a TextDirection
enum in piet and to associate this with each piet TextLayout
, but I'm not sure where that boundary should really be.
It does feel, though, that anyone using this code is going to need some variant of this functionality? Happy to worry about that a bit more during the integration phase, though.
druid-shell/src/text_input.rs
Outdated
} | ||
|
||
#[allow(dead_code)] | ||
/// Simulates `TextInputHandler` calls on `handler` for a given keypress `event`. This circumvents the platform, and so can't |
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.
Rustdoc treats doc comments kind of like git treats commit messages; the first line is 'special', and acts as a sort of summary; for instance if you're in the docs for a module, where it lists the contents of that module, the first line is displayed beside each item.
In practice what this means is it makes sense to try and have a simple first line (like the first sentence, here) and then a newline followed by the more detailed docs.
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 think this should be fixed, but if you spot a long first line I've missed somewhere, let me know!
Adds APIs for cross-platform text input, and an initial implementation of text input on macOS that allows for dead keys, IME input for Chinese, Japanese, etc. On other platforms, the text input method calls are simulated from key_down events, as a temporary measure until we can properly implement input methods on those platforms. Also adds a new druid-shell example, `edit_text`, which is a rough, LTR and single-line only implementation of the text input APIs to demonstrate that they work correctly. A massive thanks to yvt's library tcw3 (https://github.com/yvt/Stella2), which was a fantastic blueprint for the structure of this API, as well as indispensable reference when implementing the macOS text input interfaces.
Co-authored-by: Colin Rofls <[email protected]>
Also contains various small API renamings in response to code review comments.
@cmyr if you're working on this soon, would it be helpful for me to rebase it? |
Only if you're feeling like you need a project; I was planning on starting a new working branch based off of this, and getting it up-to-date myself 🤷 |
That works! I'll conserve my energy for TSF |
this has been merged as part of #1619 |
This change proposes a new set of APIs for text input, as well as fully implements proper text input using those APIs on macOS. Other platforms have text input commands simulated from key down events — this won't handle IME input on those platforms, but will hopefully work as a temporary solution for those platforms until we can add IME support to them.
To test it out:
cd druid-shell; cargo run --example edit_text
Sorry for the really long commit!! Let me know if I should split it up. Most of the changes should hopefully be boilerplate and comments, but there's still a fair amount of logic.
I have a few things I'm still unsure about, although I'm sure there's plenty more I've missed:
TextInputAction
s I've created? it seems maybe better to me than a completely flat list of commands in a singleenum
, but perhaps I've over-combined commands, like how vertical movement is shared between selection movements and scrolling. these actions are also a little macOS specific, although I've tried my best to directly implement some of the more esoteric commands like "transpose". tcw3 handles this by having a bunch of post-ime keybindings on other platforms that trigger the appropriateTextInputAction
.TextInput
prefixes everywhere on basic types, but was necessary given how druid-shell puts everything in a single top-level namespace. if we think it's ok to have atext_input_types
prefix or something like that, could make types likeTextSelection
andTextInputUpdate
look a little prettier?TextInputHandler
implementations to ensure they correctly manipulate the text state, deal with graphemes correctly, etcSee also the IME tracking issue: #1308