Commit dc7137b0 authored by Adam Barth's avatar Adam Barth

Merge pull request #763 from abarth/scroll_gravity

Cleanup how we manage scrollOffset in Scrollable
parents ca42e886 d3eaff27
...@@ -17,32 +17,33 @@ abstract class ScrollBehavior { ...@@ -17,32 +17,33 @@ abstract class ScrollBehavior {
class BoundedBehavior extends ScrollBehavior { class BoundedBehavior extends ScrollBehavior {
BoundedBehavior({ double contentsSize: 0.0, double containerSize: 0.0 }) BoundedBehavior({ double contentsSize: 0.0, double containerSize: 0.0 })
: _contentsSize = contentsSize, : _contentsExtents = contentsSize,
_containerSize = containerSize; _containerExtents = containerSize;
double _contentsSize; double _contentsExtents;
double get contentsSize => _contentsSize; double get contentsExtents => _contentsExtents;
void set contentsSize (double value) {
if (_contentsSize != value) { double _containerExtents;
_contentsSize = value; double get containerExtents => _containerExtents;
// TODO(ianh) now what? what if we have a simulation ongoing?
} /// Returns the new scrollOffset.
} double updateExtents({
double contentsExtents,
double _containerSize; double containerExtents,
double get containerSize => _containerSize; double scrollOffset: 0.0
void set containerSize (double value) { }) {
if (_containerSize != value) { if (contentsExtents != null)
_containerSize = value; _contentsExtents = contentsExtents;
// TODO(ianh) now what? what if we have a simulation ongoing? if (containerExtents != null)
} _containerExtents = containerExtents;
return scrollOffset.clamp(minScrollOffset, maxScrollOffset);
} }
final double minScrollOffset = 0.0; final double minScrollOffset = 0.0;
double get maxScrollOffset => math.max(0.0, _contentsSize - _containerSize); double get maxScrollOffset => math.max(0.0, _contentsExtents - _containerExtents);
double applyCurve(double scrollOffset, double scrollDelta) { double applyCurve(double scrollOffset, double scrollDelta) {
return (scrollOffset + scrollDelta).clamp(0.0, maxScrollOffset); return (scrollOffset + scrollDelta).clamp(minScrollOffset, maxScrollOffset);
} }
} }
...@@ -80,7 +81,7 @@ class OverscrollBehavior extends BoundedBehavior { ...@@ -80,7 +81,7 @@ class OverscrollBehavior extends BoundedBehavior {
} }
class OverscrollWhenScrollableBehavior extends OverscrollBehavior { class OverscrollWhenScrollableBehavior extends OverscrollBehavior {
bool get isScrollable => contentsSize > containerSize; bool get isScrollable => contentsExtents > containerExtents;
Simulation release(double position, double velocity) { Simulation release(double position, double velocity) {
if (isScrollable || position < minScrollOffset || position > maxScrollOffset) if (isScrollable || position < minScrollOffset || position > maxScrollOffset)
......
...@@ -22,7 +22,6 @@ import 'package:sky/widgets/scrollable.dart'; ...@@ -22,7 +22,6 @@ import 'package:sky/widgets/scrollable.dart';
export 'package:sky/widgets/mixed_viewport.dart' show MixedViewportLayoutState; export 'package:sky/widgets/mixed_viewport.dart' show MixedViewportLayoutState;
// The GestureEvent velocity properties are pixels/second, config min,max limits are pixels/ms // The GestureEvent velocity properties are pixels/second, config min,max limits are pixels/ms
const double _kMillisecondsPerSecond = 1000.0; const double _kMillisecondsPerSecond = 1000.0;
const double _kMinFlingVelocity = -config.kMaxFlingVelocity * _kMillisecondsPerSecond; const double _kMinFlingVelocity = -config.kMaxFlingVelocity * _kMillisecondsPerSecond;
...@@ -48,11 +47,11 @@ abstract class Scrollable extends StatefulComponent { ...@@ -48,11 +47,11 @@ abstract class Scrollable extends StatefulComponent {
ValueAnimation<double> _toOffsetAnimation; // Started by scrollTo() ValueAnimation<double> _toOffsetAnimation; // Started by scrollTo()
void initState() { void initState() {
_toEndAnimation = new AnimatedSimulation(_tickScrollOffset); _toEndAnimation = new AnimatedSimulation(_setScrollOffset);
_toOffsetAnimation = new ValueAnimation<double>() _toOffsetAnimation = new ValueAnimation<double>()
..addListener(() { ..addListener(() {
AnimatedValue<double> offset = _toOffsetAnimation.variable; AnimatedValue<double> offset = _toOffsetAnimation.variable;
scrollTo(offset.value); _setScrollOffset(offset.value);
}); });
} }
...@@ -93,8 +92,7 @@ abstract class Scrollable extends StatefulComponent { ...@@ -93,8 +92,7 @@ abstract class Scrollable extends StatefulComponent {
} }
Future _startToOffsetAnimation(double newScrollOffset, Duration duration, Curve curve) { Future _startToOffsetAnimation(double newScrollOffset, Duration duration, Curve curve) {
_stopToEndAnimation(); _stopAnimations();
_stopToOffsetAnimation();
_toOffsetAnimation _toOffsetAnimation
..variable = new AnimatedValue<double>(scrollOffset, ..variable = new AnimatedValue<double>(scrollOffset,
end: newScrollOffset, end: newScrollOffset,
...@@ -105,47 +103,46 @@ abstract class Scrollable extends StatefulComponent { ...@@ -105,47 +103,46 @@ abstract class Scrollable extends StatefulComponent {
return _toOffsetAnimation.play(); return _toOffsetAnimation.play();
} }
void _stopToOffsetAnimation() { void _stopAnimations() {
if (_toOffsetAnimation.isAnimating) if (_toOffsetAnimation.isAnimating)
_toOffsetAnimation.stop(); _toOffsetAnimation.stop();
if (_toEndAnimation.isAnimating)
_toEndAnimation.stop();
} }
void _startToEndAnimation({ double velocity: 0.0 }) { void _startToEndAnimation({ double velocity: 0.0 }) {
_stopToEndAnimation(); _stopAnimations();
_stopToOffsetAnimation();
Simulation simulation = scrollBehavior.release(scrollOffset, velocity); Simulation simulation = scrollBehavior.release(scrollOffset, velocity);
if (simulation != null) if (simulation != null)
_toEndAnimation.start(simulation); _toEndAnimation.start(simulation);
} }
void _stopToEndAnimation() {
_toEndAnimation.stop();
}
void didUnmount() { void didUnmount() {
_stopToEndAnimation(); _stopAnimations();
_stopToOffsetAnimation();
super.didUnmount(); super.didUnmount();
} }
void _setScrollOffset(double newScrollOffset) {
if (_scrollOffset == newScrollOffset)
return;
setState(() {
_scrollOffset = newScrollOffset;
});
if (_listeners.length > 0)
_notifyListeners();
}
Future scrollTo(double newScrollOffset, { Duration duration, Curve curve: ease }) { Future scrollTo(double newScrollOffset, { Duration duration, Curve curve: ease }) {
if (newScrollOffset == _scrollOffset) if (newScrollOffset == _scrollOffset)
return new Future.value(); return new Future.value();
Future result;
if (duration == null) { if (duration == null) {
setState(() { _stopAnimations();
_scrollOffset = newScrollOffset; _setScrollOffset(newScrollOffset);
}); return new Future.value();
result = new Future.value();
} else {
result = _startToOffsetAnimation(newScrollOffset, duration, curve);
} }
if (_listeners.length > 0) return _startToOffsetAnimation(newScrollOffset, duration, curve);
_notifyListeners();
return result;
} }
Future scrollBy(double scrollDelta, { Duration duration, Curve curve }) { Future scrollBy(double scrollDelta, { Duration duration, Curve curve }) {
...@@ -157,10 +154,6 @@ abstract class Scrollable extends StatefulComponent { ...@@ -157,10 +154,6 @@ abstract class Scrollable extends StatefulComponent {
_startToEndAnimation(); _startToEndAnimation();
} }
void _tickScrollOffset(double value) {
scrollTo(value);
}
// Return the event's velocity in pixels/second. // Return the event's velocity in pixels/second.
double _eventVelocity(sky.GestureEvent event) { double _eventVelocity(sky.GestureEvent event) {
double velocity = scrollDirection == ScrollDirection.horizontal double velocity = scrollDirection == ScrollDirection.horizontal
...@@ -170,8 +163,7 @@ abstract class Scrollable extends StatefulComponent { ...@@ -170,8 +163,7 @@ abstract class Scrollable extends StatefulComponent {
} }
EventDisposition _handlePointerDown(_) { EventDisposition _handlePointerDown(_) {
_stopToEndAnimation(); _stopAnimations();
_stopToOffsetAnimation();
return EventDisposition.processed; return EventDisposition.processed;
} }
...@@ -293,10 +285,10 @@ class ScrollableViewport extends Scrollable { ...@@ -293,10 +285,10 @@ class ScrollableViewport extends Scrollable {
_updateScrollBehaviour(); _updateScrollBehaviour();
} }
void _updateScrollBehaviour() { void _updateScrollBehaviour() {
scrollBehavior.contentsSize = _childSize; scrollTo(scrollBehavior.updateExtents(
scrollBehavior.containerSize = _viewportSize; contentsExtents: _childSize,
if (scrollOffset > scrollBehavior.maxScrollOffset) containerExtents: _viewportSize,
settleScrollOffset(); scrollOffset: scrollOffset));
} }
Widget buildContent() { Widget buildContent() {
...@@ -375,6 +367,11 @@ abstract class ScrollableWidgetList extends Scrollable { ...@@ -375,6 +367,11 @@ abstract class ScrollableWidgetList extends Scrollable {
itemExtent = source.itemExtent; itemExtent = source.itemExtent;
super.syncFields(source); // update scrollDirection super.syncFields(source); // update scrollDirection
if (itemCount != _previousItemCount) {
scrollBehaviorUpdateNeeded = true;
_previousItemCount = itemCount;
}
if (scrollBehaviorUpdateNeeded) if (scrollBehaviorUpdateNeeded)
_updateScrollBehavior(); _updateScrollBehavior();
} }
...@@ -416,17 +413,14 @@ abstract class ScrollableWidgetList extends Scrollable { ...@@ -416,17 +413,14 @@ abstract class ScrollableWidgetList extends Scrollable {
} }
void _updateScrollBehavior() { void _updateScrollBehavior() {
scrollBehavior.containerSize = _containerExtent;
double contentsExtent = itemExtent * itemCount; double contentsExtent = itemExtent * itemCount;
if (padding != null) if (padding != null)
contentsExtent += _leadingPadding + _trailingPadding; contentsExtent += _leadingPadding + _trailingPadding;
scrollBehavior.contentsSize = contentsExtent;
}
void _updateScrollOffset() { scrollTo(scrollBehavior.updateExtents(
if (scrollOffset > scrollBehavior.maxScrollOffset) contentsExtents: contentsExtent,
settleScrollOffset(); containerExtents: _containerExtent,
scrollOffset: scrollOffset));
} }
Offset _toOffset(double value) { Offset _toOffset(double value) {
...@@ -439,7 +433,6 @@ abstract class ScrollableWidgetList extends Scrollable { ...@@ -439,7 +433,6 @@ abstract class ScrollableWidgetList extends Scrollable {
if (itemCount != _previousItemCount) { if (itemCount != _previousItemCount) {
_previousItemCount = itemCount; _previousItemCount = itemCount;
_updateScrollBehavior(); _updateScrollBehavior();
_updateScrollOffset();
} }
double paddedScrollOffset = scrollOffset; double paddedScrollOffset = scrollOffset;
...@@ -643,18 +636,19 @@ class ScrollableMixedWidgetList extends Scrollable { ...@@ -643,18 +636,19 @@ class ScrollableMixedWidgetList extends Scrollable {
OverscrollBehavior get scrollBehavior => super.scrollBehavior; OverscrollBehavior get scrollBehavior => super.scrollBehavior;
void _handleSizeChanged(Size newSize) { void _handleSizeChanged(Size newSize) {
scrollBehavior.containerSize = newSize.height; scrollBy(scrollBehavior.updateExtents(
containerExtents: newSize.height,
scrollOffset: scrollOffset
));
} }
void _handleLayoutChanged() { void _handleLayoutChanged() {
if (layoutState.didReachLastChild) { double newScrollOffset = scrollBehavior.updateExtents(
scrollBehavior.contentsSize = layoutState.contentsSize; contentsExtents: layoutState.didReachLastChild ? layoutState.contentsSize : double.INFINITY,
if (_contentsChanged && scrollOffset > scrollBehavior.maxScrollOffset) { scrollOffset: scrollOffset);
_contentsChanged = false; if (_contentsChanged) {
settleScrollOffset(); _contentsChanged = false;
} scrollTo(newScrollOffset);
} else {
scrollBehavior.contentsSize = double.INFINITY;
} }
} }
......
...@@ -460,7 +460,7 @@ class TabBar extends Scrollable { ...@@ -460,7 +460,7 @@ class TabBar extends Scrollable {
} }
double _centeredTabScrollOffset(int tabIndex) { double _centeredTabScrollOffset(int tabIndex) {
double viewportWidth = scrollBehavior.containerSize; double viewportWidth = scrollBehavior.containerExtents;
return (_tabRect(tabIndex).left + _tabWidths[tabIndex] / 2.0 - viewportWidth / 2.0) return (_tabRect(tabIndex).left + _tabWidths[tabIndex] / 2.0 - viewportWidth / 2.0)
.clamp(scrollBehavior.minScrollOffset, scrollBehavior.maxScrollOffset); .clamp(scrollBehavior.minScrollOffset, scrollBehavior.maxScrollOffset);
} }
...@@ -495,8 +495,9 @@ class TabBar extends Scrollable { ...@@ -495,8 +495,9 @@ class TabBar extends Scrollable {
setState(() { setState(() {
_tabBarSize = tabBarSize; _tabBarSize = tabBarSize;
_tabWidths = tabWidths; _tabWidths = tabWidths;
scrollBehavior.containerSize = _tabBarSize.width; scrollBehavior.updateExtents(
scrollBehavior.contentsSize = _tabWidths.reduce((sum, width) => sum + width); containerExtents: _tabBarSize.width,
contentsExtents: _tabWidths.reduce((sum, width) => sum + width));
}); });
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment