Chromium Code Reviews

Side by Side Diff: sky/sdk/lib/widgets/tabs.dart

Issue 1205953002: Version 0 of TabLabel, Tab, and TabBar components (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Updates per Ian's feedback Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff |
« no previous file with comments | « sky/sdk/lib/rendering/box.dart ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 import 'dart:math' as math;
6 import 'dart:sky' as sky;
7
8 import 'package:sky/painting/text_style.dart';
9 import 'package:sky/rendering/box.dart';
10 import 'package:sky/rendering/object.dart';
11 import 'package:sky/theme/colors.dart';
12 import 'package:sky/widgets/basic.dart';
13 import 'package:sky/widgets/icon.dart';
14 import 'package:sky/widgets/ink_well.dart';
15 import 'package:sky/widgets/widget.dart';
16
17 typedef void SelectedIndexChanged(int selectedIndex);
18
19 const double _kTabHeight = 46.0;
20 const double _kTabIndicatorHeight = 2.0;
21 const double _kTabBarHeight = _kTabHeight + _kTabIndicatorHeight;
22 const double _kMinTabWidth = 72.0;
23
24 class TabBarParentData extends BoxParentData with
25 ContainerParentDataMixin<RenderBox> { }
26
27 class RenderTabBar extends RenderBox with
28 ContainerRenderObjectMixin<RenderBox, TabBarParentData>,
29 RenderBoxContainerDefaultsMixin<RenderBox, TabBarParentData> {
30
31 int _selectedIndex;
32 int get selectedIndex => _selectedIndex;
33 void set selectedIndex(int value) {
34 if (_selectedIndex != value) {
35 _selectedIndex = value;
36 markNeedsPaint();
37 }
38 }
39
40 void setParentData(RenderBox child) {
41 if (child.parentData is! TabBarParentData) {
42 child.parentData = new TabBarParentData();
43 }
44 }
45
46 double getMinIntrinsicWidth(BoxConstraints constraints) {
47 BoxConstraints widthConstraints = constraints.widthConstraints();
48 double maxWidth = 0.0;
49 int childCount = 0;
50 for (RenderBox child = firstChild; child != null; child = child.parentData.n extSibling) {
51 maxWidth = math.max(maxWidth, child.getMinIntrinsicWidth(widthConstraints) );
52 ++childCount;
53 }
54 return maxWidth * childCount.toDouble();
55 }
56
57 double getMaxIntrinsicWidth(BoxConstraints constraints) {
58 BoxConstraints widthConstraints = constraints.widthConstraints();
59 double maxWidth = 0.0;
60 int childCount = 0;
61 for (RenderBox child = firstChild; child != null; child = child.parentData.n extSibling) {
62 maxWidth = math.max(maxWidth, child.getMaxIntrinsicWidth(widthConstraints) );
63 ++childCount;
64 }
65 return maxWidth * childCount.toDouble();
66 }
67
68 double _getIntrinsicHeight(BoxConstraints constraints) => constraints.constrai nHeight(_kTabBarHeight);
69
70 double getMinIntrinsicHeight(BoxConstraints constraints) => _getIntrinsicHeigh t(constraints);
71
72 double getMaxIntrinsicHeight(BoxConstraints constraints) => _getIntrinsicHeigh t(constraints);
73
74 int _childCount() {
75 int childCount = 0;
76 for (RenderBox child = firstChild; child != null; child = child.parentData.n extSibling) {
77 ++childCount;
78 }
79 return childCount;
80 }
81
82 void performLayout() {
83 assert(constraints is BoxConstraints);
84
85 size = constraints.constrain(new Size(constraints.maxWidth, _kTabBarHeight)) ;
86 assert(size.width < double.INFINITY);
87 assert(size.height < double.INFINITY);
88
89 int childCount = _childCount();
90 if (childCount == 0) return;
91
92 double tabWidth = constraints.constrainWidth(constraints.maxWidth) / childCo unt.toDouble();
93 BoxConstraints tabConstraints =
94 new BoxConstraints.tightFor(width: tabWidth, height: size.height);
95 double x = 0.0;
96 for (RenderBox child = firstChild; child != null; child = child.parentData.n extSibling) {
97 child.layout(tabConstraints, parentUsesSize: true);
abarth-chromium 2015/06/25 18:25:49 Rather than passing |parentUsesSize: true| here, w
hansmuller 2015/06/25 19:45:50 Sorry, I wasn't disagreeing with this feedback; It
98 assert(child.parentData is TabBarParentData);
99 child.parentData.position = new Point(x, 0.0);
100 x += child.size.width;
101 }
102 }
103
104 void hitTestChildren(HitTestResult result, { Point position }) {
105 defaultHitTestChildren(result, position: position);
106 }
107
108 void _paintBackground(RenderObjectDisplayList canvas) {
109 // TODO(hansmuller): background color should be based on the theme.
110 Paint paint = new Paint()
111 ..color = Blue[500]
112 ..setStyle(sky.PaintingStyle.fill);
113 Rect rect = new Rect.fromSize(size);
114 canvas.drawRect(rect, paint);
115 }
116
117 void _paintIndicator(RenderObjectDisplayList canvas, RenderBox selectedTab) {
118 var size = new Size(selectedTab.size.width, 2.0);
119 var point = new Point(selectedTab.parentData.position.x, 46.0);
120 Rect rect = new Rect.fromPointAndSize(point, size);
121 // TODO(hansmuller): indicator color should be based on the theme.
122 Paint paint = new Paint()
123 ..color = White
124 ..setStyle(sky.PaintingStyle.fill);
125 canvas.drawRect(rect, paint);
126 }
127
128 void paint(RenderObjectDisplayList canvas) {
129 _paintBackground(canvas);
130
131 var index = 0;
132 for (RenderBox child = firstChild; child != null; child = child.parentData.n extSibling) {
133 assert(child.parentData is TabBarParentData);
134 canvas.paintChild(child, child.parentData.position);
135 if (index++ == selectedIndex) _paintIndicator(canvas, child);
136 }
137 }
138 }
139
140 class TabBarWrapper extends MultiChildRenderObjectWrapper {
141 TabBarWrapper(List<Widget> children, int this.selectedIndex, { String key })
142 : super(key: key, children: children);
143
144 final int selectedIndex;
145
146 RenderTabBar get root => super.root;
147 RenderTabBar createNode() => new RenderTabBar();
148
149 void syncRenderObject(Widget old) {
150 super.syncRenderObject(old);
151 root.selectedIndex = selectedIndex;
152 }
153 }
154
155 class TabLabel {
156 const TabLabel({ this.text, this.icon });
157
158 final String text;
159 final String icon;
160 }
161
162 class Tab extends Component {
163 Tab({
164 String key,
165 this.label,
166 this.selected: false
167 }) : super(key: key) {
168 assert(label.text != null || label.icon != null);
169 }
170
171 final TabLabel label;
172 final bool selected;
173
174 // TODO(hansmuller): use themes here.
175 static const TextStyle selectedStyle = const TextStyle(color: const Color(0xFF FFFFFF));
176 static const TextStyle style = const TextStyle(color: const Color(0xB2FFFFFF)) ;
177
178 Widget _buildLabelText() {
179 assert(label.text != null);
180 return new Text(label.text, style: style);
181 }
182
183 Widget _buildLabelIcon() {
184 assert(label.icon != null);
185 return new Icon(type: label.icon, size: 24);
186 }
187
188 Widget build() {
189 Widget labelContents;
190 if (label.icon == null) {
191 labelContents = _buildLabelText();
192 } else if (label.text == null) {
193 labelContents = _buildLabelIcon();
194 } else {
195 labelContents = new Flex(
196 <Widget>[_buildLabelText(), _buildLabelIcon()],
197 justifyContent: FlexJustifyContent.center,
198 alignItems: FlexAlignItems.center,
199 direction: FlexDirection.vertical
200 );
201 }
202
203 Widget highlightedLabel = new Opacity(
204 child: labelContents,
205 opacity: selected ? 1.0 : 0.7
206 );
207
208 Container centeredLabel = new Container(
209 child: new Center(child: highlightedLabel),
210 constraints: new BoxConstraints(minWidth: _kMinTabWidth)
211 );
212
213 return new InkWell(child: centeredLabel);
214 }
215 }
216
217 class TabBar extends Component {
218 TabBar({
219 String key,
220 this.labels,
221 this.selectedIndex: 0,
222 this.onChanged
223 }) : super(key: key, stateful: true);
224
225 List<TabLabel> labels;
226 int selectedIndex;
227 SelectedIndexChanged onChanged;
228
229 void syncFields(TabBar source) {
230 labels = source.labels;
231 selectedIndex = source.selectedIndex;
232 onChanged = source.onChanged;
233 super.syncFields(source);
234 }
235
236 void _handleTap(int tabIndex) {
237 if (tabIndex != selectedIndex) {
238 setState(() {
239 selectedIndex = tabIndex;
240 });
241 if (onChanged != null) onChanged(selectedIndex);
242 }
243 }
244
245 Widget _toTab(TabLabel label, int tabIndex) {
246 Tab tab = new Tab(
247 label: label,
248 selected: tabIndex == selectedIndex,
249 key: label.text == null ? label.icon : label.text
250 );
251 return new Listener(
252 child: tab,
253 onGestureTap: (_) => _handleTap(tabIndex)
254 );
255 }
256
257 Widget build() {
258 assert(labels != null && labels.isNotEmpty);
259 List<Widget> tabs = <Widget>[];
260 for (int tabIndex = 0; tabIndex < labels.length; tabIndex++) {
261 tabs.add(_toTab(labels[tabIndex], tabIndex));
262 }
263 return new TabBarWrapper(tabs, selectedIndex);
264 }
265 }
266
267
OLDNEW
« no previous file with comments | « sky/sdk/lib/rendering/box.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine