Today's tab drew its day centred at 16 of the calendar's 24 units, where Lucide's SVG has its body's middle; its font draws the calendar a unit higher (the top bar's stroke from 8 to 10, the foot's from 20 to 22, measured with the real fonts), so the digits sat a unit and a half low. halkyon_ui's new DayCalendarIcon centres them at 15, in Noto Sans set explicitly, and its tests draw it with the real fonts at 16 to 32 pixels: the digits centred in the body within half a unit, and 10, 20, 28 and 31 clear of the sides. Todo's TodayIcon keeps the midnight redraw around it. The agenda's, the 3-day grid's and the month's way back to today showed Lucide's calendar-1; it shows today's day in the same calendar now, as Todoist's. Whats-New: Today's date sits centred in its calendar icon, and the button back to today shows it too. Whats-New-De: Das heutige Datum steht mittig in seinem Kalendersymbol, und der Knopf zurück zu heute zeigt es auch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
130 lines
3.9 KiB
Dart
130 lines
3.9 KiB
Dart
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:halkyon_ui/halkyon_ui.dart';
|
|
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
|
import 'package:material_ui/material_ui.dart';
|
|
|
|
/// Where ink is drawn, in the calendar's 24 units.
|
|
typedef DigitInk = ({double left, double top, double right, double bottom});
|
|
|
|
void main() {
|
|
setUpAll(() async {
|
|
// The real fonts, as a phone draws them: the test font's square glyphs
|
|
// say nothing about where the digits' ink stands.
|
|
final noto = FontLoader(appFontFamily)
|
|
..addFont(rootBundle.load('assets/fonts/NotoSans-Variable.ttf'));
|
|
final lucide = FontLoader('packages/lucide_icons_flutter/Lucide')
|
|
..addFont(
|
|
rootBundle.load('packages/lucide_icons_flutter/assets/lucide.ttf'),
|
|
);
|
|
await Future.wait([noto.load(), lucide.load()]);
|
|
});
|
|
|
|
/// The alpha channel of [child] drawn at [size], ten pixels a unit.
|
|
Future<(int, List<bool>)> draw(
|
|
WidgetTester tester,
|
|
Widget child,
|
|
double size,
|
|
) async {
|
|
final key = GlobalKey();
|
|
await tester.pumpWidget(
|
|
Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: IconTheme(
|
|
data: IconThemeData(size: size, color: const Color(0xFF000000)),
|
|
child: Center(
|
|
child: RepaintBoundary(key: key, child: child),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
final boundary =
|
|
key.currentContext!.findRenderObject()! as RenderRepaintBoundary;
|
|
final drawn = await tester.runAsync(() async {
|
|
final image = await boundary.toImage(pixelRatio: 240 / size);
|
|
final bytes = await image.toByteData();
|
|
final width = image.width;
|
|
image.dispose();
|
|
final rgba = bytes!.buffer.asUint8List();
|
|
return (
|
|
width,
|
|
[for (var i = 3; i < rgba.length; i += 4) rgba[i] > 128],
|
|
);
|
|
});
|
|
return drawn!;
|
|
}
|
|
|
|
/// The ink of the day [day] at [size], apart from the calendar's own.
|
|
Future<DigitInk> digits(WidgetTester tester, int day, double size) async {
|
|
final (width, calendar) = await draw(
|
|
tester,
|
|
SizedBox.square(
|
|
dimension: size,
|
|
child: const Icon(LucideIcons.calendar),
|
|
),
|
|
size,
|
|
);
|
|
final (_, icon) = await draw(tester, DayCalendarIcon(day: day), size);
|
|
var left = width;
|
|
var top = width;
|
|
var right = -1;
|
|
var bottom = -1;
|
|
for (var i = 0; i < icon.length; i++) {
|
|
if (icon[i] && !calendar[i]) {
|
|
final x = i % width;
|
|
final y = i ~/ width;
|
|
if (x < left) {
|
|
left = x;
|
|
}
|
|
if (x > right) {
|
|
right = x;
|
|
}
|
|
if (y < top) {
|
|
top = y;
|
|
}
|
|
if (y > bottom) {
|
|
bottom = y;
|
|
}
|
|
}
|
|
}
|
|
return (
|
|
left: left / 10,
|
|
top: top / 10,
|
|
right: (right + 1) / 10,
|
|
bottom: (bottom + 1) / 10,
|
|
);
|
|
}
|
|
|
|
for (final size in [16.0, 18.0, 20.0, 24.0, 32.0]) {
|
|
testWidgets('at $size pixels, the day stands centred in the body, below '
|
|
"its top bar (the developer's review, 7 Nov 2026)", (tester) async {
|
|
final ink = await digits(tester, 7, size);
|
|
|
|
// Lucide's font draws the body's inside from 10 to 20 units.
|
|
expect(
|
|
(ink.top + ink.bottom) / 2,
|
|
moreOrLessEquals(15, epsilon: 0.5),
|
|
);
|
|
expect(ink.top, greaterThan(10));
|
|
expect(ink.bottom, lessThan(20));
|
|
});
|
|
|
|
testWidgets('at $size pixels, two digits fit between the sides', (
|
|
tester,
|
|
) async {
|
|
for (final day in [10, 20, 28, 31]) {
|
|
final ink = await digits(tester, day, size);
|
|
|
|
// The sides' strokes end at 4 and begin at 20 units; a unit clear.
|
|
expect(ink.left, greaterThan(5), reason: '$day');
|
|
expect(ink.right, lessThan(19), reason: '$day');
|
|
expect(
|
|
(ink.left + ink.right) / 2,
|
|
moreOrLessEquals(12, epsilon: 0.75),
|
|
reason: '$day',
|
|
);
|
|
}
|
|
});
|
|
}
|
|
}
|