Merge "Removed Position struct Put vecs directly into card and staticimg"
diff --git a/lib/components/board.dart b/lib/components/board.dart
index 4e98cbb..5614354 100644
--- a/lib/components/board.dart
+++ b/lib/components/board.dart
@@ -80,7 +80,8 @@
cards, false, ori,
width: i % 2 == 0 ? cccSize : null,
height: i % 2 != 0 ? cccSize : null,
- rotation: -math.PI / 2 * i);
+ rotation: -math.PI / 2 * i,
+ useKeys: true);
Widget w;
switch (i) {
case 2:
@@ -138,7 +139,8 @@
widthCard: this.cardWidth,
height: this.cardHeight,
heightCard: this.cardHeight,
- rotation: -math.PI / 2 * i);
+ rotation: -math.PI / 2 * i,
+ useKeys: true);
Widget w;
double left02 = (this.width - this.cardWidth) / 2;
diff --git a/lib/components/card.dart b/lib/components/card.dart
index 03f6db7..de932aa 100644
--- a/lib/components/card.dart
+++ b/lib/components/card.dart
@@ -4,44 +4,248 @@
import '../logic/card.dart' as logic_card;
+import 'dart:async';
+
+import 'package:flutter/animation.dart';
import 'package:flutter/material.dart' as widgets;
+import 'package:flutter/rendering.dart';
import 'package:vector_math/vector_math_64.dart' as vector_math;
-class Card extends widgets.StatelessComponent {
- final logic_card.Card card;
- final bool faceUp;
- final double _width;
- final double _height;
- final double rotation;
+enum CardAnimationType {
+ NONE, OLD_TO_NEW, IN_TOP
+}
- double get width => _width ?? 40.0;
- double get height => _height ?? 40.0;
+enum CardUIType {
+ CARD, ZCARD
+}
- Card(this.card, this.faceUp,
- {double width, double height, this.rotation: 0.0})
- : _width = width,
- _height = height;
+class GlobalCardKey extends widgets.GlobalKey {
+ logic_card.Card card;
+ CardUIType type;
- widgets.Widget build(widgets.BuildContext context) {
- // TODO(alexfandrianto): This isn't a nice way of doing Rotation.
- // The reason is that you must know the width and height of the image.
- // Feature Request: https://github.com/flutter/engine/issues/1452
- return new widgets.Listener(
- child: new widgets.Container(
- width: width,
- height: height,
- child: new widgets.Transform(
- child: _imageFromCard(card, faceUp),
- transform: new vector_math.Matrix4.identity()
- .translate(this.width / 2, this.height / 2)
- .rotateZ(this.rotation)
- .translate(-this.width / 2, -this.height / 2))));
+ GlobalCardKey(this.card, this.type): super.constructor();
+
+ bool operator ==(Object other) {
+ if (other is! GlobalCardKey) {
+ return false;
+ }
+ GlobalCardKey k = other;
+ return k.card == card && k.type == type;
}
- static widgets.Widget _imageFromCard(logic_card.Card c, bool faceUp) {
- // TODO(alexfandrianto): Instead of 'default', what if we were told which theme to use?
- String imageName =
- "images/default/${c.deck}/${faceUp ? 'up' : 'down'}/${c.identifier}.png";
- return new widgets.NetworkImage(src: imageName);
+ int get hashCode {
+ return 17 * card.hashCode + 33 * type.hashCode;
+ }
+}
+
+class ZCard extends widgets.StatefulComponent {
+ final logic_card.Card card;
+ final bool faceUp;
+ final double width;
+ final double height;
+ final double rotation;
+ final bool animateEntrance;
+ final double z;
+
+ final Point startingPosition;
+ final Point endingPosition;
+
+ ZCard(Card dataComponent, this.startingPosition, this.endingPosition) :
+ super(key: new GlobalCardKey(dataComponent.card, CardUIType.ZCARD)),
+ card = dataComponent.card,
+ faceUp = dataComponent.faceUp,
+ width = dataComponent.width ?? 40.0,
+ height = dataComponent.height ?? 40.0,
+ rotation = dataComponent.rotation,
+ animateEntrance = dataComponent.animateEntrance,
+ z = dataComponent.z;
+
+ _ZCardState createState() => new _ZCardState();
+}
+
+class Card extends widgets.StatefulComponent {
+ final logic_card.Card card;
+ final bool faceUp;
+ final double width;
+ final double height;
+ final double rotation;
+ final bool useKey;
+ final bool visible;
+ final bool animateEntrance;
+ final double z;
+
+ Card(logic_card.Card card, this.faceUp,
+ {double width, double height, this.rotation: 0.0, bool useKey: false, this.visible: true, this.animateEntrance: true, this.z})
+ : card = card,
+ width = width ?? 40.0,
+ height = height ?? 40.0,
+ useKey = useKey,
+ super(key: useKey ? new GlobalCardKey(card, CardUIType.CARD) : null);
+
+ // Use this helper to help create a Card clone.
+ // Used by the drag and drop layer.
+ Card clone({bool visible}) {
+ return new Card(this.card, this.faceUp,
+ width: width, height: height, rotation: rotation,
+ useKey: false, visible: visible ?? this.visible, animateEntrance: false,
+ z: z);
+ }
+
+ CardState createState() => new CardState();
+}
+
+class CardState extends widgets.State<Card> {
+ // TODO(alexfandrianto): This bug is why some cards appear slightly off.
+ // https://github.com/flutter/engine/issues/1939
+ Point getGlobalPosition() {
+ RenderBox box = context.findRenderObject();
+ return box.localToGlobal(Point.origin);
+ }
+
+ widgets.Widget build(widgets.BuildContext context) {
+ widgets.Widget image = null;
+ if (config.visible) {
+ image = new widgets.Transform(
+ child: _imageFromCard(config.card, config.faceUp),
+ transform: new vector_math.Matrix4.identity()
+ .rotateZ(config.rotation),
+ alignment: new FractionalOffset(0.5, 0.5));
+ }
+
+ return new widgets.Container(
+ width: config.width,
+ height: config.height,
+ child: image);
+ }
+}
+
+widgets.Widget _imageFromCard(logic_card.Card c, bool faceUp) {
+ // TODO(alexfandrianto): Instead of 'default', what if we were told which theme to use?
+ String imageName =
+ "images/default/${c.deck}/${faceUp ? 'up' : 'down'}/${c.identifier}.png";
+ return new widgets.NetworkImage(src: imageName);
+}
+
+class _ZCardState extends widgets.State<ZCard> {
+ ValuePerformance<Point> _performance;
+ List<Point> _pointQueue; // at least 1 longer than the current animation index.
+ int _animationIndex;
+ bool _cardUpdateScheduled = false;
+
+ @override
+ void initState() {
+ super.initState();
+ _initialize();
+ scheduleUpdatePosition();
+ }
+
+ void _initialize() {
+ _pointQueue = new List<Point>();
+ _animationIndex = 0;
+ if (config.startingPosition != null) {
+ _pointQueue.add(config.startingPosition);
+ }
+ _pointQueue.add(config.endingPosition);
+ _performance = new ValuePerformance<Point>(
+ variable: new AnimatedValue<Point>(Point.origin, curve: Curves.ease),
+ duration: const Duration(milliseconds: 250)
+ );
+ _performance.addStatusListener((PerformanceStatus status) {
+ if (status == PerformanceStatus.completed) {
+ _animationIndex++;
+ _tryAnimate();
+ }
+ });
+ }
+
+ void scheduleUpdatePosition() {
+ if (!_cardUpdateScheduled) {
+ _cardUpdateScheduled = true;
+ scheduleMicrotask(_updatePosition);
+ }
+ }
+
+ // These microtasks are being scheduled on every build change.
+ // Theoretically, this is too often, but to be safe, it is also good to do it.
+ @override
+ void didUpdateConfig(ZCard oldConfig) {
+ if (config.key != oldConfig.key) {
+ _initialize();
+ } else {
+ // Do we need to animate to a new location? If so, add it to the queue.
+ if (config.endingPosition != _pointQueue.last) {
+ setState(() {
+ _pointQueue.add(config.endingPosition);
+ _tryAnimate();
+ });
+ }
+ }
+ scheduleUpdatePosition();
+ }
+
+ // A callback that sets up the animation from point a to point b.
+ void _updatePosition() {
+ _cardUpdateScheduled = false; // allow the next attempt to schedule _updatePosition to succeed.
+ if (!config.animateEntrance || _pointQueue.length == 1) {
+ RenderBox box = context.findRenderObject();
+ Point endingLocation = box.globalToLocal(config.endingPosition);
+ _performance.variable
+ ..begin = endingLocation
+ ..value = endingLocation
+ ..end = endingLocation;
+ _performance.progress = 0.0;
+ return;
+ }
+
+ _tryAnimate();
+ }
+
+ bool _needsAnimation() {
+ return _animationIndex < _pointQueue.length - 1;
+ }
+
+ void _tryAnimate() {
+ // Let animations finish... (Is this a good idea?)
+ if (!_performance.isAnimating && _needsAnimation()) {
+ RenderBox box = context.findRenderObject();
+ Point globalStart = _pointQueue[_animationIndex];
+ Point globalEnd = _pointQueue[_animationIndex + 1];
+ Point startingLocation = box.globalToLocal(globalStart);
+ Point endingLocation = box.globalToLocal(globalEnd);
+ _performance.variable
+ ..begin = startingLocation
+ ..value = startingLocation
+ ..end = endingLocation;
+ _performance.progress = 0.0;
+ _performance.play();
+ }
+ }
+
+ widgets.Widget build(widgets.BuildContext context) {
+ widgets.Widget image = new widgets.Transform(
+ child: _imageFromCard(config.card, config.faceUp),
+ transform: new vector_math.Matrix4.identity()
+ .rotateZ(config.rotation),
+ alignment: new FractionalOffset(0.5, 0.5));
+
+ // Set up the drag listener.
+ widgets.Widget listeningCard = new widgets.Listener(
+ child: new widgets.Container(
+ width: config.width,
+ height: config.height,
+ child: image));
+
+ // Set up the slide transition.
+ // During animation, we must ignore all events.
+ widgets.Widget retWidget = new widgets.IgnorePointer(
+ ignoring: _performance.isAnimating,
+ child: new widgets.SlideTransition(
+ performance: _performance.view,
+ position: _performance.variable,
+ child: listeningCard
+ )
+ );
+
+ return retWidget;
}
}
diff --git a/lib/components/card_collection.dart b/lib/components/card_collection.dart
index 80c76a5..363a269 100644
--- a/lib/components/card_collection.dart
+++ b/lib/components/card_collection.dart
@@ -7,7 +7,6 @@
import 'dart:math' as math;
import 'package:flutter/material.dart';
-import 'package:flutter/material.dart' as material;
enum CardCollectionOrientation { vert, horz, fan, show1, suit }
enum DropType {
@@ -17,6 +16,7 @@
// I can see that both would be nice, but I'm not sure how to do that yet.
}
+typedef double PosComputer(int index);
typedef void AcceptCb(dynamic data, List<logic_card.Card> cards);
const double DEFAULT_WIDTH = 200.0;
@@ -43,10 +43,11 @@
final Color _backgroundColor;
final Color _altColor;
final double rotation; // This angle is in radians.
+ final bool useKeys; // If set, every Card created in this collection will be keyed.
DropType get acceptType => _acceptType ?? DropType.none;
- Color get backgroundColor => _backgroundColor ?? material.Colors.grey[500];
- Color get altColor => _altColor ?? material.Colors.grey[500];
+ Color get backgroundColor => _backgroundColor ?? Colors.grey[500];
+ Color get altColor => _altColor ?? Colors.grey[500];
CardCollectionComponent(
this.cards, this.faceUp, this.orientation,
@@ -60,7 +61,8 @@
this.heightCard: DEFAULT_CARD_HEIGHT,
Color backgroundColor,
Color altColor,
- this.rotation: 0.0})
+ this.rotation: 0.0,
+ this.useKeys: false})
: _acceptType = acceptType,
_backgroundColor = backgroundColor,
_altColor = altColor;
@@ -135,30 +137,49 @@
}
}
- double get _produceColumnWidth => config.widthCard + CARD_MARGIN * 2;
- Widget _produceColumn(List<Widget> cardWidgets) {
- // Let's do a stack of positioned cards!
- List<Widget> kids = new List<Widget>();
+ List<Widget> _makeDraggableAndPositioned(List<component_card.Card> cardWidgets, PosComputer topComputer, PosComputer leftComputer) {
+ List<Widget> ret = new List<Widget>();
+ for (int i = 0; i < cardWidgets.length; i++) {
+ Point p = new Point(leftComputer(i), topComputer(i));
+ component_card.Card w = cardWidgets[i];
+ Widget widgetToAdd = w;
+ if (config.dragChildren) {
+ widgetToAdd = new Draggable(
+ child: w,
+ data: w,
+ feedback: new Opacity(child: w.clone(visible: true), opacity: 0.5));
+ }
+ widgetToAdd = new Positioned(
+ left: p.x,
+ top: p.y,
+ child: widgetToAdd
+ );
+
+ ret.add(widgetToAdd);
+ }
+ return ret;
+ }
+
+ double get _produceColumnWidth => config.widthCard + CARD_MARGIN * 2;
+ Widget _produceColumn(List<component_card.Card> cardWidgets) {
double h = config.height ?? config.heightCard * 5;
double spacing = math.min(config.heightCard + CARD_MARGIN * 2,
(h - config.heightCard - 2 * CARD_MARGIN) / (cardWidgets.length - 1));
- for (int i = 0; i < cardWidgets.length; i++) {
- kids.add(new Positioned(
- top: CARD_MARGIN + spacing * i,
- left: CARD_MARGIN,
- child: cardWidgets[i]));
- }
+ PosComputer topComputer = (int i) => CARD_MARGIN + spacing * i;
+ PosComputer leftComputer = (int i) => CARD_MARGIN;
+
+ List<Widget> draggableKids = _makeDraggableAndPositioned(cardWidgets, topComputer, leftComputer);
return new Container(
decoration: new BoxDecoration(backgroundColor: config.backgroundColor),
height: config.height,
width: _produceColumnWidth,
- child: new Stack(kids));
+ child: new Stack(draggableKids));
}
double get _produceRowHeight => config.heightCard + CARD_MARGIN * 2;
- Widget _produceRow(List<Widget> cardWidgets, {emptyBackgroundImage: ""}) {
+ Widget _produceRow(List<component_card.Card> cardWidgets, {emptyBackgroundImage: ""}) {
if (cardWidgets.length == 0) {
// Just return a centered background image.
return new Container(
@@ -176,43 +197,36 @@
: new NetworkImage(src: emptyBackgroundImage)))));
}
- // Let's do a stack of positioned cards!
- List<Widget> kids = new List<Widget>();
-
double w = config.width ?? config.widthCard * 5;
double spacing = math.min(config.widthCard + CARD_MARGIN * 2,
(w - config.widthCard - 2 * CARD_MARGIN) / (cardWidgets.length - 1));
- for (int i = 0; i < cardWidgets.length; i++) {
- kids.add(new Positioned(
- top: CARD_MARGIN,
- left: CARD_MARGIN + spacing * i,
- child: cardWidgets[i]));
- }
+ PosComputer topComputer = (int i) => CARD_MARGIN;
+ PosComputer leftComputer = (int i) => CARD_MARGIN + spacing * i;
+
+ List<Widget> draggableKids = _makeDraggableAndPositioned(cardWidgets, topComputer, leftComputer);
return new Container(
decoration: new BoxDecoration(backgroundColor: config.backgroundColor),
height: _produceRowHeight,
width: config.width,
- child: new Stack(kids));
+ child: new Stack(draggableKids));
}
- Widget _produceSingle(List<Widget> cardWidgets) {
- List<Widget> kids = new List<Widget>();
+ Widget _produceSingle(List<component_card.Card> cardWidgets) {
+ PosComputer topComputer = (int i) => CARD_MARGIN;
+ PosComputer leftComputer = (int i) => CARD_MARGIN;
- for (int i = 0; i < cardWidgets.length; i++) {
- kids.add(new Positioned(
- top: CARD_MARGIN, left: CARD_MARGIN, child: cardWidgets[i]));
- }
+ List<Widget> draggableKids = _makeDraggableAndPositioned(cardWidgets, topComputer, leftComputer);
return new Container(
decoration: new BoxDecoration(backgroundColor: config.backgroundColor),
height: _produceRowHeight,
width: _produceColumnWidth,
- child: new Stack(kids));
+ child: new Stack(draggableKids));
}
double get _whiteLineHeight => WHITE_LINE_HEIGHT;
- Widget wrapCards(List<Widget> cardWidgets) {
+ Widget wrapCards(List<component_card.Card> cardWidgets) {
switch (config.orientation) {
case CardCollectionOrientation.vert:
return _produceColumn(cardWidgets);
@@ -224,10 +238,10 @@
case CardCollectionOrientation.show1:
return _produceSingle(cardWidgets);
case CardCollectionOrientation.suit:
- List<Widget> cs = new List<Widget>();
- List<Widget> ds = new List<Widget>();
- List<Widget> hs = new List<Widget>();
- List<Widget> ss = new List<Widget>();
+ List<component_card.Card> cs = new List<component_card.Card>();
+ List<component_card.Card> ds = new List<component_card.Card>();
+ List<component_card.Card> hs = new List<component_card.Card>();
+ List<component_card.Card> ss = new List<component_card.Card>();
List<logic_card.Card> theCards =
config.comparator != null ? this._sortedCards : config.cards;
@@ -253,7 +267,7 @@
}
return new Container(
decoration:
- new BoxDecoration(backgroundColor: material.Colors.white),
+ new BoxDecoration(backgroundColor: Colors.white),
child: new Stack(<Widget>[
new Positioned(
top: 0.0,
@@ -283,7 +297,7 @@
}
Widget _buildCollection() {
- List<Widget> cardComponents = new List<Widget>();
+ List<component_card.Card> cardComponents = new List<component_card.Card>();
List<logic_card.Card> cs =
config.comparator != null ? this._sortedCards : config.cards;
@@ -291,16 +305,12 @@
component_card.Card c = new component_card.Card(cs[i], config.faceUp,
width: config.widthCard,
height: config.heightCard,
- rotation: config.rotation);
+ rotation: config.rotation,
+ visible: !config.useKeys, // TODO(alexfandrianto): Is there a case where you want an invisible card and a key?
+ useKey: config.useKeys,
+ z: 0.0 + i);
- if (config.dragChildren) {
- cardComponents.add(new Draggable(
- child: c,
- data: c,
- feedback: new Opacity(child: c, opacity: 0.5)));
- } else {
- cardComponents.add(c);
- }
+ cardComponents.add(c);
}
// Let's draw a stack of cards with DragTargets.
diff --git a/lib/components/croupier.dart b/lib/components/croupier.dart
index bd1bc62..26e5e99 100644
--- a/lib/components/croupier.dart
+++ b/lib/components/croupier.dart
@@ -149,7 +149,7 @@
config.croupier.game, () {
config.croupier.game.quit();
makeSetStateCallback(logic_croupier.CroupierState.Welcome)();
- }, width: screenSize.width, height: screenSize.height));
+ }, width: screenSize.width, height: screenSize.height - ui.window.padding.top));
default:
assert(false);
return null;
diff --git a/lib/components/game.dart b/lib/components/game.dart
index 3bb7105..fb376d8 100644
--- a/lib/components/game.dart
+++ b/lib/components/game.dart
@@ -9,11 +9,13 @@
import '../logic/hearts/hearts.dart' show HeartsGame, HeartsPhase, HeartsType;
import '../logic/solitaire/solitaire.dart' show SolitaireGame, SolitairePhase;
import 'board.dart' show HeartsBoard;
+import 'card.dart' as component_card;
import 'card_collection.dart'
show CardCollectionComponent, DropType, CardCollectionOrientation, AcceptCb;
+import 'package:flutter/animation.dart';
import 'package:flutter/material.dart';
-import 'package:flutter/material.dart' as material;
+import 'package:flutter/rendering.dart';
part 'hearts/hearts.part.dart';
part 'proto/proto.part.dart';
@@ -32,12 +34,19 @@
}
abstract class GameComponentState<T extends GameComponent> extends State<T> {
+ Map<logic_card.Card, CardAnimationData> cardLevelMap;
+
void initState() {
super.initState();
+ cardLevelMap = new Map<logic_card.Card, CardAnimationData>();
config.game.updateCallback = update;
}
+ void _reset() {
+ cardLevelMap.clear();
+ }
+
// This callback is used to force the UI to draw when state changes occur
// outside of the UIs control (e.g., synced data).
void update() {
@@ -58,6 +67,84 @@
@override
Widget build(BuildContext context); // still UNIMPLEMENTED
+
+
+ void _cardLevelMapProcessAllVisible(List<int> visibleCardCollections) {
+ Game game = config.game;
+
+ for (int i = 0; i < visibleCardCollections.length; i++) {
+ int index = visibleCardCollections[i];
+ for (int j = 0; j < game.cardCollections[index].length; j++) {
+ _cardLevelMapProcess(game.cardCollections[index][j]);
+ }
+ }
+ }
+
+ void _cardLevelMapProcess(logic_card.Card logicCard) {
+ component_card.GlobalCardKey key = new component_card.GlobalCardKey(logicCard, component_card.CardUIType.CARD);
+ component_card.CardState cardState = key.currentState;
+ if (cardState == null) {
+ return; // There's nothing we can really do about this card since it hasn't drawn yet.
+ }
+ Point p = cardState.getGlobalPosition();
+ double z = cardState.config.z;
+ component_card.Card c = key.currentWidget;
+
+ assert(c == cardState.config);
+
+ CardAnimationData cad = cardLevelMap[logicCard];
+ if (cad == null || cad.newPoint != p || cad.z != z) {
+ setState(() {
+ cardLevelMap[logicCard] = new CardAnimationData(c, cad?.newPoint, p, z);
+ });
+ }
+ }
+
+ // Helper to build the card animation layer.
+ // Note: This isn't a component because of its dependence on Widgets.
+ Widget buildCardAnimationLayer(List<int> visibleCardCollections) {
+ // It's possible that some cards need to be moved after this build.
+ // If so, we can catch it in the next frame.
+ scheduler.requestPostFrameCallback((Duration d) {
+ _cardLevelMapProcessAllVisible(visibleCardCollections);
+ });
+
+ List<Widget> positionedCards = new List<Widget>();
+
+ // Sort the cards by z-index.
+ List<logic_card.Card> orderedKeys = cardLevelMap.keys.toList()..sort((logic_card.Card a, logic_card.Card b) {
+ double diff = cardLevelMap[a].z - cardLevelMap[b].z;
+ return diff.sign.toInt();
+ });
+
+ orderedKeys.forEach((logic_card.Card c) {
+ // Don't show a card if it isn't part of a visible collection.
+ if (!visibleCardCollections.contains(config.game.findCard(c))) {
+ cardLevelMap.remove(c); // It is an old card, which we can clean up.
+ return;
+ }
+
+ CardAnimationData data = cardLevelMap[c];
+ RenderBox box = context.findRenderObject();
+ Point p = data.newPoint;
+ Point trueP = box.globalToLocal(p);
+
+ positionedCards.add(new Positioned(
+ key: new GlobalObjectKey(c.toString()), //needed, or else the Positioned wrapper may be traded out and animations fail.
+ top: trueP.y, // must pass x and y or else it expands to the maximum Stack size.
+ left: trueP.x, // must pass x and y or else it expands to the maximum Stack size.
+ child: new component_card.ZCard(data.comp_card, data.oldPoint, data.newPoint)));
+ });
+
+ return new IgnorePointer(
+ ignoring: true,
+ child: new Container(
+ width: config.width,
+ height: config.height,
+ child: new Stack(positionedCards)
+ )
+ );
+ }
}
GameComponent createGameComponent(
@@ -79,3 +166,15 @@
return null;
}
}
+
+/// CardAnimationData contains the relevant information for a ZCard to be built.
+/// It uses the comp_card's properties, the oldPoint, newPoint, and z-index to
+/// determine how it needs to animate.
+class CardAnimationData {
+ component_card.Card comp_card;
+ Point oldPoint;
+ Point newPoint;
+ double z;
+
+ CardAnimationData(this.comp_card, this.oldPoint, this.newPoint, this.z);
+}
\ No newline at end of file
diff --git a/lib/components/hearts/hearts.part.dart b/lib/components/hearts/hearts.part.dart
index 2785ff0..966b3b3 100644
--- a/lib/components/hearts/hearts.part.dart
+++ b/lib/components/hearts/hearts.part.dart
@@ -17,12 +17,79 @@
List<logic_card.Card> passingCards2 = new List<logic_card.Card>();
List<logic_card.Card> passingCards3 = new List<logic_card.Card>();
+ HeartsType _lastViewType;
+
+ @override
+ void initState() {
+ super.initState();
+ _reset();
+ }
+
+ @override
+ void _reset() {
+ super._reset();
+ HeartsGame game = config.game as HeartsGame;
+ _lastViewType = game.viewType;
+ }
+
@override
Widget build(BuildContext context) {
- return new Container(
+ HeartsGame game = config.game as HeartsGame;
+
+ // check if we need to swap out our 's map.
+ if (_lastViewType != game.viewType) {
+ _reset();
+ }
+
+ // Hearts Widget
+ Widget heartsWidget = new Container(
decoration:
- new BoxDecoration(backgroundColor: material.Colors.grey[300]),
+ new BoxDecoration(backgroundColor: Colors.grey[300]),
child: buildHearts());
+
+ List<Widget> children = new List<Widget>();
+ children.add(new Container(
+ decoration:
+ new BoxDecoration(backgroundColor: Colors.grey[300]),
+ width: config.width,
+ height: config.height,
+ child: heartsWidget));
+ if (game.phase == HeartsPhase.Deal || game.phase != HeartsPhase.Score) {
+ List<int> visibleCardCollections = new List<int>();
+ int playerNum = game.playerNumber;
+ if (game.viewType == HeartsType.Player) {
+ switch(game.phase) {
+ case HeartsPhase.Pass:
+ visibleCardCollections.add(HeartsGame.OFFSET_PASS + playerNum);
+ visibleCardCollections.add(HeartsGame.OFFSET_HAND + playerNum);
+ break;
+ case HeartsPhase.Take:
+ visibleCardCollections.add(HeartsGame.OFFSET_PASS + game.takeTarget);
+ visibleCardCollections.add(HeartsGame.OFFSET_HAND + playerNum);
+ break;
+ case HeartsPhase.Play:
+ visibleCardCollections.add(HeartsGame.OFFSET_HAND + playerNum);
+ visibleCardCollections.add(HeartsGame.OFFSET_PLAY + playerNum);
+ break;
+ default:
+ break;
+ }
+ } else {
+ // A board will need to see these things.
+ for (int i = 0; i < 4; i++) {
+ visibleCardCollections.add(HeartsGame.OFFSET_PLAY + i);
+ visibleCardCollections.add(HeartsGame.OFFSET_PASS + i);
+ visibleCardCollections.add(HeartsGame.OFFSET_HAND + i);
+ }
+ }
+ children.add(this.buildCardAnimationLayer(visibleCardCollections));
+ }
+
+ return new Container(
+ width: config.width,
+ height: config.height,
+ child: new Stack(children)
+ );
}
void _switchViewCallback() {
@@ -160,8 +227,8 @@
@override
Widget _makeButton(String text, NoArgCb callback, {bool inactive: false}) {
var borderColor =
- inactive ? material.Colors.grey[500] : material.Colors.white;
- var backgroundColor = inactive ? material.Colors.grey[500] : null;
+ inactive ? Colors.grey[500] : Colors.white;
+ var backgroundColor = inactive ? Colors.grey[500] : null;
return new FlatButton(
child: new Container(
decoration: new BoxDecoration(
@@ -238,6 +305,8 @@
List<Widget> cardCollections = new List<Widget>();
+ // Note that this shouldn't normally be shown.
+ // Since this is a duplicate card collection, it will not have keyed cards.
List<Widget> plays = new List<Widget>();
for (int i = 0; i < 4; i++) {
plays.add(new CardCollectionComponent(
@@ -248,7 +317,7 @@
}
cardCollections.add(new Container(
decoration:
- new BoxDecoration(backgroundColor: material.Colors.teal[600]),
+ new BoxDecoration(backgroundColor: Colors.teal[600]),
width: config.width,
child:
new Flex(plays, justifyContent: FlexJustifyContent.spaceAround)));
@@ -257,22 +326,23 @@
Widget playArea = new Container(
decoration:
- new BoxDecoration(backgroundColor: material.Colors.teal[500]),
+ new BoxDecoration(backgroundColor: Colors.teal[500]),
width: config.width,
child: new Center(
child: new CardCollectionComponent(
game.cardCollections[p + HeartsGame.OFFSET_PLAY],
true,
CardCollectionOrientation.show1,
+ useKeys: true,
acceptCallback: _makeGameMoveCallback,
acceptType: p == game.whoseTurn ? DropType.card : DropType.none,
width: config.width,
backgroundColor: p == game.whoseTurn
- ? material.Colors.white
- : material.Colors.grey[500],
+ ? Colors.white
+ : Colors.grey[500],
altColor: p == game.whoseTurn
- ? material.Colors.grey[200]
- : material.Colors.grey[600])));
+ ? Colors.grey[200]
+ : Colors.grey[600])));
cardCollections.add(playArea);
List<logic_card.Card> cards = game.cardCollections[p];
@@ -280,7 +350,8 @@
cards, game.playerNumber == p, CardCollectionOrientation.suit,
dragChildren: game.whoseTurn == p,
comparator: _compareCards,
- width: config.width);
+ width: config.width,
+ useKeys: true);
cardCollections.add(c); // flex
cardCollections.add(new Text("Player ${game.whoseTurn}'s turn"));
@@ -305,7 +376,7 @@
return new Container(
decoration:
- new BoxDecoration(backgroundColor: material.Colors.pink[500]),
+ new BoxDecoration(backgroundColor: Colors.pink[500]),
child: new Flex([
new Text('Player ${game.playerNumber}'),
// TODO(alexfandrianto): we want to show round by round, deltas too, don't we?
@@ -321,7 +392,7 @@
return new Container(
decoration:
- new BoxDecoration(backgroundColor: material.Colors.pink[500]),
+ new BoxDecoration(backgroundColor: Colors.pink[500]),
child: new Flex([
new Text('Player ${game.playerNumber}'),
_makeButton('Deal', game.dealCards),
@@ -351,7 +422,7 @@
topCardWidgets.add(_makeButton(name, buttoncb, inactive: completed));
Color bgColor =
- completed ? material.Colors.teal[600] : material.Colors.teal[500];
+ completed ? Colors.teal[600] : Colors.teal[500];
Widget topArea = new Container(
decoration: new BoxDecoration(backgroundColor: bgColor),
@@ -367,8 +438,9 @@
acceptType: draggable ? DropType.card : null,
comparator: _compareCards,
width: config.width,
- backgroundColor: material.Colors.grey[500],
- altColor: material.Colors.grey[700]);
+ backgroundColor: Colors.grey[500],
+ altColor: Colors.grey[700],
+ useKeys: true);
return new Column(<Widget>[
topArea,
@@ -384,8 +456,9 @@
acceptCallback: cb,
dragChildren: cb != null,
acceptType: cb != null ? DropType.card : null,
- backgroundColor: material.Colors.white,
- altColor: material.Colors.grey[200]);
+ backgroundColor: Colors.white,
+ altColor: Colors.grey[200],
+ useKeys: true);
if (cb == null) {
ccc = new Container(child: ccc);
diff --git a/lib/components/proto/proto.part.dart b/lib/components/proto/proto.part.dart
index d714e89..7711ba0 100644
--- a/lib/components/proto/proto.part.dart
+++ b/lib/components/proto/proto.part.dart
@@ -32,7 +32,7 @@
cardCollections.add(new Container(
decoration: new BoxDecoration(
- backgroundColor: material.Colors.green[500], borderRadius: 5.0),
+ backgroundColor: Colors.green[500], borderRadius: 5.0),
child: new CardCollectionComponent(
config.game.cardCollections[4], true, CardCollectionOrientation.show1,
dragChildren: true,
@@ -44,7 +44,7 @@
return new Container(
decoration:
- new BoxDecoration(backgroundColor: material.Colors.pink[500]),
+ new BoxDecoration(backgroundColor: Colors.pink[500]),
child: new Flex(cardCollections, direction: FlexDirection.vertical));
}
diff --git a/lib/components/solitaire/solitaire.part.dart b/lib/components/solitaire/solitaire.part.dart
index 15ebe8f..a1fde84 100644
--- a/lib/components/solitaire/solitaire.part.dart
+++ b/lib/components/solitaire/solitaire.part.dart
@@ -15,12 +15,37 @@
class SolitaireGameComponentState
extends GameComponentState<SolitaireGameComponent> {
+
@override
Widget build(BuildContext context) {
+ SolitaireGame game = config.game as SolitaireGame;
+
+ print("Building Solitaire!");
+
+ // Build Solitaire and have it fill up the card level map.
+ // Unfortunately, this is required so that we can know which card components
+ // to collect.
+ Widget solitaireWidget = buildSolitaire();
+
+ List<Widget> children = new List<Widget>();
+ children.add(new Container(
+ decoration:
+ new BoxDecoration(backgroundColor: Colors.grey[300]),
+ width: config.width,
+ height: config.height,
+ child: solitaireWidget));
+ if (game.phase == SolitairePhase.Play) {
+ // All cards are visible.
+ List<int> visibleCardCollections = game.cardCollections.asMap().keys.toList();
+
+ children.add(this.buildCardAnimationLayer(visibleCardCollections));
+ }
+
return new Container(
- decoration:
- new BoxDecoration(backgroundColor: material.Colors.grey[300]),
- child: buildSolitaire());
+ width: config.width,
+ height: config.height,
+ child: new Stack(children)
+ );
}
void _cheatCallback() {
@@ -50,8 +75,8 @@
@override
Widget _makeButton(String text, NoArgCb callback, {bool inactive: false}) {
var borderColor =
- inactive ? material.Colors.grey[500] : material.Colors.white;
- var backgroundColor = inactive ? material.Colors.grey[500] : null;
+ inactive ? Colors.grey[500] : Colors.white;
+ var backgroundColor = inactive ? Colors.grey[500] : null;
return new FlatButton(
child: new Container(
decoration: new BoxDecoration(
@@ -104,44 +129,19 @@
double cardSize = config.width / 8.0;
List<Widget> row1 = new List<Widget>();
- row1.add(new Row([
- new CardCollectionComponent(
- game.cardCollections[SolitaireGame.OFFSET_ACES + 0],
+ List<CardCollectionComponent> aces = [0, 1, 2, 3].map((int i) {
+ return new CardCollectionComponent(
+ game.cardCollections[SolitaireGame.OFFSET_ACES + i],
true,
CardCollectionOrientation.show1,
widthCard: cardSize,
heightCard: cardSize,
acceptCallback: _moveCallback,
dragChildren: true,
- acceptType: DropType.card),
- new CardCollectionComponent(
- game.cardCollections[SolitaireGame.OFFSET_ACES + 1],
- true,
- CardCollectionOrientation.show1,
- widthCard: cardSize,
- heightCard: cardSize,
- acceptCallback: _moveCallback,
- dragChildren: true,
- acceptType: DropType.card),
- new CardCollectionComponent(
- game.cardCollections[SolitaireGame.OFFSET_ACES + 2],
- true,
- CardCollectionOrientation.show1,
- widthCard: cardSize,
- heightCard: cardSize,
- acceptCallback: _moveCallback,
- dragChildren: true,
- acceptType: DropType.card),
- new CardCollectionComponent(
- game.cardCollections[SolitaireGame.OFFSET_ACES + 3],
- true,
- CardCollectionOrientation.show1,
- widthCard: cardSize,
- heightCard: cardSize,
- acceptCallback: _moveCallback,
- dragChildren: true,
- acceptType: DropType.card),
- ]));
+ acceptType: DropType.card,
+ useKeys: true);
+ }).toList();
+ row1.add(new Row(aces));
row1.add(new Row([
new CardCollectionComponent(
@@ -150,14 +150,16 @@
CardCollectionOrientation.show1,
widthCard: cardSize,
heightCard: cardSize,
- dragChildren: true),
+ dragChildren: true,
+ useKeys: true),
new InkWell(
child: new CardCollectionComponent(
game.cardCollections[SolitaireGame.OFFSET_DRAW],
false,
CardCollectionOrientation.show1,
widthCard: cardSize,
- heightCard: cardSize),
+ heightCard: cardSize,
+ useKeys: true),
onTap: game.canDrawCard ? game.drawCardUI : null),
]));
@@ -169,7 +171,8 @@
false,
CardCollectionOrientation.show1,
widthCard: cardSize,
- heightCard: cardSize),
+ heightCard: cardSize,
+ useKeys: true),
onTap: game.cardCollections[SolitaireGame.OFFSET_UP + i].length == 0
? _makeFlipCallback(i)
: null));
@@ -185,7 +188,8 @@
height: config.height * 0.6,
acceptCallback: _moveCallback,
dragChildren: true,
- acceptType: DropType.card));
+ acceptType: DropType.card,
+ useKeys: true));
}
return new Column([
@@ -201,7 +205,7 @@
return new Container(
decoration:
- new BoxDecoration(backgroundColor: material.Colors.pink[500]),
+ new BoxDecoration(backgroundColor: Colors.pink[500]),
child: new Flex([
new Text('Player ${game.playerNumber}'),
_makeButton("Return to Lobby", _quitGameCallback),
@@ -214,7 +218,7 @@
return new Container(
decoration:
- new BoxDecoration(backgroundColor: material.Colors.pink[500]),
+ new BoxDecoration(backgroundColor: Colors.pink[500]),
child: new Flex([
new Text('Player ${game.playerNumber}'),
_makeButton('Deal', game.dealCardsUI),
diff --git a/lib/src/syncbase/croupier_client.dart b/lib/src/syncbase/croupier_client.dart
index 3a9b8af..1bc1ece 100644
--- a/lib/src/syncbase/croupier_client.dart
+++ b/lib/src/syncbase/croupier_client.dart
@@ -52,10 +52,12 @@
if (!(await app.exists())) {
await app.create(util.openPerms);
}
+ util.log('CroupierClient.got app');
var db = app.noSqlDatabase(util.dbName);
if (!(await db.exists())) {
await db.create(util.openPerms);
}
+ util.log('CroupierClient.got db');
return db;
}
diff --git a/lib/src/syncbase/settings_manager.dart b/lib/src/syncbase/settings_manager.dart
index 367ee76..c7ef239 100644
--- a/lib/src/syncbase/settings_manager.dart
+++ b/lib/src/syncbase/settings_manager.dart
@@ -198,7 +198,14 @@
util.log(
"SettingsScanHandler Found ${s.instanceUuid} ${s.instanceName} ${s.addrs}");
- _cc.joinSyncgroup(s.addrs[0]);
+ // TODO(alexfandrianto): Filter based on instanceName?
+ if (s.addrs.length > 0) {
+ _cc.joinSyncgroup(s.addrs[0]);
+ } else {
+ // An unexpected service was found. Who is advertising it?
+ // https://github.com/vanadium/issues/issues/846
+ util.log("Unexpected service found: ${s.toString()}");
+ }
}
void lost(List<int> instanceId) {