From 91c7a7656ea0aafa75368a3af05c03700b7304e3 Mon Sep 17 00:00:00 2001 From: ildysilva Date: Sun, 16 Feb 2025 21:43:33 +0100 Subject: [PATCH 1/7] Add GNOME-style Search-by-Typing #1142 --- lib/app/app_model.dart | 2 + lib/app/view/desktop_home_page.dart | 85 ++++++++++++++++++++--------- lib/search/search_model.dart | 2 + needs_translation.json | 60 -------------------- 4 files changed, 64 insertions(+), 85 deletions(-) diff --git a/lib/app/app_model.dart b/lib/app/app_model.dart index c4195cf2e..81e10d848 100644 --- a/lib/app/app_model.dart +++ b/lib/app/app_model.dart @@ -34,6 +34,7 @@ class AppModel extends SafeChangeNotifier { Future connectToDiscord() async => _exposeService.connectToDiscord(); Future disconnectFromDiscord() async => _exposeService.disconnectFromDiscord(); + final keyboardListenerFocus = FocusNode(); ValueNotifier get isLastFmAuthorized => _exposeService.isLastFmAuthorized; @@ -104,6 +105,7 @@ class AppModel extends SafeChangeNotifier { bool? _updateAvailable; bool? get updateAvailable => _updateAvailable; String? _onlineVersion; + String? get onlineVersion => _onlineVersion; Future checkForUpdate({ required bool isOnline, diff --git a/lib/app/view/desktop_home_page.dart b/lib/app/view/desktop_home_page.dart index 6f7231aba..0ca861a1d 100644 --- a/lib/app/view/desktop_home_page.dart +++ b/lib/app/view/desktop_home_page.dart @@ -1,14 +1,20 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:watch_it/watch_it.dart'; import '../../app_config.dart'; +import '../../common/data/audio_type.dart'; +import '../../common/page_ids.dart'; import '../../common/view/ui_constants.dart'; import '../../extensions/build_context_x.dart'; +import '../../library/library_model.dart'; import '../../patch_notes/patch_notes_dialog.dart'; import '../../player/player_model.dart'; import '../../player/view/player_view.dart'; import '../../podcasts/download_model.dart'; import '../../podcasts/podcast_model.dart'; +import '../../search/search_model.dart'; +import '../../search/search_type.dart'; import '../../settings/settings_model.dart'; import '../app_model.dart'; import '../connectivity_model.dart'; @@ -40,6 +46,31 @@ class _DesktopHomePageState extends State { }); } + void _onTypeHandler(KeyEvent event) { + final character = event.character; + if (event is! KeyDownEvent || character == null || character.isEmpty) { + return; + } + + if (FocusManager.instance.primaryFocus?.context?.widget is! FocusScope) { + return; + } + + final libraryModel = di(); + final audioType = switch (libraryModel.selectedPageId) { + PageIDs.podcasts => AudioType.podcast, + PageIDs.radio => AudioType.radio, + _ => AudioType.local + }; + + di() + ..setSearchQuery(character) + ..setAudioType(audioType) + ..search(); + + libraryModel.push(pageId: PageIDs.searchPage); + } + @override Widget build(BuildContext context) { final playerToTheRight = context.mediaQuerySize.width > kSideBarThreshHold; @@ -70,33 +101,37 @@ class _DesktopHomePageState extends State { handler: onConnectivityChangedHandler, ); - return Stack( - alignment: Alignment.center, - children: [ - Row( - children: [ - Expanded( - child: Column( - children: [ - const Expanded(child: MasterDetailPage()), - if (!playerToTheRight || isMobilePlatform) - const PlayerView(position: PlayerPosition.bottom), - ], + return KeyboardListener( + focusNode: di().keyboardListenerFocus, + onKeyEvent: _onTypeHandler, + child: Stack( + alignment: Alignment.center, + children: [ + Row( + children: [ + Expanded( + child: Column( + children: [ + const Expanded(child: MasterDetailPage()), + if (!playerToTheRight || isMobilePlatform) + const PlayerView(position: PlayerPosition.bottom), + ], + ), ), - ), - if (playerToTheRight) - const SizedBox( - width: kSideBarPlayerWidth, - child: PlayerView(position: PlayerPosition.sideBar), - ), - ], - ), - if (isFullScreen == true) - Scaffold( - backgroundColor: isVideo ? Colors.black : null, - body: const PlayerView(position: PlayerPosition.fullWindow), + if (playerToTheRight) + const SizedBox( + width: kSideBarPlayerWidth, + child: PlayerView(position: PlayerPosition.sideBar), + ), + ], ), - ], + if (isFullScreen == true) + Scaffold( + backgroundColor: isVideo ? Colors.black : null, + body: const PlayerView(position: PlayerPosition.fullWindow), + ), + ], + ), ); } } diff --git a/lib/search/search_model.dart b/lib/search/search_model.dart index 26780b5a1..721d9026d 100644 --- a/lib/search/search_model.dart +++ b/lib/search/search_model.dart @@ -349,6 +349,8 @@ class SearchModel extends SafeChangeNotifier { } SearchResult? _podcastChartsPeak; + + FocusNode fieldFocusNode = FocusNode(); SearchResult? get podcastChartsPeak => _podcastChartsPeak; Future fetchPodcastChartsPeak({int limit = 3}) async { _podcastChartsPeak = diff --git a/needs_translation.json b/needs_translation.json index 89c6a3fe1..52cd6a88f 100644 --- a/needs_translation.json +++ b/needs_translation.json @@ -686,28 +686,6 @@ "toClick" ], - "fr": [ - "markAllEpisodesAsDone", - "resetAllSettings", - "resetAllSettingsConfirm", - "confirm", - "confirmation", - "isMaybeLowBandwidthDialogTitle", - "isMaybeLowBandwidthDialogBody", - "isBackInWifiDialogTitle", - "isBackInWifiDialogBody", - "enableDataSafeModeSettingTitle", - "dataSafeModeEnabled", - "dataSafeModeDisabled", - "enableDataSafeModeSettingDescription", - "stopToNotifyAboutDataSafeMode", - "notifyMeAboutDataSafeModeTitle", - "notifyMeAboutDataSafeModeDescription", - "resourceSectionTitle", - "theClick", - "toClick" - ], - "nl": [ "home", "showArtistPage", @@ -1456,39 +1434,6 @@ ], "pt": [ - "isBackInWifiDialogTitle", - "downloadsChangeWarning", - "moreOptions", - "noRadioServerFound", - "connectedTo", - "disconnectedFrom", - "tryReconnect", - "addedTo", - "addToPlaylist", - "open", - "removeFrom", - "noCountryFound", - "noStarredTags", - "name", - "state", - "playNext", - "contributors", - "version", - "theme", - "useMoreAnimationsTitle", - "useMoreAnimationsDescription", - "showPositionDurationTitle", - "showPositionDurationDescription", - "license", - "dependencies", - "light", - "system", - "dark", - "podcastProvider", - "iTunes", - "podcastIndex", - "usePodcastIndex", - "select", "requiresAppRestart", "musicCollectionLocation", "astronomyXXXPodcastIndexOnly", @@ -1901,11 +1846,6 @@ "regionZimbabwe" ], - "pt_BR": [ - "theClick", - "toClick" - ], - "ru": [ "markAllEpisodesAsDone", "resetAllSettings", From 5abefaacb4dd7f9791c4ff47f25a0d1fb3a48327 Mon Sep 17 00:00:00 2001 From: ildysilva Date: Fri, 21 Feb 2025 16:40:46 +0100 Subject: [PATCH 2/7] add gnome style type to search --- lib/app/view/desktop_home_page.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/app/view/desktop_home_page.dart b/lib/app/view/desktop_home_page.dart index 0ca861a1d..56e58a22d 100644 --- a/lib/app/view/desktop_home_page.dart +++ b/lib/app/view/desktop_home_page.dart @@ -14,7 +14,6 @@ import '../../player/view/player_view.dart'; import '../../podcasts/download_model.dart'; import '../../podcasts/podcast_model.dart'; import '../../search/search_model.dart'; -import '../../search/search_type.dart'; import '../../settings/settings_model.dart'; import '../app_model.dart'; import '../connectivity_model.dart'; @@ -47,6 +46,10 @@ class _DesktopHomePageState extends State { } void _onTypeHandler(KeyEvent event) { + if (!isGtkApp) { + return; + } + final character = event.character; if (event is! KeyDownEvent || character == null || character.isEmpty) { return; From 5f18d35bbc84db23d860da4da7060bb84e5e8687 Mon Sep 17 00:00:00 2001 From: Ildysilva Date: Sun, 6 Apr 2025 23:32:35 +0100 Subject: [PATCH 3/7] add search shortcut --- lib/app/view/desktop_home_page.dart | 78 ++++++++++++++++------------- lib/library/library_model.dart | 7 +++ needs_translation.json | 1 - 3 files changed, 49 insertions(+), 37 deletions(-) diff --git a/lib/app/view/desktop_home_page.dart b/lib/app/view/desktop_home_page.dart index 56e58a22d..1a43547a9 100644 --- a/lib/app/view/desktop_home_page.dart +++ b/lib/app/view/desktop_home_page.dart @@ -3,7 +3,6 @@ import 'package:flutter/services.dart'; import 'package:watch_it/watch_it.dart'; import '../../app_config.dart'; -import '../../common/data/audio_type.dart'; import '../../common/page_ids.dart'; import '../../common/view/ui_constants.dart'; import '../../extensions/build_context_x.dart'; @@ -46,7 +45,7 @@ class _DesktopHomePageState extends State { } void _onTypeHandler(KeyEvent event) { - if (!isGtkApp) { + if (!isGtkApp || event.logicalKey == LogicalKeyboardKey.control) { return; } @@ -59,18 +58,19 @@ class _DesktopHomePageState extends State { return; } + _performSearch(character); + } + + void _performSearch([String? query]) { final libraryModel = di(); - final audioType = switch (libraryModel.selectedPageId) { - PageIDs.podcasts => AudioType.podcast, - PageIDs.radio => AudioType.radio, - _ => AudioType.local - }; + final audioType = libraryModel.getCurrentAudioType(); + final searchModel = di()..setAudioType(audioType); - di() - ..setSearchQuery(character) - ..setAudioType(audioType) - ..search(); + if (query != null) { + searchModel.setSearchQuery(query); + } + searchModel.search(); libraryModel.push(pageId: PageIDs.searchPage); } @@ -107,33 +107,39 @@ class _DesktopHomePageState extends State { return KeyboardListener( focusNode: di().keyboardListenerFocus, onKeyEvent: _onTypeHandler, - child: Stack( - alignment: Alignment.center, - children: [ - Row( - children: [ - Expanded( - child: Column( - children: [ - const Expanded(child: MasterDetailPage()), - if (!playerToTheRight || isMobilePlatform) - const PlayerView(position: PlayerPosition.bottom), - ], + child: CallbackShortcuts( + bindings: { + LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyF): + () => _performSearch + }, + child: Stack( + alignment: Alignment.center, + children: [ + Row( + children: [ + Expanded( + child: Column( + children: [ + const Expanded(child: MasterDetailPage()), + if (!playerToTheRight || isMobilePlatform) + const PlayerView(position: PlayerPosition.bottom), + ], + ), ), - ), - if (playerToTheRight) - const SizedBox( - width: kSideBarPlayerWidth, - child: PlayerView(position: PlayerPosition.sideBar), - ), - ], - ), - if (isFullScreen == true) - Scaffold( - backgroundColor: isVideo ? Colors.black : null, - body: const PlayerView(position: PlayerPosition.fullWindow), + if (playerToTheRight) + const SizedBox( + width: kSideBarPlayerWidth, + child: PlayerView(position: PlayerPosition.sideBar), + ), + ], ), - ], + if (isFullScreen == true) + Scaffold( + backgroundColor: isVideo ? Colors.black : null, + body: const PlayerView(position: PlayerPosition.fullWindow), + ), + ], + ), ), ); } diff --git a/lib/library/library_model.dart b/lib/library/library_model.dart index ead671ef6..333ed7391 100644 --- a/lib/library/library_model.dart +++ b/lib/library/library_model.dart @@ -6,6 +6,7 @@ import 'package:safe_change_notifier/safe_change_notifier.dart'; import '../app/view/mobile_page.dart'; import '../app_config.dart'; import '../common/data/audio.dart'; +import '../common/data/audio_type.dart'; import '../common/logging.dart'; import '../common/page_ids.dart'; import '../common/view/back_gesture.dart'; @@ -294,4 +295,10 @@ class LibraryModel extends SafeChangeNotifier implements NavigatorObserver { final GlobalKey _masterNavigatorKey = GlobalKey(); GlobalKey get masterNavigatorKey => _masterNavigatorKey; + + AudioType getCurrentAudioType() => switch (selectedPageId) { + PageIDs.podcasts => AudioType.podcast, + PageIDs.radio => AudioType.radio, + _ => AudioType.local + }; } diff --git a/needs_translation.json b/needs_translation.json index c7d036414..abb3c3048 100644 --- a/needs_translation.json +++ b/needs_translation.json @@ -1434,7 +1434,6 @@ ], "pt": [ - "basketballXXXPodcastIndexOnly", "buddhismXXXPodcastIndexOnly", "chemistryXXXPodcastIndexOnly", From a1d3ee01d2a0daa3bd32b17ebbdb6d15f285f542 Mon Sep 17 00:00:00 2001 From: Ildysilva Date: Sun, 6 Apr 2025 23:54:03 +0100 Subject: [PATCH 4/7] fix shortcut for search --- lib/app/app_model.dart | 2 ++ lib/app/view/desktop_home_page.dart | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/app/app_model.dart b/lib/app/app_model.dart index 18a1de141..62426ed91 100644 --- a/lib/app/app_model.dart +++ b/lib/app/app_model.dart @@ -62,6 +62,8 @@ class AppModel extends SafeChangeNotifier { notifyListeners(); } + final keyboardListenerFocus = FocusNode(); + bool _showQueueOverlay = false; bool get showQueueOverlay => _showQueueOverlay; void setOrToggleQueueOverlay({bool? value}) { diff --git a/lib/app/view/desktop_home_page.dart b/lib/app/view/desktop_home_page.dart index 6b73cebd8..b637f5d32 100644 --- a/lib/app/view/desktop_home_page.dart +++ b/lib/app/view/desktop_home_page.dart @@ -58,7 +58,7 @@ class _DesktopHomePageState extends State { } void _onTypeHandler(KeyEvent event) { - if (!isGtkApp || event.logicalKey == LogicalKeyboardKey.control) { + if (!AppConfig.isGtkApp || event.logicalKey == LogicalKeyboardKey.control) { return; } @@ -119,7 +119,7 @@ class _DesktopHomePageState extends State { child: CallbackShortcuts( bindings: { LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyF): - () => _performSearch + () => _performSearch() }, child: Stack( alignment: Alignment.center, From f8f08a0dfebd58e8a976e6ddea76907c8bb2e080 Mon Sep 17 00:00:00 2001 From: Ildysilva Date: Mon, 7 Apr 2025 00:12:59 +0100 Subject: [PATCH 5/7] add required trailing comma --- lib/app/view/desktop_home_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/app/view/desktop_home_page.dart b/lib/app/view/desktop_home_page.dart index b637f5d32..a84885eb2 100644 --- a/lib/app/view/desktop_home_page.dart +++ b/lib/app/view/desktop_home_page.dart @@ -119,7 +119,7 @@ class _DesktopHomePageState extends State { child: CallbackShortcuts( bindings: { LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyF): - () => _performSearch() + () => _performSearch(), }, child: Stack( alignment: Alignment.center, From 91f7dd32f5eec5f7afb178655450e25032402fb5 Mon Sep 17 00:00:00 2001 From: Ildysilva Date: Tue, 8 Apr 2025 23:16:00 +0100 Subject: [PATCH 6/7] refact: remove keyboardListner focusnode from model --- lib/app/view/desktop_home_page.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/app/view/desktop_home_page.dart b/lib/app/view/desktop_home_page.dart index a84885eb2..d36a527ab 100644 --- a/lib/app/view/desktop_home_page.dart +++ b/lib/app/view/desktop_home_page.dart @@ -87,6 +87,7 @@ class _DesktopHomePageState extends State { libraryModel.push(pageId: PageIDs.searchPage); } + final keyboardListenerFocus = FocusNode(); @override Widget build(BuildContext context) { final playerToTheRight = context.mediaQuerySize.width > kSideBarThreshHold; @@ -114,7 +115,7 @@ class _DesktopHomePageState extends State { ); return KeyboardListener( - focusNode: di().keyboardListenerFocus, + focusNode: keyboardListenerFocus, onKeyEvent: _onTypeHandler, child: CallbackShortcuts( bindings: { From 54b60f692d08101344c9f3c1fe9fcee4f71b19b1 Mon Sep 17 00:00:00 2001 From: Ildysilva Date: Wed, 9 Apr 2025 21:56:31 +0100 Subject: [PATCH 7/7] refact:remove focusnode from model --- lib/app/app_model.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/app/app_model.dart b/lib/app/app_model.dart index 62426ed91..18a1de141 100644 --- a/lib/app/app_model.dart +++ b/lib/app/app_model.dart @@ -62,8 +62,6 @@ class AppModel extends SafeChangeNotifier { notifyListeners(); } - final keyboardListenerFocus = FocusNode(); - bool _showQueueOverlay = false; bool get showQueueOverlay => _showQueueOverlay; void setOrToggleQueueOverlay({bool? value}) {