Implemented loopstation preset model

This commit is contained in:
2022-12-28 00:09:51 +01:00
parent e2a2980c4f
commit 5613fc553b
11 changed files with 419 additions and 33 deletions

View File

@ -23,6 +23,7 @@ SOURCES += \
jsonconverters.cpp \
loopstationjsonconverters.cpp \
loopstationpresets.cpp \
loopstationpresetsmodel.cpp \
main.cpp \
midicontainers.cpp \
midiinwrapper.cpp \
@ -57,6 +58,7 @@ HEADERS += \
jsonconverters.h \
loopstationjsonconverters.h \
loopstationpresets.h \
loopstationpresetsmodel.h \
midicontainers.h \
midiinwrapper.h \
midioutwrapper.h \

View File

@ -222,3 +222,13 @@ void DrumMachineSettings::setDrumpadNote(quint8 pad, quint8 note)
{
setValue(QString{"drumpad/pad%0_note"}.arg(pad), note);
}
QString DrumMachineSettings::loopstationLastPresetId() const
{
return value("loopstation/lastPresetId").toString();
}
void DrumMachineSettings::setLoopstationLastPresetId(const QString &lastPresetId)
{
setValue("loopstation/lastPresetId", lastPresetId);
}

View File

@ -27,6 +27,8 @@ public:
QString lastMidiOutDevice() const;
void setLastMidiOutDevice(const QString &lastMidiOutDevice);
QString drumpadLastPresetId() const;
void setDrumpadLastPresetId(const QString &lastPresetId);
@ -64,4 +66,9 @@ public:
void setDrumpadChannel(quint8 pad, quint8 channel);
quint8 drumpadNote(quint8 pad) const;
void setDrumpadNote(quint8 pad, quint8 note);
QString loopstationLastPresetId() const;
void setLoopstationLastPresetId(const QString &lastPresetId);
};

View File

@ -37,49 +37,49 @@ DrumPadPresetsModel::DrumPadPresetsModel(QObject *parent) :
{
}
DrumPadPresetsModel::DrumPadPresetsModel(const std::map<QString, drumpad_presets::Preset> &drumpad_presets, QObject *parent) :
DrumPadPresetsModel::DrumPadPresetsModel(const std::map<QString, drumpad_presets::Preset> &presets, QObject *parent) :
QAbstractTableModel{parent}
{
m_drumpad_presets.reserve(std::size(drumpad_presets));
for (const auto &pair : drumpad_presets)
m_drumpad_presets.emplace_back(pair.second);
m_presets.reserve(std::size(presets));
for (const auto &pair : presets)
m_presets.emplace_back(pair.second);
}
DrumPadPresetsModel::DrumPadPresetsModel(std::vector<drumpad_presets::Preset> &&drumpad_presets, QObject *parent) :
DrumPadPresetsModel::DrumPadPresetsModel(std::vector<drumpad_presets::Preset> &&presets, QObject *parent) :
QAbstractTableModel{parent}
{
m_drumpad_presets = std::move(drumpad_presets);
m_presets = std::move(presets);
}
DrumPadPresetsModel::DrumPadPresetsModel(const std::vector<drumpad_presets::Preset> &drumpad_presets, QObject *parent) :
DrumPadPresetsModel::DrumPadPresetsModel(const std::vector<drumpad_presets::Preset> &presets, QObject *parent) :
QAbstractTableModel{parent}
{
m_drumpad_presets = drumpad_presets;
m_presets = presets;
}
DrumPadPresetsModel::~DrumPadPresetsModel() = default;
void DrumPadPresetsModel::setPresets(const std::map<QString, drumpad_presets::Preset> &drumpad_presets)
void DrumPadPresetsModel::setPresets(const std::map<QString, drumpad_presets::Preset> &presets)
{
beginResetModel();
m_drumpad_presets.clear();
m_drumpad_presets.reserve(std::size(drumpad_presets));
for (const auto &pair : drumpad_presets)
m_drumpad_presets.emplace_back(pair.second);
m_presets.clear();
m_presets.reserve(std::size(presets));
for (const auto &pair : presets)
m_presets.emplace_back(pair.second);
endResetModel();
}
void DrumPadPresetsModel::setPresets(std::vector<drumpad_presets::Preset> &&drumpad_presets)
void DrumPadPresetsModel::setPresets(std::vector<drumpad_presets::Preset> &&presets)
{
beginResetModel();
m_drumpad_presets = std::move(drumpad_presets);
m_presets = std::move(presets);
endResetModel();
}
void DrumPadPresetsModel::setPresets(const std::vector<drumpad_presets::Preset> &drumpad_presets)
void DrumPadPresetsModel::setPresets(const std::vector<drumpad_presets::Preset> &presets)
{
beginResetModel();
m_drumpad_presets = drumpad_presets;
m_presets = presets;
endResetModel();
}
@ -90,18 +90,18 @@ const drumpad_presets::Preset &DrumPadPresetsModel::getPreset(const QModelIndex
const drumpad_presets::Preset &DrumPadPresetsModel::getPreset(int row) const
{
Q_ASSERT(row >= 0 && row < int(std::size(m_drumpad_presets)));
return m_drumpad_presets.at(row);
Q_ASSERT(row >= 0 && row < int(std::size(m_presets)));
return m_presets.at(row);
}
QModelIndex DrumPadPresetsModel::findPresetById(const QString &id) const
{
for (auto iter = std::cbegin(m_drumpad_presets); iter != std::cend(m_drumpad_presets); iter++)
for (auto iter = std::cbegin(m_presets); iter != std::cend(m_presets); iter++)
{
if (iter->id != id)
continue;
return createIndex(std::distance(std::cbegin(m_drumpad_presets), iter), 0);
return createIndex(std::distance(std::cbegin(m_presets), iter), 0);
}
return {};
@ -111,7 +111,7 @@ int DrumPadPresetsModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return std::size(m_drumpad_presets);
return std::size(m_presets);
}
int DrumPadPresetsModel::columnCount(const QModelIndex &parent) const
@ -132,7 +132,7 @@ QVariant DrumPadPresetsModel::data(const QModelIndex &index, int role) const
return {};
if (index.row() < 0)
return {};
if (index.row() >= int(std::size(m_drumpad_presets)))
if (index.row() >= int(std::size(m_presets)))
return {};
const auto &preset = getPreset(index);

View File

@ -12,14 +12,14 @@ class DrumPadPresetsModel : public QAbstractTableModel
public:
DrumPadPresetsModel(QObject *parent = nullptr);
DrumPadPresetsModel(const std::map<QString, drumpad_presets::Preset> &drumpad_presets, QObject *parent = nullptr);
DrumPadPresetsModel(std::vector<drumpad_presets::Preset> &&drumpad_presets, QObject *parent = nullptr);
DrumPadPresetsModel(const std::vector<drumpad_presets::Preset> &drumpad_presets, QObject *parent = nullptr);
DrumPadPresetsModel(const std::map<QString, drumpad_presets::Preset> &presets, QObject *parent = nullptr);
DrumPadPresetsModel(std::vector<drumpad_presets::Preset> &&presets, QObject *parent = nullptr);
DrumPadPresetsModel(const std::vector<drumpad_presets::Preset> &presets, QObject *parent = nullptr);
~DrumPadPresetsModel() override;
void setPresets(const std::map<QString, drumpad_presets::Preset> &drumpad_presets);
void setPresets(std::vector<drumpad_presets::Preset> &&drumpad_presets);
void setPresets(const std::vector<drumpad_presets::Preset> &drumpad_presets);
void setPresets(const std::map<QString, drumpad_presets::Preset> &presets);
void setPresets(std::vector<drumpad_presets::Preset> &&presets);
void setPresets(const std::vector<drumpad_presets::Preset> &presets);
const drumpad_presets::Preset &getPreset(const QModelIndex &index) const;
const drumpad_presets::Preset &getPreset(int row) const;
@ -32,5 +32,5 @@ public:
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
private:
std::vector<drumpad_presets::Preset> m_drumpad_presets;
std::vector<drumpad_presets::Preset> m_presets;
};

242
loopstationpresetsmodel.cpp Normal file
View File

@ -0,0 +1,242 @@
#include "loopstationpresetsmodel.h"
#include <iterator>
#include <QFont>
#include <QColor>
#include "loopstationpresets.h"
enum {
ColumnId,
ColumnTitle,
ColumnAuthor,
ColumnBpm,
ColumnCoverUrl,
ColumnLoopLength,
ColumnOrderBy,
ColumnPremium,
ColumnTags,
ColumnAudioPreviewUrl,
ColumnDELETED,
NumberOfColumns
};
LoopStationPresetsModel::LoopStationPresetsModel(QObject *parent) :
QAbstractTableModel{parent}
{
}
LoopStationPresetsModel::LoopStationPresetsModel(const std::map<QString, loopstation_presets::Preset> &presets, QObject *parent) :
QAbstractTableModel{parent}
{
m_presets.reserve(std::size(presets));
for (const auto &pair : presets)
m_presets.emplace_back(pair.second);
}
LoopStationPresetsModel::LoopStationPresetsModel(std::vector<loopstation_presets::Preset> &&presets, QObject *parent) :
QAbstractTableModel{parent}
{
m_presets = std::move(presets);
}
LoopStationPresetsModel::LoopStationPresetsModel(const std::vector<loopstation_presets::Preset> &presets, QObject *parent) :
QAbstractTableModel{parent}
{
m_presets = presets;
}
LoopStationPresetsModel::~LoopStationPresetsModel() = default;
void LoopStationPresetsModel::setPresets(const std::map<QString, loopstation_presets::Preset> &presets)
{
beginResetModel();
m_presets.clear();
m_presets.reserve(std::size(presets));
for (const auto &pair : presets)
m_presets.emplace_back(pair.second);
endResetModel();
}
void LoopStationPresetsModel::setPresets(std::vector<loopstation_presets::Preset> &&presets)
{
beginResetModel();
m_presets = std::move(presets);
endResetModel();
}
void LoopStationPresetsModel::setPresets(const std::vector<loopstation_presets::Preset> &presets)
{
beginResetModel();
m_presets = presets;
endResetModel();
}
const loopstation_presets::Preset &LoopStationPresetsModel::getPreset(const QModelIndex &index) const
{
return getPreset(index.row());
}
const loopstation_presets::Preset &LoopStationPresetsModel::getPreset(int row) const
{
Q_ASSERT(row >= 0 && row < int(std::size(m_presets)));
return m_presets.at(row);
}
QModelIndex LoopStationPresetsModel::findPresetById(const QString &id) const
{
for (auto iter = std::cbegin(m_presets); iter != std::cend(m_presets); iter++)
{
if (iter->id != id)
continue;
return createIndex(std::distance(std::cbegin(m_presets), iter), 0);
}
return {};
}
int LoopStationPresetsModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return std::size(m_presets);
}
int LoopStationPresetsModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return NumberOfColumns;
}
QVariant LoopStationPresetsModel::data(const QModelIndex &index, int role) const
{
if (role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::FontRole && role != Qt::ForegroundRole)
return {};
if (index.column() < 0)
return {};
if (index.column() >= NumberOfColumns)
return {};
if (index.row() < 0)
return {};
if (index.row() >= int(std::size(m_presets)))
return {};
const auto &preset = getPreset(index);
const auto handleData = [&](const auto &val) -> QVariant
{
if (!val)
{
if (role == Qt::DisplayRole)
return this->tr("(null)");
else if (role == Qt::FontRole)
{
QFont font;
font.setItalic(true);
return font;
}
else if (role == Qt::ForegroundRole)
return QColor{Qt::gray};
return {};
}
if (role == Qt::DisplayRole || role == Qt::EditRole)
return *val;
return {};
};
const auto handleStringVectorData = [&](const auto &val) -> QVariant
{
if (!val)
{
if (role == Qt::DisplayRole)
return this->tr("(null)");
else if (role == Qt::FontRole)
{
QFont font;
font.setItalic(true);
return font;
}
else if (role == Qt::ForegroundRole)
return QColor{Qt::gray};
return {};
}
if (role == Qt::DisplayRole || role == Qt::EditRole)
{
QString text;
for (auto iter = std::cbegin(*val); iter != std::cend(*val); iter++)
{
if (iter != std::cbegin(*val))
text += ", ";
text += *iter;
}
return text;
}
return {};
};
switch (index.column())
{
case ColumnId:
{
if (preset.id)
{
bool ok;
if (auto id = preset.id->toInt(&ok); ok)
return id;
}
return handleData(preset.id);
}
case ColumnTitle: return handleData(preset.title);
case ColumnAuthor: return handleData(preset.author);
case ColumnBpm: return handleData(preset.bpm);
case ColumnCoverUrl: return handleData(preset.coverUrl);
case ColumnLoopLength: return handleData(preset.loopLength);
case ColumnOrderBy: return handleData(preset.orderBy);
case ColumnPremium: return handleData(preset.premium);
case ColumnTags: return handleStringVectorData(preset.tags);
case ColumnAudioPreviewUrl: return handleData(preset.audioPreviewUrl);
case ColumnDELETED: return handleData(preset.DELETED);
}
Q_UNREACHABLE();
}
QVariant LoopStationPresetsModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole && role != Qt::EditRole)
return {};
if (orientation != Qt::Horizontal)
return {};
if (section < 0)
return {};
if (section >= NumberOfColumns)
return {};
switch (section)
{
case ColumnId: return tr("id");
case ColumnTitle: return tr("title");
case ColumnAuthor: return tr("author");
case ColumnBpm: return tr("bpm");
case ColumnCoverUrl: return tr("coverUrl");
case ColumnLoopLength: return tr("loopLength");
case ColumnOrderBy: return tr("orderBy");
case ColumnPremium: return tr("premium");
case ColumnTags: return tr("tags");
case ColumnAudioPreviewUrl: return tr("audioPreviewUrl");
case ColumnDELETED: return tr("DELETED");
}
Q_UNREACHABLE();
}

