adding Gui Component system
This commit is contained in:
parent
b27be1fa8e
commit
09500da9b3
7 changed files with 275 additions and 133 deletions
|
@ -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_CXX_EXTENSIONS OFF) # Disable compiler-specific extensions
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Generate compile commands (useful for IDEs)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Generate compile commands (useful for IDEs)
|
||||||
set(USE_FOLDERS ON) # Organize targets into folders (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:
|
# Helper Functions:
|
||||||
|
|
45
src/gui/ComponentStack.hpp
Normal file
45
src/gui/ComponentStack.hpp
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#pragma once
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include "GuiComponent.hpp"
|
||||||
|
|
||||||
|
class ComponentStack final
|
||||||
|
{
|
||||||
|
inline static std::vector < std::shared_ptr<sva::GuiComponent> > s_Components;
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline static bool* s_WndRunning;
|
||||||
|
inline static size_t s_WndWidth, s_WndHeight;
|
||||||
|
|
||||||
|
template<typename T, typename ...Args> requires std::is_base_of_v<sva::GuiComponent, T>
|
||||||
|
static std::shared_ptr<T> push(Args&&... args)
|
||||||
|
{
|
||||||
|
std::shared_ptr<T> comp = std::make_shared<T>(std::forward<Args>(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<sva::GuiComponent>& 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
168
src/gui/CoreComponents.hpp
Normal file
168
src/gui/CoreComponents.hpp
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
#pragma once
|
||||||
|
#include "GuiComponent.hpp"
|
||||||
|
#include <raylibs/raygui.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
|
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<float>(m_WndWidth) / 2 , static_cast<float>(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<float>(m_WndWidth) / 4, static_cast<float>(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();
|
||||||
|
}
|
||||||
|
};
|
22
src/gui/GuiComponent.hpp
Normal file
22
src/gui/GuiComponent.hpp
Normal file
|
@ -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;
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,60 +0,0 @@
|
||||||
#include "drawing_helper.hpp"
|
|
||||||
|
|
||||||
namespace sva
|
|
||||||
{
|
|
||||||
std::vector<std::string> split(const std::string& str, char delim)
|
|
||||||
{
|
|
||||||
std::vector<std::string> 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<std::string> 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<std::string> 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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <raylib.h>
|
|
||||||
#include <raylibs/raygui.h>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
namespace sva
|
|
||||||
{
|
|
||||||
template<typename T>
|
|
||||||
struct TVector2
|
|
||||||
{
|
|
||||||
T x, y;
|
|
||||||
};
|
|
||||||
typedef TVector2<int> vec2i;
|
|
||||||
typedef TVector2<float> 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);
|
|
||||||
}
|
|
82
src/main.cpp
82
src/main.cpp
|
@ -20,7 +20,9 @@
|
||||||
|
|
||||||
/* raylib includes */
|
/* raylib includes */
|
||||||
#include <raylib.h>
|
#include <raylib.h>
|
||||||
#include <raylibs/raygui.h>
|
|
||||||
|
#include "gui/ComponentStack.hpp"
|
||||||
|
#include "gui/CoreComponents.hpp"
|
||||||
|
|
||||||
constexpr int window_width = 800;
|
constexpr int window_width = 800;
|
||||||
constexpr int window_height = 650;
|
constexpr int window_height = 650;
|
||||||
|
@ -34,18 +36,6 @@ enum class logerr_level
|
||||||
SVA,
|
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)
|
int main(void)
|
||||||
{
|
{
|
||||||
|
@ -62,8 +52,6 @@ int main(void)
|
||||||
|
|
||||||
InitWindow(window_width, window_height, window_title);
|
InitWindow(window_width, window_height, window_title);
|
||||||
|
|
||||||
fitWindowToMonitor();
|
|
||||||
|
|
||||||
if (!IsWindowReady())
|
if (!IsWindowReady())
|
||||||
{
|
{
|
||||||
logerr(logerr_level::Window).logln("Window could not be created...");
|
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);
|
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();
|
SetWindowFocused();
|
||||||
|
|
||||||
|
|
||||||
bool m_Running = true;
|
bool m_Running = true;
|
||||||
|
ComponentStack::s_WndRunning = &m_Running;
|
||||||
|
|
||||||
int wnd_width = GetRenderWidth();
|
ComponentStack::s_WndWidth = GetRenderWidth();
|
||||||
int wnd_height = GetRenderHeight();
|
ComponentStack::s_WndHeight = GetRenderHeight();
|
||||||
|
|
||||||
Vector2 anchor03 = { static_cast<float>(wnd_width) / 2 , static_cast<float>(wnd_height) / 2 };
|
int run_result = 0;
|
||||||
|
|
||||||
|
ComponentStack::push<SettingsComponent>();
|
||||||
|
|
||||||
|
// 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) {
|
while (m_Running) {
|
||||||
|
|
||||||
|
|
||||||
if (IsWindowResized())
|
if (IsWindowResized())
|
||||||
{
|
{
|
||||||
wnd_width = GetRenderWidth();
|
ComponentStack::s_WndWidth = GetRenderWidth();
|
||||||
wnd_height = GetRenderHeight();
|
ComponentStack::s_WndHeight = GetRenderHeight();
|
||||||
anchor03 = { static_cast<float>(wnd_width) / 2 , static_cast<float>(wnd_height) / 2 };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (WindowShouldClose()) CloseQuestionBoxActive = !CloseQuestionBoxActive;
|
safe_close_popup.wndrsize(ComponentStack::s_WndWidth, ComponentStack::s_WndHeight);
|
||||||
|
ComponentStack::resize();
|
||||||
|
}
|
||||||
|
|
||||||
BeginDrawing();
|
BeginDrawing();
|
||||||
ClearBackground(RAYWHITE);
|
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)
|
run_result = ComponentStack::run();
|
||||||
{
|
if (run_result != 0) break;
|
||||||
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");
|
run_result = safe_close_popup.draw();
|
||||||
CQB_NoButton = GuiButton({ anchor03.x + 24, anchor03.y + 32, 120, 24 }, "No");
|
if (run_result != 0) break;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
EndDrawing();
|
EndDrawing();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CloseWindow();
|
CloseWindow();
|
||||||
|
|
||||||
|
switch (run_result)
|
||||||
|
{
|
||||||
|
case 2:
|
||||||
|
logerr(logerr_level::SVA).logln("Program exiting abnormally.");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
Loading…
Reference in a new issue