Move some common code to new header files

This commit is contained in:
falkTX 2022-02-02 21:43:25 +00:00
parent 5c3367299e
commit 6bbdf9a63d
13 changed files with 561 additions and 551 deletions

View file

@ -1,6 +1,6 @@
/*
* DISTRHO Cardinal Plugin
* Copyright (C) 2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -16,6 +16,7 @@
*/
#include "plugincontext.hpp"
#include "ModuleWidgets.hpp"
#include "CarlaNativePlugin.h"
#include "CarlaBackendUtils.hpp"
@ -442,34 +443,23 @@ static intptr_t host_dispatcher(const NativeHostHandle handle, const NativeHostD
// --------------------------------------------------------------------------------------------------------------------
#ifndef HEADLESS
struct CarlaModuleWidget : ModuleWidget, IdleCallback {
static constexpr const float startX_In = 14.0f;
static constexpr const float startX_Out = 96.0f;
static constexpr const float startY = 74.0f;
static constexpr const float padding = 29.0f;
static constexpr const float middleX = startX_In + (startX_Out - startX_In) * 0.5f + padding * 0.35f;
struct CarlaModuleWidget : ModuleWidgetWith9HP, IdleCallback {
CarlaModule* const module;
bool idleCallbackActive = false;
bool visible = false;
CarlaModuleWidget(CarlaModule* const m)
: ModuleWidget(),
module(m)
: module(m)
{
setModule(module);
setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Carla.svg")));
addChild(createWidget<ScrewBlack>(Vec(RACK_GRID_WIDTH, 0)));
addChild(createWidget<ScrewBlack>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
addChild(createWidget<ScrewBlack>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addChild(createWidget<ScrewBlack>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
setSideScrews();
for (uint i=0; i<CarlaModule::NUM_INPUTS; ++i)
addInput(createInput<PJ301MPort>(Vec(startX_In, startY + padding * i), module, i));
createAndAddInput(i);
for (uint i=0; i<CarlaModule::NUM_OUTPUTS; ++i)
addOutput(createOutput<PJ301MPort>(Vec(startX_Out, startY + padding * i), module, i));
createAndAddOutput(i);
}
~CarlaModuleWidget() override
@ -565,30 +555,11 @@ struct CarlaModuleWidget : ModuleWidget, IdleCallback {
module->fCarlaPluginDescriptor->ui_idle(module->fCarlaPluginHandle);
}
void drawTextLine(NVGcontext* const vg, const uint offset, const char* const text)
{
const float y = startY + offset * padding;
nvgBeginPath(vg);
nvgFillColor(vg, color::WHITE);
nvgText(vg, middleX, y + 16, text, nullptr);
}
void draw(const DrawArgs& args) override
{
nvgBeginPath(args.vg);
nvgRect(args.vg, 0, 0, box.size.x, box.size.y);
nvgFillPaint(args.vg, nvgLinearGradient(args.vg, 0, 0, 0, box.size.y,
nvgRGB(0x18, 0x19, 0x19), nvgRGB(0x21, 0x22, 0x22)));
nvgFill(args.vg);
nvgFontFaceId(args.vg, 0);
nvgFontSize(args.vg, 11);
nvgTextAlign(args.vg, NVG_ALIGN_CENTER);
nvgBeginPath(args.vg);
nvgRoundedRect(args.vg, startX_Out - 2.5f, startY - 2.0f, padding, padding * CarlaModule::NUM_INPUTS, 4);
nvgFillColor(args.vg, nvgRGB(0xd0, 0xd0, 0xd0));
nvgFill(args.vg);
drawBackground(args.vg);
drawOutputJacksArea(args.vg, CarlaModule::NUM_INPUTS);
setupTextLines(args.vg);
drawTextLine(args.vg, 0, "Audio 1");
drawTextLine(args.vg, 1, "Audio 2");
@ -601,7 +572,7 @@ struct CarlaModuleWidget : ModuleWidget, IdleCallback {
drawTextLine(args.vg, 8, "CV 7");
drawTextLine(args.vg, 9, "CV 8");
ModuleWidget::draw(args);
ModuleWidgetWith9HP::draw(args);
}
void showUI()

View file

@ -16,192 +16,13 @@
*/
#include "plugincontext.hpp"
#include "ModuleWidgets.hpp"
#include "Widgets.hpp"
// -----------------------------------------------------------------------------------------------------------
USE_NAMESPACE_DISTRHO;
struct NanoKnob : Knob {
static const int ringSize = 4;
std::string displayLabel = "Level";
std::string displayString = "0 dB";
float normalizedValue = 0.5f;
NanoKnob()
{
box.size = Vec(100, 100);
}
void drawLayer(const DrawArgs& args, int layer) override
{
if (layer != 1)
return Knob::drawLayer(args, layer);
const float w = box.size.x;
const float h = box.size.y;
const int knobSize = std::min(w, h - BND_WIDGET_HEIGHT * 2) - ringSize;
const int knobStartX = w / 2 - knobSize / 2;
const int knobStartY = ringSize;
const int knobCenterX = knobStartX + knobSize / 2;
const int knobCenterY = knobStartY + knobSize / 2;
const NVGcolor testing = nvgRGBf(0.76f, 0.11f, 0.22f);
nvgLineCap(args.vg, NVG_ROUND);
// outer ring value
nvgBeginPath(args.vg);
nvgArc(args.vg,
knobCenterX,
knobCenterY,
knobSize / 2 + ringSize / 2 + 1,
nvgDegToRad(135.0f),
nvgDegToRad(135.0f) + nvgDegToRad(270.0f * normalizedValue),
NVG_CW);
nvgStrokeWidth(args.vg, ringSize);
nvgStrokeColor(args.vg, testing);
nvgStroke(args.vg);
// simulate color bleeding
nvgBeginPath(args.vg);
nvgArc(args.vg,
knobCenterX,
knobCenterY,
knobSize / 2 - 3,
nvgDegToRad(135.0f),
nvgDegToRad(135.0f) + nvgDegToRad(270.0f * normalizedValue),
NVG_CW);
nvgStrokeWidth(args.vg, 5);
nvgStrokeColor(args.vg, nvgRGBAf(testing.r, testing.g, testing.b, 0.1f));
nvgStroke(args.vg);
// line indicator
nvgStrokeWidth(args.vg, 2);
nvgSave(args.vg);
nvgTranslate(args.vg, knobCenterX, knobCenterY);
nvgRotate(args.vg, nvgDegToRad(45.0f) + normalizedValue * nvgDegToRad(270.0f));
nvgBeginPath(args.vg);
nvgRoundedRect(args.vg, -2, knobSize / 2 - 9, 2, 6, 1);
nvgClosePath(args.vg);
nvgFillColor(args.vg, nvgRGBf(1.0f, 1.0f, 1.0f));
nvgFill(args.vg);
nvgRestore(args.vg);
// adjusted from VCVRack's LightWidget.cpp
if (const float halo = settings::haloBrightness)
{
float radius = knobSize * 0.5f;
float oradius = radius + std::min(radius * 4.f, 15.f);
NVGcolor icol = color::mult(nvgRGBAf(testing.r, testing.g, testing.b, 0.2f), halo);
NVGcolor ocol = nvgRGBA(0, 0, 0, 0);
NVGpaint paint = nvgRadialGradient(args.vg, knobCenterX, knobCenterY, radius, oradius, icol, ocol);
nvgBeginPath(args.vg);
nvgRect(args.vg, knobCenterX - oradius, knobCenterY - oradius, 2 * oradius, 2 * oradius);
nvgFillPaint(args.vg, paint);
nvgFill(args.vg);
}
// bottom label (value)
bndIconLabelValue(args.vg, 0, knobSize + ringSize, w, BND_WIDGET_HEIGHT, -1,
testing, BND_CENTER,
BND_LABEL_FONT_SIZE, displayString.c_str(), nullptr);
Knob::drawLayer(args, layer);
}
void draw(const DrawArgs& args) override
{
if (engine::ParamQuantity* const pq = getParamQuantity())
normalizedValue = pq->getScaledValue();
const float w = box.size.x;
const float h = box.size.y;
const int knobSize = std::min(w, h - BND_WIDGET_HEIGHT * 2) - ringSize;
const int knobStartX = w / 2 - knobSize / 2;
const int knobStartY = ringSize;
const int knobCenterX = knobStartX + knobSize / 2;
const int knobCenterY = knobStartY + knobSize / 2;
// knob
NVGcolor shade_top;
NVGcolor shade_down;
BNDwidgetState state;
if (APP->event->getDraggedWidget() == this)
state = BND_ACTIVE;
else if (APP->event->getHoveredWidget() == this)
state = BND_HOVER;
else
state = BND_DEFAULT;
bndInnerColors(&shade_top, &shade_down, &bndGetTheme()->optionTheme, state, 0);
// inner fill
nvgBeginPath(args.vg);
nvgCircle(args.vg, knobCenterX, knobCenterY, knobSize / 2);
nvgFillPaint(args.vg, nvgLinearGradient(args.vg,
knobStartX,
knobStartY,
knobStartX,
knobStartY + knobSize,
shade_top,
shade_down));
nvgFill(args.vg);
// inner fill border (inner)
nvgBeginPath(args.vg);
nvgArc(args.vg, knobCenterX, knobCenterY, knobSize / 2 - 1, nvgDegToRad(0.0f), nvgDegToRad(360.0f), NVG_CCW);
nvgClosePath(args.vg);
nvgStrokeWidth(args.vg, 1);
nvgStrokeColor(args.vg, nvgRGBAf(0.5f, 0.5f, 0.5f, 0.4f));
nvgStroke(args.vg);
// inner fill border (outer)
nvgBeginPath(args.vg);
nvgArc(args.vg, knobCenterX, knobCenterY, knobSize / 2, nvgDegToRad(0.0f), nvgDegToRad(360.0f), NVG_CCW);
nvgClosePath(args.vg);
nvgStrokeWidth(args.vg, 1);
nvgStrokeColor(args.vg, nvgRGBAf(0.0f, 0.0f, 0.0f, 0.4f));
nvgStroke(args.vg);
nvgLineCap(args.vg, NVG_ROUND);
// outer ring background
nvgBeginPath(args.vg);
nvgArc(args.vg,
knobCenterX,
knobCenterY,
knobSize / 2 + ringSize / 2 + 1,
nvgDegToRad(135.0f),
nvgDegToRad(45.0f),
NVG_CW);
nvgStrokeWidth(args.vg, ringSize);
nvgStrokeColor(args.vg, nvgRGBf(0.5f, 0.5f, 0.5f));
nvgStroke(args.vg);
// bottom label (name)
bndIconLabelValue(args.vg, 0, knobStartY + knobSize + BND_WIDGET_HEIGHT * 0.75f, w, BND_WIDGET_HEIGHT, -1,
SCHEME_WHITE, BND_CENTER,
BND_LABEL_FONT_SIZE, displayLabel.c_str(), nullptr);
Knob::draw(args);
}
void onChange(const ChangeEvent&) override
{
engine::ParamQuantity* const pq = getParamQuantity();
DISTRHO_SAFE_ASSERT_RETURN(pq != nullptr,);
displayLabel = pq->getLabel();
displayString = pq->getDisplayValueString() + pq->getUnit();
}
};
/*
* Find the highest absolute and normalized value within a float array.
*/
@ -365,132 +186,26 @@ struct HostAudio : Module {
};
template<int numIO>
struct NanoMeter : Widget {
struct HostAudioNanoMeter : NanoMeter {
HostAudio<numIO>* const module;
float gainMeterL = 0.0f;
float gainMeterR = 0.0f;
NanoMeter(HostAudio<numIO>* const m)
: module(m)
{
box.size = Vec(100, 100);
}
HostAudioNanoMeter(HostAudio<numIO>* const m)
: module(m) {}
void drawLayer(const DrawArgs& args, int layer) override
void updateMeters() override
{
if (layer != 1)
if (module == nullptr || module->resetMeters)
return;
const float usableHeight = box.size.y - 10.0f;
// draw background
nvgBeginPath(args.vg);
nvgRect(args.vg,
0,
0,
box.size.x,
usableHeight);
nvgFillColor(args.vg, nvgRGB(26, 26, 26));
nvgFill(args.vg);
nvgFillColor(args.vg, nvgRGBAf(0.76f, 0.11f, 0.22f, 0.5f));
nvgStrokeColor(args.vg, nvgRGBf(0.76f, 0.11f, 0.22f));
if (module != nullptr)
{
// Only fetch new values once DSP side is updated
if (! module->resetMeters)
{
gainMeterL = module->gainMeterL;
gainMeterR = module->gainMeterR;
module->resetMeters = true;
}
const float heightL = 1.0f + std::sqrt(gainMeterL) * (usableHeight - 1.0f);
nvgBeginPath(args.vg);
nvgRect(args.vg, 0.0f, usableHeight - heightL, box.size.x * 0.5f - 1.0f, heightL);
nvgFill(args.vg);
nvgStroke(args.vg);
const float heightR = 1.0f + std::sqrt(gainMeterR) * (usableHeight - 1.0f);
nvgBeginPath(args.vg);
nvgRect(args.vg, box.size.x * 0.5f + 1.0f, usableHeight - heightR, box.size.x * 0.5f - 2.0f, heightR);
nvgFill(args.vg);
nvgStroke(args.vg);
}
nvgLineCap(args.vg, NVG_ROUND);
nvgBeginPath(args.vg);
nvgMoveTo(args.vg, 0.0f, usableHeight + 2.0f);
nvgLineTo(args.vg, box.size.x * 0.5f - 11.0f, usableHeight + 2.0f);
nvgStroke(args.vg);
nvgBeginPath(args.vg);
nvgMoveTo(args.vg, 0.0f, usableHeight + 4.0f);
nvgLineTo(args.vg, box.size.x * 0.5f - 16.0f, usableHeight + 4.0f);
nvgStroke(args.vg);
nvgBeginPath(args.vg);
nvgMoveTo(args.vg, 0.0f, usableHeight + 6.0f);
nvgLineTo(args.vg, box.size.x * 0.5f - 19.0f, usableHeight + 6.0f);
nvgStroke(args.vg);
nvgBeginPath(args.vg);
nvgMoveTo(args.vg, 0.0f, usableHeight + 8.0f);
nvgLineTo(args.vg, box.size.x * 0.5f - 22.0f, usableHeight + 8.0f);
nvgStroke(args.vg);
nvgBeginPath(args.vg);
nvgMoveTo(args.vg, 0.0f, usableHeight + 10.0f);
nvgLineTo(args.vg, box.size.x * 0.5f - 24.0f, usableHeight + 10.0f);
nvgStroke(args.vg);
nvgBeginPath(args.vg);
nvgMoveTo(args.vg, 0.0f, usableHeight + 12.0f);
nvgLineTo(args.vg, box.size.x * 0.5f - 26.0f, usableHeight + 12.0f);
nvgStroke(args.vg);
nvgBeginPath(args.vg);
nvgMoveTo(args.vg, box.size.x * 0.5f + 10.0f, usableHeight + 2.0f);
nvgLineTo(args.vg, box.size.x - 1.0f, usableHeight + 2.0f);
nvgStroke(args.vg);
nvgBeginPath(args.vg);
nvgMoveTo(args.vg, box.size.x * 0.5f + 15.0f, usableHeight + 4.0f);
nvgLineTo(args.vg, box.size.x - 1.0f, usableHeight + 4.0f);
nvgStroke(args.vg);
nvgBeginPath(args.vg);
nvgMoveTo(args.vg, box.size.x * 0.5f + 18.0f, usableHeight + 6.0f);
nvgLineTo(args.vg, box.size.x - 1.0f, usableHeight + 6.0f);
nvgStroke(args.vg);
nvgBeginPath(args.vg);
nvgMoveTo(args.vg, box.size.x * 0.5f + 20.0f, usableHeight + 8.0f);
nvgLineTo(args.vg, box.size.x - 1.0f, usableHeight + 8.0f);
nvgStroke(args.vg);
nvgBeginPath(args.vg);
nvgMoveTo(args.vg, box.size.x * 0.5f + 22.0f, usableHeight + 10.0f);
nvgLineTo(args.vg, box.size.x - 1.0f, usableHeight + 10.0f);
nvgStroke(args.vg);
nvgBeginPath(args.vg);
nvgMoveTo(args.vg, box.size.x * 0.5f + 24.0f, usableHeight + 12.0f);
nvgLineTo(args.vg, box.size.x - 1.0f, usableHeight + 12.0f);
nvgStroke(args.vg);
// Only fetch new values once DSP side is updated
gainMeterL = module->gainMeterL;
gainMeterR = module->gainMeterR;
module->resetMeters = true;
}
};
template<int numIO>
struct HostAudioWidget : ModuleWidget {
static constexpr const float startX_In = 14.0f;
static constexpr const float startX_Out = 81.0f;
static constexpr const float startY = 74.0f;
static constexpr const float padding = 29.0f;
static constexpr const float middleX = startX_In + (startX_Out - startX_In) * 0.5f + padding * 0.35f;
struct HostAudioWidget : ModuleWidgetWith8HP {
HostAudio<numIO>* const module;
HostAudioWidget(HostAudio<numIO>* const m)
@ -498,53 +213,30 @@ struct HostAudioWidget : ModuleWidget {
{
setModule(m);
setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/HostAudio.svg")));
addChild(createWidget<ScrewBlack>(Vec(RACK_GRID_WIDTH, 0)));
addChild(createWidget<ScrewBlack>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
addChild(createWidget<ScrewBlack>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addChild(createWidget<ScrewBlack>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
setSideScrews();
for (uint i=0; i<numIO; ++i)
addInput(createInput<PJ301MPort>(Vec(startX_In, startY + padding * i), m, i));
for (uint i=0; i<numIO; ++i)
addOutput(createOutput<PJ301MPort>(Vec(startX_Out, startY + padding * i), m, i));
{
createAndAddInput(i);
createAndAddOutput(i);
}
if (numIO == 2)
{
addParam(createParamCentered<NanoKnob>(Vec(middleX, 310.0f), m, 0));
NanoMeter<numIO>* const meter = new NanoMeter<numIO>(m);
HostAudioNanoMeter<numIO>* const meter = new HostAudioNanoMeter<numIO>(m);
meter->box.pos = Vec(middleX - padding + 2.75f, startY + padding * 2);
meter->box.size = Vec(padding * 2.0f - 4.0f, 136.0f);
addChild(meter);
}
}
void drawTextLine(NVGcontext* const vg, const uint posY, const char* const text)
{
const float y = startY + posY * padding;
nvgBeginPath(vg);
nvgFillColor(vg, color::WHITE);
nvgText(vg, middleX, y + 16, text, nullptr);
}
void draw(const DrawArgs& args) override
{
nvgBeginPath(args.vg);
nvgRect(args.vg, 0, 0, box.size.x, box.size.y);
nvgFillPaint(args.vg, nvgLinearGradient(args.vg, 0, 0, 0, box.size.y,
nvgRGB(0x18, 0x19, 0x19), nvgRGB(0x21, 0x22, 0x22)));
nvgFill(args.vg);
nvgFontFaceId(args.vg, 0);
nvgFontSize(args.vg, 11);
nvgTextAlign(args.vg, NVG_ALIGN_CENTER);
nvgBeginPath(args.vg);
nvgRoundedRect(args.vg, startX_Out - 2.5f, startY - 2.0f, padding, padding * numIO, 4);
nvgFillColor(args.vg, nvgRGB(0xd0, 0xd0, 0xd0));
nvgFill(args.vg);
drawBackground(args.vg);
drawOutputJacksArea(args.vg, numIO);
setupTextLines(args.vg);
if (numIO == 2)
{
@ -560,7 +252,7 @@ struct HostAudioWidget : ModuleWidget {
}
}
ModuleWidget::draw(args);
ModuleWidgetWith8HP::draw(args);
}
void appendContextMenu(Menu* const menu) override {

View file

@ -16,6 +16,7 @@
*/
#include "plugincontext.hpp"
#include "ModuleWidgets.hpp"
#define CARDINAL_AUDIO_IO_OFFSET 8
@ -98,54 +99,25 @@ struct HostCV : Module {
}
};
struct HostCVWidget : ModuleWidget {
static constexpr const float startX_In = 14.0f;
static constexpr const float startX_Out = 81.0f;
static constexpr const float startY = 74.0f;
static constexpr const float padding = 29.0f;
static constexpr const float middleX = startX_In + (startX_Out - startX_In) * 0.5f + padding * 0.35f;
struct HostCVWidget : ModuleWidgetWith8HP {
HostCVWidget(HostCV* const module)
{
setModule(module);
setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/HostCV.svg")));
addChild(createWidget<ScrewBlack>(Vec(RACK_GRID_WIDTH, 0)));
addChild(createWidget<ScrewBlack>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
addChild(createWidget<ScrewBlack>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addChild(createWidget<ScrewBlack>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
setSideScrews();
for (uint i=0; i<HostCV::NUM_INPUTS; ++i)
addInput(createInput<PJ301MPort>(Vec(startX_In, startY + padding * i), module, i));
createAndAddInput(i);
for (uint i=0; i<HostCV::NUM_OUTPUTS; ++i)
addOutput(createOutput<PJ301MPort>(Vec(startX_Out, startY + padding * i), module, i));
}
void drawTextLine(NVGcontext* const vg, const uint offset, const char* const text)
{
const float y = startY + offset * padding;
nvgBeginPath(vg);
nvgFillColor(vg, color::WHITE);
nvgText(vg, middleX, y + 16, text, nullptr);
createAndAddOutput(i);
}
void draw(const DrawArgs& args) override
{
nvgBeginPath(args.vg);
nvgRect(args.vg, 0, 0, box.size.x, box.size.y);
nvgFillPaint(args.vg, nvgLinearGradient(args.vg, 0, 0, 0, box.size.y,
nvgRGB(0x18, 0x19, 0x19), nvgRGB(0x21, 0x22, 0x22)));
nvgFill(args.vg);
nvgFontFaceId(args.vg, 0);
nvgFontSize(args.vg, 11);
nvgTextAlign(args.vg, NVG_ALIGN_CENTER);
nvgBeginPath(args.vg);
nvgRoundedRect(args.vg, startX_Out - 2.5f, startY - 2.0f, padding, padding * HostCV::NUM_INPUTS, 4);
nvgFillColor(args.vg, nvgRGB(0xd0, 0xd0, 0xd0));
nvgFill(args.vg);
drawBackground(args.vg);
drawOutputJacksArea(args.vg, HostCV::NUM_INPUTS);
setupTextLines(args.vg);
drawTextLine(args.vg, 0, "CV 1");
drawTextLine(args.vg, 1, "CV 2");
@ -158,7 +130,7 @@ struct HostCVWidget : ModuleWidget {
drawTextLine(args.vg, 8, "CV 9");
drawTextLine(args.vg, 9, "CV 10");
ModuleWidget::draw(args);
ModuleWidgetWith8HP::draw(args);
}
void appendContextMenu(ui::Menu* const menu) override

View file

@ -26,6 +26,7 @@
*/
#include "plugincontext.hpp"
#include "Widgets.hpp"
#include <algorithm>

View file

@ -26,6 +26,7 @@
*/
#include "plugincontext.hpp"
#include "Widgets.hpp"
#include <algorithm>

View file

@ -26,6 +26,7 @@
*/
#include "plugincontext.hpp"
#include "Widgets.hpp"
#include <algorithm>

View file

@ -26,6 +26,7 @@
*/
#include "plugincontext.hpp"
#include "ModuleWidgets.hpp"
#include <algorithm>
@ -661,13 +662,7 @@ struct HostMIDI : Module {
// --------------------------------------------------------------------------------------------------------------------
struct HostMIDIWidget : ModuleWidget {
static constexpr const float startX_In = 14.0f;
static constexpr const float startX_Out = 96.0f;
static constexpr const float startY = 74.0f;
static constexpr const float padding = 29.0f;
static constexpr const float middleX = startX_In + (startX_Out - startX_In) * 0.5f + padding * 0.35f;
struct HostMIDIWidget : ModuleWidgetWith9HP {
HostMIDI* const module;
HostMIDIWidget(HostMIDI* const m)
@ -675,57 +670,34 @@ struct HostMIDIWidget : ModuleWidget {
{
setModule(m);
setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/HostMIDI.svg")));
setSideScrews();
addChild(createWidget<ScrewBlack>(Vec(RACK_GRID_WIDTH, 0)));
addChild(createWidget<ScrewBlack>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
addChild(createWidget<ScrewBlack>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addChild(createWidget<ScrewBlack>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
createAndAddInput(0, HostMIDI::PITCH_INPUT);
createAndAddInput(1, HostMIDI::GATE_INPUT);
createAndAddInput(2, HostMIDI::VELOCITY_INPUT);
createAndAddInput(3, HostMIDI::AFTERTOUCH_INPUT);
createAndAddInput(4, HostMIDI::PITCHBEND_INPUT);
createAndAddInput(5, HostMIDI::MODWHEEL_INPUT);
createAndAddInput(6, HostMIDI::START_INPUT);
createAndAddInput(7, HostMIDI::STOP_INPUT);
createAndAddInput(8, HostMIDI::CONTINUE_INPUT);
addInput(createInput<PJ301MPort>(Vec(startX_In, startY + padding * 0), m, HostMIDI::PITCH_INPUT));
addInput(createInput<PJ301MPort>(Vec(startX_In, startY + padding * 1), m, HostMIDI::GATE_INPUT));
addInput(createInput<PJ301MPort>(Vec(startX_In, startY + padding * 2), m, HostMIDI::VELOCITY_INPUT));
addInput(createInput<PJ301MPort>(Vec(startX_In, startY + padding * 3), m, HostMIDI::AFTERTOUCH_INPUT));
addInput(createInput<PJ301MPort>(Vec(startX_In, startY + padding * 4), m, HostMIDI::PITCHBEND_INPUT));
addInput(createInput<PJ301MPort>(Vec(startX_In, startY + padding * 5), m, HostMIDI::MODWHEEL_INPUT));
addInput(createInput<PJ301MPort>(Vec(startX_In, startY + padding * 6), m, HostMIDI::START_INPUT));
addInput(createInput<PJ301MPort>(Vec(startX_In, startY + padding * 7), m, HostMIDI::STOP_INPUT));
addInput(createInput<PJ301MPort>(Vec(startX_In, startY + padding * 8), m, HostMIDI::CONTINUE_INPUT));
addOutput(createOutput<PJ301MPort>(Vec(startX_Out, startY + padding * 0), m, HostMIDI::PITCH_OUTPUT));
addOutput(createOutput<PJ301MPort>(Vec(startX_Out, startY + padding * 1), m, HostMIDI::GATE_OUTPUT));
addOutput(createOutput<PJ301MPort>(Vec(startX_Out, startY + padding * 2), m, HostMIDI::VELOCITY_OUTPUT));
addOutput(createOutput<PJ301MPort>(Vec(startX_Out, startY + padding * 3), m, HostMIDI::AFTERTOUCH_OUTPUT));
addOutput(createOutput<PJ301MPort>(Vec(startX_Out, startY + padding * 4), m, HostMIDI::PITCHBEND_OUTPUT));
addOutput(createOutput<PJ301MPort>(Vec(startX_Out, startY + padding * 5), m, HostMIDI::MODWHEEL_OUTPUT));
addOutput(createOutput<PJ301MPort>(Vec(startX_Out, startY + padding * 6), m, HostMIDI::START_OUTPUT));
addOutput(createOutput<PJ301MPort>(Vec(startX_Out, startY + padding * 7), m, HostMIDI::STOP_OUTPUT));
addOutput(createOutput<PJ301MPort>(Vec(startX_Out, startY + padding * 8), m, HostMIDI::CONTINUE_OUTPUT));
}
void drawTextLine(NVGcontext* const vg, const uint posY, const char* const text)
{
const float y = startY + posY * padding;
nvgBeginPath(vg);
nvgFillColor(vg, color::WHITE);
nvgText(vg, middleX, y + 16, text, nullptr);
createAndAddOutput(0, HostMIDI::PITCH_OUTPUT);
createAndAddOutput(1, HostMIDI::GATE_OUTPUT);
createAndAddOutput(2, HostMIDI::VELOCITY_OUTPUT);
createAndAddOutput(3, HostMIDI::AFTERTOUCH_OUTPUT);
createAndAddOutput(4, HostMIDI::PITCHBEND_OUTPUT);
createAndAddOutput(5, HostMIDI::MODWHEEL_OUTPUT);
createAndAddOutput(6, HostMIDI::START_OUTPUT);
createAndAddOutput(7, HostMIDI::STOP_OUTPUT);
createAndAddOutput(8, HostMIDI::CONTINUE_OUTPUT);
}
void draw(const DrawArgs& args) override
{
nvgBeginPath(args.vg);
nvgRect(args.vg, 0, 0, box.size.x, box.size.y);
nvgFillPaint(args.vg, nvgLinearGradient(args.vg, 0, 0, 0, box.size.y,
nvgRGB(0x18, 0x19, 0x19), nvgRGB(0x21, 0x22, 0x22)));
nvgFill(args.vg);
nvgFontFaceId(args.vg, 0);
nvgFontSize(args.vg, 11);
nvgTextAlign(args.vg, NVG_ALIGN_CENTER);
nvgBeginPath(args.vg);
nvgRoundedRect(args.vg, startX_Out - 2.5f, startY - 2.0f, padding, padding * 9, 4);
nvgFillColor(args.vg, nvgRGB(0xd0, 0xd0, 0xd0));
nvgFill(args.vg);
drawBackground(args.vg);
drawOutputJacksArea(args.vg, 9);
setupTextLines(args.vg);
drawTextLine(args.vg, 0, "V/Oct");
drawTextLine(args.vg, 1, "Gate");
@ -737,7 +709,7 @@ struct HostMIDIWidget : ModuleWidget {
drawTextLine(args.vg, 7, "Stop");
drawTextLine(args.vg, 8, "Cont");
ModuleWidget::draw(args);
ModuleWidgetWith9HP::draw(args);
}
void appendContextMenu(Menu* const menu) override

View file

@ -1,6 +1,6 @@
/*
* DISTRHO Cardinal Plugin
* Copyright (C) 2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as

View file

@ -17,7 +17,7 @@
#pragma once
#include "plugin.hpp"
#include "Widgets.hpp"
#include "DearImGui/imgui.h"
struct ImGuiWidget : OpenGlWidgetWithBrowserPreview {

View file

@ -0,0 +1,90 @@
/*
* DISTRHO Cardinal Plugin
* Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the LICENSE file.
*/
#pragma once
#include "rack.hpp"
#ifdef NDEBUG
# undef DEBUG
#endif
using namespace rack;
template<int startX_Out>
struct ModuleWidgetWithSideScrews : ModuleWidget {
static constexpr const float startX_In = 14.0f;
static constexpr const float startY = 74.0f;
static constexpr const float padding = 29.0f;
static constexpr const float middleX = startX_In + (startX_Out - startX_In) * 0.5f + padding * 0.35f;
void setSideScrews() {
addChild(createWidget<ScrewBlack>(Vec(RACK_GRID_WIDTH, 0)));
addChild(createWidget<ScrewBlack>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
addChild(createWidget<ScrewBlack>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addChild(createWidget<ScrewBlack>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
}
void setupTextLines(NVGcontext* const vg) {
nvgBeginPath(vg);
nvgRect(vg, startX_Out - 2.5f, startY - 2.0f, padding, padding);
nvgFontFaceId(vg, 0);
nvgFontSize(vg, 11);
nvgTextAlign(vg, NVG_ALIGN_CENTER);
}
void createAndAddInput(const uint paramId) {
createAndAddInput(paramId, paramId);
}
void createAndAddInput(const uint posY, const uint paramId) {
addInput(createInput<PJ301MPort>(Vec(startX_In, startY + padding * posY), module, paramId));
}
void createAndAddOutput(const uint paramId) {
createAndAddOutput(paramId, paramId);
}
void createAndAddOutput(const uint posY, const uint paramId) {
addOutput(createOutput<PJ301MPort>(Vec(startX_Out, startY + padding * posY), module, paramId));
}
void drawBackground(NVGcontext* const vg) {
nvgBeginPath(vg);
nvgRect(vg, 0, 0, box.size.x, box.size.y);
nvgFillPaint(vg, nvgLinearGradient(vg, 0, 0, 0, box.size.y,
nvgRGB(0x18, 0x19, 0x19), nvgRGB(0x21, 0x22, 0x22)));
nvgFill(vg);
}
void drawOutputJacksArea(NVGcontext* const vg, const int numOutputs) {
nvgBeginPath(vg);
nvgRoundedRect(vg, startX_Out - 2.5f, startY - 2.0f, padding, padding * numOutputs, 4);
nvgFillColor(vg, nvgRGB(0xd0, 0xd0, 0xd0));
nvgFill(vg);
}
void drawTextLine(NVGcontext* const vg, const uint posY, const char* const text) {
const float y = startY + posY * padding;
nvgBeginPath(vg);
nvgFillColor(vg, color::WHITE);
nvgText(vg, middleX, y + 16, text, nullptr);
}
};
typedef ModuleWidgetWithSideScrews<81> ModuleWidgetWith8HP;
typedef ModuleWidgetWithSideScrews<96> ModuleWidgetWith9HP;

View file

@ -0,0 +1,398 @@
/*
* DISTRHO Cardinal Plugin
* Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the LICENSE file.
*/
#pragma once
#include "rack.hpp"
#ifdef NDEBUG
# undef DEBUG
#endif
using namespace rack;
struct CardinalLedDisplayChoice : LedDisplayChoice {
bool alignTextCenter = true;
CardinalLedDisplayChoice(const char* const label = nullptr)
{
color = nvgRGBf(0.76f, 0.11f, 0.22f);
textOffset.y -= 4;
if (label != nullptr)
text = label;
}
void drawLayer(const DrawArgs& args, const int layer) override
{
if (layer == 1)
{
nvgFillColor(args.vg, color);
nvgTextLetterSpacing(args.vg, 0.0f);
if (alignTextCenter)
{
nvgTextAlign(args.vg, NVG_ALIGN_CENTER);
nvgText(args.vg, box.size.x * 0.5f, textOffset.y, text.c_str(), nullptr);
}
else
{
nvgTextAlign(args.vg, NVG_ALIGN_LEFT);
nvgText(args.vg, textOffset.x, textOffset.y, text.c_str(), nullptr);
}
}
Widget::drawLayer(args, layer);
}
};
struct NanoKnob : Knob {
static const int ringSize = 4;
std::string displayLabel = "Level";
std::string displayString = "0 dB";
float normalizedValue = 0.5f;
NanoKnob()
{
box.size = Vec(100, 100);
}
void drawLayer(const DrawArgs& args, int layer) override
{
if (layer != 1)
return Knob::drawLayer(args, layer);
const float w = box.size.x;
const float h = box.size.y;
const int knobSize = std::min(w, h - BND_WIDGET_HEIGHT * 2) - ringSize;
const int knobStartX = w / 2 - knobSize / 2;
const int knobStartY = ringSize;
const int knobCenterX = knobStartX + knobSize / 2;
const int knobCenterY = knobStartY + knobSize / 2;
const NVGcolor testing = nvgRGBf(0.76f, 0.11f, 0.22f);
nvgLineCap(args.vg, NVG_ROUND);
// outer ring value
nvgBeginPath(args.vg);
nvgArc(args.vg,
knobCenterX,
knobCenterY,
knobSize / 2 + ringSize / 2 + 1,
nvgDegToRad(135.0f),
nvgDegToRad(135.0f) + nvgDegToRad(270.0f * normalizedValue),
NVG_CW);
nvgStrokeWidth(args.vg, ringSize);
nvgStrokeColor(args.vg, testing);
nvgStroke(args.vg);
// simulate color bleeding
nvgBeginPath(args.vg);
nvgArc(args.vg,
knobCenterX,
knobCenterY,
knobSize / 2 - 3,
nvgDegToRad(135.0f),
nvgDegToRad(135.0f) + nvgDegToRad(270.0f * normalizedValue),
NVG_CW);
nvgStrokeWidth(args.vg, 5);
nvgStrokeColor(args.vg, nvgRGBAf(testing.r, testing.g, testing.b, 0.1f));
nvgStroke(args.vg);
// line indicator
nvgStrokeWidth(args.vg, 2);
nvgSave(args.vg);
nvgTranslate(args.vg, knobCenterX, knobCenterY);
nvgRotate(args.vg, nvgDegToRad(45.0f) + normalizedValue * nvgDegToRad(270.0f));
nvgBeginPath(args.vg);
nvgRoundedRect(args.vg, -2, knobSize / 2 - 9, 2, 6, 1);
nvgClosePath(args.vg);
nvgFillColor(args.vg, nvgRGBf(1.0f, 1.0f, 1.0f));
nvgFill(args.vg);
nvgRestore(args.vg);
// adjusted from VCVRack's LightWidget.cpp
if (const float halo = settings::haloBrightness)
{
float radius = knobSize * 0.5f;
float oradius = radius + std::min(radius * 4.f, 15.f);
NVGcolor icol = color::mult(nvgRGBAf(testing.r, testing.g, testing.b, 0.2f), halo);
NVGcolor ocol = nvgRGBA(0, 0, 0, 0);
NVGpaint paint = nvgRadialGradient(args.vg, knobCenterX, knobCenterY, radius, oradius, icol, ocol);
nvgBeginPath(args.vg);
nvgRect(args.vg, knobCenterX - oradius, knobCenterY - oradius, 2 * oradius, 2 * oradius);
nvgFillPaint(args.vg, paint);
nvgFill(args.vg);
}
// bottom label (value)
bndIconLabelValue(args.vg, 0, knobSize + ringSize, w, BND_WIDGET_HEIGHT, -1,
testing, BND_CENTER,
BND_LABEL_FONT_SIZE, displayString.c_str(), nullptr);
Knob::drawLayer(args, layer);
}
void draw(const DrawArgs& args) override
{
if (engine::ParamQuantity* const pq = getParamQuantity())
normalizedValue = pq->getScaledValue();
const float w = box.size.x;
const float h = box.size.y;
const int knobSize = std::min(w, h - BND_WIDGET_HEIGHT * 2) - ringSize;
const int knobStartX = w / 2 - knobSize / 2;
const int knobStartY = ringSize;
const int knobCenterX = knobStartX + knobSize / 2;
const int knobCenterY = knobStartY + knobSize / 2;
// knob
NVGcolor shade_top;
NVGcolor shade_down;
BNDwidgetState state;
if (APP->event->getDraggedWidget() == this)
state = BND_ACTIVE;
else if (APP->event->getHoveredWidget() == this)
state = BND_HOVER;
else
state = BND_DEFAULT;
bndInnerColors(&shade_top, &shade_down, &bndGetTheme()->optionTheme, state, 0);
// inner fill
nvgBeginPath(args.vg);
nvgCircle(args.vg, knobCenterX, knobCenterY, knobSize / 2);
nvgFillPaint(args.vg, nvgLinearGradient(args.vg,
knobStartX,
knobStartY,
knobStartX,
knobStartY + knobSize,
shade_top,
shade_down));
nvgFill(args.vg);
// inner fill border (inner)
nvgBeginPath(args.vg);
nvgArc(args.vg, knobCenterX, knobCenterY, knobSize / 2 - 1, nvgDegToRad(0.0f), nvgDegToRad(360.0f), NVG_CCW);
nvgClosePath(args.vg);
nvgStrokeWidth(args.vg, 1);
nvgStrokeColor(args.vg, nvgRGBAf(0.5f, 0.5f, 0.5f, 0.4f));
nvgStroke(args.vg);
// inner fill border (outer)
nvgBeginPath(args.vg);
nvgArc(args.vg, knobCenterX, knobCenterY, knobSize / 2, nvgDegToRad(0.0f), nvgDegToRad(360.0f), NVG_CCW);
nvgClosePath(args.vg);
nvgStrokeWidth(args.vg, 1);
nvgStrokeColor(args.vg, nvgRGBAf(0.0f, 0.0f, 0.0f, 0.4f));
nvgStroke(args.vg);
nvgLineCap(args.vg, NVG_ROUND);
// outer ring background
nvgBeginPath(args.vg);
nvgArc(args.vg,
knobCenterX,
knobCenterY,
knobSize / 2 + ringSize / 2 + 1,
nvgDegToRad(135.0f),
nvgDegToRad(45.0f),
NVG_CW);
nvgStrokeWidth(args.vg, ringSize);
nvgStrokeColor(args.vg, nvgRGBf(0.5f, 0.5f, 0.5f));
nvgStroke(args.vg);
// bottom label (name)
bndIconLabelValue(args.vg, 0, knobStartY + knobSize + BND_WIDGET_HEIGHT * 0.75f, w, BND_WIDGET_HEIGHT, -1,
SCHEME_WHITE, BND_CENTER,
BND_LABEL_FONT_SIZE, displayLabel.c_str(), nullptr);
Knob::draw(args);
}
void onChange(const ChangeEvent&) override
{
engine::ParamQuantity* const pq = getParamQuantity();
DISTRHO_SAFE_ASSERT_RETURN(pq != nullptr,);
displayLabel = pq->getLabel();
displayString = pq->getDisplayValueString() + pq->getUnit();
}
};
struct NanoMeter : Widget {
float gainMeterL = 0.0f;
float gainMeterR = 0.0f;
virtual void updateMeters() = 0;
void drawLayer(const DrawArgs& args, int layer) override
{
if (layer != 1)
return;
const float usableHeight = box.size.y - 10.0f;
// draw background
nvgBeginPath(args.vg);
nvgRect(args.vg,
0,
0,
box.size.x,
usableHeight);
nvgFillColor(args.vg, nvgRGB(26, 26, 26));
nvgFill(args.vg);
nvgFillColor(args.vg, nvgRGBAf(0.76f, 0.11f, 0.22f, 0.5f));
nvgStrokeColor(args.vg, nvgRGBf(0.76f, 0.11f, 0.22f));
updateMeters();
const float heightL = 1.0f + std::sqrt(gainMeterL) * (usableHeight - 1.0f);
nvgBeginPath(args.vg);
nvgRect(args.vg, 0.0f, usableHeight - heightL, box.size.x * 0.5f - 1.0f, heightL);
nvgFill(args.vg);
nvgStroke(args.vg);
const float heightR = 1.0f + std::sqrt(gainMeterR) * (usableHeight - 1.0f);
nvgBeginPath(args.vg);
nvgRect(args.vg, box.size.x * 0.5f + 1.0f, usableHeight - heightR, box.size.x * 0.5f - 2.0f, heightR);
nvgFill(args.vg);
nvgStroke(args.vg);
nvgLineCap(args.vg, NVG_ROUND);
nvgBeginPath(args.vg);
nvgMoveTo(args.vg, 0.0f, usableHeight + 2.0f);
nvgLineTo(args.vg, box.size.x * 0.5f - 11.0f, usableHeight + 2.0f);
nvgStroke(args.vg);
nvgBeginPath(args.vg);
nvgMoveTo(args.vg, 0.0f, usableHeight + 4.0f);
nvgLineTo(args.vg, box.size.x * 0.5f - 16.0f, usableHeight + 4.0f);
nvgStroke(args.vg);
nvgBeginPath(args.vg);
nvgMoveTo(args.vg, 0.0f, usableHeight + 6.0f);
nvgLineTo(args.vg, box.size.x * 0.5f - 19.0f, usableHeight + 6.0f);
nvgStroke(args.vg);
nvgBeginPath(args.vg);
nvgMoveTo(args.vg, 0.0f, usableHeight + 8.0f);
nvgLineTo(args.vg, box.size.x * 0.5f - 22.0f, usableHeight + 8.0f);
nvgStroke(args.vg);
nvgBeginPath(args.vg);
nvgMoveTo(args.vg, 0.0f, usableHeight + 10.0f);
nvgLineTo(args.vg, box.size.x * 0.5f - 24.0f, usableHeight + 10.0f);
nvgStroke(args.vg);
nvgBeginPath(args.vg);
nvgMoveTo(args.vg, 0.0f, usableHeight + 12.0f);
nvgLineTo(args.vg, box.size.x * 0.5f - 26.0f, usableHeight + 12.0f);
nvgStroke(args.vg);
nvgBeginPath(args.vg);
nvgMoveTo(args.vg, box.size.x * 0.5f + 10.0f, usableHeight + 2.0f);
nvgLineTo(args.vg, box.size.x - 1.0f, usableHeight + 2.0f);
nvgStroke(args.vg);
nvgBeginPath(args.vg);
nvgMoveTo(args.vg, box.size.x * 0.5f + 15.0f, usableHeight + 4.0f);
nvgLineTo(args.vg, box.size.x - 1.0f, usableHeight + 4.0f);
nvgStroke(args.vg);
nvgBeginPath(args.vg);
nvgMoveTo(args.vg, box.size.x * 0.5f + 18.0f, usableHeight + 6.0f);
nvgLineTo(args.vg, box.size.x - 1.0f, usableHeight + 6.0f);
nvgStroke(args.vg);
nvgBeginPath(args.vg);
nvgMoveTo(args.vg, box.size.x * 0.5f + 20.0f, usableHeight + 8.0f);
nvgLineTo(args.vg, box.size.x - 1.0f, usableHeight + 8.0f);
nvgStroke(args.vg);
nvgBeginPath(args.vg);
nvgMoveTo(args.vg, box.size.x * 0.5f + 22.0f, usableHeight + 10.0f);
nvgLineTo(args.vg, box.size.x - 1.0f, usableHeight + 10.0f);
nvgStroke(args.vg);
nvgBeginPath(args.vg);
nvgMoveTo(args.vg, box.size.x * 0.5f + 24.0f, usableHeight + 12.0f);
nvgLineTo(args.vg, box.size.x - 1.0f, usableHeight + 12.0f);
nvgStroke(args.vg);
}
};
struct OpenGlWidgetWithBrowserPreview : OpenGlWidget {
NVGLUframebuffer* fb = nullptr;
void draw(const DrawArgs& args) override
{
if (args.fb == nullptr)
return OpenGlWidget::draw(args);
// set oversample to current scale
float trans[6];
nvgCurrentTransform(args.vg, trans);
oversample = std::max(1.0f, trans[0]);
// recreate framebuffer
deleteFramebuffer();
fb = nvgluCreateFramebuffer(args.vg, box.size.x * oversample, box.size.y * oversample, 0);
DISTRHO_SAFE_ASSERT_RETURN(fb != nullptr,);
// draw our special framebuffer
nvgluBindFramebuffer(fb);
drawFramebufferForBrowserPreview();
// reset to regular framebuffer
nvgluBindFramebuffer(args.fb);
// render image generated by our framebuffer
nvgBeginPath(args.vg);
nvgRect(args.vg, 0.0f, 0.0f, box.size.x, box.size.y);
NVGpaint paint = nvgImagePattern(args.vg,
0.0f, 0.0f, box.size.x, box.size.y,
0.0f, fb->image, 1.0f);
nvgFillPaint(args.vg, paint);
nvgFill(args.vg);
}
void onContextDestroy(const ContextDestroyEvent& e) override
{
deleteFramebuffer();
OpenGlWidget::onContextDestroy(e);
}
void deleteFramebuffer()
{
if (fb == nullptr)
return;
nvgluDeleteFramebuffer(fb);
fb = nullptr;
}
virtual void drawFramebufferForBrowserPreview() = 0;
};

View file

@ -17,6 +17,7 @@
#ifndef HEADLESS
# include "glBars.hpp"
# include "Widgets.hpp"
#else
# include "plugin.hpp"
#endif

View file

@ -25,95 +25,6 @@
using namespace rack;
#ifndef HEADLESS
struct CardinalLedDisplayChoice : LedDisplayChoice {
bool alignTextCenter = true;
CardinalLedDisplayChoice(const char* const label = nullptr)
{
color = nvgRGBf(0.76f, 0.11f, 0.22f);
textOffset.y -= 4;
if (label != nullptr)
text = label;
}
void drawLayer(const DrawArgs& args, const int layer) override
{
if (layer == 1)
{
nvgFillColor(args.vg, color);
nvgTextLetterSpacing(args.vg, 0.0f);
if (alignTextCenter)
{
nvgTextAlign(args.vg, NVG_ALIGN_CENTER);
nvgText(args.vg, box.size.x * 0.5f, textOffset.y, text.c_str(), nullptr);
}
else
{
nvgTextAlign(args.vg, NVG_ALIGN_LEFT);
nvgText(args.vg, textOffset.x, textOffset.y, text.c_str(), nullptr);
}
}
Widget::drawLayer(args, layer);
}
};
struct OpenGlWidgetWithBrowserPreview : OpenGlWidget {
NVGLUframebuffer* fb = nullptr;
void draw(const DrawArgs& args) override
{
if (args.fb == nullptr)
return OpenGlWidget::draw(args);
// set oversample to current scale
float trans[6];
nvgCurrentTransform(args.vg, trans);
oversample = std::max(1.0f, trans[0]);
// recreate framebuffer
deleteFramebuffer();
fb = nvgluCreateFramebuffer(args.vg, box.size.x * oversample, box.size.y * oversample, 0);
DISTRHO_SAFE_ASSERT_RETURN(fb != nullptr,);
// draw our special framebuffer
nvgluBindFramebuffer(fb);
drawFramebufferForBrowserPreview();
// reset to regular framebuffer
nvgluBindFramebuffer(args.fb);
// render image generated by our framebuffer
nvgBeginPath(args.vg);
nvgRect(args.vg, 0.0f, 0.0f, box.size.x, box.size.y);
NVGpaint paint = nvgImagePattern(args.vg,
0.0f, 0.0f, box.size.x, box.size.y,
0.0f, fb->image, 1.0f);
nvgFillPaint(args.vg, paint);
nvgFill(args.vg);
}
void onContextDestroy(const ContextDestroyEvent& e) override
{
deleteFramebuffer();
OpenGlWidget::onContextDestroy(e);
}
void deleteFramebuffer()
{
if (fb == nullptr)
return;
nvgluDeleteFramebuffer(fb);
fb = nullptr;
}
virtual void drawFramebufferForBrowserPreview() = 0;
};
#endif
extern Plugin* pluginInstance;
extern Model* modelAudioFile;