From c9cf25f66e8fcc861cedb430b7fd375ef36eb9bb Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sun, 7 Feb 2021 13:51:45 -0500 Subject: [PATCH] Merge event namespace into widget namespace. Event classes are now defined inside the Widget class. --- include/context.hpp | 8 +- include/event.hpp | 449 ------------------------------------- include/rack.hpp | 1 + include/ui/RadioButton.hpp | 1 - include/widget/Widget.hpp | 337 +++++++++++++++++++++++++--- include/widget/event.hpp | 155 +++++++++++++ src/app/CableWidget.cpp | 1 - src/ui/Button.cpp | 1 - src/ui/ChoiceButton.cpp | 1 - src/ui/MenuItem.cpp | 2 +- src/widget/ZoomWidget.cpp | 2 +- src/{ => widget}/event.cpp | 146 ++++++------ src/window.cpp | 2 +- standalone/main.cpp | 2 +- 14 files changed, 542 insertions(+), 566 deletions(-) delete mode 100644 include/event.hpp create mode 100644 include/widget/event.hpp rename src/{ => widget}/event.cpp (68%) diff --git a/include/context.hpp b/include/context.hpp index 442c672e..79881601 100644 --- a/include/context.hpp +++ b/include/context.hpp @@ -19,9 +19,9 @@ struct Window; struct PatchManager; -namespace event { -struct State; -} // namespace event +namespace widget { +struct EventState; +} // namespace widget namespace app { @@ -31,7 +31,7 @@ struct Scene; /** Contains the application state */ struct Context { - event::State* event = NULL; + widget::EventState* event = NULL; app::Scene* scene = NULL; engine::Engine* engine = NULL; Window* window = NULL; diff --git a/include/event.hpp b/include/event.hpp deleted file mode 100644 index f317291f..00000000 --- a/include/event.hpp +++ /dev/null @@ -1,449 +0,0 @@ -#pragma once -#include -#include - -#include -#include - - - -/** Remaps Ctrl to Cmd on Mac -Use this instead of GLFW_MOD_CONTROL, since Cmd should be used on Mac in place of Ctrl on Linux/Windows. -*/ -#if defined ARCH_MAC - #define RACK_MOD_CTRL GLFW_MOD_SUPER - #define RACK_MOD_CTRL_NAME "⌘" -#else - #define RACK_MOD_CTRL GLFW_MOD_CONTROL - #define RACK_MOD_CTRL_NAME "Ctrl" -#endif -#define RACK_MOD_SHIFT_NAME "Shift" -#define RACK_MOD_ALT_NAME "Alt" - -/** Filters actual mod keys from the mod flags. -Use this if you don't care about GLFW_MOD_CAPS_LOCK and GLFW_MOD_NUM_LOCK. -Example usage: - if ((e.mod & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) ... -*/ -#define RACK_MOD_MASK (GLFW_MOD_SHIFT | GLFW_MOD_CONTROL | GLFW_MOD_ALT | GLFW_MOD_SUPER) - - -namespace rack { - - -namespace widget { -struct Widget; -} - - -/** Handles user interaction with Widget. -*/ -namespace event { - - -/** A per-event state shared and writable by all widgets that recursively handle an event. */ -struct Context { - /** Whether the event should continue recursing to children Widgets. */ - bool propagating = true; - /** Whether the event has been consumed by an event handler and no more handlers should consume the event. */ - bool consumed = false; - /** The widget that responded to the event. */ - widget::Widget* target = NULL; -}; - - -/** Base class for all events. */ -struct Base { - Context* context = NULL; - - /** Prevents the event from being handled by more Widgets. - */ - void stopPropagating() const { - if (!context) - return; - context->propagating = false; - } - bool isPropagating() const { - if (!context) - return true; - return context->propagating; - } - /** Tells the event handler that a particular Widget consumed the event. - You usually want to stop propagation as well, so call consume() instead. - */ - void setTarget(widget::Widget* w) const { - if (!context) - return; - context->target = w; - } - widget::Widget* getTarget() const { - if (!context) - return NULL; - return context->target; - } - /** Sets the target Widget and stops propagating. - A NULL Widget may be passed to consume but not set a target. - */ - void consume(widget::Widget* w) const { - if (!context) - return; - context->propagating = false; - context->consumed = true; - context->target = w; - } - void unconsume() const { - if (!context) - return; - context->consumed = false; - } - bool isConsumed() const { - if (!context) - return false; - return context->consumed; - } -}; - - -/** An event prototype with a vector position. */ -struct PositionBase { - /** The pixel coordinate where the event occurred, relative to the Widget it is called on. */ - math::Vec pos; -}; - - -#define RACK_HELD 3 - - -/** An event prototype with a GLFW key. */ -struct KeyBase { - /** The key corresponding to what it would be called in its position on a QWERTY US keyboard. - For example, the WASD directional keys used for first-person shooters will always be reported as "WASD", regardless if they say "ZQSD" on an AZERTY keyboard. - You should usually not use these for printable characters such as "Ctrl+V" key commands. Instead, use `keyName`. - You *should* use these for non-printable keys, such as Escape, arrow keys, Home, F1-12, etc. - You should also use this for Enter, Tab, and Space. Although they are printable keys, they do not appear in `keyName`. - See GLFW_KEY_* for the list of possible values. - */ - int key; - /** Platform-dependent "software" key code. - This variable is only included for completion. There should be no reason for you to use this. - You should instead use `key` (for non-printable characters) or `keyName` (for printable characters). - Values are platform independent and can change between different keyboards or keyboard layouts on the same OS. - */ - int scancode; - /** String containing the lowercase key name, if it produces a printable character. - This is the only variable that correctly represents the label printed on any keyboard layout, whether it's QWERTY, AZERTY, QWERTZ, Dvorak, etc. - For example, if the user presses the key labeled "q" regardless of the key position, `keyName` will be "q". - For non-printable characters this is an empty string. - Enter, Tab, and Space do not give a `keyName`. Use `key` instead. - Shift has no effect on the key name. Shift+1 results in "1", Shift+q results in "q", etc. - */ - std::string keyName; - /** The type of event occurring with the key. - Possible values are GLFW_RELEASE, GLFW_PRESS, GLFW_REPEAT, or RACK_HELD. - RACK_HELD is sent every frame while the key is held. - */ - int action; - /** Bitwise OR of key modifiers, such as Ctrl or Shift. - Use (mods & RACK_MOD_MASK) == RACK_MOD_CTRL to check for Ctrl on Linux and Windows but Cmd on Mac. - See GLFW_MOD_* for the list of possible values. - */ - int mods; -}; - - -/** An event prototype with a Unicode character. */ -struct TextBase { - /** Unicode code point of the character */ - int codepoint; -}; - - -/** Occurs every frame when the mouse is hovering over a Widget. -Recurses. -Consume this event to allow Enter and Leave to occur. -*/ -struct Hover : Base, PositionBase { - /** Change in mouse position since the last frame. Can be zero. */ - math::Vec mouseDelta; -}; - - -/** Occurs each mouse button press or release. -Recurses. -Consume this event to allow DoubleClick, Select, Deselect, SelectKey, SelectText, DragStart, DragEnd, DragMove, and DragDrop to occur. -*/ -struct Button : Base, PositionBase { - /** GLFW_MOUSE_BUTTON_LEFT, GLFW_MOUSE_BUTTON_RIGHT, GLFW_MOUSE_BUTTON_MIDDLE, etc. */ - int button; - /** GLFW_PRESS or GLFW_RELEASE */ - int action; - /** GLFW_MOD_* */ - int mods; -}; - - -/** Occurs when the left mouse button is pressed a second time on the same Widget within a time duration. -Must consume the Button event (on left button press) to receive this event. -*/ -struct DoubleClick : Base { -}; - - -/** Occurs when a key is pressed, released, or repeated while the mouse is hovering a Widget. -Recurses. -*/ -struct HoverKey : Base, PositionBase, KeyBase { -}; - - -/** Occurs when a character is typed while the mouse is hovering a Widget. -Recurses. -*/ -struct HoverText : Base, PositionBase, TextBase { -}; - - -/** Occurs when the mouse scroll wheel is moved while the mouse is hovering a Widget. -Recurses. -*/ -struct HoverScroll : Base, PositionBase { - /** Change of scroll wheel position. */ - math::Vec scrollDelta; -}; - - -/** Occurs when a Widget begins consuming the Hover event. -Must consume the Hover event to receive this event. -The target sets `hoveredWidget`, which allows Leave to occur. -*/ -struct Enter : Base { -}; - - -/** Occurs when a different Widget is entered. -Must consume the Hover event (when a Widget is entered) to receive this event. -*/ -struct Leave : Base { -}; - - -/** Occurs when a Widget begins consuming the Button press event for the left mouse button. -Must consume the Button event (on left button press) to receive this event. -The target sets `selectedWidget`, which allows SelectText and SelectKey to occur. -*/ -struct Select : Base { -}; - - -/** Occurs when a different Widget is selected. -Must consume the Button event (on left button press, when the Widget is selected) to receive this event. -*/ -struct Deselect : Base { -}; - - -/** Occurs when a key is pressed, released, or repeated while a Widget is selected. -Must consume to prevent HoverKey from being triggered. -*/ -struct SelectKey : Base, KeyBase { -}; - - -/** Occurs when text is typed while a Widget is selected. -Must consume to prevent HoverKey from being triggered. -*/ -struct SelectText : Base, TextBase { -}; - - -struct DragBase : Base { - /** The mouse button held while dragging. */ - int button; -}; - - -/** Occurs when a Widget begins being dragged. -Must consume the Button event (on press) to receive this event. -The target sets `draggedWidget`, which allows DragEnd, DragMove, DragHover, DragEnter, and DragDrop to occur. -*/ -struct DragStart : DragBase { -}; - - -/** Occurs when a Widget stops being dragged by releasing the mouse button. -Must consume the Button event (on press, when the Widget drag begins) to receive this event. -*/ -struct DragEnd : DragBase { -}; - - -/** Occurs every frame on the dragged Widget. -Must consume the Button event (on press, when the Widget drag begins) to receive this event. -*/ -struct DragMove : DragBase { - /** Change in mouse position since the last frame. Can be zero. */ - math::Vec mouseDelta; -}; - - -/** Occurs every frame when the mouse is hovering over a Widget while another Widget (possibly the same one) is being dragged. -Recurses. -Consume this event to allow DragEnter and DragLeave to occur. -*/ -struct DragHover : DragBase, PositionBase { - /** The dragged widget */ - widget::Widget* origin = NULL; - /** Change in mouse position since the last frame. Can be zero. */ - math::Vec mouseDelta; -}; - - -/** Occurs when the mouse enters a Widget while dragging. -Must consume the DragHover event to receive this event. -The target sets `draggedWidget`, which allows DragLeave to occur. -*/ -struct DragEnter : DragBase { - /** The dragged widget */ - widget::Widget* origin = NULL; -}; - - -/** Occurs when the mouse leaves a Widget while dragging. -Must consume the DragHover event (when the Widget is entered) to receive this event. -*/ -struct DragLeave : DragBase { - /** The dragged widget */ - widget::Widget* origin = NULL; -}; - - -/** Occurs when the mouse button is released over a Widget while dragging. -Must consume the Button event (on release) to receive this event. -*/ -struct DragDrop : DragBase { - /** The dragged widget */ - widget::Widget* origin = NULL; -}; - - -/** Occurs when a selection of files from the operating system is dropped onto a Widget. -Recurses. -*/ -struct PathDrop : Base, PositionBase { - PathDrop(const std::vector& paths) : paths(paths) {} - - /** List of file paths in the dropped selection */ - const std::vector& paths; -}; - - -/** Occurs after a certain action is triggered on a Widget. -The concept of an "action" is defined by the type of Widget. -*/ -struct Action : Base { -}; - - -/** Occurs after the value of a Widget changes. -The concept of a "value" is defined by the type of Widget. -*/ -struct Change : Base { -}; - - -/** Occurs when the pixel buffer of this module must be refreshed. -Recurses. -*/ -struct Dirty : Base { -}; - - -/** Occurs after a Widget's position is set by Widget::setPosition(). -*/ -struct Reposition : Base { -}; - - -/** Occurs after a Widget's size is set by Widget::setSize(). -*/ -struct Resize : Base { -}; - - -/** Occurs after a Widget is added to a parent. -*/ -struct Add : Base { -}; - - -/** Occurs before a Widget is removed from its parent. -*/ -struct Remove : Base { -}; - - -/** Occurs after a Widget is shown with Widget::show(). -Recurses. -*/ -struct Show : Base { -}; - - -/** Occurs after a Widget is hidden with Widget::hide(). -Recurses. -*/ -struct Hide : Base { -}; - - -struct State { - widget::Widget* rootWidget = NULL; - /** State widgets - Don't set these directly unless you know what you're doing. Use the set*() methods instead. - */ - widget::Widget* hoveredWidget = NULL; - widget::Widget* draggedWidget = NULL; - int dragButton = 0; - widget::Widget* dragHoveredWidget = NULL; - widget::Widget* selectedWidget = NULL; - /** For double-clicking */ - double lastClickTime = -INFINITY; - widget::Widget* lastClickedWidget = NULL; - std::set heldKeys; - - widget::Widget* getRootWidget() { - return rootWidget; - } - widget::Widget* getHoveredWidget() { - return hoveredWidget; - } - widget::Widget* getDraggedWidget() { - return draggedWidget; - } - widget::Widget* getDragHoveredWidget() { - return dragHoveredWidget; - } - widget::Widget* getSelectedWidget() { - return selectedWidget; - } - - void setHovered(widget::Widget* w); - void setDragged(widget::Widget* w, int button); - void setDragHovered(widget::Widget* w); - void setSelected(widget::Widget* w); - /** Prepares a widget for deletion */ - void finalizeWidget(widget::Widget* w); - - bool handleButton(math::Vec pos, int button, int action, int mods); - bool handleHover(math::Vec pos, math::Vec mouseDelta); - bool handleLeave(); - bool handleScroll(math::Vec pos, math::Vec scrollDelta); - bool handleText(math::Vec pos, int codepoint); - bool handleKey(math::Vec pos, int key, int scancode, int action, int mods); - bool handleDrop(math::Vec pos, const std::vector& paths); - bool handleDirty(); -}; - - -} // namespace event -} // namespace rack diff --git a/include/rack.hpp b/include/rack.hpp index 435b8041..e601c7b4 100644 --- a/include/rack.hpp +++ b/include/rack.hpp @@ -16,6 +16,7 @@ #include #include +#include #include #include #include diff --git a/include/ui/RadioButton.hpp b/include/ui/RadioButton.hpp index 53e16440..4feaa279 100644 --- a/include/ui/RadioButton.hpp +++ b/include/ui/RadioButton.hpp @@ -3,7 +3,6 @@ #include #include #include -#include namespace rack { diff --git a/include/widget/Widget.hpp b/include/widget/Widget.hpp index bd7d31c9..db34e20a 100644 --- a/include/widget/Widget.hpp +++ b/include/widget/Widget.hpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include @@ -168,60 +168,333 @@ struct Widget : WeakBase { } } - /** Override these event callbacks to respond to events. - See event.hpp for a description of each event. + using BaseEvent = widget::BaseEvent; + + /** An event prototype with a vector position. */ + struct PositionBaseEvent { + /** The pixel coordinate where the event occurred, relative to the Widget it is called on. */ + math::Vec pos; + }; + + /** Occurs every frame when the mouse is hovering over a Widget. + Recurses. + Consume this event to allow Enter and Leave to occur. */ - virtual void onHover(const event::Hover& e) { + struct HoverEvent : BaseEvent, PositionBaseEvent { + /** Change in mouse position since the last frame. Can be zero. */ + math::Vec mouseDelta; + }; + virtual void onHover(const HoverEvent& e) { recursePositionEvent(&Widget::onHover, e); } - virtual void onButton(const event::Button& e) { + + /** Occurs each mouse button press or release. + Recurses. + Consume this event to allow DoubleClick, Select, Deselect, SelectKey, SelectText, DragStart, DragEnd, DragMove, and DragDrop to occur. + */ + struct ButtonEvent : BaseEvent, PositionBaseEvent { + /** GLFW_MOUSE_BUTTON_LEFT, GLFW_MOUSE_BUTTON_RIGHT, GLFW_MOUSE_BUTTON_MIDDLE, etc. */ + int button; + /** GLFW_PRESS or GLFW_RELEASE */ + int action; + /** GLFW_MOD_* */ + int mods; + }; + virtual void onButton(const ButtonEvent& e) { recursePositionEvent(&Widget::onButton, e); } - virtual void onDoubleClick(const event::DoubleClick& e) {} - virtual void onHoverKey(const event::HoverKey& e) { + + /** Occurs when the left mouse button is pressed a second time on the same Widget within a time duration. + Must consume the Button event (on left button press) to receive this event. + */ + struct DoubleClickEvent : BaseEvent {}; + virtual void onDoubleClick(const DoubleClickEvent& e) {} + + /** An event prototype with a GLFW key. */ + struct KeyBaseEvent { + /** The key corresponding to what it would be called in its position on a QWERTY US keyboard. + For example, the WASD directional keys used for first-person shooters will always be reported as "WASD", regardless if they say "ZQSD" on an AZERTY keyboard. + You should usually not use these for printable characters such as "Ctrl+V" key commands. Instead, use `keyName`. + You *should* use these for non-printable keys, such as Escape, arrow keys, Home, F1-12, etc. + You should also use this for Enter, Tab, and Space. Although they are printable keys, they do not appear in `keyName`. + See GLFW_KEY_* for the list of possible values. + */ + int key; + /** Platform-dependent "software" key code. + This variable is only included for completion. There should be no reason for you to use this. + You should instead use `key` (for non-printable characters) or `keyName` (for printable characters). + Values are platform independent and can change between different keyboards or keyboard layouts on the same OS. + */ + int scancode; + /** String containing the lowercase key name, if it produces a printable character. + This is the only variable that correctly represents the label printed on any keyboard layout, whether it's QWERTY, AZERTY, QWERTZ, Dvorak, etc. + For example, if the user presses the key labeled "q" regardless of the key position, `keyName` will be "q". + For non-printable characters this is an empty string. + Enter, Tab, and Space do not give a `keyName`. Use `key` instead. + Shift has no effect on the key name. Shift+1 results in "1", Shift+q results in "q", etc. + */ + std::string keyName; + /** The type of event occurring with the key. + Possible values are GLFW_RELEASE, GLFW_PRESS, GLFW_REPEAT, or RACK_HELD. + RACK_HELD is sent every frame while the key is held. + */ + int action; + /** Bitwise OR of key modifiers, such as Ctrl or Shift. + Use (mods & RACK_MOD_MASK) == RACK_MOD_CTRL to check for Ctrl on Linux and Windows but Cmd on Mac. + See GLFW_MOD_* for the list of possible values. + */ + int mods; + }; + + /** Occurs when a key is pressed, released, or repeated while the mouse is hovering a Widget. + Recurses. + */ + struct HoverKeyEvent : BaseEvent, PositionBaseEvent, KeyBaseEvent {}; + virtual void onHoverKey(const HoverKeyEvent& e) { recursePositionEvent(&Widget::onHoverKey, e); } - virtual void onHoverText(const event::HoverText& e) { + + /** An event prototype with a Unicode character. */ + struct TextBaseEvent { + /** Unicode code point of the character */ + int codepoint; + }; + /** Occurs when a character is typed while the mouse is hovering a Widget. + Recurses. + */ + struct HoverTextEvent : BaseEvent, PositionBaseEvent, TextBaseEvent {}; + virtual void onHoverText(const HoverTextEvent& e) { recursePositionEvent(&Widget::onHoverText, e); } - virtual void onHoverScroll(const event::HoverScroll& e) { + + /** Occurs when the mouse scroll wheel is moved while the mouse is hovering a Widget. + Recurses. + */ + struct HoverScrollEvent : BaseEvent, PositionBaseEvent { + /** Change of scroll wheel position. */ + math::Vec scrollDelta; + }; + virtual void onHoverScroll(const HoverScrollEvent& e) { recursePositionEvent(&Widget::onHoverScroll, e); } - virtual void onEnter(const event::Enter& e) {} - virtual void onLeave(const event::Leave& e) {} - virtual void onSelect(const event::Select& e) {} - virtual void onDeselect(const event::Deselect& e) {} - virtual void onSelectKey(const event::SelectKey& e) {} - virtual void onSelectText(const event::SelectText& e) {} - virtual void onDragStart(const event::DragStart& e) {} - virtual void onDragEnd(const event::DragEnd& e) {} - virtual void onDragMove(const event::DragMove& e) {} - virtual void onDragHover(const event::DragHover& e) { + + /** Occurs when a Widget begins consuming the Hover event. + Must consume the Hover event to receive this event. + The target sets `hoveredWidget`, which allows Leave to occur. + */ + struct EnterEvent : BaseEvent {}; + virtual void onEnter(const EnterEvent& e) {} + + /** Occurs when a different Widget is entered. + Must consume the Hover event (when a Widget is entered) to receive this event. + */ + struct LeaveEvent : BaseEvent {}; + virtual void onLeave(const LeaveEvent& e) {} + + /** Occurs when a Widget begins consuming the Button press event for the left mouse button. + Must consume the Button event (on left button press) to receive this event. + The target sets `selectedWidget`, which allows SelectText and SelectKey to occur. + */ + struct SelectEvent : BaseEvent {}; + virtual void onSelect(const SelectEvent& e) {} + + /** Occurs when a different Widget is selected. + Must consume the Button event (on left button press, when the Widget is selected) to receive this event. + */ + struct DeselectEvent : BaseEvent {}; + virtual void onDeselect(const DeselectEvent& e) {} + + /** Occurs when a key is pressed, released, or repeated while a Widget is selected. + Must consume to prevent HoverKey from being triggered. + */ + struct SelectKeyEvent : BaseEvent, KeyBaseEvent {}; + virtual void onSelectKey(const SelectKeyEvent& e) {} + + /** Occurs when text is typed while a Widget is selected. + Must consume to prevent HoverKey from being triggered. + */ + struct SelectTextEvent : BaseEvent, TextBaseEvent {}; + virtual void onSelectText(const SelectTextEvent& e) {} + + struct DragBaseEvent : BaseEvent { + /** The mouse button held while dragging. */ + int button; + }; + /** Occurs when a Widget begins being dragged. + Must consume the Button event (on press) to receive this event. + The target sets `draggedWidget`, which allows DragEnd, DragMove, DragHover, DragEnter, and DragDrop to occur. + */ + struct DragStartEvent : DragBaseEvent {}; + virtual void onDragStart(const DragStartEvent& e) {} + + /** Occurs when a Widget stops being dragged by releasing the mouse button. + Must consume the Button event (on press, when the Widget drag begins) to receive this event. + */ + struct DragEndEvent : DragBaseEvent {}; + virtual void onDragEnd(const DragEndEvent& e) {} + + /** Occurs every frame on the dragged Widget. + Must consume the Button event (on press, when the Widget drag begins) to receive this event. + */ + struct DragMoveEvent : DragBaseEvent { + /** Change in mouse position since the last frame. Can be zero. */ + math::Vec mouseDelta; + }; + virtual void onDragMove(const DragMoveEvent& e) {} + + /** Occurs every frame when the mouse is hovering over a Widget while another Widget (possibly the same one) is being dragged. + Recurses. + Consume this event to allow DragEnter and DragLeave to occur. + */ + struct DragHoverEvent : DragBaseEvent, PositionBaseEvent { + /** The dragged widget */ + Widget* origin = NULL; + /** Change in mouse position since the last frame. Can be zero. */ + math::Vec mouseDelta; + }; + virtual void onDragHover(const DragHoverEvent& e) { recursePositionEvent(&Widget::onDragHover, e); } - virtual void onDragEnter(const event::DragEnter& e) {} - virtual void onDragLeave(const event::DragLeave& e) {} - virtual void onDragDrop(const event::DragDrop& e) {} - virtual void onPathDrop(const event::PathDrop& e) { + + /** Occurs when the mouse enters a Widget while dragging. + Must consume the DragHover event to receive this event. + The target sets `draggedWidget`, which allows DragLeave to occur. + */ + struct DragEnterEvent : DragBaseEvent { + /** The dragged widget */ + Widget* origin = NULL; + }; + virtual void onDragEnter(const DragEnterEvent& e) {} + + /** Occurs when the mouse leaves a Widget while dragging. + Must consume the DragHover event (when the Widget is entered) to receive this event. + */ + struct DragLeaveEvent : DragBaseEvent { + /** The dragged widget */ + Widget* origin = NULL; + }; + virtual void onDragLeave(const DragLeaveEvent& e) {} + + /** Occurs when the mouse button is released over a Widget while dragging. + Must consume the Button event (on release) to receive this event. + */ + struct DragDropEvent : DragBaseEvent { + /** The dragged widget */ + Widget* origin = NULL; + }; + virtual void onDragDrop(const DragDropEvent& e) {} + + /** Occurs when a selection of files from the operating system is dropped onto a Widget. + Recurses. + */ + struct PathDropEvent : BaseEvent, PositionBaseEvent { + PathDropEvent(const std::vector& paths) : paths(paths) {} + + /** List of file paths in the dropped selection */ + const std::vector& paths; + }; + virtual void onPathDrop(const PathDropEvent& e) { recursePositionEvent(&Widget::onPathDrop, e); } - virtual void onAction(const event::Action& e) {} - virtual void onChange(const event::Change& e) {} - virtual void onDirty(const event::Dirty& e) { + + /** Occurs after a certain action is triggered on a Widget. + The concept of an "action" is defined by the type of Widget. + */ + struct ActionEvent : BaseEvent {}; + virtual void onAction(const ActionEvent& e) {} + + /** Occurs after the value of a Widget changes. + The concept of a "value" is defined by the type of Widget. + */ + struct ChangeEvent : BaseEvent {}; + virtual void onChange(const ChangeEvent& e) {} + + /** Occurs when the pixel buffer of this module must be refreshed. + Recurses. + */ + struct DirtyEvent : BaseEvent {}; + virtual void onDirty(const DirtyEvent& e) { recurseEvent(&Widget::onDirty, e); } - virtual void onReposition(const event::Reposition& e) {} - virtual void onResize(const event::Resize& e) {} - virtual void onAdd(const event::Add& e) {} - virtual void onRemove(const event::Remove& e) {} - virtual void onShow(const event::Show& e) { + + /** Occurs after a Widget's position is set by Widget::setPosition(). + */ + struct RepositionEvent : BaseEvent {}; + virtual void onReposition(const RepositionEvent& e) {} + + /** Occurs after a Widget's size is set by Widget::setSize(). + */ + struct ResizeEvent : BaseEvent {}; + virtual void onResize(const ResizeEvent& e) {} + + /** Occurs after a Widget is added to a parent. + */ + struct AddEvent : BaseEvent {}; + virtual void onAdd(const AddEvent& e) {} + + /** Occurs before a Widget is removed from its parent. + */ + struct RemoveEvent : BaseEvent {}; + virtual void onRemove(const RemoveEvent& e) {} + + /** Occurs after a Widget is shown with Widget::show(). + Recurses. + */ + struct ShowEvent : BaseEvent {}; + virtual void onShow(const ShowEvent& e) { recurseEvent(&Widget::onShow, e); } - virtual void onHide(const event::Hide& e) { + + /** Occurs after a Widget is hidden with Widget::hide(). + Recurses. + */ + struct HideEvent : BaseEvent {}; + virtual void onHide(const HideEvent& e) { recurseEvent(&Widget::onHide, e); } }; } // namespace widget + +/** Deprecated Rack v1 event namespace. +Use `ExampleWidget` instead of `event::Example` in new code. +*/ +namespace event { +using Base = widget::BaseEvent; +using PositionBase = widget::Widget::PositionBaseEvent; +using KeyBase = widget::Widget::KeyBaseEvent; +using TextBase = widget::Widget::TextBaseEvent; +using Hover = widget::Widget::HoverEvent; +using Button = widget::Widget::ButtonEvent; +using DoubleClick = widget::Widget::DoubleClickEvent; +using HoverKey = widget::Widget::HoverKeyEvent; +using HoverText = widget::Widget::HoverTextEvent; +using HoverScroll = widget::Widget::HoverScrollEvent; +using Enter = widget::Widget::EnterEvent; +using Leave = widget::Widget::LeaveEvent; +using Select = widget::Widget::SelectEvent; +using Deselect = widget::Widget::DeselectEvent; +using SelectKey = widget::Widget::SelectKeyEvent; +using SelectText = widget::Widget::SelectTextEvent; +using DragBase = widget::Widget::DragBaseEvent; +using DragStart = widget::Widget::DragStartEvent; +using DragEnd = widget::Widget::DragEndEvent; +using DragMove = widget::Widget::DragMoveEvent; +using DragHover = widget::Widget::DragHoverEvent; +using DragEnter = widget::Widget::DragEnterEvent; +using DragLeave = widget::Widget::DragLeaveEvent; +using DragDrop = widget::Widget::DragDropEvent; +using PathDrop = widget::Widget::PathDropEvent; +using Action = widget::Widget::ActionEvent; +using Change = widget::Widget::ChangeEvent; +using Dirty = widget::Widget::DirtyEvent; +using Reposition = widget::Widget::RepositionEvent; +using Resize = widget::Widget::ResizeEvent; +using Add = widget::Widget::AddEvent; +using Remove = widget::Widget::RemoveEvent; +using Show = widget::Widget::ShowEvent; +using Hide = widget::Widget::HideEvent; +} + } // namespace rack diff --git a/include/widget/event.hpp b/include/widget/event.hpp new file mode 100644 index 00000000..0dbfbfa3 --- /dev/null +++ b/include/widget/event.hpp @@ -0,0 +1,155 @@ +#pragma once +#include +#include + +#include +#include + + + +/** Remaps Ctrl to Cmd on Mac +Use this instead of GLFW_MOD_CONTROL, since Cmd should be used on Mac in place of Ctrl on Linux/Windows. +*/ +#if defined ARCH_MAC + #define RACK_MOD_CTRL GLFW_MOD_SUPER + #define RACK_MOD_CTRL_NAME "⌘" +#else + #define RACK_MOD_CTRL GLFW_MOD_CONTROL + #define RACK_MOD_CTRL_NAME "Ctrl" +#endif +#define RACK_MOD_SHIFT_NAME "Shift" +#define RACK_MOD_ALT_NAME "Alt" + +/** Filters actual mod keys from the mod flags. +Use this if you don't care about GLFW_MOD_CAPS_LOCK and GLFW_MOD_NUM_LOCK. +Example usage: + if ((e.mod & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) ... +*/ +#define RACK_MOD_MASK (GLFW_MOD_SHIFT | GLFW_MOD_CONTROL | GLFW_MOD_ALT | GLFW_MOD_SUPER) + +/** A key action state representing the the key is (still) being held. +*/ +#define RACK_HELD 3 + + +namespace rack { +namespace widget { + + +struct Widget; + + +/** A per-event state shared and writable by all widgets that recursively handle an event. */ +struct EventContext { + /** Whether the event should continue recursing to children Widgets. */ + bool propagating = true; + /** Whether the event has been consumed by an event handler and no more handlers should consume the event. */ + bool consumed = false; + /** The widget that responded to the event. */ + Widget* target = NULL; +}; + + +/** Base class for all events. */ +struct BaseEvent { + EventContext* context = NULL; + + /** Prevents the event from being handled by more Widgets. + */ + void stopPropagating() const { + if (!context) + return; + context->propagating = false; + } + bool isPropagating() const { + if (!context) + return true; + return context->propagating; + } + /** Tells the event handler that a particular Widget consumed the event. + You usually want to stop propagation as well, so call consume() instead. + */ + void setTarget(Widget* w) const { + if (!context) + return; + context->target = w; + } + Widget* getTarget() const { + if (!context) + return NULL; + return context->target; + } + /** Sets the target Widget and stops propagating. + A NULL Widget may be passed to consume but not set a target. + */ + void consume(Widget* w) const { + if (!context) + return; + context->propagating = false; + context->consumed = true; + context->target = w; + } + void unconsume() const { + if (!context) + return; + context->consumed = false; + } + bool isConsumed() const { + if (!context) + return false; + return context->consumed; + } +}; + + +struct EventState { + Widget* rootWidget = NULL; + /** State widgets + Don't set these directly unless you know what you're doing. Use the set*() methods instead. + */ + Widget* hoveredWidget = NULL; + Widget* draggedWidget = NULL; + int dragButton = 0; + Widget* dragHoveredWidget = NULL; + Widget* selectedWidget = NULL; + /** For double-clicking */ + double lastClickTime = -INFINITY; + Widget* lastClickedWidget = NULL; + std::set heldKeys; + + Widget* getRootWidget() { + return rootWidget; + } + Widget* getHoveredWidget() { + return hoveredWidget; + } + Widget* getDraggedWidget() { + return draggedWidget; + } + Widget* getDragHoveredWidget() { + return dragHoveredWidget; + } + Widget* getSelectedWidget() { + return selectedWidget; + } + + void setHovered(Widget* w); + void setDragged(Widget* w, int button); + void setDragHovered(Widget* w); + void setSelected(Widget* w); + /** Prepares a widget for deletion */ + void finalizeWidget(Widget* w); + + bool handleButton(math::Vec pos, int button, int action, int mods); + bool handleHover(math::Vec pos, math::Vec mouseDelta); + bool handleLeave(); + bool handleScroll(math::Vec pos, math::Vec scrollDelta); + bool handleText(math::Vec pos, int codepoint); + bool handleKey(math::Vec pos, int key, int scancode, int action, int mods); + bool handleDrop(math::Vec pos, const std::vector& paths); + bool handleDirty(); +}; + + +} // namespace widget +} // namespace rack diff --git a/src/app/CableWidget.cpp b/src/app/CableWidget.cpp index 7eaba94e..d4129bb5 100644 --- a/src/app/CableWidget.cpp +++ b/src/app/CableWidget.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include diff --git a/src/ui/Button.cpp b/src/ui/Button.cpp index 75590797..8450aca8 100644 --- a/src/ui/Button.cpp +++ b/src/ui/Button.cpp @@ -1,6 +1,5 @@ #include #include -#include namespace rack { diff --git a/src/ui/ChoiceButton.cpp b/src/ui/ChoiceButton.cpp index 3ab4dc3b..b32a66b4 100644 --- a/src/ui/ChoiceButton.cpp +++ b/src/ui/ChoiceButton.cpp @@ -1,6 +1,5 @@ #include #include -#include namespace rack { diff --git a/src/ui/MenuItem.cpp b/src/ui/MenuItem.cpp index 66ebd28e..688565d5 100644 --- a/src/ui/MenuItem.cpp +++ b/src/ui/MenuItem.cpp @@ -70,7 +70,7 @@ void MenuItem::doAction() { if (disabled) return; - event::Context cAction; + widget::EventContext cAction; event::Action eAction; eAction.context = &cAction; // Consume event by default, but allow action to un-consume it to prevent the menu from being removed. diff --git a/src/widget/ZoomWidget.cpp b/src/widget/ZoomWidget.cpp index d608d278..2491c7e0 100644 --- a/src/widget/ZoomWidget.cpp +++ b/src/widget/ZoomWidget.cpp @@ -33,7 +33,7 @@ void ZoomWidget::setZoom(float zoom) { this->zoom = zoom; // Trigger Dirty event - event::Context cDirty; + widget::EventContext cDirty; event::Dirty eDirty; eDirty.context = &cDirty; Widget::onDirty(eDirty); diff --git a/src/event.cpp b/src/widget/event.cpp similarity index 68% rename from src/event.cpp rename to src/widget/event.cpp index 4fb4d87d..9a2712d4 100644 --- a/src/event.cpp +++ b/src/widget/event.cpp @@ -1,42 +1,42 @@ -#include +#include #include #include #include namespace rack { -namespace event { +namespace widget { -void State::setHovered(widget::Widget* w) { +void EventState::setHovered(widget::Widget* w) { if (w == hoveredWidget) return; if (hoveredWidget) { - // Trigger Leave event - Leave eLeave; + // Trigger LeaveEvent + Widget::LeaveEvent eLeave; hoveredWidget->onLeave(eLeave); hoveredWidget = NULL; } if (w) { - // Trigger Enter event - Context cEnter; + // Trigger EnterEvent + EventContext cEnter; cEnter.target = w; - Enter eEnter; + Widget::EnterEvent eEnter; eEnter.context = &cEnter; w->onEnter(eEnter); hoveredWidget = cEnter.target; } } -void State::setDragged(widget::Widget* w, int button) { +void EventState::setDragged(widget::Widget* w, int button) { if (w == draggedWidget) return; if (draggedWidget) { - // Trigger DragEnd event - DragEnd eDragEnd; + // Trigger DragEndEvent + Widget::DragEndEvent eDragEnd; eDragEnd.button = dragButton; draggedWidget->onDragEnd(eDragEnd); draggedWidget = NULL; @@ -45,10 +45,10 @@ void State::setDragged(widget::Widget* w, int button) { dragButton = button; if (w) { - // Trigger DragStart event - Context cDragStart; + // Trigger DragStartEvent + EventContext cDragStart; cDragStart.target = w; - DragStart eDragStart; + Widget::DragStartEvent eDragStart; eDragStart.context = &cDragStart; eDragStart.button = dragButton; w->onDragStart(eDragStart); @@ -56,13 +56,13 @@ void State::setDragged(widget::Widget* w, int button) { } } -void State::setDragHovered(widget::Widget* w) { +void EventState::setDragHovered(widget::Widget* w) { if (w == dragHoveredWidget) return; if (dragHoveredWidget) { - // Trigger DragLeave event - DragLeave eDragLeave; + // Trigger DragLeaveEvent + Widget::DragLeaveEvent eDragLeave; eDragLeave.button = dragButton; eDragLeave.origin = draggedWidget; dragHoveredWidget->onDragLeave(eDragLeave); @@ -70,10 +70,10 @@ void State::setDragHovered(widget::Widget* w) { } if (w) { - // Trigger DragEnter event - Context cDragEnter; + // Trigger DragEnterEvent + EventContext cDragEnter; cDragEnter.target = w; - DragEnter eDragEnter; + Widget::DragEnterEvent eDragEnter; eDragEnter.context = &cDragEnter; eDragEnter.button = dragButton; eDragEnter.origin = draggedWidget; @@ -82,29 +82,29 @@ void State::setDragHovered(widget::Widget* w) { } } -void State::setSelected(widget::Widget* w) { +void EventState::setSelected(widget::Widget* w) { if (w == selectedWidget) return; if (selectedWidget) { - // Trigger Deselect event - Deselect eDeselect; + // Trigger DeselectEvent + Widget::DeselectEvent eDeselect; selectedWidget->onDeselect(eDeselect); selectedWidget = NULL; } if (w) { - // Trigger Select event - Context cSelect; + // Trigger SelectEvent + EventContext cSelect; cSelect.target = w; - Select eSelect; + Widget::SelectEvent eSelect; eSelect.context = &cSelect; w->onSelect(eSelect); selectedWidget = cSelect.target; } } -void State::finalizeWidget(widget::Widget* w) { +void EventState::finalizeWidget(widget::Widget* w) { if (hoveredWidget == w) setHovered(NULL); if (draggedWidget == w) @@ -117,14 +117,14 @@ void State::finalizeWidget(widget::Widget* w) { lastClickedWidget = NULL; } -bool State::handleButton(math::Vec pos, int button, int action, int mods) { +bool EventState::handleButton(math::Vec pos, int button, int action, int mods) { bool cursorLocked = APP->window->isCursorLocked(); widget::Widget* clickedWidget = NULL; if (!cursorLocked) { - // Trigger Button event - Context cButton; - Button eButton; + // Trigger ButtonEvent + EventContext cButton; + Widget::ButtonEvent eButton; eButton.context = &cButton; eButton.pos = pos; eButton.button = button; @@ -142,8 +142,8 @@ bool State::handleButton(math::Vec pos, int button, int action, int mods) { setDragHovered(NULL); if (clickedWidget && draggedWidget) { - // Trigger DragDrop event - DragDrop eDragDrop; + // Trigger DragDropEvent + Widget::DragDropEvent eDragDrop; eDragDrop.button = dragButton; eDragDrop.origin = draggedWidget; clickedWidget->onDragDrop(eDragDrop); @@ -163,8 +163,8 @@ bool State::handleButton(math::Vec pos, int button, int action, int mods) { if (clickedWidget && clickTime - lastClickTime <= doubleClickDuration && lastClickedWidget == clickedWidget) { - // Trigger DoubleClick event - DoubleClick eDoubleClick; + // Trigger DoubleClickEvent + Widget::DoubleClickEvent eDoubleClick; clickedWidget->onDoubleClick(eDoubleClick); // Reset double click lastClickTime = -INFINITY; @@ -180,7 +180,7 @@ bool State::handleButton(math::Vec pos, int button, int action, int mods) { return !!clickedWidget; } -bool State::handleHover(math::Vec pos, math::Vec mouseDelta) { +bool EventState::handleHover(math::Vec pos, math::Vec mouseDelta) { bool cursorLocked = APP->window->isCursorLocked(); // Fake a key RACK_HELD event for each held key @@ -195,9 +195,9 @@ bool State::handleHover(math::Vec pos, math::Vec mouseDelta) { if (draggedWidget) { bool dragHovered = false; if (!cursorLocked) { - // Trigger DragHover event - Context cDragHover; - DragHover eDragHover; + // Trigger DragHoverEvent + EventContext cDragHover; + Widget::DragHoverEvent eDragHover; eDragHover.context = &cDragHover; eDragHover.button = dragButton; eDragHover.pos = pos; @@ -206,13 +206,13 @@ bool State::handleHover(math::Vec pos, math::Vec mouseDelta) { rootWidget->onDragHover(eDragHover); setDragHovered(cDragHover.target); - // If consumed, don't continue after DragMove so Hover is not triggered. + // If consumed, don't continue after DragMoveEvent so HoverEvent is not triggered. if (cDragHover.target) dragHovered = true; } - // Trigger DragMove event - DragMove eDragMove; + // Trigger DragMoveEvent + Widget::DragMoveEvent eDragMove; eDragMove.button = dragButton; eDragMove.mouseDelta = mouseDelta; draggedWidget->onDragMove(eDragMove); @@ -221,9 +221,9 @@ bool State::handleHover(math::Vec pos, math::Vec mouseDelta) { } if (!cursorLocked) { - // Trigger Hover event - Context cHover; - Hover eHover; + // Trigger HoverEvent + EventContext cHover; + Widget::HoverEvent eHover; eHover.context = &cHover; eHover.pos = pos; eHover.mouseDelta = mouseDelta; @@ -236,7 +236,7 @@ bool State::handleHover(math::Vec pos, math::Vec mouseDelta) { return false; } -bool State::handleLeave() { +bool EventState::handleLeave() { heldKeys.clear(); // When leaving the window, don't un-hover widgets because the mouse might be dragging. // setDragHovered(NULL); @@ -244,10 +244,10 @@ bool State::handleLeave() { return true; } -bool State::handleScroll(math::Vec pos, math::Vec scrollDelta) { - // Trigger HoverScroll event - Context cHoverScroll; - HoverScroll eHoverScroll; +bool EventState::handleScroll(math::Vec pos, math::Vec scrollDelta) { + // Trigger HoverScrollEvent + EventContext cHoverScroll; + Widget::HoverScrollEvent eHoverScroll; eHoverScroll.context = &cHoverScroll; eHoverScroll.pos = pos; eHoverScroll.scrollDelta = scrollDelta; @@ -256,10 +256,10 @@ bool State::handleScroll(math::Vec pos, math::Vec scrollDelta) { return !!cHoverScroll.target; } -bool State::handleDrop(math::Vec pos, const std::vector& paths) { - // Trigger PathDrop event - Context cPathDrop; - PathDrop ePathDrop(paths); +bool EventState::handleDrop(math::Vec pos, const std::vector& paths) { + // Trigger PathDropEvent + EventContext cPathDrop; + Widget::PathDropEvent ePathDrop(paths); ePathDrop.context = &cPathDrop; ePathDrop.pos = pos; rootWidget->onPathDrop(ePathDrop); @@ -267,11 +267,11 @@ bool State::handleDrop(math::Vec pos, const std::vector& paths) { return !!cPathDrop.target; } -bool State::handleText(math::Vec pos, int codepoint) { +bool EventState::handleText(math::Vec pos, int codepoint) { if (selectedWidget) { - // Trigger SelectText event - Context cSelectText; - SelectText eSelectText; + // Trigger SelectTextEvent + EventContext cSelectText; + Widget::SelectTextEvent eSelectText; eSelectText.context = &cSelectText; eSelectText.codepoint = codepoint; selectedWidget->onSelectText(eSelectText); @@ -279,9 +279,9 @@ bool State::handleText(math::Vec pos, int codepoint) { return true; } - // Trigger HoverText event - Context cHoverText; - HoverText eHoverText; + // Trigger HoverText + EventContext cHoverText; + Widget::HoverTextEvent eHoverText; eHoverText.context = &cHoverText; eHoverText.pos = pos; eHoverText.codepoint = codepoint; @@ -290,7 +290,7 @@ bool State::handleText(math::Vec pos, int codepoint) { return !!cHoverText.target; } -bool State::handleKey(math::Vec pos, int key, int scancode, int action, int mods) { +bool EventState::handleKey(math::Vec pos, int key, int scancode, int action, int mods) { // Update heldKey state if (action == GLFW_PRESS) { heldKeys.insert(key); @@ -302,9 +302,9 @@ bool State::handleKey(math::Vec pos, int key, int scancode, int action, int mods } if (selectedWidget) { - // Trigger SelectKey event - Context cSelectKey; - SelectKey eSelectKey; + // Trigger SelectKeyEvent + EventContext cSelectKey; + Widget::SelectKeyEvent eSelectKey; eSelectKey.context = &cSelectKey; eSelectKey.key = key; eSelectKey.scancode = scancode; @@ -318,9 +318,9 @@ bool State::handleKey(math::Vec pos, int key, int scancode, int action, int mods return true; } - // Trigger HoverKey event - Context cHoverKey; - HoverKey eHoverKey; + // Trigger HoverKeyEvent + EventContext cHoverKey; + Widget::HoverKeyEvent eHoverKey; eHoverKey.context = &cHoverKey; eHoverKey.pos = pos; eHoverKey.key = key; @@ -334,15 +334,15 @@ bool State::handleKey(math::Vec pos, int key, int scancode, int action, int mods return !!cHoverKey.target; } -bool State::handleDirty() { - // Trigger Dirty event - Context cDirty; - Dirty eDirty; +bool EventState::handleDirty() { + // Trigger DirtyEvent + EventContext cDirty; + Widget::DirtyEvent eDirty; eDirty.context = &cDirty; rootWidget->onDirty(eDirty); return true; } -} // namespace event +} // namespace widget } // namespace rack diff --git a/src/window.cpp b/src/window.cpp index 3dca24d7..cdb8510d 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -12,10 +12,10 @@ #include #include +#include #include #include #include -#include #include #include #include diff --git a/standalone/main.cpp b/standalone/main.cpp index 7e5f2a90..cf282184 100644 --- a/standalone/main.cpp +++ b/standalone/main.cpp @@ -183,7 +183,7 @@ int main(int argc, char* argv[]) { contextSet(new Context); APP->engine = new engine::Engine; if (!settings::headless) { - APP->event = new event::State; + APP->event = new widget::EventState; APP->history = new history::State; APP->window = new Window; APP->scene = new app::Scene;