Add playable core UI: board painter, drag-and-drop, HUD, result overlay
CustomPainter board with gems/ghost/clear-flash, finger-lifted drag with snap preview, combo text effect, HUD chips, phase overlays with rescue stubs, demo stage. E2E widget test drives a real drag gesture. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../game/models/piece.dart';
|
||||
import '../theme/palette.dart';
|
||||
|
||||
/// Draws a piece as rounded tiles at a given cell size; reused by the tray,
|
||||
/// the drag overlay, and ghost previews.
|
||||
void paintPiece(
|
||||
Canvas canvas,
|
||||
Piece piece, {
|
||||
required double cellSize,
|
||||
Offset origin = Offset.zero,
|
||||
Color? overrideColor,
|
||||
}) {
|
||||
final paint = Paint()
|
||||
..color = overrideColor ?? GamePalette.tile(piece.colorId);
|
||||
final inset = cellSize * 0.05;
|
||||
final radius = Radius.circular(cellSize * 0.18);
|
||||
for (final (dx, dy) in piece.offsets) {
|
||||
final rect = Rect.fromLTWH(
|
||||
origin.dx + dx * cellSize + inset,
|
||||
origin.dy + dy * cellSize + inset,
|
||||
cellSize - inset * 2,
|
||||
cellSize - inset * 2,
|
||||
);
|
||||
canvas.drawRRect(RRect.fromRectAndRadius(rect, radius), paint);
|
||||
if (overrideColor == null) {
|
||||
// Subtle top highlight for depth.
|
||||
final highlight = Paint()..color = Colors.white.withValues(alpha: 0.18);
|
||||
canvas.drawRRect(
|
||||
RRect.fromRectAndRadius(
|
||||
Rect.fromLTWH(rect.left, rect.top, rect.width, rect.height * 0.32),
|
||||
radius,
|
||||
),
|
||||
highlight,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Bounding size of a piece in cells.
|
||||
(int, int) pieceCellBounds(Piece piece) {
|
||||
var w = 0;
|
||||
var h = 0;
|
||||
for (final (dx, dy) in piece.offsets) {
|
||||
if (dx + 1 > w) w = dx + 1;
|
||||
if (dy + 1 > h) h = dy + 1;
|
||||
}
|
||||
return (w, h);
|
||||
}
|
||||
|
||||
class PieceWidget extends StatelessWidget {
|
||||
const PieceWidget({super.key, required this.piece, required this.cellSize});
|
||||
|
||||
final Piece piece;
|
||||
final double cellSize;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final (w, h) = pieceCellBounds(piece);
|
||||
return CustomPaint(
|
||||
size: Size(w * cellSize, h * cellSize),
|
||||
painter: _PiecePainter(piece, cellSize),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PiecePainter extends CustomPainter {
|
||||
const _PiecePainter(this.piece, this.cellSize);
|
||||
|
||||
final Piece piece;
|
||||
final double cellSize;
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
paintPiece(canvas, piece, cellSize: cellSize);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(_PiecePainter old) =>
|
||||
old.piece != piece || old.cellSize != cellSize;
|
||||
}
|
||||
Reference in New Issue
Block a user