diff --git a/assets/screenshot.png b/assets/screenshot.png index 18d6e03..870426a 100644 Binary files a/assets/screenshot.png and b/assets/screenshot.png differ diff --git a/src/core/common/version.hpp b/src/core/common/version.hpp index 7f19563..65e55e0 100644 --- a/src/core/common/version.hpp +++ b/src/core/common/version.hpp @@ -6,7 +6,7 @@ namespace clrsync::core { -const std::string GIT_SEMVER = "1.0.5+git.g3cd637d"; +const std::string GIT_SEMVER = "1.0.5+git.g4697f69"; const std::string version_string(); } // namespace clrsync::core diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index fd38bb6..6bc9020 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -1,38 +1,39 @@ set(GUI_SOURCES main.cpp - views/color_scheme_editor.cpp - views/color_table_renderer.cpp - views/preview_renderer.cpp - controllers/theme_applier.cpp - views/template_editor.cpp - controllers/palette_controller.cpp - controllers/template_controller.cpp - views/about_window.cpp - views/settings_window.cpp - widgets/colors.cpp - widgets/dialogs.cpp - widgets/palette_selector.cpp - widgets/input_dialog.cpp - widgets/action_buttons.cpp - widgets/styled_checkbox.cpp - widgets/autocomplete.cpp - widgets/form_field.cpp - widgets/error_message.cpp - widgets/settings_buttons.cpp - widgets/section_header.cpp - widgets/link_button.cpp - widgets/centered_text.cpp - widgets/validation_message.cpp - widgets/template_controls.cpp - layout/main_layout.cpp - platform/windows/font_loader_windows.cpp - platform/linux/font_loader_linux.cpp - platform/macos/font_loader_macos.cpp - platform/linux/file_browser_linux.cpp - platform/windows/file_browser_windows.cpp - ${CMAKE_SOURCE_DIR}/lib/color_text_edit/TextEditor.cpp - backend/glfw_opengl.cpp - ui_manager.cpp + theme/app_theme.cpp + views/color_scheme_editor.cpp + views/color_table_renderer.cpp + views/preview_renderer.cpp + views/template_editor.cpp + views/about_window.cpp + views/settings_window.cpp + controllers/theme_applier.cpp + controllers/palette_controller.cpp + controllers/template_controller.cpp + widgets/colors.cpp + widgets/dialogs.cpp + widgets/palette_selector.cpp + widgets/input_dialog.cpp + widgets/action_buttons.cpp + widgets/styled_checkbox.cpp + widgets/autocomplete.cpp + widgets/form_field.cpp + widgets/error_message.cpp + widgets/settings_buttons.cpp + widgets/section_header.cpp + widgets/link_button.cpp + widgets/centered_text.cpp + widgets/validation_message.cpp + widgets/template_controls.cpp + layout/main_layout.cpp + platform/windows/font_loader_windows.cpp + platform/linux/font_loader_linux.cpp + platform/macos/font_loader_macos.cpp + platform/linux/file_browser_linux.cpp + platform/windows/file_browser_windows.cpp + ${CMAKE_SOURCE_DIR}/lib/color_text_edit/TextEditor.cpp + backend/glfw_opengl.cpp + ui_manager.cpp ) if(MACOS) diff --git a/src/gui/controllers/theme_applier.cpp b/src/gui/controllers/theme_applier.cpp index e6523e5..8f692dd 100644 --- a/src/gui/controllers/theme_applier.cpp +++ b/src/gui/controllers/theme_applier.cpp @@ -1,162 +1,53 @@ #include "gui/controllers/theme_applier.hpp" -#include "imgui.h" +#include "gui/theme/app_theme.hpp" namespace theme_applier { -static uint32_t get_color_u32(const clrsync::core::palette ¤t, const std::string &key) -{ - const auto &col = current.get_color(key); - const uint32_t hex = col.hex(); - // Convert from RRGGBBAA to AABBGGRR (ImGui format) - const uint32_t r = (hex >> 24) & 0xFF; - const uint32_t g = (hex >> 16) & 0xFF; - const uint32_t b = (hex >> 8) & 0xFF; - const uint32_t a = hex & 0xFF; - return (a << 24) | (b << 16) | (g << 8) | r; -} - void apply_to_editor(TextEditor &editor, const clrsync::core::palette ¤t) { + using namespace clrsync::gui::theme; + + auto get_color_u32 = [&](const std::string &key) -> uint32_t { + const auto &col = current.get_color(key); + return color_utils::to_imgui_u32(col.hex()); + }; + auto palette = editor.GetPalette(); - palette[int(TextEditor::PaletteIndex::Default)] = get_color_u32(current, "editor_main"); - palette[int(TextEditor::PaletteIndex::Keyword)] = get_color_u32(current, "editor_command"); - palette[int(TextEditor::PaletteIndex::Number)] = get_color_u32(current, "editor_warning"); - palette[int(TextEditor::PaletteIndex::String)] = get_color_u32(current, "editor_string"); - palette[int(TextEditor::PaletteIndex::CharLiteral)] = get_color_u32(current, "editor_string"); - palette[int(TextEditor::PaletteIndex::Punctuation)] = get_color_u32(current, "editor_main"); - palette[int(TextEditor::PaletteIndex::Preprocessor)] = - get_color_u32(current, "editor_emphasis"); - palette[int(TextEditor::PaletteIndex::Identifier)] = get_color_u32(current, "editor_main"); - palette[int(TextEditor::PaletteIndex::KnownIdentifier)] = get_color_u32(current, "editor_link"); - palette[int(TextEditor::PaletteIndex::PreprocIdentifier)] = - get_color_u32(current, "editor_link"); + palette[int(TextEditor::PaletteIndex::Default)] = get_color_u32("editor_main"); + palette[int(TextEditor::PaletteIndex::Keyword)] = get_color_u32("editor_command"); + palette[int(TextEditor::PaletteIndex::Number)] = get_color_u32("editor_warning"); + palette[int(TextEditor::PaletteIndex::String)] = get_color_u32("editor_string"); + palette[int(TextEditor::PaletteIndex::CharLiteral)] = get_color_u32("editor_string"); + palette[int(TextEditor::PaletteIndex::Punctuation)] = get_color_u32("editor_main"); + palette[int(TextEditor::PaletteIndex::Preprocessor)] = get_color_u32("editor_emphasis"); + palette[int(TextEditor::PaletteIndex::Identifier)] = get_color_u32("editor_main"); + palette[int(TextEditor::PaletteIndex::KnownIdentifier)] = get_color_u32("editor_link"); + palette[int(TextEditor::PaletteIndex::PreprocIdentifier)] = get_color_u32("editor_link"); - palette[int(TextEditor::PaletteIndex::Comment)] = get_color_u32(current, "editor_comment"); - palette[int(TextEditor::PaletteIndex::MultiLineComment)] = - get_color_u32(current, "editor_comment"); + palette[int(TextEditor::PaletteIndex::Comment)] = get_color_u32("editor_comment"); + palette[int(TextEditor::PaletteIndex::MultiLineComment)] = get_color_u32("editor_comment"); - palette[int(TextEditor::PaletteIndex::Background)] = - get_color_u32(current, "editor_background"); - palette[int(TextEditor::PaletteIndex::Cursor)] = get_color_u32(current, "cursor"); + palette[int(TextEditor::PaletteIndex::Background)] = get_color_u32("editor_background"); + palette[int(TextEditor::PaletteIndex::Cursor)] = get_color_u32("cursor"); - palette[int(TextEditor::PaletteIndex::Selection)] = get_color_u32(current, "editor_selected"); - palette[int(TextEditor::PaletteIndex::ErrorMarker)] = get_color_u32(current, "editor_error"); - palette[int(TextEditor::PaletteIndex::Breakpoint)] = get_color_u32(current, "editor_error"); + palette[int(TextEditor::PaletteIndex::Selection)] = get_color_u32("editor_selected"); + palette[int(TextEditor::PaletteIndex::ErrorMarker)] = get_color_u32("editor_error"); + palette[int(TextEditor::PaletteIndex::Breakpoint)] = get_color_u32("editor_error"); - palette[int(TextEditor::PaletteIndex::LineNumber)] = - get_color_u32(current, "editor_line_number"); + palette[int(TextEditor::PaletteIndex::LineNumber)] = get_color_u32("editor_line_number"); - palette[int(TextEditor::PaletteIndex::CurrentLineFill)] = - get_color_u32(current, "surface_variant"); - palette[int(TextEditor::PaletteIndex::CurrentLineFillInactive)] = - get_color_u32(current, "surface"); - - palette[int(TextEditor::PaletteIndex::CurrentLineEdge)] = - get_color_u32(current, "border_focused"); + palette[int(TextEditor::PaletteIndex::CurrentLineFill)] = get_color_u32("surface_variant"); + palette[int(TextEditor::PaletteIndex::CurrentLineFillInactive)] = get_color_u32("surface"); + palette[int(TextEditor::PaletteIndex::CurrentLineEdge)] = get_color_u32("border_focused"); editor.SetPalette(palette); } void apply_to_imgui(const clrsync::core::palette ¤t) { - auto getColor = [&](const std::string &key) -> ImVec4 { - const auto &col = current.get_color(key); - const uint32_t hex = col.hex(); - return {((hex >> 24) & 0xFF) / 255.0f, ((hex >> 16) & 0xFF) / 255.0f, - ((hex >> 8) & 0xFF) / 255.0f, ((hex) & 0xFF) / 255.0f}; - }; - - ImGuiStyle &style = ImGui::GetStyle(); - - const ImVec4 bg = getColor("background"); - const ImVec4 onBg = getColor("on_background"); - const ImVec4 surface = getColor("surface"); - const ImVec4 onSurface = getColor("on_surface"); - const ImVec4 surfaceVariant = getColor("surface_variant"); - const ImVec4 onSurfaceVariant = getColor("on_surface_variant"); - const ImVec4 fg = getColor("foreground"); - const ImVec4 fgInactive = getColor("editor_inactive"); - const ImVec4 accent = getColor("accent"); - const ImVec4 border = getColor("border"); - - const ImVec4 error = getColor("error"); - const ImVec4 onError = getColor("on_error"); - const ImVec4 success = getColor("success"); - const ImVec4 onSuccess = getColor("on_success"); - const ImVec4 warning = getColor("warning"); - const ImVec4 onWarning = getColor("on_warning"); - const ImVec4 info = getColor("info"); - const ImVec4 onInfo = getColor("on_info"); - - style.Colors[ImGuiCol_WindowBg] = bg; - style.Colors[ImGuiCol_ChildBg] = surface; - style.Colors[ImGuiCol_PopupBg] = surface; - - style.Colors[ImGuiCol_Border] = border; - style.Colors[ImGuiCol_BorderShadow] = ImVec4(0, 0, 0, 0); - - style.Colors[ImGuiCol_Text] = onSurface; - style.Colors[ImGuiCol_TextDisabled] = onSurfaceVariant; - style.Colors[ImGuiCol_TextSelectedBg] = accent; - - style.Colors[ImGuiCol_Header] = surfaceVariant; - style.Colors[ImGuiCol_HeaderHovered] = ImVec4(accent.x, accent.y, accent.z, 0.8f); - style.Colors[ImGuiCol_HeaderActive] = accent; - - style.Colors[ImGuiCol_Button] = surfaceVariant; - style.Colors[ImGuiCol_ButtonHovered] = ImVec4(accent.x, accent.y, accent.z, 0.6f); - style.Colors[ImGuiCol_ButtonActive] = accent; - - style.Colors[ImGuiCol_FrameBg] = surfaceVariant; - style.Colors[ImGuiCol_FrameBgHovered] = - ImVec4(surfaceVariant.x * 1.1f, surfaceVariant.y * 1.1f, surfaceVariant.z * 1.1f, 1.0f); - style.Colors[ImGuiCol_FrameBgActive] = - ImVec4(surfaceVariant.x * 1.2f, surfaceVariant.y * 1.2f, surfaceVariant.z * 1.2f, 1.0f); - - style.Colors[ImGuiCol_TitleBg] = surface; - style.Colors[ImGuiCol_TitleBgActive] = surfaceVariant; - style.Colors[ImGuiCol_TitleBgCollapsed] = surface; - - style.Colors[ImGuiCol_ScrollbarBg] = surface; - style.Colors[ImGuiCol_ScrollbarGrab] = surfaceVariant; - style.Colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(accent.x, accent.y, accent.z, 0.6f); - style.Colors[ImGuiCol_ScrollbarGrabActive] = accent; - - style.Colors[ImGuiCol_SliderGrab] = accent; - style.Colors[ImGuiCol_SliderGrabActive] = - ImVec4(accent.x * 1.2f, accent.y * 1.2f, accent.z * 1.2f, 1.0f); - - style.Colors[ImGuiCol_CheckMark] = accent; - style.Colors[ImGuiCol_ResizeGrip] = surfaceVariant; - style.Colors[ImGuiCol_ResizeGripHovered] = ImVec4(accent.x, accent.y, accent.z, 0.6f); - style.Colors[ImGuiCol_ResizeGripActive] = accent; - - style.Colors[ImGuiCol_Tab] = surface; - style.Colors[ImGuiCol_TabHovered] = ImVec4(accent.x, accent.y, accent.z, 0.8f); - style.Colors[ImGuiCol_TabActive] = surfaceVariant; - style.Colors[ImGuiCol_TabUnfocused] = surface; - style.Colors[ImGuiCol_TabUnfocusedActive] = surfaceVariant; - style.Colors[ImGuiCol_TabSelectedOverline] = accent; - - style.Colors[ImGuiCol_TableHeaderBg] = surfaceVariant; - style.Colors[ImGuiCol_TableBorderStrong] = border; - style.Colors[ImGuiCol_TableBorderLight] = - ImVec4(border.x * 0.7f, border.y * 0.7f, border.z * 0.7f, border.w); - - style.Colors[ImGuiCol_TableRowBg] = ImVec4(0, 0, 0, 0); - style.Colors[ImGuiCol_TableRowBgAlt] = - ImVec4(onSurfaceVariant.x, onSurfaceVariant.y, onSurfaceVariant.z, 0.06f); - - style.Colors[ImGuiCol_Separator] = border; - style.Colors[ImGuiCol_SeparatorHovered] = accent; - style.Colors[ImGuiCol_SeparatorActive] = accent; - - style.Colors[ImGuiCol_MenuBarBg] = surface; - - style.Colors[ImGuiCol_DockingPreview] = ImVec4(accent.x, accent.y, accent.z, 0.7f); - style.Colors[ImGuiCol_DockingEmptyBg] = bg; + clrsync::gui::theme::set_theme(current); } } // namespace theme_applier diff --git a/src/gui/controllers/theme_applier.hpp b/src/gui/controllers/theme_applier.hpp index 7cc5f67..6bf5d4a 100644 --- a/src/gui/controllers/theme_applier.hpp +++ b/src/gui/controllers/theme_applier.hpp @@ -7,6 +7,7 @@ namespace theme_applier { void apply_to_imgui(const clrsync::core::palette &pal); + void apply_to_editor(TextEditor &editor, const clrsync::core::palette &pal); } // namespace theme_applier diff --git a/src/gui/layout/main_layout.cpp b/src/gui/layout/main_layout.cpp index a8cdd6a..72f5549 100644 --- a/src/gui/layout/main_layout.cpp +++ b/src/gui/layout/main_layout.cpp @@ -5,40 +5,44 @@ namespace clrsync::gui::layout { +namespace +{ +constexpr float TOPBAR_HEIGHT = 36.0f; +constexpr float TOPBAR_PADDING_X = 12.0f; +constexpr float TOPBAR_PADDING_Y = 4.0f; +constexpr float BUTTON_SPACING = 8.0f; +} + void main_layout::render_menu_bar() { ImGuiViewport *vp = ImGui::GetMainViewport(); - const float bar_height = 30.0f; - ImGui::SetNextWindowPos(vp->Pos); - ImGui::SetNextWindowSize(ImVec2(vp->Size.x, bar_height)); + ImGui::SetNextWindowSize(ImVec2(vp->Size.x, TOPBAR_HEIGHT)); ImGui::SetNextWindowViewport(vp->ID); ImGuiWindowFlags flags = ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings; - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8, 2)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(TOPBAR_PADDING_X, TOPBAR_PADDING_Y)); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(8, 3)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(BUTTON_SPACING, 6.0f)); ImGui::Begin("##TopBar", nullptr, flags); - ImGuiStyle &style = ImGui::GetStyle(); - style.FrameBorderSize = 1.0f; - const char *settings_label = "Settings"; const char *about_label = "About"; + ImGuiStyle &style = ImGui::GetStyle(); ImVec2 settings_size = ImGui::CalcTextSize(settings_label); ImVec2 about_size = ImGui::CalcTextSize(about_label); float total_width = settings_size.x + style.FramePadding.x * 2.0f + about_size.x + style.FramePadding.x * 2.0f + style.ItemSpacing.x; - float pos_x = ImGui::GetWindowWidth() - total_width - style.WindowPadding.x; + float pos_x = ImGui::GetWindowWidth() - total_width - TOPBAR_PADDING_X; float button_height = ImGui::GetFrameHeight(); float window_height = ImGui::GetWindowHeight(); @@ -47,29 +51,24 @@ void main_layout::render_menu_bar() ImGui::SetCursorPos(ImVec2(pos_x, center_y)); if (ImGui::Button(settings_label)) - { m_show_settings = true; - } ImGui::SameLine(); ImGui::SetCursorPosY(center_y); if (ImGui::Button(about_label)) - { m_show_about = true; - } ImGui::End(); - ImGui::PopStyleVar(4); } void main_layout::setup_dockspace(bool &first_time) { const ImGuiViewport *viewport = ImGui::GetMainViewport(); - const float topbar_height = 32.0f; - ImGui::SetNextWindowPos(ImVec2(viewport->Pos.x, viewport->Pos.y + topbar_height)); - ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, viewport->Size.y - topbar_height)); + + ImGui::SetNextWindowPos(ImVec2(viewport->Pos.x, viewport->Pos.y + TOPBAR_HEIGHT)); + ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, viewport->Size.y - TOPBAR_HEIGHT)); ImGui::SetNextWindowViewport(viewport->ID); constexpr ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | @@ -80,9 +79,8 @@ void main_layout::setup_dockspace(bool &first_time) ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(8, 3)); ImGui::Begin("MainDockSpace", nullptr, flags); - ImGui::PopStyleVar(4); + ImGui::PopStyleVar(3); ImGuiID dockspace_id = ImGui::GetID("MainDockSpace"); @@ -95,13 +93,11 @@ void main_layout::setup_dockspace(bool &first_time) ImGui::DockBuilderSetNodeSize(dockspace_id, viewport->Size); ImGuiID center, right; - ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Right, 0.5f, &right, ¢er); + ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Right, 0.40f, &right, ¢er); ImGuiDockNode *center_node = ImGui::DockBuilderGetNode(center); if (center_node) - { center_node->LocalFlags |= ImGuiDockNodeFlags_CentralNode; - } ImGui::DockBuilderDockWindow("Color Schemes", right); ImGui::DockBuilderDockWindow("Color Preview", center); diff --git a/src/gui/theme/app_theme.cpp b/src/gui/theme/app_theme.cpp new file mode 100644 index 0000000..e1c4a59 --- /dev/null +++ b/src/gui/theme/app_theme.cpp @@ -0,0 +1,317 @@ +#include "app_theme.hpp" +#include + +namespace clrsync::gui::theme +{ +ImVec4 color_utils::from_hex(uint32_t hex) +{ + return { + ((hex >> 24) & 0xFF) / 255.0f, + ((hex >> 16) & 0xFF) / 255.0f, + ((hex >> 8) & 0xFF) / 255.0f, + (hex & 0xFF) / 255.0f + }; +} + +uint32_t color_utils::to_imgui_u32(uint32_t hex) +{ + const uint32_t r = (hex >> 24) & 0xFF; + const uint32_t g = (hex >> 16) & 0xFF; + const uint32_t b = (hex >> 8) & 0xFF; + const uint32_t a = hex & 0xFF; + return (a << 24) | (b << 16) | (g << 8) | r; +} + +ImVec4 color_utils::with_alpha(const ImVec4 &color, float alpha) +{ + return {color.x, color.y, color.z, std::clamp(alpha, 0.0f, 1.0f)}; +} + +ImVec4 color_utils::lighten(const ImVec4 &color, float amount) +{ + return { + std::clamp(color.x + (1.0f - color.x) * amount, 0.0f, 1.0f), + std::clamp(color.y + (1.0f - color.y) * amount, 0.0f, 1.0f), + std::clamp(color.z + (1.0f - color.z) * amount, 0.0f, 1.0f), + color.w + }; +} + +ImVec4 color_utils::darken(const ImVec4 &color, float amount) +{ + return { + std::clamp(color.x * (1.0f - amount), 0.0f, 1.0f), + std::clamp(color.y * (1.0f - amount), 0.0f, 1.0f), + std::clamp(color.z * (1.0f - amount), 0.0f, 1.0f), + color.w + }; +} + +ImVec4 color_utils::blend(const ImVec4 &a, const ImVec4 &b, float t) +{ + t = std::clamp(t, 0.0f, 1.0f); + return { + a.x + (b.x - a.x) * t, + a.y + (b.y - a.y) * t, + a.z + (b.z - a.z) * t, + a.w + (b.w - a.w) * t + }; +} + +app_theme::app_theme(const core::palette &palette) +{ + apply_palette(palette); +} + +ImVec4 app_theme::get_palette_color(const std::string &key, + const std::string &fallback) const +{ + auto colors = m_palette.colors(); + if (colors.empty()) + return ImVec4(1.0f, 1.0f, 1.0f, 1.0f); + + auto it = colors.find(key); + if (it == colors.end() && !fallback.empty()) + it = colors.find(fallback); + + if (it != colors.end()) + return color_utils::from_hex(it->second.hex()); + + return ImVec4(1.0f, 1.0f, 1.0f, 1.0f); +} + +void app_theme::apply_palette(const core::palette &palette) +{ + m_palette = palette; + + if (palette.colors().empty()) + return; + + const ImVec4 background = get_palette_color("background"); + const ImVec4 on_background = get_palette_color("on_background"); + const ImVec4 surface = get_palette_color("surface"); + const ImVec4 on_surface = get_palette_color("on_surface"); + const ImVec4 surface_variant = get_palette_color("surface_variant"); + const ImVec4 on_surface_variant = get_palette_color("on_surface_variant"); + const ImVec4 border_base = get_palette_color("border"); + const ImVec4 border_focused_base = get_palette_color("border_focused"); + const ImVec4 accent_base = get_palette_color("accent"); + const ImVec4 accent_secondary_base = get_palette_color("accent_secondary"); + + const ImVec4 success_base = get_palette_color("success"); + const ImVec4 on_success_base = get_palette_color("on_success"); + const ImVec4 warning_base = get_palette_color("warning"); + const ImVec4 on_warning_base = get_palette_color("on_warning"); + const ImVec4 error_base = get_palette_color("error"); + const ImVec4 on_error_base = get_palette_color("on_error"); + const ImVec4 info_base = get_palette_color("info"); + const ImVec4 on_info_base = get_palette_color("on_info"); + + m_window_bg = background; + m_child_bg = color_utils::with_alpha(surface, 0.0f); // Transparent child bg + m_popup_bg = color_utils::with_alpha(surface_variant, 0.98f); + m_modal_dim_bg = ImVec4(0.0f, 0.0f, 0.0f, 0.5f); + + m_text = on_surface; + m_text_disabled = on_surface_variant; + m_text_selected_bg = color_utils::with_alpha(accent_base, 0.4f); + + m_border = border_base; + m_border_focused = border_focused_base; + m_separator = border_base; + + m_button = surface_variant; + m_button_hovered = color_utils::with_alpha(accent_base, 0.7f); + m_button_active = accent_base; + + m_header = surface_variant; + m_header_hovered = color_utils::with_alpha(accent_base, 0.7f); + m_header_active = accent_base; + + m_frame_bg = surface_variant; + m_frame_bg_hovered = color_utils::lighten(surface_variant, 0.08f); + m_frame_bg_active = color_utils::lighten(surface_variant, 0.15f); + + m_tab = surface; + m_tab_hovered = color_utils::with_alpha(accent_base, 0.7f); + m_tab_active = surface_variant; + m_tab_unfocused = surface; + m_tab_unfocused_active = surface_variant; + + m_scrollbar_bg = color_utils::with_alpha(surface, 0.5f); + m_scrollbar_grab = surface_variant; + m_scrollbar_grab_hovered = color_utils::with_alpha(accent_base, 0.7f); + m_scrollbar_grab_active = accent_base; + + m_slider_grab = accent_base; + m_slider_grab_active = color_utils::lighten(accent_base, 0.2f); + + m_table_header_bg = surface_variant; + m_table_border_strong = border_base; + m_table_border_light = color_utils::darken(border_base, 0.3f); + m_table_row_bg = ImVec4(0, 0, 0, 0); + m_table_row_bg_alt = color_utils::with_alpha(on_surface_variant, 0.04f); + + m_title_bg = surface; + m_title_bg_active = surface_variant; + m_title_bg_collapsed = color_utils::with_alpha(surface, 0.75f); + + m_docking_preview = color_utils::with_alpha(accent_base, 0.7f); + m_docking_empty_bg = background; + + m_accent = accent_base; + m_accent_secondary = accent_secondary_base; + + m_success = success_base; + m_success_hovered = color_utils::lighten(success_base, 0.2f); + m_success_active = color_utils::darken(success_base, 0.2f); + m_on_success = on_success_base; + + m_warning = warning_base; + m_warning_hovered = color_utils::lighten(warning_base, 0.2f); + m_warning_active = color_utils::darken(warning_base, 0.2f); + m_on_warning = on_warning_base; + + m_error = error_base; + m_error_hovered = color_utils::lighten(error_base, 0.2f); + m_error_active = color_utils::darken(error_base, 0.2f); + m_on_error = on_error_base; + + m_info = info_base; + m_info_hovered = color_utils::lighten(info_base, 0.2f); + m_info_active = color_utils::darken(info_base, 0.2f); + m_on_info = on_info_base; + + m_checkmark = accent_base; + m_resize_grip = color_utils::with_alpha(surface_variant, 0.5f); + m_resize_grip_hovered = color_utils::with_alpha(accent_base, 0.7f); + m_resize_grip_active = accent_base; + + m_autocomplete_bg = color_utils::with_alpha(surface_variant, 0.98f); + m_autocomplete_border = border_focused_base; + m_autocomplete_selected = color_utils::with_alpha(accent_base, 0.9f); + m_autocomplete_text = on_surface; + m_autocomplete_selected_text = on_surface; + m_autocomplete_dim_text = on_surface_variant; +} + +void app_theme::apply_style_vars() const +{ + ImGuiStyle &style = ImGui::GetStyle(); + + style.WindowRounding = WINDOW_ROUNDING; + style.ChildRounding = FRAME_ROUNDING; + style.FrameRounding = FRAME_ROUNDING; + style.PopupRounding = POPUP_ROUNDING; + style.ScrollbarRounding = SCROLLBAR_ROUNDING; + style.GrabRounding = GRAB_ROUNDING; + style.TabRounding = TAB_ROUNDING; + + style.FrameBorderSize = FRAME_BORDER_SIZE; + style.WindowBorderSize = WINDOW_BORDER_SIZE; + style.PopupBorderSize = POPUP_BORDER_SIZE; + + style.WindowPadding = WINDOW_PADDING; + style.FramePadding = FRAME_PADDING; + style.ItemSpacing = ITEM_SPACING; + style.ItemInnerSpacing = ITEM_INNER_SPACING; + + style.ScrollbarSize = 12.0f; + style.GrabMinSize = 10.0f; + style.SeparatorTextBorderSize = 1.0f; + style.IndentSpacing = 20.0f; +} + +void app_theme::apply_to_imgui() const +{ + apply_style_vars(); + + ImGuiStyle &style = ImGui::GetStyle(); + + style.Colors[ImGuiCol_WindowBg] = m_window_bg; + style.Colors[ImGuiCol_ChildBg] = m_child_bg; + style.Colors[ImGuiCol_PopupBg] = m_popup_bg; + style.Colors[ImGuiCol_ModalWindowDimBg] = m_modal_dim_bg; + + style.Colors[ImGuiCol_Text] = m_text; + style.Colors[ImGuiCol_TextDisabled] = m_text_disabled; + style.Colors[ImGuiCol_TextSelectedBg] = m_text_selected_bg; + + style.Colors[ImGuiCol_Border] = m_border; + style.Colors[ImGuiCol_BorderShadow] = ImVec4(0, 0, 0, 0); + style.Colors[ImGuiCol_Separator] = m_separator; + style.Colors[ImGuiCol_SeparatorHovered] = m_accent; + style.Colors[ImGuiCol_SeparatorActive] = m_accent; + + style.Colors[ImGuiCol_Button] = m_button; + style.Colors[ImGuiCol_ButtonHovered] = m_button_hovered; + style.Colors[ImGuiCol_ButtonActive] = m_button_active; + + style.Colors[ImGuiCol_Header] = m_header; + style.Colors[ImGuiCol_HeaderHovered] = m_header_hovered; + style.Colors[ImGuiCol_HeaderActive] = m_header_active; + + style.Colors[ImGuiCol_FrameBg] = m_frame_bg; + style.Colors[ImGuiCol_FrameBgHovered] = m_frame_bg_hovered; + style.Colors[ImGuiCol_FrameBgActive] = m_frame_bg_active; + + style.Colors[ImGuiCol_Tab] = m_tab; + style.Colors[ImGuiCol_TabHovered] = m_tab_hovered; + style.Colors[ImGuiCol_TabActive] = m_tab_active; + style.Colors[ImGuiCol_TabUnfocused] = m_tab_unfocused; + style.Colors[ImGuiCol_TabUnfocusedActive] = m_tab_unfocused_active; + style.Colors[ImGuiCol_TabSelectedOverline] = m_accent; + + style.Colors[ImGuiCol_ScrollbarBg] = m_scrollbar_bg; + style.Colors[ImGuiCol_ScrollbarGrab] = m_scrollbar_grab; + style.Colors[ImGuiCol_ScrollbarGrabHovered] = m_scrollbar_grab_hovered; + style.Colors[ImGuiCol_ScrollbarGrabActive] = m_scrollbar_grab_active; + + style.Colors[ImGuiCol_SliderGrab] = m_slider_grab; + style.Colors[ImGuiCol_SliderGrabActive] = m_slider_grab_active; + + style.Colors[ImGuiCol_TableHeaderBg] = m_table_header_bg; + style.Colors[ImGuiCol_TableBorderStrong] = m_table_border_strong; + style.Colors[ImGuiCol_TableBorderLight] = m_table_border_light; + style.Colors[ImGuiCol_TableRowBg] = m_table_row_bg; + style.Colors[ImGuiCol_TableRowBgAlt] = m_table_row_bg_alt; + + style.Colors[ImGuiCol_TitleBg] = m_title_bg; + style.Colors[ImGuiCol_TitleBgActive] = m_title_bg_active; + style.Colors[ImGuiCol_TitleBgCollapsed] = m_title_bg_collapsed; + + style.Colors[ImGuiCol_MenuBarBg] = m_window_bg; + + style.Colors[ImGuiCol_DockingPreview] = m_docking_preview; + style.Colors[ImGuiCol_DockingEmptyBg] = m_docking_empty_bg; + + style.Colors[ImGuiCol_CheckMark] = m_checkmark; + style.Colors[ImGuiCol_ResizeGrip] = m_resize_grip; + style.Colors[ImGuiCol_ResizeGripHovered] = m_resize_grip_hovered; + style.Colors[ImGuiCol_ResizeGripActive] = m_resize_grip_active; + + style.Colors[ImGuiCol_NavHighlight] = m_accent; + style.Colors[ImGuiCol_NavWindowingHighlight] = color_utils::with_alpha(m_accent, 0.7f); + style.Colors[ImGuiCol_NavWindowingDimBg] = m_modal_dim_bg; + + style.Colors[ImGuiCol_PlotLines] = m_accent; + style.Colors[ImGuiCol_PlotLinesHovered] = m_accent; + style.Colors[ImGuiCol_PlotHistogram] = m_accent; + style.Colors[ImGuiCol_PlotHistogramHovered] = color_utils::lighten(m_accent, 0.2f); +} + + +static app_theme g_current_theme; + +app_theme ¤t_theme() +{ + return g_current_theme; +} + +void set_theme(const core::palette &palette) +{ + g_current_theme.apply_palette(palette); + g_current_theme.apply_to_imgui(); +} + +} // namespace clrsync::gui::theme diff --git a/src/gui/theme/app_theme.hpp b/src/gui/theme/app_theme.hpp new file mode 100644 index 0000000..b7972b0 --- /dev/null +++ b/src/gui/theme/app_theme.hpp @@ -0,0 +1,235 @@ +#ifndef CLRSYNC_GUI_THEME_APP_THEME_HPP +#define CLRSYNC_GUI_THEME_APP_THEME_HPP + +#include "core/palette/palette.hpp" +#include "imgui.h" +#include + +namespace clrsync::gui::theme +{ + +struct color_utils +{ + static ImVec4 from_hex(uint32_t hex); + static uint32_t to_imgui_u32(uint32_t hex); + static ImVec4 with_alpha(const ImVec4 &color, float alpha); + static ImVec4 lighten(const ImVec4 &color, float amount); + static ImVec4 darken(const ImVec4 &color, float amount); + static ImVec4 blend(const ImVec4 &a, const ImVec4 &b, float t); +}; + +class app_theme +{ + public: + app_theme() = default; + explicit app_theme(const core::palette &palette); + + void apply_palette(const core::palette &palette); + + ImVec4 window_bg() const { return m_window_bg; } + ImVec4 child_bg() const { return m_child_bg; } + ImVec4 popup_bg() const { return m_popup_bg; } + ImVec4 modal_dim_bg() const { return m_modal_dim_bg; } + + ImVec4 text() const { return m_text; } + ImVec4 text_disabled() const { return m_text_disabled; } + ImVec4 text_selected_bg() const { return m_text_selected_bg; } + + ImVec4 border() const { return m_border; } + ImVec4 border_focused() const { return m_border_focused; } + ImVec4 separator() const { return m_separator; } + + ImVec4 button() const { return m_button; } + ImVec4 button_hovered() const { return m_button_hovered; } + ImVec4 button_active() const { return m_button_active; } + + ImVec4 header() const { return m_header; } + ImVec4 header_hovered() const { return m_header_hovered; } + ImVec4 header_active() const { return m_header_active; } + + ImVec4 frame_bg() const { return m_frame_bg; } + ImVec4 frame_bg_hovered() const { return m_frame_bg_hovered; } + ImVec4 frame_bg_active() const { return m_frame_bg_active; } + + ImVec4 tab() const { return m_tab; } + ImVec4 tab_hovered() const { return m_tab_hovered; } + ImVec4 tab_active() const { return m_tab_active; } + ImVec4 tab_unfocused() const { return m_tab_unfocused; } + ImVec4 tab_unfocused_active() const { return m_tab_unfocused_active; } + + ImVec4 scrollbar_bg() const { return m_scrollbar_bg; } + ImVec4 scrollbar_grab() const { return m_scrollbar_grab; } + ImVec4 scrollbar_grab_hovered() const { return m_scrollbar_grab_hovered; } + ImVec4 scrollbar_grab_active() const { return m_scrollbar_grab_active; } + + ImVec4 slider_grab() const { return m_slider_grab; } + ImVec4 slider_grab_active() const { return m_slider_grab_active; } + + ImVec4 table_header_bg() const { return m_table_header_bg; } + ImVec4 table_border_strong() const { return m_table_border_strong; } + ImVec4 table_border_light() const { return m_table_border_light; } + ImVec4 table_row_bg() const { return m_table_row_bg; } + ImVec4 table_row_bg_alt() const { return m_table_row_bg_alt; } + + ImVec4 title_bg() const { return m_title_bg; } + ImVec4 title_bg_active() const { return m_title_bg_active; } + ImVec4 title_bg_collapsed() const { return m_title_bg_collapsed; } + + ImVec4 docking_preview() const { return m_docking_preview; } + ImVec4 docking_empty_bg() const { return m_docking_empty_bg; } + + ImVec4 accent() const { return m_accent; } + ImVec4 accent_secondary() const { return m_accent_secondary; } + + ImVec4 success() const { return m_success; } + ImVec4 success_hovered() const { return m_success_hovered; } + ImVec4 success_active() const { return m_success_active; } + ImVec4 on_success() const { return m_on_success; } + + ImVec4 warning() const { return m_warning; } + ImVec4 warning_hovered() const { return m_warning_hovered; } + ImVec4 warning_active() const { return m_warning_active; } + ImVec4 on_warning() const { return m_on_warning; } + + ImVec4 error() const { return m_error; } + ImVec4 error_hovered() const { return m_error_hovered; } + ImVec4 error_active() const { return m_error_active; } + ImVec4 on_error() const { return m_on_error; } + + ImVec4 info() const { return m_info; } + ImVec4 info_hovered() const { return m_info_hovered; } + ImVec4 info_active() const { return m_info_active; } + ImVec4 on_info() const { return m_on_info; } + + ImVec4 checkmark() const { return m_checkmark; } + ImVec4 resize_grip() const { return m_resize_grip; } + ImVec4 resize_grip_hovered() const { return m_resize_grip_hovered; } + ImVec4 resize_grip_active() const { return m_resize_grip_active; } + + ImVec4 autocomplete_bg() const { return m_autocomplete_bg; } + ImVec4 autocomplete_border() const { return m_autocomplete_border; } + ImVec4 autocomplete_selected() const { return m_autocomplete_selected; } + ImVec4 autocomplete_text() const { return m_autocomplete_text; } + ImVec4 autocomplete_selected_text() const { return m_autocomplete_selected_text; } + ImVec4 autocomplete_dim_text() const { return m_autocomplete_dim_text; } + + static constexpr float WINDOW_ROUNDING = 6.0f; + static constexpr float FRAME_ROUNDING = 4.0f; + static constexpr float POPUP_ROUNDING = 6.0f; + static constexpr float SCROLLBAR_ROUNDING = 4.0f; + static constexpr float GRAB_ROUNDING = 4.0f; + static constexpr float TAB_ROUNDING = 4.0f; + + static constexpr float FRAME_BORDER_SIZE = 1.0f; + static constexpr float WINDOW_BORDER_SIZE = 1.0f; + static constexpr float POPUP_BORDER_SIZE = 1.0f; + + static constexpr ImVec2 WINDOW_PADDING{10.0f, 10.0f}; + static constexpr ImVec2 FRAME_PADDING{8.0f, 5.0f}; + static constexpr ImVec2 ITEM_SPACING{8.0f, 6.0f}; + static constexpr ImVec2 ITEM_INNER_SPACING{6.0f, 4.0f}; + + void apply_to_imgui() const; + void apply_style_vars() const; + + private: + ImVec4 get_palette_color(const std::string &key, + const std::string &fallback = "") const; + + core::palette m_palette; + + ImVec4 m_window_bg{0.067f, 0.067f, 0.067f, 1.0f}; + ImVec4 m_child_bg{0.067f, 0.067f, 0.067f, 1.0f}; + ImVec4 m_popup_bg{0.098f, 0.098f, 0.098f, 0.98f}; + ImVec4 m_modal_dim_bg{0.0f, 0.0f, 0.0f, 0.5f}; + + ImVec4 m_text{0.83f, 0.83f, 0.83f, 1.0f}; + ImVec4 m_text_disabled{0.52f, 0.60f, 0.60f, 1.0f}; + ImVec4 m_text_selected_bg{0.60f, 0.52f, 0.32f, 0.5f}; + + ImVec4 m_border{0.14f, 0.14f, 0.14f, 1.0f}; + ImVec4 m_border_focused{0.18f, 0.18f, 0.18f, 1.0f}; + ImVec4 m_separator{0.14f, 0.14f, 0.14f, 1.0f}; + + ImVec4 m_button{0.098f, 0.098f, 0.098f, 1.0f}; + ImVec4 m_button_hovered{0.60f, 0.52f, 0.32f, 0.7f}; + ImVec4 m_button_active{0.60f, 0.52f, 0.32f, 1.0f}; + + ImVec4 m_header{0.098f, 0.098f, 0.098f, 1.0f}; + ImVec4 m_header_hovered{0.60f, 0.52f, 0.32f, 0.7f}; + ImVec4 m_header_active{0.60f, 0.52f, 0.32f, 1.0f}; + + ImVec4 m_frame_bg{0.098f, 0.098f, 0.098f, 1.0f}; + ImVec4 m_frame_bg_hovered{0.12f, 0.12f, 0.12f, 1.0f}; + ImVec4 m_frame_bg_active{0.14f, 0.14f, 0.14f, 1.0f}; + + ImVec4 m_tab{0.067f, 0.067f, 0.067f, 1.0f}; + ImVec4 m_tab_hovered{0.60f, 0.52f, 0.32f, 0.7f}; + ImVec4 m_tab_active{0.098f, 0.098f, 0.098f, 1.0f}; + ImVec4 m_tab_unfocused{0.067f, 0.067f, 0.067f, 1.0f}; + ImVec4 m_tab_unfocused_active{0.098f, 0.098f, 0.098f, 1.0f}; + + ImVec4 m_scrollbar_bg{0.067f, 0.067f, 0.067f, 0.5f}; + ImVec4 m_scrollbar_grab{0.14f, 0.14f, 0.14f, 1.0f}; + ImVec4 m_scrollbar_grab_hovered{0.60f, 0.52f, 0.32f, 0.7f}; + ImVec4 m_scrollbar_grab_active{0.60f, 0.52f, 0.32f, 1.0f}; + + ImVec4 m_slider_grab{0.60f, 0.52f, 0.32f, 1.0f}; + ImVec4 m_slider_grab_active{0.72f, 0.62f, 0.38f, 1.0f}; + + ImVec4 m_table_header_bg{0.098f, 0.098f, 0.098f, 1.0f}; + ImVec4 m_table_border_strong{0.14f, 0.14f, 0.14f, 1.0f}; + ImVec4 m_table_border_light{0.10f, 0.10f, 0.10f, 1.0f}; + ImVec4 m_table_row_bg{0.0f, 0.0f, 0.0f, 0.0f}; + ImVec4 m_table_row_bg_alt{0.83f, 0.83f, 0.83f, 0.04f}; + + ImVec4 m_title_bg{0.067f, 0.067f, 0.067f, 1.0f}; + ImVec4 m_title_bg_active{0.098f, 0.098f, 0.098f, 1.0f}; + ImVec4 m_title_bg_collapsed{0.067f, 0.067f, 0.067f, 0.75f}; + + ImVec4 m_docking_preview{0.60f, 0.52f, 0.32f, 0.7f}; + ImVec4 m_docking_empty_bg{0.067f, 0.067f, 0.067f, 1.0f}; + + ImVec4 m_accent{0.60f, 0.52f, 0.32f, 1.0f}; + ImVec4 m_accent_secondary{0.60f, 0.52f, 0.32f, 1.0f}; + + ImVec4 m_success{0.40f, 0.54f, 0.32f, 1.0f}; + ImVec4 m_success_hovered{0.48f, 0.65f, 0.38f, 1.0f}; + ImVec4 m_success_active{0.32f, 0.43f, 0.26f, 1.0f}; + ImVec4 m_on_success{0.82f, 0.82f, 0.82f, 1.0f}; + + ImVec4 m_warning{0.71f, 0.47f, 0.22f, 1.0f}; + ImVec4 m_warning_hovered{0.85f, 0.56f, 0.26f, 1.0f}; + ImVec4 m_warning_active{0.57f, 0.38f, 0.17f, 1.0f}; + ImVec4 m_on_warning{0.82f, 0.82f, 0.82f, 1.0f}; + + ImVec4 m_error{0.67f, 0.31f, 0.29f, 1.0f}; + ImVec4 m_error_hovered{0.80f, 0.37f, 0.35f, 1.0f}; + ImVec4 m_error_active{0.53f, 0.25f, 0.23f, 1.0f}; + ImVec4 m_on_error{0.82f, 0.82f, 0.82f, 1.0f}; + + ImVec4 m_info{0.23f, 0.54f, 0.55f, 1.0f}; + ImVec4 m_info_hovered{0.27f, 0.65f, 0.66f, 1.0f}; + ImVec4 m_info_active{0.18f, 0.43f, 0.44f, 1.0f}; + ImVec4 m_on_info{0.82f, 0.82f, 0.82f, 1.0f}; + + ImVec4 m_checkmark{0.60f, 0.52f, 0.32f, 1.0f}; + ImVec4 m_resize_grip{0.14f, 0.14f, 0.14f, 0.5f}; + ImVec4 m_resize_grip_hovered{0.60f, 0.52f, 0.32f, 0.7f}; + ImVec4 m_resize_grip_active{0.60f, 0.52f, 0.32f, 1.0f}; + + ImVec4 m_autocomplete_bg{0.098f, 0.098f, 0.098f, 0.98f}; + ImVec4 m_autocomplete_border{0.25f, 0.25f, 0.28f, 1.0f}; + ImVec4 m_autocomplete_selected{0.60f, 0.52f, 0.32f, 0.9f}; + ImVec4 m_autocomplete_text{0.85f, 0.85f, 0.9f, 1.0f}; + ImVec4 m_autocomplete_selected_text{1.0f, 1.0f, 1.0f, 1.0f}; + ImVec4 m_autocomplete_dim_text{0.52f, 0.60f, 0.60f, 1.0f}; +}; + +app_theme ¤t_theme(); + +void set_theme(const core::palette &palette); + +} // namespace clrsync::gui::theme + +#endif // CLRSYNC_GUI_THEME_APP_THEME_HPP diff --git a/src/gui/views/template_editor.cpp b/src/gui/views/template_editor.cpp index 41a6f26..cafeef3 100644 --- a/src/gui/views/template_editor.cpp +++ b/src/gui/views/template_editor.cpp @@ -3,6 +3,7 @@ #include "core/config/config.hpp" #include "core/palette/color_keys.hpp" #include "core/theme/theme_template.hpp" +#include "gui/theme/app_theme.hpp" #include "gui/widgets/colors.hpp" #include "gui/widgets/dialogs.hpp" #include "gui/ui_manager.hpp" @@ -24,12 +25,7 @@ template_editor::template_editor(clrsync::gui::ui_manager* ui_mgr) { m_control_state.name = "new_template"; - m_autocomplete_bg_color = ImVec4(0.12f, 0.12f, 0.15f, 0.98f); - m_autocomplete_border_color = ImVec4(0.4f, 0.4f, 0.45f, 1.0f); - m_autocomplete_selected_color = ImVec4(0.25f, 0.45f, 0.75f, 0.9f); - m_autocomplete_text_color = ImVec4(0.85f, 0.85f, 0.9f, 1.0f); - m_autocomplete_selected_text_color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); - m_autocomplete_dim_text_color = ImVec4(0.6f, 0.6f, 0.7f, 1.0f); + update_autocomplete_colors(); TextEditor::LanguageDefinition lang; lang.mName = "Template"; @@ -80,12 +76,26 @@ void template_editor::setup_callbacks() }; } +void template_editor::update_autocomplete_colors() +{ + const auto &t = clrsync::gui::theme::current_theme(); + m_autocomplete_bg_color = t.autocomplete_bg(); + m_autocomplete_border_color = t.autocomplete_border(); + m_autocomplete_selected_color = t.autocomplete_selected(); + m_autocomplete_text_color = t.autocomplete_text(); + m_autocomplete_selected_text_color = t.autocomplete_selected_text(); + m_autocomplete_dim_text_color = t.autocomplete_dim_text(); +} + void template_editor::apply_current_palette(const clrsync::core::palette &pal) { m_current_palette = pal; auto colors = pal.colors(); if (colors.empty()) return; + + using namespace clrsync::gui::theme; + auto get_color_u32 = [&](const std::string &key, const std::string &fallback = "") -> uint32_t { return clrsync::gui::widgets::palette_color_u32(pal, key, fallback); }; @@ -97,47 +107,30 @@ void template_editor::apply_current_palette(const clrsync::core::palette &pal) palette[int(TextEditor::PaletteIndex::Number)] = get_color_u32("editor_warning", "warning"); palette[int(TextEditor::PaletteIndex::String)] = get_color_u32("editor_string", "success"); palette[int(TextEditor::PaletteIndex::CharLiteral)] = get_color_u32("editor_string", "success"); - palette[int(TextEditor::PaletteIndex::Punctuation)] = - get_color_u32("editor_main", "foreground"); - palette[int(TextEditor::PaletteIndex::Preprocessor)] = - get_color_u32("editor_emphasis", "accent"); + palette[int(TextEditor::PaletteIndex::Punctuation)] = get_color_u32("editor_main", "foreground"); + palette[int(TextEditor::PaletteIndex::Preprocessor)] = get_color_u32("editor_emphasis", "accent"); palette[int(TextEditor::PaletteIndex::Identifier)] = get_color_u32("editor_main", "foreground"); palette[int(TextEditor::PaletteIndex::KnownIdentifier)] = get_color_u32("editor_link", "info"); - palette[int(TextEditor::PaletteIndex::PreprocIdentifier)] = - get_color_u32("editor_link", "info"); + palette[int(TextEditor::PaletteIndex::PreprocIdentifier)] = get_color_u32("editor_link", "info"); - palette[int(TextEditor::PaletteIndex::Comment)] = - get_color_u32("editor_comment", "editor_inactive"); - palette[int(TextEditor::PaletteIndex::MultiLineComment)] = - get_color_u32("editor_comment", "editor_inactive"); + palette[int(TextEditor::PaletteIndex::Comment)] = get_color_u32("editor_comment", "editor_inactive"); + palette[int(TextEditor::PaletteIndex::MultiLineComment)] = get_color_u32("editor_comment", "editor_inactive"); - palette[int(TextEditor::PaletteIndex::Background)] = - get_color_u32("editor_background", "background"); + palette[int(TextEditor::PaletteIndex::Background)] = get_color_u32("editor_background", "background"); palette[int(TextEditor::PaletteIndex::Cursor)] = get_color_u32("cursor", "accent"); - palette[int(TextEditor::PaletteIndex::Selection)] = - get_color_u32("editor_selected", "surface_variant"); + palette[int(TextEditor::PaletteIndex::Selection)] = get_color_u32("editor_selected", "surface_variant"); palette[int(TextEditor::PaletteIndex::ErrorMarker)] = get_color_u32("editor_error", "error"); palette[int(TextEditor::PaletteIndex::Breakpoint)] = get_color_u32("editor_error", "error"); - palette[int(TextEditor::PaletteIndex::LineNumber)] = - get_color_u32("editor_line_number", "editor_inactive"); + palette[int(TextEditor::PaletteIndex::LineNumber)] = get_color_u32("editor_line_number", "editor_inactive"); palette[int(TextEditor::PaletteIndex::CurrentLineFill)] = get_color_u32("surface_variant"); palette[int(TextEditor::PaletteIndex::CurrentLineFillInactive)] = get_color_u32("surface"); - - palette[int(TextEditor::PaletteIndex::CurrentLineEdge)] = - get_color_u32("border_focused", "border"); + palette[int(TextEditor::PaletteIndex::CurrentLineEdge)] = get_color_u32("border_focused", "border"); m_editor.SetPalette(palette); - m_autocomplete_bg_color = clrsync::gui::widgets::palette_color(pal, "surface", "background"); - m_autocomplete_bg_color.w = 0.98f; - m_autocomplete_border_color = clrsync::gui::widgets::palette_color(pal, "border", "surface_variant"); - m_autocomplete_selected_color = clrsync::gui::widgets::palette_color(pal, "accent", "surface_variant"); - m_autocomplete_text_color = clrsync::gui::widgets::palette_color(pal, "on_surface", "foreground"); - m_autocomplete_selected_text_color = clrsync::gui::widgets::palette_color(pal, "on_surface", "foreground"); - m_autocomplete_dim_text_color = - clrsync::gui::widgets::palette_color(pal, "on_surface_variant", "editor_inactive"); + update_autocomplete_colors(); } void template_editor::update_autocomplete_suggestions() diff --git a/src/gui/views/template_editor.hpp b/src/gui/views/template_editor.hpp index 299f7c3..a7b2b75 100644 --- a/src/gui/views/template_editor.hpp +++ b/src/gui/views/template_editor.hpp @@ -28,6 +28,7 @@ class template_editor void render_template_list(); void render_autocomplete(const ImVec2 &editor_pos); void update_autocomplete_suggestions(); + void update_autocomplete_colors(); void save_template(); void load_template(const std::string &name); diff --git a/src/gui/widgets/action_buttons.cpp b/src/gui/widgets/action_buttons.cpp index 7aa8139..b1b7e73 100644 --- a/src/gui/widgets/action_buttons.cpp +++ b/src/gui/widgets/action_buttons.cpp @@ -17,7 +17,7 @@ void action_buttons::clear() m_buttons.clear(); } -void action_buttons::render(const core::palette &theme_palette) +void action_buttons::render(const core::palette &) { if (m_buttons.empty()) return; @@ -37,30 +37,16 @@ void action_buttons::render(const core::palette &theme_palette) ImGui::SameLine(); first = false; - int style_colors_pushed = 0; + bool has_style = false; if (button.use_error_style) { - auto error = palette_color(theme_palette, "error"); - auto error_hover = ImVec4(error.x * 1.1f, error.y * 1.1f, error.z * 1.1f, error.w); - auto error_active = ImVec4(error.x * 0.8f, error.y * 0.8f, error.z * 0.8f, error.w); - auto on_error = palette_color(theme_palette, "on_error"); - ImGui::PushStyleColor(ImGuiCol_Button, error); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, error_hover); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, error_active); - ImGui::PushStyleColor(ImGuiCol_Text, on_error); - style_colors_pushed = 4; + push_error_button_style(); + has_style = true; } else if (button.use_success_style) { - auto success = palette_color(theme_palette, "success", "accent"); - auto success_hover = ImVec4(success.x * 1.1f, success.y * 1.1f, success.z * 1.1f, success.w); - auto success_active = ImVec4(success.x * 0.8f, success.y * 0.8f, success.z * 0.8f, success.w); - auto on_success = palette_color(theme_palette, "on_success", "on_surface"); - ImGui::PushStyleColor(ImGuiCol_Button, success); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, success_hover); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, success_active); - ImGui::PushStyleColor(ImGuiCol_Text, on_success); - style_colors_pushed = 4; + push_success_button_style(); + has_style = true; } bool disabled = !button.enabled; @@ -70,21 +56,17 @@ void action_buttons::render(const core::palette &theme_palette) if (ImGui::Button(button.label.c_str())) { if (button.on_click) - { button.on_click(); - } } if (disabled) ImGui::EndDisabled(); - if (style_colors_pushed > 0) - ImGui::PopStyleColor(style_colors_pushed); + if (has_style) + pop_button_style(); if (!button.tooltip.empty() && ImGui::IsItemHovered()) - { ImGui::SetTooltip("%s", button.tooltip.c_str()); - } } ImGui::PopStyleVar(); diff --git a/src/gui/widgets/colors.cpp b/src/gui/widgets/colors.cpp index 565e350..3d81463 100644 --- a/src/gui/widgets/colors.cpp +++ b/src/gui/widgets/colors.cpp @@ -12,20 +12,11 @@ ImVec4 palette_color(const core::palette &pal, const std::string &key, auto it = colors.find(key); if (it == colors.end() && !fallback.empty()) - { it = colors.find(fallback); - } if (it != colors.end()) - { - const auto &col = it->second; - const uint32_t hex = col.hex(); - const float r = ((hex >> 24) & 0xFF) / 255.0f; - const float g = ((hex >> 16) & 0xFF) / 255.0f; - const float b = ((hex >> 8) & 0xFF) / 255.0f; - const float a = (hex & 0xFF) / 255.0f; - return ImVec4(r, g, b, a); - } + return theme::color_utils::from_hex(it->second.hex()); + return ImVec4(1.0f, 1.0f, 1.0f, 1.0f); } @@ -38,21 +29,53 @@ uint32_t palette_color_u32(const core::palette &pal, const std::string &key, auto it = colors.find(key); if (it == colors.end() && !fallback.empty()) - { it = colors.find(fallback); - } if (it != colors.end()) - { - const auto &col = it->second; - const uint32_t hex = col.hex(); - const uint32_t r = (hex >> 24) & 0xFF; - const uint32_t g = (hex >> 16) & 0xFF; - const uint32_t b = (hex >> 8) & 0xFF; - const uint32_t a = hex & 0xFF; - return (a << 24) | (b << 16) | (g << 8) | r; - } + return theme::color_utils::to_imgui_u32(it->second.hex()); + return 0xFFFFFFFF; } +void push_success_button_style() +{ + const auto &t = theme::current_theme(); + ImGui::PushStyleColor(ImGuiCol_Button, t.success()); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, t.success_hovered()); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, t.success_active()); + ImGui::PushStyleColor(ImGuiCol_Text, t.on_success()); +} + +void push_error_button_style() +{ + const auto &t = theme::current_theme(); + ImGui::PushStyleColor(ImGuiCol_Button, t.error()); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, t.error_hovered()); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, t.error_active()); + ImGui::PushStyleColor(ImGuiCol_Text, t.on_error()); +} + +void push_warning_button_style() +{ + const auto &t = theme::current_theme(); + ImGui::PushStyleColor(ImGuiCol_Button, t.warning()); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, t.warning_hovered()); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, t.warning_active()); + ImGui::PushStyleColor(ImGuiCol_Text, t.on_warning()); +} + +void push_info_button_style() +{ + const auto &t = theme::current_theme(); + ImGui::PushStyleColor(ImGuiCol_Button, t.info()); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, t.info_hovered()); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, t.info_active()); + ImGui::PushStyleColor(ImGuiCol_Text, t.on_info()); +} + +void pop_button_style() +{ + ImGui::PopStyleColor(4); +} + } // namespace clrsync::gui::widgets diff --git a/src/gui/widgets/colors.hpp b/src/gui/widgets/colors.hpp index 19cfa49..f959b12 100644 --- a/src/gui/widgets/colors.hpp +++ b/src/gui/widgets/colors.hpp @@ -2,6 +2,7 @@ #define CLRSYNC_GUI_WIDGETS_COLORS_HPP #include "core/palette/palette.hpp" +#include "gui/theme/app_theme.hpp" #include "imgui.h" #include @@ -14,6 +15,18 @@ ImVec4 palette_color(const core::palette &pal, const std::string &key, uint32_t palette_color_u32(const core::palette &pal, const std::string &key, const std::string &fallback = ""); +inline const theme::app_theme &theme() { return theme::current_theme(); } + +void push_success_button_style(); + +void push_error_button_style(); + +void push_warning_button_style(); + +void push_info_button_style(); + +void pop_button_style(); + } // namespace clrsync::gui::widgets #endif // CLRSYNC_GUI_WIDGETS_COLORS_HPP diff --git a/src/gui/widgets/section_header.cpp b/src/gui/widgets/section_header.cpp index 8e6c32d..35d0c13 100644 --- a/src/gui/widgets/section_header.cpp +++ b/src/gui/widgets/section_header.cpp @@ -5,11 +5,10 @@ namespace clrsync::gui::widgets { -void section_header(const std::string& title, const core::palette& palette) +void section_header(const std::string &title, const core::palette &) { ImGui::Spacing(); - auto accent_color = palette_color(palette, "accent"); - ImGui::TextColored(accent_color, "%s", title.c_str()); + ImGui::TextColored(theme().accent(), "%s", title.c_str()); ImGui::Separator(); ImGui::Spacing(); }