use eframe::egui::{self, Color32, Rounding, Stroke};
#[derive(Debug, Clone)]
pub struct AccNetTheme {
pub canvas_bg: Color32,
pub grid_color: Color32,
pub panel_bg: Color32,
pub text_primary: Color32,
pub text_secondary: Color32,
pub accent: Color32,
pub asset_color: Color32,
pub liability_color: Color32,
pub equity_color: Color32,
pub revenue_color: Color32,
pub expense_color: Color32,
pub contra_color: Color32,
pub alert_low: Color32,
pub alert_medium: Color32,
pub alert_high: Color32,
pub alert_critical: Color32,
pub flow_normal: Color32,
pub flow_suspicious: Color32,
pub flow_fraud: Color32,
pub node_radius: f32,
pub edge_width: f32,
pub panel_rounding: Rounding,
}
impl Default for AccNetTheme {
fn default() -> Self {
Self::dark()
}
}
impl AccNetTheme {
pub fn dark() -> Self {
Self {
canvas_bg: Color32::from_rgb(18, 18, 24),
grid_color: Color32::from_rgb(40, 40, 50),
panel_bg: Color32::from_rgb(28, 28, 36),
text_primary: Color32::from_rgb(240, 240, 245),
text_secondary: Color32::from_rgb(140, 140, 155),
accent: Color32::from_rgb(100, 180, 255),
asset_color: Color32::from_rgb(66, 165, 245), liability_color: Color32::from_rgb(239, 83, 80), equity_color: Color32::from_rgb(102, 187, 106), revenue_color: Color32::from_rgb(255, 202, 40), expense_color: Color32::from_rgb(171, 71, 188), contra_color: Color32::from_rgb(158, 158, 158),
alert_low: Color32::from_rgb(100, 180, 255),
alert_medium: Color32::from_rgb(255, 202, 40),
alert_high: Color32::from_rgb(255, 152, 0),
alert_critical: Color32::from_rgb(244, 67, 54),
flow_normal: Color32::from_rgb(100, 255, 180),
flow_suspicious: Color32::from_rgb(255, 202, 40),
flow_fraud: Color32::from_rgb(244, 67, 54),
node_radius: 20.0,
edge_width: 2.0,
panel_rounding: Rounding::same(8.0),
}
}
pub fn light() -> Self {
Self {
canvas_bg: Color32::from_rgb(245, 245, 250),
grid_color: Color32::from_rgb(220, 220, 230),
panel_bg: Color32::from_rgb(255, 255, 255),
text_primary: Color32::from_rgb(30, 30, 40),
text_secondary: Color32::from_rgb(100, 100, 115),
accent: Color32::from_rgb(25, 118, 210),
asset_color: Color32::from_rgb(25, 118, 210),
liability_color: Color32::from_rgb(211, 47, 47),
equity_color: Color32::from_rgb(56, 142, 60),
revenue_color: Color32::from_rgb(255, 160, 0),
expense_color: Color32::from_rgb(142, 36, 170),
contra_color: Color32::from_rgb(117, 117, 117),
alert_low: Color32::from_rgb(25, 118, 210),
alert_medium: Color32::from_rgb(255, 160, 0),
alert_high: Color32::from_rgb(230, 81, 0),
alert_critical: Color32::from_rgb(198, 40, 40),
flow_normal: Color32::from_rgb(0, 150, 136),
flow_suspicious: Color32::from_rgb(255, 160, 0),
flow_fraud: Color32::from_rgb(198, 40, 40),
node_radius: 20.0,
edge_width: 2.0,
panel_rounding: Rounding::same(8.0),
}
}
pub fn account_color(&self, account_type: crate::models::AccountType) -> Color32 {
use crate::models::AccountType;
match account_type {
AccountType::Asset => self.asset_color,
AccountType::Liability => self.liability_color,
AccountType::Equity => self.equity_color,
AccountType::Revenue => self.revenue_color,
AccountType::Expense => self.expense_color,
AccountType::Contra => self.contra_color,
}
}
pub fn edge_stroke(&self, suspicious: bool, fraud: bool) -> Stroke {
let color = if fraud {
self.flow_fraud
} else if suspicious {
self.flow_suspicious
} else {
self.flow_normal
};
Stroke::new(self.edge_width, color)
}
pub fn edge_color_futuristic(
&self,
weight: f32,
suspicious: bool,
fraud: bool,
max_weight: f32,
) -> Color32 {
let normalized = (weight / max_weight.max(1.0)).min(1.0);
let base = if fraud {
self.flow_fraud
} else if suspicious {
self.flow_suspicious
} else {
let t = normalized;
Color32::from_rgb(
(60.0 + t * 40.0) as u8, (180.0 + t * 40.0) as u8, (220.0 + t * 35.0) as u8, )
};
let alpha = (40.0 + normalized * 120.0) as u8;
Color32::from_rgba_unmultiplied(base.r(), base.g(), base.b(), alpha)
}
pub fn apply(&self, ctx: &egui::Context) {
let mut style = (*ctx.style()).clone();
style.visuals.dark_mode = self.canvas_bg.r() < 128;
style.visuals.panel_fill = self.panel_bg;
style.visuals.window_fill = self.panel_bg;
style.visuals.widgets.noninteractive.fg_stroke.color = self.text_primary;
style.visuals.widgets.inactive.fg_stroke.color = self.text_secondary;
style.visuals.selection.bg_fill = self.accent;
style.visuals.window_rounding = self.panel_rounding;
ctx.set_style(style);
}
}