use eframe::egui;
use crate::icons::artwork;
pub const TOOLBAR_BTN: f32 = 28.0;
const GLYPH_SIZE: f32 = 17.0;
fn square() -> egui::Vec2 {
egui::vec2(TOOLBAR_BTN, TOOLBAR_BTN)
}
fn glyph_button<'a>(ui: &egui::Ui, glyph: &'a str) -> egui::Button<'a> {
match artwork(glyph) {
Some(icon) => {
egui_extras::install_image_loaders(ui.ctx());
let height = GLYPH_SIZE / icon.artwork_aspect.max(1.0);
let image = egui::Image::new(egui::ImageSource::Bytes {
uri: format!("bytes://brep-toolbar/{}.svg", icon.name).into(),
bytes: egui::load::Bytes::Static(icon.artwork_svg.as_bytes()),
})
.fit_to_exact_size(egui::vec2(height * icon.artwork_aspect, height));
egui::Button::new(image)
.image_tint_follows_text_color(icon.mono)
.min_size(square())
}
None => egui::Button::new(glyph).min_size(square()),
}
}
pub fn button(ui: &mut egui::Ui, glyph: &str, tooltip: &str) -> egui::Response {
let button = glyph_button(ui, glyph);
ui.add(button).on_hover_text(tooltip)
}
pub fn button_enabled(
ui: &mut egui::Ui,
enabled: bool,
glyph: &str,
tooltip: &str,
) -> egui::Response {
let button = glyph_button(ui, glyph);
ui.add_enabled(enabled, button).on_hover_text(tooltip)
}
pub fn toggle(ui: &mut egui::Ui, selected: bool, glyph: &str, tooltip: &str) -> egui::Response {
let button = glyph_button(ui, glyph).selected(selected);
ui.add(button).on_hover_text(tooltip)
}
pub struct SelectResult {
pub changed: Option<String>,
pub header_rect: egui::Rect,
pub item_rects: Vec<(String, egui::Rect)>,
}
const SELECT_ICON: f32 = 18.0;
const SELECT_MARKER: f32 = 12.0;
const SELECT_CHEVRON: &str = "\u{25BE}";
const SELECT_TICK: &str = "\u{2713}";
fn switcher_row(label: &str, glyph: &str, marker_slot: egui::Id) -> egui::Button<'static> {
let icon = artwork(glyph).expect("workbench icon must be catalogued");
let image = egui::Image::new(egui::ImageSource::Bytes {
uri: icon.uri.into(),
bytes: egui::load::Bytes::Static(icon.svg.as_bytes()),
})
.fit_to_exact_size(egui::Vec2::splat(SELECT_ICON));
egui::Button::new((
image,
label.to_owned(),
egui::Atom::grow(),
egui::Atom::custom(marker_slot, egui::Vec2::splat(SELECT_MARKER)),
))
.image_tint_follows_text_color(icon.mono)
.wrap_mode(egui::TextWrapMode::Extend)
}
fn switcher_row_ui(
ui: &mut egui::Ui,
label: &str,
glyph: &str,
width: f32,
selected: bool,
marker: Option<&str>,
) -> egui::Response {
let slot = ui.id().with(("switcher-marker", label));
let out = switcher_row(label, glyph, slot)
.selected(selected)
.min_size(egui::vec2(width, TOOLBAR_BTN))
.atom_ui(ui);
if let (Some(marker), Some(rect)) = (marker, out.rect(slot)) {
let icon = artwork(marker).expect("switcher marker must be catalogued");
let tint = ui.style().interact_selectable(&out.response, selected).text_color();
egui::Image::new(egui::ImageSource::Bytes {
uri: icon.uri.into(),
bytes: egui::load::Bytes::Static(icon.svg.as_bytes()),
})
.tint(tint)
.paint_at(ui, rect);
}
out.response
}
fn switcher_width(ui: &egui::Ui, options: &[(&str, &str, &str)]) -> f32 {
let mut probe = egui::Ui::new(
ui.ctx().clone(),
ui.id().with("switcher-measure"),
egui::UiBuilder::new()
.sizing_pass()
.invisible()
.style(ui.style().clone())
.layer_id(ui.layer_id())
.max_rect(egui::Rect::from_min_size(
egui::pos2(1.0e5, 1.0e5),
egui::Vec2::splat(1.0e4),
)),
);
options.iter().fold(0.0_f32, |widest, (label, _, glyph)| {
let slot = probe.id().with(("switcher-marker", *label));
let row = switcher_row(label, glyph, slot).min_size(egui::vec2(0.0, TOOLBAR_BTN));
widest.max(row.atom_ui(&mut probe).response.rect.width())
})
}
pub fn select(
ui: &mut egui::Ui,
id_source: &str,
current: &str,
options: &[(&str, &str, &str)],
) -> SelectResult {
egui_extras::install_image_loaders(ui.ctx());
let width = switcher_width(ui, options);
let mut changed = None;
let mut item_rects = Vec::new();
let header = ui
.push_id(("toolbar-select", id_source), |ui| {
let (label, _, glyph) = options
.iter()
.find(|(_, id, _)| *id == current)
.expect("selected workbench must be registered");
let response =
switcher_row_ui(ui, label, glyph, width, false, Some(SELECT_CHEVRON))
.on_hover_text("Switch workbench");
egui::Popup::menu(&response).width(width).show(|ui| {
for (label, id, glyph) in options {
let selected = *id == current;
let marker = selected.then_some(SELECT_TICK);
let resp = switcher_row_ui(ui, label, glyph, width, selected, marker);
item_rects.push(((*id).to_string(), resp.rect));
if resp.clicked() {
if !selected {
changed = Some((*id).to_string());
}
ui.close();
}
}
});
response
})
.inner;
SelectResult {
changed,
header_rect: header.rect,
item_rects,
}
}