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
2fbb529d
Unverified
Commit
2fbb529d
authored
Sep 15, 2020
by
LongCatIsLooong
Committed by
GitHub
Sep 15, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add CompositedTransformFollower.{followerAnchor, leaderAnchor} for custom anchoring (#64930)
parent
7e41425d
Changes
4
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
314 additions
and
97 deletions
+314
-97
layer.dart
packages/flutter/lib/src/rendering/layer.dart
+70
-31
proxy_box.dart
packages/flutter/lib/src/rendering/proxy_box.dart
+72
-9
basic.dart
packages/flutter/lib/src/widgets/basic.dart
+41
-6
composited_transform_test.dart
packages/flutter/test/widgets/composited_transform_test.dart
+131
-51
No files found.
packages/flutter/lib/src/rendering/layer.dart
View file @
2fbb529d
...
...
@@ -3,7 +3,6 @@
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:collection'
;
import
'dart:ui'
as
ui
;
import
'package:flutter/foundation.dart'
;
...
...
@@ -2028,6 +2027,14 @@ class LayerLink {
LeaderLayer
?
get
leader
=>
_leader
;
LeaderLayer
?
_leader
;
/// The total size of [leader]'s contents.
///
/// Generally this should be set by the [RenderObject] that paints on the
/// registered [leader] layer (for instance a [RenderLeaderLayer] that shares
/// this link with its followers). This size may be outdated before and during
/// layout.
Size
?
leaderSize
;
@override
String
toString
()
=>
'
${describeIdentity(this)}
(
${ _leader != null ? "<linked>" : "<dangling>" }
)'
;
}
...
...
@@ -2266,59 +2273,91 @@ class FollowerLayer extends ContainerLayer {
/// treated as the child of the second, and so forth. The first layer in the
/// list won't have [applyTransform] called on it. The first layer may be
/// null.
Matrix4
_collectTransformForLayerChain
(
List
<
ContainerLayer
?>
layers
)
{
static
Matrix4
_collectTransformForLayerChain
(
List
<
ContainerLayer
?>
layers
)
{
// Initialize our result matrix.
final
Matrix4
result
=
Matrix4
.
identity
();
// Apply each layer to the matrix in turn, starting from the last layer,
// and providing the previous layer as the child.
for
(
int
index
=
layers
.
length
-
1
;
index
>
0
;
index
-=
1
)
layers
[
index
]
!
.
applyTransform
(
layers
[
index
-
1
],
result
);
layers
[
index
]
?
.
applyTransform
(
layers
[
index
-
1
],
result
);
return
result
;
}
/// Find the common ancestor of two layers [a] and [b] by searching towards
/// the root of the tree, and append each ancestor of [a] or [b] visited along
/// the path to [ancestorsA] and [ancestorsB] respectively.
///
/// Returns null if [a] [b] do not share a common ancestor, in which case the
/// results in [ancestorsA] and [ancestorsB] are undefined.
static
Layer
?
_pathsToCommonAncestor
(
Layer
?
a
,
Layer
?
b
,
List
<
ContainerLayer
?>
ancestorsA
,
List
<
ContainerLayer
?>
ancestorsB
,
)
{
// No common ancestor found.
if
(
a
==
null
||
b
==
null
)
return
null
;
if
(
identical
(
a
,
b
))
return
a
;
if
(
a
.
depth
<
b
.
depth
)
{
ancestorsB
.
add
(
b
.
parent
);
return
_pathsToCommonAncestor
(
a
,
b
.
parent
,
ancestorsA
,
ancestorsB
);
}
else
if
(
a
.
depth
>
b
.
depth
){
ancestorsA
.
add
(
a
.
parent
);
return
_pathsToCommonAncestor
(
a
.
parent
,
b
,
ancestorsA
,
ancestorsB
);
}
ancestorsA
.
add
(
a
.
parent
);
ancestorsB
.
add
(
b
.
parent
);
return
_pathsToCommonAncestor
(
a
.
parent
,
b
.
parent
,
ancestorsA
,
ancestorsB
);
}
/// Populate [_lastTransform] given the current state of the tree.
void
_establishTransform
()
{
assert
(
link
!=
null
);
_lastTransform
=
null
;
final
LeaderLayer
?
leader
=
link
.
leader
;
// Check to see if we are linked.
if
(
l
ink
.
l
eader
==
null
)
if
(
leader
==
null
)
return
;
// If we're linked, check the link is valid.
assert
(
link
.
leader
!.
owner
==
owner
,
'Linked LeaderLayer anchor is not in the same layer tree as the FollowerLayer.'
);
assert
(
link
.
leader
!.
_lastOffset
!=
null
,
'LeaderLayer anchor must come before FollowerLayer in paint order, but the reverse was true.'
);
// Collect all our ancestors into a Set so we can recognize them.
final
Set
<
Layer
>
ancestors
=
HashSet
<
Layer
>();
Layer
?
ancestor
=
parent
;
while
(
ancestor
!=
null
)
{
ancestors
.
add
(
ancestor
);
ancestor
=
ancestor
.
parent
;
}
// Collect all the layers from a hypothetical child (null) of the target
// layer up to the common ancestor layer.
ContainerLayer
?
layer
=
link
.
leader
;
final
List
<
ContainerLayer
?>
forwardLayers
=
<
ContainerLayer
?>[
null
,
layer
];
do
{
layer
=
layer
!.
parent
;
forwardLayers
.
add
(
layer
);
}
while
(!
ancestors
.
contains
(
layer
));
// ignore: iterable_contains_unrelated_type
ancestor
=
layer
;
// Collect all the layers from this layer up to the common ancestor layer.
layer
=
this
;
final
List
<
ContainerLayer
>
inverseLayers
=
<
ContainerLayer
>[
layer
];
do
{
layer
=
layer
!.
parent
;
inverseLayers
.
add
(
layer
!);
}
while
(
layer
!=
ancestor
);
// Establish the forward and backward matrices given these lists of layers.
assert
(
leader
.
owner
==
owner
,
'Linked LeaderLayer anchor is not in the same layer tree as the FollowerLayer.'
,
);
assert
(
leader
.
_lastOffset
!=
null
,
'LeaderLayer anchor must come before FollowerLayer in paint order, but the reverse was true.'
,
);
// Stores [leader, ..., commonAncestor] after calling _pathsToCommonAncestor.
final
List
<
ContainerLayer
?>
forwardLayers
=
<
ContainerLayer
>[
leader
];
// Stores [this (follower), ..., commonAncestor] after calling
// _pathsToCommonAncestor.
final
List
<
ContainerLayer
?>
inverseLayers
=
<
ContainerLayer
>[
this
];
final
Layer
?
ancestor
=
_pathsToCommonAncestor
(
leader
,
this
,
forwardLayers
,
inverseLayers
,
);
assert
(
ancestor
!=
null
);
final
Matrix4
forwardTransform
=
_collectTransformForLayerChain
(
forwardLayers
);
// Further transforms the coordinate system to a hypothetical child (null)
// of the leader layer, to account for the leader's additional paint offset
// and layer offset (LeaderLayer._lastOffset).
leader
.
applyTransform
(
null
,
forwardTransform
);
forwardTransform
.
translate
(
linkedOffset
!.
dx
,
linkedOffset
!.
dy
);
final
Matrix4
inverseTransform
=
_collectTransformForLayerChain
(
inverseLayers
);
if
(
inverseTransform
.
invert
()
==
0.0
)
{
// We are in a degenerate transform, so there's not much we can do.
return
;
}
// Combine the matrices and store the result.
inverseTransform
.
multiply
(
forwardTransform
);
inverseTransform
.
translate
(
linkedOffset
!.
dx
,
linkedOffset
!.
dy
);
_lastTransform
=
inverseTransform
;
_inverseDirty
=
true
;
}
...
...
packages/flutter/lib/src/rendering/proxy_box.dart
View file @
2fbb529d
...
...
@@ -4826,28 +4826,37 @@ class RenderLeaderLayer extends RenderProxyBox {
required
LayerLink
link
,
RenderBox
?
child
,
})
:
assert
(
link
!=
null
),
super
(
child
)
{
this
.
link
=
link
;
}
_link
=
link
,
super
(
child
);
/// The link object that connects this [RenderLeaderLayer] with one or more
/// [RenderFollowerLayer]s.
///
/// This property must not be null. The object must not be associated with
/// another [RenderLeaderLayer] that is also being painted.
LayerLink
get
link
=>
_link
!
;
LayerLink
?
_link
;
LayerLink
get
link
=>
_link
;
LayerLink
_link
;
set
link
(
LayerLink
value
)
{
assert
(
value
!=
null
);
if
(
_link
==
value
)
return
;
_link
.
leaderSize
=
null
;
_link
=
value
;
if
(
hasSize
)
{
_link
.
leaderSize
=
size
;
}
markNeedsPaint
();
}
@override
bool
get
alwaysNeedsCompositing
=>
true
;
@override
void
performLayout
()
{
super
.
performLayout
();
link
.
leaderSize
=
size
;
}
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
if
(
layer
==
null
)
{
...
...
@@ -4890,6 +4899,8 @@ class RenderFollowerLayer extends RenderProxyBox {
required
LayerLink
link
,
bool
showWhenUnlinked
=
true
,
Offset
offset
=
Offset
.
zero
,
Alignment
leaderAnchor
=
Alignment
.
topLeft
,
Alignment
followerAnchor
=
Alignment
.
topLeft
,
RenderBox
?
child
,
})
:
assert
(
link
!=
null
),
assert
(
showWhenUnlinked
!=
null
),
...
...
@@ -4897,6 +4908,8 @@ class RenderFollowerLayer extends RenderProxyBox {
_link
=
link
,
_showWhenUnlinked
=
showWhenUnlinked
,
_offset
=
offset
,
_leaderAnchor
=
leaderAnchor
,
_followerAnchor
=
followerAnchor
,
super
(
child
);
/// The link object that connects this [RenderFollowerLayer] with a
...
...
@@ -4942,6 +4955,46 @@ class RenderFollowerLayer extends RenderProxyBox {
markNeedsPaint
();
}
/// The anchor point on the linked [RenderLeaderLayer] that [followerAnchor]
/// will line up with.
///
/// {@template flutter.rendering.followerLayer.anchor}
/// For example, when [leaderAnchor] and [followerAnchor] are both
/// [Alignment.topLeft], this [RenderFollowerLayer] will be top left aligned
/// with the linked [RenderLeaderLayer]. When [leaderAnchor] is
/// [Alignment.bottomLeft] and [followerAnchor] is [Alignment.topLeft], this
/// [RenderFollowerLayer] will be left aligned with the linked
/// [RenderLeaderLayer], and its top edge will line up with the
/// [RenderLeaderLayer]'s bottom edge.
/// {@endtemplate}
///
/// Defaults to [Alignment.topLeft].
Alignment
get
leaderAnchor
=>
_leaderAnchor
;
Alignment
_leaderAnchor
;
set
leaderAnchor
(
Alignment
value
)
{
assert
(
value
!=
null
);
if
(
_leaderAnchor
==
value
)
return
;
_leaderAnchor
=
value
;
markNeedsPaint
();
}
/// The anchor point on this [RenderFollowerLayer] that will line up with
/// [followerAnchor] on the linked [RenderLeaderLayer].
///
/// {@macro flutter.rendering.followerLayer.anchor}
///
/// Defaults to [Alignment.topLeft].
Alignment
get
followerAnchor
=>
_followerAnchor
;
Alignment
_followerAnchor
;
set
followerAnchor
(
Alignment
value
)
{
assert
(
value
!=
null
);
if
(
_followerAnchor
==
value
)
return
;
_followerAnchor
=
value
;
markNeedsPaint
();
}
@override
void
detach
()
{
layer
=
null
;
...
...
@@ -4990,19 +5043,29 @@ class RenderFollowerLayer extends RenderProxyBox {
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
final
Size
?
leaderSize
=
link
.
leaderSize
;
assert
(
link
.
leaderSize
!=
null
||
(
link
.
leader
==
null
||
leaderAnchor
==
Alignment
.
topLeft
),
'
$link
: layer is linked to
${link.leader}
but a valid leaderSize is not set. '
'leaderSize is required when leaderAnchor is not Alignment.topLeft'
'(current value is
$leaderAnchor
).'
,
);
final
Offset
effectiveLinkedOffset
=
leaderSize
==
null
?
this
.
offset
:
leaderAnchor
.
alongSize
(
leaderSize
)
-
followerAnchor
.
alongSize
(
size
)
+
this
.
offset
;
assert
(
showWhenUnlinked
!=
null
);
if
(
layer
==
null
)
{
layer
=
FollowerLayer
(
link:
link
,
showWhenUnlinked:
showWhenUnlinked
,
linkedOffset:
this
.
o
ffset
,
linkedOffset:
effectiveLinkedO
ffset
,
unlinkedOffset:
offset
,
);
}
else
{
layer
!
..
link
=
link
layer
?
..
link
=
link
..
showWhenUnlinked
=
showWhenUnlinked
..
linkedOffset
=
this
.
o
ffset
..
linkedOffset
=
effectiveLinkedO
ffset
..
unlinkedOffset
=
offset
;
}
context
.
pushLayer
(
...
...
packages/flutter/lib/src/widgets/basic.dart
View file @
2fbb529d
...
...
@@ -1324,9 +1324,11 @@ class CompositedTransformTarget extends SingleChildRenderObjectWidget {
///
/// When this widget is composited during the compositing phase (which comes
/// after the paint phase, as described in [WidgetsBinding.drawFrame]), it
/// applies a transformation that causes it to provide its child with a
/// coordinate space that matches that of the linked [CompositedTransformTarget]
/// widget, offset by [offset].
/// applies a transformation that brings [targetAnchor] of the linked
/// [CompositedTransformTarget] and [followerAnchor] of this widget together.
/// The two anchor points will have the same global coordinates, unless [offset]
/// is not [Offset.zero], in which case [followerAnchor] will be offset by
/// [offset] in the linked [CompositedTransformTarget]'s coordinate space.
///
/// The [LayerLink] object used as the [link] must be the same object as that
/// provided to the matching [CompositedTransformTarget].
...
...
@@ -1358,10 +1360,14 @@ class CompositedTransformFollower extends SingleChildRenderObjectWidget {
@required
this
.
link
,
this
.
showWhenUnlinked
=
true
,
this
.
offset
=
Offset
.
zero
,
this
.
targetAnchor
=
Alignment
.
topLeft
,
this
.
followerAnchor
=
Alignment
.
topLeft
,
Widget
child
,
})
:
assert
(
link
!=
null
),
assert
(
showWhenUnlinked
!=
null
),
assert
(
offset
!=
null
),
assert
(
targetAnchor
!=
null
),
assert
(
followerAnchor
!=
null
),
super
(
key:
key
,
child:
child
);
/// The link object that connects this [CompositedTransformFollower] with a
...
...
@@ -1381,8 +1387,33 @@ class CompositedTransformFollower extends SingleChildRenderObjectWidget {
/// hidden.
final
bool
showWhenUnlinked
;
/// The offset to apply to the origin of the linked
/// [CompositedTransformTarget] to obtain this widget's origin.
/// The anchor point on the linked [CompositedTransformTarget] that
/// [followerAnchor] will line up with.
///
/// {@template flutter.widgets.followerLayer.anchor}
/// For example, when [targetAnchor] and [followerAnchor] are both
/// [Alignment.topLeft], this widget will be top left aligned with the linked
/// [CompositedTransformTarget]. When [targetAnchor] is
/// [Alignment.bottomLeft] and [followerAnchor] is [Alignment.topLeft], this
/// widget will be left aligned with the linked [CompositedTransformTarget],
/// and its top edge will line up with the [CompositedTransformTarget]'s
/// bottom edge.
/// {@endtemplate}
///
/// Defaults to [Alignment.topLeft].
final
Alignment
targetAnchor
;
/// The anchor point on this widget that will line up with [followerAnchor] on
/// the linked [CompositedTransformTarget].
///
/// {@macro flutter.widgets.followerLayer.anchor}
///
/// Defaults to [Alignment.topLeft].
final
Alignment
followerAnchor
;
/// The additional offset to apply to the [targetAnchor] of the linked
/// [CompositedTransformTarget] to obtain this widget's [followerAnchor]
/// position.
final
Offset
offset
;
@override
...
...
@@ -1391,6 +1422,8 @@ class CompositedTransformFollower extends SingleChildRenderObjectWidget {
link:
link
,
showWhenUnlinked:
showWhenUnlinked
,
offset:
offset
,
leaderAnchor:
targetAnchor
,
followerAnchor:
followerAnchor
,
);
}
...
...
@@ -1399,7 +1432,9 @@ class CompositedTransformFollower extends SingleChildRenderObjectWidget {
renderObject
..
link
=
link
..
showWhenUnlinked
=
showWhenUnlinked
..
offset
=
offset
;
..
offset
=
offset
..
leaderAnchor
=
targetAnchor
..
followerAnchor
=
followerAnchor
;
}
}
...
...
packages/flutter/test/widgets/composited_transform_test.dart
View file @
2fbb529d
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