Skip to main content

teksilo_widgets/tab_widget/
id.rs

1// SPDX-License-Identifier: MPL-2.0
2// SPDX-FileCopyrightText: 2026 FernTech
3
4//! Stable per-tab identifiers.
5//!
6//! [`TabId`] is the identity of a tab as it lives, dies, reorders,
7//! and survives data-source mutations. Selection signals, close /
8//! reorder / pin callbacks, and accessibility relations are all
9//! keyed by `TabId` rather than by index — so reordering a tab via
10//! drag-drop never sends the active selection to a different tab.
11//!
12//! Apps either let the framework allocate fresh ids
13//! ([`TabId::fresh`]) or wrap their own external keys (file path
14//! hashes, document UUIDs, …) via [`TabId::from_raw`].
15
16use std::num::NonZeroU64;
17use std::sync::atomic::{AtomicU64, Ordering};
18
19/// Stable identity of a tab. Cheap to copy; persists across model
20/// reorders, rebuilds, and reorders triggered by drag-and-drop.
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
22pub struct TabId(NonZeroU64);
23
24impl TabId {
25    /// Allocate a new, never-before-seen id. Backed by a monotonic
26    /// global counter — overflow is theoretically possible after
27    /// 2^64 calls, at which point the universe has had bigger
28    /// problems.
29    pub fn fresh() -> Self {
30        static COUNTER: AtomicU64 = AtomicU64::new(1);
31        let raw = COUNTER.fetch_add(1, Ordering::Relaxed);
32        // The counter starts at 1 and only ever increments, so the
33        // value is non-zero in any practical run.
34        Self(NonZeroU64::new(raw).expect("TabId counter wrapped to zero"))
35    }
36
37    /// Wrap an externally-allocated key. Use this when the tab's
38    /// identity comes from an existing app-side store (document
39    /// UUID, file path hash, etc.) — calling [`TabId::fresh`] would
40    /// allocate a *new* id every restart, breaking session restore.
41    pub fn from_raw(value: NonZeroU64) -> Self {
42        Self(value)
43    }
44
45    /// The underlying non-zero `u64`. Useful when persisting tabs
46    /// across sessions: serialize this, restore via `from_raw`.
47    pub fn raw(self) -> NonZeroU64 {
48        self.0
49    }
50}
51
52impl std::fmt::Display for TabId {
53    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54        write!(f, "TabId({})", self.0.get())
55    }
56}