Browse Source

Added AddModuleWindow

tags/v0.5.0
Andrew Belt 7 years ago
parent
commit
5567c5f22d
18 changed files with 426 additions and 175 deletions
  1. +1
    -1
      ext/oui-blendish
  2. +7
    -0
      include/app.hpp
  3. +2
    -2
      include/math.hpp
  4. +2
    -0
      include/util.hpp
  5. +18
    -4
      include/widgets.hpp
  6. +295
    -0
      src/app/AddModuleWindow.cpp
  7. +9
    -130
      src/app/RackWidget.cpp
  8. +6
    -0
      src/gui.cpp
  9. +11
    -0
      src/util.cpp
  10. +0
    -4
      src/widgets/Menu.cpp
  11. +0
    -18
      src/widgets/MenuEntry.cpp
  12. +10
    -0
      src/widgets/MenuItem.cpp
  13. +9
    -0
      src/widgets/MenuLabel.cpp
  14. +10
    -5
      src/widgets/MenuOverlay.cpp
  15. +1
    -0
      src/widgets/Scene.cpp
  16. +0
    -10
      src/widgets/ScrollBar.cpp
  17. +28
    -1
      src/widgets/ScrollWidget.cpp
  18. +17
    -0
      src/widgets/Window.cpp

+ 1
- 1
ext/oui-blendish

@@ -1 +1 @@
Subproject commit ac99b8dd4d36ac314dad6d700701e39484c60e06
Subproject commit 7c6038857b1bb6299c2d20ff9f31248f2d53ba39

+ 7
- 0
include/app.hpp View File

@@ -153,6 +153,13 @@ struct RackRail : TransparentWidget {
void draw(NVGcontext *vg) override;
};

struct AddModuleWindow : Window {
Vec modulePos;

AddModuleWindow();
void step() override;
};

