cigui/cigui.hpp
noffie 384c758295 Switching to own List class.
Adding HStack View (WIP)
2025-04-12 14:37:50 +02:00

291 lines
6.6 KiB
C++

#pragma once
#include <vector>
#include <SFML/Graphics.hpp>
#include <nlohmann/json.hpp>
#include <spdlog/spdlog.h>
#define TYPEDEF_VECTOR(NAME, T, N, s) typedef NAME<T> vec##N##s;
#define TYPEDEF_VECTORS(NAME, N) \
typedef NAME<float> vec##N; \
TYPEDEF_VECTOR(NAME, float, N, f) \
TYPEDEF_VECTOR(NAME, double, N, d) \
TYPEDEF_VECTOR(NAME, long double, N, ld) \
TYPEDEF_VECTOR(NAME, size_t, N, sz) \
TYPEDEF_VECTOR(NAME, int, N, i) \
TYPEDEF_VECTOR(NAME, unsigned int, N, u) \
TYPEDEF_VECTOR(NAME, short, N, s) \
TYPEDEF_VECTOR(NAME, unsigned short, N, us) \
TYPEDEF_VECTOR(NAME, long, N, l) \
TYPEDEF_VECTOR(NAME, unsigned long, N, ul) \
TYPEDEF_VECTOR(NAME, long long, N, ll) \
TYPEDEF_VECTOR(NAME, unsigned long long, N, ull)
namespace cig
{
namespace utils
{
template<typename T, size_t growth_scalar = 1, size_t growth_summand = 3>
class List
{
std::unique_ptr<T> m_Data;
size_t m_Size = 0;
size_t m_Capacity;
protected:
void reserve(const size_t capacity)
{
if (!m_Data)
{
m_Data = std::unique_ptr<T>(static_cast<T*>(calloc(capacity, sizeof(T))));
m_Capacity = capacity;
return;
}
std::unique_ptr<T> newData(static_cast<T*>(calloc(capacity, sizeof(T))));
std::copy(m_Data.get(), m_Data.get() + m_Size, newData.get());
m_Data = std::move(newData);
m_Capacity = capacity;
}
public:
explicit List(const size_t capacity = 3) : m_Capacity(capacity) { reserve(capacity); }
void own(T* data, const size_t size) { m_Data = data; m_Size = size; }
void copy(T* data, const size_t size) {
m_Data = std::make_unique<T[]>(size);
std::copy(data, data + size, m_Data.get());
m_Size = size;
}
[[nodiscard]] size_t size() const { return m_Size; }
T& operator[](size_t index) { return m_Data.get()[index]; }
const T& operator[](size_t index) const { return m_Data.get()[index]; }
void need(const size_t additional_size)
{
if (m_Size + additional_size > m_Capacity)
reserve(m_Capacity + additional_size);
}
[[nodiscard]] bool empty() const { return m_Size == 0; }
void clear() { m_Size = 0; }
void push_back(const T& value)
{
if (m_Size >= m_Capacity)
reserve(m_Capacity * growth_scalar + growth_summand);
m_Data.get()[m_Size++] = value;
}
template<typename... Args>
void emplace_back(Args&&... args)
{
if (m_Size >= m_Capacity)
reserve(m_Capacity * growth_scalar + growth_summand);
m_Data.get()[m_Size++] = T(std::forward<Args>(args)...);
}
void expand(const List<T>& other)
{
need(other.size());
std::copy(other.m_Data.get(), other.m_Data.get() + other.size(), m_Data.get() + m_Size);
m_Size += other.size();
}
void iterate(const std::function<void(T&)>& func)
{
for (size_t i = 0; i < m_Size; i++)
func(m_Data.get()[i]);
}
void iterate(const std::function<void(const T&)>& func) const
{
for (size_t i = 0; i < m_Size; i++)
func(m_Data.get()[i]);
}
};
}
using json = nlohmann::json;
template<typename T>
union Vector2 {
struct { T x, y; };
struct { T a, b; };
};
TYPEDEF_VECTORS(Vector2, 2)
template<typename T>
union Vector3 {
struct { T x, y, z; };
struct { T r, g, b; };
};
TYPEDEF_VECTORS(Vector3, 3)
template<typename T>
union Vector4 {
struct { T x, y, z, w; };
struct { T r, g, b, a; };
struct { T left, top, right, bottom; };
};
TYPEDEF_VECTORS(Vector4, 4)
class RenderCall
{
std::shared_ptr<sf::Drawable> drawable;
sf::RenderStates states;
public:
explicit RenderCall(const sf::RenderStates& states, sf::Drawable* ptr) : drawable(ptr), states(states)
{
if (!drawable) { throw std::runtime_error("RenderCall::RenderCall(): Drawable is null"); }
}
void draw(sf::RenderTarget& target, const sf::RenderStates& states) const { target.draw(*drawable, states); }
};
enum class LayoutSizes : uint8_t
{
None = 0,
Min,
Max,
Fixed,
};
enum class LayoutAlignment : uint8_t
{
None = 0,
Left,
Right,
Top,
Bottom,
Center,
};
enum class LayoutDirection : uint8_t
{
None = 0,
LeftToRight,
RightToLeft,
TopToBottom,
BottomToTop,
};
enum class LayoutPosition : uint8_t
{
None = 0,
Absolute,
Left,
Right,
Top,
Bottom,
Center,
};
struct Layout
{
struct {
LayoutSizes rule = LayoutSizes::None;
vec2f minSize = {0.f, 0.f};
vec2f maxSize = {0.f, 0.f};
} size;
struct {
LayoutAlignment rule = LayoutAlignment::None;
vec4f padding = {0.f, 0.f, 0.f, 0.f};
vec4f margin = {0.f, 0.f, 0.f, 0.f};
} alignment;
struct {
LayoutDirection rule = LayoutDirection::None;
vec2f spacing = {0.f, 0.f};
} direction;
struct {
LayoutPosition rule = LayoutPosition::None;
vec2f position = {0.f, 0.f};
} position;
};
class View
{
protected:
utils::List<RenderCall> m_RenderCalls;
Layout m_Layout;
public:
virtual ~View() = default;
[[nodiscard]] const utils::List<RenderCall>& renderCalls() const { return m_RenderCalls; }
std::unique_ptr<View> content;
virtual bool update() { if (content) return content->update(); return false; }
void draw()
{
if (!m_RenderCalls.empty()) { m_RenderCalls.clear(); }
content = std::unique_ptr<View>(body());
if (!content) { return; }
content->draw();
auto& contentRenderCalls = content->renderCalls();
m_RenderCalls.expand(contentRenderCalls);
}
virtual View* body() = 0;
};
struct Rectangle : View
{
sf::Color m_Color;
sf::Color m_BorderColor;
float m_BorderThickness = 0;
Rectangle* setBorderColor(const sf::Color& color) { m_BorderColor = color; return this; }
Rectangle* setBorderThickness(float thickness) { m_BorderThickness = thickness; return this; }
Rectangle* setColor(const sf::Color& color) { m_Color = color; return this; }
Rectangle* setSize(const vec2f& size) { m_Layout.size.minSize = size; return this; }
View* body() override {
auto m_Shape = new sf::RectangleShape(sf::Vector2f{m_Layout.size.minSize.x, m_Layout.size.minSize.y});
m_Shape->setFillColor(m_Color);
if (m_BorderThickness > 0)
{
m_Shape->setOutlineThickness(m_BorderThickness);
m_Shape->setOutlineColor(m_BorderColor);
}
m_RenderCalls.emplace_back(sf::RenderStates(), m_Shape);
return nullptr;
}
explicit Rectangle(const vec2f& size) {
m_Layout.size.minSize = size;
}
};
class Renderer
{
public:
explicit Renderer(View* _view) : view(_view) { view->draw(); }
std::unique_ptr<View> view;
void update() const
{
if (view->update())
view->draw();
}
void render(sf::RenderTarget& target, const sf::RenderStates& states) const
{
view->renderCalls().iterate([&target, &states](const RenderCall& renderCall) { renderCall.draw(target, states); });
}
};
}