• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5part of engine;
6
7/// Represents semantic objects that deliver information in a visual manner.
8///
9/// Uses aria img role to convey this semantic information to the element.
10///
11/// Screen-readers takes advantage of "aria-label" to describe the visual.
12class ImageRoleManager extends RoleManager {
13  ImageRoleManager(SemanticsObject semanticsObject)
14      : super(Role.image, semanticsObject);
15
16  /// The element with role="img" and aria-label could block access to all
17  /// children elements, therefore create an auxiliary element and  describe the
18  /// image in that if the semantic object have child nodes.
19  html.Element _auxiliaryImageElement;
20
21  @override
22  void update() {
23    if (semanticsObject.isVisualOnly && semanticsObject.hasChildren) {
24      if (_auxiliaryImageElement == null) {
25        _auxiliaryImageElement = html.Element.tag('flt-semantics-img');
26        // Absolute positioning and sizing of leaf text elements confuses
27        // VoiceOver. So we let the browser size the value node. The node will
28        // still have a bigger tap area. However, if the node is a parent to
29        // other nodes, then VoiceOver behaves as expected with absolute
30        // positioning and sizing.
31        if (semanticsObject.hasChildren) {
32          _auxiliaryImageElement.style
33            ..position = 'absolute'
34            ..top = '0'
35            ..left = '0'
36            ..width = '${semanticsObject.rect.width}px'
37            ..height = '${semanticsObject.rect.height}px';
38        }
39        _auxiliaryImageElement.style.fontSize = '6px';
40        semanticsObject.element.append(_auxiliaryImageElement);
41      }
42
43      _auxiliaryImageElement.setAttribute('role', 'img');
44      _setLabel(_auxiliaryImageElement);
45    } else if (semanticsObject.isVisualOnly) {
46      semanticsObject.setAriaRole('img', true);
47      _setLabel(semanticsObject.element);
48      _cleanUpAuxiliaryElement();
49    } else {
50      _cleanUpAuxiliaryElement();
51      _cleanupElement();
52    }
53  }
54
55  void _setLabel(html.Element element) {
56    if (semanticsObject.hasLabel) {
57      element.setAttribute('aria-label', semanticsObject.label);
58    }
59  }
60
61  void _cleanUpAuxiliaryElement() {
62    if (_auxiliaryImageElement != null) {
63      _auxiliaryImageElement.remove();
64      _auxiliaryImageElement = null;
65    }
66  }
67
68  void _cleanupElement() {
69    semanticsObject.setAriaRole('img', false);
70    semanticsObject.element.attributes.remove('aria-label');
71  }
72
73  @override
74  void dispose() {
75    _cleanUpAuxiliaryElement();
76    _cleanupElement();
77  }
78}
79