diff --git a/res/keyboard/cs_CZ.kbd.cfg b/res/keyboard/cs_CZ.kbd.cfg index 6af18ba2a3f0..23bf9f34e32b 100644 --- a/res/keyboard/cs_CZ.kbd.cfg +++ b/res/keyboard/cs_CZ.kbd.cfg @@ -141,6 +141,8 @@ vinylcontrol_cueing Ctrl+Alt+U FileMenu_LoadDeck1 Ctrl+o FileMenu_LoadDeck2 Ctrl+Shift+O FileMenu_Quit Ctrl+q +LibraryMenu_SearchInCurrentView Ctrl+f +LibraryMenu_SearchInAllTracks Ctrl+Shift+F LibraryMenu_NewPlaylist Ctrl+n LibraryMenu_NewCrate Ctrl+Shift+N ViewMenu_ShowSkinSettings Ctrl+1 diff --git a/res/keyboard/da_DK.kbd.cfg b/res/keyboard/da_DK.kbd.cfg index 323a0942d82a..0676d66b9afd 100644 --- a/res/keyboard/da_DK.kbd.cfg +++ b/res/keyboard/da_DK.kbd.cfg @@ -141,6 +141,8 @@ vinylcontrol_cueing Ctrl+Alt+U FileMenu_LoadDeck1 Ctrl+o FileMenu_LoadDeck2 Ctrl+Shift+O FileMenu_Quit Ctrl+q +LibraryMenu_SearchInCurrentView Ctrl+f +LibraryMenu_SearchInAllTracks Ctrl+Shift+F LibraryMenu_NewPlaylist Ctrl+n LibraryMenu_NewCrate Ctrl+Shift+N ViewMenu_ShowSkinSettings Ctrl+1 diff --git a/res/keyboard/de_CH.kbd.cfg b/res/keyboard/de_CH.kbd.cfg index 99ccb5361ec0..99d111609898 100644 --- a/res/keyboard/de_CH.kbd.cfg +++ b/res/keyboard/de_CH.kbd.cfg @@ -141,6 +141,8 @@ vinylcontrol_cueing Ctrl+Alt+U FileMenu_LoadDeck1 Ctrl+o FileMenu_LoadDeck2 Ctrl+Shift+O FileMenu_Quit Ctrl+q +LibraryMenu_SearchInCurrentView Ctrl+f +LibraryMenu_SearchInAllTracks Ctrl+Shift+F LibraryMenu_NewPlaylist Ctrl+n LibraryMenu_NewCrate Ctrl+Shift+N ViewMenu_ShowSkinSettings Ctrl+1 diff --git a/res/keyboard/de_DE.kbd.cfg b/res/keyboard/de_DE.kbd.cfg index 9fdb8e712b6e..6055dc8bdfca 100644 --- a/res/keyboard/de_DE.kbd.cfg +++ b/res/keyboard/de_DE.kbd.cfg @@ -141,6 +141,8 @@ vinylcontrol_cueing Ctrl+Alt+U FileMenu_LoadDeck1 Ctrl+o FileMenu_LoadDeck2 Ctrl+Shift+O FileMenu_Quit Ctrl+q +LibraryMenu_SearchInCurrentView Ctrl+f +LibraryMenu_SearchInAllTracks Ctrl+Shift+F LibraryMenu_NewPlaylist Ctrl+n LibraryMenu_NewCrate Ctrl+Shift+N ViewMenu_ShowSkinSettings Ctrl+1 diff --git a/res/keyboard/el_GR.kbd.cfg b/res/keyboard/el_GR.kbd.cfg index 140ce77cf7af..622e1ff97417 100644 --- a/res/keyboard/el_GR.kbd.cfg +++ b/res/keyboard/el_GR.kbd.cfg @@ -145,6 +145,8 @@ vinylcontrol_cueing Ctrl+Alt+Θ FileMenu_LoadDeck1 Ctrl+o FileMenu_LoadDeck2 Ctrl+Shift+O FileMenu_Quit Ctrl+q +LibraryMenu_SearchInCurrentView Ctrl+f +LibraryMenu_SearchInAllTracks Ctrl+Shift+F LibraryMenu_NewPlaylist Ctrl+n LibraryMenu_NewCrate Ctrl+Shift+N ViewMenu_ShowSkinSettings Ctrl+1 diff --git a/res/keyboard/en_US.kbd.cfg b/res/keyboard/en_US.kbd.cfg index 7fe10956a9fc..7d8c18a89a7a 100644 --- a/res/keyboard/en_US.kbd.cfg +++ b/res/keyboard/en_US.kbd.cfg @@ -141,6 +141,8 @@ vinylcontrol_cueing Ctrl+Alt+U FileMenu_LoadDeck1 Ctrl+o FileMenu_LoadDeck2 Ctrl+Shift+O FileMenu_Quit Ctrl+q +LibraryMenu_SearchInCurrentView Ctrl+f +LibraryMenu_SearchInAllTracks Ctrl+Shift+F LibraryMenu_NewPlaylist Ctrl+n LibraryMenu_NewCrate Ctrl+Shift+N ViewMenu_ShowSkinSettings Ctrl+1 diff --git a/res/keyboard/es_ES.kbd.cfg b/res/keyboard/es_ES.kbd.cfg index 72423c40319d..73d2f033c99e 100644 --- a/res/keyboard/es_ES.kbd.cfg +++ b/res/keyboard/es_ES.kbd.cfg @@ -141,6 +141,8 @@ vinylcontrol_cueing Ctrl+Alt+U FileMenu_LoadDeck1 Ctrl+o FileMenu_LoadDeck2 Ctrl+Shift+O FileMenu_Quit Ctrl+q +LibraryMenu_SearchInCurrentView Ctrl+f +LibraryMenu_SearchInAllTracks Ctrl+Shift+F LibraryMenu_NewPlaylist Ctrl+n LibraryMenu_NewCrate Ctrl+Shift+N ViewMenu_ShowSkinSettings Ctrl+1 diff --git a/res/keyboard/fi_FI.kbd.cfg b/res/keyboard/fi_FI.kbd.cfg index ea197ca1f3ad..36ec3d6cd98f 100644 --- a/res/keyboard/fi_FI.kbd.cfg +++ b/res/keyboard/fi_FI.kbd.cfg @@ -141,6 +141,8 @@ vinylcontrol_cueing Ctrl+Alt+U FileMenu_LoadDeck1 Ctrl+o FileMenu_LoadDeck2 Ctrl+Shift+O FileMenu_Quit Ctrl+q +LibraryMenu_SearchInCurrentView Ctrl+f +LibraryMenu_SearchInAllTracks Ctrl+Shift+F LibraryMenu_NewPlaylist Ctrl+n LibraryMenu_NewCrate Ctrl+Shift+N ViewMenu_ShowSkinSettings Ctrl+1 diff --git a/res/keyboard/fr_CH.kbd.cfg b/res/keyboard/fr_CH.kbd.cfg index d3831aeb8d0d..df828dd9467e 100644 --- a/res/keyboard/fr_CH.kbd.cfg +++ b/res/keyboard/fr_CH.kbd.cfg @@ -141,6 +141,8 @@ vinylcontrol_cueing Ctrl+Alt+U FileMenu_LoadDeck1 Ctrl+o FileMenu_LoadDeck2 Ctrl+Shift+O FileMenu_Quit Ctrl+q +LibraryMenu_SearchInCurrentView Ctrl+f +LibraryMenu_SearchInAllTracks Ctrl+Shift+F LibraryMenu_NewPlaylist Ctrl+n LibraryMenu_NewCrate Ctrl+Shift+N ViewMenu_ShowSkinSettings Ctrl+1 diff --git a/res/keyboard/fr_FR.kbd.cfg b/res/keyboard/fr_FR.kbd.cfg index eb1a5ba474a4..0c89dc3579d7 100644 --- a/res/keyboard/fr_FR.kbd.cfg +++ b/res/keyboard/fr_FR.kbd.cfg @@ -141,6 +141,8 @@ vinylcontrol_cueing Ctrl+Alt+U FileMenu_LoadDeck1 Ctrl+o FileMenu_LoadDeck2 Ctrl+Shift+O FileMenu_Quit Ctrl+q +LibraryMenu_SearchInCurrentView Ctrl+f +LibraryMenu_SearchInAllTracks Ctrl+Shift+F LibraryMenu_NewPlaylist Ctrl+n LibraryMenu_NewCrate Ctrl+Shift+N ViewMenu_ShowSkinSettings Ctrl+1 diff --git a/res/keyboard/it_IT.kbd.cfg b/res/keyboard/it_IT.kbd.cfg index 9ef866ec5713..4f6e69649634 100644 --- a/res/keyboard/it_IT.kbd.cfg +++ b/res/keyboard/it_IT.kbd.cfg @@ -141,6 +141,8 @@ vinylcontrol_cueing Ctrl+Alt+U FileMenu_LoadDeck1 Ctrl+o FileMenu_LoadDeck2 Ctrl+Shift+O FileMenu_Quit Ctrl+q +LibraryMenu_SearchInCurrentView Ctrl+f +LibraryMenu_SearchInAllTracks Ctrl+Shift+F LibraryMenu_NewPlaylist Ctrl+n LibraryMenu_NewCrate Ctrl+Shift+N ViewMenu_ShowSkinSettings Ctrl+1 diff --git a/res/keyboard/ru_RU.kbd.cfg b/res/keyboard/ru_RU.kbd.cfg index beda6b6fa7b7..70672f10bb34 100644 --- a/res/keyboard/ru_RU.kbd.cfg +++ b/res/keyboard/ru_RU.kbd.cfg @@ -141,6 +141,8 @@ vinylcontrol_cueing Ctrl+Alt+Г FileMenu_LoadDeck1 Ctrl+o FileMenu_LoadDeck2 Ctrl+Shift+O FileMenu_Quit Ctrl+q +LibraryMenu_SearchInCurrentView Ctrl+f +LibraryMenu_SearchInAllTracks Ctrl+Shift+F LibraryMenu_NewPlaylist Ctrl+n LibraryMenu_NewCrate Ctrl+Shift+N ViewMenu_ShowSkinSettings Ctrl+1 diff --git a/src/library/library.cpp b/src/library/library.cpp index 91a5acf8f5ab..5c556366371b 100644 --- a/src/library/library.cpp +++ b/src/library/library.cpp @@ -730,13 +730,27 @@ void Library::setEditMetadataSelectedClick(bool enabled) { emit setSelectedClick(enabled); } +void Library::slotSearchInCurrentView() { + m_pLibraryControl->setLibraryFocus(FocusWidget::Searchbar, Qt::ShortcutFocusReason); +} + +void Library::slotSearchInAllTracks() { + searchTracksInCollection(); +} + +void Library::searchTracksInCollection() { + VERIFY_OR_DEBUG_ASSERT(m_pMixxxLibraryFeature) { + return; + } + m_pMixxxLibraryFeature->selectAndActivate(); + m_pLibraryControl->setLibraryFocus(FocusWidget::Searchbar, Qt::ShortcutFocusReason); +} + void Library::searchTracksInCollection(const QString& query) { VERIFY_OR_DEBUG_ASSERT(m_pMixxxLibraryFeature) { return; } m_pMixxxLibraryFeature->searchAndActivate(query); - emit switchToView(m_sTrackViewName); - m_pSidebarModel->activateDefaultSelection(); } #ifdef __ENGINEPRIME__ diff --git a/src/library/library.h b/src/library/library.h index d872b3c5d069..1c93c53c4a23 100644 --- a/src/library/library.h +++ b/src/library/library.h @@ -98,6 +98,10 @@ class Library: public QObject { void setRowHeight(int rowHeight); void setEditMetadataSelectedClick(bool enable); + /// Switches to the internal track collection view + /// and focuses the search box. + void searchTracksInCollection(); + /// Triggers a new search in the internal track collection /// and shows the results by switching the view. void searchTracksInCollection(const QString& query); @@ -126,6 +130,8 @@ class Library: public QObject { void slotRefreshLibraryModels(); void slotCreatePlaylist(); void slotCreateCrate(); + void slotSearchInCurrentView(); + void slotSearchInAllTracks(); void onSkinLoadFinished(); void slotSaveCurrentViewState() const; void slotRestoreCurrentViewState() const; diff --git a/src/library/librarycontrol.cpp b/src/library/librarycontrol.cpp index b54bc75a8257..5ee5fab23246 100644 --- a/src/library/librarycontrol.cpp +++ b/src/library/librarycontrol.cpp @@ -944,15 +944,11 @@ FocusWidget LibraryControl::getFocusedWidget() { } } -void LibraryControl::setLibraryFocus(FocusWidget newFocusWidget) { - if (!QApplication::focusWindow()) { - qInfo() << "No Mixxx window, popup or menu has focus." - << "Don't attempt to focus a specific widget."; - return; - } - - // ignore no-op - if (newFocusWidget == m_focusedWidget) { +void LibraryControl::setLibraryFocus(FocusWidget newFocusWidget, Qt::FocusReason focusReason) { + // The search box wants to do special handling when the Ctrl+f is used + // while it is already focused. Non-shortcut cases should still be a + // no-op when a control is already focused. + if (newFocusWidget == m_focusedWidget && focusReason != Qt::ShortcutFocusReason) { return; } @@ -961,13 +957,13 @@ void LibraryControl::setLibraryFocus(FocusWidget newFocusWidget) { VERIFY_OR_DEBUG_ASSERT(m_pSearchbox) { return; } - m_pSearchbox->setFocus(); + m_pSearchbox->setFocus(focusReason); return; case FocusWidget::Sidebar: VERIFY_OR_DEBUG_ASSERT(m_pSidebarWidget) { return; } - m_pSidebarWidget->setFocus(); + m_pSidebarWidget->setFocus(focusReason); return; case FocusWidget::TracksTable: VERIFY_OR_DEBUG_ASSERT(m_pLibraryWidget) { diff --git a/src/library/librarycontrol.h b/src/library/librarycontrol.h index 584d2df1a7a7..2f8c234fca70 100644 --- a/src/library/librarycontrol.h +++ b/src/library/librarycontrol.h @@ -58,8 +58,9 @@ class LibraryControl : public QObject { void bindLibraryWidget(WLibrary* pLibrary, KeyboardEventFilter* pKeyboard); void bindSidebarWidget(WLibrarySidebar* pLibrarySidebar); void bindSearchboxWidget(WSearchLineEdit* pSearchbox); - // Give the keyboard focus to one of the library widgets - void setLibraryFocus(FocusWidget newFocusWidget); + /// Give the keyboard focus to one of the library widgets + void setLibraryFocus(FocusWidget newFocusWidget, + Qt::FocusReason focusReason = Qt::OtherFocusReason); FocusWidget getFocusedWidget(); signals: diff --git a/src/library/libraryfeature.cpp b/src/library/libraryfeature.cpp index 51ad6ae3fac6..7c88fbf93bdc 100644 --- a/src/library/libraryfeature.cpp +++ b/src/library/libraryfeature.cpp @@ -32,6 +32,17 @@ LibraryFeature::LibraryFeature( } } +void LibraryFeature::selectAndActivate(const QModelIndex& index) { + if (index.isValid()) { + emit featureSelect(this, index); + activateChild(index); + } else { + // calling featureSelect with invalid index will select the root item + emit featureSelect(this, QModelIndex()); + activate(); + } +} + QStringList LibraryFeature::getPlaylistFiles(QFileDialog::FileMode mode) const { QString lastPlaylistDirectory = m_pConfig->getValue( ConfigKey("[Library]", "LastImportExportPlaylistDirectory"), diff --git a/src/library/libraryfeature.h b/src/library/libraryfeature.h index 9a91e748ddda..3c0a204cc436 100644 --- a/src/library/libraryfeature.h +++ b/src/library/libraryfeature.h @@ -106,6 +106,10 @@ class LibraryFeature : public QObject { const UserSettingsPointer m_pConfig; public slots: + /// Pretend that the user has clicked on a tree item belonging + /// to this LibraryFeature by updating both the library view + /// and the sidebar selection. + void selectAndActivate(const QModelIndex& index = QModelIndex()); // called when you single click on the root item virtual void activate() = 0; // called when you single click on a child item, e.g., a concrete playlist or crate diff --git a/src/library/mixxxlibraryfeature.cpp b/src/library/mixxxlibraryfeature.cpp index 4ea52b1e6a1f..cee77807b7c3 100644 --- a/src/library/mixxxlibraryfeature.cpp +++ b/src/library/mixxxlibraryfeature.cpp @@ -173,7 +173,7 @@ void MixxxLibraryFeature::searchAndActivate(const QString& query) { return; } m_pLibraryTableModel->search(query); - activate(); + selectAndActivate(); } #ifdef __ENGINEPRIME__ diff --git a/src/library/trackset/playlistfeature.cpp b/src/library/trackset/playlistfeature.cpp index c5f0258ec7dd..d1cacdd67eb9 100644 --- a/src/library/trackset/playlistfeature.cpp +++ b/src/library/trackset/playlistfeature.cpp @@ -308,8 +308,7 @@ void PlaylistFeature::slotPlaylistTableChanged(int playlistId) { // Else (root item was selected or for some reason no index could be created) // there's nothing to do: either no child was selected earlier, or the root // was selected and will remain selected after the child model was rebuilt. - activateChild(newIndex); - emit featureSelect(this, newIndex); + selectAndActivate(newIndex); } } diff --git a/src/library/trackset/setlogfeature.cpp b/src/library/trackset/setlogfeature.cpp index 68d4bd80a783..6bc4e634d840 100644 --- a/src/library/trackset/setlogfeature.cpp +++ b/src/library/trackset/setlogfeature.cpp @@ -707,13 +707,8 @@ void SetlogFeature::slotPlaylistTableChanged(int playlistId) { newIndex = m_pSidebarModel->index(selectedYearIndexRow - 1, 0); } } - if (newIndex.isValid()) { - emit featureSelect(this, newIndex); - activateChild(newIndex); - } else if (rootWasSelected) { - // calling featureSelect with invalid index will select the root item - emit featureSelect(this, newIndex); - activate(); // to reload the new current playlist + if (newIndex.isValid() || rootWasSelected) { + selectAndActivate(newIndex); } } diff --git a/src/mixxxmainwindow.cpp b/src/mixxxmainwindow.cpp index 952679645c1d..53bc573fd544 100644 --- a/src/mixxxmainwindow.cpp +++ b/src/mixxxmainwindow.cpp @@ -933,6 +933,16 @@ void MixxxMainWindow::connectMenuBar() { } if (m_pCoreServices->getLibrary()) { + connect(m_pMenuBar, + &WMainMenuBar::searchInCurrentView, + m_pCoreServices->getLibrary().get(), + &Library::slotSearchInCurrentView, + Qt::UniqueConnection); + connect(m_pMenuBar, + &WMainMenuBar::searchInAllTracks, + m_pCoreServices->getLibrary().get(), + &Library::slotSearchInAllTracks, + Qt::UniqueConnection); connect(m_pMenuBar, &WMainMenuBar::createCrate, m_pCoreServices->getLibrary().get(), diff --git a/src/skin/legacy/legacyskinparser.cpp b/src/skin/legacy/legacyskinparser.cpp index a509f8c0fb98..4ea024155c6b 100644 --- a/src/skin/legacy/legacyskinparser.cpp +++ b/src/skin/legacy/legacyskinparser.cpp @@ -1525,6 +1525,19 @@ QWidget* LegacySkinParser::parseSearchBox(const QDomElement& node) { commonWidgetSetup(node, pLineEditSearch, false); pLineEditSearch->setup(node, *m_pContext); + // Translate shortcuts to native text + QString searchInCurrentViewShortcut = + localizeShortcutKeys(m_pKeyboard->getKeyboardConfig()->getValue( + ConfigKey("[KeyboardShortcuts]", + "LibraryMenu_SearchInCurrentView"), + "Ctrl+f")); + QString searchInAllTracksShortcut = + localizeShortcutKeys(m_pKeyboard->getKeyboardConfig()->getValue( + ConfigKey("[KeyboardShortcuts]", + "LibraryMenu_SearchInAllTracks"), + "Ctrl+Shift+F")); + pLineEditSearch->setupToolTip(searchInCurrentViewShortcut, searchInAllTracksShortcut); + m_pLibrary->bindSearchboxWidget(pLineEditSearch); return pLineEditSearch; @@ -2587,9 +2600,6 @@ void LegacySkinParser::addShortcutToToolTip(WBaseWidget* pWidget, QString tooltip; - // translate shortcut to native text - QString nativeShortcut = QKeySequence(shortcut, QKeySequence::PortableText).toString(QKeySequence::NativeText); - tooltip += "\n"; tooltip += tr("Shortcut"); if (!cmd.isEmpty()) { @@ -2597,10 +2607,16 @@ void LegacySkinParser::addShortcutToToolTip(WBaseWidget* pWidget, tooltip += cmd; } tooltip += ": "; - tooltip += nativeShortcut; + tooltip += localizeShortcutKeys(shortcut); pWidget->appendBaseTooltip(tooltip); } +QString LegacySkinParser::localizeShortcutKeys(const QString& shortcut) { + // Translate shortcut to native text + return QKeySequence(shortcut, QKeySequence::PortableText) + .toString(QKeySequence::NativeText); +} + QString LegacySkinParser::parseLaunchImageStyle(const QDomNode& skinDoc) { QString schemeLaunchImageStyle; // Check if the skins has color schemes diff --git a/src/skin/legacy/legacyskinparser.h b/src/skin/legacy/legacyskinparser.h index 1b3a9050dc53..f7b100bc63d3 100644 --- a/src/skin/legacy/legacyskinparser.h +++ b/src/skin/legacy/legacyskinparser.h @@ -141,6 +141,7 @@ class LegacySkinParser : public QObject, public SkinParser { bool setupPosition=true); void setupConnections(const QDomNode& node, WBaseWidget* pWidget); void addShortcutToToolTip(WBaseWidget* pWidget, const QString& shortcut, const QString& cmd); + QString localizeShortcutKeys(const QString& shortcut); QString getLibraryStyle(const QDomNode& node); QString lookupNodeGroup(const QDomElement& node); diff --git a/src/widget/wlibrary.h b/src/widget/wlibrary.h index a017e9d823dd..c2ac12a3bcff 100644 --- a/src/widget/wlibrary.h +++ b/src/widget/wlibrary.h @@ -56,7 +56,8 @@ class WLibrary : public QStackedWidget, public WBaseWidget { } signals: - FocusWidget setLibraryFocus(FocusWidget newFocus); + FocusWidget setLibraryFocus(FocusWidget newFocus, + Qt::FocusReason focusReason = Qt::OtherFocusReason); public slots: // Show the view registered with the given name. Does nothing if the current diff --git a/src/widget/wlibrarysidebar.h b/src/widget/wlibrarysidebar.h index 4fa356e78b46..91d33befd709 100644 --- a/src/widget/wlibrarysidebar.h +++ b/src/widget/wlibrarysidebar.h @@ -37,7 +37,8 @@ class WLibrarySidebar : public QTreeView, public WBaseWidget { void rightClicked(const QPoint&, const QModelIndex&); void renameItem(const QModelIndex&); void deleteItem(const QModelIndex&); - FocusWidget setLibraryFocus(FocusWidget newFocus); + FocusWidget setLibraryFocus(FocusWidget newFocus, + Qt::FocusReason focusReason = Qt::OtherFocusReason); protected: bool event(QEvent* pEvent) override; diff --git a/src/widget/wmainmenubar.cpp b/src/widget/wmainmenubar.cpp index 0723efbec415..99f398f10cbf 100644 --- a/src/widget/wmainmenubar.cpp +++ b/src/widget/wmainmenubar.cpp @@ -168,6 +168,34 @@ void WMainMenuBar::initialize() { pLibraryMenu->addSeparator(); + QString searchHereTitle = tr("Search in Current View..."); + QString searchHereText = tr("Search for tracks in the current library view"); + auto* pSearchHere = new QAction(searchHereTitle, this); + pSearchHere->setShortcut(QKeySequence(m_pKbdConfig->getValue( + ConfigKey("[KeyboardShortcuts]", "LibraryMenu_SearchInCurrentView"), + tr("Ctrl+f")))); + pSearchHere->setShortcutContext(Qt::ApplicationShortcut); + pSearchHere->setStatusTip(searchHereText); + pSearchHere->setWhatsThis(buildWhatsThis(searchHereTitle, searchHereText)); + connect(pSearchHere, &QAction::triggered, this, &WMainMenuBar::searchInCurrentView); + pLibraryMenu->addAction(pSearchHere); + + QString searchAllTitle = tr("Search in Tracks Library..."); + QString searchAllText = + tr("Search in the internal track collection under \"Tracks\" in " + "the library"); + auto* pSearchAll = new QAction(searchAllTitle, this); + pSearchAll->setShortcut(QKeySequence(m_pKbdConfig->getValue( + ConfigKey("[KeyboardShortcuts]", "LibraryMenu_SearchInAllTracks"), + tr("Ctrl+Shift+F")))); + pSearchAll->setShortcutContext(Qt::ApplicationShortcut); + pSearchAll->setStatusTip(searchAllText); + pSearchAll->setWhatsThis(buildWhatsThis(searchAllText, searchAllText)); + connect(pSearchAll, &QAction::triggered, this, &WMainMenuBar::searchInAllTracks); + pLibraryMenu->addAction(pSearchAll); + + pLibraryMenu->addSeparator(); + QString createPlaylistTitle = tr("Create &New Playlist"); QString createPlaylistText = tr("Create a new playlist"); auto* pLibraryCreatePlaylist = new QAction(createPlaylistTitle, this); diff --git a/src/widget/wmainmenubar.h b/src/widget/wmainmenubar.h index 98ec66c698f7..dd9fafadbcee 100644 --- a/src/widget/wmainmenubar.h +++ b/src/widget/wmainmenubar.h @@ -67,6 +67,8 @@ class WMainMenuBar : public QMenuBar { #ifdef __ENGINEPRIME__ void exportLibrary(); #endif + void searchInCurrentView(); + void searchInAllTracks(); void showAbout(); void showKeywheel(bool visible); void showPreferences(); diff --git a/src/widget/wsearchlineedit.cpp b/src/widget/wsearchlineedit.cpp index 1154fdf88e43..fbb728c70cb3 100644 --- a/src/widget/wsearchlineedit.cpp +++ b/src/widget/wsearchlineedit.cpp @@ -122,12 +122,6 @@ WSearchLineEdit::WSearchLineEdit(QWidget* pParent, UserSettingsPointer pConfig) this, &WSearchLineEdit::slotClearSearch); - QShortcut* setFocusShortcut = new QShortcut(QKeySequence(tr("Ctrl+F", "Search|Focus")), this); - connect(setFocusShortcut, - &QShortcut::activated, - this, - &WSearchLineEdit::slotSetShortcutFocus); - // Set up a timer to search after a few hundred milliseconds timeout. This // stops us from thrashing the database if you type really fast. m_debouncingTimer.setSingleShot(true); @@ -217,31 +211,35 @@ void WSearchLineEdit::setup(const QDomNode& node, const SkinContext& context) { setPalette(pal); m_clearButton->setToolTip(tr("Clear input") + "\n" + - tr("Clear the search bar input field") + "\n\n" + - - tr("Shortcut") + ": \n" + - tr("Ctrl+Backspace")); + tr("Clear the search bar input field")); +} +void WSearchLineEdit::setupToolTip(const QString& searchInCurrentViewShortcut, + const QString& searchInAllTracksShortcut) { setBaseTooltip(tr("Search", "noun") + "\n" + - tr("Enter a string to search for") + "\n" + - tr("Use operators like bpm:115-128, artist:BooFar, -year:1990") + - "\n" + tr("For more information see User Manual > Mixxx Library") + - "\n\n" + - tr("Shortcuts") + ": \n" + - tr("Ctrl+F") + " " + - tr("Focus", "Give search bar input focus") + "\n" + - tr("Return") + " " + - tr("Trigger search before search-as-you-type timeout or" - "jump to tracks view afterwards") + + tr("Enter a string to search for.") + " " + + tr("Use operators like bpm:115-128, artist:BooFar, -year:1990.") + + "\n" + tr("See User Manual > Mixxx Library for more information.") + + "\n\n" + searchInCurrentViewShortcut + ": " + + tr("Focus/Select All (Search in current view)", + "Give search bar input focus") + + "\n" + searchInAllTracksShortcut + ": " + + tr("Focus/Select All (Search in \'Tracks\' library view)") + + "\n\n" + tr("Additional Shortcuts When Focused:") + "\n" + + tr("Return") + ": " + + tr("Trigger search before search-as-you-type timeout or " + "focus tracks view afterwards") + "\n" + - tr("Ctrl+Backspace") + " " + - tr("Clear input", "Clear the search bar input field") + "\n" + - tr("Ctrl+Space") + " " + + tr("Esc or Ctrl+Return") + ": " + + tr("Immediately trigger search and focus tracks view", + "Exit search bar and leave focus") + + "\n" + tr("Ctrl+Space") + ": " + tr("Toggle search history", "Shows/hides the search history entries") + "\n" + - tr("Delete or Backspace") + " " + tr("Delete query from history") + "\n" + - tr("Esc") + " " + tr("Exit search", "Exit search bar and leave focus")); + tr("Delete or Backspace") + + " (" + tr("in search history") + "): " + + tr("Delete query from history")); } void WSearchLineEdit::loadQueriesFromConfig() { @@ -311,15 +309,12 @@ QString WSearchLineEdit::getSearchText() const { if (isEnabled()) { DEBUG_ASSERT(!currentText().isNull()); QString text = currentText(); - QCompleter* pCompleter = completer(); - if (pCompleter && hasSelectedText()) { - if (text.startsWith(pCompleter->completionPrefix()) && - pCompleter->completionPrefix().size() == lineEdit()->cursorPosition()) { - // Search for the entered text until the user has accepted the - // completion by pressing Enter or changed/deselected the selected - // completion text with Right or Left key - return pCompleter->completionPrefix(); - } + QString completionPrefix; + if (hasCompletionAvailable(&completionPrefix)) { + // Search for the entered text until the user has accepted the + // completion by pressing Enter or changed/deselected the selected + // completion text with Right or Left key + return completionPrefix; } return text; } else { @@ -402,7 +397,12 @@ void WSearchLineEdit::keyPressEvent(QKeyEvent* keyEvent) { if (slotClearSearchIfClearButtonHasFocus()) { return; } - if (hasSelectedText()) { + if (keyEvent->modifiers() & Qt::ControlModifier) { + // Esc and Ctrl+Enter should have the same effect + emit setLibraryFocus(FocusWidget::TracksTable); + return; + } + if (hasCompletionAvailable()) { QComboBox::keyPressEvent(keyEvent); slotTriggerSearch(); return; @@ -789,11 +789,18 @@ void WSearchLineEdit::slotTextChanged(const QString& text) { m_saveTimer.start(kSaveTimeoutMillis); } -void WSearchLineEdit::slotSetShortcutFocus() { - if (hasFocus()) { +void WSearchLineEdit::setFocus(Qt::FocusReason focusReason) { + if (!hasFocus()) { + // selectAll will be called by setFocus - but only if hasFocus + // was false previously and focusReason is Tab, Backtab or Shortcut + QWidget::setFocus(focusReason); + } else if (focusReason == Qt::TabFocusReason || + focusReason == Qt::BacktabFocusReason || + focusReason == Qt::ShortcutFocusReason) { + // If this widget already had focus (which can happen when the user + // presses the shortcut key while already in the searchbox), + // we need to manually simulate this behavior instead. lineEdit()->selectAll(); - } else { - setFocus(Qt::ShortcutFocusReason); } } @@ -809,3 +816,17 @@ void WSearchLineEdit::slotSetFont(const QFont& font) { bool WSearchLineEdit::hasSelectedText() const { return lineEdit()->hasSelectedText(); } + +bool WSearchLineEdit::hasCompletionAvailable(QString* completionPrefix) const { + QCompleter* pCompleter = completer(); + QString prefix = pCompleter ? pCompleter->completionPrefix() : QString(); + if (!prefix.isEmpty() && hasSelectedText() && + lineEdit()->text().startsWith(prefix) && + prefix.size() == lineEdit()->cursorPosition()) { + if (completionPrefix) { + *completionPrefix = prefix; + } + return true; + } + return false; +} diff --git a/src/widget/wsearchlineedit.h b/src/widget/wsearchlineedit.h index 45d2d7910ca2..e56507294705 100644 --- a/src/widget/wsearchlineedit.h +++ b/src/widget/wsearchlineedit.h @@ -36,6 +36,10 @@ class WSearchLineEdit : public QComboBox, public WBaseWidget { ~WSearchLineEdit(); void setup(const QDomNode& node, const SkinContext& context); + void setupToolTip(const QString& searchInCurrentViewShortcut, + const QString& searchInAllTracksShortcut); + + void setFocus(Qt::FocusReason focusReason); protected: void resizeEvent(QResizeEvent*) override; @@ -47,7 +51,8 @@ class WSearchLineEdit : public QComboBox, public WBaseWidget { signals: void search(const QString& text); - FocusWidget setLibraryFocus(FocusWidget newFocusWidget); + FocusWidget setLibraryFocus(FocusWidget newFocusWidget, + Qt::FocusReason focusReason = Qt::OtherFocusReason); public slots: void slotSetFont(const QFont& font); @@ -65,7 +70,6 @@ class WSearchLineEdit : public QComboBox, public WBaseWidget { void slotDeleteCurrentItem(); private slots: - void slotSetShortcutFocus(); void slotTextChanged(const QString& text); void slotIndexChanged(int index); @@ -92,6 +96,7 @@ class WSearchLineEdit : public QComboBox, public WBaseWidget { void deleteSelectedListItem(); void triggerSearchDebounced(); bool hasSelectedText() const; + bool hasCompletionAvailable(QString* completionPrefix = nullptr) const; inline int findCurrentTextIndex() { return findData(currentText(), Qt::DisplayRole);