Module druid_shell::text [−][src]
Expand description
Types and functions for cross-platform text input.
Text input is a notoriously difficult problem. Unlike many other aspects of user interfaces, text input can not be correctly modeled using discrete events passed from the platform to the application. For example, many mobile phones implement autocorrect: when the user presses the spacebar, the platform peeks at the word directly behind the caret, and potentially replaces it if it’s mispelled. This means the platform needs to know the contents of a text field. On other devices, the platform may need to draw an emoji window under the caret, or look up the on-screen locations of letters for crossing out with a stylus, both of which require fetching on-screen coordinates from the application.
This is all to say: text editing is a bidirectional conversation between the application and the platform. The application, when the platform asks for it, serves up text field coordinates and content. The platform looks at this information and combines it with input from keyboards (physical or onscreen), voice, styluses, the user’s language settings, and then sends edit commands to the application.
Many platforms have an additional complication: this input fusion often
happens in a different process from your application. If we don’t
specifically account for this fact, we might get race conditions! In the
autocorrect example, if I sloppily type “meoow” and press space, the
platform might issue edits to “delete backwords one word and insert meow”.
However, if I concurrently click somewhere else in the document to move the
caret, this will replace some other word with “meow”, and leave the
“meoow” disappointingly present. To mitigate this problem, we use locks,
represented by the InputHandler
trait.
Lifecycle of a Text Input
- The user clicks a link or switches tabs, and the window content now
contains a new text field. The application registers this new field by
calling
WindowHandle::add_text_field
, and gets aTextFieldToken
that represents this new field. - The user clicks on that text field, focusing it. The application lets the
platform know by calling
WindowHandle::set_focused_text_field
with that field’sTextFieldToken
. - The user presses a key on the keyboard. The platform first calls
WinHandler::key_down
. If this method returnstrue
, the application has indicated the keypress was captured, and we skip the remaining steps. - If
key_down
returnedfalse
, druid-shell forwards the key event to the platform’s text input system - The platform, in response to either this key event or some other user
action, determines it’s time for some text input. It calls
WinHandler::text_input
to acquire a lock on the text field’s state. The application returns anInputHandler
object corresponding to the requested text field. To prevent race conditions, your application may not make any changes to the text field’s state until the platform drops theInputHandler
. - The platform calls various
InputHandler
methods to inspect and edit the text field’s state. Later, usually within a few milliseconds, the platform drops theInputHandler
, allowing the application to once again make changes to the text field’s state. These commands might be “insertq
” for a smartphone user tapping on their virtual keyboard, or “move the caret one word left” for a user pressing the left arrow key while holding control. - Eventually, after many keypresses cause steps 3–6 to repeat, the user
unfocuses the text field. The application indicates this to the platform
by calling
set_focused_text_field
. Note that even though focus has shifted away from our text field, the platform may still send edits to it by callingWinHandler::text_input
. - At some point, the user clicks a link or switches a tab, and the text
field is no longer present in the window. The application calls
WindowHandle::remove_text_field
, and the platform may no longer callWinHandler::text_input
to make changes to it.
The application also has a series of steps it follows if it wants to make its own changes to the text field’s state:
- The application determines it would like to make a change to the text field; perhaps the user has scrolled and and the text field has changed its visible location on screen, or perhaps the user has clicked to move the caret to a new location.
- The application first checks to see if there’s an outstanding
InputHandler
lock for this text field; if so, it waits until the lastInputHandler
is dropped before continuing. - The application then makes the change to the text input. If the change
would affect state visible from an
InputHandler
, the application must notify the platform viaWinHandler::update_text_field
.
Supported Platforms
Currently, druid-shell
text input is fully implemented on macOS. Our goal
is to have full support for all druid-shell targets, but for now,
InputHandler
calls are simulated from keypresses on other platforms, which
doesn’t allow for IME input, dead keys, etc.
Structs
A range of selected text, or a caret.
Enums
A special text editing command sent from the platform to the application.
Distinguishes between two visually distinct locations with the same byte index.
Indicates a horizontal direction in the text.
An event representing an application-initiated change in InputHandler
state.
Indicates a movement that transforms a particular text position in a document.
Indicates a vertical movement in a text document.
Indicates a horizontal direction for writing text.
Traits
A lock on a text field that allows the platform to retrieve state and make edits.
Functions
Simulates InputHandler
calls on handler
for a given keypress event
.