diff --git a/res/qml/FocusedWidgetControl.qml b/res/qml/FocusedWidgetControl.qml new file mode 100644 index 000000000000..1821f62a90bd --- /dev/null +++ b/res/qml/FocusedWidgetControl.qml @@ -0,0 +1,14 @@ +import Mixxx 0.1 as Mixxx + +Mixxx.ControlProxy { + + enum WidgetKind { + None, + Searchbar, + Sidebar, + LibraryView + } + + group: "[Library]" + key: "focused_widget" +} diff --git a/res/qml/Library.qml b/res/qml/Library.qml index 1aca01955a4a..f7155194985c 100644 --- a/res/qml/Library.qml +++ b/res/qml/Library.qml @@ -1,5 +1,6 @@ import "." as Skin import Mixxx 0.1 as Mixxx +import QtQml.Models 2.12 import QtQuick 2.12 import "Theme" @@ -8,22 +9,89 @@ Item { color: Theme.deckBackgroundColor anchors.fill: parent + LibraryControl { + id: libraryControl + + onMoveVertical: listView.moveSelectionVertical(offset) + onLoadSelectedTrack: listView.loadSelectedTrack(group, play) + onLoadSelectedTrackIntoNextAvailableDeck: listView.loadSelectedTrackIntoNextAvailableDeck(play) + onFocusWidgetChanged: { + switch (focusWidget) { + case FocusedWidgetControl.WidgetKind.LibraryView: + listView.forceActiveFocus(); + break; + } + } + } + ListView { + id: listView + + function moveSelectionVertical(value) { + if (value == 0) + return ; + + const rowCount = model.rowCount(); + if (rowCount == 0) + return ; + + currentIndex = Mixxx.MathUtils.positiveModulo(currentIndex + value, rowCount); + } + + function loadSelectedTrackIntoNextAvailableDeck(play) { + const url = model.get(currentIndex).fileUrl; + if (!url) + return ; + + Mixxx.PlayerManager.loadLocationUrlIntoNextAvailableDeck(url, play); + } + + function loadSelectedTrack(group, play) { + const url = model.get(currentIndex).fileUrl; + if (!url) + return ; + + const player = Mixxx.PlayerManager.getPlayer(group); + if (!player) + return ; + + player.loadTrackFromLocationUrl(url, play); + } + anchors.fill: parent anchors.margins: 10 clip: true + keyNavigationWraps: true + highlightMoveDuration: 250 + highlightResizeDuration: 50 model: Mixxx.Library.model + Keys.onPressed: { + switch (event.key) { + case Qt.Key_Enter: + case Qt.Key_Return: + listView.loadSelectedTrackIntoNextAvailableDeck(false); + break; + } + } delegate: Item { id: itemDelegate - implicitWidth: 300 + implicitWidth: listView.width implicitHeight: 30 Text { - anchors.fill: parent + anchors.verticalCenter: parent.verticalCenter text: artist + " - " + title - color: Theme.deckTextColor + color: (listView.currentIndex == index && listView.activeFocus) ? Theme.blue : Theme.deckTextColor + + Behavior on color { + ColorAnimation { + duration: listView.highlightMoveDuration + } + + } + } Image { @@ -44,13 +112,24 @@ Item { anchors.fill: parent drag.target: dragItem - onPressed: parent.grabToImage((result) => { - dragItem.Drag.imageSource = result.url; - }) + onPressed: { + listView.forceActiveFocus(); + listView.currentIndex = index; + parent.grabToImage((result) => { + dragItem.Drag.imageSource = result.url; + }); + } + onDoubleClicked: listView.loadSelectedTrackIntoNextAvailableDeck(false) } } + highlight: Rectangle { + border.color: listView.activeFocus ? Theme.blue : Theme.deckTextColor + border.width: 1 + color: "transparent" + } + } } diff --git a/res/qml/LibraryControl.qml b/res/qml/LibraryControl.qml new file mode 100644 index 000000000000..49794c2f94c2 --- /dev/null +++ b/res/qml/LibraryControl.qml @@ -0,0 +1,156 @@ +import Mixxx 0.1 as Mixxx +import QtQuick 2.12 + +Item { + id: root + + property alias focusWidget: focusedWidgetControl.value + + signal moveVertical(int offset) + signal loadSelectedTrack(string group, bool play) + signal loadSelectedTrackIntoNextAvailableDeck(bool play) + + FocusedWidgetControl { + id: focusedWidgetControl + + Component.onCompleted: this.value = FocusedWidgetControl.WidgetKind.LibraryView + } + + Mixxx.ControlProxy { + group: "[Library]" + key: "GoToItem" + onValueChanged: { + if (value != 0 && root.focusWidget == FocusedWidgetControl.WidgetKind.LibraryView) + root.loadSelectedTrackIntoNextAvailableDeck(false); + + } + } + + Mixxx.ControlProxy { + group: "[Playlist]" + key: "LoadSelectedIntoFirstStopped" + onValueChanged: { + if (value != 0 && root.focusWidget == FocusedWidgetControl.WidgetKind.LibraryView) + root.loadSelectedTrackIntoNextAvailableDeck(false); + + } + } + + Mixxx.ControlProxy { + group: "[Playlist]" + key: "SelectTrackKnob" + onValueChanged: { + if (value != 0) { + root.focusWidget = FocusedWidgetControl.WidgetKind.LibraryView; + root.moveVertical(value); + } + } + } + + Mixxx.ControlProxy { + group: "[Playlist]" + key: "SelectPrevTrack" + onValueChanged: { + if (value != 0) { + root.focusWidget = FocusedWidgetControl.WidgetKind.LibraryView; + root.moveVertical(-1); + } + } + } + + Mixxx.ControlProxy { + group: "[Playlist]" + key: "SelectNextTrack" + onValueChanged: { + if (value != 0) { + root.focusWidget = FocusedWidgetControl.WidgetKind.LibraryView; + root.moveVertical(1); + } + } + } + + Mixxx.ControlProxy { + group: "[Library]" + key: "MoveVertical" + onValueChanged: { + if (value != 0 && root.focusWidget == FocusedWidgetControl.WidgetKind.LibraryView) + root.moveVertical(value); + + } + } + + Mixxx.ControlProxy { + group: "[Library]" + key: "MoveUp" + onValueChanged: { + if (value != 0 && root.focusWidget == FocusedWidgetControl.WidgetKind.LibraryView) + root.moveVertical(-1); + + } + } + + Mixxx.ControlProxy { + group: "[Library]" + key: "MoveDown" + onValueChanged: { + if (value != 0 && root.focusWidget == FocusedWidgetControl.WidgetKind.LibraryView) + root.moveVertical(1); + + } + } + + Mixxx.ControlProxy { + id: numDecksControl + + group: "[Master]" + key: "num_decks" + } + + Instantiator { + model: numDecksControl.value + + delegate: LibraryControlLoadSelectedTrackHandler { + group: "[Channel" + (index + 1) + "]" + enabled: root.focusWidget == FocusedWidgetControl.WidgetKind.LibraryView + onLoadTrackRequested: root.loadSelectedTrack(group, play) + } + + } + + Mixxx.ControlProxy { + id: numPreviewDecksControl + + group: "[Master]" + key: "num_preview_decks" + } + + Instantiator { + model: numPreviewDecksControl.value + + delegate: LibraryControlLoadSelectedTrackHandler { + group: "[PreviewDeck" + (index + 1) + "]" + enabled: root.focusWidget == FocusedWidgetControl.WidgetKind.LibraryView + onLoadTrackRequested: root.loadSelectedTrack(group, play) + } + + } + + Mixxx.ControlProxy { + id: numSamplersControl + + group: "[Master]" + key: "num_samplers" + } + + Instantiator { + model: numSamplersControl.value + + delegate: LibraryControlLoadSelectedTrackHandler { + group: "[Sampler" + (index + 1) + "]" + enabled: root.focusWidget == FocusedWidgetControl.WidgetKind.LibraryView + onLoadTrackRequested: root.loadSelectedTrack(group, play) + } + + } + +} diff --git a/res/qml/LibraryControlLoadSelectedTrackHandler.qml b/res/qml/LibraryControlLoadSelectedTrackHandler.qml new file mode 100644 index 000000000000..fad54367703e --- /dev/null +++ b/res/qml/LibraryControlLoadSelectedTrackHandler.qml @@ -0,0 +1,38 @@ +import Mixxx 0.1 as Mixxx +import QtQuick 2.12 + +/// Usually, this component shouldn't be an (visual) `Item` and use something +/// like `QtObject` instead. However, for some reason using `QtObject` here +/// makes Mixxx crash on load (using Qt 5.15.2+kde+r43-1). We can check if this +/// is fixed upstream once we switch to Qt 6. +Item { + id: root + + property string group // required + property bool enabled: true + + signal loadTrackRequested(bool play) + + Mixxx.ControlProxy { + group: root.group + key: "LoadSelectedTrack" + onValueChanged: { + if (value == 0 || !root.enabled) + return ; + + root.loadTrackRequested(false); + } + } + + Mixxx.ControlProxy { + group: root.group + key: "LoadSelectedTrackAndPlay" + onValueChanged: { + if (value == 0 || !root.enabled) + return ; + + root.loadTrackRequested(true); + } + } + +} diff --git a/res/qml/Mixxx/MathUtils.mjs b/res/qml/Mixxx/MathUtils.mjs index e7b500b7b46a..5e69447ba982 100644 --- a/res/qml/Mixxx/MathUtils.mjs +++ b/res/qml/Mixxx/MathUtils.mjs @@ -7,3 +7,17 @@ export const clamp = function(value, min, max) { return Math.max(Math.min(value, max), min); }; + +/** + * @param {number} x Value + * @param {number} m Modulus + * @returns {number} Result of y where y = x modulo m and y > 0 + */ +export const positiveModulo = function(x, m) { + console.assert(m > 0); + let result = x % m; + while (result < 0) { + result += m; + } + return result; +}; diff --git a/src/library/library.cpp b/src/library/library.cpp index 78ecb99b9832..40750af4eb6a 100644 --- a/src/library/library.cpp +++ b/src/library/library.cpp @@ -524,11 +524,11 @@ void Library::slotLoadTrack(TrackPointer pTrack) { emit loadTrack(pTrack); } -void Library::slotLoadLocationToPlayer(const QString& location, const QString& group) { +void Library::slotLoadLocationToPlayer(const QString& location, const QString& group, bool play) { auto trackRef = TrackRef::fromFilePath(location); TrackPointer pTrack = m_pTrackCollectionManager->getOrAddTrack(trackRef); if (pTrack) { - emit loadTrackToPlayer(pTrack, group); + emit loadTrackToPlayer(pTrack, group, play); } } diff --git a/src/library/library.h b/src/library/library.h index 606755114616..ff2a9faa2a92 100644 --- a/src/library/library.h +++ b/src/library/library.h @@ -110,7 +110,7 @@ class Library: public QObject { void slotSwitchToView(const QString& view); void slotLoadTrack(TrackPointer pTrack); void slotLoadTrackToPlayer(TrackPointer pTrack, const QString& group, bool play); - void slotLoadLocationToPlayer(const QString& location, const QString& group); + void slotLoadLocationToPlayer(const QString& location, const QString& group, bool play); void slotRefreshLibraryModels(); void slotCreatePlaylist(); void slotCreateCrate(); diff --git a/src/library/librarycontrol.cpp b/src/library/librarycontrol.cpp index 1197c0ae6932..071480ef5153 100644 --- a/src/library/librarycontrol.cpp +++ b/src/library/librarycontrol.cpp @@ -13,6 +13,7 @@ #include "library/libraryview.h" #include "mixer/playermanager.h" #include "moc_librarycontrol.cpp" +#include "util/cmdlineargs.h" #include "widget/wlibrary.h" #include "widget/wlibrarysidebar.h" #include "widget/wsearchlineedit.h" @@ -75,152 +76,173 @@ LibraryControl::LibraryControl(Library* pLibrary) m_pMoveUp = std::make_unique(ConfigKey("[Library]", "MoveUp")); m_pMoveDown = std::make_unique(ConfigKey("[Library]", "MoveDown")); m_pMoveVertical = std::make_unique(ConfigKey("[Library]", "MoveVertical"), false); - connect(m_pMoveUp.get(), - &ControlPushButton::valueChanged, - this, - &LibraryControl::slotMoveUp); - connect(m_pMoveDown.get(), - &ControlPushButton::valueChanged, - this, - &LibraryControl::slotMoveDown); - connect(m_pMoveVertical.get(), - &ControlEncoder::valueChanged, - this, - &LibraryControl::slotMoveVertical); + if (!CmdlineArgs::Instance().getQml()) { + connect(m_pMoveUp.get(), + &ControlPushButton::valueChanged, + this, + &LibraryControl::slotMoveUp); + connect(m_pMoveDown.get(), + &ControlPushButton::valueChanged, + this, + &LibraryControl::slotMoveDown); + connect(m_pMoveVertical.get(), + &ControlEncoder::valueChanged, + this, + &LibraryControl::slotMoveVertical); + } // Controls to navigate vertically within currently focused widget (up/down buttons) m_pScrollUp = std::make_unique(ConfigKey("[Library]", "ScrollUp")); m_pScrollDown = std::make_unique(ConfigKey("[Library]", "ScrollDown")); m_pScrollVertical = std::make_unique(ConfigKey("[Library]", "ScrollVertical"), false); - connect(m_pScrollUp.get(), - &ControlPushButton::valueChanged, - this, - &LibraryControl::slotScrollUp); - connect(m_pScrollDown.get(), - &ControlPushButton::valueChanged, - this, - &LibraryControl::slotScrollDown); - connect(m_pScrollVertical.get(), - &ControlEncoder::valueChanged, - this, - &LibraryControl::slotScrollVertical); + if (!CmdlineArgs::Instance().getQml()) { + connect(m_pScrollUp.get(), + &ControlPushButton::valueChanged, + this, + &LibraryControl::slotScrollUp); + connect(m_pScrollDown.get(), + &ControlPushButton::valueChanged, + this, + &LibraryControl::slotScrollDown); + connect(m_pScrollVertical.get(), + &ControlEncoder::valueChanged, + this, + &LibraryControl::slotScrollVertical); + } // Controls to navigate horizontally within currently selected item (left/right buttons) m_pMoveLeft = std::make_unique(ConfigKey("[Library]", "MoveLeft")); m_pMoveRight = std::make_unique(ConfigKey("[Library]", "MoveRight")); m_pMoveHorizontal = std::make_unique(ConfigKey("[Library]", "MoveHorizontal"), false); - connect(m_pMoveLeft.get(), - &ControlPushButton::valueChanged, - this, - &LibraryControl::slotMoveLeft); - connect(m_pMoveRight.get(), - &ControlPushButton::valueChanged, - this, - &LibraryControl::slotMoveRight); - connect(m_pMoveHorizontal.get(), - &ControlEncoder::valueChanged, - this, - &LibraryControl::slotMoveHorizontal); + if (!CmdlineArgs::Instance().getQml()) { + connect(m_pMoveLeft.get(), + &ControlPushButton::valueChanged, + this, + &LibraryControl::slotMoveLeft); + connect(m_pMoveRight.get(), + &ControlPushButton::valueChanged, + this, + &LibraryControl::slotMoveRight); + connect(m_pMoveHorizontal.get(), + &ControlEncoder::valueChanged, + this, + &LibraryControl::slotMoveHorizontal); + } // Controls to navigate between widgets // Relative focus controls (tab/shift+tab button) m_pMoveFocusForward = std::make_unique(ConfigKey("[Library]", "MoveFocusForward")); m_pMoveFocusBackward = std::make_unique(ConfigKey("[Library]", "MoveFocusBackward")); m_pMoveFocus = std::make_unique(ConfigKey("[Library]", "MoveFocus"), false); - connect(m_pMoveFocusForward.get(), - &ControlPushButton::valueChanged, - this, - &LibraryControl::slotMoveFocusForward); - connect(m_pMoveFocusBackward.get(), - &ControlPushButton::valueChanged, - this, - &LibraryControl::slotMoveFocusBackward); - connect(m_pMoveFocus.get(), - &ControlEncoder::valueChanged, - this, - &LibraryControl::slotMoveFocus); + if (!CmdlineArgs::Instance().getQml()) { + connect(m_pMoveFocusForward.get(), + &ControlPushButton::valueChanged, + this, + &LibraryControl::slotMoveFocusForward); + connect(m_pMoveFocusBackward.get(), + &ControlPushButton::valueChanged, + this, + &LibraryControl::slotMoveFocusBackward); + connect(m_pMoveFocus.get(), + &ControlEncoder::valueChanged, + this, + &LibraryControl::slotMoveFocus); + } + // Direct focus control, read/write m_pLibraryFocusedWidgetCO = std::make_unique( ConfigKey("[Library]", "focused_widget")); m_pLibraryFocusedWidgetCO->setStates(static_cast(FocusWidget::Count)); - m_pLibraryFocusedWidgetCO->connectValueChangeRequest( - this, - [this](double value) { - // Focus can not be removed from a widget just moved to another one. - // Thus, to keep the CO and QApplication::focusWidget() in sync we - // have to prevent scripts or GUI buttons setting the CO to 'None'. - // It's only set to 'None' internally when one of the library widgets - // receives a FocusOutEvent(), e.g. when the focus is moved to another - // widget, or when the main window loses focus. - const int valueInt = static_cast(value); - if (valueInt != static_cast(FocusWidget::None) && - valueInt < static_cast(FocusWidget::Count)) { - setLibraryFocus(static_cast(valueInt)); - } - }); + if (!CmdlineArgs::Instance().getQml()) { + m_pLibraryFocusedWidgetCO->connectValueChangeRequest( + this, + [this](double value) { + // Focus can not be removed from a widget just moved to another one. + // Thus, to keep the CO and QApplication::focusWidget() in sync we + // have to prevent scripts or GUI buttons setting the CO to 'None'. + // It's only set to 'None' internally when one of the library widgets + // receives a FocusOutEvent(), e.g. when the focus is moved to another + // widget, or when the main window loses focus. + const int valueInt = static_cast(value); + if (valueInt != static_cast(FocusWidget::None) && + valueInt < static_cast(FocusWidget::Count)) { + setLibraryFocus(static_cast(valueInt)); + } + }); + } // Control to "goto" the currently selected item in focused widget (context dependent) m_pGoToItem = std::make_unique(ConfigKey("[Library]", "GoToItem")); - connect(m_pGoToItem.get(), - &ControlPushButton::valueChanged, - this, - &LibraryControl::slotGoToItem); + if (!CmdlineArgs::Instance().getQml()) { + connect(m_pGoToItem.get(), + &ControlPushButton::valueChanged, + this, + &LibraryControl::slotGoToItem); + } // Auto DJ controls m_pAutoDjAddTop = std::make_unique(ConfigKey("[Library]","AutoDjAddTop")); - connect(m_pAutoDjAddTop.get(), - &ControlPushButton::valueChanged, - this, - &LibraryControl::slotAutoDjAddTop); + if (!CmdlineArgs::Instance().getQml()) { + connect(m_pAutoDjAddTop.get(), + &ControlPushButton::valueChanged, + this, + &LibraryControl::slotAutoDjAddTop); + } m_pAutoDjAddBottom = std::make_unique(ConfigKey("[Library]","AutoDjAddBottom")); - connect(m_pAutoDjAddBottom.get(), - &ControlPushButton::valueChanged, - this, - &LibraryControl::slotAutoDjAddBottom); + if (!CmdlineArgs::Instance().getQml()) { + connect(m_pAutoDjAddBottom.get(), + &ControlPushButton::valueChanged, + this, + &LibraryControl::slotAutoDjAddBottom); + } m_pAutoDjAddReplace = std::make_unique( ConfigKey("[Library]", "AutoDjAddReplace")); - connect(m_pAutoDjAddReplace.get(), - &ControlPushButton::valueChanged, - this, - &LibraryControl::slotAutoDjAddReplace); + if (!CmdlineArgs::Instance().getQml()) { + connect(m_pAutoDjAddReplace.get(), + &ControlPushButton::valueChanged, + this, + &LibraryControl::slotAutoDjAddReplace); + } // Sort controls m_pSortColumn = std::make_unique(ConfigKey("[Library]", "sort_column")); m_pSortOrder = std::make_unique(ConfigKey("[Library]", "sort_order")); m_pSortOrder->setButtonMode(ControlPushButton::TOGGLE); m_pSortColumnToggle = std::make_unique(ConfigKey("[Library]", "sort_column_toggle"), false); - connect(m_pSortColumn.get(), - &ControlEncoder::valueChanged, - this, - &LibraryControl::slotSortColumn); - connect(m_pSortColumnToggle.get(), - &ControlEncoder::valueChanged, - this, - &LibraryControl::slotSortColumnToggle); - - // Font sizes - m_pFontSizeKnob = std::make_unique( - ConfigKey("[Library]", "font_size_knob"), false); - connect(m_pFontSizeKnob.get(), - &ControlObject::valueChanged, - this, - &LibraryControl::slotFontSize); - - m_pFontSizeDecrement = std::make_unique( - ConfigKey("[Library]", "font_size_decrement")); - connect(m_pFontSizeDecrement.get(), - &ControlPushButton::valueChanged, - this, - &LibraryControl::slotDecrementFontSize); - - m_pFontSizeIncrement = std::make_unique( - ConfigKey("[Library]", "font_size_increment")); - connect(m_pFontSizeIncrement.get(), - &ControlPushButton::valueChanged, - this, - &LibraryControl::slotIncrementFontSize); + if (!CmdlineArgs::Instance().getQml()) { + connect(m_pSortColumn.get(), + &ControlEncoder::valueChanged, + this, + &LibraryControl::slotSortColumn); + connect(m_pSortColumnToggle.get(), + &ControlEncoder::valueChanged, + this, + &LibraryControl::slotSortColumnToggle); + + // Font sizes + m_pFontSizeKnob = std::make_unique( + ConfigKey("[Library]", "font_size_knob"), false); + connect(m_pFontSizeKnob.get(), + &ControlObject::valueChanged, + this, + &LibraryControl::slotFontSize); + + m_pFontSizeDecrement = std::make_unique( + ConfigKey("[Library]", "font_size_decrement")); + connect(m_pFontSizeDecrement.get(), + &ControlPushButton::valueChanged, + this, + &LibraryControl::slotDecrementFontSize); + + m_pFontSizeIncrement = std::make_unique( + ConfigKey("[Library]", "font_size_increment")); + connect(m_pFontSizeIncrement.get(), + &ControlPushButton::valueChanged, + this, + &LibraryControl::slotIncrementFontSize); + } // Track Color controls m_pTrackColorPrev = std::make_unique(ConfigKey("[Library]", "track_color_prev")); diff --git a/src/mixer/playermanager.cpp b/src/mixer/playermanager.cpp index 81230764e8f6..ae76ecbc8bfc 100644 --- a/src/mixer/playermanager.cpp +++ b/src/mixer/playermanager.cpp @@ -661,22 +661,23 @@ void PlayerManager::slotLoadTrackToPlayer(TrackPointer pTrack, const QString& gr m_lastLoadedPlayer = group; } -void PlayerManager::slotLoadToPlayer(const QString& location, const QString& group) { +void PlayerManager::slotLoadLocationToPlayer( + const QString& location, const QString& group, bool play) { // The library will get the track and then signal back to us to load the // track via slotLoadTrackToPlayer. - emit loadLocationToPlayer(location, group); + emit loadLocationToPlayer(location, group, play); } void PlayerManager::slotLoadToDeck(const QString& location, int deck) { - slotLoadToPlayer(location, groupForDeck(deck-1)); + slotLoadLocationToPlayer(location, groupForDeck(deck - 1)); } void PlayerManager::slotLoadToPreviewDeck(const QString& location, int previewDeck) { - slotLoadToPlayer(location, groupForPreviewDeck(previewDeck-1)); + slotLoadLocationToPlayer(location, groupForPreviewDeck(previewDeck - 1)); } void PlayerManager::slotLoadToSampler(const QString& location, int sampler) { - slotLoadToPlayer(location, groupForSampler(sampler-1)); + slotLoadLocationToPlayer(location, groupForSampler(sampler - 1)); } void PlayerManager::slotLoadTrackIntoNextAvailableDeck(TrackPointer pTrack) { @@ -690,6 +691,17 @@ void PlayerManager::slotLoadTrackIntoNextAvailableDeck(TrackPointer pTrack) { pDeck->slotLoadTrack(pTrack, false); } +void PlayerManager::slotLoadLocationIntoNextAvailableDeck(const QString& location, bool play) { + auto locker = lockMutex(&m_mutex); + BaseTrackPlayer* pDeck = findFirstStoppedPlayerInList(m_decks); + if (pDeck == nullptr) { + qDebug() << "PlayerManager: No stopped deck found, not loading track!"; + return; + } + + slotLoadLocationToPlayer(location, pDeck->getGroup(), play); +} + void PlayerManager::slotLoadTrackIntoNextAvailableSampler(TrackPointer pTrack) { auto locker = lockMutex(&m_mutex); BaseTrackPlayer* pSampler = findFirstStoppedPlayerInList(m_samplers); diff --git a/src/mixer/playermanager.h b/src/mixer/playermanager.h index 04ab325f097e..723b9b799c4f 100644 --- a/src/mixer/playermanager.h +++ b/src/mixer/playermanager.h @@ -179,11 +179,15 @@ class PlayerManager : public QObject, public PlayerManagerInterface { public slots: // Slots for loading tracks into a Player, which is either a Sampler or a Deck void slotLoadTrackToPlayer(TrackPointer pTrack, const QString& group, bool play = false); - void slotLoadToPlayer(const QString& location, const QString& group); + void slotLoadLocationToPlayer(const QString& location, const QString& group, bool play = false); + void slotLoadLocationToPlayerStopped(const QString& location, const QString& group) { + slotLoadLocationToPlayer(location, group, false); + }; void slotCloneDeck(const QString& source_group, const QString& target_group); // Slots for loading tracks to decks void slotLoadTrackIntoNextAvailableDeck(TrackPointer pTrack); + void slotLoadLocationIntoNextAvailableDeck(const QString& location, bool play = false); // Loads the location to the deck. deckNumber is 1-indexed void slotLoadToDeck(const QString& location, int deckNumber); @@ -207,7 +211,7 @@ class PlayerManager : public QObject, public PlayerManagerInterface { void onTrackAnalysisFinished(); signals: - void loadLocationToPlayer(const QString& location, const QString& group); + void loadLocationToPlayer(const QString& location, const QString& group, bool play); // Emitted when the user tries to enable a microphone talkover control when // there is no input configured. diff --git a/src/mixer/samplerbank.cpp b/src/mixer/samplerbank.cpp index 732f58ec0bb5..64f9472b5d66 100644 --- a/src/mixer/samplerbank.cpp +++ b/src/mixer/samplerbank.cpp @@ -216,7 +216,7 @@ bool SamplerBank::loadSamplerBankFromPath(const QString& samplerBankPath) { if (location.isEmpty()) { m_pPlayerManager->slotLoadTrackToPlayer(TrackPointer(), group); } else { - m_pPlayerManager->slotLoadToPlayer(location, group); + m_pPlayerManager->slotLoadLocationToPlayer(location, group); } } diff --git a/src/qml/qmlplayermanagerproxy.cpp b/src/qml/qmlplayermanagerproxy.cpp index 8379320f0a08..483d99a56e5c 100644 --- a/src/qml/qmlplayermanagerproxy.cpp +++ b/src/qml/qmlplayermanagerproxy.cpp @@ -11,10 +11,6 @@ namespace qml { QmlPlayerManagerProxy::QmlPlayerManagerProxy( std::shared_ptr pPlayerManager, QObject* parent) : QObject(parent), m_pPlayerManager(pPlayerManager) { - connect(this, - &QmlPlayerManagerProxy::loadLocationToPlayer, - m_pPlayerManager.get(), - &PlayerManager::loadLocationToPlayer); } QObject* QmlPlayerManagerProxy::getPlayer(const QString& group) { @@ -31,8 +27,8 @@ QObject* QmlPlayerManagerProxy::getPlayer(const QString& group) { connect(pPlayerProxy, &QmlPlayerProxy::loadTrackFromLocationRequested, this, - [this, group](const QString& trackLocation) { - emit loadLocationToPlayer(trackLocation, group); + [this, group](const QString& trackLocation, bool play) { + loadLocationToPlayer(trackLocation, group, play); }); connect(pPlayerProxy, &QmlPlayerProxy::cloneFromGroup, @@ -43,5 +39,24 @@ QObject* QmlPlayerManagerProxy::getPlayer(const QString& group) { return pPlayerProxy; } +void QmlPlayerManagerProxy::loadLocationIntoNextAvailableDeck( + const QString& trackLocation, bool play) { + m_pPlayerManager->slotLoadLocationIntoNextAvailableDeck(trackLocation, play); +} + +void QmlPlayerManagerProxy::loadLocationUrlIntoNextAvailableDeck( + const QUrl& trackLocationUrl, bool play) { + if (trackLocationUrl.isLocalFile()) { + loadLocationIntoNextAvailableDeck(trackLocationUrl.toLocalFile(), play); + } else { + qWarning() << "QmlPlayerManagerProxy: URL" << trackLocationUrl << "is not a local file!"; + } +} + +void QmlPlayerManagerProxy::loadLocationToPlayer( + const QString& location, const QString& group, bool play) { + m_pPlayerManager->slotLoadLocationToPlayer(location, group, play); +} + } // namespace qml } // namespace mixxx diff --git a/src/qml/qmlplayermanagerproxy.h b/src/qml/qmlplayermanagerproxy.h index a1c6216e1402..738007d494bf 100644 --- a/src/qml/qmlplayermanagerproxy.h +++ b/src/qml/qmlplayermanagerproxy.h @@ -15,9 +15,11 @@ class QmlPlayerManagerProxy : public QObject { QObject* parent = nullptr); Q_INVOKABLE QObject* getPlayer(const QString& deck); - - signals: - void loadLocationToPlayer(const QString& location, const QString& group); + Q_INVOKABLE void loadLocationIntoNextAvailableDeck(const QString& location, bool play = false); + Q_INVOKABLE void loadLocationUrlIntoNextAvailableDeck( + const QUrl& locationUrl, bool play = false); + Q_INVOKABLE void loadLocationToPlayer( + const QString& location, const QString& group, bool play = false); private: const std::shared_ptr m_pPlayerManager; diff --git a/src/qml/qmlplayerproxy.cpp b/src/qml/qmlplayerproxy.cpp index 15226b188daf..9f5d1e651cf1 100644 --- a/src/qml/qmlplayerproxy.cpp +++ b/src/qml/qmlplayerproxy.cpp @@ -41,13 +41,13 @@ QmlPlayerProxy::QmlPlayerProxy(BaseTrackPlayer* pTrackPlayer, QObject* parent) connect(this, &QmlPlayerProxy::trackChanged, this, &QmlPlayerProxy::slotTrackChanged); } -void QmlPlayerProxy::loadTrackFromLocation(const QString& trackLocation) { - emit loadTrackFromLocationRequested(trackLocation); +void QmlPlayerProxy::loadTrackFromLocation(const QString& trackLocation, bool play) { + emit loadTrackFromLocationRequested(trackLocation, play); } -void QmlPlayerProxy::loadTrackFromLocationUrl(const QUrl& trackLocationUrl) { +void QmlPlayerProxy::loadTrackFromLocationUrl(const QUrl& trackLocationUrl, bool play) { if (trackLocationUrl.isLocalFile()) { - loadTrackFromLocation(trackLocationUrl.toLocalFile()); + loadTrackFromLocation(trackLocationUrl.toLocalFile(), play); } else { qWarning() << "QmlPlayerProxy: URL" << trackLocationUrl << "is not a local file!"; } diff --git a/src/qml/qmlplayerproxy.h b/src/qml/qmlplayerproxy.h index 89509bcdbccb..6ea28e27dc11 100644 --- a/src/qml/qmlplayerproxy.h +++ b/src/qml/qmlplayerproxy.h @@ -55,8 +55,8 @@ class QmlPlayerProxy : public QObject { return m_pTrackPlayer; } - Q_INVOKABLE void loadTrackFromLocation(const QString& trackLocation); - Q_INVOKABLE void loadTrackFromLocationUrl(const QUrl& trackLocationUrl); + Q_INVOKABLE void loadTrackFromLocation(const QString& trackLocation, bool play = false); + Q_INVOKABLE void loadTrackFromLocationUrl(const QUrl& trackLocationUrl, bool play = false); public slots: void slotTrackLoaded(TrackPointer pTrack); @@ -99,7 +99,7 @@ class QmlPlayerProxy : public QObject { void coverArtUrlChanged(); void trackLocationUrlChanged(); - void loadTrackFromLocationRequested(const QString& trackLocation); + void loadTrackFromLocationRequested(const QString& trackLocation, bool play); private: QPointer m_pTrackPlayer; diff --git a/src/skin/legacy/legacyskinparser.cpp b/src/skin/legacy/legacyskinparser.cpp index 16f00063ce81..09ffed4e62b0 100644 --- a/src/skin/legacy/legacyskinparser.cpp +++ b/src/skin/legacy/legacyskinparser.cpp @@ -962,7 +962,7 @@ QWidget* LegacySkinParser::parseOverview(const QDomElement& node) { connect(overviewWidget, &WOverview::trackDropped, m_pPlayerManager, - &PlayerManager::slotLoadToPlayer); + &PlayerManager::slotLoadLocationToPlayerStopped); connect(overviewWidget, &WOverview::cloneDeck, m_pPlayerManager, &PlayerManager::slotCloneDeck); @@ -1017,7 +1017,7 @@ QWidget* LegacySkinParser::parseVisual(const QDomElement& node) { connect(viewer, &WWaveformViewer::trackDropped, m_pPlayerManager, - &PlayerManager::slotLoadToPlayer); + &PlayerManager::slotLoadLocationToPlayerStopped); connect(viewer, &WWaveformViewer::cloneDeck, m_pPlayerManager, &PlayerManager::slotCloneDeck); @@ -1046,7 +1046,7 @@ QWidget* LegacySkinParser::parseText(const QDomElement& node) { connect(pTrackText, &WTrackText::trackDropped, m_pPlayerManager, - &PlayerManager::slotLoadToPlayer); + &PlayerManager::slotLoadLocationToPlayerStopped); connect(pTrackText, &WTrackText::cloneDeck, m_pPlayerManager, &PlayerManager::slotCloneDeck); TrackPointer pTrack = pPlayer->getLoadedTrack(); @@ -1083,7 +1083,7 @@ QWidget* LegacySkinParser::parseTrackProperty(const QDomElement& node) { connect(pTrackProperty, &WTrackProperty::trackDropped, m_pPlayerManager, - &PlayerManager::slotLoadToPlayer); + &PlayerManager::slotLoadLocationToPlayerStopped); connect(pTrackProperty, &WTrackProperty::cloneDeck, m_pPlayerManager, @@ -1126,7 +1126,7 @@ QWidget* LegacySkinParser::parseTrackWidgetGroup(const QDomElement& node) { connect(pGroup, &WTrackWidgetGroup::trackDropped, m_pPlayerManager, - &PlayerManager::slotLoadToPlayer); + &PlayerManager::slotLoadLocationToPlayerStopped); connect(pGroup, &WTrackWidgetGroup::cloneDeck, m_pPlayerManager, @@ -1282,7 +1282,10 @@ QWidget* LegacySkinParser::parseSpinny(const QDomElement& node) { spinny, &WSpinny::render); connect(waveformWidgetFactory, &WaveformWidgetFactory::swapSpinnies, spinny, &WSpinny::swap); - connect(spinny, &WSpinny::trackDropped, m_pPlayerManager, &PlayerManager::slotLoadToPlayer); + connect(spinny, + &WSpinny::trackDropped, + m_pPlayerManager, + &PlayerManager::slotLoadLocationToPlayerStopped); connect(spinny, &WSpinny::cloneDeck, m_pPlayerManager, &PlayerManager::slotCloneDeck); ControlObject* showCoverControl = controlFromConfigNode(node.toElement(), "ShowCoverControl"); @@ -1341,7 +1344,7 @@ QWidget* LegacySkinParser::parseCoverArt(const QDomElement& node) { connect(pCoverArt, &WCoverArt::trackDropped, m_pPlayerManager, - &PlayerManager::slotLoadToPlayer); + &PlayerManager::slotLoadLocationToPlayerStopped); connect(pCoverArt, &WCoverArt::cloneDeck, m_pPlayerManager, &PlayerManager::slotCloneDeck); }