On Apr 4, 2005, at 6:01 PM, Daniel Child wrote: I have been trying to understand how input method editors work in Cocoa. If you type Japanese, a long bar comes out, but the type of ?view? ?window? seems unlike those provided in Interface Builder. A line of characters are typed out, or listed in a box.
Also, I am wondering what kind of keyboard interception is taking place so that when you type roman letters it gets converted into other things like kana and kanji.
If anyone could point to relevant documentation or on the most important classes to understand, please let me know. I've looked around but haven't come up with much.
There are several ways to approach this, depending on whether you are thinking of writing a view that accepts keyboard input, or writing an input method, or just trying to understand how the whole thing works. When a key is pressed, a key-down NSEvent is generated, and NSApplication passes it to the key window, which passes it to the first responder by calling its keyDown: method. For a text-handling view like NSTextView, the keyDown: method will then call [self interpretKeyEvents:] (an NSResponder method) which will start the interpretation and key binding process.
Now, in most cases the key events will come back to the view either as insertText: (for ordinary key presses with an ordinary keyboard) or as doCommandBySelector: (for special keys like delete, return, etc.), as the NSResponder.h comments mention. However, if a special input method like Kotoeri is active, then something else will happen. To handle those cases, NSTextView conforms to the NSTextInput protocol. In those cases, unconfirmed text (called "marked" text) will be sent to the text view via the setMarkedText:selectedRange: method. NSTextView then usually displays the marked text with an underline. The remaining methods in NSTextInput are primarily to allow the input method to query the text view for information that it needs to display its apparatus.
The input method itself is a kind of plugin--for example, Kotoeri is /System/Library/Components/Kotoeri.component. The TSM documentation will help you understand how these components are written, and how they manage their communication with the rest of the app using TSM. As a plugin running in the application, the input method can put up panels or other UI apparatus as needed.
If you are writing a text-oriented view and you want to be able to receive input from input methods, then primarily what you need to do is to pass your key events to interpretKeyEvents:, and to conform to the NSTextInput protocol.
If you are just working with NSTextView, then you should be generally aware of this process. For example, I have repeatedly recommended that in most cases it is not a good idea to subclass NSTextView and override keyDown:. That is because handling key events in this manner will bypass all of the key binding and input method processing. If you were to handle returns, for example, in keyDown:, then an input method that gave a special meaning to return would probably be broken in your app. If possible, it is better to work at a higher level, with the insertText: or doCommandBySelector: calls that key presses eventually generate, or better yet to handle the resulting delegate methods like textView:doCommandBySelector:, textView:shouldChangeTextInRange:replacementString:, or textView:willChangeSelectionFromCharacterRange:toCharacterRange.
You should also be aware that in some cases the text view will contain and display text that is only tentative, not yet confirmed by the user. You can find out whether this is the case by calling -hasMarkedText, and you can determine where it is using -markedRange.
Another thing to keep in mind is that the NSTextInput methods are really intended only for dealing with input methods. In some cases I have seen developers try to use NSTextInput methods like -firstRectForCharacterRange: or -characterIndexForPoint: in other contexts. These really are not intended for general-purpose use; they are specialized for the needs of input methods, and in most cases if you are not dealing with input methods you should use something else.
Douglas Davidson
|