@@ -46,6 +46,52 @@ typedef void SemanticsAnnotator(SemanticsNode semantics);
46
46
/// Return false to stop visiting nodes.
47
47
typedef bool SemanticsNodeVisitor (SemanticsNode node);
48
48
49
+ /// Summary information about a [SemanticsNode] object.
50
+ ///
51
+ /// A semantics node might [SemanticsNode.mergeAllDescendantsIntoThisNode] ,
52
+ /// which means the individual fields on the semantics node don't fully describe
53
+ /// the semantics at that node. This data structure contains the full semantics
54
+ /// for the node.
55
+ ///
56
+ /// Typically obtained from [SemanticsNode.getSemanticsData] .
57
+ class SemanticsData {
58
+ /// Creates a semantics data object.
59
+ ///
60
+ /// The [flags] , [actions] , [label] , and [rect] arguments must not be null.
61
+ const SemanticsData ({
62
+ @required this .flags,
63
+ @required this .actions,
64
+ @required this .label,
65
+ @required this .rect,
66
+ this .transform
67
+ });
68
+
69
+ /// A bit field of [SemanticsFlags] that apply to this node.
70
+ final int flags;
71
+
72
+ /// A bit field of [SemanticsActions] that apply to this node.
73
+ final int actions;
74
+
75
+ /// A textual description of this node.
76
+ final String label;
77
+
78
+ /// The bounding box for this node in its coordinate system.
79
+ final Rect rect;
80
+
81
+ /// The transform from this node's coordinate system to its parent's coordinate system.
82
+ ///
83
+ /// By default, the transform is null, which represents the identity
84
+ /// transformation (i.e., that this node has the same coorinate system as its
85
+ /// parent).
86
+ final Matrix4 transform;
87
+
88
+ /// Whether [flags] contains the given flag.
89
+ bool hasFlag (SemanticsFlags flag) => (flags & flag.index) != 0 ;
90
+
91
+ /// Whether [actions] contains the given action.
92
+ bool hasAction (SemanticsAction action) => (actions & action.index) != 0 ;
93
+ }
94
+
49
95
/// A node that represents some semantic data.
50
96
///
51
97
/// The semantics tree is maintained during the semantics phase of the pipeline
@@ -59,7 +105,7 @@ class SemanticsNode extends AbstractNode {
59
105
/// is created.
60
106
SemanticsNode ({
61
107
SemanticsActionHandler handler
62
- }) : _id = _generateNewId (),
108
+ }) : id = _generateNewId (),
63
109
_actionHandler = handler;
64
110
65
111
/// Creates a semantic node to represent the root of the semantics tree.
@@ -68,7 +114,7 @@ class SemanticsNode extends AbstractNode {
68
114
SemanticsNode .root ({
69
115
SemanticsActionHandler handler,
70
116
SemanticsOwner owner
71
- }) : _id = 0 ,
117
+ }) : id = 0 ,
72
118
_actionHandler = handler {
73
119
attach (owner);
74
120
}
@@ -79,9 +125,13 @@ class SemanticsNode extends AbstractNode {
79
125
return _lastIdentifier;
80
126
}
81
127
82
- final int _id;
83
- final SemanticsActionHandler _actionHandler;
128
+ /// The unique identifier for this node.
129
+ ///
130
+ /// The root node has an id of zero. Other nodes are given a unique id when
131
+ /// they are created.
132
+ final int id;
84
133
134
+ final SemanticsActionHandler _actionHandler;
85
135
86
136
// GEOMETRY
87
137
// These are automatically handled by RenderObject's own logic
@@ -151,7 +201,7 @@ class SemanticsNode extends AbstractNode {
151
201
addAction (SemanticsAction .decrease);
152
202
}
153
203
154
- bool _hasAction (SemanticsAction action) {
204
+ bool _canPerformAction (SemanticsAction action) {
155
205
return _actionHandler != null && (_actions & action.index) != 0 ;
156
206
}
157
207
@@ -257,6 +307,20 @@ class SemanticsNode extends AbstractNode {
257
307
bool get hasChildren => _children? .isNotEmpty ?? false ;
258
308
bool _dead = false ;
259
309
310
+ /// Visits the immediate children of this node.
311
+ ///
312
+ /// This function calls visitor for each child in a pre-order travseral
313
+ /// until visitor returns false. Returns true if all the visitor calls
314
+ /// returned true, otherwise returns false.
315
+ void visitChildren (SemanticsNodeVisitor visitor) {
316
+ if (_children != null ) {
317
+ for (SemanticsNode child in _children) {
318
+ if (! visitor (child))
319
+ return ;
320
+ }
321
+ }
322
+ }
323
+
260
324
/// Called during the compilation phase after all the children of this node have been compiled.
261
325
///
262
326
/// This function lets the semantic node respond to all the changes to its
@@ -323,9 +387,11 @@ class SemanticsNode extends AbstractNode {
323
387
}
324
388
}
325
389
326
- // Visits all the descendants of this node, calling visitor for each one, until
327
- // visitor returns false. Returns true if all the visitor calls returned true,
328
- // otherwise returns false.
390
+ /// Visit all the descendants of this node.
391
+ ///
392
+ /// This function calls visitor for each descendant in a pre-order travseral
393
+ /// until visitor returns false. Returns true if all the visitor calls
394
+ /// returned true, otherwise returns false.
329
395
bool _visitDescendants (SemanticsNodeVisitor visitor) {
330
396
if (_children != null ) {
331
397
for (SemanticsNode child in _children) {
@@ -339,8 +405,8 @@ class SemanticsNode extends AbstractNode {
339
405
@override
340
406
void attach (SemanticsOwner owner) {
341
407
super .attach (owner);
342
- assert (! owner._nodes.containsKey (_id ));
343
- owner._nodes[_id ] = this ;
408
+ assert (! owner._nodes.containsKey (id ));
409
+ owner._nodes[id ] = this ;
344
410
owner._detachedNodes.remove (this );
345
411
if (_dirty) {
346
412
_dirty = false ;
@@ -356,9 +422,9 @@ class SemanticsNode extends AbstractNode {
356
422
357
423
@override
358
424
void detach () {
359
- assert (owner._nodes.containsKey (_id ));
425
+ assert (owner._nodes.containsKey (id ));
360
426
assert (! owner._detachedNodes.contains (this ));
361
- owner._nodes.remove (_id );
427
+ owner._nodes.remove (id );
362
428
owner._detachedNodes.add (this );
363
429
super .detach ();
364
430
if (_children != null ) {
@@ -378,9 +444,42 @@ class SemanticsNode extends AbstractNode {
378
444
}
379
445
}
380
446
447
+ /// Returns a summary of the semantics for this node.
448
+ ///
449
+ /// If this node has [mergeAllDescendantsIntoThisNode] , then the returned data
450
+ /// includes the information from this node's descendants. Otherwise, the
451
+ /// returned data matches the data on this node.
452
+ SemanticsData getSemanticsData () {
453
+ int flags = _flags;
454
+ int actions = _actions;
455
+ String label = _label;
456
+
457
+ if (mergeAllDescendantsIntoThisNode) {
458
+ _visitDescendants ((SemanticsNode node) {
459
+ flags | = node._flags;
460
+ actions | = node._actions;
461
+ if (node.label.isNotEmpty) {
462
+ if (label.isEmpty)
463
+ label = node.label;
464
+ else
465
+ label = '$label \n ${node .label }' ;
466
+ }
467
+ return true ;
468
+ });
469
+ }
470
+
471
+ return new SemanticsData (
472
+ flags: flags,
473
+ actions: actions,
474
+ label: label,
475
+ rect: rect,
476
+ transform: transform
477
+ );
478
+ }
479
+
381
480
mojom.SemanticsNode _serialize () {
382
481
mojom.SemanticsNode result = new mojom.SemanticsNode ();
383
- result.id = _id ;
482
+ result.id = id ;
384
483
if (_dirty) {
385
484
// We could be even more efficient about not sending data here, by only
386
485
// sending the bits that are dirty (tracking the geometry, flags, strings,
@@ -430,7 +529,7 @@ class SemanticsNode extends AbstractNode {
430
529
@override
431
530
String toString () {
432
531
StringBuffer buffer = new StringBuffer ();
433
- buffer.write ('$runtimeType ($_id ' );
532
+ buffer.write ('$runtimeType ($id ' );
434
533
if (_dirty)
435
534
buffer.write (" (${ owner != null && owner ._dirtyNodes .contains (this ) ? 'dirty' : 'STALE' })" );
436
535
if (_shouldMergeAllDescendantsIntoThisNode)
@@ -497,6 +596,11 @@ class SemanticsOwner {
497
596
498
597
final List <SemanticsListener > _listeners = < SemanticsListener > [];
499
598
599
+ /// The root node of the semantics tree, if any.
600
+ ///
601
+ /// If the semantics tree is empty, returns null.
602
+ SemanticsNode get rootSemanticsNode => _nodes[0 ];
603
+
500
604
/// Releases any resources retained by this object.
501
605
///
502
606
/// Requires that there are no listeners registered with [addListener] .
@@ -597,19 +701,18 @@ class SemanticsOwner {
597
701
_dirtyNodes.clear ();
598
702
}
599
703
600
- SemanticsActionHandler _getSemanticsActionHandlerForId (int id, { @required SemanticsAction action }) {
601
- assert (action != null );
704
+ SemanticsActionHandler _getSemanticsActionHandlerForId (int id, SemanticsAction action) {
602
705
SemanticsNode result = _nodes[id];
603
- if (result != null && result._shouldMergeAllDescendantsIntoThisNode && ! result._hasAction (action)) {
706
+ if (result != null && result._shouldMergeAllDescendantsIntoThisNode && ! result._canPerformAction (action)) {
604
707
result._visitDescendants ((SemanticsNode node) {
605
- if (node._actionHandler != null && node. _hasAction (action)) {
708
+ if (node._canPerformAction (action)) {
606
709
result = node;
607
710
return false ; // found node, abort walk
608
711
}
609
712
return true ; // continue walk
610
713
});
611
714
}
612
- if (result == null || ! result._hasAction (action))
715
+ if (result == null || ! result._canPerformAction (action))
613
716
return null ;
614
717
return result._actionHandler;
615
718
}
@@ -619,7 +722,51 @@ class SemanticsOwner {
619
722
/// If the [SemanticsNode] has not indicated that it can perform the action,
620
723
/// this function does nothing.
621
724
void performAction (int id, SemanticsAction action) {
622
- SemanticsActionHandler handler = _getSemanticsActionHandlerForId (id, action: action);
725
+ assert (action != null );
726
+ SemanticsActionHandler handler = _getSemanticsActionHandlerForId (id, action);
727
+ handler? .performAction (action);
728
+ }
729
+
730
+ SemanticsActionHandler _getSemanticsActionHandlerForPosition (SemanticsNode node, Point position, SemanticsAction action) {
731
+ if (node.transform != null ) {
732
+ Matrix4 inverse = new Matrix4 .identity ();
733
+ if (inverse.copyInverse (node.transform) == 0.0 )
734
+ return null ;
735
+ position = MatrixUtils .transformPoint (inverse, position);
736
+ }
737
+ if (! node.rect.contains (position))
738
+ return null ;
739
+ if (node.mergeAllDescendantsIntoThisNode) {
740
+ SemanticsNode result;
741
+ node._visitDescendants ((SemanticsNode child) {
742
+ if (child._canPerformAction (action)) {
743
+ result = child;
744
+ return false ;
745
+ }
746
+ return true ;
747
+ });
748
+ return result? ._actionHandler;
749
+ }
750
+ if (node.hasChildren) {
751
+ for (SemanticsNode child in node._children.reversed) {
752
+ SemanticsActionHandler handler = _getSemanticsActionHandlerForPosition (child, position, action);
753
+ if (handler != null )
754
+ return handler;
755
+ }
756
+ }
757
+ return node._canPerformAction (action) ? node._actionHandler : null ;
758
+ }
759
+
760
+ /// Asks the [SemanticsNode] with at the given position to perform the given action.
761
+ ///
762
+ /// If the [SemanticsNode] has not indicated that it can perform the action,
763
+ /// this function does nothing.
764
+ void performActionAt (Point position, SemanticsAction action) {
765
+ assert (action != null );
766
+ final SemanticsNode node = rootSemanticsNode;
767
+ if (node == null )
768
+ return ;
769
+ SemanticsActionHandler handler = _getSemanticsActionHandlerForPosition (node, position, action);
623
770
handler? .performAction (action);
624
771
}
625
772
}
0 commit comments