Skip to content

Commit

Permalink
Merge pull request #10825 from ronso0/track-menu-per-deck
Browse files Browse the repository at this point in the history
add control for showing a deck's track menu
  • Loading branch information
daschuer authored Jan 27, 2024
2 parents 59ca3a2 + 67a3a95 commit a7af572
Show file tree
Hide file tree
Showing 10 changed files with 165 additions and 4 deletions.
3 changes: 2 additions & 1 deletion src/library/librarycontrol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,8 @@ FocusWidget LibraryControl::getFocusedWidget() {
// WTrackMenuClassWindow = WTrackMenu + submenus
// QMenuClassWindow = e.g. sidebar context menu
// qt_edit_menuWindow = QLineEdit/QCombobox context menu
// QComboBoxListView of WEffectSelector, WSearchLineEdit, ...
// QComboBoxPrivateContainerClassWindow
// = QComboBoxListView of WEffectSelector, WSearchLineEdit, ...
return FocusWidget::ContextMenu;
} else if (focusWindow->type() == Qt::Dialog) {
// DlgPreferencesDlgWindow
Expand Down
32 changes: 32 additions & 0 deletions src/mixer/basetrackplayer.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "mixer/basetrackplayer.h"

#include <QMessageBox>
#include <QMetaMethod>

#include "control/controlobject.h"
#include "engine/channels/enginedeck.h"
Expand Down Expand Up @@ -707,6 +708,37 @@ void BaseTrackPlayerImpl::loadTrackFromGroup(const QString& group) {
slotLoadTrack(pTrack, false);
}

bool BaseTrackPlayerImpl::isTrackMenuControlAvailable() {
if (m_pShowTrackMenuControl == nullptr) {
// Create the control and return true so LegacySkinParser knows it should
// connect our signal to WTrackProperty.
m_pShowTrackMenuControl = std::make_unique<ControlPushButton>(
ConfigKey(getGroup(), "show_track_menu"));
m_pShowTrackMenuControl->connectValueChangeRequest(
this,
[this](double value) {
emit trackMenuChangeRequest(value > 0);
});
return true;
} else if (isSignalConnected(
QMetaMethod::fromSignal(&BaseTrackPlayer::trackMenuChangeRequest))) {
// Control exists and we're already connected.
// This means the request was made while creating the 2nd or later WTrackProperty.
return false;
} else {
// Control already exists but signal is not connected, which is the case
// after loading a skin. Return true so LegacySkinParser makes a new connection.
return true;
}
}

void BaseTrackPlayerImpl::slotSetAndConfirmTrackMenuControl(bool visible) {
VERIFY_OR_DEBUG_ASSERT(m_pShowTrackMenuControl) {
return;
}
m_pShowTrackMenuControl->setAndConfirm(visible ? 1.0 : 0.0);
}

void BaseTrackPlayerImpl::slotSetReplayGain(mixxx::ReplayGain replayGain) {
// Do not change replay gain when track is playing because
// this may lead to an unexpected volume change.
Expand Down
17 changes: 16 additions & 1 deletion src/mixer/basetrackplayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,17 @@ class BaseTrackPlayer : public BasePlayer {

virtual TrackPointer getLoadedTrack() const = 0;
virtual void setupEqControls() = 0;
virtual bool isTrackMenuControlAvailable() {
return false;
};

public slots:
virtual void slotLoadTrack(TrackPointer pTrack, bool bPlay = false) = 0;
virtual void slotCloneFromGroup(const QString& group) = 0;
virtual void slotCloneDeck() = 0;
virtual void slotEjectTrack(double) = 0;
virtual void slotSetTrackRating(int rating) = 0;
virtual void slotSetAndConfirmTrackMenuControl(bool){};

signals:
void newTrackLoaded(TrackPointer pLoadedTrack);
Expand All @@ -52,6 +56,7 @@ class BaseTrackPlayer : public BasePlayer {
void playerEmpty();
void noVinylControlInputConfigured();
void trackRatingChanged(int rating);
void trackMenuChangeRequest(bool show);
};

class BaseTrackPlayerImpl : public BaseTrackPlayer {
Expand All @@ -76,7 +81,13 @@ class BaseTrackPlayerImpl : public BaseTrackPlayer {

void setupEqControls() final;

// For testing, loads a fake track.
/// Returns true if PushButton has been created and no slot is currently
/// connected to trackMenuChangeRequest().
/// PushButtons persist skin reload, connected widgets don't, i.e. the
/// connection is removed on skin reload and available again afterwards.
bool isTrackMenuControlAvailable() final;

/// For testing, loads a fake track.
TrackPointer loadFakeTrack(bool bPlay, double filebpm);

public slots:
Expand All @@ -92,6 +103,8 @@ class BaseTrackPlayerImpl : public BaseTrackPlayer {
void slotAdjustReplayGain(mixxx::ReplayGain replayGain);
void slotSetTrackColor(const mixxx::RgbColor::optional_t& color);
void slotSetTrackRating(int rating) final;
/// Called via signal from WTrackProperty. Just set and confirm as requested.
void slotSetAndConfirmTrackMenuControl(bool visible) final;
void slotPlayToggled(double);

private slots:
Expand Down Expand Up @@ -164,6 +177,8 @@ class BaseTrackPlayerImpl : public BaseTrackPlayer {
std::unique_ptr<ControlPushButton> m_pShiftCuesLaterSmall;
std::unique_ptr<ControlObject> m_pShiftCues;

std::unique_ptr<ControlPushButton> m_pShowTrackMenuControl;

std::unique_ptr<ControlObject> m_pUpdateReplayGainFromPregain;

parented_ptr<ControlProxy> m_pReplayGain;
Expand Down
18 changes: 18 additions & 0 deletions src/skin/legacy/legacyskinparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1088,6 +1088,24 @@ QWidget* LegacySkinParser::parseTrackProperty(const QDomElement& node) {
group);
setupLabelWidget(node, pTrackProperty);

// Ensure 'show_track_menu' control is created for each main deck and
// valueChangeRequest hook is set up.
// Only the first WTrackProperty that is created connects the signals.
if (PlayerManager::isDeckGroup(group)) {
if (pPlayer->isTrackMenuControlAvailable()) {
connect(pPlayer,
&BaseTrackPlayer::trackMenuChangeRequest,
pTrackProperty,
&WTrackProperty::slotShowTrackMenuChangeRequest,
Qt::DirectConnection);
connect(pTrackProperty,
&WTrackProperty::setAndConfirmTrackMenuControl,
pPlayer,
&BaseTrackPlayer::slotSetAndConfirmTrackMenuControl,
Qt::DirectConnection);
}
}

connect(pPlayer,
&BaseTrackPlayer::newTrackLoaded,
pTrackProperty,
Expand Down
3 changes: 3 additions & 0 deletions src/track/track_decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,7 @@ enum class ExportTrackMetadataResult {
Skipped,
};

// key for control to open/close the decks' track menus
const QString kShowTrackMenuKey = QStringLiteral("show_track_menu");

Q_DECLARE_METATYPE(TrackPointer);
8 changes: 8 additions & 0 deletions src/widget/wtrackmenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,15 @@ int WTrackMenu::getTrackCount() const {
}
}

const QString WTrackMenu::getDeckGroup() const {
return m_deckGroup;
}

void WTrackMenu::closeEvent(QCloseEvent* event) {
// Unfortunately, trackMenuVisible(false) is emitted before the menu is effectively
// closed, which causes issues in WTrackProperty::slotShowTrackMenuChangeRequest.
// Explicitly hide() to avoid this.
hide();
// Actually the event is accepted by default. doing it explicitly doesn't hurt.
// If it's not accepted the menu remains open and entire GUI will be blocked!
event->accept();
Expand Down
1 change: 1 addition & 0 deletions src/widget/wtrackmenu.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class WTrackMenu : public QMenu {
void showDlgTrackInfo(const QString& property = QString());
// Library management
void slotRemoveFromDisk();
const QString getDeckGroup() const;

signals:
void loadTrackToPlayer(TrackPointer pTrack, const QString& group, bool play = false);
Expand Down
71 changes: 70 additions & 1 deletion src/widget/wtrackproperty.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#include "widget/wtrackproperty.h"

#include <QApplication>
#include <QDebug>
#include <QUrl>

#include "control/controlpushbutton.h"
#include "moc_wtrackproperty.cpp"
#include "skin/legacy/skincontext.h"
#include "track/track.h"
Expand Down Expand Up @@ -129,7 +132,7 @@ void WTrackProperty::contextMenuEvent(QContextMenuEvent* event) {
if (m_pCurrentTrack) {
ensureTrackMenuIsCreated();
m_pTrackMenu->loadTrack(m_pCurrentTrack, m_group);
// Create the right-click menu
// Show the right-click menu
m_pTrackMenu->popup(event->globalPos());
}
}
Expand All @@ -138,5 +141,71 @@ void WTrackProperty::ensureTrackMenuIsCreated() {
if (m_pTrackMenu.get() == nullptr) {
m_pTrackMenu = make_parented<WTrackMenu>(
this, m_pConfig, m_pLibrary, kTrackMenuFeatures);

// When a track menu for this deck is shown/hidden via contextMenuEvent
// or pushbutton, it emits trackMenuVisible(bool).
// The pushbutton is created in BaseTrackPlayer which, on value change requests,
// also emits a signal which is connected to our slotShowTrackMenuChangeRequest().
connect(m_pTrackMenu,
&WTrackMenu::trackMenuVisible,
this,
[this](bool visible) {
ControlObject::set(ConfigKey(m_group, kShowTrackMenuKey),
visible ? 1.0 : 0.0);
});
}
}

/// This slot handles show/hide requests originating from both pushbutton changes
/// and WTrackMenu's show/hide signals.
/// If the request matches the menu state we only set the control value accordingly.
/// Otherwise, we show/hide the menu as requested. This will result in another
/// change request originating from the menu, then it's a match and we setAndConfirm()
void WTrackProperty::slotShowTrackMenuChangeRequest(bool show) {
// Ignore no-op
if ((ControlObject::get(ConfigKey(m_group, kShowTrackMenuKey)) > 0) == show) {
return;
}

// Check for any open track menu.
// If this is a show request, hide all other menus (decks and library).
// Assumes there can only be one visible menu per deck
bool confirmShow = false;
const QWidgetList topLevelWidgets = QApplication::topLevelWidgets();
for (QWidget* pWidget : topLevelWidgets) {
// Ignore other popups and hidden track menus
WTrackMenu* pTrackMenu = qobject_cast<WTrackMenu*>(pWidget);
if (pTrackMenu && pTrackMenu->isVisible()) {
if (show) {
if (pTrackMenu->getDeckGroup() == m_group) {
// Don't return, yet, maybe we still need to hide other menus.
confirmShow = true;
} else {
// Hide other menus
pTrackMenu->close();
}
} else if (pTrackMenu->getDeckGroup() == m_group) {
// Hide this deck's menu, ignore other menus
pTrackMenu->close();
return;
}
}
}

// If we reach this, this is either a hide request but no menu was found for
// this deck, or this is a show request and we've found an open menu.
if (!show || confirmShow) {
emit setAndConfirmTrackMenuControl(show);
return;
}

// This is a show request and there was no open menu found for this deck.
// Pop up menu as if we right-clicked at the center of this widget
// Note: this widget may be hidden so the position may be unexpected,
// though this is okay as long as all variants of deckN are on the same
// side of the mixer.
QContextMenuEvent event(QContextMenuEvent::Mouse,
QPoint(),
mapToGlobal(rect().center()));
contextMenuEvent(&event);
}
6 changes: 5 additions & 1 deletion src/widget/wtrackproperty.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "widget/trackdroptarget.h"
#include "widget/wlabel.h"

class ControlPushButton;
class Library;
class WTrackMenu;

Expand All @@ -25,10 +26,12 @@ class WTrackProperty : public WLabel, public TrackDropTarget {
signals:
void trackDropped(const QString& filename, const QString& group) override;
void cloneDeck(const QString& sourceGroup, const QString& targetGroup) override;
void setAndConfirmTrackMenuControl(bool visible);

public slots:
void slotTrackLoaded(TrackPointer pTrack);
void slotLoadingTrack(TrackPointer pNewTrack, TrackPointer pOldTrack);
void slotShowTrackMenuChangeRequest(bool show);

protected:
void contextMenuEvent(QContextMenuEvent* event) override;
Expand All @@ -37,7 +40,8 @@ class WTrackProperty : public WLabel, public TrackDropTarget {
void mouseMoveEvent(QMouseEvent* event) override;
void mouseDoubleClickEvent(QMouseEvent* event) override;

virtual void updateLabel();
private:
void updateLabel();

void ensureTrackMenuIsCreated();

Expand Down
10 changes: 10 additions & 0 deletions src/widget/wtrackwidgetgroup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <QStylePainter>

#include "control/controlobject.h"
#include "moc_wtrackwidgetgroup.cpp"
#include "skin/legacy/skincontext.h"
#include "track/track.h"
Expand Down Expand Up @@ -133,5 +134,14 @@ void WTrackWidgetGroup::ensureTrackMenuIsCreated() {
if (m_pTrackMenu.get() == nullptr) {
m_pTrackMenu = make_parented<WTrackMenu>(
this, m_pConfig, m_pLibrary, kTrackMenuFeatures);

// See WTrackProperty for info
connect(m_pTrackMenu,
&WTrackMenu::trackMenuVisible,
this,
[this](bool visible) {
ControlObject::set(ConfigKey(m_group, kShowTrackMenuKey),
visible ? 1.0 : 0.0);
});
}
}

0 comments on commit a7af572

Please sign in to comment.