Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/facett

facett-core/src/look/navtokens.rs

//! **Navigation-graph overlay tokens** — the colours the dev-mode navgraph overlay
//! ([`crate::navgraph`]) paints with, derived from the v1 semantic [`Palette`] roles.
//!
//! The overlay has no colour literals of its own. Every colour below is a palette role,
//! sometimes with a lightness step or an alpha, so the overlay follows the active theme
//! (light, dark, device) with no per-theme code.
//!
//! # Names and look and feel v2.0
//!
//! Each field has a dotted token name in [`NavOverlayTokens::NAMES`]. The names are
//! chosen to be the keys of a future v2.0 token set (`nav.chip.plate`,
//! `nav.edge.next`, …): when v2.0 exists, the derivation in
//! [`NavOverlayTokens::from_palette`] is replaced by a lookup of these names and no
//! caller changes. v2.0 itself is not designed here.

use egui::Color32;

use super::Palette;

/// The overlay's colours. `Copy`, computed once per frame from the published palette.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct NavOverlayTokens {
    /// `nav.chip.plate` — the opaque plate behind a number (it must occlude, the same
    /// rule as the dev-id chip: a translucent plate superimposes two strings).
    pub chip_plate: Color32,
    /// `nav.chip.text.declared` — a number whose every step is declared.
    pub chip_declared: Color32,
    /// `nav.chip.text.guessed` — a number with a `~` (draw-order) step.
    pub chip_guessed: Color32,
    /// `nav.chip.text.undeclared` — a number with a `?` (missing from its parent's
    /// declared list) step.
    pub chip_undeclared: Color32,
    /// `nav.chip.outline.focused` — the ring around the chip of the focused node.
    pub chip_focused: Color32,
    /// `nav.edge.contains` — parent → child; faint, tinted per depth (see
    /// [`NavOverlayTokens::contains_at_depth`]).
    pub edge_contains: Color32,
    /// `nav.edge.next` — sibling n → n+1 in the navigation order; bright, arrowed.
    pub edge_next: Color32,
    /// `nav.edge.spatial` — the arrow-key neighbour; dashed.
    pub edge_spatial: Color32,
}

impl NavOverlayTokens {
    /// The v2.0 token names, in field order.
    pub const NAMES: [&'static str; 8] = [
        "nav.chip.plate",
        "nav.chip.text.declared",
        "nav.chip.text.guessed",
        "nav.chip.text.undeclared",
        "nav.chip.outline.focused",
        "nav.edge.contains",
        "nav.edge.next",
        "nav.edge.spatial",
    ];

    /// Derive the tokens from the v1 semantic roles.
    pub fn from_palette(p: &Palette) -> Self {
        Self {
            chip_plate: p.surface_extreme.to_color32(),
            chip_declared: p.primary.to_color32(),
            chip_guessed: p.warn.to_color32(),
            chip_undeclared: p.error.to_color32(),
            chip_focused: p.accent.to_color32(),
            edge_contains: p.outline.to_color32_alpha(150),
            edge_next: p.accent.to_color32(),
            edge_spatial: p.on_surface_dim.to_color32_alpha(200),
        }
    }

    /// The contains-edge colour for a child at `depth` (1 = root). Deeper edges fade,
    /// so the spine reads first. Alpha only; the hue stays the outline role.
    pub fn contains_at_depth(&self, depth: u8) -> Color32 {
        let k = match depth {
            0..=2 => 1.0,
            3 => 0.75,
            4 => 0.55,
            _ => 0.4,
        };
        self.edge_contains.gamma_multiply(k)
    }
}

/// The palette the active theme published ([`crate::look::Theme::apply`]), or the
/// default preset's when none was published. `Palette` is `Copy`, so this is a read.
pub fn published_palette(ctx: &egui::Context) -> Palette {
    ctx.data(|d| d.get_temp::<Palette>(egui::Id::new(PALETTE_ID)))
        .unwrap_or_else(super::presets::windows_dark_palette)
}

/// Publish the active palette (called by [`crate::look::Theme::apply`]).
pub fn publish_palette(ctx: &egui::Context, p: Palette) {
    ctx.data_mut(|d| d.insert_temp(egui::Id::new(PALETTE_ID), p));
}

const PALETTE_ID: &str = "facett_look_palette";

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

    #[test]
    fn the_three_confidences_are_tellable_apart_and_the_plate_is_opaque() {
        for p in [
            super::super::presets::windows_dark_palette(),
            super::super::presets::windows_light_palette(),
            super::super::presets::device_palette(),
        ] {
            let t = NavOverlayTokens::from_palette(&p);
            assert_eq!(t.chip_plate.a(), 255, "the chip plate must occlude");
            assert_ne!(t.chip_declared, t.chip_guessed);
            assert_ne!(t.chip_declared, t.chip_undeclared);
            assert_ne!(t.chip_guessed, t.chip_undeclared);
            assert!(t.contains_at_depth(5).a() < t.contains_at_depth(1).a(), "deeper fades");
        }
        assert_eq!(NavOverlayTokens::NAMES.len(), 8);
    }
}