Browse Source

WIP:

- Make ParamWidget hold module/paramId instead of paramQuantity.
- Add configInput/configOutput.
- Add engine::PortInfo.
- Avoid calling particular events when cursor is locked.
- Add PortTooltip.
tags/v2.0.0
Andrew Belt 5 years ago
parent
commit
99b17727ab
22 changed files with 383 additions and 180 deletions
  1. +2
    -2
      include/app/ModuleWidget.hpp
  2. +4
    -1
      include/app/ParamWidget.hpp
  3. +10
    -6
      include/app/PortWidget.hpp
  4. +2
    -4
      include/componentlibrary.hpp
  5. +32
    -3
      include/engine/Module.hpp
  6. +21
    -0
      include/engine/PortInfo.hpp
  7. +4
    -5
      include/helpers.hpp
  8. +1
    -0
      include/window.hpp
  9. +18
    -14
      src/app/Knob.cpp
  10. +29
    -29
      src/app/ModuleWidget.cpp
  11. +47
    -33
      src/app/ParamWidget.cpp
  12. +87
    -10
      src/app/PortWidget.cpp
  13. +4
    -3
      src/app/SvgKnob.cpp
  14. +3
    -2
      src/app/SvgSlider.cpp
  15. +3
    -2
      src/app/SvgSwitch.cpp
  16. +19
    -16
      src/app/Switch.cpp
  17. +12
    -0
      src/core/MIDI_CV.cpp
  18. +2
    -2
      src/core/MIDI_Map.cpp
  19. +10
    -10
      src/engine/Engine.cpp
  20. +18
    -1
      src/engine/Module.cpp
  21. +51
    -37
      src/event.cpp
  22. +4
    -0
      src/window.cpp

+ 2
- 2
include/app/ModuleWidget.hpp View File

