3138fc4b08
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>
83 lines
2.1 KiB
Dart
83 lines
2.1 KiB
Dart
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;
|
|
}
|