viewer: keyboard shortcuts for navigation
This commit is contained in:
parent
365ddc0f92
commit
fc1234ca63
3 changed files with 106 additions and 50 deletions
|
@ -84,7 +84,7 @@ mixin FeedbackMixin {
|
|||
itemCount: itemCount,
|
||||
onCancel: onCancel,
|
||||
onDone: (processed) {
|
||||
Navigator.of(context).pop();
|
||||
Navigator.pop(context);
|
||||
onDone?.call(processed);
|
||||
},
|
||||
),
|
||||
|
|
|
@ -11,6 +11,7 @@ import 'package:aves/widgets/viewer/entry_horizontal_pager.dart';
|
|||
import 'package:aves/widgets/viewer/info/info_page.dart';
|
||||
import 'package:aves/widgets/viewer/info/notifications.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:screen_brightness/screen_brightness.dart';
|
||||
|
||||
class ViewerVerticalPageView extends StatefulWidget {
|
||||
|
@ -93,18 +94,7 @@ class _ViewerVerticalPageViewState extends State<ViewerVerticalPageView> {
|
|||
// fake page for opacity transition between collection and viewer
|
||||
const transitionPage = SizedBox();
|
||||
|
||||
final imagePage = hasCollection
|
||||
? MultiEntryScroller(
|
||||
collection: collection!,
|
||||
pageController: widget.horizontalPager,
|
||||
onPageChanged: widget.onHorizontalPageChanged,
|
||||
onViewDisposed: widget.onViewDisposed,
|
||||
)
|
||||
: entry != null
|
||||
? SingleEntryScroller(
|
||||
entry: entry!,
|
||||
)
|
||||
: const SizedBox();
|
||||
final imagePage = _buildImagePage();
|
||||
|
||||
final infoPage = NotificationListener<ShowImageNotification>(
|
||||
onNotification: (notification) {
|
||||
|
@ -150,6 +140,58 @@ class _ViewerVerticalPageViewState extends State<ViewerVerticalPageView> {
|
|||
);
|
||||
}
|
||||
|
||||
Widget _buildImagePage() {
|
||||
Widget? child;
|
||||
Map<ShortcutActivator, Intent>? shortcuts;
|
||||
|
||||
if (hasCollection) {
|
||||
child = MultiEntryScroller(
|
||||
collection: collection!,
|
||||
pageController: widget.horizontalPager,
|
||||
onPageChanged: widget.onHorizontalPageChanged,
|
||||
onViewDisposed: widget.onViewDisposed,
|
||||
);
|
||||
shortcuts = const {
|
||||
SingleActivator(LogicalKeyboardKey.arrowLeft): ShowPreviousIntent(),
|
||||
SingleActivator(LogicalKeyboardKey.arrowRight): ShowNextIntent(),
|
||||
SingleActivator(LogicalKeyboardKey.arrowUp): LeaveIntent(),
|
||||
SingleActivator(LogicalKeyboardKey.arrowDown): ShowInfoIntent(),
|
||||
};
|
||||
} else if (entry != null) {
|
||||
child = SingleEntryScroller(
|
||||
entry: entry!,
|
||||
);
|
||||
shortcuts = const {
|
||||
SingleActivator(LogicalKeyboardKey.arrowUp): LeaveIntent(),
|
||||
SingleActivator(LogicalKeyboardKey.arrowDown): ShowInfoIntent(),
|
||||
};
|
||||
}
|
||||
if (child != null) {
|
||||
return FocusableActionDetector(
|
||||
autofocus: true,
|
||||
shortcuts: shortcuts,
|
||||
actions: {
|
||||
ShowPreviousIntent: CallbackAction<Intent>(onInvoke: (intent) => _jumpHorizontalPage(-1)),
|
||||
ShowNextIntent: CallbackAction<Intent>(onInvoke: (intent) => _jumpHorizontalPage(1)),
|
||||
LeaveIntent: CallbackAction<Intent>(onInvoke: (intent) => Navigator.pop(context)),
|
||||
ShowInfoIntent: CallbackAction<Intent>(onInvoke: (intent) => ShowInfoNotification().dispatch(context)),
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
return const SizedBox();
|
||||
}
|
||||
|
||||
void _jumpHorizontalPage(int delta) {
|
||||
final pageController = widget.horizontalPager;
|
||||
final page = pageController.page?.round();
|
||||
final _collection = collection;
|
||||
if (page != null && _collection != null) {
|
||||
final target = (page + delta).clamp(0, _collection.entryCount - 1);
|
||||
pageController.jumpToPage(target);
|
||||
}
|
||||
}
|
||||
|
||||
void _onVerticalPageControllerChanged() {
|
||||
final page = widget.verticalPager.page!;
|
||||
|
||||
|
@ -205,3 +247,21 @@ class _ViewerVerticalPageViewState extends State<ViewerVerticalPageView> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// keyboard shortcut intents
|
||||
|
||||
class ShowPreviousIntent extends Intent {
|
||||
const ShowPreviousIntent();
|
||||
}
|
||||
|
||||
class ShowNextIntent extends Intent {
|
||||
const ShowNextIntent();
|
||||
}
|
||||
|
||||
class LeaveIntent extends Intent {
|
||||
const LeaveIntent();
|
||||
}
|
||||
|
||||
class ShowInfoIntent extends Intent {
|
||||
const ShowInfoIntent();
|
||||
}
|
||||
|
|
|
@ -198,32 +198,34 @@ class _EntryViewerStackState extends State<EntryViewerStack> with FeedbackMixin,
|
|||
_goToCollection(notification.filter);
|
||||
} else if (notification is EntryRemovedNotification) {
|
||||
_onEntryRemoved(context, notification.entry);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
child: NotificationListener<ToggleOverlayNotification>(
|
||||
onNotification: (notification) {
|
||||
} else if (notification is ToggleOverlayNotification) {
|
||||
_overlayVisible.value = notification.visible ?? !_overlayVisible.value;
|
||||
return true;
|
||||
},
|
||||
child: Stack(
|
||||
children: [
|
||||
ViewerVerticalPageView(
|
||||
collection: collection,
|
||||
entryNotifier: _entryNotifier,
|
||||
verticalPager: _verticalPager,
|
||||
horizontalPager: _horizontalPager,
|
||||
onVerticalPageChanged: _onVerticalPageChanged,
|
||||
onHorizontalPageChanged: _onHorizontalPageChanged,
|
||||
onImagePageRequested: () => _goToVerticalPage(imagePage),
|
||||
onViewDisposed: (mainEntry, pageEntry) => viewStateConductor.reset(pageEntry ?? mainEntry),
|
||||
),
|
||||
_buildTopOverlay(),
|
||||
_buildBottomOverlay(),
|
||||
const SideGestureAreaProtector(),
|
||||
const BottomGestureAreaProtector(),
|
||||
],
|
||||
),
|
||||
} else if (notification is ShowInfoNotification) {
|
||||
// remove focus, if any, to prevent viewer shortcuts activation from the Info page
|
||||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
_goToVerticalPage(infoPage);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
child: Stack(
|
||||
children: [
|
||||
ViewerVerticalPageView(
|
||||
collection: collection,
|
||||
entryNotifier: _entryNotifier,
|
||||
verticalPager: _verticalPager,
|
||||
horizontalPager: _horizontalPager,
|
||||
onVerticalPageChanged: _onVerticalPageChanged,
|
||||
onHorizontalPageChanged: _onHorizontalPageChanged,
|
||||
onImagePageRequested: () => _goToVerticalPage(imagePage),
|
||||
onViewDisposed: (mainEntry, pageEntry) => viewStateConductor.reset(pageEntry ?? mainEntry),
|
||||
),
|
||||
_buildTopOverlay(),
|
||||
_buildBottomOverlay(),
|
||||
const SideGestureAreaProtector(),
|
||||
const BottomGestureAreaProtector(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -249,18 +251,12 @@ class _EntryViewerStackState extends State<EntryViewerStack> with FeedbackMixin,
|
|||
);
|
||||
}
|
||||
|
||||
return NotificationListener<ShowInfoNotification>(
|
||||
onNotification: (notification) {
|
||||
_goToVerticalPage(infoPage);
|
||||
return true;
|
||||
},
|
||||
child: mainEntry.isMultiPage
|
||||
? PageEntryBuilder(
|
||||
multiPageController: context.read<MultiPageConductor>().getController(mainEntry),
|
||||
builder: (pageEntry) => _buildContent(pageEntry: pageEntry),
|
||||
)
|
||||
: _buildContent(),
|
||||
);
|
||||
return mainEntry.isMultiPage
|
||||
? PageEntryBuilder(
|
||||
multiPageController: context.read<MultiPageConductor>().getController(mainEntry),
|
||||
builder: (pageEntry) => _buildContent(pageEntry: pageEntry),
|
||||
)
|
||||
: _buildContent();
|
||||
},
|
||||
);
|
||||
|
||||
|
|
Loading…
Reference in a new issue