Commit 870376da authored by Adam Barth's avatar Adam Barth

Teach ScrollableBlock how to scroll horizontally

Now ScrollableBlock can combine a horizontally scrolling viewport with a
horizontal block.

Also rename ViewportScrollDirection to just ScrollDirection for less verbosity.
parent 6f2a3e40
// 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:sky/painting/box_painter.dart';
import 'package:sky/widgets.dart';
class Circle extends Component {
Widget build() {
return new Container(
width: 50.0,
margin: new EdgeDims.symmetric(horizontal: 2.0),
decoration: new BoxDecoration(
shape: Shape.circle,
backgroundColor: const Color(0xFF00FF00)
)
);
}
}
class Pad extends Component {
Widget build() {
return new SizedBox(width: 10.0);
}
}
class HorizontalScrollingApp extends App {
Widget build() {
List<Widget> circles = [
new Pad(),
new Circle(),
new Circle(),
new Circle(),
new Circle(),
new Circle(),
new Circle(),
new Circle(),
new Circle(),
new Circle(),
new Circle(),
new Circle(),
new Circle(),
new Pad(),
];
return new Center(
child: new Container(
height: 50.0,
child: new Flex([
new ScrollableBlock(circles, scrollDirection: ScrollDirection.horizontal)
], justifyContent: FlexJustifyContent.end)
)
);
}
}
void main() {
runApp(new HorizontalScrollingApp());
}
...@@ -1121,27 +1121,27 @@ class RenderBaseline extends RenderShiftedBox { ...@@ -1121,27 +1121,27 @@ class RenderBaseline extends RenderShiftedBox {
String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}baseline: ${baseline}\nbaselineType: ${baselineType}'; String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}baseline: ${baseline}\nbaselineType: ${baselineType}';
} }
enum ViewportScrollDirection { horizontal, vertical, both } enum ScrollDirection { horizontal, vertical, both }
class RenderViewport extends RenderBox with RenderObjectWithChildMixin<RenderBox> { class RenderViewport extends RenderBox with RenderObjectWithChildMixin<RenderBox> {
RenderViewport({ RenderViewport({
RenderBox child, RenderBox child,
Offset scrollOffset, Offset scrollOffset,
ViewportScrollDirection scrollDirection: ViewportScrollDirection.vertical ScrollDirection scrollDirection: ScrollDirection.vertical
}) : _scrollOffset = scrollOffset, }) : _scrollOffset = scrollOffset,
_scrollDirection = scrollDirection { _scrollDirection = scrollDirection {
assert(_offsetIsSane(scrollOffset, scrollDirection)); assert(_offsetIsSane(scrollOffset, scrollDirection));
this.child = child; this.child = child;
} }
bool _offsetIsSane(Offset offset, ViewportScrollDirection direction) { bool _offsetIsSane(Offset offset, ScrollDirection direction) {
switch (direction) { switch (direction) {
case ViewportScrollDirection.both: case ScrollDirection.both:
return true; return true;
case ViewportScrollDirection.horizontal: case ScrollDirection.horizontal:
return offset.dy == 0.0; return offset.dy == 0.0;
case ViewportScrollDirection.vertical: case ScrollDirection.vertical:
return offset.dx == 0.0; return offset.dx == 0.0;
} }
} }
...@@ -1156,9 +1156,9 @@ class RenderViewport extends RenderBox with RenderObjectWithChildMixin<RenderBox ...@@ -1156,9 +1156,9 @@ class RenderViewport extends RenderBox with RenderObjectWithChildMixin<RenderBox
markNeedsPaint(); markNeedsPaint();
} }
ViewportScrollDirection _scrollDirection; ScrollDirection _scrollDirection;
ViewportScrollDirection get scrollDirection => _scrollDirection; ScrollDirection get scrollDirection => _scrollDirection;
void set scrollDirection(ViewportScrollDirection value) { void set scrollDirection(ScrollDirection value) {
if (value == _scrollDirection) if (value == _scrollDirection)
return; return;
assert(_offsetIsSane(scrollOffset, value)); assert(_offsetIsSane(scrollOffset, value));
...@@ -1169,13 +1169,13 @@ class RenderViewport extends RenderBox with RenderObjectWithChildMixin<RenderBox ...@@ -1169,13 +1169,13 @@ class RenderViewport extends RenderBox with RenderObjectWithChildMixin<RenderBox
BoxConstraints _getInnerConstraints(BoxConstraints constraints) { BoxConstraints _getInnerConstraints(BoxConstraints constraints) {
BoxConstraints innerConstraints; BoxConstraints innerConstraints;
switch (scrollDirection) { switch (scrollDirection) {
case ViewportScrollDirection.both: case ScrollDirection.both:
innerConstraints = new BoxConstraints(); innerConstraints = new BoxConstraints();
break; break;
case ViewportScrollDirection.horizontal: case ScrollDirection.horizontal:
innerConstraints = constraints.heightConstraints(); innerConstraints = constraints.heightConstraints();
break; break;
case ViewportScrollDirection.vertical: case ScrollDirection.vertical:
innerConstraints = constraints.widthConstraints(); innerConstraints = constraints.widthConstraints();
break; break;
} }
......
...@@ -20,7 +20,8 @@ import 'package:sky/widgets/default_text_style.dart'; ...@@ -20,7 +20,8 @@ import 'package:sky/widgets/default_text_style.dart';
import 'package:sky/widgets/framework.dart'; import 'package:sky/widgets/framework.dart';
export 'package:sky/base/hit_test.dart' show EventDisposition, combineEventDispositions; export 'package:sky/base/hit_test.dart' show EventDisposition, combineEventDispositions;
export 'package:sky/rendering/box.dart' show BackgroundImage, BoxConstraints, BoxDecoration, Border, BorderSide, EdgeDims, ViewportScrollDirection; export 'package:sky/rendering/block.dart' show BlockDirection;
export 'package:sky/rendering/box.dart' show BackgroundImage, BoxConstraints, BoxDecoration, Border, BorderSide, EdgeDims, ScrollDirection;
export 'package:sky/rendering/flex.dart' show FlexDirection, FlexJustifyContent, FlexAlignItems; export 'package:sky/rendering/flex.dart' show FlexDirection, FlexJustifyContent, FlexAlignItems;
export 'package:sky/rendering/object.dart' show Point, Offset, Size, Rect, Color, Paint, Path; export 'package:sky/rendering/object.dart' show Point, Offset, Size, Rect, Color, Paint, Path;
export 'package:sky/rendering/toggleable.dart' show ValueChanged; export 'package:sky/rendering/toggleable.dart' show ValueChanged;
...@@ -271,12 +272,12 @@ class Viewport extends OneChildRenderObjectWrapper { ...@@ -271,12 +272,12 @@ class Viewport extends OneChildRenderObjectWrapper {
Viewport({ Viewport({
Key key, Key key,
this.scrollOffset: Offset.zero, this.scrollOffset: Offset.zero,
this.scrollDirection: ViewportScrollDirection.vertical, this.scrollDirection: ScrollDirection.vertical,
Widget child Widget child
}) : super(key: key, child: child); }) : super(key: key, child: child);
final Offset scrollOffset; final Offset scrollOffset;
final ViewportScrollDirection scrollDirection; final ScrollDirection scrollDirection;
RenderViewport createNode() => new RenderViewport(scrollOffset: scrollOffset, scrollDirection: scrollDirection); RenderViewport createNode() => new RenderViewport(scrollOffset: scrollOffset, scrollDirection: scrollDirection);
RenderViewport get root => super.root; RenderViewport get root => super.root;
...@@ -371,11 +372,20 @@ class Container extends Component { ...@@ -371,11 +372,20 @@ class Container extends Component {
// LAYOUT NODES // LAYOUT NODES
class Block extends MultiChildRenderObjectWrapper { class Block extends MultiChildRenderObjectWrapper {
Block(List<Widget> children, { Key key }) Block(List<Widget> children, {
: super(key: key, children: children); Key key,
this.direction: BlockDirection.vertical
}) : super(key: key, children: children);
RenderBlock createNode() => new RenderBlock(); final BlockDirection direction;
RenderBlock createNode() => new RenderBlock(direction: direction);
RenderBlock get root => super.root; RenderBlock get root => super.root;
void syncRenderObject(Widget old) {
super.syncRenderObject(old);
root.direction = direction;
}
} }
class Stack extends MultiChildRenderObjectWrapper { class Stack extends MultiChildRenderObjectWrapper {
...@@ -417,7 +427,7 @@ class Flex extends MultiChildRenderObjectWrapper { ...@@ -417,7 +427,7 @@ class Flex extends MultiChildRenderObjectWrapper {
final FlexAlignItems alignItems; final FlexAlignItems alignItems;
final TextBaseline textBaseline; final TextBaseline textBaseline;
RenderFlex createNode() => new RenderFlex(direction: this.direction); RenderFlex createNode() => new RenderFlex(direction: direction);
RenderFlex get root => super.root; RenderFlex get root => super.root;
void syncRenderObject(Widget old) { void syncRenderObject(Widget old) {
......
...@@ -37,13 +37,13 @@ abstract class Scrollable extends StatefulComponent { ...@@ -37,13 +37,13 @@ abstract class Scrollable extends StatefulComponent {
Scrollable({ Scrollable({
Key key, Key key,
this.scrollDirection: ViewportScrollDirection.vertical this.scrollDirection: ScrollDirection.vertical
}) : super(key: key) { }) : super(key: key) {
assert(scrollDirection == ViewportScrollDirection.vertical || assert(scrollDirection == ScrollDirection.vertical ||
scrollDirection == ViewportScrollDirection.horizontal); scrollDirection == ScrollDirection.horizontal);
} }
ViewportScrollDirection scrollDirection; ScrollDirection scrollDirection;
AnimatedSimulation _toEndAnimation; // See _startToEndAnimation() AnimatedSimulation _toEndAnimation; // See _startToEndAnimation()
AnimationPerformance _toOffsetAnimation; // Started by scrollTo(offset, duration: d) AnimationPerformance _toOffsetAnimation; // Started by scrollTo(offset, duration: d)
...@@ -65,7 +65,7 @@ abstract class Scrollable extends StatefulComponent { ...@@ -65,7 +65,7 @@ abstract class Scrollable extends StatefulComponent {
double get scrollOffset => _scrollOffset; double get scrollOffset => _scrollOffset;
Offset get scrollOffsetVector { Offset get scrollOffsetVector {
if (scrollDirection == ViewportScrollDirection.horizontal) if (scrollDirection == ScrollDirection.horizontal)
return new Offset(scrollOffset, 0.0); return new Offset(scrollOffset, 0.0);
return new Offset(0.0, scrollOffset); return new Offset(0.0, scrollOffset);
} }
...@@ -176,7 +176,7 @@ abstract class Scrollable extends StatefulComponent { ...@@ -176,7 +176,7 @@ abstract class Scrollable extends StatefulComponent {
} }
bool scrollBy(double scrollDelta) { bool scrollBy(double scrollDelta) {
var newScrollOffset = scrollBehavior.applyCurve(_scrollOffset, scrollDelta); double newScrollOffset = scrollBehavior.applyCurve(_scrollOffset, scrollDelta);
return scrollTo(newScrollOffset); return scrollTo(newScrollOffset);
} }
...@@ -195,12 +195,12 @@ abstract class Scrollable extends StatefulComponent { ...@@ -195,12 +195,12 @@ abstract class Scrollable extends StatefulComponent {
} }
EventDisposition _handleScrollUpdate(sky.GestureEvent event) { EventDisposition _handleScrollUpdate(sky.GestureEvent event) {
scrollBy(scrollDirection == ViewportScrollDirection.horizontal ? event.dx : -event.dy); scrollBy(scrollDirection == ScrollDirection.horizontal ? event.dx : -event.dy);
return EventDisposition.processed; return EventDisposition.processed;
} }
EventDisposition _handleFlingStart(sky.GestureEvent event) { EventDisposition _handleFlingStart(sky.GestureEvent event) {
double eventVelocity = scrollDirection == ViewportScrollDirection.horizontal double eventVelocity = scrollDirection == ScrollDirection.horizontal
? -event.velocityX ? -event.velocityX
: -event.velocityY; : -event.velocityY;
_startToEndAnimation(velocity: _velocityForFlingGesture(eventVelocity)); _startToEndAnimation(velocity: _velocityForFlingGesture(eventVelocity));
...@@ -235,7 +235,7 @@ class ScrollableViewport extends Scrollable { ...@@ -235,7 +235,7 @@ class ScrollableViewport extends Scrollable {
ScrollableViewport({ ScrollableViewport({
Key key, Key key,
this.child, this.child,
ViewportScrollDirection scrollDirection: ViewportScrollDirection.vertical ScrollDirection scrollDirection: ScrollDirection.vertical
}) : super(key: key, scrollDirection: scrollDirection); }) : super(key: key, scrollDirection: scrollDirection);
Widget child; Widget child;
...@@ -248,19 +248,19 @@ class ScrollableViewport extends Scrollable { ...@@ -248,19 +248,19 @@ class ScrollableViewport extends Scrollable {
ScrollBehavior createScrollBehavior() => new OverscrollWhenScrollableBehavior(); ScrollBehavior createScrollBehavior() => new OverscrollWhenScrollableBehavior();
OverscrollWhenScrollableBehavior get scrollBehavior => super.scrollBehavior; OverscrollWhenScrollableBehavior get scrollBehavior => super.scrollBehavior;
double _viewportHeight = 0.0; double _viewportSize = 0.0;
double _childHeight = 0.0; double _childSize = 0.0;
void _handleViewportSizeChanged(Size newSize) { void _handleViewportSizeChanged(Size newSize) {
_viewportHeight = newSize.height; _viewportSize = scrollDirection == ScrollDirection.vertical ? newSize.height : newSize.width;
_updateScrollBehaviour(); _updateScrollBehaviour();
} }
void _handleChildSizeChanged(Size newSize) { void _handleChildSizeChanged(Size newSize) {
_childHeight = newSize.height; _childSize = scrollDirection == ScrollDirection.vertical ? newSize.height : newSize.width;
_updateScrollBehaviour(); _updateScrollBehaviour();
} }
void _updateScrollBehaviour() { void _updateScrollBehaviour() {
scrollBehavior.contentsSize = _childHeight; scrollBehavior.contentsSize = _childSize;
scrollBehavior.containerSize = _viewportHeight; scrollBehavior.containerSize = _viewportSize;
if (scrollOffset > scrollBehavior.maxScrollOffset) if (scrollOffset > scrollBehavior.maxScrollOffset)
settleScrollOffset(); settleScrollOffset();
} }
...@@ -284,13 +284,24 @@ class ScrollableViewport extends Scrollable { ...@@ -284,13 +284,24 @@ class ScrollableViewport extends Scrollable {
/// fixed number of children that you wish to arrange in a block layout and that /// fixed number of children that you wish to arrange in a block layout and that
/// might exceed the height of its container (and therefore need to scroll). /// might exceed the height of its container (and therefore need to scroll).
class ScrollableBlock extends Component { class ScrollableBlock extends Component {
ScrollableBlock(this.children, { Key key }) : super(key: key); ScrollableBlock(this.children, {
Key key,
this.scrollDirection: ScrollDirection.vertical
}) : super(key: key);
final List<Widget> children; final List<Widget> children;
final ScrollDirection scrollDirection;
BlockDirection get _direction {
if (scrollDirection == ScrollDirection.vertical)
return BlockDirection.vertical;
return BlockDirection.horizontal;
}
Widget build() { Widget build() {
return new ScrollableViewport( return new ScrollableViewport(
child: new Block(children) scrollDirection: scrollDirection,
child: new Block(children, direction: _direction)
); );
} }
} }
......
...@@ -396,7 +396,7 @@ class TabBar extends Scrollable { ...@@ -396,7 +396,7 @@ class TabBar extends Scrollable {
this.selectedIndex: 0, this.selectedIndex: 0,
this.onChanged, this.onChanged,
this.isScrollable: false this.isScrollable: false
}) : super(key: key, scrollDirection: ViewportScrollDirection.horizontal); }) : super(key: key, scrollDirection: ScrollDirection.horizontal);
Iterable<TabLabel> labels; Iterable<TabLabel> labels;
int selectedIndex; int selectedIndex;
......
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