use crate::icons::{Icon, icons as embedded_icons};
use egui::{Pos2, Rect, Sense, Ui, Vec2};
use super::actions::ChartControlAction;
use super::config::ChartControlBarConfig;
use crate::tokens::DESIGN_TOKENS;
#[derive(Clone, Debug, Default)]
pub struct ChartControlBarState {
pub auto_scale: bool,
pub log_scale: bool,
pub percentage: bool,
}
pub struct ChartControlBar {
pub config: ChartControlBarConfig,
pub state: ChartControlBarState,
}
impl Default for ChartControlBar {
fn default() -> Self {
Self::new()
}
}
impl ChartControlBar {
pub fn new() -> Self {
Self {
config: ChartControlBarConfig::default(),
state: ChartControlBarState::default(),
}
}
pub fn with_config(mut self, config: ChartControlBarConfig) -> Self {
self.config = config;
self
}
pub fn sync_from(&mut self, auto_scale: bool, log_scale: bool, percentage: bool) {
self.state.auto_scale = auto_scale;
self.state.log_scale = log_scale;
self.state.percentage = percentage;
}
pub fn show(&mut self, ui: &mut Ui, chart_rect: Rect) -> ChartControlAction {
let mut action = ChartControlAction::None;
let bg_color = ui.style().visuals.window_fill;
let border_stroke = ui.style().visuals.window_stroke;
let icon_color = ui.style().visuals.widgets.inactive.fg_stroke.color;
let icon_hover_color = ui.style().visuals.widgets.hovered.fg_stroke.color;
let active_color = ui.style().visuals.selection.bg_fill;
let btn_hover_bg = ui.style().visuals.widgets.hovered.bg_fill;
let sep_stroke = ui.style().visuals.widgets.noninteractive.bg_stroke;
let num_buttons = 6;
let separator_width = DESIGN_TOKENS.spacing.sm;
let bar_width = (self.config.button_size * num_buttons as f32)
+ (self.config.button_gap * (num_buttons - 1) as f32)
+ separator_width
+ (self.config.padding * 2.0);
let bar_height = self.config.button_size + (self.config.padding * 2.0);
let bar_pos = Pos2::new(
chart_rect.max.x - bar_width - self.config.offset_x,
chart_rect.max.y - bar_height - self.config.offset_y,
);
let bar_rect = Rect::from_min_size(bar_pos, Vec2::new(bar_width, bar_height));
let bar_response = ui.allocate_rect(bar_rect, Sense::hover());
if ui.is_rect_visible(bar_rect) {
ui.painter().rect(
bar_rect,
self.config.rounding,
bg_color,
border_stroke,
egui::StrokeKind::Inside,
);
let buttons: [(&Icon, &str, ChartControlAction, bool); 3] = [
(
&embedded_icons::ZOOM_IN,
"Zoom In",
ChartControlAction::ZoomIn,
false,
),
(
&embedded_icons::ZOOM_OUT,
"Zoom Out",
ChartControlAction::ZoomOut,
false,
),
(
&embedded_icons::MEASURE,
"Reset Zoom",
ChartControlAction::ResetZoom,
false,
),
];
let toggles: [(&Icon, &str, ChartControlAction, bool); 3] = [
(
&embedded_icons::SETTINGS,
"Auto Scale",
ChartControlAction::ToggleAutoScale,
self.state.auto_scale,
),
(
&embedded_icons::LAYOUT_GRID,
"Log Scale",
ChartControlAction::ToggleLogScale,
self.state.log_scale,
),
(
&embedded_icons::TEXT,
"Percentage",
ChartControlAction::TogglePercentage,
self.state.percentage,
),
];
let mut x = bar_rect.min.x + self.config.padding;
let center_y = bar_rect.center().y;
let icon_size = 16.0;
for (icon, tooltip, btn_action, is_active) in buttons {
let btn_rect = Rect::from_center_size(
Pos2::new(x + self.config.button_size / 2.0, center_y),
Vec2::splat(self.config.button_size),
);
let btn_response = ui.allocate_rect(btn_rect, Sense::click());
let is_hovered = btn_response.hovered();
let was_clicked = btn_response.clicked();
if is_hovered {
ui.painter()
.rect_filled(btn_rect, DESIGN_TOKENS.rounding.button, btn_hover_bg);
}
if is_active {
ui.painter()
.rect_filled(btn_rect, DESIGN_TOKENS.rounding.button, active_color);
}
let icon_rect = Rect::from_center_size(btn_rect.center(), Vec2::splat(icon_size));
let icon_color_final = if is_hovered {
icon_hover_color
} else {
icon_color
};
icon.as_image_tinted(Vec2::splat(icon_size), icon_color_final)
.paint_at(ui, icon_rect);
btn_response.on_hover_text(tooltip);
if was_clicked {
action = btn_action;
}
x += self.config.button_size + self.config.button_gap;
}
x += separator_width / 2.0;
let sep_y1 = bar_rect.min.y + self.config.padding;
let sep_y2 = bar_rect.max.y - self.config.padding;
ui.painter()
.line_segment([Pos2::new(x, sep_y1), Pos2::new(x, sep_y2)], sep_stroke);
x += separator_width / 2.0 + self.config.button_gap;
for (icon, tooltip, btn_action, is_active) in toggles {
let btn_rect = Rect::from_center_size(
Pos2::new(x + self.config.button_size / 2.0, center_y),
Vec2::splat(self.config.button_size),
);
let btn_response = ui.allocate_rect(btn_rect, Sense::click());
let is_hovered = btn_response.hovered();
let was_clicked = btn_response.clicked();
if is_active {
ui.painter()
.rect_filled(btn_rect, DESIGN_TOKENS.rounding.button, active_color);
} else if is_hovered {
ui.painter()
.rect_filled(btn_rect, DESIGN_TOKENS.rounding.button, btn_hover_bg);
}
let icon_rect = Rect::from_center_size(btn_rect.center(), Vec2::splat(icon_size));
let icon_color_final = if is_active || is_hovered {
icon_hover_color
} else {
icon_color
};
icon.as_image_tinted(Vec2::splat(icon_size), icon_color_final)
.paint_at(ui, icon_rect);
btn_response.on_hover_text(tooltip);
if was_clicked {
action = btn_action.clone();
match btn_action {
ChartControlAction::ToggleAutoScale => {
self.state.auto_scale = !self.state.auto_scale;
}
ChartControlAction::ToggleLogScale => {
self.state.log_scale = !self.state.log_scale;
}
ChartControlAction::TogglePercentage => {
self.state.percentage = !self.state.percentage;
}
_ => {}
}
}
x += self.config.button_size + self.config.button_gap;
}
}
if bar_response.hovered() {
ui.ctx().set_cursor_icon(egui::CursorIcon::Default);
}
action
}
}