#pragma once #include #include #include #include #define TYPEDEF_VECTOR(NAME, T, N, s) typedef NAME vec##N##s; #define TYPEDEF_VECTORS(NAME, N) \ typedef NAME 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 class List { std::unique_ptr 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(static_cast(calloc(capacity, sizeof(T)))); m_Capacity = capacity; return; } std::unique_ptr newData(static_cast(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(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 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)...); } void expand(const List& 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& func) { for (size_t i = 0; i < m_Size; i++) func(m_Data.get()[i]); } void iterate(const std::function& func) const { for (size_t i = 0; i < m_Size; i++) func(m_Data.get()[i]); } }; } using json = nlohmann::json; template union Vector2 { struct { T x, y; }; struct { T a, b; }; }; TYPEDEF_VECTORS(Vector2, 2) template union Vector3 { struct { T x, y, z; }; struct { T r, g, b; }; }; TYPEDEF_VECTORS(Vector3, 3) template 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 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 m_RenderCalls; Layout m_Layout; public: virtual ~View() = default; [[nodiscard]] const utils::List& renderCalls() const { return m_RenderCalls; } std::unique_ptr content; virtual bool update() { if (content) return content->update(); return false; } void draw() { if (!m_RenderCalls.empty()) { m_RenderCalls.clear(); } content = std::unique_ptr(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; 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); }); } }; }