Commit 43a5dff8 authored by Kris Giesing's avatar Kris Giesing

Merge remote-tracking branch 'upstream/master'

parents c9986651 48142d68
......@@ -11,7 +11,7 @@ class Field extends StatelessComponent {
this.inputKey,
this.icon,
this.placeholder
}): super(key: key);
}) : super(key: key);
final GlobalKey inputKey;
final String icon;
......@@ -101,7 +101,7 @@ void main() {
title: 'Address Book',
theme: theme,
routes: <String, RouteBuilder>{
'/': (NavigatorState navigator, Route route) => new AddressBookHome(navigator: navigator)
'/': (RouteArguments args) => new AddressBookHome(navigator: args.navigator)
}
));
}
......@@ -42,8 +42,8 @@ void launch(String relativeUrl, String bundle) {
activity.startActivity(intent);
}
class SkyDemo {
SkyDemo({
class FlutterDemo {
FlutterDemo({
name,
this.href,
this.bundle,
......@@ -60,8 +60,8 @@ class SkyDemo {
final BoxDecoration decoration;
}
List<SkyDemo> demos = [
new SkyDemo(
List<FlutterDemo> demos = [
new FlutterDemo(
name: 'Stocks',
href: '../../stocks/lib/main.dart',
bundle: 'stocks.skyx',
......@@ -74,7 +74,7 @@ List<SkyDemo> demos = [
)
)
),
new SkyDemo(
new FlutterDemo(
name: 'Asteroids',
href: '../../game/lib/main.dart',
bundle: 'game.skyx',
......@@ -87,7 +87,7 @@ List<SkyDemo> demos = [
)
)
),
new SkyDemo(
new FlutterDemo(
name: 'Fitness',
href: '../../fitness/lib/main.dart',
bundle: 'fitness.skyx',
......@@ -97,7 +97,7 @@ List<SkyDemo> demos = [
backgroundColor: Colors.indigo[500]
)
),
new SkyDemo(
new FlutterDemo(
name: 'Swipe Away',
href: '../../widgets/card_collection.dart',
bundle: 'cards.skyx',
......@@ -107,7 +107,7 @@ List<SkyDemo> demos = [
backgroundColor: Colors.redAccent[200]
)
),
new SkyDemo(
new FlutterDemo(
name: 'Interactive Text',
href: '../../rendering/interactive_flex.dart',
bundle: 'interactive_flex.skyx',
......@@ -120,7 +120,7 @@ List<SkyDemo> demos = [
// new SkyDemo(
// 'Touch Demo', '../../rendering/touch_demo.dart', 'Simple example showing handling of touch events at a low level'),
new SkyDemo(
new FlutterDemo(
name: 'Minedigger Game',
href: '../../mine_digger/lib/main.dart',
bundle: 'mine_digger.skyx',
......@@ -138,44 +138,47 @@ List<SkyDemo> demos = [
const double kCardHeight = 120.0;
const EdgeDims kListPadding = const EdgeDims.all(4.0);
class DemoList extends StatelessComponent {
Widget buildCardContents(SkyDemo demo) {
return new Container(
decoration: demo.decoration,
child: new InkWell(
child: new Container(
margin: const EdgeDims.only(top: 24.0, left: 24.0),
child: new Column([
new Text(demo.name, style: demo.textTheme.title),
new Flexible(
child: new Text(demo.description, style: demo.textTheme.subhead)
)
],
alignItems: FlexAlignItems.start
class DemoCard extends StatelessComponent {
DemoCard({ Key key, this.demo }) : super(key: key);
final FlutterDemo demo;
Widget build(BuildContext context) {
return new Container(
height: kCardHeight,
child: new Card(
child: new Container(
decoration: demo.decoration,
child: new InkWell(
onTap: () => launch(demo.href, demo.bundle),
child: new Container(
margin: const EdgeDims.only(top: 24.0, left: 24.0),
child: new Column([
new Text(demo.name, style: demo.textTheme.title),
new Flexible(
child: new Text(demo.description, style: demo.textTheme.subhead)
)
],
alignItems: FlexAlignItems.start
)
)
)
)
)
);
}
}
Widget buildDemo(BuildContext context, SkyDemo demo) {
return new GestureDetector(
key: demo.key,
onTap: () => launch(demo.href, demo.bundle),
child: new Container(
height: kCardHeight,
child: new Card(
child: buildCardContents(demo)
)
)
);
class DemoList extends StatelessComponent {
Widget _buildDemoCard(BuildContext context, FlutterDemo demo) {
return new DemoCard(key: demo.key, demo: demo);
}
Widget build(BuildContext context) {
return new ScrollableList<SkyDemo>(
return new ScrollableList<FlutterDemo>(
items: demos,
itemExtent: kCardHeight,
itemBuilder: buildDemo,
itemBuilder: _buildDemoCard,
padding: kListPadding
);
}
......@@ -200,10 +203,10 @@ class DemoHome extends StatelessComponent {
void main() {
runApp(new App(
title: 'Sky Demos',
title: 'Flutter Demos',
theme: _theme,
routes: {
'/': (NavigatorState navigator, Route route) => new DemoHome()
'/': (RouteArguments args) => new DemoHome()
}
));
}
......@@ -33,15 +33,13 @@ class DialogMenuItem extends StatelessComponent {
Function onPressed;
Widget build(BuildContext context) {
return new GestureDetector(
onTap: onPressed,
child: new Container(
height: 48.0,
child: new InkWell(
child: new Padding(
padding: const EdgeDims.symmetric(horizontal: 16.0),
child: new Row(children)
)
return new Container(
height: 48.0,
child: new InkWell(
onTap: onPressed,
child: new Padding(
padding: const EdgeDims.symmetric(horizontal: 16.0),
child: new Row(children)
)
)
);
......@@ -62,25 +60,20 @@ class FeedFragment extends StatefulComponent {
class FeedFragmentState extends State<FeedFragment> {
FitnessMode _fitnessMode = FitnessMode.feed;
AnimationStatus _snackBarStatus = AnimationStatus.dismissed;
PerformanceStatus _snackBarStatus = PerformanceStatus.dismissed;
bool _isShowingSnackBar = false;
void _handleFitnessModeChange(FitnessMode value) {
setState(() {
_fitnessMode = value;
_drawerShowing = false;
});
config.navigator.pop();
}
Drawer buildDrawer() {
if (_drawerStatus == AnimationStatus.dismissed)
return null;
return new Drawer(
showing: _drawerShowing,
level: 3,
onDismissed: _handleDrawerDismissed,
void _showDrawer() {
showDrawer(
navigator: config.navigator,
children: [
child: new Block([
new DrawerHeader(child: new Text('Fitness')),
new DrawerItem(
icon: 'action/view_list',
......@@ -100,26 +93,10 @@ class FeedFragmentState extends State<FeedFragment> {
new DrawerItem(
icon: 'action/help',
child: new Text('Help & Feedback'))
]
])
);
}
bool _drawerShowing = false;
AnimationStatus _drawerStatus = AnimationStatus.dismissed;
void _handleOpenDrawer() {
setState(() {
_drawerShowing = true;
_drawerStatus = AnimationStatus.forward;
});
}
void _handleDrawerDismissed() {
setState(() {
_drawerStatus = AnimationStatus.dismissed;
});
}
void _handleShowSettings() {
config.navigator.pop();
config.navigator.pushNamed('/settings');
......@@ -137,7 +114,7 @@ class FeedFragmentState extends State<FeedFragment> {
return new ToolBar(
left: new IconButton(
icon: "navigation/menu",
onPressed: _handleOpenDrawer),
onPressed: _showDrawer),
center: new Text(fitnessModeTitle)
);
}
......@@ -149,7 +126,7 @@ class FeedFragmentState extends State<FeedFragment> {
setState(() {
_undoItem = item;
_isShowingSnackBar = true;
_snackBarStatus = AnimationStatus.forward;
_snackBarStatus = PerformanceStatus.forward;
});
}
......@@ -230,13 +207,13 @@ class FeedFragmentState extends State<FeedFragment> {
}
Widget buildSnackBar() {
if (_snackBarStatus == AnimationStatus.dismissed)
if (_snackBarStatus == PerformanceStatus.dismissed)
return null;
return new SnackBar(
showing: _isShowingSnackBar,
content: new Text("Item deleted."),
actions: [new SnackBarAction(label: "UNDO", onPressed: _handleUndo)],
onDismissed: () { setState(() { _snackBarStatus = AnimationStatus.dismissed; }); }
onDismissed: () { setState(() { _snackBarStatus = PerformanceStatus.dismissed; }); }
);
}
......@@ -264,8 +241,7 @@ class FeedFragmentState extends State<FeedFragment> {
toolbar: buildToolBar(),
body: buildBody(),
snackBar: buildSnackBar(),
floatingActionButton: buildFloatingActionButton(),
drawer: buildDrawer()
floatingActionButton: buildFloatingActionButton()
);
}
}
......
......@@ -92,8 +92,6 @@ class FitnessApp extends StatefulComponent {
class FitnessAppState extends State<FitnessApp> {
UserDataImpl _userData;
Map<String, RouteBuilder> _routes;
void initState() {
super.initState();
loadFitnessData().then((UserData data) {
......@@ -102,36 +100,6 @@ class FitnessAppState extends State<FitnessApp> {
print("Failed to load data: $e");
setState(() => _userData = new UserDataImpl());
});
_routes = {
'/': (NavigatorState navigator, Route route) {
return new FeedFragment(
navigator: navigator,
userData: _userData,
onItemCreated: _handleItemCreated,
onItemDeleted: _handleItemDeleted
);
},
'/meals/new': (navigator, route) {
return new MealFragment(
navigator: navigator,
onCreated: _handleItemCreated
);
},
'/measurements/new': (NavigatorState navigator, Route route) {
return new MeasurementFragment(
navigator: navigator,
onCreated: _handleItemCreated
);
},
'/settings': (navigator, route) {
return new SettingsFragment(
navigator: navigator,
userData: _userData,
updater: settingsUpdater
);
}
};
}
void _handleItemCreated(FitnessItem item) {
......@@ -158,17 +126,43 @@ class FitnessAppState extends State<FitnessApp> {
});
}
final ThemeData _theme = new ThemeData(
brightness: ThemeBrightness.light,
primarySwatch: Colors.indigo,
accentColor: Colors.pinkAccent[200]
);
Widget build(BuildContext) {
return new App(
theme: _theme,
theme: new ThemeData(
brightness: ThemeBrightness.light,
primarySwatch: Colors.indigo,
accentColor: Colors.pinkAccent[200]
),
title: 'Fitness',
routes: _routes
routes: {
'/': (RouteArguments args) {
return new FeedFragment(
navigator: args.navigator,
userData: _userData,
onItemCreated: _handleItemCreated,
onItemDeleted: _handleItemDeleted
);
},
'/meals/new': (RouteArguments args) {
return new MealFragment(
navigator: args.navigator,
onCreated: _handleItemCreated
);
},
'/measurements/new': (RouteArguments args) {
return new MeasurementFragment(
navigator: args.navigator,
onCreated: _handleItemCreated
);
},
'/settings': (RouteArguments args) {
return new SettingsFragment(
navigator: args.navigator,
userData: _userData,
updater: settingsUpdater
);
}
}
);
}
}
......
......@@ -65,12 +65,13 @@ class MealFragmentState extends State<MealFragment> {
icon: "navigation/close",
onPressed: config.navigator.pop),
center: new Text('New Meal'),
right: [new InkWell(
child: new GestureDetector(
right: [
// TODO(abarth): Should this be a FlatButton?
new InkWell(
onTap: _handleSave,
child: new Text('SAVE')
)
)]
]
);
}
......
......@@ -136,12 +136,13 @@ class MeasurementFragmentState extends State<MeasurementFragment> {
icon: "navigation/close",
onPressed: config.navigator.pop),
center: new Text('New Measurement'),
right: [new InkWell(
child: new GestureDetector(
right: [
// TODO(abarth): Should this be a FlatButton?
new InkWell(
onTap: _handleSave,
child: new Text('SAVE')
)
)]
]
);
}
......
......@@ -62,7 +62,7 @@ class TestAppState extends State<TestApp> {
);
}
Column _buildColumn(NavigatorState navigator, Route route) {
Column _buildColumn(RouteArguments args) {
return new Column([
new Flexible(child: _buildSpriteWidget()),
_buildTabBar()
......
......@@ -92,11 +92,11 @@ class GameDemoState extends State<GameDemo> {
);
}
Widget _buildGameScene(NavigatorState navigator, Route route) {
Widget _buildGameScene(RouteArguments args) {
return new SpriteWidget(_game, SpriteBoxTransformMode.fixedWidth);
}
Widget _buildMainScene(navigator, route) {
Widget _buildMainScene(RouteArguments args) {
return new Stack([
new SpriteWidget(new MainScreenBackground(), SpriteBoxTransformMode.fixedWidth),
new Column([
......@@ -109,10 +109,10 @@ class GameDemoState extends State<GameDemo> {
_sounds,
(lastScore) {
setState(() {_lastScore = lastScore;});
navigator.pop();
args.navigator.pop();
}
);
navigator.pushNamed('/game');
args.navigator.pushNamed('/game');
},
texture: _spriteSheetUI['btn_play_up.png'],
textureDown: _spriteSheetUI['btn_play_down.png'],
......
......@@ -3,6 +3,7 @@ dependencies:
sky: any
sky_tools: any
skysprites: any
box2d: any
dependency_overrides:
material_design_icons:
path: ../../sky/packages/material_design_icons
......
import 'dart:sky';
import 'package:sky/material.dart';
import 'package:sky/rendering.dart';
import 'package:sky/services.dart';
import 'package:sky/widgets.dart';
import 'package:skysprites/skysprites.dart';
AssetBundle _initBundle() {
if (rootBundle != null)
return rootBundle;
return new NetworkAssetBundle(Uri.base);
}
final AssetBundle _bundle = _initBundle();
ImageMap _images;
SpriteSheet _spriteSheet;
TestBedApp _app;
main() async {
_images = new ImageMap(_bundle);
await _images.load([
'assets/sprites.png'
]);
String json = await _bundle.loadString('assets/sprites.json');
_spriteSheet = new SpriteSheet(_images['assets/sprites.png'], json);
_app = new TestBedApp();
runApp(_app);
}
class TestBedApp extends App {
Widget build() {
ThemeData theme = new ThemeData(
brightness: ThemeBrightness.light,
primarySwatch: Colors.purple
);
return new Theme(
data: theme,
child: new Title(
title: 'Test Bed',
child: new SpriteWidget(
new TestBed(),
SpriteBoxTransformMode.letterbox
)
)
);
}
}
class TestBed extends NodeWithSize {
TestBed() : super(new Size(1024.0, 1024.0)) {
}
}
......@@ -36,7 +36,7 @@ main() async {
title: 'Test drawAtlas',
theme: _theme,
routes: {
'/': (NavigatorState navigator, Route route) {
'/': (RouteArguments args) {
return new SpriteWidget(
new TestDrawAtlas(),
SpriteBoxTransformMode.fixedWidth
......
import 'dart:sky';
import 'package:sky/material.dart';
import 'package:sky/rendering.dart';
import 'package:sky/services.dart';
import 'package:sky/widgets.dart';
import 'package:skysprites/skysprites.dart';
AssetBundle _initBundle() {
if (rootBundle != null)
return rootBundle;
return new NetworkAssetBundle(Uri.base);
}
final AssetBundle _bundle = _initBundle();
ImageMap _images;
SpriteSheet _spriteSheet;
main() async {
_images = new ImageMap(_bundle);
await _images.load([
'assets/sprites.png'
]);
String json = await _bundle.loadString('assets/sprites.json');
_spriteSheet = new SpriteSheet(_images['assets/sprites.png'], json);
runApp(new App(
title: 'Test Physics',
theme: new ThemeData(
brightness: ThemeBrightness.light,
primarySwatch: Colors.purple
),
routes: {
'/': (RouteArguments args) {
return new SpriteWidget(
new TestBed(),
SpriteBoxTransformMode.letterbox
);
}
}
));
}
class TestBed extends NodeWithSize {
Sprite _ship;
Sprite _obstacle;
TestBed() : super(new Size(1024.0, 1024.0)) {
PhysicsNode physicsNode = new PhysicsNode(new Offset(0.0, 100.0));
_ship = new Sprite(_spriteSheet["ship.png"]);
_ship.position = new Point(512.0, 512.0);
_ship.size = new Size(64.0, 64.0);
_ship.physicsBody = new PhysicsBody(
new PhysicsShapeGroup([
new PhysicsShapeCircle(Point.origin, 32.0),
new PhysicsShapePolygon([new Point(0.0, 0.0), new Point(50.0, 0.0), new Point(50.0, 50.0), new Point(0.0, 50.0)])
]),
friction: 0.5,
tag: "ship"
);
physicsNode.addChild(_ship);
_obstacle = new Sprite(_spriteSheet["ship.png"]);
_obstacle.position = new Point(532.0, 800.0);
_obstacle.size = new Size(64.0, 64.0);
_obstacle.physicsBody = new PhysicsBody(
new PhysicsShapeCircle(Point.origin, 32.0),
type: PhysicsBodyType.static,
friction: 0.5,
tag: "obstacle"
);
physicsNode.addChild(_obstacle);
physicsNode.addContactCallback(myCallback, "obstacle", "ship", PhysicsContactType.begin);
addChild(physicsNode);
userInteractionEnabled = true;
}
void myCallback(PhysicsContactType type, PhysicsContact contact) {
print("CONTACT type: $type");
contact.nodeB.removeFromParent();
}
bool handleEvent(SpriteBoxEvent event) {
if (event.type == "pointerdown") {
Point pos = convertPointToNodeSpace(event.boxPosition);
_ship.position = pos;
}
return true;
}
}
......@@ -64,33 +64,25 @@ class MineDiggerState extends State<MineDigger> {
alive = true;
hasWon = false;
detectedCount = 0;
// Build the arrays.
cells = new List<List<bool>>();
uiState = new List<List<CellState>>();
for (int iy = 0; iy != rows; iy++) {
cells.add(new List<bool>());
uiState.add(new List<CellState>());
for (int ix = 0; ix != cols; ix++) {
cells[iy].add(false);
uiState[iy].add(CellState.covered);
}
}
// Initialize matrices.
cells = new List<List>.generate(rows, (int row) {
return new List<bool>.filled(cols, false);
});
uiState = new List<List>.generate(rows, (int row) {
return new List<CellState>.filled(cols, CellState.covered);
});
// Place the mines.
Random random = new Random();
int cellsRemaining = rows * cols;
int minesRemaining = totalMineCount;
for (int x = 0; x < cols; x += 1) {
for (int y = 0; y < rows; y += 1) {
if (random.nextInt(cellsRemaining) < minesRemaining) {
cells[y][x] = true;
minesRemaining -= 1;
if (minesRemaining <= 0)
return;
}
cellsRemaining -= 1;
while (minesRemaining > 0) {
int pos = random.nextInt(rows * cols);
int row = pos ~/ rows;
int col = pos % cols;
if (!cells[row][col]) {
cells[row][col] = true;
minesRemaining--;
}
}
assert(false);
}
PointerEventListener _pointerDownHandlerFor(int posX, int posY) {
......@@ -106,9 +98,9 @@ class MineDiggerState extends State<MineDigger> {
Widget buildBoard() {
bool hasCoveredCell = false;
List<Row> flexRows = <Row>[];
for (int iy = 0; iy != 9; iy++) {
for (int iy = 0; iy < rows; iy++) {
List<Widget> row = <Widget>[];
for (int ix = 0; ix != 9; ix++) {
for (int ix = 0; ix < cols; ix++) {
CellState state = uiState[iy][ix];
int count = mineCount(ix, iy);
if (!alive) {
......
......@@ -9,7 +9,7 @@ import 'package:sky/rendering.dart';
import 'solid_color_box.dart';
double timeBase;
Duration timeBase;
RenderTransform transformBox;
void main() {
......@@ -34,10 +34,10 @@ void main() {
scheduler.addPersistentFrameCallback(rotate);
}
void rotate(double timeStamp) {
void rotate(Duration timeStamp) {
if (timeBase == null)
timeBase = timeStamp;
double delta = (timeStamp - timeBase) / 1000; // radians
double delta = (timeStamp - timeBase).inMicroseconds.toDouble() / Duration.MICROSECONDS_PER_SECOND; // radians
transformBox.setIdentity();
transformBox.translate(transformBox.size.width / 2.0, transformBox.size.height / 2.0);
......
......@@ -82,7 +82,7 @@ class StocksAppState extends State<StocksApp> {
if (path.length != 3)
return null;
if (_stocks.containsKey(path[2]))
return (navigator, route) => new StockSymbolViewer(navigator, _stocks[path[2]]);
return (RouteArguments args) => new StockSymbolViewer(args.navigator, _stocks[path[2]]);
return null;
}
return null;
......@@ -93,8 +93,8 @@ class StocksAppState extends State<StocksApp> {
title: 'Stocks',
theme: theme,
routes: <String, RouteBuilder>{
'/': (navigator, route) => new StockHome(navigator, _stocks, _symbols, _optimismSetting, modeUpdater),
'/settings': (navigator, route) => new StockSettings(navigator, _optimismSetting, _backupSetting, settingsUpdater)
'/': (RouteArguments args) => new StockHome(args.navigator, _stocks, _symbols, _optimismSetting, modeUpdater),
'/settings': (RouteArguments args) => new StockSettings(args.navigator, _optimismSetting, _backupSetting, settingsUpdater)
},
onGenerateRoute: _getRoute
);
......
......@@ -25,7 +25,7 @@ class StockHomeState extends State<StockHome> {
bool _isSearching = false;
String _searchQuery;
AnimationStatus _snackBarStatus = AnimationStatus.dismissed;
PerformanceStatus _snackBarStatus = PerformanceStatus.dismissed;
bool _isSnackBarShowing = false;
void _handleSearchBegin() {
......@@ -56,22 +56,6 @@ class StockHomeState extends State<StockHome> {
});
}
bool _drawerShowing = false;
AnimationStatus _drawerStatus = AnimationStatus.dismissed;
void _handleOpenDrawer() {
setState(() {
_drawerShowing = true;
_drawerStatus = AnimationStatus.forward;
});
}
void _handleDrawerDismissed() {
setState(() {
_drawerStatus = AnimationStatus.dismissed;
});
}
bool _autorefresh = false;
void _handleAutorefreshChanged(bool value) {
setState(() {
......@@ -91,16 +75,10 @@ class StockHomeState extends State<StockHome> {
);
}
Drawer buildDrawer() {
if (_drawerStatus == AnimationStatus.dismissed)
return null;
assert(_drawerShowing); // TODO(mpcomplete): this is always true
return new Drawer(
level: 3,
showing: _drawerShowing,
onDismissed: _handleDrawerDismissed,
void _showDrawer() {
showDrawer(
navigator: config.navigator,
children: [
child: new Block([
new DrawerHeader(child: new Text('Stocks')),
new DrawerItem(
icon: 'action/assessment',
......@@ -141,7 +119,7 @@ class StockHomeState extends State<StockHome> {
new DrawerItem(
icon: 'action/help',
child: new Text('Help & Feedback'))
]
])
);
}
......@@ -154,7 +132,7 @@ class StockHomeState extends State<StockHome> {
return new ToolBar(
left: new IconButton(
icon: "navigation/menu",
onPressed: _handleOpenDrawer
onPressed: _showDrawer
),
center: new Text('Stocks'),
right: [
......@@ -246,20 +224,20 @@ class StockHomeState extends State<StockHome> {
GlobalKey snackBarKey = new GlobalKey(label: 'snackbar');
Widget buildSnackBar() {
if (_snackBarStatus == AnimationStatus.dismissed)
if (_snackBarStatus == PerformanceStatus.dismissed)
return null;
return new SnackBar(
showing: _isSnackBarShowing,
content: new Text("Stock purchased!"),
actions: [new SnackBarAction(label: "UNDO", onPressed: _handleUndo)],
onDismissed: () { setState(() { _snackBarStatus = AnimationStatus.dismissed; }); }
onDismissed: () { setState(() { _snackBarStatus = PerformanceStatus.dismissed; }); }
);
}
void _handleStockPurchased() {
setState(() {
_isSnackBarShowing = true;
_snackBarStatus = AnimationStatus.forward;
_snackBarStatus = PerformanceStatus.forward;
});
}
......@@ -276,8 +254,7 @@ class StockHomeState extends State<StockHome> {
toolbar: _isSearching ? buildSearchBar() : buildToolBar(),
body: buildTabNavigator(),
snackBar: buildSnackBar(),
floatingActionButton: buildFloatingActionButton(),
drawer: buildDrawer()
floatingActionButton: buildFloatingActionButton()
);
}
}
......@@ -37,13 +37,13 @@ class StockRow extends StatelessComponent {
static const double kHeight = 79.0;
GestureTapListener _getTapHandler(StockRowActionCallback callback) {
GestureTapCallback _getTapHandler(StockRowActionCallback callback) {
if (callback == null)
return null;
return () => callback(stock, key, arrowKey, symbolKey, priceKey);
}
GestureLongPressListener _getLongPressHandler(StockRowActionCallback callback) {
GestureLongPressCallback _getLongPressHandler(StockRowActionCallback callback) {
if (callback == null)
return null;
return () => callback(stock, key, arrowKey, symbolKey, priceKey);
......@@ -55,52 +55,50 @@ class StockRow extends StatelessComponent {
String changeInPrice = "${stock.percentChange.toStringAsFixed(2)}%";
if (stock.percentChange > 0) changeInPrice = "+" + changeInPrice;
return new GestureDetector(
return new InkWell(
onTap: _getTapHandler(onPressed),
onLongPress: _getLongPressHandler(onLongPressed),
child: new InkWell(
child: new Container(
padding: const EdgeDims.TRBL(16.0, 16.0, 20.0, 16.0),
decoration: new BoxDecoration(
border: new Border(
bottom: new BorderSide(color: Theme.of(context).dividerColor)
)
child: new Container(
padding: const EdgeDims.TRBL(16.0, 16.0, 20.0, 16.0),
decoration: new BoxDecoration(
border: new Border(
bottom: new BorderSide(color: Theme.of(context).dividerColor)
)
),
child: new Row([
new Container(
key: arrowKey,
child: new StockArrow(percentChange: stock.percentChange),
margin: const EdgeDims.only(right: 5.0)
),
child: new Row([
new Container(
key: arrowKey,
child: new StockArrow(percentChange: stock.percentChange),
margin: const EdgeDims.only(right: 5.0)
),
new Flexible(
child: new Row([
new Flexible(
flex: 2,
child: new Text(
stock.symbol,
key: symbolKey
)
),
new Flexible(
child: new Text(
lastSale,
style: const TextStyle(textAlign: TextAlign.right),
key: priceKey
)
),
new Flexible(
child: new Text(
changeInPrice,
style: Theme.of(context).text.caption.copyWith(textAlign: TextAlign.right)
)
),
],
alignItems: FlexAlignItems.baseline,
textBaseline: DefaultTextStyle.of(context).textBaseline
)
new Flexible(
child: new Row([
new Flexible(
flex: 2,
child: new Text(
stock.symbol,
key: symbolKey
)
),
new Flexible(
child: new Text(
lastSale,
style: const TextStyle(textAlign: TextAlign.right),
key: priceKey
)
),
new Flexible(
child: new Text(
changeInPrice,
style: Theme.of(context).text.caption.copyWith(textAlign: TextAlign.right)
)
),
],
alignItems: FlexAlignItems.baseline,
textBaseline: DefaultTextStyle.of(context).textBaseline
)
])
)
)
])
)
);
}
......
......@@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:sky/animation.dart';
import 'dart:sky' as sky;
import 'package:sky/material.dart';
import 'package:sky/painting.dart';
import 'package:sky/widgets.dart';
......@@ -16,15 +17,22 @@ class CardModel {
Key get key => new ObjectKey(this);
}
class CardCollectionApp extends StatefulComponent {
CardCollectionAppState createState() => new CardCollectionAppState();
class CardCollection extends StatefulComponent {
CardCollection({ this.navigator });
final NavigatorState navigator;
CardCollectionState createState() => new CardCollectionState();
}
class CardCollectionAppState extends State<CardCollectionApp> {
class CardCollectionState extends State<CardCollection> {
static const TextStyle cardLabelStyle =
const TextStyle(color: Colors.white, fontSize: 18.0, fontWeight: bold);
// TODO(hansmuller): need a local image asset
static const _sunshineURL = "http://www.walltor.com/images/wallpaper/good-morning-sunshine-58540.jpg";
final TextStyle backgroundTextStyle =
Typography.white.title.copyWith(textAlign: TextAlign.center);
......@@ -32,8 +40,7 @@ class CardCollectionAppState extends State<CardCollectionApp> {
DismissDirection _dismissDirection = DismissDirection.horizontal;
bool _snapToCenter = false;
bool _fixedSizeCards = false;
bool _drawerShowing = false;
AnimationStatus _drawerStatus = AnimationStatus.dismissed;
bool _sunshine = false;
InvalidatorCallback _invalidator;
Size _cardCollectionSize = new Size(200.0, 200.0);
......@@ -108,17 +115,23 @@ class CardCollectionAppState extends State<CardCollectionApp> {
}
}
void _handleOpenDrawer() {
setState(() {
_drawerShowing = true;
_drawerStatus = AnimationStatus.forward;
});
}
void _handleDrawerDismissed() {
setState(() {
_drawerStatus = AnimationStatus.dismissed;
});
void _showDrawer() {
showDrawer(
navigator: config.navigator,
child: new IconTheme(
data: const IconThemeData(color: IconThemeColor.black),
child: new Block([
new DrawerHeader(child: new Text('Options')),
buildDrawerCheckbox("Snap fling scrolls to center", _snapToCenter, _toggleSnapToCenter),
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'),
])
)
);
}
String _dismissDirectionText(DismissDirection direction) {
......@@ -139,64 +152,47 @@ class CardCollectionAppState extends State<CardCollectionApp> {
});
}
_changeDismissDirection(DismissDirection newDismissDirection) {
void _toggleSunshine() {
setState(() {
_dismissDirection = newDismissDirection;
_drawerStatus = AnimationStatus.dismissed;
_sunshine = !_sunshine;
});
}
Widget buildDrawer() {
if (_drawerStatus == AnimationStatus.dismissed)
return null;
Widget buildDrawerCheckbox(String label, bool value, Function callback) {
return new DrawerItem(
onPressed: callback,
child: new Row([
new Flexible(child: new Text(label)),
new Checkbox(value: value, onChanged: (_) { callback(); })
])
);
}
void _changeDismissDirection(DismissDirection newDismissDirection) {
setState(() {
_dismissDirection = newDismissDirection;
});
config.navigator.pop();
}
Widget buildDrawerRadioItem(DismissDirection direction, String icon) {
return new DrawerItem(
icon: icon,
onPressed: () { _changeDismissDirection(direction); },
child: new Row([
new Flexible(child: new Text(_dismissDirectionText(direction))),
new Radio(
value: direction,
onChanged: _changeDismissDirection,
groupValue: _dismissDirection
)
])
);
}
Widget buildDrawerCheckbox(String label, bool value, Function callback) {
return new DrawerItem(
onPressed: callback,
child: new Row([
new Flexible(child: new Text(label)),
new Checkbox(value: value, onChanged: (_) { callback(); })
])
);
}
return new IconTheme(
data: const IconThemeData(color: IconThemeColor.black),
child: new Drawer(
level: 3,
showing: _drawerShowing,
onDismissed: _handleDrawerDismissed,
children: [
new DrawerHeader(child: new Text('Options')),
buildDrawerCheckbox("Snap fling scrolls to center", _snapToCenter, _toggleSnapToCenter),
buildDrawerCheckbox("Fixed size cards", _fixedSizeCards, _toggleFixedSizeCards),
new DrawerDivider(),
buildDrawerRadioItem(DismissDirection.horizontal, 'action/code'),
buildDrawerRadioItem(DismissDirection.left, 'navigation/arrow_back'),
buildDrawerRadioItem(DismissDirection.right, 'navigation/arrow_forward'),
]
)
Widget buildDrawerRadioItem(DismissDirection direction, String icon) {
return new DrawerItem(
icon: icon,
onPressed: () { _changeDismissDirection(direction); },
child: new Row([
new Flexible(child: new Text(_dismissDirectionText(direction))),
new Radio(
value: direction,
onChanged: _changeDismissDirection,
groupValue: _dismissDirection
)
])
);
}
Widget buildToolBar() {
return new ToolBar(
left: new IconButton(icon: "navigation/menu", onPressed: _handleOpenDrawer),
left: new IconButton(icon: "navigation/menu", onPressed: _showDrawer),
center: new Text('Swipe Away'),
right: [
new Text(_dismissDirectionText(_dismissDirection))
......@@ -285,6 +281,16 @@ class CardCollectionAppState extends State<CardCollectionApp> {
});
}
sky.Shader _createShader(Rect bounds) {
return new LinearGradient(
begin: Point.origin,
end: new Point(0.0, bounds.height),
colors: [const Color(0x00FFFFFF), const Color(0xFFFFFFFF)],
stops: [0.1, 0.35]
)
.createShader();
}
Widget build(BuildContext context) {
Widget cardCollection;
......@@ -306,6 +312,12 @@ class CardCollectionAppState extends State<CardCollectionApp> {
);
}
if (_sunshine)
cardCollection = new Stack([
new Column([new NetworkImage(src: _sunshineURL)]),
new ShaderMask(child: cardCollection, shaderCallback: _createShader)
]);
Widget body = new SizeObserver(
callback: _updateCardCollectionSize,
child: new Container(
......@@ -329,24 +341,23 @@ class CardCollectionAppState extends State<CardCollectionApp> {
body = new Stack([body, indicator]);
}
return new Theme(
data: new ThemeData(
brightness: ThemeBrightness.light,
primarySwatch: Colors.blue,
accentColor: Colors.redAccent[200]
),
child: new Title(
title: 'Cards',
child: new Scaffold(
toolbar: buildToolBar(),
drawer: buildDrawer(),
body: body
)
)
return new Scaffold(
toolbar: buildToolBar(),
body: body
);
}
}
void main() {
runApp(new CardCollectionApp());
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),
}
));
}
......@@ -50,7 +50,7 @@ class ExampleDragTargetState extends State<ExampleDragTarget> {
}
class Dot extends StatelessComponent {
Dot({ Key key, this.color, this.size }): super(key: key);
Dot({ Key key, this.color, this.size }) : super(key: key);
final Color color;
final double size;
Widget build(BuildContext context) {
......@@ -66,7 +66,7 @@ class Dot extends StatelessComponent {
}
class ExampleDragSource extends StatelessComponent {
ExampleDragSource({ Key key, this.navigator, this.name, this.color }): super(key: key);
ExampleDragSource({ Key key, this.navigator, this.name, this.color }) : super(key: key);
final NavigatorState navigator;
final String name;
final Color color;
......@@ -133,7 +133,7 @@ void main() {
runApp(new App(
title: 'Drag and Drop Flutter Demo',
routes: {
'/': (NavigatorState navigator, Route route) => new DragAndDropApp(navigator: navigator)
'/': (RouteArguments args) => new DragAndDropApp(navigator: args.navigator)
}
));
}
......@@ -6,46 +6,46 @@ import 'package:sky/material.dart';
import 'package:sky/widgets.dart';
final Map<String, RouteBuilder> routes = <String, RouteBuilder>{
'/': (NavigatorState navigator, Route route) => new Container(
'/': (RouteArguments args) => new Container(
padding: const EdgeDims.all(30.0),
decoration: new BoxDecoration(backgroundColor: const Color(0xFFCCCCCC)),
child: new Column([
new Text("You are at home"),
new RaisedButton(
child: new Text('GO SHOPPING'),
onPressed: () => navigator.pushNamed('/shopping')
onPressed: () => args.navigator.pushNamed('/shopping')
),
new RaisedButton(
child: new Text('START ADVENTURE'),
onPressed: () => navigator.pushNamed('/adventure')
onPressed: () => args.navigator.pushNamed('/adventure')
)],
justifyContent: FlexJustifyContent.center
)
),
'/shopping': (NavigatorState navigator, Route route) => new Container(
'/shopping': (RouteArguments args) => new Container(
padding: const EdgeDims.all(20.0),
decoration: new BoxDecoration(backgroundColor: const Color(0xFFBF5FFF)),
child: new Column([
new Text("Village Shop"),
new RaisedButton(
child: new Text('RETURN HOME'),
onPressed: () => navigator.pop()
onPressed: () => args.navigator.pop()
),
new RaisedButton(
child: new Text('GO TO DUNGEON'),
onPressed: () => navigator.pushNamed('/adventure')
onPressed: () => args.navigator.pushNamed('/adventure')
)],
justifyContent: FlexJustifyContent.center
)
),
'/adventure': (NavigatorState navigator, Route route) => new Container(
'/adventure': (RouteArguments args) => new Container(
padding: const EdgeDims.all(20.0),
decoration: new BoxDecoration(backgroundColor: const Color(0xFFDC143C)),
child: new Column([
new Text("Monster's Lair"),
new RaisedButton(
child: new Text('RUN!!!'),
onPressed: () => navigator.pop()
onPressed: () => args.navigator.pop()
)],
justifyContent: FlexJustifyContent.center
)
......
......@@ -165,7 +165,7 @@ void main() {
),
title: 'Cards',
routes: {
'/': (navigator, route) => new OverlayGeometryApp()
'/': (RouteArguments args) => new OverlayGeometryApp()
}
));
}
......@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:sky/animation.dart';
import 'package:sky/material.dart';
import 'package:sky/painting.dart';
import 'package:sky/widgets.dart';
......@@ -17,6 +16,10 @@ class CardModel {
}
class PageableListApp extends StatefulComponent {
PageableListApp({ this.navigator });
final NavigatorState navigator;
PageableListAppState createState() => new PageableListAppState();
}
......@@ -85,31 +88,10 @@ class PageableListAppState extends State<PageableListApp> {
});
}
bool _drawerShowing = false;
AnimationStatus _drawerStatus = AnimationStatus.dismissed;
void _handleOpenDrawer() {
setState(() {
_drawerShowing = true;
_drawerStatus = AnimationStatus.forward;
});
}
void _handleDrawerDismissed() {
setState(() {
_drawerStatus = AnimationStatus.dismissed;
});
}
Drawer buildDrawer() {
if (_drawerStatus == AnimationStatus.dismissed)
return null;
return new Drawer(
level: 3,
showing: _drawerShowing,
onDismissed: _handleDrawerDismissed,
children: [
void _showDrawer() {
showDrawer(
navigator: config.navigator,
child: new Block([
new DrawerHeader(child: new Text('Options')),
new DrawerItem(
icon: 'navigation/more_horiz',
......@@ -130,14 +112,13 @@ class PageableListAppState extends State<PageableListApp> {
new Checkbox(value: itemsWrap)
])
)
]
])
);
}
Widget buildToolBar() {
return new ToolBar(
left: new IconButton(icon: "navigation/menu", onPressed: _handleOpenDrawer),
left: new IconButton(icon: "navigation/menu", onPressed: _showDrawer),
center: new Text('PageableList'),
right: [
new Text(scrollDirection == ScrollDirection.horizontal ? "horizontal" : "vertical")
......@@ -167,25 +148,24 @@ class PageableListAppState extends State<PageableListApp> {
Widget build(BuildContext context) {
return new IconTheme(
data: const IconThemeData(color: IconThemeColor.white),
child: new Theme(
data: new ThemeData(
brightness: ThemeBrightness.light,
primarySwatch: Colors.blue,
accentColor: Colors.redAccent[200]
),
child: new Title(
title: 'PageableList',
child: new Scaffold(
drawer: buildDrawer(),
toolbar: buildToolBar(),
body: buildBody(context)
)
)
child: new Scaffold(
toolbar: buildToolBar(),
body: buildBody(context)
)
);
}
}
void main() {
runApp(new PageableListApp());
runApp(new App(
title: 'PageableList',
theme: new ThemeData(
brightness: ThemeBrightness.light,
primarySwatch: Colors.blue,
accentColor: Colors.redAccent[200]
),
routes: {
'/': (RouteArguments args) => new PageableListApp(navigator: args.navigator),
}
));
}
......@@ -13,24 +13,23 @@ class ProgressIndicatorApp extends StatefulComponent {
class ProgressIndicatorAppState extends State<ProgressIndicatorApp> {
void initState() {
super.initState();
valueAnimation = new ValueAnimation<double>()
valueAnimation = new ValuePerformance<double>()
..duration = const Duration(milliseconds: 1500)
..variable = new AnimatedValue<double>(
0.0,
end: 1.0,
curve: ease,
reverseCurve: ease,
interval: new Interval(0.0, 0.9)
curve: new Interval(0.0, 0.9, curve: ease),
reverseCurve: ease
);
valueAnimation.addStatusListener((AnimationStatus status) {
if (status == AnimationStatus.dismissed || status == AnimationStatus.completed)
valueAnimation.addStatusListener((PerformanceStatus status) {
if (status == PerformanceStatus.dismissed || status == PerformanceStatus.completed)
reverseValueAnimationDirection();
});
valueAnimation.play(valueAnimationDirection);
}
ValueAnimation<double> valueAnimation;
Direction valueAnimationDirection = Direction.forward;
ValuePerformance<double> valueAnimation;
AnimationDirection valueAnimationDirection = AnimationDirection.forward;
void handleTap() {
setState(() {
......@@ -43,9 +42,9 @@ class ProgressIndicatorAppState extends State<ProgressIndicatorApp> {
}
void reverseValueAnimationDirection() {
valueAnimationDirection = (valueAnimationDirection == Direction.forward)
? Direction.reverse
: Direction.forward;
valueAnimationDirection = (valueAnimationDirection == AnimationDirection.forward)
? AnimationDirection.reverse
: AnimationDirection.forward;
valueAnimation.play(valueAnimationDirection);
}
......
......@@ -54,13 +54,13 @@ Widget builder() {
);
}
double timeBase;
Duration timeBase;
RenderTransform transformBox;
void rotate(double timeStamp) {
void rotate(Duration timeStamp) {
if (timeBase == null)
timeBase = timeStamp;
double delta = (timeStamp - timeBase) / 1000; // radians
double delta = (timeStamp - timeBase).inMicroseconds.toDouble() / Duration.MICROSECONDS_PER_SECOND; // radians
transformBox.setIdentity();
transformBox.translate(transformBox.size.width / 2.0, transformBox.size.height / 2.0);
......
......@@ -7,12 +7,12 @@
/// This library depends only on core Dart libraries and the `newton` package.
library animation;
export 'src/animation/animated_simulation.dart';
export 'src/animation/animated_value.dart';
export 'src/animation/animation_performance.dart';
export 'src/animation/performance.dart';
export 'src/animation/clamped_simulation.dart';
export 'src/animation/curves.dart';
export 'src/animation/forces.dart';
export 'src/animation/scheduler.dart';
export 'src/animation/scroll_behavior.dart';
export 'src/animation/timeline.dart';
export 'src/animation/simulation_stepper.dart';
export 'src/animation/ticker.dart';
......@@ -2,12 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import "dart:sky";
import 'dart:sky' show Color, Rect;
import 'package:sky/src/animation/curves.dart';
/// The direction in which an animation is running
enum Direction {
enum AnimationDirection {
/// The animation is running from beginning to end
forward,
......@@ -16,72 +16,52 @@ enum Direction {
}
/// An interface describing a variable that changes as an animation progresses.
///
/// AnimatedVariables, by convention, must be cheap to create. This allows them to be used in
/// build functions in Widgets.
abstract class AnimatedVariable {
///
/// Animatable objects, by convention, must be cheap to create. This allows them
/// to be used in build functions in Widgets.
abstract class Animatable {
/// Update the variable to a given time in an animation that is running in the given direction
void setProgress(double t, Direction direction);
void setProgress(double t, AnimationDirection direction);
String toString();
}
/// Used by [AnimationPerformance] to convert the timing of a performance to a different timescale.
/// Used by [Performance] to convert the timing of a performance to a different timescale.
/// For example, by setting different values for the interval and reverseInterval, a performance
/// can be made to take longer in one direction that the other.
class AnimationTiming {
AnimationTiming({
this.interval: const Interval(0.0, 1.0),
this.reverseInterval,
this.curve: linear,
this.reverseCurve
});
/// The interval during which this timing is active in the forward direction
Interval interval;
/// The interval during which this timing is active in the reverse direction
///
/// If this field is null, the timing defaults to using [interval] in both directions.
Interval reverseInterval;
AnimationTiming({ this.curve, this.reverseCurve });
/// The curve that this timing applies to the animation clock in the forward direction
/// The curve to use in the forward direction
Curve curve;
/// The curve that this timing applies to the animation clock in the reverse direction
/// The curve to use in the reverse direction
///
/// If this field is null, the timing defaults to using [curve] in both directions.
/// If this field is null, use [curve] in both directions.
Curve reverseCurve;
/// Applies this timing to the given animation clock value in the given direction
double transform(double t, Direction direction) {
Interval interval = _getInterval(direction);
if (interval != null)
t = interval.transform(t);
if (t == 1.0) // Or should we support inverse curves?
double transform(double t, AnimationDirection direction) {
Curve activeCurve = _getActiveCurve(direction);
if (activeCurve == null)
return t;
Curve curve = _getCurve(direction);
if (curve != null)
t = curve.transform(t);
return t;
}
Interval _getInterval(Direction direction) {
if (direction == Direction.forward || reverseInterval == null)
return interval;
return reverseInterval;
if (t == 0.0 || t == 1.0) {
assert(activeCurve.transform(t).round() == t);
return t;
}
return activeCurve.transform(t);
}
Curve _getCurve(Direction direction) {
if (direction == Direction.forward || reverseCurve == null)
Curve _getActiveCurve(AnimationDirection direction) {
if (direction == AnimationDirection.forward || reverseCurve == null)
return curve;
return reverseCurve;
}
}
/// An animated variable with a concrete type
class AnimatedValue<T extends dynamic> extends AnimationTiming implements AnimatedVariable {
AnimatedValue(this.begin, { this.end, Interval interval, Interval reverseInterval, Curve curve, Curve reverseCurve })
: super(interval: interval, reverseInterval: reverseInterval, curve: curve, reverseCurve: reverseCurve) {
class AnimatedValue<T extends dynamic> extends AnimationTiming implements Animatable {
AnimatedValue(this.begin, { this.end, Curve curve, Curve reverseCurve })
: super(curve: curve, reverseCurve: reverseCurve) {
value = begin;
}
......@@ -98,41 +78,28 @@ class AnimatedValue<T extends dynamic> extends AnimationTiming implements Animat
T lerp(double t) => begin + (end - begin) * t;
/// Updates the value of this variable according to the given animation clock value and direction
void setProgress(double t, Direction direction) {
void setProgress(double t, AnimationDirection direction) {
if (end != null) {
t = transform(t, direction);
value = (t == 1.0) ? end : lerp(t);
if (t == 0.0)
value = begin;
else if (t == 1.0)
value = end;
else
value = lerp(t);
}
}
String toString() => 'AnimatedValue(begin=$begin, end=$end, value=$value)';
}
/// A list of animated variables
class AnimatedList extends AnimationTiming implements AnimatedVariable {
/// The list of variables contained in the list
List<AnimatedVariable> variables;
AnimatedList(this.variables, { Interval interval, Interval reverseInterval, Curve curve, Curve reverseCurve })
: super(interval: interval, reverseInterval: reverseInterval, curve: curve, reverseCurve: reverseCurve);
// Updates the value of all the variables in the list according to the given animation clock value and direction
void setProgress(double t, Direction direction) {
double adjustedTime = transform(t, direction);
for (AnimatedVariable variable in variables)
variable.setProgress(adjustedTime, direction);
}
String toString() => 'AnimatedList([$variables])';
}
/// An animated variable containing a color
///
/// This class specializes the interpolation of AnimatedValue<Color> to be
/// appropriate for colors.
class AnimatedColorValue extends AnimatedValue<Color> {
AnimatedColorValue(Color begin, { Color end, Interval interval, Interval reverseInterval, Curve curve, Curve reverseCurve })
: super(begin, end: end, interval: interval, reverseInterval: reverseInterval, curve: curve, reverseCurve: reverseCurve);
AnimatedColorValue(Color begin, { Color end, Curve curve, Curve reverseCurve })
: super(begin, end: end, curve: curve, reverseCurve: reverseCurve);
Color lerp(double t) => Color.lerp(begin, end, t);
}
......@@ -141,9 +108,9 @@ class AnimatedColorValue extends AnimatedValue<Color> {
///
/// This class specializes the interpolation of AnimatedValue<Rect> to be
/// appropriate for rectangles.
class AnimatedRect extends AnimatedValue<Rect> {
AnimatedRect(Rect begin, { Rect end, Interval interval, Interval reverseInterval, Curve curve, Curve reverseCurve })
: super(begin, end: end, interval: interval, reverseInterval: reverseInterval, curve: curve, reverseCurve: reverseCurve);
class AnimatedRectValue extends AnimatedValue<Rect> {
AnimatedRectValue(Rect begin, { Rect end, Curve curve, Curve reverseCurve })
: super(begin, end: end, curve: curve, reverseCurve: reverseCurve);
Rect lerp(double t) => Rect.lerp(begin, end, t);
}
......@@ -27,9 +27,9 @@ class Linear implements Curve {
double transform(double t) => t;
}
/// A curve that is 0.0 until start, then linear from 0.0 to 1.0 at end, then 1.0
/// A curve that is 0.0 until start, then curved from 0.0 to 1.0 at end, then 1.0
class Interval implements Curve {
const Interval(this.start, this.end);
const Interval(this.start, this.end, { this.curve: linear });
/// The smallest value for which this interval is 0.0
final double start;
......@@ -37,12 +37,18 @@ class Interval implements Curve {
/// The smallest value for which this interval is 1.0
final double end;
/// The curve to apply between [start] and [end]
final Curve curve;
double transform(double t) {
assert(start >= 0.0);
assert(start <= 1.0);
assert(end >= 0.0);
assert(end <= 1.0);
return ((t - start) / (end - start)).clamp(0.0, 1.0);
t = ((t - start) / (end - start)).clamp(0.0, 1.0);
if (t == 0.0 || t == 1.0)
return t;
return curve.transform(t);
}
}
......
......@@ -14,7 +14,7 @@ double timeDilation = 1.0;
/// scheduler's epoch. Use timeStamp to determine how far to advance animation
/// timelines so that all the animations in the system are synchronized to a
/// common time base.
typedef void SchedulerCallback(double timeStamp);
typedef void SchedulerCallback(Duration timeStamp);
/// Schedules callbacks to run in concert with the engine's animation system
class Scheduler {
......@@ -35,8 +35,10 @@ class Scheduler {
/// This function first calls all the callbacks registered by
/// [requestAnimationFrame] and then calls all the callbacks registered by
/// [addPersistentFrameCallback], which typically drive the rendering pipeline.
void beginFrame(double timeStamp) {
timeStamp /= timeDilation;
void beginFrame(double timeStampMS) {
timeStampMS /= timeDilation;
Duration timeStamp = new Duration(microseconds: (timeStampMS * Duration.MICROSECONDS_PER_MILLISECOND).round());
_haveScheduledVisualUpdate = false;
......
......@@ -5,30 +5,31 @@
import 'dart:async';
import 'package:newton/newton.dart';
import 'package:sky/src/animation/animated_simulation.dart';
/// A simulation that linearly varies from [begin] to [end] over [duration]
class TweenSimulation extends Simulation {
final double _durationInSeconds;
/// The initial value of the simulation
final double begin;
/// The terminal value of the simulation
final double end;
TweenSimulation(Duration duration, this.begin, this.end) :
_durationInSeconds = duration.inMicroseconds / Duration.MICROSECONDS_PER_SECOND {
import 'package:sky/src/animation/animated_value.dart';
import 'package:sky/src/animation/curves.dart';
import 'package:sky/src/animation/ticker.dart';
/// A simulation that varies from [begin] to [end] over [duration] using [curve]
///
/// This class is an adaptor between the Simulation interface and the
/// AnimatedValue interface.
class _TweenSimulation extends Simulation {
_TweenSimulation(double begin, double end, Duration duration, Curve curve)
: _durationInSeconds = duration.inMicroseconds.toDouble() / Duration.MICROSECONDS_PER_SECOND,
_tween = new AnimatedValue<double>(begin, end: end, curve: curve) {
assert(_durationInSeconds > 0.0);
assert(begin != null && begin >= 0.0 && begin <= 1.0);
assert(end != null && end >= 0.0 && end <= 1.0);
assert(begin != null);
assert(end != null);
}
final double _durationInSeconds;
final AnimatedValue<double> _tween;
double x(double timeInSeconds) {
assert(timeInSeconds >= 0.0);
final double t = timeInSeconds / _durationInSeconds;
return t >= 1.0 ? end : begin + (end - begin) * t;
final double t = (timeInSeconds / _durationInSeconds).clamp(0.0, 1.0);
_tween.setProgress(t, AnimationDirection.forward);
return _tween.value;
}
double dx(double timeInSeconds) => 1.0;
......@@ -36,52 +37,69 @@ class TweenSimulation extends Simulation {
bool isDone(double timeInSeconds) => timeInSeconds > _durationInSeconds;
}
/// A timeline for an animation
class Timeline {
Timeline(Function onTick) {
_animation = new AnimatedSimulation(onTick);
typedef TimelineCallback(double value);
/// Steps a simulation one per frame
class SimulationStepper {
SimulationStepper(TimelineCallback onTick) : _onTick = onTick {
_ticker = new Ticker(_tick);
}
AnimatedSimulation _animation;
final TimelineCallback _onTick;
Ticker _ticker;
Simulation _simulation;
/// The current value of the timeline
double get value => _animation.value.clamp(0.0, 1.0);
double get value => _value;
double _value = 0.0;
void set value(double newValue) {
assert(newValue != null && newValue >= 0.0 && newValue <= 1.0);
assert(newValue != null);
assert(!isAnimating);
_animation.value = newValue;
_value = newValue;
_onTick(_value);
}
/// Whether the timeline is currently animating
bool get isAnimating => _animation.isAnimating;
Future _start({
Duration duration,
double begin: 0.0,
double end: 1.0
}) {
assert(!_animation.isAnimating);
assert(duration > Duration.ZERO);
return _animation.start(new TweenSimulation(duration, begin, end));
}
bool get isAnimating => _ticker.isTicking;
/// Animate value of the timeline to the given target over the given duration
///
/// Returns a future that resolves when the timeline stops animating,
/// typically when the timeline arives at the target value.
Future animateTo(double target, { Duration duration }) {
Future animateTo(double target, { Duration duration, Curve curve: linear }) {
assert(duration > Duration.ZERO);
return _start(duration: duration, begin: value, end: target);
assert(!isAnimating);
return _start(new _TweenSimulation(value, target, duration, curve));
}
/// Gives the given simulation control over the timeline
Future animateWith(Simulation simulation) {
stop();
return _start(simulation);
}
/// Start ticking the given simulation once per frame
///
/// Returns a future that resolves when the simulation stops ticking.
Future _start(Simulation simulation) {
assert(simulation != null);
assert(!isAnimating);
_simulation = simulation;
_value = simulation.x(0.0);
return _ticker.start();
}
/// Stop animating the timeline
void stop() {
_animation.stop();
_simulation = null;
_ticker.stop();
}
// Gives the given simulation control over the timeline
Future fling(Simulation simulation) {
stop();
return _animation.start(simulation);
void _tick(Duration elapsed) {
double elapsedInSeconds = elapsed.inMicroseconds.toDouble() / Duration.MICROSECONDS_PER_SECOND;
_value = _simulation.x(elapsedInSeconds);
if (_simulation.isDone(elapsedInSeconds))
stop();
_onTick(_value);
}
}
......@@ -4,29 +4,27 @@
import 'dart:async';
import 'package:newton/newton.dart';
import 'package:sky/src/animation/scheduler.dart';
const double _kSecondsPerMillisecond = 1000.0;
// TODO(abarth): Change from double to Duration.
typedef _TickerCallback(double timeStamp);
typedef TickerCallback(Duration elapsed);
/// Calls its callback once per animation frame
class Ticker {
/// Constructs a ticker that will call onTick once per frame while running
Ticker(_TickerCallback onTick) : _onTick = onTick;
Ticker(TickerCallback onTick) : _onTick = onTick;
final _TickerCallback _onTick;
final TickerCallback _onTick;
Completer _completer;
int _animationId;
Duration _startTime;
/// Start calling onTick once per animation frame
///
/// The returned future resolves once the ticker stops ticking.
Future start() {
assert(!isTicking);
assert(_startTime == null);
_completer = new Completer();
_scheduleTick();
return _completer.future;
......@@ -39,12 +37,14 @@ class Ticker {
if (!isTicking)
return;
_startTime = null;
if (_animationId != null) {
scheduler.cancelAnimationFrame(_animationId);
_animationId = null;
}
// We take the _completer into a local variable so that !isTicking
// We take the _completer into a local variable so that isTicking is false
// when we actually complete the future (isTicking uses _completer
// to determine its state).
Completer localCompleter = _completer;
......@@ -56,12 +56,15 @@ class Ticker {
/// Whether this ticker has scheduled a call to onTick
bool get isTicking => _completer != null;
void _tick(double timeStamp) {
void _tick(Duration timeStamp) {
assert(isTicking);
assert(_animationId != null);
_animationId = null;
_onTick(timeStamp);
if (_startTime == null)
_startTime = timeStamp;
_onTick(timeStamp - _startTime);
// The onTick callback may have scheduled another tick already.
if (isTicking && _animationId == null)
......@@ -74,63 +77,3 @@ class Ticker {
_animationId = scheduler.requestAnimationFrame(_tick);
}
}
/// Ticks a simulation once per frame
class AnimatedSimulation {
AnimatedSimulation(Function onTick) : _onTick = onTick {
_ticker = new Ticker(_tick);
}
final Function _onTick;
Ticker _ticker;
Simulation _simulation;
double _startTime;
double _value = 0.0;
/// The current value of the simulation
double get value => _value;
void set value(double newValue) {
assert(!_ticker.isTicking);
_value = newValue;
_onTick(_value);
}
/// Start ticking the given simulation once per frame
///
/// Returns a future that resolves when the simulation stops ticking.
Future start(Simulation simulation) {
assert(simulation != null);
assert(!_ticker.isTicking);
_simulation = simulation;
_startTime = null;
_value = simulation.x(0.0);
return _ticker.start();
}
/// Stop ticking the current simulation
void stop() {
_simulation = null;
_startTime = null;
_ticker.stop();
}
/// Whether this object is currently ticking a simulation
bool get isAnimating => _ticker.isTicking;
void _tick(double timeStamp) {
if (_startTime == null)
_startTime = timeStamp;
double timeInSeconds = (timeStamp - _startTime) / _kSecondsPerMillisecond;
_value = _simulation.x(timeInSeconds);
final bool isLastTick = _simulation.isDone(timeInSeconds);
if (isLastTick)
stop();
_onTick(_value);
}
}
......@@ -9,13 +9,13 @@ import 'package:sky/src/gestures/constants.dart';
import 'package:sky/src/gestures/pointer_router.dart';
import 'package:sky/src/gestures/recognizer.dart';
typedef void GestureLongPressListener();
typedef void GestureLongPressCallback();
class LongPressGestureRecognizer extends PrimaryPointerGestureRecognizer {
LongPressGestureRecognizer({ PointerRouter router, this.onLongPress })
: super(router: router, deadline: kTapTimeout + kLongPressTimeout);
GestureLongPressListener onLongPress;
GestureLongPressCallback onLongPress;
void didExceedDeadline() {
resolve(GestureDisposition.accepted);
......
......@@ -12,7 +12,9 @@ import 'package:sky/src/gestures/pointer_router.dart';
export 'package:sky/src/gestures/pointer_router.dart' show PointerRouter;
abstract class GestureRecognizer extends GestureArenaMember {
GestureRecognizer({ PointerRouter router }) : _router = router;
GestureRecognizer({ PointerRouter router }) : _router = router {
assert(_router != null);
}
PointerRouter _router;
......
......@@ -8,13 +8,13 @@ import 'package:sky/src/gestures/arena.dart';
import 'package:sky/src/gestures/constants.dart';
import 'package:sky/src/gestures/recognizer.dart';
typedef void GestureShowPressListener();
typedef void GestureShowPressCallback();
class ShowPressGestureRecognizer extends PrimaryPointerGestureRecognizer {
ShowPressGestureRecognizer({ PointerRouter router, this.onShowPress })
: super(router: router, deadline: kTapTimeout);
GestureShowPressListener onShowPress;
GestureShowPressCallback onShowPress;
void didExceedDeadline() {
// Show press isn't an exclusive gesture. We can recognize a show press
......
......@@ -8,13 +8,15 @@ import 'package:sky/src/gestures/arena.dart';
import 'package:sky/src/gestures/constants.dart';
import 'package:sky/src/gestures/recognizer.dart';
typedef void GestureTapListener();
typedef void GestureTapCallback();
class TapGestureRecognizer extends PrimaryPointerGestureRecognizer {
TapGestureRecognizer({ PointerRouter router, this.onTap })
: super(router: router, deadline: kTapTimeout);
GestureTapListener onTap;
GestureTapCallback onTap;
GestureTapCallback onTapDown;
GestureTapCallback onTapCancel;
void didExceedDeadline() {
stopTrackingPointer(primaryPointer);
......@@ -22,9 +24,22 @@ class TapGestureRecognizer extends PrimaryPointerGestureRecognizer {
}
void handlePrimaryPointer(sky.PointerEvent event) {
if (event.type == 'pointerup') {
if (event.type == 'pointerdown') {
if (onTapDown != null)
onTapDown();
} else if (event.type == 'pointerup') {
resolve(GestureDisposition.accepted);
onTap();
if (onTap != null)
onTap();
}
}
void rejectGesture(int pointer) {
super.rejectGesture(pointer);
if (pointer == primaryPointer) {
assert(state == GestureRecognizerState.defunct);
if (onTapCancel != null)
onTapCancel();
}
}
}
......@@ -30,13 +30,13 @@ class RadialReaction {
_outerOpacity = new AnimatedValue<double>(0.0, end: _kMaxOpacity, curve: easeOut);
_innerCenter = new AnimatedValue<Point>(startPosition, end: center, curve: easeOut);
_innerRadius = new AnimatedValue<double>(0.0, end: radius, curve: easeOut);
_showPerformance = new AnimationPerformance(duration: _kShowDuration)
_showPerformance = new Performance(duration: _kShowDuration)
..addListener(() {
_showPerformance.updateVariable(_outerOpacity);
_showPerformance.updateVariable(_innerCenter);
_showPerformance.updateVariable(_innerRadius);
});
_fade = new ValueAnimation<double>(
_fade = new ValuePerformance<double>(
variable: new AnimatedValue(1.0, end: 0.0, curve: easeIn),
duration: _kHideDuration
);
......@@ -48,14 +48,14 @@ class RadialReaction {
/// The radius of the circle in which the reaction occurs
final double radius;
AnimationPerformance _showPerformance;
Performance _showPerformance;
AnimatedValue<double> _outerOpacity;
AnimatedValue<Point> _innerCenter;
AnimatedValue<double> _innerRadius;
Future _showComplete;
ValueAnimation<double> _fade;
ValuePerformance<double> _fade;
/// Show the reaction
///
......
......@@ -78,7 +78,7 @@ class FlutterBinding extends HitTestTarget {
}
/// Pump the rendering pipeline to generate a frame for the given time stamp
void beginFrame(double timeStamp) {
void beginFrame(Duration timeStamp) {
RenderObject.flushLayout();
_renderView.updateCompositingBits();
RenderObject.flushPaint();
......
......@@ -178,6 +178,60 @@ class BoxConstraints extends Constraints {
(minHeight <= size.height) && (size.height <= math.max(minHeight, maxHeight));
}
BoxConstraints operator*(double other) {
return new BoxConstraints(
minWidth: minWidth * other,
maxWidth: maxWidth * other,
minHeight: minHeight * other,
maxHeight: maxHeight * other
);
}
BoxConstraints operator/(double other) {
return new BoxConstraints(
minWidth: minWidth / other,
maxWidth: maxWidth / other,
minHeight: minHeight / other,
maxHeight: maxHeight / other
);
}
BoxConstraints operator~/(double other) {
return new BoxConstraints(
minWidth: (minWidth ~/ other).toDouble(),
maxWidth: (maxWidth ~/ other).toDouble(),
minHeight: (minHeight ~/ other).toDouble(),
maxHeight: (maxHeight ~/ other).toDouble()
);
}
BoxConstraints operator%(double other) {
return new BoxConstraints(
minWidth: minWidth % other,
maxWidth: maxWidth % other,
minHeight: minHeight % other,
maxHeight: maxHeight % other
);
}
/// Linearly interpolate between two BoxConstraints
///
/// If either is null, this function interpolates from [BoxConstraints.zero].
static BoxConstraints lerp(BoxConstraints a, BoxConstraints b, double t) {
if (a == null && b == null)
return null;
if (a == null)
return b * t;
if (b == null)
return a * (1.0 - t);
return new BoxConstraints(
minWidth: sky.lerpDouble(a.minWidth, b.minWidth, t),
maxWidth: sky.lerpDouble(a.maxWidth, b.maxWidth, t),
minHeight: sky.lerpDouble(a.minHeight, b.minHeight, t),
maxHeight: sky.lerpDouble(a.maxHeight, b.maxHeight, t)
);
}
bool operator ==(other) {
if (identical(this, other))
return true;
......
......@@ -87,6 +87,29 @@ class PictureLayer extends Layer {
}
/// A layer that indicates to the compositor that it should display
/// certain statistics within it
class StatisticsLayer extends Layer {
StatisticsLayer({
Offset offset: Offset.zero,
this.paintBounds,
this.optionsMask
}) : super(offset: offset);
/// The rectangle in this layer's coodinate system that bounds the recording
Rect paintBounds;
/// A mask specifying the statistics to display
int optionsMask;
void addToScene(sky.SceneBuilder builder, Offset layerOffset) {
assert(optionsMask != null);
builder.addStatistics(optionsMask, paintBounds.shift(layerOffset));
}
}
/// A composited layer that has a list of children
class ContainerLayer extends Layer {
ContainerLayer({ Offset offset: Offset.zero }) : super(offset: offset);
......
......@@ -16,6 +16,8 @@ import 'package:vector_math/vector_math_64.dart';
export 'dart:sky' show Point, Offset, Size, Rect, Color, Paint, Path;
export 'package:sky/src/rendering/hit_test.dart' show HitTestTarget, HitTestEntry, HitTestResult;
typedef sky.Shader ShaderCallback(Rect bounds);
/// Base class for data associated with a [RenderObject] by its parent
///
/// Some render objects wish to store data on their children, such as their
......@@ -130,6 +132,15 @@ class PaintingContext {
}
}
void paintStatistics(int optionsMask, Offset offset, Size size) {
StatisticsLayer statsLayer = new StatisticsLayer(
offset: offset,
paintBounds: new Rect.fromLTWH(0.0, 0.0, size.width, size.height),
optionsMask : optionsMask
);
_containerLayer.append(statsLayer);
}
// Below we have various variants of the paintChild() method, which
// do additional work, such as clipping or transforming, at the same
// time as painting the children.
......@@ -300,6 +311,34 @@ class PaintingContext {
}
}
static Paint _getPaintForShaderMask(Rect bounds,
ShaderCallback shaderCallback,
sky.TransferMode transferMode) {
return new Paint()
..transferMode = transferMode
..shader = shaderCallback(bounds);
}
void paintChildWithShaderMask(RenderObject child,
Point childPosition,
Rect bounds,
ShaderCallback shaderCallback,
sky.TransferMode transferMode) {
assert(debugCanPaintChild(child));
final Offset childOffset = childPosition.toOffset();
if (!child.needsCompositing) {
canvas.saveLayer(bounds, new Paint());
canvas.translate(childOffset.dx, childOffset.dy);
insertChild(child, Offset.zero);
Paint shaderPaint = _getPaintForShaderMask(bounds, shaderCallback, transferMode);
canvas.drawRect(Offset.zero & new Size(bounds.width, bounds.height), shaderPaint);
canvas.restore();
} else {
// TODO(hansmuller) support compositing ShaderMasks
assert('Support for compositing ShaderMasks is TBD' is String);
}
}
/// Instructs the child to draw itself onto this context at the given offset
///
/// Do not call directly. This function is visible so that it can be
......
......@@ -659,6 +659,37 @@ class RenderColorFilter extends RenderProxyBox {
}
}
class RenderShaderMask extends RenderProxyBox {
RenderShaderMask({ RenderBox child, ShaderCallback shaderCallback, sky.TransferMode transferMode })
: _shaderCallback = shaderCallback, _transferMode = transferMode, super(child) {
}
ShaderCallback get shaderCallback => _shaderCallback;
ShaderCallback _shaderCallback;
void set shaderCallback (ShaderCallback newShaderCallback) {
assert(newShaderCallback != null);
if (_shaderCallback == newShaderCallback)
return;
_shaderCallback = newShaderCallback;
markNeedsPaint();
}
sky.TransferMode get transferMode => _transferMode;
sky.TransferMode _transferMode;
void set transferMode (sky.TransferMode newTransferMode) {
assert(newTransferMode != null);
if (_transferMode == newTransferMode)
return;
_transferMode = newTransferMode;
markNeedsPaint();
}
void paint(PaintingContext context, Offset offset) {
if (child != null)
context.paintChildWithShaderMask(child, offset.toPoint(), offset & size, _shaderCallback, _transferMode);
}
}
/// Clips its child using a rectangle
///
/// Prevents its child from painting outside its bounds.
......
// 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/src/rendering/box.dart';
import 'package:sky/src/rendering/object.dart';
class StatisticsBox extends RenderBox {
StatisticsBox({int optionsMask: 0}) : _optionsMask = optionsMask;
int _optionsMask;
int get optionsMask => _optionsMask;
void set optionsMask (int mask) {
if (mask == _optionsMask) {
return;
}
_optionsMask = mask;
markNeedsPaint();
}
bool get sizedByParent => true;
double getMinIntrinsicWidth(BoxConstraints constraints) {
return constraints.minWidth;
}
double getMaxIntrinsicWidth(BoxConstraints constraints) {
return constraints.maxWidth;
}
double getMinIntrinsicHeight(BoxConstraints constraints) {
return constraints.minHeight;
}
double getMaxIntrinsicHeight(BoxConstraints constraints) {
return constraints.maxHeight;
}
void performResize() {
size = constraints.constrain(Size.infinite);
}
void paint(PaintingContext context, Offset offset) {
context.paintStatistics(optionsMask, offset, size);
}
}
......@@ -24,15 +24,15 @@ abstract class RenderToggleable extends RenderConstrainedBox {
: _value = value,
_onChanged = onChanged,
super(additionalConstraints: new BoxConstraints.tight(size)) {
_performance = new ValueAnimation<double>(
_performance = new ValuePerformance<double>(
variable: new AnimatedValue<double>(0.0, end: 1.0, curve: easeIn, reverseCurve: easeOut),
duration: _kToggleDuration,
progress: _value ? 1.0 : 0.0
)..addListener(markNeedsPaint);
}
ValueAnimation<double> get performance => _performance;
ValueAnimation<double> _performance;
ValuePerformance<double> get performance => _performance;
ValuePerformance<double> _performance;
double get position => _performance.value;
......@@ -51,7 +51,7 @@ abstract class RenderToggleable extends RenderConstrainedBox {
);
}
void detatch() {
void detach() {
_tap.dispose();
_tap = null;
super.detach();
......@@ -68,7 +68,7 @@ abstract class RenderToggleable extends RenderConstrainedBox {
if (value == _value)
return;
_value = value;
performance.play(value ? Direction.forward : Direction.reverse);
performance.play(value ? AnimationDirection.forward : AnimationDirection.reverse);
}
ValueChanged get onChanged => _onChanged;
......
......@@ -9,13 +9,13 @@ abstract class AnimatedComponent extends StatefulComponent {
const AnimatedComponent({ Key key, this.direction, this.duration }) : super(key: key);
final Duration duration;
final Direction direction;
final AnimationDirection direction;
}
abstract class AnimatedState<T extends AnimatedComponent> extends State<T> {
void initState() {
super.initState();
_performance = new AnimationPerformance(duration: config.duration);
_performance = new Performance(duration: config.duration);
performance.addStatusListener(_handleAnimationStatusChanged);
if (buildDependsOnPerformance) {
performance.addListener(() {
......@@ -34,13 +34,13 @@ abstract class AnimatedState<T extends AnimatedComponent> extends State<T> {
performance.play(config.direction);
}
AnimationPerformance get performance => _performance;
AnimationPerformance _performance;
Performance get performance => _performance;
Performance _performance;
void _handleAnimationStatusChanged(AnimationStatus status) {
if (status == AnimationStatus.completed)
void _handleAnimationStatusChanged(PerformanceStatus status) {
if (status == PerformanceStatus.completed)
handleCompleted();
else if (status == AnimationStatus.dismissed)
else if (status == PerformanceStatus.dismissed)
handleDismissed();
}
......
// 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/animation.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/framework.dart';
import 'package:vector_math/vector_math_64.dart';
class AnimatedBoxConstraintsValue extends AnimatedValue<BoxConstraints> {
AnimatedBoxConstraintsValue(BoxConstraints begin, { BoxConstraints end, Curve curve, Curve reverseCurve })
: super(begin, end: end, curve: curve, reverseCurve: reverseCurve);
BoxConstraints lerp(double t) => BoxConstraints.lerp(begin, end, t);
}
class AnimatedBoxDecorationValue extends AnimatedValue<BoxDecoration> {
AnimatedBoxDecorationValue(BoxDecoration begin, { BoxDecoration end, Curve curve, Curve reverseCurve })
: super(begin, end: end, curve: curve, reverseCurve: reverseCurve);
BoxDecoration lerp(double t) => BoxDecoration.lerp(begin, end, t);
}
class AnimatedEdgeDimsValue extends AnimatedValue<EdgeDims> {
AnimatedEdgeDimsValue(EdgeDims begin, { EdgeDims end, Curve curve, Curve reverseCurve })
: super(begin, end: end, curve: curve, reverseCurve: reverseCurve);
EdgeDims lerp(double t) => EdgeDims.lerp(begin, end, t);
}
class AnimatedMatrix4Value extends AnimatedValue<Matrix4> {
AnimatedMatrix4Value(Matrix4 begin, { Matrix4 end, Curve curve, Curve reverseCurve })
: super(begin, end: end, curve: curve, reverseCurve: reverseCurve);
Matrix4 lerp(double t) {
// TODO(mpcomplete): Animate the full matrix. Will animating the cells
// separately work?
Vector3 beginT = begin.getTranslation();
Vector3 endT = end.getTranslation();
Vector3 lerpT = beginT*(1.0-t) + endT*t;
return new Matrix4.identity()..translate(lerpT);
}
}
class AnimatedContainer extends StatefulComponent {
AnimatedContainer({
Key key,
this.child,
this.constraints,
this.decoration,
this.foregroundDecoration,
this.margin,
this.padding,
this.transform,
this.width,
this.height,
this.curve: linear,
this.duration
}) : super(key: key) {
assert(margin == null || margin.isNonNegative);
assert(padding == null || padding.isNonNegative);
assert(curve != null);
assert(duration != null);
}
final Widget child;
final BoxConstraints constraints;
final BoxDecoration decoration;
final BoxDecoration foregroundDecoration;
final EdgeDims margin;
final EdgeDims padding;
final Matrix4 transform;
final double width;
final double height;
final Curve curve;
final Duration duration;
AnimatedContainerState createState() => new AnimatedContainerState();
}
class AnimatedContainerState extends State<AnimatedContainer> {
AnimatedBoxConstraintsValue _constraints;
AnimatedBoxDecorationValue _decoration;
AnimatedBoxDecorationValue _foregroundDecoration;
AnimatedEdgeDimsValue _margin;
AnimatedEdgeDimsValue _padding;
AnimatedMatrix4Value _transform;
AnimatedValue<double> _width;
AnimatedValue<double> _height;
Performance _performance;
void initState() {
super.initState();
_performance = new Performance(duration: config.duration)
..timing = new AnimationTiming(curve: config.curve)
..addListener(_updateAllVariables);
_configAllVariables();
}
void didUpdateConfig(AnimatedContainer oldConfig) {
_performance
..duration = config.duration
..timing.curve = config.curve;
if (_configAllVariables()) {
_performance.progress = 0.0;
_performance.play();
}
}
void dispose() {
_performance.stop();
super.dispose();
}
void _updateVariable(Animatable variable) {
if (variable != null)
_performance.updateVariable(variable);
}
void _updateAllVariables() {
setState(() {
_updateVariable(_constraints);
_updateVariable(_constraints);
_updateVariable(_decoration);
_updateVariable(_foregroundDecoration);
_updateVariable(_margin);
_updateVariable(_padding);
_updateVariable(_transform);
_updateVariable(_width);
_updateVariable(_height);
});
}
bool _configVariable(AnimatedValue variable, dynamic targetValue) {
dynamic currentValue = variable.value;
variable.end = targetValue;
variable.begin = currentValue;
return currentValue != targetValue;
}
bool _configAllVariables() {
bool needsAnimation = false;
if (config.constraints != null) {
_constraints ??= new AnimatedBoxConstraintsValue(config.constraints);
if (_configVariable(_constraints, config.constraints))
needsAnimation = true;
} else {
_constraints = null;
}
if (config.decoration != null) {
_decoration ??= new AnimatedBoxDecorationValue(config.decoration);
if (_configVariable(_decoration, config.decoration))
needsAnimation = true;
} else {
_decoration = null;
}
if (config.foregroundDecoration != null) {
_foregroundDecoration ??= new AnimatedBoxDecorationValue(config.foregroundDecoration);
if (_configVariable(_foregroundDecoration, config.foregroundDecoration))
needsAnimation = true;
} else {
_foregroundDecoration = null;
}
if (config.margin != null) {
_margin ??= new AnimatedEdgeDimsValue(config.margin);
if (_configVariable(_margin, config.margin))
needsAnimation = true;
} else {
_margin = null;
}
if (config.padding != null) {
_padding ??= new AnimatedEdgeDimsValue(config.padding);
if (_configVariable(_padding, config.padding))
needsAnimation = true;
} else {
_padding = null;
}
if (config.transform != null) {
_transform ??= new AnimatedMatrix4Value(config.transform);
if (_configVariable(_transform, config.transform))
needsAnimation = true;
} else {
_transform = null;
}
if (config.width != null) {
_width ??= new AnimatedValue<double>(config.width);
if (_configVariable(_width, config.width))
needsAnimation = true;
} else {
_width = null;
}
if (config.height != null) {
_height ??= new AnimatedValue<double>(config.height);
if (_configVariable(_height, config.height))
needsAnimation = true;
} else {
_height = null;
}
return needsAnimation;
}
Widget build(BuildContext context) {
return new Container(
child: config.child,
constraints: _constraints?.value,
decoration: _decoration?.value,
foregroundDecoration: _foregroundDecoration?.value,
margin: _margin?.value,
padding: _padding?.value,
transform: _transform?.value,
width: _width?.value,
height: _height?.value
);
}
}
......@@ -32,7 +32,15 @@ class App extends StatefulComponent {
this.theme,
this.routes,
this.onGenerateRoute
}): super(key: key);
}) : super(key: key) {
assert(() {
'The "routes" argument to App() is required.';
'This might be a sign that you have not upgraded to our new Widgets framework.';
'For more details see: https://groups.google.com/forum/#!topic/flutter-dev/hcX3OvLws9c';
'...or look at our examples: https://github.com/flutter/engine/tree/master/examples';
return routes != null;
});
}
final String title;
final ThemeData theme;
......
......@@ -22,6 +22,7 @@ export 'package:sky/rendering.dart' show
FlexAlignItems,
FlexDirection,
FlexJustifyContent,
Matrix4,
Offset,
Paint,
Path,
......@@ -69,6 +70,33 @@ class ColorFilter extends OneChildRenderObjectWidget {
}
}
class ShaderMask extends OneChildRenderObjectWidget {
ShaderMask({
Key key,
this.shaderCallback,
this.transferMode: sky.TransferMode.modulate,
Widget child
}) : super(key: key, child: child) {
assert(shaderCallback != null);
assert(transferMode != null);
}
final ShaderCallback shaderCallback;
final sky.TransferMode transferMode;
RenderShaderMask createRenderObject() {
return new RenderShaderMask(
shaderCallback: shaderCallback,
transferMode: transferMode
);
}
void updateRenderObject(RenderShaderMask renderObject, ShaderMask oldWidget) {
renderObject.shaderCallback = shaderCallback;
renderObject.transferMode = transferMode;
}
}
class DecoratedBox extends OneChildRenderObjectWidget {
DecoratedBox({
Key key,
......@@ -392,11 +420,11 @@ class Container extends StatelessComponent {
this.constraints,
this.decoration,
this.foregroundDecoration,
this.width,
this.height,
this.margin,
this.padding,
this.transform
this.transform,
this.width,
this.height
}) : super(key: key) {
assert(margin == null || margin.isNonNegative);
assert(padding == null || padding.isNonNegative);
......@@ -684,6 +712,11 @@ class DefaultTextStyle extends InheritedWidget {
}
bool updateShouldNotify(DefaultTextStyle old) => style != old.style;
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
'$style'.split('\n').forEach(description.add);
}
}
class Text extends StatelessComponent {
......@@ -710,6 +743,13 @@ class Text extends StatelessComponent {
text = new StyledTextSpan(combinedStyle, [text]);
return new Paragraph(text: text);
}
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
description.add('"$data"');
if (style != null)
'$style'.split('\n').forEach(description.add);
}
}
class Image extends LeafRenderObjectWidget {
......@@ -882,7 +922,7 @@ class Listener extends OneChildRenderObjectWidget {
this.onPointerMove,
this.onPointerUp,
this.onPointerCancel
}): super(key: key, child: child);
}) : super(key: key, child: child);
final PointerEventListener onPointerDown;
final PointerEventListener onPointerMove;
......@@ -940,4 +980,4 @@ class KeyedSubtree extends StatelessComponent {
final Widget child;
Widget build(BuildContext context) => child;
}
\ No newline at end of file
}
......@@ -35,7 +35,7 @@ class WidgetFlutterBinding extends FlutterBinding {
);
}
void beginFrame(double timeStamp) {
void beginFrame(Duration timeStamp) {
buildDirtyElements();
super.beginFrame(timeStamp);
Element.finalizeTree();
......
......@@ -61,7 +61,7 @@ class _CheckboxWrapper extends LeafRenderObjectWidget {
this.onChanged,
this.uncheckedColor,
this.accentColor
}): super(key: key) {
}) : super(key: key) {
assert(uncheckedColor != null);
assert(accentColor != null);
}
......
......@@ -375,22 +375,20 @@ class YearPickerState extends ScrollableWidgetListState<YearPicker> {
for(int i = start; i < start + count; i++) {
int year = config.firstDate.year + i;
String label = year.toString();
Widget item = new GestureDetector(
Widget item = new InkWell(
key: new Key(label),
onTap: () {
DateTime result = new DateTime(year, config.selectedDate.month, config.selectedDate.day);
config.onChanged(result);
},
child: new InkWell(
child: new Container(
height: config.itemExtent,
decoration: year == config.selectedDate.year ? new BoxDecoration(
backgroundColor: Theme.of(context).primarySwatch[100],
shape: Shape.circle
) : null,
child: new Center(
child: new Text(label, style: style)
)
child: new Container(
height: config.itemExtent,
decoration: year == config.selectedDate.year ? new BoxDecoration(
backgroundColor: Theme.of(context).primarySwatch[100],
shape: Shape.circle
) : null,
child: new Center(
child: new Text(label, style: style)
)
)
);
......
......@@ -5,6 +5,7 @@
import 'dart:async';
import 'package:sky/animation.dart';
import 'package:sky/gestures.dart';
import 'package:sky/material.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/focus.dart';
......@@ -30,7 +31,7 @@ class Dialog extends StatelessComponent {
this.contentPadding,
this.actions,
this.onDismiss
}): super(key: key);
}) : super(key: key);
/// The (optional) title of the dialog is displayed in a large font at the top
/// of the dialog.
......@@ -52,7 +53,7 @@ class Dialog extends StatelessComponent {
final List<Widget> actions;
/// An (optional) callback that is called when the dialog is dismissed.
final Function onDismiss;
final GestureTapCallback onDismiss;
Color _getColor(BuildContext context) {
switch (Theme.of(context).brightness) {
......@@ -140,11 +141,11 @@ class DialogRoute extends Route {
Duration get transitionDuration => _kTransitionDuration;
bool get opaque => false;
Widget build(NavigatorState navigator, WatchableAnimationPerformance nextRoutePerformance) {
Widget build(NavigatorState navigator, PerformanceView nextRoutePerformance) {
return new FadeTransition(
performance: performance,
opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: easeOut),
child: builder(navigator, this)
child: builder(new RouteArguments(navigator: navigator, previousPerformance: this.performance, nextPerformance: nextRoutePerformance))
);
}
......@@ -158,11 +159,11 @@ Future showDialog(NavigatorState navigator, DialogBuilder builder) {
Completer completer = new Completer();
navigator.push(new DialogRoute(
completer: completer,
builder: (navigator, route) {
builder: (RouteArguments args) {
return new Focus(
key: new GlobalObjectKey(route),
key: new GlobalObjectKey(completer),
autofocus: true,
child: builder(navigator)
child: builder(args.navigator)
);
}
));
......
......@@ -12,7 +12,7 @@ import 'package:sky/src/widgets/gesture_detector.dart';
const Duration _kCardDismissFadeout = const Duration(milliseconds: 200);
const Duration _kCardDismissResize = const Duration(milliseconds: 300);
final Interval _kCardDismissResizeInterval = new Interval(0.4, 1.0);
const Curve _kCardDismissResizeCurve = const Interval(0.4, 1.0, curve: ease);
const double _kMinFlingVelocity = 700.0;
const double _kMinFlingVelocityDelta = 400.0;
const double _kFlingVelocityScale = 1.0 / 300.0;
......@@ -50,15 +50,15 @@ class Dismissable extends StatefulComponent {
class DismissableState extends State<Dismissable> {
void initState() {
super.initState();
_fadePerformance = new AnimationPerformance(duration: _kCardDismissFadeout);
_fadePerformance.addStatusListener((AnimationStatus status) {
if (status == AnimationStatus.completed)
_fadePerformance = new Performance(duration: _kCardDismissFadeout);
_fadePerformance.addStatusListener((PerformanceStatus status) {
if (status == PerformanceStatus.completed)
_handleFadeCompleted();
});
}
AnimationPerformance _fadePerformance;
AnimationPerformance _resizePerformance;
Performance _fadePerformance;
Performance _resizePerformance;
Size _size;
double _dragExtent = 0.0;
......@@ -97,16 +97,11 @@ class DismissableState extends State<Dismissable> {
assert(_resizePerformance == null);
setState(() {
_resizePerformance = new AnimationPerformance()
_resizePerformance = new Performance()
..duration = _kCardDismissResize
..addListener(_handleResizeProgressChanged);
_resizePerformance.play();
});
// Our squash curve (ease) does not return v=0.0 for t=0.0, so we
// technically resize on the first frame. To make sure this doesn't confuse
// any other widgets (like MixedViewport, which checks for this kind of
// thing), we report a resize straight away.
_maybeCallOnResized();
}
void _handleResizeProgressChanged() {
......@@ -226,13 +221,12 @@ class DismissableState extends State<Dismissable> {
Widget build(BuildContext context) {
if (_resizePerformance != null) {
// make sure you remove this widget once it's been dismissed!
assert(_resizePerformance.status == AnimationStatus.forward);
assert(_resizePerformance.status == PerformanceStatus.forward);
AnimatedValue<double> squashAxisExtent = new AnimatedValue<double>(
_directionIsYAxis ? _size.width : _size.height,
end: 0.0,
curve: ease,
interval: _kCardDismissResizeInterval
curve: _kCardDismissResizeCurve
);
return new SquashTransition(
......
......@@ -45,7 +45,7 @@ class Draggable extends StatefulComponent {
this.feedback,
this.feedbackOffset: Offset.zero,
this.dragAnchor: DragAnchor.child
}): super(key: key) {
}) : super(key: key) {
assert(navigator != null);
assert(child != null);
assert(feedback != null);
......@@ -258,7 +258,7 @@ class DragRoute extends Route {
bool get opaque => false;
Duration get transitionDuration => const Duration();
Widget build(NavigatorState navigator, WatchableAnimationPerformance nextRoutePerformance) {
Widget build(NavigatorState navigator, PerformanceView nextRoutePerformance) {
return new Positioned(
left: _lastOffset.dx,
top: _lastOffset.dy,
......
......@@ -2,17 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:sky/animation.dart';
import 'package:sky/material.dart';
import 'package:sky/src/widgets/animated_container.dart';
import 'package:sky/src/widgets/framework.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/gesture_detector.dart';
import 'package:sky/src/widgets/navigator.dart';
import 'package:sky/src/widgets/scrollable.dart';
import 'package:sky/src/widgets/theme.dart';
import 'package:sky/src/widgets/transitions.dart';
import 'package:sky/src/widgets/focus.dart';
// TODO(eseidel): Draw width should vary based on device size:
// http://www.google.com/design/spec/layout/structure.html#structure-side-nav
......@@ -35,22 +34,16 @@ const Duration _kThemeChangeDuration = const Duration(milliseconds: 200);
const Point _kOpenPosition = Point.origin;
const Point _kClosedPosition = const Point(-_kWidth, 0.0);
typedef void DrawerDismissedCallback();
class Drawer extends StatefulComponent {
Drawer({
Key key,
this.children,
this.showing: false,
this.level: 0,
this.onDismissed,
this.child,
this.level: 3,
this.navigator
}) : super(key: key);
final List<Widget> children;
final bool showing;
final Widget child;
final int level;
final DrawerDismissedCallback onDismissed;
final NavigatorState navigator;
DrawerState createState() => new DrawerState();
......@@ -59,57 +52,37 @@ class Drawer extends StatefulComponent {
class DrawerState extends State<Drawer> {
void initState() {
super.initState();
_performance = new AnimationPerformance(duration: _kBaseSettleDuration);
_performance.addStatusListener((AnimationStatus status) {
if (status == AnimationStatus.dismissed)
_handleDismissed();
});
// Use a spring force for animating the drawer. We can't use curves for
// this because we need a linear curve in order to track the user's finger
// while dragging.
_performance.attachedForce = kDefaultSpringForce;
if (config.navigator != null) {
// TODO(ianh): This is crazy. We should convert drawer to use a pattern like openDialog().
// https://github.com/domokit/sky_engine/pull/1186
scheduleMicrotask(() {
config.navigator.pushState(this, (_) => _performance.reverse());
_performance = new Performance(duration: _kBaseSettleDuration)
..addStatusListener((PerformanceStatus status) {
if (status == PerformanceStatus.dismissed)
config.navigator.pop();
});
}
_performance.play(_direction);
_open();
}
AnimationPerformance _performance;
Direction get _direction => config.showing ? Direction.forward : Direction.reverse;
void didUpdateConfig(Drawer oldConfig) {
if (config.showing != oldConfig.showing)
_performance.play(_direction);
}
Performance _performance;
Widget build(BuildContext context) {
var mask = new GestureDetector(
Widget mask = new GestureDetector(
onTap: _close,
child: new ColorTransition(
performance: _performance.view,
color: new AnimatedColorValue(Colors.transparent, end: const Color(0x7F000000)),
color: new AnimatedColorValue(Colors.transparent, end: Colors.black54),
child: new Container()
),
onTap: () {
_performance.reverse();
}
)
);
Widget content = new SlideTransition(
performance: _performance.view,
position: new AnimatedValue<Point>(_kClosedPosition, end: _kOpenPosition),
// TODO(abarth): Use AnimatedContainer
child: new Container(
// behavior: implicitlyAnimate(const Duration(milliseconds: 200)),
child: new AnimatedContainer(
curve: ease,
duration: _kThemeChangeDuration,
decoration: new BoxDecoration(
backgroundColor: Theme.of(context).canvasColor,
boxShadow: shadows[config.level]),
width: _kWidth,
child: new Block(config.children)
child: config.child
)
);
......@@ -117,33 +90,64 @@ class DrawerState extends State<Drawer> {
onHorizontalDragStart: _performance.stop,
onHorizontalDragUpdate: _handleDragUpdate,
onHorizontalDragEnd: _handleDragEnd,
child: new Stack([ mask, content ])
child: new Stack([
mask,
new Positioned(
top: 0.0,
left: 0.0,
bottom: 0.0,
child: content
)
])
);
}
void _handleDismissed() {
if (config.navigator != null &&
config.navigator.currentRoute is StateRoute &&
(config.navigator.currentRoute as StateRoute).owner == this) // TODO(ianh): remove cast once analyzer is cleverer
config.navigator.pop();
if (config.onDismissed != null)
config.onDismissed();
}
bool get _isMostlyClosed => _performance.progress < 0.5;
void _settle() { _isMostlyClosed ? _performance.reverse() : _performance.play(); }
void _handleDragUpdate(double delta) {
_performance.progress += delta / _kWidth;
}
void _open() {
_performance.fling(velocity: 1.0);
}
void _close() {
_performance.fling(velocity: -1.0);
}
void _handleDragEnd(Offset velocity) {
if (velocity.dx.abs() >= _kMinFlingVelocity) {
_performance.fling(velocity: velocity.dx * _kFlingVelocityScale);
} else if (_isMostlyClosed) {
_close();
} else {
_settle();
_open();
}
}
}
class DrawerRoute extends Route {
DrawerRoute({ this.child, this.level });
final Widget child;
final int level;
bool get opaque => false;
Widget build(NavigatorState navigator, PerformanceView nextRoutePerformance) {
return new Focus(
key: new GlobalObjectKey(this),
autofocus: true,
child: new Drawer(
child: child,
level: level,
navigator: navigator
)
);
}
}
void showDrawer({ NavigatorState navigator, Widget child, int level: 3 }) {
navigator.push(new DrawerRoute(child: child, level: level));
}
......@@ -10,7 +10,6 @@ import 'package:sky/painting.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/button_state.dart';
import 'package:sky/src/widgets/framework.dart';
import 'package:sky/src/widgets/gesture_detector.dart';
import 'package:sky/src/widgets/icon.dart';
import 'package:sky/src/widgets/ink_well.dart';
import 'package:sky/src/widgets/theme.dart';
......@@ -21,7 +20,7 @@ class DrawerItem extends StatefulComponent {
final String icon;
final Widget child;
final GestureTapListener onPressed;
final GestureTapCallback onPressed;
final bool selected;
DrawerItemState createState() => new DrawerItemState();
......@@ -76,14 +75,12 @@ class DrawerItemState extends ButtonState<DrawerItem> {
)
);
return new GestureDetector(
onTap: config.onPressed,
child: new Container(
height: 48.0,
decoration: new BoxDecoration(backgroundColor: _getBackgroundColor(themeData)),
child: new InkWell(
child: new Row(flexChildren)
)
return new Container(
height: 48.0,
decoration: new BoxDecoration(backgroundColor: _getBackgroundColor(themeData)),
child: new InkWell(
onTap: config.onPressed,
child: new Row(flexChildren)
)
);
}
......
......@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:sky/gestures.dart';
import 'package:sky/material.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/framework.dart';
......@@ -13,7 +14,7 @@ class FlatButton extends MaterialButton {
Key key,
Widget child,
bool enabled: true,
Function onPressed
GestureTapCallback onPressed
}) : super(key: key,
child: child,
enabled: enabled,
......
......@@ -6,7 +6,6 @@ import 'package:sky/gestures.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/button_state.dart';
import 'package:sky/src/widgets/framework.dart';
import 'package:sky/src/widgets/gesture_detector.dart';
import 'package:sky/src/widgets/icon.dart';
import 'package:sky/src/widgets/ink_well.dart';
import 'package:sky/src/widgets/material.dart';
......@@ -26,7 +25,7 @@ class FloatingActionButton extends StatefulComponent {
final Widget child;
final Color backgroundColor;
final GestureTapListener onPressed;
final GestureTapCallback onPressed;
FloatingActionButtonState createState() => new FloatingActionButtonState();
}
......@@ -46,17 +45,15 @@ class FloatingActionButtonState extends ButtonState<FloatingActionButton> {
type: MaterialType.circle,
level: highlight ? 3 : 2,
child: new ClipOval(
child: new GestureDetector(
onTap: config.onPressed,
child: new Container(
width: _kSize,
height: _kSize,
child: new InkWell(
child: new Center(
child: new IconTheme(
data: new IconThemeData(color: iconThemeColor),
child: config.child
)
child: new Container(
width: _kSize,
height: _kSize,
child: new InkWell(
onTap: config.onPressed,
child: new Center(
child: new IconTheme(
data: new IconThemeData(color: iconThemeColor),
child: config.child
)
)
)
......
......@@ -181,7 +181,16 @@ abstract class Widget {
/// Inflates this configuration to a concrete instance.
Element createElement();
String toString() => '$runtimeType';
String toString() {
final String name = key == null ? '$runtimeType' : '$runtimeType-$key';
final List<String> data = <String>[];
debugFillDescription(data);
if (data.isEmpty)
return 'name';
return 'name(${data.join("; ")})';
}
void debugFillDescription(List<String> description) { }
}
/// RenderObjectWidgets provide the configuration for [RenderObjectElement]s,
......@@ -482,9 +491,11 @@ final _InactiveElements _inactiveElements = new _InactiveElements();
typedef void ElementVisitor(Element element);
abstract class BuildContext {
InheritedWidget inheritedWidgetOfType(Type targetType);
Widget get widget;
RenderObject findRenderObject();
InheritedWidget inheritedWidgetOfType(Type targetType);
void visitAncestorElements(bool visitor(Element element));
void visitChildElements(void visitor(Element element));
}
/// Elements are the instantiations of Widget configurations.
......@@ -536,6 +547,13 @@ abstract class Element<T extends Widget> implements BuildContext {
/// Calls the argument for each child. Must be overridden by subclasses that support having children.
void visitChildren(ElementVisitor visitor) { }
/// Wrapper around visitChildren for BuildContext.
void visitChildElements(void visitor(Element element)) {
// don't allow visitChildElements() during build, since children aren't necessarily built yet
assert(BuildableElement._debugStateLockLevel == 0);
visitChildren(visitor);
}
bool detachChild(Element child) => false;
/// This method is the core of the system.
......@@ -782,6 +800,8 @@ abstract class Element<T extends Widget> implements BuildContext {
description.add('no depth');
if (widget == null)
description.add('no widget');
else
widget.debugFillDescription(description);
}
String toStringDeep([String prefixLineOne = '', String prefixOtherLines = '']) {
......@@ -948,11 +968,11 @@ abstract class BuildableElement<T extends Widget> extends Element<T> {
/// Instantiation of StatelessComponent widgets.
class StatelessComponentElement<T extends StatelessComponent> extends BuildableElement<T> {
StatelessComponentElement(StatelessComponent widget) : super(widget) {
StatelessComponentElement(T widget) : super(widget) {
_builder = widget.build;
}
void update(StatelessComponent newWidget) {
void update(T newWidget) {
super.update(newWidget);
assert(widget == newWidget);
_builder = widget.build;
......@@ -962,10 +982,10 @@ class StatelessComponentElement<T extends StatelessComponent> extends BuildableE
}
/// Instantiation of StatefulComponent widgets.
class StatefulComponentElement extends BuildableElement<StatefulComponent> {
StatefulComponentElement(StatefulComponent widget)
class StatefulComponentElement<T extends StatefulComponent, U extends State<T>> extends BuildableElement<T> {
StatefulComponentElement(T widget)
: _state = widget.createState(), super(widget) {
assert(_state._debugTypesAreRight(widget));
assert(_state._debugTypesAreRight(widget)); // can't use T and U, since normally we don't actually set those
assert(_state._element == null);
_state._element = this;
assert(_builder == null);
......@@ -988,10 +1008,10 @@ class StatefulComponentElement extends BuildableElement<StatefulComponent> {
assert(() { _state._debugLifecycleState = _StateLifecycle.ready; return true; });
}
State get state => _state;
State _state;
U get state => _state;
U _state;
void update(StatefulComponent newWidget) {
void update(T newWidget) {
super.update(newWidget);
assert(widget == newWidget);
StatefulComponent oldConfig = _state._config;
......
......@@ -15,6 +15,8 @@ class GestureDetector extends StatefulComponent {
this.child,
this.onTap,
this.onDoubleTap,
this.onTapDown,
this.onTapCancel,
this.onShowPress,
this.onLongPress,
this.onVerticalDragStart,
......@@ -32,10 +34,14 @@ class GestureDetector extends StatefulComponent {
}) : super(key: key);
final Widget child;
final GestureTapListener onTap;
final GestureTapCallback onTap;
final GestureTapCallback onTapDown;
final GestureTapCallback onTapCancel;
final GestureTapListener onDoubleTap;
final GestureShowPressListener onShowPress;
final GestureLongPressListener onLongPress;
final GestureShowPressCallback onShowPress;
final GestureLongPressCallback onLongPress;
final GestureDragStartCallback onVerticalDragStart;
final GestureDragUpdateCallback onVerticalDragUpdate;
......@@ -57,69 +63,24 @@ class GestureDetector extends StatefulComponent {
}
class GestureDetectorState extends State<GestureDetector> {
void initState() {
super.initState();
didUpdateConfig(null);
}
final PointerRouter _router = FlutterBinding.instance.pointerRouter;
TapGestureRecognizer _tap;
TapGestureRecognizer _ensureTap() {
if (_tap == null)
_tap = new TapGestureRecognizer(router: _router);
return _tap;
}
DoubleTapGestureRecognizer _doubleTap;
DoubleTapGestureRecognizer _ensureDoubleTap() {
if (_doubleTap == null)
_doubleTap = new DoubleTapGestureRecognizer(router: _router);
return _doubleTap;
}
ShowPressGestureRecognizer _showPress;
ShowPressGestureRecognizer _ensureShowPress() {
if (_showPress == null)
_showPress = new ShowPressGestureRecognizer(router: _router);
return _showPress;
}
LongPressGestureRecognizer _longPress;
LongPressGestureRecognizer _ensureLongPress() {
if (_longPress == null)
_longPress = new LongPressGestureRecognizer(router: _router);
return _longPress;
}
VerticalDragGestureRecognizer _verticalDrag;
VerticalDragGestureRecognizer _ensureVerticalDrag() {
if (_verticalDrag == null)
_verticalDrag = new VerticalDragGestureRecognizer(router: _router);
return _verticalDrag;
}
HorizontalDragGestureRecognizer _horizontalDrag;
HorizontalDragGestureRecognizer _ensureHorizontalDrag() {
if (_horizontalDrag == null)
_horizontalDrag = new HorizontalDragGestureRecognizer(router: _router);
return _horizontalDrag;
}
PanGestureRecognizer _pan;
PanGestureRecognizer _ensurePan() {
assert(_scale == null); // Scale is a superset of pan; just use scale
if (_pan == null)
_pan = new PanGestureRecognizer(router: _router);
return _pan;
ScaleGestureRecognizer _scale;
void initState() {
super.initState();
_syncAll();
}
ScaleGestureRecognizer _scale;
ScaleGestureRecognizer _ensureScale() {
assert(_pan == null); // Scale is a superset of pan; just use scale
if (_scale == null)
_scale = new ScaleGestureRecognizer(router: _router);
return _scale;
void didUpdateConfig(GestureDetector oldConfig) {
_syncAll();
}
void dispose() {
......@@ -134,7 +95,7 @@ class GestureDetectorState extends State<GestureDetector> {
super.dispose();
}
void didUpdateConfig(GestureDetector oldConfig) {
void _syncAll() {
_syncTap();
_syncDoubleTap();
_syncShowPress();
......@@ -146,10 +107,15 @@ class GestureDetectorState extends State<GestureDetector> {
}
void _syncTap() {
if (config.onTap == null)
if (config.onTap == null && config.onTapDown == null && config.onTapCancel == null) {
_tap = _ensureDisposed(_tap);
else
_ensureTap().onTap = config.onTap;
} else {
_tap ??= new TapGestureRecognizer(router: _router);
_tap
..onTap = config.onTap
..onTapDown = config.onTapDown
..onTapCancel = config.onTapCancel;
}
}
void _syncDoubleTap() {
......@@ -160,24 +126,29 @@ class GestureDetectorState extends State<GestureDetector> {
}
void _syncShowPress() {
if (config.onShowPress == null)
if (config.onShowPress == null) {
_showPress = _ensureDisposed(_showPress);
else
_ensureShowPress().onShowPress = config.onShowPress;
} else {
_showPress ??= new ShowPressGestureRecognizer(router: _router);
_showPress.onShowPress = config.onShowPress;
}
}
void _syncLongPress() {
if (config.onLongPress == null)
if (config.onLongPress == null) {
_longPress = _ensureDisposed(_longPress);
else
_ensureLongPress().onLongPress = config.onLongPress;
} else {
_longPress ??= new LongPressGestureRecognizer(router: _router);
_longPress.onLongPress = config.onLongPress;
}
}
void _syncVerticalDrag() {
if (config.onVerticalDragStart == null && config.onVerticalDragUpdate == null && config.onVerticalDragEnd == null) {
_verticalDrag = _ensureDisposed(_verticalDrag);
} else {
_ensureVerticalDrag()
_verticalDrag ??= new VerticalDragGestureRecognizer(router: _router);
_verticalDrag
..onStart = config.onVerticalDragStart
..onUpdate = config.onVerticalDragUpdate
..onEnd = config.onVerticalDragEnd;
......@@ -188,7 +159,8 @@ class GestureDetectorState extends State<GestureDetector> {
if (config.onHorizontalDragStart == null && config.onHorizontalDragUpdate == null && config.onHorizontalDragEnd == null) {
_horizontalDrag = _ensureDisposed(_horizontalDrag);
} else {
_ensureHorizontalDrag()
_horizontalDrag ??= new HorizontalDragGestureRecognizer(router: _router);
_horizontalDrag
..onStart = config.onHorizontalDragStart
..onUpdate = config.onHorizontalDragUpdate
..onEnd = config.onHorizontalDragEnd;
......@@ -199,7 +171,9 @@ class GestureDetectorState extends State<GestureDetector> {
if (config.onPanStart == null && config.onPanUpdate == null && config.onPanEnd == null) {
_pan = _ensureDisposed(_pan);
} else {
_ensurePan()
assert(_scale == null); // Scale is a superset of pan; just use scale
_pan ??= new PanGestureRecognizer(router: _router);
_pan
..onStart = config.onPanStart
..onUpdate = config.onPanUpdate
..onEnd = config.onPanEnd;
......@@ -208,9 +182,11 @@ class GestureDetectorState extends State<GestureDetector> {
void _syncScale() {
if (config.onScaleStart == null && config.onScaleUpdate == null && config.onScaleEnd == null) {
_scale = _ensureDisposed(_pan);
_scale = _ensureDisposed(_scale);
} else {
_ensureScale()
assert(_pan == null); // Scale is a superset of pan; just use scale
_scale ??= new ScaleGestureRecognizer(router: _router);
_scale
..onStart = config.onScaleStart
..onUpdate = config.onScaleUpdate
..onEnd = config.onScaleEnd;
......
......@@ -4,6 +4,7 @@
import 'dart:sky' as sky;
import 'package:sky/gestures.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/icon.dart';
import 'package:sky/src/widgets/framework.dart';
......@@ -13,8 +14,8 @@ class IconButton extends StatelessComponent {
const IconButton({ Key key, this.icon, this.onPressed, this.color }) : super(key: key);
final String icon;
final Function onPressed;
final Color color;
final GestureTapCallback onPressed;
Widget build(BuildContext context) {
Widget child = new Icon(type: icon, size: 24);
......
......@@ -2,16 +2,18 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:math' as math;
import 'dart:sky' as sky;
import 'package:sky/animation.dart';
import 'package:sky/gestures.dart';
import 'package:sky/rendering.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/framework.dart';
const int _kSplashInitialOpacity = 0x30;
const double _kSplashCancelledVelocity = 0.7;
const double _kSplashCanceledVelocity = 0.7;
const double _kSplashConfirmedVelocity = 0.7;
const double _kSplashInitialSize = 0.0;
const double _kSplashUnconfirmedVelocity = 0.2;
......@@ -25,40 +27,60 @@ double _getSplashTargetSize(Size bounds, Point position) {
}
class InkSplash {
InkSplash(this.pointer, this.position, this.well) {
InkSplash(this.position, this.well) {
_targetRadius = _getSplashTargetSize(well.size, position);
_radius = new AnimatedValue<double>(
_kSplashInitialSize, end: _targetRadius, curve: easeOut);
_performance = new ValueAnimation<double>(
_performance = new ValuePerformance<double>(
variable: _radius,
duration: new Duration(milliseconds: (_targetRadius / _kSplashUnconfirmedVelocity).floor())
)..addListener(_handleRadiusChange)
..play();
)..addListener(_handleRadiusChange);
// Wait kTapTimeout to avoid creating tiny splashes during scrolls.
_startTimer = new Timer(kTapTimeout, _play);
}
final int pointer;
final Point position;
final RenderInkWell well;
double _targetRadius;
double _pinnedRadius;
AnimatedValue<double> _radius;
AnimationPerformance _performance;
Performance _performance;
Timer _startTimer;
bool _cancelStartTimer() {
if (_startTimer != null) {
_startTimer.cancel();
_startTimer = null;
return true;
}
return false;
}
void _play() {
_cancelStartTimer();
_performance.play();
}
void _updateVelocity(double velocity) {
int duration = (_targetRadius / velocity).floor();
_performance
..duration = new Duration(milliseconds: duration)
..play();
_performance.duration = new Duration(milliseconds: duration);
_play();
}
void confirm() {
if (_cancelStartTimer())
return;
_updateVelocity(_kSplashConfirmedVelocity);
_pinnedRadius = null;
}
void cancel() {
_updateVelocity(_kSplashCancelledVelocity);
if (_cancelStartTimer())
return;
_updateVelocity(_kSplashCanceledVelocity);
_pinnedRadius = _radius.value;
}
......@@ -77,38 +99,95 @@ class InkSplash {
}
class RenderInkWell extends RenderProxyBox {
RenderInkWell({ RenderBox child }) : super(child);
RenderInkWell({
RenderBox child,
GestureTapCallback onTap,
GestureLongPressCallback onLongPress
}) : super(child) {
this.onTap = onTap;
this.onLongPress = onLongPress;
}
GestureTapCallback get onTap => _onTap;
GestureTapCallback _onTap;
void set onTap (GestureTapCallback value) {
_onTap = value;
_syncTapRecognizer();
}
GestureTapCallback get onLongPress => _onLongPress;
GestureTapCallback _onLongPress;
void set onLongPress (GestureTapCallback value) {
_onLongPress = value;
_syncLongPressRecognizer();
}
final List<InkSplash> _splashes = new List<InkSplash>();
TapGestureRecognizer _tap;
LongPressGestureRecognizer _longPress;
void handleEvent(sky.Event event, BoxHitTestEntry entry) {
// TODO(abarth): We should trigger these effects based on gestures.
// https://github.com/flutter/engine/issues/1271
if (event is sky.PointerEvent) {
switch (event.type) {
case 'pointerdown':
_startSplash(event.pointer, entry.localPosition);
break;
case 'pointerup':
_confirmSplash(event.pointer);
break;
}
if (event.type == 'pointerdown' && (_tap != null || _longPress != null)) {
_tap?.addPointer(event);
_longPress?.addPointer(event);
_splashes.add(new InkSplash(entry.localPosition, this));
}
}
void _startSplash(int pointer, Point position) {
_splashes.add(new InkSplash(pointer, position, this));
markNeedsPaint();
void attach() {
super.attach();
_syncTapRecognizer();
_syncLongPressRecognizer();
}
void _forEachSplash(int pointer, Function callback) {
_splashes.where((splash) => splash.pointer == pointer)
.forEach(callback);
void detach() {
_disposeTapRecognizer();
_disposeLongPressRecognizer();
super.detach();
}
void _confirmSplash(int pointer) {
_forEachSplash(pointer, (splash) { splash.confirm(); });
markNeedsPaint();
void _syncTapRecognizer() {
if (onTap == null) {
_disposeTapRecognizer();
} else {
_tap ??= new TapGestureRecognizer(router: FlutterBinding.instance.pointerRouter)
..onTap = _handleTap
..onTapCancel = _handleTapCancel;
}
}
void _disposeTapRecognizer() {
_tap?.dispose();
_tap = null;
}
void _syncLongPressRecognizer() {
if (onLongPress == null) {
_disposeLongPressRecognizer();
} else {
_longPress ??= new LongPressGestureRecognizer(router: FlutterBinding.instance.pointerRouter)
..onLongPress = _handleLongPress;
}
}
void _disposeLongPressRecognizer() {
_longPress?.dispose();
_longPress = null;
}
void _handleTap() {
_splashes.last?.confirm();
onTap();
}
void _handleTapCancel() {
_splashes.last?.cancel();
}
void _handleLongPress() {
_splashes.last?.confirm();
onLongPress();
}
void paint(PaintingContext context, Offset offset) {
......@@ -126,6 +205,20 @@ class RenderInkWell extends RenderProxyBox {
}
class InkWell extends OneChildRenderObjectWidget {
InkWell({ Key key, Widget child }) : super(key: key, child: child);
RenderInkWell createRenderObject() => new RenderInkWell();
InkWell({
Key key,
Widget child,
this.onTap,
this.onLongPress
}) : super(key: key, child: child);
final GestureTapCallback onTap;
final GestureLongPressCallback onLongPress;
RenderInkWell createRenderObject() => new RenderInkWell(onTap: onTap, onLongPress: onLongPress);
void updateRenderObject(RenderInkWell renderObject, InkWell oldWidget) {
renderObject.onTap = onTap;
renderObject.onLongPress = onLongPress;
}
}
......@@ -28,7 +28,7 @@ class Input extends Scrollable {
this.placeholder,
this.onChanged,
this.keyboardType: KeyboardType.TEXT
}): super(
}) : super(
key: key,
initialScrollOffset: 0.0,
scrollDirection: ScrollDirection.horizontal
......
......@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:sky/animation.dart';
import 'package:sky/painting.dart';
import 'package:sky/material.dart';
import 'package:sky/src/widgets/animated_container.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/framework.dart';
import 'package:sky/src/widgets/theme.dart';
......@@ -61,10 +63,11 @@ class Material extends StatelessComponent {
);
}
}
// TODO(abarth): This should use AnimatedContainer.
return new DefaultTextStyle(
style: Theme.of(context).text.body1,
child: new Container(
child: new AnimatedContainer(
curve: ease,
duration: const Duration(milliseconds: 200),
decoration: new BoxDecoration(
backgroundColor: getBackgroundColor(context),
borderRadius: edges[type],
......
......@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:sky/gestures.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/button_state.dart';
import 'package:sky/src/widgets/framework.dart';
import 'package:sky/src/widgets/gesture_detector.dart';
import 'package:sky/src/widgets/ink_well.dart';
import 'package:sky/src/widgets/material.dart';
......@@ -22,7 +22,7 @@ abstract class MaterialButton extends StatefulComponent {
final Widget child;
final bool enabled;
final Function onPressed;
final GestureTapCallback onPressed;
}
abstract class MaterialButtonState<T extends MaterialButton> extends ButtonState<T> {
......@@ -37,17 +37,17 @@ abstract class MaterialButtonState<T extends MaterialButton> extends ButtonState
child: config.child // TODO(ianh): figure out a way to compell the child to have gray text when disabled...
)
);
return new GestureDetector(
onTap: config.enabled ? config.onPressed : null,
child: new Container(
height: 36.0,
constraints: new BoxConstraints(minWidth: 88.0),
margin: new EdgeDims.all(8.0),
child: new Material(
type: MaterialType.button,
child: config.enabled ? new InkWell(child: contents) : contents,
level: level,
color: getColor(context)
return new Container(
height: 36.0,
constraints: new BoxConstraints(minWidth: 88.0),
margin: new EdgeDims.all(8.0),
child: new Material(
type: MaterialType.button,
level: level,
color: getColor(context),
child: new InkWell(
onTap: config.enabled ? config.onPressed : null,
child: contents
)
)
);
......
......@@ -22,7 +22,7 @@ class MixedViewport extends RenderObjectWidget {
this.token,
this.onExtentsUpdate,
this.onInvalidatorAvailable
}): super(key: key);
}) : super(key: key);
final double startOffset;
final ScrollDirection direction;
......
......@@ -8,7 +8,14 @@ import 'package:sky/src/widgets/focus.dart';
import 'package:sky/src/widgets/framework.dart';
import 'package:sky/src/widgets/transitions.dart';
typedef Widget RouteBuilder(NavigatorState navigator, Route route);
class RouteArguments {
const RouteArguments({ this.navigator, this.previousPerformance, this.nextPerformance });
final NavigatorState navigator;
final PerformanceView previousPerformance;
final PerformanceView nextPerformance;
}
typedef Widget RouteBuilder(RouteArguments args);
typedef RouteBuilder RouteGenerator(String name);
typedef void StateRouteCallback(StateRoute route);
typedef void NotificationCallback();
......@@ -118,7 +125,7 @@ class NavigatorState extends State<Navigator> {
Widget build(BuildContext context) {
List<Widget> visibleRoutes = new List<Widget>();
bool alreadyInsertModalBarrier = false;
WatchableAnimationPerformance nextPerformance;
PerformanceView nextPerformance;
for (int i = _history.length-1; i >= 0; i -= 1) {
Route route = _history[i];
if (!route.hasContent) {
......@@ -126,11 +133,16 @@ class NavigatorState extends State<Navigator> {
continue;
}
route.ensurePerformance(
direction: (i <= _currentPosition) ? Direction.forward : Direction.reverse
direction: (i <= _currentPosition) ? AnimationDirection.forward : AnimationDirection.reverse
);
route._onDismissed = () {
assert(_history.contains(route));
if (_history.lastIndexOf(route) <= _currentPosition)
popRoute(route);
};
route._onRemoveRoute = () {
assert(_history.contains(route));
setState(() {
assert(_history.contains(route));
_history.remove(route);
});
};
......@@ -154,33 +166,39 @@ class NavigatorState extends State<Navigator> {
}
return new Focus(child: new Stack(visibleRoutes.reversed.toList()));
}
}
abstract class Route {
WatchableAnimationPerformance get performance => _performance?.view;
AnimationPerformance _performance;
PerformanceView get performance => _performance?.view;
Performance _performance;
NotificationCallback _onDismissed;
NotificationCallback _onRemoveRoute;
AnimationPerformance createPerformance() {
Performance createPerformance() {
Duration duration = transitionDuration;
if (duration > Duration.ZERO) {
return new AnimationPerformance(duration: duration)
..addStatusListener((AnimationStatus status) {
if (status == AnimationStatus.dismissed && _onDismissed != null)
_onDismissed();
return new Performance(duration: duration)
..addStatusListener((PerformanceStatus status) {
if (status == PerformanceStatus.dismissed) {
if (_onDismissed != null)
_onDismissed();
if (_onRemoveRoute != null)
_onRemoveRoute();
}
});
}
return null;
}
void ensurePerformance({ Direction direction }) {
void ensurePerformance({ AnimationDirection direction }) {
assert(direction != null);
if (_performance == null)
_performance = createPerformance();
if (_performance != null) {
AnimationStatus desiredStatus = direction == Direction.forward ? AnimationStatus.forward : AnimationStatus.reverse;
PerformanceStatus desiredStatus = direction == AnimationDirection.forward ? PerformanceStatus.forward : PerformanceStatus.reverse;
if (_performance.status != desiredStatus)
_performance.play(direction);
}
......@@ -236,17 +254,17 @@ abstract class Route {
/// cover the entire application surface or are in any way semi-transparent.
bool get opaque => false;
/// If this is set to a non-zero [Duration], then an [AnimationPerformance]
/// If this is set to a non-zero [Duration], then an [Performance]
/// object, available via the performance field, will be created when the
/// route is first built, using the duration described here.
Duration get transitionDuration => Duration.ZERO;
bool get isActuallyOpaque => (performance == null || _performance.isCompleted) && opaque;
Widget build(NavigatorState navigator, WatchableAnimationPerformance nextRoutePerformance);
Widget build(NavigatorState navigator, PerformanceView nextRoutePerformance);
void didPop([dynamic result]) {
if (performance == null && _onDismissed != null)
_onDismissed();
if (performance == null && _onRemoveRoute != null)
_onRemoveRoute();
}
String toString() => '$runtimeType()';
......@@ -263,7 +281,7 @@ class PageRoute extends Route {
bool get opaque => true;
Duration get transitionDuration => _kTransitionDuration;
Widget build(NavigatorState navigator, WatchableAnimationPerformance nextRoutePerformance) {
Widget build(NavigatorState navigator, PerformanceView nextRoutePerformance) {
// TODO(jackson): Hit testing should ignore transform
// TODO(jackson): Block input unless content is interactive
return new SlideTransition(
......@@ -272,7 +290,7 @@ class PageRoute extends Route {
child: new FadeTransition(
performance: performance,
opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: easeOut),
child: builder(navigator, this)
child: builder(new RouteArguments(navigator: navigator, previousPerformance: this.performance, nextPerformance: nextRoutePerformance))
)
);
}
......@@ -296,5 +314,5 @@ class StateRoute extends Route {
super.didPop(result);
}
Widget build(NavigatorState navigator, WatchableAnimationPerformance nextRoutePerformance) => null;
Widget build(NavigatorState navigator, PerformanceView nextRoutePerformance) => null;
}
......@@ -11,7 +11,7 @@ import 'package:sky/painting.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/focus.dart';
import 'package:sky/src/widgets/framework.dart';
import 'package:sky/src/widgets/gesture_detector.dart';
import 'package:sky/src/widgets/ink_well.dart';
import 'package:sky/src/widgets/navigator.dart';
import 'package:sky/src/widgets/popup_menu_item.dart';
import 'package:sky/src/widgets/scrollable.dart';
......@@ -29,7 +29,7 @@ const double _kMenuVerticalPadding = 8.0;
typedef List<PopupMenuItem> PopupMenuItemsBuilder(NavigatorState navigator);
class PopupMenu extends StatefulComponent {
class PopupMenu extends StatelessComponent {
PopupMenu({
Key key,
this.items,
......@@ -44,78 +44,48 @@ class PopupMenu extends StatefulComponent {
final List<PopupMenuItem> items;
final int level;
final NavigatorState navigator;
final WatchableAnimationPerformance performance;
PopupMenuState createState() => new PopupMenuState();
}
class PopupMenuState extends State<PopupMenu> {
void initState() {
super.initState();
config.performance.addListener(_performanceChanged);
}
void didUpdateConfig(PopupMenu oldConfig) {
if (config.performance != oldConfig.performance) {
oldConfig.performance.removeListener(_performanceChanged);
config.performance.addListener(_performanceChanged);
}
}
void dispose() {
config.performance.removeListener(_performanceChanged);
super.dispose();
}
void _performanceChanged() {
setState(() {
// the performance changed, and our state is tied up with the performance
});
}
BoxPainter _painter;
void _updateBoxPainter(BoxDecoration decoration) {
if (_painter == null || _painter.decoration != decoration)
_painter = new BoxPainter(decoration);
}
final PerformanceView performance;
Widget build(BuildContext context) {
_updateBoxPainter(new BoxDecoration(
final BoxPainter painter = new BoxPainter(new BoxDecoration(
backgroundColor: Theme.of(context).canvasColor,
borderRadius: 2.0,
boxShadow: shadows[config.level]
boxShadow: shadows[level]
));
double unit = 1.0 / (config.items.length + 1.5); // 1.0 for the width and 0.5 for the last item's fade.
double unit = 1.0 / (items.length + 1.5); // 1.0 for the width and 0.5 for the last item's fade.
List<Widget> children = [];
for (int i = 0; i < config.items.length; ++i) {
for (int i = 0; i < items.length; ++i) {
double start = (i + 1) * unit;
double end = (start + 1.5 * unit).clamp(0.0, 1.0);
children.add(new FadeTransition(
performance: config.performance,
opacity: new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(start, end)),
child: new GestureDetector(
onTap: () { config.navigator.pop(config.items[i].value); },
child: config.items[i]
performance: performance,
opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: new Interval(start, end)),
child: new InkWell(
onTap: () { navigator.pop(items[i].value); },
child: items[i]
))
);
}
final width = new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(0.0, unit));
final height = new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(0.0, unit * config.items.length));
final width = new AnimatedValue<double>(0.0, end: 1.0, curve: new Interval(0.0, unit));
final height = new AnimatedValue<double>(0.0, end: 1.0, curve: new Interval(0.0, unit * items.length));
return new FadeTransition(
performance: config.performance,
opacity: new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(0.0, 1.0 / 3.0)),
performance: performance,
opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: new Interval(0.0, 1.0 / 3.0)),
child: new Container(
margin: new EdgeDims.all(_kMenuMargin),
child: new BuilderTransition(
performance: config.performance,
performance: performance,
variables: [width, height],
builder: (BuildContext context) {
return new CustomPaint(
callback: (sky.Canvas canvas, Size size) {
double widthValue = width.value * size.width;
double heightValue = height.value * size.height;
_painter.paint(canvas, new Rect.fromLTWH(size.width - widthValue, 0.0, widthValue, heightValue));
painter.paint(canvas, new Rect.fromLTWH(size.width - widthValue, 0.0, widthValue, heightValue));
},
child: new ConstrainedBox(
constraints: new BoxConstraints(
......@@ -159,10 +129,10 @@ class MenuRoute extends Route {
final PopupMenuItemsBuilder builder;
final int level;
AnimationPerformance createPerformance() {
AnimationPerformance result = super.createPerformance();
Performance createPerformance() {
Performance result = super.createPerformance();
AnimationTiming timing = new AnimationTiming();
timing.reverseInterval = new Interval(0.0, _kMenuCloseIntervalEnd);
timing.reverseCurve = new Interval(0.0, _kMenuCloseIntervalEnd);
result.timing = timing;
return result;
}
......@@ -172,7 +142,7 @@ class MenuRoute extends Route {
bool get opaque => false;
Duration get transitionDuration => _kMenuDuration;
Widget build(NavigatorState navigator, WatchableAnimationPerformance nextRoutePerformance) {
Widget build(NavigatorState navigator, PerformanceView nextRoutePerformance) {
return new Positioned(
top: position?.top,
right: position?.right,
......
......@@ -4,7 +4,6 @@
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/framework.dart';
import 'package:sky/src/widgets/ink_well.dart';
import 'package:sky/src/widgets/theme.dart';
const double _kMenuItemHeight = 48.0;
......@@ -21,15 +20,13 @@ class PopupMenuItem extends StatelessComponent {
final dynamic value;
Widget build(BuildContext context) {
return new InkWell(
child: new Container(
height: _kMenuItemHeight,
child: new DefaultTextStyle(
style: Theme.of(context).text.subhead,
child: new Baseline(
baseline: _kMenuItemHeight - _kBaselineOffsetFromBottom,
child: child
)
return new Container(
height: _kMenuItemHeight,
child: new DefaultTextStyle(
style: Theme.of(context).text.subhead,
child: new Baseline(
baseline: _kMenuItemHeight - _kBaselineOffsetFromBottom,
child: child
)
)
);
......
......@@ -36,16 +36,16 @@ abstract class ProgressIndicator extends StatefulComponent {
class ProgressIndicatorState extends State<ProgressIndicator> {
ValueAnimation<double> _performance;
ValuePerformance<double> _performance;
void initState() {
super.initState();
_performance = new ValueAnimation<double>(
_performance = new ValuePerformance<double>(
variable: new AnimatedValue<double>(0.0, end: 1.0, curve: ease),
duration: const Duration(milliseconds: 1500)
);
_performance.addStatusListener((AnimationStatus status) {
if (status == AnimationStatus.completed)
_performance.addStatusListener((PerformanceStatus status) {
if (status == PerformanceStatus.completed)
_restartAnimation();
});
_performance.play();
......
......@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:sky/gestures.dart';
import 'package:sky/material.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/framework.dart';
......@@ -13,7 +14,7 @@ class RaisedButton extends MaterialButton {
Key key,
Widget child,
bool enabled: true,
Function onPressed
GestureTapCallback onPressed
}) : super(key: key,
child: child,
enabled: enabled,
......
......@@ -51,16 +51,10 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
super.initState();
if (config.initialScrollOffset is double)
_scrollOffset = config.initialScrollOffset;
_toEndAnimation = new AnimatedSimulation(_setScrollOffset);
_toOffsetAnimation = new ValueAnimation<double>()
..addListener(() {
AnimatedValue<double> offset = _toOffsetAnimation.variable;
_setScrollOffset(offset.value);
});
_animation = new SimulationStepper(_setScrollOffset);
}
AnimatedSimulation _toEndAnimation; // See _startToEndAnimation()
ValueAnimation<double> _toOffsetAnimation; // Started by scrollTo()
SimulationStepper _animation;
double _scrollOffset = 0.0;
double get scrollOffset => _scrollOffset;
......@@ -106,23 +100,10 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
Widget buildContent(BuildContext context);
Future _startToOffsetAnimation(double newScrollOffset, Duration duration, Curve curve) {
_stopAnimations();
_toOffsetAnimation
..variable = new AnimatedValue<double>(scrollOffset,
end: newScrollOffset,
curve: curve
)
..progress = 0.0
..duration = duration;
return _toOffsetAnimation.play();
}
void _stopAnimations() {
if (_toOffsetAnimation.isAnimating)
_toOffsetAnimation.stop();
if (_toEndAnimation.isAnimating)
_toEndAnimation.stop();
Future _animateTo(double newScrollOffset, Duration duration, Curve curve) {
_animation.stop();
_animation.value = scrollOffset;
return _animation.animateTo(newScrollOffset, duration: duration, curve: curve);
}
bool _scrollOffsetIsInBounds(double offset) {
......@@ -165,16 +146,16 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
}
Future _startToEndAnimation({ double velocity }) {
_stopAnimations();
_animation.stop();
Simulation simulation =
_createSnapSimulation(velocity) ?? _createFlingSimulation(velocity ?? 0.0);
if (simulation == null)
return new Future.value();
return _toEndAnimation.start(simulation);
return _animation.animateWith(simulation);
}
void dispose() {
_stopAnimations();
_animation.stop();
super.dispose();
}
......@@ -193,12 +174,12 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
return new Future.value();
if (duration == null) {
_stopAnimations();
_animation.stop();
_setScrollOffset(newScrollOffset);
return new Future.value();
}
return _startToOffsetAnimation(newScrollOffset, duration, curve);
return _animateTo(newScrollOffset, duration, curve);
}
Future scrollBy(double scrollDelta, { Duration duration, Curve curve }) {
......@@ -209,7 +190,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
Future fling(Offset velocity) {
if (velocity != Offset.zero)
return _startToEndAnimation(velocity: _scrollVelocity(velocity));
if (!_toEndAnimation.isAnimating && (_toOffsetAnimation == null || !_toOffsetAnimation.isAnimating))
if (!_animation.isAnimating)
return settleScrollOffset();
return new Future.value();
}
......@@ -226,7 +207,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
}
void _handlePointerDown(_) {
_stopAnimations();
_animation.stop();
}
void _handleDragUpdate(double delta) {
......@@ -337,7 +318,7 @@ class ScrollableViewportState extends ScrollableState<ScrollableViewport> {
});
}
void _updateScrollBehaviour() {
// if you don't call this from build() or syncConstructorArguments(), you must call it from setState().
// if you don't call this from build(), you must call it from setState().
scrollTo(scrollBehavior.updateExtents(
contentExtent: _childSize,
containerExtent: _viewportSize,
......
......@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:sky/animation.dart';
import 'package:sky/painting.dart';
import 'package:sky/gestures.dart';
import 'package:sky/material.dart';
import 'package:sky/painting.dart';
import 'package:sky/src/widgets/animated_component.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/framework.dart';
......@@ -28,7 +28,7 @@ class SnackBarAction extends StatelessComponent {
}
final String label;
final Function onPressed;
final GestureTapCallback onPressed;
Widget build(BuildContext) {
return new GestureDetector(
......@@ -49,7 +49,7 @@ class SnackBar extends AnimatedComponent {
this.actions,
bool showing,
this.onDismissed
}) : super(key: key, direction: showing ? Direction.forward : Direction.reverse, duration: _kSlideInDuration) {
}) : super(key: key, direction: showing ? AnimationDirection.forward : AnimationDirection.reverse, duration: _kSlideInDuration) {
assert(content != null);
}
......
// 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/src/widgets/framework.dart';
import 'package:sky/src/rendering/statistics_box.dart';
/// The options that control whether the statistics overlay displays certain
/// aspects of the compositor
enum StatisticsOption {
/// Display the frame time and FPS of the last frame rendered. This field is
/// updated every frame.
///
/// This is the time spent by the rasterizer as it tries
/// to convert the layer tree obtained from the widgets into OpenGL commands
/// and tries to flush them onto the screen. When the total time taken by this
/// step exceeds the frame slice, a frame is lost.
displayRasterizerStatistics,
/// Display the rasterizer frame times as they change over a set period of
/// time in the form of a graph. The y axis of the graph denotes the total
/// time spent by the rasterizer as a fraction of the total frame slice. When
/// the bar turns red, a frame is lost.
visualizeRasterizerStatistics,
/// Display the frame time and FPS at which the interface can construct a
/// layer tree for the rasterizer (whose behavior is described above) to
/// consume.
///
/// This involves all layout, animations, etc. When the total time taken by
/// this step exceeds the frame slice, a frame is lost.
displayEngineStatistics,
/// Display the engine frame times as they change over a set period of time
/// in the form of a graph. The y axis of the graph denotes the total time
/// spent by the eninge as a fraction of the total frame slice. When the bar
/// turns red, a frame is lost.
visualizeEngineStatistics,
}
class StatisticsOverlay extends LeafRenderObjectWidget {
/// Create a statistics overlay that only displays specific statistics. The
/// mask is created by shifting 1 by the index of the specific StatisticOption
/// to enable.
StatisticsOverlay({ this.optionsMask, Key key }) : super(key: key);
/// Create a statistics overaly that displays all available statistics
StatisticsOverlay.allEnabled({ Key key }) : super(key: key), optionsMask = (
1 << StatisticsOption.displayRasterizerStatistics.index |
1 << StatisticsOption.visualizeRasterizerStatistics.index |
1 << StatisticsOption.displayEngineStatistics.index |
1 << StatisticsOption.visualizeEngineStatistics.index
);
final int optionsMask;
StatisticsBox createRenderObject() => new StatisticsBox(optionsMask: optionsMask);
void updateRenderObject(StatisticsBox renderObject, RenderObjectWidget oldWidget) {
renderObject.optionsMask = optionsMask;
}
}
......@@ -7,12 +7,12 @@ import 'dart:sky' as sky;
import 'package:newton/newton.dart';
import 'package:sky/animation.dart';
import 'package:sky/gestures.dart';
import 'package:sky/material.dart';
import 'package:sky/painting.dart';
import 'package:sky/rendering.dart';
import 'package:sky/material.dart';
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/framework.dart';
import 'package:sky/src/widgets/gesture_detector.dart';
import 'package:sky/src/widgets/icon.dart';
import 'package:sky/src/widgets/ink_well.dart';
import 'package:sky/src/widgets/scrollable.dart';
......@@ -307,6 +307,7 @@ class TabLabel {
class Tab extends StatelessComponent {
Tab({
Key key,
this.onSelected,
this.label,
this.color,
this.selected: false,
......@@ -315,6 +316,7 @@ class Tab extends StatelessComponent {
assert(label.text != null || label.icon != null);
}
final GestureTapCallback onSelected;
final TabLabel label;
final Color color;
final bool selected;
......@@ -359,7 +361,10 @@ class Tab extends StatelessComponent {
padding: _kTabLabelPadding
);
return new InkWell(child: centeredLabel);
return new InkWell(
onTap: onSelected,
child: centeredLabel
);
}
}
......@@ -403,16 +408,16 @@ class TabBar extends Scrollable {
class TabBarState extends ScrollableState<TabBar> {
void initState() {
super.initState();
_indicatorAnimation = new ValueAnimation<Rect>()
_indicatorAnimation = new ValuePerformance<Rect>()
..duration = _kTabBarScroll
..variable = new AnimatedRect(null, curve: ease);
..variable = new AnimatedRectValue(null, curve: ease);
scrollBehavior.isScrollable = config.isScrollable;
}
Size _tabBarSize;
Size _viewportSize = Size.zero;
List<double> _tabWidths;
ValueAnimation<Rect> _indicatorAnimation;
ValuePerformance<Rect> _indicatorAnimation;
void didUpdateConfig(TabBar oldConfig) {
super.didUpdateConfig(oldConfig);
......@@ -420,7 +425,7 @@ class TabBarState extends ScrollableState<TabBar> {
scrollTo(0.0);
}
AnimatedRect get _indicatorRect => _indicatorAnimation.variable;
AnimatedRectValue get _indicatorRect => _indicatorAnimation.variable;
void _startIndicatorAnimation(int fromTabIndex, int toTabIndex) {
_indicatorRect
......@@ -458,7 +463,7 @@ class TabBarState extends ScrollableState<TabBar> {
.clamp(scrollBehavior.minScrollOffset, scrollBehavior.maxScrollOffset);
}
void _handleTap(int tabIndex) {
void _handleTabSelected(int tabIndex) {
if (tabIndex != config.selectedIndex) {
if (_tabWidths != null) {
if (config.isScrollable)
......@@ -471,14 +476,12 @@ class TabBarState extends ScrollableState<TabBar> {
}
Widget _toTab(TabLabel label, int tabIndex, Color color, Color selectedColor) {
return new GestureDetector(
onTap: () => _handleTap(tabIndex),
child: new Tab(
label: label,
color: color,
selected: tabIndex == config.selectedIndex,
selectedColor: selectedColor
)
return new Tab(
onSelected: () => _handleTabSelected(tabIndex),
label: label,
color: color,
selected: tabIndex == config.selectedIndex,
selectedColor: selectedColor
);
}
......
......@@ -7,7 +7,7 @@ import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/framework.dart';
import 'package:vector_math/vector_math_64.dart';
export 'package:sky/animation.dart' show Direction;
export 'package:sky/animation.dart' show AnimationDirection;
abstract class TransitionComponent extends StatefulComponent {
TransitionComponent({
......@@ -17,7 +17,7 @@ abstract class TransitionComponent extends StatefulComponent {
assert(performance != null);
}
final WatchableAnimationPerformance performance;
final PerformanceView performance;
Widget build(BuildContext context);
......@@ -57,7 +57,7 @@ abstract class TransitionWithChild extends TransitionComponent {
TransitionWithChild({
Key key,
this.child,
WatchableAnimationPerformance performance
PerformanceView performance
}) : super(key: key, performance: performance);
final Widget child;
......@@ -71,7 +71,7 @@ class SlideTransition extends TransitionWithChild {
SlideTransition({
Key key,
this.position,
WatchableAnimationPerformance performance,
PerformanceView performance,
Widget child
}) : super(key: key,
performance: performance,
......@@ -91,7 +91,7 @@ class FadeTransition extends TransitionWithChild {
FadeTransition({
Key key,
this.opacity,
WatchableAnimationPerformance performance,
PerformanceView performance,
Widget child
}) : super(key: key,
performance: performance,
......@@ -109,7 +109,7 @@ class ColorTransition extends TransitionWithChild {
ColorTransition({
Key key,
this.color,
WatchableAnimationPerformance performance,
PerformanceView performance,
Widget child
}) : super(key: key,
performance: performance,
......@@ -131,7 +131,7 @@ class SquashTransition extends TransitionWithChild {
Key key,
this.width,
this.height,
WatchableAnimationPerformance performance,
PerformanceView performance,
Widget child
}) : super(key: key,
performance: performance,
......@@ -156,7 +156,7 @@ class BuilderTransition extends TransitionComponent {
Key key,
this.variables,
this.builder,
WatchableAnimationPerformance performance
PerformanceView performance
}) : super(key: key,
performance: performance);
......
......@@ -6,6 +6,7 @@
library widgets;
export 'src/widgets/animated_component.dart';
export 'src/widgets/animated_container.dart';
export 'src/widgets/app.dart';
export 'src/widgets/basic.dart';
export 'src/widgets/binding.dart';
......@@ -43,6 +44,7 @@ export 'src/widgets/radio.dart';
export 'src/widgets/raised_button.dart';
export 'src/widgets/scaffold.dart';
export 'src/widgets/scrollable.dart';
export 'src/widgets/statistics_overlay.dart';
export 'src/widgets/snack_bar.dart';
export 'src/widgets/switch.dart';
export 'src/widgets/tabs.dart';
......
......@@ -131,6 +131,19 @@ class Node {
void set rotation(double rotation) {
assert(rotation != null);
if (_physicsBody != null && parent is PhysicsNode) {
PhysicsNode physicsNode = parent;
physicsNode._updateRotation(this.physicsBody, rotation);
return;
}
_rotation = rotation;
invalidateTransformMatrix();
}
void _setRotationFromPhysics(double rotation) {
assert(rotation != null);
_rotation = rotation;
invalidateTransformMatrix();
}
......@@ -142,6 +155,19 @@ class Node {
void set position(Point position) {
assert(position != null);
if (_physicsBody != null && parent is PhysicsNode) {
PhysicsNode physicsNode = parent;
physicsNode._updatePosition(this.physicsBody, position);
return;
}
_position = position;
invalidateTransformMatrix();
}
void _setPositionFromPhysics(Point position) {
assert(position != null);
_position = position;
invalidateTransformMatrix();
}
......@@ -609,4 +635,24 @@ class Node {
bool handleEvent(SpriteBoxEvent event) {
return false;
}
// Physics
PhysicsBody _physicsBody;
PhysicsBody get physicsBody => _physicsBody;
set physicsBody(PhysicsBody physicsBody) {
if (parent != null) {
assert(parent is PhysicsNode);
if (physicsBody == null) {
physicsBody._detach();
} else {
physicsBody._attach(parent, this);
}
}
_physicsBody = physicsBody;
}
}
part of skysprites;
enum PhysicsBodyType {
static,
dynamic
}
class PhysicsBody {
PhysicsBody(this.shape, {
this.tag: null,
this.type: PhysicsBodyType.dynamic,
this.density: 1.0,
this.friction: 0.0,
this.restitution: 0.0,
this.isSensor: false,
this.linearVelocity: Offset.zero,
this.angularVelocity: 0.0,
this.linearDampening: 0.0,
this.angularDampening: 0.0,
this.allowSleep: true,
this.awake: true,
this.fixedRotation: false,
this.bullet: false,
this.active: true,
this.gravityScale: 1.0
});
Object tag;
PhysicsShape shape;
PhysicsBodyType type;
double density;
double friction;
double restitution;
bool isSensor;
Offset linearVelocity;
double angularVelocity;
double linearDampening;
double angularDampening;
bool allowSleep;
bool awake;
bool fixedRotation;
bool bullet;
bool active;
double gravityScale;
PhysicsNode _physicsNode;
Node _node;
box2d.Body _body;
bool _attached = false;
void _attach(PhysicsNode physicsNode, Node node) {
assert(_attached == false);
// Create BodyDef
box2d.BodyDef bodyDef = new box2d.BodyDef();
bodyDef.linearVelocity = new Vector2(linearVelocity.dx, linearVelocity.dy);
bodyDef.angularVelocity = angularVelocity;
bodyDef.linearDamping = linearDampening;
bodyDef.angularDamping = angularDampening;
bodyDef.allowSleep = allowSleep;
bodyDef.awake = awake;
bodyDef.fixedRotation = fixedRotation;
bodyDef.bullet = bullet;
bodyDef.active = active;
bodyDef.gravityScale = gravityScale;
if (type == PhysicsBodyType.dynamic)
bodyDef.type = box2d.BodyType.DYNAMIC;
else
bodyDef.type = box2d.BodyType.STATIC;
double conv = physicsNode.b2WorldToNodeConversionFactor;
bodyDef.position = new Vector2(node.position.x / conv, node.position.y / conv);
bodyDef.angle = radians(node.rotation);
// Create Body
_body = physicsNode.b2World.createBody(bodyDef);
// Create FixtureDef
box2d.FixtureDef fixtureDef = new box2d.FixtureDef();
fixtureDef.friction = friction;
fixtureDef.restitution = restitution;
fixtureDef.density = density;
fixtureDef.isSensor = isSensor;
// Get shapes
List<box2d.Shape> b2Shapes = [];
List<PhysicsShape> physicsShapes = [];
_addB2Shapes(physicsNode, shape, b2Shapes, physicsShapes);
// Create fixtures
for (int i = 0; i < b2Shapes.length; i++) {
box2d.Shape b2Shape = b2Shapes[i];
PhysicsShape physicsShape = physicsShapes[i];
fixtureDef.shape = b2Shape;
box2d.Fixture fixture = _body.createFixtureFromFixtureDef(fixtureDef);
fixture.userData = physicsShape;
}
_body.userData = this;
_physicsNode = physicsNode;
_node = node;
_attached = true;
}
void _detach() {
if (_attached) {
_physicsNode._bodiesScheduledForDestruction.add(_body);
_attached = false;
}
}
void _addB2Shapes(PhysicsNode physicsNode, PhysicsShape shape, List<box2d.Shape> b2Shapes, List<PhysicsShape> physicsShapes) {
if (shape is PhysicsShapeGroup) {
for (PhysicsShape child in shape.shapes) {
_addB2Shapes(physicsNode, child, b2Shapes, physicsShapes);
}
} else {
b2Shapes.add(shape.getB2Shape(physicsNode));
physicsShapes.add(shape);
}
}
}
This diff is collapsed.
part of skysprites;
abstract class PhysicsShape {
box2d.Shape _b2Shape;
Object userObject;
box2d.Shape getB2Shape(PhysicsNode node) {
if (_b2Shape == null) {
_b2Shape = _createB2Shape(node);
}
return _b2Shape;
}
box2d.Shape _createB2Shape(PhysicsNode node);
}
class PhysicsShapeCircle extends PhysicsShape {
PhysicsShapeCircle(this.point, this.radius);
final Point point;
final double radius;
box2d.Shape _createB2Shape(PhysicsNode node) {
box2d.CircleShape shape = new box2d.CircleShape();
shape.p.x = point.x / node.b2WorldToNodeConversionFactor;
shape.p.y = point.y / node.b2WorldToNodeConversionFactor;
shape.radius = radius / node.b2WorldToNodeConversionFactor;
return shape;
}
}
class PhysicsShapePolygon extends PhysicsShape {
PhysicsShapePolygon(this.points);
final List<Point> points;
box2d.Shape _createB2Shape(PhysicsNode node) {
List<Vector2> vectors = [];
for (Point point in points) {
Vector2 vec = new Vector2(
point.x / node.b2WorldToNodeConversionFactor,
point.y / node.b2WorldToNodeConversionFactor
);
vectors.add(vec);
}
box2d.PolygonShape shape = new box2d.PolygonShape();
shape.set(vectors, vectors.length);
return shape;
}
}
class PhysicsShapeGroup extends PhysicsShape {
PhysicsShapeGroup(this.shapes);
final List<PhysicsShape> shapes;
box2d.Shape _createB2Shape(PhysicsNode node) {
return null;
}
}
......@@ -10,6 +10,7 @@ import 'dart:math' as math;
import 'dart:typed_data';
import 'dart:sky' as sky;
import 'package:box2d/box2d.dart' as box2d;
import 'package:mojo/core.dart';
import 'package:sky_services/media/media.mojom.dart';
import 'package:sky/animation.dart';
......@@ -31,6 +32,9 @@ part 'node.dart';
part 'node3d.dart';
part 'node_with_size.dart';
part 'particle_system.dart';
part 'physics_body.dart';
part 'physics_node.dart';
part 'physics_shape.dart';
part 'sound.dart';
part 'sound_manager.dart';
part 'sprite.dart';
......
......@@ -6,6 +6,7 @@ homepage: http://flutter.io
dependencies:
sky: ">=0.0.36 < 0.1.0"
sky_tools: ">=0.0.10 < 0.1.0"
box2d: any
dependency_overrides:
sky:
path: ../sky/packages/sky
......@@ -8,18 +8,18 @@ void main() {
bool firstCallbackRan = false;
bool secondCallbackRan = false;
void firstCallback(double timeStamp) {
void firstCallback(Duration timeStamp) {
expect(firstCallbackRan, isFalse);
expect(secondCallbackRan, isFalse);
expect(timeStamp, equals(16.0));
expect(timeStamp.inMilliseconds, equals(16));
firstCallbackRan = true;
scheduler.cancelAnimationFrame(secondId);
}
void secondCallback(double timeStamp) {
void secondCallback(Duration timeStamp) {
expect(firstCallbackRan, isTrue);
expect(secondCallbackRan, isFalse);
expect(timeStamp, equals(16.0));
expect(timeStamp.inMilliseconds, equals(16));
secondCallbackRan = true;
}
......
This diff is collapsed.
......@@ -13,9 +13,9 @@ void main() {
tester.pumpWidget(new Navigator(
routes: {
'/': (NavigatorState navigator, Route route) { return new Column([
'/': (RouteArguments args) { return new Column([
new Draggable(
navigator: navigator,
navigator: args.navigator,
data: 1,
child: new Text('Source'),
feedback: new Text('Dragging')
......
import 'package:sky/rendering.dart';
import 'package:sky/widgets.dart';
import 'package:test/test.dart';
......@@ -29,7 +30,8 @@ void main() {
)
));
expect(detectedSize, equals(const Size(50.0, 25.0)));
expect(inner.currentContext.findRenderObject().localToGlobal(Point.origin), equals(const Point(25.0, 37.5)));
RenderBox box = inner.currentContext.findRenderObject();
expect(box.localToGlobal(Point.origin), equals(const Point(25.0, 37.5)));
});
});
}
This diff is collapsed.
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