Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/controllers/dlgprefcontroller.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ class DlgPrefController : public DlgPreferencePage {
QUrl helpUrl() const override;
void keyPressEvent(QKeyEvent* pEvent) override;

Controller* controller() const {
return m_pController;
}

// Open the MIDI learning wizard for this controller
void showLearningWizard();

public slots:
/// Called when the preference dialog (not this page) is shown to the user.
void slotUpdate() override;
Expand Down Expand Up @@ -78,7 +85,6 @@ class DlgPrefController : public DlgPreferencePage {

// Input mappings
void addInputMapping();
void showLearningWizard();
void removeInputMappings();
void clearAllInputMappings();

Expand Down
37 changes: 37 additions & 0 deletions src/controllers/dlgprefcontrollers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,3 +286,40 @@ void DlgPrefControllers::slotMidiThroughChanged(bool checked) {
}
#endif
#endif

void DlgPrefControllers::openLearningWizard(Controller* pController) {
if (!pController) {
return;
}

// Find the DlgPrefController for this controller by matching the controller pointer
for (int i = 0; i < m_controllerPages.size(); ++i) {
DlgPrefController* pControllerDlg = m_controllerPages.at(i);
if (pControllerDlg && pControllerDlg->controller() == pController) {
Comment on lines +296 to +298
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (int i = 0; i < m_controllerPages.size(); ++i) {
DlgPrefController* pControllerDlg = m_controllerPages.at(i);
if (pControllerDlg && pControllerDlg->controller() == pController) {
for (auto* pControllerDlg : m_controllerPages) {
if (pControllerDlg->controller() == pController) {

// Temporarily disconnect the mappingEnded signal to prevent
// showing the preferences dialog when the wizard closes
disconnect(pControllerDlg,
&DlgPrefController::mappingEnded,
m_pDlgPreferences,
&DlgPreferences::show);

// Reconnect after wizard closes to restore normal behavior
connect(
pControllerDlg,
Comment on lines +307 to +308
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
connect(
pControllerDlg,
connect(pControllerDlg,

&DlgPrefController::mappingEnded,
this,
[this, pControllerDlg]() {
// Restore the connection for future uses from preferences
connect(pControllerDlg,
&DlgPrefController::mappingEnded,
m_pDlgPreferences,
&DlgPreferences::show);
},
Qt::SingleShotConnection);
Comment on lines +299 to +318
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need to disconnect this?
when a new mappng has been added, the mapping needs to be saved/applied, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stops prefs reopening

"disconnect only affects UI flow, not saving. prevents preferences dialog popping up when wizard is launched from menu instead of from within preferences. saving/applying happens in 'slotStopLearning()' before mappingEnded emits. single-shot reconnect restores normal behavior for preference-based launches."

maybe there is a more elegant way?


// Open the learning wizard directly
pControllerDlg->showLearningWizard();
return;
}
}
}
2 changes: 2 additions & 0 deletions src/controllers/dlgprefcontrollers.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "preferences/usersettings.h"
#include "util/parented_ptr.h"

class Controller;
class ControlProxy;
class DlgPreferences;
class DlgPrefController;
Expand All @@ -28,6 +29,7 @@ class DlgPrefControllers : public DlgPreferencePage, public Ui::DlgPrefControlle

bool handleTreeItemClick(QTreeWidgetItem* clickedItem);
QUrl helpUrl() const override;
void openLearningWizard(Controller* pController);

public slots:
/// Called when the preference dialog (not this page) is shown to the user.
Expand Down
44 changes: 44 additions & 0 deletions src/mixxxmainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include "widget/winitialglwidget.h"
#endif

#include "controllers/controller.h"
#include "controllers/controllermanager.h"
#include "controllers/keyboard/keyboardeventfilter.h"
#include "coreservices.h"
#include "defs_urls.h"
Expand Down Expand Up @@ -947,6 +949,22 @@ void MixxxMainWindow::connectMenuBar() {
m_pMenuBar->onNumberOfDecksChanged(pPlayerManager->numberOfDecks());
}

// Controller learning wizard menu
if (m_pCoreServices->getControllerManager()) {
connect(m_pCoreServices->getControllerManager().get(),
&ControllerManager::devicesChanged,
this,
&MixxxMainWindow::slotUpdateControllerLearningMenu,
Qt::UniqueConnection);
connect(m_pMenuBar,
&WMainMenuBar::openControllerLearningWizard,
this,
&MixxxMainWindow::slotOpenControllerLearningWizard,
Qt::UniqueConnection);
// Initialize the menu with current controllers
slotUpdateControllerLearningMenu();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't work as intended, I don't see updated after startup.
The funtion that emits devicesChanged clearly states:

// NOTE: Currently this function is only called on startup. If hotplug is added, changes to the
// controller list must be synchronized with dlgprefcontrollers to avoid dangling connections
// and possible crashes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may try to connect to ControllerManager::mappingApplied but I'm not sure if the controller is already open because there is also a blocking call openController(pController);.
See here and #15524


if (m_pCoreServices->getTrackCollectionManager()) {
connect(m_pMenuBar,
&WMainMenuBar::rescanLibrary,
Expand Down Expand Up @@ -1147,6 +1165,32 @@ void MixxxMainWindow::slotNoVinylControlInputConfigured() {
}
}

void MixxxMainWindow::slotUpdateControllerLearningMenu() {
if (!m_pCoreServices->getControllerManager()) {
return;
}
// Only show enabled/opened input controllers
// Use getControllerList(false, true) to get input devices only
QList<Controller*> allControllers =
m_pCoreServices->getControllerManager()->getControllerList(
false, true);
QList<Controller*> enabledControllers;
for (Controller* pController : std::as_const(allControllers)) {
if (pController && pController->isOpen()) {
enabledControllers.append(pController);
}
}
m_pMenuBar->onControllersChanged(enabledControllers);
}

void MixxxMainWindow::slotOpenControllerLearningWizard(Controller* pController) {
if (!pController) {
return;
}
// Open the learning wizard directly for this controller
m_pPrefDlg->openControllerLearningWizard(pController);
}

void MixxxMainWindow::slotNoDeckPassthroughInputConfigured() {
if (m_noPassthroughInputDialog && m_noPassthroughInputDialog->isVisible()) {
// Don't show redundant dialogs.
Expand Down
4 changes: 4 additions & 0 deletions src/mixxxmainwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "util/parented_ptr.h"

class ControlObject;
class Controller;
class DlgDeveloperTools;
class DlgPreferences;
class DlgKeywheel;
Expand Down Expand Up @@ -81,6 +82,9 @@ class MixxxMainWindow : public QMainWindow {
void slotNoAuxiliaryInputConfigured();
void slotNoDeckPassthroughInputConfigured();
void slotNoVinylControlInputConfigured();
/// Controller learning wizard menu management
void slotUpdateControllerLearningMenu();
void slotOpenControllerLearningWizard(Controller* pController);
#ifndef __APPLE__
/// Update whether the menubar is toggled pressing the Alt key and show/hide
/// it accordingly
Expand Down
9 changes: 8 additions & 1 deletion src/preferences/dialog/dlgpreferences.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <QScrollArea>
#include <QtGlobal>

#include "controllers/controller.h"
#include "controllers/dlgprefcontrollers.h"
#include "library/library.h"
#include "library/trackcollectionmanager.h"
Expand Down Expand Up @@ -306,11 +307,17 @@ void DlgPreferences::showSoundHardwarePage(
std::optional<mixxx::preferences::SoundHardwareTab> tab) {
switchToPage(m_soundPage.pTreeItem->text(0), m_soundPage.pDlg);
contentsTreeWidget->setCurrentItem(m_soundPage.pTreeItem);
if (tab.has_value()) {
if (tab) {
m_pSoundDlg->selectIOTab(*tab);
}
}

void DlgPreferences::openControllerLearningWizard(Controller* pController) {
if (m_pControllersDlg) {
m_pControllersDlg->openLearningWizard(pController);
}
}

bool DlgPreferences::eventFilter(QObject* o, QEvent* e) {
// Send a close signal if dialog is closing
if (e->type() == QEvent::Hide) {
Expand Down
2 changes: 2 additions & 0 deletions src/preferences/dialog/dlgpreferences.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "preferences/settingsmanager.h"
#include "preferences/usersettings.h"

class Controller;
class ControllerManager;
class DlgPrefControllers;
class DlgPrefSound;
Expand Down Expand Up @@ -67,6 +68,7 @@ class DlgPreferences : public QDialog, public Ui::DlgPreferencesDlg {
void showSoundHardwarePage(
std::optional<mixxx::preferences::SoundHardwareTab> tab =
std::nullopt);
void openControllerLearningWizard(Controller* pController);
void slotButtonPressed(QAbstractButton* pButton);
signals:
void closeDlg();
Expand Down
44 changes: 44 additions & 0 deletions src/widget/wmainmenubar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "config.h"
#include "control/controlproxy.h"
#include "controllers/controller.h"
#include "defs_urls.h"
#include "moc_wmainmenubar.cpp"
#include "util/cmdlineargs.h"
Expand Down Expand Up @@ -470,6 +471,17 @@ void WMainMenuBar::initialize() {
pOptionsMenu->addSeparator();
#endif

// Controller Learning submenu
m_pControllerLearningMenu = new QMenu(tr("&Controller Learning Wizard"), this);
QString controllerLearningText = tr(
"Open the MIDI learning wizard for an enabled controller");
m_pControllerLearningMenu->setStatusTip(controllerLearningText);
m_pControllerLearningMenu->setWhatsThis(buildWhatsThis(
tr("Controller Learning Wizard"), controllerLearningText));
// Menu will be populated dynamically with enabled controllers
pOptionsMenu->addMenu(m_pControllerLearningMenu);
pOptionsMenu->addSeparator();

QString recordTitle = tr("&Record Mix");
QString recordText = tr("Record your mix to a file");
auto* pOptionsRecord = new QAction(recordTitle, this);
Expand Down Expand Up @@ -998,3 +1010,35 @@ void VisibilityControlConnection::slotActionToggled(bool toggle) {
m_pControl->set(toggle ? 1.0 : 0.0);
}
}

void WMainMenuBar::onControllersChanged(const QList<Controller*>& controllers) {
// Clear existing actions
for (QAction* pAction : std::as_const(m_controllerLearningActions)) {
m_pControllerLearningMenu->removeAction(pAction);
delete pAction;
}
m_controllerLearningActions.clear();

// Add menu item for each controller
for (Controller* pController : controllers) {
if (!pController) {
continue;
}
QString controllerName = pController->getName();
auto* pAction = new QAction(controllerName, this);
pAction->setStatusTip(tr("Open learning wizard for %1").arg(controllerName));
connect(pAction, &QAction::triggered, this, [this, pController]() {
emit openControllerLearningWizard(pController);
});
m_pControllerLearningMenu->addAction(pAction);
m_controllerLearningActions.append(pAction);
}

// Show "No controllers enabled" if list is empty
if (m_controllerLearningActions.isEmpty()) {
auto* pNoControllersAction = new QAction(tr("No controllers enabled"), this);
pNoControllersAction->setEnabled(false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem to work (here, with Qt 6.2.3), action is still enabled, can be selected and clicked

m_pControllerLearningMenu->addAction(pNoControllersAction);
m_controllerLearningActions.append(pNoControllersAction);
}
}
5 changes: 5 additions & 0 deletions src/widget/wmainmenubar.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "preferences/usersettings.h"

class QAction;
class Controller;

class VisibilityControlConnection : public QObject {
Q_OBJECT
Expand Down Expand Up @@ -54,6 +55,7 @@ class WMainMenuBar : public QMenuBar {
void onVinylControlDeckEnabledStateChange(int deck, bool enabled);
void onNumberOfDecksChanged(int decks);
void onKeywheelChange(int state);
void onControllersChanged(const QList<Controller*>& controllers);
#ifndef __APPLE__
void slotToggleMenuBar();
#endif
Expand All @@ -80,6 +82,7 @@ class WMainMenuBar : public QMenuBar {
void toggleBroadcasting(bool toggle);
void toggleRecording(bool enabled);
void toggleVinylControl(int deck);
void openControllerLearningWizard(Controller* pController);
void visitUrl(const QString& url);
void quit();

Expand Down Expand Up @@ -115,4 +118,6 @@ class WMainMenuBar : public QMenuBar {
ConfigObject<ConfigValueKbd>* m_pKbdConfig;
QList<QAction*> m_loadToDeckActions;
QList<QAction*> m_vinylControlEnabledActions;
QMenu* m_pControllerLearningMenu;
QList<QAction*> m_controllerLearningActions;
};
Loading