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
06480523
Commit
06480523
authored
Jan 27, 2016
by
Ian Hickson
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1309 from Hixie/semantics-3
Semantics
parents
edcf4a69
28a17883
Changes
56
Show whitespace changes
Inline
Side-by-side
Showing
56 changed files
with
3395 additions
and
181 deletions
+3395
-181
page_selector_demo.dart
examples/material_gallery/lib/demo/page_selector_demo.dart
+6
-42
sector_layout.dart
examples/rendering/lib/sector_layout.dart
+1
-0
flutter.yaml
examples/stocks/flutter.yaml
+1
-0
main.dart
examples/stocks/lib/main.dart
+3
-1
stock_home.dart
examples/stocks/lib/stock_home.dart
+14
-5
stock_settings.dart
examples/stocks/lib/stock_settings.dart
+17
-0
stock_types.dart
examples/stocks/lib/stock_types.dart
+9
-4
rendering.dart
packages/flutter/lib/rendering.dart
+1
-0
tap.dart
packages/flutter/lib/src/gestures/tap.dart
+1
-1
chip.dart
packages/flutter/lib/src/material/chip.dart
+30
-21
drawer.dart
packages/flutter/lib/src/material/drawer.dart
+4
-3
drawer_item.dart
packages/flutter/lib/src/material/drawer_item.dart
+7
-5
icon_button.dart
packages/flutter/lib/src/material/icon_button.dart
+10
-10
material_app.dart
packages/flutter/lib/src/material/material_app.dart
+9
-1
popup_menu.dart
packages/flutter/lib/src/material/popup_menu.dart
+10
-8
radio.dart
packages/flutter/lib/src/material/radio.dart
+8
-5
scaffold.dart
packages/flutter/lib/src/material/scaffold.dart
+7
-4
snack_bar.dart
packages/flutter/lib/src/material/snack_bar.dart
+20
-17
tabs.dart
packages/flutter/lib/src/material/tabs.dart
+50
-1
toggleable.dart
packages/flutter/lib/src/material/toggleable.dart
+33
-4
tooltip.dart
packages/flutter/lib/src/material/tooltip.dart
+12
-1
text_painter.dart
packages/flutter/lib/src/painting/text_painter.dart
+6
-2
transforms.dart
packages/flutter/lib/src/painting/transforms.dart
+82
-0
README.md
packages/flutter/lib/src/rendering/README.md
+112
-0
basic_types.dart
packages/flutter/lib/src/rendering/basic_types.dart
+30
-0
binding.dart
packages/flutter/lib/src/rendering/binding.dart
+24
-1
block.dart
packages/flutter/lib/src/rendering/block.dart
+3
-0
box.dart
packages/flutter/lib/src/rendering/box.dart
+4
-8
debug.dart
packages/flutter/lib/src/rendering/debug.dart
+1
-1
editable_line.dart
packages/flutter/lib/src/rendering/editable_line.dart
+5
-2
flex.dart
packages/flutter/lib/src/rendering/flex.dart
+2
-0
object.dart
packages/flutter/lib/src/rendering/object.dart
+517
-2
overflow.dart
packages/flutter/lib/src/rendering/overflow.dart
+1
-0
paragraph.dart
packages/flutter/lib/src/rendering/paragraph.dart
+6
-1
proxy_box.dart
packages/flutter/lib/src/rendering/proxy_box.dart
+285
-2
semantics.dart
packages/flutter/lib/src/rendering/semantics.dart
+448
-0
stack.dart
packages/flutter/lib/src/rendering/stack.dart
+2
-0
view.dart
packages/flutter/lib/src/rendering/view.dart
+1
-0
viewport.dart
packages/flutter/lib/src/rendering/viewport.dart
+17
-2
basic.dart
packages/flutter/lib/src/widgets/basic.dart
+97
-2
focus.dart
packages/flutter/lib/src/widgets/focus.dart
+9
-7
framework.dart
packages/flutter/lib/src/widgets/framework.dart
+1
-1
gesture_detector.dart
packages/flutter/lib/src/widgets/gesture_detector.dart
+153
-3
modal_barrier.dart
packages/flutter/lib/src/widgets/modal_barrier.dart
+15
-11
semantics_debugger.dart
packages/flutter/lib/src/widgets/semantics_debugger.dart
+362
-0
widgets.dart
packages/flutter/lib/widgets.dart
+2
-1
pubspec.yaml
packages/flutter/pubspec.yaml
+2
-2
buttons_test.dart
packages/flutter/test/widget/buttons_test.dart
+54
-0
semantics_1_test.dart
packages/flutter/test/widget/semantics_1_test.dart
+250
-0
semantics_2_test.dart
packages/flutter/test/widget/semantics_2_test.dart
+190
-0
semantics_3_test.dart
packages/flutter/test/widget/semantics_3_test.dart
+152
-0
semantics_4_test.dart
packages/flutter/test/widget/semantics_4_test.dart
+120
-0
semantics_5_test.dart
packages/flutter/test/widget/semantics_5_test.dart
+49
-0
semantics_6_test.dart
packages/flutter/test/widget/semantics_6_test.dart
+50
-0
test_semantics.dart
packages/flutter/test/widget/test_semantics.dart
+18
-0
tooltip_test.dart
packages/flutter/test/widget/tooltip_test.dart
+72
-0
No files found.
examples/material_gallery/lib/demo/page_selector_demo.dart
View file @
06480523
...
...
@@ -2,49 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/animation.dart'
;
import
'package:flutter/material.dart'
;
class
PageSelectorDemo
extends
StatelessComponent
{
Widget
_buildTabIndicator
(
BuildContext
context
,
String
iconName
)
{
final
Color
color
=
Theme
.
of
(
context
).
primaryColor
;
final
ColorTween
_selectedColor
=
new
ColorTween
(
begin:
Colors
.
transparent
,
end:
color
);
final
ColorTween
_previousColor
=
new
ColorTween
(
begin:
color
,
end:
Colors
.
transparent
);
final
TabBarSelectionState
selection
=
TabBarSelection
.
of
(
context
);
CurvedAnimation
animation
=
new
CurvedAnimation
(
parent:
selection
.
animation
,
curve:
Curves
.
ease
);
return
new
AnimatedBuilder
(
animation:
animation
,
builder:
(
BuildContext
context
,
Widget
child
)
{
Color
background
=
selection
.
value
==
iconName
?
_selectedColor
.
end
:
_selectedColor
.
begin
;
if
(
selection
.
valueIsChanging
)
{
// Then the selection's animation is animating from previousValue to value.
if
(
selection
.
value
==
iconName
)
background
=
_selectedColor
.
evaluate
(
animation
);
else
if
(
selection
.
previousValue
==
iconName
)
background
=
_previousColor
.
evaluate
(
animation
);
}
return
new
Container
(
width:
12.0
,
height:
12.0
,
margin:
new
EdgeDims
.
all
(
4.0
),
decoration:
new
BoxDecoration
(
backgroundColor:
background
,
border:
new
Border
.
all
(
color:
_selectedColor
.
end
),
shape:
BoxShape
.
circle
)
);
}
);
}
Widget
_buildTabView
(
String
iconName
)
{
return
new
Container
(
key:
new
ValueKey
<
String
>(
iconName
),
padding:
const
EdgeDims
.
all
(
12.0
),
child:
new
Card
(
child:
new
Center
(
child:
new
Icon
(
icon:
"action/
$iconName
"
,
size:
IconSize
.
s48
)
child:
new
Icon
(
icon:
'action/
$iconName
'
,
size:
IconSize
.
s48
)
)
)
);
...
...
@@ -57,10 +24,10 @@ class PageSelectorDemo extends StatelessComponent {
}
Widget
build
(
BuildContext
notUsed
)
{
// Can't find the TabBarSelection from this context.
final
List
<
String
>
iconNames
=
<
String
>[
"event"
,
"home"
,
"android"
,
"alarm"
,
"face"
,
"language"
];
final
List
<
String
>
iconNames
=
<
String
>[
'event'
,
'home'
,
'android'
,
'alarm'
,
'face'
,
'language'
];
return
new
Scaffold
(
toolBar:
new
ToolBar
(
center:
new
Text
(
"Page Selector"
)),
toolBar:
new
ToolBar
(
center:
new
Text
(
'Page Selector'
)),
body:
new
TabBarSelection
(
values:
iconNames
,
child:
new
Builder
(
...
...
@@ -72,16 +39,13 @@ class PageSelectorDemo extends StatelessComponent {
child:
new
Row
(
children:
<
Widget
>[
new
IconButton
(
icon:
"navigation/arrow_back"
,
icon:
'navigation/arrow_back'
,
onPressed:
()
{
_handleArrowButtonPress
(
context
,
-
1
);
},
tooltip:
'Back'
),
new
Row
(
children:
iconNames
.
map
((
String
name
)
=>
_buildTabIndicator
(
context
,
name
)).
toList
(),
justifyContent:
FlexJustifyContent
.
collapse
),
new
TabPageSelector
<
String
>(),
new
IconButton
(
icon:
"navigation/arrow_forward"
,
icon:
'navigation/arrow_forward'
,
onPressed:
()
{
_handleArrowButtonPress
(
context
,
1
);
},
tooltip:
'Forward'
)
...
...
examples/rendering/lib/sector_layout.dart
View file @
06480523
...
...
@@ -103,6 +103,7 @@ abstract class RenderSector extends RenderObject {
}
Rect
get
paintBounds
=>
new
Rect
.
fromLTWH
(
0.0
,
0.0
,
2.0
*
deltaRadius
,
2.0
*
deltaRadius
);
Rect
get
semanticBounds
=>
new
Rect
.
fromLTWH
(-
deltaRadius
,
-
deltaRadius
,
2.0
*
deltaRadius
,
2.0
*
deltaRadius
);
bool
hitTest
(
HitTestResult
result
,
{
double
radius
,
double
theta
})
{
if
(
radius
<
parentData
.
radius
||
radius
>=
parentData
.
radius
+
deltaRadius
||
...
...
examples/stocks/flutter.yaml
View file @
06480523
...
...
@@ -2,6 +2,7 @@ name: stocks
version
:
0.0.2
update-url
:
http://localhost:9888/
material-design-icons
:
-
name
:
action/accessibility
-
name
:
action/account_balance
-
name
:
action/assessment
-
name
:
action/backup
...
...
examples/stocks/lib/main.dart
View file @
06480523
...
...
@@ -43,7 +43,8 @@ class StocksAppState extends State<StocksApp> {
backupMode:
BackupMode
.
enabled
,
debugShowGrid:
false
,
debugShowSizes:
false
,
showPerformanceOverlay:
false
showPerformanceOverlay:
false
,
showSemanticsDebugger:
false
);
void
initState
()
{
...
...
@@ -110,6 +111,7 @@ class StocksAppState extends State<StocksApp> {
theme:
theme
,
debugShowMaterialGrid:
_configuration
.
debugShowGrid
,
showPerformanceOverlay:
_configuration
.
showPerformanceOverlay
,
showSemanticsDebugger:
_configuration
.
showSemanticsDebugger
,
routes:
<
String
,
RouteBuilder
>{
'/'
:
(
RouteArguments
args
)
=>
new
StockHome
(
_stocks
,
_symbols
,
_configuration
,
configurationUpdater
),
'/settings'
:
(
RouteArguments
args
)
=>
new
StockSettings
(
_configuration
,
configurationUpdater
)
...
...
examples/stocks/lib/stock_home.dart
View file @
06480523
...
...
@@ -88,16 +88,16 @@ class StockHomeState extends State<StockHome> {
content:
new
Text
(
'This feature has not yet been implemented.'
),
actions:
<
Widget
>[
new
FlatButton
(
child:
new
Text
(
'USE IT'
),
onPressed:
()
{
Navigator
.
pop
(
context
,
false
);
}
},
child:
new
Text
(
'USE IT'
)
),
new
FlatButton
(
child:
new
Text
(
'OH WELL'
),
onPressed:
()
{
Navigator
.
pop
(
context
,
false
);
}
},
child:
new
Text
(
'OH WELL'
)
),
]
)
...
...
@@ -107,7 +107,16 @@ class StockHomeState extends State<StockHome> {
),
new
DrawerItem
(
icon:
'device/dvr'
,
onPressed:
()
{
debugDumpApp
();
debugDumpRenderTree
();
debugDumpLayerTree
();
},
onPressed:
()
{
try
{
debugDumpApp
();
debugDumpRenderTree
();
debugDumpLayerTree
();
debugDumpSemanticsTree
();
}
catch
(
e
,
stack
)
{
debugPrint
(
'Exception while dumping app:
\n
$e
\n
$stack
'
);
}
},
child:
new
Text
(
'Dump App to Console'
)
),
new
DrawerDivider
(),
...
...
examples/stocks/lib/stock_settings.dart
View file @
06480523
...
...
@@ -35,6 +35,10 @@ class StockSettingsState extends State<StockSettings> {
sendUpdates
(
config
.
configuration
.
copyWith
(
showPerformanceOverlay:
value
));
}
void
_handleShowSemanticsDebuggerChanged
(
bool
value
)
{
sendUpdates
(
config
.
configuration
.
copyWith
(
showSemanticsDebugger:
value
));
}
void
_confirmOptimismChange
()
{
switch
(
config
.
configuration
.
stockMode
)
{
case
StockMode
.
optimistic
:
...
...
@@ -118,6 +122,19 @@ class StockSettingsState extends State<StockSettings> {
]
)
),
new
DrawerItem
(
icon:
'action/accessibility'
,
onPressed:
()
{
_handleShowSemanticsDebuggerChanged
(!
config
.
configuration
.
showSemanticsDebugger
);
},
child:
new
Row
(
children:
<
Widget
>[
new
Flexible
(
child:
new
Text
(
'Show semantics overlay'
)),
new
Switch
(
value:
config
.
configuration
.
showSemanticsDebugger
,
onChanged:
_handleShowSemanticsDebuggerChanged
),
]
)
),
];
assert
(()
{
// material grid and size construction lines are only available in checked mode
...
...
examples/stocks/lib/stock_types.dart
View file @
06480523
...
...
@@ -13,13 +13,15 @@ class StockConfiguration {
this
.
backupMode
,
this
.
debugShowGrid
,
this
.
debugShowSizes
,
this
.
showPerformanceOverlay
this
.
showPerformanceOverlay
,
this
.
showSemanticsDebugger
})
{
assert
(
stockMode
!=
null
);
assert
(
backupMode
!=
null
);
assert
(
debugShowGrid
!=
null
);
assert
(
debugShowSizes
!=
null
);
assert
(
showPerformanceOverlay
!=
null
);
assert
(
showSemanticsDebugger
!=
null
);
}
final
StockMode
stockMode
;
...
...
@@ -27,20 +29,23 @@ class StockConfiguration {
final
bool
debugShowGrid
;
final
bool
debugShowSizes
;
final
bool
showPerformanceOverlay
;
final
bool
showSemanticsDebugger
;
StockConfiguration
copyWith
({
StockMode
stockMode
,
BackupMode
backupMode
,
bool
debugShowGrid
,
bool
debugShowSizes
,
bool
showPerformanceOverlay
bool
showPerformanceOverlay
,
bool
showSemanticsDebugger
})
{
return
new
StockConfiguration
(
stockMode:
stockMode
??
this
.
stockMode
,
backupMode:
backupMode
??
this
.
backupMode
,
debugShowGrid:
debugShowGrid
??
this
.
debugShowGrid
,
debugShowSizes:
debugShowSizes
??
this
.
debugShowSizes
,
showPerformanceOverlay:
showPerformanceOverlay
??
this
.
showPerformanceOverlay
showPerformanceOverlay:
showPerformanceOverlay
??
this
.
showPerformanceOverlay
,
showSemanticsDebugger:
showSemanticsDebugger
??
this
.
showSemanticsDebugger
);
}
}
packages/flutter/lib/rendering.dart
View file @
06480523
...
...
@@ -25,6 +25,7 @@ export 'src/rendering/overflow.dart';
export
'src/rendering/paragraph.dart'
;
export
'src/rendering/performance_overlay.dart'
;
export
'src/rendering/proxy_box.dart'
;
export
'src/rendering/semantics.dart'
;
export
'src/rendering/shifted_box.dart'
;
export
'src/rendering/stack.dart'
;
export
'src/rendering/view.dart'
;
...
...
packages/flutter/lib/src/gestures/tap.dart
View file @
06480523
...
...
@@ -31,7 +31,7 @@ class TapGestureRecognizer extends PrimaryPointerGestureRecognizer {
);
GestureTapDownCallback
onTapDown
;
GestureTap
Down
Callback
onTapUp
;
GestureTap
Up
Callback
onTapUp
;
GestureTapCallback
onTap
;
GestureTapCancelCallback
onTapCancel
;
...
...
packages/flutter/lib/src/material/chip.dart
View file @
06480523
...
...
@@ -7,6 +7,7 @@ import 'package:flutter/widgets.dart';
import
'colors.dart'
;
import
'debug.dart'
;
import
'icon.dart'
;
import
'tooltip.dart'
;
const
double
_kChipHeight
=
32.0
;
const
double
_kAvatarDiamater
=
_kChipHeight
;
...
...
@@ -41,11 +42,13 @@ class Chip extends StatelessComponent {
if
(
avatar
!=
null
)
{
leftPadding
=
0.0
;
children
.
add
(
new
Container
(
children
.
add
(
new
ExcludeSemantics
(
child:
new
Container
(
margin:
const
EdgeDims
.
only
(
right:
8.0
),
width:
_kAvatarDiamater
,
height:
_kAvatarDiamater
,
child:
avatar
)
));
}
...
...
@@ -58,6 +61,8 @@ class Chip extends StatelessComponent {
rightPadding
=
0.0
;
children
.
add
(
new
GestureDetector
(
onTap:
onDeleted
,
child:
new
Tooltip
(
message:
'Delete "
$label
"'
,
child:
new
Container
(
padding:
const
EdgeDims
.
symmetric
(
horizontal:
4.0
),
child:
new
Icon
(
...
...
@@ -66,10 +71,13 @@ class Chip extends StatelessComponent {
color:
Colors
.
black54
)
)
)
));
}
return
new
Container
(
return
new
Semantics
(
container:
true
,
child:
new
Container
(
height:
_kChipHeight
,
padding:
new
EdgeDims
.
only
(
left:
leftPadding
,
right:
rightPadding
),
decoration:
new
BoxDecoration
(
...
...
@@ -80,6 +88,7 @@ class Chip extends StatelessComponent {
children:
children
,
justifyContent:
FlexJustifyContent
.
collapse
)
)
);
}
}
packages/flutter/lib/src/material/drawer.dart
View file @
06480523
...
...
@@ -125,7 +125,7 @@ class DrawerControllerState extends State<DrawerController> {
});
}
void
_handle
PointerDown
(
_
)
{
void
_handle
TapDown
(
Point
position
)
{
_controller
.
stop
();
_ensureHistoryEntry
();
}
...
...
@@ -166,6 +166,7 @@ class DrawerControllerState extends State<DrawerController> {
onHorizontalDragUpdate:
_move
,
onHorizontalDragEnd:
_settle
,
behavior:
HitTestBehavior
.
translucent
,
excludeFromSemantics:
true
,
child:
new
Container
(
width:
_kEdgeDragWidth
)
)
);
...
...
@@ -188,8 +189,8 @@ class DrawerControllerState extends State<DrawerController> {
),
new
Align
(
alignment:
const
FractionalOffset
(
0.0
,
0.5
),
child:
new
Listene
r
(
on
PointerDown:
_handlePointer
Down
,
child:
new
GestureDetecto
r
(
on
TapDown:
_handleTap
Down
,
child:
new
Align
(
alignment:
const
FractionalOffset
(
1.0
,
0.5
),
widthFactor:
_controller
.
value
,
...
...
packages/flutter/lib/src/material/drawer_item.dart
View file @
06480523
...
...
@@ -70,12 +70,14 @@ class DrawerItem extends StatelessComponent {
)
);
return
new
Container
(
return
new
MergeSemantics
(
child:
new
Container
(
height:
48.0
,
child:
new
InkWell
(
onTap:
onPressed
,
child:
new
Row
(
children:
children
)
)
)
);
}
...
...
packages/flutter/lib/src/material/icon_button.dart
View file @
06480523
...
...
@@ -26,16 +26,13 @@ class IconButton extends StatelessComponent {
final
String
tooltip
;
Widget
build
(
BuildContext
context
)
{
Widget
result
=
new
InkResponse
(
onTap:
onPressed
,
child:
new
Padding
(
Widget
result
=
new
Padding
(
padding:
const
EdgeDims
.
all
(
8.0
),
child:
new
Icon
(
icon:
icon
,
colorTheme:
colorTheme
,
color:
color
)
)
);
if
(
tooltip
!=
null
)
{
result
=
new
Tooltip
(
...
...
@@ -43,7 +40,10 @@ class IconButton extends StatelessComponent {
child:
result
);
}
return
result
;
return
new
InkResponse
(
onTap:
onPressed
,
child:
result
);
}
void
debugFillDescription
(
List
<
String
>
description
)
{
...
...
packages/flutter/lib/src/material/material_app.dart
View file @
06480523
...
...
@@ -49,12 +49,14 @@ class MaterialApp extends StatefulComponent {
this
.
onGenerateRoute
,
this
.
onLocaleChanged
,
this
.
debugShowMaterialGrid
:
false
,
this
.
showPerformanceOverlay
:
false
this
.
showPerformanceOverlay
:
false
,
this
.
showSemanticsDebugger
:
false
})
:
super
(
key:
key
)
{
assert
(
routes
!=
null
);
assert
(
routes
.
containsKey
(
Navigator
.
defaultRouteName
)
||
onGenerateRoute
!=
null
);
assert
(
debugShowMaterialGrid
!=
null
);
assert
(
showPerformanceOverlay
!=
null
);
assert
(
showSemanticsDebugger
!=
null
);
}
final
String
title
;
...
...
@@ -64,6 +66,7 @@ class MaterialApp extends StatefulComponent {
final
LocaleChangedCallback
onLocaleChanged
;
final
bool
debugShowMaterialGrid
;
final
bool
showPerformanceOverlay
;
final
bool
showSemanticsDebugger
;
_MaterialAppState
createState
()
=>
new
_MaterialAppState
();
}
...
...
@@ -194,6 +197,11 @@ class _MaterialAppState extends State<MaterialApp> implements BindingObserver {
]
);
}
if
(
config
.
showSemanticsDebugger
)
{
result
=
new
SemanticsDebugger
(
child:
result
);
}
return
result
;
}
...
...
packages/flutter/lib/src/material/popup_menu.dart
View file @
06480523
...
...
@@ -32,7 +32,8 @@ class PopupMenuItem<T> extends StatelessComponent {
final
T
value
;
Widget
build
(
BuildContext
context
)
{
return
new
Container
(
return
new
MergeSemantics
(
child:
new
Container
(
height:
_kMenuItemHeight
,
padding:
const
EdgeDims
.
symmetric
(
horizontal:
_kMenuHorizontalPadding
),
child:
new
DefaultTextStyle
(
...
...
@@ -42,6 +43,7 @@ class PopupMenuItem<T> extends StatelessComponent {
child:
child
)
)
)
);
}
}
...
...
packages/flutter/lib/src/material/radio.dart
View file @
06480523
...
...
@@ -44,11 +44,14 @@ class Radio<T> extends StatelessComponent {
Widget
build
(
BuildContext
context
)
{
assert
(
debugCheckHasMaterial
(
context
));
ThemeData
themeData
=
Theme
.
of
(
context
);
return
new
_RadioRenderObjectWidget
(
return
new
Semantics
(
checked:
value
==
groupValue
,
child:
new
_RadioRenderObjectWidget
(
selected:
value
==
groupValue
,
activeColor:
activeColor
??
themeData
.
accentColor
,
inactiveColor:
_getInactiveColor
(
themeData
),
onChanged:
_enabled
?
_handleChanged
:
null
)
);
}
}
...
...
packages/flutter/lib/src/material/scaffold.dart
View file @
06480523
...
...
@@ -495,11 +495,14 @@ class _PersistentBottomSheetState extends State<_PersistentBottomSheet> {
child:
child
);
},
child:
new
Semantics
(
container:
true
,
child:
new
BottomSheet
(
animationController:
config
.
animationController
,
onClosing:
config
.
onClosing
,
builder:
config
.
builder
)
)
);
}
...
...
packages/flutter/lib/src/material/snack_bar.dart
View file @
06480523
...
...
@@ -95,6 +95,8 @@ class SnackBar extends StatelessComponent {
child:
child
);
},
child:
new
Semantics
(
container:
true
,
child:
new
Material
(
elevation:
6
,
color:
_kSnackBackground
,
...
...
@@ -118,6 +120,7 @@ class SnackBar extends StatelessComponent {
)
)
)
)
);
}
...
...
packages/flutter/lib/src/material/tabs.dart
View file @
06480523
...
...
@@ -405,7 +405,7 @@ class TabBarSelection<T> extends StatefulComponent {
final
ValueChanged
<
T
>
onChanged
;
final
Widget
child
;
TabBarSelectionState
createState
()
=>
new
TabBarSelectionState
<
T
>();
TabBarSelectionState
<
T
>
createState
()
=>
new
TabBarSelectionState
<
T
>();
static
TabBarSelectionState
of
(
BuildContext
context
)
{
return
context
.
ancestorStateOfType
(
const
TypeMatcher
<
TabBarSelectionState
>());
...
...
@@ -952,3 +952,52 @@ class _TabBarViewState extends PageableListState<TabBarView> implements TabBarSe
);
}
}
class
TabPageSelector
<
T
>
extends
StatelessComponent
{
const
TabPageSelector
({
Key
key
})
:
super
(
key:
key
);
Widget
_buildTabIndicator
(
TabBarSelectionState
<
T
>
selection
,
T
tab
,
Animation
animation
,
ColorTween
selectedColor
,
ColorTween
previousColor
)
{
Color
background
;
if
(
selection
.
valueIsChanging
)
{
// The selection's animation is animating from previousValue to value.
if
(
selection
.
value
==
tab
)
background
=
selectedColor
.
evaluate
(
animation
);
else
if
(
selection
.
previousValue
==
tab
)
background
=
previousColor
.
evaluate
(
animation
);
else
background
=
selectedColor
.
begin
;
}
else
{
background
=
selection
.
value
==
tab
?
selectedColor
.
end
:
selectedColor
.
begin
;
}
return
new
Container
(
width:
12.0
,
height:
12.0
,
margin:
new
EdgeDims
.
all
(
4.0
),
decoration:
new
BoxDecoration
(
backgroundColor:
background
,
border:
new
Border
.
all
(
color:
selectedColor
.
end
),
shape:
BoxShape
.
circle
)
);
}
Widget
build
(
BuildContext
context
)
{
final
TabBarSelectionState
selection
=
TabBarSelection
.
of
(
context
);
final
Color
color
=
Theme
.
of
(
context
).
primaryColor
;
final
ColorTween
selectedColor
=
new
ColorTween
(
begin:
Colors
.
transparent
,
end:
color
);
final
ColorTween
previousColor
=
new
ColorTween
(
begin:
color
,
end:
Colors
.
transparent
);
Animation
<
double
>
animation
=
new
CurvedAnimation
(
parent:
selection
.
animation
,
curve:
Curves
.
ease
);
return
new
AnimatedBuilder
(
animation:
animation
,
builder:
(
BuildContext
context
,
Widget
child
)
{
return
new
Semantics
(
label:
'Page
${selection.index + 1}
of
${selection.values.length}
'
,
child:
new
Row
(
children:
selection
.
values
.
map
((
T
tab
)
=>
_buildTabIndicator
(
selection
,
tab
,
animation
,
selectedColor
,
previousColor
)).
toList
(),
justifyContent:
FlexJustifyContent
.
collapse
)
);
}
);
}
}
packages/flutter/lib/src/material/toggleable.dart
View file @
06480523
...
...
@@ -14,17 +14,18 @@ const Duration _kToggleDuration = const Duration(milliseconds: 200);
// toggle animations. It handles storing the current value, dispatching
// ValueChanged on a tap gesture and driving a changed animation. Subclasses are
// responsible for painting.
abstract
class
RenderToggleable
extends
RenderConstrainedBox
{
abstract
class
RenderToggleable
extends
RenderConstrainedBox
implements
SemanticActionHandler
{
RenderToggleable
({
bool
value
,
Size
size
,
Color
activeColor
,
Color
inactiveColor
,
this
.
onChanged
,
ValueChanged
<
bool
>
onChanged
,
double
minRadialReactionRadius:
0.0
})
:
_value
=
value
,
_activeColor
=
activeColor
,
_inactiveColor
=
inactiveColor
,
_onChanged
=
onChanged
,
super
(
additionalConstraints:
new
BoxConstraints
.
tight
(
size
))
{
assert
(
value
!=
null
);
assert
(
activeColor
!=
null
);
...
...
@@ -61,6 +62,7 @@ abstract class RenderToggleable extends RenderConstrainedBox {
if
(
value
==
_value
)
return
;
_value
=
value
;
markNeedsSemanticsUpdate
(
onlyChanges:
true
,
noGeometry:
true
);
_position
..
curve
=
Curves
.
easeIn
..
reverseCurve
=
Curves
.
easeOut
;
...
...
@@ -87,9 +89,20 @@ abstract class RenderToggleable extends RenderConstrainedBox {
markNeedsPaint
();
}
bool
get
isInteractive
=>
onChanged
!=
null
;
ValueChanged
<
bool
>
get
onChanged
=>
_onChanged
;
ValueChanged
<
bool
>
_onChanged
;
void
set
onChanged
(
ValueChanged
<
bool
>
value
)
{
if
(
value
==
_onChanged
)
return
;
final
bool
wasInteractive
=
isInteractive
;
_onChanged
=
value
;
if
(
wasInteractive
!=
isInteractive
)
{
markNeedsPaint
();
markNeedsSemanticsUpdate
(
noGeometry:
true
);
}
}
ValueChanged
<
bool
>
onChanged
;
bool
get
isInteractive
=>
onChanged
!=
null
;
CurvedAnimation
get
position
=>
_position
;
CurvedAnimation
_position
;
...
...
@@ -146,4 +159,20 @@ abstract class RenderToggleable extends RenderConstrainedBox {
canvas
.
drawCircle
(
offset
.
toPoint
(),
_reaction
.
value
,
reactionPaint
);
}
}
bool
get
hasSemantics
=>
isInteractive
;
Iterable
<
SemanticAnnotator
>
getSemanticAnnotators
()
sync
*
{
yield
(
SemanticsNode
semantics
)
{
semantics
.
hasCheckedState
=
true
;
semantics
.
isChecked
=
_value
;
semantics
.
canBeTapped
=
isInteractive
;
};
}
void
handleSemanticTap
()
=>
_handleTap
();
void
handleSemanticLongPress
()
{
}
void
handleSemanticScrollLeft
()
{
}
void
handleSemanticScrollRight
()
{
}
void
handleSemanticScrollUp
()
{
}
void
handleSemanticScrollDown
()
{
}
}
packages/flutter/lib/src/material/tooltip.dart
View file @
06480523
...
...
@@ -67,6 +67,13 @@ class Tooltip extends StatefulComponent {
final
Widget
child
;
_TooltipState
createState
()
=>
new
_TooltipState
();
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
description
.
add
(
'"
$message
"'
);
description
.
add
(
'vertical offset:
$verticalOffset
'
);
description
.
add
(
'position:
${preferBelow ? "below" : "above"}
'
);
}
}
class
_TooltipState
extends
State
<
Tooltip
>
{
...
...
@@ -175,7 +182,11 @@ class _TooltipState extends State<Tooltip> {
return
new
GestureDetector
(
behavior:
HitTestBehavior
.
opaque
,
onLongPress:
showTooltip
,
excludeFromSemantics:
true
,
child:
new
Semantics
(
label:
config
.
message
,
child:
config
.
child
)
);
}
}
...
...
packages/flutter/lib/src/painting/text_painter.dart
View file @
06480523
...
...
@@ -11,9 +11,10 @@ import 'text_style.dart';
abstract
class
TextSpan
{
// This class must be immutable, because we won't notice when it changes.
const
TextSpan
();
String
toString
([
String
prefix
=
''
]);
void
build
(
ui
.
ParagraphBuilder
builder
);
ui
.
ParagraphStyle
get
paragraphStyle
=>
null
;
String
toPlainText
();
// for semantics
String
toString
([
String
prefix
=
''
]);
// for debugging
}
/// An immutable span of unstyled text.
...
...
@@ -37,6 +38,7 @@ class PlainTextSpan extends TextSpan {
int
get
hashCode
=>
text
.
hashCode
;
String
toPlainText
()
=>
text
;
String
toString
([
String
prefix
=
''
])
=>
'
$prefix$runtimeType
: "
$text
"'
;
}
...
...
@@ -81,6 +83,8 @@ class StyledTextSpan extends TextSpan {
int
get
hashCode
=>
hashValues
(
style
,
hashList
(
children
));
String
toPlainText
()
=>
children
.
map
((
TextSpan
child
)
=>
child
.
toPlainText
()).
join
();
String
toString
([
String
prefix
=
''
])
{
List
<
String
>
result
=
<
String
>[];
result
.
add
(
'
$prefix$runtimeType
:'
);
...
...
@@ -94,7 +98,7 @@ class StyledTextSpan extends TextSpan {
/// An object that paints a [TextSpan] into a canvas.
class
TextPainter
{
TextPainter
(
TextSpan
text
)
{
TextPainter
(
[
TextSpan
text
]
)
{
this
.
text
=
text
;
}
...
...
packages/flutter/lib/src/painting/transforms.dart
View file @
06480523
...
...
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:math'
as
math
;
import
'dart:typed_data'
;
import
'package:vector_math/vector_math_64.dart'
;
...
...
@@ -16,6 +17,7 @@ class MatrixUtils {
///
/// Returns null, otherwise.
static
Offset
getAsTranslation
(
Matrix4
transform
)
{
assert
(
transform
!=
null
);
Float64List
values
=
transform
.
storage
;
// Values are stored in column-major order.
if
(
values
[
0
]
==
1.0
&&
...
...
@@ -37,4 +39,84 @@ class MatrixUtils {
return
null
;
}
/// Returns true if the given matrices are exactly equal, and false
/// otherwise. Null values are assumed to be the identity matrix.
static
bool
matrixEquals
(
Matrix4
a
,
Matrix4
b
)
{
if
(
identical
(
a
,
b
))
return
true
;
assert
(
a
!=
null
||
b
!=
null
);
if
(
a
==
null
)
return
isIdentity
(
b
);
if
(
b
==
null
)
return
isIdentity
(
a
);
assert
(
a
!=
null
&&
b
!=
null
);
return
a
.
storage
[
0
]
==
b
.
storage
[
0
]
&&
a
.
storage
[
1
]
==
b
.
storage
[
1
]
&&
a
.
storage
[
2
]
==
b
.
storage
[
2
]
&&
a
.
storage
[
3
]
==
b
.
storage
[
3
]
&&
a
.
storage
[
4
]
==
b
.
storage
[
4
]
&&
a
.
storage
[
5
]
==
b
.
storage
[
5
]
&&
a
.
storage
[
6
]
==
b
.
storage
[
6
]
&&
a
.
storage
[
7
]
==
b
.
storage
[
7
]
&&
a
.
storage
[
8
]
==
b
.
storage
[
8
]
&&
a
.
storage
[
9
]
==
b
.
storage
[
9
]
&&
a
.
storage
[
10
]
==
b
.
storage
[
10
]
&&
a
.
storage
[
11
]
==
b
.
storage
[
11
]
&&
a
.
storage
[
12
]
==
b
.
storage
[
12
]
&&
a
.
storage
[
13
]
==
b
.
storage
[
13
]
&&
a
.
storage
[
14
]
==
b
.
storage
[
14
]
&&
a
.
storage
[
15
]
==
b
.
storage
[
15
];
}
static
bool
isIdentity
(
Matrix4
a
)
{
assert
(
a
!=
null
);
return
a
.
storage
[
0
]
==
1.0
// col 1
&&
a
.
storage
[
1
]
==
0.0
&&
a
.
storage
[
2
]
==
0.0
&&
a
.
storage
[
3
]
==
0.0
&&
a
.
storage
[
4
]
==
0.0
// col 2
&&
a
.
storage
[
5
]
==
1.0
&&
a
.
storage
[
6
]
==
0.0
&&
a
.
storage
[
7
]
==
0.0
&&
a
.
storage
[
8
]
==
0.0
// col 3
&&
a
.
storage
[
9
]
==
0.0
&&
a
.
storage
[
10
]
==
1.0
&&
a
.
storage
[
11
]
==
0.0
&&
a
.
storage
[
12
]
==
0.0
// col 4
&&
a
.
storage
[
13
]
==
0.0
&&
a
.
storage
[
14
]
==
0.0
&&
a
.
storage
[
15
]
==
1.0
;
}
static
Point
transformPoint
(
Matrix4
transform
,
Point
point
)
{
Vector3
position3
=
new
Vector3
(
point
.
x
,
point
.
y
,
0.0
);
Vector3
transformed3
=
transform
.
transform3
(
position3
);
return
new
Point
(
transformed3
.
x
,
transformed3
.
y
);
}
static
double
_min4
(
double
a
,
double
b
,
double
c
,
double
d
)
{
return
math
.
min
(
a
,
math
.
min
(
b
,
math
.
min
(
c
,
d
)));
}
static
double
_max4
(
double
a
,
double
b
,
double
c
,
double
d
)
{
return
math
.
max
(
a
,
math
.
max
(
b
,
math
.
max
(
c
,
d
)));
}
static
Rect
transformRect
(
Rect
rect
,
Matrix4
transform
)
{
assert
(
rect
!=
null
);
assert
(
transform
.
determinant
!=
0.0
);
if
(
isIdentity
(
transform
))
return
rect
;
transform
=
new
Matrix4
.
copy
(
transform
)..
invert
();
Point
point1
=
transformPoint
(
transform
,
rect
.
topLeft
);
Point
point2
=
transformPoint
(
transform
,
rect
.
topRight
);
Point
point3
=
transformPoint
(
transform
,
rect
.
bottomLeft
);
Point
point4
=
transformPoint
(
transform
,
rect
.
bottomRight
);
return
new
Rect
.
fromLTRB
(
_min4
(
point1
.
x
,
point2
.
x
,
point3
.
x
,
point4
.
x
),
_min4
(
point1
.
y
,
point2
.
y
,
point3
.
y
,
point4
.
y
),
_max4
(
point1
.
x
,
point2
.
x
,
point3
.
x
,
point4
.
x
),
_max4
(
point1
.
y
,
point2
.
y
,
point3
.
y
,
point4
.
y
)
);
}
}
packages/flutter/lib/src/rendering/README.md
0 → 100644
View file @
06480523
Flutter Rendering Layer
=======================
This document is intended to describe some of the core designs of the
Flutter rendering layer.
Layout
------
Paint
-----
Compositing
-----------
Semantics
---------
The last phase of a frame is the Semantics phase. This only occurs if
a semantics server has been installed, for example if the user is
using an accessibility tool.
Each frame, the semantics phase starts with a call to the static
`RenderObject.flushSemantics()`
method from the
`Renderer`
binding's
`beginFrame()`
method.
Each node marked as needing semantics (which initially is just the
root node, as scheduled by
`scheduleInitialSemantics()`
), in depth
order, has its semantics updated by calling
`_updateSemantics()`
.
The
`_updateSemantics()`
method calls
`_getSemantics()`
to obtain an
`_InterestingSemanticsFragment`
, and then calls
`compile()`
on that
fragment to obtain a
`SemanticsNode`
which becomes the value of the
`RenderObject`
's
`_semantics`
field.
**
This is essentially a two-pass
walk of the render tree. The first pass determines the shape of the
output tree, and the second creates the nodes of this tree and hooks
them together.
**
The second walk is a sparse walk; it only walks the
nodes that are interesting for the purpose of semantics.
`_getSemantics()`
is the core function that walks the render tree to
obtain the semantics. It collects semantic annotators for this
`RenderObject`
, then walks its children collecting
`_SemanticsFragment`
s for them, and then returns an appropriate
`_SemanticsFragment`
object that describes the
`RenderObject`
's
semantics.
Semantic annotators are functions that, given a
`SemanticsNode`
, set
some flags or strings on the object. They are obtained from
`getSemanticAnnotators()`
. For example, here is how
`RenderParagraph`
annotates the
`SemanticsNode`
with its text:
```
dart
Iterable
<
SemanticAnnotator
>
getSemanticAnnotators
()
sync
*
{
yield
(
SemanticsNode
node
)
{
node
.
label
=
text
.
toPlainText
();
};
}
```
A
`_SemanticsFragment`
object is a node in a short-lived tree which is
used to create the final
`SemanticsNode`
tree that is sent to the
semantics server. These objects have a list of semantic annotators,
and a list of
`_SemanticsFragment`
children.
There are several
`_SemanticsFragment`
classes. The
`_getSemantics()`
method picks its return value as follows:
*
`_CleanSemanticsFragment`
is used to represent a
`RenderObject`
that
has a
`SemanticsNode`
and which is in no way dirty. This class has
no children and no annotators, and when compiled, it returns the
`SemanticsNode`
that the
`RenderObject`
already has.
*
`_RootSemanticsFragment`
*
is used to represent the
`RenderObject`
found at the top of the render tree. This class always compiles to a
`SemanticsNode`
with ID 0.
*
`_ConcreteSemanticsFragment`
*
is used to represent a
`RenderObject`
that has
`hasSemantics`
set to true. It returns the
`SemanticsNode`
for that
`RenderObject`
.
*
`_ImplicitSemanticsFragment`
*
is used to represent a
`RenderObject`
that does not have
`hasSemantics`
set to true, but which does have
some semantic annotators. When it is compiled, if the nearest
ancestor
`_SemanticsFragment`
that isn't also an
`_ImplicitSemanticsFragment`
is a
`_RootSemanticsFragment`
or a
`_ConcreteSemanticsFragment`
, then the
`SemanticsNode`
from that
object is reused. Otherwise, a new one is created.
*
`_ForkingSemanticsFragment`
is used to represent a
`RenderObject`
that introduces no semantics of its own, but which has two or more
descendants that do introduce semantics (and which are not ancestors
or descendants of each other).
*
For
`RenderObject`
nodes that introduce no semantics but which have
a (single) child that does, the
`_SemanticsFragment`
of the child is
returned.
*
For
`RenderObject`
nodes that introduce no semantics and have no
descendants that introduce semantics,
`null`
is returned.
The classes marked with an asterisk
*
above are the
`_InterestingSemanticsFragment`
classes.
When the
`_SemanticsFragment`
tree is then compiled, the
`SemanticsNode`
objects are created (if necessary), the semantic
annotators are run on each
`SemanticsNode`
, the geometry (matrix,
size, and clip) is applied, and the children are updated.
As part of this, the code clears out the
`_semantics`
field of any
`RenderObject`
that previously had a
`SemanticsNode`
but no longer
does. This is done as part of the first walk where possible, and as
part of the second otherwise.
packages/flutter/lib/src/rendering/basic_types.dart
View file @
06480523
...
...
@@ -16,3 +16,33 @@ export 'dart:ui' show
VoidCallback
;
typedef
void
ValueChanged
<
T
>(
T
value
);
/// A BitField over an enum (or other class whose values implement "index").
/// Only the first 63 values of the enum can be used as indices.
class
BitField
<
T
extends
dynamic
>
{
static
const
_kSMIBits
=
63
;
// see https://www.dartlang.org/articles/numeric-computation/#smis-and-mints
static
const
_kAllZeros
=
0
;
static
const
_kAllOnes
=
0x7FFFFFFFFFFFFFFF
;
// 2^(_kSMIBits+1)-1
BitField
(
this
.
_length
)
:
_bits
=
_kAllZeros
{
assert
(
_length
<=
_kSMIBits
);
}
BitField
.
filled
(
this
.
_length
,
bool
value
)
:
_bits
=
value
?
_kAllOnes
:
_kAllZeros
{
assert
(
_length
<=
_kSMIBits
);
}
final
int
_length
;
int
_bits
;
bool
operator
[](
T
index
)
{
assert
(
index
.
index
<
_length
);
return
(
_bits
&
1
<<
index
.
index
)
>
0
;
}
void
operator
[]=(
T
index
,
bool
value
)
{
assert
(
index
.
index
<
_length
);
if
(
value
)
_bits
=
_bits
|
(
1
<<
index
.
index
);
else
_bits
=
_bits
&
~(
1
<<
index
.
index
);
}
void
reset
([
bool
value
=
false
])
{
_bits
=
value
?
_kAllOnes
:
_kAllZeros
;
}
}
packages/flutter/lib/src/rendering/binding.dart
View file @
06480523
...
...
@@ -7,11 +7,13 @@ import 'dart:ui' as ui;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/scheduler.dart'
;
import
'package:flutter/services.dart'
;
import
'package:sky_services/semantics/semantics.mojom.dart'
as
mojom
;
import
'box.dart'
;
import
'debug.dart'
;
import
'object.dart'
;
import
'view.dart'
;
import
'semantics.dart'
;
export
'package:flutter/gestures.dart'
show
HitTestResult
;
...
...
@@ -39,6 +41,8 @@ abstract class Renderer extends Scheduler
if
(
renderView
==
null
)
{
renderView
=
new
RenderView
();
renderView
.
scheduleInitialFrame
();
if
(
_semanticsClient
!=
null
)
renderView
.
scheduleInitialSemantics
();
}
handleMetricsChanged
();
// configures renderView's metrics
}
...
...
@@ -61,6 +65,14 @@ abstract class Renderer extends Scheduler
renderView
.
configuration
=
new
ViewConfiguration
(
size:
ui
.
window
.
size
);
}
mojom
.
SemanticsClient
_semanticsClient
;
void
setSemanticsClient
(
mojom
.
SemanticsClient
client
)
{
assert
(
_semanticsClient
==
null
);
_semanticsClient
=
client
;
if
(
renderView
!=
null
)
renderView
.
scheduleInitialSemantics
();
}
void
_handlePersistentFrameCallback
(
Duration
timeStamp
)
{
beginFrame
();
}
...
...
@@ -71,7 +83,11 @@ abstract class Renderer extends Scheduler
RenderObject
.
flushLayout
();
RenderObject
.
flushCompositingBits
();
RenderObject
.
flushPaint
();
renderView
.
compositeFrame
();
renderView
.
compositeFrame
();
// this sends the bits to the GPU
if
(
_semanticsClient
!=
null
)
{
RenderObject
.
flushSemantics
();
SemanticsNode
.
sendSemanticsTreeTo
(
_semanticsClient
);
}
}
void
hitTest
(
HitTestResult
result
,
Point
position
)
{
...
...
@@ -91,6 +107,13 @@ void debugDumpLayerTree() {
debugPrint
(
Renderer
.
instance
?.
renderView
?.
layer
?.
toStringDeep
());
}
/// Prints a textual representation of the entire semantics tree.
/// This will only work if there is a semantics client attached.
/// Otherwise, the tree is empty and this will print "null".
void
debugDumpSemanticsTree
(
)
{
debugPrint
(
Renderer
.
instance
?.
renderView
?.
debugSemantics
?.
toStringDeep
());
}
/// A concrete binding for applications that use the Rendering framework
/// directly. This is the glue that binds the framework to the Flutter engine.
class
RenderingFlutterBinding
extends
BindingBase
with
Scheduler
,
Gesturer
,
Renderer
{
...
...
packages/flutter/lib/src/rendering/block.dart
View file @
06480523
...
...
@@ -341,6 +341,7 @@ class RenderBlockViewport extends RenderBlockBase {
if
(
value
!=
_startOffset
)
{
_startOffset
=
value
;
markNeedsPaint
();
markNeedsSemanticsUpdate
();
}
}
...
...
@@ -430,6 +431,8 @@ class RenderBlockViewport extends RenderBlockBase {
super
.
applyPaintTransform
(
child
,
transform
);
}
Rect
describeApproximatePaintClip
(
RenderObject
child
)
=>
Point
.
origin
&
size
;
bool
hitTestChildren
(
HitTestResult
result
,
{
Point
position
})
{
if
(
isVertical
)
return
defaultHitTestChildren
(
result
,
position:
position
+
new
Offset
(
0.0
,
-
startOffset
));
...
...
packages/flutter/lib/src/rendering/box.dart
View file @
06480523
...
...
@@ -450,6 +450,8 @@ abstract class RenderBox extends RenderObject {
assert
(
debugDoesMeetConstraints
());
}
Rect
get
semanticBounds
=>
Point
.
origin
&
size
;
void
debugResetSize
()
{
// updates the value of size._canBeUsedByParent if necessary
size
=
size
;
...
...
@@ -627,12 +629,6 @@ abstract class RenderBox extends RenderObject {
/// visually "on top" (i.e., paints later).
bool
hitTestChildren
(
HitTestResult
result
,
{
Point
position
})
=>
false
;
static
Point
_transformPoint
(
Matrix4
transform
,
Point
point
)
{
Vector3
position3
=
new
Vector3
(
point
.
x
,
point
.
y
,
0.0
);
Vector3
transformed3
=
transform
.
transform3
(
position3
);
return
new
Point
(
transformed3
.
x
,
transformed3
.
y
);
}
/// Multiply the transform from the parent's coordinate system to this box's
/// coordinate system into the given transform.
///
...
...
@@ -666,7 +662,7 @@ abstract class RenderBox extends RenderObject {
double
det
=
transform
.
invert
();
if
(
det
==
0.0
)
return
Point
.
origin
;
return
_
transformPoint
(
transform
,
point
);
return
MatrixUtils
.
transformPoint
(
transform
,
point
);
}
/// Convert the given point from the local coordinate system for this box to
...
...
@@ -678,7 +674,7 @@ abstract class RenderBox extends RenderObject {
Matrix4
transform
=
new
Matrix4
.
identity
();
for
(
int
index
=
renderers
.
length
-
1
;
index
>
0
;
index
-=
1
)
renderers
[
index
].
applyPaintTransform
(
renderers
[
index
-
1
],
transform
);
return
_
transformPoint
(
transform
,
point
);
return
MatrixUtils
.
transformPoint
(
transform
,
point
);
}
/// Returns a rectangle that contains all the pixels painted by this box.
...
...
packages/flutter/lib/src/rendering/debug.dart
View file @
06480523
...
...
@@ -48,7 +48,7 @@ bool debugPaintLayerBordersEnabled = false;
/// The color to use when painting Layer borders.
ui
.
Color
debugPaintLayerBordersColor
=
const
ui
.
Color
(
0xFFFF9800
);
/// Causes RenderBox objects to flash while they are being tapped
/// Causes RenderBox objects to flash while they are being tapped
.
bool
debugPaintPointersEnabled
=
false
;
/// The color to use when reporting pointers.
...
...
packages/flutter/lib/src/rendering/editable_line.dart
View file @
06480523
...
...
@@ -169,11 +169,14 @@ class RenderEditableLine extends RenderBox {
}
}
bool
get
_hasVisualOverflow
=>
_contentSize
.
width
>
size
.
width
;
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
final
bool
hasVisualOverflow
=
(
_contentSize
.
width
>
size
.
width
);
if
(
hasVisualOverflow
)
if
(
_hasVisualOverflow
)
context
.
pushClipRect
(
needsCompositing
,
offset
,
Point
.
origin
&
size
,
_paintContents
);
else
_paintContents
(
context
,
offset
);
}
Rect
describeApproximatePaintClip
(
RenderObject
child
)
=>
_hasVisualOverflow
?
Point
.
origin
&
size
:
null
;
}
packages/flutter/lib/src/rendering/flex.dart
View file @
06480523
...
...
@@ -587,6 +587,8 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
});
}
Rect
describeApproximatePaintClip
(
RenderObject
child
)
=>
_overflow
>
0.0
?
Point
.
origin
&
size
:
null
;
String
toString
()
{
String
header
=
super
.
toString
();
if
(
_overflow
is
double
&&
_overflow
>
0.0
)
...
...
packages/flutter/lib/src/rendering/object.dart
View file @
06480523
...
...
@@ -6,6 +6,7 @@ import 'dart:developer';
import
'dart:ui'
as
ui
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/painting.dart'
;
import
'package:flutter/scheduler.dart'
;
import
'package:flutter/services.dart'
;
import
'package:vector_math/vector_math_64.dart'
;
...
...
@@ -13,6 +14,8 @@ import 'package:vector_math/vector_math_64.dart';
import
'debug.dart'
;
import
'layer.dart'
;
import
'node.dart'
;
import
'semantics.dart'
;
import
'binding.dart'
;
export
'layer.dart'
;
export
'package:flutter/gestures.dart'
show
HitTestEntry
,
HitTestResult
;
...
...
@@ -361,6 +364,281 @@ typedef void RenderingExceptionHandler(RenderObject source, String method, dynam
/// information will be printed to the console instead.
RenderingExceptionHandler
debugRenderingExceptionHandler
;
class
_SemanticsGeometry
{
_SemanticsGeometry
()
:
transform
=
new
Matrix4
.
identity
();
_SemanticsGeometry
.
withClipFrom
(
_SemanticsGeometry
other
)
{
clipRect
=
other
?.
clipRect
;
transform
=
new
Matrix4
.
identity
();
}
_SemanticsGeometry
.
copy
(
_SemanticsGeometry
other
)
{
if
(
other
!=
null
)
{
clipRect
=
other
.
clipRect
;
transform
=
new
Matrix4
.
copy
(
other
.
transform
);
}
else
{
transform
=
new
Matrix4
.
identity
();
}
}
Rect
clipRect
;
Rect
_intersectClipRect
(
Rect
other
)
{
if
(
clipRect
==
null
)
return
other
;
if
(
other
==
null
)
return
clipRect
;
return
clipRect
.
intersect
(
other
);
}
Matrix4
transform
;
void
applyAncestorChain
(
List
<
RenderObject
>
ancestorChain
)
{
for
(
int
index
=
ancestorChain
.
length
-
1
;
index
>
0
;
index
-=
1
)
{
RenderObject
parent
=
ancestorChain
[
index
];
RenderObject
child
=
ancestorChain
[
index
-
1
];
clipRect
=
_intersectClipRect
(
parent
.
describeApproximatePaintClip
(
child
));
if
(
clipRect
!=
null
)
{
if
(
clipRect
.
isEmpty
)
{
clipRect
=
Rect
.
zero
;
}
else
{
Matrix4
clipTransform
=
new
Matrix4
.
identity
();
parent
.
applyPaintTransform
(
child
,
clipTransform
);
clipRect
=
MatrixUtils
.
transformRect
(
clipRect
,
clipTransform
);
}
}
parent
.
applyPaintTransform
(
child
,
transform
);
}
}
void
updateSemanticsNode
({
RenderObject
rendering
,
SemanticsNode
semantics
,
SemanticsNode
parentSemantics
})
{
assert
(
rendering
!=
null
);
assert
(
semantics
!=
null
);
assert
(
parentSemantics
.
wasAffectedByClip
!=
null
);
semantics
.
transform
=
transform
;
if
(
clipRect
!=
null
)
{
semantics
.
rect
=
clipRect
.
intersect
(
rendering
.
semanticBounds
);
semantics
.
wasAffectedByClip
=
true
;
}
else
{
semantics
.
rect
=
rendering
.
semanticBounds
;
semantics
.
wasAffectedByClip
=
parentSemantics
?.
wasAffectedByClip
??
false
;
}
}
}
abstract
class
_SemanticsFragment
{
_SemanticsFragment
({
RenderObject
owner
,
Iterable
<
SemanticAnnotator
>
annotators
,
List
<
_SemanticsFragment
>
children
})
{
assert
(
owner
!=
null
);
_ancestorChain
=
<
RenderObject
>[
owner
];
if
(
annotators
!=
null
)
addAnnotators
(
annotators
);
assert
(()
{
if
(
children
==
null
)
return
true
;
Set
<
_SemanticsFragment
>
seenChildren
=
new
Set
<
_SemanticsFragment
>();
for
(
_SemanticsFragment
child
in
children
)
assert
(
seenChildren
.
add
(
child
));
// check for duplicate adds
return
true
;
});
_children
=
children
??
const
<
_SemanticsFragment
>[];
}
List
<
RenderObject
>
_ancestorChain
;
void
addAncestor
(
RenderObject
ancestor
)
{
_ancestorChain
.
add
(
ancestor
);
}
RenderObject
get
owner
=>
_ancestorChain
.
first
;
List
<
SemanticAnnotator
>
_annotators
;
void
addAnnotators
(
Iterable
<
SemanticAnnotator
>
moreAnnotators
)
{
if
(
_annotators
==
null
)
_annotators
=
moreAnnotators
is
List
<
SemanticAnnotator
>
?
moreAnnotators
:
moreAnnotators
.
toList
();
else
_annotators
.
addAll
(
moreAnnotators
);
}
List
<
_SemanticsFragment
>
_children
;
bool
_debugCompiled
=
false
;
Iterable
<
SemanticsNode
>
compile
({
_SemanticsGeometry
geometry
,
SemanticsNode
currentSemantics
,
SemanticsNode
parentSemantics
});
String
toString
()
=>
'
$runtimeType
(
$hashCode
)'
;
}
/// Represents a subtree that doesn't need updating, it already has a
/// SemanticsNode and isn't dirty. (We still update the matrix, since
/// that comes from the (dirty) ancestors.)
class
_CleanSemanticsFragment
extends
_SemanticsFragment
{
_CleanSemanticsFragment
({
RenderObject
owner
})
:
super
(
owner:
owner
)
{
assert
(
owner
.
_semantics
!=
null
);
}
Iterable
<
SemanticsNode
>
compile
({
_SemanticsGeometry
geometry
,
SemanticsNode
currentSemantics
,
SemanticsNode
parentSemantics
})
sync
*
{
assert
(!
_debugCompiled
);
assert
(()
{
_debugCompiled
=
true
;
return
true
;
});
SemanticsNode
node
=
owner
.
_semantics
;
assert
(
node
!=
null
);
if
(
geometry
!=
null
)
{
geometry
.
applyAncestorChain
(
_ancestorChain
);
geometry
.
updateSemanticsNode
(
rendering:
owner
,
semantics:
node
,
parentSemantics:
parentSemantics
);
}
else
{
assert
(
_ancestorChain
.
length
==
1
);
}
yield
node
;
}
}
abstract
class
_InterestingSemanticsFragment
extends
_SemanticsFragment
{
_InterestingSemanticsFragment
({
RenderObject
owner
,
Iterable
<
SemanticAnnotator
>
annotators
,
Iterable
<
_SemanticsFragment
>
children
})
:
super
(
owner:
owner
,
annotators:
annotators
,
children:
children
);
bool
get
haveConcreteNode
=>
true
;
Iterable
<
SemanticsNode
>
compile
({
_SemanticsGeometry
geometry
,
SemanticsNode
currentSemantics
,
SemanticsNode
parentSemantics
})
sync
*
{
assert
(!
_debugCompiled
);
assert
(()
{
_debugCompiled
=
true
;
return
true
;
});
SemanticsNode
node
=
establishSemanticsNode
(
geometry
,
currentSemantics
,
parentSemantics
);
for
(
SemanticAnnotator
annotator
in
_annotators
)
annotator
(
node
);
for
(
_SemanticsFragment
child
in
_children
)
{
assert
(
child
.
_ancestorChain
.
last
==
owner
);
node
.
addChildren
(
child
.
compile
(
geometry:
createSemanticsGeometryForChild
(
geometry
),
currentSemantics:
_children
.
length
>
1
?
null
:
node
,
parentSemantics:
node
));
}
if
(
haveConcreteNode
)
{
node
.
finalizeChildren
();
yield
node
;
}
}
SemanticsNode
establishSemanticsNode
(
_SemanticsGeometry
geometry
,
SemanticsNode
currentSemantics
,
SemanticsNode
parentSemantics
);
_SemanticsGeometry
createSemanticsGeometryForChild
(
_SemanticsGeometry
geometry
);
}
class
_RootSemanticsFragment
extends
_InterestingSemanticsFragment
{
_RootSemanticsFragment
({
RenderObject
owner
,
Iterable
<
SemanticAnnotator
>
annotators
,
Iterable
<
_SemanticsFragment
>
children
})
:
super
(
owner:
owner
,
annotators:
annotators
,
children:
children
);
SemanticsNode
establishSemanticsNode
(
_SemanticsGeometry
geometry
,
SemanticsNode
currentSemantics
,
SemanticsNode
parentSemantics
)
{
assert
(
_ancestorChain
.
length
==
1
);
assert
(
geometry
==
null
);
assert
(
currentSemantics
==
null
);
assert
(
parentSemantics
==
null
);
owner
.
_semantics
??=
new
SemanticsNode
.
root
(
handler:
owner
is
SemanticActionHandler
?
owner
as
dynamic
:
null
);
SemanticsNode
node
=
owner
.
_semantics
;
assert
(
MatrixUtils
.
matrixEquals
(
node
.
transform
,
new
Matrix4
.
identity
()));
assert
(!
node
.
wasAffectedByClip
);
node
.
rect
=
owner
.
semanticBounds
;
return
node
;
}
_SemanticsGeometry
createSemanticsGeometryForChild
(
_SemanticsGeometry
geometry
)
{
return
new
_SemanticsGeometry
();
}
}
class
_ConcreteSemanticsFragment
extends
_InterestingSemanticsFragment
{
_ConcreteSemanticsFragment
({
RenderObject
owner
,
Iterable
<
SemanticAnnotator
>
annotators
,
Iterable
<
_SemanticsFragment
>
children
})
:
super
(
owner:
owner
,
annotators:
annotators
,
children:
children
);
SemanticsNode
establishSemanticsNode
(
_SemanticsGeometry
geometry
,
SemanticsNode
currentSemantics
,
SemanticsNode
parentSemantics
)
{
owner
.
_semantics
??=
new
SemanticsNode
(
handler:
owner
is
SemanticActionHandler
?
owner
as
dynamic
:
null
);
SemanticsNode
node
=
owner
.
_semantics
;
if
(
geometry
!=
null
)
{
geometry
.
applyAncestorChain
(
_ancestorChain
);
geometry
.
updateSemanticsNode
(
rendering:
owner
,
semantics:
node
,
parentSemantics:
parentSemantics
);
}
else
{
assert
(
_ancestorChain
.
length
==
1
);
}
return
node
;
}
_SemanticsGeometry
createSemanticsGeometryForChild
(
_SemanticsGeometry
geometry
)
{
return
new
_SemanticsGeometry
.
withClipFrom
(
geometry
);
}
}
class
_ImplicitSemanticsFragment
extends
_InterestingSemanticsFragment
{
_ImplicitSemanticsFragment
({
RenderObject
owner
,
Iterable
<
SemanticAnnotator
>
annotators
,
Iterable
<
_SemanticsFragment
>
children
})
:
super
(
owner:
owner
,
annotators:
annotators
,
children:
children
);
bool
get
haveConcreteNode
=>
_haveConcreteNode
;
bool
_haveConcreteNode
;
SemanticsNode
establishSemanticsNode
(
_SemanticsGeometry
geometry
,
SemanticsNode
currentSemantics
,
SemanticsNode
parentSemantics
)
{
SemanticsNode
node
;
assert
(
_haveConcreteNode
==
null
);
_haveConcreteNode
=
currentSemantics
==
null
&&
_annotators
.
isNotEmpty
;
if
(
haveConcreteNode
)
{
owner
.
_semantics
??=
new
SemanticsNode
(
handler:
owner
is
SemanticActionHandler
?
owner
as
dynamic
:
null
);
node
=
owner
.
_semantics
;
}
else
{
owner
.
_semantics
=
null
;
node
=
currentSemantics
;
}
if
(
geometry
!=
null
)
{
geometry
.
applyAncestorChain
(
_ancestorChain
);
if
(
haveConcreteNode
)
geometry
.
updateSemanticsNode
(
rendering:
owner
,
semantics:
node
,
parentSemantics:
parentSemantics
);
}
else
{
assert
(
_ancestorChain
.
length
==
1
);
}
return
node
;
}
_SemanticsGeometry
createSemanticsGeometryForChild
(
_SemanticsGeometry
geometry
)
{
if
(
haveConcreteNode
)
return
new
_SemanticsGeometry
.
withClipFrom
(
geometry
);
return
new
_SemanticsGeometry
.
copy
(
geometry
);
}
}
class
_ForkingSemanticsFragment
extends
_SemanticsFragment
{
_ForkingSemanticsFragment
({
RenderObject
owner
,
Iterable
<
_SemanticsFragment
>
children
})
:
super
(
owner:
owner
,
children:
children
)
{
assert
(
children
!=
null
);
assert
(
children
.
length
>
1
);
}
Iterable
<
SemanticsNode
>
compile
({
_SemanticsGeometry
geometry
,
SemanticsNode
currentSemantics
,
SemanticsNode
parentSemantics
})
sync
*
{
assert
(!
_debugCompiled
);
assert
(()
{
_debugCompiled
=
true
;
return
true
;
});
assert
(
geometry
!=
null
);
geometry
.
applyAncestorChain
(
_ancestorChain
);
for
(
_SemanticsFragment
child
in
_children
)
{
assert
(
child
.
_ancestorChain
.
last
==
owner
);
yield
*
child
.
compile
(
geometry:
new
_SemanticsGeometry
.
copy
(
geometry
),
currentSemantics:
null
,
parentSemantics:
parentSemantics
);
}
}
}
/// An object in the render tree.
///
/// Render objects have a reference to their parent but do not commit to a model
...
...
@@ -448,16 +726,17 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
debugPrint
(
'This RenderObject had the following owner:
\n
$debugOwner
'
);
int
depth
=
0
;
List
<
String
>
descendants
=
<
String
>[];
const
int
maxDepth
=
5
;
void
visitor
(
RenderObject
child
)
{
descendants
.
add
(
'
${" " * depth}$child
'
);
depth
+=
1
;
if
(
depth
<
5
)
if
(
depth
<
maxDepth
)
child
.
visitChildren
(
visitor
);
depth
-=
1
;
}
visitChildren
(
visitor
);
if
(
descendants
.
length
>
1
)
{
debugPrint
(
'This RenderObject had the following descendants:'
);
debugPrint
(
'This RenderObject had the following descendants
(showing up to depth
$maxDepth
)
:'
);
}
else
if
(
descendants
.
length
==
1
)
{
debugPrint
(
'This RenderObject had the following child:'
);
}
else
{
...
...
@@ -651,6 +930,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
});
try
{
performLayout
();
markNeedsSemanticsUpdate
();
}
catch
(
e
,
stack
)
{
_debugReportException
(
'performLayout'
,
e
,
stack
);
}
...
...
@@ -744,6 +1024,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
});
try
{
performLayout
();
markNeedsSemanticsUpdate
();
assert
(
debugDoesMeetConstraints
());
}
catch
(
e
,
stack
)
{
_debugReportException
(
'performLayout'
,
e
,
stack
);
...
...
@@ -1120,6 +1401,240 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
assert
(
child
.
parent
==
this
);
}
/// Returns a rect in this object's coordinate system that describes
/// the approximate bounding box of the clip rect that would be
/// applied to the given child during the paint phase, if any.
///
/// Returns null if the child would not be clipped.
///
/// This is used in the semantics phase to avoid including children
/// that are not physically visible.
Rect
describeApproximatePaintClip
(
RenderObject
child
)
=>
null
;
// SEMANTICS
static
bool
_semanticsEnabled
=
false
;
static
bool
_debugDoingSemantics
=
false
;
static
List
<
RenderObject
>
_nodesNeedingSemantics
=
<
RenderObject
>[];
/// Bootstrap the semantics reporting mechanism by marking this node
/// as needing a semantics update.
///
/// Requires that this render object is attached, and is the root of
/// the render tree.
///
/// See [Renderer] for an example of how this function is used.
void
scheduleInitialSemantics
()
{
assert
(
attached
);
assert
(
parent
is
!
RenderObject
);
assert
(!
_debugDoingSemantics
);
assert
(
_semantics
==
null
);
assert
(
_needsSemanticsUpdate
);
assert
(
_semanticsEnabled
==
false
);
_semanticsEnabled
=
true
;
_nodesNeedingSemantics
.
add
(
this
);
Scheduler
.
instance
.
ensureVisualUpdate
();
}
static
void
flushSemantics
()
{
Timeline
.
startSync
(
'Semantics'
);
assert
(
_semanticsEnabled
);
assert
(()
{
_debugDoingSemantics
=
true
;
return
true
;
});
try
{
_nodesNeedingSemantics
.
sort
((
RenderObject
a
,
RenderObject
b
)
=>
a
.
depth
-
b
.
depth
);
for
(
RenderObject
node
in
_nodesNeedingSemantics
)
{
if
(
node
.
_needsSemanticsUpdate
)
node
.
_updateSemantics
();
}
}
finally
{
_nodesNeedingSemantics
.
clear
();
assert
(()
{
_debugDoingSemantics
=
false
;
return
true
;
});
Timeline
.
finishSync
();
}
}
/// Whether this RenderObject introduces a new box for accessibility purposes.
bool
get
hasSemantics
=>
false
;
/// The bounding box, in the local coordinate system, of this
/// object, for accessibility purposes.
Rect
get
semanticBounds
;
bool
_needsSemanticsUpdate
=
true
;
bool
_needsSemanticsGeometryUpdate
=
true
;
SemanticsNode
_semantics
;
SemanticsNode
get
debugSemantics
{
// only exposed for testing and debugging
SemanticsNode
result
;
assert
(()
{
result
=
_semantics
;
return
true
;
});
return
result
;
}
/// Mark this node as needing an update to its semantics
/// description.
///
/// If the change did not involve a removal or addition of
/// semantics, only the change of semantics (e.g. isChecked changing
/// from true to false, as opposed to isChecked changing from being
/// true to not being changed at all), then you can pass the
/// onlyChanges argument with the value true to reduce the cost. If
/// semantics are being added or removed, more work needs to be done
/// to update the semantics tree. If you pass 'onlyChanges: true'
/// but this node, which previously had a SemanticsNode, no longer
/// has one, or previously did not set any semantics, but now does,
/// or previously had a child that returned annotators, but no
/// longer does, or other such combinations, then you will either
/// assert during the subsequent call to [flushSemantics()] or you
/// will have out-of-date information in the semantics tree.
///
/// If the geometry might have changed in any way, then again, more
/// work needs to be done to update the semantics tree (to deal with
/// clips). You can pass the noGeometry argument to avoid this work
/// in the case where only the labels or flags changed. If you pass
/// 'noGeometry: true' when the geometry did change, the semantic
/// tree will be out of date.
void
markNeedsSemanticsUpdate
({
bool
onlyChanges:
false
,
bool
noGeometry:
false
})
{
assert
(!
_debugDoingSemantics
);
if
(!
_semanticsEnabled
||
!
attached
||
(
_needsSemanticsUpdate
&&
onlyChanges
&&
(
_needsSemanticsGeometryUpdate
||
noGeometry
)))
return
;
if
(!
noGeometry
&&
(
_semantics
==
null
||
(
_semantics
.
hasChildren
&&
_semantics
.
wasAffectedByClip
)))
{
// Since the geometry might have changed, we need to make sure to reapply any clips.
_needsSemanticsGeometryUpdate
=
true
;
}
if
(
onlyChanges
)
{
// The shape of the tree didn't change, but the details did.
// If we have our own SemanticsNode (our _semantics isn't null)
// then mark ourselves dirty. If we don't then we are using an
// ancestor's; mark all the nodes up to that one dirty.
RenderObject
node
=
this
;
while
(
node
.
_semantics
==
null
&&
node
.
parent
is
RenderObject
)
{
if
(
node
.
_needsSemanticsUpdate
)
return
;
node
.
_needsSemanticsUpdate
=
true
;
node
=
node
.
parent
;
}
if
(!
node
.
_needsSemanticsUpdate
)
{
node
.
_needsSemanticsUpdate
=
true
;
_nodesNeedingSemantics
.
add
(
node
);
}
}
else
{
// The shape of the semantics tree around us may have changed.
// The worst case is that we may have removed a branch of the
// semantics tree, because when that happens we have to go up
// and dirty the nearest _semantics-laden ancestor of the
// affected node to rebuild the tree.
RenderObject
node
=
this
;
do
{
if
(
node
.
parent
is
!
RenderObject
)
break
;
node
.
_needsSemanticsUpdate
=
true
;
node
.
_semantics
?.
reset
();
node
=
node
.
parent
;
}
while
(
node
.
_semantics
==
null
);
node
.
_semantics
?.
reset
();
if
(!
node
.
_needsSemanticsUpdate
)
{
node
.
_needsSemanticsUpdate
=
true
;
_nodesNeedingSemantics
.
add
(
node
);
}
}
}
void
_updateSemantics
()
{
try
{
assert
(
_needsSemanticsUpdate
);
assert
(
_semantics
!=
null
||
parent
is
!
RenderObject
);
_SemanticsFragment
fragment
=
_getSemanticsFragment
();
assert
(
fragment
is
_InterestingSemanticsFragment
);
SemanticsNode
node
=
fragment
.
compile
(
parentSemantics:
_semantics
?.
parent
).
single
;
assert
(
node
!=
null
);
assert
(
node
==
_semantics
);
}
catch
(
e
,
stack
)
{
_debugReportException
(
'_updateSemantics'
,
e
,
stack
);
}
}
_SemanticsFragment
_getSemanticsFragment
()
{
// early-exit if we're not dirty and have our own semantics
if
(!
_needsSemanticsUpdate
&&
hasSemantics
)
{
assert
(
_semantics
!=
null
);
return
new
_CleanSemanticsFragment
(
owner:
this
);
}
List
<
_SemanticsFragment
>
children
;
visitChildrenForSemantics
((
RenderObject
child
)
{
if
(
_needsSemanticsGeometryUpdate
)
{
// If our geometry changed, make sure the child also does a
// full update so that any changes to the clip are fully
// applied.
child
.
_needsSemanticsUpdate
=
true
;
child
.
_needsSemanticsGeometryUpdate
=
true
;
}
_SemanticsFragment
fragment
=
child
.
_getSemanticsFragment
();
if
(
fragment
!=
null
)
{
fragment
.
addAncestor
(
this
);
children
??=
<
_SemanticsFragment
>[];
assert
(!
children
.
contains
(
fragment
));
children
.
add
(
fragment
);
}
});
_needsSemanticsUpdate
=
false
;
_needsSemanticsGeometryUpdate
=
false
;
Iterable
<
SemanticAnnotator
>
annotators
=
getSemanticAnnotators
();
if
(
parent
is
!
RenderObject
)
return
new
_RootSemanticsFragment
(
owner:
this
,
annotators:
annotators
,
children:
children
);
if
(
hasSemantics
)
return
new
_ConcreteSemanticsFragment
(
owner:
this
,
annotators:
annotators
,
children:
children
);
if
(
annotators
.
isNotEmpty
)
return
new
_ImplicitSemanticsFragment
(
owner:
this
,
annotators:
annotators
,
children:
children
);
_semantics
=
null
;
if
(
children
==
null
)
return
null
;
if
(
children
.
length
>
1
)
return
new
_ForkingSemanticsFragment
(
owner:
this
,
children:
children
);
assert
(
children
.
length
==
1
);
return
children
.
single
;
}
/// Called when collecting the semantics of this node. Subclasses
/// that have children that are not semantically relevant (e.g.
/// because they are invisible) should skip those children here.
///
/// The default implementation mirrors the behavior of
/// [visitChildren()] (which is supposed to walk all the children).
void
visitChildrenForSemantics
(
RenderObjectVisitor
visitor
)
{
visitChildren
(
visitor
);
}
/// Returns functions that will annotate a SemanticsNode with the
/// semantics of this RenderObject.
///
/// To annotate a SemanticsNode for this node, return all the
/// annotators provided by the superclass, plus an annotator that
/// adds the annotations. When the behavior of the annotators would
/// change (e.g. the box is now checked rather than unchecked), call
/// [markNeedsSemanticsUpdate()] to indicate to the rendering system
/// that the semantics tree needs to be rebuilt.
///
/// To introduce a new SemanticsNode, set hasSemantics to true for
/// this object. The functions returned by this function will be used
/// to annotate the SemanticsNode for this object.
///
/// Semantic annotations are persistent. Values set in one pass will
/// still be set in the next pass. Therefore it is important to
/// explicitly set fields to false once they are no longer true --
/// setting them to true when they are to be enabled, and not
/// setting them at all when they are not, will mean they remain set
/// once enabled once and will never get unset.
///
/// If the number of annotators you return will change from zero to
/// non-zero, and hasSemantics isn't true, then the associated call
/// to markNeedsSemanticsUpdate() must not have 'onlyChanges' set, as
/// it is possible that the node should be entirely removed.
Iterable
<
SemanticAnnotator
>
getSemanticAnnotators
()
sync
*
{
}
// EVENTS
...
...
packages/flutter/lib/src/rendering/overflow.dart
View file @
06480523
...
...
@@ -236,4 +236,5 @@ class RenderOffStage extends RenderBox with RenderObjectWithChildMixin<RenderBox
bool
hitTest
(
HitTestResult
result
,
{
Point
position
})
=>
false
;
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
}
void
visitChildrenForSemantics
(
RenderObjectVisitor
visitor
)
{
}
}
packages/flutter/lib/src/rendering/paragraph.dart
View file @
06480523
...
...
@@ -6,6 +6,7 @@ import 'package:flutter/painting.dart';
import
'box.dart'
;
import
'object.dart'
;
import
'semantics.dart'
;
export
'package:flutter/painting.dart'
show
FontStyle
,
...
...
@@ -111,7 +112,11 @@ class RenderParagraph extends RenderBox {
_textPainter
.
paint
(
context
.
canvas
,
offset
);
}
// we should probably expose a way to do precise (inter-glpyh) hit testing
Iterable
<
SemanticAnnotator
>
getSemanticAnnotators
()
sync
*
{
yield
(
SemanticsNode
node
)
{
node
.
label
=
text
.
toPlainText
();
};
}
String
debugDescribeChildren
(
String
prefix
)
{
return
'
$prefix
\
u2558
\
u2550
\
u2566
\
u2550
\
u2550 text
\
u2550
\
u2550
\
u2550
\n
'
...
...
packages/flutter/lib/src/rendering/proxy_box.dart
View file @
06480523
...
...
@@ -11,6 +11,7 @@ import 'package:vector_math/vector_math_64.dart';
import
'box.dart'
;
import
'debug.dart'
;
import
'object.dart'
;
import
'semantics.dart'
;
export
'package:flutter/gestures.dart'
show
PointerEvent
,
...
...
@@ -557,6 +558,7 @@ class RenderOpacity extends RenderProxyBox {
_alpha
=
_getAlphaFromOpacity
(
_opacity
);
markNeedsCompositingBitsUpdate
();
markNeedsPaint
();
markNeedsSemanticsUpdate
();
}
int
_alpha
;
...
...
@@ -574,6 +576,11 @@ class RenderOpacity extends RenderProxyBox {
}
}
void
visitChildrenForSemantics
(
RenderObjectVisitor
visitor
)
{
if
(
child
!=
null
&&
_alpha
!=
0
)
visitor
(
child
);
}
void
debugDescribeSettings
(
List
<
String
>
settings
)
{
super
.
debugDescribeSettings
(
settings
);
settings
.
add
(
'opacity:
${opacity.toStringAsFixed(1)}
'
);
...
...
@@ -622,6 +629,17 @@ abstract class CustomClipper<T> {
/// Returns a description of the clip given that the render object being
/// clipped is of the given size.
T
getClip
(
Size
size
);
/// Returns an approximation of the clip returned by [getClip], as
/// an axis-aligned Rect. This is used by the semantics layer to
/// determine whether widgets should be excluded.
///
/// By default, this returns a rectangle that is the same size as
/// the RenderObject. If getClip returns a shape that is roughly the
/// same size as the RenderObject (e.g. it's a rounded rectangle
/// with very small arcs in the corners), then this may be adequate.
Rect
getApproximateClipRect
(
Size
size
)
=>
Point
.
origin
&
size
;
/// Returns true if the new instance will result in a different clip
/// than the oldClipper instance.
bool
shouldRepaint
(
CustomClipper
oldClipper
);
}
...
...
@@ -642,15 +660,19 @@ abstract class _RenderCustomClip<T> extends RenderProxyBox {
if
(
newClipper
==
null
)
{
assert
(
oldClipper
!=
null
);
markNeedsPaint
();
markNeedsSemanticsUpdate
(
onlyChanges:
true
);
}
else
if
(
oldClipper
==
null
||
oldClipper
.
runtimeType
!=
oldClipper
.
runtimeType
||
newClipper
.
shouldRepaint
(
oldClipper
))
{
markNeedsPaint
();
markNeedsSemanticsUpdate
(
onlyChanges:
true
);
}
}
T
get
_defaultClip
;
T
get
_clip
=>
_clipper
?.
getClip
(
size
)
??
_defaultClip
;
Rect
describeApproximatePaintClip
(
RenderObject
child
)
=>
_clipper
?.
getApproximateClipRect
(
size
)
??
Point
.
origin
&
size
;
}
/// Clips its child using a rectangle.
...
...
@@ -722,6 +744,9 @@ class RenderClipRRect extends RenderProxyBox {
markNeedsPaint
();
}
// TODO(ianh): either convert this to the CustomClipper world, or
// TODO(ianh): implement describeApproximatePaintClip for this class
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
if
(
child
!=
null
)
{
Rect
rect
=
Point
.
origin
&
size
;
...
...
@@ -1314,18 +1339,59 @@ class RenderRepaintBoundary extends RenderProxyBox {
/// to hit testing. It still consumes space during layout and paints its child
/// as usual. It just cannot be the target of located events because it returns
/// false from [hitTest].
///
/// When [ignoringSemantics] is true, the subtree will be invisible to
/// the semantics layer (and thus e.g. accessibility tools). If
/// [ignoringSemantics] is null, it uses the value of [ignoring].
class
RenderIgnorePointer
extends
RenderProxyBox
{
RenderIgnorePointer
({
RenderBox
child
,
this
.
ignoring
:
true
})
:
super
(
child
);
RenderIgnorePointer
({
RenderBox
child
,
bool
ignoring:
true
,
bool
ignoringSemantics
})
:
_ignoring
=
ignoring
,
_ignoringSemantics
=
ignoringSemantics
,
super
(
child
)
{
assert
(
_ignoring
!=
null
);
}
bool
ignoring
;
bool
get
ignoring
=>
_ignoring
;
bool
_ignoring
;
void
set
ignoring
(
bool
value
)
{
assert
(
value
!=
null
);
if
(
value
==
_ignoring
)
return
;
_ignoring
=
value
;
if
(
ignoringSemantics
==
null
)
markNeedsSemanticsUpdate
();
}
bool
get
ignoringSemantics
=>
_ignoringSemantics
;
bool
_ignoringSemantics
;
void
set
ignoringSemantics
(
bool
value
)
{
if
(
value
==
_ignoringSemantics
)
return
;
bool
oldEffectiveValue
=
_effectiveIgnoringSemantics
;
_ignoringSemantics
=
value
;
if
(
oldEffectiveValue
!=
_effectiveIgnoringSemantics
)
markNeedsSemanticsUpdate
();
}
bool
get
_effectiveIgnoringSemantics
=>
ignoringSemantics
==
null
?
ignoring
:
ignoringSemantics
;
bool
hitTest
(
HitTestResult
result
,
{
Point
position
})
{
return
ignoring
?
false
:
super
.
hitTest
(
result
,
position:
position
);
}
// TODO(ianh): figure out a way to still include labels and flags in
// descendants, just make them non-interactive, even when
// _effectiveIgnoringSemantics is true
void
visitChildrenForSemantics
(
RenderObjectVisitor
visitor
)
{
if
(
child
!=
null
&&
!
_effectiveIgnoringSemantics
)
visitor
(
child
);
}
void
debugDescribeSettings
(
List
<
String
>
settings
)
{
super
.
debugDescribeSettings
(
settings
);
settings
.
add
(
'ignoring:
$ignoring
'
);
settings
.
add
(
'ignoringSemantics:
${ ignoringSemantics == null ? "implicitly " : "" }$_effectiveIgnoringSemantics
'
);
}
}
...
...
@@ -1336,3 +1402,220 @@ class RenderMetaData extends RenderProxyBox {
/// Opaque meta data ignored by the render tree
dynamic
metaData
;
}
/// Listens for the specified gestures from the semantics server (e.g.
/// an accessibility tool).
class
RenderSemanticsGestureHandler
extends
RenderProxyBox
implements
SemanticActionHandler
{
RenderSemanticsGestureHandler
({
RenderBox
child
,
GestureTapCallback
onTap
,
GestureLongPressCallback
onLongPress
,
GestureDragUpdateCallback
onHorizontalDragUpdate
,
GestureDragUpdateCallback
onVerticalDragUpdate
,
this
.
scrollFactor
:
0.8
})
:
_onTap
=
onTap
,
_onLongPress
=
onLongPress
,
_onHorizontalDragUpdate
=
onHorizontalDragUpdate
,
_onVerticalDragUpdate
=
onVerticalDragUpdate
,
super
(
child
);
GestureTapCallback
get
onTap
=>
_onTap
;
GestureTapCallback
_onTap
;
void
set
onTap
(
GestureTapCallback
value
)
{
if
(
_onTap
==
value
)
return
;
bool
didHaveSemantics
=
hasSemantics
;
bool
hadHandler
=
_onTap
!=
null
;
_onTap
=
value
;
if
((
value
!=
null
)
!=
hadHandler
)
markNeedsSemanticsUpdate
(
onlyChanges:
hasSemantics
==
didHaveSemantics
);
}
GestureLongPressCallback
get
onLongPress
=>
_onLongPress
;
GestureLongPressCallback
_onLongPress
;
void
set
onLongPress
(
GestureLongPressCallback
value
)
{
if
(
_onLongPress
==
value
)
return
;
bool
didHaveSemantics
=
hasSemantics
;
bool
hadHandler
=
_onLongPress
!=
null
;
_onLongPress
=
value
;
if
((
value
!=
null
)
!=
hadHandler
)
markNeedsSemanticsUpdate
(
onlyChanges:
hasSemantics
==
didHaveSemantics
);
}
GestureDragUpdateCallback
get
onHorizontalDragUpdate
=>
_onHorizontalDragUpdate
;
GestureDragUpdateCallback
_onHorizontalDragUpdate
;
void
set
onHorizontalDragUpdate
(
GestureDragUpdateCallback
value
)
{
if
(
_onHorizontalDragUpdate
==
value
)
return
;
bool
didHaveSemantics
=
hasSemantics
;
bool
hadHandler
=
_onHorizontalDragUpdate
!=
null
;
_onHorizontalDragUpdate
=
value
;
if
((
value
!=
null
)
!=
hadHandler
)
markNeedsSemanticsUpdate
(
onlyChanges:
hasSemantics
==
didHaveSemantics
);
}
GestureDragUpdateCallback
get
onVerticalDragUpdate
=>
_onVerticalDragUpdate
;
GestureDragUpdateCallback
_onVerticalDragUpdate
;
void
set
onVerticalDragUpdate
(
GestureDragUpdateCallback
value
)
{
if
(
_onVerticalDragUpdate
==
value
)
return
;
bool
didHaveSemantics
=
hasSemantics
;
bool
hadHandler
=
_onVerticalDragUpdate
!=
null
;
_onVerticalDragUpdate
=
value
;
if
((
value
!=
null
)
!=
hadHandler
)
markNeedsSemanticsUpdate
(
onlyChanges:
hasSemantics
==
didHaveSemantics
);
}
/// The fraction of the dimension of this render box to use when
/// scrolling. For example, if this is 0.8 and the box is 200 pixels
/// wide, then when a left-scroll action is received from the
/// accessibility system, it will translate into a 160 pixel
/// leftwards drag.
double
scrollFactor
;
bool
get
hasSemantics
{
return
onTap
!=
null
||
onLongPress
!=
null
||
onHorizontalDragUpdate
!=
null
||
onVerticalDragUpdate
!=
null
;
}
Iterable
<
SemanticAnnotator
>
getSemanticAnnotators
()
sync
*
{
if
(
hasSemantics
)
{
yield
(
SemanticsNode
semantics
)
{
semantics
.
canBeTapped
=
onTap
!=
null
;
semantics
.
canBeLongPressed
=
onLongPress
!=
null
;
semantics
.
canBeScrolledHorizontally
=
onHorizontalDragUpdate
!=
null
;
semantics
.
canBeScrolledVertically
=
onVerticalDragUpdate
!=
null
;
};
}
}
void
handleSemanticTap
()
{
if
(
onTap
!=
null
)
onTap
();
}
void
handleSemanticLongPress
()
{
if
(
onLongPress
!=
null
)
onLongPress
();
}
void
handleSemanticScrollLeft
()
{
if
(
onHorizontalDragUpdate
!=
null
)
onHorizontalDragUpdate
(
size
.
width
*
-
scrollFactor
);
}
void
handleSemanticScrollRight
()
{
if
(
onHorizontalDragUpdate
!=
null
)
onHorizontalDragUpdate
(
size
.
width
*
scrollFactor
);
}
void
handleSemanticScrollUp
()
{
if
(
onVerticalDragUpdate
!=
null
)
onVerticalDragUpdate
(
size
.
height
*
-
scrollFactor
);
}
void
handleSemanticScrollDown
()
{
if
(
onVerticalDragUpdate
!=
null
)
onVerticalDragUpdate
(
size
.
height
*
scrollFactor
);
}
}
/// Add annotations to the SemanticsNode for this subtree.
class
RenderSemanticAnnotations
extends
RenderProxyBox
{
RenderSemanticAnnotations
({
RenderBox
child
,
bool
container:
false
,
bool
checked
,
String
label
})
:
_container
=
container
,
_checked
=
checked
,
_label
=
label
,
super
(
child
)
{
assert
(
container
!=
null
);
}
/// If 'container' is true, this RenderObject will introduce a new
/// node in the semantics tree. Otherwise, the semantics will be
/// merged with the semantics of any ancestors.
///
/// The 'container' flag is implicitly set to true on the immediate
/// semantics-providing descendants of a node where multiple
/// children have semantics or have descendants providing semantics.
/// In other words, the semantics of siblings are not merged. To
/// merge the semantics of an entire subtree, including siblings,
/// you can use a [RenderMergeSemantics].
bool
get
container
=>
_container
;
bool
_container
;
void
set
container
(
bool
value
)
{
assert
(
value
!=
null
);
if
(
container
==
value
)
return
;
_container
=
value
;
markNeedsSemanticsUpdate
();
}
/// If non-null, sets the "hasCheckedState" semantic to true and the
/// "isChecked" semantic to the given value.
bool
get
checked
=>
_checked
;
bool
_checked
;
void
set
checked
(
bool
value
)
{
if
(
checked
==
value
)
return
;
bool
hadValue
=
checked
!=
null
;
_checked
=
value
;
markNeedsSemanticsUpdate
(
onlyChanges:
(
value
!=
null
)
==
hadValue
);
}
/// If non-null, sets the "label" semantic to the given value.
String
get
label
=>
_label
;
String
_label
;
void
set
label
(
String
value
)
{
if
(
label
==
value
)
return
;
bool
hadValue
=
label
!=
null
;
_label
=
value
;
markNeedsSemanticsUpdate
(
onlyChanges:
(
value
!=
null
)
==
hadValue
);
}
bool
get
hasSemantics
=>
container
;
Iterable
<
SemanticAnnotator
>
getSemanticAnnotators
()
sync
*
{
if
(
checked
!=
null
)
{
yield
(
SemanticsNode
semantics
)
{
semantics
.
hasCheckedState
=
true
;
semantics
.
isChecked
=
checked
;
};
}
if
(
label
!=
null
)
{
yield
(
SemanticsNode
semantics
)
{
semantics
.
label
=
label
;
};
}
}
}
/// Causes the semantics of all descendants to be merged into this
/// node such that the entire subtree becomes a single leaf in the
/// semantics tree.
///
/// Useful for combining the semantics of multiple render objects that
/// form part of a single conceptual widget, e.g. a checkbox, a label,
/// and the gesture detector that goes with them.
class
RenderMergeSemantics
extends
RenderProxyBox
{
RenderMergeSemantics
({
RenderBox
child
})
:
super
(
child
);
Iterable
<
SemanticAnnotator
>
getSemanticAnnotators
()
sync
*
{
yield
(
SemanticsNode
node
)
{
node
.
mergeAllDescendantsIntoThisNode
=
true
;
};
}
}
/// Excludes this subtree from the semantic tree.
///
/// Useful e.g. for hiding text that is redundant with other text next
/// to it (e.g. text included only for the visual effect).
class
RenderExcludeSemantics
extends
RenderProxyBox
{
RenderExcludeSemantics
({
RenderBox
child
})
:
super
(
child
);
void
visitChildrenForSemantics
(
RenderObjectVisitor
visitor
)
{
}
}
packages/flutter/lib/src/rendering/semantics.dart
0 → 100644
View file @
06480523
// Copyright 2015 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
'dart:math'
as
math
;
import
'dart:ui'
show
Rect
;
import
'package:flutter/painting.dart'
;
import
'package:sky_services/semantics/semantics.mojom.dart'
as
mojom
;
import
'package:vector_math/vector_math_64.dart'
;
import
'basic_types.dart'
;
import
'node.dart'
;
/// The type of function returned by [RenderObject.getSemanticAnnotators()].
///
/// These callbacks are invoked with the [SemanticsNode] object that
/// corresponds to the [RenderObject]. (One [SemanticsNode] can
/// correspond to multiple [RenderObject] objects.)
///
/// See [RenderObject.getSemanticAnnotators()] for details on the
/// contract that semantic annotators must follow.
typedef
void
SemanticAnnotator
(
SemanticsNode
semantics
);
/// Interface for RenderObjects to implement when they want to support
/// being tapped, etc.
///
/// These handlers will only be called if the relevant flag is set
/// (e.g. handleSemanticTap() will only be called if canBeTapped is
/// true, handleSemanticScrollDown() will only be called if
/// canBeScrolledVertically is true, etc).
abstract
class
SemanticActionHandler
{
void
handleSemanticTap
()
{
}
void
handleSemanticLongPress
()
{
}
void
handleSemanticScrollLeft
()
{
}
void
handleSemanticScrollRight
()
{
}
void
handleSemanticScrollUp
()
{
}
void
handleSemanticScrollDown
()
{
}
}
enum
_SemanticFlags
{
mergeAllDescendantsIntoThisNode
,
canBeTapped
,
canBeLongPressed
,
canBeScrolledHorizontally
,
canBeScrolledVertically
,
hasCheckedState
,
isChecked
,
}
typedef
bool
SemanticsNodeVisitor
(
SemanticsNode
node
);
class
SemanticsNode
extends
AbstractNode
{
SemanticsNode
({
SemanticActionHandler
handler
})
:
_id
=
_generateNewId
(),
_actionHandler
=
handler
;
SemanticsNode
.
root
({
SemanticActionHandler
handler
})
:
_id
=
0
,
_actionHandler
=
handler
{
attach
();
}
static
int
_lastIdentifier
=
0
;
static
int
_generateNewId
()
{
_lastIdentifier
+=
1
;
return
_lastIdentifier
;
}
final
int
_id
;
final
SemanticActionHandler
_actionHandler
;
// GEOMETRY
// These are automatically handled by RenderObject's own logic
Matrix4
get
transform
=>
_transform
;
Matrix4
_transform
;
// defaults to null, which we say means the identity matrix
void
set
transform
(
Matrix4
value
)
{
if
(!
MatrixUtils
.
matrixEquals
(
_transform
,
value
))
{
_transform
=
value
;
_markDirty
();
}
}
Rect
get
rect
=>
_rect
;
Rect
_rect
=
Rect
.
zero
;
void
set
rect
(
Rect
value
)
{
assert
(
value
!=
null
);
if
(
_rect
!=
value
)
{
_rect
=
value
;
_markDirty
();
}
}
bool
wasAffectedByClip
=
false
;
// FLAGS AND LABELS
// These are supposed to be set by SemanticAnnotator obtained from getSemanticAnnotators
BitField
<
_SemanticFlags
>
_flags
=
new
BitField
<
_SemanticFlags
>.
filled
(
_SemanticFlags
.
values
.
length
,
false
);
void
_setFlag
(
_SemanticFlags
flag
,
bool
value
,
{
bool
needsHandler:
false
})
{
assert
(
value
!=
null
);
assert
((!
needsHandler
)
||
(
_actionHandler
!=
null
)
||
(
value
==
false
));
if
(
_flags
[
flag
]
!=
value
)
{
_flags
[
flag
]
=
value
;
_markDirty
();
}
}
bool
_canHandle
(
_SemanticFlags
flag
)
{
return
_actionHandler
!=
null
&&
_flags
[
flag
];
}
bool
get
mergeAllDescendantsIntoThisNode
=>
_flags
[
_SemanticFlags
.
mergeAllDescendantsIntoThisNode
];
void
set
mergeAllDescendantsIntoThisNode
(
bool
value
)
=>
_setFlag
(
_SemanticFlags
.
mergeAllDescendantsIntoThisNode
,
value
);
bool
get
canBeTapped
=>
_flags
[
_SemanticFlags
.
canBeTapped
];
void
set
canBeTapped
(
bool
value
)
=>
_setFlag
(
_SemanticFlags
.
canBeTapped
,
value
,
needsHandler:
true
);
bool
get
canBeLongPressed
=>
_flags
[
_SemanticFlags
.
canBeLongPressed
];
void
set
canBeLongPressed
(
bool
value
)
=>
_setFlag
(
_SemanticFlags
.
canBeLongPressed
,
value
,
needsHandler:
true
);
bool
get
canBeScrolledHorizontally
=>
_flags
[
_SemanticFlags
.
canBeScrolledHorizontally
];
void
set
canBeScrolledHorizontally
(
bool
value
)
=>
_setFlag
(
_SemanticFlags
.
canBeScrolledHorizontally
,
value
,
needsHandler:
true
);
bool
get
canBeScrolledVertically
=>
_flags
[
_SemanticFlags
.
canBeScrolledVertically
];
void
set
canBeScrolledVertically
(
bool
value
)
=>
_setFlag
(
_SemanticFlags
.
canBeScrolledVertically
,
value
,
needsHandler:
true
);
bool
get
hasCheckedState
=>
_flags
[
_SemanticFlags
.
hasCheckedState
];
void
set
hasCheckedState
(
bool
value
)
=>
_setFlag
(
_SemanticFlags
.
hasCheckedState
,
value
);
bool
get
isChecked
=>
_flags
[
_SemanticFlags
.
isChecked
];
void
set
isChecked
(
bool
value
)
=>
_setFlag
(
_SemanticFlags
.
isChecked
,
value
);
String
get
label
=>
_label
;
String
_label
=
''
;
void
set
label
(
String
value
)
{
assert
(
value
!=
null
);
if
(
_label
!=
value
)
{
_label
=
value
;
_markDirty
();
}
}
void
reset
()
{
_flags
.
reset
();
_label
=
''
;
_markDirty
();
}
List
<
SemanticsNode
>
_newChildren
;
void
addChildren
(
Iterable
<
SemanticsNode
>
children
)
{
_newChildren
??=
<
SemanticsNode
>[];
_newChildren
.
addAll
(
children
);
// we do the asserts afterwards because children is an Iterable
// and doing the asserts before would mean the behaviour is
// different in checked mode vs release mode (if you walk an
// iterator after having reached the end, it'll just start over;
// the values are not cached).
assert
(!
_newChildren
.
any
((
SemanticsNode
child
)
=>
child
==
this
));
assert
(()
{
SemanticsNode
ancestor
=
this
;
while
(
ancestor
.
parent
is
SemanticsNode
)
ancestor
=
ancestor
.
parent
;
assert
(!
_newChildren
.
any
((
SemanticsNode
child
)
=>
child
==
ancestor
));
return
true
;
});
assert
(()
{
Set
<
SemanticsNode
>
seenChildren
=
new
Set
<
SemanticsNode
>();
for
(
SemanticsNode
child
in
_newChildren
)
assert
(
seenChildren
.
add
(
child
));
// check for duplicate adds
return
true
;
});
}
List
<
SemanticsNode
>
_children
;
bool
get
hasChildren
=>
_children
?.
isNotEmpty
??
false
;
bool
_dead
=
false
;
void
finalizeChildren
()
{
if
(
_children
!=
null
)
{
for
(
SemanticsNode
child
in
_children
)
child
.
_dead
=
true
;
}
if
(
_newChildren
!=
null
)
{
for
(
SemanticsNode
child
in
_newChildren
)
child
.
_dead
=
false
;
}
bool
sawChange
=
false
;
if
(
_children
!=
null
)
{
for
(
SemanticsNode
child
in
_children
)
{
if
(
child
.
_dead
)
{
if
(
child
.
parent
==
this
)
{
// we might have already had our child stolen from us by
// another node that is deeper in the tree.
dropChild
(
child
);
}
sawChange
=
true
;
}
}
}
if
(
_newChildren
!=
null
)
{
for
(
SemanticsNode
child
in
_newChildren
)
{
if
(
child
.
parent
!=
this
)
{
if
(
child
.
parent
!=
null
)
{
// we're rebuilding the tree from the bottom up, so it's possible
// that our child was, in the last pass, a child of one of our
// ancestors. In that case, we drop the child eagerly here.
// TODO(ianh): Find a way to assert that the same node didn't
// actually appear in the tree in two places.
child
.
parent
?.
dropChild
(
child
);
}
assert
(!
child
.
attached
);
adoptChild
(
child
);
sawChange
=
true
;
}
}
}
List
<
SemanticsNode
>
oldChildren
=
_children
;
_children
=
_newChildren
;
oldChildren
?.
clear
();
_newChildren
=
oldChildren
;
if
(
sawChange
)
_markDirty
();
}
SemanticsNode
get
parent
=>
super
.
parent
;
void
redepthChildren
()
{
if
(
_children
!=
null
)
{
for
(
SemanticsNode
child
in
_children
)
redepthChild
(
child
);
}
}
// Visits all the descendants of this node, calling visitor for each one, until
// visitor returns false. Returns true if all the visitor calls returned true,
// otherwise returns false.
bool
_visitDescendants
(
SemanticsNodeVisitor
visitor
)
{
if
(
_children
!=
null
)
{
for
(
SemanticsNode
child
in
_children
)
{
if
(!
visitor
(
child
)
||
!
child
.
_visitDescendants
(
visitor
))
return
false
;
}
}
return
true
;
}
static
Map
<
int
,
SemanticsNode
>
_nodes
=
<
int
,
SemanticsNode
>{};
static
Set
<
SemanticsNode
>
_detachedNodes
=
new
Set
<
SemanticsNode
>();
void
attach
()
{
super
.
attach
();
assert
(!
_nodes
.
containsKey
(
_id
));
_nodes
[
_id
]
=
this
;
_detachedNodes
.
remove
(
this
);
if
(
_children
!=
null
)
{
for
(
SemanticsNode
child
in
_children
)
child
.
attach
();
}
}
void
detach
()
{
super
.
detach
();
assert
(
_nodes
.
containsKey
(
_id
));
assert
(!
_detachedNodes
.
contains
(
this
));
_nodes
.
remove
(
_id
);
_detachedNodes
.
add
(
this
);
if
(
_children
!=
null
)
{
for
(
SemanticsNode
child
in
_children
)
child
.
detach
();
}
}
static
List
<
SemanticsNode
>
_dirtyNodes
=
<
SemanticsNode
>[];
bool
_dirty
=
false
;
void
_markDirty
()
{
if
(
_dirty
)
return
;
_dirty
=
true
;
assert
(!
_dirtyNodes
.
contains
(
this
));
assert
(!
_detachedNodes
.
contains
(
this
));
_dirtyNodes
.
add
(
this
);
}
mojom
.
SemanticsNode
_serialize
()
{
mojom
.
SemanticsNode
result
=
new
mojom
.
SemanticsNode
();
result
.
id
=
_id
;
if
(
_dirty
)
{
// We could be even more efficient about not sending data here, by only
// sending the bits that are dirty (tracking the geometry, flags, strings,
// and children separately). For now, we send all or nothing.
result
.
geometry
=
new
mojom
.
SemanticGeometry
();
result
.
geometry
.
transform
=
transform
?.
storage
;
result
.
geometry
.
top
=
rect
.
top
;
result
.
geometry
.
left
=
rect
.
left
;
result
.
geometry
.
width
=
math
.
max
(
rect
.
width
,
0.0
);
result
.
geometry
.
height
=
math
.
max
(
rect
.
height
,
0.0
);
result
.
flags
=
new
mojom
.
SemanticFlags
();
result
.
flags
.
canBeTapped
=
canBeTapped
;
result
.
flags
.
canBeLongPressed
=
canBeLongPressed
;
result
.
flags
.
canBeScrolledHorizontally
=
canBeScrolledHorizontally
;
result
.
flags
.
canBeScrolledVertically
=
canBeScrolledVertically
;
result
.
flags
.
hasCheckedState
=
hasCheckedState
;
result
.
flags
.
isChecked
=
isChecked
;
result
.
strings
=
new
mojom
.
SemanticStrings
();
result
.
strings
.
label
=
label
;
List
<
mojom
.
SemanticsNode
>
children
=
<
mojom
.
SemanticsNode
>[];
if
(
mergeAllDescendantsIntoThisNode
)
{
_visitDescendants
((
SemanticsNode
node
)
{
result
.
flags
.
canBeTapped
=
result
.
flags
.
canBeTapped
||
node
.
canBeTapped
;
result
.
flags
.
canBeLongPressed
=
result
.
flags
.
canBeLongPressed
||
node
.
canBeLongPressed
;
result
.
flags
.
canBeScrolledHorizontally
=
result
.
flags
.
canBeScrolledHorizontally
||
node
.
canBeScrolledHorizontally
;
result
.
flags
.
canBeScrolledVertically
=
result
.
flags
.
canBeScrolledVertically
||
node
.
canBeScrolledVertically
;
result
.
flags
.
hasCheckedState
=
result
.
flags
.
hasCheckedState
||
node
.
hasCheckedState
;
result
.
flags
.
isChecked
=
result
.
flags
.
isChecked
||
node
.
isChecked
;
if
(
node
.
label
!=
''
)
result
.
strings
.
label
=
result
.
strings
.
label
.
isNotEmpty
?
'
${result.strings.label}
\n
${node.label}
'
:
node
.
label
;
return
true
;
// continue walk
});
// and we pretend to have no children
}
else
{
if
(
_children
!=
null
)
{
for
(
SemanticsNode
child
in
_children
)
children
.
add
(
child
.
_serialize
());
}
}
result
.
children
=
children
;
_dirty
=
false
;
}
return
result
;
}
static
void
sendSemanticsTreeTo
(
mojom
.
SemanticsClient
client
)
{
for
(
SemanticsNode
oldNode
in
_detachedNodes
)
{
// The other side will have forgotten this node if we even send
// it again, so make sure to mark it dirty so that it'll get
// sent if it is resurrected.
oldNode
.
_dirty
=
true
;
}
_detachedNodes
.
clear
();
if
(
_dirtyNodes
.
isEmpty
)
return
;
_dirtyNodes
.
sort
((
SemanticsNode
a
,
SemanticsNode
b
)
=>
a
.
depth
-
b
.
depth
);
for
(
int
index
=
0
;
index
<
_dirtyNodes
.
length
;
index
+=
1
)
{
// we mutate the list as we walk it here, which is why we use an index instead of an iterator
SemanticsNode
node
=
_dirtyNodes
[
index
];
assert
(
node
.
_dirty
);
assert
(
node
.
parent
==
null
||
!
node
.
parent
.
mergeAllDescendantsIntoThisNode
||
node
.
mergeAllDescendantsIntoThisNode
);
if
(
node
.
mergeAllDescendantsIntoThisNode
)
{
// if we're merged into our parent, make sure our parent is added to the list
if
(
node
.
parent
!=
null
&&
node
.
parent
.
mergeAllDescendantsIntoThisNode
)
node
.
parent
.
_markDirty
();
// this can add the node to the dirty list
// make sure all the descendants are also marked, so that if one gets marked dirty later we know to walk up then too
if
(
node
.
_children
!=
null
)
for
(
SemanticsNode
child
in
node
.
_children
)
child
.
mergeAllDescendantsIntoThisNode
=
true
;
// this can add the node to the dirty list
}
assert
(
_dirtyNodes
[
index
]
==
node
);
// make sure nothing went in front of us in the list
}
_dirtyNodes
.
sort
((
SemanticsNode
a
,
SemanticsNode
b
)
=>
a
.
depth
-
b
.
depth
);
List
<
mojom
.
SemanticsNode
>
updatedNodes
=
<
mojom
.
SemanticsNode
>[];
for
(
SemanticsNode
node
in
_dirtyNodes
)
{
assert
(
node
.
parent
?.
_dirty
!=
true
);
// could be null (no parent) or false (not dirty)
// The _serialize() method marks the node as not dirty, and
// recurses through the tree to do a deep serialization of all
// contiguous dirty nodes. This means that when we return here,
// it's quite possible that subsequent nodes are no longer
// dirty. We skip these here.
// We also skip any nodes that were reset and subsequently
// dropped entirely (RenderObject.markNeedsSemanticsUpdate()
// calls reset() on its SemanticsNode if onlyChanges isn't set,
// which happens e.g. when the node is no longer contributing
// semantics).
if
(
node
.
_dirty
&&
node
.
attached
)
updatedNodes
.
add
(
node
.
_serialize
());
}
client
.
updateSemanticsTree
(
updatedNodes
);
_dirtyNodes
.
clear
();
}
static
SemanticActionHandler
getSemanticActionHandlerForId
(
int
id
,
{
_SemanticFlags
neededFlag
})
{
assert
(
neededFlag
!=
null
);
SemanticsNode
result
=
_nodes
[
id
];
if
(
result
!=
null
&&
result
.
mergeAllDescendantsIntoThisNode
&&
!
result
.
_canHandle
(
neededFlag
))
{
result
.
_visitDescendants
((
SemanticsNode
node
)
{
if
(
node
.
_actionHandler
!=
null
&&
node
.
_flags
[
neededFlag
])
{
result
=
node
;
return
false
;
// found node, abort walk
}
return
true
;
// continue walk
});
}
if
(
result
==
null
||
!
result
.
_canHandle
(
neededFlag
))
return
null
;
return
result
.
_actionHandler
;
}
String
toString
()
{
return
'
$runtimeType
(
$_id
'
'
${_dirty ? " (dirty)" : ""}
'
'
${mergeAllDescendantsIntoThisNode ? " (leaf merge)" : ""}
'
';
$rect
'
'
${wasAffectedByClip ? " (clipped)" : ""}
'
'
${canBeTapped ? "; canBeTapped" : ""}
'
'
${canBeLongPressed ? "; canBeLongPressed" : ""}
'
'
${canBeScrolledHorizontally ? "; canBeScrolledHorizontally" : ""}
'
'
${canBeScrolledVertically ? "; canBeScrolledVertically" : ""}
'
'
${hasCheckedState ? (isChecked ? "; checked" : "; unchecked") : ""}
'
'
${label != "" ? "; \"$label\"" : ""}
'
')'
;
}
String
toStringDeep
([
String
prefixLineOne
=
''
,
String
prefixOtherLines
=
''
])
{
String
result
=
'
$prefixLineOne$this
\n
'
;
if
(
_children
!=
null
&&
_children
.
isNotEmpty
)
{
for
(
int
index
=
0
;
index
<
_children
.
length
-
1
;
index
+=
1
)
{
SemanticsNode
child
=
_children
[
index
];
result
+=
'
${child.toStringDeep("$prefixOtherLines \u251C", "$prefixOtherLines \u2502")}
'
;
}
result
+=
'
${_children.last.toStringDeep("$prefixOtherLines \u2514", "$prefixOtherLines ")}
'
;
}
return
result
;
}
}
class
SemanticsServer
extends
mojom
.
SemanticsServer
{
void
tap
(
int
nodeID
)
{
SemanticsNode
.
getSemanticActionHandlerForId
(
nodeID
,
neededFlag:
_SemanticFlags
.
canBeTapped
)?.
handleSemanticTap
();
}
void
longPress
(
int
nodeID
)
{
SemanticsNode
.
getSemanticActionHandlerForId
(
nodeID
,
neededFlag:
_SemanticFlags
.
canBeLongPressed
)?.
handleSemanticLongPress
();
}
void
scrollLeft
(
int
nodeID
)
{
SemanticsNode
.
getSemanticActionHandlerForId
(
nodeID
,
neededFlag:
_SemanticFlags
.
canBeScrolledHorizontally
)?.
handleSemanticScrollLeft
();
}
void
scrollRight
(
int
nodeID
)
{
SemanticsNode
.
getSemanticActionHandlerForId
(
nodeID
,
neededFlag:
_SemanticFlags
.
canBeScrolledHorizontally
)?.
handleSemanticScrollRight
();
}
void
scrollUp
(
int
nodeID
)
{
SemanticsNode
.
getSemanticActionHandlerForId
(
nodeID
,
neededFlag:
_SemanticFlags
.
canBeScrolledVertically
)?.
handleSemanticScrollUp
();
}
void
scrollDown
(
int
nodeID
)
{
SemanticsNode
.
getSemanticActionHandlerForId
(
nodeID
,
neededFlag:
_SemanticFlags
.
canBeScrolledVertically
)?.
handleSemanticScrollDown
();
}
}
packages/flutter/lib/src/rendering/stack.dart
View file @
06480523
...
...
@@ -404,6 +404,8 @@ abstract class RenderStackBase extends RenderBox
paintStack
(
context
,
offset
);
}
}
Rect
describeApproximatePaintClip
(
RenderObject
child
)
=>
_hasVisualOverflow
?
Point
.
origin
&
size
:
null
;
}
/// Implements the stack layout algorithm
...
...
packages/flutter/lib/src/rendering/view.dart
View file @
06480523
...
...
@@ -141,6 +141,7 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
}
Rect
get
paintBounds
=>
Point
.
origin
&
size
;
Rect
get
semanticBounds
=>
Point
.
origin
&
size
;
void
debugDescribeSettings
(
List
<
String
>
settings
)
{
// call to ${super.debugDescribeSettings(prefix)} is omitted because the root superclasses don't include any interesting information for this class
...
...
packages/flutter/lib/src/rendering/viewport.dart
View file @
06480523
...
...
@@ -55,6 +55,7 @@ class RenderViewport extends RenderBox with RenderObjectWithChildMixin<RenderBox
assert
(
_offsetIsSane
(
value
,
scrollDirection
));
_scrollOffset
=
value
;
markNeedsPaint
();
markNeedsSemanticsUpdate
();
}
/// The direction in which the child is permitted to be larger than the viewport
...
...
@@ -137,11 +138,15 @@ class RenderViewport extends RenderBox with RenderObjectWithChildMixin<RenderBox
dyInDevicePixels
/
devicePixelRatio
);
}
bool
_wouldNeedClipAtOffset
(
Offset
offset
)
{
assert
(
child
!=
null
);
return
offset
<
Offset
.
zero
||
!(
Offset
.
zero
&
size
).
contains
(((
Offset
.
zero
-
offset
)
&
child
.
size
).
bottomRight
);
}
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
if
(
child
!=
null
)
{
Offset
roundedScrollOffset
=
_scrollOffsetRoundedToIntegerDevicePixels
;
bool
_needsClip
=
offset
<
Offset
.
zero
||
!(
offset
&
size
).
contains
(((
offset
-
roundedScrollOffset
)
&
child
.
size
).
bottomRight
);
bool
_needsClip
=
_wouldNeedClipAtOffset
(
roundedScrollOffset
);
if
(
_needsClip
)
{
context
.
pushClipRect
(
needsCompositing
,
offset
,
Point
.
origin
&
size
,
(
PaintingContext
context
,
Offset
offset
)
{
context
.
paintChild
(
child
,
offset
-
roundedScrollOffset
);
...
...
@@ -157,6 +162,13 @@ class RenderViewport extends RenderBox with RenderObjectWithChildMixin<RenderBox
super
.
applyPaintTransform
(
child
,
transform
);
}
Rect
describeApproximatePaintClip
(
RenderObject
child
)
{
if
(
child
!=
null
&&
_wouldNeedClipAtOffset
(
_scrollOffsetRoundedToIntegerDevicePixels
))
return
Point
.
origin
&
size
;
return
null
;
}
bool
hitTestChildren
(
HitTestResult
result
,
{
Point
position
})
{
if
(
child
!=
null
)
{
assert
(
child
.
parentData
is
BoxParentData
);
...
...
@@ -200,6 +212,7 @@ abstract class RenderVirtualViewport<T extends ContainerBoxParentDataMixin<Rende
return
;
_paintOffset
=
value
;
markNeedsPaint
();
markNeedsSemanticsUpdate
();
}
/// Called during [layout] to determine the grid's children.
...
...
@@ -254,4 +267,6 @@ abstract class RenderVirtualViewport<T extends ContainerBoxParentDataMixin<Rende
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
context
.
pushClipRect
(
needsCompositing
,
offset
,
Point
.
origin
&
size
,
_paintContents
);
}
Rect
describeApproximatePaintClip
(
RenderObject
child
)
=>
Point
.
origin
&
size
;
}
packages/flutter/lib/src/widgets/basic.dart
View file @
06480523
...
...
@@ -2009,21 +2009,116 @@ class RepaintBoundary extends OneChildRenderObjectWidget {
}
class
IgnorePointer
extends
OneChildRenderObjectWidget
{
IgnorePointer
({
Key
key
,
Widget
child
,
this
.
ignoring
:
true
})
IgnorePointer
({
Key
key
,
Widget
child
,
this
.
ignoring
:
true
,
this
.
ignoringSemantics
})
:
super
(
key:
key
,
child:
child
);
final
bool
ignoring
;
final
bool
ignoringSemantics
;
// if null, defaults to value of ignoring
RenderIgnorePointer
createRenderObject
()
=>
new
RenderIgnorePointer
(
ignoring:
ignoring
);
RenderIgnorePointer
createRenderObject
()
=>
new
RenderIgnorePointer
(
ignoring:
ignoring
,
ignoringSemantics:
ignoringSemantics
);
void
updateRenderObject
(
RenderIgnorePointer
renderObject
,
IgnorePointer
oldWidget
)
{
renderObject
.
ignoring
=
ignoring
;
renderObject
.
ignoringSemantics
=
ignoringSemantics
;
}
}
// UTILITY NODES
/// The Semantics widget annotates the widget tree with a description
/// of the meaning of the widgets, so that accessibility tools, search
/// engines, and other semantic analysis software can determine the
/// meaning of the application.
class
Semantics
extends
OneChildRenderObjectWidget
{
Semantics
({
Key
key
,
Widget
child
,
this
.
container
:
false
,
this
.
checked
,
this
.
label
})
:
super
(
key:
key
,
child:
child
)
{
assert
(
container
!=
null
);
}
/// If 'container' is true, this Widget will introduce a new node in
/// the semantics tree. Otherwise, the semantics will be merged with
/// the semantics of any ancestors.
///
/// The 'container' flag is implicitly set to true on the immediate
/// semantics-providing descendants of a node where multiple
/// children have semantics or have descendants providing semantics.
/// In other words, the semantics of siblings are not merged. To
/// merge the semantics of an entire subtree, including siblings,
/// you can use a [MergeSemantics] widget.
final
bool
container
;
/// If non-null, indicates that this subtree represents a checkbox
/// or similar widget with a "checked" state, and what its current
/// state is.
final
bool
checked
;
/// Provides a textual description of the widget.
final
String
label
;
RenderSemanticAnnotations
createRenderObject
()
=>
new
RenderSemanticAnnotations
(
container:
container
,
checked:
checked
,
label:
label
);
void
updateRenderObject
(
RenderSemanticAnnotations
renderObject
,
Semantics
oldWidget
)
{
renderObject
.
container
=
container
;
renderObject
.
checked
=
checked
;
renderObject
.
label
=
label
;
}
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
description
.
add
(
'container:
$container
'
);
if
(
checked
!=
null
);
description
.
add
(
'checked:
$checked
'
);
if
(
label
!=
null
);
description
.
add
(
'label: "
$label
"'
);
}
}
/// Causes all the semantics of the subtree rooted at this node to be
/// merged into one node in the semantics tree. For example, if you
/// have a component with a Text node next to a checkbox widget, this
/// could be used to merge the label from the Text node with the
/// "checked" semantic state of the checkbox into a single node that
/// had both the label and the checked state. Otherwise, the label
/// would be presented as a separate feature than the checkbox, and
/// the user would not be able to be sure that they were related.
///
/// Be aware that if two nodes in the subtree have conflicting
/// semantics, the result may be nonsensical. For example, a subtree
/// with a checked checkbox and an unchecked checkbox will be
/// presented as checked. All the labels will be merged into a single
/// string (with newlines separating each label from the other). If
/// multiple nodes in the merged subtree can handle semantic gestures,
/// the first one in tree order will be the one to receive the
/// callbacks.
class
MergeSemantics
extends
OneChildRenderObjectWidget
{
MergeSemantics
({
Key
key
,
Widget
child
})
:
super
(
key:
key
,
child:
child
);
RenderMergeSemantics
createRenderObject
()
=>
new
RenderMergeSemantics
();
}
/// Drops all semantics in this subtree.
///
/// This can be used to hide subwidgets that would otherwise be
/// reported but that would only be confusing. For example, the
/// material library's [Chip] widget hides the avatar since it is
/// redundant with the chip label.
class
ExcludeSemantics
extends
OneChildRenderObjectWidget
{
ExcludeSemantics
({
Key
key
,
Widget
child
})
:
super
(
key:
key
,
child:
child
);
RenderExcludeSemantics
createRenderObject
()
=>
new
RenderExcludeSemantics
();
}
class
MetaData
extends
OneChildRenderObjectWidget
{
MetaData
({
Key
key
,
Widget
child
,
this
.
metaData
})
:
super
(
key:
key
,
child:
child
);
...
...
packages/flutter/lib/src/widgets/focus.dart
View file @
06480523
...
...
@@ -271,13 +271,15 @@ class _FocusState extends State<Focus> {
scheduleMicrotask
(
_ensureVisibleIfFocused
);
}
}
return
new
_FocusScope
(
return
new
Semantics
(
container:
true
,
child:
new
_FocusScope
(
focusState:
this
,
scopeFocused:
Focus
.
_atScope
(
context
),
focusedScope:
_focusedScope
==
_noFocusedScope
?
null
:
_focusedScope
,
focusedWidget:
_focusedWidget
,
child:
config
.
child
)
);
}
}
packages/flutter/lib/src/widgets/framework.dart
View file @
06480523
...
...
@@ -1466,7 +1466,7 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Buildab
}
void
debugUpdateRenderObjectOwner
()
{
_renderObject
.
debugOwner
=
debugGetOwnershipChain
(
4
);
_renderObject
.
debugOwner
=
debugGetOwnershipChain
(
10
);
}
void
performRebuild
()
{
...
...
packages/flutter/lib/src/widgets/gesture_detector.dart
View file @
06480523
...
...
@@ -31,6 +31,10 @@ export 'package:flutter/gestures.dart' show
///
/// Attempts to recognize gestures that correspond to its non-null callbacks.
///
/// GestureDetector also listens for accessibility events and maps
/// them to the callbacks. To ignore accessibility events, set
/// [excludeFromSemantics] to true.
///
/// See http://flutter.io/gestures/ for additional information.
class
GestureDetector
extends
StatefulComponent
{
const
GestureDetector
({
...
...
@@ -54,7 +58,8 @@ class GestureDetector extends StatefulComponent {
this
.
onScaleStart
,
this
.
onScaleUpdate
,
this
.
onScaleEnd
,
this
.
behavior
this
.
behavior
,
this
.
excludeFromSemantics
:
false
})
:
super
(
key:
key
);
final
Widget
child
;
...
...
@@ -65,7 +70,7 @@ class GestureDetector extends StatefulComponent {
/// A pointer that will trigger a tap has stopped contacting the screen at a
/// particular location.
final
GestureTap
Down
Callback
onTapUp
;
final
GestureTap
Up
Callback
onTapUp
;
/// A tap has occurred.
final
GestureTapCallback
onTap
;
...
...
@@ -117,6 +122,13 @@ class GestureDetector extends StatefulComponent {
/// How this gesture detector should behave during hit testing.
final
HitTestBehavior
behavior
;
/// Whether to exclude these gestures from the semantics tree. For
/// example, the long-press gesture for showing a tooltip is
/// excluded because the tooltip itself is included in the semantics
/// tree directly and so having a gesture to show it would result in
/// duplication of information.
final
bool
excludeFromSemantics
;
_GestureDetectorState
createState
()
=>
new
_GestureDetectorState
();
}
...
...
@@ -269,11 +281,30 @@ class _GestureDetectorState extends State<GestureDetector> {
}
Widget
build
(
BuildContext
context
)
{
return
new
Listener
(
Widget
result
=
new
Listener
(
onPointerDown:
_handlePointerDown
,
behavior:
config
.
behavior
??
_defaultBehavior
,
child:
config
.
child
);
if
(!
config
.
excludeFromSemantics
)
{
result
=
new
_GestureSemantics
(
onTapDown:
config
.
onTapDown
,
onTapUp:
config
.
onTapUp
,
onTap:
config
.
onTap
,
onLongPress:
config
.
onLongPress
,
onVerticalDragStart:
config
.
onVerticalDragStart
,
onVerticalDragUpdate:
config
.
onVerticalDragUpdate
,
onVerticalDragEnd:
config
.
onVerticalDragEnd
,
onHorizontalDragStart:
config
.
onHorizontalDragStart
,
onHorizontalDragUpdate:
config
.
onHorizontalDragUpdate
,
onHorizontalDragEnd:
config
.
onHorizontalDragEnd
,
onPanStart:
config
.
onPanStart
,
onPanUpdate:
config
.
onPanUpdate
,
onPanEnd:
config
.
onPanEnd
,
child:
result
);
}
return
result
;
}
void
debugFillDescription
(
List
<
String
>
description
)
{
...
...
@@ -309,3 +340,122 @@ class _GestureDetectorState extends State<GestureDetector> {
}
}
}
class
_GestureSemantics
extends
OneChildRenderObjectWidget
{
_GestureSemantics
({
Key
key
,
this
.
onTapDown
,
this
.
onTapUp
,
this
.
onTap
,
this
.
onLongPress
,
this
.
onVerticalDragStart
,
this
.
onVerticalDragUpdate
,
this
.
onVerticalDragEnd
,
this
.
onHorizontalDragStart
,
this
.
onHorizontalDragUpdate
,
this
.
onHorizontalDragEnd
,
this
.
onPanStart
,
this
.
onPanUpdate
,
this
.
onPanEnd
,
Widget
child
})
:
super
(
key:
key
,
child:
child
);
final
GestureTapDownCallback
onTapDown
;
final
GestureTapUpCallback
onTapUp
;
final
GestureTapCallback
onTap
;
final
GestureLongPressCallback
onLongPress
;
final
GestureDragStartCallback
onVerticalDragStart
;
final
GestureDragUpdateCallback
onVerticalDragUpdate
;
final
GestureDragEndCallback
onVerticalDragEnd
;
final
GestureDragStartCallback
onHorizontalDragStart
;
final
GestureDragUpdateCallback
onHorizontalDragUpdate
;
final
GestureDragEndCallback
onHorizontalDragEnd
;
final
GesturePanStartCallback
onPanStart
;
final
GesturePanUpdateCallback
onPanUpdate
;
final
GesturePanEndCallback
onPanEnd
;
bool
get
_watchingTaps
{
return
onTapDown
!=
null
||
onTapUp
!=
null
||
onTap
!=
null
;
}
bool
get
_watchingHorizontalDrags
{
return
onHorizontalDragStart
!=
null
||
onHorizontalDragUpdate
!=
null
||
onHorizontalDragEnd
!=
null
;
}
bool
get
_watchingVerticalDrags
{
return
onVerticalDragStart
!=
null
||
onVerticalDragUpdate
!=
null
||
onVerticalDragEnd
!=
null
;
}
bool
get
_watchingPans
{
return
onPanStart
!=
null
||
onPanUpdate
!=
null
||
onPanEnd
!=
null
;
}
void
_handleTap
()
{
if
(
onTapDown
!=
null
)
onTapDown
(
Point
.
origin
);
if
(
onTapUp
!=
null
)
onTapUp
(
Point
.
origin
);
if
(
onTap
!=
null
)
onTap
();
}
void
_handleHorizontalDragUpdate
(
double
delta
)
{
if
(
_watchingHorizontalDrags
)
{
if
(
onHorizontalDragStart
!=
null
)
onHorizontalDragStart
(
Point
.
origin
);
if
(
onHorizontalDragUpdate
!=
null
)
onHorizontalDragUpdate
(
delta
);
if
(
onHorizontalDragEnd
!=
null
)
onHorizontalDragEnd
(
Offset
.
zero
);
}
else
{
assert
(
_watchingPans
);
if
(
onPanStart
!=
null
)
onPanStart
(
Point
.
origin
);
if
(
onPanUpdate
!=
null
)
onPanUpdate
(
new
Offset
(
delta
,
0.0
));
if
(
onPanEnd
!=
null
)
onPanEnd
(
Offset
.
zero
);
}
}
void
_handleVerticalDragUpdate
(
double
delta
)
{
if
(
_watchingVerticalDrags
)
{
if
(
onVerticalDragStart
!=
null
)
onVerticalDragStart
(
Point
.
origin
);
if
(
onVerticalDragUpdate
!=
null
)
onVerticalDragUpdate
(
delta
);
if
(
onVerticalDragEnd
!=
null
)
onVerticalDragEnd
(
Offset
.
zero
);
}
else
{
assert
(
_watchingPans
);
if
(
onPanStart
!=
null
)
onPanStart
(
Point
.
origin
);
if
(
onPanUpdate
!=
null
)
onPanUpdate
(
new
Offset
(
0.0
,
delta
));
if
(
onPanEnd
!=
null
)
onPanEnd
(
Offset
.
zero
);
}
}
RenderSemanticsGestureHandler
createRenderObject
()
=>
new
RenderSemanticsGestureHandler
(
onTap:
_watchingTaps
?
_handleTap
:
null
,
onLongPress:
onLongPress
,
onHorizontalDragUpdate:
_watchingHorizontalDrags
||
_watchingPans
?
_handleHorizontalDragUpdate
:
null
,
onVerticalDragUpdate:
_watchingVerticalDrags
||
_watchingPans
?
_handleVerticalDragUpdate
:
null
);
void
updateRenderObject
(
RenderSemanticsGestureHandler
renderObject
,
_GestureSemantics
oldWidget
)
{
renderObject
.
onTap
=
_watchingTaps
?
_handleTap
:
null
;
renderObject
.
onLongPress
=
onLongPress
;
renderObject
.
onHorizontalDragUpdate
=
_watchingHorizontalDrags
||
_watchingPans
?
_handleHorizontalDragUpdate
:
null
;
renderObject
.
onVerticalDragUpdate
=
_watchingVerticalDrags
||
_watchingPans
?
_handleVerticalDragUpdate
:
null
;
}
}
packages/flutter/lib/src/widgets/modal_barrier.dart
View file @
06480523
...
...
@@ -6,6 +6,7 @@ import 'package:flutter/animation.dart';
import
'basic.dart'
;
import
'framework.dart'
;
import
'gesture_detector.dart'
;
import
'navigator.dart'
;
import
'transitions.dart'
;
...
...
@@ -24,8 +25,10 @@ class ModalBarrier extends StatelessComponent {
final
bool
dismissable
;
Widget
build
(
BuildContext
context
)
{
return
new
Listener
(
onPointerDown:
(
_
)
{
return
new
Semantics
(
container:
true
,
child:
new
GestureDetector
(
onTapDown:
(
Point
position
)
{
if
(
dismissable
)
Navigator
.
pop
(
context
);
},
...
...
@@ -38,6 +41,7 @@ class ModalBarrier extends StatelessComponent {
)
)
)
)
);
}
}
...
...
packages/flutter/lib/src/widgets/semantics_debugger.dart
0 → 100644
View file @
06480523
// Copyright 2015 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
'dart:math'
as
math
;
import
'dart:ui'
as
ui
;
import
'package:flutter/painting.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:sky_services/semantics/semantics.mojom.dart'
as
engine
;
import
'basic.dart'
;
import
'framework.dart'
;
import
'gesture_detector.dart'
;
class
SemanticsDebugger
extends
StatefulComponent
{
const
SemanticsDebugger
({
Key
key
,
this
.
child
})
:
super
(
key:
key
);
final
Widget
child
;
_SemanticsDebuggerState
createState
()
=>
new
_SemanticsDebuggerState
();
}
class
_SemanticsDebuggerState
extends
State
<
SemanticsDebugger
>
{
void
initState
()
{
super
.
initState
();
_SemanticsDebuggerClient
.
ensureInstantiated
();
_SemanticsDebuggerClient
.
instance
.
addListener
(
_update
);
}
void
dispose
()
{
_SemanticsDebuggerClient
.
instance
.
removeListener
(
_update
);
super
.
dispose
();
}
void
_update
()
{
setState
(()
{
// the generation of the _SemanticsDebuggerClient has changed
});
}
Point
_lastPointerDownLocation
;
void
_handlePointerDown
(
PointerDownEvent
event
)
{
setState
(()
{
_lastPointerDownLocation
=
event
.
position
;
});
}
void
_handleTap
()
{
assert
(
_lastPointerDownLocation
!=
null
);
_SemanticsDebuggerClient
.
instance
.
handleTap
(
_lastPointerDownLocation
);
setState
(()
{
_lastPointerDownLocation
=
null
;
});
}
void
_handleLongPress
()
{
assert
(
_lastPointerDownLocation
!=
null
);
_SemanticsDebuggerClient
.
instance
.
handleLongPress
(
_lastPointerDownLocation
);
setState
(()
{
_lastPointerDownLocation
=
null
;
});
}
void
_handlePanEnd
(
Offset
velocity
)
{
assert
(
_lastPointerDownLocation
!=
null
);
_SemanticsDebuggerClient
.
instance
.
handlePanEnd
(
_lastPointerDownLocation
,
velocity
);
setState
(()
{
_lastPointerDownLocation
=
null
;
});
}
Widget
build
(
BuildContext
context
)
{
return
new
CustomPaint
(
foregroundPainter:
new
_SemanticsDebuggerPainter
(
_SemanticsDebuggerClient
.
instance
.
generation
,
_lastPointerDownLocation
),
child:
new
GestureDetector
(
behavior:
HitTestBehavior
.
opaque
,
onTap:
_handleTap
,
onLongPress:
_handleLongPress
,
onPanEnd:
_handlePanEnd
,
excludeFromSemantics:
true
,
// otherwise if you don't hit anything, we end up receiving it, which causes an infinite loop...
child:
new
Listener
(
onPointerDown:
_handlePointerDown
,
behavior:
HitTestBehavior
.
opaque
,
child:
new
IgnorePointer
(
ignoringSemantics:
false
,
child:
config
.
child
)
)
)
);
}
}
typedef
bool
_SemanticsDebuggerEntryFilter
(
_SemanticsDebuggerEntry
entry
);
class
_SemanticsDebuggerEntry
{
_SemanticsDebuggerEntry
(
this
.
id
);
final
int
id
;
bool
canBeTapped
=
false
;
bool
canBeLongPressed
=
false
;
bool
canBeScrolledHorizontally
=
false
;
bool
canBeScrolledVertically
=
false
;
bool
hasCheckedState
=
false
;
bool
isChecked
=
false
;
String
label
;
Matrix4
transform
;
Rect
rect
;
List
<
_SemanticsDebuggerEntry
>
children
;
String
toString
()
{
return
'_SemanticsDebuggerEntry(
$id
;
$rect
; "
$label
"'
'
${canBeTapped ? "; canBeTapped" : ""}
'
'
${canBeLongPressed ? "; canBeLongPressed" : ""}
'
'
${canBeScrolledHorizontally ? "; canBeScrolledHorizontally" : ""}
'
'
${canBeScrolledVertically ? "; canBeScrolledVertically" : ""}
'
'
${hasCheckedState ? isChecked ? "; checked" : "; unchecked" : ""}
'
')'
;
}
String
toStringDeep
([
String
prefix
=
''
])
{
if
(
prefix
.
length
>
20
)
return
'
$prefix
<ABORTED>
\n
'
;
String
result
=
'
$prefix$this
\n
'
;
for
(
_SemanticsDebuggerEntry
child
in
children
.
reversed
)
{
prefix
+=
' '
;
result
+=
'
${child.toStringDeep(prefix)}
'
;
}
return
result
;
}
int
findDepth
()
{
if
(
children
==
null
||
children
.
isEmpty
)
return
1
;
return
children
.
map
((
_SemanticsDebuggerEntry
e
)
=>
e
.
findDepth
()).
reduce
(
math
.
max
)
+
1
;
}
static
const
TextStyle
textStyles
=
const
TextStyle
(
color:
const
Color
(
0xFF000000
),
fontSize:
10.0
,
height:
0.8
,
textAlign:
TextAlign
.
center
);
TextPainter
textPainter
;
void
updateMessage
()
{
List
<
String
>
annotations
=
<
String
>[];
bool
wantsTap
=
false
;
if
(
hasCheckedState
)
{
annotations
.
add
(
isChecked
?
'checked'
:
'unchecked'
);
wantsTap
=
true
;
}
if
(
canBeTapped
)
{
if
(!
wantsTap
)
annotations
.
add
(
'button'
);
}
else
{
if
(
wantsTap
)
annotations
.
add
(
'disabled'
);
}
if
(
canBeLongPressed
)
annotations
.
add
(
'long-pressable'
);
if
(
canBeScrolledHorizontally
||
canBeScrolledVertically
)
annotations
.
add
(
'scrollable'
);
String
message
;
if
(
annotations
.
isEmpty
)
{
assert
(
label
!=
null
);
message
=
label
;
}
else
{
if
(
label
==
''
)
{
message
=
annotations
.
join
(
'; '
);
}
else
{
message
=
'
$label
(
${annotations.join('; ')}
)'
;
}
}
message
=
message
.
trim
();
if
(
message
!=
''
)
{
textPainter
??=
new
TextPainter
();
textPainter
.
text
=
new
StyledTextSpan
(
textStyles
,
<
TextSpan
>[
new
PlainTextSpan
(
message
)]);
textPainter
.
maxWidth
=
rect
.
width
;
textPainter
.
maxHeight
=
rect
.
height
;
textPainter
.
layout
();
}
else
{
textPainter
=
null
;
}
}
void
paint
(
Canvas
canvas
,
int
rank
)
{
canvas
.
save
();
if
(
transform
!=
null
)
canvas
.
transform
(
transform
.
storage
);
if
(!
rect
.
isEmpty
)
{
Color
lineColor
=
new
Color
(
0xFF000000
+
new
math
.
Random
(
id
).
nextInt
(
0xFFFFFF
));
Rect
innerRect
=
rect
.
deflate
(
rank
*
1.0
);
if
(
innerRect
.
isEmpty
)
{
Paint
fill
=
new
Paint
()
..
color
=
lineColor
..
style
=
ui
.
PaintingStyle
.
fill
;
canvas
.
drawRect
(
rect
,
fill
);
}
else
{
Paint
fill
=
new
Paint
()
..
color
=
const
Color
(
0xFFFFFFFF
)
..
style
=
ui
.
PaintingStyle
.
fill
;
canvas
.
drawRect
(
rect
,
fill
);
Paint
line
=
new
Paint
()
..
strokeWidth
=
rank
*
2.0
..
color
=
lineColor
..
style
=
ui
.
PaintingStyle
.
stroke
;
canvas
.
drawRect
(
innerRect
,
line
);
}
if
(
textPainter
!=
null
)
{
canvas
.
save
();
canvas
.
clipRect
(
rect
);
textPainter
.
paint
(
canvas
,
rect
.
topLeft
.
toOffset
());
canvas
.
restore
();
}
}
for
(
_SemanticsDebuggerEntry
child
in
children
)
child
.
paint
(
canvas
,
rank
-
1
);
canvas
.
restore
();
}
_SemanticsDebuggerEntry
hitTest
(
Point
position
,
_SemanticsDebuggerEntryFilter
filter
)
{
if
(
transform
!=
null
)
{
if
(
transform
.
determinant
==
0.0
)
return
null
;
Matrix4
invertedTransform
=
new
Matrix4
.
inverted
(
transform
);
position
=
MatrixUtils
.
transformPoint
(
invertedTransform
,
position
);
}
if
(!
rect
.
contains
(
position
))
return
null
;
_SemanticsDebuggerEntry
result
;
for
(
_SemanticsDebuggerEntry
child
in
children
.
reversed
)
{
result
=
child
.
hitTest
(
position
,
filter
);
if
(
result
!=
null
)
break
;
}
if
(
result
==
null
||
!
filter
(
result
))
result
=
this
;
return
result
;
}
}
class
_SemanticsDebuggerClient
implements
engine
.
SemanticsClient
{
_SemanticsDebuggerClient
.
_
()
{
Renderer
.
instance
.
setSemanticsClient
(
this
);
}
static
_SemanticsDebuggerClient
instance
;
static
engine
.
SemanticsServer
_server
;
static
void
ensureInstantiated
({
engine
.
SemanticsServer
server
})
{
_server
=
server
??
new
SemanticsServer
();
instance
??=
new
_SemanticsDebuggerClient
.
_
();
}
Set
<
VoidCallback
>
_listeners
=
new
Set
<
VoidCallback
>();
void
addListener
(
VoidCallback
callback
)
{
assert
(!
_listeners
.
contains
(
callback
));
_listeners
.
add
(
callback
);
}
void
removeListener
(
VoidCallback
callback
)
{
_listeners
.
remove
(
callback
);
}
Map
<
int
,
_SemanticsDebuggerEntry
>
nodes
=
<
int
,
_SemanticsDebuggerEntry
>{};
_SemanticsDebuggerEntry
_updateNode
(
engine
.
SemanticsNode
node
)
{
_SemanticsDebuggerEntry
entry
=
nodes
.
putIfAbsent
(
node
.
id
,
()
=>
new
_SemanticsDebuggerEntry
(
node
.
id
));
if
(
node
.
flags
!=
null
)
{
entry
.
canBeTapped
=
node
.
flags
.
canBeTapped
;
entry
.
canBeLongPressed
=
node
.
flags
.
canBeLongPressed
;
entry
.
canBeScrolledHorizontally
=
node
.
flags
.
canBeScrolledHorizontally
;
entry
.
canBeScrolledVertically
=
node
.
flags
.
canBeScrolledVertically
;
entry
.
hasCheckedState
=
node
.
flags
.
hasCheckedState
;
entry
.
isChecked
=
node
.
flags
.
isChecked
;
}
if
(
node
.
strings
!=
null
)
{
assert
(
node
.
strings
.
label
!=
null
);
entry
.
label
=
node
.
strings
.
label
;
}
else
{
assert
(
entry
.
label
!=
null
);
}
if
(
node
.
geometry
!=
null
)
{
if
(
node
.
geometry
.
transform
!=
null
)
{
assert
(
node
.
geometry
.
transform
.
length
==
16
);
// TODO(ianh): Replace this with a cleaner call once
// https://github.com/google/vector_math.dart/issues/159
// is fixed.
List
<
double
>
array
=
node
.
geometry
.
transform
;
entry
.
transform
=
new
Matrix4
(
array
[
0
],
array
[
1
],
array
[
2
],
array
[
3
],
array
[
4
],
array
[
5
],
array
[
6
],
array
[
7
],
array
[
8
],
array
[
9
],
array
[
10
],
array
[
11
],
array
[
12
],
array
[
13
],
array
[
14
],
array
[
15
]
);
}
else
{
entry
.
transform
=
null
;
}
entry
.
rect
=
new
Rect
.
fromLTWH
(
node
.
geometry
.
left
,
node
.
geometry
.
top
,
node
.
geometry
.
width
,
node
.
geometry
.
height
);
}
entry
.
updateMessage
();
if
(
node
.
children
!=
null
)
{
Set
oldChildren
=
new
Set
<
_SemanticsDebuggerEntry
>.
from
(
entry
.
children
??
const
<
_SemanticsDebuggerEntry
>[]);
entry
.
children
?.
clear
();
entry
.
children
??=
new
List
<
_SemanticsDebuggerEntry
>();
for
(
engine
.
SemanticsNode
child
in
node
.
children
)
entry
.
children
.
add
(
_updateNode
(
child
));
Set
newChildren
=
new
Set
<
_SemanticsDebuggerEntry
>.
from
(
entry
.
children
);
Set
<
_SemanticsDebuggerEntry
>
removedChildren
=
oldChildren
.
difference
(
newChildren
);
for
(
_SemanticsDebuggerEntry
oldChild
in
removedChildren
)
nodes
.
remove
(
oldChild
.
id
);
}
return
entry
;
}
int
generation
=
0
;
updateSemanticsTree
(
List
<
engine
.
SemanticsNode
>
nodes
)
{
generation
+=
1
;
for
(
engine
.
SemanticsNode
node
in
nodes
)
_updateNode
(
node
);
for
(
VoidCallback
listener
in
_listeners
)
listener
();
}
_SemanticsDebuggerEntry
_hitTest
(
Point
position
,
_SemanticsDebuggerEntryFilter
filter
)
{
return
nodes
[
0
]?.
hitTest
(
position
,
filter
);
}
void
handleTap
(
Point
position
)
{
_server
.
tap
(
_hitTest
(
position
,
(
_SemanticsDebuggerEntry
entry
)
=>
entry
.
canBeTapped
)?.
id
??
0
);
}
void
handleLongPress
(
Point
position
)
{
_server
.
longPress
(
_hitTest
(
position
,
(
_SemanticsDebuggerEntry
entry
)
=>
entry
.
canBeLongPressed
)?.
id
??
0
);
}
void
handlePanEnd
(
Point
position
,
Offset
velocity
)
{
if
(
velocity
.
dx
.
abs
()
==
velocity
.
dy
.
abs
())
return
;
if
(
velocity
.
dx
.
abs
()
>
velocity
.
dy
.
abs
())
{
if
(
velocity
.
dx
.
sign
<
0
)
_server
.
scrollLeft
(
_hitTest
(
position
,
(
_SemanticsDebuggerEntry
entry
)
=>
entry
.
canBeScrolledHorizontally
)?.
id
??
0
);
else
_server
.
scrollRight
(
_hitTest
(
position
,
(
_SemanticsDebuggerEntry
entry
)
=>
entry
.
canBeScrolledHorizontally
)?.
id
??
0
);
}
else
{
if
(
velocity
.
dy
.
sign
<
0
)
_server
.
scrollUp
(
_hitTest
(
position
,
(
_SemanticsDebuggerEntry
entry
)
=>
entry
.
canBeScrolledVertically
)?.
id
??
0
);
else
_server
.
scrollDown
(
_hitTest
(
position
,
(
_SemanticsDebuggerEntry
entry
)
=>
entry
.
canBeScrolledVertically
)?.
id
??
0
);
}
}
}
class
_SemanticsDebuggerPainter
extends
CustomPainter
{
const
_SemanticsDebuggerPainter
(
this
.
generation
,
this
.
pointerPosition
);
final
int
generation
;
final
Point
pointerPosition
;
void
paint
(
Canvas
canvas
,
Size
size
)
{
_SemanticsDebuggerClient
.
instance
.
nodes
[
0
]?.
paint
(
canvas
,
_SemanticsDebuggerClient
.
instance
.
nodes
[
0
].
findDepth
()
);
if
(
pointerPosition
!=
null
)
{
Paint
paint
=
new
Paint
();
paint
.
color
=
const
Color
(
0x7F0090FF
);
canvas
.
drawCircle
(
pointerPosition
,
10.0
,
paint
);
}
}
bool
shouldRepaint
(
_SemanticsDebuggerPainter
oldDelegate
)
{
return
generation
!=
oldDelegate
.
generation
||
pointerPosition
!=
oldDelegate
.
pointerPosition
;
}
}
packages/flutter/lib/widgets.dart
View file @
06480523
...
...
@@ -33,10 +33,11 @@ export 'src/widgets/performance_overlay.dart';
export
'src/widgets/placeholder.dart'
;
export
'src/widgets/raw_keyboard_listener.dart'
;
export
'src/widgets/routes.dart'
;
export
'src/widgets/scroll_behavior.dart'
;
export
'src/widgets/scrollable.dart'
;
export
'src/widgets/scrollable_grid.dart'
;
export
'src/widgets/scrollable_list.dart'
;
export
'src/widgets/s
croll_behavio
r.dart'
;
export
'src/widgets/s
emantics_debugge
r.dart'
;
export
'src/widgets/status_transitions.dart'
;
export
'src/widgets/title.dart'
;
export
'src/widgets/transitions.dart'
;
...
...
packages/flutter/pubspec.yaml
View file @
06480523
...
...
@@ -8,8 +8,8 @@ dependencies:
collection
:
'
>=1.1.3
<2.0.0'
intl
:
'
>=0.12.4+2
<0.13.0'
material_design_icons
:
'
>=0.0.3
<0.1.0'
sky_engine
:
0.0.8
7
sky_services
:
0.0.8
7
sky_engine
:
0.0.8
8
sky_services
:
0.0.8
8
vector_math
:
'
>=1.4.5
<2.0.0'
quiver
:
'
>=0.21.4
<0.22.0'
...
...
packages/flutter/test/widget/buttons_test.dart
0 → 100644
View file @
06480523
// Copyright 2015 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/material.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:test/test.dart'
;
import
'test_semantics.dart'
;
void
main
(
)
{
test
(
'Does FlatButton contribute semantics'
,
()
{
testWidgets
((
WidgetTester
tester
)
{
TestSemanticsClient
client
=
new
TestSemanticsClient
();
tester
.
pumpWidget
(
new
Material
(
child:
new
Center
(
child:
new
FlatButton
(
onPressed:
()
{
},
child:
new
Text
(
'Hello'
)
)
)
)
);
expect
(
client
.
updates
.
length
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
id
,
equals
(
0
));
expect
(
client
.
updates
[
0
].
flags
.
canBeTapped
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeLongPressed
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledHorizontally
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledVertically
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
hasCheckedState
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
isChecked
,
isFalse
);
expect
(
client
.
updates
[
0
].
strings
.
label
,
equals
(
''
));
expect
(
client
.
updates
[
0
].
geometry
.
transform
,
isNull
);
expect
(
client
.
updates
[
0
].
geometry
.
left
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
top
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
width
,
equals
(
800.0
));
expect
(
client
.
updates
[
0
].
geometry
.
height
,
equals
(
600.0
));
expect
(
client
.
updates
[
0
].
children
.
length
,
equals
(
1
));
expect
(
client
.
updates
[
0
].
children
[
0
].
id
,
equals
(
1
));
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
canBeTapped
,
isTrue
);
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
canBeLongPressed
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
canBeScrolledHorizontally
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
canBeScrolledVertically
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
hasCheckedState
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
isChecked
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
strings
.
label
,
equals
(
'Hello'
));
expect
(
client
.
updates
[
0
].
children
[
0
].
children
.
length
,
equals
(
0
));
expect
(
client
.
updates
[
1
],
isNull
);
client
.
updates
.
clear
();
});
});
}
packages/flutter/test/widget/semantics_1_test.dart
0 → 100644
View file @
06480523
// Copyright 2015 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/material.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:test/test.dart'
;
import
'test_semantics.dart'
;
void
main
(
)
{
test
(
'Semantics 1'
,
()
{
testWidgets
((
WidgetTester
tester
)
{
TestSemanticsClient
client
=
new
TestSemanticsClient
();
// smoketest
tester
.
pumpWidget
(
new
Container
(
child:
new
Semantics
(
label:
'test1'
,
child:
new
Container
()
)
)
);
expect
(
client
.
updates
.
length
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
id
,
equals
(
0
));
expect
(
client
.
updates
[
0
].
flags
.
canBeTapped
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeLongPressed
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledHorizontally
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledVertically
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
hasCheckedState
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
isChecked
,
isFalse
);
expect
(
client
.
updates
[
0
].
strings
.
label
,
equals
(
'test1'
));
expect
(
client
.
updates
[
0
].
geometry
.
transform
,
isNull
);
expect
(
client
.
updates
[
0
].
geometry
.
left
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
top
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
width
,
equals
(
800.0
));
expect
(
client
.
updates
[
0
].
geometry
.
height
,
equals
(
600.0
));
expect
(
client
.
updates
[
0
].
children
.
length
,
equals
(
0
));
expect
(
client
.
updates
[
1
],
isNull
);
client
.
updates
.
clear
();
// control for forking
tester
.
pumpWidget
(
new
Column
(
children:
<
Widget
>[
new
Container
(
height:
10.0
,
child:
new
Semantics
(
label:
'child1'
)
),
new
Container
(
height:
10.0
,
child:
new
IgnorePointer
(
ignoring:
true
,
child:
new
Semantics
(
label:
'child2'
)
)
),
],
alignItems:
FlexAlignItems
.
stretch
)
);
expect
(
client
.
updates
.
length
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
id
,
equals
(
0
));
expect
(
client
.
updates
[
0
].
flags
.
canBeTapped
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeLongPressed
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledHorizontally
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledVertically
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
hasCheckedState
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
isChecked
,
isFalse
);
expect
(
client
.
updates
[
0
].
strings
.
label
,
equals
(
'child1'
));
expect
(
client
.
updates
[
0
].
geometry
.
transform
,
isNull
);
expect
(
client
.
updates
[
0
].
geometry
.
left
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
top
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
width
,
equals
(
800.0
));
expect
(
client
.
updates
[
0
].
geometry
.
height
,
equals
(
600.0
));
expect
(
client
.
updates
[
0
].
children
.
length
,
equals
(
0
));
expect
(
client
.
updates
[
1
],
isNull
);
client
.
updates
.
clear
();
// forking semantics
tester
.
pumpWidget
(
new
Column
(
children:
<
Widget
>[
new
Container
(
height:
10.0
,
child:
new
Semantics
(
label:
'child1'
)
),
new
Container
(
height:
10.0
,
child:
new
IgnorePointer
(
ignoring:
false
,
child:
new
Semantics
(
label:
'child2'
)
)
),
],
alignItems:
FlexAlignItems
.
stretch
)
);
expect
(
client
.
updates
.
length
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
id
,
equals
(
0
));
expect
(
client
.
updates
[
0
].
flags
.
canBeTapped
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeLongPressed
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledHorizontally
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledVertically
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
hasCheckedState
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
isChecked
,
isFalse
);
expect
(
client
.
updates
[
0
].
strings
.
label
,
equals
(
''
));
expect
(
client
.
updates
[
0
].
geometry
.
transform
,
isNull
);
expect
(
client
.
updates
[
0
].
geometry
.
left
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
top
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
width
,
equals
(
800.0
));
expect
(
client
.
updates
[
0
].
geometry
.
height
,
equals
(
600.0
));
expect
(
client
.
updates
[
0
].
children
.
length
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
children
[
0
].
id
,
equals
(
1
));
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
canBeTapped
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
canBeLongPressed
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
canBeScrolledHorizontally
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
canBeScrolledVertically
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
hasCheckedState
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
isChecked
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
strings
.
label
,
equals
(
'child1'
));
expect
(
client
.
updates
[
0
].
children
[
0
].
geometry
.
transform
,
isNull
);
expect
(
client
.
updates
[
0
].
children
[
0
].
geometry
.
left
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
children
[
0
].
geometry
.
top
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
children
[
0
].
geometry
.
width
,
equals
(
800.0
));
expect
(
client
.
updates
[
0
].
children
[
0
].
geometry
.
height
,
equals
(
10.0
));
expect
(
client
.
updates
[
0
].
children
[
0
].
children
.
length
,
equals
(
0
));
expect
(
client
.
updates
[
0
].
children
[
1
].
id
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
children
[
1
].
flags
.
canBeTapped
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
1
].
flags
.
canBeLongPressed
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
1
].
flags
.
canBeScrolledHorizontally
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
1
].
flags
.
canBeScrolledVertically
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
1
].
flags
.
hasCheckedState
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
1
].
flags
.
isChecked
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
1
].
strings
.
label
,
equals
(
'child2'
));
expect
(
client
.
updates
[
0
].
children
[
1
].
geometry
.
transform
,
equals
([
1.0
,
0.0
,
0.0
,
0.0
,
0.0
,
1.0
,
0.0
,
0.0
,
0.0
,
0.0
,
1.0
,
0.0
,
0.0
,
10.0
,
0.0
,
1.0
]));
expect
(
client
.
updates
[
0
].
children
[
1
].
geometry
.
left
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
children
[
1
].
geometry
.
top
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
children
[
1
].
geometry
.
width
,
equals
(
800.0
));
expect
(
client
.
updates
[
0
].
children
[
1
].
geometry
.
height
,
equals
(
10.0
));
expect
(
client
.
updates
[
0
].
children
[
1
].
children
.
length
,
equals
(
0
));
expect
(
client
.
updates
[
1
],
isNull
);
client
.
updates
.
clear
();
// toggle a branch off
tester
.
pumpWidget
(
new
Column
(
children:
<
Widget
>[
new
Container
(
height:
10.0
,
child:
new
Semantics
(
label:
'child1'
)
),
new
Container
(
height:
10.0
,
child:
new
IgnorePointer
(
ignoring:
true
,
child:
new
Semantics
(
label:
'child2'
)
)
),
],
alignItems:
FlexAlignItems
.
stretch
)
);
expect
(
client
.
updates
.
length
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
id
,
equals
(
0
));
expect
(
client
.
updates
[
0
].
flags
.
canBeTapped
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeLongPressed
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledHorizontally
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledVertically
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
hasCheckedState
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
isChecked
,
isFalse
);
expect
(
client
.
updates
[
0
].
strings
.
label
,
equals
(
'child1'
));
expect
(
client
.
updates
[
0
].
geometry
.
transform
,
isNull
);
expect
(
client
.
updates
[
0
].
geometry
.
left
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
top
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
width
,
equals
(
800.0
));
expect
(
client
.
updates
[
0
].
geometry
.
height
,
equals
(
600.0
));
expect
(
client
.
updates
[
0
].
children
.
length
,
equals
(
0
));
expect
(
client
.
updates
[
1
],
isNull
);
client
.
updates
.
clear
();
// toggle a branch back on
tester
.
pumpWidget
(
new
Column
(
children:
<
Widget
>[
new
Container
(
height:
10.0
,
child:
new
Semantics
(
label:
'child1'
)
),
new
Container
(
height:
10.0
,
child:
new
IgnorePointer
(
ignoring:
false
,
child:
new
Semantics
(
label:
'child2'
)
)
),
],
alignItems:
FlexAlignItems
.
stretch
)
);
expect
(
client
.
updates
.
length
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
id
,
equals
(
0
));
expect
(
client
.
updates
[
0
].
flags
.
canBeTapped
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeLongPressed
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledHorizontally
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledVertically
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
hasCheckedState
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
isChecked
,
isFalse
);
expect
(
client
.
updates
[
0
].
strings
.
label
,
equals
(
''
));
expect
(
client
.
updates
[
0
].
geometry
.
transform
,
isNull
);
expect
(
client
.
updates
[
0
].
geometry
.
left
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
top
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
width
,
equals
(
800.0
));
expect
(
client
.
updates
[
0
].
geometry
.
height
,
equals
(
600.0
));
expect
(
client
.
updates
[
0
].
children
.
length
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
children
[
0
].
id
,
equals
(
3
));
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
canBeTapped
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
canBeLongPressed
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
canBeScrolledHorizontally
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
canBeScrolledVertically
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
hasCheckedState
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
isChecked
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
strings
.
label
,
equals
(
'child1'
));
expect
(
client
.
updates
[
0
].
children
[
0
].
geometry
.
transform
,
isNull
);
expect
(
client
.
updates
[
0
].
children
[
0
].
geometry
.
left
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
children
[
0
].
geometry
.
top
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
children
[
0
].
geometry
.
width
,
equals
(
800.0
));
expect
(
client
.
updates
[
0
].
children
[
0
].
geometry
.
height
,
equals
(
10.0
));
expect
(
client
.
updates
[
0
].
children
[
0
].
children
.
length
,
equals
(
0
));
expect
(
client
.
updates
[
0
].
children
[
1
].
id
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
children
[
1
].
flags
.
canBeTapped
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
1
].
flags
.
canBeLongPressed
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
1
].
flags
.
canBeScrolledHorizontally
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
1
].
flags
.
canBeScrolledVertically
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
1
].
flags
.
hasCheckedState
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
1
].
flags
.
isChecked
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
1
].
strings
.
label
,
equals
(
'child2'
));
expect
(
client
.
updates
[
0
].
children
[
1
].
geometry
.
transform
,
equals
([
1.0
,
0.0
,
0.0
,
0.0
,
0.0
,
1.0
,
0.0
,
0.0
,
0.0
,
0.0
,
1.0
,
0.0
,
0.0
,
10.0
,
0.0
,
1.0
]));
expect
(
client
.
updates
[
0
].
children
[
1
].
geometry
.
left
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
children
[
1
].
geometry
.
top
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
children
[
1
].
geometry
.
width
,
equals
(
800.0
));
expect
(
client
.
updates
[
0
].
children
[
1
].
geometry
.
height
,
equals
(
10.0
));
expect
(
client
.
updates
[
0
].
children
[
1
].
children
.
length
,
equals
(
0
));
expect
(
client
.
updates
[
1
],
isNull
);
client
.
updates
.
clear
();
});
});
}
packages/flutter/test/widget/semantics_2_test.dart
0 → 100644
View file @
06480523
// Copyright 2015 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/material.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:test/test.dart'
;
import
'test_semantics.dart'
;
void
main
(
)
{
test
(
'Semantics 2'
,
()
{
testWidgets
((
WidgetTester
tester
)
{
TestSemanticsClient
client
=
new
TestSemanticsClient
();
// this test is the same as the test in Semantics 1, but
// starting with the second branch being ignored and then
// switching to not ignoring it.
// forking semantics
tester
.
pumpWidget
(
new
Column
(
children:
<
Widget
>[
new
Container
(
height:
10.0
,
child:
new
Semantics
(
label:
'child1'
)
),
new
Container
(
height:
10.0
,
child:
new
IgnorePointer
(
ignoring:
false
,
child:
new
Semantics
(
label:
'child2'
)
)
),
],
alignItems:
FlexAlignItems
.
stretch
)
);
expect
(
client
.
updates
.
length
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
id
,
equals
(
0
));
expect
(
client
.
updates
[
0
].
flags
.
canBeTapped
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeLongPressed
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledHorizontally
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledVertically
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
hasCheckedState
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
isChecked
,
isFalse
);
expect
(
client
.
updates
[
0
].
strings
.
label
,
equals
(
''
));
expect
(
client
.
updates
[
0
].
geometry
.
transform
,
isNull
);
expect
(
client
.
updates
[
0
].
geometry
.
left
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
top
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
width
,
equals
(
800.0
));
expect
(
client
.
updates
[
0
].
geometry
.
height
,
equals
(
600.0
));
expect
(
client
.
updates
[
0
].
children
.
length
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
children
[
0
].
id
,
equals
(
1
));
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
canBeTapped
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
canBeLongPressed
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
canBeScrolledHorizontally
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
canBeScrolledVertically
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
hasCheckedState
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
isChecked
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
strings
.
label
,
equals
(
'child1'
));
expect
(
client
.
updates
[
0
].
children
[
0
].
geometry
.
transform
,
isNull
);
expect
(
client
.
updates
[
0
].
children
[
0
].
geometry
.
left
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
children
[
0
].
geometry
.
top
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
children
[
0
].
geometry
.
width
,
equals
(
800.0
));
expect
(
client
.
updates
[
0
].
children
[
0
].
geometry
.
height
,
equals
(
10.0
));
expect
(
client
.
updates
[
0
].
children
[
0
].
children
.
length
,
equals
(
0
));
expect
(
client
.
updates
[
0
].
children
[
1
].
id
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
children
[
1
].
flags
.
canBeTapped
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
1
].
flags
.
canBeLongPressed
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
1
].
flags
.
canBeScrolledHorizontally
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
1
].
flags
.
canBeScrolledVertically
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
1
].
flags
.
hasCheckedState
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
1
].
flags
.
isChecked
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
1
].
strings
.
label
,
equals
(
'child2'
));
expect
(
client
.
updates
[
0
].
children
[
1
].
geometry
.
transform
,
equals
([
1.0
,
0.0
,
0.0
,
0.0
,
0.0
,
1.0
,
0.0
,
0.0
,
0.0
,
0.0
,
1.0
,
0.0
,
0.0
,
10.0
,
0.0
,
1.0
]));
expect
(
client
.
updates
[
0
].
children
[
1
].
geometry
.
left
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
children
[
1
].
geometry
.
top
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
children
[
1
].
geometry
.
width
,
equals
(
800.0
));
expect
(
client
.
updates
[
0
].
children
[
1
].
geometry
.
height
,
equals
(
10.0
));
expect
(
client
.
updates
[
0
].
children
[
1
].
children
.
length
,
equals
(
0
));
expect
(
client
.
updates
[
1
],
isNull
);
client
.
updates
.
clear
();
// toggle a branch off
tester
.
pumpWidget
(
new
Column
(
children:
<
Widget
>[
new
Container
(
height:
10.0
,
child:
new
Semantics
(
label:
'child1'
)
),
new
Container
(
height:
10.0
,
child:
new
IgnorePointer
(
ignoring:
true
,
child:
new
Semantics
(
label:
'child2'
)
)
),
],
alignItems:
FlexAlignItems
.
stretch
)
);
expect
(
client
.
updates
.
length
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
id
,
equals
(
0
));
expect
(
client
.
updates
[
0
].
flags
.
canBeTapped
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeLongPressed
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledHorizontally
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledVertically
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
hasCheckedState
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
isChecked
,
isFalse
);
expect
(
client
.
updates
[
0
].
strings
.
label
,
equals
(
'child1'
));
expect
(
client
.
updates
[
0
].
geometry
.
transform
,
isNull
);
expect
(
client
.
updates
[
0
].
geometry
.
left
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
top
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
width
,
equals
(
800.0
));
expect
(
client
.
updates
[
0
].
geometry
.
height
,
equals
(
600.0
));
expect
(
client
.
updates
[
0
].
children
.
length
,
equals
(
0
));
expect
(
client
.
updates
[
1
],
isNull
);
client
.
updates
.
clear
();
// toggle a branch back on
tester
.
pumpWidget
(
new
Column
(
children:
<
Widget
>[
new
Container
(
height:
10.0
,
child:
new
Semantics
(
label:
'child1'
)
),
new
Container
(
height:
10.0
,
child:
new
IgnorePointer
(
ignoring:
false
,
child:
new
Semantics
(
label:
'child2'
)
)
),
],
alignItems:
FlexAlignItems
.
stretch
)
);
expect
(
client
.
updates
.
length
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
id
,
equals
(
0
));
expect
(
client
.
updates
[
0
].
flags
.
canBeTapped
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeLongPressed
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledHorizontally
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledVertically
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
hasCheckedState
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
isChecked
,
isFalse
);
expect
(
client
.
updates
[
0
].
strings
.
label
,
equals
(
''
));
expect
(
client
.
updates
[
0
].
geometry
.
transform
,
isNull
);
expect
(
client
.
updates
[
0
].
geometry
.
left
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
top
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
width
,
equals
(
800.0
));
expect
(
client
.
updates
[
0
].
geometry
.
height
,
equals
(
600.0
));
expect
(
client
.
updates
[
0
].
children
.
length
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
children
[
0
].
id
,
equals
(
3
));
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
canBeTapped
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
canBeLongPressed
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
canBeScrolledHorizontally
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
canBeScrolledVertically
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
hasCheckedState
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
isChecked
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
strings
.
label
,
equals
(
'child1'
));
expect
(
client
.
updates
[
0
].
children
[
0
].
geometry
.
transform
,
isNull
);
expect
(
client
.
updates
[
0
].
children
[
0
].
geometry
.
left
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
children
[
0
].
geometry
.
top
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
children
[
0
].
geometry
.
width
,
equals
(
800.0
));
expect
(
client
.
updates
[
0
].
children
[
0
].
geometry
.
height
,
equals
(
10.0
));
expect
(
client
.
updates
[
0
].
children
[
0
].
children
.
length
,
equals
(
0
));
expect
(
client
.
updates
[
0
].
children
[
1
].
id
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
children
[
1
].
flags
.
canBeTapped
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
1
].
flags
.
canBeLongPressed
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
1
].
flags
.
canBeScrolledHorizontally
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
1
].
flags
.
canBeScrolledVertically
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
1
].
flags
.
hasCheckedState
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
1
].
flags
.
isChecked
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
1
].
strings
.
label
,
equals
(
'child2'
));
expect
(
client
.
updates
[
0
].
children
[
1
].
geometry
.
transform
,
equals
([
1.0
,
0.0
,
0.0
,
0.0
,
0.0
,
1.0
,
0.0
,
0.0
,
0.0
,
0.0
,
1.0
,
0.0
,
0.0
,
10.0
,
0.0
,
1.0
]));
expect
(
client
.
updates
[
0
].
children
[
1
].
geometry
.
left
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
children
[
1
].
geometry
.
top
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
children
[
1
].
geometry
.
width
,
equals
(
800.0
));
expect
(
client
.
updates
[
0
].
children
[
1
].
geometry
.
height
,
equals
(
10.0
));
expect
(
client
.
updates
[
0
].
children
[
1
].
children
.
length
,
equals
(
0
));
expect
(
client
.
updates
[
1
],
isNull
);
client
.
updates
.
clear
();
});
});
}
packages/flutter/test/widget/semantics_3_test.dart
0 → 100644
View file @
06480523
// Copyright 2015 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/material.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:test/test.dart'
;
import
'test_semantics.dart'
;
void
main
(
)
{
test
(
'Semantics 3'
,
()
{
testWidgets
((
WidgetTester
tester
)
{
TestSemanticsClient
client
=
new
TestSemanticsClient
();
// implicit annotators
tester
.
pumpWidget
(
new
Container
(
child:
new
Semantics
(
label:
'test'
,
child:
new
Container
(
child:
new
Semantics
(
checked:
true
)
)
)
)
);
expect
(
client
.
updates
.
length
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
id
,
equals
(
0
));
expect
(
client
.
updates
[
0
].
flags
.
canBeTapped
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeLongPressed
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledHorizontally
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledVertically
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
hasCheckedState
,
isTrue
);
expect
(
client
.
updates
[
0
].
flags
.
isChecked
,
isTrue
);
expect
(
client
.
updates
[
0
].
strings
.
label
,
equals
(
'test'
));
expect
(
client
.
updates
[
0
].
geometry
.
transform
,
isNull
);
expect
(
client
.
updates
[
0
].
geometry
.
left
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
top
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
width
,
equals
(
800.0
));
expect
(
client
.
updates
[
0
].
geometry
.
height
,
equals
(
600.0
));
expect
(
client
.
updates
[
0
].
children
.
length
,
equals
(
0
));
expect
(
client
.
updates
[
1
],
isNull
);
client
.
updates
.
clear
();
// remove one
tester
.
pumpWidget
(
new
Container
(
child:
new
Container
(
child:
new
Semantics
(
checked:
true
)
)
)
);
expect
(
client
.
updates
.
length
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
id
,
equals
(
0
));
expect
(
client
.
updates
[
0
].
flags
.
canBeTapped
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeLongPressed
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledHorizontally
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledVertically
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
hasCheckedState
,
isTrue
);
expect
(
client
.
updates
[
0
].
flags
.
isChecked
,
isTrue
);
expect
(
client
.
updates
[
0
].
strings
.
label
,
equals
(
''
));
expect
(
client
.
updates
[
0
].
geometry
.
transform
,
isNull
);
expect
(
client
.
updates
[
0
].
geometry
.
left
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
top
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
width
,
equals
(
800.0
));
expect
(
client
.
updates
[
0
].
geometry
.
height
,
equals
(
600.0
));
expect
(
client
.
updates
[
0
].
children
.
length
,
equals
(
0
));
expect
(
client
.
updates
[
1
],
isNull
);
client
.
updates
.
clear
();
// change what it says
tester
.
pumpWidget
(
new
Container
(
child:
new
Container
(
child:
new
Semantics
(
label:
'test'
)
)
)
);
expect
(
client
.
updates
.
length
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
id
,
equals
(
0
));
expect
(
client
.
updates
[
0
].
flags
.
canBeTapped
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeLongPressed
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledHorizontally
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledVertically
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
hasCheckedState
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
isChecked
,
isFalse
);
expect
(
client
.
updates
[
0
].
strings
.
label
,
equals
(
'test'
));
expect
(
client
.
updates
[
0
].
geometry
.
transform
,
isNull
);
expect
(
client
.
updates
[
0
].
geometry
.
left
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
top
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
width
,
equals
(
800.0
));
expect
(
client
.
updates
[
0
].
geometry
.
height
,
equals
(
600.0
));
expect
(
client
.
updates
[
0
].
children
.
length
,
equals
(
0
));
expect
(
client
.
updates
[
1
],
isNull
);
client
.
updates
.
clear
();
// add a node
tester
.
pumpWidget
(
new
Container
(
child:
new
Semantics
(
checked:
true
,
child:
new
Container
(
child:
new
Semantics
(
label:
'test'
)
)
)
)
);
expect
(
client
.
updates
.
length
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
id
,
equals
(
0
));
expect
(
client
.
updates
[
0
].
flags
.
canBeTapped
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeLongPressed
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledHorizontally
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledVertically
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
hasCheckedState
,
isTrue
);
expect
(
client
.
updates
[
0
].
flags
.
isChecked
,
isTrue
);
expect
(
client
.
updates
[
0
].
strings
.
label
,
equals
(
'test'
));
expect
(
client
.
updates
[
0
].
geometry
.
transform
,
isNull
);
expect
(
client
.
updates
[
0
].
geometry
.
left
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
top
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
width
,
equals
(
800.0
));
expect
(
client
.
updates
[
0
].
geometry
.
height
,
equals
(
600.0
));
expect
(
client
.
updates
[
0
].
children
.
length
,
equals
(
0
));
expect
(
client
.
updates
[
1
],
isNull
);
client
.
updates
.
clear
();
// make no changes
tester
.
pumpWidget
(
new
Container
(
child:
new
Semantics
(
checked:
true
,
child:
new
Container
(
child:
new
Semantics
(
label:
'test'
)
)
)
)
);
expect
(
client
.
updates
.
length
,
equals
(
0
));
});
});
}
packages/flutter/test/widget/semantics_4_test.dart
0 → 100644
View file @
06480523
// Copyright 2015 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/material.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:test/test.dart'
;
import
'test_semantics.dart'
;
void
main
(
)
{
test
(
'Semantics 4'
,
()
{
testWidgets
((
WidgetTester
tester
)
{
TestSemanticsClient
client
=
new
TestSemanticsClient
();
// O
// / \ O=root
// L L L=node with label
// / \ C=node with checked
// C C* *=node removed next pass
//
tester
.
pumpWidget
(
new
Stack
(
children:
<
Widget
>[
new
Semantics
(
label:
'L1'
),
new
Semantics
(
label:
'L2'
,
child:
new
Stack
(
children:
<
Widget
>[
new
Semantics
(
checked:
true
),
new
Semantics
(
checked:
false
)
]
)
)
]
)
);
expect
(
client
.
updates
.
length
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
id
,
equals
(
0
));
expect
(
client
.
updates
[
0
].
children
.
length
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
children
[
0
].
id
,
equals
(
1
));
expect
(
client
.
updates
[
0
].
children
[
0
].
children
.
length
,
equals
(
0
));
expect
(
client
.
updates
[
0
].
children
[
1
].
id
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
children
[
1
].
children
.
length
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
children
[
1
].
children
[
0
].
id
,
equals
(
3
));
expect
(
client
.
updates
[
0
].
children
[
1
].
children
[
0
].
children
.
length
,
equals
(
0
));
expect
(
client
.
updates
[
0
].
children
[
1
].
children
[
1
].
id
,
equals
(
4
));
expect
(
client
.
updates
[
0
].
children
[
1
].
children
[
1
].
children
.
length
,
equals
(
0
));
expect
(
client
.
updates
[
1
],
isNull
);
client
.
updates
.
clear
();
// O O=root
// / \ L=node with label
// L* LC C=node with checked
// *=node removed next pass
//
tester
.
pumpWidget
(
new
Stack
(
children:
<
Widget
>[
new
Semantics
(
label:
'L1'
),
new
Semantics
(
label:
'L2'
,
child:
new
Stack
(
children:
<
Widget
>[
new
Semantics
(
checked:
true
),
new
Semantics
()
]
)
)
]
)
);
expect
(
client
.
updates
.
length
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
id
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
children
.
length
,
equals
(
0
));
expect
(
client
.
updates
[
1
],
isNull
);
client
.
updates
.
clear
();
// O=root
// OLC L=node with label
// C=node with checked
//
tester
.
pumpWidget
(
new
Stack
(
children:
<
Widget
>[
new
Semantics
(),
new
Semantics
(
label:
'L2'
,
child:
new
Stack
(
children:
<
Widget
>[
new
Semantics
(
checked:
true
),
new
Semantics
()
]
)
)
]
)
);
expect
(
client
.
updates
.
length
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
id
,
equals
(
0
));
expect
(
client
.
updates
[
0
].
children
.
length
,
equals
(
0
));
expect
(
client
.
updates
[
1
],
isNull
);
client
.
updates
.
clear
();
});
});
}
packages/flutter/test/widget/semantics_5_test.dart
0 → 100644
View file @
06480523
// Copyright 2015 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/material.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:test/test.dart'
;
import
'test_semantics.dart'
;
void
main
(
)
{
test
(
'Semantics 5'
,
()
{
testWidgets
((
WidgetTester
tester
)
{
TestSemanticsClient
client
=
new
TestSemanticsClient
();
tester
.
pumpWidget
(
new
Stack
(
children:
<
Widget
>[
new
Semantics
(
// this tests that empty nodes disappear
),
new
Semantics
(
// this tests whether you can have a container with no other semantics
container:
true
),
new
Semantics
(
label:
'label'
// (force a fork)
),
]
)
);
expect
(
client
.
updates
.
length
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
id
,
equals
(
0
));
expect
(
client
.
updates
[
0
].
flags
.
hasCheckedState
,
isFalse
);
expect
(
client
.
updates
[
0
].
strings
.
label
,
equals
(
''
));
expect
(
client
.
updates
[
0
].
children
.
length
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
children
[
0
].
id
,
equals
(
1
));
expect
(
client
.
updates
[
0
].
children
[
0
].
flags
.
hasCheckedState
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
0
].
strings
.
label
,
equals
(
''
));
expect
(
client
.
updates
[
0
].
children
[
1
].
id
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
children
[
1
].
flags
.
hasCheckedState
,
isFalse
);
expect
(
client
.
updates
[
0
].
children
[
1
].
strings
.
label
,
equals
(
'label'
));
expect
(
client
.
updates
[
1
],
isNull
);
client
.
updates
.
clear
();
});
});
}
packages/flutter/test/widget/semantics_6_test.dart
0 → 100644
View file @
06480523
// Copyright 2015 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/material.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:test/test.dart'
;
void
main
(
)
{
test
(
'Semantics 6 - SemanticsDebugger smoke test'
,
()
{
testWidgets
((
WidgetTester
tester
)
{
// This is a smoketest to verify that adding a debugger doesn't crash.
tester
.
pumpWidget
(
new
Stack
(
children:
<
Widget
>[
new
Semantics
(),
new
Semantics
(
container:
true
),
new
Semantics
(
label:
'label'
),
]
)
);
tester
.
pumpWidget
(
new
SemanticsDebugger
(
child:
new
Stack
(
children:
<
Widget
>[
new
Semantics
(),
new
Semantics
(
container:
true
),
new
Semantics
(
label:
'label'
),
]
)
)
);
expect
(
true
,
isTrue
);
// expect that we reach here without crashing
});
});
}
packages/flutter/test/widget/test_semantics.dart
0 → 100644
View file @
06480523
// Copyright 2015 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/rendering.dart'
;
import
'package:sky_services/semantics/semantics.mojom.dart'
as
engine
;
class
TestSemanticsClient
implements
engine
.
SemanticsClient
{
TestSemanticsClient
()
{
Renderer
.
instance
.
setSemanticsClient
(
this
);
}
final
List
<
engine
.
SemanticsNode
>
updates
=
<
engine
.
SemanticsNode
>[];
updateSemanticsTree
(
List
<
engine
.
SemanticsNode
>
nodes
)
{
assert
(!
nodes
.
any
((
engine
.
SemanticsNode
node
)
=>
node
==
null
));
updates
.
addAll
(
nodes
);
updates
.
add
(
null
);
}
}
packages/flutter/test/widget/tooltip_test.dart
View file @
06480523
...
...
@@ -8,6 +8,8 @@ import 'package:flutter/widgets.dart';
import
'package:flutter_test/flutter_test.dart'
;
import
'package:test/test.dart'
;
import
'test_semantics.dart'
;
void
main
(
)
{
test
(
'Does tooltip end up in the right place - top left'
,
()
{
testWidgets
((
WidgetTester
tester
)
{
...
...
@@ -354,4 +356,74 @@ void main() {
expect
(
tip
.
localToGlobal
(
tip
.
size
.
bottomRight
(
Point
.
origin
)).
y
,
equals
(
320.0
));
});
});
test
(
'Does tooltip contribute semantics'
,
()
{
testWidgets
((
WidgetTester
tester
)
{
TestSemanticsClient
client
=
new
TestSemanticsClient
();
GlobalKey
key
=
new
GlobalKey
();
tester
.
pumpWidget
(
new
Overlay
(
initialEntries:
<
OverlayEntry
>[
new
OverlayEntry
(
builder:
(
BuildContext
context
)
{
return
new
Stack
(
children:
<
Widget
>[
new
Positioned
(
left:
780.0
,
top:
300.0
,
child:
new
Tooltip
(
key:
key
,
message:
'TIP'
,
fadeDuration:
const
Duration
(
seconds:
1
),
showDuration:
const
Duration
(
seconds:
2
),
child:
new
Container
(
width:
0.0
,
height:
0.0
)
)
),
]
);
}
),
]
)
);
expect
(
client
.
updates
.
length
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
id
,
equals
(
0
));
expect
(
client
.
updates
[
0
].
flags
.
canBeTapped
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeLongPressed
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledHorizontally
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledVertically
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
hasCheckedState
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
isChecked
,
isFalse
);
expect
(
client
.
updates
[
0
].
strings
.
label
,
equals
(
'TIP'
));
expect
(
client
.
updates
[
0
].
geometry
.
transform
,
isNull
);
expect
(
client
.
updates
[
0
].
geometry
.
left
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
top
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
width
,
equals
(
800.0
));
expect
(
client
.
updates
[
0
].
geometry
.
height
,
equals
(
600.0
));
expect
(
client
.
updates
[
0
].
children
.
length
,
equals
(
0
));
expect
(
client
.
updates
[
1
],
isNull
);
client
.
updates
.
clear
();
key
.
currentState
.
showTooltip
();
// this triggers a rebuild of the semantics because the tree changes
tester
.
pump
(
const
Duration
(
seconds:
2
));
// faded in, show timer started (and at 0.0)
expect
(
client
.
updates
.
length
,
equals
(
2
));
expect
(
client
.
updates
[
0
].
id
,
equals
(
0
));
expect
(
client
.
updates
[
0
].
flags
.
canBeTapped
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeLongPressed
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledHorizontally
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
canBeScrolledVertically
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
hasCheckedState
,
isFalse
);
expect
(
client
.
updates
[
0
].
flags
.
isChecked
,
isFalse
);
expect
(
client
.
updates
[
0
].
strings
.
label
,
equals
(
'TIP'
));
expect
(
client
.
updates
[
0
].
geometry
.
transform
,
isNull
);
expect
(
client
.
updates
[
0
].
geometry
.
left
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
top
,
equals
(
0.0
));
expect
(
client
.
updates
[
0
].
geometry
.
width
,
equals
(
800.0
));
expect
(
client
.
updates
[
0
].
geometry
.
height
,
equals
(
600.0
));
expect
(
client
.
updates
[
0
].
children
.
length
,
equals
(
0
));
expect
(
client
.
updates
[
1
],
isNull
);
client
.
updates
.
clear
();
});
});
}
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