Custom text input

The core text APIs in the Windows.UI.Text.Core namespace enable a Windows app to receive text input from any text service supported on Windows devices. The APIs are similar to the Text Services Framework APIs in that the app is not required to have detailed knowledge of the text services. This enables the app to receive text in any language and from any input type, like keyboard, speech, or pen.

Important APIs: Windows.UI.Text.Core, CoreTextEditContext

Why use core text APIs?

For many apps, the XAML or HTML text box controls are sufficient for text input and editing. However, if your app handles complex text scenarios, like a word processing app, you might need the flexibility of a custom text edit control. You could use the CoreWindow keyboard APIs to create your text edit control, but these don't provide a way to receive composition-based text input, which is required to support East Asian languages.

Instead, use the Windows.UI.Text.Core APIs when you need to create a custom text edit control. These APIs are designed to give you a lot of flexibility in processing text input, in any language, and let you provide the text experience best suited to your app. Text input and edit controls built with the core text APIs can receive text input from all existing text input methods on Windows devices, from Text Services Framework based Input Method Editors (IMEs) and handwriting on PCs to the WordFlow keyboard (which provides auto-correction, prediction, and dictation) on mobile devices.

Architecture

The following is a simple representation of the text input system.

  • "Application" represents a Windows app hosting a custom edit control built using the core text APIs.
  • The Windows.UI.Text.Core APIs facilitate the communication with text services through Windows. Communication between the text edit control and the text services is handled primarily through a CoreTextEditContext object that provides the methods and events to facilitate the communication.

CoreText architecture diagram

Text ranges and selection

Edit controls provide space for text entry and users expect to edit text anywhere in this space. Here, we explain the text positioning system used by the core text APIs and how ranges and selections are represented in this system.

Application caret position

Text ranges used with the core text APIs are expressed in terms of caret positions. An "Application Caret Position (ACP)" is a zero-based number that indicates the count of characters from the start of the text stream immediately before the caret, as shown here.

Screenshot showing the Application Caret Position (ACP) count of characters

Text ranges and selection

Text ranges and selections are represented by the CoreTextRange structure which contains two fields:

Field Data type Description
StartCaretPosition Number [JavaScript] | System.Int32 [.NET] | int32 [C++] The start position of a range is the ACP immediately before the first character.
EndCaretPosition Number [JavaScript] | System.Int32 [.NET] | int32 [C++] The end position of a range is the ACP immediately after the last character.

 

For example, in the text range shown previously, the range [0, 5] specifies the word "Hello". StartCaretPosition must always be less than or equal to the EndCaretPosition. The range [5, 0] is invalid.

Insertion point

The current caret position, frequently referred to as the insertion point, is represented by setting the StartCaretPosition to be equal to the EndCaretPosition.

Noncontiguous selection

Some edit controls support noncontiguous selections. For example, Microsoft Office apps support multiple arbitrary selections, and many source code editors support column selection. However, the core text APIs do not support non-contiguous selections. Edit controls must report only a single contiguous selection, most often the active sub-range of the noncontiguous selections.

For example, the following image shows a text stream with two non-contiguous selections: [0, 1] and [6, 11] for which the edit control must report only one (either [0, 1] or [6, 11]).

Screenshot showing a non-contiguous text selection, where the first character and the last five characters are selected.

Working with text

The CoreTextEditContext class enables text flow between Windows and edit controls through the TextUpdating event, the TextRequested event, and the NotifyTextChanged method.

Your edit control receives text through TextUpdating events that are generated when users interact with text input methods like keyboards, speech, or IMEs.

When you change text in your edit control, for example, by pasting text into the control, you need to notify Windows by calling NotifyTextChanged.

If the text service requires the new text, then a TextRequested event is raised. You must provide the new text in the TextRequested event handler.

Accepting text updates

Your edit control should typically accept text update requests because they represent the text the user wants to enter. In the TextUpdating event handler, these actions are expected of your edit control:

  1. Insert the text specified in CoreTextTextUpdatingEventArgs.Text in the position specified in CoreTextTextUpdatingEventArgs.Range.
  2. Place selection at the position specified in CoreTextTextUpdatingEventArgs.NewSelection.
  3. Notify the system that the update succeeded by setting CoreTextTextUpdatingEventArgs.Result to CoreTextTextUpdatingResult.Succeeded.

For example, this is the state of an edit control before the user types "d". The insertion point is at [10, 10].

Screenshot of a text stream diagram showing the insertion point at [10, 10], before an insertion

When the user types "d", a TextUpdating event is raised with the following CoreTextTextUpdatingEventArgs data:

In your edit control, apply the specified changes and set Result to Succeeded. Here's the state of the control after the changes are applied.

Screenshot of a text stream diagram showing the insertion point at \[11, 11\], after an insertion

Rejecting text updates

Sometimes, you cannot apply text updates because the requested range is in an area of the edit control that should not be changed. In this case, you should not apply any changes. Instead, notify the system that the update failed by setting CoreTextTextUpdatingEventArgs.Result to CoreTextTextUpdatingResult.Failed.

For example, consider an edit control that accepts only an e-mail address. Spaces should be rejected because e-mail addresses cannot contain spaces, so when TextUpdating events are raised for the space key, you should simply set Result to Failed in your edit control.

Notifying text changes

Sometimes, your edit control makes changes to text such as when text is pasted or auto-corrected. In these cases, you must notify the text services of these changes by calling the NotifyTextChanged method.

For example, this is the state of an edit control before the user pastes "World". The insertion point is at [6, 6].

Screenshot of a text stream diagram showing the insertion point at [6, 6], before an insertion

The user performs the paste action and the edit control after the changes are applied:

Screenshot of a text stream diagram showing the insertion point at \[11, 11\], after an insertion

When this happens, you should call NotifyTextChanged with these arguments:

  • modifiedRange = [6, 6]
  • newLength = 5
  • newSelection = [11, 11]

One or more TextRequested events will follow, which you handle to update the text that the text services are working with.

Overriding text updates

In your edit control, you might want to override a text update to provide auto-correction features.

For example, consider an edit control that provides a correction feature that formalizes contractions. This is the state of the edit control before the user types the space key to trigger the correction. The insertion point is at [3, 3].

Screenshot of a text stream diagram showing the insertion point at [3, 3], before an insertion

The user presses the space key and a corresponding TextUpdating event is raised. The edit control accepts the text update. This is the state of the edit control for a brief moment before the correction is completed. The insertion point is at [4, 4].

Screenshot of a text stream diagram showing the insertion point at [4, 4], after an insertion

Outside of the TextUpdating event handler, the edit control makes the following correction. This is the state of the edit control after the correction is complete. The insertion point is at [5, 5].

Screenshot of a text stream diagram showing the insertion point at [5, 5]

When this happens, you should call NotifyTextChanged with these arguments:

  • modifiedRange = [1, 2]
  • newLength = 2
  • newSelection = [5, 5]

One or more TextRequested events will follow, which you handle to update the text that the text services are working with.

Providing requested text

It's important for text services to have the correct text to provide features like auto-correction or prediction, especially for text that already existed in the edit control, from loading a document, for example, or text that is inserted by the edit control as explained in previous sections. Therefore, whenever a TextRequested event is raised, you must provide the text currently in your edit control for the specified range.

There will be times the Range in CoreTextTextRequest specifies a range that your edit control cannot accommodate as-is. For example, the Range is larger than the size of the edit control at the time of the TextRequested event, or the end of the Range is out of bounds. In these cases, you should return whatever range makes sense, which is typically a subset of the requested range.

Samples

Archive samples