Skip to main content

feather_tui/components/
option.rs

1use crate::{callback as cbk, error::{FtuiError, FtuiResult}};
2use unicode_segmentation::UnicodeSegmentation;
3
4/// A UI component representing an interactive option in a `Container`. 
5/// `Option` components are displayed in the order they are added to the
6/// `Container`. To make options selectable, a `Selector` must also be
7/// initialized for the `Container`.
8///
9/// # Usage
10///
11/// The `Option` component is used within a `Container` to provide interactive  
12/// choices. 
13///
14/// # Notes
15/// - A `Selector` component is required to navigate and select options.
16pub struct Option {
17    label: String,
18    len: usize,
19    line: u16,
20    id: u16,
21    selc_on: bool,
22    is_selc: bool,
23    // Use `std::option::Option` to prevent conflict with `Option`.
24    callback: std::option::Option<cbk::Callback>,
25}
26
27impl Option {
28    /// Creates a new `Option` with the specified label and callback.
29    ///
30    /// # Parameters
31    /// - `label`: A `&str` representing the text displayed for this option.
32    /// - `callback`: A `Callback` to invoked when the option is selected (optional). 
33    ///
34    /// # Returns
35    /// `Ok(Option)`: A new `Option` instance.
36    /// `Err(FtuiError)`: Returns an error.
37    ///
38    /// # Example
39    /// ```rust
40    /// // Define a callback function that quits the program when invoked.
41    /// cbk_new_callback_func!(quit_option_callback, _arg, {
42    ///     std::process::exit(0);
43    /// });
44    /// 
45    /// // Create a `Callback` with no arguments.
46    /// let callback = Callback::no_arg(quit_option_callback);
47    /// 
48    /// // Create an `Option` component labeled "Quit".
49    /// // When selected, it exits the program.
50    /// let _ = Option::new("Quit", callback)?;
51    /// 
52    /// // Create an `Option` component labeled "Nothing".
53    /// // This option has no associated callback.
54    /// // You can detect its selection using the `is_selc()` method.
55    /// let _ = Option::new("Nothing", None)?;
56    /// ```
57    pub fn new(
58        label: &str, callback: impl Into<std::option::Option<cbk::Callback>>
59    ) -> FtuiResult<Self> {
60        if label.is_empty() {
61            return Err(FtuiError::OptionLabelEmpty);
62        }
63
64        Ok(Option {
65            label: label.to_string(),
66            len: label.graphemes(true).count(),
67            id: 0,
68            line: 0,
69            selc_on: false,
70            is_selc: false,
71            callback: callback.into(),
72        })
73    }
74
75    pub(crate) fn set_line(&mut self, line: u16) {
76        self.line = line;
77    }
78
79    pub(crate) fn line(&self) -> u16 {
80        return self.line;
81    }
82
83    pub(crate) fn label(&self) -> &String {
84        return &self.label;
85    }
86
87    pub(crate) fn len(&self) -> usize {
88        return self.len;
89    }
90
91    pub(crate) fn selc_on(&self) -> bool {
92        return self.selc_on;
93    }
94
95    pub(crate) fn callback(&self) -> &std::option::Option<cbk::Callback> {
96        return &self.callback;
97    }
98
99    pub(crate) fn set_selc_on(&mut self, value: bool) {
100        self.selc_on = value;
101    }
102
103    /// Returns whether the `Option` component was selected. This method acts
104    /// like a latch or semaphore in multithreading contexts. It returns the
105    /// current state of the `is_selc` flag and then resets it to `false`. 
106    /// This method is useful for `Option` components with no `Callback`.
107    ///
108    /// # Notes 
109    /// Imagine the following timeline:
110    ///
111    /// Time (ms):    0        500         2000          ...
112    ///               |---------|------------|------------>
113    /// is_selc:      |  false  |    true    |    false
114    ///
115    /// At time 500ms, some internal event sets `is_selc = true`.
116    /// When `is_selc()` is called (e.g., at 2000ms), it returns `true`
117    /// and immediately resets the flag to `false`.
118    ///
119    /// # Returns
120    /// - `true`: if the option was selected since the last check.
121    /// - `false`: otherwise.
122    ///
123    /// # Example
124    /// ```rust
125    /// // Create an `Option` component with no callback.
126    /// let mut option = Option::new(..., None)?;
127    ///
128    /// // Check if the option was selected.
129    /// if option.is_selc() {
130    ///     // Perform an action.
131    ///     todo!();
132    /// }
133    /// ```
134    pub fn is_selc(&mut self) -> bool {
135        std::mem::take(&mut self.is_selc)
136    }
137
138    pub(crate) fn set_is_selc(&mut self, value: bool) {
139        self.is_selc = value;
140    }
141
142    pub(crate) fn id(&self) -> u16 {
143        self.id
144    }
145
146    pub(crate) fn set_id(&mut self, value: u16) {
147        self.id = value;
148    }
149}