use super::action::ActionInfo;
const ICON_BUTTON_SIZE: f32 = 32.0;
const BUTTON_SPACING: f32 = 4.0;
const TOOLBAR_PADDING: f32 = 6.0;
const TOOLBAR_MARGIN: f32 = 10.0;
#[derive(Debug, Clone)]
pub struct ToolbarButton {
pub id: String,
pub name: String,
pub bounds: (f32, f32, f32, f32),
pub icon_id: Option<String>,
}
#[derive(Debug, Clone)]
pub struct Toolbar {
buttons: Vec<ToolbarButton>,
position: (f32, f32),
size: (f32, f32),
}
impl Toolbar {
pub fn new(
actions: &[ActionInfo],
selection_bounds: ((f32, f32), (f32, f32)),
screen_size: (f32, f32),
) -> Self {
let ((min_x, _min_y), (max_x, max_y)) = selection_bounds;
let button_count = actions.len() as f32;
let toolbar_width = button_count * ICON_BUTTON_SIZE
+ (button_count - 1.0) * BUTTON_SPACING
+ 2.0 * TOOLBAR_PADDING;
let toolbar_height = ICON_BUTTON_SIZE + 2.0 * TOOLBAR_PADDING;
let selection_center_x = (min_x + max_x) / 2.0;
let mut toolbar_x = selection_center_x - toolbar_width / 2.0;
let space_below = screen_size.1 - max_y;
let toolbar_y = if space_below >= toolbar_height + TOOLBAR_MARGIN {
max_y + TOOLBAR_MARGIN
} else {
max_y - toolbar_height - TOOLBAR_MARGIN
};
toolbar_x = toolbar_x.max(TOOLBAR_MARGIN);
toolbar_x = toolbar_x.min(screen_size.0 - toolbar_width - TOOLBAR_MARGIN);
let mut buttons = Vec::with_capacity(actions.len());
let mut button_x = toolbar_x + TOOLBAR_PADDING;
let button_y = toolbar_y + TOOLBAR_PADDING;
for action in actions {
buttons.push(ToolbarButton {
id: action.id.clone(),
name: action.name.clone(),
bounds: (button_x, button_y, ICON_BUTTON_SIZE, ICON_BUTTON_SIZE),
icon_id: action.icon_id.clone(),
});
button_x += ICON_BUTTON_SIZE + BUTTON_SPACING;
}
Self { buttons, position: (toolbar_x, toolbar_y), size: (toolbar_width, toolbar_height) }
}
pub fn buttons(&self) -> &[ToolbarButton] {
&self.buttons
}
pub fn position(&self) -> (f32, f32) {
self.position
}
pub fn size(&self) -> (f32, f32) {
self.size
}
pub fn bounds(&self) -> (f32, f32, f32, f32) {
(self.position.0, self.position.1, self.size.0, self.size.1)
}
pub fn contains(&self, pos: (f32, f32)) -> bool {
let (x, y) = pos;
x >= self.position.0
&& x <= self.position.0 + self.size.0
&& y >= self.position.1
&& y <= self.position.1 + self.size.1
}
pub fn check_click(&self, pos: (f32, f32)) -> Option<&str> {
let (px, py) = pos;
for button in &self.buttons {
let (bx, by, bw, bh) = button.bounds;
if px >= bx && px <= bx + bw && py >= by && py <= by + bh {
return Some(&button.id);
}
}
None
}
pub fn render(
&self,
ui: &mut egui::Ui,
hovered_button: Option<&str>,
active_tool: Option<&str>,
) {
let (tx, ty, tw, th) = self.bounds();
let toolbar_rect = egui::Rect::from_min_size(egui::pos2(tx, ty), egui::vec2(tw, th));
ui.painter().rect_filled(
toolbar_rect,
egui::CornerRadius::same(8),
egui::Color32::from_rgba_unmultiplied(40, 40, 40, 230),
);
ui.painter().rect_stroke(
toolbar_rect,
egui::CornerRadius::same(8),
egui::Stroke::new(1.0, egui::Color32::from_gray(80)),
egui::StrokeKind::Outside,
);
for button in &self.buttons {
let (bx, by, bw, bh) = button.bounds;
let button_rect = egui::Rect::from_min_size(egui::pos2(bx, by), egui::vec2(bw, bh));
let is_hovered = hovered_button == Some(button.id.as_str());
let is_active = active_tool == Some(button.id.as_str());
let bg_color = if is_active {
egui::Color32::from_rgb(0, 100, 200)
} else if is_hovered {
egui::Color32::from_rgba_unmultiplied(80, 80, 80, 255)
} else {
egui::Color32::from_rgba_unmultiplied(60, 60, 60, 255)
};
ui.painter().rect_filled(button_rect, egui::CornerRadius::same(4), bg_color);
ui.painter().text(
button_rect.center(),
egui::Align2::CENTER_CENTER,
&button.name,
egui::FontId::proportional(16.0),
egui::Color32::WHITE,
);
}
}
}