Rewrite EffectSelectDialog to add effect groups and delete Qt Designer UI file (#7024)

This commit is contained in:
Lost Robot
2024-02-19 09:03:25 -08:00
committed by GitHub
parent 876a36cebf
commit 360254fd81
4 changed files with 227 additions and 253 deletions

View File

@@ -2,6 +2,7 @@
* EffectSelectDialog.h - dialog to choose effect plugin
*
* Copyright (c) 2006-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2023 Lost Robot <r94231/at/gmail.com>
*
* This file is part of LMMS - https://lmms.io
*
@@ -25,49 +26,86 @@
#ifndef LMMS_GUI_EFFECT_SELECT_DIALOG_H
#define LMMS_GUI_EFFECT_SELECT_DIALOG_H
#include <QDialog>
#include <QSortFilterProxyModel>
#include <QStandardItemModel>
#include "Effect.h"
namespace Ui { class EffectSelectDialog; }
#include <QDialog>
#include <QHeaderView>
#include <QKeyEvent>
#include <QMouseEvent>
#include <QPushButton>
#include <QRegExp>
#include <QScrollArea>
#include <QSortFilterProxyModel>
#include <QStandardItemModel>
#include <QTableView>
namespace lmms::gui
{
class DualColumnFilterProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
DualColumnFilterProxyModel(QObject* parent = nullptr) : QSortFilterProxyModel(parent)
{
}
void setEffectTypeFilter(const QString& filter)
{
m_effectTypeFilter = filter;
invalidateFilter();
}
protected:
bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override
{
QModelIndex nameIndex = sourceModel()->index(source_row, 0, source_parent);
QModelIndex typeIndex = sourceModel()->index(source_row, 1, source_parent);
QString name = sourceModel()->data(nameIndex, Qt::DisplayRole).toString();
QString type = sourceModel()->data(typeIndex, Qt::DisplayRole).toString();
QRegExp nameRegExp(filterRegExp());
nameRegExp.setCaseSensitivity(Qt::CaseInsensitive);
bool nameFilterPassed = nameRegExp.indexIn(name) != -1;
bool typeFilterPassed = type.contains(m_effectTypeFilter, Qt::CaseInsensitive);
return nameFilterPassed && typeFilterPassed;
}
private:
QString m_effectTypeFilter;
};
class EffectSelectDialog : public QDialog
{
Q_OBJECT
public:
EffectSelectDialog( QWidget * _parent );
~EffectSelectDialog() override;
Effect * instantiateSelectedPlugin( EffectChain * _parent );
EffectSelectDialog(QWidget* parent);
Effect* instantiateSelectedPlugin(EffectChain* parent);
protected slots:
void acceptSelection();
void rowChanged( const QModelIndex &, const QModelIndex & );
void sortAgain();
void rowChanged(const QModelIndex&, const QModelIndex&);
void updateSelection();
bool eventFilter(QObject* obj, QEvent* event) override;
private:
Ui::EffectSelectDialog * ui;
EffectKeyList m_effectKeys;
EffectKey m_currentSelection;
QStandardItemModel m_sourceModel;
QSortFilterProxyModel m_model;
QWidget * m_descriptionWidget;
} ;
DualColumnFilterProxyModel m_model;
QWidget* m_descriptionWidget;
QTableView* m_pluginList;
QScrollArea* m_scrollArea;
QLineEdit* m_filterEdit;
};
} // namespace lmms::gui
#endif // LMMS_GUI_EFFECT_SELECT_DIALOG_H
#endif

View File

