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 |