153 lines
5.3 KiB
C++
153 lines
5.3 KiB
C++
#include "sampleselectorgadget.h"
|
|
using Xybrid::UI::SampleSelectorGadget;
|
|
|
|
#include "data/project.h"
|
|
#include "data/sample.h"
|
|
using namespace Xybrid::Data;
|
|
|
|
#include "ui/directorynode.h"
|
|
#include "ui/waveformpreviewwidget.h"
|
|
using namespace Xybrid::UI;
|
|
|
|
#include "config/colorscheme.h"
|
|
|
|
#include "util/strings.h"
|
|
|
|
#include <cmath>
|
|
#include <vector>
|
|
|
|
#include <QDebug>
|
|
|
|
#include <QPainter>
|
|
#include <QGraphicsSceneMouseEvent>
|
|
#include <QStyleOptionGraphicsItem>
|
|
#include <QMenu>
|
|
#include <QWidgetAction>
|
|
|
|
SampleSelectorGadget::SampleSelectorGadget(Data::Project* project, QGraphicsItem* parent) : Gadget(parent), project(project) {
|
|
setAcceptHoverEvents(true);
|
|
}
|
|
|
|
QRectF SampleSelectorGadget::boundingRect() const {
|
|
return {QPointF(0, 0), size};
|
|
}
|
|
|
|
namespace {
|
|
void paintChannel(QPainter* p, std::shared_ptr<Sample> smp, size_t ch, QRect rect) {
|
|
auto x = rect.x();
|
|
auto y = rect.y();
|
|
auto h = rect.height();
|
|
auto c = h / 2;
|
|
double scale = static_cast<double>(smp->length()) / static_cast<double>(rect.width());
|
|
for (int i = 0; i < rect.width(); i++) {
|
|
size_t sp1 = static_cast<size_t>(std::floor(static_cast<double>(i) * scale));
|
|
size_t sp2 = static_cast<size_t>(std::floor(static_cast<double>(i+1) * scale)); // would be - 1, but this makes sure adjacent pixels always share at least one value
|
|
auto v = smp->plotBetween(ch, sp1, sp2);
|
|
v[0] = std::clamp(v[0], -1.0f, 1.0f);
|
|
v[1] = std::clamp(v[1], -1.0f, 1.0f);
|
|
//qDebug() << "x" << i << "v" << v[1] << v[2];
|
|
p->drawLine(QPoint(x+i, static_cast<int>(y+c - (c * v[0]))), QPoint(x+i, static_cast<int>(y+c - (c * v[1]))));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void SampleSelectorGadget::paint(QPainter* p, const QStyleOptionGraphicsItem* opt, QWidget*) {
|
|
const qreal corner = 5;
|
|
auto r = boundingRect();
|
|
auto br = r; // border rect
|
|
bool hover = opt->state & QStyle::State_MouseOver;
|
|
|
|
QColor outline = QColor(31, 31, 31);
|
|
if (hover) outline = QColor(127, 127, 255);
|
|
|
|
p->setPen(QPen(Qt::NoPen));
|
|
p->setBrush(QBrush(Config::colorScheme.waveformBg));
|
|
p->drawRoundedRect(br, corner, corner);
|
|
|
|
r.adjust(2, 2, -2, -2);
|
|
//p->fillRect(r, Config::colorScheme.waveformBg);
|
|
|
|
QString name = qs("(no sample selected)");
|
|
|
|
// draw waveforms
|
|
//auto aa = p->renderHints() & QPainter::Antialiasing;
|
|
p->setRenderHint(QPainter::Antialiasing, false);
|
|
p->setPen(QPen(Config::colorScheme.waveformFgPrimary));
|
|
if (auto smp = currentSample.lock(); smp) {
|
|
name = smp->name;
|
|
auto chs = smp->numChannels();
|
|
if (chs == 1) paintChannel(p, smp, 0, r.toAlignedRect());
|
|
else if (chs == 2) {
|
|
auto cs = r.height() / 2;
|
|
auto cr = r.adjusted(0, 0, 0, -cs);
|
|
paintChannel(p, smp, 0, cr.toAlignedRect());
|
|
paintChannel(p, smp, 1, cr.translated(0, cs).toAlignedRect());
|
|
}
|
|
}
|
|
p->setRenderHint(QPainter::Antialiasing, true);
|
|
p->setPen(QPen(outline, 2));
|
|
p->setBrush(QBrush(Qt::NoBrush));
|
|
p->drawRoundedRect(br, corner, corner);
|
|
|
|
// draw label
|
|
QFont font("Arcon Rounded", 8);
|
|
QFontMetricsF fm(font);
|
|
name = fm.elidedText(name.section('/', -1, -1), Qt::ElideRight, static_cast<int>(r.width() - 4));
|
|
QPainterPath path;
|
|
path.addText(r.bottomLeft() + QPointF(2, -fm.descent()), font, name);
|
|
|
|
p->fillPath(path, QColor(0, 0, 0));
|
|
p->strokePath(path, QPen(QColor(0, 0, 0), 3));
|
|
p->fillPath(path, QColor(255, 255, 255));
|
|
}
|
|
|
|
void SampleSelectorGadget::buildSubmenu(DirectoryNode* dir, QMenu* menu) {
|
|
auto smp = currentSample.lock();
|
|
bool needSeparator = false;
|
|
for (auto c : qAsConst(dir->children)) {
|
|
if (c->isDirectory()) {
|
|
needSeparator = true;
|
|
buildSubmenu(c, menu->addMenu(c->name));
|
|
} else {
|
|
if (needSeparator) { menu->addSeparator(); needSeparator = false; }
|
|
auto s = project->samples[c->data.toUuid()];
|
|
auto wa = new QWidgetAction(menu);
|
|
auto wfp = new WaveformPreviewWidget(menu);
|
|
wa->setDefaultWidget(wfp);
|
|
wfp->showName = true;
|
|
wfp->highlightable = true;
|
|
wfp->showLoopPoints = false;
|
|
wfp->setSample(s);
|
|
wfp->setMinimumSize(192, 48);
|
|
menu->addAction(wa);
|
|
if (s == smp) wa->setDisabled(true);
|
|
else connect(wa, &QWidgetAction::triggered, this, [this, s] { setSample(s); });
|
|
}
|
|
}
|
|
}
|
|
|
|
void SampleSelectorGadget::mousePressEvent(QGraphicsSceneMouseEvent* e) {
|
|
if (!project) return;
|
|
|
|
if (e->button() == Qt::LeftButton) {
|
|
auto smp = currentSample.lock();
|
|
auto* m = new QMenu();
|
|
if (project->samples.empty()) m->addAction("(no samples in project)")->setDisabled(true);
|
|
else {
|
|
m->addAction("(no sample)", this, [this] { setSample(nullptr); });
|
|
m->addSeparator();
|
|
DirectoryNode root;
|
|
for (auto& s : qAsConst(project->samples)) root.placeData(s->name, s->uuid);
|
|
root.sortTree();
|
|
|
|
buildSubmenu(&root, m);
|
|
}
|
|
m->setAttribute(Qt::WA_DeleteOnClose);
|
|
m->popup(e->screenPos());
|
|
}
|
|
}
|
|
|
|
void SampleSelectorGadget::hoverEnterEvent(QGraphicsSceneHoverEvent*) { update(); }
|
|
void SampleSelectorGadget::hoverLeaveEvent(QGraphicsSceneHoverEvent*) { update(); }
|