struct Panel : TransparentWidget {
NVGcolor backgroundColor;
std::shared_ptr<Image> backgroundImage;


+ 2
- 2
include/math.hpp View File

@@ -66,10 +66,10 @@ inline float nearf(float a, float b, float epsilon = 1e-6) {
}

/** Limits a value between a minimum and maximum
If min > max, the limits are switched
If min > max, returns min
*/
inline float clampf(float x, float min, float max) {
return fmaxf(fminf(x, fmaxf(min, max)), fminf(min, max));
return fmaxf(fminf(x, max), min);
}

/** If the magnitude of x if less than eps, return 0 */


+ 2
- 0
include/util.hpp View File

@@ -72,6 +72,8 @@ float randomNormal();

/** Converts a printf format string and optional arguments into a std::string */
std::string stringf(const char *format, ...);
std::string tolower(std::string s);
std::string toupper(std::string s);

/** Truncates and adds "..." to a string, not exceeding `len` characters */
std::string ellipsize(std::string s, size_t len);


+ 18
- 4
include/widgets.hpp View File

@@ -292,10 +292,10 @@ struct Label : Widget {
void draw(NVGcontext *vg) override;
};

// Deletes itself from parent when clicked
/** Deletes itself from parent when clicked */
struct MenuOverlay : OpaqueWidget {
void step() override;
void onDragDrop(EventDragDrop &e) override;
void onScroll(EventScroll &e) override;
void onHoverKey(EventHoverKey &e) override;
};

@@ -323,24 +323,34 @@ struct Menu : OpaqueWidget {

struct MenuEntry : OpaqueWidget {
std::string text;
std::string rightText;
MenuEntry() {
box.size = Vec(0, BND_WIDGET_HEIGHT);
}
void step() override;
};

struct MenuLabel : MenuEntry {
void draw(NVGcontext *vg) override;
void step() override;
};

struct MenuItem : MenuEntry {
std::string rightText;
void draw(NVGcontext *vg) override;
void step() override;
virtual Menu *createChildMenu() {return NULL;}
void onMouseEnter(EventMouseEnter &e) override;
void onDragDrop(EventDragDrop &e) override;
};

struct WindowOverlay : OpaqueWidget {
};

struct Window : OpaqueWidget {
std::string title;
void draw(NVGcontext *vg) override;
void onDragMove(EventDragMove &e) override;
};

struct Button : OpaqueWidget {
std::string text;
BNDwidgetState state = BND_DEFAULT;
@@ -389,6 +399,8 @@ struct Slider : OpaqueWidget, QuantityWidget {
struct ScrollBar : OpaqueWidget {
enum { VERTICAL, HORIZONTAL } orientation;
BNDwidgetState state = BND_DEFAULT;
float offset = 0.0;
float size = 0.0;

ScrollBar() {
box.size = Vec(BND_SCROLLBAR_WIDTH, BND_SCROLLBAR_HEIGHT);
@@ -407,7 +419,9 @@ struct ScrollWidget : OpaqueWidget {
Vec offset;

ScrollWidget();
void draw(NVGcontext *vg) override;
void step() override;
void onMouseMove(EventMouseMove &e) override;
void onScroll(EventScroll &e) override;
};



+ 295
- 0
src/app/AddModuleWindow.cpp View File

@@ -0,0 +1,295 @@
#include "app.hpp"
#include "plugin.hpp"
#include <thread>
#include <set>
#include <algorithm>


namespace rack {


static std::string sManufacturer;
static Model *sModel = NULL;
static std::string sFilter;


struct ListMenu : OpaqueWidget {
void draw(NVGcontext *vg) override {
Widget::draw(vg);
}

void step() override {
Widget::step();

box.size.y = 0;
for (Widget *child : children) {
if (!child->visible)
continue;
// Increase height, set position of child
child->box.pos = Vec(0, box.size.y);
box.size.y += child->box.size.y;
child->box.size.x = box.size.x;
}
}
};


struct UrlItem : MenuItem {
std::string url;
void onAction(EventAction &e) override {
std::thread t(openBrowser, url);
t.detach();
}
};


struct MetadataMenu : ListMenu {
Model *model = NULL;

void step() override {
if (model != sModel) {
clearChildren();

if (sModel) {
// Tag list
if (!sModel->tags.empty()) {
for (ModelTag tag : sModel->tags) {
addChild(construct<MenuLabel>(&MenuEntry::text, gTagNames[tag]));
}
addChild(construct<MenuEntry>());
}

// Plugin name
std::string pluginName = sModel->plugin->slug;
if (!sModel->plugin->version.empty()) {
pluginName += " v";
pluginName += sModel->plugin->version;
}
addChild(construct<MenuLabel>(&MenuEntry::text, pluginName));

// Plugin metadata
if (!sModel->plugin->website.empty()) {
addChild(construct<UrlItem>(&MenuEntry::text, "Website", &UrlItem::url, sModel->plugin->path));
}
if (!sModel->plugin->manual.empty()) {
addChild(construct<UrlItem>(&MenuEntry::text, "Manual", &UrlItem::url, sModel->plugin->manual));
}
if (!sModel->plugin->path.empty()) {
addChild(construct<UrlItem>(&MenuEntry::text, "Browse directory", &UrlItem::url, sModel->plugin->path));
}
}
model = sModel;
}

ListMenu::step();
}
};


static bool isModelMatch(Model *model, std::string search) {
// Build content string
std::string str;
str += model->manufacturer;
str += " ";
str += model->name;
str += " ";
str += model->slug;
for (ModelTag tag : model->tags) {
str += " ";
str += gTagNames[tag];
}
str = tolower(str);
search = tolower(search);
return (str.find(search) != std::string::npos);
}


struct ModelItem : MenuItem {
Model *model;
void onAction(EventAction &e) override {
ModuleWidget *moduleWidget = model->createModuleWidget();
gRackWidget->moduleContainer->addChild(moduleWidget);
// Move module nearest to the mouse position
Rect box;
box.size = moduleWidget->box.size;
AddModuleWindow *w = getAncestorOfType<AddModuleWindow>();
box.pos = w->modulePos.minus(box.getCenter());
gRackWidget->requestModuleBoxNearest(moduleWidget, box);
}
void onMouseEnter(EventMouseEnter &e) override {
sModel = model;
MenuItem::onMouseEnter(e);
}
};


struct ModelMenu : ListMenu {
std::string manufacturer;
std::string filter;

void step() override {
if (manufacturer != sManufacturer) {
clearChildren();
// Add models for the selected manufacturer
for (Plugin *plugin : gPlugins) {
for (Model *model : plugin->models) {
if (model->manufacturer == sManufacturer) {
addChild(construct<ModelItem>(&MenuEntry::text, model->name, &ModelItem::model, model));
}
}
}
manufacturer = sManufacturer;
filter = "";
}

if (filter != sFilter) {
// Make all children invisible
for (Widget *child : children) {
child->visible = false;
}
// Make children with a matching model visible
for (Widget *child : children) {
ModelItem *item = dynamic_cast<ModelItem*>(child);
if (!item)
continue;

if (isModelMatch(item->model, sFilter)) {
item->visible = true;
}
}
filter = sFilter;
}

ListMenu::step();
}
};


struct ManufacturerItem : MenuItem {
Model *model;
void onAction(EventAction &e) override {
e.consumed = false;
}
void onMouseEnter(EventMouseEnter &e) override {
sManufacturer = text;
MenuItem::onMouseEnter(e);
}
};


struct ManufacturerMenu : ListMenu {
std::string filter;

ManufacturerMenu() {
// Collect manufacturer names
std::set<std::string> manufacturers;
for (Plugin *plugin : gPlugins) {
for (Model *model : plugin->models) {
manufacturers.insert(model->manufacturer);
}
}
// Add menu item for each manufacturer name
for (std::string manufacturer : manufacturers) {
addChild(construct<ManufacturerItem>(&MenuEntry::text, manufacturer));
}
}

void step() override {
if (filter != sFilter) {
// Make all children invisible
for (Widget *child : children) {
child->visible = false;
}
// Make children with a matching model visible
for (Widget *child : children) {
MenuItem *item = dynamic_cast<MenuItem*>(child);
assert(item);

std::string manufacturer = item->text;
for (Plugin *plugin : gPlugins) {
for (Model *model : plugin->models) {
if (model->manufacturer == manufacturer) {
if (isModelMatch(model, sFilter)) {
item->visible = true;
}
}
}
}
}
filter = sFilter;
}

ListMenu::step();
}
};


struct SearchModuleField : TextField {
void onTextChange() override {
sFilter = text;
}
};


AddModuleWindow::AddModuleWindow() {
box.size = Vec(600, 300);
title = "Add module";

float posY = BND_NODE_TITLE_HEIGHT;

// Search
SearchModuleField *searchField = new SearchModuleField();
searchField->box.pos.y = posY;
posY += searchField->box.size.y;
searchField->box.size.x = box.size.x;
searchField->text = sFilter;
gFocusedWidget = searchField;
{
EventFocus eFocus;
searchField->onFocus(eFocus);
searchField->onTextChange();
}
addChild(searchField);

// Manufacturers
ManufacturerMenu *manufacturerMenu = new ManufacturerMenu();
manufacturerMenu->box.size.x = 200;

ScrollWidget *manufacturerScroll = new ScrollWidget();
manufacturerScroll->container->addChild(manufacturerMenu);
manufacturerScroll->box.pos = Vec(0, posY);
manufacturerScroll->box.size = Vec(200, box.size.y - posY);
addChild(manufacturerScroll);

// Models
ModelMenu *modelMenu = new ModelMenu();
modelMenu->box.size.x = 200;

ScrollWidget *modelScroll = new ScrollWidget();
modelScroll->container->addChild(modelMenu);
modelScroll->box.pos = Vec(200, posY);
modelScroll->box.size = Vec(200, box.size.y - posY);
addChild(modelScroll);

// Metadata
MetadataMenu *metadataMenu = new MetadataMenu();
metadataMenu->box.size.x = 200;

ScrollWidget *metadataScroll = new ScrollWidget();
metadataScroll->container->addChild(metadataMenu);
metadataScroll->box.pos = Vec(400, posY);
metadataScroll->box.size = Vec(200, box.size.y - posY);
addChild(metadataScroll);

// NVGcolor c = bndTransparent(nvgRGB(0, 0, 0));
NVGcolor c = bndGetTheme()->nodeTheme.nodeBackdropColor;
printf("%f %f %f %f\n", c.r, c.g, c.b, c.a);
}


void AddModuleWindow::step() {
Widget::step();
}


} // namespace rack

+ 9
- 130
src/app/RackWidget.cpp View File

@@ -6,8 +6,6 @@
#include "asset.hpp"
#include <map>
#include <algorithm>
#include <thread>
#include <set>
#include "../ext/osdialog/osdialog.h"


@@ -375,111 +373,6 @@ void RackWidget::draw(NVGcontext *vg) {
Widget::draw(vg);
}


struct UrlItem : MenuItem {
std::string url;
void onAction(EventAction &e) override {
std::thread t(openBrowser, url);
t.detach();
}
};


struct AddModuleMenuItem : MenuItem {
Model *model;
Vec modulePos;
void onAction(EventAction &e) override {
ModuleWidget *moduleWidget = model->createModuleWidget();
gRackWidget->moduleContainer->addChild(moduleWidget);
// Move module nearest to the mouse position
Rect box;
box.size = moduleWidget->box.size;
box.pos = modulePos.minus(box.getCenter());
gRackWidget->requestModuleBoxNearest(moduleWidget, box);
}

Menu *createChildMenu() override {
Menu *menu = new Menu();

// Tag list
if (!model->tags.empty()) {
for (ModelTag tag : model->tags) {
menu->addChild(construct<MenuLabel>(&MenuEntry::text, gTagNames[tag]));
}
menu->addChild(construct<MenuEntry>());
}

// Plugin name
std::string pluginName = model->plugin->slug;
if (!model->plugin->version.empty()) {
pluginName += " v";
pluginName += model->plugin->version;
}
menu->addChild(construct<MenuLabel>(&MenuEntry::text, pluginName));

// Plugin metadata
if (!model->plugin->website.empty()) {
menu->addChild(construct<UrlItem>(&MenuEntry::text, "Website", &UrlItem::url, model->plugin->path));
}
if (!model->plugin->manual.empty()) {
menu->addChild(construct<UrlItem>(&MenuEntry::text, "Manual", &UrlItem::url, model->plugin->manual));
}
if (!model->plugin->path.empty()) {
menu->addChild(construct<UrlItem>(&MenuEntry::text, "Browse directory", &UrlItem::url, model->plugin->path));
}

return menu;
}
};

struct AddManufacturerMenuItem : MenuItem {
std::string manufacturer;
Vec modulePos;
void onAction(EventAction &e) override {
e.consumed = false;
}
Menu *createChildMenu() override {
Menu *menu = new Menu();

// Collect models which have this manufacturer name
for (Plugin *plugin : gPlugins) {
for (Model *model : plugin->models) {
if (model->manufacturer == manufacturer) {
AddModuleMenuItem *item = new AddModuleMenuItem();
item->text = model->name;
// item->rightText = model->plugin->slug;
// if (!model->plugin->version.empty())
// item->rightText += " v" + model->plugin->version;
item->model = model;
item->modulePos = modulePos;
menu->addChild(item);
}
}
}

return menu;
}
};

struct SearchModuleField : TextField {
void onTextChange() override {
Menu *parentMenu = getAncestorOfType<Menu>();
assert(parentMenu);

for (Widget *w : parentMenu->children) {
AddManufacturerMenuItem *a = dynamic_cast<AddManufacturerMenuItem*>(w);
if (!a)
continue;
if (a->manufacturer == text) {
a->visible = true;
}
else {
a->visible = false;
}
}
}
};

void RackWidget::onMouseMove(EventMouseMove &e) {
OpaqueWidget::onMouseMove(e);
lastMousePos = e.pos;
@@ -491,29 +384,15 @@ void RackWidget::onMouseDown(EventMouseDown &e) {
return;

if (e.button == 1) {
Menu *menu = gScene->createMenu();

// TextField *searchField = construct<SearchModuleField>();
// searchField->box.size.x = 100.0;
// menu->addChild(searchField);
// // Focus search field
// gFocusedWidget = searchField;

// Collect manufacturer names
std::set<std::string> manufacturers;
for (Plugin *plugin : gPlugins) {
for (Model *model : plugin->models) {
manufacturers.insert(model->manufacturer);
}
}
// Add menu item for each manufacturer name
for (std::string manufacturer : manufacturers) {
AddManufacturerMenuItem *item = new AddManufacturerMenuItem();
item->text = manufacturer;
item->manufacturer = manufacturer;
item->modulePos = e.pos;
menu->addChild(item);
}
MenuOverlay *overlay = new MenuOverlay();

AddModuleWindow *window = new AddModuleWindow();
// Set center position
window->box.pos = gMousePos.minus(window->box.getCenter());
window->modulePos = lastMousePos;

overlay->addChild(window);
gScene->setOverlay(overlay);
}
e.consumed = true;
e.target = this;


+ 6
- 0
src/gui.cpp View File

@@ -364,6 +364,12 @@ void guiInit() {
gGuiFont = Font::load(assetGlobal("res/DejaVuSans.ttf"));
bndSetFont(gGuiFont->handle);
// bndSetIconImage(loadImage(assetGlobal("res/icons.png")));

// Blendish style
BNDtheme theme;
theme = *bndGetTheme();
theme.nodeTheme.nodeBackdropColor = theme.menuTheme.innerColor;
bndSetTheme(theme);
}

void guiDestroy() {


+ 11
- 0
src/util.cpp View File

@@ -3,6 +3,7 @@
#include <stdarg.h>
#include <string.h>
#include <random>
#include <algorithm>
#include <libgen.h> // for dirname and basename
#include <sys/time.h>

@@ -97,6 +98,16 @@ std::string stringf(const char *format, ...) {
return s;
}

std::string tolower(std::string s) {
std::transform(s.begin(), s.end(), s.begin(), ::tolower);
return s;
}

std::string toupper(std::string s) {
std::transform(s.begin(), s.end(), s.begin(), ::toupper);
return s;
}

std::string ellipsize(std::string s, size_t len) {
if (s.size() <= len)
return s;


+ 0
- 4
src/widgets/Menu.cpp View File

@@ -42,10 +42,6 @@ void Menu::step() {
for (Widget *child : children) {
child->box.size.x = box.size.x;
}

// Try to fit into the parent's box
if (parent)
box = box.nudge(Rect(Vec(0, 0), parent->box.size));
}

void Menu::draw(NVGcontext *vg) {


+ 0
- 18
src/widgets/MenuEntry.cpp View File

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


namespace rack {


void MenuEntry::step() {
// Add 10 more pixels because Retina measurements are sometimes too small
const float rightPadding = 10.0;
// HACK use gVg from the gui.
// All this does is inspect the font, so it shouldn't modify gVg and should work when called from a FramebufferWidget for example.
box.size.x = bndLabelWidth(gVg, -1, text.c_str()) + bndLabelWidth(gVg, -1, rightText.c_str()) + rightPadding;
Widget::step();
}


} // namespace rack

+ 10
- 0
src/widgets/MenuItem.cpp View File

@@ -1,4 +1,5 @@
#include "widgets.hpp"
#include "gui.hpp"


namespace rack {
@@ -22,6 +23,15 @@ void MenuItem::draw(NVGcontext *vg) {
bndIconLabelValue(vg, x, 0.0, box.size.x, box.size.y, -1, rightColor, BND_LEFT, BND_LABEL_FONT_SIZE, rightText.c_str(), NULL);
}

void MenuItem::step() {
// Add 10 more pixels because Retina measurements are sometimes too small
const float rightPadding = 10.0;
// HACK use gVg from the gui.
// All this does is inspect the font, so it shouldn't modify gVg and should work when called from a FramebufferWidget for example.
box.size.x = bndLabelWidth(gVg, -1, text.c_str()) + bndLabelWidth(gVg, -1, rightText.c_str()) + rightPadding;
Widget::step();
}

void MenuItem::onMouseEnter(EventMouseEnter &e) {
Menu *parentMenu = dynamic_cast<Menu*>(parent);
if (!parentMenu)


+ 9
- 0
src/widgets/MenuLabel.cpp View File

@@ -1,4 +1,5 @@
#include "widgets.hpp"
#include "gui.hpp"


namespace rack {
@@ -8,5 +9,13 @@ void MenuLabel::draw(NVGcontext *vg) {
bndMenuLabel(vg, 0.0, 0.0, box.size.x, box.size.y, -1, text.c_str());
}

void MenuLabel::step() {
// Add 10 more pixels because Retina measurements are sometimes too small
const float rightPadding = 10.0;
// HACK use gVg from the gui.
box.size.x = bndLabelWidth(gVg, -1, text.c_str()) + rightPadding;
Widget::step();
}


} // namespace rack

+ 10
- 5
src/widgets/MenuOverlay.cpp View File

@@ -3,6 +3,16 @@

namespace rack {


void MenuOverlay::step() {
Widget::step();

// Fit all children in the box
for (Widget *child : children) {
child->box = child->box.nudge(Rect(Vec(0, 0), parent->box.size));
}
}

void MenuOverlay::onDragDrop(EventDragDrop &e) {
if (e.origin == this) {
// deletes `this`
@@ -10,11 +20,6 @@ void MenuOverlay::onDragDrop(EventDragDrop &e) {
}
}

void MenuOverlay::onScroll(EventScroll &e) {
// Don't recurse children, consume the event
e.consumed = true;
}

void MenuOverlay::onHoverKey(EventHoverKey &e) {
// Recurse children but consume the event
Widget::onHoverKey(e);


+ 1
- 0
src/widgets/Scene.cpp View File

@@ -31,6 +31,7 @@ Menu *Scene::createMenu() {

void Scene::step() {
if (overlay) {
overlay->box.pos = Vec(0, 0);
overlay->box.size = box.size;
}



+ 0
- 10
src/widgets/ScrollBar.cpp View File

@@ -6,16 +6,6 @@ namespace rack {


void ScrollBar::draw(NVGcontext *vg) {
ScrollWidget *scrollWidget = dynamic_cast<ScrollWidget*>(parent);
assert(scrollWidget);
Vec viewportCorner = scrollWidget->container->getChildrenBoundingBox().getBottomRight();

float viewportSize = (orientation == HORIZONTAL) ? viewportCorner.x : viewportCorner.y;
float containerSize = (orientation == HORIZONTAL) ? scrollWidget->box.size.x : scrollWidget->box.size.y;
float viewportOffset = (orientation == HORIZONTAL) ? scrollWidget->offset.x : scrollWidget->offset.y;
float offset = viewportOffset / (viewportSize - containerSize);
float size = containerSize / viewportSize;
size = clampf(size, 0.0, 1.0);
bndScrollBar(vg, 0.0, 0.0, box.size.x, box.size.y, state, offset, size);
}



+ 28
- 1
src/widgets/ScrollWidget.cpp View File

@@ -10,13 +10,23 @@ ScrollWidget::ScrollWidget() {

horizontalScrollBar = new ScrollBar();
horizontalScrollBar->orientation = ScrollBar::HORIZONTAL;
horizontalScrollBar->visible = false;
addChild(horizontalScrollBar);

verticalScrollBar = new ScrollBar();
verticalScrollBar->orientation = ScrollBar::VERTICAL;
verticalScrollBar->visible = false;
addChild(verticalScrollBar);
}

void ScrollWidget::draw(NVGcontext *vg) {
nvgScissor(vg, 0, 0, box.size.x, box.size.y);

Widget::draw(vg);

nvgResetScissor(vg);
}

void ScrollWidget::step() {
// Clamp scroll offset
Vec containerCorner = container->getChildrenBoundingBox().getBottomRight();
@@ -32,6 +42,22 @@ void ScrollWidget::step() {
// Update the container's positions from the offset
container->box.pos = offset.neg().round();

// Update scrollbar offsets and sizes
Vec viewportSize = container->getChildrenBoundingBox().getBottomRight();
Vec scrollbarOffset = offset.div(viewportSize.minus(box.size));
Vec scrollbarSize = box.size.div(viewportSize);

horizontalScrollBar->offset = scrollbarOffset.x;
horizontalScrollBar->size = scrollbarSize.x;
horizontalScrollBar->visible = (0.0 < scrollbarSize.x && scrollbarSize.x < 1.0);
verticalScrollBar->offset = scrollbarOffset.y;
verticalScrollBar->size = scrollbarSize.y;
verticalScrollBar->visible = (0.0 < scrollbarSize.y && scrollbarSize.y < 1.0);

Widget::step();
}

void ScrollWidget::onMouseMove(EventMouseMove &e) {
// Scroll with arrow keys
if (!gFocusedWidget) {
float arrowSpeed = 30.0;
@@ -55,7 +81,8 @@ void ScrollWidget::step() {
offset.y += arrowSpeed;
}
}
Widget::step();

Widget::onMouseMove(e);
}

void ScrollWidget::onScroll(EventScroll &e) {


+ 17
- 0
src/widgets/Window.cpp View File

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


namespace rack {


void Window::draw(NVGcontext *vg) {
bndNodeBackground(vg, 0.0, 0.0, box.size.x, box.size.y, BND_DEFAULT, -1, title.c_str(), bndGetTheme()->backgroundColor);
Widget::draw(vg);
}

void Window::onDragMove(EventDragMove &e) {
box.pos = box.pos.plus(e.mouseRel);
}


} // namespace rack

Loading…
Cancel
Save