@@ -51,11 +51,11 @@ struct ModuleWidget : widget::OpaqueWidget {

/** Convenience functions for adding special widgets (calls addChild()) */
void addParam(ParamWidget* param);
void addOutput(PortWidget* output);
void addInput(PortWidget* input);
void addOutput(PortWidget* output);
ParamWidget* getParam(int paramId);
PortWidget* getOutput(int outputId);
PortWidget* getInput(int inputId);
PortWidget* getOutput(int outputId);

/** Serializes/unserializes the module state */
json_t* toJson();


+ 4
- 1
include/app/ParamWidget.hpp View File

@@ -12,12 +12,15 @@ namespace app {

/** Manages an engine::Param on a ModuleWidget. */
struct ParamWidget : widget::OpaqueWidget {
engine::ParamQuantity* paramQuantity = NULL;
engine::Module* module = NULL;
int paramId = 0;

ui::Tooltip* tooltip = NULL;
/** For triggering the Change event. `*/
float lastValue = NAN;

virtual void init() {}
engine::ParamQuantity* getParamQuantity();
void step() override;
void draw(const DrawArgs& args) override;



+ 10
- 6
include/app/PortWidget.hpp View File

@@ -1,8 +1,10 @@
#pragma once
#include <app/common.hpp>
#include <widget/OpaqueWidget.hpp>
#include <ui/Tooltip.hpp>
#include <app/MultiLightWidget.hpp>
#include <engine/Module.hpp>
#include <engine/PortInfo.hpp>


namespace rack {
@@ -12,18 +14,20 @@ namespace app {
/** Manages an engine::Port on a ModuleWidget. */
struct PortWidget : widget::OpaqueWidget {
engine::Module* module = NULL;
int portId;
int portId = 0;
engine::Port::Type type = engine::Port::INPUT;

ui::Tooltip* tooltip = NULL;
bool hovered = false;

enum Type {
OUTPUT,
INPUT
};
Type type;
MultiLightWidget* plugLight;

PortWidget();
~PortWidget();
engine::Port* getPort();
engine::PortInfo* getPortInfo();
void createTooltip();
void destroyTooltip();

void step() override;
void draw(const DrawArgs& args) override;


+ 2
- 4
include/componentlibrary.hpp View File

@@ -577,8 +577,7 @@ struct LightSlider : TBase {
}

void setFirstLightId(int firstLightId) {
if (this->paramQuantity)
light->module = this->paramQuantity->module;
light->module = this->module;
light->firstLightId = firstLightId;
}

@@ -730,8 +729,7 @@ struct LEDLightBezel : LEDBezel {
}

void setFirstLightId(int firstLightId) {
if (paramQuantity)
light->module = paramQuantity->module;
light->module = this->module;
light->firstLightId = firstLightId;
}
};


+ 32
- 3
include/engine/Module.hpp View File

@@ -6,6 +6,7 @@
#include <engine/Port.hpp>
#include <engine/Light.hpp>
#include <engine/ParamQuantity.hpp>
#include <engine/PortInfo.hpp>
#include <vector>
#include <jansson.h>

@@ -36,6 +37,8 @@ struct Module {
std::vector<Output> outputs;
std::vector<Light> lights;
std::vector<ParamQuantity*> paramQuantities;
std::vector<PortInfo*> inputInfos;
std::vector<PortInfo*> outputInfos;

/** Represents a message-passing channel for an adjacent module. */
struct Expander {
@@ -106,10 +109,10 @@ struct Module {
q->minValue = minValue;
q->maxValue = maxValue;
q->defaultValue = defaultValue;
if (!label.empty())
q->label = label;
if (label == "")
q->label = string::f("Parameter %d", paramId + 1);
else
q->label = string::f("#%d", paramId + 1);
q->label = label;
q->unit = unit;
q->displayBase = displayBase;
q->displayMultiplier = displayMultiplier;
@@ -117,6 +120,32 @@ struct Module {
paramQuantities[paramId] = q;
}

void configInput(int portId, std::string label = "") {
assert(portId < (int) inputs.size() && portId < (int) inputInfos.size());
if (inputInfos[portId])
delete inputInfos[portId];

PortInfo* p = new PortInfo;
if (label == "")
p->label = string::f("Input %d", portId + 1);
else
p->label = label;
inputInfos[portId] = p;
}

void configOutput(int portId, std::string label = "") {
assert(portId < (int) outputs.size() && portId < (int) outputInfos.size());
if (outputInfos[portId])
delete outputInfos[portId];

PortInfo* p = new PortInfo;
if (label == "")
p->label = string::f("Output %d", portId + 1);
else
p->label = label;
outputInfos[portId] = p;
}

struct ProcessArgs {
float sampleRate;
float sampleTime;


+ 21
- 0
include/engine/PortInfo.hpp View File

@@ -0,0 +1,21 @@
#pragma once
#include <common.hpp>
#include <engine/Port.hpp>


namespace rack {
namespace engine {


struct PortInfo {
/** The name of the port, using sentence capitalization.
e.g. "Sine", "Pitch input", "Mode CV"
*/
std::string label;
/** An optional one-sentence description of the parameter. */
std::string description;
};


} // namespace engine
} // namespace rack

+ 4
- 5
include/helpers.hpp View File

@@ -59,9 +59,8 @@ template <class TParamWidget>
TParamWidget* createParam(math::Vec pos, engine::Module* module, int paramId) {
TParamWidget* o = new TParamWidget;
o->box.pos = pos;
if (module) {
o->paramQuantity = module->paramQuantities[paramId];
}
o->module = module;
o->paramId = paramId;
o->init();
return o;
}
@@ -78,7 +77,7 @@ TPortWidget* createInput(math::Vec pos, engine::Module* module, int inputId) {
TPortWidget* o = new TPortWidget;
o->box.pos = pos;
o->module = module;
o->type = app::PortWidget::INPUT;
o->type = engine::Port::INPUT;
o->portId = inputId;
return o;
}
@@ -95,7 +94,7 @@ TPortWidget* createOutput(math::Vec pos, engine::Module* module, int outputId) {
TPortWidget* o = new TPortWidget;
o->box.pos = pos;
o->module = module;
o->type = app::PortWidget::OUTPUT;
o->type = engine::Port::OUTPUT;
o->portId = outputId;
return o;
}


+ 1
- 0
include/window.hpp View File

@@ -83,6 +83,7 @@ struct Window {
void close();
void cursorLock();
void cursorUnlock();
bool isCursorLocked();
/** Gets the current keyboard mod state
Don't call this from a Key event. Simply use `e.mods` instead.
*/


+ 18
- 14
src/app/Knob.cpp View File

@@ -14,9 +14,10 @@ static const float KNOB_SENSITIVITY = 0.0015f;

void Knob::init() {
ParamWidget::init();
if (paramQuantity) {
engine::ParamQuantity* pq = getParamQuantity();
if (pq) {
if (snap)
paramQuantity->snapEnabled = true;
pq->snapEnabled = true;
}
}

@@ -40,8 +41,9 @@ void Knob::onDragStart(const event::DragStart& e) {
if (e.button != GLFW_MOUSE_BUTTON_LEFT)
return;

if (paramQuantity) {
oldValue = paramQuantity->getSmoothValue();
engine::ParamQuantity* pq = getParamQuantity();
if (pq) {
oldValue = pq->getSmoothValue();
snapDelta = 0.f;
}

@@ -54,14 +56,15 @@ void Knob::onDragEnd(const event::DragEnd& e) {

APP->window->cursorUnlock();

if (paramQuantity) {
float newValue = paramQuantity->getSmoothValue();
engine::ParamQuantity* pq = getParamQuantity();
if (pq) {
float newValue = pq->getSmoothValue();
if (oldValue != newValue) {
// Push ParamChange history action
history::ParamChange* h = new history::ParamChange;
h->name = "move knob";
h->moduleId = paramQuantity->module->id;
h->paramId = paramQuantity->paramId;
h->moduleId = module->id;
h->paramId = paramId;
h->oldValue = oldValue;
h->newValue = newValue;
APP->history->push(h);
@@ -73,10 +76,11 @@ void Knob::onDragMove(const event::DragMove& e) {
if (e.button != GLFW_MOUSE_BUTTON_LEFT)
return;

if (paramQuantity) {
engine::ParamQuantity* pq = getParamQuantity();
if (pq) {
float range;
if (paramQuantity->isBounded()) {
range = paramQuantity->getRange();
if (pq->isBounded()) {
range = pq->getRange();
}
else {
// Continuous encoders scale as if their limits are +/-1
@@ -97,7 +101,7 @@ void Knob::onDragMove(const event::DragMove& e) {
delta /= 256.f;
}

if (paramQuantity->snapEnabled) {
if (pq->snapEnabled) {
// Replace delta with an accumulated delta since the last integer knob.
snapDelta += delta;
delta = std::trunc(snapDelta);
@@ -106,10 +110,10 @@ void Knob::onDragMove(const event::DragMove& e) {

// Set value
if (smooth) {
paramQuantity->setSmoothValue(paramQuantity->getSmoothValue() + delta);
pq->setSmoothValue(pq->getSmoothValue() + delta);
}
else {
paramQuantity->setValue(paramQuantity->getValue() + delta);
pq->setValue(pq->getValue() + delta);
}
}



+ 29
- 29
src/app/ModuleWidget.cpp View File

@@ -449,21 +449,9 @@ void ModuleWidget::addParam(ParamWidget* param) {
addChild(param);
}

void ModuleWidget::addOutput(PortWidget* output) {
// Check that the port is an output
assert(output->type == PortWidget::OUTPUT);
// Check that the port doesn't have a duplicate ID
for (PortWidget* output2 : outputs) {
assert(output->portId != output2->portId);
}
// Add port
outputs.push_back(output);
addChild(output);
}

void ModuleWidget::addInput(PortWidget* input) {
// Check that the port is an input
assert(input->type == PortWidget::INPUT);
assert(input->type == engine::Port::INPUT);
// Check that the port doesn't have a duplicate ID
for (PortWidget* input2 : inputs) {
assert(input->portId != input2->portId);
@@ -473,25 +461,37 @@ void ModuleWidget::addInput(PortWidget* input) {
addChild(input);
}

void ModuleWidget::addOutput(PortWidget* output) {
// Check that the port is an output
assert(output->type == engine::Port::OUTPUT);
// Check that the port doesn't have a duplicate ID
for (PortWidget* output2 : outputs) {
assert(output->portId != output2->portId);
}
// Add port
outputs.push_back(output);
addChild(output);
}

ParamWidget* ModuleWidget::getParam(int paramId) {
for (ParamWidget* param : params) {
if (param->paramQuantity && param->paramQuantity->paramId == paramId)
if (param->paramId == paramId)
return param;
}
return NULL;
}

PortWidget* ModuleWidget::getOutput(int outputId) {
for (PortWidget* port : outputs) {
if (port->portId == outputId)
PortWidget* ModuleWidget::getInput(int inputId) {
for (PortWidget* port : inputs) {
if (port->portId == inputId)
return port;
}
return NULL;
}

PortWidget* ModuleWidget::getInput(int inputId) {
for (PortWidget* port : inputs) {
if (port->portId == inputId)
PortWidget* ModuleWidget::getOutput(int outputId) {
for (PortWidget* port : outputs) {
if (port->portId == outputId)
return port;
}
return NULL;
@@ -690,25 +690,25 @@ void ModuleWidget::randomizeAction() {
}

static void disconnectActions(ModuleWidget* mw, history::ComplexAction* complexAction) {
// Add CableRemove action for all cables attached to outputs
for (PortWidget* output : mw->outputs) {
for (CableWidget* cw : APP->scene->rack->getCablesOnPort(output)) {
// Add CableRemove action for all cables attached to inputs
for (PortWidget* input : mw->inputs) {
for (CableWidget* cw : APP->scene->rack->getCablesOnPort(input)) {
if (!cw->isComplete())
continue;
// Avoid creating duplicate actions for self-patched cables
if (cw->outputPort->module == mw->module)
continue;
// history::CableRemove
history::CableRemove* h = new history::CableRemove;
h->setCable(cw);
complexAction->push(h);
}
}
// Add CableRemove action for all cables attached to inputs
for (PortWidget* input : mw->inputs) {
for (CableWidget* cw : APP->scene->rack->getCablesOnPort(input)) {
// Add CableRemove action for all cables attached to outputs
for (PortWidget* output : mw->outputs) {
for (CableWidget* cw : APP->scene->rack->getCablesOnPort(output)) {
if (!cw->isComplete())
continue;
// Avoid creating duplicate actions for self-patched cables
if (cw->outputPort->module == mw->module)
continue;
// history::CableRemove
history::CableRemove* h = new history::CableRemove;
h->setCable(cw);


+ 47
- 33
src/app/ParamWidget.cpp View File

@@ -24,23 +24,26 @@ struct ParamField : ui::TextField {

void setParamWidget(ParamWidget* paramWidget) {
this->paramWidget = paramWidget;
if (paramWidget->paramQuantity)
text = paramWidget->paramQuantity->getDisplayValueString();
engine::ParamQuantity* pq = paramWidget->getParamQuantity();
if (pq)
text = pq->getDisplayValueString();
selectAll();
}

void onSelectKey(const event::SelectKey& e) override {
if (e.action == GLFW_PRESS && (e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER)) {
float oldValue = paramWidget->paramQuantity->getValue();
if (paramWidget->paramQuantity)
paramWidget->paramQuantity->setDisplayValueString(text);
float newValue = paramWidget->paramQuantity->getValue();
engine::ParamQuantity* pq = paramWidget->getParamQuantity();
assert(pq);
float oldValue = pq->getValue();
if (pq)
pq->setDisplayValueString(text);
float newValue = pq->getValue();

if (oldValue != newValue) {
// Push ParamChange history action
history::ParamChange* h = new history::ParamChange;
h->moduleId = paramWidget->paramQuantity->module->id;
h->paramId = paramWidget->paramQuantity->paramId;
h->moduleId = paramWidget->module->id;
h->paramId = paramWidget->paramId;
h->oldValue = oldValue;
h->newValue = newValue;
APP->history->push(h);
@@ -61,13 +64,15 @@ struct ParamTooltip : ui::Tooltip {
ParamWidget* paramWidget;

void step() override {
if (paramWidget->paramQuantity) {
engine::ParamQuantity* pq = paramWidget->getParamQuantity();
if (pq) {
// Quantity string
text = paramWidget->paramQuantity->getString();
// Param description
std::string description = paramWidget->paramQuantity->description;
if (!description.empty())
text += "\n" + description;
text = pq->getString();
// Description
if (pq->description != "") {
text += "\n";
text += pq->description;
}
}
Tooltip::step();
// Position at bottom-right of parameter
@@ -82,7 +87,8 @@ struct ParamTooltip : ui::Tooltip {
struct ParamLabel : ui::MenuLabel {
ParamWidget* paramWidget;
void step() override {
text = paramWidget->paramQuantity->getString();
engine::ParamQuantity* pq = paramWidget->getParamQuantity();
text = pq->getString();
MenuLabel::step();
}
};
@@ -103,7 +109,7 @@ struct ParamFineItem : ui::MenuItem {
struct ParamUnmapItem : ui::MenuItem {
ParamWidget* paramWidget;
void onAction(const event::Action& e) override {
engine::ParamHandle* paramHandle = APP->engine->getParamHandle(paramWidget->paramQuantity->module->id, paramWidget->paramQuantity->paramId);
engine::ParamHandle* paramHandle = APP->engine->getParamHandle(paramWidget->module->id, paramWidget->paramId);
if (paramHandle) {
APP->engine->updateParamHandle(paramHandle, -1, 0);
}
@@ -111,10 +117,17 @@ struct ParamUnmapItem : ui::MenuItem {
};


engine::ParamQuantity* ParamWidget::getParamQuantity() {
if (!module)
return NULL;
return module->paramQuantities[paramId];
}

void ParamWidget::step() {
if (paramQuantity) {
float value = paramQuantity->getSmoothValue();
// Trigger change event when paramQuantity value changes
engine::ParamQuantity* pq = getParamQuantity();
if (pq) {
float value = pq->getSmoothValue();
// Trigger change event when the ParamQuantity value changes
if (value != lastValue) {
event::Change eChange;
onChange(eChange);
@@ -129,7 +142,7 @@ void ParamWidget::draw(const DrawArgs& args) {
Widget::draw(args);

// Param map indicator
engine::ParamHandle* paramHandle = paramQuantity ? APP->engine->getParamHandle(paramQuantity->module->id, paramQuantity->paramId) : NULL;
engine::ParamHandle* paramHandle = module ? APP->engine->getParamHandle(module->id, paramId) : NULL;
if (paramHandle) {
NVGcolor color = paramHandle->color;
nvgBeginPath(args.vg);
@@ -149,7 +162,7 @@ void ParamWidget::onButton(const event::Button& e) {

// Touch parameter
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT && (e.mods & RACK_MOD_MASK) == 0) {
if (paramQuantity) {
if (module) {
APP->scene->rack->touchedParam = this;
}
e.consume(this);
@@ -167,11 +180,11 @@ void ParamWidget::onDoubleClick(const event::DoubleClick& e) {
}

void ParamWidget::onEnter(const event::Enter& e) {
if (settings::paramTooltip && !tooltip && paramQuantity) {
ParamTooltip* paramTooltip = new ParamTooltip;
paramTooltip->paramWidget = this;
APP->scene->addChild(paramTooltip);
tooltip = paramTooltip;
if (settings::paramTooltip && !this->tooltip && module) {
ParamTooltip* tooltip = new ParamTooltip;
tooltip->paramWidget = this;
APP->scene->addChild(tooltip);
this->tooltip = tooltip;
}
}

@@ -207,7 +220,7 @@ void ParamWidget::createContextMenu() {
// fineItem->disabled = true;
// menu->addChild(fineItem);

engine::ParamHandle* paramHandle = paramQuantity ? APP->engine->getParamHandle(paramQuantity->module->id, paramQuantity->paramId) : NULL;
engine::ParamHandle* paramHandle = module ? APP->engine->getParamHandle(module->id, paramId) : NULL;
if (paramHandle) {
ParamUnmapItem* unmapItem = new ParamUnmapItem;
unmapItem->text = "Unmap";
@@ -218,17 +231,18 @@ void ParamWidget::createContextMenu() {
}

void ParamWidget::resetAction() {
if (paramQuantity && paramQuantity->resetEnabled) {
float oldValue = paramQuantity->getValue();
paramQuantity->reset();
float newValue = paramQuantity->getValue();
engine::ParamQuantity* pq = getParamQuantity();
if (pq && pq->resetEnabled) {
float oldValue = pq->getValue();
pq->reset();
float newValue = pq->getValue();

if (oldValue != newValue) {
// Push ParamChange history action
history::ParamChange* h = new history::ParamChange;
h->name = "reset parameter";
h->moduleId = paramQuantity->module->id;
h->paramId = paramQuantity->paramId;
h->moduleId = module->id;
h->paramId = paramId;
h->oldValue = oldValue;
h->newValue = newValue;
APP->history->push(h);


+ 87
- 10
src/app/PortWidget.cpp View File

@@ -3,6 +3,8 @@
#include <window.hpp>
#include <app.hpp>
#include <history.hpp>
#include <engine/Engine.hpp>
#include <settings.hpp>
#include <componentlibrary.hpp>


@@ -10,6 +12,42 @@ namespace rack {
namespace app {


struct PortTooltip : ui::Tooltip {
PortWidget* portWidget;

void step() override {
if (portWidget->module) {
engine::Port* port = portWidget->getPort();
engine::PortInfo* portInfo = portWidget->getPortInfo();
// Label
text = portInfo->label;
// Voltage, number of channels
int channels = port->getChannels();
for (int i = 0; i < channels; i++) {
// Add newline or comma
if (i % 4 == 0)
text += "\n";
else
text += " ";
text += string::f("%5gV", port->getVoltage(i));
}
// Description
std::string description = portInfo->description;
if (description != "") {
text += "\n";
text += description;
}
}
Tooltip::step();
// Position at bottom-right of parameter
box.pos = portWidget->getAbsoluteOffset(portWidget->box.size).round();
// Fit inside parent (copied from Tooltip.cpp)
assert(parent);
box = box.nudge(parent->box.zeroPos());
}
};


struct PlugLight : MultiLightWidget {
PlugLight() {
addBaseColor(componentlibrary::SCHEME_GREEN);
@@ -33,13 +71,48 @@ PortWidget::~PortWidget() {
APP->scene->rack->clearCablesOnPort(this);
}

engine::Port* PortWidget::getPort() {
if (!module)
return NULL;
if (type == engine::Port::INPUT)
return &module->inputs[portId];
else
return &module->outputs[portId];
}

engine::PortInfo* PortWidget::getPortInfo() {
if (!module)
return NULL;
if (type == engine::Port::INPUT)
return module->inputInfos[portId];
else
return module->outputInfos[portId];
}

void PortWidget::createTooltip() {
if (settings::paramTooltip && !this->tooltip && module) {
PortTooltip* tooltip = new PortTooltip;
tooltip->portWidget = this;
APP->scene->addChild(tooltip);
this->tooltip = tooltip;
}
}

void PortWidget::destroyTooltip() {
if (tooltip) {
APP->scene->removeChild(tooltip);
delete tooltip;
tooltip = NULL;
}
}

void PortWidget::step() {
if (!module)
return;

std::vector<float> values(3);
for (int i = 0; i < 3; i++) {
if (type == OUTPUT)
if (type == engine::Port::OUTPUT)
values[i] = module->outputs[portId].plugLights[i].getBrightness();
else
values[i] = module->inputs[portId].plugLights[i].getBrightness();
@@ -53,7 +126,7 @@ void PortWidget::draw(const DrawArgs& args) {
CableWidget* cw = APP->scene->rack->incompleteCable;
if (cw) {
// Dim the PortWidget if the active cable cannot plug into this PortWidget
if (type == OUTPUT ? cw->outputPort : cw->inputPort)
if (type == engine::Port::OUTPUT ? cw->outputPort : cw->inputPort)
nvgGlobalAlpha(args.vg, 0.5);
}
Widget::draw(args);
@@ -80,10 +153,12 @@ void PortWidget::onButton(const event::Button& e) {

void PortWidget::onEnter(const event::Enter& e) {
hovered = true;
createTooltip();
}

void PortWidget::onLeave(const event::Leave& e) {
hovered = false;
destroyTooltip();
}

void PortWidget::onDragStart(const event::DragStart& e) {
@@ -92,7 +167,7 @@ void PortWidget::onDragStart(const event::DragStart& e) {

CableWidget* cw = NULL;
if ((APP->window->getMods() & RACK_MOD_MASK) == RACK_MOD_CTRL) {
if (type == OUTPUT) {
if (type == engine::Port::OUTPUT) {
// Ctrl-clicking an output creates a new cable.
// Keep cable NULL. Will be created below
}
@@ -118,7 +193,7 @@ void PortWidget::onDragStart(const event::DragStart& e) {

// Disconnect and reuse existing cable
APP->scene->rack->removeCable(cw);
if (type == OUTPUT)
if (type == engine::Port::OUTPUT)
cw->outputPort = NULL;
else
cw->inputPort = NULL;
@@ -129,7 +204,7 @@ void PortWidget::onDragStart(const event::DragStart& e) {
if (!cw) {
// Create a new cable
cw = new CableWidget;
if (type == OUTPUT)
if (type == engine::Port::OUTPUT)
cw->outputPort = this;
else
cw->inputPort = this;
@@ -165,7 +240,7 @@ void PortWidget::onDragDrop(const event::DragDrop& e) {
return;

// Reject ports if this is an input port and something is already plugged into it
if (type == INPUT) {
if (type == engine::Port::INPUT) {
if (APP->scene->rack->getTopCable(this))
return;
}
@@ -173,7 +248,7 @@ void PortWidget::onDragDrop(const event::DragDrop& e) {
CableWidget* cw = APP->scene->rack->incompleteCable;
if (cw) {
cw->hoveredOutputPort = cw->hoveredInputPort = NULL;
if (type == OUTPUT)
if (type == engine::Port::OUTPUT)
cw->outputPort = this;
else
cw->inputPort = this;
@@ -182,18 +257,19 @@ void PortWidget::onDragDrop(const event::DragDrop& e) {
}

void PortWidget::onDragEnter(const event::DragEnter& e) {
createTooltip();
if (e.button != GLFW_MOUSE_BUTTON_LEFT)
return;

// Reject ports if this is an input port and something is already plugged into it
if (type == INPUT) {
if (type == engine::Port::INPUT) {
if (APP->scene->rack->getTopCable(this))
return;
}

CableWidget* cw = APP->scene->rack->incompleteCable;
if (cw) {
if (type == OUTPUT)
if (type == engine::Port::OUTPUT)
cw->hoveredOutputPort = this;
else
cw->hoveredInputPort = this;
@@ -201,6 +277,7 @@ void PortWidget::onDragEnter(const event::DragEnter& e) {
}

void PortWidget::onDragLeave(const event::DragLeave& e) {
destroyTooltip();
if (e.button != GLFW_MOUSE_BUTTON_LEFT)
return;

@@ -210,7 +287,7 @@ void PortWidget::onDragLeave(const event::DragLeave& e) {

CableWidget* cw = APP->scene->rack->incompleteCable;
if (cw) {
if (type == OUTPUT)
if (type == engine::Port::OUTPUT)
cw->hoveredOutputPort = NULL;
else
cw->hoveredInputPort = NULL;


+ 4
- 3
src/app/SvgKnob.cpp View File

@@ -33,10 +33,11 @@ void SvgKnob::setSvg(std::shared_ptr<Svg> svg) {

void SvgKnob::onChange(const event::Change& e) {
// Re-transform the widget::TransformWidget
if (paramQuantity) {
float value = paramQuantity->getScaledValue();
engine::ParamQuantity* pq = getParamQuantity();
if (pq) {
float value = pq->getScaledValue();
float angle;
if (paramQuantity->isBounded()) {
if (pq->isBounded()) {
angle = math::rescale(value, 0.f, 1.f, minAngle, maxAngle);
}
else {


+ 3
- 2
src/app/SvgSlider.cpp View File

@@ -31,9 +31,10 @@ void SvgSlider::setHandleSvg(std::shared_ptr<Svg> svg) {
}

void SvgSlider::onChange(const event::Change& e) {
if (paramQuantity) {
engine::ParamQuantity* pq = getParamQuantity();
if (pq) {
// Interpolate handle position
float v = paramQuantity->getScaledValue();
float v = pq->getScaledValue();
handle->box.pos = math::Vec(
math::rescale(v, 0.f, 1.f, minHandlePos.x, maxHandlePos.x),
math::rescale(v, 0.f, 1.f, minHandlePos.y, maxHandlePos.y));


+ 3
- 2
src/app/SvgSwitch.cpp View File

@@ -31,8 +31,9 @@ void SvgSwitch::addFrame(std::shared_ptr<Svg> svg) {
}

void SvgSwitch::onChange(const event::Change& e) {
if (!frames.empty() && paramQuantity) {
int index = (int) std::round(paramQuantity->getValue() - paramQuantity->getMinValue());
engine::ParamQuantity* pq = getParamQuantity();
if (!frames.empty() && pq) {
int index = (int) std::round(pq->getValue() - pq->getMinValue());
index = math::clamp(index, 0, (int) frames.size() - 1);
sw->setSvg(frames[index]);
fb->dirty = true;


+ 19
- 16
src/app/Switch.cpp View File

@@ -11,25 +11,27 @@ namespace app {

void Switch::init() {
ParamWidget::init();
if (paramQuantity) {
paramQuantity->snapEnabled = true;
engine::ParamQuantity* pq = getParamQuantity();
if (pq) {
pq->snapEnabled = true;
if (momentary) {
paramQuantity->resetEnabled = false;
paramQuantity->randomizeEnabled = false;
pq->resetEnabled = false;
pq->randomizeEnabled = false;
}
}
}

void Switch::step() {
engine::ParamQuantity* pq = getParamQuantity();
if (momentaryPressed) {
momentaryPressed = false;
// Wait another frame.
}
else if (momentaryReleased) {
momentaryReleased = false;
if (paramQuantity) {
if (pq) {
// Set to minimum value
paramQuantity->setMin();
pq->setMin();
}
}
ParamWidget::step();
@@ -43,32 +45,33 @@ void Switch::onDragStart(const event::DragStart& e) {
if (e.button != GLFW_MOUSE_BUTTON_LEFT)
return;

engine::ParamQuantity* pq = getParamQuantity();
if (momentary) {
if (paramQuantity) {
if (pq) {
// Set to maximum value
paramQuantity->setMax();
pq->setMax();
momentaryPressed = true;
}
}
else {
if (paramQuantity) {
float oldValue = paramQuantity->getValue();
if (paramQuantity->isMax()) {
if (pq) {
float oldValue = pq->getValue();
if (pq->isMax()) {
// Reset value back to minimum
paramQuantity->setMin();
pq->setMin();
}
else {
// Increment value by 1
paramQuantity->setValue(std::round(paramQuantity->getValue()) + 1.f);
pq->setValue(std::round(pq->getValue()) + 1.f);
}

float newValue = paramQuantity->getValue();
float newValue = pq->getValue();
if (oldValue != newValue) {
// Push ParamChange history action
history::ParamChange* h = new history::ParamChange;
h->name = "move switch";
h->moduleId = paramQuantity->module->id;
h->paramId = paramQuantity->paramId;
h->moduleId = module->id;
h->paramId = paramId;
h->oldValue = oldValue;
h->newValue = newValue;
APP->history->push(h);


+ 12
- 0
src/core/MIDI_CV.cpp View File

@@ -72,6 +72,18 @@ struct MIDI_CV : Module {

MIDI_CV() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
configOutput(CV_OUTPUT, "V/oct");
configOutput(GATE_OUTPUT, "Gate");
configOutput(VELOCITY_OUTPUT, "Velocity");
configOutput(AFTERTOUCH_OUTPUT, "Aftertouch");
configOutput(PITCH_OUTPUT, "Pitch wheel");
configOutput(MOD_OUTPUT, "Mod wheel");
configOutput(RETRIGGER_OUTPUT, "Retrigger");
configOutput(CLOCK_OUTPUT, "Clock");
configOutput(CLOCK_DIV_OUTPUT, "Clock divider");
configOutput(START_OUTPUT, "Start");
configOutput(STOP_OUTPUT, "Stop");
configOutput(CONTINUE_OUTPUT, "Continue");
heldNotes.reserve(128);
for (int c = 0; c < 16; c++) {
pitchFilters[c].setTau(1 / 30.f);


+ 2
- 2
src/core/MIDI_Map.cpp View File

@@ -321,8 +321,8 @@ struct MIDI_MapChoice : LedDisplayChoice {
ParamWidget* touchedParam = APP->scene->rack->touchedParam;
if (touchedParam) {
APP->scene->rack->touchedParam = NULL;
int moduleId = touchedParam->paramQuantity->module->id;
int paramId = touchedParam->paramQuantity->paramId;
int moduleId = touchedParam->module->id;
int paramId = touchedParam->paramId;
module->learnParam(id, moduleId, paramId);
}
else {


+ 10
- 10
src/engine/Engine.cpp View File

@@ -555,8 +555,8 @@ void Engine::removeModule(Module* module) {
}
// Check that all cables are disconnected
for (Cable* cable : internal->cables) {
assert(cable->outputModule != module);
assert(cable->inputModule != module);
assert(cable->outputModule != module);
}
// Update ParamHandles' module pointers
for (ParamHandle* paramHandle : internal->paramHandles) {
@@ -657,26 +657,26 @@ static void Engine_updateConnected(Engine* that) {
// Find disconnected ports
std::set<Port*> disconnectedPorts;
for (Module* module : that->internal->modules) {
for (Output& output : module->outputs) {
disconnectedPorts.insert(&output);
}
for (Input& input : module->inputs) {
disconnectedPorts.insert(&input);
}
for (Output& output : module->outputs) {
disconnectedPorts.insert(&output);
}
}
for (Cable* cable : that->internal->cables) {
// Connect output
Output& output = cable->outputModule->outputs[cable->outputId];
auto outputIt = disconnectedPorts.find(&output);
if (outputIt != disconnectedPorts.end())
disconnectedPorts.erase(outputIt);
Port_setConnected(&output);
// Connect input
Input& input = cable->inputModule->inputs[cable->inputId];
auto inputIt = disconnectedPorts.find(&input);
if (inputIt != disconnectedPorts.end())
disconnectedPorts.erase(inputIt);
Port_setConnected(&input);
// Connect output
Output& output = cable->outputModule->outputs[cable->outputId];
auto outputIt = disconnectedPorts.find(&output);
if (outputIt != disconnectedPorts.end())
disconnectedPorts.erase(outputIt);
Port_setConnected(&output);
}
// Disconnect ports that have no cable
for (Port* port : disconnectedPorts) {


+ 18
- 1
src/engine/Module.cpp View File

@@ -14,6 +14,14 @@ Module::~Module() {
if (paramQuantity)
delete paramQuantity;
}
for (PortInfo* inputInfo : inputInfos) {
if (inputInfo)
delete inputInfo;
}
for (PortInfo* outputInfo : outputInfos) {
if (outputInfo)
delete outputInfo;
}
}

void Module::config(int numParams, int numInputs, int numOutputs, int numLights) {
@@ -23,11 +31,20 @@ void Module::config(int numParams, int numInputs, int numOutputs, int numLights)
inputs.resize(numInputs);
outputs.resize(numOutputs);
lights.resize(numLights);
paramQuantities.resize(numParams);
// Initialize paramQuantities
paramQuantities.resize(numParams);
for (int i = 0; i < numParams; i++) {
configParam(i, 0.f, 1.f, 0.f);
}
// Initialize PortInfos
inputInfos.resize(numInputs);
for (int i = 0; i < numInputs; i++) {
configInput(i);
}
outputInfos.resize(numOutputs);
for (int i = 0; i < numOutputs; i++) {
configOutput(i);
}
}

json_t* Module::toJson() {


+ 51
- 37
src/event.cpp View File

@@ -118,16 +118,21 @@ void State::finalizeWidget(widget::Widget* w) {
}

bool State::handleButton(math::Vec pos, int button, int action, int mods) {
// Button
Context cButton;
Button eButton;
eButton.context = &cButton;
eButton.pos = pos;
eButton.button = button;
eButton.action = action;
eButton.mods = mods;
rootWidget->onButton(eButton);
widget::Widget* clickedWidget = cButton.target;
bool cursorLocked = APP->window->isCursorLocked();

widget::Widget* clickedWidget = NULL;
if (!cursorLocked) {
// Button
Context cButton;
Button eButton;
eButton.context = &cButton;
eButton.pos = pos;
eButton.button = button;
eButton.action = action;
eButton.mods = mods;
rootWidget->onButton(eButton);
clickedWidget = cButton.target;
}

if (action == GLFW_PRESS) {
setDragged(clickedWidget, button);
@@ -176,11 +181,15 @@ bool State::handleButton(math::Vec pos, int button, int action, int mods) {
}

bool State::handleHover(math::Vec pos, math::Vec mouseDelta) {
bool cursorLocked = APP->window->isCursorLocked();

// Fake a key RACK_HELD event for each held key
int mods = APP->window->getMods();
for (int key : heldKeys) {
int scancode = glfwGetKeyScancode(key);
handleKey(pos, key, scancode, RACK_HELD, mods);
if (!cursorLocked) {
int mods = APP->window->getMods();
for (int key : heldKeys) {
int scancode = glfwGetKeyScancode(key);
handleKey(pos, key, scancode, RACK_HELD, mods);
}
}

if (draggedWidget) {
@@ -190,31 +199,36 @@ bool State::handleHover(math::Vec pos, math::Vec mouseDelta) {
eDragMove.mouseDelta = mouseDelta;
draggedWidget->onDragMove(eDragMove);

// DragHover
Context cDragHover;
DragHover eDragHover;
eDragHover.context = &cDragHover;
eDragHover.button = dragButton;
eDragHover.pos = pos;
eDragHover.mouseDelta = mouseDelta;
eDragHover.origin = draggedWidget;
rootWidget->onDragHover(eDragHover);

setDragHovered(cDragHover.target);
if (cDragHover.target)
return true;
if (!cursorLocked) {
// DragHover
Context cDragHover;
DragHover eDragHover;
eDragHover.context = &cDragHover;
eDragHover.button = dragButton;
eDragHover.pos = pos;
eDragHover.mouseDelta = mouseDelta;
eDragHover.origin = draggedWidget;
rootWidget->onDragHover(eDragHover);

setDragHovered(cDragHover.target);
if (cDragHover.target)
return true;
}
}

// Hover
Context cHover;
Hover eHover;
eHover.context = &cHover;
eHover.pos = pos;
eHover.mouseDelta = mouseDelta;
rootWidget->onHover(eHover);

setHovered(cHover.target);
return !!cHover.target;
if (!cursorLocked) {
// Hover
Context cHover;
Hover eHover;
eHover.context = &cHover;
eHover.pos = pos;
eHover.mouseDelta = mouseDelta;
rootWidget->onHover(eHover);

setHovered(cHover.target);
return !!cHover.target;
}
return false;
}

bool State::handleLeave() {


+ 4
- 0
src/window.cpp View File

@@ -492,6 +492,10 @@ void Window::cursorUnlock() {
}
}

bool Window::isCursorLocked() {
return glfwGetInputMode(win, GLFW_CURSOR) != GLFW_CURSOR_NORMAL;
}

int Window::getMods() {
int mods = 0;
if (glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS)


Loading…
Cancel
Save