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)>,
}
pub fn select(
ui: &mut egui::Ui,
id_source: &str,
current: &str,
options: &[(&str, &str)],
) -> SelectResult {
let current_label = options
.iter()
.find(|(_, id)| *id == current)
.map(|(label, _)| *label)
.unwrap_or(current);
let mut changed = None;
let mut item_rects = Vec::new();
let inner = egui::ComboBox::from_id_salt(("toolbar-select", id_source))
.selected_text(current_label)
.show_ui(ui, |ui| {
for (label, id) in options {
let resp = ui.selectable_label(*id == current, *label);
item_rects.push(((*id).to_string(), resp.rect));
if resp.clicked() && *id != current {
changed = Some((*id).to_string());
}
}
});
SelectResult { changed, header_rect: inner.response.rect, item_rects }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_catalogued_glyph_takes_the_artwork_path() {
for glyph in ["\u{1F41E}", "\u{1F4DA}", "\u{270D}", "\u{E032}"] {
let icon = artwork(glyph).unwrap_or_else(|| panic!("{glyph:?} is catalogued"));
assert!(!icon.mono, "{glyph:?} is colour artwork");
}
for glyph in ["\u{2699}", "\u{26F6}", "\u{2139}"] {
let icon = artwork(glyph).unwrap_or_else(|| panic!("{glyph:?} is catalogued"));
assert!(icon.mono, "{glyph:?} is monochrome artwork");
}
assert!(artwork("A").is_none());
assert!(artwork("\u{1F41E} Submit").is_none());
assert!(artwork("").is_none());
}
#[test]
fn the_retired_layer_glyphs_are_no_longer_catalogued() {
let retired = (0xE019u32..=0xE027).chain(0xE05A..=0xE05C);
for cp in retired.filter_map(char::from_u32) {
assert!(
!crate::icons::has(cp),
"U+{:04X} was a stacked-layer glyph and should be deleted",
cp as u32
);
}
for cp in (0xE010u32..=0xE018).filter_map(char::from_u32) {
assert!(crate::icons::has(cp), "U+{:04X} composite is missing", cp as u32);
let icon = artwork(&cp.to_string()).expect("composite is catalogued");
assert!(!icon.mono, "U+{:04X} must be colour", cp as u32);
}
}
}