Commit d45bf145 authored by Hixie's avatar Hixie

HomogeneousViewport support for Theme.of()

Previously, RenderObjectElements didn't support being marked dirty. This
is fine, except for MixedViewport and HomogeneousViewport, which have
builder functions to which they hand themselves as a BuildContext. If
those builder functions call, e.g., Theme.of(), then when the theme
changes, the Inherited logic tries to tell the RenderObjectElement
object that its dependencies changed and that doesn't go down well.

This patch fixes this by making RenderObjectElement a BuildableElement,
and making MixedViewport and HomogeneousViewport hook into that to
rebuild themselves appropriately.

Also, this was only found at all because ThemeData didn't implement
operator==, so we were aggressively marking the entire tree dirty all
the time. That's fixed here too.

Also, I changed card_collection.dart to have more features to make this
easier to test. This found bugs #1524, #1522, #1528, #1529, #1530, #1531.
parent 3eaefd81
......@@ -9,11 +9,13 @@ import 'package:sky/painting.dart';
import 'package:sky/widgets.dart';
class CardModel {
CardModel(this.value, this.height, this.color);
CardModel(this.value, this.height) {
label = "Item $value";
}
int value;
double height;
Color color;
String get label => "Item $value";
int get color => ((value % 9) + 1) * 100;
String label;
Key get key => new ObjectKey(this);
}
......@@ -36,6 +38,7 @@ class CardCollectionState extends State<CardCollection> {
final TextStyle backgroundTextStyle =
Typography.white.title.copyWith(textAlign: TextAlign.center);
Map<int, Color> _primaryColor = Colors.deepPurple;
List<CardModel> _cardModels;
DismissDirection _dismissDirection = DismissDirection.horizontal;
bool _snapToCenter = false;
......@@ -50,19 +53,13 @@ class CardCollectionState extends State<CardCollection> {
48.0, 63.0, 82.0, 146.0, 60.0, 55.0, 84.0, 96.0, 50.0,
48.0, 63.0, 82.0, 146.0, 60.0, 55.0, 84.0, 96.0, 50.0
];
_cardModels = new List.generate(cardHeights.length, (i) {
Color color = Color.lerp(Colors.red[300], Colors.blue[900], i / cardHeights.length);
return new CardModel(i, cardHeights[i], color);
});
_cardModels = new List.generate(cardHeights.length, (i) => new CardModel(i, cardHeights[i]));
}
void _initFixedSizedCardModels() {
const int cardCount = 27;
const double cardHeight = 100.0;
_cardModels = new List.generate(cardCount, (i) {
Color color = Color.lerp(Colors.red[300], Colors.blue[900], i / cardCount);
return new CardModel(i, cardHeight, color);
});
_cardModels = new List.generate(cardCount, (i) => new CardModel(i, cardHeight));
}
void _initCardModels() {
......@@ -126,9 +123,14 @@ class CardCollectionState extends State<CardCollection> {
buildDrawerCheckbox("Fixed size cards", _fixedSizeCards, _toggleFixedSizeCards),
buildDrawerCheckbox("Let the sun shine", _sunshine, _toggleSunshine),
new DrawerDivider(),
buildDrawerRadioItem(DismissDirection.horizontal, 'action/code'),
buildDrawerRadioItem(DismissDirection.left, 'navigation/arrow_back'),
buildDrawerRadioItem(DismissDirection.right, 'navigation/arrow_forward'),
buildDrawerRadioItem("Deep Purple", Colors.deepPurple, _primaryColor, _selectColor),
buildDrawerRadioItem("Green", Colors.green, _primaryColor, _selectColor),
buildDrawerRadioItem("Amber", Colors.amber, _primaryColor, _selectColor),
buildDrawerRadioItem("Teal", Colors.teal, _primaryColor, _selectColor),
new DrawerDivider(),
buildDrawerRadioItem("Dismiss horizontally", DismissDirection.horizontal, _dismissDirection, _changeDismissDirection, icon: 'action/code'),
buildDrawerRadioItem("Dismiss left", DismissDirection.left, _dismissDirection, _changeDismissDirection, icon: 'navigation/arrow_back'),
buildDrawerRadioItem("Dismiss right", DismissDirection.right, _dismissDirection, _changeDismissDirection, icon: 'navigation/arrow_forward'),
])
)
);
......@@ -158,6 +160,12 @@ class CardCollectionState extends State<CardCollection> {
});
}
void _selectColor(selection) {
setState(() {
_primaryColor = selection;
});
}
void _changeDismissDirection(DismissDirection newDismissDirection) {
setState(() {
_dismissDirection = newDismissDirection;
......@@ -175,16 +183,16 @@ class CardCollectionState extends State<CardCollection> {
);
}
Widget buildDrawerRadioItem(DismissDirection direction, String icon) {
Widget buildDrawerRadioItem(String label, itemValue, currentValue, RadioValueChanged onChanged, { String icon }) {
return new DrawerItem(
icon: icon,
onPressed: () { _changeDismissDirection(direction); },
onPressed: () { onChanged(itemValue); },
child: new Row([
new Flexible(child: new Text(_dismissDirectionText(direction))),
new Flexible(child: new Text(label)),
new Radio(
value: direction,
onChanged: _changeDismissDirection,
groupValue: _dismissDirection
value: itemValue,
groupValue: currentValue,
onChanged: onChanged
)
])
);
......@@ -210,11 +218,22 @@ class CardCollectionState extends State<CardCollection> {
onResized: () { _invalidator([index]); },
onDismissed: () { dismissCard(cardModel); },
child: new Card(
color: cardModel.color,
color: Theme.of(context).primarySwatch[cardModel.color],
child: new Container(
height: cardModel.height,
padding: const EdgeDims.all(8.0),
child: new Center(child: new Text(cardModel.label, style: cardLabelStyle))
child: new Center(
child: new DefaultTextStyle(
style: cardLabelStyle,
child: new Input(
key: new GlobalObjectKey(cardModel),
initialValue: cardModel.label,
onChanged: (String value) {
cardModel.label = value;
}
)
)
)
)
)
);
......@@ -341,9 +360,14 @@ class CardCollectionState extends State<CardCollection> {
body = new Stack([body, indicator]);
}
return new Scaffold(
return new Theme(
data: new ThemeData(
primarySwatch: _primaryColor
),
child: new Scaffold(
toolBar: buildToolBar(),
body: body
)
);
}
}
......@@ -351,11 +375,6 @@ class CardCollectionState extends State<CardCollection> {
void main() {
runApp(new App(
title: 'Cards',
theme: new ThemeData(
brightness: ThemeBrightness.light,
primarySwatch: Colors.blue,
accentColor: Colors.redAccent[200]
),
routes: {
'/': (RouteArguments args) => new CardCollection(navigator: args.navigator),
}
......
......@@ -79,4 +79,38 @@ class ThemeData {
Color get accentColor => _accentColor;
final ThemeBrightness accentColorBrightness;
bool operator==(Object other) {
if (other.runtimeType != runtimeType)
return false;
ThemeData otherData = other;
return (otherData.brightness == brightness) &&
(otherData.primarySwatch == primarySwatch) &&
(otherData.canvasColor == canvasColor) &&
(otherData.cardColor == cardColor) &&
(otherData.dividerColor == dividerColor) &&
(otherData.hintColor == hintColor) &&
(otherData.highlightColor == highlightColor) &&
(otherData.selectedColor == selectedColor) &&
(otherData.hintOpacity == hintOpacity) &&
(otherData.text == text) &&
(otherData.primaryColorBrightness == primaryColorBrightness) &&
(otherData.accentColorBrightness == accentColorBrightness);
}
int get hashCode {
int value = 373;
value = 37 * value + brightness.hashCode;
value = 37 * value + primarySwatch.hashCode;
value = 37 * value + canvasColor.hashCode;
value = 37 * value + cardColor.hashCode;
value = 37 * value + dividerColor.hashCode;
value = 37 * value + hintColor.hashCode;
value = 37 * value + highlightColor.hashCode;
value = 37 * value + selectedColor.hashCode;
value = 37 * value + hintOpacity.hashCode;
value = 37 * value + text.hashCode;
value = 37 * value + primaryColorBrightness.hashCode;
value = 37 * value + accentColorBrightness.hashCode;
return value;
}
}
......@@ -37,8 +37,8 @@ class HomogeneousViewport extends RenderObjectWidget {
RenderBlockViewport createRenderObject() => new RenderBlockViewport();
bool isLayoutDifferentThan(HomogeneousViewport oldWidget) {
// changing the builder doesn't imply the layout changed
return itemsWrap != oldWidget.itemsWrap ||
itemsWrap != oldWidget.itemsWrap ||
itemExtent != oldWidget.itemExtent ||
itemCount != oldWidget.itemCount ||
direction != oldWidget.direction ||
......@@ -89,6 +89,10 @@ class _HomogeneousViewportElement extends RenderObjectElement<HomogeneousViewpor
_updateChildren();
}
void reinvokeBuilders() {
_updateChildren();
}
void layout(BoxConstraints constraints) {
// We enter a build scope (meaning that markNeedsBuild() is forbidden)
// because we are in the middle of layout and if we allowed people to set
......
......@@ -164,6 +164,11 @@ class _MixedViewportElement extends RenderObjectElement<MixedViewport> {
if (changes != _ChangeDescription.none || !isValid) {
renderObject.markNeedsLayout();
} else {
reinvokeBuilders();
}
}
void reinvokeBuilders() {
// we just need to redraw our existing widgets as-is
if (_childrenByKey.length > 0) {
assert(_firstVisibleChildIndex >= 0);
......@@ -171,7 +176,7 @@ class _MixedViewportElement extends RenderObjectElement<MixedViewport> {
final int startIndex = _firstVisibleChildIndex;
int lastIndex = startIndex + _childrenByKey.length - 1;
Element nextSibling = null;
for (int index = lastIndex; index > startIndex; index -= 1) {
for (int index = lastIndex; index >= startIndex; index -= 1) {
final Widget newWidget = _buildWidgetAt(index);
final _ChildKey key = new _ChildKey.fromWidget(newWidget);
final Element oldElement = _childrenByKey[key];
......@@ -187,7 +192,6 @@ class _MixedViewportElement extends RenderObjectElement<MixedViewport> {
}
}
}
}
void layout(BoxConstraints constraints) {
if (constraints != _lastLayoutConstraints) {
......
......@@ -18,7 +18,6 @@ class RouteArguments {
typedef Widget RouteBuilder(RouteArguments args);
typedef RouteBuilder RouteGenerator(String name);
typedef void StateRouteCallback(StateRoute route);
typedef void _RouteCallback(Route route);
class Navigator extends StatefulComponent {
Navigator({
......
......@@ -12,7 +12,7 @@ import 'package:sky/src/widgets/theme.dart';
const sky.Color _kLightOffColor = const sky.Color(0x8A000000);
const sky.Color _kDarkOffColor = const sky.Color(0xB2FFFFFF);
typedef RadioValueChanged(Object value);
typedef void RadioValueChanged(Object value);
class Radio extends StatelessComponent {
Radio({
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment