use super::{config::ChartSettings, types::ScaleMode};
use crate::config::ChartConfig;
use crate::ext::UiExt;
use crate::model::ChartType;
use crate::styles::typography;
use crate::tokens::DESIGN_TOKENS;
use egui::{Align2, Context, Id, Pos2, Rect, Response, Ui};
pub struct SettingsMenu {
pub settings: ChartSettings,
pub is_open: bool,
btn_rect: Option<Rect>,
}
impl SettingsMenu {
pub fn new() -> Self {
Self {
settings: ChartSettings::default(),
is_open: false,
btn_rect: None,
}
}
pub fn settings(&self) -> &ChartSettings {
&self.settings
}
pub fn settings_mut(&mut self) -> &mut ChartSettings {
&mut self.settings
}
pub fn apply_to_config(&self, mut config: ChartConfig) -> ChartConfig {
config.show_grid = self.settings.show_horizontal_grid;
config.show_horizontal_grid = self.settings.show_horizontal_grid;
config.show_vertical_grid = self.settings.show_vertical_grid;
config.show_right_axis = self.settings.show_right_axis;
config.show_left_axis = self.settings.show_left_axis;
config.show_symbol_labels = self.settings.show_symbol_labels;
config.show_symbol_last_val = self.settings.show_symbol_last_val;
config.show_symbol_prev_close = self.settings.show_symbol_prev_close;
config.show_indicator_labels = self.settings.show_indicator_labels;
config.show_indicator_last_val = self.settings.show_indicator_last_val;
config.show_countdown = self.settings.show_countdown;
config
}
pub fn chart_type(&self) -> ChartType {
self.settings.chart_type
}
pub fn show_btn(&mut self, ui: &mut Ui, chart_rect: Rect) -> Response {
let btn_size = egui::vec2(32.0, 32.0);
let btn_pos = Pos2::new(
chart_rect.max.x - btn_size.x - 10.0,
chart_rect.max.y - btn_size.y - 10.0,
);
let btn_rect = Rect::from_min_size(btn_pos, btn_size);
let btn_id = ui.id().with("settings_btn");
let response = ui.interact(btn_rect, btn_id, egui::Sense::click());
let visuals = ui.style().interact(&response);
let bg_color = if response.hovered() {
visuals.bg_fill
} else {
ui.visuals().widgets.inactive.bg_fill
};
ui.painter()
.rect_filled(btn_rect, DESIGN_TOKENS.rounding.md, bg_color);
ui.painter().text(
btn_rect.center(),
Align2::CENTER_CENTER,
"S",
egui::FontId::proportional(typography::TITLE),
visuals.text_color(),
);
if response.clicked() {
self.is_open = !self.is_open;
}
self.btn_rect = Some(btn_rect);
response
}
pub fn show_menu(&mut self, ctx: &Context) {
if !self.is_open {
return;
}
let Some(btn_rect) = self.btn_rect else {
return;
};
let popup_id = Id::new("settings_popup");
let popup_pos = Pos2::new(btn_rect.min.x - 200.0, btn_rect.min.y - 400.0);
egui::Area::new(popup_id)
.fixed_pos(popup_pos)
.order(egui::Order::Foreground)
.show(ctx, |ui| {
egui::Frame::popup(ui.style()).show(ui, |ui| {
ui.set_min_width(180.0);
ui.set_max_width(220.0);
self.draw_menu_sections(ui);
});
});
self.handle_click_outside(ctx, btn_rect, popup_pos);
}
fn draw_menu_sections(&mut self, ui: &mut egui::Ui) {
self.draw_chart_type_section(ui);
self.draw_appearance_section(ui);
self.draw_scale_section(ui);
self.draw_labels_section(ui);
ui.separator();
if ui.button("Scales Properties...").clicked() {
self.is_open = false;
}
}
fn draw_chart_type_section(&mut self, ui: &mut egui::Ui) {
ui.strong_label("Chart Type");
ui.separator();
for chart_type in &[
ChartType::Candles,
ChartType::Bars,
ChartType::Line,
ChartType::Area,
] {
if ui
.selectable_label(self.settings.chart_type == *chart_type, chart_type.as_str())
.clicked()
{
self.settings.chart_type = *chart_type;
}
}
ui.separator();
}
fn draw_appearance_section(&mut self, ui: &mut egui::Ui) {
ui.strong_label("Appearance");
ui.separator();
ui.checkbox(&mut self.settings.show_horizontal_grid, "Horizontal Grid");
ui.checkbox(&mut self.settings.show_vertical_grid, "Vertical Grid");
ui.separator();
}
fn draw_scale_section(&mut self, ui: &mut egui::Ui) {
ui.strong_label("Scale");
ui.separator();
if ui.button("Reset Scale").clicked() {
self.is_open = false;
}
ui.checkbox(&mut self.settings.show_left_axis, "Left Axis");
ui.checkbox(&mut self.settings.show_right_axis, "Right Axis");
if ui
.selectable_label(self.settings.scale_mode == ScaleMode::Auto, "Auto Scale")
.clicked()
{
self.settings.scale_mode = ScaleMode::Auto;
}
ui.checkbox(&mut self.settings.lock_scale, "Lock Scale");
ui.checkbox(
&mut self.settings.scale_price_chart_only,
"Scale Price Chart Only",
);
ui.separator();
if ui
.selectable_label(
self.settings.scale_mode == ScaleMode::Percentage,
"Percentage",
)
.clicked()
{
self.settings.scale_mode = ScaleMode::Percentage;
}
if ui
.selectable_label(
self.settings.scale_mode == ScaleMode::Logarithmic,
"Log Scale",
)
.clicked()
{
self.settings.scale_mode = ScaleMode::Logarithmic;
}
ui.separator();
}
fn draw_labels_section(&mut self, ui: &mut egui::Ui) {
ui.strong_label("Labels");
ui.separator();
ui.checkbox(&mut self.settings.show_symbol_labels, "Symbol Labels");
ui.checkbox(&mut self.settings.show_symbol_last_val, "Symbol Last Value");
ui.checkbox(
&mut self.settings.show_symbol_prev_close,
"Symbol Prev. Close Value",
);
ui.separator();
ui.checkbox(&mut self.settings.show_indicator_labels, "Indicator Labels");
ui.checkbox(
&mut self.settings.show_indicator_last_val,
"Indicator Last Value",
);
ui.separator();
ui.checkbox(&mut self.settings.show_countdown, "Countdown");
ui.checkbox(
&mut self.settings.no_overlapping_labels,
"No Overlapping Labels",
);
}
fn handle_click_outside(&mut self, ctx: &Context, btn_rect: Rect, popup_pos: Pos2) {
if ctx.input(|i| i.pointer.any_released())
&& let Some(pos) = ctx.input(|i| i.pointer.interact_pos())
{
let menu_rect = Rect::from_min_size(
popup_pos,
egui::vec2(
DESIGN_TOKENS.sizing.dialog.menu_settings_width,
DESIGN_TOKENS.sizing.dialog.menu_settings_height,
),
);
if !menu_rect.contains(pos) && !btn_rect.contains(pos) {
self.is_open = false;
}
}
}
}
impl Default for SettingsMenu {
fn default() -> Self {
Self::new()
}
}