#pragma once #include #include #include "fonts.h" #include "input.h" #include "types.h" #define uiPx(val) kek::UIValue(val, kek::UIUnit::PIXELS) #define uiPw(val) kek::UIValue(val, kek::UIUnit::PARENT_WIDTH) #define uiPh(val) kek::UIValue(val, kek::UIUnit::PARENT_HEIGHT) #define uiSw(val) kek::UIValue(val, kek::UIUnit::SCREEN_WIDTH) #define uiSh(val) kek::UIValue(val, kek::UIUnit::SCREEN_HEIGHT) namespace kek { struct UIBounds { int x, y, w, h; UIBounds(int x, int y, int w, int h) : x(x), y(y), w(w), h(h) {} // Checks if a point is contained in the bounds inline bool contains(IntPoint2 pos) { return pos.x > x && pos.y > y && pos.x < x + w && pos.y < y + h; } // Checks if an already offset point is contained in the bounds inline bool containsOffset(IntPoint2 pos) { return pos.x > 0 && pos.y > 0 && pos.x < w && pos.y < h; } }; enum class UIElementType { WINDOW, RECTANGLE, TEXT, BUTTON, TEXTURE, TEXT_FIELD, SLIDER, CHECKBOX, GROUP }; enum class Origin { TOP_LEFT, TOP_CENTER, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_CENTER, BOTTOM_RIGHT, LEFT_CENTER, RIGHT_CENTER, CENTER }; enum class UIUnit { PIXELS, PARENT_WIDTH, PARENT_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT }; struct UIValue { float pixels = 0; float parentWidth = 0; float parentHeight = 0; float screenWidth = 0; float screenHeight = 0; constexpr UIValue(float value, UIUnit unit) { switch(unit) { case UIUnit::PIXELS: this->pixels = value; break; case UIUnit::PARENT_WIDTH: this->parentWidth = value; break; case UIUnit::PARENT_HEIGHT: this->parentHeight = value; break; case UIUnit::SCREEN_WIDTH: this->screenWidth = value; break; case UIUnit::SCREEN_HEIGHT: this->screenHeight = value; break; } } constexpr UIValue() = default; constexpr UIValue &operator+=(const UIValue &rhs) { this->pixels += rhs.pixels; this->parentWidth += rhs.parentWidth; this->parentHeight += rhs.parentHeight; this->screenWidth += rhs.screenWidth; this->screenHeight += rhs.screenHeight; return *this; } constexpr UIValue &operator-=(const UIValue &rhs) { this->pixels -= rhs.pixels; this->parentWidth -= rhs.parentWidth; this->parentHeight -= rhs.parentHeight; this->screenWidth -= rhs.screenWidth; this->screenHeight -= rhs.screenHeight; return *this; } constexpr friend UIValue operator+(const UIValue &lhs, const UIValue &rhs) { return UIValue(lhs) += rhs; } constexpr friend UIValue operator-(const UIValue &lhs, const UIValue &rhs) { return UIValue(lhs) -= rhs; } }; class UIElement { protected: UIElement *parent = nullptr; std::vector children; public: UIValue x, y; Origin origin = Origin::TOP_LEFT; bool enableClipping = false; bool clickable = false; bool visible = true; bool focusable = false; bool draggable = false; bool hovering = false; bool focused = false; UIElement(UIValue x, UIValue y); virtual ~UIElement(); virtual UIElementType getType() = 0; // Returns the element's origin position relative to its parent in pixels (not offset by getBounds()) IntPoint2 getOriginPosition(); // Returns the element's position relative to its parent in pixels (offset by getBounds()) IntPoint2 getPosition(); // Returns the element's origin position on the screen in pixels (not offset by getBounds()) IntPoint2 getScreenOriginPosition(); // Returns the element's position on the screen in pixels (offset by getBounds()) IntPoint2 getScreenPosition(); // Returns the bounds of the element relative to its origin virtual UIBounds getBounds() = 0; // Returns the bounds of the element on the screen UIBounds getScreenBounds(); std::vector getChildren(); void addChild(UIElement *child); void removeChild(UIElement *child); protected: int uiToScreen(UIValue val); static int offsetX(int w, Origin origin); static int offsetY(int h, Origin origin); static UIBounds offsetUIBounds(int w, int h, Origin origin); public: void drawAll(IntPoint2 screenPos, glm::mat4 projection); virtual void draw(IntPoint2 screenPos, glm::mat4 projection) = 0; void hoverAll(IntPoint2 pos, IntPoint2 screenPos); void hoverExitAll(); virtual void hoverEnter(IntPoint2 pos, IntPoint2 screenPos){}; virtual void hover(IntPoint2 pos, IntPoint2 screenPos){}; virtual void hoverExit(){}; bool clickAll(IntPoint2 pos, IntPoint2 screenPos, GLFWMouseButton button); virtual void click(IntPoint2 pos, IntPoint2 screenPos, GLFWMouseButton button){}; bool focusEnterAll(IntPoint2 pos, IntPoint2 screenPos); virtual void focusEnter(){}; virtual void focusExit(){}; bool dragEnterAll(IntPoint2 pos, IntPoint2 screenPos); virtual void dragEnter(IntPoint2 pos, IntPoint2 screenPos){}; virtual void drag(IntPoint2 pos, IntPoint2 screenPos){}; virtual void dragExit(IntPoint2 pos, IntPoint2 screenPos){}; }; namespace UI { void init(); void destroy(); std::vector getElements(); void addElement(UIElement *element); void removeElement(UIElement *element); void focusElement(UIElement *element); void unfocusElement(UIElement *element); // Finds the first (topmost) element at the given screen position UIElement *findElementAt(IntPoint2 screenPos); }; } #include "uielements.h"