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
c8d80163
Unverified
Commit
c8d80163
authored
Feb 27, 2023
by
chunhtai
Committed by
GitHub
Feb 27, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Revert "Fix text.rich to merge widget span (#113461)" (#121562)
This reverts commit
660992a1
.
parent
22cbe6e8
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
21 additions
and
312 deletions
+21
-312
object.dart
packages/flutter/lib/src/rendering/object.dart
+10
-77
paragraph.dart
packages/flutter/lib/src/rendering/paragraph.dart
+9
-78
semantics.dart
packages/flutter/lib/src/semantics/semantics.dart
+2
-3
text_test.dart
packages/flutter/test/widgets/text_test.dart
+0
-154
No files found.
packages/flutter/lib/src/rendering/object.dart
View file @
c8d80163
...
...
@@ -15,7 +15,6 @@ import 'package:flutter/semantics.dart';
import
'debug.dart'
;
import
'layer.dart'
;
import
'proxy_box.dart'
;
export
'package:flutter/foundation.dart'
show
DiagnosticPropertiesBuilder
,
...
...
@@ -3228,7 +3227,7 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
final
SemanticsConfiguration
config
=
_semanticsConfiguration
;
bool
dropSemanticsOfPreviousSiblings
=
config
.
isBlockingSemanticsOfPreviouslyPaintedNodes
;
bool
producesForkingFragment
=
!
config
.
hasBeenAnnotated
&&
!
config
.
isSemanticBoundary
;
final
bool
producesForkingFragment
=
!
config
.
hasBeenAnnotated
&&
!
config
.
isSemanticBoundary
;
final
bool
childrenMergeIntoParent
=
mergeIntoParent
||
config
.
isMergingSemanticsOfDescendants
;
final
List
<
SemanticsConfiguration
>
childConfigurations
=
<
SemanticsConfiguration
>[];
final
bool
explicitChildNode
=
config
.
explicitChildNodes
||
parent
is
!
RenderObject
;
...
...
@@ -3236,7 +3235,6 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
final
Map
<
SemanticsConfiguration
,
_InterestingSemanticsFragment
>
configToFragment
=
<
SemanticsConfiguration
,
_InterestingSemanticsFragment
>{};
final
List
<
_InterestingSemanticsFragment
>
mergeUpFragments
=
<
_InterestingSemanticsFragment
>[];
final
List
<
List
<
_InterestingSemanticsFragment
>>
siblingMergeFragmentGroups
=
<
List
<
_InterestingSemanticsFragment
>>[];
final
bool
hasTags
=
config
.
tagsForChildren
?.
isNotEmpty
??
false
;
visitChildrenForSemantics
((
RenderObject
renderChild
)
{
assert
(!
_needsLayout
);
final
_SemanticsFragment
parentFragment
=
renderChild
.
_getSemanticsForParent
(
...
...
@@ -3252,9 +3250,7 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
}
for
(
final
_InterestingSemanticsFragment
fragment
in
parentFragment
.
mergeUpFragments
)
{
fragment
.
addAncestor
(
this
);
if
(
hasTags
)
{
fragment
.
addTags
(
config
.
tagsForChildren
!);
}
fragment
.
addTags
(
config
.
tagsForChildren
);
if
(
hasChildConfigurationsDelegate
&&
fragment
.
config
!=
null
)
{
// This fragment need to go through delegate to determine whether it
// merge up or not.
...
...
@@ -3270,9 +3266,7 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
for
(
final
List
<
_InterestingSemanticsFragment
>
siblingMergeGroup
in
parentFragment
.
siblingMergeGroups
)
{
for
(
final
_InterestingSemanticsFragment
siblingMergingFragment
in
siblingMergeGroup
)
{
siblingMergingFragment
.
addAncestor
(
this
);
if
(
hasTags
)
{
siblingMergingFragment
.
addTags
(
config
.
tagsForChildren
!);
}
siblingMergingFragment
.
addTags
(
config
.
tagsForChildren
);
}
siblingMergeFragmentGroups
.
add
(
siblingMergeGroup
);
}
...
...
@@ -3285,25 +3279,14 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
for
(
final
_InterestingSemanticsFragment
fragment
in
mergeUpFragments
)
{
fragment
.
markAsExplicit
();
}
}
else
if
(
hasChildConfigurationsDelegate
)
{
}
else
if
(
hasChildConfigurationsDelegate
&&
childConfigurations
.
isNotEmpty
)
{
final
ChildSemanticsConfigurationsResult
result
=
config
.
childConfigurationsDelegate
!(
childConfigurations
);
mergeUpFragments
.
addAll
(
result
.
mergeUp
.
map
<
_InterestingSemanticsFragment
>((
SemanticsConfiguration
config
)
{
final
_InterestingSemanticsFragment
?
fragment
=
configToFragment
[
config
];
if
(
fragment
==
null
)
{
// Parent fragment of Incomplete fragments can't be a forking
// fragment since they need to be merged.
producesForkingFragment
=
false
;
return
_IncompleteSemanticsFragment
(
config:
config
,
owner:
this
);
}
return
fragment
;
}),
result
.
mergeUp
.
map
<
_InterestingSemanticsFragment
>((
SemanticsConfiguration
config
)
=>
configToFragment
[
config
]!),
);
for
(
final
Iterable
<
SemanticsConfiguration
>
group
in
result
.
siblingMergeGroups
)
{
siblingMergeFragmentGroups
.
add
(
group
.
map
<
_InterestingSemanticsFragment
>((
SemanticsConfiguration
config
)
{
return
configToFragment
[
config
]
??
_IncompleteSemanticsFragment
(
config:
config
,
owner:
this
);
}).
toList
(),
group
.
map
<
_InterestingSemanticsFragment
>((
SemanticsConfiguration
config
)
=>
configToFragment
[
config
]!).
toList
()
);
}
}
...
...
@@ -4184,10 +4167,10 @@ abstract class _InterestingSemanticsFragment extends _SemanticsFragment {
Set
<
SemanticsTag
>?
_tagsForChildren
;
/// Tag all children produced by [compileChildren] with `tags`.
///
/// `tags` must not be empty.
void
addTags
(
Iterable
<
SemanticsTag
>
tags
)
{
assert
(
tags
.
isNotEmpty
);
void
addTags
(
Iterable
<
SemanticsTag
>?
tags
)
{
if
(
tags
==
null
||
tags
.
isEmpty
)
{
return
;
}
_tagsForChildren
??=
<
SemanticsTag
>{};
_tagsForChildren
!.
addAll
(
tags
);
}
...
...
@@ -4281,48 +4264,6 @@ class _RootSemanticsFragment extends _InterestingSemanticsFragment {
}
}
/// A fragment with partial information that must not form an explicit
/// semantics node without merging into another _SwitchableSemanticsFragment.
///
/// This fragment is generated from synthetic SemanticsConfiguration returned from
/// [SemanticsConfiguration.childConfigurationsDelegate].
class
_IncompleteSemanticsFragment
extends
_InterestingSemanticsFragment
{
_IncompleteSemanticsFragment
({
required
this
.
config
,
required
super
.
owner
,
})
:
super
(
dropsSemanticsOfPreviousSiblings:
false
);
@override
void
addAll
(
Iterable
<
_InterestingSemanticsFragment
>
fragments
)
{
assert
(
false
,
'This fragment must be a leaf node'
);
}
@override
void
compileChildren
({
required
Rect
?
parentSemanticsClipRect
,
required
Rect
?
parentPaintClipRect
,
required
double
elevationAdjustment
,
required
List
<
SemanticsNode
>
result
,
required
List
<
SemanticsNode
>
siblingNodes
,
})
{
// There is nothing to do because this fragment must be a leaf node and
// must not be explicit.
}
@override
final
SemanticsConfiguration
config
;
@override
void
markAsExplicit
()
{
assert
(
false
,
'SemanticsConfiguration created in '
'SemanticsConfiguration.childConfigurationsDelegate must not produce '
'its own semantics node'
);
}
}
/// An [_InterestingSemanticsFragment] that can be told to only add explicit
/// [SemanticsNode]s to the parent.
///
...
...
@@ -4601,14 +4542,6 @@ class _SwitchableSemanticsFragment extends _InterestingSemanticsFragment {
}
}
@override
void
addTags
(
Iterable
<
SemanticsTag
>
tags
)
{
super
.
addTags
(
tags
);
// _ContainerSemanticsFragments add their tags to child fragments through
// this method. This fragment must make sure its _config is in sync.
tags
.
forEach
(
_config
.
addTagForChildren
);
}
void
_ensureConfigIsWritable
()
{
if
(!
_isConfigWritable
)
{
_config
=
_config
.
copy
();
...
...
packages/flutter/lib/src/rendering/paragraph.dart
View file @
c8d80163
...
...
@@ -119,9 +119,7 @@ class RenderParagraph extends RenderBox
static
final
String
_placeholderCharacter
=
String
.
fromCharCode
(
PlaceholderSpan
.
placeholderCodeUnit
);
final
TextPainter
_textPainter
;
List
<
AttributedString
>?
_cachedAttributedLabels
;
AttributedString
?
_cachedAttributedLabel
;
List
<
InlineSpanSemanticsInformation
>?
_cachedCombinedSemanticsInfos
;
/// The text to display.
...
...
@@ -137,7 +135,7 @@ class RenderParagraph extends RenderBox
break
;
case
RenderComparison
.
paint
:
_textPainter
.
text
=
value
;
_cachedAttributedLabel
s
=
null
;
_cachedAttributedLabel
=
null
;
_cachedCombinedSemanticsInfos
=
null
;
_extractPlaceholderSpans
(
value
);
markNeedsPaint
();
...
...
@@ -146,7 +144,7 @@ class RenderParagraph extends RenderBox
case
RenderComparison
.
layout
:
_textPainter
.
text
=
value
;
_overflowShader
=
null
;
_cachedAttributedLabel
s
=
null
;
_cachedAttributedLabel
=
null
;
_cachedCombinedSemanticsInfos
=
null
;
_extractPlaceholderSpans
(
value
);
markNeedsLayout
();
...
...
@@ -1037,23 +1035,12 @@ class RenderParagraph extends RenderBox
void
describeSemanticsConfiguration
(
SemanticsConfiguration
config
)
{
super
.
describeSemanticsConfiguration
(
config
);
_semanticsInfo
=
text
.
getSemanticsInformation
();
bool
needsAssembleSemanticsNode
=
false
;
bool
needsChildConfigrationsDelegate
=
false
;
for
(
final
InlineSpanSemanticsInformation
info
in
_semanticsInfo
!)
{
if
(
info
.
recognizer
!=
null
)
{
needsAssembleSemanticsNode
=
true
;
break
;
}
needsChildConfigrationsDelegate
=
needsChildConfigrationsDelegate
||
info
.
isPlaceholder
;
}
if
(
needsAssembleSemanticsNode
)
{
if
(
_semanticsInfo
!.
any
((
InlineSpanSemanticsInformation
info
)
=>
info
.
recognizer
!=
null
)
)
{
config
.
explicitChildNodes
=
true
;
config
.
isSemanticBoundary
=
true
;
}
else
if
(
needsChildConfigrationsDelegate
)
{
config
.
childConfigurationsDelegate
=
_childSemanticsConfigurationsDelegate
;
}
else
{
if
(
_cachedAttributedLabel
s
==
null
)
{
if
(
_cachedAttributedLabel
==
null
)
{
final
StringBuffer
buffer
=
StringBuffer
();
int
offset
=
0
;
final
List
<
StringAttribute
>
attributes
=
<
StringAttribute
>[];
...
...
@@ -1063,77 +1050,21 @@ class RenderParagraph extends RenderBox
final
TextRange
originalRange
=
infoAttribute
.
range
;
attributes
.
add
(
infoAttribute
.
copy
(
range:
TextRange
(
start:
offset
+
originalRange
.
start
,
end:
offset
+
originalRange
.
end
,
),
range:
TextRange
(
start:
offset
+
originalRange
.
start
,
end:
offset
+
originalRange
.
end
)
),
);
}
buffer
.
write
(
label
);
offset
+=
label
.
length
;
}
_cachedAttributedLabel
s
=
<
AttributedString
>[
AttributedString
(
buffer
.
toString
(),
attributes:
attributes
)]
;
_cachedAttributedLabel
=
AttributedString
(
buffer
.
toString
(),
attributes:
attributes
)
;
}
config
.
attributedLabel
=
_cachedAttributedLabel
s
![
0
]
;
config
.
attributedLabel
=
_cachedAttributedLabel
!
;
config
.
textDirection
=
textDirection
;
}
}
ChildSemanticsConfigurationsResult
_childSemanticsConfigurationsDelegate
(
List
<
SemanticsConfiguration
>
childConfigs
)
{
final
ChildSemanticsConfigurationsResultBuilder
builder
=
ChildSemanticsConfigurationsResultBuilder
();
int
placeholderIndex
=
0
;
int
childConfigsIndex
=
0
;
int
attributedLabelCacheIndex
=
0
;
InlineSpanSemanticsInformation
?
seenTextInfo
;
_cachedCombinedSemanticsInfos
??=
combineSemanticsInfo
(
_semanticsInfo
!);
for
(
final
InlineSpanSemanticsInformation
info
in
_cachedCombinedSemanticsInfos
!)
{
if
(
info
.
isPlaceholder
)
{
if
(
seenTextInfo
!=
null
)
{
builder
.
markAsMergeUp
(
_createSemanticsConfigForTextInfo
(
seenTextInfo
,
attributedLabelCacheIndex
));
attributedLabelCacheIndex
+=
1
;
}
// Mark every childConfig belongs to this placeholder to merge up group.
while
(
childConfigsIndex
<
childConfigs
.
length
&&
childConfigs
[
childConfigsIndex
].
tagsChildrenWith
(
PlaceholderSpanIndexSemanticsTag
(
placeholderIndex
)))
{
builder
.
markAsMergeUp
(
childConfigs
[
childConfigsIndex
]);
childConfigsIndex
+=
1
;
}
placeholderIndex
+=
1
;
}
else
{
seenTextInfo
=
info
;
}
}
// Handle plain text info at the end.
if
(
seenTextInfo
!=
null
)
{
builder
.
markAsMergeUp
(
_createSemanticsConfigForTextInfo
(
seenTextInfo
,
attributedLabelCacheIndex
));
}
return
builder
.
build
();
}
SemanticsConfiguration
_createSemanticsConfigForTextInfo
(
InlineSpanSemanticsInformation
textInfo
,
int
cacheIndex
)
{
assert
(!
textInfo
.
requiresOwnNode
);
final
List
<
AttributedString
>
cachedStrings
=
_cachedAttributedLabels
??=
<
AttributedString
>[];
assert
(
cacheIndex
<=
cachedStrings
.
length
);
final
bool
hasCache
=
cacheIndex
<
cachedStrings
.
length
;
late
AttributedString
attributedLabel
;
if
(
hasCache
)
{
attributedLabel
=
cachedStrings
[
cacheIndex
];
}
else
{
assert
(
cachedStrings
.
length
==
cacheIndex
);
attributedLabel
=
AttributedString
(
textInfo
.
semanticsLabel
??
textInfo
.
text
,
attributes:
textInfo
.
stringAttributes
,
);
cachedStrings
.
add
(
attributedLabel
);
}
return
SemanticsConfiguration
()
..
textDirection
=
textDirection
..
attributedLabel
=
attributedLabel
;
}
// Caches [SemanticsNode]s created during [assembleSemanticsNode] so they
// can be re-used when [assembleSemanticsNode] is called again. This ensures
// stable ids for the [SemanticsNode]s of [TextSpan]s across
...
...
packages/flutter/lib/src/semantics/semantics.dart
View file @
c8d80163
...
...
@@ -346,7 +346,7 @@ class AttributedString {
}
@override
int
get
hashCode
=>
Object
.
hash
(
string
,
attributes
);
int
get
hashCode
=>
Object
.
hash
(
string
,
attributes
,
);
@override
String
toString
()
{
...
...
@@ -3805,8 +3805,7 @@ class SemanticsConfiguration {
/// which of them should be merged upwards into the parent SemanticsNode.
///
/// The input list of [SemanticsConfiguration]s can be empty if the rendering
/// object of this semantics configuration is a leaf node or child rendering
/// objects do not contribute to the semantics.
/// object of this semantics configuration is a leaf node.
ChildSemanticsConfigurationsDelegate
?
get
childConfigurationsDelegate
=>
_childConfigurationsDelegate
;
ChildSemanticsConfigurationsDelegate
?
_childConfigurationsDelegate
;
set
childConfigurationsDelegate
(
ChildSemanticsConfigurationsDelegate
?
value
)
{
...
...
packages/flutter/test/widgets/text_test.dart
View file @
c8d80163
...
...
@@ -310,160 +310,6 @@ void main() {
semantics
.
dispose
();
});
testWidgets
(
'semantics label is in order when uses widget span'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
Text
.
rich
(
TextSpan
(
children:
<
InlineSpan
>[
const
TextSpan
(
text:
'before '
),
WidgetSpan
(
alignment:
PlaceholderAlignment
.
baseline
,
baseline:
TextBaseline
.
alphabetic
,
child:
Semantics
(
label:
'foo'
),
),
const
TextSpan
(
text:
' after'
),
],
),
),
),
);
expect
(
tester
.
getSemantics
(
find
.
byType
(
Text
)),
matchesSemantics
(
label:
'before
\n
foo
\n
after'
),
);
// If the Paragraph is not dirty it should use the cache correctly.
final
RenderObject
parent
=
tester
.
renderObject
<
RenderObject
>(
find
.
byType
(
Directionality
));
parent
.
markNeedsSemanticsUpdate
();
await
tester
.
pumpAndSettle
();
expect
(
tester
.
getSemantics
(
find
.
byType
(
Text
)),
matchesSemantics
(
label:
'before
\n
foo
\n
after'
),
);
});
testWidgets
(
'semantics can handle some widget spans without semantics'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
Text
.
rich
(
TextSpan
(
children:
<
InlineSpan
>[
const
TextSpan
(
text:
'before '
),
const
WidgetSpan
(
alignment:
PlaceholderAlignment
.
baseline
,
baseline:
TextBaseline
.
alphabetic
,
child:
SizedBox
(
width:
10.0
),
),
const
TextSpan
(
text:
' mid'
),
WidgetSpan
(
alignment:
PlaceholderAlignment
.
baseline
,
baseline:
TextBaseline
.
alphabetic
,
child:
Semantics
(
label:
'foo'
),
),
const
TextSpan
(
text:
' after'
),
const
WidgetSpan
(
alignment:
PlaceholderAlignment
.
baseline
,
baseline:
TextBaseline
.
alphabetic
,
child:
SizedBox
(
width:
10.0
),
),
],
),
),
),
);
expect
(
tester
.
getSemantics
(
find
.
byType
(
Text
)),
matchesSemantics
(
label:
'before
\n
mid
\n
foo
\n
after'
));
// If the Paragraph is not dirty it should use the cache correctly.
final
RenderObject
parent
=
tester
.
renderObject
<
RenderObject
>(
find
.
byType
(
Directionality
));
parent
.
markNeedsSemanticsUpdate
();
await
tester
.
pumpAndSettle
();
expect
(
tester
.
getSemantics
(
find
.
byType
(
Text
)),
matchesSemantics
(
label:
'before
\n
mid
\n
foo
\n
after'
));
});
testWidgets
(
'semantics can handle all widget spans without semantics'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
Text
.
rich
(
TextSpan
(
children:
<
InlineSpan
>[
TextSpan
(
text:
'before '
),
WidgetSpan
(
alignment:
PlaceholderAlignment
.
baseline
,
baseline:
TextBaseline
.
alphabetic
,
child:
SizedBox
(
width:
10.0
),
),
TextSpan
(
text:
' mid'
),
WidgetSpan
(
alignment:
PlaceholderAlignment
.
baseline
,
baseline:
TextBaseline
.
alphabetic
,
child:
SizedBox
(
width:
10.0
),
),
TextSpan
(
text:
' after'
),
WidgetSpan
(
alignment:
PlaceholderAlignment
.
baseline
,
baseline:
TextBaseline
.
alphabetic
,
child:
SizedBox
(
width:
10.0
),
),
],
),
),
),
);
expect
(
tester
.
getSemantics
(
find
.
byType
(
Text
)),
matchesSemantics
(
label:
'before
\n
mid
\n
after'
));
// If the Paragraph is not dirty it should use the cache correctly.
final
RenderObject
parent
=
tester
.
renderObject
<
RenderObject
>(
find
.
byType
(
Directionality
));
parent
.
markNeedsSemanticsUpdate
();
await
tester
.
pumpAndSettle
();
expect
(
tester
.
getSemantics
(
find
.
byType
(
Text
)),
matchesSemantics
(
label:
'before
\n
mid
\n
after'
));
});
testWidgets
(
'semantics can handle widget spans with explicit semantics node'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
Text
.
rich
(
TextSpan
(
children:
<
InlineSpan
>[
const
TextSpan
(
text:
'before '
),
WidgetSpan
(
alignment:
PlaceholderAlignment
.
baseline
,
baseline:
TextBaseline
.
alphabetic
,
child:
Semantics
(
label:
'inner'
,
container:
true
),
),
const
TextSpan
(
text:
' after'
),
],
),
),
),
);
expect
(
tester
.
getSemantics
(
find
.
byType
(
Text
)),
matchesSemantics
(
label:
'before
\n
after'
,
children:
<
Matcher
>[
matchesSemantics
(
label:
'inner'
)]),
);
// If the Paragraph is not dirty it should use the cache correctly.
final
RenderObject
parent
=
tester
.
renderObject
<
RenderObject
>(
find
.
byType
(
Directionality
));
parent
.
markNeedsSemanticsUpdate
();
await
tester
.
pumpAndSettle
();
expect
(
tester
.
getSemantics
(
find
.
byType
(
Text
)),
matchesSemantics
(
label:
'before
\n
after'
,
children:
<
Matcher
>[
matchesSemantics
(
label:
'inner'
)]),
);
});
testWidgets
(
'semanticsLabel can be shorter than text'
,
(
WidgetTester
tester
)
async
{
final
SemanticsTester
semantics
=
SemanticsTester
(
tester
);
await
tester
.
pumpWidget
(
Directionality
(
...
...
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