diff --git a/lib/ui/common/collectible_item.dart b/lib/ui/common/collectible_item.dart index cd771361..8e3b7e88 100644 --- a/lib/ui/common/collectible_item.dart +++ b/lib/ui/common/collectible_item.dart @@ -6,7 +6,7 @@ import 'package:wonders/ui/common/utils/app_haptics.dart'; import 'package:wonders/ui/screens/collectible_found/collectible_found_screen.dart'; class CollectibleItem extends StatelessWidget with GetItMixin { - CollectibleItem(this.collectible, {this.size = 64.0, Key? key}) : super(key: key) { + CollectibleItem(this.collectible, {this.size = 64.0, Key? key, this.focus}) : super(key: key) { // pre-fetch the image, so it's ready if we show the collectible found screen. _imageProvider = NetworkImage(collectible.imageUrl); _imageProvider.resolve(ImageConfiguration()).addListener(ImageStreamListener((_, __) {})); @@ -15,6 +15,7 @@ class CollectibleItem extends StatelessWidget with GetItMixin { final CollectibleData collectible; final double size; late final ImageProvider _imageProvider; + final FocusNode? focus; void _handleTap(BuildContext context) async { final screen = CollectibleFoundScreen(collectible: collectible, imageProvider: _imageProvider); @@ -39,6 +40,7 @@ class CollectibleItem extends StatelessWidget with GetItMixin { // Note: In order for the collapse animation to run properly, we must return a non-zero height or width. closedBuilder: (_) => SizedBox(width: 1, height: 0), openBuilder: (_) => AppBtn.basic( + focusNode: focus, semanticLabel: $strings.collectibleItemSemanticCollectible, onPressed: () => _handleTap(context), enableFeedback: false, diff --git a/lib/ui/common/hidden_collectible.dart b/lib/ui/common/hidden_collectible.dart index ecab5e2e..dde15ac9 100644 --- a/lib/ui/common/hidden_collectible.dart +++ b/lib/ui/common/hidden_collectible.dart @@ -5,13 +5,15 @@ import 'package:wonders/ui/common/collectible_item.dart'; /// The item is looked up via index, and expects that 3 items always exist for each wonder. /// If `wonders` is empty, then the collectible is always shown. class HiddenCollectible extends StatelessWidget with GetItMixin { - HiddenCollectible(this.currentWonder, {Key? key, required this.index, this.matches = const [], this.size = 64}) + HiddenCollectible(this.currentWonder, + {Key? key, required this.index, this.matches = const [], this.size = 64, this.focus}) : assert(index <= 2, 'index should not exceed 2'), super(key: key); final int index; final double size; final List matches; final WonderType currentWonder; + final FocusNode? focus; @override Widget build(BuildContext context) { final data = collectiblesLogic.forWonder(currentWonder); @@ -19,6 +21,6 @@ class HiddenCollectible extends StatelessWidget with GetItMixin { if (matches.isNotEmpty && matches.contains(currentWonder) == false) { return SizedBox.shrink(); } - return CollectibleItem(data[index], size: size); + return CollectibleItem(data[index], size: size, focus: focus); } } diff --git a/lib/ui/screens/photo_gallery/photo_gallery.dart b/lib/ui/screens/photo_gallery/photo_gallery.dart index 644649bd..acdfcdfc 100644 --- a/lib/ui/screens/photo_gallery/photo_gallery.dart +++ b/lib/ui/screens/photo_gallery/photo_gallery.dart @@ -99,8 +99,6 @@ class _PhotoGalleryState extends State { } bool _handleKeyDown(KeyDownEvent event) { - var newIndex = -1; - bool handled = false; final key = event.logicalKey; Map keyActions = { LogicalKeyboardKey.arrowUp: -_gridSize, @@ -109,24 +107,22 @@ class _PhotoGalleryState extends State { LogicalKeyboardKey.arrowLeft: -1, }; - int? action = keyActions[key]; - if (action != null) { - newIndex = _index + action; - handled = true; - bool isRightSide = _index % _gridSize == _gridSize - 1; - if (isRightSide && key == LogicalKeyboardKey.arrowRight) { - newIndex = -1; - } - bool isLeftSide = _index % _gridSize == 0; - if (isLeftSide && key == LogicalKeyboardKey.arrowLeft) newIndex = -1; - if (newIndex > _gridSize * _gridSize) { - newIndex = -1; - } - if (newIndex >= 0) { - _setIndex(newIndex); - } + // Apply key action, exit early if no action is defined + int? actionValue = keyActions[key]; + if (actionValue == null) return false; + int newIndex = _index + actionValue; + + // Block actions along edges of the grid + bool isRightSide = _index % _gridSize == _gridSize - 1; + bool isLeftSide = _index % _gridSize == 0; + bool outOfBounds = newIndex < 0 || newIndex >= _imgCount; + if ((isRightSide && key == LogicalKeyboardKey.arrowRight) || + (isLeftSide && key == LogicalKeyboardKey.arrowLeft) || + outOfBounds) { + return false; } - return handled; + _setIndex(newIndex); + return true; } /// Converts a swipe direction into a new index @@ -274,14 +270,16 @@ class _PhotoGalleryState extends State { liveRegion: isSelected, onIncrease: () => _handleImageTapped(_index + 1, false), onDecrease: () => _handleImageTapped(_index - 1, false), - child: AppBtn.basic( - semanticLabel: semanticLbl, - focusNode: _focusNodes[index], - onFocusChanged: (isFocused) => _handleImageFocusChanged(index, isFocused), - onPressed: () => _handleImageTapped(index, isSelected), - child: _checkCollectibleIndex(index) - ? Center(child: HiddenCollectible(widget.wonderType, index: 1, size: 100)) - : ClipRRect( + child: _checkCollectibleIndex(index) + ? Center( + child: HiddenCollectible(widget.wonderType, index: 1, size: 100, focus: _focusNodes[index]), + ) + : AppBtn.basic( + semanticLabel: semanticLbl, + focusNode: _focusNodes[index], + onFocusChanged: (isFocused) => _handleImageFocusChanged(index, isFocused), + onPressed: () => _handleImageTapped(index, isSelected), + child: ClipRRect( borderRadius: BorderRadius.circular(8), child: SizedBox( width: imgSize.width, @@ -303,7 +301,7 @@ class _PhotoGalleryState extends State { ), ), ), - ), + ), ), ); }),