| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 import 'dart:math' as math; | 5 import 'dart:math' as math; |
| 6 | 6 |
| 7 import 'package:sky/rendering/box.dart'; | 7 import 'package:sky/rendering/box.dart'; |
| 8 import 'package:sky/rendering/object.dart'; | 8 import 'package:sky/rendering/object.dart'; |
| 9 import 'package:sky/widgets/basic.dart'; | 9 import 'package:sky/widgets/basic.dart'; |
| 10 import 'package:sky/widgets/icon.dart'; | 10 import 'package:sky/widgets/icon.dart'; |
| 11 import 'package:sky/widgets/ink_well.dart'; | 11 import 'package:sky/widgets/ink_well.dart'; |
| 12 import 'package:sky/widgets/theme.dart'; | 12 import 'package:sky/widgets/theme.dart'; |
| 13 import 'package:sky/widgets/widget.dart'; | 13 import 'package:sky/widgets/widget.dart'; |
| 14 | 14 |
| 15 typedef void SelectedIndexChanged(int selectedIndex); | 15 typedef void SelectedIndexChanged(int selectedIndex); |
| 16 | 16 |
| 17 const double _kTabHeight = 46.0; | 17 const double _kTabHeight = 46.0; |
| 18 const double _kTextAndIconTabHeight = 72.0; |
| 18 const double _kTabIndicatorHeight = 2.0; | 19 const double _kTabIndicatorHeight = 2.0; |
| 19 const double _kTabBarHeight = _kTabHeight + _kTabIndicatorHeight; | |
| 20 const double _kMinTabWidth = 72.0; | 20 const double _kMinTabWidth = 72.0; |
| 21 const int _kTabIconSize = 24; | 21 const int _kTabIconSize = 24; |
| 22 | 22 |
| 23 class TabBarParentData extends BoxParentData with | 23 class TabBarParentData extends BoxParentData with |
| 24 ContainerParentDataMixin<RenderBox> { } | 24 ContainerParentDataMixin<RenderBox> { } |
| 25 | 25 |
| 26 class RenderTabBar extends RenderBox with | 26 class RenderTabBar extends RenderBox with |
| 27 ContainerRenderObjectMixin<RenderBox, TabBarParentData>, | 27 ContainerRenderObjectMixin<RenderBox, TabBarParentData>, |
| 28 RenderBoxContainerDefaultsMixin<RenderBox, TabBarParentData> { | 28 RenderBoxContainerDefaultsMixin<RenderBox, TabBarParentData> { |
| 29 | 29 |
| (...skipping 17 matching lines...) Expand all Loading... |
| 47 | 47 |
| 48 Color _indicatorColor; | 48 Color _indicatorColor; |
| 49 Color get indicatorColor => _indicatorColor; | 49 Color get indicatorColor => _indicatorColor; |
| 50 void set indicatorColor(Color value) { | 50 void set indicatorColor(Color value) { |
| 51 if (_indicatorColor != value) { | 51 if (_indicatorColor != value) { |
| 52 _indicatorColor = value; | 52 _indicatorColor = value; |
| 53 markNeedsPaint(); | 53 markNeedsPaint(); |
| 54 } | 54 } |
| 55 } | 55 } |
| 56 | 56 |
| 57 bool _textAndIcons; |
| 58 bool get textAndIcons => _textAndIcons; |
| 59 void set textAndIcons(bool value) { |
| 60 if (_textAndIcons != value) { |
| 61 _textAndIcons = value; |
| 62 markNeedsLayout(); |
| 63 } |
| 64 } |
| 65 |
| 57 void setupParentData(RenderBox child) { | 66 void setupParentData(RenderBox child) { |
| 58 if (child.parentData is! TabBarParentData) | 67 if (child.parentData is! TabBarParentData) |
| 59 child.parentData = new TabBarParentData(); | 68 child.parentData = new TabBarParentData(); |
| 60 } | 69 } |
| 61 | 70 |
| 62 double getMinIntrinsicWidth(BoxConstraints constraints) { | 71 double getMinIntrinsicWidth(BoxConstraints constraints) { |
| 63 BoxConstraints widthConstraints = | 72 BoxConstraints widthConstraints = |
| 64 new BoxConstraints(maxWidth: constraints.maxWidth, maxHeight: constraint
s.maxHeight); | 73 new BoxConstraints(maxWidth: constraints.maxWidth, maxHeight: constraint
s.maxHeight); |
| 65 double maxWidth = 0.0; | 74 double maxWidth = 0.0; |
| 66 RenderBox child = firstChild; | 75 RenderBox child = firstChild; |
| (...skipping 11 matching lines...) Expand all Loading... |
| 78 double maxWidth = 0.0; | 87 double maxWidth = 0.0; |
| 79 RenderBox child = firstChild; | 88 RenderBox child = firstChild; |
| 80 while (child != null) { | 89 while (child != null) { |
| 81 maxWidth = math.max(maxWidth, child.getMaxIntrinsicWidth(widthConstraints)
); | 90 maxWidth = math.max(maxWidth, child.getMaxIntrinsicWidth(widthConstraints)
); |
| 82 assert(child.parentData is TabBarParentData); | 91 assert(child.parentData is TabBarParentData); |
| 83 child = child.parentData.nextSibling; | 92 child = child.parentData.nextSibling; |
| 84 } | 93 } |
| 85 return constraints.constrainWidth(maxWidth * childCount); | 94 return constraints.constrainWidth(maxWidth * childCount); |
| 86 } | 95 } |
| 87 | 96 |
| 88 double _getIntrinsicHeight(BoxConstraints constraints) => constraints.constrai
nHeight(_kTabBarHeight); | 97 double get _tabBarHeight { |
| 98 return (textAndIcons ? _kTextAndIconTabHeight : _kTabHeight) + _kTabIndicato
rHeight; |
| 99 } |
| 100 |
| 101 double _getIntrinsicHeight(BoxConstraints constraints) => constraints.constrai
nHeight(_tabBarHeight); |
| 89 | 102 |
| 90 double getMinIntrinsicHeight(BoxConstraints constraints) => _getIntrinsicHeigh
t(constraints); | 103 double getMinIntrinsicHeight(BoxConstraints constraints) => _getIntrinsicHeigh
t(constraints); |
| 91 | 104 |
| 92 double getMaxIntrinsicHeight(BoxConstraints constraints) => _getIntrinsicHeigh
t(constraints); | 105 double getMaxIntrinsicHeight(BoxConstraints constraints) => _getIntrinsicHeigh
t(constraints); |
| 93 | 106 |
| 94 void performLayout() { | 107 void performLayout() { |
| 95 assert(constraints is BoxConstraints); | 108 assert(constraints is BoxConstraints); |
| 96 | 109 |
| 97 size = constraints.constrain(new Size(constraints.maxWidth, _kTabBarHeight))
; | 110 size = constraints.constrain(new Size(constraints.maxWidth, _tabBarHeight)); |
| 98 assert(!size.isInfinite); | 111 assert(!size.isInfinite); |
| 99 | 112 |
| 100 if (childCount == 0) | 113 if (childCount == 0) |
| 101 return; | 114 return; |
| 102 | 115 |
| 103 double tabWidth = size.width / childCount; | 116 double tabWidth = size.width / childCount; |
| 104 BoxConstraints tabConstraints = | 117 BoxConstraints tabConstraints = |
| 105 new BoxConstraints.tightFor(width: tabWidth, height: size.height); | 118 new BoxConstraints.tightFor(width: tabWidth, height: size.height); |
| 106 double x = 0.0; | 119 double x = 0.0; |
| 107 RenderBox child = firstChild; | 120 RenderBox child = firstChild; |
| 108 while (child != null) { | 121 while (child != null) { |
| 109 child.layout(tabConstraints); | 122 child.layout(tabConstraints); |
| 110 assert(child.parentData is TabBarParentData); | 123 assert(child.parentData is TabBarParentData); |
| 111 child.parentData.position = new Point(x, 0.0); | 124 child.parentData.position = new Point(x, 0.0); |
| 112 x += tabWidth; | 125 x += tabWidth; |
| 113 child = child.parentData.nextSibling; | 126 child = child.parentData.nextSibling; |
| 114 } | 127 } |
| 115 } | 128 } |
| 116 | 129 |
| 117 void hitTestChildren(HitTestResult result, { Point position }) { | 130 void hitTestChildren(HitTestResult result, { Point position }) { |
| 118 defaultHitTestChildren(result, position: position); | 131 defaultHitTestChildren(result, position: position); |
| 119 } | 132 } |
| 120 | 133 |
| 121 void _paintIndicator(RenderCanvas canvas, RenderBox selectedTab) { | 134 void _paintIndicator(RenderCanvas canvas, RenderBox selectedTab) { |
| 122 if (indicatorColor == null) | 135 if (indicatorColor == null) |
| 123 return; | 136 return; |
| 124 | 137 |
| 125 var size = new Size(selectedTab.size.width, _kTabIndicatorHeight); | 138 var size = new Size(selectedTab.size.width, _kTabIndicatorHeight); |
| 126 var point = new Point(selectedTab.parentData.position.x, _kTabHeight); | 139 var point = new Point( |
| 140 selectedTab.parentData.position.x, |
| 141 _tabBarHeight - _kTabIndicatorHeight); |
| 127 Rect rect = new Rect.fromPointAndSize(point, size); | 142 Rect rect = new Rect.fromPointAndSize(point, size); |
| 128 canvas.drawRect(rect, new Paint()..color = indicatorColor); | 143 canvas.drawRect(rect, new Paint()..color = indicatorColor); |
| 129 } | 144 } |
| 130 | 145 |
| 131 void paint(RenderCanvas canvas) { | 146 void paint(RenderCanvas canvas) { |
| 132 if (backgroundColor != null) { | 147 if (backgroundColor != null) { |
| 133 Rect rect = new Rect.fromSize(size); | 148 Rect rect = new Rect.fromSize(size); |
| 134 canvas.drawRect(rect, new Paint()..color = backgroundColor); | 149 canvas.drawRect(rect, new Paint()..color = backgroundColor); |
| 135 } | 150 } |
| 136 | 151 |
| 137 int index = 0; | 152 int index = 0; |
| 138 RenderBox child = firstChild; | 153 RenderBox child = firstChild; |
| 139 while (child != null) { | 154 while (child != null) { |
| 140 assert(child.parentData is TabBarParentData); | 155 assert(child.parentData is TabBarParentData); |
| 141 canvas.paintChild(child, child.parentData.position); | 156 canvas.paintChild(child, child.parentData.position); |
| 142 if (index++ == selectedIndex) | 157 if (index++ == selectedIndex) |
| 143 _paintIndicator(canvas, child); | 158 _paintIndicator(canvas, child); |
| 144 child = child.parentData.nextSibling; | 159 child = child.parentData.nextSibling; |
| 145 } | 160 } |
| 146 } | 161 } |
| 147 } | 162 } |
| 148 | 163 |
| 149 class TabBarWrapper extends MultiChildRenderObjectWrapper { | 164 class TabBarWrapper extends MultiChildRenderObjectWrapper { |
| 150 TabBarWrapper({ | 165 TabBarWrapper({ |
| 151 List<Widget> children, | 166 List<Widget> children, |
| 152 this.selectedIndex, | 167 this.selectedIndex, |
| 153 this.backgroundColor, | 168 this.backgroundColor, |
| 154 this.indicatorColor, | 169 this.indicatorColor, |
| 170 this.textAndIcons, |
| 155 String key | 171 String key |
| 156 }) : super(key: key, children: children); | 172 }) : super(key: key, children: children); |
| 157 | 173 |
| 158 final int selectedIndex; | 174 final int selectedIndex; |
| 159 final Color backgroundColor; | 175 final Color backgroundColor; |
| 160 final Color indicatorColor; | 176 final Color indicatorColor; |
| 177 final bool textAndIcons; |
| 161 | 178 |
| 162 RenderTabBar get root => super.root; | 179 RenderTabBar get root => super.root; |
| 163 RenderTabBar createNode() => new RenderTabBar(); | 180 RenderTabBar createNode() => new RenderTabBar(); |
| 164 | 181 |
| 165 void syncRenderObject(Widget old) { | 182 void syncRenderObject(Widget old) { |
| 166 super.syncRenderObject(old); | 183 super.syncRenderObject(old); |
| 167 root.selectedIndex = selectedIndex; | 184 root.selectedIndex = selectedIndex; |
| 168 root.backgroundColor = backgroundColor; | 185 root.backgroundColor = backgroundColor; |
| 169 root.indicatorColor = indicatorColor; | 186 root.indicatorColor = indicatorColor; |
| 187 root.textAndIcons = textAndIcons; |
| 170 } | 188 } |
| 171 } | 189 } |
| 172 | 190 |
| 173 class TabLabel { | 191 class TabLabel { |
| 174 const TabLabel({ this.text, this.icon }); | 192 const TabLabel({ this.text, this.icon }); |
| 175 | 193 |
| 176 final String text; | 194 final String text; |
| 177 final String icon; | 195 final String icon; |
| 178 } | 196 } |
| 179 | 197 |
| (...skipping 20 matching lines...) Expand all Loading... |
| 200 } | 218 } |
| 201 | 219 |
| 202 Widget build() { | 220 Widget build() { |
| 203 Widget labelContents; | 221 Widget labelContents; |
| 204 if (label.icon == null) { | 222 if (label.icon == null) { |
| 205 labelContents = _buildLabelText(); | 223 labelContents = _buildLabelText(); |
| 206 } else if (label.text == null) { | 224 } else if (label.text == null) { |
| 207 labelContents = _buildLabelIcon(); | 225 labelContents = _buildLabelIcon(); |
| 208 } else { | 226 } else { |
| 209 labelContents = new Flex( | 227 labelContents = new Flex( |
| 210 <Widget>[_buildLabelText(), _buildLabelIcon()], | 228 <Widget>[ |
| 229 new Container( |
| 230 child: _buildLabelIcon(), |
| 231 margin: const EdgeDims.only(bottom: 10.0) |
| 232 ), |
| 233 _buildLabelText() |
| 234 ], |
| 211 justifyContent: FlexJustifyContent.center, | 235 justifyContent: FlexJustifyContent.center, |
| 212 alignItems: FlexAlignItems.center, | 236 alignItems: FlexAlignItems.center, |
| 213 direction: FlexDirection.vertical | 237 direction: FlexDirection.vertical |
| 214 ); | 238 ); |
| 215 } | 239 } |
| 216 | 240 |
| 217 Widget highlightedLabel = new Opacity( | 241 Widget highlightedLabel = new Opacity( |
| 218 child: labelContents, | 242 child: labelContents, |
| 219 opacity: selected ? 1.0 : 0.7 | 243 opacity: selected ? 1.0 : 0.7 |
| 220 ); | 244 ); |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 253 ); | 277 ); |
| 254 return new Listener( | 278 return new Listener( |
| 255 child: tab, | 279 child: tab, |
| 256 onGestureTap: (_) => _handleTap(tabIndex) | 280 onGestureTap: (_) => _handleTap(tabIndex) |
| 257 ); | 281 ); |
| 258 } | 282 } |
| 259 | 283 |
| 260 Widget build() { | 284 Widget build() { |
| 261 assert(labels != null && labels.isNotEmpty); | 285 assert(labels != null && labels.isNotEmpty); |
| 262 List<Widget> tabs = <Widget>[]; | 286 List<Widget> tabs = <Widget>[]; |
| 287 bool textAndIcons = false; |
| 263 for (int tabIndex = 0; tabIndex < labels.length; tabIndex++) { | 288 for (int tabIndex = 0; tabIndex < labels.length; tabIndex++) { |
| 264 tabs.add(_toTab(labels[tabIndex], tabIndex)); | 289 tabs.add(_toTab(labels[tabIndex], tabIndex)); |
| 290 if (labels[tabIndex].text != null && labels[tabIndex].icon != null) |
| 291 textAndIcons = true; |
| 265 } | 292 } |
| 266 return new TabBarWrapper( | 293 return new TabBarWrapper( |
| 267 children: tabs, | 294 children: tabs, |
| 268 selectedIndex: selectedIndex, | 295 selectedIndex: selectedIndex, |
| 269 backgroundColor: Theme.of(this).primary[500], | 296 backgroundColor: Theme.of(this).primary[500], |
| 270 indicatorColor: Theme.of(this).accent[200] | 297 indicatorColor: Theme.of(this).accent[200], |
| 298 textAndIcons: textAndIcons |
| 271 ); | 299 ); |
| 272 } | 300 } |
| 273 } | 301 } |
| 274 | |
| 275 | |
| OLD | NEW |