From 09500da9b3144205dc98f19c807c3d645a68b7bc Mon Sep 17 00:00:00 2001 From: noffie Date: Thu, 12 Dec 2024 21:45:27 +0100 Subject: [PATCH] adding Gui Component system --- CMakeLists.txt | 2 +- src/gui/ComponentStack.hpp | 45 ++++++++++ src/gui/CoreComponents.hpp | 168 +++++++++++++++++++++++++++++++++++++ src/gui/GuiComponent.hpp | 22 +++++ src/gui/drawing_helper.cpp | 60 ------------- src/gui/drawing_helper.hpp | 29 ------- src/main.cpp | 82 +++++++++--------- 7 files changed, 275 insertions(+), 133 deletions(-) create mode 100644 src/gui/ComponentStack.hpp create mode 100644 src/gui/CoreComponents.hpp create mode 100644 src/gui/GuiComponent.hpp delete mode 100644 src/gui/drawing_helper.cpp delete mode 100644 src/gui/drawing_helper.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 887e4db..b1bb158 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) # Enforce C++20 standard set(CMAKE_CXX_EXTENSIONS OFF) # Disable compiler-specific extensions set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Generate compile commands (useful for IDEs) set(USE_FOLDERS ON) # Organize targets into folders (for IDEs) -set(BUILD_SHARED_LIBS OFF) # Build static libraries by default +set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) # Build static libraries by default # ======================================================== # Helper Functions: diff --git a/src/gui/ComponentStack.hpp b/src/gui/ComponentStack.hpp new file mode 100644 index 0000000..a483e3c --- /dev/null +++ b/src/gui/ComponentStack.hpp @@ -0,0 +1,45 @@ +#pragma once +#include +#include +#include "GuiComponent.hpp" + +class ComponentStack final +{ + inline static std::vector < std::shared_ptr > s_Components; + +public: + inline static bool* s_WndRunning; + inline static size_t s_WndWidth, s_WndHeight; + + template requires std::is_base_of_v + static std::shared_ptr push(Args&&... args) + { + std::shared_ptr comp = std::make_shared(std::forward(args)...); + + s_Components.push_back(comp); + comp->wndrsize(s_WndWidth, s_WndHeight); + comp->attach(s_WndRunning); + + return comp; + } + + static int run() + { + for (std::shared_ptr& component : s_Components) + { + int r = component->input(); + if (r != 0) return r; + r = component->draw(); + if (r != 0) return r; + } + return 0; + } + + static void resize() + { + for (auto& comp : s_Components) + { + comp->wndrsize(s_WndWidth, s_WndHeight); + } + } +}; \ No newline at end of file diff --git a/src/gui/CoreComponents.hpp b/src/gui/CoreComponents.hpp new file mode 100644 index 0000000..b991597 --- /dev/null +++ b/src/gui/CoreComponents.hpp @@ -0,0 +1,168 @@ +#pragma once +#include "GuiComponent.hpp" +#include +#include + + +class GuiMovableWindow : public sva::GuiComponent +{ +private: + bool m_DragWindow = false; + Vector2 m_PanOffset = { 0,0 }; +protected: + Rectangle m_WndRect = { 20,20, 200, 100 }; + bool m_WindowOpen = true; +public: + + virtual int input() override + { + if (!m_WindowOpen) return 0; + Vector2 mouse_pos = GetMousePosition(); + + if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && !m_DragWindow) + { + if (CheckCollisionPointRec(mouse_pos, { m_WndRect.x, m_WndRect.y, m_WndRect.width - 24, 20 })) + { + m_DragWindow = true; + m_PanOffset = { mouse_pos.x - m_WndRect.x, mouse_pos.y - m_WndRect.y }; + } + } + + if (m_DragWindow) + { + m_WndRect.x = (mouse_pos.x - m_PanOffset.x); + m_WndRect.y = (mouse_pos.y - m_PanOffset.y); + if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) m_DragWindow = false; + } + return rinput(mouse_pos); + } + + virtual int rinput(Vector2& mouse_position) { return 0; }; + + virtual int draw() override + { + if (!m_WindowOpen) return 0; + + m_WindowOpen = !GuiWindowBox(m_WndRect, "Example Movable Window"); + + return 0; + } +}; + +class SafeClosePopup final : public GuiMovableWindow +{ + Vector2 anchor03 = { 0,0 }; + + bool m_CQB_YesButton = false; + bool m_CQB_NoButton = false; +public: + void onAttach() override + { + m_WindowOpen = false; + } + + int draw() override + { + if (WindowShouldClose()) + { + m_WindowOpen = !m_WindowOpen; + onResize(); + } + + if (m_WindowOpen) + { + anchor03 = { m_WndRect.x + 168, m_WndRect.y + 88 }; + + m_WindowOpen = !GuiWindowBox(m_WndRect, "#191# Are you sure you want to close this program?"); + m_CQB_YesButton = GuiButton({ anchor03.x + -152, anchor03.y + 32, 120, 24 }, "#112#Yes"); + m_CQB_NoButton = GuiButton({ anchor03.x + 24, anchor03.y + 32, 120, 24 }, "#113#No"); + GuiLabel({ anchor03.x + -104, anchor03.y + -40, 208, 24 }, "Are you sure you want to close this?"); + GuiLabel({ anchor03.x + -56, anchor03.y + -8, 120, 24 }, "Press \"Yes\" to close"); + if (m_CQB_YesButton) + { + *m_WndRunning = false; + return 1; + } + if (m_CQB_NoButton) m_WindowOpen = false; + + } + return 0; + } + + int rinput(Vector2& mouse_position) override + { + if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && !CheckCollisionPointRec(mouse_position, m_WndRect)) + { + SetMousePosition(anchor03.x, anchor03.y); + } + return 0; + } + + + void onResize() override + { + anchor03 = { static_cast(m_WndWidth) / 2 , static_cast(m_WndHeight) / 2 }; + m_WndRect = { anchor03.x + -168, anchor03.y + -88, 328, 160 }; + } +}; + +class SettingsComponent final : public sva::GuiComponent +{ + Vector2 anchor01 = { 0,0 }; + + int ActiveMonitorID = 0; + + std::string m_ScreenListString; + +public: + void onAttach() override + { + for (int i = 0; i < GetMonitorCount(); ++i) + { + m_ScreenListString += GetMonitorName(i); + m_ScreenListString += ";"; + } + m_ScreenListString.pop_back(); + + fitWindowToMonitor(); + } + + void onResize() override + { + anchor01 = { static_cast(m_WndWidth) / 4, static_cast(m_WndHeight) / 4 }; + } + + int draw() override + { + if (GetCurrentMonitor() != ActiveMonitorID) + { + fitWindowToMonitor(ActiveMonitorID); + m_ScreenListString.clear(); + for (int i = 0; i < GetMonitorCount(); ++i) + { + m_ScreenListString += GetMonitorName(i); + m_ScreenListString += ";"; + } + m_ScreenListString.pop_back(); + } + + GuiGroupBox({ anchor01.x + 0, anchor01.y + 0, 264, 104 }, "Settings"); + GuiLabel({ anchor01.x + 16, anchor01.y + 24, 72, 24 }, "#181#Screen:"); + GuiComboBox({ anchor01.x + 88, anchor01.y + 24, 160, 24 }, m_ScreenListString.c_str(), &ActiveMonitorID); + return 0; + } + +private: + static void fitWindowToMonitor(int monitor = 0) + { + if (GetMonitorCount() < monitor) return; + int width = GetMonitorHeight(monitor); + int height = GetMonitorHeight(monitor); + SetWindowMonitor(monitor); + SetWindowSize(width, height); + + int refresh_rate = GetMonitorRefreshRate(monitor); + SetTargetFPS(refresh_rate); + ToggleBorderlessWindowed(); + } +}; diff --git a/src/gui/GuiComponent.hpp b/src/gui/GuiComponent.hpp new file mode 100644 index 0000000..4bbbd60 --- /dev/null +++ b/src/gui/GuiComponent.hpp @@ -0,0 +1,22 @@ +#pragma once + +namespace sva { + + class GuiComponent + { + public: + void wndrsize(size_t width, size_t height) { m_WndWidth = width; m_WndHeight = height; onResize(); } + virtual void onResize() {} + virtual int draw() { if (!m_WndRunning) { return 1; } return 0; } + virtual int input() { return 0; } + + virtual ~GuiComponent() = default; + + void attach(bool* wnd_running) { m_WndRunning = wnd_running; onAttach(); } + virtual void onAttach() {} + protected: + bool* m_WndRunning = nullptr; + size_t m_WndWidth = 0; + size_t m_WndHeight = 0; + }; +} diff --git a/src/gui/drawing_helper.cpp b/src/gui/drawing_helper.cpp deleted file mode 100644 index 3ec9b0b..0000000 --- a/src/gui/drawing_helper.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "drawing_helper.hpp" - -namespace sva -{ - std::vector split(const std::string& str, char delim) - { - std::vector tokens; - std::string token; - std::istringstream tokenStream(str); - while (std::getline(tokenStream, token, delim)) - { - tokens.push_back(token); - } - return tokens; - } -} - - -Color sva::GetThemeColor(GuiControlProperty property) -{ - return GetColor(GuiGetStyle(DEFAULT, TEXT_COLOR_NORMAL)); -} - -void sva::DrawText(const std::string& text, vec2i pos, int size, Color color, TEXT_ALIGNMENT alignment) -{ - switch (alignment) - { - case TEXT_ALIGN_LEFT: - return DrawText(text.c_str(), pos.x, pos.y, size, color); - case TEXT_ALIGN_RIGHT: - if (text.find('\n') != std::string::npos) - { - std::vector lines = split(text, '\n'); - for (auto& line : lines) - { - pos.x -= MeasureText(line.c_str(), size); - DrawText(line.c_str(), pos.x, pos.y, size, color); - pos.y += size; - } - return; - } - pos.x -= MeasureText(text.c_str(), size); - return DrawText(text.c_str(), pos.x, pos.y, size, color); - - case TEXT_ALIGN_CENTER: - if (text.find('\n') != std::string::npos) - { - std::vector lines = sva::split(text, '\n'); - for (auto& line : lines) - { - pos.x -= MeasureText(line.c_str(), size) / 2; - DrawText(line.c_str(), pos.x, pos.y, size, color); - pos.y += size; - } - return; - } - pos.x -= MeasureText(text.c_str(), size) / 2; - DrawText(text.c_str(), pos.x, pos.y, size, color); - } -} diff --git a/src/gui/drawing_helper.hpp b/src/gui/drawing_helper.hpp deleted file mode 100644 index 92c74ed..0000000 --- a/src/gui/drawing_helper.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include - -namespace sva -{ - template - struct TVector2 - { - T x, y; - }; - typedef TVector2 vec2i; - typedef TVector2 vec2f; - - enum TEXT_ALIGNMENT - { - TEXT_ALIGN_LEFT, - TEXT_ALIGN_CENTER, - TEXT_ALIGN_RIGHT - }; - - - Color GetThemeColor(GuiControlProperty property); - - void DrawText(const std::string& text, vec2i pos, int size, Color color = GetColor(GuiGetStyle(DEFAULT, TEXT_COLOR_NORMAL)), TEXT_ALIGNMENT alignment = TEXT_ALIGN_LEFT); -} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 92400de..f2562ae 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,7 +20,9 @@ /* raylib includes */ #include -#include + +#include "gui/ComponentStack.hpp" +#include "gui/CoreComponents.hpp" constexpr int window_width = 800; constexpr int window_height = 650; @@ -34,18 +36,6 @@ enum class logerr_level SVA, }; -void fitWindowToMonitor(int monitor = 0) -{ - if (GetMonitorCount() < monitor) return; - int width = GetMonitorHeight(monitor); - int height = GetMonitorHeight(monitor); - SetWindowMonitor(monitor); - SetWindowSize(width, height); - - int refresh_rate = GetMonitorRefreshRate(monitor); - SetTargetFPS(refresh_rate); -} - int main(void) { @@ -62,8 +52,6 @@ int main(void) InitWindow(window_width, window_height, window_title); - fitWindowToMonitor(); - if (!IsWindowReady()) { logerr(logerr_level::Window).logln("Window could not be created..."); @@ -71,57 +59,65 @@ int main(void) } ClearWindowState(ConfigFlags::FLAG_WINDOW_RESIZABLE | ConfigFlags::FLAG_WINDOW_TRANSPARENT); - SetWindowState(ConfigFlags::FLAG_WINDOW_ALWAYS_RUN | ConfigFlags::FLAG_BORDERLESS_WINDOWED_MODE); + SetWindowState(ConfigFlags::FLAG_WINDOW_ALWAYS_RUN); SetWindowFocused(); bool m_Running = true; + ComponentStack::s_WndRunning = &m_Running; - int wnd_width = GetRenderWidth(); - int wnd_height = GetRenderHeight(); + ComponentStack::s_WndWidth = GetRenderWidth(); + ComponentStack::s_WndHeight = GetRenderHeight(); - Vector2 anchor03 = { static_cast(wnd_width) / 2 , static_cast(wnd_height) / 2 }; + int run_result = 0; + + ComponentStack::push(); + + // always on top... + SafeClosePopup safe_close_popup; + safe_close_popup.attach(&m_Running); + safe_close_popup.wndrsize(ComponentStack::s_WndWidth, ComponentStack::s_WndHeight); - bool CloseQuestionBoxActive = false; - bool CQB_YesButton = false; - bool CQB_NoButton = false; while (m_Running) { + + if (IsWindowResized()) { - wnd_width = GetRenderWidth(); - wnd_height = GetRenderHeight(); - anchor03 = { static_cast(wnd_width) / 2 , static_cast(wnd_height) / 2 }; - } + ComponentStack::s_WndWidth = GetRenderWidth(); + ComponentStack::s_WndHeight = GetRenderHeight(); - if (WindowShouldClose()) CloseQuestionBoxActive = !CloseQuestionBoxActive; + safe_close_popup.wndrsize(ComponentStack::s_WndWidth, ComponentStack::s_WndHeight); + ComponentStack::resize(); + } BeginDrawing(); ClearBackground(RAYWHITE); - DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY); + run_result = safe_close_popup.input(); + if (run_result != 0) break; - if (CloseQuestionBoxActive) - { - CloseQuestionBoxActive = !GuiWindowBox({ anchor03.x + -168, anchor03.y + -88, 328, 160 }, "#191# Are you sure you want to close this program?"); - CQB_YesButton = GuiButton({ anchor03.x + -152, anchor03.y + 32, 120, 24 }, "Yes"); - CQB_NoButton = GuiButton({ anchor03.x + 24, anchor03.y + 32, 120, 24 }, "No"); - GuiLabel({ anchor03.x + -104, anchor03.y + -40, 208, 24 }, "Are you sure you want to close this?"); - GuiLabel({ anchor03.x + -56, anchor03.y + -8, 120, 24 }, "Press \"Yes\" to close"); - if (CQB_YesButton) - { - m_Running = false; - break; - } - if (CQB_NoButton) CloseQuestionBoxActive = false; - } + run_result = ComponentStack::run(); + if (run_result != 0) break; + + run_result = safe_close_popup.draw(); + if (run_result != 0) break; EndDrawing(); - } + CloseWindow(); + switch (run_result) + { + case 2: + logerr(logerr_level::SVA).logln("Program exiting abnormally."); + break; + default: + break; + } + return 0; } \ No newline at end of file