blob: 0f40266dbf44c59cefd8bdd63cad438d8fa4e699 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/// The 3rd generation text editing API for Fuchsia.
///
/// Currently under development.
@available(added=8)
library fuchsia.input.text;
// TODO(fxbug.dev/94006): Pick and justify limits.
const MAX_STRING_LENGTH uint32 = 65536;
const MAX_COMPOSITION_SEGMENTS uint32 = 8;
type TextEditServerError = flexible enum : uint32 {
/// The given text field is already registered with the text edit server.
ALREADY_REGISTERED = 1;
};
type TextFieldError = flexible enum : uint32 {
/// An internal error that likely wasn't caused by the client.
INTERNAL_ERROR = 1;
/// The operation failed because the current state does not allow it (e.g. attempting a
/// non-composition action during an ongoing composition).
BAD_STATE = 2;
/// The server passed a revision ID that is stale or invalid.
BAD_REVISION_ID = 3;
/// The server passed a transaction ID that doesn't match the ongoing transaction.
BAD_TRANSACTION_ID = 4;
/// The server attempted to insert text that the text field does not allow.
INVALID_CONTENT = 5;
/// The server attempted to change the selection to an invalid range.
INVALID_SELECTION = 6;
/// Another argument with an invalid value, not covered by the specific cases above.
INVALID_ARGUMENT = 7;
};
/// Describes different input field types.
///
/// These can serve as hints for which keyboard layout (plain text, phone number, numeric,
/// etc.) should be displayed.
///
/// See also:
///
/// * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types
/// * https://api.flutter.dev/flutter/services/TextInputType-class.html#constants
/// * https://developer.android.com/reference/android/text/InputType
///
/// TODO(fxbug.dev/94008): Date, time, password
/// TODO(fxbug.dev/94008): Allow specifying number range, precision.
type InputType = flexible enum : uint8 {
/// Free-form text.
TEXT = 1;
/// A number.
NUMBER = 2;
/// A phone number.
PHONE = 3;
/// A URL.
URL = 4;
/// An email address.
EMAIL_ADDRESS = 5;
};
/// Settings related to the text field that are important for the edit server to be aware of.
///
/// TODO(fxbug.dev/94008): Autocorrect options
/// TODO(fxbug.dev/94008): Privacy options
type TextFieldOptions = table {
/// The type of content expected in the text box. This may affect the appearance and behavior of
/// on-screen keyboards.
1: input_type InputType;
};
/// The read-only methods of a text-field.
protocol ReadableTextField {
/// Retrieves part of the contents of the text field.
///
/// A text field's contents may be much larger than can transported in a single FIDL message
/// (imagine a PhD thesis or an EULA), so this method allows the text server to retrieve just
/// the immediately relevant chunks).
///
/// TODO(fxbug.dev/94007): Should we allow retrieving the full text contents as a read-only VMO
/// containing UTF-8 data?
GetText(struct {
range Range;
}) -> (struct {
contents string:MAX_STRING_LENGTH;
}) error TextFieldError;
};
protocol WritableTextField {
/// Begins an atomic transaction on the text field state. This is how the edit server sends one
/// or more edit requests to the client.
///
/// The cumulative text change applied by a single transaction usually corresponds to a single
/// "undoable" action in clients that offer undo history.
///
/// Returns: A `TransactionId` that the text server must send in subsequent commands during this
/// transaction.
BeginTransaction(struct {
args BeginTransactionArgs;
}) -> (struct {
transaction_id TransactionId;
}) error TextFieldError;
/// Updates the current selection.
///
/// This must be done inside a transaction.
SetSelection(struct {
transaction_id TransactionId;
selection Selection;
}) -> (struct {}) error TextFieldError;
/// Replaces or inserts `new_text` at `old_range`. After the text change, the caret moves to the
/// position immediately after the end of the edit location.
///
/// Note that `old_range` is required and supersedes any previous text _selection_.
///
/// This must be done inside a transaction.
SetText(struct {
transaction_id TransactionId;
old_range Range;
new_text string:MAX_STRING_LENGTH;
}) -> (struct {}) error TextFieldError;
/// Commits the changes made so far in the transaction and ends the transaction. The active
/// transaction ID becomes invalid.
///
/// This must be done inside a transaction.
CommitTransaction(struct {
transaction_id TransactionId;
}) -> (struct {
state TextFieldState;
}) error TextFieldError;
/// Cancels the ongoing transaction and reverts any changes within it. The active transaction ID
/// becomes invalid.
///
/// This must be done inside a transaction.
CancelTransaction(struct {
transaction_id TransactionId;
}) -> (struct {
state TextFieldState;
}) error TextFieldError;
/// Begins a composition session on the text field state.
///
/// This is how the edit server handles composable "dead keys" (e.g. for typing diacritics) or
/// connects an IME (e.g. for typing Pinyin to generate Chinese text).
///
/// A composition session groups together multiple text edit transactions and
/// `CompositionUpdate`s. If the user or the client cancels a composition, all edit transactions
/// within it should be reverted.
BeginComposition(struct {
args BeginCompositionArgs;
}) -> (struct {}) error TextFieldError;
/// Commits the changes made so far in the transaction, updates the composition metdata, and
/// ends the transaction. The active transaction ID becomes invalid.
///
/// This must be done inside a transaction, during a composition.
CommitTransactionInComposition(struct {
args CommitCompositionInTransactionArgs;
}) -> (struct {
state TextFieldState;
}) error TextFieldError;
/// Completes the composition, accepting any currently active composition segments into the
/// text stream. When the text field completes the composition, any visible composition-related
/// UI (such as underlined or highlighted composition segments) should be hidden.
///
/// There must not be an outstanding transaction at this point.
CompleteComposition() -> (struct {
state TextFieldState;
}) error TextFieldError;
/// Cancels the composition, reverting the text field to its state before the composition began.
///
/// This also cancels the outstanding transaction, if any.
CancelComposition() -> (struct {
state TextFieldState;
}) error TextFieldError;
};
type BeginTransactionArgs = table {
/// The revision ID that the server believes to be current for the text field. Required.
///
/// If this ID is out of date, then the server's view of the text field state is stale, so
/// the transaction must fail.
1: revision_id RevisionId;
};
type BeginCompositionArgs = table {
/// The revision ID that server believes to be current for the text field.
///
/// If this ID is out of date, then the server's view of the text field state is stale, so
/// the composition must fail.
1: revision_id RevisionId;
};
type CommitCompositionInTransactionArgs = table {
/// Required.
1: transaction_id TransactionId;
2: composition_update CompositionUpdate;
};
/// `TextField` represents an editable (usually) text widget inside a client application.
protocol TextField {
compose ReadableTextField;
compose WritableTextField;
};
/// Represents the system's text edit server, as exposed to client applications with text fields.
@discoverable
protocol TextEditServer {
/// Registers a `TextField` hosted by the client application and creates `EditServerSession`
/// that serves as the text field's connection to the `TextEditServer`.
///
/// The server keeps track of the client and the session. All subsequent requests from the
/// client to server take place via the `EditServerSession` protocol, while all requests from
/// the server to the client run through the `TextField` protocol.
RegisterFocusedTextField(resource struct {
text_field client_end:TextField;
server_session server_end:TextEditServerSession;
options TextFieldOptions;
}) -> (struct {}) error TextEditServerError;
};
/// An opaque type representing an `TextField`'s revision ID.
///
/// This is modified whenever the content, selection, or other state of the `TextField` changes.
type RevisionId = struct {
id uint64;
};
/// An opaque type identifying a `TextField` transaction.
type TransactionId = struct {
id uint64;
};
/// A compact representation of the current state of an `TextField`.
///
/// Note that this does not include the _contents_ of the `TextField`, as that string may be large
/// and should be retrieved piecemeal.
type TextFieldState = table {
1: revision_id RevisionId;
/// A range representing the entire editable contents of the TextField.
2: contents_range Range;
/// The current selection or the caret position.
3: selection Selection;
};
/// Represents an editing session for a single `TextField` and `TextEditServer` pair.
///
/// Note that to conserve resources, the `TextEditServer` may occasionally drop unfocused sessions.
/// In this case, the channel will appear closed, so the client should recreate the session using
/// `RegisterFocusedTextField` the next time that the field regains focus.
protocol TextEditServerSession {
/// Notify the server that the state of the text field has changed.
NotifyStateChanged(struct {
state TextFieldState;
}) -> (struct {}) error TextEditServerError;
/// Notifies the server that the text field has regained focus within its parent view.
NotifyFocusGained() -> (struct {}) error TextEditServerError;
/// Notifies the server that the text field has lost focus (either to some other field within
/// the same client view, or because the client has lost focus).
NotifyFocusLost() -> (struct {}) error TextEditServerError;
/// Notifies the server that the text field options have changed.
///
/// For example, a user might toggle the visibility of characters in a password field.
UpdateTextFieldOptions(struct {
options TextFieldOptions;
}) -> (struct {}) error TextEditServerError;
};
/// Describes composition-specific metadata that can be updated in a text field, outside of changes
/// to the actual text contents.
type CompositionUpdate = table {
/// The list of text segments that are being modified during this composition.
1: composition_segments vector<CompositionSegment>:MAX_COMPOSITION_SEGMENTS;
/// Optionally, the index of the composition segment that should be highlighted as currently
/// active.
2: highlighted_segment uint8;
};
/// During a composition session, a `CompositionSegment` identifies segments of the text that the
/// user can individually focus and choose suggestions for. Some IMEs may use just a single
/// `CompositionSegment`; others may allow multiple segments in one composition segment, and
/// highlight the currently active one (see `CompositionUpdate`).
///
/// (In the current design, the suggestion list and UI are managed by an IME, so they are not
/// stored here.)
type CompositionSegment = table {
/// The code point range within the overall text stream that is covered by the segment.
1: range Range;
/// The raw, user-input string that is being composed.
///
/// During a composition, this preserves the unconverted text, while the text content visible in
/// the text field has already been replaced by an IME.
2: raw_input_text string:MAX_STRING_LENGTH;
};
/// Represents a half-open range of code points within a stream of text. Can be used to describe a
/// contiguous text selection, or if the width of the range is 0, a caret position.
///
/// For example, in the stream "abcdклмн":
///
/// Range | Selection | Note
/// ------ | ---------- | ----
/// [0, 0) | |abcdклмн | Caret at the beginning of the stream
/// [0, 3) | [ab]cdклмн | First two code points selected
/// [5, 8) | abcdк[лмн] | Last three code points selected
/// [4, 4) | abcd|клмн | Caret after the 4th code point in the stream
///
type Range = struct {
/// The index of the first code point in the range.
start uint32;
/// The index _after_ the last code point in the range.
end uint32;
};
/// Represents a selection of code points within a stream of text.
type Selection = struct {
/// The start of the selection, and the position of the caret.
base uint32;
/// The end of the selection, changed when the user adjusts the selection with arrow keys.
/// This can be less than, greater than, or equal to the base. (Base and extent are equal if
/// the selection is empty.)
extent uint32;
/// When the selection is empty and the caret can be drawn at one of two possible locations
/// (e.g. at the end of one line or at the beginning of the next one), this determines which to
/// use.
affinity TextAffinity;
};
type TextAffinity = strict enum {
DOWNSTREAM = 1;
UPSTREAM = 2;
};