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
ea451690
Commit
ea451690
authored
Sep 23, 2015
by
Hixie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fn3: Port HomogeneousViewport
parent
1836ca61
Changes
4
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
524 additions
and
150 deletions
+524
-150
fn3.dart
packages/flutter/lib/src/fn3.dart
+1
-0
framework.dart
packages/flutter/lib/src/fn3/framework.dart
+175
-150
homogeneous_viewport.dart
packages/flutter/lib/src/fn3/homogeneous_viewport.dart
+176
-0
homogeneous_viewport_test.dart
packages/unit/test/fn3/homogeneous_viewport_test.dart
+172
-0
No files found.
packages/flutter/lib/src/fn3.dart
View file @
ea451690
...
...
@@ -7,3 +7,4 @@ library fn3;
export
'fn3/basic.dart'
;
export
'fn3/framework.dart'
;
export
'fn3/binding.dart'
;
export
'fn3/homogeneous_viewport.dart'
;
packages/flutter/lib/src/fn3/framework.dart
View file @
ea451690
This diff is collapsed.
Click to expand it.
packages/flutter/lib/src/fn3/homogeneous_viewport.dart
0 → 100644
View file @
ea451690
// 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:math'
as
math
;
import
'package:sky/rendering.dart'
;
import
'package:sky/src/fn3/framework.dart'
;
import
'package:sky/src/fn3/basic.dart'
;
typedef
List
<
Widget
>
ListBuilder
(
int
startIndex
,
int
count
,
BuildContext
context
);
class
HomogeneousViewport
extends
RenderObjectWidget
{
HomogeneousViewport
({
Key
key
,
this
.
builder
,
this
.
itemsWrap
:
false
,
this
.
itemExtent
,
// required
this
.
itemCount
,
// optional, but you cannot shrink-wrap this class or otherwise use its intrinsic dimensions if you don't specify it
this
.
direction
:
ScrollDirection
.
vertical
,
this
.
startOffset
:
0.0
})
:
super
(
key:
key
)
{
assert
(
itemExtent
!=
null
);
}
final
ListBuilder
builder
;
final
bool
itemsWrap
;
final
double
itemExtent
;
final
int
itemCount
;
final
ScrollDirection
direction
;
final
double
startOffset
;
RenderObjectElement
createElement
()
=>
new
HomogeneousViewportElement
(
this
);
// we don't pass constructor arguments to the RenderBlockViewport() because until
// we know our children, the constructor arguments we could give have no effect
RenderObject
createRenderObject
()
=>
new
RenderBlockViewport
();
bool
isLayoutDifferentThan
(
HomogeneousViewport
oldWidget
)
{
return
itemsWrap
!=
oldWidget
.
itemsWrap
||
itemsWrap
!=
oldWidget
.
itemsWrap
||
itemExtent
!=
oldWidget
.
itemExtent
||
itemCount
!=
oldWidget
.
itemCount
||
direction
!=
oldWidget
.
direction
||
startOffset
!=
oldWidget
.
startOffset
;
}
// all the actual work is done in the element
}
class
HomogeneousViewportElement
extends
RenderObjectElement
<
HomogeneousViewport
>
{
HomogeneousViewportElement
(
HomogeneousViewport
widget
)
:
super
(
widget
);
List
<
Element
>
_children
=
const
<
Element
>[];
bool
_layoutDirty
=
true
;
int
_layoutFirstIndex
;
int
_layoutItemCount
;
RenderBlockViewport
get
renderObject
=>
super
.
renderObject
;
void
visitChildren
(
ElementVisitor
visitor
)
{
if
(
_children
==
null
)
return
;
for
(
Element
child
in
_children
)
visitor
(
child
);
}
void
mount
(
Element
parent
,
dynamic
newSlot
)
{
super
.
mount
(
parent
,
newSlot
);
renderObject
.
callback
=
layout
;
renderObject
.
totalExtentCallback
=
getTotalExtent
;
renderObject
.
minCrossAxisExtentCallback
=
getMinCrossAxisExtent
;
renderObject
.
maxCrossAxisExtentCallback
=
getMaxCrossAxisExtent
;
}
void
unmount
()
{
renderObject
.
callback
=
null
;
renderObject
.
totalExtentCallback
=
null
;
renderObject
.
minCrossAxisExtentCallback
=
null
;
renderObject
.
maxCrossAxisExtentCallback
=
null
;
super
.
unmount
();
}
void
update
(
HomogeneousViewport
newWidget
)
{
bool
needLayout
=
newWidget
.
isLayoutDifferentThan
(
widget
);
super
.
update
(
newWidget
);
if
(
needLayout
)
renderObject
.
markNeedsLayout
();
else
_updateChildren
();
}
void
layout
(
BoxConstraints
constraints
)
{
// we lock the framework state (meaning that no elements can call markNeedsBuild()) because we are
// in the middle of layout and if we allowed people to set state, they'd expect to have that state
// reflected immediately, which, if we were to try to honour it, would potentially result in
// assertions since you can't normally mutate the render object tree during layout. (If there was
// a way to limit this to only descendants of this, it'd be ok, since we are exempt from that
// assert since we are actively doing our own layout still.)
BuildableElement
.
lockState
(()
{
double
mainAxisExtent
=
widget
.
direction
==
ScrollDirection
.
vertical
?
constraints
.
maxHeight
:
constraints
.
maxWidth
;
double
offset
;
if
(
widget
.
startOffset
<=
0.0
&&
!
widget
.
itemsWrap
)
{
_layoutFirstIndex
=
0
;
offset
=
-
widget
.
startOffset
;
}
else
{
_layoutFirstIndex
=
(
widget
.
startOffset
/
widget
.
itemExtent
).
floor
();
offset
=
-(
widget
.
startOffset
%
widget
.
itemExtent
);
}
if
(
mainAxisExtent
<
double
.
INFINITY
)
{
_layoutItemCount
=
((
mainAxisExtent
-
offset
)
/
widget
.
itemExtent
).
ceil
();
if
(
widget
.
itemCount
!=
null
&&
!
widget
.
itemsWrap
)
_layoutItemCount
=
math
.
min
(
_layoutItemCount
,
widget
.
itemCount
-
_layoutFirstIndex
);
}
else
{
assert
(()
{
'This HomogeneousViewport has no specified number of items (meaning it has infinite items), '
+
'and has been placed in an unconstrained environment where all items can be rendered. '
+
'It is most likely that you have placed your HomogeneousViewport (which is an internal '
+
'component of several scrollable widgets) inside either another scrolling box, a flexible '
+
'box (Row, Column), or a Stack, without giving it a specific size.'
;
return
widget
.
itemCount
!=
null
;
});
_layoutItemCount
=
widget
.
itemCount
-
_layoutFirstIndex
;
}
_layoutItemCount
=
math
.
max
(
0
,
_layoutItemCount
);
_updateChildren
();
// Update the renderObject configuration
renderObject
.
direction
=
widget
.
direction
==
ScrollDirection
.
vertical
?
BlockDirection
.
vertical
:
BlockDirection
.
horizontal
;
renderObject
.
itemExtent
=
widget
.
itemExtent
;
renderObject
.
minExtent
=
getTotalExtent
(
null
);
renderObject
.
startOffset
=
offset
;
});
}
void
_updateChildren
()
{
assert
(
_layoutFirstIndex
!=
null
);
assert
(
_layoutItemCount
!=
null
);
List
<
Widget
>
newWidgets
;
if
(
_layoutItemCount
>
0
)
newWidgets
=
widget
.
builder
(
_layoutFirstIndex
,
_layoutItemCount
,
this
);
else
newWidgets
=
<
Widget
>[];
_children
=
updateChildren
(
_children
,
newWidgets
);
}
double
getTotalExtent
(
BoxConstraints
constraints
)
{
// constraints is null when called by layout() above
return
widget
.
itemCount
!=
null
?
widget
.
itemCount
*
widget
.
itemExtent
:
double
.
INFINITY
;
}
double
getMinCrossAxisExtent
(
BoxConstraints
constraints
)
{
return
0.0
;
}
double
getMaxCrossAxisExtent
(
BoxConstraints
constraints
)
{
if
(
widget
.
direction
==
ScrollDirection
.
vertical
)
return
constraints
.
maxWidth
;
return
constraints
.
maxHeight
;
}
void
insertChildRenderObject
(
RenderObject
child
,
Element
slot
)
{
RenderObject
nextSibling
=
slot
?.
renderObject
;
renderObject
.
add
(
child
,
before:
nextSibling
);
}
void
moveChildRenderObject
(
RenderObject
child
,
dynamic
slot
)
{
RenderObject
nextSibling
=
slot
?.
renderObject
;
renderObject
.
move
(
child
,
before:
nextSibling
);
}
void
removeChildRenderObject
(
RenderObject
child
)
{
assert
(
child
.
parent
==
renderObject
);
renderObject
.
remove
(
child
);
}
}
packages/unit/test/fn3/homogeneous_viewport_test.dart
0 → 100644
View file @
ea451690
import
'package:sky/src/fn3.dart'
;
import
'package:test/test.dart'
;
import
'widget_tester.dart'
;
class
TestComponent
extends
StatefulComponent
{
TestComponent
(
this
.
viewport
);
final
HomogeneousViewport
viewport
;
TestComponentState
createState
()
=>
new
TestComponentState
(
this
);
}
class
TestComponentState
extends
ComponentState
<
TestComponent
>
{
TestComponentState
(
TestComponent
config
):
super
(
config
);
bool
_flag
=
true
;
void
go
(
bool
flag
)
{
setState
(()
{
_flag
=
flag
;
});
}
Widget
build
(
BuildContext
context
)
{
return
_flag
?
config
.
viewport
:
new
Text
(
'Not Today'
);
}
}
void
main
(
)
{
test
(
'HomogeneousViewport mount/dismount smoke test'
,
()
{
WidgetTester
tester
=
new
WidgetTester
();
List
<
int
>
callbackTracker
=
<
int
>[];
// the root view is 800x600 in the test environment
// so if our widget is 100 pixels tall, it should fit exactly 6 times.
Widget
builder
()
{
return
new
TestComponent
(
new
HomogeneousViewport
(
builder:
(
int
start
,
int
count
,
BuildContext
context
)
{
List
<
Widget
>
result
=
<
Widget
>[];
for
(
int
index
=
start
;
index
<
start
+
count
;
index
+=
1
)
{
callbackTracker
.
add
(
index
);
result
.
add
(
new
Container
(
key:
new
ValueKey
<
int
>(
index
),
height:
100.0
,
child:
new
Text
(
"
$index
"
)
));
}
return
result
;
},
startOffset:
0.0
,
itemExtent:
100.0
));
}
tester
.
pumpFrame
(
builder
());
TestComponentState
testComponent
=
tester
.
findElement
((
element
)
=>
element
.
widget
is
TestComponent
).
state
;
expect
(
callbackTracker
,
equals
([
0
,
1
,
2
,
3
,
4
,
5
]));
callbackTracker
.
clear
();
testComponent
.
go
(
false
);
tester
.
pumpFrameWithoutChange
();
expect
(
callbackTracker
,
equals
([]));
callbackTracker
.
clear
();
testComponent
.
go
(
true
);
tester
.
pumpFrameWithoutChange
();
expect
(
callbackTracker
,
equals
([
0
,
1
,
2
,
3
,
4
,
5
]));
});
test
(
'HomogeneousViewport vertical'
,
()
{
WidgetTester
tester
=
new
WidgetTester
();
List
<
int
>
callbackTracker
=
<
int
>[];
// the root view is 800x600 in the test environment
// so if our widget is 200 pixels tall, it should fit exactly 3 times.
// but if we are offset by 300 pixels, there will be 4, numbered 1-4.
double
offset
=
300.0
;
ListBuilder
itemBuilder
=
(
int
start
,
int
count
,
BuildContext
context
)
{
List
<
Widget
>
result
=
<
Widget
>[];
for
(
int
index
=
start
;
index
<
start
+
count
;
index
+=
1
)
{
callbackTracker
.
add
(
index
);
result
.
add
(
new
Container
(
key:
new
ValueKey
<
int
>(
index
),
width:
500.0
,
// this should be ignored
height:
400.0
,
// should be overridden by itemExtent
child:
new
Text
(
"
$index
"
)
));
}
return
result
;
};
TestComponent
testComponent
;
Widget
builder
()
{
testComponent
=
new
TestComponent
(
new
HomogeneousViewport
(
builder:
itemBuilder
,
startOffset:
offset
,
itemExtent:
200.0
));
return
testComponent
;
}
tester
.
pumpFrame
(
builder
());
expect
(
callbackTracker
,
equals
([
1
,
2
,
3
,
4
]));
callbackTracker
.
clear
();
offset
=
400.0
;
// now only 3 should fit, numbered 2-4.
tester
.
pumpFrame
(
builder
());
expect
(
callbackTracker
,
equals
([
2
,
3
,
4
]));
callbackTracker
.
clear
();
});
test
(
'HomogeneousViewport horizontal'
,
()
{
WidgetTester
tester
=
new
WidgetTester
();
List
<
int
>
callbackTracker
=
<
int
>[];
// the root view is 800x600 in the test environment
// so if our widget is 200 pixels wide, it should fit exactly 4 times.
// but if we are offset by 300 pixels, there will be 5, numbered 1-5.
double
offset
=
300.0
;
ListBuilder
itemBuilder
=
(
int
start
,
int
count
,
BuildContext
context
)
{
List
<
Widget
>
result
=
<
Widget
>[];
for
(
int
index
=
start
;
index
<
start
+
count
;
index
+=
1
)
{
callbackTracker
.
add
(
index
);
result
.
add
(
new
Container
(
key:
new
ValueKey
<
int
>(
index
),
width:
400.0
,
// this should be overridden by itemExtent
height:
500.0
,
// this should be ignored
child:
new
Text
(
"
$index
"
)
));
}
return
result
;
};
TestComponent
testComponent
;
Widget
builder
()
{
testComponent
=
new
TestComponent
(
new
HomogeneousViewport
(
builder:
itemBuilder
,
startOffset:
offset
,
itemExtent:
200.0
,
direction:
ScrollDirection
.
horizontal
));
return
testComponent
;
}
tester
.
pumpFrame
(
builder
());
expect
(
callbackTracker
,
equals
([
1
,
2
,
3
,
4
,
5
]));
callbackTracker
.
clear
();
offset
=
400.0
;
// now only 4 should fit, numbered 2-5.
tester
.
pumpFrame
(
builder
());
expect
(
callbackTracker
,
equals
([
2
,
3
,
4
,
5
]));
callbackTracker
.
clear
();
});
}
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