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
6ab2445e
Unverified
Commit
6ab2445e
authored
5 years ago
by
Michael Goderbauer
Committed by
GitHub
5 years ago
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix Textfields in Semantics Debugger (#37158)
parent
d82bca2a
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
249 additions
and
137 deletions
+249
-137
semantics_debugger.dart
packages/flutter/lib/src/widgets/semantics_debugger.dart
+140
-137
semantics_debugger_test.dart
packages/flutter/test/widgets/semantics_debugger_test.dart
+109
-0
No files found.
packages/flutter/lib/src/widgets/semantics_debugger.dart
View file @
6ab2445e
...
...
@@ -194,143 +194,6 @@ class _SemanticsClient extends ChangeNotifier {
}
}
String
_getMessage
(
SemanticsNode
node
)
{
final
SemanticsData
data
=
node
.
getSemanticsData
();
final
List
<
String
>
annotations
=
<
String
>[];
bool
wantsTap
=
false
;
if
(
data
.
hasFlag
(
SemanticsFlag
.
hasCheckedState
))
{
annotations
.
add
(
data
.
hasFlag
(
SemanticsFlag
.
isChecked
)
?
'checked'
:
'unchecked'
);
wantsTap
=
true
;
}
if
(
data
.
hasAction
(
SemanticsAction
.
tap
))
{
if
(!
wantsTap
)
annotations
.
add
(
'button'
);
}
else
{
if
(
wantsTap
)
annotations
.
add
(
'disabled'
);
}
if
(
data
.
hasAction
(
SemanticsAction
.
longPress
))
annotations
.
add
(
'long-pressable'
);
final
bool
isScrollable
=
data
.
hasAction
(
SemanticsAction
.
scrollLeft
)
||
data
.
hasAction
(
SemanticsAction
.
scrollRight
)
||
data
.
hasAction
(
SemanticsAction
.
scrollUp
)
||
data
.
hasAction
(
SemanticsAction
.
scrollDown
);
final
bool
isAdjustable
=
data
.
hasAction
(
SemanticsAction
.
increase
)
||
data
.
hasAction
(
SemanticsAction
.
decrease
);
if
(
isScrollable
)
annotations
.
add
(
'scrollable'
);
if
(
isAdjustable
)
annotations
.
add
(
'adjustable'
);
assert
(
data
.
label
!=
null
);
String
message
;
if
(
data
.
label
.
isEmpty
)
{
message
=
annotations
.
join
(
'; '
);
}
else
{
String
label
;
if
(
data
.
textDirection
==
null
)
{
label
=
'
${Unicode.FSI}${data.label}${Unicode.PDI}
'
;
annotations
.
insert
(
0
,
'MISSING TEXT DIRECTION'
);
}
else
{
switch
(
data
.
textDirection
)
{
case
TextDirection
.
rtl
:
label
=
'
${Unicode.RLI}${data.label}${Unicode.PDF}
'
;
break
;
case
TextDirection
.
ltr
:
label
=
data
.
label
;
break
;
}
}
if
(
annotations
.
isEmpty
)
{
message
=
label
;
}
else
{
message
=
'
$label
(
${annotations.join('; ')}
)'
;
}
}
return
message
.
trim
();
}
const
TextStyle
_messageStyle
=
TextStyle
(
color:
Color
(
0xFF000000
),
fontSize:
10.0
,
height:
0.8
,
);
void
_paintMessage
(
Canvas
canvas
,
SemanticsNode
node
)
{
final
String
message
=
_getMessage
(
node
);
if
(
message
.
isEmpty
)
return
;
final
Rect
rect
=
node
.
rect
;
canvas
.
save
();
canvas
.
clipRect
(
rect
);
final
TextPainter
textPainter
=
TextPainter
()
..
text
=
TextSpan
(
style:
_messageStyle
,
text:
message
,
)
..
textDirection
=
TextDirection
.
ltr
// _getMessage always returns LTR text, even if node.label is RTL
..
textAlign
=
TextAlign
.
center
..
layout
(
maxWidth:
rect
.
width
);
textPainter
.
paint
(
canvas
,
Alignment
.
center
.
inscribe
(
textPainter
.
size
,
rect
).
topLeft
);
canvas
.
restore
();
}
int
_findDepth
(
SemanticsNode
node
)
{
if
(!
node
.
hasChildren
||
node
.
mergeAllDescendantsIntoThisNode
)
return
1
;
int
childrenDepth
=
0
;
node
.
visitChildren
((
SemanticsNode
child
)
{
childrenDepth
=
math
.
max
(
childrenDepth
,
_findDepth
(
child
));
return
true
;
});
return
childrenDepth
+
1
;
}
void
_paint
(
Canvas
canvas
,
SemanticsNode
node
,
int
rank
)
{
canvas
.
save
();
if
(
node
.
transform
!=
null
)
canvas
.
transform
(
node
.
transform
.
storage
);
final
Rect
rect
=
node
.
rect
;
if
(!
rect
.
isEmpty
)
{
final
Color
lineColor
=
Color
(
0xFF000000
+
math
.
Random
(
node
.
id
).
nextInt
(
0xFFFFFF
));
final
Rect
innerRect
=
rect
.
deflate
(
rank
*
1.0
);
if
(
innerRect
.
isEmpty
)
{
final
Paint
fill
=
Paint
()
..
color
=
lineColor
..
style
=
PaintingStyle
.
fill
;
canvas
.
drawRect
(
rect
,
fill
);
}
else
{
final
Paint
fill
=
Paint
()
..
color
=
const
Color
(
0xFFFFFFFF
)
..
style
=
PaintingStyle
.
fill
;
canvas
.
drawRect
(
rect
,
fill
);
final
Paint
line
=
Paint
()
..
strokeWidth
=
rank
*
2.0
..
color
=
lineColor
..
style
=
PaintingStyle
.
stroke
;
canvas
.
drawRect
(
innerRect
,
line
);
}
_paintMessage
(
canvas
,
node
);
}
if
(!
node
.
mergeAllDescendantsIntoThisNode
)
{
final
int
childRank
=
rank
-
1
;
node
.
visitChildren
((
SemanticsNode
child
)
{
_paint
(
canvas
,
child
,
childRank
);
return
true
;
});
}
canvas
.
restore
();
}
class
_SemanticsDebuggerPainter
extends
CustomPainter
{
const
_SemanticsDebuggerPainter
(
this
.
owner
,
this
.
generation
,
this
.
pointerPosition
,
this
.
devicePixelRatio
);
...
...
@@ -364,4 +227,144 @@ class _SemanticsDebuggerPainter extends CustomPainter {
||
generation
!=
oldDelegate
.
generation
||
pointerPosition
!=
oldDelegate
.
pointerPosition
;
}
@visibleForTesting
String
getMessage
(
SemanticsNode
node
)
{
final
SemanticsData
data
=
node
.
getSemanticsData
();
final
List
<
String
>
annotations
=
<
String
>[];
bool
wantsTap
=
false
;
if
(
data
.
hasFlag
(
SemanticsFlag
.
hasCheckedState
))
{
annotations
.
add
(
data
.
hasFlag
(
SemanticsFlag
.
isChecked
)
?
'checked'
:
'unchecked'
);
wantsTap
=
true
;
}
if
(
data
.
hasFlag
(
SemanticsFlag
.
isTextField
))
{
annotations
.
add
(
'textfield'
);
wantsTap
=
true
;
}
if
(
data
.
hasAction
(
SemanticsAction
.
tap
))
{
if
(!
wantsTap
)
annotations
.
add
(
'button'
);
}
else
{
if
(
wantsTap
)
annotations
.
add
(
'disabled'
);
}
if
(
data
.
hasAction
(
SemanticsAction
.
longPress
))
annotations
.
add
(
'long-pressable'
);
final
bool
isScrollable
=
data
.
hasAction
(
SemanticsAction
.
scrollLeft
)
||
data
.
hasAction
(
SemanticsAction
.
scrollRight
)
||
data
.
hasAction
(
SemanticsAction
.
scrollUp
)
||
data
.
hasAction
(
SemanticsAction
.
scrollDown
);
final
bool
isAdjustable
=
data
.
hasAction
(
SemanticsAction
.
increase
)
||
data
.
hasAction
(
SemanticsAction
.
decrease
);
if
(
isScrollable
)
annotations
.
add
(
'scrollable'
);
if
(
isAdjustable
)
annotations
.
add
(
'adjustable'
);
assert
(
data
.
label
!=
null
);
String
message
;
if
(
data
.
label
.
isEmpty
)
{
message
=
annotations
.
join
(
'; '
);
}
else
{
String
label
;
if
(
data
.
textDirection
==
null
)
{
label
=
'
${Unicode.FSI}${data.label}${Unicode.PDI}
'
;
annotations
.
insert
(
0
,
'MISSING TEXT DIRECTION'
);
}
else
{
switch
(
data
.
textDirection
)
{
case
TextDirection
.
rtl
:
label
=
'
${Unicode.RLI}${data.label}${Unicode.PDF}
'
;
break
;
case
TextDirection
.
ltr
:
label
=
data
.
label
;
break
;
}
}
if
(
annotations
.
isEmpty
)
{
message
=
label
;
}
else
{
message
=
'
$label
(
${annotations.join('; ')}
)'
;
}
}
return
message
.
trim
();
}
void
_paintMessage
(
Canvas
canvas
,
SemanticsNode
node
)
{
final
String
message
=
getMessage
(
node
);
if
(
message
.
isEmpty
)
return
;
final
Rect
rect
=
node
.
rect
;
canvas
.
save
();
canvas
.
clipRect
(
rect
);
final
TextPainter
textPainter
=
TextPainter
()
..
text
=
TextSpan
(
style:
const
TextStyle
(
color:
Color
(
0xFF000000
),
fontSize:
10.0
,
height:
0.8
,
),
text:
message
,
)
..
textDirection
=
TextDirection
.
ltr
// _getMessage always returns LTR text, even if node.label is RTL
..
textAlign
=
TextAlign
.
center
..
layout
(
maxWidth:
rect
.
width
);
textPainter
.
paint
(
canvas
,
Alignment
.
center
.
inscribe
(
textPainter
.
size
,
rect
).
topLeft
);
canvas
.
restore
();
}
int
_findDepth
(
SemanticsNode
node
)
{
if
(!
node
.
hasChildren
||
node
.
mergeAllDescendantsIntoThisNode
)
return
1
;
int
childrenDepth
=
0
;
node
.
visitChildren
((
SemanticsNode
child
)
{
childrenDepth
=
math
.
max
(
childrenDepth
,
_findDepth
(
child
));
return
true
;
});
return
childrenDepth
+
1
;
}
void
_paint
(
Canvas
canvas
,
SemanticsNode
node
,
int
rank
)
{
canvas
.
save
();
if
(
node
.
transform
!=
null
)
canvas
.
transform
(
node
.
transform
.
storage
);
final
Rect
rect
=
node
.
rect
;
if
(!
rect
.
isEmpty
)
{
final
Color
lineColor
=
Color
(
0xFF000000
+
math
.
Random
(
node
.
id
).
nextInt
(
0xFFFFFF
));
final
Rect
innerRect
=
rect
.
deflate
(
rank
*
1.0
);
if
(
innerRect
.
isEmpty
)
{
final
Paint
fill
=
Paint
()
..
color
=
lineColor
..
style
=
PaintingStyle
.
fill
;
canvas
.
drawRect
(
rect
,
fill
);
}
else
{
final
Paint
fill
=
Paint
()
..
color
=
const
Color
(
0xFFFFFFFF
)
..
style
=
PaintingStyle
.
fill
;
canvas
.
drawRect
(
rect
,
fill
);
final
Paint
line
=
Paint
()
..
strokeWidth
=
rank
*
2.0
..
color
=
lineColor
..
style
=
PaintingStyle
.
stroke
;
canvas
.
drawRect
(
innerRect
,
line
);
}
_paintMessage
(
canvas
,
node
);
}
if
(!
node
.
mergeAllDescendantsIntoThisNode
)
{
final
int
childRank
=
rank
-
1
;
node
.
visitChildren
((
SemanticsNode
child
)
{
_paint
(
canvas
,
child
,
childRank
);
return
true
;
});
}
canvas
.
restore
();
}
}
This diff is collapsed.
Click to expand it.
packages/flutter/test/widgets/semantics_debugger_test.dart
View file @
6ab2445e
...
...
@@ -362,4 +362,113 @@ void main() {
expect
(
valueTop
,
isFalse
);
expect
(
valueTop
,
isFalse
);
});
testWidgets
(
'SemanticsDebugger checkbox message'
,
(
WidgetTester
tester
)
async
{
final
Key
checkbox
=
UniqueKey
();
final
Key
checkboxUnchecked
=
UniqueKey
();
final
Key
checkboxDisabled
=
UniqueKey
();
final
Key
checkboxDisabledUnchecked
=
UniqueKey
();
final
Key
debugger
=
UniqueKey
();
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
SemanticsDebugger
(
key:
debugger
,
child:
Material
(
child:
ListView
(
children:
<
Widget
>[
Semantics
(
container:
true
,
key:
checkbox
,
child:
Checkbox
(
value:
true
,
onChanged:
(
bool
_
)
{
},
),
),
Semantics
(
container:
true
,
key:
checkboxUnchecked
,
child:
Checkbox
(
value:
false
,
onChanged:
(
bool
_
)
{
},
),
),
Semantics
(
container:
true
,
key:
checkboxDisabled
,
child:
const
Checkbox
(
value:
true
,
onChanged:
null
,
),
),
Semantics
(
container:
true
,
key:
checkboxDisabledUnchecked
,
child:
const
Checkbox
(
value:
false
,
onChanged:
null
,
),
),
],
),
),
),
),
);
expect
(
_getMessageShownInSemanticsDebugger
(
widgetKey:
checkbox
,
debuggerKey:
debugger
,
tester:
tester
),
'checked'
,
);
expect
(
_getMessageShownInSemanticsDebugger
(
widgetKey:
checkboxUnchecked
,
debuggerKey:
debugger
,
tester:
tester
),
'unchecked'
,
);
expect
(
_getMessageShownInSemanticsDebugger
(
widgetKey:
checkboxDisabled
,
debuggerKey:
debugger
,
tester:
tester
),
'checked; disabled'
,
);
expect
(
_getMessageShownInSemanticsDebugger
(
widgetKey:
checkboxDisabledUnchecked
,
debuggerKey:
debugger
,
tester:
tester
),
'unchecked; disabled'
,
);
});
testWidgets
(
'SemanticsDebugger textfield'
,
(
WidgetTester
tester
)
async
{
final
UniqueKey
textField
=
UniqueKey
();
final
UniqueKey
debugger
=
UniqueKey
();
await
tester
.
pumpWidget
(
MaterialApp
(
home:
SemanticsDebugger
(
key:
debugger
,
child:
Material
(
child:
TextField
(
key:
textField
,
),
),
),
),
);
expect
(
_getMessageShownInSemanticsDebugger
(
widgetKey:
textField
,
debuggerKey:
debugger
,
tester:
tester
),
'textfield'
,
);
});
}
String
_getMessageShownInSemanticsDebugger
(
{
@required
Key
widgetKey
,
@required
Key
debuggerKey
,
@required
WidgetTester
tester
,
})
{
final
CustomPaint
customPaint
=
tester
.
widgetList
(
find
.
descendant
(
of:
find
.
byKey
(
debuggerKey
),
matching:
find
.
byType
(
CustomPaint
),
)).
first
;
final
dynamic
semanticsDebuggerPainter
=
customPaint
.
foregroundPainter
;
expect
(
semanticsDebuggerPainter
.
runtimeType
.
toString
(),
'_SemanticsDebuggerPainter'
);
return
semanticsDebuggerPainter
.
getMessage
(
tester
.
renderObject
(
find
.
byKey
(
widgetKey
)).
debugSemantics
);
}
This diff is collapsed.
Click to expand it.
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