提交 003083a1 编写于 作者: H Hixie

Remove one more use of mirrors: Components now have to explicitly sync their fields.

This also removes one bit of magic to make it more obvious what on is
going on during a sync, which should hopefully help.

Components have to decide if they support being stateful or not. If
they do, then they must implement syncFields() and have mutable
fields; if they don't, then they must have final fields. This isn't
particularly enforced, though.

This also renames _willSync() to _retainStatefulNodeIfPossible(), for
clarity, and fixes some minor style issues and one typo that was
breaking the drawer.

R=abarth@chromium.org

Review URL: https://codereview.chromium.org/1174023003
上级 d174ed2a
......@@ -46,6 +46,8 @@ class StocksApp extends App {
_drawerController = new DrawerController(_handleDrawerStatusChanged);
}
void syncFields(StocksApp source) { }
bool _isSearching = false;
String _searchQuery;
......
......@@ -12,10 +12,11 @@ import 'dart:math' as math;
import 'dart:sky' as sky;
class StockArrow extends Component {
double percentChange;
StockArrow({ Object key, this.percentChange }) : super(key: key);
final double percentChange;
int _colorIndexForPercentChange(double percentChange) {
double maxPercent = 10.0;
double normalizedPercentChange = math.min(percentChange.abs(), maxPercent) / maxPercent;
......@@ -71,4 +72,5 @@ class StockArrow extends Component {
height: size,
margin: const EdgeDims.symmetric(horizontal: 5.0));
}
}
......@@ -8,8 +8,6 @@ import 'stock_data.dart';
import 'stock_row.dart';
class Stocklist extends FixedHeightScrollable {
String query;
List<Stock> stocks;
Stocklist({
Object key,
......@@ -17,6 +15,15 @@ class Stocklist extends FixedHeightScrollable {
this.query
}) : super(itemHeight: StockRow.kHeight, key: key);
String query;
List<Stock> stocks;
void syncFields(Stocklist source) {
query = source.query;
stocks = source.stocks;
super.syncFields(source);
}
List<UINode> buildItems(int start, int count) {
var filteredStocks = stocks.where((stock) {
return query == null ||
......
......@@ -8,10 +8,15 @@ import 'package:sky/framework/components2/checkbox.dart';
import 'package:sky/framework/theme/view_configuration.dart';
class StockMenu extends Component {
PopupMenuController controller;
StockMenu({Object key, this.controller, this.autorefresh: false, this.onAutorefreshChanged}) : super(key: key);
StockMenu({
Object key,
this.controller,
this.autorefresh: false,
this.onAutorefreshChanged
}) : super(key: key);
final PopupMenuController controller;
final bool autorefresh;
final ValueChanged onAutorefreshChanged;
......
......@@ -12,13 +12,12 @@ import 'stock_arrow.dart';
import 'stock_data.dart';
class StockRow extends Component {
static const double kHeight = 70.0;
Stock stock;
StockRow({ Stock stock }) : this.stock = stock, super(key: stock.symbol);
StockRow({ Stock stock }) : super(key: stock.symbol) {
this.stock = stock;
}
final Stock stock;
static const double kHeight = 70.0;
UINode build() {
String lastSale = "\$${stock.lastSale.toStringAsFixed(2)}";
......
......@@ -12,6 +12,8 @@ abstract class AnimatedComponent extends Component {
AnimatedComponent({ Object key }) : super(key: key, stateful: true);
void syncFields(AnimatedComponent source) { }
animate(AnimatedValue value, SetterFunction setter) {
setter(value.value);
StreamSubscription<double> subscription;
......
......@@ -19,8 +19,8 @@ class Button extends Component {
border-radius: 2px;'''
);
UINode content;
int level;
final UINode content;
final int level;
UINode build() {
return new StyleNode(
......
......@@ -6,21 +6,14 @@ import '../fn2.dart';
abstract class ButtonBase extends Component {
ButtonBase({ Object key }) : super(key: key);
ButtonBase({ Object key, this.highlight: false }) : super(key: key);
UINode buildContent();
bool highlight;
UINode build() {
return new EventListenerNode(
buildContent(),
onPointerDown: _handlePointerDown,
onPointerUp: _handlePointerUp,
onPointerCancel: _handlePointerCancel
);
void syncFields(ButtonBase source) {
highlight = source.highlight;
}
bool highlight = false;
void _handlePointerDown(_) {
setState(() {
highlight = true;
......@@ -36,4 +29,16 @@ abstract class ButtonBase extends Component {
highlight = false;
});
}
UINode build() {
return new EventListenerNode(
buildContent(),
onPointerDown: _handlePointerDown,
onPointerUp: _handlePointerUp,
onPointerCancel: _handlePointerCancel
);
}
UINode buildContent();
}
......@@ -19,6 +19,12 @@ class Checkbox extends ButtonBase {
bool checked;
ValueChanged onChanged;
void syncFields(Checkbox source) {
checked = source.checked;
onChanged = source.onChanged;
super.syncFields(source);
}
void _handleClick(sky.Event e) {
onChanged(!checked);
}
......
......@@ -109,6 +109,13 @@ class Drawer extends AnimatedComponent {
int level;
DrawerController controller;
void syncFields(Drawer source) {
children = source.children;
level = source.level;
controller = source.controller;
super.syncFields(source);
}
double _position;
UINode build() {
......
......@@ -10,7 +10,7 @@ class DrawerHeader extends Component {
DrawerHeader({ Object key, this.children }) : super(key: key);
List<UINode> children;
final List<UINode> children;
UINode build() {
return new Container(
......
......@@ -10,16 +10,21 @@ import 'package:vector_math/vector_math.dart';
import 'scrollable.dart';
abstract class FixedHeightScrollable extends Scrollable {
FixedHeightScrollable({ this.itemHeight, Object key }) : super(key: key) {
assert(itemHeight != null);
}
double itemHeight;
void syncFields(FixedHeightScrollable source) {
itemHeight = source.itemHeight;
super.syncFields(source);
}
ScrollBehavior createScrollBehavior() => new OverscrollBehavior();
OverscrollBehavior get scrollBehavior => super.scrollBehavior as OverscrollBehavior;
double _height;
final double itemHeight;
int _itemCount = 0;
int get itemCount => _itemCount;
void set itemCount (int value) {
......@@ -29,6 +34,7 @@ abstract class FixedHeightScrollable extends Scrollable {
}
}
double _height;
void _handleSizeChanged(Size newSize) {
setState(() {
_height = newSize.height;
......@@ -37,24 +43,24 @@ abstract class FixedHeightScrollable extends Scrollable {
}
UINode buildContent() {
var itemNumber = 0;
var itemCount = 0;
var itemShowIndex = 0;
var itemShowCount = 0;
Matrix4 transform = new Matrix4.identity();
if (_height != null && _height > 0.0) {
if (scrollOffset < 0.0) {
double visibleHeight = _height + scrollOffset;
itemCount = (visibleHeight / itemHeight).round() + 1;
itemShowCount = (visibleHeight / itemHeight).round() + 1;
transform.translate(0.0, -scrollOffset);
} else {
itemCount = (_height / itemHeight).ceil() + 1;
itemShowCount = (_height / itemHeight).ceil() + 1;
double alignmentDelta = -scrollOffset % itemHeight;
if (alignmentDelta != 0.0)
alignmentDelta -= itemHeight;
double drawStart = scrollOffset + alignmentDelta;
itemNumber = math.max(0, (drawStart / itemHeight).floor());
itemShowIndex = math.max(0, (drawStart / itemHeight).floor());
transform.translate(0.0, alignmentDelta);
}
......@@ -70,7 +76,7 @@ abstract class FixedHeightScrollable extends Scrollable {
child: new Transform(
transform: transform,
child: new BlockContainer(
children: buildItems(itemNumber, itemCount))
children: buildItems(itemShowIndex, itemShowCount))
)
)
)
......@@ -78,4 +84,5 @@ abstract class FixedHeightScrollable extends Scrollable {
}
List<UINode> buildItems(int start, int count);
}
......@@ -17,8 +17,8 @@ class FloatingActionButton extends Component {
FloatingActionButton({ Object key, this.content, this.level: 0 })
: super(key: key);
UINode content;
int level;
final UINode content;
final int level;
UINode build() {
List<UINode> children = [];
......
......@@ -15,8 +15,8 @@ class Icon extends Component {
this.type: ''
}) : super(key: key);
int size;
String type;
final int size;
final String type;
UINode build() {
String category = '';
......
......@@ -11,8 +11,8 @@ class IconButton extends Component {
IconButton({ String icon: '', this.onGestureTap })
: super(key: icon), icon = icon;
String icon;
GestureEventListener onGestureTap;
final String icon;
final GestureEventListener onGestureTap;
UINode build() {
return new EventListenerNode(
......
......@@ -113,7 +113,7 @@ class InkWellWrapper extends OneChildRenderObjectWrapper {
class InkWell extends Component {
InkWell({ Object key, this.children }) : super(key: key);
List<UINode> children;
final List<UINode> children;
UINode build() {
return new InkWellWrapper(
......
......@@ -50,10 +50,17 @@ class Input extends Component {
// padding: 7px;
// border-bottom: 2px solid ${Blue[500]};''';
ValueChanged onChanged;
String placeholder;
ValueChanged onChanged;
bool focused = false;
void syncFields(Input source) {
placeholder = source.placeholder;
onChanged = source.onChanged;
focused = source.focused;
super.syncFields(source);
}
String _value = '';
bool _isAttachedToKeyboard = false;
EditableString _editableValue;
......
......@@ -18,8 +18,8 @@ class Material extends Component {
// new Style('box-shadow: ${Shadow[5]}'),
// ];
UINode content;
int level;
final UINode content;
final int level;
UINode build() {
// TODO(eseidel): Add a shadow.
......
......@@ -12,7 +12,7 @@ const BoxDecoration _kHighlightDecoration = const BoxDecoration(
);
// TODO(abarth): We shouldn't need _kHighlightBoring, but currently Container
// isn't smarth enough to retain the components it builds when we
// isn't smart enough to retain the components it builds when we
// add or remove a |decoration|. For now, we use a transparent
// decoration to avoid changing the structure of the tree. The
// right fix, however, is to make Container smarter about how it
......@@ -24,10 +24,17 @@ const BoxDecoration _kHighlightBoring = const BoxDecoration(
class MenuItem extends ButtonBase {
MenuItem({ Object key, this.icon, this.children, this.onGestureTap }) : super(key: key);
List<UINode> children;
String icon;
List<UINode> children;
GestureEventListener onGestureTap;
void syncFields(MenuItem source) {
icon = source.icon;
children = source.children;
onGestureTap = source.onGestureTap;
super.syncFields(source);
}
UINode buildContent() {
return new EventListenerNode(
new Container(
......
......@@ -15,8 +15,8 @@ class ModalOverlay extends Component {
// bottom: 0;
// right: 0;''');
List<UINode> children;
GestureEventListener onDismiss;
final List<UINode> children;
final GestureEventListener onDismiss;
UINode build() {
return new EventListenerNode(
......
......@@ -60,9 +60,16 @@ class PopupMenu extends AnimatedComponent {
// onDidMount(_measureSize);
}
PopupMenuController controller;
List<List<UINode>> items;
int level;
PopupMenuController controller;
void syncFields(PopupMenu source) {
controller = source.controller;
items = source.items;
level = source.level;
super.syncFields(source);
}
double _position;
// int _width;
......
......@@ -8,8 +8,8 @@ import 'ink_well.dart';
class PopupMenuItem extends Component {
PopupMenuItem({ Object key, this.children, this.opacity}) : super(key: key);
List<UINode> children;
double opacity;
final List<UINode> children;
final double opacity;
UINode build() {
return new Container(
......
......@@ -16,15 +16,22 @@ class Radio extends ButtonBase {
Radio({
Object key,
this.onChanged,
this.value,
this.groupValue
this.groupValue,
this.onChanged
}) : super(key: key);
Object value;
Object groupValue;
ValueChanged onChanged;
void syncFields(Radio source) {
value = source.value;
groupValue = source.groupValue;
onChanged = source.onChanged;
super.syncFields(source);
}
UINode buildContent() {
// TODO(jackson): This should change colors with the theme
Color color = highlight ? colors.Purple[500] : const Color(0x8A000000);
......
......@@ -23,10 +23,12 @@ abstract class ScrollClient {
abstract class Scrollable extends Component {
Scrollable({Object key}) : super(key: key) {
Scrollable({Object key}) : super(key: key, stateful: true) {
onDidUnmount(_stopSimulation);
}
void syncFields(Scrollable source) { }
double _scrollOffset = 0.0;
double get scrollOffset => _scrollOffset;
......
......@@ -16,10 +16,10 @@ class ToolBar extends Component {
this.backgroundColor
}) : super(key: key);
UINode left;
UINode center;
List<UINode> right;
Color backgroundColor;
final UINode left;
final UINode center;
final List<UINode> right;
final Color backgroundColor;
UINode build() {
List<UINode> children = [
......
......@@ -10,7 +10,6 @@ import 'dart:collection';
import 'dart:mirrors';
import 'dart:sky' as sky;
import 'package:vector_math/vector_math.dart';
import 'reflect.dart' as reflect;
import 'rendering/block.dart';
import 'rendering/box.dart';
import 'rendering/flex.dart';
......@@ -89,7 +88,9 @@ abstract class UINode {
// Subclasses which implements Nodes that become stateful may return true
// if the |old| node has become stateful and should be retained.
bool _willSync(UINode old) => false;
// This is called immediately before _sync().
// Component._retainStatefulNodeIfPossible() calls syncFields().
bool _retainStatefulNodeIfPossible(UINode old) => false;
bool get interchangeable => false; // if true, then keys can be duplicated
......@@ -131,7 +132,7 @@ abstract class UINode {
return null;
}
if (oldNode != null && node._key == oldNode._key && node._willSync(oldNode)) {
if (oldNode != null && node._key == oldNode._key && node._retainStatefulNodeIfPossible(oldNode)) {
assert(oldNode.mounted);
assert(!node.mounted);
oldNode._sync(node, slot);
......@@ -929,26 +930,38 @@ abstract class Component extends UINode {
super.remove();
}
bool _willSync(UINode old) {
bool _retainStatefulNodeIfPossible(UINode old) {
assert(!_disqualifiedFromEverAppearingAgain);
Component oldComponent = old as Component;
if (oldComponent == null || !oldComponent._stateful)
return false;
// Make |this| the "old" Component
assert(key == oldComponent.key);
// Make |this|, the newly-created object, into the "old" Component, and kill it
_stateful = false;
_built = oldComponent._built;
assert(_built != null);
_disqualifiedFromEverAppearingAgain = true;
// Make |oldComponent| the "new" component
reflect.copyPublicFields(this, oldComponent);
oldComponent._built = null;
oldComponent._dirty = true;
oldComponent.syncFields(this);
return true;
}
// This is called by _retainStatefulNodeIfPossible(), during
// syncChild(), just before _sync() is called.
// This must be implemented on any subclass that can become stateful
// (but don't call super.syncFields() if you inherit directly from
// Component, since that'll fire an assert).
// If you don't ever become stateful, then don't override this.
void syncFields(Component source) {
assert(false);
}
final int _order;
static int _currentOrder = 0;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册