#pragma once #include #include #include "nanovg.h" #include "nanosvg.h" #include "util/common.hpp" #include "events.hpp" #include "util/color.hpp" namespace rack { //////////////////// // resources //////////////////// // Constructing these directly will load from the disk each time. Use the load() functions to load from disk and cache them as long as the shared_ptr is held. // Implemented in window.cpp struct Font { int handle; Font(const std::string &filename); ~Font(); static std::shared_ptr load(const std::string &filename); }; struct Image { int handle; Image(const std::string &filename); ~Image(); static std::shared_ptr load(const std::string &filename); }; struct SVG { NSVGimage *handle; SVG(const std::string &filename); ~SVG(); static std::shared_ptr load(const std::string &filename); }; //////////////////// // Base widget //////////////////// /** A node in the 2D scene graph Never inherit from Widget directly. Instead, inherit from VirtualWidget declared below. */ struct Widget { /** Stores position and size */ Rect box = Rect(Vec(), Vec(INFINITY, INFINITY)); Widget *parent = NULL; std::list children; bool visible = true; virtual ~Widget(); virtual Rect getChildrenBoundingBox(); /** Returns `v` transformed into the coordinate system of `relative` */ virtual Vec getRelativeOffset(Vec v, Widget *relative); /** Returns `v` transformed into world coordinates */ Vec getAbsoluteOffset(Vec v) { return getRelativeOffset(v, NULL); } /** Returns a subset of the given Rect bounded by the box of this widget and all ancestors */ virtual Rect getViewport(Rect r); template T *getAncestorOfType() { if (!parent) return NULL; T *p = dynamic_cast(parent); if (p) return p; return parent->getAncestorOfType(); } template T *getFirstDescendantOfType() { for (Widget *child : children) { T *c = dynamic_cast(child); if (c) return c; c = child->getFirstDescendantOfType(); if (c) return c; } return NULL; } /** Adds widget to list of children. Gives ownership of widget to this widget instance. */ void addChild(Widget *widget); /** Removes widget from list of children if it exists. Does not delete widget but transfers ownership to caller */ void removeChild(Widget *widget); /** Removes and deletes all children */ void clearChildren(); /** Recursively finalizes event start/end pairs as needed */ void finalizeEvents(); /** Advances the module by one frame */ virtual void step(); /** Draws to NanoVG context */ virtual void draw(NVGcontext *vg); // Events /** Called when a mouse button is pressed over this widget */ virtual void onMouseDown(EventMouseDown &e); /** Called when a mouse button is released over this widget */ virtual void onMouseUp(EventMouseUp &e); /** Called when the mouse moves over this widget. Called on every frame, even if `mouseRel = Vec(0, 0)`. */ virtual void onMouseMove(EventMouseMove &e); /** Called when a key is pressed while hovering over this widget */ virtual void onHoverKey(EventHoverKey &e); /** Called when this widget begins responding to `onMouseMove` events */ virtual void onMouseEnter(EventMouseEnter &e) {} /** Called when this widget no longer responds to `onMouseMove` events */ virtual void onMouseLeave(EventMouseLeave &e) {} /** Called when this widget gains focus by responding to the `onMouseDown` event */ virtual void onFocus(EventFocus &e) {} virtual void onDefocus(EventDefocus &e) {} /** Called when a printable character is received while this widget is focused */ virtual void onText(EventText &e) {} /** Called when a key is pressed while this widget is focused */ virtual void onKey(EventKey &e) {} /** Called when the scroll wheel is moved while the mouse is hovering over this widget */ virtual void onScroll(EventScroll &e); /** Called when a widget responds to `onMouseDown` for a left button press */ virtual void onDragStart(EventDragStart &e) {} /** Called when the left button is released and this widget is being dragged */ virtual void onDragEnd(EventDragEnd &e) {} /** Called when a widget responds to `onMouseMove` and is being dragged */ virtual void onDragMove(EventDragMove &e) {} /** Called when a widget responds to `onMouseUp` for a left button release and a widget is being dragged */ virtual void onDragEnter(EventDragEnter &e) {} virtual void onDragLeave(EventDragEnter &e) {} /** Called when a drag action ends while hovering this widget */ virtual void onDragDrop(EventDragDrop &e) {} /** Called when an OS selection of files is dragged-and-dropped on this widget */ virtual void onPathDrop(EventPathDrop &e); /** Called when an event triggers an action */ virtual void onAction(EventAction &e) {} /** For widgets with some concept of values, called when the value is changed */ virtual void onChange(EventChange &e) {} /** Called when the zoom level is changed of this widget */ virtual void onZoom(EventZoom &e); /** Helper function for creating and initializing a Widget with certain arguments (in this case just the position). In this project, you will find this idiom everywhere, as an easier alternative to constructor arguments, for building a Widget (or a subclass) with a one-liner. Example: addChild(Widget::create(Vec(10, 10))) */ template static T *create(Vec pos = Vec()) { T *o = new T(); o->box.pos = pos; return o; } }; /** Instead of inheriting from Widget directly, inherit from VirtualWidget to guarantee that only one copy of Widget's member variables are used by each instance of the Widget hierarchy. */ struct VirtualWidget : virtual Widget {}; struct TransformWidget : VirtualWidget { /** The transformation matrix */ float transform[6]; TransformWidget(); Rect getChildrenBoundingBox() override; void identity(); void translate(Vec delta); void rotate(float angle); void scale(Vec s); void draw(NVGcontext *vg) override; }; struct ZoomWidget : VirtualWidget { float zoom = 1.0; Vec getRelativeOffset(Vec v, Widget *relative) override; Rect getViewport(Rect r) override; void setZoom(float zoom); void draw(NVGcontext *vg) override; void onMouseDown(EventMouseDown &e) override; void onMouseUp(EventMouseUp &e) override; void onMouseMove(EventMouseMove &e) override; void onHoverKey(EventHoverKey &e) override; void onScroll(EventScroll &e) override; void onPathDrop(EventPathDrop &e) override; }; //////////////////// // Trait widgets //////////////////// /** Widget that does not respond to events */ struct TransparentWidget : VirtualWidget { void onMouseDown(EventMouseDown &e) override {} void onMouseUp(EventMouseUp &e) override {} void onMouseMove(EventMouseMove &e) override {} void onScroll(EventScroll &e) override {} }; /** Widget that automatically responds to all mouse events but gives a chance for children to respond instead */ struct OpaqueWidget : VirtualWidget { void onMouseDown(EventMouseDown &e) override { Widget::onMouseDown(e); if (!e.target) e.target = this; e.consumed = true; } void onMouseUp(EventMouseUp &e) override { Widget::onMouseUp(e); if (!e.target) e.target = this; e.consumed = true; } void onMouseMove(EventMouseMove &e) override { Widget::onMouseMove(e); if (!e.target) e.target = this; e.consumed = true; } void onScroll(EventScroll &e) override { Widget::onScroll(e); e.consumed = true; } }; struct SpriteWidget : VirtualWidget { Vec spriteOffset; Vec spriteSize; std::shared_ptr spriteImage; int index = 0; void draw(NVGcontext *vg) override; }; struct SVGWidget : VirtualWidget { std::shared_ptr svg; /** Sets the box size to the svg image size */ void wrap(); /** Sets and wraps the SVG */ void setSVG(std::shared_ptr svg); void draw(NVGcontext *vg) override; }; /** Caches a widget's draw() result to a framebuffer so it is called less frequently When `dirty` is true, its children will be re-rendered on the next call to step() override. Events are not passed to the underlying scene. */ struct FramebufferWidget : VirtualWidget { /** Set this to true to re-render the children to the framebuffer the next time it is drawn */ bool dirty = true; /** A margin in pixels around the children in the framebuffer This prevents cutting the rendered SVG off on the box edges. */ float oversample; /** The root object in the framebuffer scene The FramebufferWidget owns the pointer */ struct Internal; Internal *internal; FramebufferWidget(); ~FramebufferWidget(); void draw(NVGcontext *vg) override; int getImageHandle(); void onZoom(EventZoom &e) override; }; /** A Widget representing a float value */ struct QuantityWidget : VirtualWidget { float value = 0.0; float minValue = 0.0; float maxValue = 1.0; float defaultValue = 0.0; std::string label; /** Include a space character if you want a space after the number, e.g. " Hz" */ std::string unit; /** The decimal place to round for displaying values. A precision of 2 will display as "1.00" for example. */ int precision = 2; QuantityWidget(); void setValue(float value); void setLimits(float minValue, float maxValue); void setDefaultValue(float defaultValue); /** Generates the display value */ std::string getText(); }; //////////////////// // globals //////////////////// extern Widget *gHoveredWidget; extern Widget *gDraggedWidget; extern Widget *gDragHoveredWidget; extern Widget *gFocusedWidget; extern Widget *gTempWidget; } // namespace rack