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

  1. 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 a TextFieldToken that represents this new field.
  2. 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’s TextFieldToken.
  3. The user presses a key on the keyboard. The platform first calls WinHandler::key_down. If this method returns true, the application has indicated the keypress was captured, and we skip the remaining steps.
  4. If key_down returned false, druid-shell forwards the key event to the platform’s text input system
  5. 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 an InputHandler 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 the InputHandler.
  6. The platform calls various InputHandler methods to inspect and edit the text field’s state. Later, usually within a few milliseconds, the platform drops the InputHandler, allowing the application to once again make changes to the text field’s state. These commands might be “insert q” 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.
  7. 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 calling WinHandler::text_input.
  8. 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 call WinHandler::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:

  1. 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.
  2. The application first checks to see if there’s an outstanding InputHandler lock for this text field; if so, it waits until the last InputHandler is dropped before continuing.
  3. 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 via WinHandler::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.