Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
72517f0a
Unverified
Commit
72517f0a
authored
Feb 10, 2018
by
Michael Goderbauer
Committed by
GitHub
Feb 10, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Encode scrolling status into tree (#14536)
parent
248919fa
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
317 additions
and
170 deletions
+317
-170
engine.version
bin/internal/engine.version
+1
-1
utils.dart
packages/flutter/lib/src/physics/utils.dart
+1
-1
semantics.dart
packages/flutter/lib/src/semantics/semantics.dart
+156
-2
semantics_event.dart
packages/flutter/lib/src/semantics/semantics_event.dart
+0
-70
scroll_metrics.dart
packages/flutter/lib/src/widgets/scroll_metrics.dart
+5
-1
scrollable.dart
packages/flutter/lib/src/widgets/scrollable.dart
+39
-34
utils_test.dart
packages/flutter/test/physics/utils_test.dart
+19
-0
semantics_test.dart
packages/flutter/test/semantics/semantics_test.dart
+2
-2
scrollable_semantics_test.dart
packages/flutter/test/widgets/scrollable_semantics_test.dart
+62
-58
semantics_tester.dart
packages/flutter/test/widgets/semantics_tester.dart
+32
-1
No files found.
bin/internal/engine.version
View file @
72517f0a
a031239a5d4e44e60d0ebc62b8c544a9f592fc22
8ac6f6efa177fb548dcdc81f1501f060b2ad1115
packages/flutter/lib/src/physics/utils.dart
View file @
72517f0a
...
...
@@ -12,7 +12,7 @@ bool nearEqual(double a, double b, double epsilon) {
assert
(
epsilon
>=
0.0
);
if
(
a
==
null
||
b
==
null
)
return
a
==
b
;
return
(
a
>
(
b
-
epsilon
))
&&
(
a
<
(
b
+
epsilon
));
return
(
a
>
(
b
-
epsilon
))
&&
(
a
<
(
b
+
epsilon
))
||
a
==
b
;
}
/// Whether a double is within a given distance of zero.
...
...
packages/flutter/lib/src/semantics/semantics.dart
View file @
72517f0a
...
...
@@ -95,6 +95,9 @@ class SemanticsData extends Diagnosticable {
@required
this
.
nextNodeId
,
@required
this
.
rect
,
@required
this
.
textSelection
,
@required
this
.
scrollPosition
,
@required
this
.
scrollExtentMax
,
@required
this
.
scrollExtentMin
,
this
.
tags
,
this
.
transform
,
})
:
assert
(
flags
!=
null
),
...
...
@@ -156,6 +159,38 @@ class SemanticsData extends Diagnosticable {
/// if this node represents a text field.
final
TextSelection
textSelection
;
/// Indicates the current scrolling position in logical pixels if the node is
/// scrollable.
///
/// The properties [scrollExtentMin] and [scrollExtentMax] indicate the valid
/// in-range values for this property. The value for [scrollPosition] may
/// (temporarily) be outside that range, e.g. during an overscroll.
///
/// See also:
///
/// * [ScrollPosition.pixels], from where this value is usually taken.
final
double
scrollPosition
;
/// Indicates the maximum in-range value for [scrollPosition] if the node is
/// scrollable.
///
/// This value may be infinity if the scroll is unbound.
///
/// See also:
///
/// * [ScrollPosition.maxScrollExtent], from where this value is usually taken.
final
double
scrollExtentMax
;
/// Indicates the mimimum in-range value for [scrollPosition] if the node is
/// scrollable.
///
/// This value may be infinity if the scroll is unbound.
///
/// See also:
///
/// * [ScrollPosition.minScrollExtent], from where this value is usually taken.
final
double
scrollExtentMin
;
/// The bounding box for this node in its coordinate system.
final
Rect
rect
;
...
...
@@ -204,7 +239,10 @@ class SemanticsData extends Diagnosticable {
properties
.
add
(
new
EnumProperty
<
TextDirection
>(
'textDirection'
,
textDirection
,
defaultValue:
null
));
properties
.
add
(
new
IntProperty
(
'nextNodeId'
,
nextNodeId
,
defaultValue:
null
));
if
(
textSelection
?.
isValid
==
true
)
properties
.
add
(
new
MessageProperty
(
'text selection'
,
'[
${textSelection.start}
,
${textSelection.end}
]'
));
properties
.
add
(
new
MessageProperty
(
'textSelection'
,
'[
${textSelection.start}
,
${textSelection.end}
]'
));
properties
.
add
(
new
DoubleProperty
(
'scrollExtentMin'
,
scrollExtentMin
,
defaultValue:
null
));
properties
.
add
(
new
DoubleProperty
(
'scrollPosition'
,
scrollPosition
,
defaultValue:
null
));
properties
.
add
(
new
DoubleProperty
(
'scrollExtentMax'
,
scrollExtentMax
,
defaultValue:
null
));
}
@override
...
...
@@ -224,11 +262,14 @@ class SemanticsData extends Diagnosticable {
&&
typedOther
.
rect
==
rect
&&
setEquals
(
typedOther
.
tags
,
tags
)
&&
typedOther
.
textSelection
==
textSelection
&&
typedOther
.
scrollPosition
==
scrollPosition
&&
typedOther
.
scrollExtentMax
==
scrollExtentMax
&&
typedOther
.
scrollExtentMin
==
scrollExtentMin
&&
typedOther
.
transform
==
transform
;
}
@override
int
get
hashCode
=>
ui
.
hashValues
(
flags
,
actions
,
label
,
value
,
increasedValue
,
decreasedValue
,
hint
,
textDirection
,
nextNodeId
,
rect
,
tags
,
textSelection
,
transform
);
int
get
hashCode
=>
ui
.
hashValues
(
flags
,
actions
,
label
,
value
,
increasedValue
,
decreasedValue
,
hint
,
textDirection
,
nextNodeId
,
rect
,
tags
,
textSelection
,
scrollPosition
,
scrollExtentMax
,
scrollExtentMin
,
transform
);
}
class
_SemanticsDiagnosticableNode
extends
DiagnosticableNode
<
SemanticsNode
>
{
...
...
@@ -915,6 +956,9 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
_textDirection
!=
config
.
textDirection
||
_sortOrder
!=
config
.
_sortOrder
||
_textSelection
!=
config
.
_textSelection
||
_scrollPosition
!=
config
.
_scrollPosition
||
_scrollExtentMax
!=
config
.
_scrollExtentMax
||
_scrollExtentMin
!=
config
.
_scrollExtentMin
||
_actionsAsBits
!=
config
.
_actionsAsBits
||
_mergeAllDescendantsIntoThisNode
!=
config
.
isMergingSemanticsOfDescendants
;
}
...
...
@@ -1011,6 +1055,42 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
TextSelection
get
textSelection
=>
_textSelection
;
TextSelection
_textSelection
;
/// Indicates the current scrolling position in logical pixels if the node is
/// scrollable.
///
/// The properties [scrollExtentMin] and [scrollExtentMax] indicate the valid
/// in-range values for this property. The value for [scrollPosition] may
/// (temporarily) be outside that range, e.g. during an overscroll.
///
/// See also:
///
/// * [ScrollPosition.pixels], from where this value is usually taken.
double
get
scrollPosition
=>
_scrollPosition
;
double
_scrollPosition
;
/// Indicates the maximum in-range value for [scrollPosition] if the node is
/// scrollable.
///
/// This value may be infinity if the scroll is unbound.
///
/// See also:
///
/// * [ScrollPosition.maxScrollExtent], from where this value is usually taken.
double
get
scrollExtentMax
=>
_scrollExtentMax
;
double
_scrollExtentMax
;
/// Indicates the mimimum in-range value for [scrollPosition] if the node is
/// scrollable.
///
/// This value may be infinity if the scroll is unbound.
///
/// See also:
///
/// * [ScrollPosition.minScrollExtent] from where this value is usually taken.
double
get
scrollExtentMin
=>
_scrollExtentMin
;
double
_scrollExtentMin
;
bool
_canPerformAction
(
SemanticsAction
action
)
=>
_actions
.
containsKey
(
action
);
static
final
SemanticsConfiguration
_kEmptyConfig
=
new
SemanticsConfiguration
();
...
...
@@ -1043,6 +1123,9 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
_actions
=
new
Map
<
SemanticsAction
,
_SemanticsActionHandler
>.
from
(
config
.
_actions
);
_actionsAsBits
=
config
.
_actionsAsBits
;
_textSelection
=
config
.
_textSelection
;
_scrollPosition
=
config
.
_scrollPosition
;
_scrollExtentMax
=
config
.
_scrollExtentMax
;
_scrollExtentMin
=
config
.
_scrollExtentMin
;
_mergeAllDescendantsIntoThisNode
=
config
.
isMergingSemanticsOfDescendants
;
_replaceChildren
(
childrenInInversePaintOrder
??
const
<
SemanticsNode
>[]);
...
...
@@ -1074,6 +1157,9 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
int
nextNodeId
=
_nextNodeId
;
Set
<
SemanticsTag
>
mergedTags
=
tags
==
null
?
null
:
new
Set
<
SemanticsTag
>.
from
(
tags
);
TextSelection
textSelection
=
_textSelection
;
double
scrollPosition
=
_scrollPosition
;
double
scrollExtentMax
=
_scrollExtentMax
;
double
scrollExtentMin
=
_scrollExtentMin
;
if
(
mergeAllDescendantsIntoThisNode
)
{
_visitDescendants
((
SemanticsNode
node
)
{
...
...
@@ -1083,6 +1169,9 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
textDirection
??=
node
.
_textDirection
;
nextNodeId
??=
node
.
_nextNodeId
;
textSelection
??=
node
.
_textSelection
;
scrollPosition
??=
node
.
_scrollPosition
;
scrollExtentMax
??=
node
.
_scrollExtentMax
;
scrollExtentMin
??=
node
.
_scrollExtentMin
;
if
(
value
==
''
||
value
==
null
)
value
=
node
.
_value
;
if
(
increasedValue
==
''
||
increasedValue
==
null
)
...
...
@@ -1123,6 +1212,9 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
transform:
transform
,
tags:
mergedTags
,
textSelection:
textSelection
,
scrollPosition:
scrollPosition
,
scrollExtentMax:
scrollExtentMax
,
scrollExtentMin:
scrollExtentMin
,
);
}
...
...
@@ -1160,6 +1252,9 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
nextNodeId:
data
.
nextNodeId
,
textSelectionBase:
data
.
textSelection
!=
null
?
data
.
textSelection
.
baseOffset
:
-
1
,
textSelectionExtent:
data
.
textSelection
!=
null
?
data
.
textSelection
.
extentOffset
:
-
1
,
scrollPosition:
data
.
scrollPosition
!=
null
?
data
.
scrollPosition
:
double
.
nan
,
scrollExtentMax:
data
.
scrollExtentMax
!=
null
?
data
.
scrollExtentMax
:
double
.
nan
,
scrollExtentMin:
data
.
scrollExtentMin
!=
null
?
data
.
scrollExtentMin
:
double
.
nan
,
transform:
data
.
transform
?.
storage
??
_kIdentityTransform
,
children:
children
,
);
...
...
@@ -1232,6 +1327,9 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
properties
.
add
(
new
DiagnosticsProperty
<
SemanticsSortOrder
>(
'sortOrder'
,
sortOrder
,
defaultValue:
null
));
if
(
_textSelection
?.
isValid
==
true
)
properties
.
add
(
new
MessageProperty
(
'text selection'
,
'[
${_textSelection.start}
,
${_textSelection.end}
]'
));
properties
.
add
(
new
DoubleProperty
(
'scrollExtentMin'
,
scrollExtentMin
,
defaultValue:
null
));
properties
.
add
(
new
DoubleProperty
(
'scrollPosition'
,
scrollPosition
,
defaultValue:
null
));
properties
.
add
(
new
DoubleProperty
(
'scrollExtentMax'
,
scrollExtentMax
,
defaultValue:
null
));
}
/// Returns a string representation of this node and its descendants.
...
...
@@ -2088,6 +2186,56 @@ class SemanticsConfiguration {
_hasBeenAnnotated
=
true
;
}
/// Indicates the current scrolling position in logical pixels if the node is
/// scrollable.
///
/// The properties [scrollExtentMin] and [scrollExtentMax] indicate the valid
/// in-range values for this property. The value for [scrollPosition] may
/// (temporarily) be outside that range, e.g. during an overscroll.
///
/// See also:
///
/// * [ScrollPosition.pixels], from where this value is usually taken.
double
get
scrollPosition
=>
_scrollPosition
;
double
_scrollPosition
;
set
scrollPosition
(
double
value
)
{
assert
(
value
!=
null
);
_scrollPosition
=
value
;
_hasBeenAnnotated
=
true
;
}
/// Indicates the maximum in-range value for [scrollPosition] if the node is
/// scrollable.
///
/// This value may be infinity if the scroll is unbound.
///
/// See also:
///
/// * [ScrollPosition.maxScrollExtent], from where this value is usually taken.
double
get
scrollExtentMax
=>
_scrollExtentMax
;
double
_scrollExtentMax
;
set
scrollExtentMax
(
double
value
)
{
assert
(
value
!=
null
);
_scrollExtentMax
=
value
;
_hasBeenAnnotated
=
true
;
}
/// Indicates the minimum in-range value for [scrollPosition] if the node is
/// scrollable.
///
/// This value may be infinity if the scroll is unbound.
///
/// See also:
///
/// * [ScrollPosition.minScrollExtent], from where this value is usually taken.
double
get
scrollExtentMin
=>
_scrollExtentMin
;
double
_scrollExtentMin
;
set
scrollExtentMin
(
double
value
)
{
assert
(
value
!=
null
);
_scrollExtentMin
=
value
;
_hasBeenAnnotated
=
true
;
}
// TAGS
/// The set of tags that this configuration wants to add to all child
...
...
@@ -2171,6 +2319,9 @@ class SemanticsConfiguration {
_actionsAsBits
|=
other
.
_actionsAsBits
;
_flags
|=
other
.
_flags
;
_textSelection
??=
other
.
_textSelection
;
_scrollPosition
??=
other
.
_scrollPosition
;
_scrollExtentMax
??=
other
.
_scrollExtentMax
;
_scrollExtentMin
??=
other
.
_scrollExtentMin
;
textDirection
??=
other
.
textDirection
;
_sortOrder
=
_sortOrder
?.
merge
(
other
.
_sortOrder
);
...
...
@@ -2214,6 +2365,9 @@ class SemanticsConfiguration {
..
_flags
=
_flags
..
_tagsForChildren
=
_tagsForChildren
..
_textSelection
=
_textSelection
..
_scrollPosition
=
_scrollPosition
..
_scrollExtentMax
=
_scrollExtentMax
..
_scrollExtentMin
=
_scrollExtentMin
..
_actionsAsBits
=
_actionsAsBits
..
_actions
.
addAll
(
_actions
);
}
...
...
packages/flutter/lib/src/semantics/semantics_event.dart
View file @
72517f0a
...
...
@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/foundation.dart'
;
import
'package:flutter/painting.dart'
;
/// An event sent by the application to notify interested listeners that
...
...
@@ -53,75 +52,6 @@ abstract class SemanticsEvent {
}
}
/// Notifies that a scroll action has been completed.
///
/// This event translates into a `AccessibilityEvent.TYPE_VIEW_SCROLLED` on
/// Android and a `UIAccessibilityPageScrolledNotification` on iOS. It is
/// processed by the accessibility systems of the operating system to provide
/// additional feedback to the user about the state of a scrollable view (e.g.
/// on Android, a ping sound is played to indicate that a scroll action was
/// successful).
class
ScrollCompletedSemanticsEvent
extends
SemanticsEvent
{
/// Creates a [ScrollCompletedSemanticsEvent].
///
/// This event should be sent after a scroll action is completed. It is
/// interpreted by assistive technologies to provide additional feedback about
/// the just completed scroll action to the user.
///
/// The parameters [axis], [pixels], [minScrollExtent], and [maxScrollExtent] are
/// required and may not be null.
ScrollCompletedSemanticsEvent
({
@required
this
.
axis
,
@required
this
.
pixels
,
@required
this
.
maxScrollExtent
,
@required
this
.
minScrollExtent
})
:
assert
(
axis
!=
null
),
assert
(
pixels
!=
null
),
assert
(
maxScrollExtent
!=
null
),
assert
(
minScrollExtent
!=
null
),
super
(
'scroll'
);
/// The axis in which the scroll view was scrolled.
///
/// See also [ScrollPosition.axis].
final
Axis
axis
;
/// The current scroll position, in logical pixels.
///
/// See also [ScrollPosition.pixels].
final
double
pixels
;
/// The minimum in-range value for [pixels].
///
/// See also [ScrollPosition.minScrollExtent].
final
double
minScrollExtent
;
/// The maximum in-range value for [pixels].
///
/// See also [ScrollPosition.maxScrollExtent].
final
double
maxScrollExtent
;
@override
Map
<
String
,
dynamic
>
getDataMap
()
{
final
Map
<
String
,
dynamic
>
map
=
<
String
,
dynamic
>{
'pixels'
:
pixels
.
clamp
(
minScrollExtent
,
maxScrollExtent
),
'minScrollExtent'
:
minScrollExtent
,
'maxScrollExtent'
:
maxScrollExtent
,
};
switch
(
axis
)
{
case
Axis
.
horizontal
:
map
[
'axis'
]
=
'h'
;
break
;
case
Axis
.
vertical
:
map
[
'axis'
]
=
'v'
;
break
;
}
return
map
;
}
}
/// An event for a semantic announcement.
///
/// This should be used for announcement that are not seamlessly announced by
...
...
packages/flutter/lib/src/widgets/scroll_metrics.dart
View file @
72517f0a
...
...
@@ -59,11 +59,15 @@ abstract class ScrollMetrics {
/// The minimum in-range value for [pixels].
///
/// The actual [pixels] value might be [outOfRange].
///
/// This value can be negative infinity, if the scroll is unbounded.
double
get
minScrollExtent
;
/// The maximum in-range value for [pixels].
///
/// The actual [pixels] value might be [outOfRange].
///
/// This value can be infinity, if the scroll is unbounded.
double
get
maxScrollExtent
;
/// The current scroll position, in logical pixels along the [axisDirection].
...
...
@@ -140,4 +144,4 @@ class FixedScrollMetrics extends ScrollMetrics {
String
toString
()
{
return
'
$runtimeType
(
${extentBefore.toStringAsFixed(1)}
..[
${extentInside.toStringAsFixed(1)}
]..
${extentAfter.toStringAsFixed(1)}
)'
;
}
}
\ No newline at end of file
}
packages/flutter/lib/src/widgets/scrollable.dart
View file @
72517f0a
...
...
@@ -275,7 +275,6 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin
final
ScrollPosition
oldPosition
=
position
;
if
(
oldPosition
!=
null
)
{
controller
?.
detach
(
oldPosition
);
oldPosition
.
removeListener
(
_sendSemanticsScrollEvent
);
// It's important that we not dispose the old position until after the
// viewport has had a chance to unregister its listeners from the old
// position. So, schedule a microtask to do it.
...
...
@@ -284,30 +283,10 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin
_position
=
controller
?.
createScrollPosition
(
_physics
,
this
,
oldPosition
)
??
new
ScrollPositionWithSingleContext
(
physics:
_physics
,
context:
this
,
oldPosition:
oldPosition
);
_position
.
addListener
(
_sendSemanticsScrollEvent
);
assert
(
position
!=
null
);
controller
?.
attach
(
position
);
}
bool
_semanticsScrollEventScheduled
=
false
;
void
_sendSemanticsScrollEvent
()
{
if
(
_semanticsScrollEventScheduled
)
return
;
SchedulerBinding
.
instance
.
addPostFrameCallback
((
Duration
timestamp
)
{
final
_RenderExcludableScrollSemantics
render
=
_excludableScrollSemanticsKey
.
currentContext
?.
findRenderObject
();
render
?.
sendSemanticsEvent
(
new
ScrollCompletedSemanticsEvent
(
axis:
position
.
axis
,
pixels:
position
.
pixels
,
minScrollExtent:
position
.
minScrollExtent
,
maxScrollExtent:
position
.
maxScrollExtent
,
));
_semanticsScrollEventScheduled
=
false
;
});
_semanticsScrollEventScheduled
=
true
;
}
@override
void
didChangeDependencies
()
{
super
.
didChangeDependencies
();
...
...
@@ -529,6 +508,7 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin
result
=
new
_ExcludableScrollSemantics
(
key:
_excludableScrollSemanticsKey
,
child:
result
,
position:
position
,
);
}
...
...
@@ -557,28 +537,61 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin
/// node, which is annotated with the scrolling actions, will house the
/// scrollable children.
class
_ExcludableScrollSemantics
extends
SingleChildRenderObjectWidget
{
const
_ExcludableScrollSemantics
({
Key
key
,
Widget
child
})
:
super
(
key:
key
,
child:
child
);
const
_ExcludableScrollSemantics
({
Key
key
,
@required
this
.
position
,
Widget
child
})
:
assert
(
position
!=
null
),
super
(
key:
key
,
child:
child
);
final
ScrollPosition
position
;
@override
_RenderExcludableScrollSemantics
createRenderObject
(
BuildContext
context
)
=>
new
_RenderExcludableScrollSemantics
(
position:
position
);
@override
_RenderExcludableScrollSemantics
createRenderObject
(
BuildContext
context
)
=>
new
_RenderExcludableScrollSemantics
();
void
updateRenderObject
(
BuildContext
context
,
_RenderExcludableScrollSemantics
renderObject
)
{
renderObject
.
position
=
position
;
}
}
class
_RenderExcludableScrollSemantics
extends
RenderProxyBox
{
_RenderExcludableScrollSemantics
({
RenderBox
child
})
:
super
(
child
);
_RenderExcludableScrollSemantics
({
@required
ScrollPosition
position
,
RenderBox
child
,
})
:
_position
=
position
,
assert
(
position
!=
null
),
super
(
child
)
{
position
.
addListener
(
markNeedsSemanticsUpdate
);
}
/// Whether this render object is excluded from the semantic tree.
ScrollPosition
get
position
=>
_position
;
ScrollPosition
_position
;
set
position
(
ScrollPosition
value
)
{
assert
(
value
!=
null
);
if
(
value
==
_position
)
return
;
_position
.
removeListener
(
markNeedsSemanticsUpdate
);
_position
=
value
;
_position
.
addListener
(
markNeedsSemanticsUpdate
);
markNeedsSemanticsUpdate
();
}
@override
void
describeSemanticsConfiguration
(
SemanticsConfiguration
config
)
{
super
.
describeSemanticsConfiguration
(
config
);
config
.
isSemanticBoundary
=
true
;
if
(
position
.
haveDimensions
)
{
config
..
scrollPosition
=
_position
.
pixels
..
scrollExtentMax
=
_position
.
maxScrollExtent
..
scrollExtentMin
=
_position
.
minScrollExtent
;
}
}
SemanticsNode
_innerNode
;
SemanticsNode
_annotatedNode
;
@override
void
assembleSemanticsNode
(
SemanticsNode
node
,
SemanticsConfiguration
config
,
Iterable
<
SemanticsNode
>
children
)
{
if
(
children
.
isEmpty
||
!
children
.
first
.
isTagged
(
RenderViewport
.
useTwoPaneSemantics
))
{
_annotatedNode
=
node
;
super
.
assembleSemanticsNode
(
node
,
config
,
children
);
return
;
}
...
...
@@ -587,7 +600,6 @@ class _RenderExcludableScrollSemantics extends RenderProxyBox {
_innerNode
..
isMergedIntoParent
=
node
.
isPartOfNodeMerging
..
rect
=
Offset
.
zero
&
node
.
rect
.
size
;
_annotatedNode
=
_innerNode
;
final
List
<
SemanticsNode
>
excluded
=
<
SemanticsNode
>[
_innerNode
];
final
List
<
SemanticsNode
>
included
=
<
SemanticsNode
>[];
...
...
@@ -606,12 +618,5 @@ class _RenderExcludableScrollSemantics extends RenderProxyBox {
void
clearSemantics
()
{
super
.
clearSemantics
();
_innerNode
=
null
;
_annotatedNode
=
null
;
}
/// Sends a [SemanticsEvent] in the context of the [SemanticsNode] that is
/// annotated with this object's semantics information.
void
sendSemanticsEvent
(
SemanticsEvent
event
)
{
_annotatedNode
?.
sendEvent
(
event
);
}
}
packages/flutter/test/physics/utils_test.dart
0 → 100644
View file @
72517f0a
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/physics.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
test
(
'nearEquals'
,
()
{
expect
(
nearEqual
(
double
.
infinity
,
double
.
infinity
,
0.1
),
isTrue
);
expect
(
nearEqual
(
double
.
negativeInfinity
,
double
.
negativeInfinity
,
0.1
),
isTrue
);
expect
(
nearEqual
(
double
.
infinity
,
double
.
negativeInfinity
,
0.1
),
isFalse
);
expect
(
nearEqual
(
0.1
,
0.11
,
0.001
),
isFalse
);
expect
(
nearEqual
(
0.1
,
0.11
,
0.1
),
isTrue
);
expect
(
nearEqual
(
0.1
,
0.1
,
0.0000001
),
isTrue
);
});
}
packages/flutter/test/semantics/semantics_test.dart
View file @
72517f0a
...
...
@@ -348,7 +348,7 @@ void main() {
expect
(
minimalProperties
.
toStringDeep
(
minLevel:
DiagnosticLevel
.
hidden
),
'SemanticsNode#1(owner: null, isMergedIntoParent: false, mergeAllDescendantsIntoThisNode: false, Rect.fromLTRB(0.0, 0.0, 0.0, 0.0), actions: [], isInMutuallyExcusiveGroup: false, isSelected: false, isFocused: false, isButton: false, isTextField: false, invisible, label: "", value: "", increasedValue: "", decreasedValue: "", hint: "", textDirection: null, nextNodeId: null, sortOrder: null)
\n
'
'SemanticsNode#1(owner: null, isMergedIntoParent: false, mergeAllDescendantsIntoThisNode: false, Rect.fromLTRB(0.0, 0.0, 0.0, 0.0), actions: [], isInMutuallyExcusiveGroup: false, isSelected: false, isFocused: false, isButton: false, isTextField: false, invisible, label: "", value: "", increasedValue: "", decreasedValue: "", hint: "", textDirection: null, nextNodeId: null, sortOrder: null
, scrollExtentMin: null, scrollPosition: null, scrollExtentMax: null
)
\n
'
);
final
SemanticsConfiguration
config
=
new
SemanticsConfiguration
()
...
...
@@ -516,4 +516,4 @@ class TestRender extends RenderProxyBox {
class
CustomSortKey
extends
OrdinalSortKey
{
const
CustomSortKey
(
double
order
,
{
String
name
})
:
super
(
order
,
name:
name
);
}
\ No newline at end of file
}
packages/flutter/test/widgets/scrollable_semantics_test.dart
View file @
72517f0a
...
...
@@ -5,7 +5,6 @@
import
'package:flutter/material.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/services.dart'
;
import
'package:flutter/widgets.dart'
;
import
'semantics_tester.dart'
;
...
...
@@ -204,12 +203,7 @@ void main() {
expect
(
tester
.
getTopLeft
(
find
.
byWidget
(
children
[
1
])).
dy
,
kToolbarHeight
);
});
testWidgets
(
'vertical scrolling sends ScrollCompletedSemanticsEvent'
,
(
WidgetTester
tester
)
async
{
final
List
<
dynamic
>
messages
=
<
dynamic
>[];
SystemChannels
.
accessibility
.
setMockMessageHandler
((
dynamic
message
)
{
messages
.
add
(
message
);
});
testWidgets
(
'correct scrollProgress'
,
(
WidgetTester
tester
)
async
{
semantics
=
new
SemanticsTester
(
tester
);
final
List
<
Widget
>
textWidgets
=
<
Widget
>[];
...
...
@@ -220,74 +214,84 @@ void main() {
child:
new
ListView
(
children:
textWidgets
),
));
await
flingUp
(
tester
);
expect
(
messages
,
isNot
(
hasLength
(
0
)));
expect
(
messages
.
every
((
dynamic
message
)
=>
message
[
'type'
]
==
'scroll'
),
isTrue
);
expect
(
semantics
,
includesNodeWith
(
scrollExtentMin:
0.0
,
scrollPosition:
0.0
,
scrollExtentMax:
520.0
,
actions:
<
SemanticsAction
>[
SemanticsAction
.
scrollUp
,
],
));
Map
<
Object
,
Object
>
message
=
messages
.
last
[
'data'
];
expect
(
message
[
'axis'
],
'v'
);
expect
(
message
[
'pixels'
],
isPositive
);
expect
(
message
[
'minScrollExtent'
],
0.0
);
expect
(
message
[
'maxScrollExtent'
],
520.0
);
await
flingUp
(
tester
);
messages
.
clear
();
await
flingDown
(
tester
);
expect
(
semantics
,
includesNodeWith
(
scrollExtentMin:
0.0
,
scrollPosition:
380.2
,
scrollExtentMax:
520.0
,
actions:
<
SemanticsAction
>[
SemanticsAction
.
scrollUp
,
SemanticsAction
.
scrollDown
,
],
));
expect
(
messages
,
isNot
(
hasLength
(
0
)));
expect
(
messages
.
every
((
dynamic
message
)
=>
message
[
'type'
]
==
'scroll'
),
isTrue
);
await
flingUp
(
tester
);
message
=
messages
.
last
[
'data'
];
expect
(
message
[
'axis'
],
'v'
);
expect
(
message
[
'pixels'
],
isNonNegative
);
expect
(
message
[
'minScrollExtent'
],
0.0
);
expect
(
message
[
'maxScrollExtent'
],
520.0
);
expect
(
semantics
,
includesNodeWith
(
scrollExtentMin:
0.0
,
scrollPosition:
520.0
,
scrollExtentMax:
520.0
,
actions:
<
SemanticsAction
>[
SemanticsAction
.
scrollDown
,
],
));
});
testWidgets
(
'horizontal scrolling sends ScrollCompletedSemanticsEvent'
,
(
WidgetTester
tester
)
async
{
final
List
<
dynamic
>
messages
=
<
dynamic
>[];
SystemChannels
.
accessibility
.
setMockMessageHandler
((
dynamic
message
)
{
messages
.
add
(
message
);
});
testWidgets
(
'correct scrollProgress for unbound'
,
(
WidgetTester
tester
)
async
{
semantics
=
new
SemanticsTester
(
tester
);
final
List
<
Widget
>
children
=
<
Widget
>[];
for
(
int
i
=
0
;
i
<
80
;
i
++)
children
.
add
(
new
Container
(
child:
new
Text
(
'
$i
'
),
width:
100.0
,
));
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
new
ListView
(
children:
children
,
scrollDirection:
Axis
.
horizontal
,
child:
new
ListView
.
builder
(
itemExtent:
20.0
,
itemBuilder:
(
BuildContext
context
,
int
index
)
{
return
new
Text
(
'entry
$index
'
);
},
),
));
await
flingLeft
(
tester
);
expect
(
messages
,
isNot
(
hasLength
(
0
)));
expect
(
messages
.
every
((
dynamic
message
)
=>
message
[
'type'
]
==
'scroll'
),
isTrue
);
expect
(
semantics
,
includesNodeWith
(
scrollExtentMin:
0.0
,
scrollPosition:
0.0
,
scrollExtentMax:
double
.
infinity
,
actions:
<
SemanticsAction
>[
SemanticsAction
.
scrollUp
,
],
));
Map
<
Object
,
Object
>
message
=
messages
.
last
[
'data'
];
expect
(
message
[
'axis'
],
'h'
);
expect
(
message
[
'pixels'
],
isPositive
);
expect
(
message
[
'minScrollExtent'
],
0.0
);
expect
(
message
[
'maxScrollExtent'
],
7200.0
);
await
flingUp
(
tester
);
messages
.
clear
();
await
flingRight
(
tester
);
expect
(
semantics
,
includesNodeWith
(
scrollExtentMin:
0.0
,
scrollPosition:
380.2
,
scrollExtentMax:
double
.
infinity
,
actions:
<
SemanticsAction
>[
SemanticsAction
.
scrollUp
,
SemanticsAction
.
scrollDown
,
],
));
expect
(
messages
,
isNot
(
hasLength
(
0
)));
expect
(
messages
.
every
((
dynamic
message
)
=>
message
[
'type'
]
==
'scroll'
),
isTrue
);
await
flingUp
(
tester
);
message
=
messages
.
last
[
'data'
];
expect
(
message
[
'axis'
],
'h'
);
expect
(
message
[
'pixels'
],
isNonNegative
);
expect
(
message
[
'minScrollExtent'
],
0.0
);
expect
(
message
[
'maxScrollExtent'
],
7200.0
);
expect
(
semantics
,
includesNodeWith
(
scrollExtentMin:
0.0
,
scrollPosition:
760.4
,
scrollExtentMax:
double
.
infinity
,
actions:
<
SemanticsAction
>[
SemanticsAction
.
scrollUp
,
SemanticsAction
.
scrollDown
,
],
));
});
testWidgets
(
'Semantics tree is populated mid-scroll'
,
(
WidgetTester
tester
)
async
{
...
...
packages/flutter/test/widgets/semantics_tester.dart
View file @
72517f0a
...
...
@@ -5,6 +5,7 @@
import
'dart:ui'
show
SemanticsFlag
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/physics.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:meta/meta.dart'
;
...
...
@@ -369,6 +370,9 @@ class SemanticsTester {
TextDirection
textDirection
,
List
<
SemanticsAction
>
actions
,
List
<
SemanticsFlag
>
flags
,
double
scrollPosition
,
double
scrollExtentMax
,
double
scrollExtentMin
,
SemanticsNode
ancestor
,
})
{
bool
checkNode
(
SemanticsNode
node
)
{
...
...
@@ -390,6 +394,12 @@ class SemanticsTester {
if
(
expectedFlags
!=
actualFlags
)
return
false
;
}
if
(
scrollPosition
!=
null
&&
!
nearEqual
(
node
.
scrollPosition
,
scrollPosition
,
0.1
))
return
false
;
if
(
scrollExtentMax
!=
null
&&
!
nearEqual
(
node
.
scrollExtentMax
,
scrollExtentMax
,
0.1
))
return
false
;
if
(
scrollExtentMin
!=
null
&&
!
nearEqual
(
node
.
scrollExtentMin
,
scrollExtentMin
,
0.1
))
return
false
;
return
true
;
}
...
...
@@ -578,13 +588,19 @@ class _IncludesNodeWith extends Matcher {
this
.
textDirection
,
this
.
actions
,
this
.
flags
,
})
:
assert
(
label
!=
null
||
value
!=
null
||
actions
!=
null
||
flags
!=
null
);
this
.
scrollPosition
,
this
.
scrollExtentMax
,
this
.
scrollExtentMin
,
})
:
assert
(
label
!=
null
||
value
!=
null
||
actions
!=
null
||
flags
!=
null
||
scrollPosition
!=
null
||
scrollExtentMax
!=
null
||
scrollExtentMin
!=
null
);
final
String
label
;
final
String
value
;
final
TextDirection
textDirection
;
final
List
<
SemanticsAction
>
actions
;
final
List
<
SemanticsFlag
>
flags
;
final
double
scrollPosition
;
final
double
scrollExtentMax
;
final
double
scrollExtentMin
;
@override
bool
matches
(
covariant
SemanticsTester
item
,
Map
<
dynamic
,
dynamic
>
matchState
)
{
...
...
@@ -594,6 +610,9 @@ class _IncludesNodeWith extends Matcher {
textDirection:
textDirection
,
actions:
actions
,
flags:
flags
,
scrollPosition:
scrollPosition
,
scrollExtentMax:
scrollExtentMax
,
scrollExtentMin:
scrollExtentMin
,
).
isNotEmpty
;
}
...
...
@@ -619,6 +638,12 @@ class _IncludesNodeWith extends Matcher {
strings
.
add
(
'actions "
${actions.join(', ')}
"'
);
if
(
flags
!=
null
)
strings
.
add
(
'flags "
${flags.join(', ')}
"'
);
if
(
scrollPosition
!=
null
)
strings
.
add
(
'scrollPosition "
$scrollPosition
"'
);
if
(
scrollExtentMax
!=
null
)
strings
.
add
(
'scrollExtentMax "
$scrollExtentMax
"'
);
if
(
scrollExtentMin
!=
null
)
strings
.
add
(
'scrollExtentMin "
$scrollExtentMin
"'
);
return
strings
.
join
(
', '
);
}
}
...
...
@@ -633,6 +658,9 @@ Matcher includesNodeWith({
TextDirection
textDirection
,
List
<
SemanticsAction
>
actions
,
List
<
SemanticsFlag
>
flags
,
double
scrollPosition
,
double
scrollExtentMax
,
double
scrollExtentMin
,
})
{
return
new
_IncludesNodeWith
(
label:
label
,
...
...
@@ -640,5 +668,8 @@ Matcher includesNodeWith({
textDirection:
textDirection
,
actions:
actions
,
flags:
flags
,
scrollPosition:
scrollPosition
,
scrollExtentMax:
scrollExtentMax
,
scrollExtentMin:
scrollExtentMin
,
);
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment