From 384c758295ed470e85998a63574c0775aa9b3280 Mon Sep 17 00:00:00 2001 From: noffie Date: Sat, 12 Apr 2025 14:37:50 +0200 Subject: [PATCH] Switching to own List class. Adding HStack View (WIP) --- CMakeLists.txt | 4 +- cigui.hpp | 291 +++++++++++++++++++++++++++++++++++++++++++++++++ cigus.hpp | 204 ---------------------------------- src/main.cpp | 52 ++++++--- 4 files changed, 329 insertions(+), 222 deletions(-) create mode 100644 cigui.hpp delete mode 100644 cigus.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6abcb81..3e27952 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.20) -project(cigus +project(cigui VERSION 0.0.1 LANGUAGES CXX) @@ -42,7 +42,7 @@ set(PROJECT_SOURCE_NAME "${PROJECT_NAME}_SOURCES") find_files(ExampleSources src cpp hpp c h cxx hxx) -add_executable(${PROJECT_NAME} ${ExampleSources} cigus.hpp) +add_executable(${PROJECT_NAME} ${ExampleSources} cigui.hpp) target_include_directories(${PROJECT_NAME} PUBLIC include ./) diff --git a/cigui.hpp b/cigui.hpp new file mode 100644 index 0000000..b48bdcd --- /dev/null +++ b/cigui.hpp @@ -0,0 +1,291 @@ +#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); }); + } + }; +} diff --git a/cigus.hpp b/cigus.hpp deleted file mode 100644 index a644f4b..0000000 --- a/cigus.hpp +++ /dev/null @@ -1,204 +0,0 @@ -#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 UI -{ - 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::unique_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: - std::vector m_RenderCalls; - Layout m_Layout; - - public: - virtual ~View() = default; - - [[nodiscard]] const std::vector& renderCalls() const { return m_RenderCalls; } - - virtual bool update() { return false; } - - void draw() - { - if (!m_RenderCalls.empty()) { m_RenderCalls.clear(); } - body(); - } - - virtual void body() = 0; - }; - - class Rectangle : public View - { - - vec2f m_Size; - sf::Color m_Color; - sf::Color m_BorderColor; - float m_BorderThickness = 0; - - - void setBorderColor(const sf::Color& color) { m_BorderColor = color; } - void setBorderThickness(float thickness) { m_BorderThickness = thickness; } - void setColor(const sf::Color& color) { m_Color = color; } - void setSize(const vec2f& size) { m_Size = size; } - - void body() override - { - auto m_Shape = new sf::RectangleShape(sf::Vector2f{m_Size.x, m_Size.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); - } - - Rectangle(const vec2f& size) : m_Size(size) {} - }; - - class Renderer - { - public: - explicit Renderer(View* _view) : view(_view) { view->body(); } - - std::unique_ptr view; - - void update() const - { - if (view->update()) - view->draw(); - } - - void render(sf::RenderTarget& target, const sf::RenderStates& states) const - { - for (const auto& renderCall : view->renderCalls()) { renderCall.draw(target, states); } - } - }; -} diff --git a/src/main.cpp b/src/main.cpp index ecd7d9a..cd3fc9a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,32 +4,52 @@ #include -#include +#include -class NewView : public cigus::UI::View { -public: - bool update() override - { - std::cout << "Update...\n"; - return true; +// WIP - not working yet +// Layout unsupported +struct HStack final : cig::View { + using Stack = cig::utils::List; + Stack views; + + void append(View* view) { views.push_back(view); } + + bool update() override { + bool needs_redraw = false; + views.iterate([this, &needs_redraw](View*& view) { if (view->update()) needs_redraw = true; }); + return needs_redraw; } - void body() override { - std::cout << "Body\n"; - Rectangle(400,400, sf::Color(20,20,120)); - Rectangle(100, 100, sf::Color(250,250,250)); - Rectangle(10,20, sf::Color(120,120,120)); + View* body() override { + views.iterate([this](View* view) { view->draw(); }); + views.iterate([this](const View* view) { + this->m_RenderCalls.expand(view->renderCalls()); + }); + return nullptr; + } +}; + +struct NewView final : cig::View { + View* body() override { + auto List = new HStack(); + + auto view = new cig::Rectangle({400, 400}); + view->setBorderColor(sf::Color::Red)->setBorderThickness(5)->setColor(sf::Color::Green); + List->append(view); + + view = new cig::Rectangle({100, 100}); + view->setBorderColor(sf::Color::Blue)->setBorderThickness(5)->setColor(sf::Color::Yellow); + List->append(view); + + return List; } }; int main(int argc, char** argv) { - - namespace UI = cigus::UI; sf::RenderWindow window(sf::VideoMode({800, 600}), "Hello World!"); - window.setFramerateLimit(60); const auto view = new NewView(); - const UI::Renderer renderer(view); + const cig::Renderer renderer(view); while (window.isOpen()) {