Commit 1a3379d5 authored by Greg Spencer's avatar Greg Spencer Committed by Flutter GitHub Bot

Fix semantic sort name (#48920)

parent 8b299333
...@@ -3981,30 +3981,18 @@ String _concatStrings({ ...@@ -3981,30 +3981,18 @@ String _concatStrings({
return '$thisString\n$nestedLabel'; return '$thisString\n$nestedLabel';
} }
/// Base class for all sort keys for [Semantics] accessibility traversal order /// Base class for all sort keys for [SemanticsProperties.sortKey] accessibility
/// sorting. /// traversal order sorting.
/// ///
/// Only keys of the same type and having matching [name]s are compared. If a /// Sort keys are sorted by [name], then by the comparison that the subclass
/// list of sibling [SemanticsNode]s contains keys that are not comparable with /// implements. If [SemanticsProperties.sortKey] is specified, sort keys within
/// each other the list is first sorted using the default sorting algorithm. /// the same semantic group must all be of the same type.
/// Then the nodes are broken down into groups by moving comparable nodes
/// towards the _earliest_ node in the group. Finally each group is sorted by
/// sort key and the resulting list is made by concatenating the sorted groups
/// back.
/// ///
/// For example, let's take nodes (C, D, B, E, A, F). Let's assign node A key 1, /// Keys with no [name] are compared to other keys with no [name], and will
/// node B key 2, node C key 3. Let's also assume that the default sort order /// be traversed before those with a [name].
/// leaves the original list intact. Because nodes A, B, and C, have comparable
/// sort key, they will form a group by pulling all nodes towards the earliest
/// node, which is C. The result is group (C, B, A). The remaining nodes D, E,
/// F, form a second group with sort key being `null`. The first group is sorted
/// using their sort keys becoming (A, B, C). The second group is left as is
/// because it does not specify sort keys. Then we concatenate the two groups -
/// (A, B, C) and (D, E, F) - into the final (A, B, C, D, E, F).
/// ///
/// Because of the complexity introduced by incomparable sort keys among sibling /// If no sort key is applied to a semantics node, then it will be ordered using
/// nodes, it is recommended to either use comparable keys for all nodes, or /// a platform dependent default algorithm.
/// use null for all of them, leaving the sort order to the default algorithm.
/// ///
/// See also: /// See also:
/// ///
...@@ -4014,17 +4002,35 @@ abstract class SemanticsSortKey extends Diagnosticable implements Comparable<Sem ...@@ -4014,17 +4002,35 @@ abstract class SemanticsSortKey extends Diagnosticable implements Comparable<Sem
/// const constructors so that they can be used in const expressions. /// const constructors so that they can be used in const expressions.
const SemanticsSortKey({this.name}); const SemanticsSortKey({this.name});
/// An optional name that will make this sort key only order itself /// An optional name that will group this sort key with other sort keys of the
/// with respect to other sort keys of the same [name], as long as /// same [name].
/// they are of the same [runtimeType]. ///
/// Sort keys must have the same `runtimeType` when compared.
///
/// Keys with no [name] are compared to other keys with no [name], and will
/// be traversed before those with a [name].
final String name; final String name;
@override @override
int compareTo(SemanticsSortKey other) { int compareTo(SemanticsSortKey other) {
// The sorting algorithm must not compare incomparable keys. // Sort by name first and then subclass ordering.
assert(runtimeType == other.runtimeType); assert(runtimeType == other.runtimeType, 'Semantics sort keys can only be compared to other sort keys of the same type.');
assert(name == other.name);
return doCompare(other); // Defer to the subclass implementation for ordering only if the names are
// identical (or both null).
if (name == other.name) {
return doCompare(other);
}
// Keys that don't have a name are sorted together and come before those with
// a name.
if (name == null && other.name != null) {
return -1;
} else if (name != null && other.name == null) {
return 1;
}
return name.compareTo(other.name);
} }
/// The implementation of [compareTo]. /// The implementation of [compareTo].
...@@ -4052,14 +4058,22 @@ abstract class SemanticsSortKey extends Diagnosticable implements Comparable<Sem ...@@ -4052,14 +4058,22 @@ abstract class SemanticsSortKey extends Diagnosticable implements Comparable<Sem
/// The [OrdinalSortKey] compares itself with other [OrdinalSortKey]s /// The [OrdinalSortKey] compares itself with other [OrdinalSortKey]s
/// to sort based on the order it is given. /// to sort based on the order it is given.
/// ///
/// The ordinal value `order` is typically a whole number, though it can be /// [OrdinalSortKey]s are sorted by the optional [name], then by their [order].
/// If [SemanticsProperties.sortKey] is a [OrdinalSortKey], then all the other
/// speficied sort keys in the same semantics group must also be
/// [OrdinalSortKey]s.
///
/// Keys with no [name] are compared to other keys with no [name], and will
/// be traversed before those with a [name].
///
/// The ordinal value [order] is typically a whole number, though it can be
/// fractional, e.g. in order to fit between two other consecutive whole /// fractional, e.g. in order to fit between two other consecutive whole
/// numbers. The value must be finite (it cannot be [double.nan], /// numbers. The value must be finite (it cannot be [double.nan],
/// [double.infinity], or [double.negativeInfinity]). /// [double.infinity], or [double.negativeInfinity]).
class OrdinalSortKey extends SemanticsSortKey { class OrdinalSortKey extends SemanticsSortKey {
/// Creates a semantics sort key that uses a [double] as its key value. /// Creates a const semantics sort key that uses a [double] as its key value.
/// ///
/// The [order] must be a finite number. /// The [order] must be a finite number, and must not be null.
const OrdinalSortKey( const OrdinalSortKey(
this.order, { this.order, {
String name, String name,
...@@ -4073,7 +4087,8 @@ class OrdinalSortKey extends SemanticsSortKey { ...@@ -4073,7 +4087,8 @@ class OrdinalSortKey extends SemanticsSortKey {
/// the order in which this node is traversed by the platform's accessibility /// the order in which this node is traversed by the platform's accessibility
/// services. /// services.
/// ///
/// Lower values will be traversed first. /// Lower values will be traversed first. Keys with the same [name] will be
/// grouped together and sorted by name first, and then sorted by [order].
final double order; final double order;
@override @override
......
...@@ -247,36 +247,50 @@ void main() { ...@@ -247,36 +247,50 @@ void main() {
expect(() { expect(() {
const OrdinalSortKey(0.0).compareTo(const CustomSortKey(0.0)); const OrdinalSortKey(0.0).compareTo(const CustomSortKey(0.0));
}, throwsAssertionError); }, throwsAssertionError);
// Different names.
expect(() {
const OrdinalSortKey(0.0, name: 'a').compareTo(const OrdinalSortKey(0.0, name: 'b'));
}, throwsAssertionError);
}); });
test('OrdinalSortKey compares correctly', () { test('OrdinalSortKey compares correctly when names are the same', () {
const List<List<SemanticsSortKey>> tests = <List<SemanticsSortKey>>[ const List<List<SemanticsSortKey>> tests = <List<SemanticsSortKey>>[
<SemanticsSortKey>[OrdinalSortKey(0.0), OrdinalSortKey(0.0)], <SemanticsSortKey>[OrdinalSortKey(0.0), OrdinalSortKey(0.0)],
<SemanticsSortKey>[OrdinalSortKey(0.0), OrdinalSortKey(1.0)], <SemanticsSortKey>[OrdinalSortKey(0.0), OrdinalSortKey(1.0)],
<SemanticsSortKey>[OrdinalSortKey(1.0), OrdinalSortKey(0.0)], <SemanticsSortKey>[OrdinalSortKey(1.0), OrdinalSortKey(0.0)],
<SemanticsSortKey>[OrdinalSortKey(1.0), OrdinalSortKey(1.0)], <SemanticsSortKey>[OrdinalSortKey(1.0), OrdinalSortKey(1.0)],
<SemanticsSortKey>[OrdinalSortKey(0.0, name: 'a'), OrdinalSortKey(0.0, name: 'a')],
<SemanticsSortKey>[OrdinalSortKey(0.0, name: 'a'), OrdinalSortKey(1.0, name: 'a')],
<SemanticsSortKey>[OrdinalSortKey(1.0, name: 'a'), OrdinalSortKey(0.0, name: 'a')],
<SemanticsSortKey>[OrdinalSortKey(1.0, name: 'a'), OrdinalSortKey(1.0, name: 'a')],
]; ];
final List<int> expectedResults = <int>[0, -1, 1, 0]; final List<int> expectedResults = <int>[0, -1, 1, 0, 0, -1, 1, 0];
assert(tests.length == expectedResults.length); assert(tests.length == expectedResults.length);
final List<int> results = <int>[ final List<int> results = <int>[
for (final List<SemanticsSortKey> tuple in tests) tuple[0].compareTo(tuple[1]), for (final List<SemanticsSortKey> tuple in tests) tuple[0].compareTo(tuple[1]),
]; ];
expect(results, orderedEquals(expectedResults)); expect(results, orderedEquals(expectedResults));
// Differing types should throw an assertion.
expect(() => const OrdinalSortKey(0.0).compareTo(const CustomSortKey(0.0)), throwsAssertionError);
}); });
test('OrdinalSortKey compares correctly', () { test('OrdinalSortKey compares correctly when the names are different', () {
const List<List<SemanticsSortKey>> tests = <List<SemanticsSortKey>>[ const List<List<SemanticsSortKey>> tests = <List<SemanticsSortKey>>[
<SemanticsSortKey>[OrdinalSortKey(0.0), OrdinalSortKey(0.0)], <SemanticsSortKey>[OrdinalSortKey(0.0), OrdinalSortKey(0.0, name: 'bar')],
<SemanticsSortKey>[OrdinalSortKey(0.0), OrdinalSortKey(1.0)], <SemanticsSortKey>[OrdinalSortKey(0.0), OrdinalSortKey(1.0, name: 'bar')],
<SemanticsSortKey>[OrdinalSortKey(1.0), OrdinalSortKey(0.0)], <SemanticsSortKey>[OrdinalSortKey(1.0), OrdinalSortKey(0.0, name: 'bar')],
<SemanticsSortKey>[OrdinalSortKey(1.0), OrdinalSortKey(1.0)], <SemanticsSortKey>[OrdinalSortKey(1.0), OrdinalSortKey(1.0, name: 'bar')],
<SemanticsSortKey>[OrdinalSortKey(0.0, name: 'foo'), OrdinalSortKey(0.0)],
<SemanticsSortKey>[OrdinalSortKey(0.0, name: 'foo'), OrdinalSortKey(1.0)],
<SemanticsSortKey>[OrdinalSortKey(1.0, name: 'foo'), OrdinalSortKey(0.0)],
<SemanticsSortKey>[OrdinalSortKey(1.0, name: 'foo'), OrdinalSortKey(1.0)],
<SemanticsSortKey>[OrdinalSortKey(0.0, name: 'foo'), OrdinalSortKey(0.0, name: 'bar')],
<SemanticsSortKey>[OrdinalSortKey(0.0, name: 'foo'), OrdinalSortKey(1.0, name: 'bar')],
<SemanticsSortKey>[OrdinalSortKey(1.0, name: 'foo'), OrdinalSortKey(0.0, name: 'bar')],
<SemanticsSortKey>[OrdinalSortKey(1.0, name: 'foo'), OrdinalSortKey(1.0, name: 'bar')],
<SemanticsSortKey>[OrdinalSortKey(0.0, name: 'bar'), OrdinalSortKey(0.0, name: 'foo')],
<SemanticsSortKey>[OrdinalSortKey(0.0, name: 'bar'), OrdinalSortKey(1.0, name: 'foo')],
<SemanticsSortKey>[OrdinalSortKey(1.0, name: 'bar'), OrdinalSortKey(0.0, name: 'foo')],
<SemanticsSortKey>[OrdinalSortKey(1.0, name: 'bar'), OrdinalSortKey(1.0, name: 'foo')],
]; ];
final List<int> expectedResults = <int>[0, -1, 1, 0]; final List<int> expectedResults = <int>[ -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1];
assert(tests.length == expectedResults.length); assert(tests.length == expectedResults.length);
final List<int> results = <int>[ final List<int> results = <int>[
for (final List<SemanticsSortKey> tuple in tests) tuple[0].compareTo(tuple[1]), for (final List<SemanticsSortKey> tuple in tests) tuple[0].compareTo(tuple[1]),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment