amuse/Editor/StudioSetupWidget.cpp

1159 lines
35 KiB
C++

#include "StudioSetupWidget.hpp"
#include "amuse/EffectChorus.hpp"
#include "amuse/EffectDelay.hpp"
#include "amuse/EffectReverb.hpp"
#include <QPainter>
#include <QScrollBar>
#include <QStylePainter>
using namespace std::literals;
static const EffectIntrospection ReverbStdIntrospective =
{
amuse::EffectType::ReverbStd,
"Reverb Std"sv,
"Standard Reverb"sv,
{
{
EffectIntrospection::Field::Type::Float,
"Coloration"sv,
0.f, 1.f, 0.f
},
{
EffectIntrospection::Field::Type::Float,
"Mix"sv,
0.f, 1.f, 0.f
},
{
EffectIntrospection::Field::Type::Float,
"Time"sv,
0.01f, 10.f, 0.01f
},
{
EffectIntrospection::Field::Type::Float,
"Damping"sv,
0.f, 1.f, 0.f
},
{
EffectIntrospection::Field::Type::Float,
"Pre Delay"sv,
0.f, 0.1f, 0.f
}
}
};
typedef float (amuse::EffectReverbStd::*ReverbStdGetFunc)() const;
typedef void (amuse::EffectReverbStd::*ReverbStdSetFunc)(float);
static const ReverbStdGetFunc ReverbStdGetters[] =
{
&amuse::EffectReverbStd::getColoration,
&amuse::EffectReverbStd::getMix,
&amuse::EffectReverbStd::getTime,
&amuse::EffectReverbStd::getDamping,
&amuse::EffectReverbStd::getPreDelay
};
static const ReverbStdSetFunc ReverbStdSetters[] =
{
&amuse::EffectReverbStd::setColoration,
&amuse::EffectReverbStd::setMix,
&amuse::EffectReverbStd::setTime,
&amuse::EffectReverbStd::setDamping,
&amuse::EffectReverbStd::setPreDelay
};
static const EffectIntrospection ReverbHiIntrospective =
{
amuse::EffectType::ReverbHi,
"Reverb Hi"sv,
"High Reverb"sv,
{
{
EffectIntrospection::Field::Type::Float,
"Coloration"sv,
0.f, 1.f, 0.f
},
{
EffectIntrospection::Field::Type::Float,
"Mix"sv,
0.f, 1.f, 0.f
},
{
EffectIntrospection::Field::Type::Float,
"Time"sv,
0.01f, 10.f, 0.01f
},
{
EffectIntrospection::Field::Type::Float,
"Damping"sv,
0.f, 1.f, 0.f
},
{
EffectIntrospection::Field::Type::Float,
"Pre Delay"sv,
0.f, 0.1f, 0.f
},
{
EffectIntrospection::Field::Type::Float,
"Crosstalk"sv,
0.f, 1.f, 0.f
}
}
};
typedef float (amuse::EffectReverbHi::*ReverbHiGetFunc)() const;
typedef void (amuse::EffectReverbHi::*ReverbHiSetFunc)(float);
static const ReverbHiGetFunc ReverbHiGetters[] =
{
&amuse::EffectReverbHi::getColoration,
&amuse::EffectReverbHi::getMix,
&amuse::EffectReverbHi::getTime,
&amuse::EffectReverbHi::getDamping,
&amuse::EffectReverbHi::getPreDelay,
&amuse::EffectReverbHi::getCrosstalk
};
static const ReverbHiSetFunc ReverbHiSetters[] =
{
&amuse::EffectReverbHi::setColoration,
&amuse::EffectReverbHi::setMix,
&amuse::EffectReverbHi::setTime,
&amuse::EffectReverbHi::setDamping,
&amuse::EffectReverbHi::setPreDelay,
&amuse::EffectReverbHi::setCrosstalk
};
static const EffectIntrospection DelayIntrospective =
{
amuse::EffectType::Delay,
"Delay"sv,
"Delay"sv,
{
{
EffectIntrospection::Field::Type::UInt32x8,
"Delay"sv,
10, 5000, 10
},
{
EffectIntrospection::Field::Type::UInt32x8,
"Feedback"sv,
0, 100, 0
},
{
EffectIntrospection::Field::Type::UInt32x8,
"Output"sv,
0, 100, 0
},
}
};
typedef uint32_t (amuse::EffectDelay::*DelayGetFunc)(int) const;
typedef void (amuse::EffectDelay::*DelaySetFunc)(int, uint32_t);
static const DelayGetFunc DelayGetters[] =
{
&amuse::EffectDelay::getChanDelay,
&amuse::EffectDelay::getChanFeedback,
&amuse::EffectDelay::getChanOutput
};
static const DelaySetFunc DelaySetters[] =
{
&amuse::EffectDelay::setChanDelay,
&amuse::EffectDelay::setChanFeedback,
&amuse::EffectDelay::setChanOutput
};
static const EffectIntrospection ChorusIntrospective =
{
amuse::EffectType::Chorus,
"Chorus"sv,
"Chorus"sv,
{
{
EffectIntrospection::Field::Type::UInt32,
"Base Delay"sv,
5, 15, 5
},
{
EffectIntrospection::Field::Type::UInt32,
"Variation"sv,
0, 5, 0
},
{
EffectIntrospection::Field::Type::UInt32,
"Period"sv,
500, 10000, 500
},
}
};
typedef uint32_t (amuse::EffectChorus::*ChorusGetFunc)() const;
typedef void (amuse::EffectChorus::*ChorusSetFunc)(uint32_t);
static const ChorusGetFunc ChorusGetters[] =
{
&amuse::EffectChorus::getBaseDelay,
&amuse::EffectChorus::getVariation,
&amuse::EffectChorus::getPeriod
};
static const ChorusSetFunc ChorusSetters[] =
{
&amuse::EffectChorus::setBaseDelay,
&amuse::EffectChorus::setVariation,
&amuse::EffectChorus::setPeriod
};
static const EffectIntrospection* GetEffectIntrospection(amuse::EffectType type)
{
switch (type)
{
case amuse::EffectType::ReverbStd:
return &ReverbStdIntrospective;
case amuse::EffectType::ReverbHi:
return &ReverbHiIntrospective;
case amuse::EffectType::Delay:
return &DelayIntrospective;
case amuse::EffectType::Chorus:
return &ChorusIntrospective;
default:
return nullptr;
}
}
template <typename T>
static T GetEffectParm(const amuse::EffectBaseTypeless* effect, int idx, int chanIdx)
{
switch (effect->Isa())
{
case amuse::EffectType::ReverbStd:
return (static_cast<const amuse::EffectReverbStdImp<float>*>(effect)->*ReverbStdGetters[idx])();
case amuse::EffectType::ReverbHi:
return (static_cast<const amuse::EffectReverbHiImp<float>*>(effect)->*ReverbHiGetters[idx])();
case amuse::EffectType::Delay:
return (static_cast<const amuse::EffectDelayImp<float>*>(effect)->*DelayGetters[idx])(chanIdx);
case amuse::EffectType::Chorus:
return (static_cast<const amuse::EffectChorusImp<float>*>(effect)->*ChorusGetters[idx])();
default:
return 0.f;
}
}
template <typename T>
static void SetEffectParm(amuse::EffectBaseTypeless* effect, int idx, int chanIdx, T val)
{
switch (effect->Isa())
{
case amuse::EffectType::ReverbStd:
(static_cast<amuse::EffectReverbStdImp<float>*>(effect)->*ReverbStdSetters[idx])(val); break;
case amuse::EffectType::ReverbHi:
(static_cast<amuse::EffectReverbHiImp<float>*>(effect)->*ReverbHiSetters[idx])(val); break;
case amuse::EffectType::Delay:
(static_cast<amuse::EffectDelayImp<float>*>(effect)->*DelaySetters[idx])(chanIdx, val); break;
case amuse::EffectType::Chorus:
(static_cast<amuse::EffectChorusImp<float>*>(effect)->*ChorusSetters[idx])(val); break;
default:
break;
}
}
static const char* ChanNames[] =
{
QT_TRANSLATE_NOOP("Uint32X8Popup", "Front Left"),
QT_TRANSLATE_NOOP("Uint32X8Popup", "Front Right"),
QT_TRANSLATE_NOOP("Uint32X8Popup", "Rear Left"),
QT_TRANSLATE_NOOP("Uint32X8Popup", "Rear Right"),
QT_TRANSLATE_NOOP("Uint32X8Popup", "Front Center"),
QT_TRANSLATE_NOOP("Uint32X8Popup", "LFE"),
QT_TRANSLATE_NOOP("Uint32X8Popup", "Side Left"),
QT_TRANSLATE_NOOP("Uint32X8Popup", "Side Right")
};
Uint32X8Popup::Uint32X8Popup(int min, int max, QWidget* parent)
: QFrame(parent, Qt::Popup)
{
setAttribute(Qt::WA_WindowPropagation);
setAttribute(Qt::WA_X11NetWmWindowTypeCombo);
Uint32X8Button* combo = static_cast<Uint32X8Button*>(parent);
QStyleOptionComboBox opt = combo->comboStyleOption();
setFrameStyle(combo->style()->styleHint(QStyle::SH_ComboBox_PopupFrameStyle, &opt, combo));
QGridLayout* layout = new QGridLayout;
for (int i = 0; i < 8; ++i)
{
layout->addWidget(new QLabel(tr(ChanNames[i])), i, 0);
FieldSlider* slider = new FieldSlider(this);
m_sliders[i] = slider;
slider->setToolTip(QStringLiteral("[%1,%2]").arg(min).arg(max));
slider->setProperty("chanIdx", i);
slider->setRange(min, max);
connect(slider, SIGNAL(valueChanged(int)), this, SLOT(doValueChanged(int)));
layout->addWidget(slider, i, 1);
}
setLayout(layout);
}
void Uint32X8Popup::setValue(int chanIdx, int val)
{
m_sliders[chanIdx]->setValue(val);
}
void Uint32X8Popup::doValueChanged(int val)
{
FieldSlider* slider = static_cast<FieldSlider*>(sender());
int chanIdx = slider->property("chanIdx").toInt();
emit valueChanged(chanIdx, val);
}
Uint32X8Button::Uint32X8Button(int min, int max, QWidget* parent)
: QPushButton(parent), m_popup(new Uint32X8Popup(min, max, this))
{
connect(this, SIGNAL(pressed()), this, SLOT(onPressed()));
}
void Uint32X8Button::paintEvent(QPaintEvent*)
{
QStylePainter painter(this);
painter.setPen(palette().color(QPalette::Text));
// draw the combobox frame, focusrect and selected etc.
QStyleOptionComboBox opt = comboStyleOption();
painter.drawComplexControl(QStyle::CC_ComboBox, opt);
// draw the icon and text
painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
}
QStyleOptionComboBox Uint32X8Button::comboStyleOption() const
{
QStyleOptionComboBox opt;
opt.initFrom(this);
opt.editable = false;
opt.frame = true;
opt.currentText = tr("Channels");
return opt;
}
void Uint32X8Button::onPressed()
{
QPoint pt = parentWidget()->mapToGlobal(pos());
m_popup->move(pt.x(), pt.y());
m_popup->show();
}
EffectWidget::EffectWidget(QWidget* parent, amuse::EffectBaseTypeless* effect, amuse::EffectType type)
: QWidget(parent), m_titleLabel(this), m_deleteButton(this),
m_effect(effect), m_introspection(GetEffectIntrospection(type))
{
QFont titleFont = m_titleLabel.font();
titleFont.setWeight(QFont::Bold);
m_titleLabel.setFont(titleFont);
m_titleLabel.setForegroundRole(QPalette::Background);
m_titleLabel.setContentsMargins(46, 0, 0, 0);
m_titleLabel.setFixedHeight(20);
m_numberText.setTextOption(QTextOption(Qt::AlignRight));
m_numberText.setTextWidth(25);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_numberFont.setWeight(QFont::Bold);
m_numberFont.setStyleHint(QFont::Monospace);
m_numberFont.setPointSize(16);
setContentsMargins(QMargins());
setFixedHeight(100);
QVBoxLayout* mainLayout = new QVBoxLayout;
mainLayout->setContentsMargins(QMargins());
mainLayout->setSpacing(0);
QHBoxLayout* headLayout = new QHBoxLayout;
headLayout->setContentsMargins(QMargins());
headLayout->setSpacing(0);
headLayout->addWidget(&m_titleLabel);
m_deleteButton.setVisible(true);
connect(&m_deleteButton, SIGNAL(clicked(bool)), this, SLOT(deleteClicked()));
headLayout->addWidget(&m_deleteButton);
mainLayout->addLayout(headLayout);
mainLayout->addSpacing(8);
QGridLayout* layout = new QGridLayout;
layout->setSpacing(6);
layout->setContentsMargins(64, 0, 12, 12);
if (m_introspection)
{
m_titleLabel.setText(tr(m_introspection->m_name.data()));
m_titleLabel.setToolTip(tr(m_introspection->m_description.data()));
for (int f = 0; f < 7; ++f)
{
const EffectIntrospection::Field& field = m_introspection->m_fields[f];
if (!field.m_name.empty())
{
QString fieldName = tr(field.m_name.data());
layout->addWidget(new QLabel(fieldName, this), 0, f);
switch (field.m_tp)
{
case EffectIntrospection::Field::Type::UInt32:
{
FieldSlider* sb = new FieldSlider(this);
sb->setProperty("fieldIndex", f);
sb->setRange(int(field.m_min), int(field.m_max));
sb->setToolTip(QStringLiteral("[%1,%2]").arg(int(field.m_min)).arg(int(field.m_max)));
sb->setValue(GetEffectParm<uint32_t>(m_effect, f, 0));
connect(sb, SIGNAL(valueChanged(int)), this, SLOT(numChanged(int)));
layout->addWidget(sb, 1, f);
break;
}
case EffectIntrospection::Field::Type::UInt32x8:
{
Uint32X8Button* sb = new Uint32X8Button(int(field.m_min), int(field.m_max), this);
sb->popup()->setProperty("fieldIndex", f);
for (int i = 0; i < 8; ++i)
sb->popup()->setValue(i, GetEffectParm<uint32_t>(m_effect, f, i));
connect(sb->popup(), SIGNAL(valueChanged(int, int)), this, SLOT(chanNumChanged(int, int)));
layout->addWidget(sb, 1, f);
break;
}
case EffectIntrospection::Field::Type::Float:
{
FieldDoubleSlider* sb = new FieldDoubleSlider(this);
sb->setProperty("fieldIndex", f);
sb->setRange(field.m_min, field.m_max);
sb->setToolTip(QStringLiteral("[%1,%2]").arg(field.m_min).arg(field.m_max));
sb->setValue(GetEffectParm<float>(m_effect, f, 0));
connect(sb, SIGNAL(valueChanged(double)), this, SLOT(numChanged(double)));
layout->addWidget(sb, 1, f);
break;
}
default:
break;
}
}
}
}
mainLayout->addLayout(layout);
layout->setRowMinimumHeight(0, 22);
layout->setRowMinimumHeight(1, 22);
setLayout(mainLayout);
}
void EffectWidget::paintEvent(QPaintEvent* event)
{
/* Rounded frame */
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
QPoint points[] =
{
{1, 20},
{1, 99},
{width() - 1, 99},
{width() - 1, 1},
{20, 1},
{1, 20},
};
painter.setBrush(palette().brush(QPalette::Background));
painter.drawPolygon(points, 6);
painter.setPen(QPen(QColor(127, 127, 127), 2.0));
painter.drawPolyline(points, 6);
QPoint headPoints[] =
{
{1, 20},
{1, 55},
{35, 20},
{width() - 1, 20},
{width() - 1, 1},
{20, 1},
{1, 20}
};
painter.setBrush(QColor(127, 127, 127));
painter.drawPolygon(headPoints, 7);
painter.drawRect(17, 51, 32, 32);
QTransform rotate;
rotate.rotate(-45.0);
painter.setTransform(rotate);
painter.setFont(m_numberFont);
painter.setPen(palette().color(QPalette::Background));
painter.drawStaticText(-15, 10, m_numberText);
}
EffectWidget::EffectWidget(QWidget* parent, amuse::EffectBaseTypeless* cmd)
: EffectWidget(parent, cmd, cmd->Isa()) {}
EffectWidget::EffectWidget(QWidget* parent, amuse::EffectType type)
: EffectWidget(parent, nullptr, type) {}
void EffectWidget::numChanged(int value)
{
SetEffectParm<uint32_t>(m_effect, sender()->property("fieldIndex").toInt(), 0, value);
}
void EffectWidget::numChanged(double value)
{
SetEffectParm<float>(m_effect, sender()->property("fieldIndex").toInt(), 0, value);
}
void EffectWidget::chanNumChanged(int chanIdx, int value)
{
SetEffectParm<uint32_t>(m_effect, sender()->property("fieldIndex").toInt(), chanIdx, value);
}
void EffectWidget::deleteClicked()
{
if (m_index != -1)
if (EffectListing* listing = getParent())
listing->deleteEffect(m_index);
}
EffectListing* EffectWidget::getParent() const
{
return qobject_cast<EffectListing*>(parentWidget()->parentWidget());
}
void EffectWidget::setIndex(int index)
{
m_index = index;
m_numberText.setText(QString::number(index));
update();
}
void EffectWidgetContainer::animateOpen()
{
int newHeight = 200 + parentWidget()->layout()->spacing();
m_animation = new QPropertyAnimation(this, "minimumHeight");
m_animation->setDuration(abs(minimumHeight() - newHeight) * 4);
m_animation->setStartValue(minimumHeight());
m_animation->setEndValue(newHeight);
m_animation->setEasingCurve(QEasingCurve::InOutExpo);
connect(m_animation, SIGNAL(valueChanged(const QVariant&)), parentWidget(), SLOT(update()));
connect(m_animation, SIGNAL(destroyed(QObject*)), this, SLOT(animationDestroyed()));
m_animation->start(QAbstractAnimation::DeleteWhenStopped);
}
void EffectWidgetContainer::animateClosed()
{
m_animation = new QPropertyAnimation(this, "minimumHeight");
m_animation->setDuration(abs(minimumHeight() - 100) * 4);
m_animation->setStartValue(minimumHeight());
m_animation->setEndValue(100);
m_animation->setEasingCurve(QEasingCurve::InOutExpo);
connect(m_animation, SIGNAL(valueChanged(const QVariant&)), parentWidget(), SLOT(update()));
connect(m_animation, SIGNAL(destroyed(QObject*)), this, SLOT(animationDestroyed()));
m_animation->start(QAbstractAnimation::DeleteWhenStopped);
}
void EffectWidgetContainer::snapOpen()
{
if (m_animation)
m_animation->stop();
setMinimumHeight(200 + parentWidget()->layout()->spacing());
}
void EffectWidgetContainer::snapClosed()
{
if (m_animation)
m_animation->stop();
setMinimumHeight(100);
}
void EffectWidgetContainer::animationDestroyed()
{
m_animation = nullptr;
}
template <class..._Args>
EffectWidgetContainer::EffectWidgetContainer(QWidget* parent, _Args&&... args)
: QWidget(parent), m_effectWidget(new EffectWidget(this, std::forward<_Args>(args)...))
{
setMinimumHeight(100);
setContentsMargins(QMargins());
QVBoxLayout* outerLayout = new QVBoxLayout;
outerLayout->setAlignment(Qt::AlignBottom);
outerLayout->setContentsMargins(QMargins());
outerLayout->setSpacing(0);
outerLayout->addWidget(m_effectWidget);
setLayout(outerLayout);
}
void EffectListing::startAutoscroll(QWidget* source, QMouseEvent* event, int delta)
{
if (m_autoscrollTimer == -1)
m_autoscrollTimer = startTimer(50);
m_autoscrollDelta = delta;
m_autoscrollSource = source;
m_autoscrollEvent = *event;
}
void EffectListing::stopAutoscroll()
{
if (m_autoscrollTimer != -1)
{
killTimer(m_autoscrollTimer);
m_autoscrollTimer = -1;
}
m_autoscrollDelta = 0;
m_autoscrollSource = nullptr;
}
void EffectListing::timerEvent(QTimerEvent* event)
{
if (QScrollArea* scrollArea = qobject_cast<QScrollArea*>(parentWidget()->parentWidget()))
{
QScrollBar* bar = scrollArea->verticalScrollBar();
int oldValue = bar->value();
bar->setValue(oldValue + m_autoscrollDelta);
int valueDelta = bar->value() - oldValue;
if (valueDelta != 0)
{
if (m_autoscrollSource)
QApplication::sendEvent(m_autoscrollSource, &m_autoscrollEvent);
update();
}
}
}
bool EffectListing::beginDrag(EffectWidget* widget)
{
int origIdx = m_layout->indexOf(widget->parentWidget());
if (origIdx < 0 || origIdx >= m_layout->count() - 1)
return false;
if (origIdx < m_layout->count() - 1)
{
// Animate next item open
m_dragOpenIdx = origIdx;
if (EffectWidgetContainer* nextItem =
qobject_cast<EffectWidgetContainer*>(m_layout->itemAt(origIdx + 1)->widget()))
nextItem->snapOpen();
}
else
{
m_dragOpenIdx = -1;
}
m_origIdx = origIdx;
m_dragItem = m_layout->takeAt(origIdx);
m_dragItem->widget()->raise();
return true;
}
void EffectListing::endDrag()
{
int insertIdx;
if (m_dragOpenIdx != -1)
{
if (EffectWidgetContainer* prevItem =
qobject_cast<EffectWidgetContainer*>(m_layout->itemAt(m_dragOpenIdx)->widget()))
prevItem->snapClosed();
insertIdx = m_dragOpenIdx;
m_dragOpenIdx = -1;
}
else
{
insertIdx = m_layout->count() - 1;
}
if (m_prevDragOpen)
{
m_prevDragOpen->snapClosed();
m_prevDragOpen = nullptr;
}
if (m_origIdx != insertIdx)
std::swap(m_submix->getEffectStack()[m_origIdx], m_submix->getEffectStack()[insertIdx]);
m_layout->insertItem(insertIdx, m_dragItem);
m_dragItem = nullptr;
stopAutoscroll();
reindex();
}
void EffectListing::cancelDrag()
{
if (m_dragOpenIdx != -1)
{
if (EffectWidgetContainer* prevItem =
qobject_cast<EffectWidgetContainer*>(m_layout->itemAt(m_dragOpenIdx)->widget()))
prevItem->snapClosed();
m_dragOpenIdx = -1;
}
m_layout->insertItem(m_origIdx, m_dragItem);
if (m_prevDragOpen)
{
m_prevDragOpen->snapClosed();
m_prevDragOpen = nullptr;
}
m_dragItem = nullptr;
stopAutoscroll();
}
void EffectListing::_moveDrag(int hoverIdx, const QPoint& pt, QWidget* source, QMouseEvent* event)
{
QRect scrollVpRect = parentWidget()->parentWidget()->rect();
QPoint scrollVpPoint = mapTo(parentWidget()->parentWidget(), pt);
if (scrollVpRect.bottom() - scrollVpPoint.y() < 50)
startAutoscroll(source, event, 10); // Scroll Down
else if (scrollVpRect.top() - scrollVpPoint.y() > -50)
startAutoscroll(source, event, -10);
else
stopAutoscroll();
hoverIdx = std::max(0, std::min(hoverIdx, m_layout->count() - 1));
if (hoverIdx != m_dragOpenIdx)
{
if (m_dragOpenIdx != -1)
if (EffectWidgetContainer* prevItem =
qobject_cast<EffectWidgetContainer*>(m_layout->itemAt(m_dragOpenIdx)->widget()))
{
m_prevDragOpen = prevItem;
prevItem->animateClosed();
}
if (EffectWidgetContainer* nextItem =
qobject_cast<EffectWidgetContainer*>(m_layout->itemAt(hoverIdx)->widget()))
nextItem->animateOpen();
m_dragOpenIdx = hoverIdx;
}
update();
}
void EffectListing::moveDrag(EffectWidget* widget, const QPoint& pt, QWidget* source, QMouseEvent* event)
{
EffectWidgetContainer* container = static_cast<EffectWidgetContainer*>(widget->parentWidget());
int pitch = 100 + m_layout->spacing();
_moveDrag((container->pos().y() - m_layout->contentsMargins().top() + pitch / 2) / pitch, pt, source, event);
}
int EffectListing::moveInsertDrag(const QPoint& pt, QWidget* source, QMouseEvent* event)
{
int pitch = 100 + m_layout->spacing();
_moveDrag(pt.y() / pitch, pt, source, event);
return m_dragOpenIdx;
}
void EffectListing::insertDragout()
{
if (m_dragOpenIdx != -1)
{
if (EffectWidgetContainer* prevItem =
qobject_cast<EffectWidgetContainer*>(m_layout->itemAt(m_dragOpenIdx)->widget()))
{
m_prevDragOpen = prevItem;
prevItem->animateClosed();
}
m_dragOpenIdx = -1;
}
stopAutoscroll();
}
void EffectListing::insert(amuse::EffectType type, const QString& text)
{
int insertIdx;
if (m_dragOpenIdx != -1)
{
if (EffectWidgetContainer* prevItem =
qobject_cast<EffectWidgetContainer*>(m_layout->itemAt(m_dragOpenIdx)->widget()))
prevItem->snapClosed();
insertIdx = m_dragOpenIdx;
m_dragOpenIdx = -1;
}
else
{
insertIdx = m_layout->count() - 1;
}
if (m_prevDragOpen)
{
m_prevDragOpen->snapClosed();
m_prevDragOpen = nullptr;
}
std::unique_ptr<amuse::EffectBaseTypeless> newEffect;
switch (type)
{
case amuse::EffectType::ReverbStd:
newEffect = m_submix->_makeEffect<amuse::EffectReverbStd>(amuse::EffectReverbStdInfo{});
break;
case amuse::EffectType::ReverbHi:
newEffect = m_submix->_makeEffect<amuse::EffectReverbHi>(amuse::EffectReverbHiInfo{});
break;
case amuse::EffectType::Delay:
newEffect = m_submix->_makeEffect<amuse::EffectDelay>(amuse::EffectDelayInfo{});
break;
case amuse::EffectType::Chorus:
newEffect = m_submix->_makeEffect<amuse::EffectChorus>(amuse::EffectChorusInfo{});
break;
default:
break;
}
auto it = m_submix->getEffectStack().insert(m_submix->getEffectStack().begin() + insertIdx, std::move(newEffect));
m_layout->insertWidget(insertIdx, new EffectWidgetContainer(this, it->get()));
stopAutoscroll();
reindex();
}
void EffectListing::deleteEffect(int index)
{
QLayoutItem* item = m_layout->takeAt(index);
m_submix->getEffectStack().erase(m_submix->getEffectStack().begin() + index);
item->widget()->deleteLater();
delete item;
reindex();
}
void EffectListing::reindex()
{
for (int i = 0; i < m_layout->count() - 1; ++i)
if (EffectWidgetContainer* item =
qobject_cast<EffectWidgetContainer*>(m_layout->itemAt(i)->widget()))
item->m_effectWidget->setIndex(i);
}
void EffectListing::clear()
{
while (m_layout->count() > 2)
{
QLayoutItem* item = m_layout->takeAt(0);
item->widget()->deleteLater();
delete item;
}
}
bool EffectListing::loadData(amuse::Submix* submix)
{
m_submix = submix;
clear();
int i = 0;
for (auto& effect : submix->getEffectStack())
m_layout->insertWidget(i++, new EffectWidgetContainer(this, effect.get()));
reindex();
update();
return true;
}
void EffectListing::unloadData()
{
m_submix = nullptr;
clear();
reindex();
update();
}
EffectListing::EffectListing(QWidget* parent)
: QWidget(parent), m_layout(new QVBoxLayout)
{
m_layout->addStretch();
setLayout(m_layout);
reindex();
}
EffectCatalogueItem::EffectCatalogueItem(amuse::EffectType type, const QString& name,
const QString& doc, QWidget* parent)
: QWidget(parent), m_type(type), m_iconLab(this), m_label(name, this)
{
QHBoxLayout* layout = new QHBoxLayout;
QString iconPath = QStringLiteral(":/commands/%1.svg").arg(name);
if (QFile(iconPath).exists())
m_iconLab.setPixmap(QIcon(iconPath).pixmap(32, 32));
else
m_iconLab.setPixmap(QIcon(QStringLiteral(":/icons/IconOpen.svg")).pixmap(32, 32));
layout->addWidget(&m_iconLab);
layout->addWidget(&m_label);
layout->addStretch();
layout->setContentsMargins(QMargins());
setLayout(layout);
setToolTip(doc);
}
EffectCatalogueItem::EffectCatalogueItem(const EffectCatalogueItem& other, QWidget* parent)
: QWidget(parent), m_type(other.getType())
{
QHBoxLayout* layout = new QHBoxLayout;
QHBoxLayout* oldLayout = static_cast<QHBoxLayout*>(other.layout());
m_iconLab.setPixmap(*static_cast<QLabel*>(oldLayout->itemAt(0)->widget())->pixmap());
layout->addWidget(&m_iconLab);
m_label.setText(static_cast<QLabel*>(oldLayout->itemAt(1)->widget())->text());
layout->addWidget(&m_label);
layout->addStretch();
layout->setContentsMargins(QMargins());
setLayout(layout);
}
static const char* EffectStrings[] =
{
QT_TRANSLATE_NOOP("EffectCatalogue", "Reverb Standard"),
QT_TRANSLATE_NOOP("EffectCatalogue", "Reverb High"),
QT_TRANSLATE_NOOP("EffectCatalogue", "Delay"),
QT_TRANSLATE_NOOP("EffectCatalogue", "Chorus")
};
static const char* EffectDocStrings[] =
{
QT_TRANSLATE_NOOP("EffectCatalogue", "Reverb Standard"),
QT_TRANSLATE_NOOP("EffectCatalogue", "Reverb High"),
QT_TRANSLATE_NOOP("EffectCatalogue", "Delay"),
QT_TRANSLATE_NOOP("EffectCatalogue", "Chorus")
};
EffectCatalogue::EffectCatalogue(QWidget* parent)
: QTreeWidget(parent)
{
setSelectionMode(QAbstractItemView::NoSelection);
setColumnCount(1);
setHeaderHidden(true);
for (int i = 1; i < int(amuse::EffectType::EffectTypeMAX); ++i)
{
QTreeWidgetItem* item = new QTreeWidgetItem(this);
setItemWidget(item, 0, new EffectCatalogueItem(amuse::EffectType(i),
tr(EffectStrings[i-1]), tr(EffectDocStrings[i-1]), this));
}
}
void EffectCatalogue::mousePressEvent(QMouseEvent* event)
{
QTreeWidget::mousePressEvent(event);
event->ignore();
}
void EffectCatalogue::mouseReleaseEvent(QMouseEvent* event)
{
QTreeWidget::mouseReleaseEvent(event);
event->ignore();
}
void EffectCatalogue::mouseMoveEvent(QMouseEvent* event)
{
StudioSetupWidget* editor = qobject_cast<StudioSetupWidget*>(parentWidget()->parentWidget()->parentWidget());
if (!editor || !editor->m_draggedItem)
QTreeWidget::mouseMoveEvent(event);
event->ignore();
}
EffectListing* StudioSetupWidget::getCurrentListing() const
{
return static_cast<EffectListing*>(static_cast<QScrollArea*>(m_tabs->currentWidget())->widget());
}
void StudioSetupWidget::beginCommandDrag(EffectWidget* widget, const QPoint& eventPt, const QPoint& pt)
{
if (widget->getParent()->beginDrag(widget))
{
m_draggedPt = pt;
m_draggedCmd = widget;
}
}
void StudioSetupWidget::beginCatalogueDrag(EffectCatalogueItem* item, const QPoint& eventPt, const QPoint& pt)
{
m_draggedPt = pt;
m_draggedItem = new EffectCatalogueItem(*item, this);
m_draggedItem->setGeometry(item->geometry());
m_draggedItem->move(eventPt - m_draggedPt);
m_draggedItem->raise();
m_draggedItem->show();
}
void StudioSetupWidget::mousePressEvent(QMouseEvent* event)
{
if (m_catalogue->geometry().contains(event->pos()))
{
QPoint fromParent1 = m_catalogue->mapFrom(this, event->pos());
QWidget* ch = m_catalogue->childAt(fromParent1);
if (ch)
{
EffectCatalogueItem* child = nullptr;
while (ch && !(child = qobject_cast<EffectCatalogueItem*>(ch)))
ch = ch->parentWidget();
if (child)
{
QPoint fromParent2 = child->mapFrom(m_catalogue, fromParent1);
beginCatalogueDrag(child, event->pos(), fromParent2);
}
}
}
else
{
EffectListing* listing = getCurrentListing();
if (listing->parentWidget()->parentWidget()->parentWidget()->geometry().contains(event->pos()))
{
QPoint fromParent1 = listing->mapFrom(this, event->pos());
QWidget* ch = listing->childAt(fromParent1);
if (ch)
{
EffectWidget* child = nullptr;
while (ch && !(child = qobject_cast<EffectWidget*>(ch)))
ch = ch->parentWidget();
if (child)
{
QPoint fromParent2 = child->mapFrom(listing, fromParent1);
beginCommandDrag(child, event->pos(), fromParent2);
}
}
}
}
}
void StudioSetupWidget::mouseReleaseEvent(QMouseEvent* event)
{
EffectListing* listing = getCurrentListing();
if (m_draggedItem)
{
amuse::EffectType type = m_draggedItem->getType();
QString text = m_draggedItem->getText();
m_draggedItem->deleteLater();
m_draggedItem = nullptr;
if (listing->parentWidget()->parentWidget()->parentWidget()->geometry().contains(event->pos()))
{
if (m_dragInsertIdx != -1)
listing->insert(type, text);
else
listing->insertDragout();
}
else
{
listing->insertDragout();
}
m_dragInsertIdx = -1;
}
else if (m_draggedCmd)
{
listing->endDrag();
m_draggedCmd = nullptr;
}
}
void StudioSetupWidget::mouseMoveEvent(QMouseEvent* event)
{
EffectListing* listing = getCurrentListing();
if (m_draggedItem)
{
m_draggedItem->move(event->pos() - m_draggedPt);
if (listing->parentWidget()->parentWidget()->parentWidget()->geometry().contains(event->pos()))
{
m_dragInsertIdx = listing->moveInsertDrag(listing->mapFrom(this, event->pos()), this, event);
}
else if (m_dragInsertIdx != -1)
{
listing->insertDragout();
m_dragInsertIdx = -1;
}
m_catalogue->update();
update();
}
else if (m_draggedCmd)
{
QPoint listingPt = listing->mapFrom(this, event->pos());
EffectWidgetContainer* container = static_cast<EffectWidgetContainer*>(m_draggedCmd->parentWidget());
container->move(container->x(), listingPt.y() - m_draggedPt.y());
if (listing->parentWidget()->parentWidget()->parentWidget()->geometry().contains(event->pos()))
listing->moveDrag(m_draggedCmd, listingPt, this, event);
listing->update();
update();
}
}
void StudioSetupWidget::keyPressEvent(QKeyEvent* event)
{
if (event->key() == Qt::Key_Escape)
{
EffectListing* listing = getCurrentListing();
if (m_draggedItem)
{
m_draggedItem->deleteLater();
m_draggedItem = nullptr;
listing->insertDragout();
}
else if (m_draggedCmd)
{
listing->cancelDrag();
m_draggedCmd = nullptr;
}
}
}
void StudioSetupWidget::hideEvent(QHideEvent* event)
{
emit hidden();
}
void StudioSetupWidget::showEvent(QShowEvent* event)
{
emit shown();
}
void StudioSetupWidget::updateWindowPosition()
{
QWidget* parent = parentWidget();
move(parent->width() / 2 - width() / 2 + parent->x(),
parent->height() / 2 - height() / 2 + parent->y());
}
void StudioSetupWidget::catalogueDoubleClicked(QTreeWidgetItem* item, int column)
{
if (EffectCatalogueItem* cItem = qobject_cast<EffectCatalogueItem*>(m_catalogue->itemWidget(item, column)))
{
amuse::EffectType type = cItem->getType();
if (type != amuse::EffectType::Invalid)
{
EffectListing* listing = getCurrentListing();
listing->insert(type, cItem->getText());
}
}
}
bool StudioSetupWidget::loadData(amuse::Studio* studio)
{
m_listing[0]->loadData(&studio->getAuxA());
m_listing[1]->loadData(&studio->getAuxB());
return true;
}
void StudioSetupWidget::unloadData()
{
m_listing[0]->unloadData();
m_listing[1]->unloadData();
}
StudioSetupWidget::StudioSetupWidget(QWidget* parent)
: QWidget(parent, Qt::Tool), m_splitter(new QSplitter), m_tabs(new QTabWidget), m_catalogue(new EffectCatalogue)
{
setWindowTitle(tr("Studio Setup"));
setGeometry(0, 0, 900, 450);
QScrollArea* scrollAreas[2];
for (int i = 0; i < 2; ++i)
{
m_listing[i] = new EffectListing;
QScrollArea* listingScroll = new QScrollArea;
scrollAreas[i] = listingScroll;
listingScroll->setWidget(m_listing[i]);
listingScroll->setWidgetResizable(true);
QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
sizePolicy.setHorizontalStretch(1);
sizePolicy.setVerticalStretch(0);
listingScroll->setSizePolicy(sizePolicy);
listingScroll->setMinimumWidth(350);
m_splitter->addWidget(listingScroll);
}
m_tabs->addTab(scrollAreas[0], tr("Aux A"));
m_tabs->addTab(scrollAreas[1], tr("Aux B"));
m_splitter->addWidget(m_tabs);
{
QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
sizePolicy.setHorizontalStretch(0);
sizePolicy.setVerticalStretch(0);
m_catalogue->setSizePolicy(sizePolicy);
m_catalogue->setMinimumWidth(150);
m_catalogue->setGeometry(0, 0, 215, 0);
m_catalogue->setMaximumWidth(300);
m_splitter->addWidget(m_catalogue);
}
connect(m_catalogue, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)),
this, SLOT(catalogueDoubleClicked(QTreeWidgetItem*, int)));
m_splitter->setCollapsible(0, false);
QGridLayout* layout = new QGridLayout;
layout->setContentsMargins(QMargins());
layout->addWidget(m_splitter);
setLayout(layout);
}