From 3607ccee3b4504b694843059a61238508710e3c4 Mon Sep 17 00:00:00 2001 From: Nick Koirala Date: Fri, 14 Nov 2025 12:33:41 +1300 Subject: [PATCH 1/9] feat: Toggle log view --- imgui.ini | 79 ++++++++------------------------------------ include/GuiManager.h | 2 ++ src/GuiManager.cpp | 64 ++++++++++++++++++++--------------- 3 files changed, 53 insertions(+), 92 deletions(-) diff --git a/imgui.ini b/imgui.ini index 28ec247..2369be6 100644 --- a/imgui.ini +++ b/imgui.ini @@ -1,78 +1,27 @@ -[Window][Debug##Default] -Pos=60,60 -Size=400,400 -Collapsed=0 - -[Window][Simian ImGui Demo] -Pos=141,224 -Size=516,224 -Collapsed=0 - [Window][DockSpaceHost] Pos=0,0 Size=1280,720 Collapsed=0 -[Window][Left Panel] -Pos=292,567 -Size=980,145 +[Window][Debug##Default] +Pos=384,193 +Size=400,400 Collapsed=0 -DockId=0x00000002,0 - -[Window][Right Panel] -Pos=8,27 -Size=282,685 -Collapsed=0 -DockId=0x00000003,0 - -[Window][##TOAST2] -Pos=548,314 -Size=232,82 -Collapsed=0 - -[Window][##TOAST1] -Pos=548,406 -Size=232,82 -Collapsed=0 - -[Window][##TOAST0] -Pos=548,498 -Size=232,82 -Collapsed=0 - -[Window][##TOAST4] -Pos=548,130 -Size=232,82 -Collapsed=0 - -[Window][##TOAST3] -Pos=548,222 -Size=232,82 -Collapsed=0 - -[Window][##TOAST5] -Pos=548,38 -Size=232,82 -Collapsed=0 - -[Window][Log Viewer] -Pos=8,580 -Size=1264,132 -Collapsed=0 -DockId=0x00000006,0 [Window][Game Window] Pos=8,27 -Size=1264,551 +Size=1264,579 Collapsed=0 DockId=0x00000001,0 -[Docking][Data] -DockSpace ID=0x9076BACA Window=0x34F970D7 Pos=8,27 Size=1264,685 Split=Y Selected=0x6D1308E5 - DockNode ID=0x00000005 Parent=0x9076BACA SizeRef=1264,551 Split=X - DockNode ID=0x00000003 Parent=0x00000005 SizeRef=282,685 Selected=0x6D1308E5 - DockNode ID=0x00000004 Parent=0x00000005 SizeRef=980,685 Split=Y - DockNode ID=0x00000001 Parent=0x00000004 SizeRef=1264,538 CentralNode=1 Selected=0x27A02DAA - DockNode ID=0x00000002 Parent=0x00000004 SizeRef=1264,145 Selected=0x995FC207 - DockNode ID=0x00000006 Parent=0x9076BACA SizeRef=1264,132 Selected=0xBEDDA0C1 +[Window][Log Viewer] +Pos=8,608 +Size=1264,104 +Collapsed=0 +DockId=0x00000002,0 + +[Docking][Data] +DockSpace ID=0x9076BACA Window=0x34F970D7 Pos=8,27 Size=1264,685 Split=Y Selected=0x27A02DAA + DockNode ID=0x00000001 Parent=0x9076BACA SizeRef=1264,579 CentralNode=1 Selected=0x27A02DAA + DockNode ID=0x00000002 Parent=0x9076BACA SizeRef=1264,104 Selected=0xBEDDA0C1 diff --git a/include/GuiManager.h b/include/GuiManager.h index 5294a7e..a7b9084 100644 --- a/include/GuiManager.h +++ b/include/GuiManager.h @@ -17,4 +17,6 @@ public: private: void SetupDockspace(RenderTexture2D& renderTexture); void RenderNotifications(); + + bool showLogWindow = true; }; \ No newline at end of file diff --git a/src/GuiManager.cpp b/src/GuiManager.cpp index 7898f01..d46c083 100644 --- a/src/GuiManager.cpp +++ b/src/GuiManager.cpp @@ -66,7 +66,15 @@ void GuiManager::SetupDockspace(RenderTexture2D &renderTexture) ImGui::Begin("DockSpaceHost", &dockspaceOpen, window_flags); if (opt_fullscreen) ImGui::PopStyleVar(2); - + if (ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("View")) + { + ImGui::MenuItem("Log Viewer", nullptr, &showLogWindow); + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + } ImGuiID dockspace_id = ImGui::GetID("MyDockSpace"); ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags); @@ -77,34 +85,36 @@ void GuiManager::SetupDockspace(RenderTexture2D &renderTexture) rlImGuiImageRenderTexture(&renderTexture); ImGui::End(); - - // Log Viewer Window - ImGui::Begin("Log Viewer"); - static std::string logContent; - static size_t lastFileSize = 0; - - // Read the log file if it has changed - std::ifstream logFile("log.txt", std::ios::ate); // Open at the end to get the file size - if (logFile.is_open()) + if (showLogWindow) { - size_t fileSize = logFile.tellg(); - if (fileSize != lastFileSize) - { - lastFileSize = fileSize; - logFile.seekg(0, std::ios::beg); // Go back to the beginning - logContent.assign((std::istreambuf_iterator(logFile)), - std::istreambuf_iterator()); - } - logFile.close(); - } + // Log Viewer Window + ImGui::Begin("Log Viewer"); + static std::string logContent; + static size_t lastFileSize = 0; - // Display the log content in a scrollable text area - ImGui::BeginChild("LogText", ImVec2(0, 0), true, ImGuiWindowFlags_HorizontalScrollbar); - ImGui::TextUnformatted(logContent.c_str()); - if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) - ImGui::SetScrollHereY(1.0f); // Auto-scroll to the bottom - ImGui::EndChild(); - ImGui::End(); + // Read the log file if it has changed + std::ifstream logFile("log.txt", std::ios::ate); // Open at the end to get the file size + if (logFile.is_open()) + { + size_t fileSize = logFile.tellg(); + if (fileSize != lastFileSize) + { + lastFileSize = fileSize; + logFile.seekg(0, std::ios::beg); // Go back to the beginning + logContent.assign((std::istreambuf_iterator(logFile)), + std::istreambuf_iterator()); + } + logFile.close(); + } + + // Display the log content in a scrollable text area + ImGui::BeginChild("LogText", ImVec2(0, 0), true, ImGuiWindowFlags_HorizontalScrollbar); + ImGui::TextUnformatted(logContent.c_str()); + if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) + ImGui::SetScrollHereY(1.0f); // Auto-scroll to the bottom + ImGui::EndChild(); + ImGui::End(); + } ImGui::End(); } From 34304fcb2cd18f2574ab43ae570e44b92ba696fe Mon Sep 17 00:00:00 2001 From: Nick Koirala Date: Fri, 14 Nov 2025 13:02:10 +1300 Subject: [PATCH 2/9] feat: keyboard shortcut for log window --- imgui.ini | 2 +- src/GuiManager.cpp | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/imgui.ini b/imgui.ini index 2369be6..0df5797 100644 --- a/imgui.ini +++ b/imgui.ini @@ -10,7 +10,7 @@ Collapsed=0 [Window][Game Window] Pos=8,27 -Size=1264,579 +Size=1264,685 Collapsed=0 DockId=0x00000001,0 diff --git a/src/GuiManager.cpp b/src/GuiManager.cpp index d46c083..95b0734 100644 --- a/src/GuiManager.cpp +++ b/src/GuiManager.cpp @@ -70,11 +70,16 @@ void GuiManager::SetupDockspace(RenderTexture2D &renderTexture) { if (ImGui::BeginMenu("View")) { - ImGui::MenuItem("Log Viewer", nullptr, &showLogWindow); + ImGui::MenuItem("Log Viewer", "Ctrl + L", &showLogWindow); ImGui::EndMenu(); } ImGui::EndMenuBar(); } + + if (ImGui::GetIO().KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_L)) + { + showLogWindow = !showLogWindow; + } ImGuiID dockspace_id = ImGui::GetID("MyDockSpace"); ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags); From f352b432d9bbbaa62800e506ab253db207710108 Mon Sep 17 00:00:00 2001 From: Nick Koirala Date: Fri, 14 Nov 2025 15:04:00 +1300 Subject: [PATCH 3/9] feat: added toast support to scripting --- imgui.ini | 16 ++- include/Application.h | 5 +- include/GuiManager.h | 7 +- include/gui/Toast.h | 57 ++++++++++ scripts/as.predefined | 14 ++- scripts/{test.as => game.as} | 0 scripts/update.as | 3 +- src/Application.cpp | 18 ++- src/GuiManager.cpp | 111 +++++++++++++++++- src/ScriptBindings.cpp | 215 ++++++++++++++++++++++++++++------- tests/CMakeLists.txt | 4 + 11 files changed, 386 insertions(+), 64 deletions(-) create mode 100644 include/gui/Toast.h rename scripts/{test.as => game.as} (100%) diff --git a/imgui.ini b/imgui.ini index 0df5797..187746c 100644 --- a/imgui.ini +++ b/imgui.ini @@ -9,8 +9,8 @@ Size=400,400 Collapsed=0 [Window][Game Window] -Pos=8,27 -Size=1264,685 +Pos=8,25 +Size=1264,581 Collapsed=0 DockId=0x00000001,0 @@ -20,8 +20,18 @@ Size=1264,104 Collapsed=0 DockId=0x00000002,0 +[Window][##TOAST0] +Pos=1062,643 +Size=198,57 +Collapsed=0 + +[Window][##TOAST1] +Pos=1062,576 +Size=198,57 +Collapsed=0 + [Docking][Data] -DockSpace ID=0x9076BACA Window=0x34F970D7 Pos=8,27 Size=1264,685 Split=Y Selected=0x27A02DAA +DockSpace ID=0x9076BACA Window=0x34F970D7 Pos=8,25 Size=1264,687 Split=Y Selected=0x27A02DAA DockNode ID=0x00000001 Parent=0x9076BACA SizeRef=1264,579 CentralNode=1 Selected=0x27A02DAA DockNode ID=0x00000002 Parent=0x9076BACA SizeRef=1264,104 Selected=0xBEDDA0C1 diff --git a/include/Application.h b/include/Application.h index d761fa0..9defe7d 100644 --- a/include/Application.h +++ b/include/Application.h @@ -13,6 +13,9 @@ public: void Run(); void Shutdown(); + bool IsEditorEnabled() const { return enableEditor; } + bool HasScriptCompilationError() const { return scriptCompilationError; } + private: ScriptEngine scriptEngine; HotReload* hotReload; @@ -20,7 +23,7 @@ private: FILE* logFile; GuiManager guiManager; bool enableEditor; - RenderTexture2D renderTexture; // Declare renderTexture for Raylib rendering + RenderTexture2D renderTexture; // renderTexture for Raylib rendering static const int WINDOW_WIDTH = 1280; static const int WINDOW_HEIGHT = 720; diff --git a/include/GuiManager.h b/include/GuiManager.h index a7b9084..c4477df 100644 --- a/include/GuiManager.h +++ b/include/GuiManager.h @@ -5,18 +5,23 @@ #include "gui/fa-solid-900.h" #include "gui/ImGuiNotify.hpp" +class Application; + class GuiManager { public: GuiManager(); ~GuiManager(); - void Initialize(); + void Initialize(Application* application); void Render(RenderTexture2D& renderTexture); void Shutdown(); private: void SetupDockspace(RenderTexture2D& renderTexture); void RenderNotifications(); + void RenderErrorBanner(); + void SetTheme(); bool showLogWindow = true; + Application *app; }; \ No newline at end of file diff --git a/include/gui/Toast.h b/include/gui/Toast.h new file mode 100644 index 0000000..7a0ab92 --- /dev/null +++ b/include/gui/Toast.h @@ -0,0 +1,57 @@ +#pragma once + +#include "gui/ImGuiNotify.hpp" +#include +#include +#include +#include +#include + +class Toast +{ +public: + template + static void Info(std::string_view fmt, Args&&... args) + { + ImGui::InsertNotification({ImGuiToastType::Info, 3000, Format(fmt, std::forward(args)...).c_str()}); + } + + template + static void Warning(std::string_view fmt, Args&&... args) + { + ImGui::InsertNotification({ImGuiToastType::Warning, 4000, Format(fmt, std::forward(args)...).c_str()}); + } + + template + static void Error(std::string_view fmt, Args&&... args) + { + ImGui::InsertNotification({ImGuiToastType::Error, 5000, Format(fmt, std::forward(args)...).c_str()}); + } + + template + static void Success(std::string_view fmt, Args&&... args) + { + ImGui::InsertNotification({ImGuiToastType::Success, 2500, Format(fmt, std::forward(args)...).c_str()}); + } + +private: + template + static std::string Format(std::string_view fmt, Args&&... args) + { + if constexpr (sizeof...(Args) == 0) + { + return std::string(fmt); + } + else + { + std::string format(fmt); + int size = std::snprintf(nullptr, 0, format.c_str(), std::forward(args)...) + 1; + if (size <= 0) + return std::string(fmt); + + std::vector buffer(static_cast(size)); + std::snprintf(buffer.data(), static_cast(size), format.c_str(), std::forward(args)...); + return std::string(buffer.data()); + } + } +}; \ No newline at end of file diff --git a/scripts/as.predefined b/scripts/as.predefined index ba2e406..0b7b961 100644 --- a/scripts/as.predefined +++ b/scripts/as.predefined @@ -1,11 +1,15 @@ - typedef void string; -void Print(const string &in); -void Log(int level, const string &in); -void DrawText(const string &in, int x, int y, int fontSize, int color); +void Print(const string&in); +void Log(int level, const string&in); +void DrawText(const string&in, int x, int y, int fontSize, int color); int LOG_TRACE; int LOG_DEBUG; int LOG_INFO; -int LOG_WARN; +int LOG_WARNING; int LOG_ERROR; int LOG_FATAL; + +namespace Toast { + void Info(const string&in); + void Info(const string&in, const ?&in ...); +} diff --git a/scripts/test.as b/scripts/game.as similarity index 100% rename from scripts/test.as rename to scripts/game.as diff --git a/scripts/update.as b/scripts/update.as index f4a82de..81b075a 100644 --- a/scripts/update.as +++ b/scripts/update.as @@ -2,10 +2,11 @@ float x = 50; float y = 100; void Update(float dt) { - x += 50 * dt; + x += 500 * dt; if (x > 800) { x = 0; Print("X position reset!"); Log(LOG_INFO, "Log INFO: reset happened"); + Toast::Info("X position has been reset."); } } diff --git a/src/Application.cpp b/src/Application.cpp index f8378a4..be4ba14 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -11,6 +11,7 @@ #include "extras/IconsFontAwesome6.h" #include "gui/ImGuiNotify.hpp" #include "GuiManager.h" +#include "gui/Toast.h" #ifdef _WIN32 // On Windows, fopen_s is already available, so no need to define it. @@ -19,7 +20,7 @@ #define fopen_s(pFile, filename, mode) ((*(pFile) = fopen((filename), (mode))) == NULL) #endif const char *Application::WINDOW_TITLE = "Simian"; -const char *Application::SCRIPT_FILE = "scripts/test.as"; +const char *Application::SCRIPT_FILE = "scripts/game.as"; Application::Application() : hotReload(nullptr), scriptCompilationError(false), logFile(nullptr), renderTexture{} // Initialize renderTexture { @@ -27,7 +28,7 @@ Application::Application() : hotReload(nullptr), scriptCompilationError(false), Application::~Application() { - // Reminder in case the change main.cpp, but we do not want to close the + // Reminder in case we change main.cpp, but we do not want to close the // window more than once // Shutdown(); } @@ -68,7 +69,7 @@ bool Application::Initialize(int argc, char *argv[]) } // Initialize hot reload to watch the scripts directory so any script change - // (not just the main file) triggers a reload. + // triggers a reload. { std::filesystem::path p(SCRIPT_FILE); std::string watchPath = p.parent_path().string(); @@ -83,7 +84,7 @@ bool Application::Initialize(int argc, char *argv[]) if (enableEditor) { - guiManager.Initialize(); + guiManager.Initialize(this); renderTexture = LoadRenderTexture(WINDOW_WIDTH, WINDOW_HEIGHT); } @@ -116,6 +117,9 @@ void Application::Update(float deltaTime) if (!success) { log_warn("Script compilation failed - keeping previous version"); + Toast::Warning("Script compilation failed - check console for details."); + } else { + Toast::Success("Script reloaded successfully."); } } @@ -144,12 +148,6 @@ void Application::Draw() BeginTextureMode(renderTexture); ClearBackground(RAYWHITE); - // Show script error status in top left if there's an error, otherwise show normal message - if (scriptCompilationError) - { - DrawText("SCRIPT ERROR - Check console for details", 10, 10, 16, RED); - } - // Call script Draw function scriptEngine.CallScriptFunction(scriptEngine.GetDrawFunction()); diff --git a/src/GuiManager.cpp b/src/GuiManager.cpp index 95b0734..2bf66bf 100644 --- a/src/GuiManager.cpp +++ b/src/GuiManager.cpp @@ -4,12 +4,15 @@ #include #include #include "log.h" +#include "Application.h" + GuiManager::GuiManager() {} GuiManager::~GuiManager() {} -void GuiManager::Initialize() +void GuiManager::Initialize(Application *application) { + app = application; rlImGuiSetup(true); ImGuiIO &io = ImGui::GetIO(); io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; @@ -25,6 +28,7 @@ void GuiManager::Initialize() iconsConfig.PixelSnapH = true; iconsConfig.GlyphMinAdvanceX = iconFontSize; io.Fonts->AddFontFromMemoryCompressedTTF(fa_solid_900_compressed_data, fa_solid_900_compressed_size, iconFontSize, &iconsConfig, iconsRanges); + SetTheme(); log_trace("GuiManager::Initialize - Started"); } @@ -32,6 +36,8 @@ void GuiManager::Render(RenderTexture2D &renderTexture) { rlImGuiBegin(); SetupDockspace(renderTexture); // Pass the renderTexture parameter + + RenderErrorBanner(); RenderNotifications(); rlImGuiEnd(); @@ -124,6 +130,21 @@ void GuiManager::SetupDockspace(RenderTexture2D &renderTexture) ImGui::End(); } +void GuiManager::RenderErrorBanner() +{ + if (!app || !app->HasScriptCompilationError()) + return; + + ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f)); + ImGui::SetNextWindowSize(ImVec2(ImGui::GetIO().DisplaySize.x, 0.0f)); + ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | + ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoSavedSettings; + ImGui::Begin("PersistentBanner", nullptr, flags); + ImGui::TextColored(ImVec4(1, 0.4f, 0.4f, 1), ICON_FA_TRIANGLE_EXCLAMATION " Script Compilation Error - running last working version"); + ImGui::End(); +} + void GuiManager::RenderNotifications() { ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.f); @@ -134,4 +155,90 @@ void GuiManager::RenderNotifications() ImGui::PopStyleVar(2); ImGui::PopStyleColor(1); -} \ No newline at end of file +} + +void GuiManager::SetTheme() +{ + + ImVec4 *colors = ImGui::GetStyle().Colors; + colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); + colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f); + colors[ImGuiCol_WindowBg] = ImVec4(0.10f, 0.10f, 0.10f, 1.00f); + colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); + colors[ImGuiCol_PopupBg] = ImVec4(0.19f, 0.19f, 0.19f, 0.92f); + colors[ImGuiCol_Border] = ImVec4(0.19f, 0.19f, 0.19f, 0.29f); + colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.24f); + colors[ImGuiCol_FrameBg] = ImVec4(0.05f, 0.05f, 0.05f, 0.54f); + colors[ImGuiCol_FrameBgHovered] = ImVec4(0.19f, 0.19f, 0.19f, 0.54f); + colors[ImGuiCol_FrameBgActive] = ImVec4(0.20f, 0.22f, 0.23f, 1.00f); + colors[ImGuiCol_TitleBg] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); + colors[ImGuiCol_TitleBgActive] = ImVec4(0.06f, 0.06f, 0.06f, 1.00f); + colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); + colors[ImGuiCol_MenuBarBg] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f); + colors[ImGuiCol_ScrollbarBg] = ImVec4(0.05f, 0.05f, 0.05f, 0.54f); + colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.34f, 0.34f, 0.34f, 0.54f); + colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.40f, 0.40f, 0.40f, 0.54f); + colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.56f, 0.56f, 0.56f, 0.54f); + colors[ImGuiCol_CheckMark] = ImVec4(0.33f, 0.67f, 0.86f, 1.00f); + colors[ImGuiCol_SliderGrab] = ImVec4(0.34f, 0.34f, 0.34f, 0.54f); + colors[ImGuiCol_SliderGrabActive] = ImVec4(0.56f, 0.56f, 0.56f, 0.54f); + colors[ImGuiCol_Button] = ImVec4(0.05f, 0.05f, 0.05f, 0.54f); + colors[ImGuiCol_ButtonHovered] = ImVec4(0.19f, 0.19f, 0.19f, 0.54f); + colors[ImGuiCol_ButtonActive] = ImVec4(0.20f, 0.22f, 0.23f, 1.00f); + colors[ImGuiCol_Header] = ImVec4(0.00f, 0.00f, 0.00f, 0.52f); + colors[ImGuiCol_HeaderHovered] = ImVec4(0.00f, 0.00f, 0.00f, 0.36f); + colors[ImGuiCol_HeaderActive] = ImVec4(0.20f, 0.22f, 0.23f, 0.33f); + colors[ImGuiCol_Separator] = ImVec4(0.28f, 0.28f, 0.28f, 0.29f); + colors[ImGuiCol_SeparatorHovered] = ImVec4(0.44f, 0.44f, 0.44f, 0.29f); + colors[ImGuiCol_SeparatorActive] = ImVec4(0.40f, 0.44f, 0.47f, 1.00f); + colors[ImGuiCol_ResizeGrip] = ImVec4(0.28f, 0.28f, 0.28f, 0.29f); + colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.44f, 0.44f, 0.44f, 0.29f); + colors[ImGuiCol_ResizeGripActive] = ImVec4(0.40f, 0.44f, 0.47f, 1.00f); + colors[ImGuiCol_Tab] = ImVec4(0.00f, 0.00f, 0.00f, 0.52f); + colors[ImGuiCol_TabHovered] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f); + colors[ImGuiCol_TabActive] = ImVec4(0.20f, 0.20f, 0.20f, 0.36f); + colors[ImGuiCol_TabUnfocused] = ImVec4(0.00f, 0.00f, 0.00f, 0.52f); + colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f); + // colors[ImGuiCol_DockingPreview] = ImVec4(0.33f, 0.67f, 0.86f, 1.00f); + // colors[ImGuiCol_DockingEmptyBg] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + colors[ImGuiCol_PlotHistogram] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + colors[ImGuiCol_TableHeaderBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.52f); + colors[ImGuiCol_TableBorderStrong] = ImVec4(0.00f, 0.00f, 0.00f, 0.52f); + colors[ImGuiCol_TableBorderLight] = ImVec4(0.28f, 0.28f, 0.28f, 0.29f); + colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); + colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f); + colors[ImGuiCol_TextSelectedBg] = ImVec4(0.20f, 0.22f, 0.23f, 1.00f); + colors[ImGuiCol_DragDropTarget] = ImVec4(0.33f, 0.67f, 0.86f, 1.00f); + colors[ImGuiCol_NavHighlight] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 0.00f, 0.00f, 0.70f); + colors[ImGuiCol_NavWindowingDimBg] = ImVec4(1.00f, 0.00f, 0.00f, 0.20f); + colors[ImGuiCol_ModalWindowDimBg] = ImVec4(1.00f, 0.00f, 0.00f, 0.35f); + colors[ImGuiCol_CheckMark] = ImVec4(0.33f, 0.67f, 0.86f, 1.00f); + + ImGuiStyle &style = ImGui::GetStyle(); + style.WindowPadding = ImVec2(8.00f, 8.00f); + style.FramePadding = ImVec2(5.00f, 2.00f); + style.CellPadding = ImVec2(6.00f, 6.00f); + style.ItemSpacing = ImVec2(6.00f, 6.00f); + style.ItemInnerSpacing = ImVec2(6.00f, 6.00f); + style.TouchExtraPadding = ImVec2(0.00f, 0.00f); + style.IndentSpacing = 25; + style.ScrollbarSize = 15; + style.GrabMinSize = 10; + style.WindowBorderSize = 1; + style.ChildBorderSize = 1; + style.PopupBorderSize = 1; + style.FrameBorderSize = 1; + style.TabBorderSize = 1; + style.WindowRounding = 7; + style.ChildRounding = 4; + style.FrameRounding = 3; + style.PopupRounding = 4; + style.ScrollbarRounding = 9; + style.GrabRounding = 3; + style.LogSliderDeadzone = 4; + style.TabRounding = 4; +} diff --git a/src/ScriptBindings.cpp b/src/ScriptBindings.cpp index d715063..d1fb0d3 100644 --- a/src/ScriptBindings.cpp +++ b/src/ScriptBindings.cpp @@ -3,6 +3,7 @@ #include #include #include "log/log.h" +#include "gui/Toast.h" // Expose log level constants to AngelScript static int AS_LOG_TRACE = 1; @@ -12,76 +13,195 @@ static int AS_LOG_WARNING = 4; static int AS_LOG_ERROR = 5; static int AS_LOG_FATAL = 6; -void ScriptBindings::RegisterAll(asIScriptEngine* engine) { +void ToastInfo_Generic(asIScriptGeneric* gen) +{ + // First argument is the format string + std::string fmt = *reinterpret_cast(gen->GetArgAddress(0)); + + // Handle variadic arguments + int argCount = gen->GetArgCount(); + std::vector args; + + for (int i = 1; i < argCount; ++i) + { + // For simplicity, assume all extra args are strings + std::string arg = *reinterpret_cast(gen->GetArgAddress(i)); + args.push_back(arg); + } + + // Simple formatting: replace %s sequentially + std::string result = fmt; + size_t pos = 0; + for (const auto& a : args) + { + pos = result.find("%s", pos); + if (pos == std::string::npos) break; + result.replace(pos, 2, a); + pos += a.size(); + } + + Toast::Info(result); +} + +void ToastWarning_Generic(asIScriptGeneric* gen) +{ + std::string fmt = *reinterpret_cast(gen->GetArgAddress(0)); + int argCount = gen->GetArgCount(); + std::vector args; + + for (int i = 1; i < argCount; ++i) + args.push_back(*reinterpret_cast(gen->GetArgAddress(i))); + + std::string result = fmt; + size_t pos = 0; + for (const auto& a : args) + { + pos = result.find("%s", pos); + if (pos == std::string::npos) break; + result.replace(pos, 2, a); + pos += a.size(); + } + + Toast::Warning(result); +} + +void ToastError_Generic(asIScriptGeneric* gen) +{ + std::string fmt = *reinterpret_cast(gen->GetArgAddress(0)); + int argCount = gen->GetArgCount(); + std::vector args; + + for (int i = 1; i < argCount; ++i) + args.push_back(*reinterpret_cast(gen->GetArgAddress(i))); + + std::string result = fmt; + size_t pos = 0; + for (const auto& a : args) + { + pos = result.find("%s", pos); + if (pos == std::string::npos) break; + result.replace(pos, 2, a); + pos += a.size(); + } + + Toast::Error(result); +} + +void ToastSuccess_Generic(asIScriptGeneric* gen) +{ + std::string fmt = *reinterpret_cast(gen->GetArgAddress(0)); + int argCount = gen->GetArgCount(); + std::vector args; + + for (int i = 1; i < argCount; ++i) + args.push_back(*reinterpret_cast(gen->GetArgAddress(i))); + + std::string result = fmt; + size_t pos = 0; + for (const auto& a : args) + { + pos = result.find("%s", pos); + if (pos == std::string::npos) break; + result.replace(pos, 2, a); + pos += a.size(); + } + + Toast::Success(result); +} + + +void ScriptBindings::RegisterAll(asIScriptEngine *engine) +{ // Register Print function with generic calling convention to access context - int r = engine->RegisterGlobalFunction("void Print(const string &in)", - asFUNCTION(Print), asCALL_GENERIC); + int r = engine->RegisterGlobalFunction("void Print(const string &in)", + asFUNCTION(Print), asCALL_GENERIC); assert(r >= 0); // Register log level constants so scripts can refer to LOG_INFO, LOG_ERROR, etc. - r = engine->RegisterGlobalProperty("int LOG_TRACE", (void*)&AS_LOG_TRACE); + r = engine->RegisterGlobalProperty("int LOG_TRACE", (void *)&AS_LOG_TRACE); assert(r >= 0); - r = engine->RegisterGlobalProperty("int LOG_DEBUG", (void*)&AS_LOG_DEBUG); + r = engine->RegisterGlobalProperty("int LOG_DEBUG", (void *)&AS_LOG_DEBUG); assert(r >= 0); - r = engine->RegisterGlobalProperty("int LOG_INFO", (void*)&AS_LOG_INFO); + r = engine->RegisterGlobalProperty("int LOG_INFO", (void *)&AS_LOG_INFO); assert(r >= 0); - r = engine->RegisterGlobalProperty("int LOG_WARNING", (void*)&AS_LOG_WARNING); + r = engine->RegisterGlobalProperty("int LOG_WARNING", (void *)&AS_LOG_WARNING); assert(r >= 0); - r = engine->RegisterGlobalProperty("int LOG_ERROR", (void*)&AS_LOG_ERROR); + r = engine->RegisterGlobalProperty("int LOG_ERROR", (void *)&AS_LOG_ERROR); assert(r >= 0); - r = engine->RegisterGlobalProperty("int LOG_FATAL", (void*)&AS_LOG_FATAL); + r = engine->RegisterGlobalProperty("int LOG_FATAL", (void *)&AS_LOG_FATAL); assert(r >= 0); // Register Log(level, message) to allow scripts to log at different levels r = engine->RegisterGlobalFunction("void Log(int, const string &in)", - asFUNCTION(Log), asCALL_GENERIC); + asFUNCTION(Log), asCALL_GENERIC); assert(r >= 0); + // Toast functions + engine->SetDefaultNamespace("Toast"); + r = engine->RegisterGlobalFunction("void Info(const string &in, const ?&in ...)", + asFUNCTION(ToastInfo_Generic), asCALL_GENERIC); + assert(r >= 0); + r = engine->RegisterGlobalFunction("void Info(const string &in)", + asFUNCTION(ToastInfo_Generic), asCALL_GENERIC); + assert(r >= 0); + engine->SetDefaultNamespace(""); + + // Register DrawText function r = engine->RegisterGlobalFunction("void DrawText(const string &in, int, int, int, uint)", - asFUNCTION(AS_DrawText), asCALL_CDECL); + asFUNCTION(AS_DrawText), asCALL_CDECL); assert(r >= 0); } -void ScriptBindings::Print(asIScriptGeneric* gen) { +void ScriptBindings::Print(asIScriptGeneric *gen) +{ // Get the message parameter - std::string* msg = static_cast(gen->GetArgObject(0)); - + std::string *msg = static_cast(gen->GetArgObject(0)); + // Get the active context to retrieve script file and line number - asIScriptContext* ctx = asGetActiveContext(); - - if (ctx) { - const char* section = nullptr; + asIScriptContext *ctx = asGetActiveContext(); + + if (ctx) + { + const char *section = nullptr; int line = 0; - + // Get the current line number and script section line = ctx->GetLineNumber(0, nullptr, §ion); - - if (section && line > 0) { + + if (section && line > 0) + { // Log with script file and line number log_log(LOG_INFO, section, line, "%s", msg->c_str()); - } else { + } + else + { // Fallback if we can't get the info log_info("%s", msg->c_str()); } - } else { + } + else + { // No context available, just log normally log_info("%s", msg->c_str()); } } -void ScriptBindings::Log(asIScriptGeneric* gen) { +void ScriptBindings::Log(asIScriptGeneric *gen) +{ // arg0: int level, arg1: string message int level = gen->GetArgDWord(0); - std::string* msg = static_cast(gen->GetArgObject(1)); + std::string *msg = static_cast(gen->GetArgObject(1)); // Obtain script context to fetch file/line - asIScriptContext* ctx = asGetActiveContext(); - if (ctx) { - const char* section = nullptr; + asIScriptContext *ctx = asGetActiveContext(); + if (ctx) + { + const char *section = nullptr; int line = 0; line = ctx->GetLineNumber(0, nullptr, §ion); - if (section && line > 0) { + if (section && line > 0) + { // Use the library's log_log to preserve filename/line info log_log(level, section, line, "%s", msg ? msg->c_str() : ""); return; @@ -89,20 +209,32 @@ void ScriptBindings::Log(asIScriptGeneric* gen) { } // Fallback: log without script location - switch (level) { - case LOG_TRACE: log_trace("%s", msg ? msg->c_str() : ""); break; - case LOG_DEBUG: log_debug("%s", msg ? msg->c_str() : ""); break; - case LOG_WARNING: log_warn("%s", msg ? msg->c_str() : ""); break; - case LOG_ERROR: log_error("%s", msg ? msg->c_str() : ""); break; - case LOG_FATAL: log_fatal("%s", msg ? msg->c_str() : ""); break; - case LOG_INFO: - default: - log_info("%s", msg ? msg->c_str() : ""); - break; + switch (level) + { + case LOG_TRACE: + log_trace("%s", msg ? msg->c_str() : ""); + break; + case LOG_DEBUG: + log_debug("%s", msg ? msg->c_str() : ""); + break; + case LOG_WARNING: + log_warn("%s", msg ? msg->c_str() : ""); + break; + case LOG_ERROR: + log_error("%s", msg ? msg->c_str() : ""); + break; + case LOG_FATAL: + log_fatal("%s", msg ? msg->c_str() : ""); + break; + case LOG_INFO: + default: + log_info("%s", msg ? msg->c_str() : ""); + break; } } -Color ColorFromUInt(unsigned int c) { +Color ColorFromUInt(unsigned int c) +{ Color col; col.r = (c >> 24) & 0xFF; col.g = (c >> 16) & 0xFF; @@ -111,6 +243,7 @@ Color ColorFromUInt(unsigned int c) { return col; } -void ScriptBindings::AS_DrawText(const std::string &text, int x, int y, int fontSize, unsigned int color) { +void ScriptBindings::AS_DrawText(const std::string &text, int x, int y, int fontSize, unsigned int color) +{ DrawText(text.c_str(), x, y, fontSize, ColorFromUInt(color)); } \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a50434e..3dd2033 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -17,6 +17,9 @@ target_include_directories(unit_tests PRIVATE ${CMAKE_SOURCE_DIR}/external/angelscript/sdk/add_on/scriptstdstring ${CMAKE_SOURCE_DIR}/external/angelscript/sdk/add_on/scriptbuilder ${CMAKE_SOURCE_DIR}/external/raylib/src + ${CMAKE_SOURCE_DIR}/external/imgui + ${CMAKE_SOURCE_DIR}/external/rlImGui + ) # Link AngelScript and its add-ons needed by ScriptEngine @@ -25,6 +28,7 @@ target_link_libraries(unit_tests PRIVATE scriptstdstring scriptbuilder raylib + imgui ) add_test(NAME unit_tests COMMAND unit_tests) \ No newline at end of file From 2ed2a5c953bfdb8716406394f83bffc4912d9859 Mon Sep 17 00:00:00 2001 From: Nick Koirala Date: Fri, 14 Nov 2025 15:06:08 +1300 Subject: [PATCH 4/9] chore: build in CI pipeline --- .gitea/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 538e457..1f9d786 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -34,3 +34,6 @@ jobs: run: | cd build ctest --output-on-failure -j $(nproc) + + - name: Build application + run: cmake --build build --target simian -j $(nproc) From dcc21f2ba2f2d64ecebedec3afeb6401f6f594ff Mon Sep 17 00:00:00 2001 From: Nick Koirala Date: Fri, 14 Nov 2025 15:42:41 +1300 Subject: [PATCH 5/9] fix: forget arargs for toasts - too complex, will implement string formatting in AngelScript instead --- imgui.ini | 5 ++ scripts/as.predefined | 4 +- src/ScriptBindings.cpp | 110 +++++++---------------------------------- 3 files changed, 27 insertions(+), 92 deletions(-) diff --git a/imgui.ini b/imgui.ini index 187746c..4dcc263 100644 --- a/imgui.ini +++ b/imgui.ini @@ -30,6 +30,11 @@ Pos=1062,576 Size=198,57 Collapsed=0 +[Window][##TOAST2] +Pos=1062,509 +Size=198,57 +Collapsed=0 + [Docking][Data] DockSpace ID=0x9076BACA Window=0x34F970D7 Pos=8,25 Size=1264,687 Split=Y Selected=0x27A02DAA DockNode ID=0x00000001 Parent=0x9076BACA SizeRef=1264,579 CentralNode=1 Selected=0x27A02DAA diff --git a/scripts/as.predefined b/scripts/as.predefined index 0b7b961..1b40eb8 100644 --- a/scripts/as.predefined +++ b/scripts/as.predefined @@ -11,5 +11,7 @@ int LOG_FATAL; namespace Toast { void Info(const string&in); - void Info(const string&in, const ?&in ...); + void Warning(const string&in); + void Error(const string&in); + void Success(const string&in); } diff --git a/src/ScriptBindings.cpp b/src/ScriptBindings.cpp index d1fb0d3..5d149c0 100644 --- a/src/ScriptBindings.cpp +++ b/src/ScriptBindings.cpp @@ -13,107 +13,29 @@ static int AS_LOG_WARNING = 4; static int AS_LOG_ERROR = 5; static int AS_LOG_FATAL = 6; -void ToastInfo_Generic(asIScriptGeneric* gen) +static void AS_ToastInfo(const std::string& message) { - // First argument is the format string - std::string fmt = *reinterpret_cast(gen->GetArgAddress(0)); - - // Handle variadic arguments - int argCount = gen->GetArgCount(); - std::vector args; - - for (int i = 1; i < argCount; ++i) - { - // For simplicity, assume all extra args are strings - std::string arg = *reinterpret_cast(gen->GetArgAddress(i)); - args.push_back(arg); - } - - // Simple formatting: replace %s sequentially - std::string result = fmt; - size_t pos = 0; - for (const auto& a : args) - { - pos = result.find("%s", pos); - if (pos == std::string::npos) break; - result.replace(pos, 2, a); - pos += a.size(); - } - - Toast::Info(result); + Toast::Info(message); } -void ToastWarning_Generic(asIScriptGeneric* gen) +static void AS_ToastWarning(const std::string& message) { - std::string fmt = *reinterpret_cast(gen->GetArgAddress(0)); - int argCount = gen->GetArgCount(); - std::vector args; - - for (int i = 1; i < argCount; ++i) - args.push_back(*reinterpret_cast(gen->GetArgAddress(i))); - - std::string result = fmt; - size_t pos = 0; - for (const auto& a : args) - { - pos = result.find("%s", pos); - if (pos == std::string::npos) break; - result.replace(pos, 2, a); - pos += a.size(); - } - - Toast::Warning(result); + Toast::Warning(message); } -void ToastError_Generic(asIScriptGeneric* gen) +static void AS_ToastError(const std::string& message) { - std::string fmt = *reinterpret_cast(gen->GetArgAddress(0)); - int argCount = gen->GetArgCount(); - std::vector args; - - for (int i = 1; i < argCount; ++i) - args.push_back(*reinterpret_cast(gen->GetArgAddress(i))); - - std::string result = fmt; - size_t pos = 0; - for (const auto& a : args) - { - pos = result.find("%s", pos); - if (pos == std::string::npos) break; - result.replace(pos, 2, a); - pos += a.size(); - } - - Toast::Error(result); + Toast::Error(message); } -void ToastSuccess_Generic(asIScriptGeneric* gen) +static void AS_ToastSuccess(const std::string& message) { - std::string fmt = *reinterpret_cast(gen->GetArgAddress(0)); - int argCount = gen->GetArgCount(); - std::vector args; - - for (int i = 1; i < argCount; ++i) - args.push_back(*reinterpret_cast(gen->GetArgAddress(i))); - - std::string result = fmt; - size_t pos = 0; - for (const auto& a : args) - { - pos = result.find("%s", pos); - if (pos == std::string::npos) break; - result.replace(pos, 2, a); - pos += a.size(); - } - - Toast::Success(result); + Toast::Success(message); } - - void ScriptBindings::RegisterAll(asIScriptEngine *engine) { // Register Print function with generic calling convention to access context - int r = engine->RegisterGlobalFunction("void Print(const string &in)", + [[maybe_unused]] int r = engine->RegisterGlobalFunction("void Print(const string &in)", asFUNCTION(Print), asCALL_GENERIC); assert(r >= 0); @@ -138,11 +60,17 @@ void ScriptBindings::RegisterAll(asIScriptEngine *engine) // Toast functions engine->SetDefaultNamespace("Toast"); - r = engine->RegisterGlobalFunction("void Info(const string &in, const ?&in ...)", - asFUNCTION(ToastInfo_Generic), asCALL_GENERIC); - assert(r >= 0); r = engine->RegisterGlobalFunction("void Info(const string &in)", - asFUNCTION(ToastInfo_Generic), asCALL_GENERIC); + asFUNCTION(AS_ToastInfo), asCALL_CDECL); + assert(r >= 0); + r = engine->RegisterGlobalFunction("void Warning(const string &in)", + asFUNCTION(AS_ToastWarning), asCALL_CDECL); + assert(r >= 0); + r = engine->RegisterGlobalFunction("void Error(const string &in)", + asFUNCTION(AS_ToastError), asCALL_CDECL); + assert(r >= 0); + r = engine->RegisterGlobalFunction("void Success(const string &in)", + asFUNCTION(AS_ToastSuccess), asCALL_CDECL); assert(r >= 0); engine->SetDefaultNamespace(""); From 26bcfac7d162d0ca71baaa347a263444f233207c Mon Sep 17 00:00:00 2001 From: Nick Koirala Date: Mon, 17 Nov 2025 19:14:27 +1300 Subject: [PATCH 6/9] feat: attempts to fix logging issue --- .vscode/settings.json | 20 +++++++++++- imgui.ini | 26 ++++++++-------- scripts/update.as | 2 +- src/GuiManager.cpp | 72 +++++++++++++++++++++++++++++++++++++------ 4 files changed, 95 insertions(+), 25 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 6715a50..64d298f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -72,6 +72,24 @@ "typeinfo": "cpp", "variant": "cpp", "format": "cpp", - "*.m": "cpp" + "*.m": "cpp", + "filesystem": "cpp", + "forward_list": "cpp", + "ios": "cpp", + "locale": "cpp", + "xfacet": "cpp", + "xhash": "cpp", + "xiosbase": "cpp", + "xlocale": "cpp", + "xlocbuf": "cpp", + "xlocinfo": "cpp", + "xlocmes": "cpp", + "xlocmon": "cpp", + "xlocnum": "cpp", + "xloctime": "cpp", + "xmemory": "cpp", + "xtr1common": "cpp", + "xtree": "cpp", + "xutility": "cpp" } } \ No newline at end of file diff --git a/imgui.ini b/imgui.ini index 4dcc263..fecbc7a 100644 --- a/imgui.ini +++ b/imgui.ini @@ -9,34 +9,34 @@ Size=400,400 Collapsed=0 [Window][Game Window] -Pos=8,25 -Size=1264,581 +Pos=8,29 +Size=1264,360 Collapsed=0 DockId=0x00000001,0 [Window][Log Viewer] -Pos=8,608 -Size=1264,104 +Pos=8,391 +Size=1264,321 Collapsed=0 DockId=0x00000002,0 [Window][##TOAST0] -Pos=1062,643 -Size=198,57 +Pos=1010,635 +Size=250,65 Collapsed=0 [Window][##TOAST1] -Pos=1062,576 -Size=198,57 +Pos=1010,560 +Size=250,65 Collapsed=0 [Window][##TOAST2] -Pos=1062,509 -Size=198,57 +Pos=1010,485 +Size=250,65 Collapsed=0 [Docking][Data] -DockSpace ID=0x9076BACA Window=0x34F970D7 Pos=8,25 Size=1264,687 Split=Y Selected=0x27A02DAA - DockNode ID=0x00000001 Parent=0x9076BACA SizeRef=1264,579 CentralNode=1 Selected=0x27A02DAA - DockNode ID=0x00000002 Parent=0x9076BACA SizeRef=1264,104 Selected=0xBEDDA0C1 +DockSpace ID=0x9076BACA Window=0x34F970D7 Pos=8,29 Size=1264,683 Split=Y Selected=0x27A02DAA + DockNode ID=0x00000001 Parent=0x9076BACA SizeRef=1264,360 CentralNode=1 Selected=0x27A02DAA + DockNode ID=0x00000002 Parent=0x9076BACA SizeRef=1264,321 Selected=0xBEDDA0C1 diff --git a/scripts/update.as b/scripts/update.as index 81b075a..9ab5d7d 100644 --- a/scripts/update.as +++ b/scripts/update.as @@ -2,7 +2,7 @@ float x = 50; float y = 100; void Update(float dt) { - x += 500 * dt; + x += 640 * dt; if (x > 800) { x = 0; Print("X position reset!"); diff --git a/src/GuiManager.cpp b/src/GuiManager.cpp index 2bf66bf..92018d8 100644 --- a/src/GuiManager.cpp +++ b/src/GuiManager.cpp @@ -5,6 +5,14 @@ #include #include "log.h" #include "Application.h" +#include // Add this for cross-platform file handling + +#ifdef _WIN32 +#include +#include +#include +#undef ShowCursor // Prevent conflict with Windows API +#endif GuiManager::GuiManager() {} @@ -103,19 +111,63 @@ void GuiManager::SetupDockspace(RenderTexture2D &renderTexture) static std::string logContent; static size_t lastFileSize = 0; - // Read the log file if it has changed - std::ifstream logFile("log.txt", std::ios::ate); // Open at the end to get the file size - if (logFile.is_open()) + // Use std::filesystem to construct the log file path + std::filesystem::path logFilePath = std::filesystem::current_path() / "log.txt"; + + // Debugging: Print the log file path to the console + std::cout << "Log file path: " << logFilePath << std::endl; + + // Check if the log file exists + if (std::filesystem::exists(logFilePath)) { - size_t fileSize = logFile.tellg(); - if (fileSize != lastFileSize) +#ifdef _WIN32 + // Convert the path to a narrow-character string + std::string logFilePathStr = logFilePath.string(); + + // Open the file with shared access on Windows + HANDLE hFile = CreateFileA(logFilePathStr.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile != INVALID_HANDLE_VALUE) { - lastFileSize = fileSize; - logFile.seekg(0, std::ios::beg); // Go back to the beginning - logContent.assign((std::istreambuf_iterator(logFile)), - std::istreambuf_iterator()); + LARGE_INTEGER fileSize; + if (GetFileSizeEx(hFile, &fileSize) && fileSize.QuadPart != lastFileSize) + { + lastFileSize = static_cast(fileSize.QuadPart); + logContent.resize(lastFileSize); + + DWORD bytesRead; + SetFilePointer(hFile, 0, NULL, FILE_BEGIN); + ReadFile(hFile, logContent.data(), static_cast(lastFileSize), &bytesRead, NULL); + } + CloseHandle(hFile); } - logFile.close(); + else + { + std::cerr << "Failed to open log file with shared access: " << logFilePath << std::endl; + } +#else + // Standard file reading for non-Windows platforms + std::ifstream logFile(logFilePath, std::ios::ate); + if (logFile.is_open()) + { + size_t fileSize = logFile.tellg(); + if (fileSize != lastFileSize) + { + lastFileSize = fileSize; + logFile.seekg(0, std::ios::beg); + logContent.assign((std::istreambuf_iterator(logFile)), + std::istreambuf_iterator()); + } + logFile.close(); + } + else + { + std::cerr << "Failed to open log file: " << logFilePath << std::endl; + } +#endif + } + else + { + std::cerr << "Log file does not exist: " << logFilePath << std::endl; } // Display the log content in a scrollable text area From 2ae078a82bca348958d22144744e3688333c3aad Mon Sep 17 00:00:00 2001 From: Nick Koirala Date: Mon, 17 Nov 2025 19:43:14 +1300 Subject: [PATCH 7/9] fix: log window is now working correctly --- imgui.ini | 10 ++-- include/GuiManager.h | 8 ++- scripts/update.as | 2 +- src/GuiManager.cpp | 122 ++++++++++++++++++------------------------- 4 files changed, 64 insertions(+), 78 deletions(-) diff --git a/imgui.ini b/imgui.ini index fecbc7a..d4c8a11 100644 --- a/imgui.ini +++ b/imgui.ini @@ -21,18 +21,18 @@ Collapsed=0 DockId=0x00000002,0 [Window][##TOAST0] -Pos=1010,635 -Size=250,65 +Pos=982,635 +Size=278,65 Collapsed=0 [Window][##TOAST1] -Pos=1010,560 +Pos=1010,543 Size=250,65 Collapsed=0 [Window][##TOAST2] -Pos=1010,485 -Size=250,65 +Pos=982,485 +Size=278,65 Collapsed=0 [Docking][Data] diff --git a/include/GuiManager.h b/include/GuiManager.h index c4477df..8185a25 100644 --- a/include/GuiManager.h +++ b/include/GuiManager.h @@ -6,6 +6,11 @@ #include "gui/ImGuiNotify.hpp" class Application; +struct LogEntry { + std::string message; + ImVec4 color; +}; + class GuiManager { public: @@ -15,13 +20,14 @@ public: void Initialize(Application* application); void Render(RenderTexture2D& renderTexture); void Shutdown(); - + std::vector logEntries; private: void SetupDockspace(RenderTexture2D& renderTexture); void RenderNotifications(); void RenderErrorBanner(); void SetTheme(); + bool showLogWindow = true; Application *app; }; \ No newline at end of file diff --git a/scripts/update.as b/scripts/update.as index 9ab5d7d..237741a 100644 --- a/scripts/update.as +++ b/scripts/update.as @@ -2,7 +2,7 @@ float x = 50; float y = 100; void Update(float dt) { - x += 640 * dt; + x += 64 * dt; if (x > 800) { x = 0; Print("X position reset!"); diff --git a/src/GuiManager.cpp b/src/GuiManager.cpp index 92018d8..9971a8d 100644 --- a/src/GuiManager.cpp +++ b/src/GuiManager.cpp @@ -7,17 +7,50 @@ #include "Application.h" #include // Add this for cross-platform file handling -#ifdef _WIN32 -#include -#include -#include -#undef ShowCursor // Prevent conflict with Windows API -#endif - GuiManager::GuiManager() {} GuiManager::~GuiManager() {} +void LogCallback(log_Event *ev) +{ + // Retrieve the GuiManager instance from udata + GuiManager *guiManager = static_cast(ev->udata); + + // Format the log message + char buffer[1024]; + vsnprintf(buffer, sizeof(buffer), ev->fmt, ev->ap); + + // Determine the color based on the log level + ImVec4 color; + switch (ev->level) + { + case LOG_TRACE: + color = ImVec4(0.5f, 0.5f, 0.5f, 1.0f); + break; // Gray + case LOG_DEBUG: + color = ImVec4(0.0f, 0.5f, 1.0f, 1.0f); + break; // Blue + case LOG_INFO: + color = ImVec4(0.0f, 1.0f, 0.0f, 1.0f); + break; // Green + case LOG_WARNING: + color = ImVec4(1.0f, 1.0f, 0.0f, 1.0f); + break; // Yellow + case LOG_ERROR: + color = ImVec4(1.0f, 0.0f, 0.0f, 1.0f); + break; // Red + case LOG_FATAL: + color = ImVec4(1.0f, 0.0f, 1.0f, 1.0f); + break; // Magenta + default: + color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); + break; // White + } + + // Add the log entry to the vector + guiManager->logEntries.push_back({buffer, color}); +} + void GuiManager::Initialize(Application *application) { app = application; @@ -37,6 +70,9 @@ void GuiManager::Initialize(Application *application) iconsConfig.GlyphMinAdvanceX = iconFontSize; io.Fonts->AddFontFromMemoryCompressedTTF(fa_solid_900_compressed_data, fa_solid_900_compressed_size, iconFontSize, &iconsConfig, iconsRanges); SetTheme(); + logEntries.clear(); + log_add_callback(LogCallback, this, LOG_ALL); + log_trace("GuiManager::Initialize - Started"); } @@ -108,73 +144,17 @@ void GuiManager::SetupDockspace(RenderTexture2D &renderTexture) { // Log Viewer Window ImGui::Begin("Log Viewer"); - static std::string logContent; - static size_t lastFileSize = 0; - - // Use std::filesystem to construct the log file path - std::filesystem::path logFilePath = std::filesystem::current_path() / "log.txt"; - - // Debugging: Print the log file path to the console - std::cout << "Log file path: " << logFilePath << std::endl; - - // Check if the log file exists - if (std::filesystem::exists(logFilePath)) - { -#ifdef _WIN32 - // Convert the path to a narrow-character string - std::string logFilePathStr = logFilePath.string(); - - // Open the file with shared access on Windows - HANDLE hFile = CreateFileA(logFilePathStr.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile != INVALID_HANDLE_VALUE) - { - LARGE_INTEGER fileSize; - if (GetFileSizeEx(hFile, &fileSize) && fileSize.QuadPart != lastFileSize) - { - lastFileSize = static_cast(fileSize.QuadPart); - logContent.resize(lastFileSize); - - DWORD bytesRead; - SetFilePointer(hFile, 0, NULL, FILE_BEGIN); - ReadFile(hFile, logContent.data(), static_cast(lastFileSize), &bytesRead, NULL); - } - CloseHandle(hFile); - } - else - { - std::cerr << "Failed to open log file with shared access: " << logFilePath << std::endl; - } -#else - // Standard file reading for non-Windows platforms - std::ifstream logFile(logFilePath, std::ios::ate); - if (logFile.is_open()) - { - size_t fileSize = logFile.tellg(); - if (fileSize != lastFileSize) - { - lastFileSize = fileSize; - logFile.seekg(0, std::ios::beg); - logContent.assign((std::istreambuf_iterator(logFile)), - std::istreambuf_iterator()); - } - logFile.close(); - } - else - { - std::cerr << "Failed to open log file: " << logFilePath << std::endl; - } -#endif - } - else - { - std::cerr << "Log file does not exist: " << logFilePath << std::endl; - } - - // Display the log content in a scrollable text area ImGui::BeginChild("LogText", ImVec2(0, 0), true, ImGuiWindowFlags_HorizontalScrollbar); - ImGui::TextUnformatted(logContent.c_str()); + + // Display each log entry with its color + for (const auto &entry : logEntries) + { + ImGui::TextColored(entry.color, "%s", entry.message.c_str()); + } + if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) ImGui::SetScrollHereY(1.0f); // Auto-scroll to the bottom + ImGui::EndChild(); ImGui::End(); } From 24e4d3330705a8408b62bb249e850e54cd9edfdf Mon Sep 17 00:00:00 2001 From: Nick Koirala Date: Mon, 17 Nov 2025 20:03:26 +1300 Subject: [PATCH 8/9] chore: refactor --- CMakeLists.txt | 1 + imgui.ini | 18 +++++------ include/GuiManager.h | 12 +++---- include/LogWindow.h | 20 ++++++++++++ src/GuiManager.cpp | 77 +++++++++----------------------------------- src/LogWindow.cpp | 50 ++++++++++++++++++++++++++++ 6 files changed, 98 insertions(+), 80 deletions(-) create mode 100644 include/LogWindow.h create mode 100644 src/LogWindow.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 935d25f..77b91d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,6 +78,7 @@ add_executable(simian src/ScriptBindings.cpp src/HotReload.cpp src/GuiManager.cpp + src/LogWindow.cpp src/log/log.c ) diff --git a/imgui.ini b/imgui.ini index d4c8a11..6f258e0 100644 --- a/imgui.ini +++ b/imgui.ini @@ -10,19 +10,19 @@ Collapsed=0 [Window][Game Window] Pos=8,29 -Size=1264,360 +Size=1264,683 Collapsed=0 -DockId=0x00000001,0 +DockId=0x9076BACA,0 [Window][Log Viewer] -Pos=8,391 -Size=1264,321 +Pos=8,29 +Size=1264,683 Collapsed=0 -DockId=0x00000002,0 +DockId=0x9076BACA,1 [Window][##TOAST0] -Pos=982,635 -Size=278,65 +Pos=1010,635 +Size=250,65 Collapsed=0 [Window][##TOAST1] @@ -36,7 +36,5 @@ Size=278,65 Collapsed=0 [Docking][Data] -DockSpace ID=0x9076BACA Window=0x34F970D7 Pos=8,29 Size=1264,683 Split=Y Selected=0x27A02DAA - DockNode ID=0x00000001 Parent=0x9076BACA SizeRef=1264,360 CentralNode=1 Selected=0x27A02DAA - DockNode ID=0x00000002 Parent=0x9076BACA SizeRef=1264,321 Selected=0xBEDDA0C1 +DockSpace ID=0x9076BACA Window=0x34F970D7 Pos=8,29 Size=1264,683 CentralNode=1 Selected=0xBEDDA0C1 diff --git a/include/GuiManager.h b/include/GuiManager.h index 8185a25..bbd51e0 100644 --- a/include/GuiManager.h +++ b/include/GuiManager.h @@ -4,13 +4,9 @@ #include "extras/IconsFontAwesome6.h" #include "gui/fa-solid-900.h" #include "gui/ImGuiNotify.hpp" +#include "LogWindow.h" // Include the new LogWindow class -class Application; -struct LogEntry { - std::string message; - ImVec4 color; -}; - +class Application; class GuiManager { public: @@ -20,14 +16,14 @@ public: void Initialize(Application* application); void Render(RenderTexture2D& renderTexture); void Shutdown(); - std::vector logEntries; + private: void SetupDockspace(RenderTexture2D& renderTexture); void RenderNotifications(); void RenderErrorBanner(); void SetTheme(); - bool showLogWindow = true; Application *app; + LogWindow logWindow; // Add LogWindow as a member }; \ No newline at end of file diff --git a/include/LogWindow.h b/include/LogWindow.h new file mode 100644 index 0000000..73700b3 --- /dev/null +++ b/include/LogWindow.h @@ -0,0 +1,20 @@ +#pragma once +#include "imgui.h" +#include "log.h" +#include +#include + +struct LogEntry { + std::string message; + ImVec4 color; +}; + +class LogWindow { +public: + void Initialize(); + void Render(bool& showLogWindow); + static void LogCallback(log_Event* ev); + +private: + std::vector logEntries; +}; diff --git a/src/GuiManager.cpp b/src/GuiManager.cpp index 9971a8d..3823a64 100644 --- a/src/GuiManager.cpp +++ b/src/GuiManager.cpp @@ -11,46 +11,6 @@ GuiManager::GuiManager() {} GuiManager::~GuiManager() {} -void LogCallback(log_Event *ev) -{ - // Retrieve the GuiManager instance from udata - GuiManager *guiManager = static_cast(ev->udata); - - // Format the log message - char buffer[1024]; - vsnprintf(buffer, sizeof(buffer), ev->fmt, ev->ap); - - // Determine the color based on the log level - ImVec4 color; - switch (ev->level) - { - case LOG_TRACE: - color = ImVec4(0.5f, 0.5f, 0.5f, 1.0f); - break; // Gray - case LOG_DEBUG: - color = ImVec4(0.0f, 0.5f, 1.0f, 1.0f); - break; // Blue - case LOG_INFO: - color = ImVec4(0.0f, 1.0f, 0.0f, 1.0f); - break; // Green - case LOG_WARNING: - color = ImVec4(1.0f, 1.0f, 0.0f, 1.0f); - break; // Yellow - case LOG_ERROR: - color = ImVec4(1.0f, 0.0f, 0.0f, 1.0f); - break; // Red - case LOG_FATAL: - color = ImVec4(1.0f, 0.0f, 1.0f, 1.0f); - break; // Magenta - default: - color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); - break; // White - } - - // Add the log entry to the vector - guiManager->logEntries.push_back({buffer, color}); -} - void GuiManager::Initialize(Application *application) { app = application; @@ -70,19 +30,19 @@ void GuiManager::Initialize(Application *application) iconsConfig.GlyphMinAdvanceX = iconFontSize; io.Fonts->AddFontFromMemoryCompressedTTF(fa_solid_900_compressed_data, fa_solid_900_compressed_size, iconFontSize, &iconsConfig, iconsRanges); SetTheme(); - logEntries.clear(); - log_add_callback(LogCallback, this, LOG_ALL); + logWindow.Initialize(); // Initialize the LogWindow log_trace("GuiManager::Initialize - Started"); } void GuiManager::Render(RenderTexture2D &renderTexture) { rlImGuiBegin(); - SetupDockspace(renderTexture); // Pass the renderTexture parameter + SetupDockspace(renderTexture); RenderErrorBanner(); RenderNotifications(); + logWindow.Render(showLogWindow); // Render the LogWindow rlImGuiEnd(); ImGui::UpdatePlatformWindows(); @@ -118,6 +78,14 @@ void GuiManager::SetupDockspace(RenderTexture2D &renderTexture) ImGui::PopStyleVar(2); if (ImGui::BeginMenuBar()) { + if (ImGui::BeginMenu("Game")) + { + if (ImGui::MenuItem("Quit", "Ctrl + Q")) { + // Handle quit here + app->Shutdown(); + } + ImGui::EndMenu(); + } if (ImGui::BeginMenu("View")) { ImGui::MenuItem("Log Viewer", "Ctrl + L", &showLogWindow); @@ -130,6 +98,10 @@ void GuiManager::SetupDockspace(RenderTexture2D &renderTexture) { showLogWindow = !showLogWindow; } + if (ImGui::GetIO().KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_Q)) + { + app->Shutdown(); + } ImGuiID dockspace_id = ImGui::GetID("MyDockSpace"); ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags); @@ -140,25 +112,6 @@ void GuiManager::SetupDockspace(RenderTexture2D &renderTexture) rlImGuiImageRenderTexture(&renderTexture); ImGui::End(); - if (showLogWindow) - { - // Log Viewer Window - ImGui::Begin("Log Viewer"); - ImGui::BeginChild("LogText", ImVec2(0, 0), true, ImGuiWindowFlags_HorizontalScrollbar); - - // Display each log entry with its color - for (const auto &entry : logEntries) - { - ImGui::TextColored(entry.color, "%s", entry.message.c_str()); - } - - if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) - ImGui::SetScrollHereY(1.0f); // Auto-scroll to the bottom - - ImGui::EndChild(); - ImGui::End(); - } - ImGui::End(); } diff --git a/src/LogWindow.cpp b/src/LogWindow.cpp new file mode 100644 index 0000000..1f93770 --- /dev/null +++ b/src/LogWindow.cpp @@ -0,0 +1,50 @@ +#include "LogWindow.h" +#include "log.h" + +void LogWindow::Initialize() +{ + logEntries.clear(); + log_add_callback(LogCallback, this, LOG_ALL); +} + +void LogWindow::Render(bool& showLogWindow) +{ + if (!showLogWindow) + return; + + ImGui::Begin("Log Viewer"); + ImGui::BeginChild("LogText", ImVec2(0, 0), true, ImGuiWindowFlags_HorizontalScrollbar); + + for (const auto& entry : logEntries) + { + ImGui::TextColored(entry.color, "%s", entry.message.c_str()); + } + + if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) + ImGui::SetScrollHereY(1.0f); + + ImGui::EndChild(); + ImGui::End(); +} + +void LogWindow::LogCallback(log_Event* ev) +{ + LogWindow* logWindow = static_cast(ev->udata); + + char buffer[1024]; + vsnprintf(buffer, sizeof(buffer), ev->fmt, ev->ap); + + ImVec4 color; + switch (ev->level) + { + case LOG_TRACE: color = ImVec4(0.5f, 0.5f, 0.5f, 1.0f); break; + case LOG_DEBUG: color = ImVec4(0.0f, 0.5f, 1.0f, 1.0f); break; + case LOG_INFO: color = ImVec4(0.0f, 1.0f, 0.0f, 1.0f); break; + case LOG_WARNING: color = ImVec4(1.0f, 1.0f, 0.0f, 1.0f); break; + case LOG_ERROR: color = ImVec4(1.0f, 0.0f, 0.0f, 1.0f); break; + case LOG_FATAL: color = ImVec4(1.0f, 0.0f, 1.0f, 1.0f); break; + default: color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); break; + } + + logWindow->logEntries.push_back({buffer, color}); +} From 64023f875b1b4ea88047347ad94a2a1d36850505 Mon Sep 17 00:00:00 2001 From: Nick Koirala Date: Mon, 17 Nov 2025 20:35:34 +1300 Subject: [PATCH 9/9] chore: fix all compile warnings --- imgui.ini | 14 ++++++++------ include/gui/ImGuiNotify.hpp | 18 +++++++++--------- src/ScriptEngine.cpp | 2 +- src/log/log.c | 10 +++++++++- 4 files changed, 27 insertions(+), 17 deletions(-) diff --git a/imgui.ini b/imgui.ini index 6f258e0..074a4e9 100644 --- a/imgui.ini +++ b/imgui.ini @@ -10,15 +10,15 @@ Collapsed=0 [Window][Game Window] Pos=8,29 -Size=1264,683 +Size=1264,513 Collapsed=0 -DockId=0x9076BACA,0 +DockId=0x00000001,0 [Window][Log Viewer] -Pos=8,29 -Size=1264,683 +Pos=8,544 +Size=1264,168 Collapsed=0 -DockId=0x9076BACA,1 +DockId=0x00000002,0 [Window][##TOAST0] Pos=1010,635 @@ -36,5 +36,7 @@ Size=278,65 Collapsed=0 [Docking][Data] -DockSpace ID=0x9076BACA Window=0x34F970D7 Pos=8,29 Size=1264,683 CentralNode=1 Selected=0xBEDDA0C1 +DockSpace ID=0x9076BACA Window=0x34F970D7 Pos=8,29 Size=1264,683 Split=Y Selected=0x27A02DAA + DockNode ID=0x00000001 Parent=0x9076BACA SizeRef=1264,513 CentralNode=1 Selected=0x27A02DAA + DockNode ID=0x00000002 Parent=0x9076BACA SizeRef=1264,168 Selected=0xBEDDA0C1 diff --git a/include/gui/ImGuiNotify.hpp b/include/gui/ImGuiNotify.hpp index 4219029..c633b40 100644 --- a/include/gui/ImGuiNotify.hpp +++ b/include/gui/ImGuiNotify.hpp @@ -155,10 +155,10 @@ public: * * @param type The type of the toast notification. */ - inline void setType(const ImGuiToastType &type) + inline void setType(const ImGuiToastType &typ) { - IM_ASSERT(type < ImGuiToastType::COUNT); - this->type = type; + IM_ASSERT(typ < ImGuiToastType::COUNT); + this->type = typ; }; /** @@ -166,9 +166,9 @@ public: * * @param flags ImGui window flags to set. */ - inline void setWindowFlags(const ImGuiWindowFlags &flags) + inline void setWindowFlags(const ImGuiWindowFlags &flgs) { - this->flags = flags; + this->flags = flgs; } /** @@ -176,9 +176,9 @@ public: * * @param onButtonPress std::fuction or lambda expression, which contains the code for execution. */ - inline void setOnButtonPress(const std::function &onButtonPress) + inline void setOnButtonPress(const std::function &onBtnPress) { - this->onButtonPress = onButtonPress; + this->onButtonPress = onBtnPress; } /** @@ -564,7 +564,7 @@ namespace ImGui #endif // Set notification window flags - if (!NOTIFY_USE_DISMISS_BUTTON && currentToast->getOnButtonPress() == nullptr) + if constexpr (!NOTIFY_USE_DISMISS_BUTTON && currentToast->getOnButtonPress() == nullptr) { currentToast->setWindowFlags(NOTIFY_DEFAULT_TOAST_FLAGS | ImGuiWindowFlags_NoInputs); } @@ -630,7 +630,7 @@ namespace ImGui // If the button is pressed, we want to remove the notification if (Button(ICON_FA_XMARK)) { - RemoveNotification(i); + RemoveNotification(static_cast(i)); } } diff --git a/src/ScriptEngine.cpp b/src/ScriptEngine.cpp index b2ba34f..9863604 100644 --- a/src/ScriptEngine.cpp +++ b/src/ScriptEngine.cpp @@ -215,7 +215,7 @@ void ScriptEngine::MessageCallback(const asSMessageInfo* msg, void*) { } } -int ScriptEngine::IncludeCallback(const char* include, const char* from, CScriptBuilder* builder, void* userParam) { +int ScriptEngine::IncludeCallback(const char* include, const char* from, CScriptBuilder* builder, [[maybe_unused]] void* userParam) { // Extract just the filename from the from path for cleaner logging const char* fromFile = from; if (from) { diff --git a/src/log/log.c b/src/log/log.c index ef058a4..6f9acaa 100644 --- a/src/log/log.c +++ b/src/log/log.c @@ -182,7 +182,15 @@ static void init_event(log_Event *ev, void *udata) if (!ev->time) { time_t t = time(NULL); - ev->time = localtime(&t); + static struct tm tm_buf; // Thread-safe buffer for time struct + + #ifdef _WIN32 + localtime_s(&tm_buf, &t); // Use localtime_s on Windows + #else + localtime_r(&t, &tm_buf); // Use localtime_r on Linux/Unix + #endif + + ev->time = &tm_buf; } ev->udata = udata; }