egui-probe 0.11.0

Trait and derive macro for exposing value editing in egui
Documentation
use hashbrown::HashMap;
use hashbrown::hash_map::Entry;
use std::{fmt::Display, str::FromStr};

use crate::map::HashMapProbe;
use crate::{
    EguiProbe, Style,
    collections::{DeleteMe, EguiProbeFrozen},
    option::option_probe_with,
};

impl<K, V, S> EguiProbe for HashMap<K, V, S>
where
    K: Display + FromStr + Eq + std::hash::Hash,
    V: EguiProbe + Default,
    S: std::hash::BuildHasher,
{
    fn probe(&mut self, ui: &mut egui::Ui, style: &Style) -> egui::Response {
        let mut changed = false;

        let mut r = ui
            .horizontal(|ui| {
                let mut probe = HashMapProbe::load(ui.ctx(), ui.make_persistent_id("HashMapProbe"));

                let mut reduce_text_width = 0.0;

                let r = ui.weak(format!("[{}]", self.len()));
                reduce_text_width += r.rect.width() + ui.spacing().item_spacing.x;

                let r = ui.small_button(style.add_button_text());
                if r.clicked() {
                    if let Ok(key) = K::from_str(&probe.state.new_key) {
                        match self.entry(key) {
                            Entry::Occupied(_) => {
                                probe.key_error();
                            }
                            Entry::Vacant(entry) => {
                                entry.insert(V::default());
                                probe.key_accepted();
                            }
                        }
                    } else {
                        probe.key_error();
                    }

                    changed = true;
                }

                reduce_text_width += r.rect.width() + ui.spacing().item_spacing.x;

                probe.new_key_edit(ui, reduce_text_width);
                probe.store(ui.ctx());
            })
            .response;

        if changed {
            r.mark_changed();
        }

        r
    }

    fn iterate_inner(
        &mut self,
        ui: &mut egui::Ui,
        f: &mut dyn FnMut(&str, &mut egui::Ui, &mut dyn EguiProbe),
    ) {
        self.retain(|key, value| {
            let mut item = DeleteMe {
                value,
                delete: false,
            };
            f(&key.to_string(), ui, &mut item);
            !item.delete
        });
    }
}

impl<K, V, S> EguiProbe for EguiProbeFrozen<'_, HashMap<K, V, S>>
where
    K: Display + Eq + std::hash::Hash,
    V: EguiProbe,
    S: std::hash::BuildHasher,
{
    fn probe(&mut self, ui: &mut egui::Ui, _style: &Style) -> egui::Response {
        ui.weak(format!("[{}]", self.value.len()))
    }

    fn iterate_inner(
        &mut self,
        ui: &mut egui::Ui,
        f: &mut dyn FnMut(&str, &mut egui::Ui, &mut dyn EguiProbe),
    ) {
        for (key, value) in self.value.iter_mut() {
            f(&key.to_string(), ui, value);
        }
    }
}

impl<K, V, S> EguiProbe for EguiProbeFrozen<'_, Option<HashMap<K, V, S>>>
where
    K: Display + Eq + std::hash::Hash,
    V: EguiProbe,
    S: std::hash::BuildHasher + Default,
{
    fn probe(&mut self, ui: &mut egui::Ui, style: &Style) -> egui::Response {
        option_probe_with(
            self.value,
            ui,
            style,
            || HashMap::with_hasher(S::default()),
            |value, ui, _style| ui.weak(format!("[{}]", value.len())),
        )
    }

    fn iterate_inner(
        &mut self,
        ui: &mut egui::Ui,
        f: &mut dyn FnMut(&str, &mut egui::Ui, &mut dyn EguiProbe),
    ) {
        if let Some(map) = self.value {
            for (key, value) in map.iter_mut() {
                f(&key.to_string(), ui, value);
            }
        }
    }
}