blob: 1b97f46f0bb4ccd6786d87cd890ffdef3149ea0b [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.6
part of engine;
/// Represents semantic objects that deliver information in a visual manner.
///
/// Uses aria img role to convey this semantic information to the element.
///
/// Screen-readers takes advantage of "aria-label" to describe the visual.
class ImageRoleManager extends RoleManager {
ImageRoleManager(SemanticsObject semanticsObject)
: super(Role.image, semanticsObject);
/// The element with role="img" and aria-label could block access to all
/// children elements, therefore create an auxiliary element and describe the
/// image in that if the semantic object have child nodes.
html.Element _auxiliaryImageElement;
@override
void update() {
if (semanticsObject.isVisualOnly && semanticsObject.hasChildren) {
if (_auxiliaryImageElement == null) {
_auxiliaryImageElement = html.Element.tag('flt-semantics-img');
// Absolute positioning and sizing of leaf text elements confuses
// VoiceOver. So we let the browser size the value node. The node will
// still have a bigger tap area. However, if the node is a parent to
// other nodes, then VoiceOver behaves as expected with absolute
// positioning and sizing.
if (semanticsObject.hasChildren) {
_auxiliaryImageElement.style
..position = 'absolute'
..top = '0'
..left = '0'
..width = '${semanticsObject.rect.width}px'
..height = '${semanticsObject.rect.height}px';
}
_auxiliaryImageElement.style.fontSize = '6px';
semanticsObject.element.append(_auxiliaryImageElement);
}
_auxiliaryImageElement.setAttribute('role', 'img');
_setLabel(_auxiliaryImageElement);
} else if (semanticsObject.isVisualOnly) {
semanticsObject.setAriaRole('img', true);
_setLabel(semanticsObject.element);
_cleanUpAuxiliaryElement();
} else {
_cleanUpAuxiliaryElement();
_cleanupElement();
}
}
void _setLabel(html.Element element) {
if (semanticsObject.hasLabel) {
element.setAttribute('aria-label', semanticsObject.label);
}
}
void _cleanUpAuxiliaryElement() {
if (_auxiliaryImageElement != null) {
_auxiliaryImageElement.remove();
_auxiliaryImageElement = null;
}
}
void _cleanupElement() {
semanticsObject.setAriaRole('img', false);
semanticsObject.element.attributes.remove('aria-label');
}
@override
void dispose() {
_cleanUpAuxiliaryElement();
_cleanupElement();
}
}