From 5567c5f22d1966974e16c63411826fc817a8f7d1 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 13 Nov 2017 01:51:30 -0500 Subject: [PATCH] Added AddModuleWindow --- ext/oui-blendish | 2 +- include/app.hpp | 7 + include/math.hpp | 4 +- include/util.hpp | 2 + include/widgets.hpp | 22 ++- src/app/AddModuleWindow.cpp | 295 +++++++++++++++++++++++++++++++++++ src/app/RackWidget.cpp | 139 ++--------------- src/gui.cpp | 6 + src/util.cpp | 11 ++ src/widgets/Menu.cpp | 4 - src/widgets/MenuEntry.cpp | 18 --- src/widgets/MenuItem.cpp | 10 ++ src/widgets/MenuLabel.cpp | 9 ++ src/widgets/MenuOverlay.cpp | 15 +- src/widgets/Scene.cpp | 1 + src/widgets/ScrollBar.cpp | 10 -- src/widgets/ScrollWidget.cpp | 29 +++- src/widgets/Window.cpp | 17 ++ 18 files changed, 426 insertions(+), 175 deletions(-) create mode 100644 src/app/AddModuleWindow.cpp delete mode 100644 src/widgets/MenuEntry.cpp create mode 100644 src/widgets/Window.cpp diff --git a/ext/oui-blendish b/ext/oui-blendish index ac99b8dd..7c603885 160000 --- a/ext/oui-blendish +++ b/ext/oui-blendish @@ -1 +1 @@ -Subproject commit ac99b8dd4d36ac314dad6d700701e39484c60e06 +Subproject commit 7c6038857b1bb6299c2d20ff9f31248f2d53ba39 diff --git a/include/app.hpp b/include/app.hpp index b0bcb4b4..0790a2d3 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -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 backgroundImage; diff --git a/include/math.hpp b/include/math.hpp index 73bee8e5..0b94c9c1 100644 --- a/include/math.hpp +++ b/include/math.hpp @@ -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 */ diff --git a/include/util.hpp b/include/util.hpp index c205b1c9..3b4a319f 100644 --- a/include/util.hpp +++ b/include/util.hpp @@ -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); diff --git a/include/widgets.hpp b/include/widgets.hpp index e0e76df4..b26245af 100644 --- a/include/widgets.hpp +++ b/include/widgets.hpp @@ -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; }; diff --git a/src/app/AddModuleWindow.cpp b/src/app/AddModuleWindow.cpp new file mode 100644 index 00000000..5b2a22ac --- /dev/null +++ b/src/app/AddModuleWindow.cpp @@ -0,0 +1,295 @@ +#include "app.hpp" +#include "plugin.hpp" +#include +#include +#include + + +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(&MenuEntry::text, gTagNames[tag])); + } + addChild(construct()); + } + + // Plugin name + std::string pluginName = sModel->plugin->slug; + if (!sModel->plugin->version.empty()) { + pluginName += " v"; + pluginName += sModel->plugin->version; + } + addChild(construct(&MenuEntry::text, pluginName)); + + // Plugin metadata + if (!sModel->plugin->website.empty()) { + addChild(construct(&MenuEntry::text, "Website", &UrlItem::url, sModel->plugin->path)); + } + if (!sModel->plugin->manual.empty()) { + addChild(construct(&MenuEntry::text, "Manual", &UrlItem::url, sModel->plugin->manual)); + } + if (!sModel->plugin->path.empty()) { + addChild(construct(&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(); + 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(&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(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 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(&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(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 diff --git a/src/app/RackWidget.cpp b/src/app/RackWidget.cpp index 3c3e52ce..9d3c975a 100644 --- a/src/app/RackWidget.cpp +++ b/src/app/RackWidget.cpp @@ -6,8 +6,6 @@ #include "asset.hpp" #include #include -#include -#include #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(&MenuEntry::text, gTagNames[tag])); - } - menu->addChild(construct()); - } - - // Plugin name - std::string pluginName = model->plugin->slug; - if (!model->plugin->version.empty()) { - pluginName += " v"; - pluginName += model->plugin->version; - } - menu->addChild(construct(&MenuEntry::text, pluginName)); - - // Plugin metadata - if (!model->plugin->website.empty()) { - menu->addChild(construct(&MenuEntry::text, "Website", &UrlItem::url, model->plugin->path)); - } - if (!model->plugin->manual.empty()) { - menu->addChild(construct(&MenuEntry::text, "Manual", &UrlItem::url, model->plugin->manual)); - } - if (!model->plugin->path.empty()) { - menu->addChild(construct(&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(); - assert(parentMenu); - - for (Widget *w : parentMenu->children) { - AddManufacturerMenuItem *a = dynamic_cast(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(); - // searchField->box.size.x = 100.0; - // menu->addChild(searchField); - // // Focus search field - // gFocusedWidget = searchField; - - // Collect manufacturer names - std::set 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; diff --git a/src/gui.cpp b/src/gui.cpp index 05c6d061..bb8f72fc 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -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() { diff --git a/src/util.cpp b/src/util.cpp index 06b17508..c7af0887 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include // for dirname and basename #include @@ -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; diff --git a/src/widgets/Menu.cpp b/src/widgets/Menu.cpp index 6a4042d2..973362fd 100644 --- a/src/widgets/Menu.cpp +++ b/src/widgets/Menu.cpp @@ -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) { diff --git a/src/widgets/MenuEntry.cpp b/src/widgets/MenuEntry.cpp deleted file mode 100644 index e4ea9502..00000000 --- a/src/widgets/MenuEntry.cpp +++ /dev/null @@ -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 diff --git a/src/widgets/MenuItem.cpp b/src/widgets/MenuItem.cpp index 5f8f3288..b2ff5c8c 100644 --- a/src/widgets/MenuItem.cpp +++ b/src/widgets/MenuItem.cpp @@ -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(parent); if (!parentMenu) diff --git a/src/widgets/MenuLabel.cpp b/src/widgets/MenuLabel.cpp index 8edd7a01..9eca1293 100644 --- a/src/widgets/MenuLabel.cpp +++ b/src/widgets/MenuLabel.cpp @@ -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 diff --git a/src/widgets/MenuOverlay.cpp b/src/widgets/MenuOverlay.cpp index e7bde39b..efecef8c 100644 --- a/src/widgets/MenuOverlay.cpp +++ b/src/widgets/MenuOverlay.cpp @@ -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); diff --git a/src/widgets/Scene.cpp b/src/widgets/Scene.cpp index 9042871b..c71b4133 100644 --- a/src/widgets/Scene.cpp +++ b/src/widgets/Scene.cpp @@ -31,6 +31,7 @@ Menu *Scene::createMenu() { void Scene::step() { if (overlay) { + overlay->box.pos = Vec(0, 0); overlay->box.size = box.size; } diff --git a/src/widgets/ScrollBar.cpp b/src/widgets/ScrollBar.cpp index 803071e5..2b7dd845 100644 --- a/src/widgets/ScrollBar.cpp +++ b/src/widgets/ScrollBar.cpp @@ -6,16 +6,6 @@ namespace rack { void ScrollBar::draw(NVGcontext *vg) { - ScrollWidget *scrollWidget = dynamic_cast(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); } diff --git a/src/widgets/ScrollWidget.cpp b/src/widgets/ScrollWidget.cpp index c4faabf0..1763fbac 100644 --- a/src/widgets/ScrollWidget.cpp +++ b/src/widgets/ScrollWidget.cpp @@ -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) { diff --git a/src/widgets/Window.cpp b/src/widgets/Window.cpp new file mode 100644 index 00000000..dab954be --- /dev/null +++ b/src/widgets/Window.cpp @@ -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