Add theme management and background rendering features
- Introduced a comprehensive theme management system, allowing users to load, save, and switch between multiple themes. - Added support for various built-in themes, enhancing the visual customization of the application. - Implemented a background renderer for improved visual effects in docking windows, including grid backgrounds and subtle animations. - Enhanced the EditorManager UI with themed elements, providing a more cohesive and engaging user experience. - Updated CMake configuration to include new theme and background renderer source files, ensuring proper integration into the build system.
This commit is contained in:
@@ -123,6 +123,10 @@ endif()
|
|||||||
include(cmake/imgui.cmake)
|
include(cmake/imgui.cmake)
|
||||||
|
|
||||||
# Project Files
|
# Project Files
|
||||||
|
# Copy theme files to build directory
|
||||||
|
file(GLOB THEME_FILES "${CMAKE_SOURCE_DIR}/assets/themes/*.theme")
|
||||||
|
file(COPY ${THEME_FILES} DESTINATION "${CMAKE_BINARY_DIR}/assets/themes/")
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
|
||||||
# Tests
|
# Tests
|
||||||
|
|||||||
62
assets/themes/cyberpunk.theme
Normal file
62
assets/themes/cyberpunk.theme
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
# Cyberpunk Theme
|
||||||
|
# Neon-inspired futuristic theme
|
||||||
|
name=Cyberpunk
|
||||||
|
description=Neon-inspired futuristic theme
|
||||||
|
author=YAZE Team
|
||||||
|
version=1.0
|
||||||
|
|
||||||
|
[colors]
|
||||||
|
# Primary colors (neon cyberpunk)
|
||||||
|
primary=255,20,147,255
|
||||||
|
secondary=0,255,255,255
|
||||||
|
accent=255,0,128,255
|
||||||
|
background=10,10,20,255
|
||||||
|
surface=20,20,40,255
|
||||||
|
|
||||||
|
# Status colors
|
||||||
|
error=255,50,100,255
|
||||||
|
warning=255,255,0,255
|
||||||
|
success=0,255,100,255
|
||||||
|
info=100,200,255,255
|
||||||
|
|
||||||
|
# Text colors
|
||||||
|
text_primary=255,255,255,255
|
||||||
|
text_secondary=200,200,255,255
|
||||||
|
text_disabled=100,100,150,255
|
||||||
|
|
||||||
|
# Window colors
|
||||||
|
window_bg=15,15,30,240
|
||||||
|
child_bg=10,10,25,200
|
||||||
|
popup_bg=20,20,40,250
|
||||||
|
|
||||||
|
# Interactive elements
|
||||||
|
button=40,20,60,255
|
||||||
|
button_hovered=120,20,120,255
|
||||||
|
button_active=160,40,160,255
|
||||||
|
frame_bg=30,30,50,255
|
||||||
|
frame_bg_hovered=40,40,70,255
|
||||||
|
frame_bg_active=60,20,80,255
|
||||||
|
|
||||||
|
# Navigation
|
||||||
|
header=30,10,50,255
|
||||||
|
header_hovered=80,20,100,255
|
||||||
|
header_active=120,40,140,255
|
||||||
|
tab=25,15,45,255
|
||||||
|
tab_hovered=60,30,80,255
|
||||||
|
tab_active=100,20,120,255
|
||||||
|
menu_bar_bg=20,10,40,255
|
||||||
|
title_bg=25,15,45,255
|
||||||
|
title_bg_active=30,10,50,255
|
||||||
|
title_bg_collapsed=25,15,45,255
|
||||||
|
|
||||||
|
[style]
|
||||||
|
window_rounding=10.0
|
||||||
|
frame_rounding=8.0
|
||||||
|
scrollbar_rounding=10.0
|
||||||
|
grab_rounding=6.0
|
||||||
|
tab_rounding=6.0
|
||||||
|
window_border_size=1.0
|
||||||
|
frame_border_size=1.0
|
||||||
|
enable_animations=true
|
||||||
|
enable_glow_effects=true
|
||||||
|
animation_speed=1.5
|
||||||
73
assets/themes/forest.theme
Normal file
73
assets/themes/forest.theme
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
# Forest Theme
|
||||||
|
# Enhanced forest theme with better readability
|
||||||
|
name=Forest
|
||||||
|
description=Deep forest theme with enhanced readability
|
||||||
|
author=YAZE Team
|
||||||
|
version=1.0
|
||||||
|
|
||||||
|
[colors]
|
||||||
|
# Primary colors (enhanced forest with better contrast)
|
||||||
|
primary=100,180,120,255 # Brighter forest green
|
||||||
|
secondary=70,130,85,255 # Mid forest green
|
||||||
|
accent=130,220,150,255 # Light accent green
|
||||||
|
background=15,25,15,255 # Darker background for contrast
|
||||||
|
surface=25,35,25,255
|
||||||
|
|
||||||
|
# Status colors
|
||||||
|
error=255,120,120,255
|
||||||
|
warning=255,220,120,255
|
||||||
|
success=100,180,120,255
|
||||||
|
info=120,200,180,255
|
||||||
|
|
||||||
|
# Text colors (enhanced for readability)
|
||||||
|
text_primary=250,255,250,255 # Very light for contrast
|
||||||
|
text_secondary=220,240,220,255 # Light green tint
|
||||||
|
text_disabled=150,170,150,255 # Brighter disabled text
|
||||||
|
|
||||||
|
# Window colors (better contrast)
|
||||||
|
window_bg=20,30,20,240
|
||||||
|
child_bg=15,25,15,200
|
||||||
|
popup_bg=25,35,25,250
|
||||||
|
|
||||||
|
# Interactive elements (better visibility)
|
||||||
|
button=70,110,80,255
|
||||||
|
button_hovered=100,150,115,255
|
||||||
|
button_active=130,180,145,255
|
||||||
|
frame_bg=40,60,45,200
|
||||||
|
frame_bg_hovered=55,80,60,220
|
||||||
|
frame_bg_active=70,110,80,240
|
||||||
|
|
||||||
|
# Navigation (better contrast)
|
||||||
|
header=55,85,60,255
|
||||||
|
header_hovered=100,150,115,255
|
||||||
|
header_active=130,180,145,255
|
||||||
|
tab=45,75,50,255
|
||||||
|
tab_hovered=70,110,80,255
|
||||||
|
tab_active=100,150,115,255
|
||||||
|
menu_bar_bg=40,70,45,255
|
||||||
|
title_bg=50,80,55,255
|
||||||
|
title_bg_active=55,85,60,255
|
||||||
|
title_bg_collapsed=45,75,50,255
|
||||||
|
|
||||||
|
# Separators (better visibility)
|
||||||
|
separator=120,160,130,180
|
||||||
|
separator_hovered=150,200,160,220
|
||||||
|
separator_active=180,240,190,255
|
||||||
|
|
||||||
|
# Scrollbars (better visibility)
|
||||||
|
scrollbar_bg=40,60,45,180
|
||||||
|
scrollbar_grab=80,120,90,200
|
||||||
|
scrollbar_grab_hovered=100,150,115,230
|
||||||
|
scrollbar_grab_active=130,180,145,255
|
||||||
|
|
||||||
|
[style]
|
||||||
|
window_rounding=6.0
|
||||||
|
frame_rounding=4.0
|
||||||
|
scrollbar_rounding=6.0
|
||||||
|
grab_rounding=3.0
|
||||||
|
tab_rounding=3.0
|
||||||
|
window_border_size=0.0
|
||||||
|
frame_border_size=0.0
|
||||||
|
enable_animations=true
|
||||||
|
enable_glow_effects=false
|
||||||
|
animation_speed=1.0
|
||||||
73
assets/themes/midnight.theme
Normal file
73
assets/themes/midnight.theme
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
# Midnight Theme
|
||||||
|
# Enhanced midnight theme with better readability
|
||||||
|
name=Midnight
|
||||||
|
description=Deep blue midnight theme with enhanced readability
|
||||||
|
author=YAZE Team
|
||||||
|
version=1.0
|
||||||
|
|
||||||
|
[colors]
|
||||||
|
# Primary colors (enhanced midnight with better contrast)
|
||||||
|
primary=100,160,230,255 # Brighter blue
|
||||||
|
secondary=70,120,180,255 # Mid blue
|
||||||
|
accent=140,200,255,255 # Light blue accent
|
||||||
|
background=10,15,25,255 # Darker background for contrast
|
||||||
|
surface=20,25,35,255
|
||||||
|
|
||||||
|
# Status colors
|
||||||
|
error=255,120,120,255
|
||||||
|
warning=255,220,120,255
|
||||||
|
success=120,255,180,255
|
||||||
|
info=140,200,255,255
|
||||||
|
|
||||||
|
# Text colors (enhanced for readability)
|
||||||
|
text_primary=245,250,255,255 # Very light blue-white
|
||||||
|
text_secondary=200,220,240,255 # Light blue tint
|
||||||
|
text_disabled=140,160,180,255 # Brighter disabled text
|
||||||
|
|
||||||
|
# Window colors (better contrast)
|
||||||
|
window_bg=15,20,30,240
|
||||||
|
child_bg=10,15,25,200
|
||||||
|
popup_bg=20,25,35,250
|
||||||
|
|
||||||
|
# Interactive elements (better visibility)
|
||||||
|
button=50,80,120,255
|
||||||
|
button_hovered=80,120,160,255
|
||||||
|
button_active=110,150,190,255
|
||||||
|
frame_bg=30,45,65,200
|
||||||
|
frame_bg_hovered=40,60,85,220
|
||||||
|
frame_bg_active=60,90,130,240
|
||||||
|
|
||||||
|
# Navigation (better contrast)
|
||||||
|
header=40,65,100,255
|
||||||
|
header_hovered=70,110,150,255
|
||||||
|
header_active=100,140,180,255
|
||||||
|
tab=30,55,90,255
|
||||||
|
tab_hovered=50,80,120,255
|
||||||
|
tab_active=70,110,150,255
|
||||||
|
menu_bar_bg=25,50,85,255
|
||||||
|
title_bg=35,60,95,255
|
||||||
|
title_bg_active=40,65,100,255
|
||||||
|
title_bg_collapsed=30,55,90,255
|
||||||
|
|
||||||
|
# Separators (better visibility)
|
||||||
|
separator=100,140,180,180
|
||||||
|
separator_hovered=130,170,210,220
|
||||||
|
separator_active=160,200,240,255
|
||||||
|
|
||||||
|
# Scrollbars (better visibility)
|
||||||
|
scrollbar_bg=30,45,65,180
|
||||||
|
scrollbar_grab=70,110,150,200
|
||||||
|
scrollbar_grab_hovered=100,140,180,230
|
||||||
|
scrollbar_grab_active=130,170,210,255
|
||||||
|
|
||||||
|
[style]
|
||||||
|
window_rounding=8.0
|
||||||
|
frame_rounding=6.0
|
||||||
|
scrollbar_rounding=8.0
|
||||||
|
grab_rounding=4.0
|
||||||
|
tab_rounding=6.0
|
||||||
|
window_border_size=0.0
|
||||||
|
frame_border_size=0.0
|
||||||
|
enable_animations=true
|
||||||
|
enable_glow_effects=true
|
||||||
|
animation_speed=1.2
|
||||||
62
assets/themes/sunset.theme
Normal file
62
assets/themes/sunset.theme
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
# Sunset Theme
|
||||||
|
# Warm orange and purple sunset theme
|
||||||
|
name=Sunset
|
||||||
|
description=Warm orange and purple sunset theme
|
||||||
|
author=YAZE Team
|
||||||
|
version=1.0
|
||||||
|
|
||||||
|
[colors]
|
||||||
|
# Primary colors (sunset)
|
||||||
|
primary=255,140,60,255
|
||||||
|
secondary=200,100,150,255
|
||||||
|
accent=255,180,100,255
|
||||||
|
background=30,20,35,255
|
||||||
|
surface=40,30,45,255
|
||||||
|
|
||||||
|
# Status colors
|
||||||
|
error=255,100,120,255
|
||||||
|
warning=255,200,100,255
|
||||||
|
success=150,255,150,255
|
||||||
|
info=150,200,255,255
|
||||||
|
|
||||||
|
# Text colors
|
||||||
|
text_primary=255,245,235,255
|
||||||
|
text_secondary=220,200,180,255
|
||||||
|
text_disabled=150,130,120,255
|
||||||
|
|
||||||
|
# Window colors
|
||||||
|
window_bg=35,25,40,240
|
||||||
|
child_bg=30,20,35,200
|
||||||
|
popup_bg=40,30,45,250
|
||||||
|
|
||||||
|
# Interactive elements
|
||||||
|
button=60,40,70,255
|
||||||
|
button_hovered=120,80,100,255
|
||||||
|
button_active=150,100,120,255
|
||||||
|
frame_bg=45,35,50,255
|
||||||
|
frame_bg_hovered=55,45,60,255
|
||||||
|
frame_bg_active=80,60,90,255
|
||||||
|
|
||||||
|
# Navigation
|
||||||
|
header=50,35,60,255
|
||||||
|
header_hovered=100,70,90,255
|
||||||
|
header_active=130,90,110,255
|
||||||
|
tab=40,30,50,255
|
||||||
|
tab_hovered=70,50,70,255
|
||||||
|
tab_active=100,70,90,255
|
||||||
|
menu_bar_bg=35,25,45,255
|
||||||
|
title_bg=40,30,50,255
|
||||||
|
title_bg_active=50,35,60,255
|
||||||
|
title_bg_collapsed=40,30,50,255
|
||||||
|
|
||||||
|
[style]
|
||||||
|
window_rounding=12.0
|
||||||
|
frame_rounding=8.0
|
||||||
|
scrollbar_rounding=12.0
|
||||||
|
grab_rounding=6.0
|
||||||
|
tab_rounding=8.0
|
||||||
|
window_border_size=0.0
|
||||||
|
frame_border_size=0.0
|
||||||
|
enable_animations=true
|
||||||
|
enable_glow_effects=false
|
||||||
|
animation_speed=1.0
|
||||||
92
assets/themes/yaze_tre.theme
Normal file
92
assets/themes/yaze_tre.theme
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
# YAZE Tre Theme
|
||||||
|
# Theme resource edition based on original ColorsYaze() function
|
||||||
|
name=YAZE Tre
|
||||||
|
description=YAZE theme resource edition (file-based)
|
||||||
|
author=YAZE Team
|
||||||
|
version=1.0
|
||||||
|
|
||||||
|
[colors]
|
||||||
|
# Primary colors (exact ALTTP colors from original code)
|
||||||
|
primary=92,115,92,255 # 0.36f, 0.45f, 0.36f - allttpLightGreen
|
||||||
|
secondary=71,92,71,255 # 0.28f, 0.36f, 0.28f - alttpMidGreen
|
||||||
|
accent=89,119,89,255 # 0.347f, 0.466f, 0.347f - TabActive exact
|
||||||
|
background=8,8,8,255 # Very dark gray for better grid visibility
|
||||||
|
surface=12,12,12,255 # Slightly lighter dark gray surface
|
||||||
|
|
||||||
|
# Status colors
|
||||||
|
error=220,50,47,255
|
||||||
|
warning=255,193,7,255
|
||||||
|
success=92,115,92,255
|
||||||
|
info=52,152,219,255
|
||||||
|
|
||||||
|
# Text colors (exact from original)
|
||||||
|
text_primary=230,230,230,255 # 0.90f, 0.90f, 0.90f
|
||||||
|
text_secondary=180,180,180,255
|
||||||
|
text_disabled=153,153,153,255 # 0.60f, 0.60f, 0.60f
|
||||||
|
|
||||||
|
# Window colors (exact from original)
|
||||||
|
window_bg=8,8,8,217 # Very dark gray with same alpha
|
||||||
|
child_bg=0,0,0,0 # 0.00f, 0.00f, 0.00f, 0.00f - transparent
|
||||||
|
popup_bg=28,28,36,235 # 0.11f, 0.11f, 0.14f, 0.92f
|
||||||
|
|
||||||
|
# Interactive elements (exact from original)
|
||||||
|
button=71,92,71,255 # alttpMidGreen
|
||||||
|
button_hovered=125,145,125,255 # allttpLightestGreen (exact)
|
||||||
|
button_active=92,115,92,255 # allttpLightGreen
|
||||||
|
frame_bg=110,110,110,99 # 0.43f, 0.43f, 0.43f, 0.39f
|
||||||
|
frame_bg_hovered=71,92,71,102 # 0.28f, 0.36f, 0.28f, 0.40f
|
||||||
|
frame_bg_active=71,92,71,176 # 0.28f, 0.36f, 0.28f, 0.69f
|
||||||
|
|
||||||
|
# Navigation (exact from original)
|
||||||
|
header=46,66,46,255 # alttpDarkGreen
|
||||||
|
header_hovered=92,115,92,255 # allttpLightGreen
|
||||||
|
header_active=71,92,71,255 # alttpMidGreen
|
||||||
|
tab=46,66,46,255 # alttpDarkGreen
|
||||||
|
tab_hovered=71,92,71,255 # alttpMidGreen
|
||||||
|
tab_active=89,119,89,255 # TabActive exact color
|
||||||
|
menu_bar_bg=46,66,46,255 # alttpDarkGreen
|
||||||
|
title_bg=71,92,71,255 # alttpMidGreen
|
||||||
|
title_bg_active=46,66,46,255 # alttpDarkGreen
|
||||||
|
title_bg_collapsed=71,92,71,255 # alttpMidGreen
|
||||||
|
|
||||||
|
# Borders and separators (exact from original)
|
||||||
|
border=92,115,92,255 # allttpLightGreen
|
||||||
|
border_shadow=0,0,0,0 # No shadow in original
|
||||||
|
separator=127,127,127,153 # 0.50f, 0.50f, 0.50f, 0.60f
|
||||||
|
separator_hovered=153,153,178,255 # 0.60f, 0.60f, 0.70f
|
||||||
|
separator_active=178,178,230,255 # 0.70f, 0.70f, 0.90f
|
||||||
|
|
||||||
|
# Scrollbars (exact from original)
|
||||||
|
scrollbar_bg=92,115,92,153 # 0.36f, 0.45f, 0.36f, 0.60f
|
||||||
|
scrollbar_grab=92,115,92,76 # 0.36f, 0.45f, 0.36f, 0.30f (exact)
|
||||||
|
scrollbar_grab_hovered=92,115,92,102 # 0.36f, 0.45f, 0.36f, 0.40f
|
||||||
|
scrollbar_grab_active=92,115,92,153 # 0.36f, 0.45f, 0.36f, 0.60f
|
||||||
|
|
||||||
|
# Resize grips (exact from original - these are light blue, not white!)
|
||||||
|
resize_grip=255,255,255,26 # 1.00f, 1.00f, 1.00f, 0.10f
|
||||||
|
resize_grip_hovered=199,209,255,153 # 0.78f, 0.82f, 1.00f, 0.60f (light blue!)
|
||||||
|
resize_grip_active=199,209,255,230 # 0.78f, 0.82f, 1.00f, 0.90f (light blue!)
|
||||||
|
|
||||||
|
# Additional controls (missing from theme)
|
||||||
|
check_mark=230,230,230,128 # 0.90f, 0.90f, 0.90f, 0.50f
|
||||||
|
slider_grab=255,255,255,77 # 1.00f, 1.00f, 1.00f, 0.30f
|
||||||
|
slider_grab_active=92,115,92,153 # 0.36f, 0.45f, 0.36f, 0.60f
|
||||||
|
|
||||||
|
# Table colors (from original)
|
||||||
|
table_header_bg=46,66,46,255 # alttpDarkGreen
|
||||||
|
table_border_strong=71,92,71,255 # alttpMidGreen
|
||||||
|
table_border_light=66,66,71,255 # 0.26f, 0.26f, 0.28f
|
||||||
|
table_row_bg=0,0,0,0 # Transparent
|
||||||
|
table_row_bg_alt=255,255,255,18 # 1.00f, 1.00f, 1.00f, 0.07f
|
||||||
|
|
||||||
|
[style]
|
||||||
|
window_rounding=0.0
|
||||||
|
frame_rounding=5.0
|
||||||
|
scrollbar_rounding=5.0
|
||||||
|
grab_rounding=3.0
|
||||||
|
tab_rounding=0.0
|
||||||
|
window_border_size=0.0
|
||||||
|
frame_border_size=0.0
|
||||||
|
enable_animations=true
|
||||||
|
enable_glow_effects=false
|
||||||
|
animation_speed=1.0
|
||||||
@@ -5,6 +5,8 @@
|
|||||||
#include "absl/status/status.h"
|
#include "absl/status/status.h"
|
||||||
#include "app/core/window.h"
|
#include "app/core/window.h"
|
||||||
#include "app/editor/editor_manager.h"
|
#include "app/editor/editor_manager.h"
|
||||||
|
#include "app/gui/background_renderer.h"
|
||||||
|
#include "app/gui/theme_manager.h"
|
||||||
#include "imgui/backends/imgui_impl_sdl2.h"
|
#include "imgui/backends/imgui_impl_sdl2.h"
|
||||||
#include "imgui/backends/imgui_impl_sdlrenderer2.h"
|
#include "imgui/backends/imgui_impl_sdlrenderer2.h"
|
||||||
#include "imgui/imgui.h"
|
#include "imgui/imgui.h"
|
||||||
@@ -54,10 +56,10 @@ absl::Status Controller::OnLoad() {
|
|||||||
ImGui::Begin("DockSpaceWindow", nullptr, window_flags);
|
ImGui::Begin("DockSpaceWindow", nullptr, window_flags);
|
||||||
ImGui::PopStyleVar(3);
|
ImGui::PopStyleVar(3);
|
||||||
|
|
||||||
// Create DockSpace
|
// Create DockSpace first
|
||||||
ImGuiID dockspace_id = ImGui::GetID("MyDockSpace");
|
ImGuiID dockspace_id = ImGui::GetID("MyDockSpace");
|
||||||
ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f),
|
gui::DockSpaceRenderer::BeginEnhancedDockSpace(dockspace_id, ImVec2(0.0f, 0.0f),
|
||||||
ImGuiDockNodeFlags_PassthruCentralNode);
|
ImGuiDockNodeFlags_PassthruCentralNode);
|
||||||
|
|
||||||
editor_manager_.DrawMenuBar(); // Draw the fixed menu bar at the top
|
editor_manager_.DrawMenuBar(); // Draw the fixed menu bar at the top
|
||||||
|
|
||||||
|
|||||||
@@ -78,6 +78,19 @@ void SaveFile(const std::string &filename, const std::string &contents) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string GetResourcePath(const std::string &resource_path) {
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#if TARGET_OS_IOS == 1
|
||||||
|
const std::string kBundlePath = GetBundleResourcePath();
|
||||||
|
return kBundlePath + resource_path;
|
||||||
|
#else
|
||||||
|
return GetBundleResourcePath() + "Contents/Resources/" + resource_path;
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
return resource_path; // On Linux/Windows, resources are relative to executable
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
std::string GetConfigDirectory() {
|
std::string GetConfigDirectory() {
|
||||||
std::string config_directory = ".yaze";
|
std::string config_directory = ".yaze";
|
||||||
Platform platform;
|
Platform platform;
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ std::string GetFileName(const std::string &filename);
|
|||||||
std::string LoadFile(const std::string &filename);
|
std::string LoadFile(const std::string &filename);
|
||||||
std::string LoadConfigFile(const std::string &filename);
|
std::string LoadConfigFile(const std::string &filename);
|
||||||
std::string GetConfigDirectory();
|
std::string GetConfigDirectory();
|
||||||
|
std::string GetResourcePath(const std::string &resource_path);
|
||||||
void SaveFile(const std::string &filename, const std::string &data);
|
void SaveFile(const std::string &filename, const std::string &data);
|
||||||
|
|
||||||
} // namespace core
|
} // namespace core
|
||||||
|
|||||||
@@ -6,7 +6,9 @@
|
|||||||
#include "app/core/platform/sdl_deleter.h"
|
#include "app/core/platform/sdl_deleter.h"
|
||||||
#include "app/gfx/arena.h"
|
#include "app/gfx/arena.h"
|
||||||
#include "app/gui/style.h"
|
#include "app/gui/style.h"
|
||||||
|
#include "app/gui/theme_manager.h"
|
||||||
#include "app/test/test_manager.h"
|
#include "app/test/test_manager.h"
|
||||||
|
#include "util/log.h"
|
||||||
#include "imgui/backends/imgui_impl_sdl2.h"
|
#include "imgui/backends/imgui_impl_sdl2.h"
|
||||||
#include "imgui/backends/imgui_impl_sdlrenderer2.h"
|
#include "imgui/backends/imgui_impl_sdlrenderer2.h"
|
||||||
#include "imgui/imgui.h"
|
#include "imgui/imgui.h"
|
||||||
@@ -49,7 +51,16 @@ absl::Status CreateWindow(Window& window, int flags) {
|
|||||||
|
|
||||||
RETURN_IF_ERROR(LoadPackageFonts());
|
RETURN_IF_ERROR(LoadPackageFonts());
|
||||||
|
|
||||||
|
// Apply original YAZE colors as fallback, then try to load theme system
|
||||||
gui::ColorsYaze();
|
gui::ColorsYaze();
|
||||||
|
|
||||||
|
// Try to initialize theme system (will fallback to ColorsYaze if files fail)
|
||||||
|
auto& theme_manager = gui::ThemeManager::Get();
|
||||||
|
auto status = theme_manager.LoadTheme("YAZE Classic");
|
||||||
|
if (!status.ok()) {
|
||||||
|
// Theme system failed, stick with original ColorsYaze()
|
||||||
|
util::logf("Theme system failed, using original ColorsYaze(): %s", status.message().data());
|
||||||
|
}
|
||||||
|
|
||||||
const int audio_frequency = 48000;
|
const int audio_frequency = 48000;
|
||||||
SDL_AudioSpec want, have;
|
SDL_AudioSpec want, have;
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
#include "app/gui/icons.h"
|
#include "app/gui/icons.h"
|
||||||
#include "app/gui/input.h"
|
#include "app/gui/input.h"
|
||||||
#include "app/gui/style.h"
|
#include "app/gui/style.h"
|
||||||
|
#include "app/gui/theme_manager.h"
|
||||||
|
#include "app/gui/background_renderer.h"
|
||||||
#include "app/rom.h"
|
#include "app/rom.h"
|
||||||
#include "app/zelda3/overworld/overworld_map.h"
|
#include "app/zelda3/overworld/overworld_map.h"
|
||||||
#include "app/test/test_manager.h"
|
#include "app/test/test_manager.h"
|
||||||
@@ -704,6 +706,21 @@ absl::Status EditorManager::Update() {
|
|||||||
ExecuteShortcuts(context_.shortcut_manager);
|
ExecuteShortcuts(context_.shortcut_manager);
|
||||||
toast_manager_.Draw();
|
toast_manager_.Draw();
|
||||||
|
|
||||||
|
// Draw background grid effects for the entire viewport
|
||||||
|
if (ImGui::GetCurrentContext()) {
|
||||||
|
ImDrawList* bg_draw_list = ImGui::GetBackgroundDrawList();
|
||||||
|
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||||
|
|
||||||
|
auto& theme_manager = gui::ThemeManager::Get();
|
||||||
|
auto current_theme = theme_manager.GetCurrentTheme();
|
||||||
|
auto& bg_renderer = gui::BackgroundRenderer::Get();
|
||||||
|
|
||||||
|
// Draw grid covering the entire main viewport
|
||||||
|
ImVec2 grid_pos = viewport->WorkPos;
|
||||||
|
ImVec2 grid_size = viewport->WorkSize;
|
||||||
|
bg_renderer.RenderDockingBackground(bg_draw_list, grid_pos, grid_size, current_theme.primary);
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure TestManager always has the current ROM
|
// Ensure TestManager always has the current ROM
|
||||||
static Rom* last_test_rom = nullptr;
|
static Rom* last_test_rom = nullptr;
|
||||||
if (last_test_rom != current_rom_) {
|
if (last_test_rom != current_rom_) {
|
||||||
@@ -938,9 +955,6 @@ void EditorManager::DrawMenuBar() {
|
|||||||
// Enhanced ROM status with metadata popup
|
// Enhanced ROM status with metadata popup
|
||||||
if (current_rom_ && current_rom_->is_loaded()) {
|
if (current_rom_ && current_rom_->is_loaded()) {
|
||||||
std::string rom_display = current_rom_->title();
|
std::string rom_display = current_rom_->title();
|
||||||
if (rom_display.length() > 16) {
|
|
||||||
rom_display = rom_display.substr(0, 16) + "..";
|
|
||||||
}
|
|
||||||
|
|
||||||
ImVec4 status_color = current_rom_->dirty() ?
|
ImVec4 status_color = current_rom_->dirty() ?
|
||||||
ImVec4(1.0f, 0.5f, 0.0f, 1.0f) : // Orange for modified
|
ImVec4(1.0f, 0.5f, 0.0f, 1.0f) : // Orange for modified
|
||||||
@@ -2155,23 +2169,30 @@ void EditorManager::DrawWelcomeScreen() {
|
|||||||
ImVec2 window_pos = ImGui::GetWindowPos();
|
ImVec2 window_pos = ImGui::GetWindowPos();
|
||||||
ImVec2 window_size = ImGui::GetWindowSize();
|
ImVec2 window_size = ImGui::GetWindowSize();
|
||||||
|
|
||||||
// Draw animated gradient background
|
// Get theme colors for welcome screen
|
||||||
ImU32 bg_color_top = IM_COL32(25, 35, 50, 240);
|
auto& theme_manager = gui::ThemeManager::Get();
|
||||||
ImU32 bg_color_bottom = IM_COL32(15, 25, 40, 240);
|
auto bg_color = theme_manager.GetWelcomeScreenBackground();
|
||||||
|
auto border_color = theme_manager.GetWelcomeScreenBorder();
|
||||||
|
auto accent_color = theme_manager.GetWelcomeScreenAccent();
|
||||||
|
|
||||||
|
// Draw themed gradient background
|
||||||
|
ImU32 bg_top = ImGui::ColorConvertFloat4ToU32(ImVec4(bg_color.red, bg_color.green, bg_color.blue, bg_color.alpha));
|
||||||
|
ImU32 bg_bottom = ImGui::ColorConvertFloat4ToU32(ImVec4(bg_color.red * 0.8f, bg_color.green * 0.8f, bg_color.blue * 0.8f, bg_color.alpha));
|
||||||
draw_list->AddRectFilledMultiColor(
|
draw_list->AddRectFilledMultiColor(
|
||||||
window_pos,
|
window_pos,
|
||||||
ImVec2(window_pos.x + window_size.x, window_pos.y + window_size.y),
|
ImVec2(window_pos.x + window_size.x, window_pos.y + window_size.y),
|
||||||
bg_color_top, bg_color_top, bg_color_bottom, bg_color_bottom);
|
bg_top, bg_top, bg_bottom, bg_bottom);
|
||||||
|
|
||||||
// Animated border with gradient effect
|
// Themed animated border
|
||||||
float border_thickness = 3.0f;
|
float border_thickness = 3.0f;
|
||||||
float pulse = 0.8f + 0.2f * sinf(animation_time * 2.0f);
|
float pulse = 0.8f + 0.2f * sinf(animation_time * 2.0f);
|
||||||
ImU32 border_color = IM_COL32(70, 130, 200, (int)(255 * pulse));
|
ImU32 themed_border = ImGui::ColorConvertFloat4ToU32(ImVec4(
|
||||||
|
border_color.red, border_color.green, border_color.blue, pulse * border_color.alpha));
|
||||||
draw_list->AddRect(window_pos,
|
draw_list->AddRect(window_pos,
|
||||||
ImVec2(window_pos.x + window_size.x, window_pos.y + window_size.y),
|
ImVec2(window_pos.x + window_size.x, window_pos.y + window_size.y),
|
||||||
border_color, 12.0f, 0, border_thickness);
|
themed_border, 12.0f, 0, border_thickness);
|
||||||
|
|
||||||
// Subtle floating particles effect
|
// Themed floating particles effect
|
||||||
for (int i = 0; i < 8; ++i) {
|
for (int i = 0; i < 8; ++i) {
|
||||||
float offset_x = sinf(animation_time * 0.5f + i * 0.8f) * 20.0f;
|
float offset_x = sinf(animation_time * 0.5f + i * 0.8f) * 20.0f;
|
||||||
float offset_y = cosf(animation_time * 0.3f + i * 1.2f) * 15.0f;
|
float offset_y = cosf(animation_time * 0.3f + i * 1.2f) * 15.0f;
|
||||||
@@ -2180,29 +2201,34 @@ void EditorManager::DrawWelcomeScreen() {
|
|||||||
window_pos.y + 100 + offset_y);
|
window_pos.y + 100 + offset_y);
|
||||||
|
|
||||||
float alpha = 0.3f + 0.2f * sinf(animation_time * 1.5f + i);
|
float alpha = 0.3f + 0.2f * sinf(animation_time * 1.5f + i);
|
||||||
ImU32 particle_color = IM_COL32(100, 150, 255, (int)(alpha * 100));
|
ImU32 particle_color = ImGui::ColorConvertFloat4ToU32(ImVec4(
|
||||||
|
accent_color.red, accent_color.green, accent_color.blue, alpha * 0.4f));
|
||||||
draw_list->AddCircleFilled(particle_pos, 2.0f + sinf(animation_time + i) * 0.5f, particle_color);
|
draw_list->AddCircleFilled(particle_pos, 2.0f + sinf(animation_time + i) * 0.5f, particle_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header with enhanced styling
|
// Header with themed styling
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::SetCursorPosX((window_size.x - ImGui::CalcTextSize("Welcome to Yet Another Zelda3 Editor").x) * 0.5f);
|
ImGui::SetCursorPosX((window_size.x - ImGui::CalcTextSize("Welcome to Yet Another Zelda3 Editor").x) * 0.5f);
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.9f, 0.95f, 1.0f, 1.0f));
|
auto text_color = theme_manager.GetCurrentTheme().text_primary;
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(text_color.red, text_color.green, text_color.blue, text_color.alpha));
|
||||||
ImGui::Text("Welcome to Yet Another Zelda3 Editor");
|
ImGui::Text("Welcome to Yet Another Zelda3 Editor");
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
|
|
||||||
ImGui::SetCursorPosX((window_size.x - ImGui::CalcTextSize("The Legend of Zelda: A Link to the Past").x) * 0.5f);
|
ImGui::SetCursorPosX((window_size.x - ImGui::CalcTextSize("The Legend of Zelda: A Link to the Past").x) * 0.5f);
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.7f, 0.8f, 0.9f, 0.9f));
|
auto subtitle_color = theme_manager.GetCurrentTheme().text_secondary;
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(subtitle_color.red, subtitle_color.green, subtitle_color.blue, subtitle_color.alpha * 0.9f));
|
||||||
ImGui::Text("The Legend of Zelda: A Link to the Past");
|
ImGui::Text("The Legend of Zelda: A Link to the Past");
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
// Decorative line with glow effect
|
// Themed decorative line with glow effect
|
||||||
ImVec2 line_start = ImVec2(window_pos.x + 50, window_pos.y + 120);
|
ImVec2 line_start = ImVec2(window_pos.x + 50, window_pos.y + 120);
|
||||||
ImVec2 line_end = ImVec2(window_pos.x + window_size.x - 50, window_pos.y + 120);
|
ImVec2 line_end = ImVec2(window_pos.x + window_size.x - 50, window_pos.y + 120);
|
||||||
float glow_alpha = 0.5f + 0.3f * sinf(animation_time * 1.5f);
|
float glow_alpha = 0.5f + 0.3f * sinf(animation_time * 1.5f);
|
||||||
draw_list->AddLine(line_start, line_end, IM_COL32(100, 150, 255, (int)(glow_alpha * 255)), 2.0f);
|
ImU32 line_color = ImGui::ColorConvertFloat4ToU32(ImVec4(
|
||||||
|
accent_color.red, accent_color.green, accent_color.blue, glow_alpha));
|
||||||
|
draw_list->AddLine(line_start, line_end, line_color, 2.0f);
|
||||||
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
@@ -2226,10 +2252,11 @@ void EditorManager::DrawWelcomeScreen() {
|
|||||||
ImGui::Text("Get Started:");
|
ImGui::Text("Get Started:");
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
// Stylish primary buttons with hover effects
|
// Themed primary buttons with enhanced effects
|
||||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.5f, 0.9f, 0.8f));
|
auto current_theme = theme_manager.GetCurrentTheme();
|
||||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.3f, 0.6f, 1.0f, 0.9f));
|
ImGui::PushStyleColor(ImGuiCol_Button, ConvertColorToImVec4(current_theme.primary));
|
||||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.1f, 0.4f, 0.8f, 1.0f));
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ConvertColorToImVec4(current_theme.accent));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ConvertColorToImVec4(current_theme.secondary));
|
||||||
|
|
||||||
if (ImGui::Button(ICON_MD_FILE_OPEN " Open ROM File", ImVec2(200, 40))) {
|
if (ImGui::Button(ICON_MD_FILE_OPEN " Open ROM File", ImVec2(200, 40))) {
|
||||||
status_ = LoadRom();
|
status_ = LoadRom();
|
||||||
|
|||||||
380
src/app/gui/background_renderer.cc
Normal file
380
src/app/gui/background_renderer.cc
Normal file
@@ -0,0 +1,380 @@
|
|||||||
|
#include "background_renderer.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#include "app/gui/theme_manager.h"
|
||||||
|
#include "imgui/imgui.h"
|
||||||
|
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI 3.14159265358979323846
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace gui {
|
||||||
|
|
||||||
|
// BackgroundRenderer Implementation
|
||||||
|
BackgroundRenderer& BackgroundRenderer::Get() {
|
||||||
|
static BackgroundRenderer instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackgroundRenderer::RenderDockingBackground(ImDrawList* draw_list, const ImVec2& window_pos,
|
||||||
|
const ImVec2& window_size, const Color& theme_color) {
|
||||||
|
if (!draw_list) return;
|
||||||
|
|
||||||
|
UpdateAnimation(ImGui::GetIO().DeltaTime);
|
||||||
|
|
||||||
|
// Get current theme colors
|
||||||
|
auto& theme_manager = ThemeManager::Get();
|
||||||
|
auto current_theme = theme_manager.GetCurrentTheme();
|
||||||
|
|
||||||
|
// Create a subtle tinted background
|
||||||
|
Color bg_tint = {
|
||||||
|
current_theme.background.red * 1.1f,
|
||||||
|
current_theme.background.green * 1.1f,
|
||||||
|
current_theme.background.blue * 1.1f,
|
||||||
|
0.3f
|
||||||
|
};
|
||||||
|
|
||||||
|
ImU32 bg_color = ImGui::ColorConvertFloat4ToU32(ConvertColorToImVec4(bg_tint));
|
||||||
|
draw_list->AddRectFilled(window_pos,
|
||||||
|
ImVec2(window_pos.x + window_size.x, window_pos.y + window_size.y),
|
||||||
|
bg_color);
|
||||||
|
|
||||||
|
// Render the grid if enabled
|
||||||
|
if (grid_settings_.grid_size > 0) {
|
||||||
|
RenderGridBackground(draw_list, window_pos, window_size, theme_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add subtle corner accents
|
||||||
|
if (current_theme.enable_glow_effects) {
|
||||||
|
float corner_size = 60.0f;
|
||||||
|
Color accent_faded = current_theme.accent;
|
||||||
|
accent_faded.alpha = 0.1f + 0.05f * sinf(animation_time_ * 2.0f);
|
||||||
|
|
||||||
|
ImU32 corner_color = ImGui::ColorConvertFloat4ToU32(ConvertColorToImVec4(accent_faded));
|
||||||
|
|
||||||
|
// Top-left corner
|
||||||
|
draw_list->AddRectFilledMultiColor(
|
||||||
|
window_pos,
|
||||||
|
ImVec2(window_pos.x + corner_size, window_pos.y + corner_size),
|
||||||
|
corner_color, IM_COL32(0,0,0,0), IM_COL32(0,0,0,0), corner_color);
|
||||||
|
|
||||||
|
// Bottom-right corner
|
||||||
|
draw_list->AddRectFilledMultiColor(
|
||||||
|
ImVec2(window_pos.x + window_size.x - corner_size, window_pos.y + window_size.y - corner_size),
|
||||||
|
ImVec2(window_pos.x + window_size.x, window_pos.y + window_size.y),
|
||||||
|
IM_COL32(0,0,0,0), corner_color, corner_color, IM_COL32(0,0,0,0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackgroundRenderer::RenderGridBackground(ImDrawList* draw_list, const ImVec2& window_pos,
|
||||||
|
const ImVec2& window_size, const Color& grid_color) {
|
||||||
|
if (!draw_list || grid_settings_.grid_size <= 0) return;
|
||||||
|
|
||||||
|
// Grid parameters with optional animation
|
||||||
|
float grid_size = grid_settings_.grid_size;
|
||||||
|
float offset_x = 0.0f;
|
||||||
|
float offset_y = 0.0f;
|
||||||
|
|
||||||
|
// Apply animation if enabled
|
||||||
|
if (grid_settings_.enable_animation) {
|
||||||
|
float animation_offset = animation_time_ * grid_settings_.animation_speed * 10.0f;
|
||||||
|
offset_x = fmodf(animation_offset, grid_size);
|
||||||
|
offset_y = fmodf(animation_offset * 0.7f, grid_size); // Different speed for interesting effect
|
||||||
|
}
|
||||||
|
|
||||||
|
// Window center for radial calculations
|
||||||
|
ImVec2 center = ImVec2(window_pos.x + window_size.x * 0.5f,
|
||||||
|
window_pos.y + window_size.y * 0.5f);
|
||||||
|
float max_distance = sqrtf(window_size.x * window_size.x + window_size.y * window_size.y) * 0.5f;
|
||||||
|
|
||||||
|
// Apply breathing effect to color if enabled
|
||||||
|
Color themed_grid_color = grid_color;
|
||||||
|
themed_grid_color.alpha = grid_settings_.opacity;
|
||||||
|
|
||||||
|
if (grid_settings_.enable_breathing) {
|
||||||
|
float breathing_factor = 1.0f + grid_settings_.breathing_intensity *
|
||||||
|
sinf(animation_time_ * grid_settings_.breathing_speed);
|
||||||
|
themed_grid_color.red = std::min(1.0f, themed_grid_color.red * breathing_factor);
|
||||||
|
themed_grid_color.green = std::min(1.0f, themed_grid_color.green * breathing_factor);
|
||||||
|
themed_grid_color.blue = std::min(1.0f, themed_grid_color.blue * breathing_factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (grid_settings_.enable_dots) {
|
||||||
|
// Render grid as dots
|
||||||
|
for (float x = window_pos.x - offset_x; x < window_pos.x + window_size.x + grid_size; x += grid_size) {
|
||||||
|
for (float y = window_pos.y - offset_y; y < window_pos.y + window_size.y + grid_size; y += grid_size) {
|
||||||
|
ImVec2 dot_pos(x, y);
|
||||||
|
|
||||||
|
// Calculate radial fade
|
||||||
|
float fade_factor = 1.0f;
|
||||||
|
if (grid_settings_.radial_fade) {
|
||||||
|
float distance = sqrtf((dot_pos.x - center.x) * (dot_pos.x - center.x) +
|
||||||
|
(dot_pos.y - center.y) * (dot_pos.y - center.y));
|
||||||
|
fade_factor = 1.0f - std::min(distance / grid_settings_.fade_distance, 1.0f);
|
||||||
|
fade_factor = fade_factor * fade_factor; // Square for smoother falloff
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fade_factor > 0.01f) {
|
||||||
|
ImU32 dot_color = BlendColorWithFade(themed_grid_color, fade_factor);
|
||||||
|
DrawGridDot(draw_list, dot_pos, dot_color, grid_settings_.dot_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Render grid as lines
|
||||||
|
// Vertical lines
|
||||||
|
for (float x = window_pos.x - offset_x; x < window_pos.x + window_size.x + grid_size; x += grid_size) {
|
||||||
|
ImVec2 line_start(x, window_pos.y);
|
||||||
|
ImVec2 line_end(x, window_pos.y + window_size.y);
|
||||||
|
|
||||||
|
// Calculate average fade for this line
|
||||||
|
float avg_fade = 0.0f;
|
||||||
|
if (grid_settings_.radial_fade) {
|
||||||
|
for (float y = window_pos.y; y < window_pos.y + window_size.y; y += grid_size * 0.5f) {
|
||||||
|
float distance = sqrtf((x - center.x) * (x - center.x) + (y - center.y) * (y - center.y));
|
||||||
|
float fade = 1.0f - std::min(distance / grid_settings_.fade_distance, 1.0f);
|
||||||
|
avg_fade += fade * fade;
|
||||||
|
}
|
||||||
|
avg_fade /= (window_size.y / (grid_size * 0.5f));
|
||||||
|
} else {
|
||||||
|
avg_fade = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (avg_fade > 0.01f) {
|
||||||
|
ImU32 line_color = BlendColorWithFade(themed_grid_color, avg_fade);
|
||||||
|
DrawGridLine(draw_list, line_start, line_end, line_color, grid_settings_.line_thickness);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Horizontal lines
|
||||||
|
for (float y = window_pos.y - offset_y; y < window_pos.y + window_size.y + grid_size; y += grid_size) {
|
||||||
|
ImVec2 line_start(window_pos.x, y);
|
||||||
|
ImVec2 line_end(window_pos.x + window_size.x, y);
|
||||||
|
|
||||||
|
// Calculate average fade for this line
|
||||||
|
float avg_fade = 0.0f;
|
||||||
|
if (grid_settings_.radial_fade) {
|
||||||
|
for (float x = window_pos.x; x < window_pos.x + window_size.x; x += grid_size * 0.5f) {
|
||||||
|
float distance = sqrtf((x - center.x) * (x - center.x) + (y - center.y) * (y - center.y));
|
||||||
|
float fade = 1.0f - std::min(distance / grid_settings_.fade_distance, 1.0f);
|
||||||
|
avg_fade += fade * fade;
|
||||||
|
}
|
||||||
|
avg_fade /= (window_size.x / (grid_size * 0.5f));
|
||||||
|
} else {
|
||||||
|
avg_fade = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (avg_fade > 0.01f) {
|
||||||
|
ImU32 line_color = BlendColorWithFade(themed_grid_color, avg_fade);
|
||||||
|
DrawGridLine(draw_list, line_start, line_end, line_color, grid_settings_.line_thickness);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackgroundRenderer::RenderRadialGradient(ImDrawList* draw_list, const ImVec2& center,
|
||||||
|
float radius, const Color& inner_color, const Color& outer_color) {
|
||||||
|
if (!draw_list) return;
|
||||||
|
|
||||||
|
const int segments = 32;
|
||||||
|
const int rings = 8;
|
||||||
|
|
||||||
|
for (int ring = 0; ring < rings; ++ring) {
|
||||||
|
float ring_radius = radius * (ring + 1) / rings;
|
||||||
|
float inner_ring_radius = radius * ring / rings;
|
||||||
|
|
||||||
|
// Interpolate colors for this ring
|
||||||
|
float t = static_cast<float>(ring) / rings;
|
||||||
|
Color ring_color = {
|
||||||
|
inner_color.red * (1.0f - t) + outer_color.red * t,
|
||||||
|
inner_color.green * (1.0f - t) + outer_color.green * t,
|
||||||
|
inner_color.blue * (1.0f - t) + outer_color.blue * t,
|
||||||
|
inner_color.alpha * (1.0f - t) + outer_color.alpha * t
|
||||||
|
};
|
||||||
|
|
||||||
|
ImU32 color = ImGui::ColorConvertFloat4ToU32(ConvertColorToImVec4(ring_color));
|
||||||
|
|
||||||
|
if (ring == 0) {
|
||||||
|
// Center circle
|
||||||
|
draw_list->AddCircleFilled(center, ring_radius, color, segments);
|
||||||
|
} else {
|
||||||
|
// Ring
|
||||||
|
for (int i = 0; i < segments; ++i) {
|
||||||
|
float angle1 = (2.0f * M_PI * i) / segments;
|
||||||
|
float angle2 = (2.0f * M_PI * (i + 1)) / segments;
|
||||||
|
|
||||||
|
ImVec2 p1_inner = ImVec2(center.x + cosf(angle1) * inner_ring_radius,
|
||||||
|
center.y + sinf(angle1) * inner_ring_radius);
|
||||||
|
ImVec2 p2_inner = ImVec2(center.x + cosf(angle2) * inner_ring_radius,
|
||||||
|
center.y + sinf(angle2) * inner_ring_radius);
|
||||||
|
ImVec2 p1_outer = ImVec2(center.x + cosf(angle1) * ring_radius,
|
||||||
|
center.y + sinf(angle1) * ring_radius);
|
||||||
|
ImVec2 p2_outer = ImVec2(center.x + cosf(angle2) * ring_radius,
|
||||||
|
center.y + sinf(angle2) * ring_radius);
|
||||||
|
|
||||||
|
draw_list->AddQuadFilled(p1_inner, p2_inner, p2_outer, p1_outer, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackgroundRenderer::UpdateAnimation(float delta_time) {
|
||||||
|
if (grid_settings_.enable_animation) {
|
||||||
|
animation_time_ += delta_time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackgroundRenderer::UpdateForTheme(const Color& primary_color, const Color& background_color) {
|
||||||
|
// Create a grid color that's a subtle blend of the theme's primary and background
|
||||||
|
cached_grid_color_ = {
|
||||||
|
(primary_color.red * 0.3f + background_color.red * 0.7f),
|
||||||
|
(primary_color.green * 0.3f + background_color.green * 0.7f),
|
||||||
|
(primary_color.blue * 0.3f + background_color.blue * 0.7f),
|
||||||
|
grid_settings_.opacity
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackgroundRenderer::DrawSettingsUI() {
|
||||||
|
if (ImGui::CollapsingHeader("Background Grid Settings")) {
|
||||||
|
ImGui::Indent();
|
||||||
|
|
||||||
|
ImGui::SliderFloat("Grid Size", &grid_settings_.grid_size, 8.0f, 128.0f, "%.0f px");
|
||||||
|
ImGui::SliderFloat("Line Thickness", &grid_settings_.line_thickness, 0.5f, 3.0f, "%.1f px");
|
||||||
|
ImGui::SliderFloat("Opacity", &grid_settings_.opacity, 0.01f, 0.3f, "%.3f");
|
||||||
|
ImGui::SliderFloat("Fade Distance", &grid_settings_.fade_distance, 50.0f, 500.0f, "%.0f px");
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Visual Effects:");
|
||||||
|
ImGui::Checkbox("Enable Animation", &grid_settings_.enable_animation);
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Makes the grid move slowly across the screen");
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Checkbox("Color Breathing", &grid_settings_.enable_breathing);
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Grid color pulses with a breathing effect");
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Checkbox("Radial Fade", &grid_settings_.radial_fade);
|
||||||
|
ImGui::Checkbox("Use Dots Instead of Lines", &grid_settings_.enable_dots);
|
||||||
|
|
||||||
|
// Animation settings (only show if animation is enabled)
|
||||||
|
if (grid_settings_.enable_animation) {
|
||||||
|
ImGui::Indent();
|
||||||
|
ImGui::SliderFloat("Animation Speed", &grid_settings_.animation_speed, 0.1f, 3.0f, "%.1fx");
|
||||||
|
ImGui::Unindent();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Breathing settings (only show if breathing is enabled)
|
||||||
|
if (grid_settings_.enable_breathing) {
|
||||||
|
ImGui::Indent();
|
||||||
|
ImGui::SliderFloat("Breathing Speed", &grid_settings_.breathing_speed, 0.5f, 3.0f, "%.1fx");
|
||||||
|
ImGui::SliderFloat("Breathing Intensity", &grid_settings_.breathing_intensity, 0.1f, 0.8f, "%.1f");
|
||||||
|
ImGui::Unindent();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (grid_settings_.enable_dots) {
|
||||||
|
ImGui::SliderFloat("Dot Size", &grid_settings_.dot_size, 1.0f, 8.0f, "%.1f px");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preview
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::Text("Preview:");
|
||||||
|
ImVec2 preview_size(200, 100);
|
||||||
|
ImVec2 preview_pos = ImGui::GetCursorScreenPos();
|
||||||
|
|
||||||
|
ImDrawList* preview_draw_list = ImGui::GetWindowDrawList();
|
||||||
|
auto& theme_manager = ThemeManager::Get();
|
||||||
|
auto theme_color = theme_manager.GetCurrentTheme().primary;
|
||||||
|
|
||||||
|
// Draw preview background
|
||||||
|
preview_draw_list->AddRectFilled(preview_pos,
|
||||||
|
ImVec2(preview_pos.x + preview_size.x, preview_pos.y + preview_size.y),
|
||||||
|
IM_COL32(30, 30, 30, 255));
|
||||||
|
|
||||||
|
// Draw preview grid
|
||||||
|
RenderGridBackground(preview_draw_list, preview_pos, preview_size, theme_color);
|
||||||
|
|
||||||
|
// Advance cursor
|
||||||
|
ImGui::Dummy(preview_size);
|
||||||
|
|
||||||
|
ImGui::Unindent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float BackgroundRenderer::CalculateRadialFade(const ImVec2& pos, const ImVec2& center, float max_distance) const {
|
||||||
|
float distance = sqrtf((pos.x - center.x) * (pos.x - center.x) +
|
||||||
|
(pos.y - center.y) * (pos.y - center.y));
|
||||||
|
float fade = 1.0f - std::min(distance / max_distance, 1.0f);
|
||||||
|
return fade * fade; // Square for smoother falloff
|
||||||
|
}
|
||||||
|
|
||||||
|
ImU32 BackgroundRenderer::BlendColorWithFade(const Color& base_color, float fade_factor) const {
|
||||||
|
Color faded_color = {
|
||||||
|
base_color.red,
|
||||||
|
base_color.green,
|
||||||
|
base_color.blue,
|
||||||
|
base_color.alpha * fade_factor
|
||||||
|
};
|
||||||
|
return ImGui::ColorConvertFloat4ToU32(ConvertColorToImVec4(faded_color));
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackgroundRenderer::DrawGridLine(ImDrawList* draw_list, const ImVec2& start, const ImVec2& end,
|
||||||
|
ImU32 color, float thickness) const {
|
||||||
|
draw_list->AddLine(start, end, color, thickness);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackgroundRenderer::DrawGridDot(ImDrawList* draw_list, const ImVec2& pos, ImU32 color, float size) const {
|
||||||
|
draw_list->AddCircleFilled(pos, size, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DockSpaceRenderer Implementation
|
||||||
|
bool DockSpaceRenderer::background_enabled_ = true;
|
||||||
|
bool DockSpaceRenderer::grid_enabled_ = true;
|
||||||
|
bool DockSpaceRenderer::effects_enabled_ = true;
|
||||||
|
ImVec2 DockSpaceRenderer::last_dockspace_pos_{};
|
||||||
|
ImVec2 DockSpaceRenderer::last_dockspace_size_{};
|
||||||
|
|
||||||
|
void DockSpaceRenderer::BeginEnhancedDockSpace(ImGuiID dockspace_id, const ImVec2& size,
|
||||||
|
ImGuiDockNodeFlags flags) {
|
||||||
|
// Store window info
|
||||||
|
last_dockspace_pos_ = ImGui::GetWindowPos();
|
||||||
|
last_dockspace_size_ = ImGui::GetWindowSize();
|
||||||
|
|
||||||
|
// Create the actual dockspace first
|
||||||
|
ImGui::DockSpace(dockspace_id, size, flags);
|
||||||
|
|
||||||
|
// NOW draw the background effects on the foreground draw list so they're visible
|
||||||
|
if (background_enabled_) {
|
||||||
|
ImDrawList* fg_draw_list = ImGui::GetForegroundDrawList();
|
||||||
|
auto& theme_manager = ThemeManager::Get();
|
||||||
|
auto current_theme = theme_manager.GetCurrentTheme();
|
||||||
|
|
||||||
|
if (grid_enabled_) {
|
||||||
|
auto& bg_renderer = BackgroundRenderer::Get();
|
||||||
|
// Use the main viewport for full-screen grid
|
||||||
|
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||||
|
ImVec2 grid_pos = viewport->WorkPos;
|
||||||
|
ImVec2 grid_size = viewport->WorkSize;
|
||||||
|
|
||||||
|
// Use subtle grid color that doesn't distract
|
||||||
|
Color subtle_grid_color = current_theme.primary;
|
||||||
|
// Use the grid settings opacity for consistency
|
||||||
|
subtle_grid_color.alpha = bg_renderer.GetGridSettings().opacity;
|
||||||
|
|
||||||
|
bg_renderer.RenderGridBackground(fg_draw_list, grid_pos, grid_size, subtle_grid_color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockSpaceRenderer::EndEnhancedDockSpace() {
|
||||||
|
// Additional post-processing effects could go here
|
||||||
|
// For now, this is just for API consistency
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace gui
|
||||||
|
} // namespace yaze
|
||||||
96
src/app/gui/background_renderer.h
Normal file
96
src/app/gui/background_renderer.h
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#ifndef YAZE_APP_GUI_BACKGROUND_RENDERER_H
|
||||||
|
#define YAZE_APP_GUI_BACKGROUND_RENDERER_H
|
||||||
|
|
||||||
|
#include "imgui/imgui.h"
|
||||||
|
#include "app/gui/color.h"
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace gui {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class BackgroundRenderer
|
||||||
|
* @brief Renders themed background effects for docking windows
|
||||||
|
*/
|
||||||
|
class BackgroundRenderer {
|
||||||
|
public:
|
||||||
|
struct GridSettings {
|
||||||
|
float grid_size = 32.0f; // Size of grid cells
|
||||||
|
float line_thickness = 1.0f; // Thickness of grid lines
|
||||||
|
float opacity = 0.12f; // Subtle but visible opacity
|
||||||
|
float fade_distance = 400.0f; // Distance over which grid fades
|
||||||
|
bool enable_animation = false; // Animation toggle (default off)
|
||||||
|
bool enable_breathing = false; // Color breathing effect toggle (default off)
|
||||||
|
bool radial_fade = true; // Re-enable subtle radial fade
|
||||||
|
bool enable_dots = false; // Use dots instead of lines
|
||||||
|
float dot_size = 2.0f; // Size of grid dots
|
||||||
|
float animation_speed = 1.0f; // Animation speed multiplier
|
||||||
|
float breathing_speed = 1.5f; // Breathing effect speed
|
||||||
|
float breathing_intensity = 0.3f; // How much color changes during breathing
|
||||||
|
};
|
||||||
|
|
||||||
|
static BackgroundRenderer& Get();
|
||||||
|
|
||||||
|
// Main rendering functions
|
||||||
|
void RenderDockingBackground(ImDrawList* draw_list, const ImVec2& window_pos,
|
||||||
|
const ImVec2& window_size, const Color& theme_color);
|
||||||
|
void RenderGridBackground(ImDrawList* draw_list, const ImVec2& window_pos,
|
||||||
|
const ImVec2& window_size, const Color& grid_color);
|
||||||
|
void RenderRadialGradient(ImDrawList* draw_list, const ImVec2& center,
|
||||||
|
float radius, const Color& inner_color, const Color& outer_color);
|
||||||
|
|
||||||
|
// Configuration
|
||||||
|
void SetGridSettings(const GridSettings& settings) { grid_settings_ = settings; }
|
||||||
|
const GridSettings& GetGridSettings() const { return grid_settings_; }
|
||||||
|
|
||||||
|
// Animation
|
||||||
|
void UpdateAnimation(float delta_time);
|
||||||
|
void SetAnimationEnabled(bool enabled) { grid_settings_.enable_animation = enabled; }
|
||||||
|
|
||||||
|
// Theme integration
|
||||||
|
void UpdateForTheme(const Color& primary_color, const Color& background_color);
|
||||||
|
|
||||||
|
// UI for settings
|
||||||
|
void DrawSettingsUI();
|
||||||
|
|
||||||
|
private:
|
||||||
|
BackgroundRenderer() = default;
|
||||||
|
|
||||||
|
GridSettings grid_settings_;
|
||||||
|
float animation_time_ = 0.0f;
|
||||||
|
Color cached_grid_color_{0.5f, 0.5f, 0.5f, 0.1f};
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
float CalculateRadialFade(const ImVec2& pos, const ImVec2& center, float max_distance) const;
|
||||||
|
ImU32 BlendColorWithFade(const Color& base_color, float fade_factor) const;
|
||||||
|
void DrawGridLine(ImDrawList* draw_list, const ImVec2& start, const ImVec2& end,
|
||||||
|
ImU32 color, float thickness) const;
|
||||||
|
void DrawGridDot(ImDrawList* draw_list, const ImVec2& pos, ImU32 color, float size) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class DockSpaceRenderer
|
||||||
|
* @brief Enhanced docking space with themed background effects
|
||||||
|
*/
|
||||||
|
class DockSpaceRenderer {
|
||||||
|
public:
|
||||||
|
static void BeginEnhancedDockSpace(ImGuiID dockspace_id, const ImVec2& size = ImVec2(0, 0),
|
||||||
|
ImGuiDockNodeFlags flags = 0);
|
||||||
|
static void EndEnhancedDockSpace();
|
||||||
|
|
||||||
|
// Configuration
|
||||||
|
static void SetBackgroundEnabled(bool enabled) { background_enabled_ = enabled; }
|
||||||
|
static void SetGridEnabled(bool enabled) { grid_enabled_ = enabled; }
|
||||||
|
static void SetEffectsEnabled(bool enabled) { effects_enabled_ = enabled; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool background_enabled_;
|
||||||
|
static bool grid_enabled_;
|
||||||
|
static bool effects_enabled_;
|
||||||
|
static ImVec2 last_dockspace_pos_;
|
||||||
|
static ImVec2 last_dockspace_size_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace gui
|
||||||
|
} // namespace yaze
|
||||||
|
|
||||||
|
#endif // YAZE_APP_GUI_BACKGROUND_RENDERER_H
|
||||||
@@ -7,4 +7,6 @@ set(
|
|||||||
app/gui/style.cc
|
app/gui/style.cc
|
||||||
app/gui/color.cc
|
app/gui/color.cc
|
||||||
app/gui/zeml.cc
|
app/gui/zeml.cc
|
||||||
|
app/gui/theme_manager.cc
|
||||||
|
app/gui/background_renderer.cc
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#include "style.h"
|
#include "style.h"
|
||||||
|
|
||||||
#include "app/core/platform/file_dialog.h"
|
#include "app/core/platform/file_dialog.h"
|
||||||
|
#include "app/gui/theme_manager.h"
|
||||||
|
#include "app/gui/background_renderer.h"
|
||||||
#include "core/platform/font_loader.h"
|
#include "core/platform/font_loader.h"
|
||||||
#include "gui/color.h"
|
#include "gui/color.h"
|
||||||
#include "imgui/imgui.h"
|
#include "imgui/imgui.h"
|
||||||
@@ -408,6 +410,68 @@ void DrawDisplaySettings(ImGuiStyle *ref) {
|
|||||||
|
|
||||||
ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.50f);
|
ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.50f);
|
||||||
|
|
||||||
|
// Enhanced theme selector
|
||||||
|
auto& theme_manager = ThemeManager::Get();
|
||||||
|
static bool show_theme_selector = false;
|
||||||
|
|
||||||
|
ImGui::Text("Theme Selection:");
|
||||||
|
|
||||||
|
// Add special "Classic YAZE" option first
|
||||||
|
std::string current_display_name = theme_manager.GetCurrentTheme().name;
|
||||||
|
if (current_display_name == "Classic YAZE") {
|
||||||
|
current_display_name = "Classic YAZE (Original)";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::BeginCombo("##ThemeSelector", current_display_name.c_str())) {
|
||||||
|
// Classic YAZE option (direct ColorsYaze() function)
|
||||||
|
bool is_classic_selected = (theme_manager.GetCurrentTheme().name == "Classic YAZE");
|
||||||
|
if (ImGui::Selectable("Classic YAZE (Original)", is_classic_selected)) {
|
||||||
|
theme_manager.ApplyClassicYazeTheme();
|
||||||
|
ref_saved_style = style; // Update reference when theme changes
|
||||||
|
}
|
||||||
|
if (is_classic_selected) {
|
||||||
|
ImGui::SetItemDefaultFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
// File-based themes (sorted)
|
||||||
|
auto available_themes = theme_manager.GetAvailableThemes();
|
||||||
|
std::sort(available_themes.begin(), available_themes.end());
|
||||||
|
|
||||||
|
for (const auto& theme_name : available_themes) {
|
||||||
|
bool is_selected = (theme_name == theme_manager.GetCurrentTheme().name);
|
||||||
|
if (ImGui::Selectable(theme_name.c_str(), is_selected)) {
|
||||||
|
auto status = theme_manager.LoadTheme(theme_name); // Use LoadTheme for consistency
|
||||||
|
if (!status.ok()) {
|
||||||
|
// Could show error message to user here
|
||||||
|
}
|
||||||
|
ref_saved_style = style; // Update reference when theme changes
|
||||||
|
}
|
||||||
|
if (is_selected) {
|
||||||
|
ImGui::SetItemDefaultFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndCombo();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Theme Settings")) {
|
||||||
|
show_theme_selector = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (show_theme_selector) {
|
||||||
|
theme_manager.ShowThemeSelector(&show_theme_selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
// Background effects settings
|
||||||
|
auto& bg_renderer = gui::BackgroundRenderer::Get();
|
||||||
|
bg_renderer.DrawSettingsUI();
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
if (ImGui::ShowStyleSelector("Colors##Selector")) ref_saved_style = style;
|
if (ImGui::ShowStyleSelector("Colors##Selector")) ref_saved_style = style;
|
||||||
ImGui::ShowFontSelector("Fonts##Selector");
|
ImGui::ShowFontSelector("Fonts##Selector");
|
||||||
|
|
||||||
|
|||||||
879
src/app/gui/theme_manager.cc
Normal file
879
src/app/gui/theme_manager.cc
Normal file
@@ -0,0 +1,879 @@
|
|||||||
|
#include "theme_manager.h"
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "absl/strings/str_format.h"
|
||||||
|
#include "absl/strings/str_split.h"
|
||||||
|
#include "app/core/platform/file_dialog.h"
|
||||||
|
#include "app/gui/icons.h"
|
||||||
|
#include "app/gui/style.h" // For ColorsYaze function
|
||||||
|
#include "imgui/imgui.h"
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace gui {
|
||||||
|
|
||||||
|
// Helper function to create Color from RGB values
|
||||||
|
Color RGB(float r, float g, float b, float a = 1.0f) {
|
||||||
|
return {r / 255.0f, g / 255.0f, b / 255.0f, a};
|
||||||
|
}
|
||||||
|
|
||||||
|
Color RGBA(int r, int g, int b, int a = 255) {
|
||||||
|
return {r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Theme Implementation
|
||||||
|
void EnhancedTheme::ApplyToImGui() const {
|
||||||
|
ImGuiStyle* style = &ImGui::GetStyle();
|
||||||
|
ImVec4* colors = style->Colors;
|
||||||
|
|
||||||
|
// Apply colors
|
||||||
|
colors[ImGuiCol_Text] = ConvertColorToImVec4(text_primary);
|
||||||
|
colors[ImGuiCol_TextDisabled] = ConvertColorToImVec4(text_disabled);
|
||||||
|
colors[ImGuiCol_WindowBg] = ConvertColorToImVec4(window_bg);
|
||||||
|
colors[ImGuiCol_ChildBg] = ConvertColorToImVec4(child_bg);
|
||||||
|
colors[ImGuiCol_PopupBg] = ConvertColorToImVec4(popup_bg);
|
||||||
|
colors[ImGuiCol_Border] = ConvertColorToImVec4(border);
|
||||||
|
colors[ImGuiCol_BorderShadow] = ConvertColorToImVec4(border_shadow);
|
||||||
|
colors[ImGuiCol_FrameBg] = ConvertColorToImVec4(frame_bg);
|
||||||
|
colors[ImGuiCol_FrameBgHovered] = ConvertColorToImVec4(frame_bg_hovered);
|
||||||
|
colors[ImGuiCol_FrameBgActive] = ConvertColorToImVec4(frame_bg_active);
|
||||||
|
colors[ImGuiCol_TitleBg] = ConvertColorToImVec4(title_bg);
|
||||||
|
colors[ImGuiCol_TitleBgActive] = ConvertColorToImVec4(title_bg_active);
|
||||||
|
colors[ImGuiCol_TitleBgCollapsed] = ConvertColorToImVec4(title_bg_collapsed);
|
||||||
|
colors[ImGuiCol_MenuBarBg] = ConvertColorToImVec4(menu_bar_bg);
|
||||||
|
colors[ImGuiCol_ScrollbarBg] = ConvertColorToImVec4(scrollbar_bg);
|
||||||
|
colors[ImGuiCol_ScrollbarGrab] = ConvertColorToImVec4(scrollbar_grab);
|
||||||
|
colors[ImGuiCol_ScrollbarGrabHovered] = ConvertColorToImVec4(scrollbar_grab_hovered);
|
||||||
|
colors[ImGuiCol_ScrollbarGrabActive] = ConvertColorToImVec4(scrollbar_grab_active);
|
||||||
|
colors[ImGuiCol_Button] = ConvertColorToImVec4(button);
|
||||||
|
colors[ImGuiCol_ButtonHovered] = ConvertColorToImVec4(button_hovered);
|
||||||
|
colors[ImGuiCol_ButtonActive] = ConvertColorToImVec4(button_active);
|
||||||
|
colors[ImGuiCol_Header] = ConvertColorToImVec4(header);
|
||||||
|
colors[ImGuiCol_HeaderHovered] = ConvertColorToImVec4(header_hovered);
|
||||||
|
colors[ImGuiCol_HeaderActive] = ConvertColorToImVec4(header_active);
|
||||||
|
colors[ImGuiCol_Separator] = ConvertColorToImVec4(separator);
|
||||||
|
colors[ImGuiCol_SeparatorHovered] = ConvertColorToImVec4(separator_hovered);
|
||||||
|
colors[ImGuiCol_SeparatorActive] = ConvertColorToImVec4(separator_active);
|
||||||
|
colors[ImGuiCol_ResizeGrip] = ConvertColorToImVec4(resize_grip);
|
||||||
|
colors[ImGuiCol_ResizeGripHovered] = ConvertColorToImVec4(resize_grip_hovered);
|
||||||
|
colors[ImGuiCol_ResizeGripActive] = ConvertColorToImVec4(resize_grip_active);
|
||||||
|
colors[ImGuiCol_Tab] = ConvertColorToImVec4(tab);
|
||||||
|
colors[ImGuiCol_TabHovered] = ConvertColorToImVec4(tab_hovered);
|
||||||
|
colors[ImGuiCol_TabSelected] = ConvertColorToImVec4(tab_active);
|
||||||
|
colors[ImGuiCol_DockingPreview] = ConvertColorToImVec4(docking_preview);
|
||||||
|
colors[ImGuiCol_DockingEmptyBg] = ConvertColorToImVec4(docking_empty_bg);
|
||||||
|
|
||||||
|
// Complete ImGui color support
|
||||||
|
colors[ImGuiCol_CheckMark] = ConvertColorToImVec4(check_mark);
|
||||||
|
colors[ImGuiCol_SliderGrab] = ConvertColorToImVec4(slider_grab);
|
||||||
|
colors[ImGuiCol_SliderGrabActive] = ConvertColorToImVec4(slider_grab_active);
|
||||||
|
colors[ImGuiCol_InputTextCursor] = ConvertColorToImVec4(input_text_cursor);
|
||||||
|
colors[ImGuiCol_NavCursor] = ConvertColorToImVec4(nav_cursor);
|
||||||
|
colors[ImGuiCol_NavWindowingHighlight] = ConvertColorToImVec4(nav_windowing_highlight);
|
||||||
|
colors[ImGuiCol_NavWindowingDimBg] = ConvertColorToImVec4(nav_windowing_dim_bg);
|
||||||
|
colors[ImGuiCol_ModalWindowDimBg] = ConvertColorToImVec4(modal_window_dim_bg);
|
||||||
|
colors[ImGuiCol_TextSelectedBg] = ConvertColorToImVec4(text_selected_bg);
|
||||||
|
colors[ImGuiCol_DragDropTarget] = ConvertColorToImVec4(drag_drop_target);
|
||||||
|
colors[ImGuiCol_TableHeaderBg] = ConvertColorToImVec4(table_header_bg);
|
||||||
|
colors[ImGuiCol_TableBorderStrong] = ConvertColorToImVec4(table_border_strong);
|
||||||
|
colors[ImGuiCol_TableBorderLight] = ConvertColorToImVec4(table_border_light);
|
||||||
|
colors[ImGuiCol_TableRowBg] = ConvertColorToImVec4(table_row_bg);
|
||||||
|
colors[ImGuiCol_TableRowBgAlt] = ConvertColorToImVec4(table_row_bg_alt);
|
||||||
|
colors[ImGuiCol_TextLink] = ConvertColorToImVec4(text_link);
|
||||||
|
colors[ImGuiCol_PlotLines] = ConvertColorToImVec4(plot_lines);
|
||||||
|
colors[ImGuiCol_PlotLinesHovered] = ConvertColorToImVec4(plot_lines_hovered);
|
||||||
|
colors[ImGuiCol_PlotHistogram] = ConvertColorToImVec4(plot_histogram);
|
||||||
|
colors[ImGuiCol_PlotHistogramHovered] = ConvertColorToImVec4(plot_histogram_hovered);
|
||||||
|
|
||||||
|
// Apply style parameters
|
||||||
|
style->WindowRounding = window_rounding;
|
||||||
|
style->FrameRounding = frame_rounding;
|
||||||
|
style->ScrollbarRounding = scrollbar_rounding;
|
||||||
|
style->GrabRounding = grab_rounding;
|
||||||
|
style->TabRounding = tab_rounding;
|
||||||
|
style->WindowBorderSize = window_border_size;
|
||||||
|
style->FrameBorderSize = frame_border_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ThemeManager Implementation
|
||||||
|
ThemeManager& ThemeManager::Get() {
|
||||||
|
static ThemeManager instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThemeManager::InitializeBuiltInThemes() {
|
||||||
|
// Always create fallback theme first
|
||||||
|
CreateFallbackYazeClassic();
|
||||||
|
|
||||||
|
// Try to load themes from files (will override fallback if successful)
|
||||||
|
std::vector<std::string> theme_files = {
|
||||||
|
"yaze_tre.theme",
|
||||||
|
"cyberpunk.theme",
|
||||||
|
"sunset.theme",
|
||||||
|
"forest.theme",
|
||||||
|
"midnight.theme"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto& theme_file : theme_files) {
|
||||||
|
auto status = LoadThemeFromFile(theme_file);
|
||||||
|
if (!status.ok()) {
|
||||||
|
util::logf("Failed to load theme file %s: %s", theme_file.c_str(), status.message().data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure we have a valid current theme (prefer file-based theme)
|
||||||
|
if (themes_.find("YAZE Classic") != themes_.end()) {
|
||||||
|
current_theme_ = themes_["YAZE Classic"];
|
||||||
|
current_theme_name_ = "YAZE Classic";
|
||||||
|
} else if (themes_.find("YAZE Tre") != themes_.end()) {
|
||||||
|
current_theme_ = themes_["YAZE Tre"];
|
||||||
|
current_theme_name_ = "YAZE Tre";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThemeManager::CreateFallbackYazeClassic() {
|
||||||
|
// Fallback theme that matches the original ColorsYaze() function colors but in theme format
|
||||||
|
EnhancedTheme theme;
|
||||||
|
theme.name = "YAZE Tre";
|
||||||
|
theme.description = "YAZE theme resource edition";
|
||||||
|
theme.author = "YAZE Team";
|
||||||
|
|
||||||
|
// Use the exact original ColorsYaze colors
|
||||||
|
theme.primary = RGBA(92, 115, 92); // allttpLightGreen
|
||||||
|
theme.secondary = RGBA(71, 92, 71); // alttpMidGreen
|
||||||
|
theme.accent = RGBA(89, 119, 89); // TabActive
|
||||||
|
theme.background = RGBA(8, 8, 8); // Very dark gray for better grid visibility
|
||||||
|
|
||||||
|
theme.text_primary = RGBA(230, 230, 230); // 0.90f, 0.90f, 0.90f
|
||||||
|
theme.text_disabled = RGBA(153, 153, 153); // 0.60f, 0.60f, 0.60f
|
||||||
|
theme.window_bg = RGBA(8, 8, 8, 217); // Very dark gray with same alpha
|
||||||
|
theme.child_bg = RGBA(0, 0, 0, 0); // Transparent
|
||||||
|
theme.popup_bg = RGBA(28, 28, 36, 235); // 0.11f, 0.11f, 0.14f, 0.92f
|
||||||
|
|
||||||
|
theme.button = RGBA(71, 92, 71); // alttpMidGreen
|
||||||
|
theme.button_hovered = RGBA(125, 146, 125); // allttpLightestGreen
|
||||||
|
theme.button_active = RGBA(92, 115, 92); // allttpLightGreen
|
||||||
|
|
||||||
|
theme.header = RGBA(46, 66, 46); // alttpDarkGreen
|
||||||
|
theme.header_hovered = RGBA(92, 115, 92); // allttpLightGreen
|
||||||
|
theme.header_active = RGBA(71, 92, 71); // alttpMidGreen
|
||||||
|
|
||||||
|
theme.menu_bar_bg = RGBA(46, 66, 46); // alttpDarkGreen
|
||||||
|
theme.tab = RGBA(46, 66, 46); // alttpDarkGreen
|
||||||
|
theme.tab_hovered = RGBA(71, 92, 71); // alttpMidGreen
|
||||||
|
theme.tab_active = RGBA(89, 119, 89); // TabActive
|
||||||
|
|
||||||
|
// Complete all remaining ImGui colors from original ColorsYaze() function
|
||||||
|
theme.title_bg = RGBA(71, 92, 71); // alttpMidGreen
|
||||||
|
theme.title_bg_active = RGBA(46, 66, 46); // alttpDarkGreen
|
||||||
|
theme.title_bg_collapsed = RGBA(71, 92, 71); // alttpMidGreen
|
||||||
|
|
||||||
|
// Borders and separators
|
||||||
|
theme.border = RGBA(92, 115, 92); // allttpLightGreen
|
||||||
|
theme.border_shadow = RGBA(0, 0, 0, 0); // Transparent
|
||||||
|
theme.separator = RGBA(128, 128, 128, 153); // 0.50f, 0.50f, 0.50f, 0.60f
|
||||||
|
theme.separator_hovered = RGBA(153, 153, 178); // 0.60f, 0.60f, 0.70f
|
||||||
|
theme.separator_active = RGBA(178, 178, 230); // 0.70f, 0.70f, 0.90f
|
||||||
|
|
||||||
|
// Scrollbars
|
||||||
|
theme.scrollbar_bg = RGBA(92, 115, 92, 153); // 0.36f, 0.45f, 0.36f, 0.60f
|
||||||
|
theme.scrollbar_grab = RGBA(92, 115, 92, 76); // 0.36f, 0.45f, 0.36f, 0.30f
|
||||||
|
theme.scrollbar_grab_hovered = RGBA(92, 115, 92, 102); // 0.36f, 0.45f, 0.36f, 0.40f
|
||||||
|
theme.scrollbar_grab_active = RGBA(92, 115, 92, 153); // 0.36f, 0.45f, 0.36f, 0.60f
|
||||||
|
|
||||||
|
// Resize grips (from original - light blue highlights)
|
||||||
|
theme.resize_grip = RGBA(255, 255, 255, 26); // 1.00f, 1.00f, 1.00f, 0.10f
|
||||||
|
theme.resize_grip_hovered = RGBA(199, 209, 255, 153); // 0.78f, 0.82f, 1.00f, 0.60f
|
||||||
|
theme.resize_grip_active = RGBA(199, 209, 255, 230); // 0.78f, 0.82f, 1.00f, 0.90f
|
||||||
|
|
||||||
|
// Complete ImGui colors with smart defaults using accent colors
|
||||||
|
theme.check_mark = RGBA(230, 230, 230, 128); // 0.90f, 0.90f, 0.90f, 0.50f
|
||||||
|
theme.slider_grab = RGBA(255, 255, 255, 77); // 1.00f, 1.00f, 1.00f, 0.30f
|
||||||
|
theme.slider_grab_active = RGBA(92, 115, 92, 153); // Same as scrollbar for consistency
|
||||||
|
theme.input_text_cursor = theme.text_primary; // Use primary text color
|
||||||
|
theme.nav_cursor = theme.accent; // Use accent color for navigation
|
||||||
|
theme.nav_windowing_highlight = theme.accent; // Accent for window switching
|
||||||
|
theme.nav_windowing_dim_bg = RGBA(0, 0, 0, 128); // Semi-transparent overlay
|
||||||
|
theme.modal_window_dim_bg = RGBA(0, 0, 0, 89); // 0.35f alpha
|
||||||
|
theme.text_selected_bg = RGBA(89, 119, 89, 89); // Accent color with transparency
|
||||||
|
theme.drag_drop_target = theme.accent; // Use accent for drag targets
|
||||||
|
|
||||||
|
// Table colors (from original)
|
||||||
|
theme.table_header_bg = RGBA(46, 66, 46); // alttpDarkGreen
|
||||||
|
theme.table_border_strong = RGBA(71, 92, 71); // alttpMidGreen
|
||||||
|
theme.table_border_light = RGBA(66, 66, 71); // 0.26f, 0.26f, 0.28f
|
||||||
|
theme.table_row_bg = RGBA(0, 0, 0, 0); // Transparent
|
||||||
|
theme.table_row_bg_alt = RGBA(255, 255, 255, 18); // 1.00f, 1.00f, 1.00f, 0.07f
|
||||||
|
|
||||||
|
// Links and plots - use accent colors intelligently
|
||||||
|
theme.text_link = theme.accent; // Accent for links
|
||||||
|
theme.plot_lines = RGBA(255, 255, 255); // White for plots
|
||||||
|
theme.plot_lines_hovered = RGBA(230, 178, 0); // 0.90f, 0.70f, 0.00f
|
||||||
|
theme.plot_histogram = RGBA(230, 178, 0); // Same as above
|
||||||
|
theme.plot_histogram_hovered = RGBA(255, 153, 0); // 1.00f, 0.60f, 0.00f
|
||||||
|
|
||||||
|
// Docking colors
|
||||||
|
theme.docking_preview = RGBA(92, 115, 92, 180); // Light green with transparency
|
||||||
|
theme.docking_empty_bg = RGBA(46, 66, 46, 255); // Dark green
|
||||||
|
|
||||||
|
// Apply original style settings
|
||||||
|
theme.window_rounding = 0.0f;
|
||||||
|
theme.frame_rounding = 5.0f;
|
||||||
|
theme.scrollbar_rounding = 5.0f;
|
||||||
|
theme.tab_rounding = 0.0f;
|
||||||
|
theme.enable_glow_effects = false;
|
||||||
|
|
||||||
|
themes_["YAZE Tre"] = theme;
|
||||||
|
current_theme_ = theme;
|
||||||
|
current_theme_name_ = "YAZE Tre";
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status ThemeManager::LoadTheme(const std::string& theme_name) {
|
||||||
|
auto it = themes_.find(theme_name);
|
||||||
|
if (it == themes_.end()) {
|
||||||
|
return absl::NotFoundError(absl::StrFormat("Theme '%s' not found", theme_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
current_theme_ = it->second;
|
||||||
|
current_theme_name_ = theme_name;
|
||||||
|
current_theme_.ApplyToImGui();
|
||||||
|
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status ThemeManager::LoadThemeFromFile(const std::string& filepath) {
|
||||||
|
// Try multiple possible paths where theme files might be located
|
||||||
|
std::vector<std::string> possible_paths = {
|
||||||
|
filepath, // Absolute path
|
||||||
|
"assets/themes/" + filepath, // Relative from build dir
|
||||||
|
"../assets/themes/" + filepath, // Relative from bin dir
|
||||||
|
core::GetResourcePath("assets/themes/" + filepath), // Platform-specific resource path
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ifstream file;
|
||||||
|
std::string successful_path;
|
||||||
|
|
||||||
|
for (const auto& path : possible_paths) {
|
||||||
|
util::logf("Trying to open theme file: %s", path.c_str());
|
||||||
|
file.open(path);
|
||||||
|
if (file.is_open()) {
|
||||||
|
successful_path = path;
|
||||||
|
util::logf("✅ Successfully opened theme file: %s", path.c_str());
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
util::logf("❌ Failed to open theme file: %s", path.c_str());
|
||||||
|
file.clear(); // Clear any error flags before trying next path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.is_open()) {
|
||||||
|
return absl::InvalidArgumentError(absl::StrFormat("Cannot open theme file: %s (tried %zu paths)",
|
||||||
|
filepath, possible_paths.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string content((std::istreambuf_iterator<char>(file)),
|
||||||
|
std::istreambuf_iterator<char>());
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
if (content.empty()) {
|
||||||
|
return absl::InvalidArgumentError(absl::StrFormat("Theme file is empty: %s", successful_path));
|
||||||
|
}
|
||||||
|
|
||||||
|
EnhancedTheme theme;
|
||||||
|
auto parse_status = ParseThemeFile(content, theme);
|
||||||
|
if (!parse_status.ok()) {
|
||||||
|
return absl::InvalidArgumentError(absl::StrFormat("Failed to parse theme file %s: %s",
|
||||||
|
successful_path, parse_status.message()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theme.name.empty()) {
|
||||||
|
return absl::InvalidArgumentError(absl::StrFormat("Theme file missing name: %s", successful_path));
|
||||||
|
}
|
||||||
|
|
||||||
|
themes_[theme.name] = theme;
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> ThemeManager::GetAvailableThemes() const {
|
||||||
|
std::vector<std::string> theme_names;
|
||||||
|
for (const auto& [name, theme] : themes_) {
|
||||||
|
theme_names.push_back(name);
|
||||||
|
}
|
||||||
|
return theme_names;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EnhancedTheme* ThemeManager::GetTheme(const std::string& name) const {
|
||||||
|
auto it = themes_.find(name);
|
||||||
|
return (it != themes_.end()) ? &it->second : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThemeManager::ApplyTheme(const std::string& theme_name) {
|
||||||
|
auto status = LoadTheme(theme_name);
|
||||||
|
if (!status.ok()) {
|
||||||
|
// Fallback to YAZE Tre if theme not found
|
||||||
|
auto fallback_status = LoadTheme("YAZE Tre");
|
||||||
|
if (!fallback_status.ok()) {
|
||||||
|
util::logf("Failed to load fallback theme: %s", fallback_status.message().data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThemeManager::ApplyTheme(const EnhancedTheme& theme) {
|
||||||
|
current_theme_ = theme;
|
||||||
|
current_theme_.ApplyToImGui();
|
||||||
|
}
|
||||||
|
|
||||||
|
Color ThemeManager::GetWelcomeScreenBackground() const {
|
||||||
|
// Create a darker version of the window background for welcome screen
|
||||||
|
Color bg = current_theme_.window_bg;
|
||||||
|
return {bg.red * 0.8f, bg.green * 0.8f, bg.blue * 0.8f, bg.alpha};
|
||||||
|
}
|
||||||
|
|
||||||
|
Color ThemeManager::GetWelcomeScreenBorder() const {
|
||||||
|
return current_theme_.accent;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color ThemeManager::GetWelcomeScreenAccent() const {
|
||||||
|
return current_theme_.primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThemeManager::ShowThemeSelector(bool* p_open) {
|
||||||
|
if (!p_open || !*p_open) return;
|
||||||
|
|
||||||
|
if (ImGui::Begin(absl::StrFormat("%s Theme Selector", ICON_MD_PALETTE).c_str(), p_open)) {
|
||||||
|
ImGui::Text("%s Available Themes", ICON_MD_COLOR_LENS);
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
// Add Classic YAZE button first (direct ColorsYaze() application)
|
||||||
|
bool is_classic_active = (current_theme_name_ == "Classic YAZE");
|
||||||
|
if (is_classic_active) {
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.36f, 0.45f, 0.36f, 1.0f)); // allttpLightGreen
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button(absl::StrFormat("%s YAZE Classic (Original)",
|
||||||
|
is_classic_active ? ICON_MD_CHECK : ICON_MD_STAR).c_str(),
|
||||||
|
ImVec2(-1, 50))) {
|
||||||
|
ApplyClassicYazeTheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_classic_active) {
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::BeginTooltip();
|
||||||
|
ImGui::Text("Original YAZE theme using ColorsYaze() function");
|
||||||
|
ImGui::Text("This is the authentic classic look - direct function call");
|
||||||
|
ImGui::EndTooltip();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
// Sort themes alphabetically for consistent ordering (by name only)
|
||||||
|
std::vector<std::string> sorted_theme_names;
|
||||||
|
for (const auto& [name, theme] : themes_) {
|
||||||
|
sorted_theme_names.push_back(name);
|
||||||
|
}
|
||||||
|
std::sort(sorted_theme_names.begin(), sorted_theme_names.end());
|
||||||
|
|
||||||
|
for (const auto& name : sorted_theme_names) {
|
||||||
|
const auto& theme = themes_.at(name);
|
||||||
|
bool is_current = (name == current_theme_name_);
|
||||||
|
|
||||||
|
if (is_current) {
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, ConvertColorToImVec4(theme.accent));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button(absl::StrFormat("%s %s",
|
||||||
|
is_current ? ICON_MD_CHECK : ICON_MD_CIRCLE,
|
||||||
|
name.c_str()).c_str(), ImVec2(-1, 40))) {
|
||||||
|
auto status = LoadTheme(name); // Use LoadTheme instead of ApplyTheme to ensure correct tracking
|
||||||
|
if (!status.ok()) {
|
||||||
|
util::logf("Failed to load theme %s: %s", name.c_str(), status.message().data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_current) {
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show theme preview colors
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::ColorButton(absl::StrFormat("##primary_%s", name.c_str()).c_str(),
|
||||||
|
ConvertColorToImVec4(theme.primary),
|
||||||
|
ImGuiColorEditFlags_NoTooltip, ImVec2(20, 20));
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::ColorButton(absl::StrFormat("##secondary_%s", name.c_str()).c_str(),
|
||||||
|
ConvertColorToImVec4(theme.secondary),
|
||||||
|
ImGuiColorEditFlags_NoTooltip, ImVec2(20, 20));
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::ColorButton(absl::StrFormat("##accent_%s", name.c_str()).c_str(),
|
||||||
|
ConvertColorToImVec4(theme.accent),
|
||||||
|
ImGuiColorEditFlags_NoTooltip, ImVec2(20, 20));
|
||||||
|
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::BeginTooltip();
|
||||||
|
ImGui::Text("%s", theme.description.c_str());
|
||||||
|
ImGui::Text("Author: %s", theme.author.c_str());
|
||||||
|
ImGui::EndTooltip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
if (ImGui::Button(absl::StrFormat("%s Load Custom Theme", ICON_MD_FOLDER_OPEN).c_str())) {
|
||||||
|
auto file_path = core::FileDialogWrapper::ShowOpenFileDialog();
|
||||||
|
if (!file_path.empty()) {
|
||||||
|
auto status = LoadThemeFromFile(file_path);
|
||||||
|
if (!status.ok()) {
|
||||||
|
// Show error toast (would need access to toast manager)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
static bool show_simple_editor = false;
|
||||||
|
if (ImGui::Button(absl::StrFormat("%s Theme Editor", ICON_MD_EDIT).c_str())) {
|
||||||
|
show_simple_editor = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::BeginTooltip();
|
||||||
|
ImGui::Text("Edit and save custom themes");
|
||||||
|
ImGui::Text("Includes 'Save to File' functionality");
|
||||||
|
ImGui::EndTooltip();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (show_simple_editor) {
|
||||||
|
ShowSimpleThemeEditor(&show_simple_editor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status ThemeManager::ParseThemeFile(const std::string& content, EnhancedTheme& theme) {
|
||||||
|
std::istringstream stream(content);
|
||||||
|
std::string line;
|
||||||
|
std::string current_section = "";
|
||||||
|
|
||||||
|
while (std::getline(stream, line)) {
|
||||||
|
// Skip empty lines and comments
|
||||||
|
if (line.empty() || line[0] == '#') continue;
|
||||||
|
|
||||||
|
// Check for section headers [section_name]
|
||||||
|
if (line[0] == '[' && line.back() == ']') {
|
||||||
|
current_section = line.substr(1, line.length() - 2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t eq_pos = line.find('=');
|
||||||
|
if (eq_pos == std::string::npos) continue;
|
||||||
|
|
||||||
|
std::string key = line.substr(0, eq_pos);
|
||||||
|
std::string value = line.substr(eq_pos + 1);
|
||||||
|
|
||||||
|
// Trim whitespace and comments
|
||||||
|
key.erase(0, key.find_first_not_of(" \t"));
|
||||||
|
key.erase(key.find_last_not_of(" \t") + 1);
|
||||||
|
value.erase(0, value.find_first_not_of(" \t"));
|
||||||
|
|
||||||
|
// Remove inline comments
|
||||||
|
size_t comment_pos = value.find('#');
|
||||||
|
if (comment_pos != std::string::npos) {
|
||||||
|
value = value.substr(0, comment_pos);
|
||||||
|
}
|
||||||
|
value.erase(value.find_last_not_of(" \t") + 1);
|
||||||
|
|
||||||
|
// Parse based on section
|
||||||
|
if (current_section == "colors") {
|
||||||
|
Color color = ParseColorFromString(value);
|
||||||
|
|
||||||
|
if (key == "primary") theme.primary = color;
|
||||||
|
else if (key == "secondary") theme.secondary = color;
|
||||||
|
else if (key == "accent") theme.accent = color;
|
||||||
|
else if (key == "background") theme.background = color;
|
||||||
|
else if (key == "surface") theme.surface = color;
|
||||||
|
else if (key == "error") theme.error = color;
|
||||||
|
else if (key == "warning") theme.warning = color;
|
||||||
|
else if (key == "success") theme.success = color;
|
||||||
|
else if (key == "info") theme.info = color;
|
||||||
|
else if (key == "text_primary") theme.text_primary = color;
|
||||||
|
else if (key == "text_secondary") theme.text_secondary = color;
|
||||||
|
else if (key == "text_disabled") theme.text_disabled = color;
|
||||||
|
else if (key == "window_bg") theme.window_bg = color;
|
||||||
|
else if (key == "child_bg") theme.child_bg = color;
|
||||||
|
else if (key == "popup_bg") theme.popup_bg = color;
|
||||||
|
else if (key == "button") theme.button = color;
|
||||||
|
else if (key == "button_hovered") theme.button_hovered = color;
|
||||||
|
else if (key == "button_active") theme.button_active = color;
|
||||||
|
else if (key == "frame_bg") theme.frame_bg = color;
|
||||||
|
else if (key == "frame_bg_hovered") theme.frame_bg_hovered = color;
|
||||||
|
else if (key == "frame_bg_active") theme.frame_bg_active = color;
|
||||||
|
else if (key == "header") theme.header = color;
|
||||||
|
else if (key == "header_hovered") theme.header_hovered = color;
|
||||||
|
else if (key == "header_active") theme.header_active = color;
|
||||||
|
else if (key == "tab") theme.tab = color;
|
||||||
|
else if (key == "tab_hovered") theme.tab_hovered = color;
|
||||||
|
else if (key == "tab_active") theme.tab_active = color;
|
||||||
|
else if (key == "menu_bar_bg") theme.menu_bar_bg = color;
|
||||||
|
else if (key == "title_bg") theme.title_bg = color;
|
||||||
|
else if (key == "title_bg_active") theme.title_bg_active = color;
|
||||||
|
else if (key == "title_bg_collapsed") theme.title_bg_collapsed = color;
|
||||||
|
else if (key == "separator") theme.separator = color;
|
||||||
|
else if (key == "separator_hovered") theme.separator_hovered = color;
|
||||||
|
else if (key == "separator_active") theme.separator_active = color;
|
||||||
|
else if (key == "scrollbar_bg") theme.scrollbar_bg = color;
|
||||||
|
else if (key == "scrollbar_grab") theme.scrollbar_grab = color;
|
||||||
|
else if (key == "scrollbar_grab_hovered") theme.scrollbar_grab_hovered = color;
|
||||||
|
else if (key == "scrollbar_grab_active") theme.scrollbar_grab_active = color;
|
||||||
|
else if (key == "border") theme.border = color;
|
||||||
|
else if (key == "border_shadow") theme.border_shadow = color;
|
||||||
|
else if (key == "resize_grip") theme.resize_grip = color;
|
||||||
|
else if (key == "resize_grip_hovered") theme.resize_grip_hovered = color;
|
||||||
|
else if (key == "resize_grip_active") theme.resize_grip_active = color;
|
||||||
|
// Note: Additional colors like check_mark, slider_grab, table colors
|
||||||
|
// are handled by the fallback or can be added to EnhancedTheme struct as needed
|
||||||
|
}
|
||||||
|
else if (current_section == "style") {
|
||||||
|
if (key == "window_rounding") theme.window_rounding = std::stof(value);
|
||||||
|
else if (key == "frame_rounding") theme.frame_rounding = std::stof(value);
|
||||||
|
else if (key == "scrollbar_rounding") theme.scrollbar_rounding = std::stof(value);
|
||||||
|
else if (key == "grab_rounding") theme.grab_rounding = std::stof(value);
|
||||||
|
else if (key == "tab_rounding") theme.tab_rounding = std::stof(value);
|
||||||
|
else if (key == "window_border_size") theme.window_border_size = std::stof(value);
|
||||||
|
else if (key == "frame_border_size") theme.frame_border_size = std::stof(value);
|
||||||
|
else if (key == "enable_animations") theme.enable_animations = (value == "true");
|
||||||
|
else if (key == "enable_glow_effects") theme.enable_glow_effects = (value == "true");
|
||||||
|
else if (key == "animation_speed") theme.animation_speed = std::stof(value);
|
||||||
|
}
|
||||||
|
else if (current_section == "" || current_section == "metadata") {
|
||||||
|
// Top-level metadata
|
||||||
|
if (key == "name") theme.name = value;
|
||||||
|
else if (key == "description") theme.description = value;
|
||||||
|
else if (key == "author") theme.author = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
Color ThemeManager::ParseColorFromString(const std::string& color_str) const {
|
||||||
|
std::vector<std::string> components = absl::StrSplit(color_str, ',');
|
||||||
|
if (components.size() != 4) {
|
||||||
|
return RGBA(255, 255, 255, 255); // White fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
int r = std::stoi(components[0]);
|
||||||
|
int g = std::stoi(components[1]);
|
||||||
|
int b = std::stoi(components[2]);
|
||||||
|
int a = std::stoi(components[3]);
|
||||||
|
return RGBA(r, g, b, a);
|
||||||
|
} catch (...) {
|
||||||
|
return RGBA(255, 255, 255, 255); // White fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ThemeManager::SerializeTheme(const EnhancedTheme& theme) const {
|
||||||
|
std::ostringstream ss;
|
||||||
|
|
||||||
|
// Helper function to convert color to RGB string
|
||||||
|
auto colorToString = [](const Color& c) -> std::string {
|
||||||
|
int r = static_cast<int>(c.red * 255.0f);
|
||||||
|
int g = static_cast<int>(c.green * 255.0f);
|
||||||
|
int b = static_cast<int>(c.blue * 255.0f);
|
||||||
|
int a = static_cast<int>(c.alpha * 255.0f);
|
||||||
|
return std::to_string(r) + "," + std::to_string(g) + "," + std::to_string(b) + "," + std::to_string(a);
|
||||||
|
};
|
||||||
|
|
||||||
|
ss << "# YAZE Theme File\n";
|
||||||
|
ss << "# Generated by YAZE Theme Editor\n";
|
||||||
|
ss << "name=" << theme.name << "\n";
|
||||||
|
ss << "description=" << theme.description << "\n";
|
||||||
|
ss << "author=" << theme.author << "\n";
|
||||||
|
ss << "version=1.0\n";
|
||||||
|
ss << "\n[colors]\n";
|
||||||
|
|
||||||
|
// Primary colors
|
||||||
|
ss << "# Primary colors\n";
|
||||||
|
ss << "primary=" << colorToString(theme.primary) << "\n";
|
||||||
|
ss << "secondary=" << colorToString(theme.secondary) << "\n";
|
||||||
|
ss << "accent=" << colorToString(theme.accent) << "\n";
|
||||||
|
ss << "background=" << colorToString(theme.background) << "\n";
|
||||||
|
ss << "surface=" << colorToString(theme.surface) << "\n";
|
||||||
|
ss << "\n";
|
||||||
|
|
||||||
|
// Status colors
|
||||||
|
ss << "# Status colors\n";
|
||||||
|
ss << "error=" << colorToString(theme.error) << "\n";
|
||||||
|
ss << "warning=" << colorToString(theme.warning) << "\n";
|
||||||
|
ss << "success=" << colorToString(theme.success) << "\n";
|
||||||
|
ss << "info=" << colorToString(theme.info) << "\n";
|
||||||
|
ss << "\n";
|
||||||
|
|
||||||
|
// Text colors
|
||||||
|
ss << "# Text colors\n";
|
||||||
|
ss << "text_primary=" << colorToString(theme.text_primary) << "\n";
|
||||||
|
ss << "text_secondary=" << colorToString(theme.text_secondary) << "\n";
|
||||||
|
ss << "text_disabled=" << colorToString(theme.text_disabled) << "\n";
|
||||||
|
ss << "\n";
|
||||||
|
|
||||||
|
// Window colors
|
||||||
|
ss << "# Window colors\n";
|
||||||
|
ss << "window_bg=" << colorToString(theme.window_bg) << "\n";
|
||||||
|
ss << "child_bg=" << colorToString(theme.child_bg) << "\n";
|
||||||
|
ss << "popup_bg=" << colorToString(theme.popup_bg) << "\n";
|
||||||
|
ss << "\n";
|
||||||
|
|
||||||
|
// Interactive elements
|
||||||
|
ss << "# Interactive elements\n";
|
||||||
|
ss << "button=" << colorToString(theme.button) << "\n";
|
||||||
|
ss << "button_hovered=" << colorToString(theme.button_hovered) << "\n";
|
||||||
|
ss << "button_active=" << colorToString(theme.button_active) << "\n";
|
||||||
|
ss << "frame_bg=" << colorToString(theme.frame_bg) << "\n";
|
||||||
|
ss << "frame_bg_hovered=" << colorToString(theme.frame_bg_hovered) << "\n";
|
||||||
|
ss << "frame_bg_active=" << colorToString(theme.frame_bg_active) << "\n";
|
||||||
|
ss << "\n";
|
||||||
|
|
||||||
|
// Navigation
|
||||||
|
ss << "# Navigation\n";
|
||||||
|
ss << "header=" << colorToString(theme.header) << "\n";
|
||||||
|
ss << "header_hovered=" << colorToString(theme.header_hovered) << "\n";
|
||||||
|
ss << "header_active=" << colorToString(theme.header_active) << "\n";
|
||||||
|
ss << "tab=" << colorToString(theme.tab) << "\n";
|
||||||
|
ss << "tab_hovered=" << colorToString(theme.tab_hovered) << "\n";
|
||||||
|
ss << "tab_active=" << colorToString(theme.tab_active) << "\n";
|
||||||
|
ss << "menu_bar_bg=" << colorToString(theme.menu_bar_bg) << "\n";
|
||||||
|
ss << "title_bg=" << colorToString(theme.title_bg) << "\n";
|
||||||
|
ss << "title_bg_active=" << colorToString(theme.title_bg_active) << "\n";
|
||||||
|
ss << "title_bg_collapsed=" << colorToString(theme.title_bg_collapsed) << "\n";
|
||||||
|
ss << "\n";
|
||||||
|
|
||||||
|
// Borders and separators
|
||||||
|
ss << "# Borders and separators\n";
|
||||||
|
ss << "border=" << colorToString(theme.border) << "\n";
|
||||||
|
ss << "border_shadow=" << colorToString(theme.border_shadow) << "\n";
|
||||||
|
ss << "separator=" << colorToString(theme.separator) << "\n";
|
||||||
|
ss << "separator_hovered=" << colorToString(theme.separator_hovered) << "\n";
|
||||||
|
ss << "separator_active=" << colorToString(theme.separator_active) << "\n";
|
||||||
|
ss << "\n";
|
||||||
|
|
||||||
|
// Scrollbars
|
||||||
|
ss << "# Scrollbars\n";
|
||||||
|
ss << "scrollbar_bg=" << colorToString(theme.scrollbar_bg) << "\n";
|
||||||
|
ss << "scrollbar_grab=" << colorToString(theme.scrollbar_grab) << "\n";
|
||||||
|
ss << "scrollbar_grab_hovered=" << colorToString(theme.scrollbar_grab_hovered) << "\n";
|
||||||
|
ss << "scrollbar_grab_active=" << colorToString(theme.scrollbar_grab_active) << "\n";
|
||||||
|
ss << "\n";
|
||||||
|
|
||||||
|
// Style settings
|
||||||
|
ss << "[style]\n";
|
||||||
|
ss << "window_rounding=" << theme.window_rounding << "\n";
|
||||||
|
ss << "frame_rounding=" << theme.frame_rounding << "\n";
|
||||||
|
ss << "scrollbar_rounding=" << theme.scrollbar_rounding << "\n";
|
||||||
|
ss << "tab_rounding=" << theme.tab_rounding << "\n";
|
||||||
|
ss << "enable_animations=" << (theme.enable_animations ? "true" : "false") << "\n";
|
||||||
|
ss << "enable_glow_effects=" << (theme.enable_glow_effects ? "true" : "false") << "\n";
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status ThemeManager::SaveThemeToFile(const EnhancedTheme& theme, const std::string& filepath) const {
|
||||||
|
std::string theme_content = SerializeTheme(theme);
|
||||||
|
|
||||||
|
std::ofstream file(filepath);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
return absl::InternalError(absl::StrFormat("Failed to open file for writing: %s", filepath));
|
||||||
|
}
|
||||||
|
|
||||||
|
file << theme_content;
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
if (file.fail()) {
|
||||||
|
return absl::InternalError(absl::StrFormat("Failed to write theme file: %s", filepath));
|
||||||
|
}
|
||||||
|
|
||||||
|
util::logf("✅ Successfully saved theme '%s' to file: %s", theme.name.c_str(), filepath.c_str());
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThemeManager::ApplyClassicYazeTheme() {
|
||||||
|
// Apply the original ColorsYaze() function directly
|
||||||
|
ColorsYaze();
|
||||||
|
current_theme_name_ = "Classic YAZE";
|
||||||
|
|
||||||
|
// Update current_theme_ to reflect the applied colors for consistency
|
||||||
|
// (This creates a temporary theme object that matches what ColorsYaze() sets)
|
||||||
|
EnhancedTheme classic_theme;
|
||||||
|
classic_theme.name = "Classic YAZE";
|
||||||
|
classic_theme.description = "Original YAZE theme (direct ColorsYaze() function)";
|
||||||
|
classic_theme.author = "YAZE Team";
|
||||||
|
|
||||||
|
// Extract the basic colors that ColorsYaze() sets (adjusted for grid visibility)
|
||||||
|
classic_theme.primary = RGBA(92, 115, 92); // allttpLightGreen
|
||||||
|
classic_theme.secondary = RGBA(71, 92, 71); // alttpMidGreen
|
||||||
|
classic_theme.accent = RGBA(89, 119, 89); // TabActive color
|
||||||
|
classic_theme.background = RGBA(8, 8, 8); // Very dark gray for better grid visibility
|
||||||
|
|
||||||
|
current_theme_ = classic_theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThemeManager::ShowSimpleThemeEditor(bool* p_open) {
|
||||||
|
if (!p_open || !*p_open) return;
|
||||||
|
|
||||||
|
if (ImGui::Begin(absl::StrFormat("%s Simple Theme Editor", ICON_MD_PALETTE).c_str(), p_open)) {
|
||||||
|
ImGui::Text("%s Create or modify themes with basic controls", ICON_MD_EDIT);
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
static EnhancedTheme edit_theme = current_theme_;
|
||||||
|
static char theme_name[128];
|
||||||
|
static char theme_description[256];
|
||||||
|
static char theme_author[128];
|
||||||
|
|
||||||
|
// Basic theme info
|
||||||
|
ImGui::InputText("Theme Name", theme_name, sizeof(theme_name));
|
||||||
|
ImGui::InputText("Description", theme_description, sizeof(theme_description));
|
||||||
|
ImGui::InputText("Author", theme_author, sizeof(theme_author));
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
// Primary Colors
|
||||||
|
if (ImGui::CollapsingHeader("Primary Colors", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||||
|
ImVec4 primary = ConvertColorToImVec4(edit_theme.primary);
|
||||||
|
ImVec4 secondary = ConvertColorToImVec4(edit_theme.secondary);
|
||||||
|
ImVec4 accent = ConvertColorToImVec4(edit_theme.accent);
|
||||||
|
ImVec4 background = ConvertColorToImVec4(edit_theme.background);
|
||||||
|
|
||||||
|
if (ImGui::ColorEdit3("Primary", &primary.x)) {
|
||||||
|
edit_theme.primary = {primary.x, primary.y, primary.z, primary.w};
|
||||||
|
}
|
||||||
|
if (ImGui::ColorEdit3("Secondary", &secondary.x)) {
|
||||||
|
edit_theme.secondary = {secondary.x, secondary.y, secondary.z, secondary.w};
|
||||||
|
}
|
||||||
|
if (ImGui::ColorEdit3("Accent", &accent.x)) {
|
||||||
|
edit_theme.accent = {accent.x, accent.y, accent.z, accent.w};
|
||||||
|
}
|
||||||
|
if (ImGui::ColorEdit3("Background", &background.x)) {
|
||||||
|
edit_theme.background = {background.x, background.y, background.z, background.w};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Text Colors
|
||||||
|
if (ImGui::CollapsingHeader("Text Colors")) {
|
||||||
|
ImVec4 text_primary = ConvertColorToImVec4(edit_theme.text_primary);
|
||||||
|
ImVec4 text_secondary = ConvertColorToImVec4(edit_theme.text_secondary);
|
||||||
|
ImVec4 text_disabled = ConvertColorToImVec4(edit_theme.text_disabled);
|
||||||
|
|
||||||
|
if (ImGui::ColorEdit3("Primary Text", &text_primary.x)) {
|
||||||
|
edit_theme.text_primary = {text_primary.x, text_primary.y, text_primary.z, text_primary.w};
|
||||||
|
}
|
||||||
|
if (ImGui::ColorEdit3("Secondary Text", &text_secondary.x)) {
|
||||||
|
edit_theme.text_secondary = {text_secondary.x, text_secondary.y, text_secondary.z, text_secondary.w};
|
||||||
|
}
|
||||||
|
if (ImGui::ColorEdit3("Disabled Text", &text_disabled.x)) {
|
||||||
|
edit_theme.text_disabled = {text_disabled.x, text_disabled.y, text_disabled.z, text_disabled.w};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Window Colors
|
||||||
|
if (ImGui::CollapsingHeader("Window Colors")) {
|
||||||
|
ImVec4 window_bg = ConvertColorToImVec4(edit_theme.window_bg);
|
||||||
|
ImVec4 popup_bg = ConvertColorToImVec4(edit_theme.popup_bg);
|
||||||
|
|
||||||
|
if (ImGui::ColorEdit4("Window Background", &window_bg.x)) {
|
||||||
|
edit_theme.window_bg = {window_bg.x, window_bg.y, window_bg.z, window_bg.w};
|
||||||
|
}
|
||||||
|
if (ImGui::ColorEdit4("Popup Background", &popup_bg.x)) {
|
||||||
|
edit_theme.popup_bg = {popup_bg.x, popup_bg.y, popup_bg.z, popup_bg.w};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interactive Elements
|
||||||
|
if (ImGui::CollapsingHeader("Interactive Elements")) {
|
||||||
|
ImVec4 button = ConvertColorToImVec4(edit_theme.button);
|
||||||
|
ImVec4 button_hovered = ConvertColorToImVec4(edit_theme.button_hovered);
|
||||||
|
ImVec4 button_active = ConvertColorToImVec4(edit_theme.button_active);
|
||||||
|
|
||||||
|
if (ImGui::ColorEdit3("Button", &button.x)) {
|
||||||
|
edit_theme.button = {button.x, button.y, button.z, button.w};
|
||||||
|
}
|
||||||
|
if (ImGui::ColorEdit3("Button Hovered", &button_hovered.x)) {
|
||||||
|
edit_theme.button_hovered = {button_hovered.x, button_hovered.y, button_hovered.z, button_hovered.w};
|
||||||
|
}
|
||||||
|
if (ImGui::ColorEdit3("Button Active", &button_active.x)) {
|
||||||
|
edit_theme.button_active = {button_active.x, button_active.y, button_active.z, button_active.w};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
if (ImGui::Button("Preview Theme")) {
|
||||||
|
ApplyTheme(edit_theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Reset to Current")) {
|
||||||
|
edit_theme = current_theme_;
|
||||||
|
strncpy(theme_name, current_theme_.name.c_str(), sizeof(theme_name));
|
||||||
|
strncpy(theme_description, current_theme_.description.c_str(), sizeof(theme_description));
|
||||||
|
strncpy(theme_author, current_theme_.author.c_str(), sizeof(theme_author));
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Save Theme")) {
|
||||||
|
edit_theme.name = std::string(theme_name);
|
||||||
|
edit_theme.description = std::string(theme_description);
|
||||||
|
edit_theme.author = std::string(theme_author);
|
||||||
|
|
||||||
|
// Add to themes map and apply
|
||||||
|
themes_[edit_theme.name] = edit_theme;
|
||||||
|
ApplyTheme(edit_theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Save to File...")) {
|
||||||
|
edit_theme.name = std::string(theme_name);
|
||||||
|
edit_theme.description = std::string(theme_description);
|
||||||
|
edit_theme.author = std::string(theme_author);
|
||||||
|
|
||||||
|
// Use folder dialog to choose save location
|
||||||
|
auto folder_path = core::FileDialogWrapper::ShowOpenFolderDialog();
|
||||||
|
if (!folder_path.empty()) {
|
||||||
|
// Create filename from theme name (sanitize it)
|
||||||
|
std::string safe_name = edit_theme.name;
|
||||||
|
// Replace spaces and special chars with underscores
|
||||||
|
for (char& c : safe_name) {
|
||||||
|
if (!std::isalnum(c)) {
|
||||||
|
c = '_';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string file_path = folder_path + "/" + safe_name + ".theme";
|
||||||
|
|
||||||
|
auto status = SaveThemeToFile(edit_theme, file_path);
|
||||||
|
if (status.ok()) {
|
||||||
|
// Also add to themes map for immediate use
|
||||||
|
themes_[edit_theme.name] = edit_theme;
|
||||||
|
ApplyTheme(edit_theme);
|
||||||
|
util::logf("Theme saved successfully to: %s", file_path.c_str());
|
||||||
|
} else {
|
||||||
|
util::logf("Failed to save theme: %s", status.message().data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::BeginTooltip();
|
||||||
|
ImGui::Text("Save theme to a .theme file");
|
||||||
|
ImGui::Text("Saved themes can be shared and loaded later");
|
||||||
|
ImGui::EndTooltip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace gui
|
||||||
|
} // namespace yaze
|
||||||
179
src/app/gui/theme_manager.h
Normal file
179
src/app/gui/theme_manager.h
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
#ifndef YAZE_APP_GUI_THEME_MANAGER_H
|
||||||
|
#define YAZE_APP_GUI_THEME_MANAGER_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "absl/status/status.h"
|
||||||
|
#include "absl/status/statusor.h"
|
||||||
|
#include "app/gui/color.h"
|
||||||
|
#include "imgui/imgui.h"
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace gui {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @struct EnhancedTheme
|
||||||
|
* @brief Comprehensive theme structure for YAZE
|
||||||
|
*/
|
||||||
|
struct EnhancedTheme {
|
||||||
|
std::string name;
|
||||||
|
std::string description;
|
||||||
|
std::string author;
|
||||||
|
|
||||||
|
// Primary colors
|
||||||
|
Color primary;
|
||||||
|
Color secondary;
|
||||||
|
Color accent;
|
||||||
|
Color background;
|
||||||
|
Color surface;
|
||||||
|
Color error;
|
||||||
|
Color warning;
|
||||||
|
Color success;
|
||||||
|
Color info;
|
||||||
|
|
||||||
|
// Text colors
|
||||||
|
Color text_primary;
|
||||||
|
Color text_secondary;
|
||||||
|
Color text_disabled;
|
||||||
|
|
||||||
|
// Window colors
|
||||||
|
Color window_bg;
|
||||||
|
Color child_bg;
|
||||||
|
Color popup_bg;
|
||||||
|
Color modal_bg;
|
||||||
|
|
||||||
|
// Interactive elements
|
||||||
|
Color button;
|
||||||
|
Color button_hovered;
|
||||||
|
Color button_active;
|
||||||
|
Color frame_bg;
|
||||||
|
Color frame_bg_hovered;
|
||||||
|
Color frame_bg_active;
|
||||||
|
|
||||||
|
// Navigation and selection
|
||||||
|
Color header;
|
||||||
|
Color header_hovered;
|
||||||
|
Color header_active;
|
||||||
|
Color tab;
|
||||||
|
Color tab_hovered;
|
||||||
|
Color tab_active;
|
||||||
|
Color menu_bar_bg;
|
||||||
|
Color title_bg;
|
||||||
|
Color title_bg_active;
|
||||||
|
Color title_bg_collapsed;
|
||||||
|
|
||||||
|
// Borders and separators
|
||||||
|
Color border;
|
||||||
|
Color border_shadow;
|
||||||
|
Color separator;
|
||||||
|
Color separator_hovered;
|
||||||
|
Color separator_active;
|
||||||
|
|
||||||
|
// Scrollbars and controls
|
||||||
|
Color scrollbar_bg;
|
||||||
|
Color scrollbar_grab;
|
||||||
|
Color scrollbar_grab_hovered;
|
||||||
|
Color scrollbar_grab_active;
|
||||||
|
|
||||||
|
// Special elements
|
||||||
|
Color resize_grip;
|
||||||
|
Color resize_grip_hovered;
|
||||||
|
Color resize_grip_active;
|
||||||
|
Color docking_preview;
|
||||||
|
Color docking_empty_bg;
|
||||||
|
|
||||||
|
// Complete ImGui color support
|
||||||
|
Color check_mark;
|
||||||
|
Color slider_grab;
|
||||||
|
Color slider_grab_active;
|
||||||
|
Color input_text_cursor;
|
||||||
|
Color nav_cursor;
|
||||||
|
Color nav_windowing_highlight;
|
||||||
|
Color nav_windowing_dim_bg;
|
||||||
|
Color modal_window_dim_bg;
|
||||||
|
Color text_selected_bg;
|
||||||
|
Color drag_drop_target;
|
||||||
|
Color table_header_bg;
|
||||||
|
Color table_border_strong;
|
||||||
|
Color table_border_light;
|
||||||
|
Color table_row_bg;
|
||||||
|
Color table_row_bg_alt;
|
||||||
|
Color text_link;
|
||||||
|
Color plot_lines;
|
||||||
|
Color plot_lines_hovered;
|
||||||
|
Color plot_histogram;
|
||||||
|
Color plot_histogram_hovered;
|
||||||
|
|
||||||
|
// Style parameters
|
||||||
|
float window_rounding = 0.0f;
|
||||||
|
float frame_rounding = 5.0f;
|
||||||
|
float scrollbar_rounding = 5.0f;
|
||||||
|
float grab_rounding = 3.0f;
|
||||||
|
float tab_rounding = 0.0f;
|
||||||
|
float window_border_size = 0.0f;
|
||||||
|
float frame_border_size = 0.0f;
|
||||||
|
|
||||||
|
// Animation and effects
|
||||||
|
bool enable_animations = true;
|
||||||
|
float animation_speed = 1.0f;
|
||||||
|
bool enable_glow_effects = false;
|
||||||
|
|
||||||
|
// Helper methods
|
||||||
|
void ApplyToImGui() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class ThemeManager
|
||||||
|
* @brief Manages themes, loading, saving, and switching
|
||||||
|
*/
|
||||||
|
class ThemeManager {
|
||||||
|
public:
|
||||||
|
static ThemeManager& Get();
|
||||||
|
|
||||||
|
// Theme management
|
||||||
|
absl::Status LoadTheme(const std::string& theme_name);
|
||||||
|
absl::Status SaveTheme(const EnhancedTheme& theme, const std::string& filename);
|
||||||
|
absl::Status LoadThemeFromFile(const std::string& filepath);
|
||||||
|
absl::Status SaveThemeToFile(const EnhancedTheme& theme, const std::string& filepath) const;
|
||||||
|
|
||||||
|
// Built-in themes
|
||||||
|
void InitializeBuiltInThemes();
|
||||||
|
std::vector<std::string> GetAvailableThemes() const;
|
||||||
|
const EnhancedTheme* GetTheme(const std::string& name) const;
|
||||||
|
const EnhancedTheme& GetCurrentTheme() const { return current_theme_; }
|
||||||
|
|
||||||
|
// Theme application
|
||||||
|
void ApplyTheme(const std::string& theme_name);
|
||||||
|
void ApplyTheme(const EnhancedTheme& theme);
|
||||||
|
void ApplyClassicYazeTheme(); // Apply original ColorsYaze() function
|
||||||
|
|
||||||
|
// Theme creation and editing
|
||||||
|
EnhancedTheme CreateCustomTheme(const std::string& name);
|
||||||
|
void ShowThemeEditor(bool* p_open);
|
||||||
|
void ShowThemeSelector(bool* p_open);
|
||||||
|
void ShowSimpleThemeEditor(bool* p_open);
|
||||||
|
|
||||||
|
// Integration with welcome screen
|
||||||
|
Color GetWelcomeScreenBackground() const;
|
||||||
|
Color GetWelcomeScreenBorder() const;
|
||||||
|
Color GetWelcomeScreenAccent() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ThemeManager() { InitializeBuiltInThemes(); }
|
||||||
|
|
||||||
|
std::map<std::string, EnhancedTheme> themes_;
|
||||||
|
EnhancedTheme current_theme_;
|
||||||
|
std::string current_theme_name_ = "YAZE Classic";
|
||||||
|
|
||||||
|
void CreateFallbackYazeClassic();
|
||||||
|
absl::Status ParseThemeFile(const std::string& content, EnhancedTheme& theme);
|
||||||
|
Color ParseColorFromString(const std::string& color_str) const;
|
||||||
|
std::string SerializeTheme(const EnhancedTheme& theme) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace gui
|
||||||
|
} // namespace yaze
|
||||||
|
|
||||||
|
#endif // YAZE_APP_GUI_THEME_MANAGER_H
|
||||||
@@ -5,7 +5,9 @@
|
|||||||
#include "absl/debugging/failure_signal_handler.h"
|
#include "absl/debugging/failure_signal_handler.h"
|
||||||
#include "absl/debugging/symbolize.h"
|
#include "absl/debugging/symbolize.h"
|
||||||
#include "app/core/controller.h"
|
#include "app/core/controller.h"
|
||||||
|
#include "app/core/features.h"
|
||||||
#include "util/flag.h"
|
#include "util/flag.h"
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @namespace yaze
|
* @namespace yaze
|
||||||
@@ -13,7 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
using namespace yaze;
|
using namespace yaze;
|
||||||
|
|
||||||
|
// Enhanced flags for debugging
|
||||||
DEFINE_FLAG(std::string, rom_file, "", "The ROM file to load.");
|
DEFINE_FLAG(std::string, rom_file, "", "The ROM file to load.");
|
||||||
|
DEFINE_FLAG(std::string, log_file, "", "Output log file path for debugging.");
|
||||||
|
DEFINE_FLAG(bool, debug, false, "Enable debug logging and verbose output.");
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
absl::InitializeSymbolizer(argv[0]);
|
absl::InitializeSymbolizer(argv[0]);
|
||||||
@@ -30,8 +35,22 @@ int main(int argc, char **argv) {
|
|||||||
options.writerfn =
|
options.writerfn =
|
||||||
nullptr; // Use default writer to avoid custom handling issues
|
nullptr; // Use default writer to avoid custom handling issues
|
||||||
absl::InstallFailureSignalHandler(options);
|
absl::InstallFailureSignalHandler(options);
|
||||||
|
|
||||||
|
// Parse command line flags with custom parser
|
||||||
yaze::util::FlagParser parser(yaze::util::global_flag_registry());
|
yaze::util::FlagParser parser(yaze::util::global_flag_registry());
|
||||||
RETURN_IF_EXCEPTION(parser.Parse(argc, argv));
|
RETURN_IF_EXCEPTION(parser.Parse(argc, argv));
|
||||||
|
|
||||||
|
// Set up logging and debug flags (using custom flag system)
|
||||||
|
if (!FLAGS_log_file->Get().empty()) {
|
||||||
|
yaze::util::SetLogFile(FLAGS_log_file->Get());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable debugging if requested
|
||||||
|
if (FLAGS_debug->Get()) {
|
||||||
|
yaze::core::FeatureFlags::get().kLogToConsole = true;
|
||||||
|
yaze::util::logf("🚀 YAZE started in debug mode");
|
||||||
|
}
|
||||||
|
|
||||||
std::string rom_filename = "";
|
std::string rom_filename = "";
|
||||||
if (!FLAGS_rom_file->Get().empty()) {
|
if (!FLAGS_rom_file->Get().empty()) {
|
||||||
rom_filename = FLAGS_rom_file->Get();
|
rom_filename = FLAGS_rom_file->Get();
|
||||||
|
|||||||
@@ -128,6 +128,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)drawInMTKView:(MTKView *)view {
|
- (void)drawInMTKView:(MTKView *)view {
|
||||||
|
if (!_controller->active()) return;
|
||||||
|
|
||||||
ImGuiIO &io = ImGui::GetIO();
|
ImGuiIO &io = ImGui::GetIO();
|
||||||
io.DisplaySize.x = view.bounds.size.width;
|
io.DisplaySize.x = view.bounds.size.width;
|
||||||
io.DisplaySize.y = view.bounds.size.height;
|
io.DisplaySize.y = view.bounds.size.height;
|
||||||
@@ -235,6 +237,9 @@
|
|||||||
ImGuiIO &io = ImGui::GetIO();
|
ImGuiIO &io = ImGui::GetIO();
|
||||||
io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen);
|
io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen);
|
||||||
io.AddMouseWheelEvent(0.0f, gesture.scale);
|
io.AddMouseWheelEvent(0.0f, gesture.scale);
|
||||||
|
UIGestureRecognizer *gestureRecognizer = (UIGestureRecognizer *)gesture;
|
||||||
|
io.AddMousePosEvent([gestureRecognizer locationInView:self.view].x,
|
||||||
|
[gestureRecognizer locationInView:self.view].y);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)HandleSwipe:(UISwipeGestureRecognizer *)gesture {
|
- (void)HandleSwipe:(UISwipeGestureRecognizer *)gesture {
|
||||||
@@ -245,12 +250,18 @@
|
|||||||
} else if (gesture.direction == UISwipeGestureRecognizerDirectionLeft) {
|
} else if (gesture.direction == UISwipeGestureRecognizerDirectionLeft) {
|
||||||
io.AddMouseWheelEvent(-1.0f, 0.0f); // Swipe Left
|
io.AddMouseWheelEvent(-1.0f, 0.0f); // Swipe Left
|
||||||
}
|
}
|
||||||
|
UIGestureRecognizer *gestureRecognizer = (UIGestureRecognizer *)gesture;
|
||||||
|
io.AddMousePosEvent([gestureRecognizer locationInView:self.view].x,
|
||||||
|
[gestureRecognizer locationInView:self.view].y);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)handleLongPress:(UILongPressGestureRecognizer *)gesture {
|
- (void)handleLongPress:(UILongPressGestureRecognizer *)gesture {
|
||||||
ImGuiIO &io = ImGui::GetIO();
|
ImGuiIO &io = ImGui::GetIO();
|
||||||
io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen);
|
io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen);
|
||||||
io.AddMouseButtonEvent(1, gesture.state == UIGestureRecognizerStateBegan);
|
io.AddMouseButtonEvent(1, gesture.state == UIGestureRecognizerStateBegan);
|
||||||
|
UIGestureRecognizer *gestureRecognizer = (UIGestureRecognizer *)gesture;
|
||||||
|
io.AddMousePosEvent([gestureRecognizer locationInView:self.view].x,
|
||||||
|
[gestureRecognizer locationInView:self.view].y);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -14,7 +14,12 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace util {
|
namespace util {
|
||||||
|
|
||||||
static const std::string kLogFileOut = "yaze_log.txt";
|
static std::string g_log_file_path = "yaze_log.txt";
|
||||||
|
|
||||||
|
// Set custom log file path
|
||||||
|
inline void SetLogFile(const std::string& filepath) {
|
||||||
|
g_log_file_path = filepath;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
static void logf(const absl::FormatSpec<Args...> &format, const Args &...args) {
|
static void logf(const absl::FormatSpec<Args...> &format, const Args &...args) {
|
||||||
@@ -29,8 +34,20 @@ static void logf(const absl::FormatSpec<Args...> &format, const Args &...args) {
|
|||||||
if (core::FeatureFlags::get().kLogToConsole) {
|
if (core::FeatureFlags::get().kLogToConsole) {
|
||||||
std::cout << message;
|
std::cout << message;
|
||||||
}
|
}
|
||||||
static std::ofstream fout(kLogFileOut, std::ios::out | std::ios::app);
|
|
||||||
|
// Use the configurable log file path
|
||||||
|
static std::ofstream fout;
|
||||||
|
static std::string last_log_path = "";
|
||||||
|
|
||||||
|
// Reopen file if path changed
|
||||||
|
if (g_log_file_path != last_log_path) {
|
||||||
|
fout.close();
|
||||||
|
fout.open(g_log_file_path, std::ios::out | std::ios::app);
|
||||||
|
last_log_path = g_log_file_path;
|
||||||
|
}
|
||||||
|
|
||||||
fout << message;
|
fout << message;
|
||||||
|
fout.flush(); // Ensure immediate write for debugging
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace util
|
} // namespace util
|
||||||
|
|||||||
Reference in New Issue
Block a user