Browse Source

Added SVGKnob, added TextField, PasswordField, migrated to SVGKnobs

tags/v0.3.0
Andrew Belt 7 years ago
parent
commit
71a1b6a987
17 changed files with 275 additions and 61 deletions
  1. +1
    -1
      Makefile
  2. +21
    -30
      include/components.hpp
  3. +5
    -0
      include/rack.hpp
  4. +14
    -0
      include/scene.hpp
  5. +30
    -4
      include/widgets.hpp
  6. +28
    -8
      src/gui.cpp
  7. +7
    -2
      src/widgets/FramebufferWidget.cpp
  8. +3
    -0
      src/widgets/Light.cpp
  9. +1
    -1
      src/widgets/ParamWidget.cpp
  10. +15
    -0
      src/widgets/PasswordField.cpp
  11. +45
    -0
      src/widgets/SVGKnob.cpp
  12. +1
    -2
      src/widgets/SVGWidget.cpp
  13. +95
    -0
      src/widgets/TextField.cpp
  14. +3
    -2
      src/widgets/Toolbar.cpp
  15. +1
    -1
      src/widgets/Tooltip.cpp
  16. +2
    -2
      src/widgets/TransformWidget.cpp
  17. +3
    -8
      src/widgets/WireWidget.cpp

+ 1
- 1
Makefile View File

@@ -1,5 +1,5 @@
ARCH ?= lin
FLAGS = -g -Wall -O3 -msse -mfpmath=sse -ffast-math \
FLAGS = -g -Wall -O3 -msse -mfpmath=sse -ffast-math -fno-finite-math-only \
-I./ext -I./include
CXXFLAGS = -fno-exceptions



+ 21
- 30
include/components.hpp View File

@@ -36,56 +36,47 @@ struct SynthTechAlco : SpriteKnob {
}
};

