From 91366f7fe79a6b4b3d20f6caa8b1e7986b310986 Mon Sep 17 00:00:00 2001 From: Pierre Dejoue Date: Sat, 26 Aug 2023 20:44:02 +0200 Subject: [PATCH] GUI: More refactoring --- src/gui/CMakeLists.txt | 3 + src/gui/src/err_window.cpp | 4 +- src/gui/src/glfw_context.cpp | 78 ++++++++++++ src/gui/src/glfw_context.h | 20 +++ src/gui/src/goal_window.cpp | 4 +- src/gui/src/grid_window.cpp | 4 +- src/gui/src/imgui_helpers.cpp | 86 +++++++++++++ src/gui/src/imgui_helpers.h | 46 +++++++ src/gui/src/main.cpp | 225 +++++++++++++++------------------- src/gui/src/screen.h | 13 ++ src/gui/src/style.cpp | 32 +++++ src/gui/src/style.h | 7 ++ src/gui/src/window_layout.h | 34 +++++ 13 files changed, 429 insertions(+), 127 deletions(-) create mode 100644 src/gui/src/glfw_context.cpp create mode 100644 src/gui/src/glfw_context.h create mode 100644 src/gui/src/imgui_helpers.cpp create mode 100644 src/gui/src/imgui_helpers.h create mode 100644 src/gui/src/screen.h create mode 100644 src/gui/src/style.cpp create mode 100644 src/gui/src/style.h create mode 100644 src/gui/src/window_layout.h diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 16099a8..f3683fe 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -10,6 +10,8 @@ include(pfd) set(GUI_SOURCES src/draw_grid.cpp src/err_window.cpp + src/glfw_context.cpp + src/imgui_helpers.cpp src/goal_window.cpp src/grid_info.cpp src/grid_window.cpp @@ -17,6 +19,7 @@ set(GUI_SOURCES src/picross_file.cpp src/settings.cpp src/settings_window.cpp + src/style.cpp ) file(GLOB GUI_HEADERS src/*.h) diff --git a/src/gui/src/err_window.cpp b/src/gui/src/err_window.cpp index 318a83f..fe11fb6 100644 --- a/src/gui/src/err_window.cpp +++ b/src/gui/src/err_window.cpp @@ -25,7 +25,9 @@ void ErrWindow::visit(bool& can_be_erased) { ImGui::SetNextWindowSizeConstraints(ImVec2(0, 300), ImVec2(FLT_MAX, 600)); - constexpr ImGuiWindowFlags win_flags = ImGuiWindowFlags_AlwaysAutoResize; + constexpr ImGuiWindowFlags win_flags = ImGuiWindowFlags_AlwaysAutoResize + | ImGuiWindowFlags_NoSavedSettings; + bool is_window_open = true; if (!ImGui::Begin(pImpl->title.c_str(), &is_window_open, win_flags)) { diff --git a/src/gui/src/glfw_context.cpp b/src/gui/src/glfw_context.cpp new file mode 100644 index 0000000..bdce72e --- /dev/null +++ b/src/gui/src/glfw_context.cpp @@ -0,0 +1,78 @@ +#include "glfw_context.h" + + +#include + + +namespace +{ + +// Target OpenGL 3.3 for this project +constexpr int TARGET_OPENGL_MAJOR = 3; +constexpr int TARGET_OPENGL_MINOR = 0; +constexpr bool TARGET_OPENGL_CORE_PROFILE = false; // 3.2+ only +constexpr bool TARGET_OPENGL_FORWARD_COMPATIBILITY = true; // 3.0 only, recommended for MacOS +constexpr const char* TARGET_GLSL_VERSION_STR = "#version 130"; + +// GLFW error handling function +stdutils::io::ErrorHandler s_glfw_err_handler; +void glfw_error_callback(int error, const char* description) +{ + if(!s_glfw_err_handler) { return; } + std::stringstream out; + out << "GLFW Error " << error << ": " << description; + s_glfw_err_handler(stdutils::io::Severity::ERR, out.str()); +} + +} // Anonymous namespace + +GLFWWindowContext::GLFWWindowContext(int width, int height, const std::string_view& title, const stdutils::io::ErrorHandler* err_handler) + : m_window_ptr(nullptr) + , m_glfw_init(false) +{ + static bool call_once = false; + if (call_once) + return; + call_once = true; + + if (err_handler) + { + s_glfw_err_handler = *err_handler; + glfwSetErrorCallback(glfw_error_callback); + } + if (!glfwInit()) + { + if (err_handler) { (*err_handler)(stdutils::io::Severity::FATAL, "GLFW failed to initialize"); } + return; + } + m_glfw_init = true; + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, TARGET_OPENGL_MAJOR); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, TARGET_OPENGL_MINOR); + if constexpr (TARGET_OPENGL_CORE_PROFILE) + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + if constexpr (TARGET_OPENGL_FORWARD_COMPATIBILITY) + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + m_window_ptr = glfwCreateWindow(width, height, title.data(), nullptr, nullptr); + if (m_window_ptr == nullptr) + { + if (err_handler) { (*err_handler)(stdutils::io::Severity::FATAL, "GLFW failed to create the window"); } + return; + } + glfwMakeContextCurrent(m_window_ptr); + glfwSwapInterval(1); +} + +GLFWWindowContext::~GLFWWindowContext() +{ + if(m_window_ptr) + glfwDestroyWindow(m_window_ptr); + m_window_ptr = nullptr; + if (m_glfw_init) + glfwTerminate(); +} + +const char* glsl_version() +{ + return TARGET_GLSL_VERSION_STR; +} diff --git a/src/gui/src/glfw_context.h b/src/gui/src/glfw_context.h new file mode 100644 index 0000000..9549d09 --- /dev/null +++ b/src/gui/src/glfw_context.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +// Wrapper class for GLFW initialization and window +struct GLFWwindow; +class GLFWWindowContext +{ +public: + GLFWWindowContext(int width, int height, const std::string_view& title, const stdutils::io::ErrorHandler* err_handler = nullptr); + ~GLFWWindowContext(); + GLFWwindow* window() { return m_window_ptr; } + +private: + GLFWwindow* m_window_ptr; + bool m_glfw_init; +}; + +const char* glsl_version(); diff --git a/src/gui/src/goal_window.cpp b/src/gui/src/goal_window.cpp index 4e0b551..fc04bd7 100644 --- a/src/gui/src/goal_window.cpp +++ b/src/gui/src/goal_window.cpp @@ -25,7 +25,9 @@ void GoalWindow::visit(bool& can_be_erased, Settings& settings) const float min_win_height = 80.f + static_cast(height * tile_size); ImGui::SetNextWindowSizeConstraints(ImVec2(min_win_width, min_win_height), ImVec2(FLT_MAX, FLT_MAX)); - constexpr ImGuiWindowFlags win_flags = ImGuiWindowFlags_AlwaysAutoResize; + constexpr ImGuiWindowFlags win_flags = ImGuiWindowFlags_AlwaysAutoResize + | ImGuiWindowFlags_NoSavedSettings; + bool is_window_open = true; if (!ImGui::Begin(title.c_str(), &is_window_open, win_flags)) { diff --git a/src/gui/src/grid_window.cpp b/src/gui/src/grid_window.cpp index d0a69ef..8f80959 100644 --- a/src/gui/src/grid_window.cpp +++ b/src/gui/src/grid_window.cpp @@ -100,7 +100,9 @@ void GridWindow::visit(bool& can_be_erased, Settings& settings) const float min_win_height = 100.f + static_cast(height * tile_size); ImGui::SetNextWindowSizeConstraints(ImVec2(min_win_width, min_win_height), ImVec2(FLT_MAX, FLT_MAX)); - constexpr ImGuiWindowFlags win_flags = ImGuiWindowFlags_AlwaysAutoResize; + constexpr ImGuiWindowFlags win_flags = ImGuiWindowFlags_AlwaysAutoResize + | ImGuiWindowFlags_NoSavedSettings; + bool is_window_open = true; if (!ImGui::Begin(title.c_str(), &is_window_open, win_flags)) { diff --git a/src/gui/src/imgui_helpers.cpp b/src/gui/src/imgui_helpers.cpp new file mode 100644 index 0000000..2d3c096 --- /dev/null +++ b/src/gui/src/imgui_helpers.cpp @@ -0,0 +1,86 @@ +#include "imgui_helpers.h" + +#include +#include +#include +#include +#include "glfw_context.h" + + +namespace ImGui +{ + +void HelpMarker(const char* desc) +{ + ImGui::TextDisabled("(?)"); + if (ImGui::BeginItemTooltip()) + { + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); + ImGui::TextUnformatted(desc); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } +} + +// Place the window in the working area, that is the position of the viewport minus task bars, menus bars, status bars, etc. +void SetNextWindowPosAndSize(const WindowLayout& window_layout, ImGuiCond cond) +{ + const auto work_tl_corner = ImGui::GetMainViewport()->WorkPos; + const auto work_size = ImGui::GetMainViewport()->WorkSize; + const ImVec2 tl_corner(window_layout.m_position.x + work_tl_corner.x, window_layout.m_position.y + work_tl_corner.y); + ImGui::SetNextWindowPos(tl_corner, cond); + ImGui::SetNextWindowSize(to_imgui_vec2(window_layout.window_size(to_screen_size(work_size))), cond); +} + +} // namespace ImGui + +DearImGuiContext::DearImGuiContext(GLFWwindow* glfw_window, bool& any_fatal_error) noexcept +{ + any_fatal_error = false; + try + { + const bool versions_ok = IMGUI_CHECKVERSION(); + const auto* ctx = ImGui::CreateContext(); + + // Setup Platform/Renderer backends + const bool init_glfw = ImGui_ImplGlfw_InitForOpenGL(glfw_window, true); + const bool init_opengl3 = ImGui_ImplOpenGL3_Init(glsl_version()); + + any_fatal_error = !versions_ok || (ctx == nullptr) || !init_glfw || !init_opengl3; + } + catch(const std::exception&) + { + any_fatal_error = true; + } +} + +DearImGuiContext::~DearImGuiContext() +{ + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplGlfw_Shutdown(); + ImGui::DestroyContext(); +} + +void DearImGuiContext::new_frame() const +{ + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); +} + +void DearImGuiContext::render() const +{ + ImGui::Render(); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); +} + +void DearImGuiContext::backend_info(std::ostream& out) const +{ + const ImGuiIO& io = ImGui::GetIO(); + out << "Dear ImGui " << IMGUI_VERSION + << " (Backend platform: " << (io.BackendPlatformName ? io.BackendPlatformName : "NULL") + << ", renderer: " << (io.BackendRendererName ? io.BackendRendererName : "NULL") << ")" << std::endl; + out << "GLFW " << GLFW_VERSION_MAJOR << "." << GLFW_VERSION_MINOR << "." << GLFW_VERSION_REVISION << std::endl; + out << "OpenGL Version " << glGetString(GL_VERSION) << std::endl; + out << "OpenGL Renderer " << glGetString(GL_RENDERER) << std::endl; +} diff --git a/src/gui/src/imgui_helpers.h b/src/gui/src/imgui_helpers.h new file mode 100644 index 0000000..3246eb7 --- /dev/null +++ b/src/gui/src/imgui_helpers.h @@ -0,0 +1,46 @@ +#pragma once + +#include "screen.h" +#include "window_layout.h" + +#include + +#include + +inline ScreenPos to_screen_pos(ImVec2 vec2) +{ + return ScreenPos(vec2.x, vec2.y); +} + +inline ScreenSize to_screen_size(ImVec2 vec2) +{ + return ScreenSize(vec2.x, vec2.y); +} + +inline ImVec2 to_imgui_vec2(ScreenPos pos) +{ + return ImVec2(pos.x, pos.y); +} + +namespace ImGui +{ +void HelpMarker(const char* desc); // Function taken from imgui_demo.cpp +void SetNextWindowPosAndSize(const WindowLayout& window_layout, ImGuiCond cond = 0); +} + +// Do not call this class ImGuiContext because this is an internal class of Dear ImGui +struct GLFWwindow; +class DearImGuiContext +{ +public: + explicit DearImGuiContext(GLFWwindow* glfw_window, bool& any_fatal_error) noexcept; + ~DearImGuiContext(); + DearImGuiContext(const DearImGuiContext&) = delete; + DearImGuiContext(DearImGuiContext&&) = delete; + DearImGuiContext& operator=(const DearImGuiContext&) = delete; + DearImGuiContext& operator=(DearImGuiContext&&) = delete; + + void new_frame() const; + void render() const; + void backend_info(std::ostream& out) const; +}; diff --git a/src/gui/src/main.cpp b/src/gui/src/main.cpp index d8ef54d..67f5cf0 100644 --- a/src/gui/src/main.cpp +++ b/src/gui/src/main.cpp @@ -9,21 +9,25 @@ #include "picross_file.h" #include "settings.h" #include "settings_window.h" +#include "style.h" +#include "window_layout.h" #include +#include #include // Order matters in this section #include -#include -#include +#include "imgui_helpers.h" #include #include -// NB: No OpenGL loader here: this project only relies on the drawing features provided by Dear ImGui. +#include "glfw_context.h" +// NB: No OpenGL loader here: This project only relies on the drawing features provided by Dear ImGui. // Dear ImGui embeds its own minimal loader for the OpenGL 3.x functions it needs. // See: https://github.com/ocornut/imgui/issues/4445 "OpenGL backend now embeds its own GL loader" #include +#include #include #include #include @@ -34,28 +38,16 @@ namespace { -void glfw_error_callback(int error, const char* description) +void err_callback(stdutils::io::SeverityCode sev, std::string_view msg) { - std::cerr << "Glfw Error " << error << ": " << description << std::endl; + std::cerr << stdutils::io::str_severity_code(sev) << ": " << msg << std::endl; } -const ImColor WindowBackgroundColor_Classic(29, 75, 99, 255); -const ImColor WindowBackgroundColor_Dark(4, 8, 25, 255); -const ImColor WindowMainBackgroundColor_Classic(35, 92, 121, 255); -const ImColor WindowMainBackgroundColor_Dark(10, 25, 50, 255); - -void imgui_set_style(bool dark_mode) +std::string project_title() { - if (dark_mode) - { - ImGui::StyleColorsDark(); - ImGui::GetStyle().Colors[ImGuiCol_WindowBg] = WindowBackgroundColor_Dark; - } - else - { - ImGui::StyleColorsClassic(); - ImGui::GetStyle().Colors[ImGuiCol_WindowBg] = WindowBackgroundColor_Classic; - } + std::stringstream title; + title << "Picross Solver " << picross::get_version_string(); + return title.str(); } // Application windows @@ -65,49 +57,94 @@ struct AppWindows std::vector> picross; }; +void main_menu_bar(AppWindows& windows, bool& application_should_close, bool& gui_dark_mode) +{ + application_should_close = false; + if (ImGui::BeginMainMenuBar()) + { + if (ImGui::BeginMenu("File")) + { + if (ImGui::MenuItem("Open", "Ctrl+O")) + { + const auto paths = pfd::open_file("Select a Picross file", "", + { "Picross file", "*.txt *.nin *.non", "All files", "*" }).result(); + for (const auto& path : paths) + { + const auto format = picross::io::picross_file_format_from_filepath(path); + std::cout << "User selected file " << path << " (format: " << format << ")" << std::endl; + windows.picross.emplace_back(std::make_unique(path, format)); + } + } + if (ImGui::MenuItem("Import bitmap", "Ctrl+I")) + { + const auto paths = pfd::open_file("Select a bitmap file", "", + { "PBM file", "*.pbm", "All files", "*" }).result(); + for (const auto& path : paths) + { + const auto format = picross::io::PicrossFileFormat::PBM; + std::cout << "User selected bitmap " << path << " (format: " << format << ")" << std::endl; + windows.picross.emplace_back(std::make_unique(path, format)); + } + } + if (ImGui::MenuItem("Import solution")) + { + const auto paths = pfd::open_file("Select a solution file", "", + { "All files", "*" }).result(); + for (const auto& path : paths) + { + const auto format = picross::io::PicrossFileFormat::OutputGrid; + std::cout << "User selected solution file " << path << std::endl; + windows.picross.emplace_back(std::make_unique(path, format)); + } + } + ImGui::Separator(); + if (ImGui::BeginMenu("Options")) + { + if (ImGui::MenuItem("Dark Mode", "", &gui_dark_mode)) + { + imgui_set_style(gui_dark_mode); + } + ImGui::EndMenu(); + } + ImGui::Separator(); + if (ImGui::MenuItem("Quit", "Alt+F4")) + { + application_should_close = true; + } + ImGui::EndMenu(); + } + ImGui::EndMainMenuBar(); + } +} + } // Anonymous namespace int main(int argc, char *argv[]) { - UNUSED(argc); - UNUSED(argv); - - // Versions - std::stringstream picross_title; - picross_title << "Picross Solver " << picross::get_version_string(); - std::cout << picross_title.str() << std::endl; + UNUSED(argc); UNUSED(argv); // Setup main window - glfwSetErrorCallback(glfw_error_callback); - if (!glfwInit()) - return 1; - const char* glsl_version = "#version 130"; - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); - GLFWwindow* window = glfwCreateWindow(1280, 720, picross_title.str().c_str(), nullptr, nullptr); - if (window == nullptr) - return 1; - glfwMakeContextCurrent(window); - glfwSwapInterval(1); // Enable vsync + constexpr int WINDOW_WIDTH = 1280; + constexpr int WINDOW_HEIGHT = 720; + stdutils::io::ErrorHandler err_handler(err_callback); + GLFWWindowContext glfw_context(WINDOW_WIDTH, WINDOW_HEIGHT, project_title().data(), &err_handler); + if (glfw_context.window() == nullptr) + return EXIT_FAILURE; // Setup Dear ImGui context - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - - // Setup Platform/Renderer backends - ImGui_ImplGlfw_InitForOpenGL(window, true); - ImGui_ImplOpenGL3_Init(glsl_version); + bool any_fatal_err = false; + const DearImGuiContext dear_imgui_context(glfw_context.window(), any_fatal_err); + if (any_fatal_err) + return EXIT_FAILURE; // Print out version information - std::cout << "Dear ImGui " << IMGUI_VERSION << std::endl; - std::cout << "GLFW " << GLFW_VERSION_MAJOR << "." << GLFW_VERSION_MINOR << "." << GLFW_VERSION_REVISION << std::endl; - std::cout << "OpenGL Version " << glGetString(GL_VERSION) << std::endl; - std::cout << "OpenGL Renderer " << glGetString(GL_RENDERER) << std::endl; + std::cout << project_title() << std::endl; + dear_imgui_context.backend_info(std::cout); // Style - bool imgui_dark_mode = false; - imgui_set_style(imgui_dark_mode); + bool gui_dark_mode = false; + imgui_set_style(gui_dark_mode); // Application Settings Settings settings; @@ -116,7 +153,7 @@ int main(int argc, char *argv[]) AppWindows windows; // Main loop - while (!glfwWindowShouldClose(window)) + while (!glfwWindowShouldClose(glfw_context.window())) { // Poll and handle events (inputs, window resize, etc.) // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. @@ -125,66 +162,15 @@ int main(int argc, char *argv[]) // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. glfwPollEvents(); - // Start the Dear ImGui frame - ImGui_ImplOpenGL3_NewFrame(); - ImGui_ImplGlfw_NewFrame(); - ImGui::NewFrame(); + // Start the Dear ImGui frame + dear_imgui_context.new_frame(); // Main menu - if (ImGui::BeginMainMenuBar()) { - if (ImGui::BeginMenu("File")) - { - if (ImGui::MenuItem("Open", "Ctrl+O")) - { - const auto paths = pfd::open_file("Select a Picross file", "", - { "Picross file", "*.txt *.nin *.non", "All files", "*" }).result(); - for (const auto& path : paths) - { - const auto format = picross::io::picross_file_format_from_filepath(path); - std::cout << "User selected file " << path << " (format: " << format << ")" << std::endl; - windows.picross.emplace_back(std::make_unique(path, format)); - } - } - if (ImGui::MenuItem("Import bitmap", "Ctrl+I")) - { - const auto paths = pfd::open_file("Select a bitmap file", "", - { "PBM file", "*.pbm", "All files", "*" }).result(); - for (const auto& path : paths) - { - const auto format = picross::io::PicrossFileFormat::PBM; - std::cout << "User selected bitmap " << path << " (format: " << format << ")" << std::endl; - windows.picross.emplace_back(std::make_unique(path, format)); - } - } - if (ImGui::MenuItem("Import solution")) - { - const auto paths = pfd::open_file("Select a solution file", "", - { "All files", "*" }).result(); - for (const auto& path : paths) - { - const auto format = picross::io::PicrossFileFormat::OutputGrid; - std::cout << "User selected solution file " << path << std::endl; - windows.picross.emplace_back(std::make_unique(path, format)); - } - } - ImGui::Separator(); - if (ImGui::BeginMenu("Options")) - { - if (ImGui::MenuItem("Dark Mode", "", &imgui_dark_mode)) - { - imgui_set_style(imgui_dark_mode); - } - ImGui::EndMenu(); - } - ImGui::Separator(); - if (ImGui::MenuItem("Quit", "Alt+F4")) - { - glfwSetWindowShouldClose(window, 1); - } - ImGui::EndMenu(); - } - ImGui::EndMainMenuBar(); + bool app_should_close = false; + main_menu_bar(windows, app_should_close, gui_dark_mode); + if (app_should_close) + glfwSetWindowShouldClose(glfw_context.window(), 1); } // Picross files windows (one window per grid, so possibly multiple windows per file) @@ -213,25 +199,16 @@ int main(int argc, char *argv[]) if (!windows.settings) { windows.settings = std::make_unique(settings); } // Rendering - ImGui::Render(); int display_w, display_h; - glfwGetFramebufferSize(window, &display_w, &display_h); + glfwGetFramebufferSize(glfw_context.window(), &display_w, &display_h); glViewport(0, 0, display_w, display_h); - const auto clear_color = static_cast(imgui_dark_mode ? WindowMainBackgroundColor_Dark : WindowMainBackgroundColor_Classic); - glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); + const auto clear_color = get_background_color(gui_dark_mode); + glClearColor(clear_color[0], clear_color[1], clear_color[2], clear_color[3]); glClear(GL_COLOR_BUFFER_BIT); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - glfwSwapBuffers(window); + dear_imgui_context.render(); + glfwSwapBuffers(glfw_context.window()); } // while (!glfwWindowShouldClose(window)) - // Cleanup - windows.picross.clear(); - ImGui_ImplOpenGL3_Shutdown(); - ImGui_ImplGlfw_Shutdown(); - ImGui::DestroyContext(); - glfwDestroyWindow(window); - glfwTerminate(); - - return 0; + return EXIT_SUCCESS; } diff --git a/src/gui/src/screen.h b/src/gui/src/screen.h new file mode 100644 index 0000000..7fe1b55 --- /dev/null +++ b/src/gui/src/screen.h @@ -0,0 +1,13 @@ +#pragma once + +template +struct Vec2 +{ + using scalar = F; + Vec2() : x(F(0)), y(F(0)) {} + Vec2(F x, F y) : x(x), y(y) {} + F x, y; +}; + +using ScreenPos = Vec2; +using ScreenSize = Vec2; diff --git a/src/gui/src/style.cpp b/src/gui/src/style.cpp new file mode 100644 index 0000000..ca29afd --- /dev/null +++ b/src/gui/src/style.cpp @@ -0,0 +1,32 @@ +#include "style.h" + +#include + + +namespace +{ + constexpr ImColor WindowBackgroundColor_Classic(29, 75, 99, 217); + constexpr ImColor WindowBackgroundColor_Dark(4, 8, 25, 240); + constexpr ImColor WindowMainBackgroundColor_Classic(35, 92, 121, 255); + constexpr ImColor WindowMainBackgroundColor_Dark(10, 25, 50, 255); +} + +void imgui_set_style(bool dark_mode) +{ + if (dark_mode) + { + ImGui::StyleColorsDark(); + ImGui::GetStyle().Colors[ImGuiCol_WindowBg] = WindowBackgroundColor_Dark; + } + else + { + ImGui::StyleColorsClassic(); + ImGui::GetStyle().Colors[ImGuiCol_WindowBg] = WindowBackgroundColor_Classic; + } +} + +std::array get_background_color(bool dark_mode) +{ + const auto c = static_cast(dark_mode ? WindowMainBackgroundColor_Dark : WindowMainBackgroundColor_Classic); + return std::array{ c.x, c.y, c.z, c.w }; +} diff --git a/src/gui/src/style.h b/src/gui/src/style.h new file mode 100644 index 0000000..91bfae6 --- /dev/null +++ b/src/gui/src/style.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +void imgui_set_style(bool dark_mode); + +std::array get_background_color(bool dark_mode); diff --git a/src/gui/src/window_layout.h b/src/gui/src/window_layout.h new file mode 100644 index 0000000..2f8a908 --- /dev/null +++ b/src/gui/src/window_layout.h @@ -0,0 +1,34 @@ +#pragma once + +#include "screen.h" + +#include +#include + + +struct WindowLayout +{ + static constexpr float DEFAULT_PADDING = 2.0f; + + // A size of 0.f or less on an axis means the window will occupy whatever is left of the workspace on that axis + WindowLayout(float pos_x = 0.f, float pos_y = 0.f, float sz_x = 0.f, float sz_y = 0.f, float padding = DEFAULT_PADDING) + : m_position(pos_x + padding, pos_y + padding) + , m_size(sz_x, sz_y) + , m_padding(padding) + { + assert(m_position.x >= 0.f); + assert(m_position.y >= 0.f); + } + + ScreenSize window_size(ScreenSize workspace_sz) const + { + return ScreenSize( + std::max(1.f, m_size.x > 0.f ? m_size.x - m_padding : workspace_sz.x - m_position.x - m_padding), + std::max(1.f, m_size.y > 0.f ? m_size.y - m_padding : workspace_sz.y - m_position.y - m_padding) + ); + } + + ScreenPos m_position; + ScreenSize m_size; + float m_padding; +};