Skip to content

Commit

Permalink
Search related Tracks: add checkboxes + Search button
Browse files Browse the repository at this point in the history
Menu items are checkboxes now, so there are two ways to search for tracks:
* single filter (as before):
  click an action (text), or press `Return` on a selected checkbox, to trigger its search
* combine filters:
   * check multiple actions (click `[ ]` box or press `Space` on a selected item
   * click **Search** (or press `S`, or `Return` on Search) to search with all checked filters
  • Loading branch information
ronso0 committed Oct 24, 2023
1 parent c4204d2 commit 3e18079
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 1 deletion.
111 changes: 110 additions & 1 deletion src/widget/wsearchrelatedtracksmenu.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
#include "widget/wsearchrelatedtracksmenu.h"

#include <QCheckBox>
#include <QMouseEvent>
#include <QScreen>
#include <QStyleOptionButton>
#include <QWidgetAction>

#include "moc_wsearchrelatedtracksmenu.cpp"
#include "track/track.h"
#include "util/math.h"
#include "util/parented_ptr.h"
#include "util/qt.h"
#include "util/widgethelper.h"

Expand Down Expand Up @@ -78,12 +83,29 @@ void WSearchRelatedTracksMenu::addTriggerSearchAction(
elideActionText(
actionTextPrefix,
elidableTextSuffix);
addAction(

auto pCheckBox = make_parented<QCheckBox>(
mixxx::escapeTextPropertyWithoutShortcuts(elidedActionText),
this);
pCheckBox->setProperty("query", searchQuery);
connect(pCheckBox.get(),
&QCheckBox::toggled,
this,
&WSearchRelatedTracksMenu::updateSearchButton);
// Use the event filter to capture clicks on the checkbox label
pCheckBox.get()->installEventFilter(this);

auto pAction = make_parented<QWidgetAction>(this);
pAction->setDefaultWidget(pCheckBox.get());
// While the checkbox is selected (via keyboard, not hovered by pointer)
// pressing Space will toggle it whereas pressing Return triggers the action.
connect(pAction.get(),
&QAction::triggered,
this,
[this, searchQuery]() {
emit triggerSearch(searchQuery);
});
addAction(pAction.get());
}

QString WSearchRelatedTracksMenu::elideActionText(
Expand Down Expand Up @@ -124,6 +146,13 @@ void WSearchRelatedTracksMenu::addActionsForTrack(
// string concatenation will fail at runtime!

// Mixing actions
// TODO(ronso0) Nice to have: remember & restore checked state of filters.
// This would simplify repeating the previous filter combo, e.g. search again
// for genre and BPM.
// For good UX (in decks) all track menu widgets of a certain deck should
// share a track menu (track menu features are/should be the same anyway for
// WTrackProperty, WTrackText & WTrackWidgetGroup.
// LegacySkinParser might take care of this :|
bool addSeparatorBeforeNextAction = !isEmpty();
{
const auto keyText = track.getKeyText();
Expand Down Expand Up @@ -332,4 +361,84 @@ void WSearchRelatedTracksMenu::addActionsForTrack(
locationPathWithTerminator);
}
}

addSeparator();

m_pSearchAction = make_parented<QAction>(tr("&Search"), this);
// TODO(ronso0) Add 'looking glass' icon?
addAction(m_pSearchAction.get());
m_pSearchAction.get()->setDisabled(true);
connect(m_pSearchAction.get(),
&QAction::triggered,
this,
&WSearchRelatedTracksMenu::combineQueriesTriggerSearch);
}

bool WSearchRelatedTracksMenu::eventFilter(QObject* obj, QEvent* e) {
if (e->type() == QEvent::MouseButtonPress) {
// Clicking the text of a checkbox triggers the search, ignoring other
// checked boxes.
// Clicks in other places are passed on to the event filter so toggling
// the checkbox is happening as usual.
QCheckBox* box = qobject_cast<QCheckBox*>(obj);
if (box) {
QMouseEvent* me = static_cast<QMouseEvent*>(e);
VERIFY_OR_DEBUG_ASSERT(me) {
return true;
}
QStyleOptionButton option;
option.initFrom(box);
auto pStyle = box->style();

Check warning on line 391 in src/widget/wsearchrelatedtracksmenu.cpp

View workflow job for this annotation

GitHub Actions / clang-tidy

'auto pStyle' can be declared as 'auto *pStyle' [readability-qualified-auto]
if (!pStyle) {
return true;
}
const QRect labelRect = pStyle->subElementRect(QStyle::SE_CheckBoxContents,
&option,
box);
if (labelRect.contains(me->pos())) {
// Text was clicked, trigger the search.
const QString query = box->property("query").toString();
emit triggerSearch(query);
// Note that this click will not emit QAction::triggered like
// when pressing Return on a selected action, hence we need to
// make sure WTrackMenu closes when receiving triggerSearch().
}
}
}
return QObject::eventFilter(obj, e);
}

void WSearchRelatedTracksMenu::updateSearchButton() {
// Enable the Search button if at least one box is checked.
VERIFY_OR_DEBUG_ASSERT(m_pSearchAction) {
return;
}
m_pSearchAction->setDisabled(true);
for (const auto* child : std::as_const(children())) {
const auto* box = qobject_cast<const QCheckBox*>(child);
if (box && box->isChecked()) {
m_pSearchAction->setEnabled(true);
break;
}
}
}

void WSearchRelatedTracksMenu::combineQueriesTriggerSearch() {
// collect queries of all checked checkboxes
QStringList queries;
for (const auto* child : std::as_const(children())) {
const auto* box = qobject_cast<const QCheckBox*>(child);
if (box && box->isChecked()) {
QString query = box->property("query").toString();
if (!query.isEmpty()) {
queries.append(query);
}
}
}
if (queries.isEmpty()) {
return;
} else {
QString queryCombo = queries.join(QStringLiteral(" "));
emit triggerSearch(queryCombo);
}
}
9 changes: 9 additions & 0 deletions src/widget/wsearchrelatedtracksmenu.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include <QMenu>

#include "util/parented_ptr.h"

class Track;

class WSearchRelatedTracksMenu : public QMenu {
Expand All @@ -13,11 +15,16 @@ class WSearchRelatedTracksMenu : public QMenu {

void addActionsForTrack(
const Track& track);
bool eventFilter(QObject* obj, QEvent* e) override;

signals:
void triggerSearch(
const QString& searchQuery);

private slots:
void updateSearchButton();
void combineQueriesTriggerSearch();

private:
void addTriggerSearchAction(
bool* /*in/out*/ pAddSeparatorBeforeNextAction,
Expand All @@ -27,4 +34,6 @@ class WSearchRelatedTracksMenu : public QMenu {
QString elideActionText(
const QString& actionTextPrefix,
const QString& elidableTextSuffix) const;

parented_ptr<QAction> m_pSearchAction;
};
1 change: 1 addition & 0 deletions src/widget/wtrackmenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ void WTrackMenu::createMenus() {
this,
[this](const QString& searchQuery) {
m_pLibrary->searchTracksInCollection(searchQuery);
hide();
});
}

Expand Down

0 comments on commit 3e18079

Please sign in to comment.