@@ -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); | |||
} | |||
} | |||