Expose Carla parameters for automation (#5846)
A new button called "Params" is added to the Carla instrument window; on press it will open a new sub-window where Carla parameters can be found, ready for connecting to a automation-track or controller. The exposed parameters in the sub-window can be filtered by text and there is the ability to display only parameters that are connected to a automation-track or controller. When there are multiple plugins loaded inside Carla, the combo-box inside the (LMMS) parameters sub-window can be used to switch between parameters of a specific plugin (Carla). Notes: - Available when compiled with Carla version 2.1 and up. - Currently Carla (2.1) will expose a maximum of 110 parameters. - The param window state isn't stored yet in a LMMS project, this is still TODO. - Connected paramters will NOT instantly disappear when they are disconnected with the "Show only knobs with a connection" filter enabled. See https://github.com/LMMS/lmms/pull/5846#issuecomment-762666428
This commit is contained in:
@@ -26,16 +26,29 @@
|
||||
|
||||
#include "Engine.h"
|
||||
#include "Song.h"
|
||||
#include "gui_templates.h"
|
||||
#include "GuiApplication.h"
|
||||
#include "InstrumentPlayHandle.h"
|
||||
#include "InstrumentTrack.h"
|
||||
#include "MidiEventToByteSeq.h"
|
||||
#include "MainWindow.h"
|
||||
#include "Mixer.h"
|
||||
#include "Song.h"
|
||||
#include "gui_templates.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QComboBox>
|
||||
#include <QFileDialog>
|
||||
#include <QFileInfo>
|
||||
#include <QHBoxLayout>
|
||||
#include <QMdiArea>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QScrollArea>
|
||||
#include <QSizePolicy>
|
||||
#include <QSpacerItem>
|
||||
#include <QSplitter>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QTimerEvent>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
@@ -135,7 +148,8 @@ CarlaInstrument::CarlaInstrument(InstrumentTrack* const instrumentTrack, const D
|
||||
kIsPatchbay(isPatchbay),
|
||||
fHandle(NULL),
|
||||
fDescriptor(isPatchbay ? carla_get_native_patchbay_plugin() : carla_get_native_rack_plugin()),
|
||||
fMidiEventCount(0)
|
||||
fMidiEventCount(0),
|
||||
m_paramModels()
|
||||
{
|
||||
fHost.handle = this;
|
||||
fHost.uiName = NULL;
|
||||
@@ -177,6 +191,24 @@ CarlaInstrument::CarlaInstrument(InstrumentTrack* const instrumentTrack, const D
|
||||
InstrumentPlayHandle * iph = new InstrumentPlayHandle( this, instrumentTrack );
|
||||
Engine::mixer()->addPlayHandle( iph );
|
||||
|
||||
#if CARLA_VERSION_HEX >= CARLA_MIN_PARAM_VERSION
|
||||
// text filter completion
|
||||
m_completerModel = new QStringListModel(this);
|
||||
m_paramsCompleter = new QCompleter(m_completerModel, this);
|
||||
m_paramsCompleter->setCaseSensitivity(Qt::CaseInsensitive);
|
||||
m_paramsCompleter->setCompletionMode(QCompleter::PopupCompletion);
|
||||
|
||||
// Add static amount of CarlaParamFloatModel's.
|
||||
int paramCount = fDescriptor->get_parameter_count(fHandle);
|
||||
m_paramModels.reserve(paramCount);
|
||||
for (int i=0; i < paramCount; ++i)
|
||||
{
|
||||
m_paramModels.push_back(new CarlaParamFloatModel(this));
|
||||
connect(m_paramModels[i], &CarlaParamFloatModel::dataChanged,
|
||||
this, [this, i]() {paramModelChanged(i);}, Qt::DirectConnection);
|
||||
}
|
||||
#endif
|
||||
|
||||
connect(Engine::mixer(), SIGNAL(sampleRateChanged()), this, SLOT(sampleRateChanged()));
|
||||
}
|
||||
|
||||
@@ -206,6 +238,10 @@ CarlaInstrument::~CarlaInstrument()
|
||||
fDescriptor->cleanup(fHandle);
|
||||
|
||||
fHandle = NULL;
|
||||
|
||||
#if CARLA_VERSION_HEX >= CARLA_MIN_PARAM_VERSION
|
||||
clearParamModels();
|
||||
#endif
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
@@ -230,8 +266,12 @@ const NativeTimeInfo* CarlaInstrument::handleGetTimeInfo() const
|
||||
return &fTimeInfo;
|
||||
}
|
||||
|
||||
void CarlaInstrument::handleUiParameterChanged(const uint32_t /*index*/, const float /*value*/) const
|
||||
void CarlaInstrument::handleUiParameterChanged(const uint32_t index, const float value) const
|
||||
{
|
||||
if (m_paramModels.count() > index)
|
||||
{
|
||||
m_paramModels[index]->setValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
void CarlaInstrument::handleUiClosed()
|
||||
@@ -239,10 +279,24 @@ void CarlaInstrument::handleUiClosed()
|
||||
emit uiClosed();
|
||||
}
|
||||
|
||||
intptr_t CarlaInstrument::handleDispatcher(const NativeHostDispatcherOpcode opcode, const int32_t, const intptr_t, void* const, const float)
|
||||
intptr_t CarlaInstrument::handleDispatcher(const NativeHostDispatcherOpcode opcode, const int32_t index,
|
||||
const intptr_t value, void* const ptr, const float opt)
|
||||
{
|
||||
intptr_t ret = 0;
|
||||
|
||||
// source/includes/CarlaNative.h
|
||||
// NATIVE_HOST_OPCODE_NULL = 0, nothing
|
||||
// NATIVE_HOST_OPCODE_UPDATE_PARAMETER = 1, uses index, -1 for all
|
||||
// NATIVE_HOST_OPCODE_UPDATE_MIDI_PROGRAM = 2, uses index, -1 for all; may use value for channel
|
||||
// NATIVE_HOST_OPCODE_RELOAD_PARAMETERS = 3, nothing
|
||||
// NATIVE_HOST_OPCODE_RELOAD_MIDI_PROGRAMS = 4, nothing
|
||||
// NATIVE_HOST_OPCODE_RELOAD_ALL = 5, nothing
|
||||
// NATIVE_HOST_OPCODE_UI_UNAVAILABLE = 6, nothing
|
||||
// NATIVE_HOST_OPCODE_HOST_IDLE = 7, nothing
|
||||
// NATIVE_HOST_OPCODE_INTERNAL_PLUGIN = 8, nothing
|
||||
// NATIVE_HOST_OPCODE_QUEUE_INLINE_DISPLAY = 9, nothing
|
||||
// NATIVE_HOST_OPCODE_UI_TOUCH_PARAMETER = 10 uses index, value as bool
|
||||
|
||||
switch (opcode)
|
||||
{
|
||||
case NATIVE_HOST_OPCODE_UI_UNAVAILABLE:
|
||||
@@ -251,6 +305,29 @@ intptr_t CarlaInstrument::handleDispatcher(const NativeHostDispatcherOpcode opco
|
||||
case NATIVE_HOST_OPCODE_HOST_IDLE:
|
||||
qApp->processEvents();
|
||||
break;
|
||||
#if CARLA_VERSION_HEX >= CARLA_MIN_PARAM_VERSION
|
||||
case NATIVE_HOST_OPCODE_UI_TOUCH_PARAMETER:
|
||||
// param index, value as bool
|
||||
// true = mousePress
|
||||
// false = mouseRelease
|
||||
if (!value) {
|
||||
updateParamModel(index);
|
||||
}
|
||||
break;
|
||||
case NATIVE_HOST_OPCODE_RELOAD_ALL:
|
||||
refreshParams();
|
||||
break;
|
||||
case NATIVE_HOST_OPCODE_UPDATE_PARAMETER:
|
||||
break;
|
||||
case NATIVE_HOST_OPCODE_RELOAD_PARAMETERS:
|
||||
refreshParams();
|
||||
break;
|
||||
case NATIVE_HOST_OPCODE_UPDATE_MIDI_PROGRAM:
|
||||
case NATIVE_HOST_OPCODE_RELOAD_MIDI_PROGRAMS:
|
||||
case NATIVE_HOST_OPCODE_INTERNAL_PLUGIN:
|
||||
case NATIVE_HOST_OPCODE_QUEUE_INLINE_DISPLAY:
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -289,6 +366,115 @@ void CarlaInstrument::saveSettings(QDomDocument& doc, QDomElement& parent)
|
||||
}
|
||||
|
||||
std::free(state);
|
||||
|
||||
#if CARLA_VERSION_HEX >= CARLA_MIN_PARAM_VERSION
|
||||
for (uint32_t index = 0; index < m_paramModels.count(); ++index)
|
||||
{
|
||||
QString idStr = CARLA_SETTING_PREFIX + QString::number(index);
|
||||
m_paramModels[index]->saveSettings(doc, parent, idStr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CarlaInstrument::refreshParams(bool init)
|
||||
{
|
||||
m_paramGroupCount = 0;
|
||||
if (fDescriptor->get_parameter_count != nullptr &&
|
||||
fDescriptor->get_parameter_info != nullptr &&
|
||||
fDescriptor->get_parameter_value != nullptr &&
|
||||
fDescriptor->set_parameter_value != nullptr)
|
||||
{
|
||||
QList<QString> completerData;
|
||||
QList<QString> groups; // used to count no. groups.
|
||||
|
||||
uint32_t paramCount = fDescriptor->get_parameter_count(fHandle);
|
||||
for (uint32_t i=0; i < paramCount; ++i)
|
||||
{
|
||||
const NativeParameter* paramInfo(fDescriptor->get_parameter_info(fHandle, i));
|
||||
|
||||
m_paramModels[i]->setOutput((paramInfo->hints & NATIVE_PARAMETER_IS_OUTPUT));
|
||||
m_paramModels[i]->setEnabled((paramInfo->hints & NATIVE_PARAMETER_IS_ENABLED));
|
||||
m_paramModels[i]->setValue(fDescriptor->get_parameter_value(fHandle, i));
|
||||
|
||||
// Get parameter name
|
||||
QString name = "_NO_NAME_";
|
||||
if (paramInfo->name != nullptr)
|
||||
{
|
||||
name = paramInfo->name;
|
||||
}
|
||||
|
||||
if (paramInfo->groupName != nullptr)
|
||||
{
|
||||
m_paramModels[i]->setGroupName(paramInfo->groupName);
|
||||
|
||||
if (m_paramModels[i]->enabled() && !groups.contains(paramInfo->groupName))
|
||||
{
|
||||
groups.push_back(paramInfo->groupName);
|
||||
m_paramGroupCount++;
|
||||
}
|
||||
m_paramModels[i]->setGroupId(groups.indexOf(paramInfo->groupName));
|
||||
}
|
||||
|
||||
completerData.push_back(name);
|
||||
|
||||
m_paramModels[i]->setDisplayName(name);
|
||||
m_paramModels[i]->setRange(paramInfo->ranges.min,
|
||||
paramInfo->ranges.max,
|
||||
paramInfo->ranges.step);
|
||||
|
||||
// Load settings into model.
|
||||
if (init)
|
||||
{
|
||||
QString idStr = CARLA_SETTING_PREFIX + QString::number(i);
|
||||
m_paramModels[i]->loadSettings(m_settingsElem, idStr);
|
||||
}
|
||||
}
|
||||
// Set completer data
|
||||
m_completerModel->setStringList(completerData);
|
||||
}
|
||||
emit paramsUpdated();
|
||||
}
|
||||
|
||||
void CarlaInstrument::clearParamModels()
|
||||
{
|
||||
//Delete the models, this also disconnects all connections (automation and controller connections)
|
||||
for (uint32_t index=0; index < m_paramModels.count(); ++index)
|
||||
{
|
||||
delete m_paramModels[index];
|
||||
}
|
||||
|
||||
//Clear the list
|
||||
m_paramModels.clear();
|
||||
|
||||
m_paramGroupCount = 0;
|
||||
}
|
||||
|
||||
void CarlaInstrument::paramModelChanged(uint32_t index)
|
||||
{ // Update Carla param (LMMS -> Carla)
|
||||
if (!m_paramModels[index]->isOutput())
|
||||
{
|
||||
if (fDescriptor->set_parameter_value != nullptr)
|
||||
{
|
||||
fDescriptor->set_parameter_value(fHandle, index, m_paramModels[index]->value());
|
||||
|
||||
}
|
||||
|
||||
// TODO? Shouldn't Carla be doing this?
|
||||
if (fDescriptor->ui_set_parameter_value != nullptr)
|
||||
{
|
||||
fDescriptor->ui_set_parameter_value(fHandle, index, m_paramModels[index]->value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CarlaInstrument::updateParamModel(uint32_t index)
|
||||
{ // Called on param changed (Carla -> LMMS)
|
||||
if (fDescriptor->get_parameter_value != nullptr)
|
||||
{
|
||||
m_paramModels[index]->setValue(
|
||||
fDescriptor->get_parameter_value(fHandle, index)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void CarlaInstrument::loadSettings(const QDomElement& elem)
|
||||
@@ -300,6 +486,12 @@ void CarlaInstrument::loadSettings(const QDomElement& elem)
|
||||
carlaDoc.appendChild(carlaDoc.importNode(elem.firstChildElement(), true ));
|
||||
|
||||
fDescriptor->set_state(fHandle, carlaDoc.toString(0).toUtf8().constData());
|
||||
|
||||
#if CARLA_VERSION_HEX >= CARLA_MIN_PARAM_VERSION
|
||||
// Store to load parameter knobs settings when added.
|
||||
m_settingsElem = const_cast<QDomElement&>(elem);
|
||||
refreshParams(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CarlaInstrument::play(sampleFrame* workingBuffer)
|
||||
@@ -342,7 +534,14 @@ void CarlaInstrument::play(sampleFrame* workingBuffer)
|
||||
|
||||
{
|
||||
const QMutexLocker ml(&fMutex);
|
||||
// TODO FIXME this is just here so it compiles.
|
||||
// https://github.com/falkTX/Carla/blob/8bceb9ed173a10b29038f8abb4383710c0e497c1/source/includes/CarlaNative.h
|
||||
// FIXME for v3.0, use const for the input buffer
|
||||
#if CARLA_VERSION_HEX >= CARLA_VERSION_HEX_3
|
||||
fDescriptor->process(fHandle, (const float**)rBuf, rBuf, bufsize, fMidiEvents, fMidiEventCount);
|
||||
#else
|
||||
fDescriptor->process(fHandle, rBuf, rBuf, bufsize, fMidiEvents, fMidiEventCount);
|
||||
#endif
|
||||
fMidiEventCount = 0;
|
||||
}
|
||||
|
||||
@@ -405,7 +604,11 @@ CarlaInstrumentView::CarlaInstrumentView(CarlaInstrument* const instrument, QWid
|
||||
: InstrumentViewFixedSize(instrument, parent),
|
||||
fHandle(instrument->fHandle),
|
||||
fDescriptor(instrument->fDescriptor),
|
||||
fTimerId(fHandle != NULL && fDescriptor->ui_idle != NULL ? startTimer(30) : 0)
|
||||
fTimerId(fHandle != NULL && fDescriptor->ui_idle != NULL ? startTimer(30) : 0),
|
||||
m_carlaInstrument(instrument),
|
||||
m_parent(parent),
|
||||
m_paramsSubWindow(nullptr),
|
||||
m_paramsView(nullptr)
|
||||
{
|
||||
setAutoFillBackground(true);
|
||||
|
||||
@@ -413,10 +616,12 @@ CarlaInstrumentView::CarlaInstrumentView(CarlaInstrument* const instrument, QWid
|
||||
pal.setBrush(backgroundRole(), instrument->kIsPatchbay ? PLUGIN_NAME::getIconPixmap("artwork-patchbay") : PLUGIN_NAME::getIconPixmap("artwork-rack"));
|
||||
setPalette(pal);
|
||||
|
||||
QVBoxLayout * l = new QVBoxLayout( this );
|
||||
QHBoxLayout* l = new QHBoxLayout(this);
|
||||
l->setContentsMargins( 20, 180, 10, 10 );
|
||||
l->setSpacing( 10 );
|
||||
l->setSpacing(3);
|
||||
l->setAlignment(Qt::AlignTop);
|
||||
|
||||
// Show GUI button
|
||||
m_toggleUIButton = new QPushButton( tr( "Show GUI" ), this );
|
||||
m_toggleUIButton->setCheckable( true );
|
||||
m_toggleUIButton->setChecked( false );
|
||||
@@ -424,16 +629,44 @@ CarlaInstrumentView::CarlaInstrumentView(CarlaInstrument* const instrument, QWid
|
||||
m_toggleUIButton->setFont( pointSize<8>( m_toggleUIButton->font() ) );
|
||||
connect( m_toggleUIButton, SIGNAL( clicked(bool) ), this, SLOT( toggleUI( bool ) ) );
|
||||
|
||||
l->addWidget( m_toggleUIButton );
|
||||
l->addStretch();
|
||||
m_toggleUIButton->setToolTip(
|
||||
tr("Click here to show or hide the graphical user interface (GUI) of Carla."));
|
||||
|
||||
// Open params sub window button
|
||||
m_toggleParamsWindowButton = new QPushButton(tr("Params"), this);
|
||||
m_toggleParamsWindowButton->setIcon(embed::getIconPixmap("controller"));
|
||||
m_toggleParamsWindowButton->setCheckable(true);
|
||||
m_toggleParamsWindowButton->setFont(pointSize<8>(m_toggleParamsWindowButton->font()));
|
||||
#if CARLA_VERSION_HEX < CARLA_MIN_PARAM_VERSION
|
||||
m_toggleParamsWindowButton->setEnabled(false);
|
||||
m_toggleParamsWindowButton->setToolTip(tr("Available from Carla version 2.1 and up."));
|
||||
#else
|
||||
connect(m_toggleParamsWindowButton, SIGNAL(clicked(bool)), this, SLOT(toggleParamsWindow()));
|
||||
#endif
|
||||
|
||||
// Add widgets to layout
|
||||
l->addWidget( m_toggleUIButton );
|
||||
l->addWidget(m_toggleParamsWindowButton);
|
||||
|
||||
// Connect signals
|
||||
connect(m_toggleUIButton, SIGNAL(clicked(bool)), this, SLOT(toggleUI(bool)));
|
||||
connect(instrument, SIGNAL(uiClosed()), this, SLOT(uiClosed()));
|
||||
}
|
||||
|
||||
CarlaInstrumentView::~CarlaInstrumentView()
|
||||
{
|
||||
if (m_toggleUIButton->isChecked())
|
||||
{
|
||||
toggleUI(false);
|
||||
}
|
||||
|
||||
#if CARLA_VERSION_HEX >= CARLA_MIN_PARAM_VERSION
|
||||
if (m_paramsView)
|
||||
{
|
||||
delete m_paramsView;
|
||||
m_paramsView = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CarlaInstrumentView::toggleUI(bool visible)
|
||||
@@ -471,5 +704,408 @@ void CarlaInstrumentView::timerEvent(QTimerEvent* event)
|
||||
InstrumentView::timerEvent(event);
|
||||
}
|
||||
|
||||
void CarlaInstrumentView::toggleParamsWindow()
|
||||
{
|
||||
if (!m_paramsSubWindow)
|
||||
{
|
||||
m_paramsView = new CarlaParamsView(this, m_parent);
|
||||
connect(m_paramsSubWindow, SIGNAL(uiClosed()), this, SLOT(paramsUiClosed()));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_paramsSubWindow->isVisible())
|
||||
{
|
||||
m_paramsSubWindow->hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_paramsSubWindow->show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CarlaInstrumentView::paramsUiClosed()
|
||||
{
|
||||
m_toggleParamsWindowButton->setChecked(false);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
CarlaParamsView::CarlaParamsView(CarlaInstrumentView* const instrumentView, QWidget* const parent)
|
||||
: InstrumentView(instrumentView->m_carlaInstrument, parent),
|
||||
m_carlaInstrument(instrumentView->m_carlaInstrument),
|
||||
m_carlaInstrumentView(instrumentView),
|
||||
m_maxColumns(6),
|
||||
m_curColumn(0),
|
||||
m_curRow(0),
|
||||
m_curOutColumn(0),
|
||||
m_curOutRow(0)
|
||||
{
|
||||
QWidget* centralWidget = new QWidget(this);
|
||||
QVBoxLayout* verticalLayout = new QVBoxLayout(centralWidget);
|
||||
|
||||
// -- Toolbar
|
||||
m_toolBarLayout = new QHBoxLayout();
|
||||
|
||||
// Toolbar widgets
|
||||
QSizePolicy sizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
|
||||
sizePolicy.setHorizontalStretch(0);
|
||||
sizePolicy.setVerticalStretch(0);
|
||||
|
||||
// Params filter line edit
|
||||
m_paramsFilterLineEdit = new QLineEdit(this);
|
||||
m_paramsFilterLineEdit->setPlaceholderText(tr("Search.."));
|
||||
m_paramsFilterLineEdit->setCompleter(m_carlaInstrument->m_paramsCompleter);
|
||||
|
||||
// Clear filter line edit button
|
||||
m_clearFilterButton = new QPushButton(tr(""), this);
|
||||
m_clearFilterButton->setIcon(embed::getIconPixmap("edit_erase"));
|
||||
m_clearFilterButton->setToolTip(tr("Clear filter text"));
|
||||
sizePolicy.setHeightForWidth(m_clearFilterButton->sizePolicy().hasHeightForWidth());
|
||||
m_clearFilterButton->setSizePolicy(sizePolicy);
|
||||
|
||||
// Show automated only button
|
||||
m_automatedOnlyButton = new QPushButton(tr(""), this);
|
||||
m_automatedOnlyButton->setIcon(embed::getIconPixmap("automation"));
|
||||
m_automatedOnlyButton->setToolTip(
|
||||
tr("Only show knobs with a connection."));
|
||||
m_automatedOnlyButton->setCheckable(true);
|
||||
sizePolicy.setHeightForWidth(m_automatedOnlyButton->sizePolicy().hasHeightForWidth());
|
||||
m_automatedOnlyButton->setSizePolicy(sizePolicy);
|
||||
|
||||
// Group name combobox
|
||||
m_groupFilterCombo = new QComboBox(this);
|
||||
m_groupFilterModel = new QStringListModel(this);
|
||||
m_groupFilterCombo->setModel(m_groupFilterModel);
|
||||
|
||||
// Add stuff to toolbar
|
||||
m_toolBarLayout->addWidget(m_paramsFilterLineEdit);
|
||||
m_toolBarLayout->addWidget(m_clearFilterButton);
|
||||
m_toolBarLayout->addWidget(m_automatedOnlyButton);
|
||||
m_toolBarLayout->addWidget(m_groupFilterCombo);
|
||||
|
||||
// -- Input params
|
||||
QFrame* inputFrame = new QFrame(this);
|
||||
QVBoxLayout* inputLayout = new QVBoxLayout(inputFrame);
|
||||
QLabel* inputLabel = new QLabel("Input parameters", inputFrame);
|
||||
|
||||
m_inputScrollArea = new QScrollArea(inputFrame);
|
||||
m_inputScrollAreaWidgetContent = new QWidget();
|
||||
m_inputScrollAreaLayout = new QGridLayout(m_inputScrollAreaWidgetContent);
|
||||
|
||||
m_inputScrollAreaWidgetContent->setLayout(m_inputScrollAreaLayout);
|
||||
m_inputScrollAreaWidgetContent->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
|
||||
|
||||
m_inputScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
m_inputScrollArea->setWidget(m_inputScrollAreaWidgetContent);
|
||||
m_inputScrollArea->setWidgetResizable(true);
|
||||
m_inputScrollArea->setFrameShadow(QFrame::Plain);
|
||||
m_inputScrollArea->setFrameShape(QFrame::NoFrame);
|
||||
|
||||
m_inputScrollAreaLayout->setContentsMargins(3, 3, 3, 3);
|
||||
m_inputScrollAreaLayout->setVerticalSpacing(12);
|
||||
m_inputScrollAreaLayout->setHorizontalSpacing(6);
|
||||
m_inputScrollAreaLayout->setColumnStretch(m_maxColumns, 1);
|
||||
|
||||
inputLayout->addWidget(inputLabel);
|
||||
inputLayout->addWidget(m_inputScrollArea);
|
||||
|
||||
// -- Output params
|
||||
QFrame* outputFrame = new QFrame(this);
|
||||
QVBoxLayout* outputLayout = new QVBoxLayout(outputFrame);
|
||||
QLabel* outputLabel = new QLabel("Output parameters", outputFrame);
|
||||
|
||||
m_outputScrollArea = new QScrollArea(outputFrame);
|
||||
m_outputScrollAreaWidgetContent = new QWidget();
|
||||
m_outputScrollAreaLayout = new QGridLayout(m_outputScrollAreaWidgetContent);
|
||||
|
||||
m_outputScrollAreaWidgetContent->setLayout(m_outputScrollAreaLayout);
|
||||
m_outputScrollAreaWidgetContent->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
|
||||
|
||||
m_outputScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
m_outputScrollArea->setWidget(m_outputScrollAreaWidgetContent);
|
||||
m_outputScrollArea->setWidgetResizable(true);
|
||||
m_outputScrollArea->setFrameShadow(QFrame::Plain);
|
||||
m_outputScrollArea->setFrameShape(QFrame::NoFrame);
|
||||
|
||||
m_outputScrollAreaLayout->setContentsMargins(3, 28, 3, 3);
|
||||
m_outputScrollAreaLayout->setVerticalSpacing(12);
|
||||
m_outputScrollAreaLayout->setHorizontalSpacing(6);
|
||||
m_outputScrollAreaLayout->setColumnStretch(m_maxColumns, 1);
|
||||
|
||||
outputLayout->addWidget(outputLabel);
|
||||
outputLayout->addWidget(m_outputScrollArea);
|
||||
|
||||
// -- QSplitter
|
||||
QSplitter* splitter = new QSplitter(Qt::Vertical, this);
|
||||
|
||||
// -- Add layout and widgets.
|
||||
verticalLayout->addLayout(m_toolBarLayout);
|
||||
splitter->addWidget(inputFrame);
|
||||
splitter->addWidget(outputFrame);
|
||||
verticalLayout->addWidget(splitter);
|
||||
|
||||
// -- Sub window
|
||||
CarlaParamsSubWindow* win = new CarlaParamsSubWindow(gui->mainWindow()->workspace()->viewport(), Qt::SubWindow |
|
||||
Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
|
||||
m_carlaInstrumentView->m_paramsSubWindow = gui->mainWindow()->workspace()->addSubWindow(win);
|
||||
m_carlaInstrumentView->m_paramsSubWindow->setSizePolicy(
|
||||
QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
|
||||
m_carlaInstrumentView->m_paramsSubWindow->setMinimumHeight(200);
|
||||
m_carlaInstrumentView->m_paramsSubWindow->setMinimumWidth(200);
|
||||
m_carlaInstrumentView->m_paramsSubWindow->resize(600, 400);
|
||||
m_carlaInstrumentView->m_paramsSubWindow->setWidget(centralWidget);
|
||||
centralWidget->setWindowTitle(m_carlaInstrument->instrumentTrack()->name() + tr(" - Parameters"));
|
||||
|
||||
// -- Connect signals
|
||||
connect(m_carlaInstrumentView->m_paramsSubWindow, SIGNAL(resized()), this, SLOT(windowResized()));
|
||||
connect(m_paramsFilterLineEdit, SIGNAL(textChanged(const QString)), this, SLOT(filterKnobs()));
|
||||
connect(m_clearFilterButton, SIGNAL(clicked(bool)), this, SLOT(clearFilterText()));
|
||||
connect(m_automatedOnlyButton, SIGNAL(toggled(bool)), this, SLOT(filterKnobs()));
|
||||
connect(m_groupFilterCombo, SIGNAL(currentTextChanged(const QString)), this, SLOT(filterKnobs()));
|
||||
connect(m_carlaInstrument, SIGNAL(paramsUpdated()), this, SLOT(refreshKnobs()));
|
||||
|
||||
m_carlaInstrumentView->m_paramsSubWindow->show(); // Show the subwindow
|
||||
|
||||
// Add knobs if there are any already.
|
||||
// Call this after show() so the m_inputScrollArea->width() is set properly.
|
||||
refreshKnobs(); // Will trigger filterKnobs() due m_groupFilterCombo->setCurrentIndex(0)
|
||||
}
|
||||
|
||||
CarlaParamsView::~CarlaParamsView()
|
||||
{
|
||||
// Close and delete m_paramsSubWindow
|
||||
if (m_carlaInstrumentView->m_paramsSubWindow)
|
||||
{
|
||||
m_carlaInstrumentView->m_paramsSubWindow->setAttribute(Qt::WA_DeleteOnClose);
|
||||
m_carlaInstrumentView->m_paramsSubWindow->close();
|
||||
|
||||
delete m_carlaInstrumentView->m_paramsSubWindow;
|
||||
m_carlaInstrumentView->m_paramsSubWindow = nullptr;
|
||||
}
|
||||
|
||||
m_carlaInstrumentView->m_paramsView = nullptr;
|
||||
|
||||
// Clear models
|
||||
if (m_carlaInstrument->m_paramModels.isEmpty() == false)
|
||||
{
|
||||
m_carlaInstrument->clearParamModels();
|
||||
}
|
||||
}
|
||||
|
||||
void CarlaParamsView::clearFilterText()
|
||||
{
|
||||
m_paramsFilterLineEdit->setText("");
|
||||
}
|
||||
|
||||
void CarlaParamsView::filterKnobs()
|
||||
{
|
||||
clearKnobs(); // Remove all knobs from the layout.
|
||||
|
||||
if (!m_carlaInstrument->m_paramGroupCount)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Calc how many knobs will fit horizontal in the params window.
|
||||
uint16_t maxKnobWidth = m_maxKnobWidthPerGroup[m_groupFilterCombo->currentIndex()];
|
||||
maxKnobWidth += m_inputScrollAreaLayout->spacing();
|
||||
if (!maxKnobWidth)
|
||||
{
|
||||
// Prevent possible division by zero.
|
||||
return;
|
||||
}
|
||||
m_maxColumns = m_inputScrollArea->width() / maxKnobWidth;
|
||||
|
||||
QString text = m_paramsFilterLineEdit->text();
|
||||
for (uint32_t i=0; i < m_knobs.count(); ++i)
|
||||
{
|
||||
// Don't show disabled (unused) knobs.
|
||||
if (!m_carlaInstrument->m_paramModels[i]->enabled())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Filter on automation only
|
||||
if (m_automatedOnlyButton->isChecked())
|
||||
{
|
||||
if (! m_carlaInstrument->m_paramModels[i]->isAutomatedOrControlled())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Filter on group name
|
||||
if (m_groupFilterCombo->currentText() != m_carlaInstrument->m_paramModels[i]->groupName())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Filter on text
|
||||
if (text != "")
|
||||
{
|
||||
if (m_knobs[i]->objectName().contains(text, Qt::CaseInsensitive))
|
||||
{
|
||||
addKnob(i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
addKnob(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Add spacer so all knobs go to top
|
||||
QSpacerItem* verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||
m_inputScrollAreaLayout->addItem(verticalSpacer, m_curRow+1, 0, 1, 1);
|
||||
}
|
||||
|
||||
void CarlaParamsView::refreshKnobs()
|
||||
{
|
||||
// Make sure all the knobs are deleted.
|
||||
for (uint32_t i=0; i < m_knobs.count(); ++i)
|
||||
{
|
||||
delete m_knobs[i]; // Delete knob widgets itself.
|
||||
}
|
||||
m_knobs.clear(); // Clear the pointer list.
|
||||
|
||||
// Reset position data.
|
||||
m_curColumn = 0;
|
||||
m_curRow = 0;
|
||||
|
||||
m_curOutColumn = 0;
|
||||
m_curOutRow = 0;
|
||||
|
||||
// Clear max knob width per group
|
||||
m_maxKnobWidthPerGroup.clear();
|
||||
m_maxKnobWidthPerGroup.reserve(m_carlaInstrument->m_paramGroupCount);
|
||||
for (uint8_t i = 0; i < m_carlaInstrument->m_paramGroupCount; i++)
|
||||
{
|
||||
m_maxKnobWidthPerGroup[i] = 0;
|
||||
}
|
||||
|
||||
if (!m_carlaInstrument->m_paramModels.count()) { return; }
|
||||
|
||||
// Make room in QList m_knobs
|
||||
m_knobs.reserve(m_carlaInstrument->m_paramModels.count());
|
||||
|
||||
QStringList groupNameList;
|
||||
groupNameList.reserve(m_carlaInstrument->m_paramGroupCount);
|
||||
|
||||
for (uint32_t i=0; i < m_carlaInstrument->m_paramModels.count(); ++i)
|
||||
{
|
||||
bool enabled = m_carlaInstrument->m_paramModels[i]->enabled();
|
||||
m_knobs.push_back(new Knob(knobDark_28, m_inputScrollAreaWidgetContent));
|
||||
QString name = (*m_carlaInstrument->m_paramModels[i]).displayName();
|
||||
m_knobs[i]->setHintText(name, "");
|
||||
m_knobs[i]->setLabel(name);
|
||||
m_knobs[i]->setObjectName(name); // this is being used for filtering the knobs.
|
||||
|
||||
// Set the newly created model to the knob.
|
||||
m_knobs[i]->setModel(m_carlaInstrument->m_paramModels[i]);
|
||||
m_knobs[i]->setEnabled(enabled);
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
// Collect group names
|
||||
if (!groupNameList.contains(m_carlaInstrument->m_paramModels[i]->groupName()))
|
||||
{
|
||||
groupNameList.append(m_carlaInstrument->m_paramModels[i]->groupName());
|
||||
}
|
||||
|
||||
// Store biggest knob width per group (so we can calc how many
|
||||
// knobs we can horizontaly fit)
|
||||
uint8_t groupId = m_carlaInstrument->m_paramModels[i]->groupId();
|
||||
if (m_maxKnobWidthPerGroup[groupId] < m_knobs[i]->width())
|
||||
{
|
||||
m_maxKnobWidthPerGroup[groupId] = m_knobs[i]->width();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set new list with group names to the model
|
||||
if (!groupNameList.count())
|
||||
{
|
||||
groupNameList.append("No params");
|
||||
}
|
||||
m_groupFilterModel->setStringList(groupNameList);
|
||||
m_groupFilterCombo->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
|
||||
void CarlaParamsView::windowResized()
|
||||
{
|
||||
filterKnobs();
|
||||
}
|
||||
|
||||
|
||||
void CarlaParamsView::addKnob(uint32_t index)
|
||||
{
|
||||
bool output = m_carlaInstrument->m_paramModels[index]->isOutput();
|
||||
if (output)
|
||||
{
|
||||
m_outputScrollAreaLayout->addWidget(
|
||||
m_knobs[index], m_curOutRow, m_curOutColumn, Qt::AlignHCenter | Qt::AlignTop);
|
||||
m_knobs[index]->setEnabled(false); // We should not be able to adjust output.
|
||||
m_knobs[index]->show();
|
||||
if (m_curOutColumn < m_maxColumns - 1)
|
||||
{
|
||||
m_curOutColumn++;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_curOutColumn = 0;
|
||||
m_curOutRow++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add the new knob to layout
|
||||
m_inputScrollAreaLayout->addWidget(m_knobs[index], m_curRow, m_curColumn, Qt::AlignHCenter | Qt::AlignTop);
|
||||
m_inputScrollAreaLayout->setColumnStretch(m_curColumn, 1);
|
||||
|
||||
// Chances that we did close() on the widget is big, so show it.
|
||||
m_knobs[index]->show();
|
||||
|
||||
// Keep track of current column and row index.
|
||||
if (m_curColumn < m_maxColumns - 1)
|
||||
{
|
||||
m_curColumn++;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_curColumn = 0;
|
||||
m_curRow++;
|
||||
}
|
||||
}
|
||||
}
|
||||
void CarlaParamsView::clearKnobs()
|
||||
{
|
||||
// Remove knobs from layout.
|
||||
for (uint16_t i=0; i < m_knobs.count(); ++i)
|
||||
{
|
||||
m_knobs[i]->close();
|
||||
}
|
||||
|
||||
// Remove spacers
|
||||
QLayoutItem* item;
|
||||
for (int16_t i=m_inputScrollAreaLayout->count() - 1; i > 0; i--)
|
||||
{
|
||||
item = m_inputScrollAreaLayout->takeAt(i);
|
||||
if (item->widget()) {continue;}
|
||||
delete item;
|
||||
}
|
||||
for (int16_t i=m_outputScrollAreaLayout->count() - 1; i > 0; i--)
|
||||
{
|
||||
item = m_outputScrollAreaLayout->takeAt(i);
|
||||
if (item->widget()) {continue;}
|
||||
delete item;
|
||||
}
|
||||
|
||||
// Reset position data.
|
||||
m_curColumn = 0;
|
||||
m_curRow = 0;
|
||||
|
||||
m_curOutColumn = 0;
|
||||
m_curOutRow = 0;
|
||||
}
|
||||
|
||||
@@ -25,10 +25,21 @@
|
||||
#ifndef CARLA_H
|
||||
#define CARLA_H
|
||||
|
||||
#include <QtCore/QMutex>
|
||||
#include "carlabase_export.h"
|
||||
#define CARLA_SETTING_PREFIX "PARAM_KNOB_"
|
||||
#define CARLA_MIN_PARAM_VERSION 0x020090
|
||||
#define CARLA_VERSION_HEX_3 0x30000
|
||||
|
||||
#include "CarlaNative.h"
|
||||
// qt
|
||||
#include <QCloseEvent>
|
||||
#include <QCompleter>
|
||||
#include <QGridLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QScrollArea>
|
||||
#include <QStringListModel>
|
||||
#include <QtCore/QMutex>
|
||||
|
||||
// carla/source/includes
|
||||
#include "carlabase_export.h"
|
||||
#if CARLA_VERSION_HEX >= 0x010911
|
||||
#include "CarlaNativePlugin.h"
|
||||
#else
|
||||
@@ -42,10 +53,163 @@
|
||||
const NativePluginDescriptor* carla_get_native_rack_plugin();
|
||||
#endif
|
||||
|
||||
// lmms/include/
|
||||
#include "EffectControls.h"
|
||||
#include "Instrument.h"
|
||||
#include "InstrumentView.h"
|
||||
#include "Knob.h"
|
||||
#include "SubWindow.h"
|
||||
|
||||
class QPushButton;
|
||||
class QComboBox;
|
||||
|
||||
class CarlaParamsView;
|
||||
|
||||
class CarlaParamFloatModel : public FloatModel
|
||||
{
|
||||
public:
|
||||
CarlaParamFloatModel(Model * parent):
|
||||
FloatModel(0.0, 0.0, 1.0, 0.001, parent, "Unused"),
|
||||
m_isOutput(false),
|
||||
m_isEnabled(false)
|
||||
{
|
||||
}
|
||||
|
||||
// From AutomatableModel.h, it's private there.
|
||||
inline static bool mustQuoteName(const QString &name)
|
||||
{
|
||||
QRegExp reg("^[A-Za-z0-9._-]+$");
|
||||
return !reg.exactMatch(name);
|
||||
}
|
||||
|
||||
inline virtual void loadSettings(const QDomElement& element, const QString& name = QString("value")) override
|
||||
{
|
||||
AutomatableModel::loadSettings(element, name);
|
||||
bool mustQuote = mustQuoteName(name);
|
||||
QDomElement me = element.firstChildElement(mustQuote ? QString("automatablemodel") : name);
|
||||
if (!me.isNull()) {
|
||||
m_isOutput = (bool)me.attribute("output", "0").toInt();
|
||||
m_groupName = QString(me.attribute("groupName", ""));
|
||||
}
|
||||
}
|
||||
|
||||
inline virtual void saveSettings(QDomDocument& doc, QDomElement& element,
|
||||
const QString& name = QString( "value" )) override
|
||||
{
|
||||
if (m_isEnabled)
|
||||
{
|
||||
AutomatableModel::saveSettings(doc, element, name);
|
||||
bool mustQuote = mustQuoteName(name);
|
||||
QDomElement me = element.firstChildElement(mustQuote ? QString("automatablemodel") : name);
|
||||
if (!me.isNull())
|
||||
{
|
||||
me.setAttribute("output", m_isOutput);
|
||||
me.setAttribute("groupName", m_groupName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline const bool enabled()
|
||||
{
|
||||
return m_isEnabled;
|
||||
}
|
||||
|
||||
inline const bool isOutput()
|
||||
{
|
||||
return m_isOutput;
|
||||
}
|
||||
|
||||
inline void setOutput(bool state = true)
|
||||
{
|
||||
m_isOutput = state;
|
||||
}
|
||||
|
||||
inline void setEnabled(bool state = true)
|
||||
{
|
||||
m_isEnabled = state;
|
||||
}
|
||||
|
||||
inline void setGroupName(QString groupName)
|
||||
{
|
||||
m_groupName = groupName;
|
||||
}
|
||||
|
||||
inline void setGroupId(uint8_t groupId)
|
||||
{
|
||||
m_groupId = groupId;
|
||||
}
|
||||
|
||||
virtual QString groupName() const
|
||||
{
|
||||
return m_groupName;
|
||||
}
|
||||
|
||||
virtual uint8_t groupId() const
|
||||
{
|
||||
return m_groupId;
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_isOutput;
|
||||
bool m_isEnabled;
|
||||
uint8_t m_groupId;
|
||||
QString m_groupName;
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
class CarlaParamsSubWindow : public SubWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
signals:
|
||||
void uiClosed();
|
||||
void resized();
|
||||
|
||||
public:
|
||||
CarlaParamsSubWindow(QWidget * _parent, Qt::WindowFlags windowFlags) :
|
||||
SubWindow(_parent)
|
||||
{
|
||||
setAttribute(Qt::WA_DeleteOnClose, false);
|
||||
setWindowFlags(windowFlags);
|
||||
}
|
||||
|
||||
virtual void resizeEvent(QResizeEvent * event) override
|
||||
{
|
||||
if (mousePress) {
|
||||
resizing = true;
|
||||
}
|
||||
SubWindow::resizeEvent(event);
|
||||
}
|
||||
|
||||
virtual void mousePressEvent(QMouseEvent * event) override
|
||||
{
|
||||
mousePress = true;
|
||||
SubWindow::mousePressEvent(event);
|
||||
}
|
||||
|
||||
virtual void mouseReleaseEvent(QMouseEvent * event) override
|
||||
{
|
||||
if (resizing) {
|
||||
resizing = false;
|
||||
mousePress = false;
|
||||
emit resized();
|
||||
}
|
||||
SubWindow::mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
virtual void closeEvent(QCloseEvent * event) override
|
||||
{
|
||||
emit uiClosed();
|
||||
event->accept();
|
||||
}
|
||||
|
||||
private:
|
||||
bool resizing = false;
|
||||
bool mousePress = false;
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
class CARLABASE_EXPORT CarlaInstrument : public Instrument
|
||||
{
|
||||
@@ -77,9 +241,14 @@ public:
|
||||
|
||||
signals:
|
||||
void uiClosed();
|
||||
void paramsUpdated();
|
||||
|
||||
private slots:
|
||||
void sampleRateChanged();
|
||||
void refreshParams(bool init = false);
|
||||
void clearParamModels();
|
||||
void paramModelChanged(uint32_t index);
|
||||
void updateParamModel(uint32_t index);
|
||||
|
||||
private:
|
||||
const bool kIsPatchbay;
|
||||
@@ -95,9 +264,20 @@ private:
|
||||
// this is only needed because note-offs are being sent during play
|
||||
QMutex fMutex;
|
||||
|
||||
uint8_t m_paramGroupCount;
|
||||
QList<CarlaParamFloatModel*> m_paramModels;
|
||||
QDomElement m_settingsElem;
|
||||
|
||||
QCompleter* m_paramsCompleter;
|
||||
QStringListModel* m_completerModel;
|
||||
|
||||
friend class CarlaInstrumentView;
|
||||
friend class CarlaParamsView;
|
||||
};
|
||||
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
class CarlaInstrumentView : public InstrumentViewFixedSize
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -109,6 +289,8 @@ public:
|
||||
private slots:
|
||||
void toggleUI(bool);
|
||||
void uiClosed();
|
||||
void toggleParamsWindow();
|
||||
void paramsUiClosed();
|
||||
|
||||
private:
|
||||
virtual void modelChanged();
|
||||
@@ -118,7 +300,66 @@ private:
|
||||
const NativePluginDescriptor* fDescriptor;
|
||||
int fTimerId;
|
||||
|
||||
QPushButton * m_toggleUIButton;
|
||||
CarlaInstrument* const m_carlaInstrument;
|
||||
QWidget* const m_parent;
|
||||
|
||||
QMdiSubWindow* m_paramsSubWindow;
|
||||
CarlaParamsView* m_paramsView;
|
||||
|
||||
QPushButton* m_toggleUIButton;
|
||||
QPushButton* m_toggleParamsWindowButton;
|
||||
|
||||
friend class CarlaParamsView;
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
class CarlaParamsView : public InstrumentView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CarlaParamsView(CarlaInstrumentView* const instrumentView, QWidget* const parent);
|
||||
virtual ~CarlaParamsView();
|
||||
|
||||
signals:
|
||||
void uiClosed();
|
||||
|
||||
private slots:
|
||||
void refreshKnobs();
|
||||
void filterKnobs();
|
||||
void clearFilterText();
|
||||
void windowResized();
|
||||
|
||||
private:
|
||||
void adjustWindowWidth();
|
||||
void addKnob(uint32_t index);
|
||||
void clearKnobs();
|
||||
|
||||
CarlaInstrument* const m_carlaInstrument;
|
||||
CarlaInstrumentView* const m_carlaInstrumentView;
|
||||
QList<Knob*> m_knobs;
|
||||
|
||||
// Keep track of the biggest knob width per group
|
||||
QList<uint16_t> m_maxKnobWidthPerGroup;
|
||||
|
||||
uint32_t m_maxColumns;
|
||||
uint32_t m_curColumn;
|
||||
uint32_t m_curRow;
|
||||
uint32_t m_curOutColumn;
|
||||
uint32_t m_curOutRow;
|
||||
|
||||
QScrollArea* m_inputScrollArea;
|
||||
QGridLayout* m_inputScrollAreaLayout;
|
||||
QWidget* m_inputScrollAreaWidgetContent;
|
||||
QScrollArea* m_outputScrollArea;
|
||||
QGridLayout* m_outputScrollAreaLayout;
|
||||
QWidget* m_outputScrollAreaWidgetContent;
|
||||
QHBoxLayout* m_toolBarLayout;
|
||||
QLineEdit* m_paramsFilterLineEdit;
|
||||
QPushButton* m_clearFilterButton;
|
||||
QPushButton* m_automatedOnlyButton;
|
||||
QComboBox* m_groupFilterCombo;
|
||||
QStringListModel* m_groupFilterModel;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user