提交 0ff3df83 编写于 作者: H Hans Muller

Version 0 of TabLabel, Tab, and TabBar components

There's is no support for animating the selected tab indicator, there isn't a TabNavigator container yet, overflow layout (tabs don't fit) isn't supported yet, etc.

R=abarth@chromium.org, ianh@google.com

Review URL: https://codereview.chromium.org/1205953002.
上级 1a1ef489
// 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/theme/colors.dart';
import 'package:sky/theme/typography.dart';
import 'package:sky/widgets/basic.dart';
import 'package:sky/widgets/material.dart';
import 'package:sky/widgets/scaffold.dart';
import 'package:sky/widgets/tabs.dart';
import 'package:sky/widgets/tool_bar.dart';
import 'package:sky/widgets/widget.dart';
class TabbedNavigatorApp extends App {
static Iterable<String> items = const <String>["ONE", "TWO", "FREE", "FOUR"];
final List<int> navigatorSelections = new List<int>.filled(items.length, 0);
Widget buildTabNavigator(Iterable<TabLabel> labels, int navigatorIndex) {
TabBar tabBar = new TabBar(
labels: labels.toList(),
selectedIndex: navigatorSelections[navigatorIndex],
onChanged: (selectionIndex) {
setState(() {
navigatorSelections[navigatorIndex] = selectionIndex;
});
}
);
return new Container(child: tabBar, margin: new EdgeDims.only(bottom: 16.0));
}
Widget build() {
Iterable<TabLabel> textLabels = items
.map((s) => new TabLabel(text: "ITEM " + s));
Iterable<TabLabel> iconLabels = items
.map((s) => new TabLabel(icon: 'action/search_white'));
Iterable<TabLabel> textAndIconLabels = items
.map((s) => new TabLabel(text: "ITEM " + s, icon: 'action/search_white'));
var navigatorIndex = 0;
Iterable<Widget> tabNavigators = [textLabels, iconLabels, textAndIconLabels]
.map((labels) => buildTabNavigator(labels, navigatorIndex++));
ToolBar toolbar = new ToolBar(
center: new Text('Tabbed Navigator', style: white.title),
backgroundColor: Blue[500]);
return new Scaffold(
toolbar: toolbar,
body: new Material(
child: new Center(child: new Block(tabNavigators.toList())),
color: Grey[500]
)
);
}
}
void main() {
runApp(new TabbedNavigatorApp());
}
......@@ -119,6 +119,7 @@ dart_pkg("sdk") {
"lib/widgets/scaffold.dart",
"lib/widgets/scrollable.dart",
"lib/widgets/switch.dart",
"lib/widgets/tabs.dart",
"lib/widgets/theme.dart",
"lib/widgets/toggleable.dart",
"lib/widgets/tool_bar.dart",
......
......@@ -28,11 +28,11 @@ class RenderBlock extends RenderBox with ContainerRenderObjectMixin<RenderBox, B
double getMinIntrinsicWidth(BoxConstraints constraints) {
double width = 0.0;
BoxConstraints innerConstraints = new BoxConstraints(
minWidth: constraints.minWidth, maxWidth: constraints.maxWidth);
BoxConstraints innerConstraints = constraints.widthConstraints();
RenderBox child = firstChild;
while (child != null) {
width = math.max(width, child.getMinIntrinsicWidth(innerConstraints));
assert(child.parentData is BlockParentData);
child = child.parentData.nextSibling;
}
return width;
......@@ -40,11 +40,11 @@ class RenderBlock extends RenderBox with ContainerRenderObjectMixin<RenderBox, B
double getMaxIntrinsicWidth(BoxConstraints constraints) {
double width = 0.0;
BoxConstraints innerConstraints = new BoxConstraints(
minWidth: constraints.minWidth, maxWidth: constraints.maxWidth);
BoxConstraints innerConstraints = constraints.widthConstraints();
RenderBox child = firstChild;
while (child != null) {
width = math.max(width, child.getMaxIntrinsicWidth(innerConstraints));
assert(child.parentData is BlockParentData);
child = child.parentData.nextSibling;
}
return width;
......@@ -63,6 +63,7 @@ class RenderBlock extends RenderBox with ContainerRenderObjectMixin<RenderBox, B
double childHeight = child.getMinIntrinsicHeight(innerConstraints);
assert(childHeight == child.getMaxIntrinsicHeight(innerConstraints));
height += childHeight;
assert(child.parentData is BlockParentData);
child = child.parentData.nextSibling;
}
return height;
......
......@@ -163,6 +163,10 @@ class BoxConstraints extends Constraints {
maxHeight: math.min(maxHeight, newMaxHeight));
}
BoxConstraints widthConstraints() => new BoxConstraints(minWidth: minWidth, maxWidth: maxWidth);
BoxConstraints heightConstraints() => new BoxConstraints(minHeight: minHeight, maxHeight: maxHeight);
final double minWidth;
final double maxWidth;
final double minHeight;
......
// 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 'dart:math' as math;
import 'dart:sky' as sky;
import 'package:sky/painting/text_style.dart';
import 'package:sky/rendering/box.dart';
import 'package:sky/rendering/object.dart';
import 'package:sky/theme/colors.dart';
import 'package:sky/widgets/basic.dart';
import 'package:sky/widgets/icon.dart';
import 'package:sky/widgets/ink_well.dart';
import 'package:sky/widgets/widget.dart';
typedef void SelectedIndexChanged(int selectedIndex);
const double _kTabHeight = 46.0;
const double _kTabIndicatorHeight = 2.0;
const double _kTabBarHeight = _kTabHeight + _kTabIndicatorHeight;
const double _kMinTabWidth = 72.0;
class TabBarParentData extends BoxParentData with
ContainerParentDataMixin<RenderBox> { }
class RenderTabBar extends RenderBox with
ContainerRenderObjectMixin<RenderBox, TabBarParentData>,
RenderBoxContainerDefaultsMixin<RenderBox, TabBarParentData> {
int _selectedIndex;
int get selectedIndex => _selectedIndex;
void set selectedIndex(int value) {
if (_selectedIndex != value) {
_selectedIndex = value;
markNeedsPaint();
}
}
void setParentData(RenderBox child) {
if (child.parentData is! TabBarParentData)
child.parentData = new TabBarParentData();
}
double getMinIntrinsicWidth(BoxConstraints constraints) {
BoxConstraints widthConstraints =
new BoxConstraints(maxWidth: constraints.maxWidth, maxHeight: constraints.maxHeight);
double maxWidth = 0.0;
int childCount = 0;
RenderBox child = firstChild;
while (child != null) {
maxWidth = math.max(maxWidth, child.getMinIntrinsicWidth(widthConstraints));
++childCount;
assert(child.parentData is TabBarParentData);
child = child.parentData.nextSibling;
}
return constraints.constrainWidth(maxWidth * childCount);
}
double getMaxIntrinsicWidth(BoxConstraints constraints) {
BoxConstraints widthConstraints =
new BoxConstraints(maxWidth: constraints.maxWidth, maxHeight: constraints.maxHeight);
double maxWidth = 0.0;
int childCount = 0;
RenderBox child = firstChild;
while (child != null) {
maxWidth = math.max(maxWidth, child.getMaxIntrinsicWidth(widthConstraints));
++childCount;
assert(child.parentData is TabBarParentData);
child = child.parentData.nextSibling;
}
return constraints.constrainWidth(maxWidth * childCount);
}
double _getIntrinsicHeight(BoxConstraints constraints) => constraints.constrainHeight(_kTabBarHeight);
double getMinIntrinsicHeight(BoxConstraints constraints) => _getIntrinsicHeight(constraints);
double getMaxIntrinsicHeight(BoxConstraints constraints) => _getIntrinsicHeight(constraints);
// TODO(hansmuller): track this value in the parent rather than computing it.
int _childCount() {
int childCount = 0;
RenderBox child = firstChild;
while (child != null) {
++childCount;
assert(child.parentData is TabBarParentData);
child = child.parentData.nextSibling;
}
return childCount;
}
void performLayout() {
assert(constraints is BoxConstraints);
size = constraints.constrain(new Size(constraints.maxWidth, _kTabBarHeight));
assert(size.width < double.INFINITY);
assert(size.height < double.INFINITY);
int childCount = _childCount();
if (childCount == 0)
return;
double tabWidth = size.width / childCount;
BoxConstraints tabConstraints =
new BoxConstraints.tightFor(width: tabWidth, height: size.height);
double x = 0.0;
RenderBox child = firstChild;
while (child != null) {
child.layout(tabConstraints);
assert(child.parentData is TabBarParentData);
child.parentData.position = new Point(x, 0.0);
x += tabWidth;
child = child.parentData.nextSibling;
}
}
void hitTestChildren(HitTestResult result, { Point position }) {
defaultHitTestChildren(result, position: position);
}
void _paintIndicator(RenderCanvas canvas, RenderBox selectedTab) {
var size = new Size(selectedTab.size.width, _kTabIndicatorHeight);
var point = new Point(selectedTab.parentData.position.x, _kTabHeight);
Rect rect = new Rect.fromPointAndSize(point, size);
// TODO(hansmuller): indicator color should be based on the theme.
canvas.drawRect(rect, new Paint()..color = White);
}
void paint(RenderCanvas canvas) {
Rect rect = new Rect.fromSize(size);
canvas.drawRect(rect, new Paint()..color = Blue[500]);
int index = 0;
RenderBox child = firstChild;
while (child != null) {
assert(child.parentData is TabBarParentData);
canvas.paintChild(child, child.parentData.position);
if (index++ == selectedIndex)
_paintIndicator(canvas, child);
child = child.parentData.nextSibling;
}
}
}
class TabBarWrapper extends MultiChildRenderObjectWrapper {
TabBarWrapper(List<Widget> children, this.selectedIndex, { String key })
: super(key: key, children: children);
final int selectedIndex;
RenderTabBar get root => super.root;
RenderTabBar createNode() => new RenderTabBar();
void syncRenderObject(Widget old) {
super.syncRenderObject(old);
root.selectedIndex = selectedIndex;
}
}
class TabLabel {
const TabLabel({ this.text, this.icon });
final String text;
final String icon;
}
class Tab extends Component {
Tab({
String key,
this.label,
this.selected: false
}) : super(key: key) {
assert(label.text != null || label.icon != null);
}
final TabLabel label;
final bool selected;
// TODO(hansmuller): use themes here.
static const TextStyle selectedStyle = const TextStyle(color: const Color(0xFFFFFFFF));
static const TextStyle style = const TextStyle(color: const Color(0xB2FFFFFF));
Widget _buildLabelText() {
assert(label.text != null);
return new Text(label.text, style: style);
}
Widget _buildLabelIcon() {
assert(label.icon != null);
return new Icon(type: label.icon, size: 24);
}
Widget build() {
Widget labelContents;
if (label.icon == null) {
labelContents = _buildLabelText();
} else if (label.text == null) {
labelContents = _buildLabelIcon();
} else {
labelContents = new Flex(
<Widget>[_buildLabelText(), _buildLabelIcon()],
justifyContent: FlexJustifyContent.center,
alignItems: FlexAlignItems.center,
direction: FlexDirection.vertical
);
}
Widget highlightedLabel = new Opacity(
child: labelContents,
opacity: selected ? 1.0 : 0.7
);
Container centeredLabel = new Container(
child: new Center(child: highlightedLabel),
constraints: new BoxConstraints(minWidth: _kMinTabWidth)
);
return new InkWell(child: centeredLabel);
}
}
class TabBar extends Component {
TabBar({
String key,
this.labels,
this.selectedIndex: 0,
this.onChanged
}) : super(key: key);
final List<TabLabel> labels;
final int selectedIndex;
final SelectedIndexChanged onChanged;
void _handleTap(int tabIndex) {
if (tabIndex != selectedIndex && onChanged != null)
onChanged(tabIndex);
}
Widget _toTab(TabLabel label, int tabIndex) {
Tab tab = new Tab(
label: label,
selected: tabIndex == selectedIndex,
key: label.text == null ? label.icon : label.text
);
return new Listener(
child: tab,
onGestureTap: (_) => _handleTap(tabIndex)
);
}
Widget build() {
assert(labels != null && labels.isNotEmpty);
List<Widget> tabs = <Widget>[];
for (int tabIndex = 0; tabIndex < labels.length; tabIndex++) {
tabs.add(_toTab(labels[tabIndex], tabIndex));
}
return new TabBarWrapper(tabs, selectedIndex);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册