Reformat MixerChannelView classes (#7431)

This commit is contained in:
saker
2024-09-30 19:32:21 -04:00
committed by GitHub
parent 729593c022
commit 860749a8a1
2 changed files with 503 additions and 514 deletions

View File

@@ -25,6 +25,13 @@
#ifndef MIXER_CHANNEL_VIEW_H
#define MIXER_CHANNEL_VIEW_H
#include <QGraphicsView>
#include <QLabel>
#include <QLineEdit>
#include <QPixmap>
#include <QStackedWidget>
#include <QWidget>
#include "EffectRackView.h"
#include "Fader.h"
#include "Knob.h"
@@ -32,109 +39,100 @@
#include "PixmapButton.h"
#include "SendButtonIndicator.h"
#include <QGraphicsView>
#include <QLabel>
#include <QLineEdit>
#include <QPixmap>
#include <QWidget>
#include <QStackedWidget>
namespace lmms
{
class MixerChannel;
namespace lmms {
class MixerChannel;
}
namespace lmms::gui
namespace lmms::gui {
class PeakIndicator;
constexpr int MIXER_CHANNEL_INNER_BORDER_SIZE = 3;
constexpr int MIXER_CHANNEL_OUTER_BORDER_SIZE = 1;
class MixerChannelView : public QWidget
{
class PeakIndicator;
Q_OBJECT
Q_PROPERTY(QBrush backgroundActive READ backgroundActive WRITE setBackgroundActive)
Q_PROPERTY(QColor strokeOuterActive READ strokeOuterActive WRITE setStrokeOuterActive)
Q_PROPERTY(QColor strokeOuterInactive READ strokeOuterInactive WRITE setStrokeOuterInactive)
Q_PROPERTY(QColor strokeInnerActive READ strokeInnerActive WRITE setStrokeInnerActive)
Q_PROPERTY(QColor strokeInnerInactive READ strokeInnerInactive WRITE setStrokeInnerInactive)
public:
MixerChannelView(QWidget* parent, MixerView* mixerView, int channelIndex);
void paintEvent(QPaintEvent* event) override;
void contextMenuEvent(QContextMenuEvent*) override;
void mousePressEvent(QMouseEvent*) override;
void mouseDoubleClickEvent(QMouseEvent*) override;
bool eventFilter(QObject* dist, QEvent* event) override;
constexpr int MIXER_CHANNEL_INNER_BORDER_SIZE = 3;
constexpr int MIXER_CHANNEL_OUTER_BORDER_SIZE = 1;
int channelIndex() const;
void setChannelIndex(int index);
class MixerChannelView : public QWidget
{
Q_OBJECT
Q_PROPERTY(QBrush backgroundActive READ backgroundActive WRITE setBackgroundActive)
Q_PROPERTY(QColor strokeOuterActive READ strokeOuterActive WRITE setStrokeOuterActive)
Q_PROPERTY(QColor strokeOuterInactive READ strokeOuterInactive WRITE setStrokeOuterInactive)
Q_PROPERTY(QColor strokeInnerActive READ strokeInnerActive WRITE setStrokeInnerActive)
Q_PROPERTY(QColor strokeInnerInactive READ strokeInnerInactive WRITE setStrokeInnerInactive)
public:
MixerChannelView(QWidget* parent, MixerView* mixerView, int channelIndex);
void paintEvent(QPaintEvent* event) override;
void contextMenuEvent(QContextMenuEvent*) override;
void mousePressEvent(QMouseEvent*) override;
void mouseDoubleClickEvent(QMouseEvent*) override;
bool eventFilter(QObject* dist, QEvent* event) override;
QBrush backgroundActive() const;
void setBackgroundActive(const QBrush& c);
int channelIndex() const;
void setChannelIndex(int index);
QColor strokeOuterActive() const;
void setStrokeOuterActive(const QColor& c);
QBrush backgroundActive() const;
void setBackgroundActive(const QBrush& c);
QColor strokeOuterInactive() const;
void setStrokeOuterInactive(const QColor& c);
QColor strokeOuterActive() const;
void setStrokeOuterActive(const QColor& c);
QColor strokeInnerActive() const;
void setStrokeInnerActive(const QColor& c);
QColor strokeOuterInactive() const;
void setStrokeOuterInactive(const QColor& c);
QColor strokeInnerInactive() const;
void setStrokeInnerInactive(const QColor& c);
QColor strokeInnerActive() const;
void setStrokeInnerActive(const QColor& c);
void reset();
QColor strokeInnerInactive() const;
void setStrokeInnerInactive(const QColor& c);
public slots:
void renameChannel();
void resetColor();
void selectColor();
void randomizeColor();
void reset();
private slots:
void renameFinished();
void removeChannel();
void removeUnusedChannels();
void moveChannelLeft();
void moveChannelRight();
public slots:
void renameChannel();
void resetColor();
void selectColor();
void randomizeColor();
private:
bool confirmRemoval(int index);
QString elideName(const QString& name);
MixerChannel* mixerChannel() const;
auto isMasterChannel() const -> bool { return m_channelIndex == 0; }
private slots:
void renameFinished();
void removeChannel();
void removeUnusedChannels();
void moveChannelLeft();
void moveChannelRight();
private:
SendButtonIndicator* m_sendButton;
QLabel* m_receiveArrow;
QStackedWidget* m_receiveArrowOrSendButton;
int m_receiveArrowStackedIndex = -1;
int m_sendButtonStackedIndex = -1;
private:
bool confirmRemoval(int index);
QString elideName(const QString& name);
MixerChannel* mixerChannel() const;
auto isMasterChannel() const -> bool { return m_channelIndex == 0; }
Knob* m_sendKnob;
LcdWidget* m_channelNumberLcd;
QLineEdit* m_renameLineEdit;
QGraphicsView* m_renameLineEditView;
QLabel* m_sendArrow;
PixmapButton* m_muteButton;
PixmapButton* m_soloButton;
PeakIndicator* m_peakIndicator = nullptr;
Fader* m_fader;
EffectRackView* m_effectRackView;
MixerView* m_mixerView;
int m_channelIndex = 0;
bool m_inRename = false;
private:
SendButtonIndicator* m_sendButton;
QLabel* m_receiveArrow;
QStackedWidget* m_receiveArrowOrSendButton;
int m_receiveArrowStackedIndex = -1;
int m_sendButtonStackedIndex = -1;
QBrush m_backgroundActive;
QColor m_strokeOuterActive;
QColor m_strokeOuterInactive;
QColor m_strokeInnerActive;
QColor m_strokeInnerInactive;
Knob* m_sendKnob;
LcdWidget* m_channelNumberLcd;
QLineEdit* m_renameLineEdit;
QGraphicsView* m_renameLineEditView;
QLabel* m_sendArrow;
PixmapButton* m_muteButton;
PixmapButton* m_soloButton;
PeakIndicator* m_peakIndicator = nullptr;
Fader* m_fader;
EffectRackView* m_effectRackView;
MixerView* m_mixerView;
int m_channelIndex = 0;
bool m_inRename = false;
QBrush m_backgroundActive;
QColor m_strokeOuterActive;
QColor m_strokeOuterInactive;
QColor m_strokeInnerActive;
QColor m_strokeInnerInactive;
friend class MixerView;
};
friend class MixerView;
};
} // namespace lmms::gui
#endif // MIXER_CHANNEL_VIEW_H

View File

@@ -22,450 +22,441 @@
*
*/
#include "CaptionMenu.h"
#include "ColorChooser.h"
#include "GuiApplication.h"
#include "Mixer.h"
#include "MixerChannelView.h"
#include "MixerView.h"
#include "PeakIndicator.h"
#include "Song.h"
#include "ConfigManager.h"
#include "FontHelper.h"
#include <QCheckBox>
#include <QFont>
#include <QGraphicsProxyWidget>
#include <QGraphicsScene>
#include <QMenu>
#include <QPainter>
#include <QFont>
#include <QMessageBox>
#include <QCheckBox>
#include <QPainter>
#include <cassert>
namespace lmms::gui
#include "CaptionMenu.h"
#include "ColorChooser.h"
#include "ConfigManager.h"
#include "FontHelper.h"
#include "GuiApplication.h"
#include "Mixer.h"
#include "MixerView.h"
#include "PeakIndicator.h"
#include "Song.h"
namespace lmms::gui {
MixerChannelView::MixerChannelView(QWidget* parent, MixerView* mixerView, int channelIndex)
: QWidget(parent)
, m_mixerView(mixerView)
, m_channelIndex(channelIndex)
{
MixerChannelView::MixerChannelView(QWidget* parent, MixerView* mixerView, int channelIndex) :
QWidget(parent),
m_mixerView(mixerView),
m_channelIndex(channelIndex)
{
auto retainSizeWhenHidden = [](QWidget* widget)
{
auto sizePolicy = widget->sizePolicy();
sizePolicy.setRetainSizeWhenHidden(true);
widget->setSizePolicy(sizePolicy);
};
auto receiveArrowContainer = new QWidget{};
auto receiveArrowLayout = new QVBoxLayout{receiveArrowContainer};
m_receiveArrow = new QLabel{};
m_receiveArrow->setPixmap(embed::getIconPixmap("receive_bg_arrow"));
receiveArrowLayout->setContentsMargins(0, 0, 0, 0);
receiveArrowLayout->setSpacing(0);
receiveArrowLayout->addWidget(m_receiveArrow, 0, Qt::AlignHCenter);
auto sendButtonContainer = new QWidget{};
auto sendButtonLayout = new QVBoxLayout{sendButtonContainer};
m_sendButton = new SendButtonIndicator{this, this, mixerView};
sendButtonLayout->setContentsMargins(0, 0, 0, 0);
sendButtonLayout->setSpacing(0);
sendButtonLayout->addWidget(m_sendButton, 0, Qt::AlignHCenter);
m_receiveArrowOrSendButton = new QStackedWidget{this};
m_receiveArrowStackedIndex = m_receiveArrowOrSendButton->addWidget(receiveArrowContainer);
m_sendButtonStackedIndex = m_receiveArrowOrSendButton->addWidget(sendButtonContainer);
retainSizeWhenHidden(m_receiveArrowOrSendButton);
m_sendKnob = new Knob{KnobType::Bright26, this, tr("Channel send amount")};
retainSizeWhenHidden(m_sendKnob);
m_sendArrow = new QLabel{};
m_sendArrow->setPixmap(embed::getIconPixmap("send_bg_arrow"));
retainSizeWhenHidden(m_sendArrow);
m_channelNumberLcd = new LcdWidget{2, this};
m_channelNumberLcd->setValue(channelIndex);
retainSizeWhenHidden(m_channelNumberLcd);
const auto mixerChannel = Engine::mixer()->mixerChannel(channelIndex);
const auto mixerName = mixerChannel->m_name;
setToolTip(mixerName);
m_renameLineEdit = new QLineEdit{mixerName, nullptr};
m_renameLineEdit->setFixedWidth(65);
m_renameLineEdit->setFont(adjustedToPixelSize(font(), LARGE_FONT_SIZE));
m_renameLineEdit->setReadOnly(true);
m_renameLineEdit->installEventFilter(this);
auto renameLineEditScene = new QGraphicsScene{};
m_renameLineEditView = new QGraphicsView{};
m_renameLineEditView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_renameLineEditView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_renameLineEditView->setAttribute(Qt::WA_TransparentForMouseEvents, true);
m_renameLineEditView->setScene(renameLineEditScene);
auto renameLineEditProxy = renameLineEditScene->addWidget(m_renameLineEdit);
renameLineEditProxy->setRotation(-90);
m_renameLineEditView->setFixedSize(m_renameLineEdit->height() + 5, m_renameLineEdit->width() + 5);
m_muteButton = new PixmapButton(this, tr("Mute"));
m_muteButton->setModel(&mixerChannel->m_muteModel);
m_muteButton->setActiveGraphic(embed::getIconPixmap("led_off"));
m_muteButton->setInactiveGraphic(embed::getIconPixmap("led_green"));
m_muteButton->setCheckable(true);
m_muteButton->setToolTip(tr("Mute this channel"));
m_soloButton = new PixmapButton(this, tr("Solo"));
m_soloButton->setModel(&mixerChannel->m_soloModel);
m_soloButton->setActiveGraphic(embed::getIconPixmap("led_red"));
m_soloButton->setInactiveGraphic(embed::getIconPixmap("led_off"));
m_soloButton->setCheckable(true);
m_soloButton->setToolTip(tr("Solo this channel"));
auto soloMuteLayout = new QVBoxLayout();
soloMuteLayout->setContentsMargins(0, 0, 0, 0);
soloMuteLayout->setSpacing(0);
soloMuteLayout->addWidget(m_soloButton, 0, Qt::AlignHCenter);
soloMuteLayout->addWidget(m_muteButton, 0, Qt::AlignHCenter);
m_fader = new Fader{&mixerChannel->m_volumeModel, tr("Fader %1").arg(channelIndex), this};
m_peakIndicator = new PeakIndicator(this);
connect(m_fader, &Fader::peakChanged, m_peakIndicator, &PeakIndicator::updatePeak);
m_effectRackView = new EffectRackView{&mixerChannel->m_fxChain, mixerView->m_racksWidget};
m_effectRackView->setFixedWidth(EffectRackView::DEFAULT_WIDTH);
auto mainLayout = new QVBoxLayout{this};
mainLayout->setContentsMargins(4, 4, 4, 4);
mainLayout->setSpacing(2);
mainLayout->addWidget(m_receiveArrowOrSendButton, 0, Qt::AlignHCenter);
mainLayout->addWidget(m_sendKnob, 0, Qt::AlignHCenter);
mainLayout->addWidget(m_sendArrow, 0, Qt::AlignHCenter);
mainLayout->addWidget(m_channelNumberLcd, 0, Qt::AlignHCenter);
mainLayout->addWidget(m_renameLineEditView, 0, Qt::AlignHCenter);
mainLayout->addLayout(soloMuteLayout);
mainLayout->addWidget(m_peakIndicator);
mainLayout->addWidget(m_fader, 1, Qt::AlignHCenter);
connect(m_renameLineEdit, &QLineEdit::editingFinished, this, &MixerChannelView::renameFinished);
}
void MixerChannelView::contextMenuEvent(QContextMenuEvent*)
{
auto contextMenu = new CaptionMenu(mixerChannel()->m_name, this);
if (!isMasterChannel()) // no move-options in master
{
contextMenu->addAction(tr("Move &left"), this, &MixerChannelView::moveChannelLeft);
contextMenu->addAction(tr("Move &right"), this, &MixerChannelView::moveChannelRight);
}
contextMenu->addAction(tr("Rename &channel"), this, &MixerChannelView::renameChannel);
contextMenu->addSeparator();
if (!isMasterChannel()) // no remove-option in master
{
contextMenu->addAction(embed::getIconPixmap("cancel"), tr("R&emove channel"), this, &MixerChannelView::removeChannel);
contextMenu->addSeparator();
}
contextMenu->addAction(embed::getIconPixmap("cancel"), tr("Remove &unused channels"), this, &MixerChannelView::removeUnusedChannels);
contextMenu->addSeparator();
auto colorMenu = QMenu{tr("Color"), this};
colorMenu.setIcon(embed::getIconPixmap("colorize"));
colorMenu.addAction(tr("Change"), this, &MixerChannelView::selectColor);
colorMenu.addAction(tr("Reset"), this, &MixerChannelView::resetColor);
colorMenu.addAction(tr("Pick random"), this, &MixerChannelView::randomizeColor);
contextMenu->addMenu(&colorMenu);
contextMenu->exec(QCursor::pos());
delete contextMenu;
}
void MixerChannelView::paintEvent(QPaintEvent* event)
{
const auto channel = mixerChannel();
const bool muted = channel->m_muteModel.value();
const auto name = channel->m_name;
const auto elidedName = elideName(name);
const auto isActive = m_mixerView->currentMixerChannel() == this;
if (!m_inRename && m_renameLineEdit->text() != elidedName)
{
m_renameLineEdit->setText(elidedName);
}
const auto width = rect().width();
const auto height = rect().height();
auto painter = QPainter{this};
if (channel->color().has_value() && !muted)
{
painter.fillRect(rect(), channel->color()->darker(isActive ? 120 : 150));
}
else
{
painter.fillRect(rect(), isActive ? backgroundActive().color() : painter.background().color());
}
// inner border
painter.setPen(isActive ? strokeInnerActive() : strokeInnerInactive());
painter.drawRect(1, 1, width - MIXER_CHANNEL_INNER_BORDER_SIZE, height - MIXER_CHANNEL_INNER_BORDER_SIZE);
// outer border
painter.setPen(isActive ? strokeOuterActive() : strokeOuterInactive());
painter.drawRect(0, 0, width - MIXER_CHANNEL_OUTER_BORDER_SIZE, height - MIXER_CHANNEL_OUTER_BORDER_SIZE);
QWidget::paintEvent(event);
}
void MixerChannelView::mousePressEvent(QMouseEvent*)
{
if (m_mixerView->currentMixerChannel() != this)
{
m_mixerView->setCurrentMixerChannel(this);
}
}
void MixerChannelView::mouseDoubleClickEvent(QMouseEvent*)
{
renameChannel();
}
bool MixerChannelView::eventFilter(QObject* dist, QEvent* event)
{
// If we are in a rename, capture the enter/return events and handle them
if (event->type() == QEvent::KeyPress)
{
auto keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return)
{
if (m_inRename)
{
renameFinished();
event->accept(); // Stop the event from propagating
return true;
}
}
}
return false;
}
int MixerChannelView::channelIndex() const
{
return m_channelIndex;
}
void MixerChannelView::setChannelIndex(int index)
{
MixerChannel* mixerChannel = Engine::mixer()->mixerChannel(index);
m_fader->setModel(&mixerChannel->m_volumeModel);
m_muteButton->setModel(&mixerChannel->m_muteModel);
m_soloButton->setModel(&mixerChannel->m_soloModel);
m_effectRackView->setModel(&mixerChannel->m_fxChain);
m_channelNumberLcd->setValue(index);
m_channelIndex = index;
}
QBrush MixerChannelView::backgroundActive() const
{
return m_backgroundActive;
}
void MixerChannelView::setBackgroundActive(const QBrush& c)
{
m_backgroundActive = c;
}
QColor MixerChannelView::strokeOuterActive() const
{
return m_strokeOuterActive;
}
void MixerChannelView::setStrokeOuterActive(const QColor& c)
{
m_strokeOuterActive = c;
}
QColor MixerChannelView::strokeOuterInactive() const
{
return m_strokeOuterInactive;
}
void MixerChannelView::setStrokeOuterInactive(const QColor& c)
{
m_strokeOuterInactive = c;
}
QColor MixerChannelView::strokeInnerActive() const
{
return m_strokeInnerActive;
}
void MixerChannelView::setStrokeInnerActive(const QColor& c)
{
m_strokeInnerActive = c;
}
QColor MixerChannelView::strokeInnerInactive() const
{
return m_strokeInnerInactive;
}
void MixerChannelView::setStrokeInnerInactive(const QColor& c)
{
m_strokeInnerInactive = c;
}
void MixerChannelView::reset()
{
m_peakIndicator->resetPeakToMinusInf();
}
void MixerChannelView::renameChannel()
{
m_inRename = true;
setToolTip("");
m_renameLineEdit->setReadOnly(false);
m_channelNumberLcd->hide();
m_renameLineEdit->setFixedWidth(m_renameLineEdit->width());
m_renameLineEdit->setText(mixerChannel()->m_name);
m_renameLineEditView->setFocus();
m_renameLineEdit->selectAll();
m_renameLineEdit->setFocus();
}
void MixerChannelView::renameFinished()
{
m_inRename = false;
m_renameLineEdit->deselect();
m_renameLineEdit->setReadOnly(true);
m_renameLineEdit->setFixedWidth(m_renameLineEdit->width());
m_channelNumberLcd->show();
auto newName = m_renameLineEdit->text();
setFocus();
const auto mc = mixerChannel();
if (!newName.isEmpty() && mc->m_name != newName)
{
mc->m_name = newName;
m_renameLineEdit->setText(elideName(newName));
Engine::getSong()->setModified();
}
setToolTip(mc->m_name);
}
void MixerChannelView::resetColor()
{
mixerChannel()->setColor(std::nullopt);
Engine::getSong()->setModified();
update();
}
void MixerChannelView::selectColor()
{
const auto channel = mixerChannel();
const auto initialColor = channel->color().value_or(backgroundActive().color());
const auto * colorChooser = ColorChooser{this}.withPalette(ColorChooser::Palette::Mixer);
const auto newColor = colorChooser->getColor(initialColor);
if (!newColor.isValid()) { return; }
channel->setColor(newColor);
Engine::getSong()->setModified();
update();
}
void MixerChannelView::randomizeColor()
{
auto channel = mixerChannel();
channel->setColor(ColorChooser::getPalette(ColorChooser::Palette::Mixer)[rand() % 48]);
Engine::getSong()->setModified();
update();
}
bool MixerChannelView::confirmRemoval(int index)
auto retainSizeWhenHidden = [](QWidget* widget) {
auto sizePolicy = widget->sizePolicy();
sizePolicy.setRetainSizeWhenHidden(true);
widget->setSizePolicy(sizePolicy);
};
auto receiveArrowContainer = new QWidget{};
auto receiveArrowLayout = new QVBoxLayout{receiveArrowContainer};
m_receiveArrow = new QLabel{};
m_receiveArrow->setPixmap(embed::getIconPixmap("receive_bg_arrow"));
receiveArrowLayout->setContentsMargins(0, 0, 0, 0);
receiveArrowLayout->setSpacing(0);
receiveArrowLayout->addWidget(m_receiveArrow, 0, Qt::AlignHCenter);
auto sendButtonContainer = new QWidget{};
auto sendButtonLayout = new QVBoxLayout{sendButtonContainer};
m_sendButton = new SendButtonIndicator{this, this, mixerView};
sendButtonLayout->setContentsMargins(0, 0, 0, 0);
sendButtonLayout->setSpacing(0);
sendButtonLayout->addWidget(m_sendButton, 0, Qt::AlignHCenter);
m_receiveArrowOrSendButton = new QStackedWidget{this};
m_receiveArrowStackedIndex = m_receiveArrowOrSendButton->addWidget(receiveArrowContainer);
m_sendButtonStackedIndex = m_receiveArrowOrSendButton->addWidget(sendButtonContainer);
retainSizeWhenHidden(m_receiveArrowOrSendButton);
m_sendKnob = new Knob{KnobType::Bright26, this, tr("Channel send amount")};
retainSizeWhenHidden(m_sendKnob);
m_sendArrow = new QLabel{};
m_sendArrow->setPixmap(embed::getIconPixmap("send_bg_arrow"));
retainSizeWhenHidden(m_sendArrow);
m_channelNumberLcd = new LcdWidget{2, this};
m_channelNumberLcd->setValue(channelIndex);
retainSizeWhenHidden(m_channelNumberLcd);
const auto mixerChannel = Engine::mixer()->mixerChannel(channelIndex);
const auto mixerName = mixerChannel->m_name;
setToolTip(mixerName);
m_renameLineEdit = new QLineEdit{mixerName, nullptr};
m_renameLineEdit->setFixedWidth(65);
m_renameLineEdit->setFont(adjustedToPixelSize(font(), LARGE_FONT_SIZE));
m_renameLineEdit->setReadOnly(true);
m_renameLineEdit->installEventFilter(this);
auto renameLineEditScene = new QGraphicsScene{};
m_renameLineEditView = new QGraphicsView{};
m_renameLineEditView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_renameLineEditView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_renameLineEditView->setAttribute(Qt::WA_TransparentForMouseEvents, true);
m_renameLineEditView->setScene(renameLineEditScene);
auto renameLineEditProxy = renameLineEditScene->addWidget(m_renameLineEdit);
renameLineEditProxy->setRotation(-90);
m_renameLineEditView->setFixedSize(m_renameLineEdit->height() + 5, m_renameLineEdit->width() + 5);
m_muteButton = new PixmapButton(this, tr("Mute"));
m_muteButton->setModel(&mixerChannel->m_muteModel);
m_muteButton->setActiveGraphic(embed::getIconPixmap("led_off"));
m_muteButton->setInactiveGraphic(embed::getIconPixmap("led_green"));
m_muteButton->setCheckable(true);
m_muteButton->setToolTip(tr("Mute this channel"));
m_soloButton = new PixmapButton(this, tr("Solo"));
m_soloButton->setModel(&mixerChannel->m_soloModel);
m_soloButton->setActiveGraphic(embed::getIconPixmap("led_red"));
m_soloButton->setInactiveGraphic(embed::getIconPixmap("led_off"));
m_soloButton->setCheckable(true);
m_soloButton->setToolTip(tr("Solo this channel"));
auto soloMuteLayout = new QVBoxLayout();
soloMuteLayout->setContentsMargins(0, 0, 0, 0);
soloMuteLayout->setSpacing(0);
soloMuteLayout->addWidget(m_soloButton, 0, Qt::AlignHCenter);
soloMuteLayout->addWidget(m_muteButton, 0, Qt::AlignHCenter);
m_fader = new Fader{&mixerChannel->m_volumeModel, tr("Fader %1").arg(channelIndex), this};
m_peakIndicator = new PeakIndicator(this);
connect(m_fader, &Fader::peakChanged, m_peakIndicator, &PeakIndicator::updatePeak);
m_effectRackView = new EffectRackView{&mixerChannel->m_fxChain, mixerView->m_racksWidget};
m_effectRackView->setFixedWidth(EffectRackView::DEFAULT_WIDTH);
auto mainLayout = new QVBoxLayout{this};
mainLayout->setContentsMargins(4, 4, 4, 4);
mainLayout->setSpacing(2);
mainLayout->addWidget(m_receiveArrowOrSendButton, 0, Qt::AlignHCenter);
mainLayout->addWidget(m_sendKnob, 0, Qt::AlignHCenter);
mainLayout->addWidget(m_sendArrow, 0, Qt::AlignHCenter);
mainLayout->addWidget(m_channelNumberLcd, 0, Qt::AlignHCenter);
mainLayout->addWidget(m_renameLineEditView, 0, Qt::AlignHCenter);
mainLayout->addLayout(soloMuteLayout);
mainLayout->addWidget(m_peakIndicator);
mainLayout->addWidget(m_fader, 1, Qt::AlignHCenter);
connect(m_renameLineEdit, &QLineEdit::editingFinished, this, &MixerChannelView::renameFinished);
}
void MixerChannelView::contextMenuEvent(QContextMenuEvent*)
{
auto contextMenu = new CaptionMenu(mixerChannel()->m_name, this);
if (!isMasterChannel()) // no move-options in master
{
// if config variable is set to false, there is no need for user confirmation
bool needConfirm = ConfigManager::inst()->value("ui", "mixerchanneldeletionwarning", "1").toInt();
if (!needConfirm) { return true; }
// 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"
"Are you sure you want to remove this channel?\n\n"
"Warning: This operation can not be undone.");
QString messageTitleRemoveTrack = tr("Confirm removal");
QString askAgainText = tr("Don't ask again");
auto askAgainCheckBox = new QCheckBox(askAgainText, nullptr);
connect(askAgainCheckBox, &QCheckBox::stateChanged, [](int state) {
// Invert button state, if it's checked we *shouldn't* ask again
ConfigManager::inst()->setValue("ui", "mixerchanneldeletionwarning", state ? "0" : "1");
});
QMessageBox mb(this);
mb.setText(messageRemoveTrack);
mb.setWindowTitle(messageTitleRemoveTrack);
mb.setIcon(QMessageBox::Warning);
mb.addButton(QMessageBox::Cancel);
mb.addButton(QMessageBox::Ok);
mb.setCheckBox(askAgainCheckBox);
mb.setDefaultButton(QMessageBox::Cancel);
int answer = mb.exec();
return answer == QMessageBox::Ok;
contextMenu->addAction(tr("Move &left"), this, &MixerChannelView::moveChannelLeft);
contextMenu->addAction(tr("Move &right"), this, &MixerChannelView::moveChannelRight);
}
void MixerChannelView::removeChannel()
{
if (!confirmRemoval(m_channelIndex)) { return; }
auto mix = getGUI()->mixerView();
mix->deleteChannel(m_channelIndex);
}
contextMenu->addAction(tr("Rename &channel"), this, &MixerChannelView::renameChannel);
contextMenu->addSeparator();
void MixerChannelView::removeUnusedChannels()
{
auto mix = getGUI()->mixerView();
mix->deleteUnusedChannels();
}
if (!isMasterChannel()) // no remove-option in master
{
contextMenu->addAction(
embed::getIconPixmap("cancel"), tr("R&emove channel"), this, &MixerChannelView::removeChannel);
contextMenu->addSeparator();
}
void MixerChannelView::moveChannelLeft()
{
auto mix = getGUI()->mixerView();
mix->moveChannelLeft(m_channelIndex);
}
contextMenu->addAction(
embed::getIconPixmap("cancel"), tr("Remove &unused channels"), this, &MixerChannelView::removeUnusedChannels);
contextMenu->addSeparator();
void MixerChannelView::moveChannelRight()
{
auto mix = getGUI()->mixerView();
mix->moveChannelRight(m_channelIndex);
}
auto colorMenu = QMenu{tr("Color"), this};
colorMenu.setIcon(embed::getIconPixmap("colorize"));
colorMenu.addAction(tr("Change"), this, &MixerChannelView::selectColor);
colorMenu.addAction(tr("Reset"), this, &MixerChannelView::resetColor);
colorMenu.addAction(tr("Pick random"), this, &MixerChannelView::randomizeColor);
contextMenu->addMenu(&colorMenu);
QString MixerChannelView::elideName(const QString& name)
{
const auto maxTextHeight = m_renameLineEdit->width();
const auto metrics = QFontMetrics{m_renameLineEdit->font()};
const auto elidedName = metrics.elidedText(name, Qt::ElideRight, maxTextHeight);
return elidedName;
}
contextMenu->exec(QCursor::pos());
delete contextMenu;
}
MixerChannel* MixerChannelView::mixerChannel() const
{
return Engine::mixer()->mixerChannel(m_channelIndex);
}
void MixerChannelView::paintEvent(QPaintEvent* event)
{
const auto channel = mixerChannel();
const bool muted = channel->m_muteModel.value();
const auto name = channel->m_name;
const auto elidedName = elideName(name);
const auto isActive = m_mixerView->currentMixerChannel() == this;
if (!m_inRename && m_renameLineEdit->text() != elidedName) { m_renameLineEdit->setText(elidedName); }
const auto width = rect().width();
const auto height = rect().height();
auto painter = QPainter{this};
if (channel->color().has_value() && !muted)
{
painter.fillRect(rect(), channel->color()->darker(isActive ? 120 : 150));
}
else { painter.fillRect(rect(), isActive ? backgroundActive().color() : painter.background().color()); }
// inner border
painter.setPen(isActive ? strokeInnerActive() : strokeInnerInactive());
painter.drawRect(1, 1, width - MIXER_CHANNEL_INNER_BORDER_SIZE, height - MIXER_CHANNEL_INNER_BORDER_SIZE);
// outer border
painter.setPen(isActive ? strokeOuterActive() : strokeOuterInactive());
painter.drawRect(0, 0, width - MIXER_CHANNEL_OUTER_BORDER_SIZE, height - MIXER_CHANNEL_OUTER_BORDER_SIZE);
QWidget::paintEvent(event);
}
void MixerChannelView::mousePressEvent(QMouseEvent*)
{
if (m_mixerView->currentMixerChannel() != this) { m_mixerView->setCurrentMixerChannel(this); }
}
void MixerChannelView::mouseDoubleClickEvent(QMouseEvent*)
{
renameChannel();
}
bool MixerChannelView::eventFilter(QObject* dist, QEvent* event)
{
// If we are in a rename, capture the enter/return events and handle them
if (event->type() == QEvent::KeyPress)
{
auto keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return)
{
if (m_inRename)
{
renameFinished();
event->accept(); // Stop the event from propagating
return true;
}
}
}
return false;
}
int MixerChannelView::channelIndex() const
{
return m_channelIndex;
}
void MixerChannelView::setChannelIndex(int index)
{
MixerChannel* mixerChannel = Engine::mixer()->mixerChannel(index);
m_fader->setModel(&mixerChannel->m_volumeModel);
m_muteButton->setModel(&mixerChannel->m_muteModel);
m_soloButton->setModel(&mixerChannel->m_soloModel);
m_effectRackView->setModel(&mixerChannel->m_fxChain);
m_channelNumberLcd->setValue(index);
m_channelIndex = index;
}
QBrush MixerChannelView::backgroundActive() const
{
return m_backgroundActive;
}
void MixerChannelView::setBackgroundActive(const QBrush& c)
{
m_backgroundActive = c;
}
QColor MixerChannelView::strokeOuterActive() const
{
return m_strokeOuterActive;
}
void MixerChannelView::setStrokeOuterActive(const QColor& c)
{
m_strokeOuterActive = c;
}
QColor MixerChannelView::strokeOuterInactive() const
{
return m_strokeOuterInactive;
}
void MixerChannelView::setStrokeOuterInactive(const QColor& c)
{
m_strokeOuterInactive = c;
}
QColor MixerChannelView::strokeInnerActive() const
{
return m_strokeInnerActive;
}
void MixerChannelView::setStrokeInnerActive(const QColor& c)
{
m_strokeInnerActive = c;
}
QColor MixerChannelView::strokeInnerInactive() const
{
return m_strokeInnerInactive;
}
void MixerChannelView::setStrokeInnerInactive(const QColor& c)
{
m_strokeInnerInactive = c;
}
void MixerChannelView::reset()
{
m_peakIndicator->resetPeakToMinusInf();
}
void MixerChannelView::renameChannel()
{
m_inRename = true;
setToolTip("");
m_renameLineEdit->setReadOnly(false);
m_channelNumberLcd->hide();
m_renameLineEdit->setFixedWidth(m_renameLineEdit->width());
m_renameLineEdit->setText(mixerChannel()->m_name);
m_renameLineEditView->setFocus();
m_renameLineEdit->selectAll();
m_renameLineEdit->setFocus();
}
void MixerChannelView::renameFinished()
{
m_inRename = false;
m_renameLineEdit->deselect();
m_renameLineEdit->setReadOnly(true);
m_renameLineEdit->setFixedWidth(m_renameLineEdit->width());
m_channelNumberLcd->show();
auto newName = m_renameLineEdit->text();
setFocus();
const auto mc = mixerChannel();
if (!newName.isEmpty() && mc->m_name != newName)
{
mc->m_name = newName;
m_renameLineEdit->setText(elideName(newName));
Engine::getSong()->setModified();
}
setToolTip(mc->m_name);
}
void MixerChannelView::resetColor()
{
mixerChannel()->setColor(std::nullopt);
Engine::getSong()->setModified();
update();
}
void MixerChannelView::selectColor()
{
const auto channel = mixerChannel();
const auto initialColor = channel->color().value_or(backgroundActive().color());
const auto* colorChooser = ColorChooser{this}.withPalette(ColorChooser::Palette::Mixer);
const auto newColor = colorChooser->getColor(initialColor);
if (!newColor.isValid()) { return; }
channel->setColor(newColor);
Engine::getSong()->setModified();
update();
}
void MixerChannelView::randomizeColor()
{
auto channel = mixerChannel();
channel->setColor(ColorChooser::getPalette(ColorChooser::Palette::Mixer)[rand() % 48]);
Engine::getSong()->setModified();
update();
}
bool MixerChannelView::confirmRemoval(int index)
{
// if config variable is set to false, there is no need for user confirmation
bool needConfirm = ConfigManager::inst()->value("ui", "mixerchanneldeletionwarning", "1").toInt();
if (!needConfirm) { return true; }
// 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"
"Are you sure you want to remove this channel?\n\n"
"Warning: This operation can not be undone.");
QString messageTitleRemoveTrack = tr("Confirm removal");
QString askAgainText = tr("Don't ask again");
auto askAgainCheckBox = new QCheckBox(askAgainText, nullptr);
connect(askAgainCheckBox, &QCheckBox::stateChanged, [](int state) {
// Invert button state, if it's checked we *shouldn't* ask again
ConfigManager::inst()->setValue("ui", "mixerchanneldeletionwarning", state ? "0" : "1");
});
QMessageBox mb(this);
mb.setText(messageRemoveTrack);
mb.setWindowTitle(messageTitleRemoveTrack);
mb.setIcon(QMessageBox::Warning);
mb.addButton(QMessageBox::Cancel);
mb.addButton(QMessageBox::Ok);
mb.setCheckBox(askAgainCheckBox);
mb.setDefaultButton(QMessageBox::Cancel);
int answer = mb.exec();
return answer == QMessageBox::Ok;
}
void MixerChannelView::removeChannel()
{
if (!confirmRemoval(m_channelIndex)) { return; }
auto mix = getGUI()->mixerView();
mix->deleteChannel(m_channelIndex);
}
void MixerChannelView::removeUnusedChannels()
{
auto mix = getGUI()->mixerView();
mix->deleteUnusedChannels();
}
void MixerChannelView::moveChannelLeft()
{
auto mix = getGUI()->mixerView();
mix->moveChannelLeft(m_channelIndex);
}
void MixerChannelView::moveChannelRight()
{
auto mix = getGUI()->mixerView();
mix->moveChannelRight(m_channelIndex);
}
QString MixerChannelView::elideName(const QString& name)
{
const auto maxTextHeight = m_renameLineEdit->width();
const auto metrics = QFontMetrics{m_renameLineEdit->font()};
const auto elidedName = metrics.elidedText(name, Qt::ElideRight, maxTextHeight);
return elidedName;
}
MixerChannel* MixerChannelView::mixerChannel() const
{
return Engine::mixer()->mixerChannel(m_channelIndex);
}
} // namespace lmms::gui