@@ -132,7 +132,6 @@ SET(LMMS_SRCS
set(LMMS_UIS
${LMMS_UIS}
gui/modals/about_dialog.ui
gui/modals/EffectSelectDialog.ui
gui/modals/export_project.ui
PARENT_SCOPE

View File

@@ -2,6 +2,7 @@
* EffectSelectDialog.cpp - dialog to choose effect plugin
*
* Copyright (c) 2006-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2023 Lost Robot <r94231/at/gmail.com>
*
* This file is part of LMMS - https://lmms.io
*
@@ -23,63 +24,62 @@
*/
#include "EffectSelectDialog.h"
#include "ui_EffectSelectDialog.h"
#include "DummyEffect.h"
#include "EffectChain.h"
#include "embed.h"
#include "PluginFactory.h"
#include <QApplication>
#include <QDialogButtonBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QScrollArea>
#include <QVBoxLayout>
namespace lmms::gui
{
EffectSelectDialog::EffectSelectDialog( QWidget * _parent ) :
QDialog( _parent ),
ui( new Ui::EffectSelectDialog ),
EffectSelectDialog::EffectSelectDialog(QWidget* parent) :
QDialog(parent),
m_effectKeys(),
m_currentSelection(),
m_sourceModel(),
m_model(),
m_descriptionWidget( nullptr )
m_descriptionWidget(nullptr),
m_pluginList(new QTableView(this)),
m_scrollArea(new QScrollArea(this))
{
ui->setupUi( this );
setWindowIcon( embed::getIconPixmap( "setup_audio" ) );
// query effects
setWindowTitle(tr("Add effect"));
resize(640, 480);
setWindowIcon(embed::getIconPixmap("setup_audio"));
// Query effects
EffectKeyList subPluginEffectKeys;
for (const Plugin::Descriptor* desc: getPluginFactory()->descriptors(Plugin::Type::Effect))
for (const auto desc : getPluginFactory()->descriptors(Plugin::Type::Effect))
{
if( desc->subPluginFeatures )
if (desc->subPluginFeatures)
{
desc->subPluginFeatures->listSubPluginKeys(
desc,
subPluginEffectKeys );
desc->subPluginFeatures->listSubPluginKeys(desc, subPluginEffectKeys);
}
else
{
m_effectKeys << EffectKey( desc, desc->name );
m_effectKeys << EffectKey(desc, desc->name);
}
}
m_effectKeys += subPluginEffectKeys;
// and fill our source model
m_sourceModel.setHorizontalHeaderItem( 0, new QStandardItem( tr( "Name" ) ) );
m_sourceModel.setHorizontalHeaderItem( 1, new QStandardItem( tr( "Type" ) ) );
// Fill the source model
m_sourceModel.setHorizontalHeaderItem(0, new QStandardItem(tr("Name")));
m_sourceModel.setHorizontalHeaderItem(1, new QStandardItem(tr("Type")));
int row = 0;
for( EffectKeyList::ConstIterator it = m_effectKeys.begin();
it != m_effectKeys.end(); ++it )
for (EffectKeyList::ConstIterator it = m_effectKeys.begin(); it != m_effectKeys.end(); ++it)
{
QString name;
QString type;
if( it->desc->subPluginFeatures )
if (it->desc->subPluginFeatures)
{
name = it->displayName();
type = it->desc->displayName;
@@ -89,114 +89,148 @@ EffectSelectDialog::EffectSelectDialog( QWidget * _parent ) :
name = it->desc->displayName;
type = "LMMS";
}
m_sourceModel.setItem( row, 0, new QStandardItem( name ) );
m_sourceModel.setItem( row, 1, new QStandardItem( type ) );
m_sourceModel.setItem(row, 0, new QStandardItem(name));
m_sourceModel.setItem(row, 1, new QStandardItem(type));
++row;
}
// setup filtering
m_model.setSourceModel( &m_sourceModel );
m_model.setFilterCaseSensitivity( Qt::CaseInsensitive );
// Setup filtering
m_model.setSourceModel(&m_sourceModel);
m_model.setFilterCaseSensitivity(Qt::CaseInsensitive);
ui->filterEdit->setPlaceholderText(tr("Search"));
ui->filterEdit->setClearButtonEnabled(true);
ui->filterEdit->addAction(embed::getIconPixmap("zoom"), QLineEdit::LeadingPosition);
QHBoxLayout* mainLayout = new QHBoxLayout(this);
connect(ui->filterEdit, &QLineEdit::textChanged, &m_model, &QSortFilterProxyModel::setFilterFixedString);
connect(ui->filterEdit, &QLineEdit::textChanged, this, &EffectSelectDialog::updateSelection);
connect(ui->filterEdit, &QLineEdit::textChanged, this, &EffectSelectDialog::sortAgain);
QVBoxLayout* leftSectionLayout = new QVBoxLayout();
ui->pluginList->setModel( &m_model );
QStringList buttonLabels = { tr("All"), "LMMS", "LADSPA", "LV2", "VST" };
QStringList buttonSearchString = { "", "LMMS", "LADSPA", "LV2", "VST" };
for (int i = 0; i < buttonLabels.size(); ++i)
{
const QString& label = buttonLabels[i];
const QString& searchString = buttonSearchString[i];
QPushButton* button = new QPushButton(label, this);
button->setFixedSize(50, 50);
button->setFocusPolicy(Qt::NoFocus);
leftSectionLayout->addWidget(button);
connect(button, &QPushButton::clicked, this, [this, searchString] {
m_model.setEffectTypeFilter(searchString);
updateSelection();
});
}
leftSectionLayout->addStretch();// Add stretch to the button layout to push buttons to the top
mainLayout->addLayout(leftSectionLayout);
m_filterEdit = new QLineEdit(this);
connect(m_filterEdit, &QLineEdit::textChanged, this, [this](const QString &text) {
m_model.setFilterRegExp(QRegExp(text, Qt::CaseInsensitive));
});
connect(m_filterEdit, &QLineEdit::textChanged, this, &EffectSelectDialog::updateSelection);
m_filterEdit->setFocus();
m_filterEdit->setFocusPolicy(Qt::StrongFocus);
m_filterEdit->setPlaceholderText(tr("Search"));
m_filterEdit->setClearButtonEnabled(true);
m_filterEdit->addAction(embed::getIconPixmap("zoom"), QLineEdit::LeadingPosition);
m_pluginList->setModel(&m_model);
m_pluginList->setEditTriggers(QAbstractItemView::NoEditTriggers);
m_pluginList->setSelectionBehavior(QAbstractItemView::SelectRows);
m_pluginList->setSelectionMode(QAbstractItemView::SingleSelection);
m_pluginList->setSortingEnabled(true);
m_pluginList->sortByColumn(0, Qt::AscendingOrder); // Initial sort by column 0 (Name)
m_pluginList->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
m_pluginList->verticalHeader()->hide();
m_pluginList->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
m_pluginList->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
m_pluginList->setFocusPolicy(Qt::NoFocus);
// Scroll Area
m_scrollArea->setWidgetResizable(true);
QWidget* scrollAreaWidgetContents = new QWidget(m_scrollArea);
scrollAreaWidgetContents->setObjectName("scrollAreaWidgetContents");
m_scrollArea->setWidget(scrollAreaWidgetContents);
m_scrollArea->setMaximumHeight(180);
m_scrollArea->setFocusPolicy(Qt::NoFocus);
// Button Box
QDialogButtonBox* buttonBox = new QDialogButtonBox(Qt::Horizontal, this);
buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
buttonBox->setFocusPolicy(Qt::NoFocus);
connect(buttonBox, &QDialogButtonBox::accepted, this, &EffectSelectDialog::acceptSelection);
connect(buttonBox, &QDialogButtonBox::rejected, this, &EffectSelectDialog::reject);
QVBoxLayout* rightSectionLayout = new QVBoxLayout();
rightSectionLayout->addWidget(m_filterEdit);
rightSectionLayout->addWidget(m_pluginList);
rightSectionLayout->addWidget(m_scrollArea);
rightSectionLayout->addWidget(buttonBox);
mainLayout->addLayout(rightSectionLayout);
setLayout(mainLayout);
// setup selection model
auto selectionModel = new QItemSelectionModel(&m_model);
ui->pluginList->setSelectionModel( selectionModel );
connect( selectionModel, SIGNAL( currentRowChanged( const QModelIndex&,
const QModelIndex & ) ),
SLOT( rowChanged( const QModelIndex &, const QModelIndex& ) ) );
connect( ui->pluginList, SIGNAL( doubleClicked( const QModelIndex& ) ),
SLOT(acceptSelection()));
m_pluginList->setSelectionModel(selectionModel);
connect(selectionModel, &QItemSelectionModel::currentRowChanged,
this, &EffectSelectDialog::rowChanged);
// try to accept current selection when pressing "OK"
connect( ui->buttonBox, SIGNAL(accepted()),
this, SLOT(acceptSelection()));
ui->filterEdit->setClearButtonEnabled( true );
ui->pluginList->verticalHeader()->setSectionResizeMode(
QHeaderView::ResizeToContents );
ui->pluginList->verticalHeader()->hide();
ui->pluginList->horizontalHeader()->setSectionResizeMode( 0,
QHeaderView::Stretch );
ui->pluginList->horizontalHeader()->setSectionResizeMode( 1,
QHeaderView::ResizeToContents );
ui->pluginList->sortByColumn( 0, Qt::AscendingOrder );
connect(m_pluginList, &QTableView::doubleClicked,
this, &EffectSelectDialog::acceptSelection);
setModal(true);
installEventFilter(this);
updateSelection();
show();
}
EffectSelectDialog::~EffectSelectDialog()
{
delete ui;
}
Effect * EffectSelectDialog::instantiateSelectedPlugin( EffectChain * _parent )
Effect* EffectSelectDialog::instantiateSelectedPlugin(EffectChain* parent)
{
Effect* result = nullptr;
if(!m_currentSelection.name.isEmpty() && m_currentSelection.desc)
if (!m_currentSelection.name.isEmpty() && m_currentSelection.desc)
{
result = Effect::instantiate(m_currentSelection.desc->name,
_parent, &m_currentSelection);
result = Effect::instantiate(m_currentSelection.desc->name, parent, &m_currentSelection);
}
if(!result)
if (!result)
{
result = new DummyEffect(_parent, QDomElement());
result = new DummyEffect(parent, QDomElement());
}
return result;
}
void EffectSelectDialog::acceptSelection()
{
if( m_currentSelection.isValid() )
if (m_currentSelection.isValid())
{
accept();
}
}
void EffectSelectDialog::rowChanged( const QModelIndex & _idx,
const QModelIndex & )
void EffectSelectDialog::rowChanged(const QModelIndex& idx, const QModelIndex&)
{
delete m_descriptionWidget;
m_descriptionWidget = nullptr;
if( m_model.mapToSource( _idx ).row() < 0 )
if (m_model.mapToSource(idx).row() < 0)
{
// invalidate current selection
// Invalidate current selection
m_currentSelection = Plugin::Descriptor::SubPluginFeatures::Key();
}
else
{
m_currentSelection = m_effectKeys[m_model.mapToSource( _idx ).row()];
m_currentSelection = m_effectKeys[m_model.mapToSource(idx).row()];
}
if( m_currentSelection.desc )
if (m_currentSelection.desc)
{
m_descriptionWidget = new QWidget;
auto hbox = new QHBoxLayout(m_descriptionWidget);
hbox->setAlignment(Qt::AlignTop);
Plugin::Descriptor const & descriptor = *( m_currentSelection.desc );
Plugin::Descriptor const& descriptor = *(m_currentSelection.desc);
const PixmapLoader* pixLoa = m_currentSelection.logo();
if (pixLoa)
@@ -204,76 +238,88 @@ void EffectSelectDialog::rowChanged( const QModelIndex & _idx,
auto logoLabel = new QLabel(m_descriptionWidget);
logoLabel->setPixmap(pixLoa->pixmap());
logoLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
logoLabel->setMaximumSize(64, 64);
hbox->addWidget( logoLabel );
hbox->setAlignment( logoLabel, Qt::AlignTop);
hbox->addWidget(logoLabel);
}
auto textualInfoWidget = new QWidget(m_descriptionWidget);
hbox->addWidget(textualInfoWidget);
auto textWidgetLayout = new QVBoxLayout(textualInfoWidget);
textWidgetLayout->setContentsMargins(4, 4, 4, 4);
textWidgetLayout->setSpacing( 0 );
textWidgetLayout->setSpacing(8);
if ( m_currentSelection.desc->subPluginFeatures )
if (m_currentSelection.desc->subPluginFeatures)
{
auto subWidget = new QWidget(textualInfoWidget);
auto subLayout = new QVBoxLayout(subWidget);
subLayout->setContentsMargins(4, 4, 4, 4);
subLayout->setSpacing( 0 );
subLayout->setContentsMargins(0, 0, 0, 0);
subLayout->setSpacing(8);
m_currentSelection.desc->subPluginFeatures->
fillDescriptionWidget( subWidget, &m_currentSelection );
for( QWidget * w : subWidget->findChildren<QWidget *>() )
fillDescriptionWidget(subWidget, &m_currentSelection);
for (QWidget* w : subWidget->findChildren<QWidget*>())
{
if( w->parent() == subWidget )
if (w->parent() == subWidget)
{
subLayout->addWidget( w );
subLayout->addWidget(w);
subLayout->setAlignment(w, QFlags<Qt::AlignmentFlag>(Qt::AlignTop | Qt::AlignLeft));
}
}
textWidgetLayout->addWidget(subWidget);
}
else
{
auto label = new QLabel(m_descriptionWidget);
QString labelText = "<p><b>" + tr("Name") + ":</b> " + QString::fromUtf8(descriptor.displayName) + "</p>";
labelText += "<p><b>" + tr("Description") + ":</b> " + qApp->translate( "PluginBrowser", descriptor.description ) + "</p>";
labelText += "<p><b>" + tr("Description") + ":</b> " + qApp->translate("PluginBrowser", descriptor.description) + "</p>";
labelText += "<p><b>" + tr("Author") + ":</b> " + QString::fromUtf8(descriptor.author) + "</p>";
label->setText(labelText);
label->setWordWrap(true);
textWidgetLayout->addWidget(label);
}
ui->scrollArea->setWidget( m_descriptionWidget );
m_scrollArea->setWidget(m_descriptionWidget);
m_descriptionWidget->show();
}
}
void EffectSelectDialog::sortAgain()
{
ui->pluginList->setSortingEnabled( ui->pluginList->isSortingEnabled() );
}
void EffectSelectDialog::updateSelection()
{
// no valid selection anymore due to changed filter?
if( ui->pluginList->selectionModel()->selection().size() <= 0 )
// No valid selection anymore due to changed filter?
if (m_pluginList->selectionModel()->selection().size() <= 0)
{
// then select our first item
ui->pluginList->selectionModel()->select( m_model.index( 0, 0 ),
QItemSelectionModel::ClearAndSelect
| QItemSelectionModel::Rows );
rowChanged( m_model.index( 0, 0 ), QModelIndex() );
// Then select our first item
m_pluginList->selectionModel()->
select(m_model.index(0, 0), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
rowChanged(m_model.index(0, 0), QModelIndex());
}
}
bool EffectSelectDialog::eventFilter(QObject *obj, QEvent *event)
{
if (obj == this && event->type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down)
{
QItemSelectionModel* selectionModel = m_pluginList->selectionModel();
int currentRow = selectionModel->currentIndex().row();
int newRow = (keyEvent->key() == Qt::Key_Up) ? currentRow - 1 : currentRow + 1;
int rowCount = m_pluginList->model()->rowCount();
newRow = qBound(0, newRow, rowCount - 1);
selectionModel->setCurrentIndex(m_pluginList->model()->index(newRow, 0),
QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
m_pluginList->scrollTo(m_pluginList->model()->index(newRow, 0));
return true;
}
}
return QDialog::eventFilter(obj, event);
}
} // namespace lmms::gui

View File

@@ -1,109 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EffectSelectDialog</class>
<widget class="QDialog" name="EffectSelectDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>585</width>
<height>550</height>
</rect>
</property>
<property name="windowTitle">
<string>Add effect</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>10</number>
</property>
<item>
<widget class="QLineEdit" name="filterEdit"/>
</item>
<item>
<widget class="lmms::gui::RowTableView" name="pluginList">
<property name="minimumSize">
<size>
<width>500</width>
<height>250</height>
</size>
</property>
<property name="editTriggers">
<enum>QAbstractItemView::NoEditTriggers</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>497</width>
<height>109</height>
</rect>
</property>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>EffectSelectDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
<customwidgets>
<customwidget>
<class>RowTableView</class>
<extends>QTableView</extends>
<header>RowTableView.h</header>
</customwidget>
</customwidgets>
</ui>