Skip to content
Draft
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
2 changes: 1 addition & 1 deletion data/themes/classic/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,7 @@ lmms--gui--MixerChannelView {
color: #e0e0e0;
qproperty-backgroundActive: qlineargradient(spread:reflect, x1:0, y1:0, x2:1, y2:0,
stop:0 #7b838d, stop:1 #6b7581 );
qproperty-strokeOuterActive: rgb( 0, 0, 0 );
qproperty-strokeOuterActive: rgb( 0, 255, 0 );
qproperty-strokeOuterInactive: rgba( 0, 0, 0, 50 );
qproperty-strokeInnerActive: rgba( 255, 255, 255, 100 );
qproperty-strokeInnerInactive: rgba( 255, 255, 255, 50 );
Expand Down
2 changes: 1 addition & 1 deletion data/themes/default/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -691,7 +691,7 @@ lmms--gui--MixerChannelView {
qproperty-backgroundActive: #3B424A;
qproperty-strokeOuterActive: #262B30;
qproperty-strokeOuterInactive: #262B30;
qproperty-strokeInnerActive: #0C0D0F;
qproperty-strokeInnerActive: #0bd556;
qproperty-strokeInnerInactive: #0C0D0F;
}

Expand Down
36 changes: 31 additions & 5 deletions include/MixerChannelView.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,14 @@

#include "EffectRackView.h"
#include "Fader.h"
#include "GuiApplication.h"
#include "Knob.h"
#include "LcdWidget.h"
#include "PixmapButton.h"
#include "SendButtonIndicator.h"

#include <set>

namespace lmms {
class MixerChannel;
}
Expand Down Expand Up @@ -82,21 +85,42 @@ class MixerChannelView : public QWidget
QColor strokeInnerInactive() const { return m_strokeInnerInactive; }
void setStrokeInnerInactive(const QColor& c) { m_strokeInnerInactive = c; }

static const std::set<MixerChannelView*>& selectedChannels()
{
return s_selectedChannels;
}
static void select(MixerChannelView* mcv)
{
s_selectedChannels.insert(mcv);
}
static void deselect(MixerChannelView* mcv)
{
s_selectedChannels.erase(mcv);
}
static void deselectAll()
{
s_selectedChannels.clear();
}
static void sanitizeSelection();


public slots:
void renameChannel();
void resetColor();
void selectColor();
void randomizeColor();
void toggledSolo();
void toggledMute();

private slots:
void renameFinished();
void removeChannel();
void removeUnusedChannels();
void moveChannelLeft();
void moveChannelRight();
static void removeSelectedChannels();
static void removeUnusedChannels();
static void moveChannelLeft();
static void moveChannelRight();

private:
bool confirmRemoval(int index);
static bool confirmRemoval(int index);
QString elideName(const QString& name);
MixerChannel* mixerChannel() const;
auto isMasterChannel() const -> bool { return m_channelIndex == 0; }
Expand Down Expand Up @@ -128,6 +152,8 @@ private slots:
QColor m_strokeInnerActive;
QColor m_strokeInnerInactive;

static std::set<MixerChannelView*> s_selectedChannels;

friend class MixerView;
};
} // namespace lmms::gui
Expand Down
16 changes: 6 additions & 10 deletions include/MixerView.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,12 @@ class LMMS_EXPORT MixerView : public QWidget, public ModelView,
}


void setCurrentMixerChannel(MixerChannelView* channel);
void setCurrentMixerChannel(int channel);
void setCurrentMixerChannel(MixerChannelView* channel, bool keepSelection = false, bool rangeSelect = false);
void setCurrentMixerChannel(int channel, bool keepSelection = false, bool rangeSelect = false);

void selectMixerChannelsInRange(int index1, int index2);

void sanitizeSelection();

void clear();

Expand Down Expand Up @@ -101,17 +105,9 @@ public slots:

private slots:
void updateFaders();
// TODO This should be improved. Currently the solo and mute models are connected via
// the MixerChannelView's constructor with the MixerView. It would already be an improvement
// if the MixerView connected itself to each new MixerChannel that it creates/handles.
void toggledSolo();
void toggledMute();

private:
Mixer* getMixer() const;
void updateAllMixerChannels();
void connectToSoloAndMute(int channelIndex);
void disconnectFromSoloAndMute(int channelIndex);

private:
QVector<MixerChannelView*> m_mixerChannelViews;
Expand Down
133 changes: 113 additions & 20 deletions src/gui/MixerChannelView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "MixerChannelView.h"

#include <QCheckBox>
#include <QDebug>
#include <QFont>
#include <QGraphicsProxyWidget>
#include <QGraphicsScene>
Expand All @@ -44,6 +45,9 @@
#include "Song.h"

