| @@ -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); | ||||