feat: add keyboard shortcuts and Tab link traversal on pointer hosts

This commit is contained in:
randogoth 2026-10-11 22:32:44 +03:00
parent fa7aa5557c
commit 56db479df0
6 changed files with 798 additions and 272 deletions

View file

@ -1,6 +1,7 @@
import 'dart:io' show File;
import 'package:flutter/foundation.dart' show TargetPlatform, debugDefaultTargetPlatformOverride;
import 'package:flutter/services.dart' show LogicalKeyboardKey;
import 'package:drift/native.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart' show RenderParagraph;
@ -529,6 +530,81 @@ void main() {
debugDefaultTargetPlatformOverride = null;
});
testWidgets('the address bar stays on screen on pointer hosts', (tester) async {
debugDefaultTargetPlatformOverride = TargetPlatform.linux;
await pumpApp(tester);
// A page long enough to scroll.
final tall = pageSource.replaceFirst(
'</body>',
'<p>Padding paragraph to make the page scroll.</p>' * 80 + '</body>');
final page = parseSource(tall, 'https://example.com/index.html');
deps.browser.currentPage.value = page;
await tester.pumpAndSettle();
// Scrolling down leaves the bar where it is: a pointer host has keys to
// reach it with, so it never hides.
await tester.drag(find.byType(ListView), const Offset(0, -300));
await tester.pumpAndSettle();
expect(find.byType(AddressBar), findsOneWidget);
debugDefaultTargetPlatformOverride = null;
});
testWidgets('the keyboard drives the reader on pointer hosts', (tester) async {
final stubDeps = AppDependencies(
database: MewsDatabase.test(NativeDatabase.memory()),
fetch: FakeFetch({
'https://example.com/a.html':
_page('Page A', '<h1>Page A</h1><p>The first pages content.</p>'),
'https://example.com/b.html':
_page('Page B', '<h1>Page B</h1><p>The second pages content.</p>'),
}),
strings: strings,
);
debugDefaultTargetPlatformOverride = TargetPlatform.linux;
await tester.pumpWidget(MewsApp(deps: stubDeps, strings: strings));
await tester.pumpAndSettle();
await stubDeps.browser.open('https://example.com/a.html');
await stubDeps.browser.open('https://example.com/b.html');
await tester.pumpAndSettle();
// Alt+Left goes back through the walk, Alt+Right forward again.
await tester.sendKeyDownEvent(LogicalKeyboardKey.altLeft);
await tester.sendKeyDownEvent(LogicalKeyboardKey.arrowLeft);
await tester.pumpAndSettle();
expect(stubDeps.browser.currentPage.value?.url, 'https://example.com/a.html');
await tester.sendKeyUpEvent(LogicalKeyboardKey.arrowLeft);
await tester.sendKeyDownEvent(LogicalKeyboardKey.arrowRight);
await tester.pumpAndSettle();
expect(stubDeps.browser.currentPage.value?.url, 'https://example.com/b.html');
await tester.sendKeyUpEvent(LogicalKeyboardKey.altLeft);
await tester.sendKeyUpEvent(LogicalKeyboardKey.arrowRight);
// The Mac spellings walk the same way.
await tester.sendKeyDownEvent(LogicalKeyboardKey.metaLeft);
await tester.sendKeyDownEvent(LogicalKeyboardKey.arrowLeft);
await tester.pumpAndSettle();
expect(stubDeps.browser.currentPage.value?.url, 'https://example.com/a.html');
await tester.sendKeyDownEvent(LogicalKeyboardKey.bracketRight);
await tester.pumpAndSettle();
expect(stubDeps.browser.currentPage.value?.url, 'https://example.com/b.html');
await tester.sendKeyUpEvent(LogicalKeyboardKey.metaLeft);
await tester.sendKeyUpEvent(LogicalKeyboardKey.bracketRight);
// Ctrl+L hands the focus to the address field, with its text selected.
await tester.sendKeyDownEvent(LogicalKeyboardKey.controlLeft);
await tester.sendKeyDownEvent(LogicalKeyboardKey.keyL);
await tester.pump();
final field = tester.widget<TextField>(find.byType(TextField).first);
expect(field.focusNode!.hasFocus, isTrue);
// The invariant check runs before test tear-downs, so reset by hand.
debugDefaultTargetPlatformOverride = null;
});
testWidgets('bookmarks can be searched and removed from their own page',
(tester) async {
await pumpApp(tester);