use eframe::egui::Color32;
use std::path::PathBuf;
use std::fs;
use std::collections::HashMap;
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
pub struct LogColors {
#[serde(with = "color32_serde")]
pub info_level: Color32,
#[serde(with = "color32_serde")]
pub warning_level: Color32,
#[serde(with = "color32_serde")]
pub error_level: Color32,
#[serde(with = "color32_serde")]
pub debug_level: Color32,
#[serde(with = "color32_serde")]
pub info_message: Color32,
#[serde(with = "color32_serde")]
pub warning_message: Color32,
#[serde(with = "color32_serde")]
pub error_message: Color32,
#[serde(with = "color32_serde")]
pub debug_message: Color32,
#[serde(with = "color32_serde")]
pub info: Color32,
#[serde(with = "color32_serde")]
pub warning: Color32,
#[serde(with = "color32_serde")]
pub error: Color32,
#[serde(with = "color32_serde")]
pub debug: Color32,
#[serde(with = "color32_serde")]
pub timestamp: Color32,
#[serde(with = "color32_serde")]
pub system: Color32,
#[serde(with = "color32_serde")]
pub user_action: Color32,
#[serde(with = "color32_serde")]
pub config: Color32,
#[serde(with = "color32_serde")]
pub status: Color32,
#[serde(with = "color32_serde")]
pub progress: Color32,
#[serde(with = "color32_serde")]
pub success: Color32,
#[serde(with = "color32_serde")]
pub default: Color32,
#[serde(default)]
pub custom_colors: HashMap<String, Color32Wrapper>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
pub struct Color32Wrapper {
#[serde(with = "color32_serde")]
pub level_color: Color32,
#[serde(with = "color32_serde")]
pub message_color: Color32,
}
impl Default for Color32Wrapper {
fn default() -> Self {
Self {
level_color: Color32::from_rgb(200, 200, 200), message_color: Color32::from_rgb(255, 255, 255), }
}
}
impl Default for LogColors {
fn default() -> Self {
let mut custom_colors = HashMap::new();
custom_colors.insert("custom1".to_string(), Color32Wrapper {
level_color: Color32::from_rgb(255, 200, 200), message_color: Color32::from_rgb(255, 220, 220) });
custom_colors.insert("custom2".to_string(), Color32Wrapper {
level_color: Color32::from_rgb(200, 255, 200), message_color: Color32::from_rgb(220, 255, 220) });
custom_colors.insert("custom3".to_string(), Color32Wrapper {
level_color: Color32::from_rgb(200, 200, 255), message_color: Color32::from_rgb(220, 220, 255) });
let info_level = Color32::from_rgb(150, 255, 150); let warning_level = Color32::from_rgb(255, 255, 100); let error_level = Color32::from_rgb(255, 100, 100); let debug_level = Color32::from_rgb(150, 150, 255);
let info_message = Color32::from_rgb(180, 255, 180); let warning_message = Color32::from_rgb(255, 255, 140); let error_message = Color32::from_rgb(255, 140, 140); let debug_message = Color32::from_rgb(180, 180, 255);
Self {
info_level,
warning_level,
error_level,
debug_level,
info_message,
warning_message,
error_message,
debug_message,
info: info_level,
warning: warning_level,
error: error_level,
debug: debug_level,
timestamp: Color32::from_rgb(180, 180, 180), system: Color32::from_rgb(100, 200, 255), user_action: Color32::from_rgb(255, 180, 100), config: Color32::from_rgb(200, 150, 255), status: Color32::from_rgb(200, 200, 200), progress: Color32::from_rgb(100, 255, 200), success: Color32::from_rgb(100, 255, 100), default: Color32::from_rgb(255, 255, 255),
custom_colors,
}
}
}
impl LogColors {
pub fn get_custom_color_level(&self, identifier: &str) -> Color32 {
if let Some(wrapper) = self.custom_colors.get(identifier) {
wrapper.level_color
} else {
self.default
}
}
pub fn get_custom_color_message(&self, identifier: &str) -> Color32 {
if let Some(wrapper) = self.custom_colors.get(identifier) {
wrapper.message_color
} else {
self.default
}
}
pub fn get_custom_color(&self, identifier: &str) -> Color32 {
self.get_custom_color_level(identifier)
}
pub fn set_custom_color(&mut self, identifier: &str, color: Color32) {
self.custom_colors.insert(identifier.to_string(), Color32Wrapper {
level_color: color,
message_color: color
});
}
pub fn set_custom_colors(&mut self, identifier: &str, level_color: Color32, message_color: Color32) {
self.custom_colors.insert(identifier.to_string(), Color32Wrapper {
level_color,
message_color
});
}
}
pub mod color32_serde {
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use eframe::egui::Color32;
pub fn serialize<S>(color: &Color32, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let rgba = [color.r(), color.g(), color.b(), color.a()];
rgba.serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Color32, D::Error>
where
D: Deserializer<'de>,
{
let rgba = <[u8; 4]>::deserialize(deserializer)?;
Ok(Color32::from_rgba_unmultiplied(rgba[0], rgba[1], rgba[2], rgba[3]))
}
}
impl LogColors {
#[allow(dead_code)]
pub fn load() -> Self {
let config_dir = dirs::config_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join("egui_mobius_template");
let config_path = config_dir.join("log_colors.json");
println!("Loading colors from: {}", config_path.display());
match fs::read_to_string(&config_path) {
Ok(file_content) => {
match serde_json::from_str(&file_content) {
Ok(colors) => {
println!("Successfully loaded colors from file");
colors
}
Err(e) => {
eprintln!("Failed to parse colors JSON: {}", e);
Self::default()
}
}
}
Err(e) => {
eprintln!("Failed to read colors file: {}", e);
Self::default()
}
}
}
#[allow(dead_code)]
pub fn save(&self) {
let colors = self.clone();
std::thread::spawn(move || {
let config_dir = dirs::config_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join("egui_mobius_template");
if let Err(e) = fs::create_dir_all(&config_dir) {
eprintln!("Failed to create config directory: {}", e);
return;
}
let config_path = config_dir.join("log_colors.json");
match serde_json::to_string_pretty(&colors) {
Ok(json) => {
if let Err(e) = fs::write(&config_path, json) {
eprintln!("Failed to write colors to {}: {}", config_path.display(), e);
} else {
println!("Successfully saved colors to {}", config_path.display());
}
},
Err(e) => eprintln!("Failed to serialize colors: {}", e),
}
});
}
}