1275 Theme& theme,
const std::set<std::string>& declared_keys) {
1287 auto declared = [&declared_keys](
const char* key) {
1288 return key !=
nullptr && declared_keys.count(key) != 0;
1290 auto looks_default = [](
const Color& color) {
1291 return color.red == 0.0f && color.green == 0.0f && color.blue == 0.0f &&
1292 (color.alpha == 0.0f || color.alpha == 1.0f);
1294 auto is_unset = [&](
const Color& color,
const char* key =
nullptr) {
1295 return !declared(key) && looks_default(color);
1300 auto needs_semantic_default = [&](
const Color& color,
1301 const char* key =
nullptr) {
1302 return is_unset(color, key);
1316 if (is_unset(theme.
accent,
"accent")) {
1318 ?
Color{0.35f, 0.70f, 0.95f, 1.0f}
1321 if (is_unset(theme.
error,
"error")) {
1322 theme.
error =
Color{0.90f, 0.30f, 0.30f, 1.0f};
1324 if (is_unset(theme.
warning,
"warning")) {
1327 if (is_unset(theme.
success,
"success")) {
1330 if (is_unset(theme.
info,
"info")) {
1331 theme.
info =
Color{0.35f, 0.70f, 0.95f, 1.0f};
1335 auto with_alpha = [](
const Color& color,
float alpha) {
1340 auto lighten = [](
const Color& color,
float amount) {
1341 return Color{std::min(1.0f, color.
red + amount),
1342 std::min(1.0f, color.
green + amount),
1343 std::min(1.0f, color.
blue + amount), color.
alpha};
1347 auto darken = [](
const Color& color,
float amount) {
1348 return Color{std::max(0.0f, color.
red - amount),
1349 std::max(0.0f, color.
green - amount),
1350 std::max(0.0f, color.
blue - amount), color.
alpha};
1352 auto darken_with_floor = [](
const Color& color,
float amount,
1353 float floor = 0.03f) {
1354 return Color{std::max(floor, color.
red - amount),
1355 std::max(floor, color.
green - amount),
1356 std::max(floor, color.
blue - amount), color.
alpha};
1358 auto is_effectively_transparent = [](
const Color& color) {
1359 return color.
alpha <= 0.01f;
1363 if (is_unset(theme.
border,
"border")) {
1369 if (is_unset(theme.
separator,
"separator")) {
1405 if (is_unset(theme.
check_mark,
"check_mark")) {
1433 if (is_unset(theme.
text_link,
"text_link")) {
1439 if (is_unset(theme.
plot_lines,
"plot_lines")) {
1456 if (is_unset(theme.
nav_cursor,
"nav_cursor")) {
1484 if (is_unset(theme.
tree_lines,
"tree_lines")) {
1489 if (is_unset(theme.
tab_dimmed,
"tab_dimmed")) {
1496 "tab_dimmed_selected_overline")) {
1504 if (is_unset(theme.
surface,
"surface")) {
1512 if (needs_semantic_default(theme.
text_highlight,
"text_highlight")) {
1515 if (needs_semantic_default(theme.
link_hover,
"link_hover")) {
1518 if (needs_semantic_default(theme.
code_background,
"code_background")) {
1521 if (needs_semantic_default(theme.
success_light,
"success_light")) {
1524 if (needs_semantic_default(theme.
warning_light,
"warning_light")) {
1527 if (needs_semantic_default(theme.
error_light,
"error_light")) {
1530 if (needs_semantic_default(theme.
info_light,
"info_light")) {
1546 if (needs_semantic_default(theme.
hover_highlight,
"hover_highlight")) {
1549 if (needs_semantic_default(theme.
focus_border,
"focus_border")) {
1560 if (needs_semantic_default(theme.
editor_grid,
"editor_grid")) {
1563 if (needs_semantic_default(theme.
editor_cursor,
"editor_cursor")) {
1591 if (needs_semantic_default(theme.
hole_color)) {
1594 if (needs_semantic_default(theme.
exit_color)) {
1597 if (needs_semantic_default(theme.
item_color)) {
1716 ? with_alpha(theme.
surface, 0.95f)
2366 if (!p_open || !*p_open)
2369 ImGui::SetNextWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
2371 if (ImGui::Begin(absl::StrFormat(
"%s Theme Editor",
ICON_MD_PALETTE).c_str(),
2372 p_open, ImGuiWindowFlags_MenuBar)) {
2374 static float editor_animation_time = 0.0f;
2375 editor_animation_time += ImGui::GetIO().DeltaTime;
2377 ImDrawList* draw_list = ImGui::GetWindowDrawList();
2378 ImVec2 window_pos = ImGui::GetWindowPos();
2379 ImVec2 window_size = ImGui::GetWindowSize();
2383 std::vector<gui::Color> theme_colors = {
2384 current_theme.primary, current_theme.secondary, current_theme.accent,
2385 current_theme.success, current_theme.warning, current_theme.error};
2387 for (
size_t i = 0; i < theme_colors.size(); ++i) {
2388 float time_offset = i * 1.0f;
2389 float orbit_radius = 60.0f + i * 8.0f;
2390 float x = window_pos.x + window_size.x * 0.8f +
2391 cosf(editor_animation_time * 0.3f + time_offset) * orbit_radius;
2392 float y = window_pos.y + window_size.y * 0.3f +
2393 sinf(editor_animation_time * 0.3f + time_offset) * orbit_radius;
2396 0.15f + 0.1f * sinf(editor_animation_time * 1.5f + time_offset);
2397 ImU32 orb_color = ImGui::ColorConvertFloat4ToU32(
2398 ImVec4(theme_colors[i].red, theme_colors[i].green,
2399 theme_colors[i].blue, alpha));
2402 4.0f + sinf(editor_animation_time * 2.0f + time_offset) * 1.0f;
2403 draw_list->AddCircleFilled(ImVec2(x, y), radius, orb_color);
2407 if (ImGui::BeginMenuBar()) {
2408 if (ImGui::BeginMenu(tr(
"File"))) {
2409 if (ImGui::MenuItem(
2410 absl::StrFormat(
"%s New Theme",
ICON_MD_ADD).c_str())) {
2414 if (ImGui::MenuItem(
2418 if (!file_path.empty()) {
2423 if (ImGui::MenuItem(
2424 absl::StrFormat(
"%s Save Theme",
ICON_MD_SAVE).c_str())) {
2427 if (!current_file_path.empty()) {
2430 LOG_ERROR(
"Theme Manager",
"Failed to save theme");
2436 if (!file_path.empty()) {
2439 LOG_ERROR(
"Theme Manager",
"Failed to save theme");
2444 if (ImGui::MenuItem(
2449 if (!file_path.empty()) {
2452 LOG_ERROR(
"Theme Manager",
"Failed to save theme");
2457 if (ImGui::MenuItem(
2466 for (
char& c : safe_name) {
2467 if (c ==
' ' || c ==
'/' || c ==
'\\' || c ==
':' || c ==
'*' ||
2468 c ==
'?' || c ==
'"' || c ==
'<' || c ==
'>' || c ==
'|') {
2472 std::string file_path = user_themes_dir + safe_name +
".theme";
2476 LOG_INFO(
"Theme Manager",
"Exported theme to: %s",
2479 LOG_ERROR(
"Theme Manager",
"Failed to export theme to user themes");
2485 if (ImGui::BeginMenu(tr(
"Presets"))) {
2486 if (ImGui::MenuItem(tr(
"YAZE Classic"))) {
2491 if (!available_themes.empty()) {
2493 for (
const auto& theme_name : available_themes) {
2494 if (theme_name ==
"Classic YAZE") {
2497 if (ImGui::MenuItem(theme_name.c_str())) {
2505 ImGui::EndMenuBar();
2509 static char theme_name[128];
2510 static char theme_description[256];
2511 static char theme_author[128];
2512 static bool live_preview =
true;
2513 static Theme original_theme;
2514 static bool theme_backup_made =
false;
2517 auto apply_live_preview = [&]() {
2519 if (!theme_backup_made) {
2521 theme_backup_made =
true;
2530 ImGui::Checkbox(tr(
"Live Preview"), &live_preview);
2532 ImGui::Text(tr(
"| Changes apply immediately when enabled"));
2535 static bool prev_live_preview = live_preview;
2536 if (prev_live_preview && !live_preview && theme_backup_made) {
2538 theme_backup_made =
false;
2540 prev_live_preview = live_preview;
2545 if (ImGui::BeginTable(
"ThemeMetadata", 2,
2546 ImGuiTableFlags_SizingStretchProp)) {
2547 ImGui::TableSetupColumn(
"Field", ImGuiTableColumnFlags_WidthFixed,
2549 ImGui::TableSetupColumn(
"Value", ImGuiTableColumnFlags_WidthStretch);
2551 ImGui::TableNextRow();
2552 ImGui::TableNextColumn();
2553 ImGui::AlignTextToFramePadding();
2554 ImGui::Text(tr(
"Name:"));
2555 ImGui::TableNextColumn();
2556 if (ImGui::InputText(
"##theme_name", theme_name,
sizeof(theme_name))) {
2557 edit_theme.
name = std::string(theme_name);
2560 ImGui::TableNextRow();
2561 ImGui::TableNextColumn();
2562 ImGui::AlignTextToFramePadding();
2563 ImGui::Text(tr(
"Description:"));
2564 ImGui::TableNextColumn();
2565 if (ImGui::InputText(
"##theme_description", theme_description,
2566 sizeof(theme_description))) {
2567 edit_theme.
description = std::string(theme_description);
2570 ImGui::TableNextRow();
2571 ImGui::TableNextColumn();
2572 ImGui::AlignTextToFramePadding();
2573 ImGui::Text(tr(
"Author:"));
2574 ImGui::TableNextColumn();
2575 if (ImGui::InputText(
"##theme_author", theme_author,
2576 sizeof(theme_author))) {
2577 edit_theme.
author = std::string(theme_author);
2586 if (ImGui::BeginTabBar(
"ThemeEditorTabs", ImGuiTabBarFlags_None)) {
2588 static bool first_frame =
true;
2589 if (first_frame && live_preview) {
2590 apply_live_preview();
2591 first_frame =
false;
2595 if (ImGui::BeginTabItem(
2599 ImGui::PushStyleColor(ImGuiCol_ChildBg,
2600 ImVec4(0.1f, 0.1f, 0.12f, 0.5f));
2601 ImGui::BeginChild(
"AccentGenerator", ImVec2(0, 120),
true);
2603 ImGui::TextColored(ImVec4(0.7f, 0.8f, 1.0f, 1.0f),
2604 tr(
"%s Generate Theme from Accent Color"),
2608 static Color accent_picker_color = {0.4f, 0.6f, 0.9f, 1.0f};
2609 static bool dark_mode_generate =
true;
2611 ImGui::Text(tr(
"Accent Color:"));
2614 if (ImGui::ColorEdit3(
"##AccentPicker", &accent_vec.x,
2615 ImGuiColorEditFlags_NoInputs)) {
2616 accent_picker_color = {accent_vec.x, accent_vec.y, accent_vec.z,
2621 ImGui::Checkbox(tr(
"Dark Mode"), &dark_mode_generate);
2624 if (ImGui::Button(absl::StrFormat(
"%s Generate",
ICON_MD_BOLT).c_str(),
2629 edit_theme = generated;
2630 apply_live_preview();
2634 ImGui::Text(tr(
"Preview: "));
2638 Theme preview_theme =
2645 ImGui::ColorButton(
"##prev_primary", prev_primary, 0, ImVec2(24, 24));
2646 ImGui::SameLine(0, 2);
2647 ImGui::ColorButton(
"##prev_secondary", prev_secondary, 0,
2649 ImGui::SameLine(0, 2);
2650 ImGui::ColorButton(
"##prev_bg", prev_bg, 0, ImVec2(24, 24));
2651 ImGui::SameLine(0, 2);
2652 ImGui::ColorButton(
"##prev_surface", prev_surface, 0, ImVec2(24, 24));
2655 ImGui::PopStyleColor();
2659 if (ImGui::BeginTable(
"PrimaryColorsTable", 3,
2660 ImGuiTableFlags_SizingStretchProp)) {
2661 ImGui::TableSetupColumn(
"Color", ImGuiTableColumnFlags_WidthFixed,
2663 ImGui::TableSetupColumn(
"Picker", ImGuiTableColumnFlags_WidthStretch,
2665 ImGui::TableSetupColumn(
"Preview", ImGuiTableColumnFlags_WidthStretch,
2667 ImGui::TableHeadersRow();
2670 ImGui::TableNextRow();
2671 ImGui::TableNextColumn();
2672 ImGui::AlignTextToFramePadding();
2673 ImGui::Text(tr(
"Primary:"));
2674 ImGui::TableNextColumn();
2676 if (ImGui::ColorEdit3(
"##primary", &primary.x)) {
2677 edit_theme.
primary = {primary.x, primary.y, primary.z, primary.w};
2678 apply_live_preview();
2680 ImGui::TableNextColumn();
2681 ImGui::Button(tr(
"Primary Preview"), ImVec2(-1, 30));
2684 ImGui::TableNextRow();
2685 ImGui::TableNextColumn();
2686 ImGui::AlignTextToFramePadding();
2687 ImGui::Text(tr(
"Secondary:"));
2688 ImGui::TableNextColumn();
2690 if (ImGui::ColorEdit3(
"##secondary", &secondary.x)) {
2691 edit_theme.
secondary = {secondary.x, secondary.y, secondary.z,
2693 apply_live_preview();
2695 ImGui::TableNextColumn();
2696 ImGui::PushStyleColor(ImGuiCol_Button, secondary);
2697 ImGui::Button(tr(
"Secondary Preview"), ImVec2(-1, 30));
2698 ImGui::PopStyleColor();
2701 ImGui::TableNextRow();
2702 ImGui::TableNextColumn();
2703 ImGui::AlignTextToFramePadding();
2704 ImGui::Text(tr(
"Accent:"));
2705 ImGui::TableNextColumn();
2707 if (ImGui::ColorEdit3(
"##accent", &accent.x)) {
2708 edit_theme.
accent = {accent.x, accent.y, accent.z, accent.w};
2709 apply_live_preview();
2711 ImGui::TableNextColumn();
2712 ImGui::PushStyleColor(ImGuiCol_Button, accent);
2713 ImGui::Button(tr(
"Accent Preview"), ImVec2(-1, 30));
2714 ImGui::PopStyleColor();
2717 ImGui::TableNextRow();
2718 ImGui::TableNextColumn();
2719 ImGui::AlignTextToFramePadding();
2720 ImGui::Text(tr(
"Background:"));
2721 ImGui::TableNextColumn();
2723 if (ImGui::ColorEdit4(
"##background", &background.x)) {
2724 edit_theme.
background = {background.x, background.y, background.z,
2726 apply_live_preview();
2728 ImGui::TableNextColumn();
2729 ImGui::Text(tr(
"Background preview shown in window"));
2733 ImGui::EndTabItem();
2737 if (ImGui::BeginTabItem(
2739 if (ImGui::BeginTable(
"TextColorsTable", 3,
2740 ImGuiTableFlags_SizingStretchProp)) {
2741 ImGui::TableSetupColumn(
"Color", ImGuiTableColumnFlags_WidthFixed,
2743 ImGui::TableSetupColumn(
"Picker", ImGuiTableColumnFlags_WidthStretch,
2745 ImGui::TableSetupColumn(
"Preview", ImGuiTableColumnFlags_WidthStretch,
2747 ImGui::TableHeadersRow();
2750 auto text_colors = std::vector<std::pair<const char*, Color*>>{
2760 for (
auto& [label, color_ptr] : text_colors) {
2761 ImGui::TableNextRow();
2762 ImGui::TableNextColumn();
2763 ImGui::AlignTextToFramePadding();
2764 ImGui::Text(
"%s:", label);
2766 ImGui::TableNextColumn();
2768 std::string
id = absl::StrFormat(
"##%s", label);
2769 if (ImGui::ColorEdit3(
id.c_str(), &color_vec.x)) {
2770 *color_ptr = {color_vec.x, color_vec.y, color_vec.z, color_vec.w};
2771 apply_live_preview();
2774 ImGui::TableNextColumn();
2775 ImGui::PushStyleColor(ImGuiCol_Text, color_vec);
2776 ImGui::Text(tr(
"Sample %s"), label);
2777 ImGui::PopStyleColor();
2782 ImGui::EndTabItem();
2786 if (ImGui::BeginTabItem(
2788 if (ImGui::BeginTable(
"InteractiveColorsTable", 3,
2789 ImGuiTableFlags_SizingStretchProp)) {
2790 ImGui::TableSetupColumn(
"Element", ImGuiTableColumnFlags_WidthFixed,
2792 ImGui::TableSetupColumn(
"Picker", ImGuiTableColumnFlags_WidthStretch,
2794 ImGui::TableSetupColumn(
"Preview", ImGuiTableColumnFlags_WidthStretch,
2796 ImGui::TableHeadersRow();
2799 auto interactive_colors =
2800 std::vector<std::tuple<const char*, Color*, ImGuiCol>>{
2801 {
"Button", &edit_theme.
button, ImGuiCol_Button},
2803 ImGuiCol_ButtonHovered},
2805 ImGuiCol_ButtonActive},
2806 {
"Frame Background", &edit_theme.
frame_bg, ImGuiCol_FrameBg},
2808 ImGuiCol_FrameBgHovered},
2810 ImGuiCol_FrameBgActive},
2811 {
"Check Mark", &edit_theme.
check_mark, ImGuiCol_CheckMark},
2812 {
"Slider Grab", &edit_theme.
slider_grab, ImGuiCol_SliderGrab},
2814 ImGuiCol_SliderGrabActive}};
2816 for (
auto& [label, color_ptr, imgui_col] : interactive_colors) {
2817 ImGui::TableNextRow();
2818 ImGui::TableNextColumn();
2819 ImGui::AlignTextToFramePadding();
2820 ImGui::Text(
"%s:", label);
2822 ImGui::TableNextColumn();
2824 std::string
id = absl::StrFormat(
"##%s", label);
2825 if (ImGui::ColorEdit3(
id.c_str(), &color_vec.x)) {
2826 *color_ptr = {color_vec.x, color_vec.y, color_vec.z, color_vec.w};
2827 apply_live_preview();
2830 ImGui::TableNextColumn();
2831 ImGui::PushStyleColor(imgui_col, color_vec);
2832 ImGui::Button(absl::StrFormat(
"Preview %s", label).c_str(),
2834 ImGui::PopStyleColor();
2839 ImGui::EndTabItem();
2843 if (ImGui::BeginTabItem(
2845 ImGui::Text(tr(
"Rounding and Border Settings:"));
2847 if (ImGui::SliderFloat(tr(
"Window Rounding"),
2852 if (ImGui::SliderFloat(tr(
"Frame Rounding"), &edit_theme.
frame_rounding,
2857 if (ImGui::SliderFloat(tr(
"Scrollbar Rounding"),
2862 if (ImGui::SliderFloat(tr(
"Tab Rounding"), &edit_theme.
tab_rounding,
2867 if (ImGui::SliderFloat(tr(
"Grab Rounding"), &edit_theme.
grab_rounding,
2874 ImGui::Text(tr(
"Border Sizes:"));
2876 if (ImGui::SliderFloat(tr(
"Window Border Size"),
2881 if (ImGui::SliderFloat(tr(
"Frame Border Size"),
2888 ImGui::Text(tr(
"Animation & Effects:"));
2890 if (ImGui::Checkbox(tr(
"Enable Animations"),
2896 if (ImGui::SliderFloat(tr(
"Animation Speed"),
2898 apply_live_preview();
2901 if (ImGui::Checkbox(tr(
"Enable Glow Effects"),
2907 ImGui::EndTabItem();
2911 if (ImGui::BeginTabItem(
2913 if (ImGui::BeginTable(
"NavigationTable", 3,
2914 ImGuiTableFlags_SizingStretchProp)) {
2915 ImGui::TableSetupColumn(
"Element", ImGuiTableColumnFlags_WidthFixed,
2917 ImGui::TableSetupColumn(
"Picker", ImGuiTableColumnFlags_WidthStretch,
2919 ImGui::TableSetupColumn(
"Preview", ImGuiTableColumnFlags_WidthStretch,
2921 ImGui::TableHeadersRow();
2924 auto window_colors =
2925 std::vector<std::tuple<const char*, Color*, const char*>>{
2926 {
"Window Background", &edit_theme.
window_bg,
2927 "Main window background"},
2928 {
"Child Background", &edit_theme.
child_bg,
2929 "Child window background"},
2930 {
"Popup Background", &edit_theme.
popup_bg,
2931 "Popup window background"},
2932 {
"Modal Background", &edit_theme.
modal_bg,
2933 "Modal window background"},
2935 "Menu bar background"}};
2937 for (
auto& [label, color_ptr, description] : window_colors) {
2938 ImGui::TableNextRow();
2939 ImGui::TableNextColumn();
2940 ImGui::AlignTextToFramePadding();
2941 ImGui::Text(
"%s:", label);
2943 ImGui::TableNextColumn();
2945 std::string
id = absl::StrFormat(
"##window_%s", label);
2946 if (ImGui::ColorEdit4(
id.c_str(), &color_vec.x)) {
2947 *color_ptr = {color_vec.x, color_vec.y, color_vec.z, color_vec.w};
2948 apply_live_preview();
2951 ImGui::TableNextColumn();
2952 ImGui::TextWrapped(
"%s", description);
2961 if (ImGui::CollapsingHeader(tr(
"Headers & Tabs"),
2962 ImGuiTreeNodeFlags_DefaultOpen)) {
2963 if (ImGui::BeginTable(
"HeaderTabTable", 3,
2964 ImGuiTableFlags_SizingStretchProp)) {
2965 ImGui::TableSetupColumn(
"Element", ImGuiTableColumnFlags_WidthFixed,
2967 ImGui::TableSetupColumn(
"Picker",
2968 ImGuiTableColumnFlags_WidthStretch, 0.6f);
2969 ImGui::TableSetupColumn(
"Preview",
2970 ImGuiTableColumnFlags_WidthStretch, 0.4f);
2971 ImGui::TableHeadersRow();
2973 auto header_tab_colors =
2974 std::vector<std::pair<const char*, Color*>>{
2975 {
"Header", &edit_theme.
header},
2978 {
"Tab", &edit_theme.
tab},
2985 {
"Title Background", &edit_theme.
title_bg},
2989 for (
auto& [label, color_ptr] : header_tab_colors) {
2990 ImGui::TableNextRow();
2991 ImGui::TableNextColumn();
2992 ImGui::AlignTextToFramePadding();
2993 ImGui::Text(
"%s:", label);
2995 ImGui::TableNextColumn();
2997 std::string
id = absl::StrFormat(
"##header_%s", label);
2998 if (ImGui::ColorEdit3(
id.c_str(), &color_vec.x)) {
2999 *color_ptr = {color_vec.x, color_vec.y, color_vec.z,
3001 apply_live_preview();
3004 ImGui::TableNextColumn();
3005 ImGui::PushStyleColor(ImGuiCol_Button, color_vec);
3006 ImGui::Button(absl::StrFormat(
"Preview %s", label).c_str(),
3008 ImGui::PopStyleColor();
3016 if (ImGui::CollapsingHeader(tr(
"Navigation & Special"))) {
3017 if (ImGui::BeginTable(
"NavSpecialTable", 3,
3018 ImGuiTableFlags_SizingStretchProp)) {
3019 ImGui::TableSetupColumn(
"Element", ImGuiTableColumnFlags_WidthFixed,
3021 ImGui::TableSetupColumn(
"Picker",
3022 ImGuiTableColumnFlags_WidthStretch, 0.6f);
3023 ImGui::TableSetupColumn(
"Description",
3024 ImGuiTableColumnFlags_WidthStretch, 0.4f);
3025 ImGui::TableHeadersRow();
3027 auto nav_special_colors =
3028 std::vector<std::tuple<const char*, Color*, const char*>>{
3030 "Navigation cursor color"},
3032 "Window selection highlight"},
3034 "Background dimming for navigation"},
3036 "Background dimming for modals"},
3038 "Drag and drop target highlight"},
3040 "Docking area preview"},
3042 "Empty docking space background"}};
3044 for (
auto& [label, color_ptr, description] : nav_special_colors) {
3045 ImGui::TableNextRow();
3046 ImGui::TableNextColumn();
3047 ImGui::AlignTextToFramePadding();
3048 ImGui::Text(
"%s:", label);
3050 ImGui::TableNextColumn();
3052 std::string
id = absl::StrFormat(
"##nav_%s", label);
3053 if (ImGui::ColorEdit4(
id.c_str(), &color_vec.x)) {
3054 *color_ptr = {color_vec.x, color_vec.y, color_vec.z,
3056 apply_live_preview();
3059 ImGui::TableNextColumn();
3060 ImGui::TextWrapped(
"%s", description);
3067 ImGui::EndTabItem();
3071 if (ImGui::BeginTabItem(
3073 if (ImGui::BeginTable(
"TablesDataTable", 3,
3074 ImGuiTableFlags_SizingStretchProp)) {
3075 ImGui::TableSetupColumn(
"Element", ImGuiTableColumnFlags_WidthFixed,
3077 ImGui::TableSetupColumn(
"Picker", ImGuiTableColumnFlags_WidthStretch,
3079 ImGui::TableSetupColumn(
"Description",
3080 ImGuiTableColumnFlags_WidthStretch, 0.4f);
3081 ImGui::TableHeadersRow();
3084 std::vector<std::tuple<const char*, Color*, const char*>>{
3086 "Table column headers"},
3088 "Outer table borders"},
3090 "Inner table borders"},
3092 "Normal table rows"},
3094 "Alternating table rows"},
3096 "Tree view connection lines"}};
3098 for (
auto& [label, color_ptr, description] : table_colors) {
3099 ImGui::TableNextRow();
3100 ImGui::TableNextColumn();
3101 ImGui::AlignTextToFramePadding();
3102 ImGui::Text(
"%s:", label);
3104 ImGui::TableNextColumn();
3106 std::string
id = absl::StrFormat(
"##table_%s", label);
3107 if (ImGui::ColorEdit4(
id.c_str(), &color_vec.x)) {
3108 *color_ptr = {color_vec.x, color_vec.y, color_vec.z, color_vec.w};
3109 apply_live_preview();
3112 ImGui::TableNextColumn();
3113 ImGui::TextWrapped(
"%s", description);
3122 if (ImGui::CollapsingHeader(tr(
"Plots & Graphs"))) {
3123 if (ImGui::BeginTable(
"PlotsTable", 3,
3124 ImGuiTableFlags_SizingStretchProp)) {
3125 ImGui::TableSetupColumn(
"Element", ImGuiTableColumnFlags_WidthFixed,
3127 ImGui::TableSetupColumn(
"Picker",
3128 ImGuiTableColumnFlags_WidthStretch, 0.6f);
3129 ImGui::TableSetupColumn(
"Description",
3130 ImGuiTableColumnFlags_WidthStretch, 0.4f);
3131 ImGui::TableHeadersRow();
3134 std::vector<std::tuple<const char*, Color*, const char*>>{
3135 {
"Plot Lines", &edit_theme.
plot_lines,
"Line plot color"},
3137 "Line plot hover color"},
3139 "Histogram fill color"},
3140 {
"Plot Histogram Hovered",
3142 "Histogram hover color"}};
3144 for (
auto& [label, color_ptr, description] : plot_colors) {
3145 ImGui::TableNextRow();
3146 ImGui::TableNextColumn();
3147 ImGui::AlignTextToFramePadding();
3148 ImGui::Text(
"%s:", label);
3150 ImGui::TableNextColumn();
3152 std::string
id = absl::StrFormat(
"##plot_%s", label);
3153 if (ImGui::ColorEdit3(
id.c_str(), &color_vec.x)) {
3154 *color_ptr = {color_vec.x, color_vec.y, color_vec.z,
3156 apply_live_preview();
3159 ImGui::TableNextColumn();
3160 ImGui::TextWrapped(
"%s", description);
3167 ImGui::EndTabItem();
3171 if (ImGui::BeginTabItem(
3173 if (ImGui::BeginTable(
"BordersControlsTable", 3,
3174 ImGuiTableFlags_SizingStretchProp)) {
3175 ImGui::TableSetupColumn(
"Element", ImGuiTableColumnFlags_WidthFixed,
3177 ImGui::TableSetupColumn(
"Picker", ImGuiTableColumnFlags_WidthStretch,
3179 ImGui::TableSetupColumn(
"Description",
3180 ImGuiTableColumnFlags_WidthStretch, 0.4f);
3181 ImGui::TableHeadersRow();
3183 auto border_control_colors =
3184 std::vector<std::tuple<const char*, Color*, const char*>>{
3185 {
"Border", &edit_theme.
border,
"General border color"},
3187 "Border shadow/depth"},
3189 "Horizontal/vertical separators"},
3191 "Separator hover state"},
3193 "Separator active/dragged state"},
3195 "Scrollbar track background"},
3197 "Scrollbar handle"},
3199 "Scrollbar handle hover"},
3201 "Scrollbar handle active"},
3203 "Window resize grip"},
3205 "Resize grip hover"},
3207 "Resize grip active"}};
3209 for (
auto& [label, color_ptr, description] : border_control_colors) {
3210 ImGui::TableNextRow();
3211 ImGui::TableNextColumn();
3212 ImGui::AlignTextToFramePadding();
3213 ImGui::Text(
"%s:", label);
3215 ImGui::TableNextColumn();
3217 std::string
id = absl::StrFormat(
"##border_%s", label);
3218 if (ImGui::ColorEdit4(
id.c_str(), &color_vec.x)) {
3219 *color_ptr = {color_vec.x, color_vec.y, color_vec.z, color_vec.w};
3220 apply_live_preview();
3223 ImGui::TableNextColumn();
3224 ImGui::TextWrapped(
"%s", description);
3230 ImGui::EndTabItem();
3234 if (ImGui::BeginTabItem(
3237 tr(
"Enhanced semantic colors and editor-specific customization"));
3241 if (ImGui::CollapsingHeader(tr(
"Enhanced Semantic Colors"),
3242 ImGuiTreeNodeFlags_DefaultOpen)) {
3243 if (ImGui::BeginTable(
"EnhancedSemanticTable", 3,
3244 ImGuiTableFlags_SizingStretchProp)) {
3245 ImGui::TableSetupColumn(
"Color", ImGuiTableColumnFlags_WidthFixed,
3247 ImGui::TableSetupColumn(
"Picker",
3248 ImGuiTableColumnFlags_WidthStretch, 0.6f);
3249 ImGui::TableSetupColumn(
"Description",
3250 ImGuiTableColumnFlags_WidthStretch, 0.4f);
3251 ImGui::TableHeadersRow();
3253 auto enhanced_colors =
3254 std::vector<std::tuple<const char*, Color*, const char*>>{
3256 "Code blocks background"},
3258 "Light success variant"},
3260 "Light warning variant"},
3262 "Light error variant"},
3264 "Light info variant"}};
3266 for (
auto& [label, color_ptr, description] : enhanced_colors) {
3267 ImGui::TableNextRow();
3268 ImGui::TableNextColumn();
3269 ImGui::AlignTextToFramePadding();
3270 ImGui::Text(
"%s:", label);
3272 ImGui::TableNextColumn();
3274 std::string
id = absl::StrFormat(
"##enhanced_%s", label);
3275 if (ImGui::ColorEdit3(
id.c_str(), &color_vec.x)) {
3276 *color_ptr = {color_vec.x, color_vec.y, color_vec.z,
3278 apply_live_preview();
3281 ImGui::TableNextColumn();
3282 ImGui::TextWrapped(
"%s", description);
3290 if (ImGui::CollapsingHeader(tr(
"UI State Colors"))) {
3291 if (ImGui::BeginTable(
"UIStateTable", 3,
3292 ImGuiTableFlags_SizingStretchProp)) {
3293 ImGui::TableSetupColumn(
"Color", ImGuiTableColumnFlags_WidthFixed,
3295 ImGui::TableSetupColumn(
"Picker",
3296 ImGuiTableColumnFlags_WidthStretch, 0.6f);
3297 ImGui::TableSetupColumn(
"Description",
3298 ImGuiTableColumnFlags_WidthStretch, 0.4f);
3299 ImGui::TableHeadersRow();
3302 ImGui::TableNextRow();
3303 ImGui::TableNextColumn();
3304 ImGui::AlignTextToFramePadding();
3305 ImGui::Text(tr(
"Active Selection:"));
3306 ImGui::TableNextColumn();
3307 ImVec4 active_selection =
3309 if (ImGui::ColorEdit4(
"##active_selection", &active_selection.x)) {
3311 active_selection.x, active_selection.y, active_selection.z,
3312 active_selection.w};
3313 apply_live_preview();
3315 ImGui::TableNextColumn();
3316 ImGui::TextWrapped(tr(
"Active/selected UI elements"));
3318 ImGui::TableNextRow();
3319 ImGui::TableNextColumn();
3320 ImGui::AlignTextToFramePadding();
3321 ImGui::Text(tr(
"Hover Highlight:"));
3322 ImGui::TableNextColumn();
3323 ImVec4 hover_highlight =
3325 if (ImGui::ColorEdit4(
"##hover_highlight", &hover_highlight.x)) {
3327 hover_highlight.x, hover_highlight.y, hover_highlight.z,
3329 apply_live_preview();
3331 ImGui::TableNextColumn();
3332 ImGui::TextWrapped(tr(
"General hover state highlighting"));
3334 ImGui::TableNextRow();
3335 ImGui::TableNextColumn();
3336 ImGui::AlignTextToFramePadding();
3337 ImGui::Text(tr(
"Focus Border:"));
3338 ImGui::TableNextColumn();
3340 if (ImGui::ColorEdit3(
"##focus_border", &focus_border.x)) {
3341 edit_theme.
focus_border = {focus_border.x, focus_border.y,
3342 focus_border.z, focus_border.w};
3343 apply_live_preview();
3345 ImGui::TableNextColumn();
3346 ImGui::TextWrapped(tr(
"Border for focused input elements"));
3348 ImGui::TableNextRow();
3349 ImGui::TableNextColumn();
3350 ImGui::AlignTextToFramePadding();
3351 ImGui::Text(tr(
"Disabled Overlay:"));
3352 ImGui::TableNextColumn();
3353 ImVec4 disabled_overlay =
3355 if (ImGui::ColorEdit4(
"##disabled_overlay", &disabled_overlay.x)) {
3357 disabled_overlay.x, disabled_overlay.y, disabled_overlay.z,
3358 disabled_overlay.w};
3359 apply_live_preview();
3361 ImGui::TableNextColumn();
3363 tr(
"Semi-transparent overlay for disabled elements"));
3370 if (ImGui::CollapsingHeader(tr(
"Editor-Specific Colors"))) {
3371 if (ImGui::BeginTable(
"EditorColorsTable", 3,
3372 ImGuiTableFlags_SizingStretchProp)) {
3373 ImGui::TableSetupColumn(
"Color", ImGuiTableColumnFlags_WidthFixed,
3375 ImGui::TableSetupColumn(
"Picker",
3376 ImGuiTableColumnFlags_WidthStretch, 0.6f);
3377 ImGui::TableSetupColumn(
"Description",
3378 ImGuiTableColumnFlags_WidthStretch, 0.4f);
3379 ImGui::TableHeadersRow();
3381 auto editor_colors =
3382 std::vector<std::tuple<const char*, Color*, const char*, bool>>{
3384 "Main editor canvas background",
false},
3386 "Grid lines in map/graphics editors",
true},
3388 "Cursor color in editors",
false},
3390 "Selection highlight in editors",
true}};
3392 for (
auto& [label, color_ptr, description, use_alpha] :
3394 ImGui::TableNextRow();
3395 ImGui::TableNextColumn();
3396 ImGui::AlignTextToFramePadding();
3397 ImGui::Text(
"%s:", label);
3399 ImGui::TableNextColumn();
3401 std::string
id = absl::StrFormat(
"##editor_%s", label);
3402 if (use_alpha ? ImGui::ColorEdit4(
id.c_str(), &color_vec.x)
3403 : ImGui::ColorEdit3(
id.c_str(), &color_vec.x)) {
3404 *color_ptr = {color_vec.x, color_vec.y, color_vec.z,
3406 apply_live_preview();
3409 ImGui::TableNextColumn();
3410 ImGui::TextWrapped(
"%s", description);
3417 ImGui::EndTabItem();
3421 if (ImGui::BeginTabItem(
3423 ImGui::Text(tr(
"Control UI density, spacing, and typography scaling"));
3427 ImGui::TextColored(ImVec4(0.7f, 0.8f, 1.0f, 1.0f),
3432 int current_preset =
static_cast<int>(edit_theme.
density_preset);
3434 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(20, 12));
3435 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(12, 0));
3438 bool is_compact = (current_preset == 0);
3440 ImGui::PushStyleColor(
3449 apply_live_preview();
3452 ImGui::PopStyleColor();
3454 if (ImGui::IsItemHovered()) {
3456 tr(
"Dense UI with smaller widgets and tighter spacing\n"
3457 "Best for: Information-dense workflows"));
3463 bool is_normal = (current_preset == 1);
3465 ImGui::PushStyleColor(
3470 if (ImGui::Button(absl::StrFormat(
"%s Normal",
ICON_MD_CHECK).c_str(),
3473 apply_live_preview();
3476 ImGui::PopStyleColor();
3478 if (ImGui::IsItemHovered()) {
3480 tr(
"Balanced spacing and widget sizes\n"
3481 "Best for: General use"));
3487 bool is_comfortable = (current_preset == 2);
3488 if (is_comfortable) {
3489 ImGui::PushStyleColor(
3498 apply_live_preview();
3500 if (is_comfortable) {
3501 ImGui::PopStyleColor();
3503 if (ImGui::IsItemHovered()) {
3505 tr(
"Spacious layout with larger click targets\n"
3506 "Best for: Touch screens, accessibility"));
3509 ImGui::PopStyleVar(2);
3516 if (ImGui::CollapsingHeader(tr(
"Advanced Density Controls"))) {
3519 ImGui::Text(tr(
"Fine-tune individual spacing multipliers:"));
3523 if (ImGui::SliderFloat(tr(
"Compact Factor"),
3526 apply_live_preview();
3528 if (ImGui::IsItemHovered()) {
3530 tr(
"Global density multiplier (0.5 = very compact, "
3531 "1.5 = very spacious)"));
3537 if (ImGui::SliderFloat(tr(
"Widget Height"),
3540 apply_live_preview();
3544 if (ImGui::SliderFloat(tr(
"Item Spacing"),
3547 apply_live_preview();
3551 if (ImGui::SliderFloat(tr(
"Toolbar Height"),
3554 apply_live_preview();
3558 if (ImGui::SliderFloat(tr(
"Panel Padding"),
3561 apply_live_preview();
3565 if (ImGui::SliderFloat(tr(
"Button Padding"),
3568 apply_live_preview();
3572 if (ImGui::SliderFloat(tr(
"Table Row Height"),
3575 apply_live_preview();
3582 if (ImGui::CollapsingHeader(tr(
"Corner Rounding"))) {
3585 if (ImGui::SliderFloat(tr(
"Window Rounding"),
3588 apply_live_preview();
3591 if (ImGui::SliderFloat(tr(
"Frame Rounding"),
3594 apply_live_preview();
3597 if (ImGui::SliderFloat(tr(
"Tab Rounding"), &edit_theme.
tab_rounding,
3598 0.0f, 12.0f,
"%.1f")) {
3599 apply_live_preview();
3602 if (ImGui::SliderFloat(tr(
"Scrollbar Rounding"),
3605 apply_live_preview();
3608 if (ImGui::SliderFloat(tr(
"Grab Rounding"), &edit_theme.
grab_rounding,
3609 0.0f, 12.0f,
"%.1f")) {
3610 apply_live_preview();
3617 if (ImGui::CollapsingHeader(tr(
"Animation Settings"))) {
3620 if (ImGui::Checkbox(tr(
"Enable Animations"),
3622 apply_live_preview();
3626 if (ImGui::SliderFloat(tr(
"Animation Speed"),
3629 apply_live_preview();
3633 if (ImGui::Checkbox(tr(
"Enable Glow Effects"),
3635 apply_live_preview();
3641 ImGui::EndTabItem();
3649 if (ImGui::Button(tr(
"Preview Theme"))) {
3654 if (ImGui::Button(tr(
"Reset to Current"))) {
3660 theme_name[name_len] =
'\0';
3663 sizeof(theme_description) - 1);
3666 theme_description[desc_len] =
'\0';
3671 theme_author[author_len] =
'\0';
3674 if (theme_backup_made) {
3675 theme_backup_made =
false;
3685 if (ImGui::Button(tr(
"Save Theme"))) {
3686 edit_theme.
name = std::string(theme_name);
3687 edit_theme.
description = std::string(theme_description);
3688 edit_theme.
author = std::string(theme_author);
3695 theme_backup_made =
false;
3702 bool can_save_over = !current_file_path.empty();
3704 if (!can_save_over) {
3705 ImGui::BeginDisabled();
3708 if (ImGui::Button(tr(
"Save Over Current"))) {
3709 edit_theme.
name = std::string(theme_name);
3710 edit_theme.
description = std::string(theme_description);
3711 edit_theme.
author = std::string(theme_author);
3721 LOG_ERROR(
"Theme Manager",
"Failed to save over current theme");
3725 if (!can_save_over) {
3726 ImGui::EndDisabled();
3729 if (ImGui::IsItemHovered() && can_save_over) {
3730 ImGui::BeginTooltip();
3731 ImGui::Text(tr(
"Save over current theme file:"));
3732 ImGui::Text(
"%s", current_file_path.c_str());
3733 ImGui::EndTooltip();
3734 }
else if (ImGui::IsItemHovered()) {
3735 ImGui::BeginTooltip();
3736 ImGui::Text(tr(
"No current theme file to overwrite"));
3737 ImGui::Text(tr(
"Use 'Save to File...' to create a new theme file"));
3738 ImGui::EndTooltip();
3742 if (ImGui::Button(tr(
"Save to File..."))) {
3743 edit_theme.
name = std::string(theme_name);
3744 edit_theme.
description = std::string(theme_description);
3745 edit_theme.
author = std::string(theme_author);
3748 std::string safe_name =
3749 edit_theme.
name.empty() ?
"custom_theme" : edit_theme.
name;
3753 if (!file_path.empty()) {
3755 if (file_path.find(
".theme") == std::string::npos) {
3756 file_path +=
".theme";
3765 LOG_ERROR(
"Theme Manager",
"Failed to save theme");
3770 if (ImGui::IsItemHovered()) {
3771 ImGui::BeginTooltip();
3772 ImGui::Text(tr(
"Save theme to a .theme file"));
3773 ImGui::Text(tr(
"Saved themes can be shared and loaded later"));
3774 ImGui::EndTooltip();