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}