#pragma once #include "delegates.hpp" #include "graphics/core/commons.hpp" #include "window/input.hpp" #include #include #include #include #include #include class DrawContext; class Assets; namespace gui { class UINode; class GUI; class Container; using onaction = std::function; using onnumberchange = std::function; using onstringchange = std::function; template class CallbacksSet { public: using Func = std::function; private: std::unique_ptr> callbacks; public: void listen(const Func& callback) { if (callbacks == nullptr) { callbacks = std::make_unique>(); } callbacks->push_back(callback); } void notify(Args&&... args) { if (callbacks) { for (auto& callback : *callbacks) { callback(std::forward(args)...); } } } }; using ActionsSet = CallbacksSet; using StringCallbacksSet = CallbacksSet; enum class Align { left, center, right, top=left, bottom=right, }; enum class Gravity { none, top_left, top_center, top_right, center_left, center_center, center_right, bottom_left, bottom_center, bottom_right }; /// @brief Base abstract class for all UI elements class UINode : public std::enable_shared_from_this { protected: GUI& gui; bool mustRefresh = true; private: /// @brief element identifier used for direct access in UiDocument std::string id = ""; /// @brief element enabled state bool enabled = true; protected: /// @brief element position within the parent element glm::vec2 pos {0.0f}; /// @brief element size (width, height) glm::vec2 size; /// @brief minimal element size glm::vec2 minSize {1.0f}; /// @brief maximal element size glm::vec2 maxSize {1e6f}; /// @brief element primary color (background-color or text-color if label) glm::vec4 color {1.0f}; /// @brief element color when mouse is over it glm::vec4 hoverColor {1.0f}; /// @brief element color when clicked glm::vec4 pressedColor {1.0f}; /// @brief element margin (only supported for Panel sub-nodes) glm::vec4 margin {0.0f}; /// @brief is element visible bool visible = true; /// @brief is mouse over the element bool hover = false; /// @brief is mouse has been pressed over the element and not released yet bool pressed = false; /// @brief is element focused bool focused = false; /// @brief is element opaque for cursor interaction bool interactive = true; /// @brief does the element support resizing by parent elements bool resizing = true; /// @brief z-index property specifies the stack order of an element int zindex = 0; /// @brief element content alignment (supported by Label only) Align align = Align::left; /// @brief parent element UINode* parent = nullptr; /// @brief position supplier for the element (called on parent element size update) vec2supplier positionfunc = nullptr; /// @brief size supplier for the element (called on parent element size update) vec2supplier sizefunc = nullptr; /// @brief 'onclick' callbacks ActionsSet actions; /// @brief 'ondoubleclick' callbacks ActionsSet doubleClickCallbacks; /// @brief 'onfocus' callbacks ActionsSet focusCallbacks; /// @brief 'ondefocus' callbacks ActionsSet defocusCallbacks; /// @brief element tooltip text std::wstring tooltip; /// @brief element tooltip delay float tooltipDelay = 0.5f; /// @brief cursor shape when mouse is over the element CursorShape cursor = CursorShape::ARROW; UINode(GUI& gui, glm::vec2 size); public: virtual ~UINode(); /// @brief Called every frame for all visible elements /// @param delta delta timУ virtual void act(float delta) { if (mustRefresh) { mustRefresh = false; refresh(); } }; virtual void draw(const DrawContext& pctx, const Assets& assets) = 0; virtual void setVisible(bool flag); bool isVisible() const; virtual void setEnabled(bool flag); bool isEnabled() const; virtual void setAlign(Align align); Align getAlign() const; virtual void setHover(bool flag); bool isHover() const; virtual void setParent(UINode* node); UINode* getParent() const; /// @brief Set element color (doesn't affect inner elements). /// Also replaces hover color to avoid adding extra properties virtual void setColor(glm::vec4 newColor); /// @brief Get element color /// @return (float R,G,B,A in range [0.0, 1.0]) glm::vec4 getColor() const; virtual void setHoverColor(glm::vec4 newColor); glm::vec4 getHoverColor() const; virtual glm::vec4 getPressedColor() const; virtual void setPressedColor(glm::vec4 color); virtual void setMargin(glm::vec4 margin); glm::vec4 getMargin() const; /// @brief Specifies the stack order of an element /// @attention Is not supported by Panel virtual void setZIndex(int idx); /// @brief Get element z-index int getZIndex() const; virtual UINode* listenAction(const onaction& action); virtual UINode* listenDoubleClick(const onaction& action); virtual UINode* listenFocus(const onaction& action); virtual UINode* listenDefocus(const onaction& action); virtual void onFocus(); virtual void doubleClick(int x, int y); virtual void click(int x, int y); virtual void clicked(Mousecode button) {} virtual void mouseMove(int x, int y) {}; virtual void mouseRelease(int x, int y); virtual void scrolled(int value); bool isPressed() const; void defocus(); bool isFocused() const; /// @brief Check if element catches all user input when focused virtual bool isFocuskeeper() const {return false;} virtual void typed(unsigned int codepoint) {}; virtual void keyPressed(Keycode key) {}; /// @brief Check if screen position is inside of the element /// @param pos screen position virtual bool isInside(glm::vec2 pos); /// @brief Get element under the cursor. /// @param pos cursor screen position /// @param self shared pointer to element /// @return self, sub-element or nullptr if element is not interractive virtual std::shared_ptr getAt(const glm::vec2& pos); /// @brief Check if element is opaque for cursor virtual bool isInteractive() const; /// @brief Make the element opaque (true) or transparent (false) for cursor virtual void setInteractive(bool flag); virtual void setResizing(bool flag); virtual bool isResizing() const; virtual void setTooltip(const std::wstring& text); virtual const std::wstring& getTooltip() const; virtual void setTooltipDelay(float delay); virtual float getTooltipDelay() const; virtual void setCursor(CursorShape shape); virtual CursorShape getCursor() const; virtual glm::vec4 calcColor() const; /// @brief Get inner content offset. Used for scroll virtual glm::vec2 getContentOffset() {return glm::vec2(0.0f);}; /// @brief Calculate screen position of the element virtual glm::vec2 calcPos() const; virtual void setPos(glm::vec2 pos); virtual glm::vec2 getPos() const; glm::vec2 getSize() const; virtual void setSize(glm::vec2 size); glm::vec2 getMinSize() const; virtual void setMinSize(glm::vec2 size); glm::vec2 getMaxSize() const; virtual void setMaxSize(glm::vec2 size); /// @brief Called in containers when new element added virtual void refresh() {}; virtual void fullRefresh() { if (parent) { parent->fullRefresh(); } }; static void moveInto( const std::shared_ptr& node, const std::shared_ptr& dest ); virtual vec2supplier getPositionFunc() const; virtual void setPositionFunc(vec2supplier); virtual vec2supplier getSizeFunc() const; virtual void setSizeFunc(vec2supplier); void setId(const std::string& id); const std::string& getId() const; /// @brief Fetch pos from positionfunc if assigned virtual void reposition(); virtual void setGravity(Gravity gravity); bool isSubnodeOf(const UINode* node); /// @brief collect all nodes having id static void getIndices( const std::shared_ptr& node, std::unordered_map>& map ); static std::shared_ptr find( const std::shared_ptr& node, const std::string& id ); void setMustRefresh() { mustRefresh = true; } }; }