2024-08-25 21:41:54 +02:00

222 lines
5.2 KiB
C++

#pragma once
#include <glm/glm.hpp>
#include <string>
#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<UIElement *> 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<UIElement *> 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<UIElement *> 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"