diff --git a/CMakeLists.txt b/CMakeLists.txt
index 465f4728d144..f18cc009e149 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3465,6 +3465,7 @@ if (STEM)
src/sources/soundsourcestem.cpp
src/track/steminfoimporter.cpp
src/track/steminfo.cpp
+ src/widget/wstemlabel.cpp
)
if(QOPENGL)
target_sources(mixxx-lib PRIVATE
diff --git a/res/skins/LateNight/classic/buttons/btn__stem_controls_collapse.svg b/res/skins/LateNight/classic/buttons/btn__stem_controls_collapse.svg
new file mode 100644
index 000000000000..f602b10c819d
--- /dev/null
+++ b/res/skins/LateNight/classic/buttons/btn__stem_controls_collapse.svg
@@ -0,0 +1,83 @@
+
+
diff --git a/res/skins/LateNight/classic/buttons/btn__stem_controls_expand.svg b/res/skins/LateNight/classic/buttons/btn__stem_controls_expand.svg
new file mode 100644
index 000000000000..049b7875b56a
--- /dev/null
+++ b/res/skins/LateNight/classic/buttons/btn__stem_controls_expand.svg
@@ -0,0 +1,83 @@
+
+
diff --git a/res/skins/LateNight/palemoon/buttons/btn__stem_controls_collapse.svg b/res/skins/LateNight/palemoon/buttons/btn__stem_controls_collapse.svg
new file mode 100644
index 000000000000..f602b10c819d
--- /dev/null
+++ b/res/skins/LateNight/palemoon/buttons/btn__stem_controls_collapse.svg
@@ -0,0 +1,83 @@
+
+
diff --git a/res/skins/LateNight/palemoon/buttons/btn__stem_controls_expand.svg b/res/skins/LateNight/palemoon/buttons/btn__stem_controls_expand.svg
new file mode 100644
index 000000000000..049b7875b56a
--- /dev/null
+++ b/res/skins/LateNight/palemoon/buttons/btn__stem_controls_expand.svg
@@ -0,0 +1,83 @@
+
+
diff --git a/res/skins/LateNight/stem_channel.xml b/res/skins/LateNight/stem_channel.xml
new file mode 100644
index 000000000000..b3beec9895df
--- /dev/null
+++ b/res/skins/LateNight/stem_channel.xml
@@ -0,0 +1,188 @@
+
+
+ [QuickEffectRack1_]
+
+
+ horizontal
+ min,min
+
+
+ ChannelMixer_KnobContainer
+ horizontal
+ min,min
+
+
+
+ StemLabel
+
+
+ left
+ right
+ 50,26
+
+
+
+
+
+ QuickEffectButton
+ 36,4
+ 18f,18f
+ 2
+
+ 0
+
+ skins:LateNight//buttons/btn__eqkill.svg
+
+
+ skins:LateNight//buttons/btn__eqkill_active.svg
+
+
+
+ 1
+
+ skins:LateNight//buttons/btn__eqkill_active.svg
+
+
+ skins:LateNight//buttons/btn__eqkill_active.svg
+
+
+
+ LeftButton
+ [ChannelStem],mute_toggle
+
+
+
+
+
+ LeftButton
+ [ChannelStem],mute
+ true
+
+
+
+
+
+
+
+ QuickEffectRack_super1
+ 0,0
+ 40f,26f
+ skins:LateNight//knobs/knob_indicator__orange.svg
+ skins:LateNight//knobs/knob_bg_.svg
+
+
+
+
+
+
+
+
+ 1.998
+
+ [ChannelStem],volume
+
+
+
+
+
+ QuickEffectSelectorLeft
+ 85f,18f
+ [QuickEffectRack1_[ChannelStem]]
+
+
+
+ QuickEffectRack_enabled
+ QuickEffectDot
+ 30,20
+
+
+
+ 2
+ false
+
+ 0
+
+
+ 1
+
+
+ [QuickEffectRack1_[ChannelStem]],enabled
+ LeftButton
+
+
+ [Skin],show_eq_kill_buttons
+
+
+
+ visible
+
+
+
+
+ QuickEffectRack_enabled
+ QuickEffectButton
+ 36,4
+ 18f,18f
+ 2
+
+ 0
+
+ skins:LateNight//buttons/btn__eqkill.svg
+
+
+ skins:LateNight//buttons/btn__eqkill_active.svg
+
+
+
+ 1
+
+ skins:LateNight//buttons/btn__eqkill_active.svg
+
+
+ skins:LateNight//buttons/btn__eqkill_active.svg
+
+
+
+ [QuickEffectRack1_[ChannelStem]],enabled
+ LeftButton
+
+
+ [Skin],show_eq_kill_buttons
+ visible
+
+
+
+
+ QuickEffectRack_super1
+ 0,0
+ 40f,26f
+ skins:LateNight//knobs/knob_indicator__.svg
+ skins:LateNight//knobs/knob_bg_.svg
+
+
+
+
+
+
+
+
+ 1.998
+
+ [QuickEffectRack1_[ChannelStem]],super1
+
+
+
+
+
+
+
+
+ ,loaded_chain_preset
+
+ 0
+
+
+ visible
+
+
+
diff --git a/res/skins/LateNight/style_classic.qss b/res/skins/LateNight/style_classic.qss
index b8bd1cce93fe..1b0d9b4cc4ec 100644
--- a/res/skins/LateNight/style_classic.qss
+++ b/res/skins/LateNight/style_classic.qss
@@ -1371,6 +1371,7 @@ WEffectSelector[highlight="1"] { /*
}
#BeatgridControls WPushButton, WPushButton#BeatgridControlsToggle,
+#StemControls WPushButton, WPushButton#StemControlsToggle,
#DeckRow_5_LoopCuesTransport WPushButton,
WPushButton#PlayDeck,
WPushButton#PreviewIndicator,
@@ -1626,6 +1627,7 @@ WPushButton#FxExpand[displayValue="0"],
WPushButton#FxExpandOverlay[displayValue="0"],
WPushButton#SamplerExpand[displayValue="0"],
#BeatgridControlsToggle,
+#StemControlsToggle,
#SamplerControlsMini WPushButton,
#RecDot,
/* transparent buttons, 0-4 (max button state we have currently) */
@@ -1940,6 +1942,14 @@ WPushButton#BpmLockToggle[value="1"] {
#BeatgridControlsToggle[displayValue="1"] {
image: url(skins:LateNight/classic/buttons/btn__beatgrid_controls_collapse.svg) no-repeat center center;
}
+
+#StemControlsToggle[displayValue="0"] {
+ image: url(skins:LateNight/classic/buttons/btn__stem_controls_expand.svg) no-repeat center center;
+ }
+ #StemControlsToggle[displayValue="1"] {
+ image: url(skins:LateNight/classic/buttons/btn__stem_controls_collapse.svg) no-repeat center center;
+ }
+
#BpmLockToggle[value="1"] {
image: url(skins:LateNight/classic/buttons/btn__bpm_locked.svg);
}
diff --git a/res/skins/LateNight/style_palemoon.qss b/res/skins/LateNight/style_palemoon.qss
index dcf3a966714b..23b2691345f9 100644
--- a/res/skins/LateNight/style_palemoon.qss
+++ b/res/skins/LateNight/style_palemoon.qss
@@ -1553,6 +1553,7 @@ WEffectSelector:!editable,
}
#BeatgridControls WPushButton, WPushButton#BeatgridControlsToggle,
+#StemControls WPushButton, WPushButton#StemControlsToggle,
#DeckRow_5_LoopCuesTransport WPushButton,
#RateControls WPushButton,
WPushButton#PlayDeck,
@@ -1866,6 +1867,7 @@ QPushButton#pushButtonRepeatPlaylist:checked {
/* Special flat/invisible buttons */
#BeatgridControlsToggle,
+#StemControlsToggle,
WPushButton#PlayDeck[displayValue="0"],
WPushButton#PlayDeckMini[displayValue="0"],
WPushButton#PlaySampler[displayValue="0"],
@@ -2369,6 +2371,13 @@ WPushButton#PlayDeck[value="0"] {
image: url(skins:LateNight/palemoon/buttons/btn__keylock_active_34.svg) no-repeat center center;
}
+#StemControlsToggle[displayValue="0"] {
+ image: url(skins:LateNight/palemoon/buttons/btn__stem_controls_expand.svg) no-repeat center center;
+ }
+ #StemControlsToggle[displayValue="1"] {
+ image: url(skins:LateNight/palemoon/buttons/btn__stem_controls_collapse.svg) no-repeat center center;
+ }
+
#BeatgridControlsToggle[displayValue="0"] {
image: url(skins:LateNight/palemoon/buttons/btn__beatgrid_controls_expand.svg) no-repeat center center;
}
diff --git a/res/skins/LateNight/waveform.xml b/res/skins/LateNight/waveform.xml
index 2af82f4188bd..e1325158f030 100644
--- a/res/skins/LateNight/waveform.xml
+++ b/res/skins/LateNight/waveform.xml
@@ -5,12 +5,133 @@
BeatEditCover
,bpmlock
+
+ [EqualizerRack1_]
+
+
+ [EqualizerRack1__Effect1]
+
+
+ regular
+ regular
+
+
+
+
+
+
+
+
+
Waveform
horizontal
me,me
-
+
+ StemControls
+ vertical
+ max,me
+
+
+ 0me,1me
+
+
+
+
+ horizontal
+ f,f
+
+
+
+ 1f,0min
+
+
+
+
+ vertical
+
+
+
+ horizontal
+
+
+ 1min,2me
+
+
+ 1
+
+
+ 1min,3f
+
+
+
+
+
+ horizontal
+
+
+ 1min,2me
+
+
+ 2
+
+
+ 1min,3f
+
+
+
+
+
+ horizontal
+
+
+ 1min,2me
+
+
+ 3
+
+
+ 1min,3f
+
+
+
+
+
+ horizontal
+
+
+ 1min,2me
+
+
+ 4
+
+
+ 1min,3f
+
+
+
+
+
+
+
+
+
+
+
+
+ 1me,1me
+
+
+
+ [Skin],show_stem_controls
+ visible
+
+
+
+
+
+
WaveformBox
horizontal
me,me
diff --git a/res/skins/LateNight/waveforms_container.xml b/res/skins/LateNight/waveforms_container.xml
index 86800623a937..9d5e6ad5e25d 100644
--- a/res/skins/LateNight/waveforms_container.xml
+++ b/res/skins/LateNight/waveforms_container.xml
@@ -5,7 +5,15 @@
me,min
-
+
+ show_stem_controls
+ StemControlsToggle
+ 26f,52f
+
+ [Skin],show_stem_controls
+
+
+
WaveformsContainer
horizontal
me,min
@@ -16,7 +24,7 @@
100me,40me
-
+
horizontal
me,me
diff --git a/src/skin/legacy/legacyskinparser.cpp b/src/skin/legacy/legacyskinparser.cpp
index 6cab8aba4e5e..4be713991f57 100644
--- a/src/skin/legacy/legacyskinparser.cpp
+++ b/src/skin/legacy/legacyskinparser.cpp
@@ -74,6 +74,9 @@
#include "widget/wsplitter.h"
#include "widget/wstarrating.h"
#include "widget/wstatuslight.h"
+#ifdef __STEM__
+#include "widget/wstemlabel.h"
+#endif
#include "widget/wtime.h"
#include "widget/wtrackproperty.h"
#include "widget/wtrackwidgetgroup.h"
@@ -559,7 +562,13 @@ QList LegacySkinParser::parseNode(const QDomElement& node) {
result = wrapWidget(parseLabelWidget(node));
} else if (nodeName == "Label") {
result = wrapWidget(parseLabelWidget(node));
- } else if (nodeName == "Knob") {
+ }
+#ifdef __STEM__
+ else if (nodeName == "StemLabel") {
+ result = wrapWidget(parseStemLabelWidget(node));
+ }
+#endif
+ else if (nodeName == "Knob") {
result = wrapWidget(parseStandardWidget(node));
} else if (nodeName == "KnobComposed") {
result = wrapWidget(parseStandardWidget(node));
@@ -967,6 +976,42 @@ void LegacySkinParser::setupLabelWidget(const QDomElement& element, WLabel* pLab
pLabel->Init();
}
+#ifdef __STEM__
+template
+QWidget* LegacySkinParser::parseStemLabelWidget(const QDomElement& element) {
+ T* pLabel = new T(m_pParent);
+ setupStemLabelWidget(element, pLabel);
+
+ QString group = lookupNodeGroup(element);
+ BaseTrackPlayer* pPlayer = m_pPlayerManager->getPlayer(group);
+ connect(pPlayer,
+ &BaseTrackPlayer::newTrackLoaded,
+ pLabel,
+ &WStemLabel::slotTrackLoaded);
+
+ connect(pPlayer,
+ &BaseTrackPlayer::trackUnloaded,
+ pLabel,
+ &WStemLabel::slotTrackUnloaded);
+
+ return pLabel;
+}
+
+void LegacySkinParser::setupStemLabelWidget(const QDomElement& element, WStemLabel* pLabel) {
+ // NOTE(rryan): To support color schemes, the WWidget::setup() call must
+ // come first. This is because WLabel derivatives change the palette based
+ // on the node and setupWidget() will set the widget style. If the style is
+ // set before the palette is set then the custom palette will not take
+ // effect which breaks color scheme support.
+ pLabel->setup(element, *m_pContext);
+ commonWidgetSetup(element, pLabel);
+ pLabel->installEventFilter(m_pKeyboard);
+ pLabel->installEventFilter(
+ m_pControllerManager->getControllerLearningEventFilter());
+ pLabel->Init();
+}
+#endif
+
QWidget* LegacySkinParser::parseOverview(const QDomElement& node) {
#ifdef MIXXX_USE_QML
if (CmdlineArgs::Instance().isQml()) {
@@ -1732,7 +1777,6 @@ QDomElement LegacySkinParser::loadTemplate(const QString& path) {
if (!templateFile.open(QIODevice::ReadOnly)) {
qWarning() << "Could not open template file:" << absolutePath;
- return QDomElement();
}
QDomDocument tmpl("template");
diff --git a/src/skin/legacy/legacyskinparser.h b/src/skin/legacy/legacyskinparser.h
index 11cb4639b713..283bb08f8cb1 100644
--- a/src/skin/legacy/legacyskinparser.h
+++ b/src/skin/legacy/legacyskinparser.h
@@ -22,6 +22,7 @@ class RecordingManager;
class ControllerManager;
class SkinContext;
class WLabel;
+class WStemLabel;
class ControlObject;
class LaunchImage;
class WWidgetGroup;
@@ -75,6 +76,13 @@ class LegacySkinParser : public QObject, public SkinParser {
template
QWidget* parseLabelWidget(const QDomElement& element);
void setupLabelWidget(const QDomElement& element, WLabel* pLabel);
+
+#ifdef __STEM__
+ template
+ QWidget* parseStemLabelWidget(const QDomElement& element);
+ void setupStemLabelWidget(const QDomElement& element, WStemLabel* pLabel);
+#endif
+
QWidget* parseText(const QDomElement& node);
QWidget* parseTrackProperty(const QDomElement& node);
QWidget* parseStarRating(const QDomElement& node);
diff --git a/src/widget/wstemlabel.cpp b/src/widget/wstemlabel.cpp
new file mode 100644
index 000000000000..ab4f2355957b
--- /dev/null
+++ b/src/widget/wstemlabel.cpp
@@ -0,0 +1,51 @@
+#include "wstemlabel.h"
+
+#include "moc_wstemlabel.cpp"
+
+WStemLabel::WStemLabel(QWidget* pParent)
+ : WLabel(pParent),
+ m_stemInfo(QString(), QColor()),
+ m_stemNo(0) {
+}
+
+void WStemLabel::setup(const QDomNode& node, const SkinContext& context) {
+ m_stemNo = context.selectInt(node, "StemNum", 0);
+}
+
+void WStemLabel::slotTrackUnloaded(TrackPointer track) {
+ Q_UNUSED(track);
+ m_stemInfo = StemInfo();
+ updateLabel();
+}
+
+void WStemLabel::slotTrackLoaded(TrackPointer track) {
+ if (!track) {
+ return;
+ }
+
+ auto stemInfo = track->getStemInfo();
+
+ if (stemInfo.isEmpty()) {
+ return;
+ }
+
+ m_stemInfo = stemInfo[m_stemNo - 1];
+ updateLabel();
+}
+
+void WStemLabel::updateLabel() {
+ QColor color = m_stemInfo.getColor();
+ QString text = m_stemInfo.getLabel();
+ setTextColor(color);
+ setLabelText(text);
+}
+
+void WStemLabel::setTextColor(const QColor& color) {
+ QPalette palette = this->palette();
+ palette.setColor(QPalette::WindowText, color);
+ this->setPalette(palette);
+}
+
+void WStemLabel::setLabelText(const QString& text) {
+ this->setText(text);
+}
diff --git a/src/widget/wstemlabel.h b/src/widget/wstemlabel.h
new file mode 100644
index 000000000000..52cb22707f6a
--- /dev/null
+++ b/src/widget/wstemlabel.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "control/controlproxy.h"
+#include "skin/legacy/skincontext.h"
+#include "track/track.h"
+#include "widget/wlabel.h"
+
+class WStemLabel : public WLabel {
+ Q_OBJECT
+ public:
+ explicit WStemLabel(QWidget* pParent = nullptr);
+
+ void setup(const QDomNode& node, const SkinContext& context) override;
+
+ public slots:
+ void slotTrackLoaded(TrackPointer track);
+ void slotTrackUnloaded(TrackPointer track);
+
+ private slots:
+ void updateLabel();
+
+ private:
+ void setTextColor(const QColor& color);
+ void setLabelText(const QString& text);
+
+ StemInfo m_stemInfo;
+ QString m_group;
+ int m_stemNo;
+};