fix(todo): the + is not dragged over a day's hours, in Today's Calendar and Upcoming's 3-day

There a tap, a click or a stretch dragged says when (the developer's
review, 10 Nov 2026); Upcoming's agenda and the lists keep dragging it.

Whats-New: In Calendar and 3-day, the + button no longer drags; tap the hours instead.
Whats-New-De: In Kalender und 3 Tage lässt sich der +-Knopf nicht mehr ziehen; tippe stattdessen auf die Stunden.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-11-10 01:03:25 +01:00
commit f9cd30dd5e
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
5 changed files with 65 additions and 12 deletions

File diff suppressed because one or more lines are too long

View file

@ -437,9 +437,14 @@ class const NewTaskDropArea({
/// The button adding a task, above the navigation bar (ADR 0033, D2); it /// The button adding a task, above the navigation bar (ADR 0033, D2); it
/// can be dragged onto the list to add the task there (D16), its place /// can be dragged onto the list to add the task there (D16), its place
/// meanwhile a larger × where dropping it gives up. /// meanwhile a larger × where dropping it gives up. Not [draggable] over
class const AddTaskButton({required final VoidCallback onPressed, super.key}) /// the hours of a day, where a tap or a stretch dragged says when instead
extends StatelessWidget { /// (the developer's review, 10 Nov 2026).
class const AddTaskButton({
required final VoidCallback onPressed,
final bool draggable = true,
super.key,
}) extends StatelessWidget {
/// The key of the × left in the button's place while it is dragged. /// The key of the × left in the button's place while it is dragged.
static const cancelKey = ValueKey('add-task-cancel'); static const cancelKey = ValueKey('add-task-cancel');
@ -447,6 +452,16 @@ class const AddTaskButton({required final VoidCallback onPressed, super.key})
Widget build(BuildContext context) { Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context); final l10n = AppLocalizations.of(context);
final scheme = Theme.of(context).colorScheme; final scheme = Theme.of(context).colorScheme;
final button = FloatingActionButton(
// Every tab keeps its own, alive beside the others: no hero flies.
heroTag: null,
tooltip: l10n.taskAddTitle,
onPressed: onPressed,
child: const Icon(LucideIcons.plus),
);
if (!draggable) {
return button;
}
return Draggable<NewTaskDrag>( return Draggable<NewTaskDrag>(
data: const NewTaskDrag(), data: const NewTaskDrag(),
dragAnchorStrategy: pointerDragAnchorStrategy, dragAnchorStrategy: pointerDragAnchorStrategy,
@ -487,13 +502,7 @@ class const AddTaskButton({required final VoidCallback onPressed, super.key})
), ),
), ),
), ),
child: FloatingActionButton( child: button,
// Every tab keeps its own, alive beside the others: no hero flies.
heroTag: null,
tooltip: l10n.taskAddTitle,
onPressed: onPressed,
child: const Icon(LucideIcons.plus),
),
); );
} }
} }

View file

@ -77,7 +77,10 @@ class _TodayTabState() extends State<TodayTab> with PlanHost<TodayTab> {
], ],
floatingActionButton: calendar && planSheetOpen floatingActionButton: calendar && planSheetOpen
? null ? null
: AddTaskButton(onPressed: () => showQuickAdd(context, day: today)), : AddTaskButton(
draggable: !calendar,
onPressed: () => showQuickAdd(context, day: today),
),
body: Column( body: Column(
children: [ children: [
const KeysResetBanner(), const KeysResetBanner(),

View file

@ -89,6 +89,7 @@ class _UpcomingTabState()
floatingActionButton: planSheetOpen floatingActionButton: planSheetOpen
? null ? null
: AddTaskButton( : AddTaskButton(
draggable: upcoming.layout != UpcomingLayout.threeDay,
onPressed: () => showQuickAdd( onPressed: () => showQuickAdd(
context, context,
day: switch (_selected) { day: switch (_selected) {

View file

@ -2069,6 +2069,26 @@ void main() {
expect(find.byKey(quickAddTitleKey), findsOneWidget); expect(find.byKey(quickAddTitleKey), findsOneWidget);
}); });
testWidgets('its + is not dragged, the hours taking a tap '
"instead (the developer's review, 10 Nov 2026); a tap opens quick "
'add', (tester) async {
await start(tester, usedBefore: true);
await threeDay(tester);
final button = tester.getCenter(find.byType(AddTaskButton));
final gesture = await tester.startGesture(button);
for (var step = 1; step <= 5; step++) {
await gesture.moveTo(button - Offset(0, 40.0 * step));
await tester.pump();
}
expect(find.byKey(AddTaskButton.cancelKey), findsNothing);
await gesture.up();
await tester.pumpAndSettle();
await tester.tap(find.byType(AddTaskButton));
await tester.pumpAndSettle();
expect(find.byKey(quickAddTitleKey), findsOneWidget);
});
group('a stretch of time chosen by holding and dragging, as Proton ' group('a stretch of time chosen by holding and dragging, as Proton '
"Calendar's (the developer's review, 10 Nov 2026)", () { "Calendar's (the developer's review, 10 Nov 2026)", () {
/// Presses at [from] down the [day]'s column with [kind], holds /// Presses at [from] down the [day]'s column with [kind], holds
@ -2449,6 +2469,26 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
} }
testWidgets('its + is not dragged, the hours taking a tap '
"instead (the developer's review, 10 Nov 2026); a tap opens quick "
'add', (tester) async {
await start(tester, usedBefore: true);
await calendar(tester);
final button = tester.getCenter(find.byType(AddTaskButton));
final gesture = await tester.startGesture(button);
for (var step = 1; step <= 5; step++) {
await gesture.moveTo(button - Offset(0, 40.0 * step));
await tester.pump();
}
expect(find.byKey(AddTaskButton.cancelKey), findsNothing);
await gesture.up();
await tester.pumpAndSettle();
await tester.tap(find.byType(AddTaskButton));
await tester.pumpAndSettle();
expect(find.byKey(quickAddTitleKey), findsOneWidget);
});
/// Scrolls the day so that [hour] is at the top of its hours. /// Scrolls the day so that [hour] is at the top of its hours.
Future<void> showHour(WidgetTester tester, int hour) async { Future<void> showHour(WidgetTester tester, int hour) async {
tester tester