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
7a52116d
Commit
7a52116d
authored
May 10, 2017
by
Ian Hickson
Committed by
GitHub
May 10, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Make PhysicalModel more convenient, and add debug info (#9975)
parent
ff7d9432
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
169 additions
and
23 deletions
+169
-23
material.dart
packages/flutter/lib/src/material/material.dart
+1
-1
mergeable_material.dart
packages/flutter/lib/src/material/mergeable_material.dart
+1
-1
box_painter.dart
packages/flutter/lib/src/painting/box_painter.dart
+40
-0
proxy_box.dart
packages/flutter/lib/src/rendering/proxy_box.dart
+38
-15
stack.dart
packages/flutter/lib/src/rendering/stack.dart
+2
-1
banner.dart
packages/flutter/lib/src/widgets/banner.dart
+20
-0
basic.dart
packages/flutter/lib/src/widgets/basic.dart
+57
-4
implicit_animations.dart
packages/flutter/lib/src/widgets/implicit_animations.dart
+1
-1
scroll_configuration.dart
packages/flutter/lib/src/widgets/scroll_configuration.dart
+9
-0
No files found.
packages/flutter/lib/src/material/material.dart
View file @
7a52116d
...
@@ -173,7 +173,7 @@ class Material extends StatefulWidget {
...
@@ -173,7 +173,7 @@ class Material extends StatefulWidget {
void
debugFillDescription
(
List
<
String
>
description
)
{
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
super
.
debugFillDescription
(
description
);
description
.
add
(
'
$type
'
);
description
.
add
(
'
$type
'
);
description
.
add
(
'elevation:
$
elevation
'
);
description
.
add
(
'elevation:
$
{elevation.toStringAsFixed(1)}
'
);
if
(
color
!=
null
)
if
(
color
!=
null
)
description
.
add
(
'color:
$color
'
);
description
.
add
(
'color:
$color
'
);
if
(
textStyle
!=
null
)
{
if
(
textStyle
!=
null
)
{
...
...
packages/flutter/lib/src/material/mergeable_material.dart
View file @
7a52116d
...
@@ -120,7 +120,7 @@ class MergeableMaterial extends StatefulWidget {
...
@@ -120,7 +120,7 @@ class MergeableMaterial extends StatefulWidget {
@override
@override
String
toString
()
{
String
toString
()
{
return
'MergeableMaterial('
return
'MergeableMaterial('
'key:
$key
, mainAxis:
$mainAxis
, elevation:
$
elevation
'
'key:
$key
, mainAxis:
$mainAxis
, elevation:
$
{elevation.toStringAsFixed(1)}
'
')'
;
')'
;
}
}
...
...
packages/flutter/lib/src/painting/box_painter.dart
View file @
7a52116d
...
@@ -136,6 +136,46 @@ class BorderRadius {
...
@@ -136,6 +136,46 @@ class BorderRadius {
@override
@override
String
toString
()
{
String
toString
()
{
if
(
topLeft
==
topRight
&&
topRight
==
bottomRight
&&
bottomRight
==
bottomLeft
)
{
if
(
topLeft
==
Radius
.
zero
)
return
'BorderRadius.zero'
;
if
(
topLeft
.
x
==
topLeft
.
y
)
return
'BorderRadius.circular(
${topLeft.x.toStringAsFixed(1)}
)'
;
return
'BorderRadius.all(
$topLeft
)'
;
}
if
(
topLeft
==
Radius
.
zero
||
topRight
==
Radius
.
zero
||
bottomLeft
==
Radius
.
zero
||
bottomRight
==
Radius
.
zero
)
{
final
StringBuffer
result
=
new
StringBuffer
();
result
.
write
(
'BorderRadius.only('
);
bool
comma
=
false
;
if
(
topLeft
!=
Radius
.
zero
)
{
result
.
write
(
'topLeft:
$topLeft
'
);
comma
=
true
;
}
if
(
topRight
!=
Radius
.
zero
)
{
if
(
comma
)
result
.
write
(
', '
);
result
.
write
(
'topRight:
$topRight
'
);
comma
=
true
;
}
if
(
bottomLeft
!=
Radius
.
zero
)
{
if
(
comma
)
result
.
write
(
', '
);
result
.
write
(
'bottomLeft:
$bottomLeft
'
);
comma
=
true
;
}
if
(
bottomRight
!=
Radius
.
zero
)
{
if
(
comma
)
result
.
write
(
', '
);
result
.
write
(
'bottomRight:
$bottomRight
'
);
}
result
.
write
(
')'
);
return
result
.
toString
();
}
return
'BorderRadius(
$topLeft
,
$topRight
,
$bottomRight
,
$bottomLeft
)'
;
return
'BorderRadius(
$topLeft
,
$topRight
,
$bottomRight
,
$bottomLeft
)'
;
}
}
}
}
...
...
packages/flutter/lib/src/rendering/proxy_box.dart
View file @
7a52116d
...
@@ -926,6 +926,9 @@ abstract class CustomClipper<T> {
...
@@ -926,6 +926,9 @@ abstract class CustomClipper<T> {
/// [shouldReclip] returns false or if the [shouldReclip] method is never
/// [shouldReclip] returns false or if the [shouldReclip] method is never
/// called at all (e.g. if the box changes size).
/// called at all (e.g. if the box changes size).
bool
shouldReclip
(
covariant
CustomClipper
<
T
>
oldClipper
);
bool
shouldReclip
(
covariant
CustomClipper
<
T
>
oldClipper
);
@override
String
toString
()
=>
'
$runtimeType
'
;
}
}
abstract
class
_RenderCustomClip
<
T
>
extends
RenderProxyBox
{
abstract
class
_RenderCustomClip
<
T
>
extends
RenderProxyBox
{
...
@@ -1190,32 +1193,39 @@ class RenderClipPath extends _RenderCustomClip<Path> {
...
@@ -1190,32 +1193,39 @@ class RenderClipPath extends _RenderCustomClip<Path> {
/// Creates a physical model layer that clips its children to a rounded
/// Creates a physical model layer that clips its children to a rounded
/// rectangle.
/// rectangle.
///
/// A physical model layer casts a shadow based on its [elevation].
class
RenderPhysicalModel
extends
_RenderCustomClip
<
RRect
>
{
class
RenderPhysicalModel
extends
_RenderCustomClip
<
RRect
>
{
/// Creates a rounded-rectangular clip.
/// Creates a rounded-rectangular clip.
///
///
/// The [borderRadius] defaults to [BorderRadius.zero], i.e. a rectangle with
/// The [color] is required.
/// right-angled corners.
///
/// The [shape], [elevation], and [color] must not be null.
RenderPhysicalModel
({
RenderPhysicalModel
({
RenderBox
child
,
RenderBox
child
,
BoxShape
shape
,
BoxShape
shape
:
BoxShape
.
rectangle
,
BorderRadius
borderRadius
:
BorderRadius
.
zero
,
BorderRadius
borderRadius
,
double
elevation
,
double
elevation
:
0.0
,
Color
color
,
@required
Color
color
,
})
:
_shape
=
shape
,
})
:
_shape
=
shape
,
_borderRadius
=
borderRadius
,
_borderRadius
=
borderRadius
,
_elevation
=
elevation
,
_elevation
=
elevation
,
_color
=
color
,
_color
=
color
,
super
(
child:
child
)
{
super
(
child:
child
)
{
if
(
shape
==
BoxShape
.
rectangle
)
assert
(
shape
!=
null
);
assert
(
_borderRadius
!=
null
);
assert
(
elevation
!=
null
);
assert
(
color
!=
null
);
}
}
/// The shape of the layer.
/// The shape of the layer.
///
/// Defaults to [BoxShape.rectangle]. The [borderRadius] affects the corners
/// of the rectangle.
BoxShape
get
shape
=>
_shape
;
BoxShape
get
shape
=>
_shape
;
BoxShape
_shape
;
BoxShape
_shape
;
set
shape
(
BoxShape
value
)
{
set
shape
(
BoxShape
value
)
{
assert
(
value
!=
null
);
assert
(
value
!=
null
);
if
(
_
shape
==
value
)
if
(
shape
==
value
)
return
;
return
;
_shape
=
value
;
_shape
=
value
;
_markNeedsClip
();
_markNeedsClip
();
...
@@ -1225,11 +1235,14 @@ class RenderPhysicalModel extends _RenderCustomClip<RRect> {
...
@@ -1225,11 +1235,14 @@ class RenderPhysicalModel extends _RenderCustomClip<RRect> {
///
///
/// Values are clamped so that horizontal and vertical radii sums do not
/// Values are clamped so that horizontal and vertical radii sums do not
/// exceed width/height.
/// exceed width/height.
///
/// This property is ignored if the [shape] is not [BoxShape.rectangle].
///
/// The value null is treated like [BorderRadius.zero].
BorderRadius
get
borderRadius
=>
_borderRadius
;
BorderRadius
get
borderRadius
=>
_borderRadius
;
BorderRadius
_borderRadius
;
BorderRadius
_borderRadius
;
set
borderRadius
(
BorderRadius
value
)
{
set
borderRadius
(
BorderRadius
value
)
{
assert
(
value
!=
null
);
if
(
borderRadius
==
value
)
if
(
_borderRadius
==
value
)
return
;
return
;
_borderRadius
=
value
;
_borderRadius
=
value
;
_markNeedsClip
();
_markNeedsClip
();
...
@@ -1240,7 +1253,7 @@ class RenderPhysicalModel extends _RenderCustomClip<RRect> {
...
@@ -1240,7 +1253,7 @@ class RenderPhysicalModel extends _RenderCustomClip<RRect> {
double
_elevation
;
double
_elevation
;
set
elevation
(
double
value
)
{
set
elevation
(
double
value
)
{
assert
(
value
!=
null
);
assert
(
value
!=
null
);
if
(
_
elevation
==
value
)
if
(
elevation
==
value
)
return
;
return
;
_elevation
=
value
;
_elevation
=
value
;
markNeedsPaint
();
markNeedsPaint
();
...
@@ -1251,7 +1264,7 @@ class RenderPhysicalModel extends _RenderCustomClip<RRect> {
...
@@ -1251,7 +1264,7 @@ class RenderPhysicalModel extends _RenderCustomClip<RRect> {
Color
_color
;
Color
_color
;
set
color
(
Color
value
)
{
set
color
(
Color
value
)
{
assert
(
value
!=
null
);
assert
(
value
!=
null
);
if
(
_
color
==
value
)
if
(
color
==
value
)
return
;
return
;
_color
=
value
;
_color
=
value
;
markNeedsPaint
();
markNeedsPaint
();
...
@@ -1259,8 +1272,9 @@ class RenderPhysicalModel extends _RenderCustomClip<RRect> {
...
@@ -1259,8 +1272,9 @@ class RenderPhysicalModel extends _RenderCustomClip<RRect> {
@override
@override
RRect
get
_defaultClip
{
RRect
get
_defaultClip
{
assert
(
hasSize
);
if
(
_shape
==
BoxShape
.
rectangle
)
{
if
(
_shape
==
BoxShape
.
rectangle
)
{
return
_borderRadius
.
toRRect
(
Offset
.
zero
&
size
);
return
(
borderRadius
??
BorderRadius
.
zero
)
.
toRRect
(
Offset
.
zero
&
size
);
}
else
{
}
else
{
final
Rect
rect
=
Offset
.
zero
&
size
;
final
Rect
rect
=
Offset
.
zero
&
size
;
return
new
RRect
.
fromRectXY
(
rect
,
rect
.
width
/
2
,
rect
.
height
/
2
);
return
new
RRect
.
fromRectXY
(
rect
,
rect
.
width
/
2
,
rect
.
height
/
2
);
...
@@ -1282,9 +1296,18 @@ class RenderPhysicalModel extends _RenderCustomClip<RRect> {
...
@@ -1282,9 +1296,18 @@ class RenderPhysicalModel extends _RenderCustomClip<RRect> {
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
if
(
child
!=
null
)
{
if
(
child
!=
null
)
{
_updateClip
();
_updateClip
();
context
.
pushPhysicalModel
(
needsCompositing
,
offset
,
_clip
.
outerRect
,
_clip
,
_elevation
,
_
color
,
super
.
paint
);
context
.
pushPhysicalModel
(
needsCompositing
,
offset
,
_clip
.
outerRect
,
_clip
,
elevation
,
color
,
super
.
paint
);
}
}
}
}
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
description
.
add
(
'shape:
$shape
'
);
description
.
add
(
'borderRadius:
$borderRadius
'
);
description
.
add
(
'elevation:
${elevation.toStringAsFixed(1)}
'
);
description
.
add
(
'color:
$color
'
);
}
}
}
/// Where to paint a box decoration.
/// Where to paint a box decoration.
...
...
packages/flutter/lib/src/rendering/stack.dart
View file @
7a52116d
...
@@ -526,8 +526,9 @@ class RenderStack extends RenderBox
...
@@ -526,8 +526,9 @@ class RenderStack extends RenderBox
@override
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
super
.
debugFillDescription
(
description
);
description
.
add
(
'overflow:
$overflow
'
);
description
.
add
(
'alignment:
$alignment
'
);
description
.
add
(
'alignment:
$alignment
'
);
description
.
add
(
'fit:
$fit
'
);
description
.
add
(
'overflow:
$overflow
'
);
}
}
}
}
...
...
packages/flutter/lib/src/widgets/banner.dart
View file @
7a52116d
...
@@ -207,6 +207,15 @@ class Banner extends StatelessWidget {
...
@@ -207,6 +207,15 @@ class Banner extends StatelessWidget {
child:
child
,
child:
child
,
);
);
}
}
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
description
.
add
(
'"
$message
"'
);
description
.
add
(
'
$location
'
);
description
.
add
(
'
$color
'
);
'
$textStyle
'
.
split
(
'
\n
'
).
map
((
String
value
)
=>
'text
$value
'
).
forEach
(
description
.
add
);
}
}
}
/// Displays a [Banner] saying "SLOW MODE" when running in checked mode.
/// Displays a [Banner] saying "SLOW MODE" when running in checked mode.
...
@@ -234,4 +243,15 @@ class CheckedModeBanner extends StatelessWidget {
...
@@ -234,4 +243,15 @@ class CheckedModeBanner extends StatelessWidget {
});
});
return
result
;
return
result
;
}
}
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
String
message
=
'disabled'
;
assert
(()
{
message
=
'"SLOW MODE"'
;
return
true
;
});
description
.
add
(
message
);
}
}
}
packages/flutter/lib/src/widgets/basic.dart
View file @
7a52116d
...
@@ -328,6 +328,13 @@ class ClipRect extends SingleChildRenderObjectWidget {
...
@@ -328,6 +328,13 @@ class ClipRect extends SingleChildRenderObjectWidget {
void
didUnmountRenderObject
(
RenderClipRect
renderObject
)
{
void
didUnmountRenderObject
(
RenderClipRect
renderObject
)
{
renderObject
.
clipper
=
null
;
renderObject
.
clipper
=
null
;
}
}
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
if
(
clipper
!=
null
)
description
.
add
(
'clipper:
$clipper
'
);
}
}
}
/// A widget that clips its child using a rounded rectangle.
/// A widget that clips its child using a rounded rectangle.
...
@@ -377,6 +384,15 @@ class ClipRRect extends SingleChildRenderObjectWidget {
...
@@ -377,6 +384,15 @@ class ClipRRect extends SingleChildRenderObjectWidget {
..
borderRadius
=
borderRadius
..
borderRadius
=
borderRadius
..
clipper
=
clipper
;
..
clipper
=
clipper
;
}
}
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
if
(
borderRadius
!=
null
)
description
.
add
(
'
$borderRadius
'
);
if
(
clipper
!=
null
)
description
.
add
(
'clipper:
$clipper
'
);
}
}
}
/// A widget that clips its child using an oval.
/// A widget that clips its child using an oval.
...
@@ -422,6 +438,13 @@ class ClipOval extends SingleChildRenderObjectWidget {
...
@@ -422,6 +438,13 @@ class ClipOval extends SingleChildRenderObjectWidget {
void
didUnmountRenderObject
(
RenderClipOval
renderObject
)
{
void
didUnmountRenderObject
(
RenderClipOval
renderObject
)
{
renderObject
.
clipper
=
null
;
renderObject
.
clipper
=
null
;
}
}
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
if
(
clipper
!=
null
)
description
.
add
(
'clipper:
$clipper
'
);
}
}
}
/// A widget that clips its child using a path.
/// A widget that clips its child using a path.
...
@@ -464,20 +487,33 @@ class ClipPath extends SingleChildRenderObjectWidget {
...
@@ -464,20 +487,33 @@ class ClipPath extends SingleChildRenderObjectWidget {
void
didUnmountRenderObject
(
RenderClipPath
renderObject
)
{
void
didUnmountRenderObject
(
RenderClipPath
renderObject
)
{
renderObject
.
clipper
=
null
;
renderObject
.
clipper
=
null
;
}
}
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
if
(
clipper
!=
null
)
description
.
add
(
'clipper:
$clipper
'
);
}
}
}
/// A widget representing a physical layer that clips its children to a shape.
/// A widget representing a physical layer that clips its children to a shape.
///
/// Physical layers cast shadows based on an [elevation] which is nominally in
/// logical pixels, coming vertically out of the rendering surface.
class
PhysicalModel
extends
SingleChildRenderObjectWidget
{
class
PhysicalModel
extends
SingleChildRenderObjectWidget
{
/// Creates a physical model with a rounded-rectangular clip.
/// Creates a physical model with a rounded-rectangular clip.
///
/// The [color] is required; physical things have a color.
///
/// The [shape], [elevation], and [color] must not be null.
const
PhysicalModel
({
const
PhysicalModel
({
Key
key
,
Key
key
,
@required
this
.
shap
e
,
this
.
shape
:
BoxShape
.
rectangl
e
,
this
.
borderRadius
:
BorderRadius
.
zero
,
this
.
borderRadius
,
@required
this
.
elevation
,
this
.
elevation
:
0.0
,
@required
this
.
color
,
@required
this
.
color
,
Widget
child
,
Widget
child
,
})
:
assert
(
shape
!=
null
),
})
:
assert
(
shape
!=
null
),
assert
(
borderRadius
!=
null
),
assert
(
elevation
!=
null
),
assert
(
elevation
!=
null
),
assert
(
color
!=
null
),
assert
(
color
!=
null
),
super
(
key:
key
,
child:
child
);
super
(
key:
key
,
child:
child
);
...
@@ -510,6 +546,15 @@ class PhysicalModel extends SingleChildRenderObjectWidget {
...
@@ -510,6 +546,15 @@ class PhysicalModel extends SingleChildRenderObjectWidget {
..
elevation
=
elevation
..
elevation
=
elevation
..
color
=
color
;
..
color
=
color
;
}
}
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
description
.
add
(
'shape:
$shape
'
);
description
.
add
(
'borderRadius:
$borderRadius
'
);
description
.
add
(
'elevation:
${elevation.toStringAsFixed(1)}
'
);
description
.
add
(
'color:
$color
'
);
}
}
}
// POSITIONING AND SIZING NODES
// POSITIONING AND SIZING NODES
...
@@ -1712,6 +1757,14 @@ class Stack extends MultiChildRenderObjectWidget {
...
@@ -1712,6 +1757,14 @@ class Stack extends MultiChildRenderObjectWidget {
..
fit
=
fit
..
fit
=
fit
..
overflow
=
overflow
;
..
overflow
=
overflow
;
}
}
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
description
.
add
(
'alignment:
$alignment
'
);
description
.
add
(
'fit:
$fit
'
);
description
.
add
(
'overflow:
$overflow
'
);
}
}
}
/// A [Stack] that shows a single child from a list of children.
/// A [Stack] that shows a single child from a list of children.
...
...
packages/flutter/lib/src/widgets/implicit_animations.dart
View file @
7a52116d
...
@@ -761,7 +761,7 @@ class AnimatedPhysicalModel extends ImplicitlyAnimatedWidget {
...
@@ -761,7 +761,7 @@ class AnimatedPhysicalModel extends ImplicitlyAnimatedWidget {
super
.
debugFillDescription
(
description
);
super
.
debugFillDescription
(
description
);
description
.
add
(
'shape:
$shape
'
);
description
.
add
(
'shape:
$shape
'
);
description
.
add
(
'borderRadius:
$borderRadius
'
);
description
.
add
(
'borderRadius:
$borderRadius
'
);
description
.
add
(
'elevation:
$
elevation
'
);
description
.
add
(
'elevation:
$
{elevation.toStringAsFixed(1)}
'
);
description
.
add
(
'color:
$color
'
);
description
.
add
(
'color:
$color
'
);
description
.
add
(
'animateColor:
$animateColor
'
);
description
.
add
(
'animateColor:
$animateColor
'
);
}
}
...
...
packages/flutter/lib/src/widgets/scroll_configuration.dart
View file @
7a52116d
...
@@ -73,6 +73,9 @@ class ScrollBehavior {
...
@@ -73,6 +73,9 @@ class ScrollBehavior {
/// [ScrollConfiguration] will rebuild using the new [ScrollBehavior]. If this
/// [ScrollConfiguration] will rebuild using the new [ScrollBehavior]. If this
/// method returns false, the rebuilds might be optimized away.
/// method returns false, the rebuilds might be optimized away.
bool
shouldNotify
(
covariant
ScrollBehavior
oldDelegate
)
=>
false
;
bool
shouldNotify
(
covariant
ScrollBehavior
oldDelegate
)
=>
false
;
@override
String
toString
()
=>
'
$runtimeType
'
;
}
}
/// Controls how [Scrollable] widgets behave in a subtree.
/// Controls how [Scrollable] widgets behave in a subtree.
...
@@ -107,4 +110,10 @@ class ScrollConfiguration extends InheritedWidget {
...
@@ -107,4 +110,10 @@ class ScrollConfiguration extends InheritedWidget {
return
behavior
.
runtimeType
!=
oldWidget
.
behavior
.
runtimeType
return
behavior
.
runtimeType
!=
oldWidget
.
behavior
.
runtimeType
||
(
behavior
!=
oldWidget
.
behavior
&&
behavior
.
shouldNotify
(
oldWidget
.
behavior
));
||
(
behavior
!=
oldWidget
.
behavior
&&
behavior
.
shouldNotify
(
oldWidget
.
behavior
));
}
}
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
description
.
add
(
'behavior:
$behavior
'
);
}
}
}
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