Commit ac6342ab authored by Hixie's avatar Hixie

Use the navigator to stack dialogs.

This removes the need to manually include the dialog builder in the main window's build() function.
It also removes the need to track if a dialog is visible.

Other changes:
- I made dialog.dart a bit more readable.
- I renamed transitionFinished to fullyOpaque since that's what actually matters.
- I made Routes track if they're opaque. Eventually this should probably be more configurable when creating the route.

Directions for Future Research:
- Use this for focus management somehow.
- The popup menu should use something like this.
- We should factor the following out into a showDialog() function that returns a future for the dialog's exit result:
    navigator.push(new DialogRoute(builder: (navigator, route) { ... }));
- Maybe navigator.pop() should take a value to return to that Future.
parent c6fe01ed
......@@ -68,10 +68,30 @@ class AddressBookApp extends App {
child: new Icon(type: 'image/photo_camera', size: 24),
backgroundColor: Theme.of(this).accentColor,
onPressed: () {
showDialog = true;
navigator.pushState(this, (_) {
showDialog = false;
});
navigator.push(new DialogRoute(builder: (navigator, route) {
return new Dialog(
title: new Text("Describe your picture"),
content: new ScrollableBlock([
new Field(inputKey: fillKey, icon: "editor/format_color_fill", placeholder: "Color"),
new Field(inputKey: emoticonKey, icon: "editor/insert_emoticon", placeholder: "Emotion"),
]),
onDismiss: navigator.pop,
actions: [
new FlatButton(
child: new Text('DISCARD'),
onPressed: () {
navigator.pop();
}
),
new FlatButton(
child: new Text('SAVE'),
onPressed: () {
navigator.pop();
}
),
]
);
}));
}
);
}
......@@ -104,47 +124,15 @@ class AddressBookApp extends App {
);
}
bool showDialog = false;
Widget buildMain(Navigator navigator) {
List<Widget> layers = [
new Focus(
return new Focus(
initialFocus: nameKey,
child: new Scaffold(
toolbar: buildToolBar(navigator),
body: buildBody(navigator),
floatingActionButton: buildFloatingActionButton(navigator)
)
)
];
if (showDialog) {
layers.add(new Focus(
initialFocus: fillKey,
child: new Dialog(
title: new Text("Describe your picture"),
content: new ScrollableBlock([
new Field(inputKey: fillKey, icon: "editor/format_color_fill", placeholder: "Color"),
new Field(inputKey: emoticonKey, icon: "editor/insert_emoticon", placeholder: "Emotion"),
]),
onDismiss: navigator.pop,
actions: [
new FlatButton(
child: new Text('DISCARD'),
onPressed: () {
navigator.pop();
}
),
new FlatButton(
child: new Text('SAVE'),
onPressed: () {
navigator.pop();
}
),
]
)
));
}
return new Stack(layers);
);
}
NavigationState _navigationState;
......
......@@ -18,8 +18,6 @@ class StockSettings extends StatefulComponent {
BackupMode backup;
SettingsUpdater updater;
bool _showModeDialog = false;
void syncFields(StockSettings source) {
navigator = source.navigator;
optimism = source.optimism;
......@@ -47,10 +45,26 @@ class StockSettings extends StatefulComponent {
_handleOptimismChanged(false);
break;
case StockMode.pessimistic:
_showModeDialog = true;
navigator.pushState(this, (_) {
_showModeDialog = false;
});
navigator.push(new DialogRoute(builder: (navigator, route) {
return new Dialog(
title: new Text("Change mode?"),
content: new Text("Optimistic mode means everything is awesome. Are you sure you can handle that?"),
onDismiss: navigator.pop,
actions: [
new FlatButton(
child: new Text('NO THANKS'),
onPressed: navigator.pop
),
new FlatButton(
child: new Text('AGREE'),
onPressed: () {
_handleOptimismChanged(true);
navigator.pop();
}
),
]
);
}));
break;
}
}
......@@ -104,32 +118,9 @@ class StockSettings extends StatefulComponent {
}
Widget build() {
List<Widget> layers = [
new Scaffold(
return new Scaffold(
toolbar: buildToolBar(),
body: buildSettingsPane()
)
];
if (_showModeDialog) {
layers.add(new Dialog(
title: new Text("Change mode?"),
content: new Text("Optimistic mode means everything is awesome. Are you sure you can handle that?"),
onDismiss: navigator.pop,
actions: [
new FlatButton(
child: new Text('NO THANKS'),
onPressed: navigator.pop
),
new FlatButton(
child: new Text('AGREE'),
onPressed: () {
_handleOptimismChanged(true);
navigator.pop();
}
),
]
));
}
return new Stack(layers);
);
}
}
......@@ -46,14 +46,11 @@ class Dialog extends Component {
}
Widget build() {
Container mask = new Container(
decoration: const BoxDecoration(
backgroundColor: const Color(0x7F000000)));
List<Widget> children = new List<Widget>();
List<Widget> dialogBody = new List<Widget>();
if (title != null) {
children.add(new Padding(
dialogBody.add(new Padding(
padding: new EdgeDims(24.0, 24.0, content == null ? 20.0 : 0.0, 24.0),
child: new DefaultTextStyle(
style: Theme.of(this).text.title,
......@@ -63,7 +60,7 @@ class Dialog extends Component {
}
if (content != null) {
children.add(new Padding(
dialogBody.add(new Padding(
padding: const EdgeDims(20.0, 24.0, 24.0, 24.0),
child: new DefaultTextStyle(
style: Theme.of(this).text.subhead,
......@@ -73,11 +70,15 @@ class Dialog extends Component {
}
if (actions != null)
children.add(new Flex(actions, justifyContent: FlexJustifyContent.end));
dialogBody.add(new Flex(actions, justifyContent: FlexJustifyContent.end));
return new Stack([
new Listener(
child: mask,
child: new Container(
decoration: const BoxDecoration(
backgroundColor: const Color(0x7F000000)
)
),
onGestureTap: (_) => onDismiss()
),
new Center(
......@@ -89,12 +90,13 @@ class Dialog extends Component {
level: 4,
color: _color,
child: new ShrinkWrapWidth(
child: new ScrollableBlock(children)
child: new ScrollableBlock(dialogBody)
)
)
)
)
)
]);
}
}
......@@ -9,22 +9,40 @@ import 'package:sky/widgets/animated_component.dart';
import 'package:sky/widgets/basic.dart';
import 'package:vector_math/vector_math.dart';
typedef Widget Builder(Navigator navigator, RouteBase route);
typedef Widget RouteBuilder(Navigator navigator, RouteBase route);
abstract class RouteBase {
Widget build(Navigator navigator, RouteBase route);
bool get isOpaque;
void popState() { }
}
class Route extends RouteBase {
Route({ this.name, this.builder });
final String name;
final Builder builder;
final RouteBuilder builder;
Widget build(Navigator navigator, RouteBase route) => builder(navigator, route);
bool get isOpaque => true;
}
class RouteState extends RouteBase {
class DialogRoute extends RouteBase {
DialogRoute({ this.builder, this.callback });
final RouteBuilder builder;
Function callback;
Widget build(Navigator navigator, RouteBase route) => builder(navigator, route);
bool get isOpaque => false;
void popState() {
if (callback != null)
callback(this);
}
}
class RouteState extends RouteBase {
RouteState({ this.callback, this.route, this.owner });
Function callback;
......@@ -32,6 +50,7 @@ class RouteState extends RouteBase {
StatefulComponent owner;
Widget build(Navigator navigator, RouteBase route) => null;
bool get isOpaque => false;
void popState() {
if (callback != null)
......@@ -52,7 +71,7 @@ class Transition extends AnimatedComponent {
this.onDismissed,
this.onCompleted,
this.interactive
}) : super(key: key);
}): super(key: key);
Widget content;
TransitionDirection direction;
bool interactive;
......@@ -145,7 +164,7 @@ class Transition extends AnimatedComponent {
class HistoryEntry {
HistoryEntry({ this.route });
final RouteBase route;
bool transitionFinished = false;
bool fullyOpaque = false;
// TODO(jackson): Keep track of the requested transition
}
......@@ -182,7 +201,7 @@ class NavigationState {
if (historyIndex > 0) {
HistoryEntry entry = history[historyIndex];
entry.route.popState();
entry.transitionFinished = false;
entry.fullyOpaque = false;
historyIndex--;
}
}
......@@ -231,7 +250,7 @@ class Navigator extends StatefulComponent {
List<Widget> visibleRoutes = new List<Widget>();
for (int i = 0; i < state.history.length; i++) {
// Avoid building routes that are not visible
if (i + 1 < state.history.length && state.history[i + 1].transitionFinished)
if (i + 1 < state.history.length && state.history[i + 1].fullyOpaque)
continue;
HistoryEntry historyEntry = state.history[i];
Widget content = historyEntry.route.build(this, historyEntry.route);
......@@ -253,7 +272,7 @@ class Navigator extends StatefulComponent {
},
onCompleted: () {
setState(() {
historyEntry.transitionFinished = true;
historyEntry.fullyOpaque = historyEntry.route.isOpaque;
});
}
);
......
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