use egui::{Color32, Context, Ui};
use crate::tokens::DESIGN_TOKENS;
#[derive(Clone, Debug)]
pub struct ThemeData {
pub ui_panel_bg: Color32,
pub ui_panel_bg_secondary: Color32,
pub ui_button_bg: Color32,
pub ui_button_bg_hover: Color32,
pub ui_button_bg_active: Color32,
pub ui_text: Color32,
pub ui_text_secondary: Color32,
pub ui_text_muted: Color32,
pub ui_icon: Color32,
pub ui_icon_hover: Color32,
pub ui_icon_active: Color32,
pub ui_border: Color32,
pub ui_border_subtle: Color32,
pub ui_accent: Color32,
pub ui_accent_hover: Color32,
pub page_bg: Color32,
pub chart_bg: Color32,
pub chart_bg_axis: Color32,
pub chart_grid: Color32,
pub chart_grid_major: Color32,
pub chart_text: Color32,
pub chart_text_secondary: Color32,
pub chart_crosshair: Color32,
pub chart_crosshair_label_bg: Color32,
pub chart_crosshair_label_text: Color32,
pub bullish: Color32,
pub bearish: Color32,
pub volume_bullish: Color32,
pub volume_bearish: Color32,
pub footprint_poc: Color32,
pub footprint_value_area: Color32,
pub footprint_imbalance_buy: Color32,
pub footprint_imbalance_sell: Color32,
pub tpo_poc: Color32,
pub tpo_value_area: Color32,
pub tpo_initial_balance: Color32,
pub tpo_single_print: Color32,
pub tpo_session_separator: Color32,
pub tpo_letter_default: Color32,
pub warning: Color32,
pub success: Color32,
pub is_dark_ui: bool,
pub is_dark_chart: bool,
}
impl Default for ThemeData {
fn default() -> Self {
let tokens = &DESIGN_TOKENS.semantic;
Self {
ui_panel_bg: tokens.ui.panel_bg_light,
ui_panel_bg_secondary: tokens.ui.panel_bg_secondary_light,
ui_button_bg: tokens.ui.btn_bg_light,
ui_button_bg_hover: tokens.ui.btn_bg_hover_light,
ui_button_bg_active: tokens.ui.btn_bg_active_light,
ui_text: tokens.ui.text_light,
ui_text_secondary: tokens.ui.text_secondary_light,
ui_text_muted: tokens.ui.text_muted_light,
ui_icon: tokens.ui.icon_light,
ui_icon_hover: tokens.ui.icon_hover_light,
ui_icon_active: tokens.ui.icon_active,
ui_border: tokens.ui.border_light,
ui_border_subtle: tokens.ui.border_subtle_light,
ui_accent: tokens.ui.accent,
ui_accent_hover: tokens.ui.accent_hover,
page_bg: tokens.chart.bg,
chart_bg: tokens.chart.bg,
chart_bg_axis: tokens.chart.bg_axis,
chart_grid: tokens.chart.grid_line,
chart_grid_major: tokens.chart.grid_line_major,
chart_text: tokens.chart.axis_text,
chart_text_secondary: tokens.chart.axis_text_secondary,
chart_crosshair: tokens.chart.crosshair_line,
chart_crosshair_label_bg: tokens.chart.crosshair_label_bg,
chart_crosshair_label_text: tokens.chart.crosshair_label_text,
bullish: tokens.chart.bullish,
bearish: tokens.chart.bearish,
volume_bullish: {
let [r, g, b, _] = tokens.chart.bullish.to_array();
Color32::from_rgba_unmultiplied(r, g, b, tokens.chart.volume_up_alpha)
},
volume_bearish: {
let [r, g, b, _] = tokens.chart.bearish.to_array();
Color32::from_rgba_unmultiplied(r, g, b, tokens.chart.volume_down_alpha)
},
footprint_poc: tokens.footprint.poc,
footprint_value_area: tokens.footprint.value_area,
footprint_imbalance_buy: tokens.footprint.imbalance_buy,
footprint_imbalance_sell: tokens.footprint.imbalance_sell,
tpo_poc: tokens.tpo.poc,
tpo_value_area: tokens.tpo.value_area,
tpo_initial_balance: tokens.tpo.initial_balance,
tpo_single_print: tokens.tpo.single_print,
tpo_session_separator: tokens.tpo.session_separator,
tpo_letter_default: tokens.tpo.letter_default,
warning: tokens.ui.warning,
success: tokens.ui.success,
is_dark_ui: false,
is_dark_chart: true,
}
}
}
impl ThemeData {
pub fn from_theme(theme: &super::Theme) -> Self {
Self {
ui_panel_bg: theme.semantic.ui.panel_bg,
ui_panel_bg_secondary: theme.semantic.ui.panel_bg_secondary,
ui_button_bg: theme.semantic.ui.btn_bg,
ui_button_bg_hover: theme.semantic.ui.btn_bg_hover,
ui_button_bg_active: theme.semantic.ui.btn_bg_active,
ui_text: theme.semantic.ui.text,
ui_text_secondary: theme.semantic.ui.text_secondary,
ui_text_muted: theme.semantic.ui.text_muted,
ui_icon: theme.semantic.ui.icon,
ui_icon_hover: theme.semantic.ui.icon_hover,
ui_icon_active: theme.semantic.ui.icon_active,
ui_border: theme.semantic.ui.border,
ui_border_subtle: theme.semantic.ui.border_subtle,
ui_accent: theme.semantic.ui.accent,
ui_accent_hover: theme.semantic.ui.accent_hover,
page_bg: if theme.preset.is_dark_ui() {
theme.semantic.ui.panel_bg_secondary
} else {
theme.semantic.chart.bg
},
chart_bg: theme.semantic.chart.bg,
chart_bg_axis: theme.semantic.chart.bg_axis,
chart_grid: theme.semantic.chart.grid_line,
chart_grid_major: theme.semantic.chart.grid_line_major,
chart_text: theme.semantic.chart.axis_text,
chart_text_secondary: theme.semantic.chart.axis_text_secondary,
chart_crosshair: theme.semantic.chart.crosshair_line,
chart_crosshair_label_bg: theme.semantic.chart.crosshair_label_bg,
chart_crosshair_label_text: theme.semantic.chart.crosshair_label_text,
bullish: theme.semantic.chart.candle_up,
bearish: theme.semantic.chart.candle_down,
volume_bullish: theme.semantic.chart.volume_up,
volume_bearish: theme.semantic.chart.volume_down,
footprint_poc: DESIGN_TOKENS.semantic.footprint.poc,
footprint_value_area: DESIGN_TOKENS.semantic.footprint.value_area,
footprint_imbalance_buy: DESIGN_TOKENS.semantic.footprint.imbalance_buy,
footprint_imbalance_sell: DESIGN_TOKENS.semantic.footprint.imbalance_sell,
tpo_poc: DESIGN_TOKENS.semantic.tpo.poc,
tpo_value_area: DESIGN_TOKENS.semantic.tpo.value_area,
tpo_initial_balance: DESIGN_TOKENS.semantic.tpo.initial_balance,
tpo_single_print: DESIGN_TOKENS.semantic.tpo.single_print,
tpo_session_separator: DESIGN_TOKENS.semantic.tpo.session_separator,
tpo_letter_default: DESIGN_TOKENS.semantic.tpo.letter_default,
warning: theme.semantic.ui.warning,
success: theme.semantic.ui.success,
is_dark_ui: theme.preset.is_dark_ui(),
is_dark_chart: theme.preset.is_dark_chart(),
}
}
#[inline]
pub fn bullish_alpha(&self, alpha: u8) -> Color32 {
let [r, g, b, _] = self.bullish.to_array();
Color32::from_rgba_unmultiplied(r, g, b, alpha)
}
#[inline]
pub fn bearish_alpha(&self, alpha: u8) -> Color32 {
let [r, g, b, _] = self.bearish.to_array();
Color32::from_rgba_unmultiplied(r, g, b, alpha)
}
}
pub trait ThemeContextExt {
fn set_theme_data(&self, data: ThemeData);
fn theme_data(&self) -> ThemeData;
fn is_dark_ui(&self) -> bool;
fn is_dark_chart(&self) -> bool;
}
impl ThemeContextExt for Context {
fn set_theme_data(&self, data: ThemeData) {
self.data_mut(|d| d.insert_temp(egui::Id::NULL.with("theme_data"), data));
}
fn theme_data(&self) -> ThemeData {
self.data(|d| d.get_temp::<ThemeData>(egui::Id::NULL.with("theme_data")))
.unwrap_or_default()
}
fn is_dark_ui(&self) -> bool {
self.theme_data().is_dark_ui
}
fn is_dark_chart(&self) -> bool {
self.theme_data().is_dark_chart
}
}
pub trait ThemeUiExt {
fn theme_panel_bg(&self) -> Color32;
fn theme_hover_bg(&self) -> Color32;
fn theme_active_bg(&self) -> Color32;
fn theme_text(&self) -> Color32;
fn theme_text_secondary(&self) -> Color32;
fn theme_icon(&self) -> Color32;
fn theme_icon_hover(&self) -> Color32;
fn theme_icon_active(&self) -> Color32;
fn theme_border(&self) -> Color32;
fn theme_accent(&self) -> Color32;
fn theme_chart_bg(&self) -> Color32;
fn theme_chart_grid(&self) -> Color32;
fn theme_chart_text(&self) -> Color32;
fn theme_bullish(&self) -> Color32;
fn theme_bearish(&self) -> Color32;
fn theme_warning(&self) -> Color32;
fn theme_success(&self) -> Color32;
fn theme_poc_highlight(&self) -> Color32;
fn theme_value_area_fill(&self) -> Color32;
fn theme_imbalance_buy(&self) -> Color32;
fn theme_imbalance_sell(&self) -> Color32;
fn theme_data(&self) -> ThemeData;
}
impl ThemeUiExt for Ui {
#[inline]
fn theme_panel_bg(&self) -> Color32 {
self.style().visuals.panel_fill
}
#[inline]
fn theme_hover_bg(&self) -> Color32 {
self.style().visuals.widgets.hovered.bg_fill
}
#[inline]
fn theme_active_bg(&self) -> Color32 {
self.style().visuals.widgets.active.bg_fill
}
#[inline]
fn theme_text(&self) -> Color32 {
self.style().visuals.widgets.active.fg_stroke.color
}
#[inline]
fn theme_text_secondary(&self) -> Color32 {
self.style().visuals.widgets.noninteractive.fg_stroke.color
}
#[inline]
fn theme_icon(&self) -> Color32 {
self.style().visuals.widgets.noninteractive.fg_stroke.color
}
#[inline]
fn theme_icon_hover(&self) -> Color32 {
self.style().visuals.widgets.hovered.fg_stroke.color
}
#[inline]
fn theme_icon_active(&self) -> Color32 {
self.style().visuals.selection.bg_fill
}
#[inline]
fn theme_border(&self) -> Color32 {
self.style().visuals.widgets.noninteractive.bg_stroke.color
}
#[inline]
fn theme_accent(&self) -> Color32 {
self.style().visuals.selection.bg_fill
}
#[inline]
fn theme_chart_bg(&self) -> Color32 {
self.ctx().theme_data().chart_bg
}
#[inline]
fn theme_chart_grid(&self) -> Color32 {
self.ctx().theme_data().chart_grid
}
#[inline]
fn theme_chart_text(&self) -> Color32 {
self.ctx().theme_data().chart_text
}
#[inline]
fn theme_bullish(&self) -> Color32 {
self.ctx().theme_data().bullish
}
#[inline]
fn theme_bearish(&self) -> Color32 {
self.ctx().theme_data().bearish
}
#[inline]
fn theme_warning(&self) -> Color32 {
self.ctx().theme_data().warning
}
#[inline]
fn theme_success(&self) -> Color32 {
self.ctx().theme_data().success
}
#[inline]
fn theme_poc_highlight(&self) -> Color32 {
self.ctx().theme_data().footprint_poc
}
#[inline]
fn theme_value_area_fill(&self) -> Color32 {
self.ctx().theme_data().footprint_value_area
}
#[inline]
fn theme_imbalance_buy(&self) -> Color32 {
self.ctx().theme_data().footprint_imbalance_buy
}
#[inline]
fn theme_imbalance_sell(&self) -> Color32 {
self.ctx().theme_data().footprint_imbalance_sell
}
fn theme_data(&self) -> ThemeData {
self.ctx().theme_data()
}
}
pub fn get_icon_color(ctx: &Context) -> Color32 {
let tokens = &DESIGN_TOKENS.semantic.ui;
if ctx.is_dark_ui() {
tokens.icon_dark
} else {
tokens.icon_light
}
}
pub fn get_icon_hover_color(ctx: &Context) -> Color32 {
let tokens = &DESIGN_TOKENS.semantic.ui;
if ctx.is_dark_ui() {
tokens.icon_hover_dark
} else {
tokens.icon_hover_light
}
}
pub fn get_icon_active_color() -> Color32 {
DESIGN_TOKENS.semantic.ui.icon_active
}