Reference

A single-page summary of the public API. For full signatures, doc comments, and version-tracked details, see docs.rs/egui_citizen.

Registry

use egui_citizen::Registry;
CallWhat it does
Registry::new()Empty registry.
add().with_name(name) -> CitizenStateAdd a citizen; return its CitizenState handle.
get(&id) -> Option<&CitizenState>Look up a registered citizen's state.
activate(name)One-hot: this one on, all others off. Emits messages.
send(message)Push a CitizenMessage onto the queue without activating.
drain_messages() -> Vec<CitizenMessage>Take all pending messages. Call once per frame.
len() / is_empty()Citizen count / emptiness.

See the registry chapter for the one-hot invariant and the canonical drain loop.

Citizen trait

use egui_citizen::Citizen;
MethodProvided?Purpose
id() -> &CitizenIdrequiredStable identity.
citizen_state() -> &CitizenStaterequiredRead access to lifecycle state.
citizen_state_mut() -> &mut CitizenStaterequiredMutable access to lifecycle state.
on_activate()defaultSets citizen_state.active = true.
on_deactivate()defaultSets citizen_state.active = false.
on_click()defaultSets citizen_state.clicked = true.
is_active() -> booldefaultcitizen_state.active.get().
is_selected() -> booldefaultcitizen_state.selected.get().

See the Citizen trait chapter for the minimum-viable impl and override semantics.

CitizenState

use egui_citizen::CitizenState;

Six reactive Dynamic<T> fields:

FieldTypeMeaning
activeDynamic<bool>This citizen is the active one (one-hot).
clickedDynamic<bool>True for the frame this citizen was clicked.
selectedDynamic<bool>Persistent selection toggle.
movedDynamic<bool>Citizen moved to a new dock location.
locationDynamic<[f32; 2]>Last known dock-layout position.
visibleDynamic<bool>Citizen is currently visible.

Cloning a CitizenState clones the Arcs — every clone refers to the same storage. See Reactive lifecycle and Inside Dynamic<T> for the underlying machinery.

CitizenMessage

use egui_citizen::CitizenMessage;
VariantEmitted by
Activated { id }Registry::activate(name)
Deactivated { id }Registry::activate(name) for the previously active citizen
Clicked { id }App code via Registry::send
Selected { id, selected: bool }App code via Registry::send
Moved { id, location: [f32; 2] }App code via Registry::send
VisibilityChanged { id, visible: bool }App code via Registry::send

Derives: Debug, Clone. See the messages chapter.

CitizenId

pub struct CitizenId(pub String);

impl CitizenId {
    pub fn new(id: impl Into<String>) -> Self { /* ... */ }
}

Stable string identifier for a citizen. Define ids as consts in your app to make typos a compile-time error:

const PLOT_ID:    &str = "plot";
const LOGGER_ID:  &str = "logger";

Common idioms

Register and activate

let mut registry = Registry::new();
let plot_state = registry.add().with_name("plot");
registry.activate("plot");

Implement Citizen on a panel struct

struct PlotPanel {
    citizen_id: CitizenId,
    citizen_state: CitizenState,
}

impl Citizen for PlotPanel {
    fn id(&self) -> &CitizenId               { &self.citizen_id }
    fn citizen_state(&self) -> &CitizenState  { &self.citizen_state }
    fn citizen_state_mut(&mut self) -> &mut CitizenState {
        &mut self.citizen_state
    }
}

Or generate the panel with citizen_panel!

// Struct + new(citizen_state) + Citizen impl, one declaration.
// Extra fields become pub members initialized to their defaults.
citizen_panel!(PlotPanel, "plot",
    samples: Vec<f32> = Vec::new(),
);

See the macro section of the Citizen trait chapter for when to write the impl by hand instead.

Wire activation through egui_dock::TabViewer

fn on_tab_button(&mut self, tab: &mut Tab, response: &egui::Response) {
    if response.clicked() {
        self.registry.activate(&tab.citizen_id());
    }
}

Drain messages once per frame

for msg in self.registry.drain_messages() {
    match msg {
        CitizenMessage::Activated { id } => { /* ... */ }
        CitizenMessage::Deactivated { id } => { /* ... */ }
        _ => {}
    }
}

Wrap in your own AppMessage

pub enum AppMessage {
    Citizen(CitizenMessage),
    /* domain variants ... */
}

for msg in self.registry.drain_messages() {
    let _ = self.tx_backend.send(AppMessage::Citizen(msg));
}

See also