namespace lmms::gui {

std::set<MixerChannelView*> MixerChannelView::s_selectedChannels;

MixerChannelView::MixerChannelView(QWidget* parent, MixerView* mixerView, int channelIndex)
: QWidget(parent)
, m_mixerView(mixerView)
Expand Down Expand Up @@ -120,6 +124,8 @@ MixerChannelView::MixerChannelView(QWidget* parent, MixerView* mixerView, int ch
m_soloButton->setInactiveGraphic(embed::getIconPixmap("led_off"));
m_soloButton->setCheckable(true);
m_soloButton->setToolTip(tr("Solo this channel"));
connect(m_muteButton, &PixmapButton::toggled, this, &MixerChannelView::toggledMute);
connect(m_soloButton, &PixmapButton::toggled, this, &MixerChannelView::toggledSolo);

auto soloMuteLayout = new QVBoxLayout();
soloMuteLayout->setContentsMargins(0, 0, 0, 0);
Expand Down Expand Up @@ -151,6 +157,11 @@ MixerChannelView::MixerChannelView(QWidget* parent, MixerView* mixerView, int ch
connect(m_renameLineEdit, &QLineEdit::editingFinished, this, &MixerChannelView::renameFinished);
}

void MixerChannelView::sanitizeSelection()
{
std::erase_if(MixerChannelView::s_selectedChannels, [](auto m){ return !getGUI()->mixerView()->m_mixerChannelViews.contains(m); });
}

void MixerChannelView::contextMenuEvent(QContextMenuEvent*)
{
auto contextMenu = new CaptionMenu(mixerChannel()->m_name, this);
Expand All @@ -167,7 +178,7 @@ void MixerChannelView::contextMenuEvent(QContextMenuEvent*)
if (!isMasterChannel()) // no remove-option in master
{
contextMenu->addAction(
embed::getIconPixmap("cancel"), tr("R&emove channel"), this, &MixerChannelView::removeChannel);
embed::getIconPixmap("cancel"), tr("R&emove selected channels"), this, &MixerChannelView::removeSelectedChannels);
contextMenu->addSeparator();
}

Expand All @@ -192,16 +203,17 @@ void MixerChannelView::paintEvent(QPaintEvent*)
static constexpr auto outerBorderSize = 1;

const auto channel = mixerChannel();
const auto isSelected = selectedChannels().contains(this);
const auto isActive = m_mixerView->currentMixerChannel() == this;
const auto width = rect().width();
const auto height = rect().height();
auto painter = QPainter{this};

if (channel->color().has_value() && !channel->m_muteModel.value())
{
painter.fillRect(rect(), channel->color()->darker(isActive ? 120 : 150));
painter.fillRect(rect(), channel->color()->darker(isSelected ? 120 : 150));
}
else { painter.fillRect(rect(), isActive ? backgroundActive().color() : painter.background().color()); }
else { painter.fillRect(rect(), isSelected ? backgroundActive().color() : painter.background().color()); }

// inner border
painter.setPen(isActive ? strokeInnerActive() : strokeInnerInactive());
Expand All @@ -212,9 +224,13 @@ void MixerChannelView::paintEvent(QPaintEvent*)
painter.drawRect(0, 0, width - outerBorderSize, height - outerBorderSize);
}

void MixerChannelView::mousePressEvent(QMouseEvent*)
void MixerChannelView::mousePressEvent(QMouseEvent* me)
{
if (m_mixerView->currentMixerChannel() != this) { m_mixerView->setCurrentMixerChannel(this); }
bool keepSelection = me->modifiers() & Qt::ControlModifier || me->modifiers() & Qt::ShiftModifier;
bool rangeSelect = me->modifiers() & Qt::ShiftModifier;
if (m_mixerView->currentMixerChannel() != this) {
m_mixerView->setCurrentMixerChannel(this, keepSelection, rangeSelect);
}
}

void MixerChannelView::mouseDoubleClickEvent(QMouseEvent*)
Expand Down Expand Up @@ -293,9 +309,12 @@ void MixerChannelView::renameFinished()

void MixerChannelView::resetColor()
{
mixerChannel()->setColor(std::nullopt);
for (auto mcv : selectedChannels())
{
mcv->mixerChannel()->setColor(std::nullopt);
mcv->update();
}
Engine::getSong()->setModified();
update();
}

void MixerChannelView::selectColor()
Expand All @@ -308,20 +327,63 @@ void MixerChannelView::selectColor()

if (!newColor.isValid()) { return; }

channel->setColor(newColor);

for (auto mcv : selectedChannels())
{
mcv->mixerChannel()->setColor(newColor);
mcv->update();
}
Engine::getSong()->setModified();
update();
}

void MixerChannelView::randomizeColor()
{
auto channel = mixerChannel();
channel->setColor(ColorChooser::getPalette(ColorChooser::Palette::Mixer)[rand() % 48]);
const QColor randomColor = ColorChooser::getPalette(ColorChooser::Palette::Mixer)[rand() % 48];
for (auto mcv : selectedChannels())
{
mcv->mixerChannel()->setColor(randomColor);
mcv->update();
}
Engine::getSong()->setModified();
update();
}

