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

nordisk/facett

facett-app/src/popover.rs

//! **`Popover` — an anchored floating panel.** Generalises the dropdown popup
//! into a reusable overlay: attach arbitrary content to a trigger response. Like
//! the other overlays, its `open` state lives in the struct (FC-1), so it is
//! headless-drivable + observable.

use serde_json::{Value, json};

/// A floating panel anchored under a trigger widget.
#[derive(Default)]
pub struct Popover {
    id_salt: String,
    open: bool,
}

impl Popover {
    pub fn new(id_salt: impl Into<String>) -> Self {
        Self { id_salt: id_salt.into(), open: false }
    }
    pub fn is_open(&self) -> bool {
        self.open
    }
    pub fn open(&mut self) {
        self.open = true;
    }
    pub fn close(&mut self) {
        self.open = false;
    }
    pub fn toggle(&mut self) {
        self.open = !self.open;
    }

    /// Draw the popover (if open) anchored below `trigger`, filling it with
    /// `add_contents`. Esc closes. Call after drawing the trigger widget.
    pub fn show(&mut self, ui: &mut egui::Ui, trigger: &egui::Response, add_contents: impl FnOnce(&mut egui::Ui)) {
        if !self.open {
            return;
        }
        let area_id = egui::Id::new((&self.id_salt, "popover"));
        egui::Area::new(area_id)
            .order(egui::Order::Foreground)
            .fixed_pos(trigger.rect.left_bottom())
            .show(ui.ctx(), |ui| {
                egui::Frame::popup(ui.style()).inner_margin(8.0).show(ui, |ui| {
                    ui.set_max_width(280.0);
                    add_contents(ui);
                });
            });
        if ui.input(|i| i.key_pressed(egui::Key::Escape)) {
            self.open = false;
        }
    }

    pub fn state_json(&self) -> Value {
        json!({ "open": self.open })
    }
}