From 84f40a4a6bfab07fa203b4d9220421676350b22e Mon Sep 17 00:00:00 2001 From: Kumar Date: Tue, 11 Aug 2020 14:31:58 +0530 Subject: [PATCH] Enable mixer color-coding (#5589) * Enable mixer color-coding * Cleanup * Fix warnings * Improvements * Improvements * Use ColorChooser instead of QColorDialog * Fix default palette being out of range * Remove a redundant function * Rename and make stuff efficient * Comment on the code * Make things more efficient * Fix breaking builds * Improvements * Improvements pt. 2 * Improvements pt. 3 * Improvements pt. 4 * Improvements pt. 5 * Apply suggestions from code review Co-authored-by: Hyunjin Song Co-authored-by: Hyunjin Song --- include/ColorChooser.h | 22 +++++++- include/FxLine.h | 5 ++ include/FxMixer.h | 7 +++ src/core/FxMixer.cpp | 7 +++ src/gui/CMakeLists.txt | 1 + src/gui/dialogs/ColorChooser.cpp | 93 ++++++++++++++++++++++++++++++++ src/gui/widgets/FxLine.cpp | 58 ++++++++++++++++++-- 7 files changed, 186 insertions(+), 7 deletions(-) create mode 100644 src/gui/dialogs/ColorChooser.cpp diff --git a/include/ColorChooser.h b/include/ColorChooser.h index fe5b7a22a4e..ac2a1b62d4a 100644 --- a/include/ColorChooser.h +++ b/include/ColorChooser.h @@ -21,21 +21,39 @@ * */ -#include #include +#include +#include #include +#include class ColorChooser: public QColorDialog { public: ColorChooser(const QColor &initial, QWidget *parent): QColorDialog(initial, parent) {}; ColorChooser(QWidget *parent): QColorDialog(parent) {}; + //! For getting a color without having to initialise a color dialog + ColorChooser() {}; + enum class Palette {Default, Track, Mixer}; + //! Set global palette via array, checking bounds + void setPalette (QVector); + //! Set global paletter via enum + void setPalette (Palette); + //! Set palette via enum, return self pointer for chaining + ColorChooser* withPalette (Palette); + //! Return a certain palette + static QVector getPalette (Palette); protected: - // Forward key events to the parent to prevent stuck notes when the dialog gets focus + //! Forward key events to the parent to prevent stuck notes when the dialog gets focus void keyReleaseEvent(QKeyEvent *event) override { QKeyEvent ke(*event); QApplication::sendEvent(parentWidget(), &ke); } +private: + //! Copy the current QColorDialog palette into an array + static QVector defaultPalette(); + //! Generate a nice palette, with adjustable value + static QVector nicePalette (int); }; diff --git a/include/FxLine.h b/include/FxLine.h index c16dcd5f598..e9ee248b811 100644 --- a/include/FxLine.h +++ b/include/FxLine.h @@ -26,10 +26,12 @@ #ifndef FX_LINE_H #define FX_LINE_H +#include #include #include #include +#include "ColorChooser.h" #include "Knob.h" #include "LcdWidget.h" #include "SendButtonIndicator.h" @@ -101,6 +103,9 @@ class FxLine : public QWidget public slots: void renameChannel(); + void resetColor(); + void changeColor(); + void randomColor(); private slots: void renameFinished(); diff --git a/include/FxMixer.h b/include/FxMixer.h index 68b69d9bc88..920d4e27bc3 100644 --- a/include/FxMixer.h +++ b/include/FxMixer.h @@ -32,6 +32,8 @@ #include +#include + class FxRoute; typedef QVector FxRouteVector; @@ -70,6 +72,11 @@ class FxChannel : public ThreadableJob bool requiresProcessing() const override { return true; } void unmuteForSolo(); + + // TODO C++17 and above: use std::optional insteads + QColor m_color; + bool m_hasColor; + std::atomic_int m_dependenciesMet; void incrementDeps(); diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index 4a68c3f3b05..7ddcbea623a 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -73,6 +73,7 @@ FxChannel::FxChannel( int idx, Model * _parent ) : m_lock(), m_channelIndex( idx ), m_queued( false ), + m_hasColor( false ), m_dependenciesMet(0) { BufferManager::clear( m_buffer, Engine::mixer()->framesPerPeriod() ); @@ -741,6 +742,7 @@ void FxMixer::saveSettings( QDomDocument & _doc, QDomElement & _this ) ch->m_soloModel.saveSettings( _doc, fxch, "soloed" ); fxch.setAttribute( "num", i ); fxch.setAttribute( "name", ch->m_name ); + if( ch->m_hasColor ) fxch.setAttribute( "color", ch->m_color.name() ); // add the channel sends for( int si = 0; si < ch->m_sends.size(); ++si ) @@ -786,6 +788,11 @@ void FxMixer::loadSettings( const QDomElement & _this ) m_fxChannels[num]->m_muteModel.loadSettings( fxch, "muted" ); m_fxChannels[num]->m_soloModel.loadSettings( fxch, "soloed" ); m_fxChannels[num]->m_name = fxch.attribute( "name" ); + if( fxch.hasAttribute( "color" ) ) + { + m_fxChannels[num]->m_hasColor = true; + m_fxChannels[num]->m_color.setNamedColor( fxch.attribute( "color" ) ); + } m_fxChannels[num]->m_fxChain.restoreState( fxch.firstChildElement( m_fxChannels[num]->m_fxChain.nodeName() ) ); diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 3c6529e0b78..e1be929aab1 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -37,6 +37,7 @@ SET(LMMS_SRCS gui/dialogs/FileDialog.cpp gui/dialogs/VersionedSaveDialog.cpp + gui/dialogs/ColorChooser.cpp gui/editors/AutomationEditor.cpp gui/editors/BBEditor.cpp diff --git a/src/gui/dialogs/ColorChooser.cpp b/src/gui/dialogs/ColorChooser.cpp new file mode 100644 index 00000000000..b25aa97be6c --- /dev/null +++ b/src/gui/dialogs/ColorChooser.cpp @@ -0,0 +1,93 @@ +/* ColorChooser.cpp - definition of ColorChooser class. + * + * Copyright (c) 2020 russiankumar + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include + + + + +//! Set global palette via array, checking bounds +void ColorChooser::setPalette (QVector colors) +{ + const int max = qMin (colors.size(), 48); + for (int i = 0; i < max; i++) + { + ColorChooser::setStandardColor (i, colors[i]); + } +} + + +//! Set global paletter via enum +void ColorChooser::setPalette (Palette palette) +{ + setPalette (getPalette (palette)); +} + + +//! Set palette via enum, return self pointer for chaining +ColorChooser* ColorChooser::withPalette (Palette palette) +{ + setPalette (palette); + return this; +} + + +//! Return a certain palette +QVector ColorChooser::getPalette (Palette palette) +{ + switch (palette) + { + case Palette::Mixer: return nicePalette(140); + case Palette::Track: return nicePalette(150); + default: return defaultPalette(); + } +} + + + + +//! Copy the current QColorDialog palette into an array +QVector ColorChooser::defaultPalette() +{ + QVector result (48); + for (int i = 0; i < 48; i++) + { + result[i] = (QColorDialog::standardColor(i)); + } + return result; +} + + +//! Generate a nice palette, with adjustable value +QVector ColorChooser::nicePalette (int base) +{ + QVector result (48); + for (int x = 0; x < 8; x++) + { + for (int y = 0; y < 6; y++) + { + result[6 * x + y].setHsl (qMax(0, 44 * x - 1), 150 - 20 * y, base - 10 * y); + } + } + return result; +} diff --git a/src/gui/widgets/FxLine.cpp b/src/gui/widgets/FxLine.cpp index 3314301fd77..476d8773b2b 100644 --- a/src/gui/widgets/FxLine.cpp +++ b/src/gui/widgets/FxLine.cpp @@ -25,6 +25,8 @@ #include "FxLine.h" +#include + #include #include "CaptionMenu.h" @@ -120,6 +122,8 @@ FxLine::FxLine( QWidget * _parent, FxMixerView * _mv, int _channelIndex ) : proxyWidget->setPos( 8, 145 ); connect( m_renameLineEdit, SIGNAL( editingFinished() ), this, SLOT( renameFinished() ) ); + connect( &Engine::fxMixer()->effectChannel( m_channelIndex )->m_muteModel, SIGNAL( dataChanged() ), this, SLOT( update() ) ); + } @@ -144,10 +148,11 @@ void FxLine::setChannelIndex( int index ) - void FxLine::drawFxLine( QPainter* p, const FxLine *fxLine, bool isActive, bool sendToThis, bool receiveFromThis ) { - QString name = Engine::fxMixer()->effectChannel( m_channelIndex )->m_name; + auto channel = Engine::fxMixer()->effectChannel( m_channelIndex ); + bool muted = channel->m_muteModel.value(); + QString name = channel->m_name; QString elidedName = elideName( name ); if( !m_inRename && m_renameLineEdit->text() != elidedName ) { @@ -156,8 +161,16 @@ void FxLine::drawFxLine( QPainter* p, const FxLine *fxLine, bool isActive, bool int width = fxLine->rect().width(); int height = fxLine->rect().height(); - - p->fillRect( fxLine->rect(), isActive ? fxLine->backgroundActive() : p->background() ); + + if( channel->m_hasColor && !muted ) + { + p->fillRect( fxLine->rect(), channel->m_color.darker( isActive ? 120 : 150 ) ); + } + else + { + p->fillRect( fxLine->rect(), + isActive ? fxLine->backgroundActive().color() : p->background().color() ); + } // inner border p->setPen( isActive ? fxLine->strokeInnerActive() : fxLine->strokeInnerInactive() ); @@ -224,7 +237,7 @@ void FxLine::mouseDoubleClickEvent( QMouseEvent * ) void FxLine::contextMenuEvent( QContextMenuEvent * ) { QPointer contextMenu = new CaptionMenu( Engine::fxMixer()->effectChannel( m_channelIndex )->m_name, this ); - if( m_channelIndex != 0 ) // no move-options in master + if( m_channelIndex != 0 ) // no move-options in master { contextMenu->addAction( tr( "Move &left" ), this, SLOT( moveChannelLeft() ) ); contextMenu->addAction( tr( "Move &right" ), this, SLOT( moveChannelRight() ) ); @@ -238,6 +251,10 @@ void FxLine::contextMenuEvent( QContextMenuEvent * ) contextMenu->addSeparator(); } contextMenu->addAction( embed::getIconPixmap( "cancel" ), tr( "Remove &unused channels" ), this, SLOT( removeUnusedChannels() ) ); + contextMenu->addSeparator(); + contextMenu->addAction( embed::getIconPixmap( "colorize" ), tr( "Set channel color" ), this, SLOT( changeColor() ) ); + contextMenu->addAction( embed::getIconPixmap( "colorize" ), tr( "Remove channel color" ), this, SLOT( resetColor() ) ); + contextMenu->addAction( embed::getIconPixmap( "colorize" ), tr( "Pick random channel color" ), this, SLOT( randomColor() ) ); contextMenu->exec( QCursor::pos() ); delete contextMenu; } @@ -395,3 +412,34 @@ void FxLine::setStrokeInnerInactive( const QColor & c ) { m_strokeInnerInactive = c; } + + +// Ask user for a color, and set it as the mixer line color +void FxLine::changeColor() +{ + auto channel = Engine::fxMixer()->effectChannel( m_channelIndex ); + auto new_color = ColorChooser( this ).withPalette( ColorChooser::Palette::Mixer )->getColor( channel->m_color ); + if( ! new_color.isValid() ) + { return; } + channel->m_color = new_color; + channel->m_hasColor = true; + update(); +} + + +// Disable the usage of color on this mixer line +void FxLine::resetColor() +{ + Engine::fxMixer()->effectChannel( m_channelIndex )->m_hasColor = false; + update(); +} + + +// Pick a random color from the mixer palette and set it as our color +void FxLine::randomColor() +{ + auto channel = Engine::fxMixer()->effectChannel( m_channelIndex ); + channel->m_color = ColorChooser::getPalette( ColorChooser::Palette::Mixer )[ rand() % 48 ]; + channel->m_hasColor = true; + update(); +}