@@ -37,7 +37,6 @@ struct ModuleWidget : widget::OpaqueWidget { | |||||
void draw(const DrawArgs &args) override; | void draw(const DrawArgs &args) override; | ||||
void drawShadow(const DrawArgs &args); | void drawShadow(const DrawArgs &args); | ||||
void onHover(const event::Hover &e) override; | |||||
void onButton(const event::Button &e) override; | void onButton(const event::Button &e) override; | ||||
void onHoverKey(const event::HoverKey &e) override; | void onHoverKey(const event::HoverKey &e) override; | ||||
void onDragStart(const event::DragStart &e) override; | void onDragStart(const event::DragStart &e) override; | ||||
@@ -18,7 +18,7 @@ struct RackScrollWidget : ui::ScrollWidget { | |||||
RackScrollWidget(); | RackScrollWidget(); | ||||
void step() override; | void step() override; | ||||
void draw(const DrawArgs &args) override; | void draw(const DrawArgs &args) override; | ||||
void onHover(const event::Hover &e) override; | |||||
void onHoverKey(const event::HoverKey &e) override; | |||||
void onHoverScroll(const event::HoverScroll &e) override; | void onHoverScroll(const event::HoverScroll &e) override; | ||||
void reset(); | void reset(); | ||||
}; | }; | ||||
@@ -2,6 +2,7 @@ | |||||
#include "common.hpp" | #include "common.hpp" | ||||
#include "math.hpp" | #include "math.hpp" | ||||
#include <vector> | #include <vector> | ||||
#include <set> | |||||
namespace rack { | namespace rack { | ||||
@@ -76,13 +77,16 @@ struct PositionBase { | |||||
}; | }; | ||||
#define RACK_HELD 3 | |||||
/** An event prototype with a GLFW key. */ | /** An event prototype with a GLFW key. */ | ||||
struct KeyBase { | struct KeyBase { | ||||
/** GLFW_KEY_* */ | /** GLFW_KEY_* */ | ||||
int key; | int key; | ||||
/** GLFW_KEY_*. You should usually use `key` instead. */ | /** GLFW_KEY_*. You should usually use `key` instead. */ | ||||
int scancode; | int scancode; | ||||
/** GLFW_RELEASE, GLFW_PRESS, or GLFW_REPEAT */ | |||||
/** GLFW_RELEASE, GLFW_PRESS, GLFW_REPEAT, or RACK_HELD */ | |||||
int action; | int action; | ||||
/** GLFW_MOD_* */ | /** GLFW_MOD_* */ | ||||
int mods; | int mods; | ||||
@@ -338,6 +342,7 @@ struct State { | |||||
/** For double-clicking */ | /** For double-clicking */ | ||||
double lastClickTime = -INFINITY; | double lastClickTime = -INFINITY; | ||||
widget::Widget *lastClickedWidget = NULL; | widget::Widget *lastClickedWidget = NULL; | ||||
std::set<int> heldKeys; | |||||
void setHovered(widget::Widget *w); | void setHovered(widget::Widget *w); | ||||
void setDragged(widget::Widget *w, int button); | void setDragged(widget::Widget *w, int button); | ||||
@@ -304,19 +304,6 @@ void ModuleWidget::drawShadow(const DrawArgs &args) { | |||||
nvgFill(args.vg); | nvgFill(args.vg); | ||||
} | } | ||||
void ModuleWidget::onHover(const event::Hover &e) { | |||||
// Instead of checking key-down events, delete the module even if key-repeat hasn't fired yet and the cursor is hovering over the widget. | |||||
if ((glfwGetKey(APP->window->win, GLFW_KEY_DELETE) == GLFW_PRESS | |||||
|| glfwGetKey(APP->window->win, GLFW_KEY_BACKSPACE) == GLFW_PRESS) | |||||
&& (APP->window->getMods() & RACK_MOD_MASK) == 0) { | |||||
removeAction(); | |||||
e.consume(NULL); | |||||
return; | |||||
} | |||||
OpaqueWidget::onHover(e); | |||||
} | |||||
void ModuleWidget::onButton(const event::Button &e) { | void ModuleWidget::onButton(const event::Button &e) { | ||||
OpaqueWidget::onButton(e); | OpaqueWidget::onButton(e); | ||||
if (e.isConsumed()) | if (e.isConsumed()) | ||||
@@ -379,6 +366,18 @@ void ModuleWidget::onHoverKey(const event::HoverKey &e) { | |||||
} break; | } break; | ||||
} | } | ||||
} | } | ||||
if (e.action == RACK_HELD) { | |||||
switch (e.key) { | |||||
case GLFW_KEY_DELETE: | |||||
case GLFW_KEY_BACKSPACE: { | |||||
if ((e.mods & RACK_MOD_MASK) == 0) { | |||||
removeAction(); | |||||
e.consume(NULL); | |||||
} | |||||
} break; | |||||
} | |||||
} | |||||
} | } | ||||
void ModuleWidget::onDragStart(const event::DragStart &e) { | void ModuleWidget::onDragStart(const event::DragStart &e) { | ||||
@@ -47,7 +47,7 @@ struct ParamField : ui::TextField { | |||||
} | } | ||||
ui::MenuOverlay *overlay = getAncestorOfType<ui::MenuOverlay>(); | ui::MenuOverlay *overlay = getAncestorOfType<ui::MenuOverlay>(); | ||||
overlay->requestedDelete = true; | |||||
overlay->requestDelete(); | |||||
e.consume(this); | e.consume(this); | ||||
} | } | ||||
@@ -74,26 +74,40 @@ void RackScrollWidget::draw(const DrawArgs &args) { | |||||
ScrollWidget::draw(args); | ScrollWidget::draw(args); | ||||
} | } | ||||
void RackScrollWidget::onHover(const event::Hover &e) { | |||||
void RackScrollWidget::onHoverKey(const event::HoverKey &e) { | |||||
ScrollWidget::onHoverKey(e); | |||||
if (e.isConsumed()) | |||||
return; | |||||
// Scroll with arrow keys | // Scroll with arrow keys | ||||
float arrowSpeed = 30.0; | float arrowSpeed = 30.0; | ||||
if ((APP->window->getMods() & RACK_MOD_MASK) == (RACK_MOD_CTRL |GLFW_MOD_SHIFT)) | |||||
if ((e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL |GLFW_MOD_SHIFT)) | |||||
arrowSpeed /= 16.0; | arrowSpeed /= 16.0; | ||||
else if ((APP->window->getMods() & RACK_MOD_MASK) == RACK_MOD_CTRL) | |||||
else if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) | |||||
arrowSpeed *= 4.0; | arrowSpeed *= 4.0; | ||||
else if ((APP->window->getMods() & RACK_MOD_MASK) == GLFW_MOD_SHIFT) | |||||
else if ((e.mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT) | |||||
arrowSpeed /= 4.0; | arrowSpeed /= 4.0; | ||||
if (glfwGetKey(APP->window->win, GLFW_KEY_LEFT) == GLFW_PRESS) | |||||
offset.x -= arrowSpeed; | |||||
if (glfwGetKey(APP->window->win, GLFW_KEY_RIGHT) == GLFW_PRESS) | |||||
offset.x += arrowSpeed; | |||||
if (glfwGetKey(APP->window->win, GLFW_KEY_UP) == GLFW_PRESS) | |||||
offset.y -= arrowSpeed; | |||||
if (glfwGetKey(APP->window->win, GLFW_KEY_DOWN) == GLFW_PRESS) | |||||
offset.y += arrowSpeed; | |||||
ScrollWidget::onHover(e); | |||||
if (e.action == RACK_HELD) { | |||||
switch (e.key) { | |||||
case GLFW_KEY_LEFT: { | |||||
offset.x -= arrowSpeed; | |||||
e.consume(this); | |||||
} break; | |||||
case GLFW_KEY_RIGHT: { | |||||
offset.x += arrowSpeed; | |||||
e.consume(this); | |||||
} break; | |||||
case GLFW_KEY_UP: { | |||||
offset.y -= arrowSpeed; | |||||
e.consume(this); | |||||
} break; | |||||
case GLFW_KEY_DOWN: { | |||||
offset.y += arrowSpeed; | |||||
e.consume(this); | |||||
} break; | |||||
} | |||||
} | |||||
} | } | ||||
void RackScrollWidget::onHoverScroll(const event::HoverScroll &e) { | void RackScrollWidget::onHoverScroll(const event::HoverScroll &e) { | ||||
@@ -169,6 +169,13 @@ bool State::handleButton(math::Vec pos, int button, int action, int mods) { | |||||
} | } | ||||
bool State::handleHover(math::Vec pos, math::Vec mouseDelta) { | bool State::handleHover(math::Vec pos, math::Vec mouseDelta) { | ||||
// Fake a key RACK_HELD event for each held key | |||||
int mods = 0; //APP->window->getMods(); | |||||
for (int key : heldKeys) { | |||||
int scancode = glfwGetKeyScancode(key); | |||||
handleKey(pos, key, scancode, RACK_HELD, mods); | |||||
} | |||||
if (draggedWidget) { | if (draggedWidget) { | ||||
// DragMove | // DragMove | ||||
DragMove eDragMove; | DragMove eDragMove; | ||||
@@ -204,6 +211,7 @@ bool State::handleHover(math::Vec pos, math::Vec mouseDelta) { | |||||
} | } | ||||
bool State::handleLeave() { | bool State::handleLeave() { | ||||
heldKeys.clear(); | |||||
setDragHovered(NULL); | setDragHovered(NULL); | ||||
setHovered(NULL); | setHovered(NULL); | ||||
return true; | return true; | ||||
@@ -256,6 +264,16 @@ bool State::handleText(math::Vec pos, int codepoint) { | |||||
} | } | ||||
bool State::handleKey(math::Vec pos, int key, int scancode, int action, int mods) { | bool State::handleKey(math::Vec pos, int key, int scancode, int action, int mods) { | ||||
// Update heldKey state | |||||
if (action == GLFW_PRESS) { | |||||
heldKeys.insert(key); | |||||
} | |||||
else if (action == GLFW_RELEASE) { | |||||
auto it = heldKeys.find(key); | |||||
if (it != heldKeys.end()) | |||||
heldKeys.erase(it); | |||||
} | |||||
if (selectedWidget) { | if (selectedWidget) { | ||||
// SelectKey | // SelectKey | ||||
Context cSelectKey; | Context cSelectKey; | ||||
@@ -81,7 +81,7 @@ void MenuItem::doAction() { | |||||
MenuOverlay *overlay = getAncestorOfType<MenuOverlay>(); | MenuOverlay *overlay = getAncestorOfType<MenuOverlay>(); | ||||
if (overlay) { | if (overlay) { | ||||
overlay->requestedDelete = true; | |||||
overlay->requestDelete(); | |||||
} | } | ||||
} | } | ||||
@@ -23,7 +23,8 @@ void MenuOverlay::onButton(const event::Button &e) { | |||||
return; | return; | ||||
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT) { | if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT) { | ||||
requestedDelete = true; | |||||
requestDelete(); | |||||
e.consume(this); | |||||
} | } | ||||
} | } | ||||
@@ -33,7 +34,8 @@ void MenuOverlay::onHoverKey(const event::HoverKey &e) { | |||||
return; | return; | ||||
if (e.action == GLFW_PRESS && e.key == GLFW_KEY_ESCAPE) { | if (e.action == GLFW_PRESS && e.key == GLFW_KEY_ESCAPE) { | ||||
requestedDelete = true; | |||||
requestDelete(); | |||||
e.consume(this); | |||||
} | } | ||||
} | } | ||||