|
- #pragma once
- #include <list>
-
- #include <common.hpp>
- #include <math.hpp>
- #include <Window.hpp>
- #include <color.hpp>
- #include <widget/event.hpp>
- #include <weakptr.hpp>
-
-
- namespace rack {
-
-
- /** General UI widgets
- */
- namespace widget {
-
-
- /** A node in the 2D [scene graph](https://en.wikipedia.org/wiki/Scene_graph).
- The bounding box of a Widget is a rectangle specified by `box` relative to their parent.
- The appearance is defined by overriding `draw()`, and the behavior is defined by overriding `step()` and `on*()` event handlers.
- */
- struct Widget : WeakBase {
- /** Stores position and size */
- math::Rect box = math::Rect(math::Vec(), math::Vec(INFINITY, INFINITY));
- /** Automatically set when Widget is added as a child to another Widget */
- Widget* parent = NULL;
- std::list<Widget*> children;
- /** Disables rendering but allow stepping */
- bool visible = true;
- /** If set to true, parent will delete Widget in the next step() */
- bool requestedDelete = false;
-
- virtual ~Widget();
-
- math::Rect getBox();
- void setBox(math::Rect box);
- math::Vec getPosition();
- void setPosition(math::Vec pos);
- math::Vec getSize();
- void setSize(math::Vec size);
- widget::Widget* getParent();
- bool isVisible();
- void setVisible(bool visible);
- void show() {
- setVisible(true);
- }
- void hide() {
- setVisible(false);
- }
-
- void requestDelete();
-
- /** Returns the smallest rectangle containing this widget's children (visible and invisible) in its local coordinates.
- Returns `Rect(Vec(inf, inf), Vec(-inf, -inf))` if there are no children.
- */
- virtual math::Rect getChildrenBoundingBox();
- virtual math::Rect getVisibleChildrenBoundingBox();
- /** Returns `v` (given in local coordinates) transformed into the coordinate system of `relative`.
- */
- virtual math::Vec getRelativeOffset(math::Vec v, Widget* relative);
- /** Returns `v` transformed into world/root/global/absolute coordinates.
- */
- math::Vec getAbsoluteOffset(math::Vec v) {
- return getRelativeOffset(v, NULL);
- }
- /** Returns the zoom level in the coordinate system of `relative`.
- Only `ZoomWidget` should override this to return value other than 1.
- */
- virtual float getRelativeZoom(Widget* relative);
- float getAbsoluteZoom() {
- return getRelativeZoom(NULL);
- }
- /** Returns a subset of the given Rect bounded by the box of this widget and all ancestors.
- */
- virtual math::Rect getViewport(math::Rect r);
-
- template <class T>
- T* getAncestorOfType() {
- if (!parent)
- return NULL;
- T* p = dynamic_cast<T*>(parent);
- if (p)
- return p;
- return parent->getAncestorOfType<T>();
- }
-
- template <class T>
- T* getFirstDescendantOfType() {
- for (Widget* child : children) {
- T* c = dynamic_cast<T*>(child);
- if (c)
- return c;
- c = child->getFirstDescendantOfType<T>();
- if (c)
- return c;
- }
- return NULL;
- }
-
- /** Checks if the given widget is a child of `this` widget.
- */
- bool hasChild(Widget* child);
- /** Adds widget to the top of the children.
- Gives ownership of widget to this widget instance.
- */
- void addChild(Widget* child);
- /** Adds widget to the bottom of the children.
- */
- void addChildBottom(Widget* child);
- /** Adds widget directly below another widget.
- The sibling widget must already be a child of `this` widget.
- */
- void addChildBelow(Widget* child, Widget* sibling);
- void addChildAbove(Widget* child, Widget* sibling);
- /** Removes widget from list of children if it exists.
- Does not delete widget but transfers ownership to caller
- */
- void removeChild(Widget* child);
- /** Removes and deletes all children */
- void clearChildren();
-
- /** Advances the module by one frame */
- virtual void step();
-
- struct DrawArgs {
- NVGcontext* vg = NULL;
- /** Local box representing the visible viewport. */
- math::Rect clipBox;
- NVGLUframebuffer* fb = NULL;
- /** Custom widgets may draw children multiple times, passing a different layer number each time. */
- int layer = 0;
- };
-
- /** Draws the widget to the NanoVG context */
- virtual void draw(const DrawArgs& args);
- /** Override draw(const DrawArgs &args) instead */
- DEPRECATED virtual void draw(NVGcontext* vg) {}
-
- /** Draws a particular child. */
- void drawChild(Widget* child, const DrawArgs& args);
-
- // Events
-
- /** Recurses an event to all visible Widgets */
- template <typename TMethod, class TEvent>
- void recurseEvent(TMethod f, const TEvent& e) {
- for (auto it = children.rbegin(); it != children.rend(); it++) {
- // Stop propagation if requested
- if (!e.isPropagating())
- break;
- Widget* child = *it;
- // Don't filter child by visibility. Typically only position events need to be filtered by visibility.
- // if (!child->visible)
- // continue;
-
- // Clone event for (currently) no reason
- TEvent e2 = e;
- // Call child event handler
- (child->*f)(e2);
- }
- }
-
- /** Recurses an event to all visible Widgets until it is consumed. */
- template <typename TMethod, class TEvent>
- void recursePositionEvent(TMethod f, const TEvent& e) {
- for (auto it = children.rbegin(); it != children.rend(); it++) {
- // Stop propagation if requested
- if (!e.isPropagating())
- break;
- Widget* child = *it;
- // Filter child by visibility and position
- if (!child->visible)
- continue;
- if (!child->box.contains(e.pos))
- continue;
-
- // Clone event and adjust its position
- TEvent e2 = e;
- e2.pos = e.pos.minus(child->box.pos);
- // Call child event handler
- (child->*f)(e2);
- }
- }
-
- 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.
- */
- 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);
- }
-
- /** 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);
- }
-
- /** 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);
- }
-
- /** 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);
- }
-
- /** 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);
- }
-
- /** 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);
- }
-
- /** 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<std::string>& paths) : paths(paths) {}
-
- /** List of file paths in the dropped selection */
- const std::vector<std::string>& paths;
- };
- virtual void onPathDrop(const PathDropEvent& e) {
- recursePositionEvent(&Widget::onPathDrop, 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);
- }
-
- /** 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);
- }
-
- /** Occurs after a Widget is hidden with Widget::hide().
- Recurses.
- */
- struct HideEvent : BaseEvent {};
- virtual void onHide(const HideEvent& e) {
- recurseEvent(&Widget::onHide, e);
- }
-
- /** Occurs after the Window, OpenGL context, and Nanovg are created.
- Recurses.
- */
- struct ContextCreateEvent : BaseEvent {
- NVGcontext* vg;
- };
- virtual void onContextCreate(const ContextCreateEvent& e) {
- recurseEvent(&Widget::onContextCreate, e);
- }
-
- /** Occurs before the Window, OpenGL context, and Nanovg are destroyed.
- Recurses.
- */
- struct ContextDestroyEvent : BaseEvent {
- NVGcontext* vg;
- };
- virtual void onContextDestroy(const ContextDestroyEvent& e) {
- recurseEvent(&Widget::onContextDestroy, e);
- }
- };
-
-
- } // namespace widget
-
- /** Deprecated Rack v1 event namespace.
- Use `FooEvent` (defined in widget::Widget) instead of `event::Foo` 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
|