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
6af9e6c9
Commit
6af9e6c9
authored
Mar 25, 2016
by
Hixie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
RenderTable
parent
8cbeb2e9
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
935 additions
and
0 deletions
+935
-0
rendering.dart
packages/flutter/lib/rendering.dart
+1
-0
table.dart
packages/flutter/lib/src/rendering/table.dart
+875
-0
print.dart
packages/flutter/lib/src/services/print.dart
+1
-0
table_test.dart
packages/flutter/test/rendering/table_test.dart
+58
-0
No files found.
packages/flutter/lib/rendering.dart
View file @
6af9e6c9
...
...
@@ -44,6 +44,7 @@ export 'src/rendering/rotated_box.dart';
export
'src/rendering/semantics.dart'
;
export
'src/rendering/shifted_box.dart'
;
export
'src/rendering/stack.dart'
;
export
'src/rendering/table.dart'
;
export
'src/rendering/view.dart'
;
export
'src/rendering/viewport.dart'
;
...
...
packages/flutter/lib/src/rendering/table.dart
0 → 100644
View file @
6af9e6c9
// 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:collection'
;
import
'dart:math'
as
math
;
import
'box.dart'
;
import
'object.dart'
;
/// Parent data used by [RenderTable] for its children.
class
TableCellParentData
extends
BoxParentData
{
TableCellVerticalAlignment
verticalAlignment
;
@override
String
toString
()
=>
'
${super.toString()}
;
$verticalAlignment
'
;
}
/// Base class to describe how wide a column in a [RenderTable] should be.
abstract
class
TableColumnWidth
{
const
TableColumnWidth
();
double
minIntrinsicWidth
(
Iterable
<
RenderBox
>
cells
,
double
containerWidth
);
double
maxIntrinsicWidth
(
Iterable
<
RenderBox
>
cells
,
double
containerWidth
);
double
flex
(
Iterable
<
RenderBox
>
cells
)
=>
null
;
@override
String
toString
()
=>
'
$runtimeType
'
;
}
/// Sizes the column according to the intrinsic dimensions of all the
/// cells in that column.
///
/// This is a very expensive way to size a column.
class
IntrinsicColumnWidth
extends
TableColumnWidth
{
const
IntrinsicColumnWidth
();
@override
double
minIntrinsicWidth
(
Iterable
<
RenderBox
>
cells
,
double
containerWidth
)
{
double
result
=
0.0
;
for
(
RenderBox
cell
in
cells
)
result
=
math
.
max
(
result
,
cell
.
getMinIntrinsicWidth
(
const
BoxConstraints
()));
return
result
;
}
@override
double
maxIntrinsicWidth
(
Iterable
<
RenderBox
>
cells
,
double
containerWidth
)
{
double
result
=
0.0
;
for
(
RenderBox
cell
in
cells
)
result
=
math
.
max
(
result
,
cell
.
getMaxIntrinsicWidth
(
const
BoxConstraints
()));
return
result
;
}
}
/// Sizes the column to a specific number of pixels.
///
/// This is the cheapest way to size a column.
class
FixedColumnWidth
extends
TableColumnWidth
{
const
FixedColumnWidth
(
this
.
value
);
final
double
value
;
@override
double
minIntrinsicWidth
(
Iterable
<
RenderBox
>
cells
,
double
containerWidth
)
{
return
value
;
}
@override
double
maxIntrinsicWidth
(
Iterable
<
RenderBox
>
cells
,
double
containerWidth
)
{
return
value
;
}
@override
String
toString
()
=>
'
$runtimeType
(
$value
)'
;
}
/// Sizes the column to a fraction of the table's constraints' maxWidth.
///
/// This is a cheap way to size a column.
class
FractionColumnWidth
extends
TableColumnWidth
{
const
FractionColumnWidth
(
this
.
value
);
final
double
value
;
@override
double
minIntrinsicWidth
(
Iterable
<
RenderBox
>
cells
,
double
containerWidth
)
{
if
(!
containerWidth
.
isFinite
)
return
0.0
;
return
value
*
containerWidth
;
}
@override
double
maxIntrinsicWidth
(
Iterable
<
RenderBox
>
cells
,
double
containerWidth
)
{
if
(!
containerWidth
.
isFinite
)
return
0.0
;
return
value
*
containerWidth
;
}
@override
String
toString
()
=>
'
$runtimeType
(
$value
)'
;
}
/// Sizes the column by taking a part of the remaining space once all
/// the other columns have been laid out.
///
/// For example, if two columns have FlexColumnWidth(), then half the
/// space will go to one and half the space will go to the other.
///
/// This is a cheap way to size a column.
class
FlexColumnWidth
extends
TableColumnWidth
{
const
FlexColumnWidth
([
this
.
value
=
1.0
]);
final
double
value
;
@override
double
minIntrinsicWidth
(
Iterable
<
RenderBox
>
cells
,
double
containerWidth
)
{
return
0.0
;
}
@override
double
maxIntrinsicWidth
(
Iterable
<
RenderBox
>
cells
,
double
containerWidth
)
{
return
0.0
;
}
@override
double
flex
(
Iterable
<
RenderBox
>
cells
)
{
return
value
;
}
@override
String
toString
()
=>
'
$runtimeType
(
$value
)'
;
}
/// Sizes the column such that it is the size that is the maximum of
/// two column width specifications.
///
/// For example, to have a column be 10% of the container width or
/// 100px, whichever is bigger, you could use:
///
/// const MaxColumnWidth(const FixedColumnWidth(100.0), FractionColumnWidth(0.1))
///
/// Both specifications are evaluated, so if either specification is
/// expensive, so is this.
class
MaxColumnWidth
extends
TableColumnWidth
{
const
MaxColumnWidth
(
this
.
a
,
this
.
b
);
final
TableColumnWidth
a
;
final
TableColumnWidth
b
;
@override
double
minIntrinsicWidth
(
Iterable
<
RenderBox
>
cells
,
double
containerWidth
)
{
return
math
.
max
(
a
.
minIntrinsicWidth
(
cells
,
containerWidth
),
b
.
minIntrinsicWidth
(
cells
,
containerWidth
)
);
}
@override
double
maxIntrinsicWidth
(
Iterable
<
RenderBox
>
cells
,
double
containerWidth
)
{
return
math
.
max
(
a
.
maxIntrinsicWidth
(
cells
,
containerWidth
),
b
.
maxIntrinsicWidth
(
cells
,
containerWidth
)
);
}
@override
double
flex
(
Iterable
<
RenderBox
>
cells
)
{
double
aFlex
=
a
.
flex
(
cells
);
if
(
aFlex
==
null
)
return
b
.
flex
(
cells
);
return
math
.
max
(
aFlex
,
b
.
flex
(
cells
));
}
@override
String
toString
()
=>
'
$runtimeType
(
$a
,
$b
)'
;
}
/// Sizes the column such that it is the size that is the minimum of
/// two column width specifications.
///
/// For example, to have a column be 10% of the container width but
/// never bigger than 100px, you could use:
///
/// const MinColumnWidth(const FixedColumnWidth(100.0), FractionColumnWidth(0.1))
///
/// Both specifications are evaluated, so if either specification is
/// expensive, so is this.
class
MinColumnWidth
extends
TableColumnWidth
{
const
MinColumnWidth
(
this
.
a
,
this
.
b
);
// at most as big as a or b
final
TableColumnWidth
a
;
final
TableColumnWidth
b
;
@override
double
minIntrinsicWidth
(
Iterable
<
RenderBox
>
cells
,
double
containerWidth
)
{
return
math
.
min
(
a
.
minIntrinsicWidth
(
cells
,
containerWidth
),
b
.
minIntrinsicWidth
(
cells
,
containerWidth
)
);
}
@override
double
maxIntrinsicWidth
(
Iterable
<
RenderBox
>
cells
,
double
containerWidth
)
{
return
math
.
min
(
a
.
maxIntrinsicWidth
(
cells
,
containerWidth
),
b
.
maxIntrinsicWidth
(
cells
,
containerWidth
)
);
}
@override
double
flex
(
Iterable
<
RenderBox
>
cells
)
{
double
aFlex
=
a
.
flex
(
cells
);
if
(
aFlex
==
null
)
return
b
.
flex
(
cells
);
return
math
.
min
(
aFlex
,
b
.
flex
(
cells
));
}
@override
String
toString
()
=>
'
$runtimeType
(
$a
,
$b
)'
;
}
/// Border specification for [RenderTable].
///
/// This is like [Border], with the addition of two sides: the inner
/// horizontal borders and the inner vertical borders.
class
TableBorder
extends
Border
{
const
TableBorder
({
BorderSide
top:
BorderSide
.
none
,
BorderSide
right:
BorderSide
.
none
,
BorderSide
bottom:
BorderSide
.
none
,
BorderSide
left:
BorderSide
.
none
,
this
.
horizontalInside
:
BorderSide
.
none
,
this
.
verticalInside
:
BorderSide
.
none
})
:
super
(
top:
top
,
right:
right
,
bottom:
bottom
,
left:
left
);
factory
TableBorder
.
all
({
Color
color:
const
Color
(
0xFF000000
),
double
width:
1.0
})
{
final
BorderSide
side
=
new
BorderSide
(
color:
color
,
width:
width
);
return
new
TableBorder
(
top:
side
,
right:
side
,
bottom:
side
,
left:
side
,
horizontalInside:
side
,
verticalInside:
side
);
}
factory
TableBorder
.
symmetric
({
BorderSide
inside:
BorderSide
.
none
,
BorderSide
outside:
BorderSide
.
none
})
{
return
new
TableBorder
(
top:
outside
,
right:
outside
,
bottom:
outside
,
left:
outside
,
horizontalInside:
inside
,
verticalInside:
inside
);
}
final
BorderSide
horizontalInside
;
final
BorderSide
verticalInside
;
@override
TableBorder
scale
(
double
t
)
{
return
new
TableBorder
(
top:
top
.
copyWith
(
width:
t
*
top
.
width
),
right:
right
.
copyWith
(
width:
t
*
right
.
width
),
bottom:
bottom
.
copyWith
(
width:
t
*
bottom
.
width
),
left:
left
.
copyWith
(
width:
t
*
left
.
width
),
horizontalInside:
horizontalInside
.
copyWith
(
width:
t
*
horizontalInside
.
width
),
verticalInside:
verticalInside
.
copyWith
(
width:
t
*
verticalInside
.
width
)
);
}
static
TableBorder
lerp
(
TableBorder
a
,
TableBorder
b
,
double
t
)
{
if
(
a
==
null
&&
b
==
null
)
return
null
;
if
(
a
==
null
)
return
b
.
scale
(
t
);
if
(
b
==
null
)
return
a
.
scale
(
1.0
-
t
);
return
new
TableBorder
(
top:
BorderSide
.
lerp
(
a
.
top
,
b
.
top
,
t
),
right:
BorderSide
.
lerp
(
a
.
right
,
b
.
right
,
t
),
bottom:
BorderSide
.
lerp
(
a
.
bottom
,
b
.
bottom
,
t
),
left:
BorderSide
.
lerp
(
a
.
left
,
b
.
left
,
t
),
horizontalInside:
BorderSide
.
lerp
(
a
.
horizontalInside
,
b
.
horizontalInside
,
t
),
verticalInside:
BorderSide
.
lerp
(
a
.
verticalInside
,
b
.
verticalInside
,
t
)
);
}
@override
bool
operator
==(
dynamic
other
)
{
if
(
super
!=
other
)
return
false
;
final
TableBorder
typedOther
=
other
;
return
horizontalInside
==
typedOther
.
horizontalInside
&&
verticalInside
==
typedOther
.
verticalInside
;
}
@override
int
get
hashCode
=>
hashValues
(
super
.
hashCode
,
horizontalInside
,
verticalInside
);
@override
String
toString
()
=>
'TableBorder(
$top
,
$right
,
$bottom
,
$left
,
$horizontalInside
,
$verticalInside
)'
;
}
/// Vertical alignment options for cells in [RenderTable] objects.
///
/// This is specified using [TableCellParentData] objects on the
/// [RenderObject.parentData] of the children of the [RenderTable].
enum
TableCellVerticalAlignment
{
/// Cells with this alignment are placed with their top at the top of the row.
top
,
/// Cells with this alignment are vertically centered in the row.
middle
,
/// Cells with this alignment are placed with their bottom at the bottom of the row.
bottom
,
/// Cells with this alignment are aligned such that they all share the same
/// baseline. Cells with no baseline are top-aligned instead. The baseline
/// used is specified by [RenderTable.baseline]. It is not valid to use the
/// baseline value if [RenderTable.baseline] is not specified.
baseline
,
/// Cells with this alignment are sized to be as tall as the row, then made to fit the row.
/// If all the cells have this alignment, then the row will have zero height.
fill
}
/// A table where the columns and rows are sized to fit the contents of the cells.
class
RenderTable
extends
RenderBox
{
RenderTable
({
int
columns
,
int
rows
,
Map
<
int
,
TableColumnWidth
>
columnWidths
,
TableColumnWidth
defaultColumnWidth:
const
FlexColumnWidth
(
1.0
),
TableBorder
border
,
TableCellVerticalAlignment
defaultVerticalAlignment:
TableCellVerticalAlignment
.
top
,
TextBaseline
textBaseline
,
List
<
List
<
RenderBox
>>
children
})
{
assert
(
columns
==
null
||
columns
>=
0
);
assert
(
rows
==
null
||
rows
>=
0
);
assert
(
rows
==
null
||
children
==
null
);
assert
(
defaultColumnWidth
!=
null
);
_columns
=
columns
??
(
children
!=
null
&&
children
.
length
>
0
?
children
.
first
.
length
:
0
);
_rows
=
rows
??
0
;
_children
=
new
List
<
RenderBox
>()..
length
=
_columns
*
_rows
;
_columnWidths
=
columnWidths
??
new
HashMap
<
int
,
TableColumnWidth
>();
_defaultColumnWidth
=
defaultColumnWidth
;
_border
=
border
;
_defaultVerticalAlignment
=
defaultVerticalAlignment
;
_textBaseline
=
textBaseline
;
if
(
children
!=
null
)
{
for
(
List
<
RenderBox
>
row
in
children
)
addRow
(
row
);
}
}
// TODO(ianh): Add a 'decoration' field to the children's parent data, to paint on each cell.
// Children are stored in row-major order.
// _children.length must be rows * columns
List
<
RenderBox
>
_children
=
const
<
RenderBox
>[];
int
get
columns
=>
_columns
;
int
_columns
;
void
set
columns
(
int
value
)
{
assert
(
value
!=
null
);
assert
(
value
>=
0
);
if
(
value
==
columns
)
return
;
int
oldColumns
=
columns
;
List
<
RenderBox
>
oldChildren
=
_children
;
_columns
=
value
;
_children
=
new
List
<
RenderBox
>()..
length
=
columns
*
rows
;
int
columnsToCopy
=
math
.
min
(
columns
,
oldColumns
);
for
(
int
y
=
0
;
y
<
rows
;
y
+=
1
)
{
for
(
int
x
=
0
;
x
<
columnsToCopy
;
x
+=
1
)
_children
[
x
+
y
*
columns
]
=
oldChildren
[
x
+
y
*
oldColumns
];
}
markNeedsLayout
();
}
int
get
rows
=>
_rows
;
int
_rows
;
void
set
rows
(
int
value
)
{
assert
(
value
!=
null
);
assert
(
value
>=
0
);
if
(
value
==
rows
)
return
;
_rows
=
value
;
_children
.
length
=
columns
*
rows
;
markNeedsLayout
();
}
Map
<
int
,
TableColumnWidth
>
_columnWidths
;
void
setColumnWidths
(
Map
<
int
,
TableColumnWidth
>
value
)
{
assert
(
value
!=
null
);
if
(
_columnWidths
==
value
)
return
;
_columnWidths
=
value
;
markNeedsLayout
();
}
void
setColumnWidth
(
int
column
,
TableColumnWidth
value
)
{
if
(
_columnWidths
[
column
]
==
value
)
return
;
_columnWidths
[
column
]
=
value
;
markNeedsLayout
();
}
TableColumnWidth
get
defaultColumnWidth
=>
_defaultColumnWidth
;
TableColumnWidth
_defaultColumnWidth
;
void
set
defaultColumnWidth
(
TableColumnWidth
value
)
{
assert
(
value
!=
null
);
if
(
defaultColumnWidth
==
value
)
return
;
_defaultColumnWidth
=
value
;
markNeedsLayout
();
}
TableBorder
get
border
=>
_border
;
TableBorder
_border
;
void
set
border
(
TableBorder
value
)
{
if
(
border
==
value
)
return
;
_border
=
value
;
markNeedsPaint
();
}
TableCellVerticalAlignment
get
defaultVerticalAlignment
=>
_defaultVerticalAlignment
;
TableCellVerticalAlignment
_defaultVerticalAlignment
;
void
set
defaultVerticalAlignment
(
TableCellVerticalAlignment
value
)
{
if
(
_defaultVerticalAlignment
==
value
)
return
;
_defaultVerticalAlignment
=
value
;
markNeedsLayout
();
}
TextBaseline
get
textBaseline
=>
_textBaseline
;
TextBaseline
_textBaseline
;
void
set
textBaseline
(
TextBaseline
value
)
{
if
(
_textBaseline
==
value
)
return
;
_textBaseline
=
value
;
markNeedsLayout
();
}
@override
void
setupParentData
(
RenderObject
child
)
{
if
(
child
.
parentData
is
!
TableCellParentData
)
child
.
parentData
=
new
TableCellParentData
();
}
void
setFlatChildren
(
int
columns
,
List
<
RenderBox
>
cells
)
{
if
(
cells
==
_children
&&
columns
==
_columns
)
return
;
for
(
RenderBox
oldChild
in
cells
)
{
if
(
oldChild
!=
null
)
dropChild
(
oldChild
);
}
assert
(
columns
>=
0
);
if
(
columns
==
0
)
{
assert
(
cells
==
null
||
cells
.
length
==
0
);
if
(
_children
.
length
==
0
)
return
;
_columns
=
0
;
_rows
=
0
;
_children
.
clear
();
markNeedsLayout
();
return
;
}
assert
(
cells
!=
null
);
assert
(
cells
.
length
%
columns
==
0
);
_columns
=
columns
;
_rows
=
cells
.
length
%
columns
;
_children
=
cells
;
for
(
RenderBox
cell
in
cells
)
{
if
(
cell
!=
null
)
adoptChild
(
cell
);
}
markNeedsLayout
();
}
void
setChildren
(
List
<
List
<
RenderBox
>>
cells
)
{
if
(
cells
==
null
)
{
setFlatChildren
(
0
,
null
);
return
;
}
for
(
RenderBox
oldChild
in
cells
)
{
if
(
oldChild
!=
null
)
dropChild
(
oldChild
);
}
_columns
=
cells
.
length
>
0
?
cells
.
first
.
length
:
0
;
_rows
=
0
;
_children
.
clear
();
for
(
List
<
RenderBox
>
row
in
cells
)
addRow
(
row
);
}
void
addRow
(
List
<
RenderBox
>
cells
)
{
assert
(
cells
.
length
==
columns
);
_rows
+=
1
;
_children
.
addAll
(
cells
);
for
(
RenderBox
cell
in
cells
)
{
if
(
cell
!=
null
)
adoptChild
(
cell
);
}
markNeedsLayout
();
}
void
setChild
(
int
x
,
int
y
,
RenderBox
value
)
{
assert
(
x
!=
null
);
assert
(
y
!=
null
);
assert
(
x
>=
0
&&
x
<
columns
&&
y
>=
0
&&
y
<
rows
);
final
int
xy
=
x
+
y
*
columns
;
RenderBox
oldChild
=
_children
[
xy
];
if
(
oldChild
!=
null
)
dropChild
(
oldChild
);
_children
[
xy
]
=
value
;
if
(
value
!=
null
)
adoptChild
(
value
);
}
@override
void
attach
(
PipelineOwner
owner
)
{
super
.
attach
(
owner
);
for
(
RenderBox
child
in
_children
)
child
?.
attach
(
owner
);
}
@override
void
detach
()
{
super
.
detach
();
for
(
RenderBox
child
in
_children
)
child
?.
detach
();
}
@override
void
visitChildren
(
RenderObjectVisitor
visitor
)
{
for
(
RenderBox
child
in
_children
)
{
if
(
child
!=
null
)
visitor
(
child
);
}
}
@override
double
getMinIntrinsicWidth
(
BoxConstraints
constraints
)
{
assert
(
constraints
.
debugAssertIsNormalized
);
double
totalMinWidth
=
0.0
;
for
(
int
x
=
0
;
x
<
columns
;
x
+=
1
)
{
TableColumnWidth
columnWidth
=
_columnWidths
[
x
]
??
defaultColumnWidth
;
Iterable
<
RenderBox
>
columnCells
=
column
(
x
);
totalMinWidth
+=
columnWidth
.
minIntrinsicWidth
(
columnCells
,
constraints
.
maxWidth
);
}
return
constraints
.
constrainWidth
(
totalMinWidth
);
}
@override
double
getMaxIntrinsicWidth
(
BoxConstraints
constraints
)
{
assert
(
constraints
.
debugAssertIsNormalized
);
double
totalMaxWidth
=
0.0
;
for
(
int
x
=
0
;
x
<
columns
;
x
+=
1
)
{
TableColumnWidth
columnWidth
=
_columnWidths
[
x
]
??
defaultColumnWidth
;
Iterable
<
RenderBox
>
columnCells
=
column
(
x
);
totalMaxWidth
+=
columnWidth
.
maxIntrinsicWidth
(
columnCells
,
constraints
.
maxWidth
);
}
return
constraints
.
constrainWidth
(
totalMaxWidth
);
}
@override
double
getMinIntrinsicHeight
(
BoxConstraints
constraints
)
{
// winner of the 2016 world's most expensive intrinsic dimension function award
// honorable mention, most likely to improve if taught about memoization award
assert
(
constraints
.
debugAssertIsNormalized
);
final
List
<
double
>
widths
=
computeColumnWidths
(
constraints
);
double
rowTop
=
0.0
;
for
(
int
y
=
0
;
y
<
rows
;
y
+=
1
)
{
double
rowHeight
=
0.0
;
for
(
int
x
=
0
;
x
<
columns
;
x
+=
1
)
{
final
int
xy
=
x
+
y
*
columns
;
RenderBox
child
=
_children
[
xy
];
if
(
child
!=
null
)
rowHeight
=
math
.
max
(
rowHeight
,
child
.
getMaxIntrinsicHeight
(
new
BoxConstraints
.
tightFor
(
width:
widths
[
x
])));
}
rowTop
+=
rowHeight
;
}
return
constraints
.
constrainHeight
(
rowTop
);
}
@override
double
getMaxIntrinsicHeight
(
BoxConstraints
constraints
)
{
return
getMinIntrinsicHeight
(
constraints
);
}
double
_baselineDistance
;
@override
double
computeDistanceToActualBaseline
(
TextBaseline
baseline
)
{
// returns the baseline of the first cell that has a baseline in the first row
assert
(!
needsLayout
);
return
_baselineDistance
;
}
Iterable
<
RenderBox
>
column
(
int
x
)
sync
*
{
for
(
int
y
=
0
;
y
<
rows
;
y
+=
1
)
{
final
int
xy
=
x
+
y
*
columns
;
RenderBox
child
=
_children
[
xy
];
if
(
child
!=
null
)
yield
child
;
}
}
Iterable
<
RenderBox
>
row
(
int
y
)
sync
*
{
final
int
start
=
y
*
columns
;
final
int
end
=
(
y
+
1
)
*
columns
;
for
(
int
xy
=
start
;
xy
<
end
;
xy
+=
1
)
{
RenderBox
child
=
_children
[
xy
];
if
(
child
!=
null
)
yield
child
;
}
}
List
<
double
>
computeColumnWidths
(
BoxConstraints
constraints
)
{
final
List
<
double
>
widths
=
new
List
<
double
>(
columns
);
final
List
<
double
>
flexes
=
new
List
<
double
>(
columns
);
double
totalMinWidth
=
0.0
;
double
totalMaxWidth
=
constraints
.
maxWidth
.
isFinite
?
constraints
.
maxWidth
:
0.0
;
double
totalFlex
=
0.0
;
for
(
int
x
=
0
;
x
<
columns
;
x
+=
1
)
{
TableColumnWidth
columnWidth
=
_columnWidths
[
x
]
??
defaultColumnWidth
;
Iterable
<
RenderBox
>
columnCells
=
column
(
x
);
double
minIntrinsicWidth
=
columnWidth
.
minIntrinsicWidth
(
columnCells
,
constraints
.
maxWidth
);
widths
[
x
]
=
minIntrinsicWidth
;
totalMinWidth
+=
minIntrinsicWidth
;
if
(!
constraints
.
maxWidth
.
isFinite
)
{
double
maxIntrinsicWidth
=
columnWidth
.
maxIntrinsicWidth
(
columnCells
,
constraints
.
maxWidth
);
assert
(
minIntrinsicWidth
<=
maxIntrinsicWidth
);
totalMaxWidth
+=
maxIntrinsicWidth
;
}
double
flex
=
columnWidth
.
flex
(
columnCells
);
if
(
flex
!=
null
)
{
assert
(
flex
!=
0.0
);
flexes
[
x
]
=
flex
;
totalFlex
+=
flex
;
}
}
assert
(!
widths
.
any
((
double
value
)
=>
value
==
null
));
// table is going to be the biggest of:
// - the incoming minimum width
// - the sum of the cells' minimum widths
// - the incoming maximum width if it is finite, or else the table's ideal shrink-wrap width
double
tableWidth
=
math
.
max
(
constraints
.
minWidth
,
math
.
max
(
totalMinWidth
,
totalMaxWidth
));
double
remainingWidth
=
tableWidth
-
totalMinWidth
;
if
(
remainingWidth
>
0.0
)
{
if
(
totalFlex
>
0.0
)
{
for
(
int
x
=
0
;
x
<
columns
;
x
+=
1
)
{
if
(
flexes
[
x
]
!=
null
)
{
widths
[
x
]
+=
math
.
max
((
flexes
[
x
]
/
totalFlex
)
*
remainingWidth
-
widths
[
x
],
0.0
);
}
}
}
else
{
for
(
int
x
=
0
;
x
<
columns
;
x
+=
1
)
widths
[
x
]
+=
remainingWidth
/
columns
;
}
}
return
widths
;
}
// cache the table geometry for painting purposes
List
<
double
>
_rowTops
=
<
double
>[];
List
<
double
>
_columnLefts
;
@override
void
performLayout
()
{
if
(
rows
*
columns
==
0
)
{
size
=
constraints
.
constrain
(
const
Size
(
double
.
INFINITY
,
0.0
));
return
;
}
final
List
<
double
>
widths
=
computeColumnWidths
(
constraints
);
final
List
<
double
>
positions
=
new
List
<
double
>(
columns
);
_rowTops
.
clear
();
positions
[
0
]
=
0.0
;
for
(
int
x
=
1
;
x
<
columns
;
x
+=
1
)
positions
[
x
]
=
positions
[
x
-
1
]
+
widths
[
x
-
1
];
_columnLefts
=
positions
;
assert
(!
positions
.
any
((
double
value
)
=>
value
==
null
));
_baselineDistance
=
null
;
// then, lay out each row
double
rowTop
=
0.0
;
for
(
int
y
=
0
;
y
<
rows
;
y
+=
1
)
{
_rowTops
.
add
(
rowTop
);
double
rowHeight
=
0.0
;
bool
haveBaseline
=
false
;
double
beforeBaselineDistance
=
0.0
;
double
afterBaselineDistance
=
0.0
;
List
<
double
>
baselines
=
new
List
<
double
>(
columns
);
for
(
int
x
=
0
;
x
<
columns
;
x
+=
1
)
{
final
int
xy
=
x
+
y
*
columns
;
RenderBox
child
=
_children
[
xy
];
if
(
child
!=
null
)
{
TableCellParentData
childParentData
=
child
.
parentData
;
switch
(
childParentData
.
verticalAlignment
??
defaultVerticalAlignment
)
{
case
TableCellVerticalAlignment
.
baseline
:
assert
(
textBaseline
!=
null
);
child
.
layout
(
new
BoxConstraints
.
tightFor
(
width:
widths
[
x
]),
parentUsesSize:
true
);
double
childBaseline
=
child
.
getDistanceToBaseline
(
textBaseline
,
onlyReal:
true
);
if
(
childBaseline
!=
null
)
{
beforeBaselineDistance
=
math
.
max
(
beforeBaselineDistance
,
childBaseline
);
afterBaselineDistance
=
math
.
max
(
afterBaselineDistance
,
child
.
size
.
height
-
childBaseline
);
baselines
[
x
]
=
childBaseline
;
haveBaseline
=
true
;
}
else
{
rowHeight
=
math
.
max
(
rowHeight
,
child
.
size
.
height
);
childParentData
.
offset
=
new
Offset
(
positions
[
x
],
rowTop
);
}
break
;
case
TableCellVerticalAlignment
.
top
:
case
TableCellVerticalAlignment
.
middle
:
case
TableCellVerticalAlignment
.
bottom
:
child
.
layout
(
new
BoxConstraints
.
tightFor
(
width:
widths
[
x
]),
parentUsesSize:
true
);
rowHeight
=
math
.
max
(
rowHeight
,
child
.
size
.
height
);
break
;
case
TableCellVerticalAlignment
.
fill
:
break
;
}
}
}
if
(
haveBaseline
)
{
if
(
y
==
0
)
_baselineDistance
=
beforeBaselineDistance
;
rowHeight
=
math
.
max
(
rowHeight
,
beforeBaselineDistance
+
afterBaselineDistance
);
}
for
(
int
x
=
0
;
x
<
columns
;
x
+=
1
)
{
final
int
xy
=
x
+
y
*
columns
;
RenderBox
child
=
_children
[
xy
];
if
(
child
!=
null
)
{
final
TableCellParentData
childParentData
=
child
.
parentData
;
switch
(
childParentData
.
verticalAlignment
??
defaultVerticalAlignment
)
{
case
TableCellVerticalAlignment
.
baseline
:
if
(
baselines
[
x
]
!=
null
)
childParentData
.
offset
=
new
Offset
(
positions
[
x
],
rowTop
+
beforeBaselineDistance
-
baselines
[
x
]);
break
;
case
TableCellVerticalAlignment
.
top
:
childParentData
.
offset
=
new
Offset
(
positions
[
x
],
rowTop
);
break
;
case
TableCellVerticalAlignment
.
middle
:
childParentData
.
offset
=
new
Offset
(
positions
[
x
],
rowTop
+
(
rowHeight
-
child
.
size
.
height
)
/
2.0
);
break
;
case
TableCellVerticalAlignment
.
bottom
:
childParentData
.
offset
=
new
Offset
(
positions
[
x
],
rowTop
+
rowHeight
-
child
.
size
.
height
);
break
;
case
TableCellVerticalAlignment
.
fill
:
child
.
layout
(
new
BoxConstraints
.
tightFor
(
width:
widths
[
x
],
height:
rowHeight
));
childParentData
.
offset
=
new
Offset
(
positions
[
x
],
rowTop
);
break
;
}
}
}
rowTop
+=
rowHeight
;
}
size
=
constraints
.
constrain
(
new
Size
(
positions
.
last
+
widths
.
last
,
rowTop
));
assert
(
_rowTops
.
length
==
rows
);
}
@override
bool
hitTestChildren
(
HitTestResult
result
,
{
Point
position
})
{
for
(
int
index
=
_children
.
length
-
1
;
index
>=
0
;
index
-=
1
)
{
RenderBox
child
=
_children
[
index
];
if
(
child
!=
null
)
{
final
BoxParentData
childParentData
=
child
.
parentData
;
Point
transformed
=
new
Point
(
position
.
x
-
childParentData
.
offset
.
dx
,
position
.
y
-
childParentData
.
offset
.
dy
);
if
(
child
.
hitTest
(
result
,
position:
transformed
))
return
true
;
}
}
return
false
;
}
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
if
(
rows
*
columns
==
0
)
return
;
assert
(
_rowTops
.
length
==
rows
);
for
(
int
index
=
0
;
index
<
_children
.
length
;
index
+=
1
)
{
RenderBox
child
=
_children
[
index
];
if
(
child
!=
null
)
{
final
BoxParentData
childParentData
=
child
.
parentData
;
context
.
paintChild
(
child
,
childParentData
.
offset
+
offset
);
}
}
Rect
bounds
=
offset
&
size
;
Canvas
canvas
=
context
.
canvas
;
canvas
.
saveLayer
(
bounds
,
new
Paint
());
switch
(
border
.
verticalInside
.
style
)
{
case
BorderStyle
.
solid
:
Paint
paint
=
new
Paint
()
..
color
=
border
.
verticalInside
.
color
..
strokeWidth
=
border
.
verticalInside
.
width
..
style
=
PaintingStyle
.
stroke
;
Path
path
=
new
Path
();
for
(
int
x
=
1
;
x
<
columns
;
x
+=
1
)
{
path
.
moveTo
(
bounds
.
left
+
_columnLefts
[
x
],
bounds
.
top
);
path
.
lineTo
(
bounds
.
left
+
_columnLefts
[
x
],
bounds
.
bottom
);
}
canvas
.
drawPath
(
path
,
paint
);
break
;
case
BorderStyle
.
none
:
break
;
}
switch
(
border
.
horizontalInside
.
style
)
{
case
BorderStyle
.
solid
:
Paint
paint
=
new
Paint
()
..
color
=
border
.
horizontalInside
.
color
..
strokeWidth
=
border
.
horizontalInside
.
width
..
style
=
PaintingStyle
.
stroke
;
Path
path
=
new
Path
();
for
(
int
y
=
1
;
y
<
rows
;
y
+=
1
)
{
path
.
moveTo
(
bounds
.
left
,
bounds
.
top
+
_rowTops
[
y
]);
path
.
lineTo
(
bounds
.
right
,
bounds
.
top
+
_rowTops
[
y
]);
}
canvas
.
drawPath
(
path
,
paint
);
break
;
case
BorderStyle
.
none
:
break
;
}
border
.
paint
(
canvas
,
bounds
);
canvas
.
restore
();
}
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
if
(
border
!=
null
)
description
.
add
(
'border:
$border
'
);
if
(
_columnWidths
.
length
>
0
)
description
.
add
(
'specified column widths:
$_columnWidths
'
);
description
.
add
(
'default column width:
$defaultColumnWidth
'
);
description
.
add
(
'table size:
$columns
\
u00D7
$rows
'
);
if
(!
needsLayout
)
{
description
.
add
(
'column offsets:
${ _columnLefts ?? "unknown" }
'
);
description
.
add
(
'row offsets:
${ _rowTops ?? "unknown" }
'
);
}
}
@override
String
debugDescribeChildren
(
String
prefix
)
{
StringBuffer
result
=
new
StringBuffer
();
result
.
writeln
(
'
$prefix
\
u2502'
);
int
lastIndex
=
_children
.
length
-
1
;
for
(
int
y
=
0
;
y
<
rows
;
y
+=
1
)
{
for
(
int
x
=
0
;
x
<
columns
;
x
+=
1
)
{
final
int
xy
=
x
+
y
*
columns
;
RenderBox
child
=
_children
[
xy
];
if
(
child
!=
null
)
{
if
(
xy
<
lastIndex
)
{
result
.
write
(
'
${child.toStringDeep("$prefix \u251C\u2500child ($x, $y): ", "$prefix \u2502")}
'
);
}
else
{
result
.
write
(
'
${child.toStringDeep("$prefix \u2514\u2500child ($x, $y): ", "$prefix ")}
'
);
}
}
else
{
if
(
xy
<
lastIndex
)
{
result
.
writeln
(
'
$prefix
\
u251C
\
u2500child (
$x
,
$y
) is null'
);
}
else
{
result
.
writeln
(
'
$prefix
\
u2514
\
u2500child (
$x
,
$y
) is null'
);
}
}
}
}
return
result
.
toString
();
}
}
packages/flutter/lib/src/services/print.dart
View file @
6af9e6c9
...
...
@@ -14,6 +14,7 @@ import 'dart:collection';
/// [debugDumpApp]) and to the Dart [print] method can result in out-of-order
/// messages in the logs.
void
debugPrint
(
String
message
)
{
print
(
message
);
_debugPrintBuffer
.
addAll
(
message
.
split
(
'
\n
'
));
if
(!
_debugPrintScheduled
)
_debugPrintTask
();
...
...
packages/flutter/test/rendering/table_test.dart
0 → 100644
View file @
6af9e6c9
// 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:test/test.dart'
;
import
'rendering_tester.dart'
;
RenderBox
sizedBox
(
double
width
,
double
height
)
{
return
new
RenderConstrainedBox
(
additionalConstraints:
new
BoxConstraints
.
tight
(
new
Size
(
width
,
height
))
);
}
void
main
(
)
{
test
(
'Table control test; tight'
,
()
{
RenderTable
table
;
layout
(
table
=
new
RenderTable
());
expect
(
table
.
size
.
width
,
equals
(
800.0
));
expect
(
table
.
size
.
height
,
equals
(
600.0
));
});
test
(
'Table control test; loose'
,
()
{
RenderTable
table
;
layout
(
new
RenderPositionedBox
(
child:
table
=
new
RenderTable
()));
expect
(
table
.
size
,
equals
(
const
Size
(
800.0
,
0.0
)));
});
test
(
'Table test: combinations'
,
()
{
RenderTable
table
;
layout
(
new
RenderPositionedBox
(
child:
table
=
new
RenderTable
(
columns:
5
,
rows:
5
,
defaultColumnWidth:
const
IntrinsicColumnWidth
(),
defaultVerticalAlignment:
TableCellVerticalAlignment
.
baseline
,
textBaseline:
TextBaseline
.
alphabetic
)));
expect
(
table
.
size
,
equals
(
const
Size
(
800.0
,
0.0
)));
table
.
setChild
(
2
,
4
,
sizedBox
(
100.0
,
200.0
));
pumpFrame
();
expect
(
table
.
size
,
equals
(
new
Size
(
800.0
,
200.0
)));
table
.
setChild
(
0
,
0
,
sizedBox
(
10.0
,
30.0
));
table
.
setChild
(
1
,
0
,
sizedBox
(
20.0
,
20.0
));
table
.
setChild
(
2
,
0
,
sizedBox
(
30.0
,
10.0
));
pumpFrame
();
expect
(
table
.
size
,
equals
(
new
Size
(
800.0
,
230.0
)));
});
}
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