Skip to main content

ccf_gpui_widgets/widgets/
selection.rs

1//! Selection item trait for generic selection widgets
2//!
3//! This module defines the `SelectionItem` trait used by RadioGroup, SegmentedControl,
4//! TabBar, and SidebarNav widgets. It also provides `StringItem` as a simple default
5//! implementation for string-based selections.
6//!
7//! # Example: Custom Selection Type
8//!
9//! ```ignore
10//! use ccf_gpui_widgets::widgets::SelectionItem;
11//! use gpui::*;
12//!
13//! #[derive(Clone, PartialEq)]
14//! enum Size {
15//!     Small,
16//!     Medium,
17//!     Large,
18//! }
19//!
20//! impl SelectionItem for Size {
21//!     fn label(&self) -> SharedString {
22//!         match self {
23//!             Size::Small => "Small".into(),
24//!             Size::Medium => "Medium".into(),
25//!             Size::Large => "Large".into(),
26//!         }
27//!     }
28//!
29//!     fn id(&self) -> ElementId {
30//!         match self {
31//!             Size::Small => "size_small".into(),
32//!             Size::Medium => "size_medium".into(),
33//!             Size::Large => "size_large".into(),
34//!         }
35//!     }
36//! }
37//! ```
38
39use gpui::{ElementId, SharedString};
40
41/// Trait for items that can be displayed in selection widgets
42///
43/// Implement this trait to use custom types with RadioGroup, SegmentedControl,
44/// TabBar, and SidebarNav widgets.
45///
46/// # Required Methods
47///
48/// - `label()`: Display text for the item
49/// - `id()`: Unique element ID for the item (used for click handling and GPUI rendering)
50pub trait SelectionItem: Clone + PartialEq + 'static {
51    /// The display label for this item
52    fn label(&self) -> SharedString;
53
54    /// A unique element ID for this item (used for click handling)
55    fn id(&self) -> ElementId;
56}
57
58/// A simple string-based selection item
59///
60/// Use this type for simple string selections without defining a custom enum.
61/// The label and id are both derived from the string value.
62///
63/// # Example
64///
65/// ```ignore
66/// use ccf_gpui_widgets::widgets::{RadioGroup, StringItem};
67///
68/// let radio = cx.new(|cx| {
69///     RadioGroup::new(
70///         vec![
71///             StringItem::new("small"),
72///             StringItem::new("medium"),
73///             StringItem::new("large"),
74///         ],
75///         StringItem::new("medium"),
76///         cx,
77///     )
78/// });
79/// ```
80#[derive(Clone, PartialEq, Debug)]
81pub struct StringItem {
82    value: String,
83}
84
85impl StringItem {
86    /// Create a new StringItem from a string value
87    pub fn new(value: impl Into<String>) -> Self {
88        Self {
89            value: value.into(),
90        }
91    }
92
93    /// Get the underlying string value
94    pub fn value(&self) -> &str {
95        &self.value
96    }
97
98    /// Consume and return the underlying string value
99    pub fn into_value(self) -> String {
100        self.value
101    }
102}
103
104impl SelectionItem for StringItem {
105    fn label(&self) -> SharedString {
106        self.value.clone().into()
107    }
108
109    fn id(&self) -> ElementId {
110        // Create stable ID from value - lowercase with underscores
111        let id_str = format!("item_{}", self.value.to_lowercase().replace(' ', "_"));
112        ElementId::Name(id_str.into())
113    }
114}
115
116impl From<String> for StringItem {
117    fn from(value: String) -> Self {
118        Self::new(value)
119    }
120}
121
122impl From<&str> for StringItem {
123    fn from(value: &str) -> Self {
124        Self::new(value)
125    }
126}
127
128impl std::fmt::Display for StringItem {
129    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130        write!(f, "{}", self.value)
131    }
132}