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