working mixer board, proper tool tip system, bunch of misc UI stuffs

portability/boost
zetaPRIME 2019-06-30 00:04:39 -04:00
parent 0081b1de1e
commit c726d53687
21 changed files with 308 additions and 62 deletions

View File

@ -29,8 +29,8 @@ namespace Xybrid::Data {
r *= m;
}
static AudioFrame gainBalanceMult(double gain, double balance);
inline AudioFrame gainBalance(double gain, double balance) const { return *this*gainBalanceMult(gain, balance); }
static AudioFrame gainBalanceMult(double gain, double balance = 0.0);
inline AudioFrame gainBalance(double gain, double balance = 0.0) const { return *this*gainBalanceMult(gain, balance); }
};
struct StorageFrame {

View File

@ -17,6 +17,7 @@ using namespace Xybrid::Audio;
#include "ui/patchboard/nodeobject.h"
#include "ui/gadgets/layoutgadget.h"
#include "ui/gadgets/buttongadget.h"
#include "ui/gadgets/togglegadget.h"
#include "ui/gadgets/knobgadget.h"
using namespace Xybrid::UI;
@ -48,16 +49,25 @@ void MixBoard::init() {
if (sections.empty()) insertSection(0);
}
void MixBoard::process() { // TODO: lerp from tick to tick?
/*auto m = AudioFrame::MixBoardMult(gain.load(), balance.load());
auto in = std::static_pointer_cast<AudioPort>(port(Port::Input, Port::Audio, 0));
void MixBoard::process() {
auto out = std::static_pointer_cast<AudioPort>(port(Port::Output, Port::Audio, 0));
in->pull();
out->pull();
bool solo = false;
for (auto& s : sections) if (s.solo) { solo = true; break; }
size_t ts = audioEngine->curTickSize();
for (size_t s = 0; s < ts; s++) (*out)[s] = (*in)[s] * m;*/
for (size_t i = 0; i < sections.size(); i++) {
auto& s = sections[i];
if (s.mute || (solo && !s.solo)) continue; // muted
auto m = AudioFrame::gainBalanceMult(s.gain);
auto in = std::static_pointer_cast<AudioPort>(port(Port::Input, Port::Audio, static_cast<uint8_t>(i)));
in->pull();
for (size_t f = 0; f < ts; f++) (*out)[f] += (*in)[f] * m;
}
}
void MixBoard::saveData(QCborMap& m) const {
@ -144,6 +154,17 @@ void MixBoard::onGadgetCreated() {
for (size_t i = 0; i < count; i++) {
auto ln = new LayoutGadget(l);
auto tl = new LayoutGadget(ln, true);
auto mute = new ToggleGadget(tl);
mute->color = {255, 0, 0};
mute->text = "Mute";
mute->bind(sections[i].mute);
tl->addSpacer();
auto solo = new ToggleGadget(tl);
solo->color = {191, 191, 0};
solo->text = "Solo";
solo->bind(sections[i].solo);
auto gain = new KnobGadget(ln);
gain->setLabel("Gain");
gain->fText = [](double d) { return QString("%1dB").arg(d); };

View File

@ -2,16 +2,19 @@
using Xybrid::UI::ButtonGadget;
#include <QPainter>
#include <QFontMetricsF>
#include <QStyleOptionGraphicsItem>
#include <QGraphicsSceneMouseEvent>
#include <QCursor>
#include <QMenu>
namespace {
const QFont font("Arcon Rounded", 8);
}
ButtonGadget::ButtonGadget(QGraphicsItem *parent) : Gadget(parent) {
setAcceptHoverEvents(true);
font = QFont("Arcon Rounded", 8);
}
QRectF ButtonGadget::boundingRect() const {
@ -45,7 +48,7 @@ void ButtonGadget::paint(QPainter* p, const QStyleOptionGraphicsItem* opt, QWidg
p->setFont(font);
p->setBrush(QBrush(Qt::NoBrush));
p->setPen(QColor(255, 255, 255));
QFontMetrics f(font);
QFontMetricsF f(font);
auto t = f.elidedText(text, Qt::ElideRight, static_cast<int>(r.width() - 8));
p->drawText(QPointF(r.center().x() - f.width(t)/2, r.center().y() + f.ascent()/2), t);
}

View File

@ -13,7 +13,6 @@ namespace Xybrid::UI {
private:
QSizeF size = {96, 24};
QFont font;
bool pressed = false;
public:

View File

@ -1,6 +1,8 @@
#include "gadget.h"
using Xybrid::UI::Gadget;
#include "ui/patchboard/gadgetscene.h"
Gadget::Gadget(QGraphicsItem* parent) : QGraphicsObject(parent) { }
void Gadget::centerOn(const QPointF& newPos) {
@ -10,6 +12,11 @@ void Gadget::centerOn(const QPointF& newPos) {
if (auto nc = pos() + c; nc != newPos) setPos(newPos - (nc - newPos));
}
void Gadget::toolTip(const QString& s, const QPointF& pos, const QColor& color) {
auto gs = static_cast<GadgetScene*>(scene());
gs->toolTip(this, s, pos, color);
}
void Gadget::updateGeometry() { }
QRectF Gadget::boundingRect() const { return { }; }

View File

@ -12,6 +12,8 @@ namespace Xybrid::UI {
void centerOn(const QPointF&);
inline void centerOn(qreal x, qreal y) { centerOn(QPointF(x, y)); }
void toolTip(const QString& = { }, const QPointF& = {0, 0}, const QColor& = {255, 255, 255});
virtual void updateGeometry();
QRectF boundingRect() const override;

View File

@ -29,7 +29,7 @@ namespace Xybrid::UI {
};
std::function<double()> fGet = [] { return 0.0; };
std::function<void(double)> fSet;
std::function<void(double)> fSet = [](double) { };
std::function<QString(double)> fText = [](double d) { return QString::number(d); };
double min = 0.0;

View File

@ -0,0 +1,39 @@
#include "labelgadget.h"
using Xybrid::UI::LabelGadget;
#include <QFont>
#include <QFontMetricsF>
#include <QPainter>
namespace {
const QFont font("Arcon Rounded", 8);
}
LabelGadget::LabelGadget(QGraphicsItem* parent, const QString& t) : Gadget(parent) {
setText(t);
}
QRectF LabelGadget::boundingRect() const {
return {{ }, size};
}
void LabelGadget::paint(QPainter* p, const QStyleOptionGraphicsItem*, QWidget*) {
if (text.isEmpty()) return;
//p->fillRect(boundingRect(), {255, 0, 255, 127});
QFontMetricsF fm(font);
QPainterPath path;
path.addText({1, fm.ascent() + 1}, font, text);
auto oc = color.lighter(static_cast<int>(color.saturationF() * 20));
p->fillPath(path, oc);
p->strokePath(path, QPen(oc, 3));
p->fillPath(path, color);
}
void LabelGadget::setText(const QString& t) {
text = t;
QFontMetricsF fm(font);
size = {fm.width(text) + 2, fm.height() + 2};
update();
}

View File

@ -0,0 +1,23 @@
#pragma once
#include "ui/gadgets/gadget.h"
namespace Xybrid::UI {
class LabelGadget : public Gadget {
QSizeF size;
QString text;
QColor color = {255, 255, 255};
public:
LabelGadget(QGraphicsItem* parent = nullptr, const QString& = { });
~LabelGadget() override = default;
QRectF boundingRect() const override;
void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override;
void setText(const QString&);
inline void setColor(const QColor& c) { color = c; update(); }
};
}

View File

@ -36,6 +36,8 @@ QRectF LayoutGadget::orient(const QRectF& o) const {
}
LayoutGadget::LayoutGadget(QGraphicsItem* parent, bool vertical) : Gadget(parent), vertical(vertical) {
setFlag(GraphicsItemFlag::ItemClipsChildrenToShape, false);
setFlag(GraphicsItemFlag::ItemClipsToShape, false);
if (vertical) { // different defaults for vertical layouts
margin = 0;
spacing = 3;

View File

@ -93,7 +93,7 @@ void SampleSelectorGadget::paint(QPainter* p, const QStyleOptionGraphicsItem* op
// draw label
QFont font("Arcon Rounded", 8);
QFontMetrics fm(font);
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);

View File

@ -0,0 +1,77 @@
#include "togglegadget.h"
using Xybrid::UI::ToggleGadget;
#include <QPainter>
#include <QStyleOptionGraphicsItem>
#include <QGraphicsSceneMouseEvent>
#include <QCursor>
#include <QMenu>
ToggleGadget::ToggleGadget(QGraphicsItem *parent) : Gadget(parent) {
setAcceptHoverEvents(true);
font = QFont("Arcon Rounded", 8);
setFlag(GraphicsItemFlag::ItemClipsToShape, false);
}
QRectF ToggleGadget::boundingRect() const {
return QRectF(QPointF(0, 0), QSizeF(16, 16));
}
void ToggleGadget::paint(QPainter* p, const QStyleOptionGraphicsItem* opt, QWidget*) {
auto r = boundingRect();
//p->fillRect(rect, QColor(255, 0, 0));
bool hover = opt->state & QStyle::State_MouseOver;
auto c = color;
if (!fGet()) c = c.darker();
QColor outline = QColor(31, 31, 31);
QRadialGradient fill(r.center(), r.height()/2, QPointF(r.center().x(), r.top()));
if (pressed) {
auto c = color.darker(300);
fill.setFocalPoint(fill.center());
fill.setColorAt(0.0, c.darker());
fill.setColorAt(1.0, c);
} else {
fill.setColorAt(0.0, c);
if (hover) fill.setColorAt(0.0, c.lighter(125));
fill.setColorAt(1.0, c.darker());
}
p->setRenderHint(QPainter::RenderHint::Antialiasing);
p->setBrush(QBrush(fill));
p->setPen(QPen(QBrush(outline), 2));
p->drawEllipse(r);
if (hover) toolTip(text, {0, -1}, color);
else toolTip();
}
void ToggleGadget::mousePressEvent(QGraphicsSceneMouseEvent* e) {
if (e->button() == Qt::LeftButton) {
pressed = true;
update();
}
}
void ToggleGadget::mouseReleaseEvent(QGraphicsSceneMouseEvent* e) {
if (e->button() == Qt::LeftButton) {
if (pressed) {
fSet(!fGet());
}
pressed = false;
update();
}
}
void ToggleGadget::hoverEnterEvent(QGraphicsSceneHoverEvent*) { update(); }
void ToggleGadget::hoverLeaveEvent(QGraphicsSceneHoverEvent*) { pressed = false; update(); }
void ToggleGadget::bind(bool& b) {
auto p = &b;
fGet = [p] { return *p; };
fSet = [p](bool b) { *p = b; };
}

View File

@ -0,0 +1,42 @@
#pragma once
#include "ui/gadgets/gadget.h"
#include <memory>
#include <functional>
#include <QFont>
namespace Xybrid::UI {
class ToggleGadget : public Gadget {
private:
QFont font;
bool pressed = false;
//QGraphicsSimpleTextItem* labelShadow = nullptr;
//QGraphicsSimpleTextItem* label = nullptr;
public:
QString text;
QColor color = QColor(127, 127, 127);
std::function<bool()> fGet = [] { return false; };
std::function<void(bool)> fSet = [](bool) { };
ToggleGadget(QGraphicsItem* parent = nullptr);
~ToggleGadget() override = default;
QRectF boundingRect() const override;
void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override;
void mousePressEvent(QGraphicsSceneMouseEvent*) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent*) override;
void hoverEnterEvent(QGraphicsSceneHoverEvent*) override;
void hoverLeaveEvent(QGraphicsSceneHoverEvent*) override;
void bind(bool&);
};
}

View File

@ -0,0 +1,37 @@
#include "gadgetscene.h"
using Xybrid::UI::GadgetScene;
#include "ui/gadgets/labelgadget.h"
GadgetScene::GadgetScene(QGraphicsView* view) : QGraphicsScene(view) {
this->view = view;
}
void GadgetScene::toolTip(QGraphicsItem* g, const QString& s, const QPointF& pos, const QColor& color) {
if (!g) return; // umm???
if (s.isEmpty()) {
if (toolTipSource && toolTipSource != g) return;
// remove existing tool tip
if (toolTipObject) toolTipObject->deleteLater();
toolTipObject = nullptr;
} else {
// create/set up
toolTipSource = g;
toolTipText = s;
if (!toolTipObject) {
toolTipObject = new LabelGadget();
addItem(toolTipObject);
toolTipObject->setZValue(10000);
}
auto r = g->boundingRect().translated(g->scenePos());
toolTipObject->setText(s);
toolTipObject->setColor(color);
constexpr double pad = 2.0;
auto tr = toolTipObject->boundingRect();
QPointF f = {(r.width() + tr.width()) / 2.0 + pad, (r.height() + tr.height()) / 2.0 + pad};
toolTipObject->centerOn(r.center() + QPointF(pos.x() * f.x(), pos.y() * f.y()));
}
}

View File

@ -0,0 +1,22 @@
#pragma once
#include <QGraphicsScene>
#include <QGraphicsView>
namespace Xybrid::UI {
class LabelGadget;
class GadgetScene : public QGraphicsScene {
LabelGadget* toolTipObject = nullptr;
QGraphicsItem* toolTipSource = nullptr;
QString toolTipText;
protected:
QGraphicsView* view;
public:
GadgetScene(QGraphicsView* view);
~GadgetScene() override = default;
void toolTip(QGraphicsItem*, const QString&, const QPointF&, const QColor&);
};
}

View File

@ -7,6 +7,10 @@ using Xybrid::Data::Port;
#include "fileops.h"
#include "ui/patchboard/gadgetscene.h"
#include "util/strings.h"
#include <cmath>
#include <QDebug>
@ -22,7 +26,6 @@ using Xybrid::Data::Port;
#include <QMessageBox>
#include <QInputDialog>
#include "util/strings.h"
namespace {
const QColor tcolor[] {
@ -54,33 +57,14 @@ void PortObject::connectTo(PortObject* o) {
void PortObject::setHighlighted(bool h, bool hideLabel) {
highlighted = h;
bool lv = h && !hideLabel;
if (lv) {
static QFont f = [] {
QFont f("Arcon Rounded", 8);
return f;
}();
QString txt = QString("%1 %2").arg(Util::enumName(port->dataType()).toLower()).arg(Util::hex(port->index));
if (!port->name.isEmpty()) txt = QString("%1 (%2)").arg(port->name).arg(txt);
QColor c = tcolor[port->dataType()];
label->setFont(f);
label->setText(txt);
label->setBrush(c);
labelShadow->setFont(f);
labelShadow->setText(txt);
labelShadow->setBrush(c.darker(400));
labelShadow->setPen(QPen(labelShadow->brush(), 2.5));
auto lbr = label->boundingRect();
if (port->type == Port::Input) label->setPos(QPointF(-lbr.width() - (portSize/2 + portSpacing), lbr.height() * -.5));
else label->setPos(QPointF(portSize/2 + portSpacing, lbr.height() * -.5));
auto lbsr = labelShadow->boundingRect();
labelShadow->setPos(label->pos() + (lbr.bottomRight() - lbsr.bottomRight()) / 2);
}
label->setVisible(lv);
labelShadow->setVisible(lv);
auto gs = static_cast<GadgetScene*>(scene());
if (h && !hideLabel) {
auto c = tcolor[port->dataType()];
auto txt = qs("%1 %2").arg(Util::enumName(port->dataType()).toLower()).arg(Util::hex(port->index));
if (!port->name.isEmpty()) txt = qs("%1 (%2)").arg(port->name).arg(txt);
double side = port->type == Port::Input ? -1.0 : 1.0;
gs->toolTip(this, txt, {side, 0}, c);
} else gs->toolTip(this, { }, { }, { });
update();
}
@ -92,11 +76,6 @@ PortObject::PortObject(const std::shared_ptr<Data::Port>& p) {
setAcceptedMouseButtons(Qt::LeftButton);
setFlag(QGraphicsItem::ItemSendsScenePositionChanges);
labelShadow = new QGraphicsSimpleTextItem(this);
labelShadow->setVisible(false);
label = new QGraphicsSimpleTextItem(this);
label->setVisible(false);
for (auto c : port->connections) {
if (auto cc = c.lock(); cc && cc->obj) connectTo(cc->obj);
}

View File

@ -12,8 +12,6 @@ namespace Xybrid::UI {
friend class PortConnectionObject;
std::shared_ptr<Data::Port> port;
QGraphicsSimpleTextItem* labelShadow;
QGraphicsSimpleTextItem* label;
bool highlighted = false;
std::unique_ptr<QGraphicsLineItem> dragLine;

View File

@ -19,7 +19,7 @@ using namespace Xybrid::Audio;
#include <QKeyEvent>
#include <QScrollBar>
NodeUIScene::NodeUIScene(QGraphicsView* view, const std::shared_ptr<Xybrid::Data::Node>& node) : QGraphicsScene(view), node(node), view(view) {
NodeUIScene::NodeUIScene(QGraphicsView* v, const std::shared_ptr<Xybrid::Data::Node>& node) : GadgetScene(v), node(node) {
setSceneRect(QRectF(QPointF(-9999,-9999), QPointF(9999, 9999))); // keep view anchored
node->initUI(this);
view->centerOn(itemsBoundingRect().center());

View File

@ -1,21 +1,19 @@
#pragma once
#include "ui/patchboard/gadgetscene.h"
#include <memory>
#include <unordered_map>
#include <QGraphicsScene>
#include <QGraphicsView>
class QShortcut;
namespace Xybrid::Data { class Node; }
namespace Xybrid::UI {
class NodeUIScene : public QGraphicsScene {
class NodeUIScene : public GadgetScene {
Q_OBJECT
std::shared_ptr<Data::Node> node;
QGraphicsView* view;
bool resizeQueued = false;

View File

@ -45,9 +45,8 @@ QShortcut* PatchboardScene::shortcut(QKeySequence s) {
return c;
}
PatchboardScene::PatchboardScene(QGraphicsView* parent, const std::shared_ptr<Xybrid::Data::Graph>& g) : QGraphicsScene(parent) {
PatchboardScene::PatchboardScene(QGraphicsView* parent, const std::shared_ptr<Xybrid::Data::Graph>& g) : GadgetScene(parent) {
graph = g;
view = parent;
connect(view->horizontalScrollBar(), &QScrollBar::valueChanged, this, &PatchboardScene::queueResize);
connect(view->verticalScrollBar(), &QScrollBar::valueChanged, this, &PatchboardScene::queueResize);

View File

@ -1,19 +1,17 @@
#pragma once
#include "ui/patchboard/gadgetscene.h"
#include <memory>
#include <unordered_map>
#include <QGraphicsScene>
#include <QGraphicsView>
class QShortcut;
namespace Xybrid::Data { class Graph; }
namespace Xybrid::UI {
class PatchboardScene : public QGraphicsScene {
class PatchboardScene : public GadgetScene {
std::shared_ptr<Data::Graph> graph;
QGraphicsView* view;
std::unordered_map<int, std::pair<int16_t, uint16_t>> previewKey;