提交 471a7f24 编写于 作者: H Hixie

Make the popup menu work again.

This removes the requirement that things with the same type things have unique keys.
Now, anything without a key is assumed to be interchangeable.

R=abarth@chromium.org

Review URL: https://codereview.chromium.org/1178723010.
上级 1339d2bc
......@@ -114,38 +114,32 @@ class StockHome extends Component {
children: [
new DrawerHeader(children: [new Text('Stocks')]),
new MenuItem(
key: 'Stock list',
icon: 'action/assessment',
children: [new Text('Stock List')]),
new MenuItem(
key: 'Account Balance',
icon: 'action/account_balance',
children: [new Text('Account Balance')]),
new MenuDivider(key: 'div1'),
new MenuDivider(),
new MenuItem(
key: 'Optimistic Menu Item',
icon: 'action/thumb_up',
onGestureTap: (event) => _handleStockModeChange(StockMode.optimistic),
children: [
new Flexible(child: new Text('Optimistic')),
new Radio(key: 'optimistic-radio', value: StockMode.optimistic, groupValue: _stockMode, onChanged: _handleStockModeChange)
new Radio(value: StockMode.optimistic, groupValue: _stockMode, onChanged: _handleStockModeChange)
]),
new MenuItem(
key: 'Pessimistic Menu Item',
icon: 'action/thumb_down',
onGestureTap: (event) => _handleStockModeChange(StockMode.pessimistic),
children: [
new Flexible(child: new Text('Pessimistic')),
new Radio(key: 'pessimistic-radio', value: StockMode.pessimistic, groupValue: _stockMode, onChanged: _handleStockModeChange)
new Radio(value: StockMode.pessimistic, groupValue: _stockMode, onChanged: _handleStockModeChange)
]),
new MenuDivider(key: 'div2'),
new MenuDivider(),
new MenuItem(
key: 'Settings',
icon: 'action/settings',
onGestureTap: (event) => _navigator.pushNamed('/settings'),
children: [new Text('Settings')]),
new MenuItem(
key: 'Help & Feedback',
icon: 'action/help',
children: [new Text('Help & Feedback')])
]
......
......@@ -27,16 +27,25 @@ class StockRow extends Component {
List<Widget> children = [
new Container(
child: new StockArrow(percentChange: stock.percentChange),
margin: const EdgeDims.only(right: 5.0)),
new Flexible(child: new Text(stock.symbol), flex: 2, key: "symbol"),
// TODO(hansmuller): text-align: right
new Flexible(child: new Text(lastSale,
style: const TextStyle(textAlign: TextAlign.right)),
key: "lastSale"),
new Flexible(child: new Text(changeInPrice,
style: typography.black.caption.copyWith(textAlign: TextAlign.right)),
key: "changeInPrice")
child: new StockArrow(percentChange: stock.percentChange),
margin: const EdgeDims.only(right: 5.0)
),
new Flexible(
child: new Text(stock.symbol),
flex: 2
),
new Flexible(
child: new Text(
lastSale,
style: const TextStyle(textAlign: TextAlign.right)
)
),
new Flexible(
child: new Text(
changeInPrice,
style: typography.black.caption.copyWith(textAlign: TextAlign.right)
)
)
];
// TODO(hansmuller): An explicit |height| shouldn't be needed
......
......@@ -41,12 +41,11 @@ class StockSettings extends Component {
decoration: new BoxDecoration(backgroundColor: colors.Grey[50]),
child: new Block([
new MenuItem(
key: 'Optimistic Setting',
icon: 'action/thumb_up',
onGestureTap: (event) => _handleAwesomeChanged(!_awesome),
children: [
new Flexible(child: new Text('Everything is awesome')),
new Checkbox(key: 'awesome', value: _awesome, onChanged: _handleAwesomeChanged)
new Checkbox(value: _awesome, onChanged: _handleAwesomeChanged)
]
),
])
......
......@@ -346,7 +346,7 @@ class Flexible extends ParentDataNode {
}
class Inline extends RenderObjectWrapper {
Inline({ Object key, this.text }) : super(key: key);
Inline({ String key, this.text }) : super(key: key);
RenderParagraph get root => super.root;
RenderParagraph createNode() => new RenderParagraph(text);
......@@ -366,7 +366,7 @@ class Inline extends RenderObjectWrapper {
}
class Text extends Component {
Text(this.data, { TextStyle this.style }) : super(key: '*text*');
Text(data, { String key, TextStyle this.style }) : data = data, super(key: key);
final String data;
final TextStyle style;
bool get interchangeable => true;
......
......@@ -27,9 +27,8 @@ class DrawerHeader extends Component {
padding: const EdgeDims.only(bottom: 7.0),
margin: const EdgeDims.only(bottom: 8.0),
child: new Flex([
new Flexible(child: new Container(key: 'drawer-header-spacer')),
new Flexible(child: new Container()),
new Container(
key: 'drawer-header-label',
padding: const EdgeDims.symmetric(horizontal: 16.0),
child: new Flex(children, direction: FlexDirection.horizontal)
)],
......
......@@ -68,12 +68,15 @@ abstract class FixedHeightScrollable extends Scrollable {
}
}
List<Widget> items = buildItems(itemShowIndex, itemShowCount);
assert(items.every((item) => item.key != null));
return new SizeObserver(
callback: _handleSizeChanged,
child: new ClipRect(
child: new Transform(
transform: transform,
child: new Block(buildItems(itemShowIndex, itemShowCount))
child: new Block(items)
)
)
);
......
......@@ -94,9 +94,7 @@ class PopupMenu extends AnimatedComponent {
int i = 0;
List<Widget> children = new List.from(items.map((Widget item) {
double opacity = _opacityFor(i);
return new PopupMenuItem(key: '${key}-${item.key}',
child: item,
opacity: opacity);
return new PopupMenuItem(child: item, opacity: opacity);
}));
return new Opacity(
......
......@@ -21,8 +21,7 @@ final bool _shouldLogRenderDuration = false;
// can be sync'd.
abstract class Widget {
Widget({ String key }) {
_key = key != null ? key : runtimeType.toString();
Widget({ String key }) : _key = key {
assert(this is AbstractWidgetRoot || this is App || _inRenderDirtyComponents); // you should not build the UI tree ahead of time, build it only during build()
}
......@@ -84,8 +83,6 @@ abstract class Widget {
// Component._retainStatefulNodeIfPossible() calls syncFields().
bool _retainStatefulNodeIfPossible(Widget old) => false;
bool get interchangeable => false; // if true, then keys can be duplicated
void _sync(Widget old, dynamic slot);
// 'slot' is the identifier that the parent RenderObjectWrapper uses to know
// where to put this descendant
......@@ -154,7 +151,11 @@ abstract class Widget {
return node;
}
String toString() => '$runtimeType($key)';
String toString() {
if (key == null)
return '$runtimeType(unkeyed)';
return '$runtimeType("$key")';
}
}
......@@ -628,11 +629,11 @@ abstract class MultiChildRenderObjectWrapper extends RenderObjectWrapper {
var idSet = new HashSet<String>();
for (var child in children) {
assert(child != null);
if (child.interchangeable)
if (child.key == null)
continue; // when these nodes are reordered, we just reassign the data
if (!idSet.add(child.key)) {
throw '''If multiple non-interchangeable nodes exist as children of another node, they must have unique keys. Duplicate: "${child.key}"''';
throw '''If multiple keyed nodes exist as children of another node, they must have unique keys. $this has duplicate child key "${child.key}".''';
}
}
return false;
......@@ -700,13 +701,13 @@ abstract class MultiChildRenderObjectWrapper extends RenderObjectWrapper {
oldNodeIdMap = new HashMap<String, Widget>();
for (int i = oldStartIndex; i < oldEndIndex; i++) {
var node = oldChildren[i];
if (!node.interchangeable)
if (node.key != null)
oldNodeIdMap.putIfAbsent(node.key, () => node);
}
}
bool searchForOldNode() {
if (currentNode.interchangeable)
if (currentNode.key == null)
return false; // never re-order these nodes
ensureOldIdMap();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册