feat(todo): side panes widen by their line, and a tap adds a task (ADR 0033, D33)
The line beside the task pane and Today's Plan is dragged, between 300 px for the pane and 360 px beside it, each pane's width kept on the device. A tap or click in the hour grid opens quick add at the quarter hour under it, on a phone too, as where it lands says when; a long press takes quarter hours as well. A tap that stops the day scrolling adds nothing. Whats-New: Drag the line beside a task or the Plan to resize it; tap or click into the calendar to add a task at that time. Whats-New-De: Ziehe die Linie neben einer Aufgabe oder dem Plan, um die Breite zu ändern; tippe oder klicke in den Kalender, um dort eine Aufgabe anzulegen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
8628d80d89
commit
4aec923476
2 changed files with 454 additions and 31 deletions
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart' show kLongPressTimeout;
|
||||
import 'package:flutter/gestures.dart'
|
||||
show PointerDeviceKind, kLongPressTimeout;
|
||||
import 'package:flutter/rendering.dart' show RenderParagraph;
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
|
@ -1132,6 +1133,77 @@ void main() {
|
|||
expect(find.text('Far'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'a click adds a task at that quarter hour, for half an hour',
|
||||
(tester) async {
|
||||
await start(tester, usedBefore: true);
|
||||
await threeDay(tester);
|
||||
final today = Day.of(DateTime.now());
|
||||
final column = find.byKey(HourGrid.columnKey(today));
|
||||
// 14:15 and a little: an hour is 60 logical pixels.
|
||||
final at = tester.getTopLeft(column) + const Offset(20, 14 * 60 + 20);
|
||||
final mouse = await tester.startGesture(
|
||||
at,
|
||||
kind: PointerDeviceKind.mouse,
|
||||
);
|
||||
await mouse.up();
|
||||
await tester.pumpAndSettle();
|
||||
await tester.enterText(find.byKey(quickAddTitleKey), 'Call');
|
||||
await tester.pump();
|
||||
await tester.tap(find.byTooltip('Add'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final date = (await tester.runAsync(() => tasks().all()))!
|
||||
.single
|
||||
.content!
|
||||
.date!;
|
||||
expect(date.day, today);
|
||||
expect(date.time, 14 * 60 + 15);
|
||||
expect(date.duration, const Duration(minutes: 30));
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets("a finger's tap adds a task at that quarter hour too", (
|
||||
tester,
|
||||
) async {
|
||||
await start(tester, usedBefore: true);
|
||||
await threeDay(tester);
|
||||
final today = Day.of(DateTime.now());
|
||||
final column = find.byKey(HourGrid.columnKey(today));
|
||||
await tester.tapAt(
|
||||
tester.getTopLeft(column) + const Offset(20, 14 * 60 + 35),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.enterText(find.byKey(quickAddTitleKey), 'Call');
|
||||
await tester.pump();
|
||||
await tester.tap(find.byTooltip('Add'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final date = (await tester.runAsync(
|
||||
() => tasks().all(),
|
||||
))!.single.content!.date!;
|
||||
expect(date.time, 14 * 60 + 30);
|
||||
});
|
||||
|
||||
testWidgets('a tap that stops the day scrolling adds nothing', (
|
||||
tester,
|
||||
) async {
|
||||
await start(tester, usedBefore: true);
|
||||
await threeDay(tester);
|
||||
final column = find.byKey(
|
||||
HourGrid.columnKey(Day.of(DateTime.now())),
|
||||
);
|
||||
final at = tester.getCenter(find.byType(HourGrid));
|
||||
await tester.flingFrom(at, const Offset(0, -300), 2000);
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
expect(tester.binding.hasScheduledFrame, isTrue);
|
||||
await tester.tapAt(at);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(column, findsOneWidget);
|
||||
expect(find.byKey(quickAddTitleKey), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('a long press on a half hour adds a task there for half an '
|
||||
'hour', (tester) async {
|
||||
await start(tester, usedBefore: true);
|
||||
|
|
|
|||
113
apps/todo/halkyon_todo_flutter/test/tabs/side_pane_test.dart
Normal file
113
apps/todo/halkyon_todo_flutter/test/tabs/side_pane_test.dart
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:halkyon_todo_flutter/l10n/app_localizations.dart';
|
||||
import 'package:halkyon_todo_flutter/src/tabs/pane_widths.dart';
|
||||
import 'package:halkyon_todo_flutter/src/tabs/side_pane.dart';
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
|
||||
/// The line beside a pane dragged (ADR 0033, D33).
|
||||
void main() {
|
||||
const paneKey = ValueKey('pane');
|
||||
|
||||
Future<void> pump(WidgetTester tester, PaneWidths widths) async {
|
||||
tester.view.physicalSize = const Size(1200, 800);
|
||||
tester.view.devicePixelRatio = 1;
|
||||
addTearDown(tester.view.reset);
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
locale: const Locale('en'),
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
home: Scaffold(
|
||||
body: SidePane(
|
||||
kind: SidePaneKind.task,
|
||||
widths: widths,
|
||||
fallback: 420,
|
||||
main: const SizedBox.expand(),
|
||||
pane: const SizedBox.expand(key: paneKey),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
double width(WidgetTester tester) =>
|
||||
tester.getSize(find.byKey(paneKey)).width;
|
||||
|
||||
Future<void> drag(WidgetTester tester, double dx) async {
|
||||
await tester.timedDrag(
|
||||
find.byKey(SidePane.gripKey(SidePaneKind.task)),
|
||||
Offset(dx, 0),
|
||||
const Duration(milliseconds: 200),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
testWidgets('starts at its width, and the line dragged widens it', (
|
||||
tester,
|
||||
) async {
|
||||
await pump(tester, PaneWidths());
|
||||
expect(width(tester), 420);
|
||||
|
||||
await drag(tester, -100);
|
||||
expect(width(tester), closeTo(520, 40));
|
||||
final wider = width(tester);
|
||||
|
||||
await drag(tester, 60);
|
||||
expect(width(tester), lessThan(wider));
|
||||
});
|
||||
|
||||
testWidgets('stops short of too narrow a pane, and of too narrow a '
|
||||
'calendar', (tester) async {
|
||||
await pump(tester, PaneWidths());
|
||||
|
||||
await drag(tester, 800);
|
||||
expect(width(tester), SidePane.least);
|
||||
|
||||
await drag(tester, -1100);
|
||||
expect(width(tester), 1200 - SidePane.mainLeast);
|
||||
});
|
||||
|
||||
testWidgets('a width from a wider window fits this one, and is kept', (
|
||||
tester,
|
||||
) async {
|
||||
final widths = PaneWidths()..drag(SidePaneKind.task, 1000);
|
||||
await pump(tester, widths);
|
||||
|
||||
expect(width(tester), 1200 - SidePane.mainLeast);
|
||||
expect(widths.of(SidePaneKind.task), 1000);
|
||||
});
|
||||
|
||||
test(
|
||||
'the width chosen is there at the next start, each pane its own',
|
||||
() async {
|
||||
final folder = await Directory.systemTemp.createTemp('panes');
|
||||
addTearDown(() => folder.delete(recursive: true));
|
||||
final file = File('${folder.path}/panes.json');
|
||||
|
||||
final widths = await PaneWidths.open(file);
|
||||
expect(widths.of(SidePaneKind.plan), isNull);
|
||||
widths
|
||||
..drag(SidePaneKind.plan, 380)
|
||||
..drag(SidePaneKind.task, 500);
|
||||
await widths.save();
|
||||
|
||||
final again = await PaneWidths.open(file);
|
||||
expect(again.of(SidePaneKind.plan), 380);
|
||||
expect(again.of(SidePaneKind.task), 500);
|
||||
},
|
||||
);
|
||||
|
||||
test("a file that can't be read means no choice yet", () async {
|
||||
final folder = await Directory.systemTemp.createTemp('panes');
|
||||
addTearDown(() => folder.delete(recursive: true));
|
||||
final file = File('${folder.path}/panes.json')
|
||||
..writeAsStringSync(jsonEncode([1]));
|
||||
|
||||
expect((await PaneWidths.open(file)).of(SidePaneKind.task), isNull);
|
||||
file.writeAsStringSync('{');
|
||||
expect((await PaneWidths.open(file)).of(SidePaneKind.task), isNull);
|
||||
});
|
||||
}
|
||||
Loading…
Reference in a new issue