@@ -20,6 +20,7 @@ struct ParamWidget : OpaqueWidget { | |||||
/** For legacy patch loading */ | /** For legacy patch loading */ | ||||
void fromJson(json_t *rootJ); | void fromJson(json_t *rootJ); | ||||
void onButton(event::Button &e) override; | void onButton(event::Button &e) override; | ||||
void onDragMove(event::DragMove &e) override; | |||||
}; | }; | ||||
@@ -6,7 +6,7 @@ namespace rack { | |||||
namespace event { | namespace event { | ||||
struct Context; | |||||
struct State; | |||||
} | } | ||||
struct Scene; | struct Scene; | ||||
@@ -15,7 +15,7 @@ struct Window; | |||||
struct Context { | struct Context { | ||||
event::Context *event = NULL; | |||||
event::State *event = NULL; | |||||
Scene *scene = NULL; | Scene *scene = NULL; | ||||
Engine *engine = NULL; | Engine *engine = NULL; | ||||
Window *window = NULL; | Window *window = NULL; | ||||
@@ -13,13 +13,25 @@ struct Widget; | |||||
namespace event { | namespace event { | ||||
/** Base event class */ | |||||
struct Event { | |||||
struct Context { | |||||
/** The Widget that consumes the event. | /** The Widget that consumes the event. | ||||
Set to `this` in your event handler method if consumed. | |||||
This stops propagation of the event if applicable. | This stops propagation of the event if applicable. | ||||
*/ | */ | ||||
Widget *target = NULL; | |||||
Widget *consumed = NULL; | |||||
}; | |||||
/** Base event class */ | |||||
struct Event { | |||||
Context *context = NULL; | |||||
void consume(Widget *w) const { | |||||
if (context) | |||||
context->consumed = w; | |||||
} | |||||
Widget *getConsumed() const { | |||||
return context ? context->consumed : NULL; | |||||
} | |||||
}; | }; | ||||
@@ -208,7 +220,7 @@ struct Zoom : Event { | |||||
}; | }; | ||||
struct Context { | |||||
struct State { | |||||
/** State widgets | /** State widgets | ||||
Don't set these directly unless you know what you're doing. Use the set*() methods instead. | Don't set these directly unless you know what you're doing. Use the set*() methods instead. | ||||
*/ | */ | ||||
@@ -23,7 +23,7 @@ struct MenuOverlay : OpaqueWidget { | |||||
void onButton(event::Button &e) override { | void onButton(event::Button &e) override { | ||||
OpaqueWidget::onButton(e); | OpaqueWidget::onButton(e); | ||||
if (e.target == this && e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT) { | |||||
if (e.getConsumed() == this && e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT) { | |||||
requestedDelete = true; | requestedDelete = true; | ||||
} | } | ||||
} | } | ||||
@@ -31,7 +31,7 @@ struct MenuOverlay : OpaqueWidget { | |||||
void onHoverKey(event::HoverKey &e) override { | void onHoverKey(event::HoverKey &e) override { | ||||
OpaqueWidget::onHoverKey(e); | OpaqueWidget::onHoverKey(e); | ||||
if (e.target == this && e.action == GLFW_PRESS && e.key == GLFW_KEY_ESCAPE) { | |||||
if (e.getConsumed() == this && e.action == GLFW_PRESS && e.key == GLFW_KEY_ESCAPE) { | |||||
requestedDelete = true; | requestedDelete = true; | ||||
} | } | ||||
} | } | ||||
@@ -140,7 +140,7 @@ struct ScrollWidget : OpaqueWidget { | |||||
void onHoverScroll(event::HoverScroll &e) override { | void onHoverScroll(event::HoverScroll &e) override { | ||||
offset = offset.minus(e.scrollDelta); | offset = offset.minus(e.scrollDelta); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
}; | }; | ||||
@@ -51,7 +51,7 @@ struct Slider : OpaqueWidget { | |||||
if (quantity) | if (quantity) | ||||
quantity->reset(); | quantity->reset(); | ||||
} | } | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
}; | }; | ||||
@@ -12,38 +12,38 @@ You may also call OpaqueWidget::on*() from the overridden method to continue rec | |||||
struct OpaqueWidget : virtual Widget { | struct OpaqueWidget : virtual Widget { | ||||
void onHover(event::Hover &e) override { | void onHover(event::Hover &e) override { | ||||
Widget::onHover(e); | Widget::onHover(e); | ||||
if (!e.target) | |||||
e.target = this; | |||||
if (!e.getConsumed()) | |||||
e.consume(this); | |||||
} | } | ||||
void onButton(event::Button &e) override { | void onButton(event::Button &e) override { | ||||
Widget::onButton(e); | Widget::onButton(e); | ||||
if (!e.target) | |||||
e.target = this; | |||||
if (!e.getConsumed()) | |||||
e.consume(this); | |||||
} | } | ||||
void onHoverKey(event::HoverKey &e) override { | void onHoverKey(event::HoverKey &e) override { | ||||
Widget::onHoverKey(e); | Widget::onHoverKey(e); | ||||
if (!e.target) | |||||
e.target = this; | |||||
if (!e.getConsumed()) | |||||
e.consume(this); | |||||
} | } | ||||
void onHoverText(event::HoverText &e) override { | void onHoverText(event::HoverText &e) override { | ||||
Widget::onHoverText(e); | Widget::onHoverText(e); | ||||
if (!e.target) | |||||
e.target = this; | |||||
if (!e.getConsumed()) | |||||
e.consume(this); | |||||
} | } | ||||
void onHoverScroll(event::HoverScroll &e) override { | void onHoverScroll(event::HoverScroll &e) override { | ||||
Widget::onHoverScroll(e); | Widget::onHoverScroll(e); | ||||
if (!e.target) | |||||
e.target = this; | |||||
if (!e.getConsumed()) | |||||
e.consume(this); | |||||
} | } | ||||
void onDragHover(event::DragHover &e) override { | void onDragHover(event::DragHover &e) override { | ||||
Widget::onDragHover(e); | Widget::onDragHover(e); | ||||
if (!e.target) | |||||
e.target = this; | |||||
if (!e.getConsumed()) | |||||
e.consume(this); | |||||
} | } | ||||
void onPathDrop(event::PathDrop &e) override { | void onPathDrop(event::PathDrop &e) override { | ||||
Widget::onPathDrop(e); | Widget::onPathDrop(e); | ||||
if (!e.target) | |||||
e.target = this; | |||||
if (!e.getConsumed()) | |||||
e.consume(this); | |||||
} | } | ||||
}; | }; | ||||
@@ -74,6 +74,22 @@ struct Widget { | |||||
// Events | // Events | ||||
template <typename TMethod, class TEvent> | |||||
void recurseEvent(TMethod f, TEvent &e) { | |||||
for (auto it = children.rbegin(); it != children.rend(); it++) { | |||||
Widget *child = *it; | |||||
// Filter child by visibility | |||||
if (!child->visible) | |||||
continue; | |||||
// Call child event handler | |||||
(child->*f)(e); | |||||
// Stop iterating if consumed | |||||
if (e.getConsumed()) | |||||
break; | |||||
} | |||||
} | |||||
template <typename TMethod, class TEvent> | template <typename TMethod, class TEvent> | ||||
void recursePositionEvent(TMethod f, TEvent &e) { | void recursePositionEvent(TMethod f, TEvent &e) { | ||||
for (auto it = children.rbegin(); it != children.rend(); it++) { | for (auto it = children.rbegin(); it != children.rend(); it++) { | ||||
@@ -84,16 +100,14 @@ struct Widget { | |||||
if (!child->box.contains(e.pos)) | if (!child->box.contains(e.pos)) | ||||
continue; | continue; | ||||
// Clone event so modifications do not up-propagate | |||||
// Clone event and adjust its position | |||||
TEvent e2 = e; | TEvent e2 = e; | ||||
e2.pos = e.pos.minus(child->box.pos); | e2.pos = e.pos.minus(child->box.pos); | ||||
// Call child event handler | // Call child event handler | ||||
(child->*f)(e2); | (child->*f)(e2); | ||||
// Up-propagate target if consumed | |||||
if (e2.target) { | |||||
e.target = e2.target; | |||||
// Stop iterating if consumed | |||||
if (e.getConsumed()) | |||||
break; | break; | ||||
} | |||||
} | } | ||||
} | } | ||||
@@ -121,7 +135,7 @@ struct Widget { | |||||
virtual void onPathDrop(event::PathDrop &e) {recursePositionEvent(&Widget::onPathDrop, e);} | virtual void onPathDrop(event::PathDrop &e) {recursePositionEvent(&Widget::onPathDrop, e);} | ||||
virtual void onAction(event::Action &e) {} | virtual void onAction(event::Action &e) {} | ||||
virtual void onChange(event::Change &e) {} | virtual void onChange(event::Change &e) {} | ||||
virtual void onZoom(event::Zoom &e) {} | |||||
virtual void onZoom(event::Zoom &e) {recurseEvent(&Widget::onZoom, e);} | |||||
}; | }; | ||||
@@ -135,7 +135,7 @@ struct MidiCcChoice : GridChoice { | |||||
} | } | ||||
void onSelect(event::Select &e) override { | void onSelect(event::Select &e) override { | ||||
e.target = this; | |||||
e.consume(this); | |||||
module->learningId = id; | module->learningId = id; | ||||
focusCc = -1; | focusCc = -1; | ||||
} | } | ||||
@@ -154,7 +154,7 @@ struct MidiCcChoice : GridChoice { | |||||
focusCc = 0; | focusCc = 0; | ||||
focusCc = focusCc * 10 + (c - '0'); | focusCc = focusCc * 10 + (c - '0'); | ||||
} | } | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
void onSelectKey(event::SelectKey &e) override { | void onSelectKey(event::SelectKey &e) override { | ||||
@@ -163,7 +163,7 @@ struct MidiCcChoice : GridChoice { | |||||
event::Deselect eDeselect; | event::Deselect eDeselect; | ||||
onDeselect(eDeselect); | onDeselect(eDeselect); | ||||
context()->event->selectedWidget = NULL; | context()->event->selectedWidget = NULL; | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -179,7 +179,7 @@ struct MidiTrigChoice : GridChoice { | |||||
} | } | ||||
void onSelect(event::Select &e) override { | void onSelect(event::Select &e) override { | ||||
e.target = this; | |||||
e.consume(this); | |||||
module->learningId = id; | module->learningId = id; | ||||
} | } | ||||
@@ -38,6 +38,8 @@ void Knob::onDragMove(event::DragMove &e) { | |||||
delta /= 16.f; | delta /= 16.f; | ||||
quantity->moveValue(delta); | quantity->moveValue(delta); | ||||
} | } | ||||
ParamWidget::onDragMove(e); | |||||
} | } | ||||
@@ -58,7 +58,7 @@ void LedDisplayChoice::onButton(event::Button &e) { | |||||
if (e.action == GLFW_PRESS && (e.button == GLFW_MOUSE_BUTTON_LEFT || e.button == GLFW_MOUSE_BUTTON_RIGHT)) { | if (e.action == GLFW_PRESS && (e.button == GLFW_MOUSE_BUTTON_LEFT || e.button == GLFW_MOUSE_BUTTON_RIGHT)) { | ||||
event::Action eAction; | event::Action eAction; | ||||
onAction(eAction); | onAction(eAction); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
} | } | ||||
@@ -110,10 +110,12 @@ struct BrowserListItem : OpaqueWidget { | |||||
} | } | ||||
void doAction() { | void doAction() { | ||||
event::Context eActionContext; | |||||
event::Action eAction; | event::Action eAction; | ||||
eAction.target = this; | |||||
eAction.context = &eActionContext; | |||||
eAction.consume(this); | |||||
onAction(eAction); | onAction(eAction); | ||||
if (eAction.target) { | |||||
if (eActionContext.consumed) { | |||||
MenuOverlay *overlay = getAncestorOfType<MenuOverlay>(); | MenuOverlay *overlay = getAncestorOfType<MenuOverlay>(); | ||||
overlay->requestedDelete = true; | overlay->requestedDelete = true; | ||||
} | } | ||||
@@ -462,7 +464,7 @@ void AuthorItem::onAction(event::Action &e) { | |||||
sAuthorFilter = author; | sAuthorFilter = author; | ||||
moduleBrowser->clearSearch(); | moduleBrowser->clearSearch(); | ||||
moduleBrowser->refreshSearch(); | moduleBrowser->refreshSearch(); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
void TagItem::onAction(event::Action &e) { | void TagItem::onAction(event::Action &e) { | ||||
@@ -470,7 +472,7 @@ void TagItem::onAction(event::Action &e) { | |||||
sTagFilter = tag; | sTagFilter = tag; | ||||
moduleBrowser->clearSearch(); | moduleBrowser->clearSearch(); | ||||
moduleBrowser->refreshSearch(); | moduleBrowser->refreshSearch(); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
void ClearFilterItem::onAction(event::Action &e) { | void ClearFilterItem::onAction(event::Action &e) { | ||||
@@ -478,7 +480,7 @@ void ClearFilterItem::onAction(event::Action &e) { | |||||
sAuthorFilter = ""; | sAuthorFilter = ""; | ||||
sTagFilter = ""; | sTagFilter = ""; | ||||
moduleBrowser->refreshSearch(); | moduleBrowser->refreshSearch(); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
void FavoriteRadioButton::onAction(event::Action &e) { | void FavoriteRadioButton::onAction(event::Action &e) { | ||||
@@ -515,40 +517,40 @@ void SearchModuleField::onSelectKey(event::SelectKey &e) { | |||||
case GLFW_KEY_ESCAPE: { | case GLFW_KEY_ESCAPE: { | ||||
MenuOverlay *overlay = getAncestorOfType<MenuOverlay>(); | MenuOverlay *overlay = getAncestorOfType<MenuOverlay>(); | ||||
overlay->requestedDelete = true; | overlay->requestedDelete = true; | ||||
e.target = this; | |||||
e.consume(this); | |||||
return; | return; | ||||
} break; | } break; | ||||
case GLFW_KEY_UP: { | case GLFW_KEY_UP: { | ||||
moduleBrowser->moduleList->incrementSelection(-1); | moduleBrowser->moduleList->incrementSelection(-1); | ||||
moduleBrowser->moduleList->scrollSelected(); | moduleBrowser->moduleList->scrollSelected(); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} break; | } break; | ||||
case GLFW_KEY_DOWN: { | case GLFW_KEY_DOWN: { | ||||
moduleBrowser->moduleList->incrementSelection(1); | moduleBrowser->moduleList->incrementSelection(1); | ||||
moduleBrowser->moduleList->scrollSelected(); | moduleBrowser->moduleList->scrollSelected(); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} break; | } break; | ||||
case GLFW_KEY_PAGE_UP: { | case GLFW_KEY_PAGE_UP: { | ||||
moduleBrowser->moduleList->incrementSelection(-5); | moduleBrowser->moduleList->incrementSelection(-5); | ||||
moduleBrowser->moduleList->scrollSelected(); | moduleBrowser->moduleList->scrollSelected(); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} break; | } break; | ||||
case GLFW_KEY_PAGE_DOWN: { | case GLFW_KEY_PAGE_DOWN: { | ||||
moduleBrowser->moduleList->incrementSelection(5); | moduleBrowser->moduleList->incrementSelection(5); | ||||
moduleBrowser->moduleList->scrollSelected(); | moduleBrowser->moduleList->scrollSelected(); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} break; | } break; | ||||
case GLFW_KEY_ENTER: { | case GLFW_KEY_ENTER: { | ||||
BrowserListItem *item = moduleBrowser->moduleList->getSelectedItem(); | BrowserListItem *item = moduleBrowser->moduleList->getSelectedItem(); | ||||
if (item) { | if (item) { | ||||
item->doAction(); | item->doAction(); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
} break; | } break; | ||||
} | } | ||||
} | } | ||||
if (!e.target) | |||||
if (!e.getConsumed()) | |||||
TextField::onSelectKey(e); | TextField::onSelectKey(e); | ||||
} | } | ||||
@@ -329,7 +329,7 @@ void ModuleWidget::onHover(event::Hover &e) { | |||||
void ModuleWidget::onButton(event::Button &e) { | void ModuleWidget::onButton(event::Button &e) { | ||||
OpaqueWidget::onButton(e); | OpaqueWidget::onButton(e); | ||||
if (e.target == this) { | |||||
if (e.getConsumed() == this) { | |||||
if (e.button == 1) { | if (e.button == 1) { | ||||
createContextMenu(); | createContextMenu(); | ||||
} | } | ||||
@@ -342,43 +342,43 @@ void ModuleWidget::onHoverKey(event::HoverKey &e) { | |||||
case GLFW_KEY_I: { | case GLFW_KEY_I: { | ||||
if (context()->window->isModPressed() && !context()->window->isShiftPressed()) { | if (context()->window->isModPressed() && !context()->window->isShiftPressed()) { | ||||
reset(); | reset(); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
} break; | } break; | ||||
case GLFW_KEY_R: { | case GLFW_KEY_R: { | ||||
if (context()->window->isModPressed() && !context()->window->isShiftPressed()) { | if (context()->window->isModPressed() && !context()->window->isShiftPressed()) { | ||||
randomize(); | randomize(); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
} break; | } break; | ||||
case GLFW_KEY_C: { | case GLFW_KEY_C: { | ||||
if (context()->window->isModPressed() && !context()->window->isShiftPressed()) { | if (context()->window->isModPressed() && !context()->window->isShiftPressed()) { | ||||
copyClipboard(); | copyClipboard(); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
} break; | } break; | ||||
case GLFW_KEY_V: { | case GLFW_KEY_V: { | ||||
if (context()->window->isModPressed() && !context()->window->isShiftPressed()) { | if (context()->window->isModPressed() && !context()->window->isShiftPressed()) { | ||||
pasteClipboard(); | pasteClipboard(); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
} break; | } break; | ||||
case GLFW_KEY_D: { | case GLFW_KEY_D: { | ||||
if (context()->window->isModPressed() && !context()->window->isShiftPressed()) { | if (context()->window->isModPressed() && !context()->window->isShiftPressed()) { | ||||
context()->scene->rackWidget->cloneModule(this); | context()->scene->rackWidget->cloneModule(this); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
} break; | } break; | ||||
case GLFW_KEY_U: { | case GLFW_KEY_U: { | ||||
if (context()->window->isModPressed() && !context()->window->isShiftPressed()) { | if (context()->window->isModPressed() && !context()->window->isShiftPressed()) { | ||||
disconnect(); | disconnect(); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
} break; | } break; | ||||
} | } | ||||
} | } | ||||
if (!e.target) | |||||
if (!e.getConsumed()) | |||||
OpaqueWidget::onHoverKey(e); | OpaqueWidget::onHoverKey(e); | ||||
} | } | ||||
@@ -39,5 +39,11 @@ void ParamWidget::onButton(event::Button &e) { | |||||
OpaqueWidget::onButton(e); | OpaqueWidget::onButton(e); | ||||
} | } | ||||
void ParamWidget::onDragMove(event::DragMove &e) { | |||||
if (quantity) { | |||||
DEBUG("%s", quantity->getString().c_str()); | |||||
} | |||||
} | |||||
} // namespace rack | } // namespace rack |
@@ -59,7 +59,7 @@ void PortWidget::onButton(event::Button &e) { | |||||
// event::DragEnter eDragEnter; | // event::DragEnter eDragEnter; | ||||
// onDragEnter(eDragEnter); | // onDragEnter(eDragEnter); | ||||
} | } | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
void PortWidget::onDragStart(event::DragStart &e) { | void PortWidget::onDragStart(event::DragStart &e) { | ||||
@@ -551,7 +551,7 @@ void RackWidget::onDragHover(event::DragHover &e) { | |||||
void RackWidget::onButton(event::Button &e) { | void RackWidget::onButton(event::Button &e) { | ||||
OpaqueWidget::onButton(e); | OpaqueWidget::onButton(e); | ||||
if (e.target == this) { | |||||
if (e.getConsumed() == this) { | |||||
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) { | if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) { | ||||
moduleBrowserCreate(); | moduleBrowserCreate(); | ||||
} | } | ||||
@@ -72,54 +72,54 @@ void Scene::onHoverKey(event::HoverKey &e) { | |||||
case GLFW_KEY_N: { | case GLFW_KEY_N: { | ||||
if (context()->window->isModPressed() && !context()->window->isShiftPressed()) { | if (context()->window->isModPressed() && !context()->window->isShiftPressed()) { | ||||
rackWidget->reset(); | rackWidget->reset(); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
} break; | } break; | ||||
case GLFW_KEY_Q: { | case GLFW_KEY_Q: { | ||||
if (context()->window->isModPressed() && !context()->window->isShiftPressed()) { | if (context()->window->isModPressed() && !context()->window->isShiftPressed()) { | ||||
context()->window->close(); | context()->window->close(); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
} break; | } break; | ||||
case GLFW_KEY_O: { | case GLFW_KEY_O: { | ||||
if (context()->window->isModPressed() && !context()->window->isShiftPressed()) { | if (context()->window->isModPressed() && !context()->window->isShiftPressed()) { | ||||
rackWidget->loadDialog(); | rackWidget->loadDialog(); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
if (context()->window->isModPressed() && context()->window->isShiftPressed()) { | if (context()->window->isModPressed() && context()->window->isShiftPressed()) { | ||||
rackWidget->revert(); | rackWidget->revert(); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
} break; | } break; | ||||
case GLFW_KEY_S: { | case GLFW_KEY_S: { | ||||
if (context()->window->isModPressed() && !context()->window->isShiftPressed()) { | if (context()->window->isModPressed() && !context()->window->isShiftPressed()) { | ||||
rackWidget->saveDialog(); | rackWidget->saveDialog(); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
if (context()->window->isModPressed() && context()->window->isShiftPressed()) { | if (context()->window->isModPressed() && context()->window->isShiftPressed()) { | ||||
rackWidget->saveAsDialog(); | rackWidget->saveAsDialog(); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
} break; | } break; | ||||
case GLFW_KEY_V: { | case GLFW_KEY_V: { | ||||
if (context()->window->isModPressed() && !context()->window->isShiftPressed()) { | if (context()->window->isModPressed() && !context()->window->isShiftPressed()) { | ||||
rackWidget->pastePresetClipboard(); | rackWidget->pastePresetClipboard(); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
} break; | } break; | ||||
case GLFW_KEY_ENTER: | case GLFW_KEY_ENTER: | ||||
case GLFW_KEY_KP_ENTER: { | case GLFW_KEY_KP_ENTER: { | ||||
moduleBrowserCreate(); | moduleBrowserCreate(); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} break; | } break; | ||||
case GLFW_KEY_F11: { | case GLFW_KEY_F11: { | ||||
context()->window->setFullScreen(!context()->window->isFullScreen()); | context()->window->setFullScreen(!context()->window->isFullScreen()); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
if (!e.target) | |||||
if (!e.getConsumed()) | |||||
OpaqueWidget::onHoverKey(e); | OpaqueWidget::onHoverKey(e); | ||||
} | } | ||||
@@ -128,11 +128,11 @@ void Scene::onPathDrop(event::PathDrop &e) { | |||||
const std::string &path = e.paths[0]; | const std::string &path = e.paths[0]; | ||||
if (string::extension(path) == "vcv") { | if (string::extension(path) == "vcv") { | ||||
rackWidget->load(path); | rackWidget->load(path); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
} | } | ||||
if (!e.target) | |||||
if (!e.getConsumed()) | |||||
OpaqueWidget::onPathDrop(e); | OpaqueWidget::onPathDrop(e); | ||||
} | } | ||||
@@ -280,7 +280,7 @@ struct AccountEmailField : TextField { | |||||
void onSelectKey(event::SelectKey &e) override { | void onSelectKey(event::SelectKey &e) override { | ||||
if (e.action == GLFW_PRESS && e.key == GLFW_KEY_TAB) { | if (e.action == GLFW_PRESS && e.key == GLFW_KEY_TAB) { | ||||
context()->event->selectedWidget = passwordField; | context()->event->selectedWidget = passwordField; | ||||
e.target = this; | |||||
e.consume(this); | |||||
return; | return; | ||||
} | } | ||||
TextField::onSelectKey(e); | TextField::onSelectKey(e); | ||||
@@ -296,7 +296,7 @@ struct AccountPasswordField : PasswordField { | |||||
void onSelectKey(event::SelectKey &e) override { | void onSelectKey(event::SelectKey &e) override { | ||||
if (e.action == GLFW_PRESS && (e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER)) { | if (e.action == GLFW_PRESS && (e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER)) { | ||||
logInItem->doAction(); | logInItem->doAction(); | ||||
e.target = this; | |||||
e.consume(this); | |||||
return; | return; | ||||
} | } | ||||
PasswordField::onSelectKey(e); | PasswordField::onSelectKey(e); | ||||
@@ -6,7 +6,7 @@ namespace rack { | |||||
namespace event { | namespace event { | ||||
void Context::setHovered(Widget *w) { | |||||
void State::setHovered(Widget *w) { | |||||
if (w == hoveredWidget) | if (w == hoveredWidget) | ||||
return; | return; | ||||
@@ -25,7 +25,7 @@ void Context::setHovered(Widget *w) { | |||||
} | } | ||||
} | } | ||||
void Context::setDragged(Widget *w) { | |||||
void State::setDragged(Widget *w) { | |||||
if (w == draggedWidget) | if (w == draggedWidget) | ||||
return; | return; | ||||
@@ -44,7 +44,7 @@ void Context::setDragged(Widget *w) { | |||||
} | } | ||||
} | } | ||||
void Context::setDragHovered(Widget *w) { | |||||
void State::setDragHovered(Widget *w) { | |||||
if (w == dragHoveredWidget) | if (w == dragHoveredWidget) | ||||
return; | return; | ||||
@@ -65,7 +65,7 @@ void Context::setDragHovered(Widget *w) { | |||||
} | } | ||||
} | } | ||||
void Context::setSelected(Widget *w) { | |||||
void State::setSelected(Widget *w) { | |||||
if (w == selectedWidget) | if (w == selectedWidget) | ||||
return; | return; | ||||
@@ -84,7 +84,7 @@ void Context::setSelected(Widget *w) { | |||||
} | } | ||||
} | } | ||||
void Context::finalizeWidget(Widget *w) { | |||||
void State::finalizeWidget(Widget *w) { | |||||
if (hoveredWidget == w) setHovered(NULL); | if (hoveredWidget == w) setHovered(NULL); | ||||
if (draggedWidget == w) setDragged(NULL); | if (draggedWidget == w) setDragged(NULL); | ||||
if (dragHoveredWidget == w) setDragHovered(NULL); | if (dragHoveredWidget == w) setDragHovered(NULL); | ||||
@@ -92,15 +92,17 @@ void Context::finalizeWidget(Widget *w) { | |||||
if (scrollWidget == w) scrollWidget = NULL; | if (scrollWidget == w) scrollWidget = NULL; | ||||
} | } | ||||
void Context::handleButton(math::Vec pos, int button, int action, int mods) { | |||||
void State::handleButton(math::Vec pos, int button, int action, int mods) { | |||||
// event::Button | // event::Button | ||||
event::Context eButtonContext; | |||||
event::Button eButton; | event::Button eButton; | ||||
eButton.context = &eButtonContext; | |||||
eButton.pos = pos; | eButton.pos = pos; | ||||
eButton.button = button; | eButton.button = button; | ||||
eButton.action = action; | eButton.action = action; | ||||
eButton.mods = mods; | eButton.mods = mods; | ||||
rootWidget->onButton(eButton); | rootWidget->onButton(eButton); | ||||
Widget *clickedWidget = eButton.target; | |||||
Widget *clickedWidget = eButtonContext.consumed; | |||||
if (button == GLFW_MOUSE_BUTTON_LEFT) { | if (button == GLFW_MOUSE_BUTTON_LEFT) { | ||||
if (action == GLFW_PRESS) { | if (action == GLFW_PRESS) { | ||||
@@ -135,7 +137,7 @@ void Context::handleButton(math::Vec pos, int button, int action, int mods) { | |||||
// } | // } | ||||
} | } | ||||
void Context::handleHover(math::Vec pos, math::Vec mouseDelta) { | |||||
void State::handleHover(math::Vec pos, math::Vec mouseDelta) { | |||||
if (draggedWidget) { | if (draggedWidget) { | ||||
// event::DragMove | // event::DragMove | ||||
event::DragMove eDragMove; | event::DragMove eDragMove; | ||||
@@ -143,13 +145,15 @@ void Context::handleHover(math::Vec pos, math::Vec mouseDelta) { | |||||
draggedWidget->onDragMove(eDragMove); | draggedWidget->onDragMove(eDragMove); | ||||
// event::DragHover | // event::DragHover | ||||
event::Context eDragHoverContext; | |||||
event::DragHover eDragHover; | event::DragHover eDragHover; | ||||
eDragHover.context = &eDragHoverContext; | |||||
eDragHover.pos = pos; | eDragHover.pos = pos; | ||||
eDragHover.mouseDelta = mouseDelta; | eDragHover.mouseDelta = mouseDelta; | ||||
eDragHover.origin = draggedWidget; | eDragHover.origin = draggedWidget; | ||||
rootWidget->onDragHover(eDragHover); | rootWidget->onDragHover(eDragHover); | ||||
setDragHovered(eDragHover.target); | |||||
setDragHovered(eDragHoverContext.consumed); | |||||
return; | return; | ||||
} | } | ||||
@@ -163,67 +167,81 @@ void Context::handleHover(math::Vec pos, math::Vec mouseDelta) { | |||||
// } | // } | ||||
// event::Hover | // event::Hover | ||||
event::Context eHoverContext; | |||||
event::Hover eHover; | event::Hover eHover; | ||||
eHover.context = &eHoverContext; | |||||
eHover.pos = pos; | eHover.pos = pos; | ||||
eHover.mouseDelta = mouseDelta; | eHover.mouseDelta = mouseDelta; | ||||
rootWidget->onHover(eHover); | rootWidget->onHover(eHover); | ||||
setHovered(eHover.target); | |||||
setHovered(eHoverContext.consumed); | |||||
} | } | ||||
void Context::handleLeave() { | |||||
void State::handleLeave() { | |||||
setDragHovered(NULL); | setDragHovered(NULL); | ||||
setHovered(NULL); | setHovered(NULL); | ||||
} | } | ||||
void Context::handleScroll(math::Vec pos, math::Vec scrollDelta) { | |||||
void State::handleScroll(math::Vec pos, math::Vec scrollDelta) { | |||||
// event::HoverScroll | // event::HoverScroll | ||||
event::Context eHoverScrollContext; | |||||
event::HoverScroll eHoverScroll; | event::HoverScroll eHoverScroll; | ||||
eHoverScroll.context = &eHoverScrollContext; | |||||
eHoverScroll.pos = pos; | eHoverScroll.pos = pos; | ||||
eHoverScroll.scrollDelta = scrollDelta; | eHoverScroll.scrollDelta = scrollDelta; | ||||
rootWidget->onHoverScroll(eHoverScroll); | rootWidget->onHoverScroll(eHoverScroll); | ||||
} | } | ||||
void Context::handleDrop(math::Vec pos, std::vector<std::string> paths) { | |||||
void State::handleDrop(math::Vec pos, std::vector<std::string> paths) { | |||||
// event::PathDrop | // event::PathDrop | ||||
event::Context ePathDropContext; | |||||
event::PathDrop ePathDrop; | event::PathDrop ePathDrop; | ||||
ePathDrop.context = &ePathDropContext; | |||||
ePathDrop.pos = pos; | ePathDrop.pos = pos; | ||||
ePathDrop.paths = paths; | ePathDrop.paths = paths; | ||||
rootWidget->onPathDrop(ePathDrop); | rootWidget->onPathDrop(ePathDrop); | ||||
} | } | ||||
void Context::handleText(math::Vec pos, int codepoint) { | |||||
void State::handleText(math::Vec pos, int codepoint) { | |||||
if (selectedWidget) { | if (selectedWidget) { | ||||
// event::SelectText | // event::SelectText | ||||
event::Context eSelectTextContext; | |||||
event::SelectText eSelectText; | event::SelectText eSelectText; | ||||
eSelectText.context = &eSelectTextContext; | |||||
eSelectText.codepoint = codepoint; | eSelectText.codepoint = codepoint; | ||||
selectedWidget->onSelectText(eSelectText); | selectedWidget->onSelectText(eSelectText); | ||||
if (eSelectText.target) | |||||
if (eSelectTextContext.consumed) | |||||
return; | return; | ||||
} | } | ||||
// event::HoverText | // event::HoverText | ||||
event::Context eHoverTextContext; | |||||
event::HoverText eHoverText; | event::HoverText eHoverText; | ||||
eHoverText.context = &eHoverTextContext; | |||||
eHoverText.pos = pos; | eHoverText.pos = pos; | ||||
eHoverText.codepoint = codepoint; | eHoverText.codepoint = codepoint; | ||||
rootWidget->onHoverText(eHoverText); | rootWidget->onHoverText(eHoverText); | ||||
} | } | ||||
void Context::handleKey(math::Vec pos, int key, int scancode, int action, int mods) { | |||||
void State::handleKey(math::Vec pos, int key, int scancode, int action, int mods) { | |||||
if (selectedWidget) { | if (selectedWidget) { | ||||
// event::SelectKey | // event::SelectKey | ||||
event::Context eSelectKeyContext; | |||||
event::SelectKey eSelectKey; | event::SelectKey eSelectKey; | ||||
eSelectKey.context = &eSelectKeyContext; | |||||
eSelectKey.key = key; | eSelectKey.key = key; | ||||
eSelectKey.scancode = scancode; | eSelectKey.scancode = scancode; | ||||
eSelectKey.action = action; | eSelectKey.action = action; | ||||
eSelectKey.mods = mods; | eSelectKey.mods = mods; | ||||
selectedWidget->onSelectKey(eSelectKey); | selectedWidget->onSelectKey(eSelectKey); | ||||
if (eSelectKey.target) | |||||
if (eSelectKeyContext.consumed) | |||||
return; | return; | ||||
} | } | ||||
// event::HoverKey | // event::HoverKey | ||||
event::Context eHoverKeyContext; | |||||
event::HoverKey eHoverKey; | event::HoverKey eHoverKey; | ||||
eHoverKey.context = &eHoverKeyContext; | |||||
eHoverKey.pos = pos; | eHoverKey.pos = pos; | ||||
eHoverKey.key = key; | eHoverKey.key = key; | ||||
eHoverKey.scancode = scancode; | eHoverKey.scancode = scancode; | ||||
@@ -232,9 +250,11 @@ void Context::handleKey(math::Vec pos, int key, int scancode, int action, int mo | |||||
rootWidget->onHoverKey(eHoverKey); | rootWidget->onHoverKey(eHoverKey); | ||||
} | } | ||||
void Context::handleZoom() { | |||||
void State::handleZoom() { | |||||
// event::Zoom | // event::Zoom | ||||
event::Context eZoomContext; | |||||
event::Zoom eZoom; | event::Zoom eZoom; | ||||
eZoom.context = &eZoomContext; | |||||
rootWidget->onZoom(eZoom); | rootWidget->onZoom(eZoom); | ||||
} | } | ||||
@@ -79,7 +79,7 @@ int main(int argc, char *argv[]) { | |||||
// Initialize app | // Initialize app | ||||
context()->engine = new Engine; | context()->engine = new Engine; | ||||
context()->event = new event::Context; | |||||
context()->event = new event::State; | |||||
context()->scene = new Scene; | context()->scene = new Scene; | ||||
context()->scene->devMode = devMode; | context()->scene->devMode = devMode; | ||||
context()->event->rootWidget = context()->scene; | context()->event->rootWidget = context()->scene; | ||||
@@ -61,11 +61,13 @@ void MenuItem::doAction() { | |||||
if (disabled) | if (disabled) | ||||
return; | return; | ||||
event::Context eActionContext; | |||||
event::Action eAction; | event::Action eAction; | ||||
eAction.context = &eActionContext; | |||||
// Consume event by default, but allow action to un-consume it to prevent the menu from being removed. | // Consume event by default, but allow action to un-consume it to prevent the menu from being removed. | ||||
eAction.target = this; | |||||
eAction.consume(this); | |||||
onAction(eAction); | onAction(eAction); | ||||
if (!eAction.target) | |||||
if (!eActionContext.consumed) | |||||
return; | return; | ||||
Widget *overlay = getAncestorOfType<MenuOverlay>(); | Widget *overlay = getAncestorOfType<MenuOverlay>(); | ||||
@@ -47,7 +47,7 @@ void TextField::onHover(event::Hover &e) { | |||||
} | } | ||||
void TextField::onEnter(event::Enter &e) { | void TextField::onEnter(event::Enter &e) { | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
void TextField::onSelectText(event::SelectText &e) { | void TextField::onSelectText(event::SelectText &e) { | ||||
@@ -55,11 +55,11 @@ void TextField::onSelectText(event::SelectText &e) { | |||||
std::string newText(1, (char) e.codepoint); | std::string newText(1, (char) e.codepoint); | ||||
insertText(newText); | insertText(newText); | ||||
} | } | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
void TextField::onSelectKey(event::SelectKey &e) { | void TextField::onSelectKey(event::SelectKey &e) { | ||||
if (e.action == GLFW_PRESS) { | |||||
if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) { | |||||
switch (e.key) { | switch (e.key) { | ||||
case GLFW_KEY_BACKSPACE: { | case GLFW_KEY_BACKSPACE: { | ||||
if (cursor == selection) { | if (cursor == selection) { | ||||
@@ -172,7 +172,7 @@ void TextField::onSelectKey(event::SelectKey &e) { | |||||
cursor = math::clamp(cursor, 0, (int) text.size()); | cursor = math::clamp(cursor, 0, (int) text.size()); | ||||
selection = math::clamp(selection, 0, (int) text.size()); | selection = math::clamp(selection, 0, (int) text.size()); | ||||
e.target = this; | |||||
e.consume(this); | |||||
} | } | ||||
} | } | ||||