1301 Theme& theme,
const std::set<std::string>& declared_keys) {
1313 auto declared = [&declared_keys](
const char* key) {
1314 return key !=
nullptr && declared_keys.count(key) != 0;
1316 auto looks_default = [](
const Color& color) {
1317 return color.red == 0.0f && color.green == 0.0f && color.blue == 0.0f &&
1318 (color.alpha == 0.0f || color.alpha == 1.0f);
1320 auto is_unset = [&](
const Color& color,
const char* key =
nullptr) {
1321 return !declared(key) && looks_default(color);
1326 auto needs_semantic_default = [&](
const Color& color,
1327 const char* key =
nullptr) {
1328 return is_unset(color, key);
1342 if (is_unset(theme.
accent,
"accent")) {
1344 ?
Color{0.35f, 0.70f, 0.95f, 1.0f}
1347 if (is_unset(theme.
error,
"error")) {
1348 theme.
error =
Color{0.90f, 0.30f, 0.30f, 1.0f};
1350 if (is_unset(theme.
warning,
"warning")) {
1353 if (is_unset(theme.
success,
"success")) {
1356 if (is_unset(theme.
info,
"info")) {
1357 theme.
info =
Color{0.35f, 0.70f, 0.95f, 1.0f};
1361 auto with_alpha = [](
const Color& color,
float alpha) {
1366 auto lighten = [](
const Color& color,
float amount) {
1367 return Color{std::min(1.0f, color.
red + amount),
1368 std::min(1.0f, color.
green + amount),
1369 std::min(1.0f, color.
blue + amount), color.
alpha};
1373 auto darken = [](
const Color& color,
float amount) {
1374 return Color{std::max(0.0f, color.
red - amount),
1375 std::max(0.0f, color.
green - amount),
1376 std::max(0.0f, color.
blue - amount), color.
alpha};
1378 auto darken_with_floor = [](
const Color& color,
float amount,
1379 float floor = 0.03f) {
1380 return Color{std::max(floor, color.
red - amount),
1381 std::max(floor, color.
green - amount),
1382 std::max(floor, color.
blue - amount), color.
alpha};
1384 auto is_effectively_transparent = [](
const Color& color) {
1385 return color.
alpha <= 0.01f;
1389 if (is_unset(theme.
border,
"border")) {
1395 if (is_unset(theme.
separator,
"separator")) {
1431 if (is_unset(theme.
check_mark,
"check_mark")) {
1459 if (is_unset(theme.
text_link,
"text_link")) {
1465 if (is_unset(theme.
plot_lines,
"plot_lines")) {
1482 if (is_unset(theme.
nav_cursor,
"nav_cursor")) {
1510 if (is_unset(theme.
tree_lines,
"tree_lines")) {
1515 if (is_unset(theme.
tab_dimmed,
"tab_dimmed")) {
1522 "tab_dimmed_selected_overline")) {
1530 if (is_unset(theme.
surface,
"surface")) {
1538 if (needs_semantic_default(theme.
text_highlight,
"text_highlight")) {
1541 if (needs_semantic_default(theme.
link_hover,
"link_hover")) {
1544 if (needs_semantic_default(theme.
code_background,
"code_background")) {
1547 if (needs_semantic_default(theme.
success_light,
"success_light")) {
1550 if (needs_semantic_default(theme.
warning_light,
"warning_light")) {
1553 if (needs_semantic_default(theme.
error_light,
"error_light")) {
1556 if (needs_semantic_default(theme.
info_light,
"info_light")) {
1572 if (needs_semantic_default(theme.
hover_highlight,
"hover_highlight")) {
1575 if (needs_semantic_default(theme.
focus_border,
"focus_border")) {
1587 ?
RGBA(8, 8, 10, 255)
1590 if (needs_semantic_default(theme.
editor_grid,
"editor_grid")) {
1593 if (needs_semantic_default(theme.
editor_cursor,
"editor_cursor")) {
1622 if (needs_semantic_default(theme.
entrance_color,
"entrance_color")) {
1625 if (needs_semantic_default(theme.
hole_color,
"hole_color")) {
1628 if (needs_semantic_default(theme.
exit_color,
"exit_color")) {
1631 if (needs_semantic_default(theme.
item_color,
"item_color")) {
1634 if (needs_semantic_default(theme.
sprite_color,
"sprite_color")) {
1637 if (needs_semantic_default(theme.
transport_color,
"transport_color")) {
1750 ? with_alpha(theme.
surface, 0.95f)
2419 if (!p_open || !*p_open)
2422 ImGui::SetNextWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
2424 if (ImGui::Begin(absl::StrFormat(
"%s Theme Editor",
ICON_MD_PALETTE).c_str(),
2425 p_open, ImGuiWindowFlags_MenuBar)) {
2427 static float editor_animation_time = 0.0f;
2428 editor_animation_time += ImGui::GetIO().DeltaTime;
2430 ImDrawList* draw_list = ImGui::GetWindowDrawList();
2431 ImVec2 window_pos = ImGui::GetWindowPos();
2432 ImVec2 window_size = ImGui::GetWindowSize();
2436 std::vector<gui::Color> theme_colors = {
2437 current_theme.primary, current_theme.secondary, current_theme.accent,
2438 current_theme.success, current_theme.warning, current_theme.error};
2440 for (
size_t i = 0; i < theme_colors.size(); ++i) {
2441 float time_offset = i * 1.0f;
2442 float orbit_radius = 60.0f + i * 8.0f;
2443 float x = window_pos.x + window_size.x * 0.8f +
2444 cosf(editor_animation_time * 0.3f + time_offset) * orbit_radius;
2445 float y = window_pos.y + window_size.y * 0.3f +
2446 sinf(editor_animation_time * 0.3f + time_offset) * orbit_radius;
2449 0.15f + 0.1f * sinf(editor_animation_time * 1.5f + time_offset);
2450 ImU32 orb_color = ImGui::ColorConvertFloat4ToU32(
2451 ImVec4(theme_colors[i].red, theme_colors[i].green,
2452 theme_colors[i].blue, alpha));
2455 4.0f + sinf(editor_animation_time * 2.0f + time_offset) * 1.0f;
2456 draw_list->AddCircleFilled(ImVec2(x, y), radius, orb_color);
2460 if (ImGui::BeginMenuBar()) {
2461 if (ImGui::BeginMenu(tr(
"File"))) {
2462 if (ImGui::MenuItem(
2463 absl::StrFormat(
"%s New Theme",
ICON_MD_ADD).c_str())) {
2467 if (ImGui::MenuItem(
2471 if (!file_path.empty()) {
2476 if (ImGui::MenuItem(
2477 absl::StrFormat(
"%s Save Theme",
ICON_MD_SAVE).c_str())) {
2480 if (!current_file_path.empty()) {
2483 LOG_ERROR(
"Theme Manager",
"Failed to save theme");
2489 if (!file_path.empty()) {
2492 LOG_ERROR(
"Theme Manager",
"Failed to save theme");
2497 if (ImGui::MenuItem(
2502 if (!file_path.empty()) {
2505 LOG_ERROR(
"Theme Manager",
"Failed to save theme");
2510 if (ImGui::MenuItem(
2519 for (
char& c : safe_name) {
2520 if (c ==
' ' || c ==
'/' || c ==
'\\' || c ==
':' || c ==
'*' ||
2521 c ==
'?' || c ==
'"' || c ==
'<' || c ==
'>' || c ==
'|') {
2525 std::string file_path = user_themes_dir + safe_name +
".theme";
2529 LOG_INFO(
"Theme Manager",
"Exported theme to: %s",
2532 LOG_ERROR(
"Theme Manager",
"Failed to export theme to user themes");
2538 if (ImGui::BeginMenu(tr(
"Presets"))) {
2539 if (ImGui::MenuItem(tr(
"YAZE Classic"))) {
2544 if (!available_themes.empty()) {
2546 for (
const auto& theme_name : available_themes) {
2547 if (theme_name ==
"Classic YAZE") {
2550 if (ImGui::MenuItem(theme_name.c_str())) {
2558 ImGui::EndMenuBar();
2562 static char theme_name[128];
2563 static char theme_description[256];
2564 static char theme_author[128];
2565 static bool live_preview =
true;
2566 static Theme original_theme;
2567 static bool theme_backup_made =
false;
2570 auto apply_live_preview = [&]() {
2572 if (!theme_backup_made) {
2574 theme_backup_made =
true;
2583 ImGui::Checkbox(tr(
"Live Preview"), &live_preview);
2585 ImGui::Text(tr(
"| Changes apply immediately when enabled"));
2588 static bool prev_live_preview = live_preview;
2589 if (prev_live_preview && !live_preview && theme_backup_made) {
2591 theme_backup_made =
false;
2593 prev_live_preview = live_preview;
2598 if (ImGui::BeginTable(
"ThemeMetadata", 2,
2599 ImGuiTableFlags_SizingStretchProp)) {
2600 ImGui::TableSetupColumn(
"Field", ImGuiTableColumnFlags_WidthFixed,
2602 ImGui::TableSetupColumn(
"Value", ImGuiTableColumnFlags_WidthStretch);
2604 ImGui::TableNextRow();
2605 ImGui::TableNextColumn();
2606 ImGui::AlignTextToFramePadding();
2607 ImGui::Text(tr(
"Name:"));
2608 ImGui::TableNextColumn();
2609 if (ImGui::InputText(
"##theme_name", theme_name,
sizeof(theme_name))) {
2610 edit_theme.
name = std::string(theme_name);
2613 ImGui::TableNextRow();
2614 ImGui::TableNextColumn();
2615 ImGui::AlignTextToFramePadding();
2616 ImGui::Text(tr(
"Description:"));
2617 ImGui::TableNextColumn();
2618 if (ImGui::InputText(
"##theme_description", theme_description,
2619 sizeof(theme_description))) {
2620 edit_theme.
description = std::string(theme_description);
2623 ImGui::TableNextRow();
2624 ImGui::TableNextColumn();
2625 ImGui::AlignTextToFramePadding();
2626 ImGui::Text(tr(
"Author:"));
2627 ImGui::TableNextColumn();
2628 if (ImGui::InputText(
"##theme_author", theme_author,
2629 sizeof(theme_author))) {
2630 edit_theme.
author = std::string(theme_author);
2639 if (ImGui::BeginTabBar(
"ThemeEditorTabs", ImGuiTabBarFlags_None)) {
2641 static bool first_frame =
true;
2642 if (first_frame && live_preview) {
2643 apply_live_preview();
2644 first_frame =
false;
2648 if (ImGui::BeginTabItem(
2652 ImGui::PushStyleColor(ImGuiCol_ChildBg,
2653 ImVec4(0.1f, 0.1f, 0.12f, 0.5f));
2654 ImGui::BeginChild(
"AccentGenerator", ImVec2(0, 120),
true);
2656 ImGui::TextColored(ImVec4(0.7f, 0.8f, 1.0f, 1.0f),
2657 tr(
"%s Generate Theme from Accent Color"),
2661 static Color accent_picker_color = {0.4f, 0.6f, 0.9f, 1.0f};
2662 static bool dark_mode_generate =
true;
2664 ImGui::Text(tr(
"Accent Color:"));
2667 if (ImGui::ColorEdit3(
"##AccentPicker", &accent_vec.x,
2668 ImGuiColorEditFlags_NoInputs)) {
2669 accent_picker_color = {accent_vec.x, accent_vec.y, accent_vec.z,
2674 ImGui::Checkbox(tr(
"Dark Mode"), &dark_mode_generate);
2677 if (ImGui::Button(absl::StrFormat(
"%s Generate",
ICON_MD_BOLT).c_str(),
2682 edit_theme = generated;
2683 apply_live_preview();
2687 ImGui::Text(tr(
"Preview: "));
2691 Theme preview_theme =
2698 ImGui::ColorButton(
"##prev_primary", prev_primary, 0, ImVec2(24, 24));
2699 ImGui::SameLine(0, 2);
2700 ImGui::ColorButton(
"##prev_secondary", prev_secondary, 0,
2702 ImGui::SameLine(0, 2);
2703 ImGui::ColorButton(
"##prev_bg", prev_bg, 0, ImVec2(24, 24));
2704 ImGui::SameLine(0, 2);
2705 ImGui::ColorButton(
"##prev_surface", prev_surface, 0, ImVec2(24, 24));
2708 ImGui::PopStyleColor();
2712 if (ImGui::BeginTable(
"PrimaryColorsTable", 3,
2713 ImGuiTableFlags_SizingStretchProp)) {
2714 ImGui::TableSetupColumn(
"Color", ImGuiTableColumnFlags_WidthFixed,
2716 ImGui::TableSetupColumn(
"Picker", ImGuiTableColumnFlags_WidthStretch,
2718 ImGui::TableSetupColumn(
"Preview", ImGuiTableColumnFlags_WidthStretch,
2720 ImGui::TableHeadersRow();
2723 ImGui::TableNextRow();
2724 ImGui::TableNextColumn();
2725 ImGui::AlignTextToFramePadding();
2726 ImGui::Text(tr(
"Primary:"));
2727 ImGui::TableNextColumn();
2729 if (ImGui::ColorEdit3(
"##primary", &primary.x)) {
2730 edit_theme.
primary = {primary.x, primary.y, primary.z, primary.w};
2731 apply_live_preview();
2733 ImGui::TableNextColumn();
2734 ImGui::Button(tr(
"Primary Preview"), ImVec2(-1, 30));
2737 ImGui::TableNextRow();
2738 ImGui::TableNextColumn();
2739 ImGui::AlignTextToFramePadding();
2740 ImGui::Text(tr(
"Secondary:"));
2741 ImGui::TableNextColumn();
2743 if (ImGui::ColorEdit3(
"##secondary", &secondary.x)) {
2744 edit_theme.
secondary = {secondary.x, secondary.y, secondary.z,
2746 apply_live_preview();
2748 ImGui::TableNextColumn();
2749 ImGui::PushStyleColor(ImGuiCol_Button, secondary);
2750 ImGui::Button(tr(
"Secondary Preview"), ImVec2(-1, 30));
2751 ImGui::PopStyleColor();
2754 ImGui::TableNextRow();
2755 ImGui::TableNextColumn();
2756 ImGui::AlignTextToFramePadding();
2757 ImGui::Text(tr(
"Accent:"));
2758 ImGui::TableNextColumn();
2760 if (ImGui::ColorEdit3(
"##accent", &accent.x)) {
2761 edit_theme.
accent = {accent.x, accent.y, accent.z, accent.w};
2762 apply_live_preview();
2764 ImGui::TableNextColumn();
2765 ImGui::PushStyleColor(ImGuiCol_Button, accent);
2766 ImGui::Button(tr(
"Accent Preview"), ImVec2(-1, 30));
2767 ImGui::PopStyleColor();
2770 ImGui::TableNextRow();
2771 ImGui::TableNextColumn();
2772 ImGui::AlignTextToFramePadding();
2773 ImGui::Text(tr(
"Background:"));
2774 ImGui::TableNextColumn();
2776 if (ImGui::ColorEdit4(
"##background", &background.x)) {
2777 edit_theme.
background = {background.x, background.y, background.z,
2779 apply_live_preview();
2781 ImGui::TableNextColumn();
2782 ImGui::Text(tr(
"Background preview shown in window"));
2786 ImGui::EndTabItem();
2790 if (ImGui::BeginTabItem(
2792 if (ImGui::BeginTable(
"TextColorsTable", 3,
2793 ImGuiTableFlags_SizingStretchProp)) {
2794 ImGui::TableSetupColumn(
"Color", ImGuiTableColumnFlags_WidthFixed,
2796 ImGui::TableSetupColumn(
"Picker", ImGuiTableColumnFlags_WidthStretch,
2798 ImGui::TableSetupColumn(
"Preview", ImGuiTableColumnFlags_WidthStretch,
2800 ImGui::TableHeadersRow();
2803 auto text_colors = std::vector<std::pair<const char*, Color*>>{
2813 for (
auto& [label, color_ptr] : text_colors) {
2814 ImGui::TableNextRow();
2815 ImGui::TableNextColumn();
2816 ImGui::AlignTextToFramePadding();
2817 ImGui::Text(
"%s:", label);
2819 ImGui::TableNextColumn();
2821 std::string
id = absl::StrFormat(
"##%s", label);
2822 if (ImGui::ColorEdit3(
id.c_str(), &color_vec.x)) {
2823 *color_ptr = {color_vec.x, color_vec.y, color_vec.z, color_vec.w};
2824 apply_live_preview();
2827 ImGui::TableNextColumn();
2828 ImGui::PushStyleColor(ImGuiCol_Text, color_vec);
2829 ImGui::Text(tr(
"Sample %s"), label);
2830 ImGui::PopStyleColor();
2835 ImGui::EndTabItem();
2839 if (ImGui::BeginTabItem(
2841 if (ImGui::BeginTable(
"InteractiveColorsTable", 3,
2842 ImGuiTableFlags_SizingStretchProp)) {
2843 ImGui::TableSetupColumn(
"Element", ImGuiTableColumnFlags_WidthFixed,
2845 ImGui::TableSetupColumn(
"Picker", ImGuiTableColumnFlags_WidthStretch,
2847 ImGui::TableSetupColumn(
"Preview", ImGuiTableColumnFlags_WidthStretch,
2849 ImGui::TableHeadersRow();
2852 auto interactive_colors =
2853 std::vector<std::tuple<const char*, Color*, ImGuiCol>>{
2854 {
"Button", &edit_theme.
button, ImGuiCol_Button},
2856 ImGuiCol_ButtonHovered},
2858 ImGuiCol_ButtonActive},
2859 {
"Frame Background", &edit_theme.
frame_bg, ImGuiCol_FrameBg},
2861 ImGuiCol_FrameBgHovered},
2863 ImGuiCol_FrameBgActive},
2864 {
"Check Mark", &edit_theme.
check_mark, ImGuiCol_CheckMark},
2865 {
"Slider Grab", &edit_theme.
slider_grab, ImGuiCol_SliderGrab},
2867 ImGuiCol_SliderGrabActive}};
2869 for (
auto& [label, color_ptr, imgui_col] : interactive_colors) {
2870 ImGui::TableNextRow();
2871 ImGui::TableNextColumn();
2872 ImGui::AlignTextToFramePadding();
2873 ImGui::Text(
"%s:", label);
2875 ImGui::TableNextColumn();
2877 std::string
id = absl::StrFormat(
"##%s", label);
2878 if (ImGui::ColorEdit3(
id.c_str(), &color_vec.x)) {
2879 *color_ptr = {color_vec.x, color_vec.y, color_vec.z, color_vec.w};
2880 apply_live_preview();
2883 ImGui::TableNextColumn();
2884 ImGui::PushStyleColor(imgui_col, color_vec);
2885 ImGui::Button(absl::StrFormat(
"Preview %s", label).c_str(),
2887 ImGui::PopStyleColor();
2892 ImGui::EndTabItem();
2896 if (ImGui::BeginTabItem(
2898 ImGui::Text(tr(
"Rounding and Border Settings:"));
2900 if (ImGui::SliderFloat(tr(
"Window Rounding"),
2905 if (ImGui::SliderFloat(tr(
"Frame Rounding"), &edit_theme.
frame_rounding,
2910 if (ImGui::SliderFloat(tr(
"Scrollbar Rounding"),
2915 if (ImGui::SliderFloat(tr(
"Tab Rounding"), &edit_theme.
tab_rounding,
2920 if (ImGui::SliderFloat(tr(
"Grab Rounding"), &edit_theme.
grab_rounding,
2927 ImGui::Text(tr(
"Border Sizes:"));
2929 if (ImGui::SliderFloat(tr(
"Window Border Size"),
2934 if (ImGui::SliderFloat(tr(
"Frame Border Size"),
2941 ImGui::Text(tr(
"Animation & Effects:"));
2943 if (ImGui::Checkbox(tr(
"Enable Animations"),
2949 if (ImGui::SliderFloat(tr(
"Animation Speed"),
2951 apply_live_preview();
2954 if (ImGui::Checkbox(tr(
"Enable Glow Effects"),
2960 ImGui::EndTabItem();
2964 if (ImGui::BeginTabItem(
2966 if (ImGui::BeginTable(
"NavigationTable", 3,
2967 ImGuiTableFlags_SizingStretchProp)) {
2968 ImGui::TableSetupColumn(
"Element", ImGuiTableColumnFlags_WidthFixed,
2970 ImGui::TableSetupColumn(
"Picker", ImGuiTableColumnFlags_WidthStretch,
2972 ImGui::TableSetupColumn(
"Preview", ImGuiTableColumnFlags_WidthStretch,
2974 ImGui::TableHeadersRow();
2977 auto window_colors =
2978 std::vector<std::tuple<const char*, Color*, const char*>>{
2979 {
"Window Background", &edit_theme.
window_bg,
2980 "Main window background"},
2981 {
"Child Background", &edit_theme.
child_bg,
2982 "Child window background"},
2983 {
"Popup Background", &edit_theme.
popup_bg,
2984 "Popup window background"},
2985 {
"Modal Background", &edit_theme.
modal_bg,
2986 "Modal window background"},
2988 "Menu bar background"}};
2990 for (
auto& [label, color_ptr, description] : window_colors) {
2991 ImGui::TableNextRow();
2992 ImGui::TableNextColumn();
2993 ImGui::AlignTextToFramePadding();
2994 ImGui::Text(
"%s:", label);
2996 ImGui::TableNextColumn();
2998 std::string
id = absl::StrFormat(
"##window_%s", label);
2999 if (ImGui::ColorEdit4(
id.c_str(), &color_vec.x)) {
3000 *color_ptr = {color_vec.x, color_vec.y, color_vec.z, color_vec.w};
3001 apply_live_preview();
3004 ImGui::TableNextColumn();
3005 ImGui::TextWrapped(
"%s", description);
3014 if (ImGui::CollapsingHeader(tr(
"Headers & Tabs"),
3015 ImGuiTreeNodeFlags_DefaultOpen)) {
3016 if (ImGui::BeginTable(
"HeaderTabTable", 3,
3017 ImGuiTableFlags_SizingStretchProp)) {
3018 ImGui::TableSetupColumn(
"Element", ImGuiTableColumnFlags_WidthFixed,
3020 ImGui::TableSetupColumn(
"Picker",
3021 ImGuiTableColumnFlags_WidthStretch, 0.6f);
3022 ImGui::TableSetupColumn(
"Preview",
3023 ImGuiTableColumnFlags_WidthStretch, 0.4f);
3024 ImGui::TableHeadersRow();
3026 auto header_tab_colors =
3027 std::vector<std::pair<const char*, Color*>>{
3028 {
"Header", &edit_theme.
header},
3031 {
"Tab", &edit_theme.
tab},
3038 {
"Title Background", &edit_theme.
title_bg},
3042 for (
auto& [label, color_ptr] : header_tab_colors) {
3043 ImGui::TableNextRow();
3044 ImGui::TableNextColumn();
3045 ImGui::AlignTextToFramePadding();
3046 ImGui::Text(
"%s:", label);
3048 ImGui::TableNextColumn();
3050 std::string
id = absl::StrFormat(
"##header_%s", label);
3051 if (ImGui::ColorEdit3(
id.c_str(), &color_vec.x)) {
3052 *color_ptr = {color_vec.x, color_vec.y, color_vec.z,
3054 apply_live_preview();
3057 ImGui::TableNextColumn();
3058 ImGui::PushStyleColor(ImGuiCol_Button, color_vec);
3059 ImGui::Button(absl::StrFormat(
"Preview %s", label).c_str(),
3061 ImGui::PopStyleColor();
3069 if (ImGui::CollapsingHeader(tr(
"Navigation & Special"))) {
3070 if (ImGui::BeginTable(
"NavSpecialTable", 3,
3071 ImGuiTableFlags_SizingStretchProp)) {
3072 ImGui::TableSetupColumn(
"Element", ImGuiTableColumnFlags_WidthFixed,
3074 ImGui::TableSetupColumn(
"Picker",
3075 ImGuiTableColumnFlags_WidthStretch, 0.6f);
3076 ImGui::TableSetupColumn(
"Description",
3077 ImGuiTableColumnFlags_WidthStretch, 0.4f);
3078 ImGui::TableHeadersRow();
3080 auto nav_special_colors =
3081 std::vector<std::tuple<const char*, Color*, const char*>>{
3083 "Navigation cursor color"},
3085 "Window selection highlight"},
3087 "Background dimming for navigation"},
3089 "Background dimming for modals"},
3091 "Drag and drop target highlight"},
3093 "Docking area preview"},
3095 "Empty docking space background"}};
3097 for (
auto& [label, color_ptr, description] : nav_special_colors) {
3098 ImGui::TableNextRow();
3099 ImGui::TableNextColumn();
3100 ImGui::AlignTextToFramePadding();
3101 ImGui::Text(
"%s:", label);
3103 ImGui::TableNextColumn();
3105 std::string
id = absl::StrFormat(
"##nav_%s", label);
3106 if (ImGui::ColorEdit4(
id.c_str(), &color_vec.x)) {
3107 *color_ptr = {color_vec.x, color_vec.y, color_vec.z,
3109 apply_live_preview();
3112 ImGui::TableNextColumn();
3113 ImGui::TextWrapped(
"%s", description);
3120 ImGui::EndTabItem();
3124 if (ImGui::BeginTabItem(
3126 if (ImGui::BeginTable(
"TablesDataTable", 3,
3127 ImGuiTableFlags_SizingStretchProp)) {
3128 ImGui::TableSetupColumn(
"Element", ImGuiTableColumnFlags_WidthFixed,
3130 ImGui::TableSetupColumn(
"Picker", ImGuiTableColumnFlags_WidthStretch,
3132 ImGui::TableSetupColumn(
"Description",
3133 ImGuiTableColumnFlags_WidthStretch, 0.4f);
3134 ImGui::TableHeadersRow();
3137 std::vector<std::tuple<const char*, Color*, const char*>>{
3139 "Table column headers"},
3141 "Outer table borders"},
3143 "Inner table borders"},
3145 "Normal table rows"},
3147 "Alternating table rows"},
3149 "Tree view connection lines"}};
3151 for (
auto& [label, color_ptr, description] : table_colors) {
3152 ImGui::TableNextRow();
3153 ImGui::TableNextColumn();
3154 ImGui::AlignTextToFramePadding();
3155 ImGui::Text(
"%s:", label);
3157 ImGui::TableNextColumn();
3159 std::string
id = absl::StrFormat(
"##table_%s", label);
3160 if (ImGui::ColorEdit4(
id.c_str(), &color_vec.x)) {
3161 *color_ptr = {color_vec.x, color_vec.y, color_vec.z, color_vec.w};
3162 apply_live_preview();
3165 ImGui::TableNextColumn();
3166 ImGui::TextWrapped(
"%s", description);
3175 if (ImGui::CollapsingHeader(tr(
"Plots & Graphs"))) {
3176 if (ImGui::BeginTable(
"PlotsTable", 3,
3177 ImGuiTableFlags_SizingStretchProp)) {
3178 ImGui::TableSetupColumn(
"Element", ImGuiTableColumnFlags_WidthFixed,
3180 ImGui::TableSetupColumn(
"Picker",
3181 ImGuiTableColumnFlags_WidthStretch, 0.6f);
3182 ImGui::TableSetupColumn(
"Description",
3183 ImGuiTableColumnFlags_WidthStretch, 0.4f);
3184 ImGui::TableHeadersRow();
3187 std::vector<std::tuple<const char*, Color*, const char*>>{
3188 {
"Plot Lines", &edit_theme.
plot_lines,
"Line plot color"},
3190 "Line plot hover color"},
3192 "Histogram fill color"},
3193 {
"Plot Histogram Hovered",
3195 "Histogram hover color"}};
3197 for (
auto& [label, color_ptr, description] : plot_colors) {
3198 ImGui::TableNextRow();
3199 ImGui::TableNextColumn();
3200 ImGui::AlignTextToFramePadding();
3201 ImGui::Text(
"%s:", label);
3203 ImGui::TableNextColumn();
3205 std::string
id = absl::StrFormat(
"##plot_%s", label);
3206 if (ImGui::ColorEdit3(
id.c_str(), &color_vec.x)) {
3207 *color_ptr = {color_vec.x, color_vec.y, color_vec.z,
3209 apply_live_preview();
3212 ImGui::TableNextColumn();
3213 ImGui::TextWrapped(
"%s", description);
3220 ImGui::EndTabItem();
3224 if (ImGui::BeginTabItem(
3226 if (ImGui::BeginTable(
"BordersControlsTable", 3,
3227 ImGuiTableFlags_SizingStretchProp)) {
3228 ImGui::TableSetupColumn(
"Element", ImGuiTableColumnFlags_WidthFixed,
3230 ImGui::TableSetupColumn(
"Picker", ImGuiTableColumnFlags_WidthStretch,
3232 ImGui::TableSetupColumn(
"Description",
3233 ImGuiTableColumnFlags_WidthStretch, 0.4f);
3234 ImGui::TableHeadersRow();
3236 auto border_control_colors =
3237 std::vector<std::tuple<const char*, Color*, const char*>>{
3238 {
"Border", &edit_theme.
border,
"General border color"},
3240 "Border shadow/depth"},
3242 "Horizontal/vertical separators"},
3244 "Separator hover state"},
3246 "Separator active/dragged state"},
3248 "Scrollbar track background"},
3250 "Scrollbar handle"},
3252 "Scrollbar handle hover"},
3254 "Scrollbar handle active"},
3256 "Window resize grip"},
3258 "Resize grip hover"},
3260 "Resize grip active"}};
3262 for (
auto& [label, color_ptr, description] : border_control_colors) {
3263 ImGui::TableNextRow();
3264 ImGui::TableNextColumn();
3265 ImGui::AlignTextToFramePadding();
3266 ImGui::Text(
"%s:", label);
3268 ImGui::TableNextColumn();
3270 std::string
id = absl::StrFormat(
"##border_%s", label);
3271 if (ImGui::ColorEdit4(
id.c_str(), &color_vec.x)) {
3272 *color_ptr = {color_vec.x, color_vec.y, color_vec.z, color_vec.w};
3273 apply_live_preview();
3276 ImGui::TableNextColumn();
3277 ImGui::TextWrapped(
"%s", description);
3283 ImGui::EndTabItem();
3287 if (ImGui::BeginTabItem(
3290 tr(
"Enhanced semantic colors and editor-specific customization"));
3294 if (ImGui::CollapsingHeader(tr(
"Enhanced Semantic Colors"),
3295 ImGuiTreeNodeFlags_DefaultOpen)) {
3296 if (ImGui::BeginTable(
"EnhancedSemanticTable", 3,
3297 ImGuiTableFlags_SizingStretchProp)) {
3298 ImGui::TableSetupColumn(
"Color", ImGuiTableColumnFlags_WidthFixed,
3300 ImGui::TableSetupColumn(
"Picker",
3301 ImGuiTableColumnFlags_WidthStretch, 0.6f);
3302 ImGui::TableSetupColumn(
"Description",
3303 ImGuiTableColumnFlags_WidthStretch, 0.4f);
3304 ImGui::TableHeadersRow();
3306 auto enhanced_colors =
3307 std::vector<std::tuple<const char*, Color*, const char*>>{
3309 "Code blocks background"},
3311 "Light success variant"},
3313 "Light warning variant"},
3315 "Light error variant"},
3317 "Light info variant"}};
3319 for (
auto& [label, color_ptr, description] : enhanced_colors) {
3320 ImGui::TableNextRow();
3321 ImGui::TableNextColumn();
3322 ImGui::AlignTextToFramePadding();
3323 ImGui::Text(
"%s:", label);
3325 ImGui::TableNextColumn();
3327 std::string
id = absl::StrFormat(
"##enhanced_%s", label);
3328 if (ImGui::ColorEdit3(
id.c_str(), &color_vec.x)) {
3329 *color_ptr = {color_vec.x, color_vec.y, color_vec.z,
3331 apply_live_preview();
3334 ImGui::TableNextColumn();
3335 ImGui::TextWrapped(
"%s", description);
3343 if (ImGui::CollapsingHeader(tr(
"UI State Colors"))) {
3344 if (ImGui::BeginTable(
"UIStateTable", 3,
3345 ImGuiTableFlags_SizingStretchProp)) {
3346 ImGui::TableSetupColumn(
"Color", ImGuiTableColumnFlags_WidthFixed,
3348 ImGui::TableSetupColumn(
"Picker",
3349 ImGuiTableColumnFlags_WidthStretch, 0.6f);
3350 ImGui::TableSetupColumn(
"Description",
3351 ImGuiTableColumnFlags_WidthStretch, 0.4f);
3352 ImGui::TableHeadersRow();
3355 ImGui::TableNextRow();
3356 ImGui::TableNextColumn();
3357 ImGui::AlignTextToFramePadding();
3358 ImGui::Text(tr(
"Active Selection:"));
3359 ImGui::TableNextColumn();
3360 ImVec4 active_selection =
3362 if (ImGui::ColorEdit4(
"##active_selection", &active_selection.x)) {
3364 active_selection.x, active_selection.y, active_selection.z,
3365 active_selection.w};
3366 apply_live_preview();
3368 ImGui::TableNextColumn();
3369 ImGui::TextWrapped(tr(
"Active/selected UI elements"));
3371 ImGui::TableNextRow();
3372 ImGui::TableNextColumn();
3373 ImGui::AlignTextToFramePadding();
3374 ImGui::Text(tr(
"Hover Highlight:"));
3375 ImGui::TableNextColumn();
3376 ImVec4 hover_highlight =
3378 if (ImGui::ColorEdit4(
"##hover_highlight", &hover_highlight.x)) {
3380 hover_highlight.x, hover_highlight.y, hover_highlight.z,
3382 apply_live_preview();
3384 ImGui::TableNextColumn();
3385 ImGui::TextWrapped(tr(
"General hover state highlighting"));
3387 ImGui::TableNextRow();
3388 ImGui::TableNextColumn();
3389 ImGui::AlignTextToFramePadding();
3390 ImGui::Text(tr(
"Focus Border:"));
3391 ImGui::TableNextColumn();
3393 if (ImGui::ColorEdit3(
"##focus_border", &focus_border.x)) {
3394 edit_theme.
focus_border = {focus_border.x, focus_border.y,
3395 focus_border.z, focus_border.w};
3396 apply_live_preview();
3398 ImGui::TableNextColumn();
3399 ImGui::TextWrapped(tr(
"Border for focused input elements"));
3401 ImGui::TableNextRow();
3402 ImGui::TableNextColumn();
3403 ImGui::AlignTextToFramePadding();
3404 ImGui::Text(tr(
"Disabled Overlay:"));
3405 ImGui::TableNextColumn();
3406 ImVec4 disabled_overlay =
3408 if (ImGui::ColorEdit4(
"##disabled_overlay", &disabled_overlay.x)) {
3410 disabled_overlay.x, disabled_overlay.y, disabled_overlay.z,
3411 disabled_overlay.w};
3412 apply_live_preview();
3414 ImGui::TableNextColumn();
3416 tr(
"Semi-transparent overlay for disabled elements"));
3423 if (ImGui::CollapsingHeader(tr(
"Editor-Specific Colors"))) {
3424 if (ImGui::BeginTable(
"EditorColorsTable", 3,
3425 ImGuiTableFlags_SizingStretchProp)) {
3426 ImGui::TableSetupColumn(
"Color", ImGuiTableColumnFlags_WidthFixed,
3428 ImGui::TableSetupColumn(
"Picker",
3429 ImGuiTableColumnFlags_WidthStretch, 0.6f);
3430 ImGui::TableSetupColumn(
"Description",
3431 ImGuiTableColumnFlags_WidthStretch, 0.4f);
3432 ImGui::TableHeadersRow();
3434 auto editor_colors =
3435 std::vector<std::tuple<const char*, Color*, const char*, bool>>{
3437 "Main editor canvas background",
false},
3439 "Grid lines in map/graphics editors",
true},
3441 "Cursor color in editors",
false},
3443 "Selection highlight in editors",
true},
3445 "Overworld entrance boxes",
true},
3447 "Overworld hole boxes",
true},
3449 "Overworld exit boxes",
true},
3451 "Overworld item boxes",
true},
3453 "Overworld sprite boxes",
true},
3455 "Transport and connection markers",
true},
3457 "Music zone markers",
true}};
3459 for (
auto& [label, color_ptr, description, use_alpha] :
3461 ImGui::TableNextRow();
3462 ImGui::TableNextColumn();
3463 ImGui::AlignTextToFramePadding();
3464 ImGui::Text(
"%s:", label);
3466 ImGui::TableNextColumn();
3468 std::string
id = absl::StrFormat(
"##editor_%s", label);
3469 if (use_alpha ? ImGui::ColorEdit4(
id.c_str(), &color_vec.x)
3470 : ImGui::ColorEdit3(
id.c_str(), &color_vec.x)) {
3471 *color_ptr = {color_vec.x, color_vec.y, color_vec.z,
3473 apply_live_preview();
3476 ImGui::TableNextColumn();
3477 ImGui::TextWrapped(
"%s", description);
3484 ImGui::EndTabItem();
3488 if (ImGui::BeginTabItem(
3490 ImGui::Text(tr(
"Control UI density, spacing, and typography scaling"));
3494 ImGui::TextColored(ImVec4(0.7f, 0.8f, 1.0f, 1.0f),
3499 int current_preset =
static_cast<int>(edit_theme.
density_preset);
3501 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(20, 12));
3502 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(12, 0));
3505 bool is_compact = (current_preset == 0);
3507 ImGui::PushStyleColor(
3516 apply_live_preview();
3519 ImGui::PopStyleColor();
3521 if (ImGui::IsItemHovered()) {
3523 tr(
"Dense UI with smaller widgets and tighter spacing\n"
3524 "Best for: Information-dense workflows"));
3530 bool is_normal = (current_preset == 1);
3532 ImGui::PushStyleColor(
3537 if (ImGui::Button(absl::StrFormat(
"%s Normal",
ICON_MD_CHECK).c_str(),
3540 apply_live_preview();
3543 ImGui::PopStyleColor();
3545 if (ImGui::IsItemHovered()) {
3547 tr(
"Balanced spacing and widget sizes\n"
3548 "Best for: General use"));
3554 bool is_comfortable = (current_preset == 2);
3555 if (is_comfortable) {
3556 ImGui::PushStyleColor(
3565 apply_live_preview();
3567 if (is_comfortable) {
3568 ImGui::PopStyleColor();
3570 if (ImGui::IsItemHovered()) {
3572 tr(
"Spacious layout with larger click targets\n"
3573 "Best for: Touch screens, accessibility"));
3576 ImGui::PopStyleVar(2);
3583 if (ImGui::CollapsingHeader(tr(
"Advanced Density Controls"))) {
3586 ImGui::Text(tr(
"Fine-tune individual spacing multipliers:"));
3590 if (ImGui::SliderFloat(tr(
"Compact Factor"),
3593 apply_live_preview();
3595 if (ImGui::IsItemHovered()) {
3597 tr(
"Global density multiplier (0.5 = very compact, "
3598 "1.5 = very spacious)"));
3604 if (ImGui::SliderFloat(tr(
"Widget Height"),
3607 apply_live_preview();
3611 if (ImGui::SliderFloat(tr(
"Item Spacing"),
3614 apply_live_preview();
3618 if (ImGui::SliderFloat(tr(
"Toolbar Height"),
3621 apply_live_preview();
3625 if (ImGui::SliderFloat(tr(
"Panel Padding"),
3628 apply_live_preview();
3632 if (ImGui::SliderFloat(tr(
"Button Padding"),
3635 apply_live_preview();
3639 if (ImGui::SliderFloat(tr(
"Table Row Height"),
3642 apply_live_preview();
3649 if (ImGui::CollapsingHeader(tr(
"Corner Rounding"))) {
3652 if (ImGui::SliderFloat(tr(
"Window Rounding"),
3655 apply_live_preview();
3658 if (ImGui::SliderFloat(tr(
"Frame Rounding"),
3661 apply_live_preview();
3664 if (ImGui::SliderFloat(tr(
"Tab Rounding"), &edit_theme.
tab_rounding,
3665 0.0f, 12.0f,
"%.1f")) {
3666 apply_live_preview();
3669 if (ImGui::SliderFloat(tr(
"Scrollbar Rounding"),
3672 apply_live_preview();
3675 if (ImGui::SliderFloat(tr(
"Grab Rounding"), &edit_theme.
grab_rounding,
3676 0.0f, 12.0f,
"%.1f")) {
3677 apply_live_preview();
3684 if (ImGui::CollapsingHeader(tr(
"Animation Settings"))) {
3687 if (ImGui::Checkbox(tr(
"Enable Animations"),
3689 apply_live_preview();
3693 if (ImGui::SliderFloat(tr(
"Animation Speed"),
3696 apply_live_preview();
3700 if (ImGui::Checkbox(tr(
"Enable Glow Effects"),
3702 apply_live_preview();
3708 ImGui::EndTabItem();
3716 if (ImGui::Button(tr(
"Preview Theme"))) {
3721 if (ImGui::Button(tr(
"Reset to Current"))) {
3727 theme_name[name_len] =
'\0';
3730 sizeof(theme_description) - 1);
3733 theme_description[desc_len] =
'\0';
3738 theme_author[author_len] =
'\0';
3741 if (theme_backup_made) {
3742 theme_backup_made =
false;
3752 if (ImGui::Button(tr(
"Save Theme"))) {
3753 edit_theme.
name = std::string(theme_name);
3754 edit_theme.
description = std::string(theme_description);
3755 edit_theme.
author = std::string(theme_author);
3762 theme_backup_made =
false;
3769 bool can_save_over = !current_file_path.empty();
3771 if (!can_save_over) {
3772 ImGui::BeginDisabled();
3775 if (ImGui::Button(tr(
"Save Over Current"))) {
3776 edit_theme.
name = std::string(theme_name);
3777 edit_theme.
description = std::string(theme_description);
3778 edit_theme.
author = std::string(theme_author);
3788 LOG_ERROR(
"Theme Manager",
"Failed to save over current theme");
3792 if (!can_save_over) {
3793 ImGui::EndDisabled();
3796 if (ImGui::IsItemHovered() && can_save_over) {
3797 ImGui::BeginTooltip();
3798 ImGui::Text(tr(
"Save over current theme file:"));
3799 ImGui::Text(
"%s", current_file_path.c_str());
3800 ImGui::EndTooltip();
3801 }
else if (ImGui::IsItemHovered()) {
3802 ImGui::BeginTooltip();
3803 ImGui::Text(tr(
"No current theme file to overwrite"));
3804 ImGui::Text(tr(
"Use 'Save to File...' to create a new theme file"));
3805 ImGui::EndTooltip();
3809 if (ImGui::Button(tr(
"Save to File..."))) {
3810 edit_theme.
name = std::string(theme_name);
3811 edit_theme.
description = std::string(theme_description);
3812 edit_theme.
author = std::string(theme_author);
3815 std::string safe_name =
3816 edit_theme.
name.empty() ?
"custom_theme" : edit_theme.
name;
3820 if (!file_path.empty()) {
3822 if (file_path.find(
".theme") == std::string::npos) {
3823 file_path +=
".theme";
3832 LOG_ERROR(
"Theme Manager",
"Failed to save theme");
3837 if (ImGui::IsItemHovered()) {
3838 ImGui::BeginTooltip();
3839 ImGui::Text(tr(
"Save theme to a .theme file"));
3840 ImGui::Text(tr(
"Saved themes can be shared and loaded later"));
3841 ImGui::EndTooltip();