assembleSemanticsNode method

  1. @override
void assembleSemanticsNode (SemanticsNode node, SemanticsConfiguration config, Iterable<SemanticsNode> children)
override

Assemble the SemanticsNode for this RenderObject.

If isSemanticBoundary is true, this method is called with the node created for this RenderObject, the config to be applied to that node and the children SemanticNodes that descendants of this RenderObject have generated.

By default, the method will annotate node with config and add the children to it.

Subclasses can override this method to add additional SemanticsNodes to the tree. If new SemanticsNodes are instantiated in this method they must be disposed in clearSemantics.

Implementation

@override
void assembleSemanticsNode(SemanticsNode node, SemanticsConfiguration config, Iterable<SemanticsNode> children) {
  assert(_recognizerOffsets.isNotEmpty);
  assert(_recognizerOffsets.length.isEven);
  assert(_recognizers.isNotEmpty);
  assert(children.isEmpty);
  final List<SemanticsNode> newChildren = <SemanticsNode>[];
  final String rawLabel = text.toPlainText();
  int current = 0;
  double order = -1.0;
  TextDirection currentDirection = textDirection;
  Rect currentRect;

  SemanticsConfiguration buildSemanticsConfig(int start, int end) {
    final TextDirection initialDirection = currentDirection;
    final TextSelection selection = TextSelection(baseOffset: start, extentOffset: end);
    final List<ui.TextBox> rects = getBoxesForSelection(selection);
    Rect rect;
    for (ui.TextBox textBox in rects) {
      rect ??= textBox.toRect();
      rect = rect.expandToInclude(textBox.toRect());
      currentDirection = textBox.direction;
    }
    // round the current rectangle to make this API testable and add some
    // padding so that the accessibility rects do not overlap with the text.
    // TODO(jonahwilliams): implement this for all text accessibility rects.
    currentRect = Rect.fromLTRB(
      rect.left.floorToDouble() - 4.0,
      rect.top.floorToDouble() - 4.0,
      rect.right.ceilToDouble() + 4.0,
      rect.bottom.ceilToDouble() + 4.0,
    );
    order += 1;
    return SemanticsConfiguration()
      ..sortKey = OrdinalSortKey(order)
      ..textDirection = initialDirection
      ..label = rawLabel.substring(start, end);
  }

  for (int i = 0, j = 0; i < _recognizerOffsets.length; i += 2, j++) {
    final int start = _recognizerOffsets[i];
    final int end = _recognizerOffsets[i + 1];
    if (current != start) {
      final SemanticsNode node = SemanticsNode();
      final SemanticsConfiguration configuration = buildSemanticsConfig(current, start);
      node.updateWith(config: configuration);
      node.rect = currentRect;
      newChildren.add(node);
    }
    final SemanticsNode node = SemanticsNode();
    final SemanticsConfiguration configuration = buildSemanticsConfig(start, end);
    final GestureRecognizer recognizer = _recognizers[j];
    if (recognizer is TapGestureRecognizer) {
      configuration.onTap = recognizer.onTap;
    } else if (recognizer is LongPressGestureRecognizer) {
      configuration.onLongPress = recognizer.onLongPress;
    } else {
      assert(false);
    }
    node.updateWith(config: configuration);
    node.rect = currentRect;
    newChildren.add(node);
    current = end;
  }
  if (current < rawLabel.length) {
    final SemanticsNode node = SemanticsNode();
    final SemanticsConfiguration configuration = buildSemanticsConfig(current, rawLabel.length);
    node.updateWith(config: configuration);
    node.rect = currentRect;
    newChildren.add(node);
  }
  node.updateWith(config: config, childrenInInversePaintOrder: newChildren);
}