@@ -8,3 +8,4 @@ | |||||
*.vcv | *.vcv | ||||
/patches | /patches | ||||
/design | /design | ||||
.DS_Store |
@@ -155,6 +155,7 @@ struct Panel : TransparentWidget { | |||||
}; | }; | ||||
struct SVGPanel : FramebufferWidget { | struct SVGPanel : FramebufferWidget { | ||||
void step() override; | |||||
void setBackground(std::shared_ptr<SVG> svg); | void setBackground(std::shared_ptr<SVG> svg); | ||||
}; | }; | ||||
@@ -354,6 +355,7 @@ extern std::string gApplicationVersion; | |||||
extern std::string gApiHost; | extern std::string gApiHost; | ||||
// Easy access to "singleton" widgets | // Easy access to "singleton" widgets | ||||
extern RackScene *gRackScene; | |||||
extern RackWidget *gRackWidget; | extern RackWidget *gRackWidget; | ||||
extern Toolbar *gToolbar; | extern Toolbar *gToolbar; | ||||
@@ -30,8 +30,9 @@ struct Output { | |||||
}; | }; | ||||
struct Light { | struct Light { | ||||
/** The square of the brightness value */ | |||||
float value = 0.0; | float value = 0.0; | ||||
void setSmooth(float v); | |||||
void setBrightnessSmooth(float brightness); | |||||
}; | }; | ||||
@@ -2,7 +2,7 @@ | |||||
#include <stdint.h> | #include <stdint.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <math.h> | |||||
#include <cmath> | |||||
namespace rack { | namespace rack { | ||||
@@ -61,6 +61,10 @@ inline float eucmodf(float a, float base) { | |||||
return mod < 0.0 ? mod + base : mod; | return mod < 0.0 ? mod + base : mod; | ||||
} | } | ||||
inline float nearf(float a, float b, float epsilon = 1e-6) { | |||||
return fabsf(a - b) <= epsilon; | |||||
} | |||||
/** Limits a value between a minimum and maximum | /** Limits a value between a minimum and maximum | ||||
If min > max, the limits are switched | If min > max, the limits are switched | ||||
*/ | */ | ||||
@@ -204,7 +208,7 @@ struct Vec { | |||||
return x == 0.0 && y == 0.0; | return x == 0.0 && y == 0.0; | ||||
} | } | ||||
bool isFinite() { | bool isFinite() { | ||||
return isfinite(x) && isfinite(y); | |||||
return std::isfinite(x) && std::isfinite(y); | |||||
} | } | ||||
Vec clamp(Rect bound); | Vec clamp(Rect bound); | ||||
}; | }; | ||||
@@ -267,6 +271,21 @@ struct Rect { | |||||
r.pos.y = clampf(pos.y, bound.pos.y, bound.pos.y + bound.size.y - size.y); | r.pos.y = clampf(pos.y, bound.pos.y, bound.pos.y + bound.size.y - size.y); | ||||
return r; | return r; | ||||
} | } | ||||
/** Expands this Rect to contain `other` */ | |||||
Rect expand(Rect other) { | |||||
Rect r; | |||||
r.pos.x = fminf(pos.x, other.pos.x); | |||||
r.pos.y = fminf(pos.y, other.pos.y); | |||||
r.size.x = fmaxf(pos.x + size.x, other.pos.x + other.size.x) - r.pos.x; | |||||
r.size.y = fmaxf(pos.y + size.y, other.pos.y + other.size.y) - r.pos.y; | |||||
return r; | |||||
} | |||||
/** Returns a Rect with its position set to zero */ | |||||
Rect zeroPos() { | |||||
Rect r; | |||||
r.size = size; | |||||
return r; | |||||
} | |||||
}; | }; | ||||
@@ -56,8 +56,13 @@ struct Widget { | |||||
virtual ~Widget(); | virtual ~Widget(); | ||||
Vec getAbsolutePos(); | |||||
Rect getChildrenBoundingBox(); | Rect getChildrenBoundingBox(); | ||||
/** Returns `v` transformed into the coordinate system of `relative` */ | |||||
virtual Vec getRelativeOffset(Vec v, Widget *relative); | |||||
/** Returns `v` transformed into world coordinates */ | |||||
Vec getAbsoluteOffset(Vec v) { | |||||
return getRelativeOffset(v, NULL); | |||||
} | |||||
/** Returns a subset of the given Rect bounded by the box of this widget and all ancestors */ | /** Returns a subset of the given Rect bounded by the box of this widget and all ancestors */ | ||||
virtual Rect getViewport(Rect r); | virtual Rect getViewport(Rect r); | ||||
@@ -149,6 +154,7 @@ struct TransformWidget : Widget { | |||||
struct ZoomWidget : Widget { | struct ZoomWidget : Widget { | ||||
float zoom = 1.0; | float zoom = 1.0; | ||||
Vec getRelativeOffset(Vec v, Widget *relative) override; | |||||
Rect getViewport(Rect r) override; | Rect getViewport(Rect r) override; | ||||
void setZoom(float zoom); | void setZoom(float zoom); | ||||
void draw(NVGcontext *vg) override; | void draw(NVGcontext *vg) override; | ||||
@@ -230,6 +236,10 @@ Events are not passed to the underlying scene. | |||||
struct FramebufferWidget : virtual Widget { | struct FramebufferWidget : virtual Widget { | ||||
/** Set this to true to re-render the children to the framebuffer the next time it is drawn */ | /** Set this to true to re-render the children to the framebuffer the next time it is drawn */ | ||||
bool dirty = true; | bool dirty = true; | ||||
/** A margin in pixels around the children in the framebuffer | |||||
This prevents cutting the rendered SVG off on the box edges. | |||||
*/ | |||||
float oversample; | |||||
/** The root object in the framebuffer scene | /** The root object in the framebuffer scene | ||||
The FramebufferWidget owns the pointer | The FramebufferWidget owns the pointer | ||||
*/ | */ | ||||
@@ -14,10 +14,12 @@ std::string gApiHost = "http://api.vcvrack.com"; | |||||
RackWidget *gRackWidget = NULL; | RackWidget *gRackWidget = NULL; | ||||
Toolbar *gToolbar = NULL; | Toolbar *gToolbar = NULL; | ||||
RackScene *gRackScene = NULL; | |||||
void sceneInit() { | void sceneInit() { | ||||
gScene = new RackScene(); | |||||
gRackScene = new RackScene(); | |||||
gScene = gRackScene; | |||||
} | } | ||||
void sceneDestroy() { | void sceneDestroy() { | ||||
@@ -62,9 +62,6 @@ void RackScene::step() { | |||||
.plus(Vec(500, 500)) | .plus(Vec(500, 500)) | ||||
.div(zoomWidget->zoom); | .div(zoomWidget->zoom); | ||||
// Set zoom from the toolbar's zoom slider | |||||
zoomWidget->setZoom(gToolbar->zoomSlider->value / 100.0); | |||||
Scene::step(); | Scene::step(); | ||||
zoomWidget->box.size = gRackWidget->box.size.mult(zoomWidget->zoom); | zoomWidget->box.size = gRackWidget->box.size.mult(zoomWidget->zoom); | ||||
@@ -6,7 +6,7 @@ namespace rack { | |||||
void RackScrollWidget::step() { | void RackScrollWidget::step() { | ||||
Vec pos = gMousePos.minus(getAbsolutePos()); | |||||
Vec pos = gRackWidget->lastMousePos; | |||||
// Scroll rack if dragging cable near the edge of the screen | // Scroll rack if dragging cable near the edge of the screen | ||||
if (gRackWidget->wireContainer->activeWire) { | if (gRackWidget->wireContainer->activeWire) { | ||||
float margin = 20.0; | float margin = 20.0; | ||||
@@ -17,6 +17,7 @@ namespace rack { | |||||
RackWidget::RackWidget() { | RackWidget::RackWidget() { | ||||
rails = new FramebufferWidget(); | rails = new FramebufferWidget(); | ||||
rails->box.size = Vec(); | rails->box.size = Vec(); | ||||
rails->oversample = 1.0; | |||||
{ | { | ||||
RackRail *rail = new RackRail(); | RackRail *rail = new RackRail(); | ||||
rail->box.size = Vec(); | rail->box.size = Vec(); | ||||
@@ -1,4 +1,5 @@ | |||||
#include "app.hpp" | #include "app.hpp" | ||||
#include "gui.hpp" | |||||
namespace rack { | namespace rack { | ||||
@@ -16,6 +17,13 @@ struct PanelBorder : TransparentWidget { | |||||
}; | }; | ||||
void SVGPanel::step() { | |||||
if (nearf(gPixelRatio, 1.0)) { | |||||
oversample = 2.0; | |||||
} | |||||
FramebufferWidget::step(); | |||||
} | |||||
void SVGPanel::setBackground(std::shared_ptr<SVG> svg) { | void SVGPanel::setBackground(std::shared_ptr<SVG> svg) { | ||||
clearChildren(); | clearChildren(); | ||||
@@ -39,7 +39,7 @@ struct QuitItem : MenuItem { | |||||
struct FileChoice : ChoiceButton { | struct FileChoice : ChoiceButton { | ||||
void onAction() override { | void onAction() override { | ||||
Menu *menu = gScene->createMenu(); | Menu *menu = gScene->createMenu(); | ||||
menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); | |||||
menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)); | |||||
menu->box.size.x = box.size.x; | menu->box.size.x = box.size.x; | ||||
{ | { | ||||
@@ -70,7 +70,7 @@ struct SampleRateItem : MenuItem { | |||||
struct SampleRateChoice : ChoiceButton { | struct SampleRateChoice : ChoiceButton { | ||||
void onAction() override { | void onAction() override { | ||||
Menu *menu = gScene->createMenu(); | Menu *menu = gScene->createMenu(); | ||||
menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); | |||||
menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)); | |||||
menu->box.size.x = box.size.x; | menu->box.size.x = box.size.x; | ||||
PauseItem *pauseItem = new PauseItem(); | PauseItem *pauseItem = new PauseItem(); | ||||
@@ -148,12 +148,18 @@ Toolbar::Toolbar() { | |||||
xPos += margin; | xPos += margin; | ||||
{ | { | ||||
zoomSlider = new Slider(); | |||||
struct ZoomSlider : Slider { | |||||
void onAction() override { | |||||
Slider::onAction(); | |||||
gRackScene->zoomWidget->setZoom(value / 100.0); | |||||
} | |||||
}; | |||||
zoomSlider = new ZoomSlider(); | |||||
zoomSlider->box.pos = Vec(xPos, margin); | zoomSlider->box.pos = Vec(xPos, margin); | ||||
zoomSlider->box.size.x = 150; | zoomSlider->box.size.x = 150; | ||||
zoomSlider->label = "Zoom"; | zoomSlider->label = "Zoom"; | ||||
zoomSlider->unit = "%"; | zoomSlider->unit = "%"; | ||||
zoomSlider->setLimits(33.33, 200.0); | |||||
zoomSlider->setLimits(25.0, 200.0); | |||||
zoomSlider->setDefaultValue(100.0); | zoomSlider->setDefaultValue(100.0); | ||||
addChild(zoomSlider); | addChild(zoomSlider); | ||||
xPos += zoomSlider->box.size.x; | xPos += zoomSlider->box.size.x; | ||||
@@ -124,31 +124,27 @@ void WireWidget::updateWire() { | |||||
} | } | ||||
Vec WireWidget::getOutputPos() { | Vec WireWidget::getOutputPos() { | ||||
Vec pos; | |||||
if (outputPort) { | if (outputPort) { | ||||
pos = Rect(outputPort->getAbsolutePos(), outputPort->box.size).getCenter(); | |||||
return outputPort->getRelativeOffset(outputPort->box.zeroPos().getCenter(), gRackWidget); | |||||
} | } | ||||
else if (hoveredOutputPort) { | else if (hoveredOutputPort) { | ||||
pos = Rect(hoveredOutputPort->getAbsolutePos(), hoveredOutputPort->box.size).getCenter(); | |||||
return hoveredOutputPort->getRelativeOffset(hoveredOutputPort->box.zeroPos().getCenter(), gRackWidget); | |||||
} | } | ||||
else { | else { | ||||
return gRackWidget->lastMousePos; | return gRackWidget->lastMousePos; | ||||
} | } | ||||
return pos.minus(getAbsolutePos().minus(box.pos)); | |||||
} | } | ||||
Vec WireWidget::getInputPos() { | Vec WireWidget::getInputPos() { | ||||
Vec pos; | |||||
if (inputPort) { | if (inputPort) { | ||||
pos = Rect(inputPort->getAbsolutePos(), inputPort->box.size).getCenter(); | |||||
return inputPort->getRelativeOffset(inputPort->box.zeroPos().getCenter(), gRackWidget); | |||||
} | } | ||||
else if (hoveredInputPort) { | else if (hoveredInputPort) { | ||||
pos = Rect(hoveredInputPort->getAbsolutePos(), hoveredInputPort->box.size).getCenter(); | |||||
return hoveredInputPort->getRelativeOffset(hoveredInputPort->box.zeroPos().getCenter(), gRackWidget); | |||||
} | } | ||||
else { | else { | ||||
return gRackWidget->lastMousePos; | return gRackWidget->lastMousePos; | ||||
} | } | ||||
return pos.minus(getAbsolutePos().minus(box.pos)); | |||||
} | } | ||||
void WireWidget::draw(NVGcontext *vg) { | void WireWidget::draw(NVGcontext *vg) { | ||||
@@ -335,7 +335,7 @@ struct AudioChoice : ChoiceButton { | |||||
AudioInterface *audioInterface; | AudioInterface *audioInterface; | ||||
void onAction() override { | void onAction() override { | ||||
Menu *menu = gScene->createMenu(); | Menu *menu = gScene->createMenu(); | ||||
menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); | |||||
menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); | |||||
menu->box.size.x = box.size.x; | menu->box.size.x = box.size.x; | ||||
int deviceCount = audioInterface->getDeviceCount(); | int deviceCount = audioInterface->getDeviceCount(); | ||||
@@ -373,7 +373,7 @@ struct SampleRateChoice : ChoiceButton { | |||||
AudioInterface *audioInterface; | AudioInterface *audioInterface; | ||||
void onAction() override { | void onAction() override { | ||||
Menu *menu = gScene->createMenu(); | Menu *menu = gScene->createMenu(); | ||||
menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); | |||||
menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); | |||||
menu->box.size.x = box.size.x; | menu->box.size.x = box.size.x; | ||||
const float sampleRates[6] = {44100, 48000, 88200, 96000, 176400, 192000}; | const float sampleRates[6] = {44100, 48000, 88200, 96000, 176400, 192000}; | ||||
@@ -404,7 +404,7 @@ struct BlockSizeChoice : ChoiceButton { | |||||
AudioInterface *audioInterface; | AudioInterface *audioInterface; | ||||
void onAction() override { | void onAction() override { | ||||
Menu *menu = gScene->createMenu(); | Menu *menu = gScene->createMenu(); | ||||
menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); | |||||
menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); | |||||
menu->box.size.x = box.size.x; | menu->box.size.x = box.size.x; | ||||
const int blockSizes[] = {64, 128, 256, 512, 1024, 2048, 4096}; | const int blockSizes[] = {64, 128, 256, 512, 1024, 2048, 4096}; | ||||
@@ -205,7 +205,7 @@ struct ClockRatioChoice : ChoiceButton { | |||||
void onAction() { | void onAction() { | ||||
Menu *menu = gScene->createMenu(); | Menu *menu = gScene->createMenu(); | ||||
menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); | |||||
menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); | |||||
menu->box.size.x = box.size.x; | menu->box.size.x = box.size.x; | ||||
for (unsigned long ratio = 0; ratio < ratioNames.size(); ratio++) { | for (unsigned long ratio = 0; ratio < ratioNames.size(); ratio++) { | ||||
@@ -181,7 +181,7 @@ void MidiItem::onAction() { | |||||
void MidiChoice::onAction() { | void MidiChoice::onAction() { | ||||
Menu *menu = gScene->createMenu(); | Menu *menu = gScene->createMenu(); | ||||
menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); | |||||
menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); | |||||
menu->box.size.x = box.size.x; | menu->box.size.x = box.size.x; | ||||
{ | { | ||||
@@ -216,7 +216,7 @@ void ChannelItem::onAction() { | |||||
void ChannelChoice::onAction() { | void ChannelChoice::onAction() { | ||||
Menu *menu = gScene->createMenu(); | Menu *menu = gScene->createMenu(); | ||||
menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); | |||||
menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); | |||||
menu->box.size.x = box.size.x; | menu->box.size.x = box.size.x; | ||||
{ | { | ||||
@@ -256,7 +256,7 @@ struct ModeChoice : ChoiceButton { | |||||
void onAction() { | void onAction() { | ||||
Menu *menu = gScene->createMenu(); | Menu *menu = gScene->createMenu(); | ||||
menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); | |||||
menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); | |||||
menu->box.size.x = box.size.x; | menu->box.size.x = box.size.x; | ||||
for (unsigned long i = 0; i < modeNames.size(); i++) { | for (unsigned long i = 0; i < modeNames.size(); i++) { | ||||
@@ -33,8 +33,8 @@ static int smoothParamId; | |||||
static float smoothValue; | static float smoothValue; | ||||
void Light::setSmooth(float v) { | |||||
value += (v - value) / sampleRate * 60.0; | |||||
void Light::setBrightnessSmooth(float brightness) { | |||||
value += (powf(brightness, 2) - value) / sampleRate * 60.0; | |||||
} | } | ||||
@@ -218,9 +218,6 @@ void errorCallback(int error, const char *description) { | |||||
void renderGui() { | void renderGui() { | ||||
int width, height; | int width, height; | ||||
glfwGetFramebufferSize(gWindow, &width, &height); | glfwGetFramebufferSize(gWindow, &width, &height); | ||||
int windowWidth, windowHeight; | |||||
glfwGetWindowSize(gWindow, &windowWidth, &windowHeight); | |||||
gPixelRatio = (float)width / windowWidth; | |||||
// Update and render | // Update and render | ||||
nvgBeginFrame(gVg, width, height, gPixelRatio); | nvgBeginFrame(gVg, width, height, gPixelRatio); | ||||
@@ -257,7 +254,7 @@ void guiInit() { | |||||
glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE); | glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE); | ||||
gWindow = glfwCreateWindow(640, 480, "", NULL, NULL); | gWindow = glfwCreateWindow(640, 480, "", NULL, NULL); | ||||
if (!gWindow) { | if (!gWindow) { | ||||
osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Cannot open window with OpenGL 2.0 renderer. Does your graphics card support OpenGL 2.0 or greater? If so, are the latest drivers installed?"); | |||||
osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Cannot open window with OpenGL 2.0 renderer. Does your graphics card support OpenGL 2.0 or greater? If so, make sure you have the latest graphics drivers installed."); | |||||
exit(1); | exit(1); | ||||
} | } | ||||
@@ -277,7 +274,7 @@ void guiInit() { | |||||
glewExperimental = GL_TRUE; | glewExperimental = GL_TRUE; | ||||
err = glewInit(); | err = glewInit(); | ||||
if (err != GLEW_OK) { | if (err != GLEW_OK) { | ||||
osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Could not initialize GLEW. Does your graphics card support OpenGL 2.0 or greater? If so, are the latest drivers installed?"); | |||||
osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Could not initialize GLEW. Does your graphics card support OpenGL 2.0 or greater? If so, make sure you have the latest graphics drivers installed."); | |||||
exit(1); | exit(1); | ||||
} | } | ||||
@@ -341,6 +338,13 @@ void guiRun() { | |||||
} | } | ||||
glfwSetWindowTitle(gWindow, title.c_str()); | glfwSetWindowTitle(gWindow, title.c_str()); | ||||
// Get framebuffer size | |||||
int width, height; | |||||
glfwGetFramebufferSize(gWindow, &width, &height); | |||||
int windowWidth, windowHeight; | |||||
glfwGetWindowSize(gWindow, &windowWidth, &windowHeight); | |||||
gPixelRatio = (float)width / windowWidth; | |||||
// Step scene | // Step scene | ||||
gScene->step(); | gScene->step(); | ||||
@@ -28,7 +28,7 @@ static json_t *settingsToJson() { | |||||
json_object_set_new(rootJ, "wireTension", tensionJ); | json_object_set_new(rootJ, "wireTension", tensionJ); | ||||
// zoom | // zoom | ||||
float zoom = gToolbar->zoomSlider->value; | |||||
float zoom = gRackScene->zoomWidget->zoom; | |||||
json_t *zoomJ = json_real(zoom); | json_t *zoomJ = json_real(zoom); | ||||
json_object_set_new(rootJ, "zoom", zoomJ); | json_object_set_new(rootJ, "zoom", zoomJ); | ||||
@@ -69,8 +69,10 @@ static void settingsFromJson(json_t *rootJ) { | |||||
// zoom | // zoom | ||||
json_t *zoomJ = json_object_get(rootJ, "zoom"); | json_t *zoomJ = json_object_get(rootJ, "zoom"); | ||||
if (zoomJ) | |||||
gToolbar->zoomSlider->value = json_number_value(zoomJ); | |||||
if (zoomJ) { | |||||
gRackScene->zoomWidget->setZoom(clampf(json_number_value(zoomJ), 0.25, 4.0)); | |||||
gToolbar->zoomSlider->setValue(json_number_value(zoomJ) * 100.0); | |||||
} | |||||
// allowCursorLock | // allowCursorLock | ||||
json_t *allowCursorLockJ = json_object_get(rootJ, "allowCursorLock"); | json_t *allowCursorLockJ = json_object_get(rootJ, "allowCursorLock"); | ||||
@@ -9,12 +9,6 @@ | |||||
namespace rack { | namespace rack { | ||||
/** A margin in pixels around the children in the framebuffer | |||||
This prevents cutting the rendered SVG off on the box edges. | |||||
*/ | |||||
static const float oversample = 2.0; | |||||
struct FramebufferWidget::Internal { | struct FramebufferWidget::Internal { | ||||
NVGLUframebuffer *fb = NULL; | NVGLUframebuffer *fb = NULL; | ||||
Rect box; | Rect box; | ||||
@@ -31,6 +25,7 @@ struct FramebufferWidget::Internal { | |||||
FramebufferWidget::FramebufferWidget() { | FramebufferWidget::FramebufferWidget() { | ||||
oversample = 1.0; | |||||
internal = new Internal(); | internal = new Internal(); | ||||
} | } | ||||
@@ -65,6 +60,7 @@ void FramebufferWidget::draw(NVGcontext *vg) { | |||||
if (fbSize.x <= 0.0 || fbSize.y <= 0.0) | if (fbSize.x <= 0.0 || fbSize.y <= 0.0) | ||||
return; | return; | ||||
// printf("rendering framebuffer %f %f\n", fbSize.x, fbSize.y); | |||||
// Delete old one first to free up GPU memory | // Delete old one first to free up GPU memory | ||||
internal->setFramebuffer(NULL); | internal->setFramebuffer(NULL); | ||||
// Create a framebuffer from the main nanovg context. We will draw to this in the secondary nanovg context. | // Create a framebuffer from the main nanovg context. We will draw to this in the secondary nanovg context. | ||||
@@ -201,6 +201,7 @@ void SVGWidget::wrap() { | |||||
void SVGWidget::draw(NVGcontext *vg) { | void SVGWidget::draw(NVGcontext *vg) { | ||||
if (svg && svg->handle) { | if (svg && svg->handle) { | ||||
// printf("drawing svg %f %f\n", box.size.x, box.size.y); | |||||
drawSVG(vg, svg->handle); | drawSVG(vg, svg->handle); | ||||
} | } | ||||
} | } | ||||
@@ -23,11 +23,13 @@ void Slider::onDragMove(Vec mouseRel) { | |||||
void Slider::onDragEnd() { | void Slider::onDragEnd() { | ||||
state = BND_DEFAULT; | state = BND_DEFAULT; | ||||
guiCursorUnlock(); | guiCursorUnlock(); | ||||
onAction(); | |||||
} | } | ||||
void Slider::onMouseDownOpaque(int button) { | void Slider::onMouseDownOpaque(int button) { | ||||
if (button == 1) { | if (button == 1) { | ||||
setValue(defaultValue); | setValue(defaultValue); | ||||
onAction(); | |||||
} | } | ||||
} | } | ||||
@@ -6,7 +6,7 @@ namespace rack { | |||||
void Tooltip::step() { | void Tooltip::step() { | ||||
// Follow the mouse | // Follow the mouse | ||||
box.pos = gMousePos.minus(parent->getAbsolutePos()); | |||||
box.pos = gMousePos; | |||||
// Wrap size to contents | // Wrap size to contents | ||||
// box.size = getChildrenBoundingBox().getBottomRight(); | // box.size = getChildrenBoundingBox().getBottomRight(); | ||||
@@ -16,28 +16,28 @@ Widget::~Widget() { | |||||
clearChildren(); | clearChildren(); | ||||
} | } | ||||
Vec Widget::getAbsolutePos() { | |||||
// Recursively compute position offset from parents | |||||
if (!parent) { | |||||
return box.pos; | |||||
} | |||||
else { | |||||
return box.pos.plus(parent->getAbsolutePos()); | |||||
Rect Widget::getChildrenBoundingBox() { | |||||
Rect bound; | |||||
for (Widget *child : children) { | |||||
if (child == children.front()) { | |||||
bound = child->box; | |||||
} | |||||
else { | |||||
bound = bound.expand(child->box); | |||||
} | |||||
} | } | ||||
return bound; | |||||
} | } | ||||
Rect Widget::getChildrenBoundingBox() { | |||||
if (children.empty()) { | |||||
return Rect(); | |||||
Vec Widget::getRelativeOffset(Vec v, Widget *relative) { | |||||
if (this == relative) { | |||||
return v; | |||||
} | } | ||||
Vec topLeft = Vec(INFINITY, INFINITY); | |||||
Vec bottomRight = Vec(-INFINITY, -INFINITY); | |||||
for (Widget *child : children) { | |||||
topLeft = topLeft.min(child->box.pos); | |||||
bottomRight = bottomRight.max(child->box.getBottomRight()); | |||||
v = v.plus(box.pos); | |||||
if (parent) { | |||||
v = parent->getRelativeOffset(v, relative); | |||||
} | } | ||||
return Rect(topLeft, bottomRight.minus(topLeft)); | |||||
return v; | |||||
} | } | ||||
Rect Widget::getViewport(Rect r) { | Rect Widget::getViewport(Rect r) { | ||||
@@ -3,6 +3,11 @@ | |||||
namespace rack { | namespace rack { | ||||
Vec ZoomWidget::getRelativeOffset(Vec v, Widget *relative) { | |||||
return Widget::getRelativeOffset(v.mult(zoom), relative); | |||||
} | |||||
Rect ZoomWidget::getViewport(Rect r) { | Rect ZoomWidget::getViewport(Rect r) { | ||||
r.pos = r.pos.mult(zoom); | r.pos = r.pos.mult(zoom); | ||||
r.size = r.size.mult(zoom); | r.size = r.size.mult(zoom); | ||||