use crate::icons::Icon;
use crate::theming;
use egui::{Color32, Pos2, Rect, Sense, Stroke, Ui, Vec2};
use super::svg_helpers::render_svg_at_rect_themed;
use crate::tokens::DESIGN_TOKENS;
#[derive(Debug, Clone)]
pub struct PairButtonResponse {
pub icon_clicked: bool,
pub arrow_clicked: bool,
pub icon_hovered: bool,
pub arrow_hovered: bool,
pub icon_rect: Rect,
pub arrow_rect: Rect,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ArrowDirection {
Up,
Down,
Left,
Right,
}
pub struct PairButton<'a> {
pub icon: &'a Icon,
pub tooltip: &'a str,
pub arrow_tooltip: &'a str,
pub is_icon_active: bool,
pub is_expanded: bool,
}
impl<'a> PairButton<'a> {
pub fn show(
&self,
ui: &mut Ui,
width: f32,
height: f32,
icon_size: f32,
padding: f32,
) -> PairButtonResponse {
let cursor_pos = ui.cursor().min;
let pair_rect = Rect::from_min_size(cursor_pos, Vec2::new(width, height));
let arrow_width = padding;
let _left_padding = arrow_width;
let icon_rect = Rect::from_center_size(pair_rect.center(), Vec2::splat(icon_size));
let icon_res = ui.allocate_rect(icon_rect, Sense::click());
let arrow_rect = Rect::from_min_size(
Pos2::new(pair_rect.max.x - arrow_width, pair_rect.min.y),
Vec2::new(arrow_width, height),
);
let arrow_res = ui.allocate_rect(arrow_rect, Sense::click());
if ui.is_rect_visible(icon_rect) {
let icon_bg = if self.is_icon_active && icon_res.hovered() {
theming::active_hover_color(ui)
} else if self.is_icon_active {
theming::sel_color(ui)
} else if icon_res.hovered() {
theming::hover_color(ui)
} else {
Color32::TRANSPARENT };
ui.painter()
.rect_filled(icon_rect, DESIGN_TOKENS.rounding.button, icon_bg);
}
let interactive_width = width - padding - arrow_width;
let icon_center_x = pair_rect.min.x + padding + interactive_width / 2.0;
let icon_rect_center = Rect::from_center_size(
Pos2::new(icon_center_x, pair_rect.center().y),
Vec2::splat(icon_size),
);
if ui.is_rect_visible(icon_rect_center) {
render_svg_at_rect_themed(
ui,
self.icon,
icon_rect_center,
icon_res.hovered(),
self.is_icon_active,
);
}
if ui.is_rect_visible(arrow_rect) {
let arrow_stroke = Stroke::new(
1.2,
if arrow_res.hovered() {
theming::active_icon_color(ui)
} else {
theming::icon_color(ui)
},
);
let arrow_center = arrow_rect.center();
draw_arrow(
ui,
arrow_center,
if self.is_expanded {
ArrowDirection::Up
} else {
ArrowDirection::Down
},
arrow_stroke,
);
}
let icon_clicked = icon_res.clicked();
let arrow_clicked = arrow_res.clicked();
let icon_hovered = icon_res.hovered();
let arrow_hovered = arrow_res.hovered();
icon_res.on_hover_text(self.tooltip);
arrow_res.on_hover_text(self.arrow_tooltip);
PairButtonResponse {
icon_clicked,
arrow_clicked,
icon_hovered,
arrow_hovered,
icon_rect,
arrow_rect,
}
}
pub fn show_styled(&self, ui: &mut Ui) -> PairButtonResponse {
let tokens = &DESIGN_TOKENS.sizing.toolbar;
let btn_width = tokens.button_width;
let btn_height = tokens.button_height;
let margin_h = tokens.hover_margin_h;
let margin_v = tokens.hover_margin_v;
let inner_rounding = tokens.inner_rounding;
let icon_size = tokens.icon_size;
let cursor_pos = ui.cursor().min;
let outer_rect = Rect::from_min_size(cursor_pos, Vec2::new(btn_width, btn_height));
let inner_rect = Rect::from_min_max(
Pos2::new(outer_rect.min.x + margin_h, outer_rect.min.y + margin_v),
Pos2::new(outer_rect.max.x - margin_h, outer_rect.max.y - margin_v),
);
let arrow_width = margin_h + 2.0;
let arrow_rect = Rect::from_min_max(
Pos2::new(outer_rect.max.x - arrow_width, outer_rect.min.y),
outer_rect.max,
);
let icon_zone_rect = Rect::from_min_max(
outer_rect.min,
Pos2::new(outer_rect.max.x - arrow_width, outer_rect.max.y),
);
let icon_res = ui.allocate_rect(icon_zone_rect, Sense::click());
let arrow_res = ui.allocate_rect(arrow_rect, Sense::click());
let icon_hovered = icon_res.hovered();
let arrow_hovered = arrow_res.hovered();
let icon_clicked = icon_res.clicked();
let arrow_clicked = arrow_res.clicked();
let any_hovered = icon_hovered || arrow_hovered;
let show_bg = any_hovered || self.is_icon_active;
if show_bg && ui.is_rect_visible(inner_rect) {
let bg = if self.is_icon_active && icon_hovered {
theming::active_hover_color(ui)
} else if self.is_icon_active {
theming::sel_color(ui)
} else if any_hovered {
theming::hover_color(ui)
} else {
Color32::TRANSPARENT
};
ui.painter().rect_filled(inner_rect, inner_rounding, bg);
}
let icon_rect = Rect::from_center_size(inner_rect.center(), Vec2::splat(icon_size));
if ui.is_rect_visible(icon_rect) {
render_svg_at_rect_themed(ui, self.icon, icon_rect, any_hovered, self.is_icon_active);
}
if ui.is_rect_visible(arrow_rect) {
let arrow_stroke = Stroke::new(
1.2,
if arrow_hovered {
theming::active_icon_color(ui)
} else {
theming::icon_color(ui)
},
);
let arrow_center = Pos2::new(outer_rect.max.x - margin_h / 2.0, outer_rect.center().y);
draw_arrow(
ui,
arrow_center,
if self.is_expanded {
ArrowDirection::Up
} else {
ArrowDirection::Down
},
arrow_stroke,
);
}
icon_res.on_hover_text(self.tooltip);
arrow_res.on_hover_text(self.arrow_tooltip);
PairButtonResponse {
icon_clicked,
arrow_clicked,
icon_hovered,
arrow_hovered,
icon_rect,
arrow_rect,
}
}
}
pub fn draw_arrow(ui: &mut Ui, center: Pos2, direction: ArrowDirection, stroke: Stroke) {
let arrow_x = center.x;
let arrow_y = center.y;
match direction {
ArrowDirection::Down => {
ui.painter().line_segment(
[
Pos2::new(arrow_x - 2.0, arrow_y - 1.0),
Pos2::new(arrow_x, arrow_y + 1.0),
],
stroke,
);
ui.painter().line_segment(
[
Pos2::new(arrow_x, arrow_y + 1.0),
Pos2::new(arrow_x + 2.0, arrow_y - 1.0),
],
stroke,
);
}
ArrowDirection::Up => {
ui.painter().line_segment(
[
Pos2::new(arrow_x - 2.0, arrow_y + 1.0),
Pos2::new(arrow_x, arrow_y - 1.0),
],
stroke,
);
ui.painter().line_segment(
[
Pos2::new(arrow_x, arrow_y - 1.0),
Pos2::new(arrow_x + 2.0, arrow_y + 1.0),
],
stroke,
);
}
ArrowDirection::Right => {
ui.painter().line_segment(
[
Pos2::new(arrow_x - 1.0, arrow_y - 2.0),
Pos2::new(arrow_x + 1.0, arrow_y),
],
stroke,
);
ui.painter().line_segment(
[
Pos2::new(arrow_x + 1.0, arrow_y),
Pos2::new(arrow_x - 1.0, arrow_y + 2.0),
],
stroke,
);
}
ArrowDirection::Left => {
ui.painter().line_segment(
[
Pos2::new(arrow_x + 1.0, arrow_y - 2.0),
Pos2::new(arrow_x - 1.0, arrow_y),
],
stroke,
);
ui.painter().line_segment(
[
Pos2::new(arrow_x - 1.0, arrow_y),
Pos2::new(arrow_x + 1.0, arrow_y + 2.0),
],
stroke,
);
}
}
}