concinnity-dev 0.19.119

The Concinnity dev tooling library: world authoring, the in-engine editor, the debug server, docs and packaging
//! Pure composition of the editor's two hide mechanisms: the manual per-asset
//! hide set (outliner eye, H) and an active isolate (Shift+H), which keeps a
//! snapshot of assets visible and hides everything else. The manual set is
//! never mutated by isolate, so leaving isolate restores exactly the
//! manually-hidden state, and a name in both stays hidden.

use std::collections::BTreeSet;

// The set of assets to hide this frame. With no isolate active this is the
// manual set; with one active it is the manual set plus everything outside
// the kept snapshot.
pub(crate) fn effective_hidden<K: Ord + Clone>(
    manual: &BTreeSet<K>,
    isolate: Option<&BTreeSet<K>>,
    all: impl IntoIterator<Item = K>,
) -> BTreeSet<K> {
    let mut hidden = manual.clone();
    if let Some(keep) = isolate {
        hidden.extend(all.into_iter().filter(|k| !keep.contains(k)));
    }
    hidden
}

// Whether one asset is hidden under the same rule, for per-entry filters that
// never materialize the full set.
pub(crate) fn is_hidden<K: Ord>(
    key: &K,
    manual: &BTreeSet<K>,
    isolate: Option<&BTreeSet<K>>,
) -> bool {
    manual.contains(key) || isolate.is_some_and(|keep| !keep.contains(key))
}

#[cfg(test)]
mod tests {
    use super::*;

    fn set(names: &[&'static str]) -> BTreeSet<&'static str> {
        names.iter().copied().collect()
    }

    #[test]
    fn no_isolate_is_just_the_manual_set() {
        let manual = set(&["a"]);
        let hidden = effective_hidden(&manual, None, ["a", "b", "c"]);
        assert_eq!(hidden, set(&["a"]));
    }

    #[test]
    fn isolate_hides_everything_outside_the_kept_set() {
        let manual = BTreeSet::new();
        let keep = set(&["b"]);
        let hidden = effective_hidden(&manual, Some(&keep), ["a", "b", "c"]);
        assert_eq!(hidden, set(&["a", "c"]));
    }

    #[test]
    fn manual_wins_inside_the_kept_set() {
        // An asset both isolated and manually hidden stays hidden.
        let manual = set(&["b"]);
        let keep = set(&["a", "b"]);
        let hidden = effective_hidden(&manual, Some(&keep), ["a", "b", "c"]);
        assert_eq!(hidden, set(&["b", "c"]));
        assert!(is_hidden(&"b", &manual, Some(&keep)));
        assert!(!is_hidden(&"a", &manual, Some(&keep)));
    }

    #[test]
    fn leaving_isolate_restores_the_manual_state() {
        let manual = set(&["a"]);
        let keep = set(&["b"]);
        let during = effective_hidden(&manual, Some(&keep), ["a", "b", "c"]);
        assert_eq!(during, set(&["a", "c"]));
        // Dropping the isolate recomputes to exactly the manual set: nothing
        // was mutated while it was active.
        let after = effective_hidden(&manual, None, ["a", "b", "c"]);
        assert_eq!(after, set(&["a"]));
    }

    #[test]
    fn per_name_check_matches_the_set() {
        let manual = set(&["a"]);
        let keep = set(&["b"]);
        for n in ["a", "b", "c"] {
            let in_set = effective_hidden(&manual, Some(&keep), ["a", "b", "c"]).contains(n);
            assert_eq!(is_hidden(&n, &manual, Some(&keep)), in_set, "{n}");
        }
    }
}