36
loopstationpresetsmodel.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#include <vector>
#include <QAbstractTableModel>
namespace loopstation_presets { class Preset; }
class LoopStationPresetsModel : public QAbstractTableModel
{
Q_OBJECT
public:
LoopStationPresetsModel(QObject *parent = nullptr);
LoopStationPresetsModel(const std::map<QString, loopstation_presets::Preset> &presets, QObject *parent = nullptr);
LoopStationPresetsModel(std::vector<loopstation_presets::Preset> &&presets, QObject *parent = nullptr);
LoopStationPresetsModel(const std::vector<loopstation_presets::Preset> &presets, QObject *parent = nullptr);
~LoopStationPresetsModel() override;
void setPresets(const std::map<QString, loopstation_presets::Preset> &presets);
void setPresets(std::vector<loopstation_presets::Preset> &&presets);
void setPresets(const std::vector<loopstation_presets::Preset> &presets);
const loopstation_presets::Preset &getPreset(const QModelIndex &index) const;
const loopstation_presets::Preset &getPreset(int row) const;
QModelIndex findPresetById(const QString &id) const;
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
private:
std::vector<loopstation_presets::Preset> m_presets;
};

View File

@ -132,7 +132,13 @@ void DrumPadWidget::midiReceived(const midi::MidiMessage &message)
void DrumPadWidget::currentRowChanged(const QModelIndex &current)
{
if (!current.isValid())
{
if (m_settings)
m_settings->setDrumpadLastPresetId(QString{});
else
qWarning() << "no settings available";
return;
}
const auto &preset = m_presetsModel.getPreset(m_presetsProxyModel.mapToSource(current));

View File

@ -21,6 +21,17 @@ LoopStationWidget::LoopStationWidget(QWidget *parent) :
connect(m_ui->pushButtonUp, &QAbstractButton::pressed, this, &LoopStationWidget::selectPrevPreset);
connect(m_ui->pushButtonDown, &QAbstractButton::pressed, this, &LoopStationWidget::selectNextPreset);
connect(m_ui->pushButtonRefresh, &QAbstractButton::pressed, this, &LoopStationWidget::loadPresets);
m_presetsProxyModel.setFilterCaseSensitivity(Qt::CaseInsensitive);
m_presetsProxyModel.setSortRole(Qt::EditRole);
m_presetsProxyModel.setSourceModel(&m_presetsModel);
m_ui->presetsView->setModel(&m_presetsProxyModel);
m_presetsProxyModel.setFilterKeyColumn(1);
connect(m_ui->lineEdit, &QLineEdit::textChanged, &m_presetsProxyModel, &QSortFilterProxyModel::setFilterFixedString);
connect(m_ui->presetsView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &LoopStationWidget::currentRowChanged);
}
LoopStationWidget::~LoopStationWidget() = default;
@ -43,7 +54,7 @@ void LoopStationWidget::injectDecodingThread(QThread &thread)
void LoopStationWidget::loadSettings(DrumMachineSettings &settings)
{
m_settings = &settings;
}
void LoopStationWidget::unsendColors()
@ -61,6 +72,25 @@ void LoopStationWidget::midiReceived(const midi::MidiMessage &message)
}
void LoopStationWidget::currentRowChanged(const QModelIndex &current)
{
if (!current.isValid())
{
if (m_settings)
m_settings->setLoopstationLastPresetId(QString{});
else
qWarning() << "no settings available";
return;
}
const auto &preset = m_presetsModel.getPreset(m_presetsProxyModel.mapToSource(current));
if (m_settings)
m_settings->setLoopstationLastPresetId(preset.id ? *preset.id : QString{});
else
qWarning() << "no settings available";
}
void LoopStationWidget::loadPresets()
{
if (!m_networkAccessManager)
@ -101,7 +131,28 @@ void LoopStationWidget::requestFinished()
if (!result.presets)
throw std::runtime_error("presets missing in response");
// TODO
m_presetsModel.setPresets(std::move(*std::move(result).presets));
if (m_settings)
{
if (const auto &lastPresetId = m_settings->loopstationLastPresetId(); !lastPresetId.isEmpty())
{
if (const auto &index = m_presetsModel.findPresetById(lastPresetId); index.isValid())
selectIndex(m_presetsProxyModel.mapFromSource(index));
else
{
qWarning() << "invalid last preset id" << lastPresetId;
goto noLastId;
}
}
else
goto noLastId;
}
else
{
noLastId:
selectFirstPreset();
}
}
catch (const std::exception &e)
{
@ -123,3 +174,17 @@ void LoopStationWidget::selectNextPreset()
{
}
void LoopStationWidget::selectIndex(const QModelIndex &index)
{
if (!index.isValid())
{
qWarning() << "invalid index";
return;
}
m_ui->presetsView->scrollTo(index);
// m_ui->presetsView->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
m_ui->presetsView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
currentRowChanged(index);
}

View File

@ -1,9 +1,12 @@
#pragma once
#include <QSplitter>
#include <QSortFilterProxyModel>
#include <memory>
#include "loopstationpresetsmodel.h"
namespace Ui { class LoopStationWidget; }
namespace midi { struct MidiMessage; }
class QNetworkAccessManager;
@ -33,6 +36,7 @@ public slots:
void midiReceived(const midi::MidiMessage &message);
private slots:
void currentRowChanged(const QModelIndex &current);
void loadPresets();
void requestFinished();
void selectFirstPreset();
@ -40,8 +44,15 @@ private slots:
void selectNextPreset();
private:
void selectIndex(const QModelIndex &index);
const std::unique_ptr<Ui::LoopStationWidget> m_ui;
DrumMachineSettings *m_settings{};
LoopStationPresetsModel m_presetsModel;
QSortFilterProxyModel m_presetsProxyModel;
QNetworkAccessManager *m_networkAccessManager{};
std::unique_ptr<QNetworkReply> m_reply;

View File

@ -66,7 +66,14 @@
</layout>
</item>
<item>
<widget class="QTreeView" name="treeView"/>
<widget class="QTreeView" name="presetsView">
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>