Skip to content

Commit

Permalink
fix editable placement in a11y mode (flutter#14581)
Browse files Browse the repository at this point in the history
  • Loading branch information
yjbanov authored Dec 20, 2019
1 parent cce5afd commit 9b299f2
Show file tree
Hide file tree
Showing 4 changed files with 443 additions and 282 deletions.
75 changes: 70 additions & 5 deletions lib/web_ui/lib/src/engine/semantics/text_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,70 @@

part of engine;

/// Text editing used by accesibility mode.
///
/// [SemanticsTextEditingStrategy] assumes the caller will own the creation,
/// insertion and disposal of the DOM element. Due to this
/// [initializeElementPlacement], [initializeTextEditing] and
/// [disable] strategies are handled differently.
///
/// This class is still responsible for hooking up the DOM element with the
/// [HybridTextEditing] instance so that changes are communicated to Flutter.
class SemanticsTextEditingStrategy extends DefaultTextEditingStrategy {
/// Creates a [SemanticsTextEditingStrategy] that eagerly instantiates
/// [domElement] so the caller can insert it before calling
/// [SemanticsTextEditingStrategy.enable].
SemanticsTextEditingStrategy(
HybridTextEditing owner, html.HtmlElement domElement)
: super(owner) {
// Make sure the DOM element is of a type that we support for text editing.
// TODO(yjbanov): move into initializer list when https://github.com/dart-lang/sdk/issues/37881 is fixed.
assert((domElement is html.InputElement) ||
(domElement is html.TextAreaElement));
super.domElement = domElement;
}

@override
void disable() {
// We don't want to remove the DOM element because the caller is responsible
// for that.
//
// Remove focus from the editable element to cause the keyboard to hide.
// Otherwise, the keyboard stays on screen even when the user navigates to
// a different screen (e.g. by hitting the "back" button).
domElement.blur();
}

@override
void initializeElementPlacement() {
// Element placement is done by [TextField].
}

@override
void initializeTextEditing(InputConfiguration inputConfig,
{_OnChangeCallback onChange, _OnActionCallback onAction}) {
// In accesibilty mode, the user of this class is supposed to insert the
// [domElement] on their own. Let's make sure they did.
assert(domElement != null);
assert(html.document.body.contains(domElement));

isEnabled = true;
_inputConfiguration = inputConfig;
_onChange = onChange;
_onAction = onAction;

domElement.focus();
}

@override
void setEditingState(EditingState editingState) {
super.setEditingState(editingState);

// Refocus after setting editing state.
domElement.focus();
}
}

/// Manages semantics objects that represent editable text fields.
///
/// This role is implemented via a content-editable HTML element. This role does
Expand All @@ -19,15 +83,15 @@ class TextField extends RoleManager {
semanticsObject.hasFlag(ui.SemanticsFlag.isMultiline)
? html.TextAreaElement()
: html.InputElement();
persistentTextEditingElement = PersistentTextEditingElement(
textEditingElement = SemanticsTextEditingStrategy(
textEditing,
editableDomElement,
);
_setupDomElement();
}

PersistentTextEditingElement persistentTextEditingElement;
html.Element get _textFieldElement => persistentTextEditingElement.domElement;
SemanticsTextEditingStrategy textEditingElement;
html.Element get _textFieldElement => textEditingElement.domElement;

void _setupDomElement() {
// On iOS, even though the semantic text field is transparent, the cursor
Expand Down Expand Up @@ -61,6 +125,7 @@ class TextField extends RoleManager {
switch (browserEngine) {
case BrowserEngine.blink:
case BrowserEngine.edge:
case BrowserEngine.ie11:
case BrowserEngine.firefox:
case BrowserEngine.ie11:
case BrowserEngine.unknown:
Expand All @@ -82,7 +147,7 @@ class TextField extends RoleManager {
return;
}

textEditing.useCustomEditableElement(persistentTextEditingElement);
textEditing.useCustomEditableElement(textEditingElement);
ui.window
.onSemanticsAction(semanticsObject.id, ui.SemanticsAction.tap, null);
});
Expand All @@ -98,7 +163,7 @@ class TextField extends RoleManager {
num lastTouchStartOffsetY;

_textFieldElement.addEventListener('touchstart', (html.Event event) {
textEditing.useCustomEditableElement(persistentTextEditingElement);
textEditing.useCustomEditableElement(textEditingElement);
final html.TouchEvent touchEvent = event;
lastTouchStartOffsetX = touchEvent.changedTouches.last.client.x;
lastTouchStartOffsetY = touchEvent.changedTouches.last.client.y;
Expand Down
Loading

0 comments on commit 9b299f2

Please sign in to comment.