struct KnobDavies1900h : SpriteKnob {
KnobDavies1900h() {
struct Davies1900hKnob : SVGKnob {
Davies1900hKnob() {
box.size = Vec(36, 36);
spriteOffset = Vec(-2, -2);
spriteSize = Vec(42, 42);
minIndex = 44;
maxIndex = -46;
spriteCount = 120;
minAngle = -0.75*M_PI;
maxAngle = 0.75*M_PI;
}
};

struct KnobDavies1900hWhite : KnobDavies1900h {
KnobDavies1900hWhite() {
spriteImage = Image::load("res/ComponentLibrary/Davies1900hWhite.png");
struct Davies1900hWhiteKnob : Davies1900hKnob {
Davies1900hWhiteKnob() {
setSVG(SVG::load("res/ComponentLibrary/Davies1900hWhite.svg"));
}
};

struct KnobDavies1900hBlack : KnobDavies1900h {
KnobDavies1900hBlack() {
spriteImage = Image::load("res/ComponentLibrary/Davies1900hBlack.png");
struct Davies1900hBlackKnob : Davies1900hKnob {
Davies1900hBlackKnob() {
setSVG(SVG::load("res/ComponentLibrary/Davies1900hBlack.svg"));
}
};

struct KnobDavies1900hRed : KnobDavies1900h {
KnobDavies1900hRed() {
spriteImage = Image::load("res/ComponentLibrary/Davies1900hRed.png");
struct Davies1900hRedKnob : Davies1900hKnob {
Davies1900hRedKnob() {
setSVG(SVG::load("res/ComponentLibrary/Davies1900hRed.svg"));
}
};

struct BefacoBigKnob : SpriteKnob {
struct BefacoBigKnob : SVGKnob {
BefacoBigKnob() {
box.size = Vec(75, 75);
spriteOffset = Vec(-2, -2);
spriteSize = Vec(81, 81);
minIndex = 44;
maxIndex = -46;
spriteCount = 120;
spriteImage = Image::load("res/ComponentLibrary/BefacoBigKnob.png");
minAngle = -0.75*M_PI;
maxAngle = 0.75*M_PI;
setSVG(SVG::load("res/ComponentLibrary/BefacoBigKnob.svg"));
}
};

struct BefacoTinyKnob : SpriteKnob {
struct BefacoTinyKnob : SVGKnob {
BefacoTinyKnob() {
box.size = Vec(26, 26);
spriteOffset = Vec(-2, -2);
spriteSize = Vec(32, 32);
minIndex = 44;
maxIndex = -46;
spriteCount = 120;
spriteImage = Image::load("res/ComponentLibrary/BefacoTinyKnob.png");
minAngle = -0.75*M_PI;
maxAngle = 0.75*M_PI;
setSVG(SVG::load("res/ComponentLibrary/BefacoTinyKnob.svg"));
}
};



+ 5
- 0
include/rack.hpp View File

@@ -1,8 +1,13 @@
#pragma once

#include "math.hpp"
#include "util.hpp"
#include "plugin.hpp"
#include "engine.hpp"
#include "gui.hpp"
#include "scene.hpp"
#include "components.hpp"
#include "dsp.hpp"


namespace rack {


+ 14
- 0
include/scene.hpp View File

@@ -131,6 +131,20 @@ struct SpriteKnob : Knob, SpriteWidget {
void step();
};

/** A knob which rotates an SVG and caches it in a framebuffer */
struct SVGKnob : Knob, FramebufferWidget {
/** Angles in radians */
float minAngle, maxAngle;
/** Not owned */
TransformWidget *tw;
SVGWidget *sw;

SVGKnob();
void setSVG(std::shared_ptr<SVG> svg);
void step();
void onChange();
};

struct Switch : ParamWidget, SpriteWidget {
};



+ 30
- 4
include/widgets.hpp View File

@@ -95,6 +95,10 @@ struct Widget {
virtual void onMouseEnter() {}
/** Called when another widget begins responding to `onMouseMove` events */
virtual void onMouseLeave() {}
virtual void onSelect() {}
virtual void onDeselect() {}
virtual void onText(int codepoint) {}
virtual void onKey(int key) {}
virtual Widget *onScroll(Vec pos, Vec scrollRel);

/** Called when a widget responds to `onMouseDown` for a left button press */
@@ -116,7 +120,7 @@ struct TransformWidget : Widget {
/** The transformation matrix */
float transform[6];
TransformWidget();
void reset();
void identity();
void translate(Vec delta);
void rotate(float angle);
void scale(Vec s);
@@ -175,8 +179,8 @@ struct SpriteWidget : virtual Widget {

struct SVGWidget : virtual Widget {
std::shared_ptr<SVG> svg;
/** Sets the box size to the svg page */
void step();
/** Sets the box size to the svg image size */
void wrap();
void draw(NVGcontext *vg);
};

@@ -185,9 +189,9 @@ When `dirty` is true, `scene` will be re-rendered on the next call to step().
Events are not passed to the underlying scene.
*/
struct FramebufferWidget : virtual Widget {
/** Set this to true to re-render the scene to the framebuffer in the next step() */
bool dirty = true;
/** The root object in the framebuffer scene
Its position is ignored for now, fixed at (0, 0)
The FramebufferWidget owns the pointer
*/
Widget *scene = NULL;
@@ -196,6 +200,9 @@ struct FramebufferWidget : virtual Widget {

FramebufferWidget();
~FramebufferWidget();
void setScene(Widget *w) {
scene = w;
}
void step();
void draw(NVGcontext *vg);
};
@@ -344,6 +351,25 @@ struct ScrollWidget : OpaqueWidget {
Widget *onScroll(Vec pos, Vec scrollRel);
};

struct TextField : OpaqueWidget {
std::string text;
int begin = 0;
int end = 0;

TextField() {
box.size.y = BND_WIDGET_HEIGHT;
}
void draw(NVGcontext *vg);
Widget *onMouseDown(Vec pos, int button);
void onText(int codepoint);
void onKey(int scancode);
void onSelect();
};

struct PasswordField : TextField {
void draw(NVGcontext *vg);
};

struct Tooltip : Widget {
void step();
void draw(NVGcontext *vg);


+ 28
- 8
src/gui.cpp View File

@@ -6,8 +6,7 @@
#include "gui.hpp"
#include "scene.hpp"

// Include implementations here
// By the way, please stop packaging your libraries like this. It's best to use a single source file (e.g. foo.c) and a single header (e.g. foo.h)
// #define NANOVG_GL2_IMPLEMENTATION
#define NANOVG_GL3_IMPLEMENTATION
#include "../ext/nanovg/src/nanovg_gl.h"
#include "../ext/nanovg/src/nanovg_gl_utils.h"
@@ -36,13 +35,22 @@ void mouseButtonCallback(GLFWwindow *window, int button, int action, int mods) {
if (action == GLFW_PRESS) {
// onMouseDown
Widget *w = gScene->onMouseDown(gMousePos, button);
gSelectedWidget = w;

if (button == GLFW_MOUSE_BUTTON_LEFT) {
gDraggedWidget = w;
if (gDraggedWidget) {
if (w) {
// onDragStart
gDraggedWidget->onDragStart();
w->onDragStart();
}
gDraggedWidget = w;

if (w != gSelectedWidget) {
if (gSelectedWidget) {
w->onDeselect();
}
if (w) {
w->onSelect();
}
gSelectedWidget = w;
}
}
}
@@ -127,13 +135,16 @@ void scrollCallback(GLFWwindow *window, double x, double y) {
gScene->onScroll(gMousePos, scrollRel.mult(-95));
}

void charCallback(GLFWwindow *window, unsigned int value) {
void charCallback(GLFWwindow *window, unsigned int codepoint) {
if (gSelectedWidget) {
gSelectedWidget->onText(codepoint);
}
}

static int lastWindowX, lastWindowY, lastWindowWidth, lastWindowHeight;

void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
if (action == GLFW_PRESS) {
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
if (key == GLFW_KEY_F11 || key == GLFW_KEY_ESCAPE) {
// Toggle fullscreen
GLFWmonitor *monitor = glfwGetWindowMonitor(window);
@@ -151,6 +162,11 @@ void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods
glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
}
}
else {
if (gSelectedWidget) {
gSelectedWidget->onKey(key);
}
}
}
}

@@ -181,6 +197,8 @@ void guiInit() {
err = glfwInit();
assert(err);

// glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
// glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
@@ -209,6 +227,7 @@ void guiInit() {
glfwSetWindowSizeLimits(window, 240, 160, GLFW_DONT_CARE, GLFW_DONT_CARE);

// Set up NanoVG
// gVg = nvgCreateGL2(NVG_ANTIALIAS);
gVg = nvgCreateGL3(NVG_ANTIALIAS);
assert(gVg);

@@ -220,6 +239,7 @@ void guiInit() {

void guiDestroy() {
defaultFont.reset();
// nvgDeleteGL2(gVg);
nvgDeleteGL3(gVg);
glfwDestroyWindow(window);
glfwTerminate();


+ 7
- 2
src/widgets/FramebufferWidget.cpp View File

@@ -32,13 +32,16 @@ FramebufferWidget::~FramebufferWidget() {
delete internal;
}

/** A margin in pixels around the scene in the framebuffer */
/** A margin in pixels around the scene in the framebuffer
This prevents cutting the rendered SVG off on the box edges.
*/
static const int margin = 1;

void FramebufferWidget::step() {
if (!scene)
return;

// Step scene before rendering
scene->step();

// Render the scene to the framebuffer if dirty
@@ -73,13 +76,15 @@ void FramebufferWidget::step() {
void FramebufferWidget::draw(NVGcontext *vg) {
if (!internal->fb)
return;
if (!scene)
return;

// Draw framebuffer image
int width, height;
nvgImageSize(vg, internal->fb->image, &width, &height);
nvgBeginPath(vg);
nvgRect(vg, -margin, -margin, width, height);
NVGpaint paint = nvgImagePattern(vg, -margin, -margin, width, height, 0.0, internal->fb->image, 1.0);
NVGpaint paint = nvgImagePattern(vg, -margin + scene->box.pos.x, -margin + scene->box.pos.y, width, height, 0.0, internal->fb->image, 1.0);
nvgFillPaint(vg, paint);
nvgFill(vg);
}


+ 3
- 0
src/widgets/Light.cpp View File

@@ -8,15 +8,18 @@ void Light::draw(NVGcontext *vg) {
NVGcolor colorOutline = nvgLerpRGBA(color, nvgRGBf(0.0, 0.0, 0.0), 0.5);
float radius = box.size.x / 2.0;

// Solid
nvgBeginPath(vg);
nvgCircle(vg, radius, radius, radius);
nvgFillColor(vg, color);
nvgFill(vg);

// Border
nvgStrokeWidth(vg, 1.0);
nvgStrokeColor(vg, colorOutline);
nvgStroke(vg);

// Glow
nvgGlobalCompositeOperation(vg, NVG_LIGHTER);
NVGpaint paint;
NVGcolor icol = color;


+ 1
- 1
src/widgets/ParamWidget.cpp View File

@@ -23,7 +23,7 @@ void ParamWidget::onChange() {
if (!module)
return;

// moduleWidget->module->params[paramId] = value;
// module->params[paramId] = value;
engineSetParamSmooth(module, paramId, value);
}



+ 15
- 0
src/widgets/PasswordField.cpp View File

@@ -0,0 +1,15 @@
#include "widgets.hpp"


namespace rack {


void PasswordField::draw(NVGcontext *vg) {
std::string textTmp = text;
text = std::string(textTmp.size(), '*');
TextField::draw(vg);
text = textTmp;
}


} // namespace rack

+ 45
- 0
src/widgets/SVGKnob.cpp View File

@@ -0,0 +1,45 @@
#include "scene.hpp"


namespace rack {


SVGKnob::SVGKnob() {
tw = new TransformWidget();
setScene(tw);

sw = new SVGWidget();
tw->addChild(sw);
}

void SVGKnob::setSVG(std::shared_ptr<SVG> svg) {
sw->svg = svg;
sw->wrap();
}

void SVGKnob::step() {
// Re-transform TransformWidget if dirty
if (dirty) {
float angle = mapf(value, minValue, maxValue, minAngle, maxAngle);
tw->box.size = box.size;
tw->identity();
// Resize SVG
Vec scale = Vec(box.size.x / sw->box.size.x, box.size.y / sw->box.size.y);
tw->scale(scale);
// Rotate SVG
Vec center = sw->box.getCenter();
tw->translate(center);
tw->rotate(angle);
tw->translate(center.neg());
}
FramebufferWidget::step();
}

void SVGKnob::onChange() {
dirty = true;
ParamWidget::onChange();
}



} // namespace rack

+ 1
- 2
src/widgets/SVGWidget.cpp View File

@@ -79,8 +79,7 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) {
}


void SVGWidget::step() {
// Automatically wrap box size to SVG page size
void SVGWidget::wrap() {
if (svg)
box.size = Vec(svg->handle->width, svg->handle->height);
else


+ 95
- 0
src/widgets/TextField.cpp View File

@@ -0,0 +1,95 @@
#include "widgets.hpp"
// for gVg
#include "gui.hpp"
// for key codes
#include <GLFW/glfw3.h>


namespace rack {


void TextField::draw(NVGcontext *vg) {
BNDwidgetState state;
if (this == gSelectedWidget)
state = BND_ACTIVE;
else if (this == gHoveredWidget)
state = BND_HOVER;
else
state = BND_DEFAULT;

bndTextField(vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_NONE, state, -1, text.c_str(), begin, end);
}

Widget *TextField::onMouseDown(Vec pos, int button) {
end = begin = bndTextFieldTextPosition(gVg, 0.0, 0.0, box.size.x, box.size.y, -1, text.c_str(), pos.x, pos.y);
return OpaqueWidget::onMouseDown(pos, button);
}


void TextField::onText(int codepoint) {
if (begin < end)
text.erase(begin, end - begin);
char c = codepoint;
text.insert(begin, &c, 1);
begin++;
end = begin;
}

void TextField::onKey(int key) {
switch (key) {
case GLFW_KEY_BACKSPACE:
if (begin < end) {
text.erase(begin, end - begin);
}
else {
begin--;
if (begin >= 0)
text.erase(begin, 1);
}
end = begin;
break;
case GLFW_KEY_DELETE:
if (begin < end) {
text.erase(begin, end - begin);
}
else {
text.erase(begin, 1);
}
end = begin;
break;
case GLFW_KEY_LEFT:
if (begin < end) {
}
else {
begin--;
}
end = begin;
break;
case GLFW_KEY_RIGHT:
if (begin < end) {
begin = end;
}
else {
begin++;
}
end = begin;
break;
case GLFW_KEY_HOME:
end = begin = 0;
break;
case GLFW_KEY_END:
end = begin = text.size();
break;
}

begin = mini(maxi(begin, 0), text.size());
end = mini(maxi(end, 0), text.size());
}

void TextField::onSelect() {
begin = 0;
end = text.size();
}


} // namespace rack

+ 3
- 2
src/widgets/Toolbar.cpp View File

@@ -75,8 +75,9 @@ struct SampleRateChoice : ChoiceButton {
menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y));
menu->box.size.x = box.size.x;

float sampleRates[6] = {44100, 48000, 88200, 96000, 176400, 192000};
for (int i = 0; i < 6; i++) {
float sampleRates[] = {44100, 48000, 88200, 96000, 176400, 192000};
int sampleRatesLen = sizeof(sampleRates) / sizeof(sampleRates[0]);
for (int i = 0; i < sampleRatesLen; i++) {
SampleRateItem *item = new SampleRateItem();
item->text = stringf("%.0f Hz", sampleRates[i]);
item->sampleRate = sampleRates[i];


+ 1
- 1
src/widgets/Tooltip.cpp View File

@@ -15,7 +15,7 @@ void Tooltip::step() {


void Tooltip::draw(NVGcontext *vg) {
bndTooltipBackground(vg, box.pos.x, box.pos.y, box.size.x, box.size.y);
bndTooltipBackground(vg, 0.0, 0.0, box.size.x, box.size.y);
Widget::draw(vg);
}



+ 2
- 2
src/widgets/TransformWidget.cpp View File

@@ -5,10 +5,10 @@ namespace rack {


TransformWidget::TransformWidget() {
reset();
identity();
}

void TransformWidget::reset() {
void TransformWidget::identity() {
nvgTransformIdentity(transform);
}



+ 3
- 8
src/widgets/WireWidget.cpp View File

@@ -68,7 +68,7 @@ static void drawWire(NVGcontext *vg, Vec pos1, Vec pos2, NVGcolor color, float t
}


static NVGcolor wireColors[8] = {
static const NVGcolor wireColors[8] = {
nvgRGB(0xc9, 0xb7, 0x0e), // yellow
nvgRGB(0xc9, 0x18, 0x47), // red
nvgRGB(0x0c, 0x8e, 0x15), // green
@@ -82,13 +82,8 @@ static int lastWireColorId = -1;


WireWidget::WireWidget() {
int wireColorId;
do {
wireColorId = randomu32() % 8;
} while (wireColorId == lastWireColorId);
lastWireColorId = wireColorId;

color = wireColors[wireColorId];
lastWireColorId = (lastWireColorId + 1) % 8;
color = wireColors[lastWireColorId];
}

WireWidget::~WireWidget() {


Loading…
Cancel
Save