void MixerChannelView::toggledMute()
{
if (selectedChannels().contains(this))
{
for (auto mcv : selectedChannels())
{
getGUI()->mixerView()->getMixer()->mixerChannel(mcv->channelIndex())->m_muteModel.setValue(Engine::mixer()->mixerChannel(channelIndex())->m_muteModel.value());
mcv->update();
}
}
else
{
update();
}
}

// TODO PLEAE FIX
void MixerChannelView::toggledSolo()
{
getGUI()->mixerView()->getMixer()->toggledSolo();
if (selectedChannels().contains(this))
{
for (auto mcv : selectedChannels())
{
if (mcv != this)
{
getGUI()->mixerView()->getMixer()->mixerChannel(mcv->channelIndex())->m_muteModel.setValue(!getGUI()->mixerView()->getMixer()->mixerChannel(mcv->channelIndex())->m_muteBeforeSolo);
}
mcv->update();
}
}
else
{
update();
}
}


bool MixerChannelView::confirmRemoval(int index)
{
// if config variable is set to false, there is no need for user confirmation
Expand All @@ -331,9 +393,9 @@ bool MixerChannelView::confirmRemoval(int index)
// is the channel is not in use, there is no need for user confirmation
if (!getGUI()->mixerView()->getMixer()->isChannelInUse(index)) { return true; }

QString messageRemoveTrack = tr("This Mixer Channel is being used.\n"
QString messageRemoveTrack = tr("Mixer Channel %1 is being used.\n"
"Are you sure you want to remove this channel?\n\n"
"Warning: This operation can not be undone.");
"Warning: This operation can not be undone.").arg(index);

QString messageTitleRemoveTrack = tr("Confirm removal");
QString askAgainText = tr("Don't ask again");
Expand All @@ -357,11 +419,16 @@ bool MixerChannelView::confirmRemoval(int index)
return answer == QMessageBox::Ok;
}

void MixerChannelView::removeChannel()
void MixerChannelView::removeSelectedChannels()
{
if (!confirmRemoval(m_channelIndex)) { return; }
auto mix = getGUI()->mixerView();
mix->deleteChannel(m_channelIndex);
std::set<MixerChannelView*> tempSelectedChannels = selectedChannels();
for (auto mcv : tempSelectedChannels)
{
if (!confirmRemoval(mcv->channelIndex())) { continue; }
mix->deleteChannel(mcv->channelIndex());
MixerChannelView::deselect(mcv);
}
}

void MixerChannelView::removeUnusedChannels()
Expand All @@ -373,13 +440,39 @@ void MixerChannelView::removeUnusedChannels()
void MixerChannelView::moveChannelLeft()
{
auto mix = getGUI()->mixerView();
mix->moveChannelLeft(m_channelIndex);
int oldSelectedIndex = mix->currentMixerChannel()->channelIndex();
auto min_channel = *std::min_element(selectedChannels().begin(), selectedChannels().end(), [](auto mcv1, auto mcv2){ return mcv1->channelIndex() < mcv2->channelIndex(); });
// Don't shift if it would collide with master
if (min_channel->channelIndex() <= 1) { return; }

std::set<MixerChannelView*> tempSelectedChannels = selectedChannels();
std::set<MixerChannelView*> newSelectedChannels;
for (auto it = tempSelectedChannels.begin(); it != tempSelectedChannels.end(); ++it)
{
mix->moveChannelLeft((*it)->channelIndex());
newSelectedChannels.insert(mix->m_mixerChannelViews[(*it)->channelIndex() - 1]);
}
mix->setCurrentMixerChannel(oldSelectedIndex - 1, true);
s_selectedChannels = newSelectedChannels;
}

void MixerChannelView::moveChannelRight()
{
auto mix = getGUI()->mixerView();
mix->moveChannelRight(m_channelIndex);
int oldSelectedIndex = mix->currentMixerChannel()->channelIndex();
auto max_channel = *std::max_element(selectedChannels().begin(), selectedChannels().end(), [](auto mcv1, auto mcv2){ return mcv1->channelIndex() < mcv2->channelIndex(); });
// Don't shift if it would go over the end
if (max_channel->channelIndex() >= mix->m_mixerChannelViews.size() - 1) { return; }

std::set<MixerChannelView*> tempSelectedChannels = selectedChannels();
std::set<MixerChannelView*> newSelectedChannels;
for (auto it = tempSelectedChannels.rbegin(); it != tempSelectedChannels.rend(); ++it)
{
mix->moveChannelRight((*it)->channelIndex());
newSelectedChannels.insert(mix->m_mixerChannelViews[(*it)->channelIndex() + 1]);
}
mix->setCurrentMixerChannel(oldSelectedIndex + 1, true);
s_selectedChannels = newSelectedChannels;
}

QString MixerChannelView::elideName(const QString& name)
Expand Down
Loading
Loading