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