mirror of https://github.com/flutter/pinball.git
commit
bf199eb758
After Width: | Height: | Size: 2.3 KiB |
@ -0,0 +1,95 @@
|
|||||||
|
import 'package:flame/components.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart';
|
||||||
|
import 'package:pinball_ui/pinball_ui.dart';
|
||||||
|
|
||||||
|
final _boldLabelTextPaint = TextPaint(
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 1.8,
|
||||||
|
color: PinballColors.white,
|
||||||
|
fontFamily: PinballFonts.pixeloidSans,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final _labelTextPaint = TextPaint(
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 1.8,
|
||||||
|
color: PinballColors.white,
|
||||||
|
fontFamily: PinballFonts.pixeloidSans,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
/// {@template error_component}
|
||||||
|
/// A plain visual component used to show errors for the user.
|
||||||
|
/// {@endtemplate}
|
||||||
|
class ErrorComponent extends SpriteComponent with HasGameRef {
|
||||||
|
/// {@macro error_component}
|
||||||
|
ErrorComponent({required this.label, Vector2? position})
|
||||||
|
: _textPaint = _labelTextPaint,
|
||||||
|
super(
|
||||||
|
position: position,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// {@macro error_component}
|
||||||
|
ErrorComponent.bold({required this.label, Vector2? position})
|
||||||
|
: _textPaint = _boldLabelTextPaint,
|
||||||
|
super(
|
||||||
|
position: position,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Text shown on the error message.
|
||||||
|
final String label;
|
||||||
|
final TextPaint _textPaint;
|
||||||
|
|
||||||
|
List<String> _splitInLines() {
|
||||||
|
final maxWidth = size.x - 8;
|
||||||
|
final lines = <String>[];
|
||||||
|
var currentLine = '';
|
||||||
|
final words = label.split(' ');
|
||||||
|
while (words.isNotEmpty) {
|
||||||
|
final word = words.removeAt(0);
|
||||||
|
|
||||||
|
if (_textPaint.measureTextWidth('$currentLine $word') <= maxWidth) {
|
||||||
|
currentLine = '$currentLine $word'.trim();
|
||||||
|
} else {
|
||||||
|
lines.add(currentLine);
|
||||||
|
currentLine = word;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.add(currentLine);
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> onLoad() async {
|
||||||
|
anchor = Anchor.center;
|
||||||
|
final sprite = await gameRef.loadSprite(
|
||||||
|
Assets.images.errorBackground.keyName,
|
||||||
|
);
|
||||||
|
|
||||||
|
size = sprite.originalSize / 20;
|
||||||
|
this.sprite = sprite;
|
||||||
|
|
||||||
|
final lines = _splitInLines();
|
||||||
|
|
||||||
|
// Calculates vertical offset based on the number of lines of text to be
|
||||||
|
// displayed. This offset is used to keep the middle of the multi-line text
|
||||||
|
// at the center of the [ErrorComponent].
|
||||||
|
final yOffset = ((size.y / 2.2) / lines.length) * 1.5;
|
||||||
|
|
||||||
|
for (var i = 0; i < lines.length; i++) {
|
||||||
|
await add(
|
||||||
|
TextComponent(
|
||||||
|
position: Vector2(size.x / 2, yOffset + 2.2 * i),
|
||||||
|
size: Vector2(size.x - 4, 2.2),
|
||||||
|
text: lines[i],
|
||||||
|
textRenderer: _textPaint,
|
||||||
|
anchor: Anchor.center,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
import 'package:flame/components.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart';
|
||||||
|
import 'package:sandbox/common/common.dart';
|
||||||
|
|
||||||
|
class ErrorComponentGame extends AssetsGame {
|
||||||
|
ErrorComponentGame({required this.text});
|
||||||
|
|
||||||
|
static const description = 'Shows how ErrorComponents are rendered.';
|
||||||
|
|
||||||
|
final String text;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> onLoad() async {
|
||||||
|
camera.followVector2(Vector2.zero());
|
||||||
|
|
||||||
|
await add(ErrorComponent(label: text));
|
||||||
|
await add(
|
||||||
|
ErrorComponent.bold(
|
||||||
|
label: text,
|
||||||
|
position: Vector2(0, 10),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
import 'package:dashbook/dashbook.dart';
|
||||||
|
import 'package:sandbox/common/common.dart';
|
||||||
|
import 'package:sandbox/stories/error_component/error_component_game.dart';
|
||||||
|
|
||||||
|
void addErrorComponentStories(Dashbook dashbook) {
|
||||||
|
dashbook.storiesOf('ErrorComponent').addGame(
|
||||||
|
title: 'Basic',
|
||||||
|
description: ErrorComponentGame.description,
|
||||||
|
gameBuilder: (context) => ErrorComponentGame(
|
||||||
|
text: context.textProperty(
|
||||||
|
'label',
|
||||||
|
'Oh no, something went wrong!',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
// ignore_for_file: cascade_invocations
|
||||||
|
|
||||||
|
import 'package:flame/components.dart';
|
||||||
|
import 'package:flame_test/flame_test.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart';
|
||||||
|
|
||||||
|
import '../../helpers/helpers.dart';
|
||||||
|
|
||||||
|
extension _IterableX on Iterable<Component> {
|
||||||
|
int countTexts(String value) {
|
||||||
|
return where(
|
||||||
|
(component) => component is TextComponent && component.text == value,
|
||||||
|
).length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('ErrorComponent', () {
|
||||||
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
final assets = [
|
||||||
|
Assets.images.errorBackground.keyName,
|
||||||
|
];
|
||||||
|
final flameTester = FlameTester(() => TestGame(assets));
|
||||||
|
|
||||||
|
flameTester.test('renders correctly', (game) async {
|
||||||
|
await game.ensureAdd(ErrorComponent(label: 'Error Message'));
|
||||||
|
final count = game.descendants().countTexts('Error Message');
|
||||||
|
|
||||||
|
expect(count, equals(1));
|
||||||
|
});
|
||||||
|
|
||||||
|
group('when the text is longer than one line', () {
|
||||||
|
flameTester.test('renders correctly', (game) async {
|
||||||
|
await game.ensureAdd(
|
||||||
|
ErrorComponent(
|
||||||
|
label: 'Error With A Longer Message',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final count1 = game.descendants().countTexts('Error With A');
|
||||||
|
final count2 = game.descendants().countTexts('Longer Message');
|
||||||
|
|
||||||
|
expect(count1, equals(1));
|
||||||
|
expect(count2, equals(1));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('when using the bold font', () {
|
||||||
|
flameTester.test('renders correctly', (game) async {
|
||||||
|
await game.ensureAdd(ErrorComponent.bold(label: 'Error Message'));
|
||||||
|
final count = game.descendants().countTexts('Error Message');
|
||||||
|
|
||||||
|
expect(count, equals(1));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in new issue