@@ -7,9 +7,6 @@ | |||
namespace rack { | |||
static const float KNOB_SENSITIVITY = 0.0015f; | |||
/** Implements vertical dragging behavior for ParamWidgets */ | |||
struct Knob : ParamWidget { | |||
/** Multiplier for mouse movement to adjust knob value */ | |||
@@ -1,11 +1,23 @@ | |||
#pragma once | |||
#include "app/common.hpp" | |||
#include "ui/ScrollWidget.hpp" | |||
#include "ui/SequentialLayout.hpp" | |||
namespace rack { | |||
void moduleBrowserCreate(); | |||
struct ModuleBrowser : OpaqueWidget { | |||
ScrollWidget *moduleScroll; | |||
SequentialLayout *moduleLayout; | |||
ModuleBrowser(); | |||
void step() override; | |||
void draw(NVGcontext *vg) override; | |||
void onHoverKey(const event::HoverKey &e) override; | |||
}; | |||
json_t *moduleBrowserToJson(); | |||
void moduleBrowserFromJson(json_t *rootJ); | |||
@@ -5,6 +5,7 @@ | |||
#include "ui/ScrollWidget.hpp" | |||
#include "app/RackWidget.hpp" | |||
#include "app/Toolbar.hpp" | |||
#include "app/ModuleBrowser.hpp" | |||
namespace rack { | |||
@@ -16,6 +17,7 @@ struct Scene : OpaqueWidget { | |||
ZoomWidget *zoomWidget; | |||
RackWidget *rackWidget; | |||
Toolbar *toolbar; | |||
ModuleBrowser *moduleBrowser; | |||
// Version checking | |||
bool devMode = false; | |||
@@ -221,10 +221,10 @@ struct Zoom : Event { | |||
struct State { | |||
Widget *rootWidget = NULL; | |||
/** State widgets | |||
Don't set these directly unless you know what you're doing. Use the set*() methods instead. | |||
*/ | |||
Widget *rootWidget = NULL; | |||
Widget *hoveredWidget = NULL; | |||
Widget *draggedWidget = NULL; | |||
Widget *dragHoveredWidget = NULL; | |||
@@ -7,14 +7,15 @@ namespace rack { | |||
struct Label : virtual Widget { | |||
std::string text; | |||
float fontSize; | |||
NVGcolor color; | |||
enum Alignment { | |||
LEFT_ALIGNMENT, | |||
CENTER_ALIGNMENT, | |||
RIGHT_ALIGNMENT, | |||
}; | |||
std::string text; | |||
float fontSize; | |||
NVGcolor color; | |||
Alignment alignment = LEFT_ALIGNMENT; | |||
Label() { | |||
@@ -0,0 +1,28 @@ | |||
#pragma once | |||
#include "widgets/OpaqueWidget.hpp" | |||
#include "ui/common.hpp" | |||
namespace rack { | |||
/** Parent must be a ScrollWidget */ | |||
struct ScrollBar : OpaqueWidget { | |||
enum Orientation { | |||
VERTICAL, | |||
HORIZONTAL | |||
}; | |||
Orientation orientation; | |||
BNDwidgetState state = BND_DEFAULT; | |||
float offset = 0.0; | |||
float size = 0.0; | |||
ScrollBar(); | |||
void draw(NVGcontext *vg) override; | |||
void onDragStart(const event::DragStart &e) override; | |||
void onDragMove(const event::DragMove &e) override; | |||
void onDragEnd(const event::DragEnd &e) override; | |||
}; | |||
} // namespace rack |
@@ -1,46 +1,12 @@ | |||
#pragma once | |||
#include "widgets/OpaqueWidget.hpp" | |||
#include "ui/common.hpp" | |||
#include "event.hpp" | |||
#include "context.hpp" | |||
#include "widgets/OpaqueWidget.hpp" | |||
#include "ui/ScrollBar.hpp" | |||
namespace rack { | |||
/** Parent must be a ScrollWidget */ | |||
struct ScrollBar : OpaqueWidget { | |||
enum Orientation { | |||
VERTICAL, | |||
HORIZONTAL | |||
}; | |||
Orientation orientation; | |||
BNDwidgetState state = BND_DEFAULT; | |||
float offset = 0.0; | |||
float size = 0.0; | |||
ScrollBar() { | |||
box.size = math::Vec(BND_SCROLLBAR_WIDTH, BND_SCROLLBAR_HEIGHT); | |||
} | |||
void draw(NVGcontext *vg) override { | |||
bndScrollBar(vg, 0.0, 0.0, box.size.x, box.size.y, state, offset, size); | |||
} | |||
void onDragStart(const event::DragStart &e) override { | |||
state = BND_ACTIVE; | |||
context()->window->cursorLock(); | |||
} | |||
void onDragMove(const event::DragMove &e) override; | |||
void onDragEnd(const event::DragEnd &e) override { | |||
state = BND_DEFAULT; | |||
context()->window->cursorUnlock(); | |||
} | |||
}; | |||
/** Handles a container with ScrollBar */ | |||
struct ScrollWidget : OpaqueWidget { | |||
Widget *container; | |||
@@ -48,111 +14,13 @@ struct ScrollWidget : OpaqueWidget { | |||
ScrollBar *verticalScrollBar; | |||
math::Vec offset; | |||
ScrollWidget() { | |||
container = new Widget; | |||
addChild(container); | |||
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 scrollTo(math::Rect r) { | |||
math::Rect bound = math::Rect::fromMinMax(r.getBottomRight().minus(box.size), r.pos); | |||
offset = offset.clampBetween(bound); | |||
} | |||
void draw(NVGcontext *vg) override { | |||
nvgScissor(vg, 0, 0, box.size.x, box.size.y); | |||
Widget::draw(vg); | |||
nvgResetScissor(vg); | |||
} | |||
void step() override { | |||
Widget::step(); | |||
// Clamp scroll offset | |||
math::Vec containerCorner = container->getChildrenBoundingBox().getBottomRight(); | |||
math::Rect containerBox = math::Rect(math::Vec(0, 0), containerCorner.minus(box.size)); | |||
offset = offset.clamp(containerBox); | |||
// Lock offset to top/left if no scrollbar will display | |||
if (containerBox.size.x < 0.0) | |||
offset.x = 0.0; | |||
if (containerBox.size.y < 0.0) | |||
offset.y = 0.0; | |||
// Update the container's positions from the offset | |||
container->box.pos = offset.neg().round(); | |||
// Update scrollbar offsets and sizes | |||
math::Vec viewportSize = container->getChildrenBoundingBox().getBottomRight(); | |||
math::Vec scrollbarOffset = offset.div(viewportSize.minus(box.size)); | |||
math::Vec scrollbarSize = box.size.div(viewportSize); | |||
horizontalScrollBar->visible = (0.0 < scrollbarSize.x && scrollbarSize.x < 1.0); | |||
verticalScrollBar->visible = (0.0 < scrollbarSize.y && scrollbarSize.y < 1.0); | |||
horizontalScrollBar->offset = scrollbarOffset.x; | |||
verticalScrollBar->offset = scrollbarOffset.y; | |||
horizontalScrollBar->size = scrollbarSize.x; | |||
verticalScrollBar->size = scrollbarSize.y; | |||
// Resize scroll bars | |||
math::Vec inner = math::Vec(box.size.x - verticalScrollBar->box.size.x, box.size.y - horizontalScrollBar->box.size.y); | |||
horizontalScrollBar->box.pos.y = inner.y; | |||
verticalScrollBar->box.pos.x = inner.x; | |||
horizontalScrollBar->box.size.x = verticalScrollBar->visible ? inner.x : box.size.x; | |||
verticalScrollBar->box.size.y = horizontalScrollBar->visible ? inner.y : box.size.y; | |||
} | |||
void onHover(const event::Hover &e) override { | |||
// Scroll with arrow keys | |||
if (!context()->event->selectedWidget) { | |||
float arrowSpeed = 30.0; | |||
if (context()->window->isShiftPressed() && context()->window->isModPressed()) | |||
arrowSpeed /= 16.0; | |||
else if (context()->window->isShiftPressed()) | |||
arrowSpeed *= 4.0; | |||
else if (context()->window->isModPressed()) | |||
arrowSpeed /= 4.0; | |||
if (glfwGetKey(context()->window->win, GLFW_KEY_LEFT) == GLFW_PRESS) { | |||
offset.x -= arrowSpeed; | |||
} | |||
if (glfwGetKey(context()->window->win, GLFW_KEY_RIGHT) == GLFW_PRESS) { | |||
offset.x += arrowSpeed; | |||
} | |||
if (glfwGetKey(context()->window->win, GLFW_KEY_UP) == GLFW_PRESS) { | |||
offset.y -= arrowSpeed; | |||
} | |||
if (glfwGetKey(context()->window->win, GLFW_KEY_DOWN) == GLFW_PRESS) { | |||
offset.y += arrowSpeed; | |||
} | |||
} | |||
OpaqueWidget::onHover(e); | |||
} | |||
void onHoverScroll(const event::HoverScroll &e) override { | |||
offset = offset.minus(e.scrollDelta); | |||
e.consume(this); | |||
} | |||
ScrollWidget(); | |||
void scrollTo(math::Rect r); | |||
void draw(NVGcontext *vg) override; | |||
void step() override; | |||
void onHover(const event::Hover &e) override; | |||
void onHoverScroll(const event::HoverScroll &e) override; | |||
}; | |||
inline void ScrollBar::onDragMove(const event::DragMove &e) { | |||
ScrollWidget *scrollWidget = dynamic_cast<ScrollWidget*>(parent); | |||
assert(scrollWidget); | |||
if (orientation == HORIZONTAL) | |||
scrollWidget->offset.x += e.mouseDelta.x; | |||
else | |||
scrollWidget->offset.y += e.mouseDelta.y; | |||
} | |||
} // namespace rack |
@@ -4,6 +4,9 @@ | |||
namespace rack { | |||
static const float KNOB_SENSITIVITY = 0.0015f; | |||
void Knob::onButton(const event::Button &e) { | |||
float r = box.size.x / 2; | |||
math::Vec c = box.size.div(2); | |||
@@ -1,17 +1,11 @@ | |||
#include "app/ModuleBrowser.hpp" | |||
// TODO clean up | |||
#include "window.hpp" | |||
#include "helpers.hpp" | |||
#include "event.hpp" | |||
#include "ui/Quantity.hpp" | |||
#include "ui/RadioButton.hpp" | |||
#include "widgets/OpaqueWidget.hpp" | |||
#include "widgets/TransparentWidget.hpp" | |||
#include "widgets/ZoomWidget.hpp" | |||
#include "ui/Label.hpp" | |||
#include "ui/MenuOverlay.hpp" | |||
#include "app/ModuleWidget.hpp" | |||
#include "app/Scene.hpp" | |||
#include "ui/List.hpp" | |||
#include "ui/TextField.hpp" | |||
#include "ui/SequentialLayout.hpp" | |||
#include "widgets/ObstructWidget.hpp" | |||
#include "widgets/ZoomWidget.hpp" | |||
#include "plugin.hpp" | |||
#include "context.hpp" | |||
@@ -27,75 +21,122 @@ static std::string sAuthorFilter; | |||
static std::string sTagFilter; | |||
struct ModuleWidgetWrapper : ObstructWidget { | |||
struct ModuleBox : OpaqueWidget { | |||
Model *model; | |||
void onDragDrop(const event::DragDrop &e) override { | |||
if (e.origin == this) { | |||
void setModel(Model *model) { | |||
this->model = model; | |||
Widget *transparentWidget = new TransparentWidget; | |||
addChild(transparentWidget); | |||
ZoomWidget *zoomWidget = new ZoomWidget; | |||
zoomWidget->setZoom(0.5); | |||
transparentWidget->addChild(zoomWidget); | |||
ModuleWidget *moduleWidget = model->createModuleWidgetNull(); | |||
zoomWidget->addChild(moduleWidget); | |||
box.size = math::Vec(moduleWidget->box.size.x, RACK_GRID_SIZE.y).mult(zoomWidget->zoom).ceil(); | |||
math::Vec p; | |||
p.y = box.size.y; | |||
box.size.y += 40.0; | |||
box.size.x = std::max(box.size.x, 70.f); | |||
Label *nameLabel = new Label; | |||
nameLabel->text = model->name; | |||
nameLabel->box.pos = p; | |||
p.y += nameLabel->box.size.y; | |||
addChild(nameLabel); | |||
Label *pluginLabel = new Label; | |||
pluginLabel->text = model->plugin->name; | |||
pluginLabel->box.pos = p; | |||
p.y += pluginLabel->box.size.y; | |||
addChild(pluginLabel); | |||
} | |||
void draw(NVGcontext *vg) override { | |||
OpaqueWidget::draw(vg); | |||
if (context()->event->hoveredWidget == this) { | |||
nvgBeginPath(vg); | |||
nvgRect(vg, 0.0, 0.0, box.size.x, box.size.y); | |||
nvgFillColor(vg, nvgRGBAf(1, 1, 1, 0.25)); | |||
nvgFill(vg); | |||
} | |||
} | |||
void onButton(const event::Button &e) override { | |||
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT) { | |||
// Create module | |||
ModuleWidget *moduleWidget = model->createModuleWidget(); | |||
assert(moduleWidget); | |||
context()->scene->rackWidget->addModuleAtMouse(moduleWidget); | |||
// This is a bit nonstandard/unsupported usage, but pretend the moduleWidget was clicked so it can be dragged in the RackWidget | |||
e.consume(moduleWidget); | |||
// Close Module Browser | |||
MenuOverlay *menuOverlay = getAncestorOfType<MenuOverlay>(); | |||
menuOverlay->requestedDelete = true; | |||
ModuleBrowser *moduleBrowser = getAncestorOfType<ModuleBrowser>(); | |||
moduleBrowser->visible = false; | |||
} | |||
OpaqueWidget::onButton(e); | |||
} | |||
}; | |||
struct ModuleBrowser : OpaqueWidget { | |||
SequentialLayout *moduleLayout; | |||
ModuleBrowser() { | |||
moduleLayout = new SequentialLayout; | |||
moduleLayout->spacing = math::Vec(10, 10); | |||
addChild(moduleLayout); | |||
ModuleBrowser::ModuleBrowser() { | |||
moduleScroll = new ScrollWidget; | |||
addChild(moduleScroll); | |||
for (Plugin *plugin : plugin::plugins) { | |||
for (Model *model : plugin->models) { | |||
ModuleWidgetWrapper *wrapper = new ModuleWidgetWrapper; | |||
wrapper->model = model; | |||
moduleLayout->addChild(wrapper); | |||
moduleLayout = new SequentialLayout; | |||
moduleLayout->spacing = math::Vec(10, 10); | |||
moduleScroll->container->addChild(moduleLayout); | |||
ZoomWidget *zoomWidget = new ZoomWidget; | |||
zoomWidget->setZoom(0.5); | |||
wrapper->addChild(zoomWidget); | |||
ModuleWidget *moduleWidget = model->createModuleWidgetNull(); | |||
zoomWidget->addChild(moduleWidget); | |||
wrapper->box.size = moduleWidget->box.size.mult(zoomWidget->zoom); | |||
} | |||
for (Plugin *plugin : plugin::plugins) { | |||
for (Model *model : plugin->models) { | |||
ModuleBox *moduleBox = new ModuleBox; | |||
moduleBox->setModel(model); | |||
moduleLayout->addChild(moduleBox); | |||
} | |||
} | |||
} | |||
void step() override { | |||
assert(parent); | |||
box = parent->box.zeroPos().grow(math::Vec(-50, -50)); | |||
moduleLayout->box.size = box.size; | |||
void ModuleBrowser::step() { | |||
// TODO resize sidebar | |||
float sidebarWidth = 300.0; | |||
OpaqueWidget::step(); | |||
} | |||
moduleScroll->box.pos.x = sidebarWidth; | |||
moduleScroll->box.size.x = box.size.x - sidebarWidth; | |||
moduleScroll->box.size.y = box.size.y; | |||
moduleLayout->box.size.x = moduleScroll->box.size.x; | |||
moduleLayout->box.size.y = moduleLayout->getChildrenBoundingBox().getBottomRight().y; | |||
void draw(NVGcontext *vg) override { | |||
bndTooltipBackground(vg, 0.0, 0.0, box.size.x, box.size.y); | |||
Widget::draw(vg); | |||
} | |||
}; | |||
OpaqueWidget::step(); | |||
} | |||
void ModuleBrowser::draw(NVGcontext *vg) { | |||
bndMenuBackground(vg, 0.0, 0.0, box.size.x, box.size.y, 0); | |||
Widget::draw(vg); | |||
} | |||
void ModuleBrowser::onHoverKey(const event::HoverKey &e) { | |||
if (e.action == GLFW_PRESS) { | |||
switch (e.key) { | |||
case GLFW_KEY_ESCAPE: { | |||
// Close menu | |||
this->visible = false; | |||
e.consume(this); | |||
} break; | |||
} | |||
} | |||
// Global functions | |||
if (!e.getConsumed()) | |||
OpaqueWidget::onHoverKey(e); | |||
} | |||
void moduleBrowserCreate() { | |||
MenuOverlay *overlay = new MenuOverlay; | |||
ModuleBrowser *moduleBrowser = new ModuleBrowser; | |||
overlay->addChild(moduleBrowser); | |||
context()->scene->addChild(overlay); | |||
} | |||
// Global functions | |||
json_t *moduleBrowserToJson() { | |||
json_t *rootJ = json_object(); | |||
@@ -284,7 +284,7 @@ void ModuleWidget::draw(NVGcontext *vg) { | |||
if (module && module->bypass) { | |||
nvgGlobalAlpha(vg, 0.5); | |||
} | |||
nvgScissor(vg, 0, 0, box.size.x, box.size.y); | |||
// nvgScissor(vg, 0, 0, box.size.x, box.size.y); | |||
Widget::draw(vg); | |||
// Power meter | |||
@@ -312,7 +312,7 @@ void ModuleWidget::draw(NVGcontext *vg) { | |||
nvgFill(vg); | |||
} | |||
nvgResetScissor(vg); | |||
// nvgResetScissor(vg); | |||
} | |||
void ModuleWidget::drawShadow(NVGcontext *vg) { | |||
@@ -566,7 +566,7 @@ void RackWidget::onButton(const event::Button &e) { | |||
OpaqueWidget::onButton(e); | |||
if (e.getConsumed() == this) { | |||
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) { | |||
moduleBrowserCreate(); | |||
context()->scene->moduleBrowser->visible = true; | |||
} | |||
} | |||
} | |||
@@ -26,6 +26,10 @@ Scene::Scene() { | |||
toolbar = new Toolbar; | |||
addChild(toolbar); | |||
scrollWidget->box.pos.y = toolbar->box.size.y; | |||
moduleBrowser = new ModuleBrowser; | |||
moduleBrowser->visible = false; | |||
addChild(moduleBrowser); | |||
} | |||
Scene::~Scene() { | |||
@@ -45,6 +49,7 @@ void Scene::step() { | |||
OpaqueWidget::step(); | |||
zoomWidget->box.size = rackWidget->box.size.mult(zoomWidget->zoom); | |||
moduleBrowser->box.size = box.size; | |||
// Request latest version from server | |||
if (!devMode && checkVersion && !checkedVersion) { | |||
@@ -112,7 +117,7 @@ void Scene::onHoverKey(const event::HoverKey &e) { | |||
} break; | |||
case GLFW_KEY_ENTER: | |||
case GLFW_KEY_KP_ENTER: { | |||
moduleBrowserCreate(); | |||
moduleBrowser->visible = true; | |||
e.consume(this); | |||
} break; | |||
case GLFW_KEY_F11: { | |||
@@ -560,7 +560,7 @@ Toolbar::Toolbar() { | |||
} | |||
void Toolbar::draw(NVGcontext *vg) { | |||
bndMenuBackground(vg, 0.0, 0.0, box.size.x, box.size.y, 0); | |||
bndMenuBackground(vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_ALL); | |||
bndBevel(vg, 0.0, 0.0, box.size.x, box.size.y); | |||
Widget::draw(vg); | |||
@@ -79,12 +79,12 @@ int main(int argc, char *argv[]) { | |||
plugin::init(devMode); | |||
// Initialize app | |||
context()->engine = new Engine; | |||
context()->event = new event::State; | |||
context()->window = new Window; | |||
context()->engine = new Engine; | |||
context()->scene = new Scene; | |||
context()->scene->devMode = devMode; | |||
context()->event->rootWidget = context()->scene; | |||
context()->window = new Window; | |||
settings::load(asset::user("settings.json")); | |||
if (patchFile.empty()) { | |||
@@ -120,10 +120,10 @@ int main(int argc, char *argv[]) { | |||
context()->scene = NULL; | |||
delete context()->event; | |||
context()->event = NULL; | |||
delete context()->window; | |||
context()->window = NULL; | |||
delete context()->engine; | |||
context()->engine = NULL; | |||
delete context()->window; | |||
context()->window = NULL; | |||
// Destroy environment | |||
plugin::destroy(); | |||
@@ -0,0 +1,41 @@ | |||
#include "ui/ScrollBar.hpp" | |||
#include "ui/ScrollWidget.hpp" | |||
#include "context.hpp" | |||
#include "window.hpp" | |||
namespace rack { | |||
static const float SCROLLBAR_SENSITIVITY = 2.f; | |||
ScrollBar::ScrollBar() { | |||
box.size = math::Vec(BND_SCROLLBAR_WIDTH, BND_SCROLLBAR_HEIGHT); | |||
} | |||
void ScrollBar::draw(NVGcontext *vg) { | |||
bndScrollBar(vg, 0.0, 0.0, box.size.x, box.size.y, state, offset, size); | |||
} | |||
void ScrollBar::onDragStart(const event::DragStart &e) { | |||
state = BND_ACTIVE; | |||
context()->window->cursorLock(); | |||
} | |||
void ScrollBar::onDragMove(const event::DragMove &e) { | |||
ScrollWidget *scrollWidget = dynamic_cast<ScrollWidget*>(parent); | |||
assert(scrollWidget); | |||
if (orientation == HORIZONTAL) | |||
scrollWidget->offset.x += SCROLLBAR_SENSITIVITY * e.mouseDelta.x; | |||
else | |||
scrollWidget->offset.y += SCROLLBAR_SENSITIVITY * e.mouseDelta.y; | |||
} | |||
void ScrollBar::onDragEnd(const event::DragEnd &e) { | |||
state = BND_DEFAULT; | |||
context()->window->cursorUnlock(); | |||
} | |||
} // namespace rack |
@@ -0,0 +1,105 @@ | |||
#include "ui/ScrollWidget.hpp" | |||
#include "context.hpp" | |||
#include "event.hpp" | |||
namespace rack { | |||
ScrollWidget::ScrollWidget() { | |||
container = new Widget; | |||
addChild(container); | |||
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::scrollTo(math::Rect r) { | |||
math::Rect bound = math::Rect::fromMinMax(r.getBottomRight().minus(box.size), r.pos); | |||
offset = offset.clampBetween(bound); | |||
} | |||
void ScrollWidget::draw(NVGcontext *vg) { | |||
nvgScissor(vg, 0, 0, box.size.x, box.size.y); | |||
Widget::draw(vg); | |||
nvgResetScissor(vg); | |||
} | |||
void ScrollWidget::step() { | |||
Widget::step(); | |||
// Clamp scroll offset | |||
math::Vec containerCorner = container->getChildrenBoundingBox().getBottomRight(); | |||
math::Rect containerBox = math::Rect(math::Vec(0, 0), containerCorner.minus(box.size)); | |||
offset = offset.clamp(containerBox); | |||
// Lock offset to top/left if no scrollbar will display | |||
if (containerBox.size.x < 0.0) | |||
offset.x = 0.0; | |||
if (containerBox.size.y < 0.0) | |||
offset.y = 0.0; | |||
// Update the container's positions from the offset | |||
container->box.pos = offset.neg().round(); | |||
// Update scrollbar offsets and sizes | |||
math::Vec viewportSize = container->getChildrenBoundingBox().getBottomRight(); | |||
math::Vec scrollbarOffset = offset.div(viewportSize.minus(box.size)); | |||
math::Vec scrollbarSize = box.size.div(viewportSize); | |||
horizontalScrollBar->visible = (0.0 < scrollbarSize.x && scrollbarSize.x < 1.0); | |||
verticalScrollBar->visible = (0.0 < scrollbarSize.y && scrollbarSize.y < 1.0); | |||
horizontalScrollBar->offset = scrollbarOffset.x; | |||
verticalScrollBar->offset = scrollbarOffset.y; | |||
horizontalScrollBar->size = scrollbarSize.x; | |||
verticalScrollBar->size = scrollbarSize.y; | |||
// Resize scroll bars | |||
math::Vec inner = math::Vec(box.size.x - verticalScrollBar->box.size.x, box.size.y - horizontalScrollBar->box.size.y); | |||
horizontalScrollBar->box.pos.y = inner.y; | |||
verticalScrollBar->box.pos.x = inner.x; | |||
horizontalScrollBar->box.size.x = verticalScrollBar->visible ? inner.x : box.size.x; | |||
verticalScrollBar->box.size.y = horizontalScrollBar->visible ? inner.y : box.size.y; | |||
} | |||
void ScrollWidget::onHover(const event::Hover &e) { | |||
// Scroll with arrow keys | |||
if (!context()->event->selectedWidget) { | |||
float arrowSpeed = 30.0; | |||
if (context()->window->isShiftPressed() && context()->window->isModPressed()) | |||
arrowSpeed /= 16.0; | |||
else if (context()->window->isShiftPressed()) | |||
arrowSpeed *= 4.0; | |||
else if (context()->window->isModPressed()) | |||
arrowSpeed /= 4.0; | |||
if (glfwGetKey(context()->window->win, GLFW_KEY_LEFT) == GLFW_PRESS) { | |||
offset.x -= arrowSpeed; | |||
} | |||
if (glfwGetKey(context()->window->win, GLFW_KEY_RIGHT) == GLFW_PRESS) { | |||
offset.x += arrowSpeed; | |||
} | |||
if (glfwGetKey(context()->window->win, GLFW_KEY_UP) == GLFW_PRESS) { | |||
offset.y -= arrowSpeed; | |||
} | |||
if (glfwGetKey(context()->window->win, GLFW_KEY_DOWN) == GLFW_PRESS) { | |||
offset.y += arrowSpeed; | |||
} | |||
} | |||
OpaqueWidget::onHover(e); | |||
} | |||
void ScrollWidget::onHoverScroll(const event::HoverScroll &e) { | |||
offset = offset.minus(e.scrollDelta); | |||
e.consume(this); | |||
} | |||
} // namespace rack |