Skip to main content

feather_tui/
container.rs

1use crate::{
2    callback::Callback, components as cpn, error::{FtuiError, FtuiResult},
3    renderer::Renderer, trigger::Trigger, util::id::IdGenerator
4};
5
6/// `Container` is a data structure used to store and organize UI components,
7/// including `Header`, `Option`, `Text`, `Separator`, and `Selector`.
8/// It is created using a `ContainerBuilder`.
9///
10/// # Usage
11/// - Handle UI events with the `looper` method.
12/// - Render the UI using a `Renderer` (recommended).
13/// - Alternatively, use the `draw` or `draw_fullscreen` methods.
14/// - Access `Option` components by ID using `option` and `option_mut`.
15/// - Access `Text` components by ID using `text` and `text_mut`.
16/// - Navigate using `selector_up`, `selector_down`, and `selector_select`.
17pub struct Container {
18    id_generator: IdGenerator<u16>,
19    header: Option<cpn::Header>,
20    options: Vec<cpn::Option>,
21    texts: Vec<cpn::Text>,
22    separators: Vec<cpn::Separator>,
23    selector: Option<cpn::Selector>,
24    component_count: u16,
25}
26
27impl Container {
28    /// Constructs a new `Container`. 
29    ///
30    /// # Returns
31    /// `Container`: A new instance of `Container`.
32    ///
33    /// # Example
34    /// ```rust
35    /// let _ = Container::new();
36    /// ```
37    pub(crate) fn new() -> Container {
38        Container {
39            id_generator: IdGenerator::new(),
40            header: None,
41            options: vec![],
42            texts: vec![],
43            separators: vec![],
44            selector: None,
45            component_count: 0,
46        }
47    }
48
49    /// Updates the container and returns whether a change occurred.
50    ///
51    /// # Returns
52    /// - `Ok(bool)`: Returns whether an update occurred.
53    /// - `Err(FtuiError)`: Returns an error.
54    ///
55    /// # Example
56    /// ```rust
57    /// fn render() {
58    ///     todo!();
59    /// }
60    ///
61    /// // Re-render the UI if an update occurred.
62    /// if container.looper()? {
63    ///     render();
64    /// }
65    /// ```
66    pub fn looper(&mut self) -> FtuiResult<bool> {
67        if self.options.is_empty() {
68            return Ok(false);
69        }
70
71        self.selector
72            .as_mut()
73            .ok_or(FtuiError::ContainerLooperNoSelector)?
74            .looper(&mut self.options)
75            .or_else(|e| match e {
76                FtuiError::SelectorNoTriggers => Ok(false),
77                _ => Err(e),
78            })
79    }
80
81    pub(crate) fn set_header(&mut self, header: cpn::Header) {
82        self.header = Some(header);
83        self.component_count += 1;
84    }
85
86    pub(crate) fn set_selector(&mut self, selector: cpn::Selector) {
87        self.selector = Some(selector);
88    }
89
90    // Return added Option ID.
91    pub(crate) fn add_option(&mut self, mut option: cpn::Option) -> u16 {
92        let id = self.id_generator.get_id();
93        option.set_id(id);
94        option.set_line(self.component_count);
95
96        if self.options.is_empty() {
97            option.set_selc_on(true);
98        }
99
100        self.options.push(option);
101        self.component_count += 1;
102
103        id
104    }
105
106    // Return added Text ID.
107    pub(crate) fn add_text(&mut self, mut text: cpn::Text) -> u16 {
108        let id = self.id_generator.get_id();
109        text.set_id(id);
110        text.set_line(self.component_count);
111
112        self.texts.push(text);
113        self.component_count += 1;
114
115        id
116    }
117
118    pub(crate) fn add_separator(&mut self, mut separator: cpn::Separator) {
119        separator.set_line(self.component_count);
120        self.separators.push(separator);
121        self.component_count += 1;
122    }
123
124    
125    /// Renders the `Container` using a temporary `Renderer`. This method is ideal
126    /// for quick, one-off renderings where performance isn't critical. Internally,
127    /// it creates a new `Renderer` with the given dimensions and uses it to draw
128    /// the `Container`. If you need to render multiple times, consider using a
129    /// persistent `Renderer` for better performance.
130    ///
131    ///
132    /// # Parameters
133    /// - `width`: The width of the rendering area.
134    /// - `height`: The height of the rendering area.
135    ///
136    /// # Returns
137    /// - `Ok(())`: Returns nothing.
138    /// - `Err(FtuiError)`: Returns an error.
139    ///
140    /// # Example
141    /// ```rust
142    /// // Good for quick one-off rendering like this.
143    /// fn render_ui() -> FtuiResult<()> {
144    ///     ContainerBuilder::new()
145    ///         .header(...)?
146    ///         .text(...)?
147    ///         .build()
148    ///         .draw(40, 20)?; // Render with dimensions 40x20.
149    ///
150    ///     Ok(())
151    /// }
152    /// ```
153    #[inline]
154    pub fn draw(&mut self, width: u16, height: u16) -> FtuiResult<()> {
155        Renderer::new(width, height).simple_draw(self)
156    }
157
158    /// Renders the `Container` using a temporary `Renderer`. This method is ideal
159    /// for quick, one-off renderings where performance isn't critical. Internally,
160    /// it creates a new fullscreen `Renderer` and uses it to draw the `Container`.
161    /// If you need to render multiple times, consider using a persistent `Renderer`
162    /// for better performance.
163    ///
164    /// # Returns
165    /// - `Ok(())`: Returns nothing.
166    /// - `Err(FtuiError)`: Returns an error.
167    ///
168    /// # Example
169    /// ```rust
170    /// // Good for quick one-off rendering like this.
171    /// fn render_ui() -> FtuiResult<()> {
172    ///     ContainerBuilder::new()
173    ///         .header(...)?
174    ///         .text(...)?
175    ///         .build()
176    ///         .draw_fullscreen()?; // Render in fullscreen.
177    ///
178    ///     Ok(())
179    /// }
180    /// ```
181    #[inline]
182    pub fn draw_fullscreen(&mut self) -> FtuiResult<()> {
183        Renderer::fullscreen()?.simple_draw(self)
184    }
185
186    /// Query an `Option` component by its ID (`O(n)` lookup).
187    ///
188    /// # Parameters
189    /// - `id`: The ID of the `Option` component to query.
190    ///
191    /// # Returns
192    /// - `Ok(&Option)`: A reference to the `Option` component.
193    /// - `Err(FtuiError)`: Returns an error.
194    ///
195    /// # Example
196    /// ```rust
197    /// // A mutable `u16` to store the ID of a `Option` component.
198    /// let mut option_id = 0;
199    ///
200    /// let container = ContainerBuilder::new()
201    ///     .option_id(..., &mut option_id)?
202    ///     .build();
203    ///
204    /// // Query the option by its ID.
205    /// container.option(option_id)?;
206    /// ```
207    #[inline]
208    pub fn option(&self, id: u16) -> FtuiResult<&cpn::Option> {
209        self.options.iter()
210            .find(|option| option.id() == id)
211            .ok_or(FtuiError::ContainerNoComponentById)
212    }
213
214    /// Query an `Option` component by its ID (`O(n)` lookup).
215    ///
216    /// # Parameters
217    /// - `id`: The ID of the `Option` component to query.
218    ///
219    /// # Returns
220    /// - `Ok(&Option)`: A mutable reference to the `Option` component.
221    /// - `Err(FtuiError)`: Returns an error.
222    ///
223    /// # Example
224    /// ```rust
225    /// // A mutable `u16` to store the ID of a `Option` component.
226    /// let mut option_id = 0;
227    ///
228    /// let container = ContainerBuilder::new()
229    ///     .option_id(..., &mut option_id)?
230    ///     .build();
231    ///
232    /// // Query the option by its ID.
233    /// container.option_mut(option_id)?;
234    /// ```
235    #[inline]
236    pub fn option_mut(&mut self, id: u16) -> FtuiResult<&mut cpn::Option> {
237        self.options.iter_mut()
238            .find(|option| option.id() == id)
239            .ok_or(FtuiError::ContainerNoComponentById)
240    }
241
242    /// Query an `Text` component by its ID (`O(n)` lookup).
243    ///
244    /// # Parameters
245    /// - `id`: The ID of the `Text` component to query.
246    ///
247    /// # Returns
248    /// - `Ok(&Option)`: A reference to the `Text` component.
249    /// - `Err(FtuiError)`: Returns an error.
250    ///
251    /// # Example
252    /// ```rust
253    /// // A mutable `u16` to store the ID of a `Text` component.
254    /// let mut text_id: u16 = 0;
255    ///
256    /// let container = ContainerBuilder::new()
257    ///     .text_id(..., &mut text_id)?
258    ///     .build();
259    ///
260    /// // Query the option by its ID.
261    /// container.text(text_id)?;
262    /// ```
263    #[inline]
264    pub fn text(&self, id: u16) -> FtuiResult<&cpn::Text> {
265        self.texts.iter()
266            .find(|text| text.id() == id)
267            .ok_or(FtuiError::ContainerNoComponentById)
268    }
269
270    /// Query an `Text` component by its ID (`O(n)` lookup).
271    ///
272    /// # Parameters
273    /// - `id`: The ID of the `Text` component to query.
274    ///
275    /// # Returns
276    /// - `Ok(&Option)`: A mutable reference to the `Text` component.
277    /// - `Err(FtuiError)`: Returns an error.
278    ///
279    /// # Example
280    /// ```rust
281    /// // A mutable `u16` to store the ID of a `Text` component.
282    /// let mut text_id: u16 = 0;
283    ///
284    /// let container = ContainerBuilder::new()
285    ///     .text_id(..., &mut text_id)?
286    ///     .build();
287    ///
288    /// // Query the option by its ID.
289    /// container.text_mut(text_id)?;
290    /// ```
291    #[inline]
292    pub fn text_mut(&mut self, id: u16) -> FtuiResult<&mut cpn::Text> {
293        self.texts.iter_mut()
294            .find(|text| text.id() == id)
295            .ok_or(FtuiError::ContainerNoComponentById)
296    }
297
298    /// Returns a mutable reference to the `Selector` component, 
299    /// if the `Container` has one.
300    /// 
301    /// # Returns
302    /// - `Ok(&mut Selector)`: A mutable reference to the `Selector` component.
303    /// - `Err(FtuiError)`: Return an error.
304    ///
305    /// # Example
306    /// ```rust
307    /// // Create a container with a `Selector`.
308    /// let mut container = ContainerBuilder::new()
309    ///     .selector_no_triggers()
310    ///     .build();
311    ///
312    /// // Get a mutable reference to the `Selector`.
313    /// container.selector_mut()?;
314    /// ```
315    #[inline]
316    pub fn selector_mut(&mut self) -> FtuiResult<&mut cpn::Selector> {
317        self.selector.as_mut()
318            .ok_or(FtuiError::ContainerNoSelector)
319    }
320    
321    /// Attempts to move the `Selector` up by one position, if possible.
322    ///
323    /// # Returns
324    /// - `Ok(true)`: The selector moved up successfully.
325    /// - `Ok(false)`: The selector could not move (already at the top).
326    /// - `Err(FtuiError)`: Returns an error.
327    ///
328    /// # Example
329    /// ```rust
330    /// // Create a container with two `Option`s component and a `Selector`.
331    /// let mut container = ContainerBuilder::new()
332    ///     .option(...)? // This is where the `Selector` will be when initialize.
333    ///     .option(...)?
334    ///     .selector_no_triggers()
335    ///     .build();
336    ///
337    /// // The `Selector` cannot move up since it is at the top.
338    /// assert_eq!(container.selector_up()?, false);
339    /// ```
340    #[inline]
341    pub fn selector_up(&mut self) -> FtuiResult<bool> {
342        Ok(self.selector
343            .as_mut()
344            .ok_or(FtuiError::ContainerNoSelector)?
345            .up(&mut self.options))
346    }
347
348    /// Attempts to move the `Selector` down by one position, if possible.
349    ///
350    /// # Returns
351    /// - `Ok(true)`: The selector moved down successfully.
352    /// - `Ok(false)`: The selector could not move (already at the bottom).
353    /// - `Err(FtuiError)`: Returns an error.
354    ///
355    /// # Example
356    /// ```rust
357    /// // Create a container with two `Option`s component and a `Selector`.
358    /// let mut container = ContainerBuilder::new()
359    ///     .option(...)? // This is where the `Selector` will be when initialize.
360    ///     .option(...)?
361    ///     .selector_no_triggers()
362    ///     .build();
363    ///
364    /// // The `Selector` can move up since it is not at the bottom.
365    /// assert_eq!(container.selector_up()?, true);
366    /// ```
367    #[inline]
368    pub fn selector_down(&mut self) -> FtuiResult<bool> {
369        Ok(self.selector
370            .as_mut()
371            .ok_or(FtuiError::ContainerNoSelector)?
372            .down(&mut self.options))
373    }
374
375    /// Attempts to select the `Option` that the `Selector` is currently on. 
376    /// This operation should always succeed unless an error occurs internally.
377    ///
378    /// # Returns
379    /// - `Ok(true)`: The selection was successful.
380    /// - `Err(FtuiError)`: An error occurred during selection.
381    ///
382    /// # Example
383    /// ```rust
384    /// // Create a container with on `Option` components and a `Selector`.
385    /// let mut container = ContainerBuilder::new()
386    ///     .option(...)? // The `Selector` starts at this `Option`.
387    ///     .selector_no_triggers()
388    ///     .build();
389    ///
390    /// // Selecting the current `Option` is always possible unless an error occurs.
391    /// assert_eq!(container.selector_select()?, true);
392    /// ```
393    #[inline]
394    pub fn selector_select(&mut self) -> FtuiResult<bool> {
395        Ok(self.selector
396            .as_mut()
397            .ok_or(FtuiError::ContainerNoSelector)?
398            .select(&mut self.options)?)
399    }
400
401    pub(crate) fn component_count(&self) -> u16 {
402        self.component_count
403    }
404
405    pub(crate) fn header(&self) -> &Option<cpn::Header> {
406        &self.header
407    }
408
409    pub(crate) fn options(&self) -> &[cpn::Option] {
410        &self.options
411    }
412
413    pub(crate) fn texts_mut(&mut self) -> &mut [cpn::Text] {
414        &mut self.texts
415    }
416
417    pub(crate) fn separators(&self) -> &[cpn::Separator] {
418        &self.separators
419    }
420}
421
422/// `ContainerBuilder` is used to create `Container` instances using the builder
423/// pattern. This allows for a flexible and readable way to construct complex
424/// containers by chaining method calls.
425///
426/// # Example
427/// ```rust
428/// // Create a container with a header, two options, a separator, some text,
429/// // and a selector.
430/// let container: Container = ContainerBuilder::new()
431///     .header(...)?
432///     .option(...)?
433///     .option(...)?
434///     .separator_normal(...)
435///     .text(...)?
436///     .selector(...)?
437///     .build();
438/// ```
439pub struct ContainerBuilder {
440    container: Container,
441}
442
443impl ContainerBuilder {
444    /// Constructs a new `ContainerBuilder`. 
445    ///
446    /// # Return
447    /// `ContainerBuilder`: A new instance of `ContainerBuilder`.
448    ///
449    /// # Example
450    /// ```rust
451    /// let _ = ContainerBuilder::new();
452    /// ```
453    #[inline]
454    pub fn new() -> Self {
455        ContainerBuilder { container: Container::new(), }
456    }
457
458    /// Explicitly sets a `Header` component for the `Container`. Unlike the `header`
459    /// method, which takes a label and internally constructs a `Header`, this 
460    /// method allows you to directly provide a preconstructed `Header` component.
461    ///
462    /// # Parameters
463    /// - `header`: A `Header` component.
464    ///
465    /// # Returns
466    /// - `ContainerBuilder`: Returns `self`.
467    ///
468    /// # Example
469    /// ```rust
470    /// // Create a `Header` component.
471    /// let header = Header::new(...)?;
472    ///
473    /// // Set a preconstructed `Header` component.
474    /// ContainerBuilder::new()
475    ///     .header_expl(header);
476    /// ```
477    pub fn header_expl(mut self, header: cpn::Header) -> Self {
478        self.container.set_header(header);
479        self
480    }
481
482    /// Sets a `Header` component for the `Container`.
483    ///
484    /// # Parameters
485    /// - `label`: A `&str` representing the text to display in the header.
486    ///
487    /// # Returns
488    /// - `Ok(ContainerBuilder)`: Returns `self`.
489    /// - `Err(FtuiError)`: Returns an error.
490    ///
491    /// # Example
492    /// ```rust
493    /// // Sets a `Header` component with the label "Welcome".
494    /// ContainerBuilder::new()
495    ///     .header("Welcome")?;
496    /// ```
497    #[inline]
498    pub fn header(self, label: &str) -> FtuiResult<Self> {
499        Ok(self.header_expl(cpn::Header::new(label)?))
500    }
501
502    /// Explicitly add an `Option` component to the `Container`. Unlike the `option`
503    /// method, which takes a label and an optional callback to internally constructs
504    /// an `Option`, this method allows you to directly provide a preconstructed 
505    /// `Option` component.
506    ///
507    /// # Parameters
508    /// - `option`: An `Option` component.
509    ///
510    /// # Returns
511    /// - `ContainerBuilder`: Returns `self`.
512    ///
513    /// # Example
514    /// ```rust
515    /// // Create an `Option` component.
516    /// let option = Option::new(...)?;
517    ///
518    /// // Set a preconstructed `Option` component.
519    /// ContainerBuilder::new()
520    ///     .option_expl(option);
521    /// ```
522    pub fn option_expl(mut self, option: cpn::Option) -> Self {
523        self.container.add_option(option);
524        self
525    }
526
527    /// Adds an `Option` component to the `Container`.
528    ///
529    /// # Parameters
530    /// - `label`: A `&str` representing the text displayed for this option.
531    /// - `callback`: An optional `Callback` invoked when the option is selected.
532    ///
533    /// # Returns
534    /// - `Ok(ContainerBuilder)`: Returns `self`.
535    /// - `Err(FtuiError)`: Returns an error.
536    ///
537    /// # Example
538    /// ```rust
539    /// // Add an `Option` component with the label "Option" and no `Callback`.
540    /// ContainerBuilder::new()
541    ///     .option("Option", None)?;
542    /// ```
543    #[inline]
544    pub fn option(
545        self, label: &str, callback: impl Into<Option<Callback>>
546    ) -> FtuiResult<Self> {
547        Ok(self.option_expl(cpn::Option::new(label, callback)?))
548    }
549
550    /// Explicitly add an `Option` component to the `Container` and stores its ID. 
551    /// Unlike the `option_id` method, which takes a label and an optional callback
552    /// to internally constructs an `Option`, this method allows you to directly
553    /// provide a preconstructed `Option` component.
554    ///
555    /// # Parameters
556    /// - `option`: An `Option` component.
557    /// - `store_id`: A `&mut u16` to store the created `Option` component ID.
558    /// 
559    /// # Returns
560    /// - `Ok(ContainerBuilder)`: Returns `self`.
561    /// - `Err(FtuiError)`: Returns an error.
562    /// 
563    /// # Example
564    /// ```rust
565    /// let mut id = 0u16;
566    ///
567    /// // Create an `Option` component.
568    /// let option = Option::new(...)?;
569    ///
570    /// // Add the create `Option` component and storing the generated ID in `id`.
571    /// ContainerBuilder::new()
572    ///     .option_id(option, &mut id)?;
573    /// ```
574    pub fn option_id_expl(
575        mut self, option: cpn::Option, store_id: &mut u16
576    ) -> Self {
577        *store_id = self.container.add_option(option);
578        self
579    }
580
581    /// Adds an `Option` component to the `Container` and stores its ID.
582    ///
583    /// # Parameters
584    /// - `label`: The text displayed for this option.
585    /// - `callback`: An optional `Callback` invoked when the option is selected.
586    /// - `store_id`: A `&mut u16` to store the created `Option` component ID.
587    /// 
588    /// # Returns
589    /// - `Ok(ContainerBuilder)`: Returns `self`.
590    /// - `Err(FtuiError)`: Returns an error.
591    /// 
592    /// # Example
593    /// ```rust
594    /// let mut id = 0u16;
595    ///
596    /// // Add an `Option` labeled "Option" with no `Callback`,
597    /// // storing the generated ID in `id`.
598    /// ContainerBuilder::new()
599    ///     .option_id("Option", None, &mut id)?;
600    /// ```
601    #[inline]
602    pub fn option_id(
603        self,
604        label: &str, callback: impl Into<Option<Callback>>, store_id: &mut u16
605    ) -> FtuiResult<Self> {
606        Ok(self.option_id_expl(cpn::Option::new(label, callback)?, store_id))
607    }
608
609    /// Explicitly add a `Text` component to the `Container`. Unlike the `text`
610    /// method, which takes a label and flags to internally constructs an `Text`,
611    /// this method allows you to directly provide a preconstructed `Text` component.
612    ///
613    /// # Parameters
614    /// - `text`: A `Text` component.
615    ///
616    /// # Returns
617    /// - `ContainerBuilder`: Returns `self`.
618    ///
619    /// # Example
620    /// ```rust
621    /// // Create an `Text` component.
622    /// let text = Text::new(...)?;
623    ///
624    /// // Set a preconstructed `Text` component.
625    /// ContainerBuilder::new()
626    ///     .text_expl(text);
627    /// ```
628    pub fn text_expl(mut self, text: cpn::Text) -> Self {
629        self.container.add_text(text);
630        self
631    }
632
633    /// Adds a `Text` component to the `Container`.
634    /// 
635    /// # Parameters
636    /// - `label`: A `&str` representing the text to display.
637    /// - `flags`: A set of `TextFlags`, combined using the bitwise OR operator.
638    ///
639    /// # Notes
640    /// - This is what bitwise OR operator look like -> `flag1 | flag2 | flag3 ...`
641    ///
642    /// # Returns
643    /// - `Ok(ContainerBuilder)`: Returns `self`.
644    /// - `Err(FtuiError)`: Returns an error.
645    ///
646    /// # Example
647    /// ```rust
648    /// // Create a `Text` component labeled "Text", right-aligned and with
649    /// // a magenta background.
650    /// ContainerBuilder::new()
651    ///     .text("Text", TextFlags::ALIGN_RIGHT | TextFlags::COLOR_MAGENTA_BACK)?;
652    /// ```
653    #[inline]
654    pub fn text(
655        self, label: &str, flags: impl Into<Option<cpn::TextFlags>>
656    ) -> FtuiResult<Self> {
657        Ok(self.text_expl(cpn::Text::new(label, flags)?))
658    }
659
660    /// Explicitly add a `Text` component to the `Container` and stores its ID. 
661    /// Unlike the `text_id` method, which takes a label and flags to internally
662    /// constructs an `Text`, this method allows you to directly provide a
663    /// preconstructed `Text` component.
664    ///
665    /// # Parameters
666    /// - `text`: An `Text` component.
667    /// - `store_id`: A `&mut u16` to store the created `Option` component ID.
668    /// 
669    /// # Returns
670    /// - `ContainerBuilder`: Returns `self`.
671    /// 
672    /// # Example
673    /// ```rust
674    /// let mut id = 0u16;
675    ///
676    /// // Create an `Text` component.
677    /// let text = Text::new(...)?;
678    ///
679    /// // Add the create `Text` component and storing the generated ID in `id`.
680    /// ContainerBuilder::new()
681    ///     .text_id_expl(text, &mut id)?;
682    /// ```
683    pub fn text_id_expl(mut self, text: cpn::Text, store_id: &mut u16) -> Self {
684        *store_id = self.container.add_text(text);
685        self
686    }
687
688    /// Adds a `Text` component to the `Container` and stores its ID.
689    /// 
690    /// # Parameters
691    /// - `label`: A `&str` representing the text to display.
692    /// - `flags`: A set of `TextFlags`, combined using the bitwise OR operator.
693    /// - `store_id`: A `&mut u16` to store the created `Text` component ID.
694    ///
695    /// # Notes
696    /// - This is what bitwise OR operator look like -> `flag1 | flag2 | flag3 ...`
697    ///
698    /// # Returns
699    /// - `Ok(ContainerBuilder)`: Returns `self`.
700    /// - `Err(FtuiError)`: Returns an error.
701    ///
702    /// # Example
703    /// ```rust
704    /// let mut id = 0u16;
705    ///
706    /// // Create a `Text` component labeled "Text", right-aligned and with
707    /// // a magenta background. storing the generated ID in `id`.
708    /// ContainerBuilder::new()
709    ///     .text(
710    ///         "Text",
711    ///         TextFlags::ALIGN_RIGHT | TextFlags::COLOR_MAGENTA_BACK, &mut id)?;
712    /// ```
713    #[inline]
714    pub fn text_id(
715        self, 
716        label: &str, flags: impl Into<Option<cpn::TextFlags>>, store_id: &mut u16
717    ) -> FtuiResult<Self> {
718        Ok(self.text_id_expl(cpn::Text::new(label, flags)?, store_id))
719    }
720
721    /// Explicitly add a normal `Separator` component to the `Container`. 
722    /// Unlike the `Separator` method, which takes a style and internally 
723    /// constructs a `Separator`, this method allows you to directly provide a
724    /// preconstructed `Separator` component.
725    ///
726    /// # Parameters
727    /// - `separator`: A `Separator` component.
728    ///
729    /// # Returns
730    /// - `ContainerBuilder`: Returns `self`.
731    ///
732    /// # Example
733    /// ```rust
734    /// // Create a normal `Separator` component.
735    /// let separator = Separator::normal(...)?;
736    ///
737    /// // Set a preconstructed `Header` component.
738    /// ContainerBuilder::new()
739    ///     .separator_normal_expl(separator);
740    /// ```
741    pub fn separator_normal_expl(mut self, separator: cpn::Separator) -> Self {
742        self.container.add_separator(separator);
743        self
744    }
745
746    /// Add a standard (non-dotted) `Separator` with the given style.
747    ///
748    /// # Parameters
749    /// - `style`: The visual style of the separator, specified as a `SeparatorStyle`.
750    ///
751    /// # Returns
752    /// - `ContainerBuilder`: Returns `self`.
753    ///
754    /// # Example
755    /// ```rust
756    /// // Add a normal separator with a solid style.
757    /// ContainerBuilder::new()
758    ///     separator_normal(SeparatorStyle::Solid);
759    /// ```
760    #[inline]
761    pub fn separator_normal(self, style: cpn::SeparatorStyle) -> Self {
762        self.separator_normal_expl(cpn::Separator::normal(style))
763    }
764
765    /// Explicitly add a dotted `Separator` component to the `Container`. 
766    /// Unlike the `Separator` method, which takes a style and internally 
767    /// constructs a `Separator`, this method allows you to directly provide a
768    /// preconstructed `Separator` component.
769    ///
770    /// # Parameters
771    /// - `separator`: A `Separator` component.
772    ///
773    /// # Returns
774    /// - `ContainerBuilder`: Returns `self`.
775    ///
776    /// # Example
777    /// ```rust
778    /// // Create a dotted `Separator` component.
779    /// let separator = Separator::dotted(...)?;
780    ///
781    /// // Set a preconstructed `Header` component.
782    /// ContainerBuilder::new()
783    ///     .separator_dotted_expl(separator);
784    /// ```
785    pub fn separator_dotted_expl(mut self, separator: cpn::Separator) -> Self {
786        self.container.add_separator(separator);
787        self
788    }
789
790    /// Add a dotted `Separator` with the given style.
791    ///
792    /// # Parameters
793    /// - `style`: The visual style of the separator, specified as a `SeparatorStyle`.
794    ///
795    /// # Returns
796    /// - `ContainerBuilder`: Returns `self`.
797    ///
798    /// # Example
799    /// ```rust
800    /// // Add a dotted separator with a solid style.
801    /// ContainerBuilder::new()
802    ///     separator_dotted(SeparatorStyle::Solid);
803    /// ```
804    #[inline]
805    pub fn separator_dotted(self, style: cpn::SeparatorStyle) -> Self {
806        self.separator_dotted_expl(cpn::Separator::dotted(style))
807    }
808
809    /// Explicitly sets a `Selector` component for the `Container`. Unlike the `selector`
810    /// method, which takes triggers and internally constructs a `selector`, this 
811    /// method allows you to directly provide a preconstructed `Selector` component.
812    ///
813    /// # Parameters
814    /// - `selector`: A `Selector` component.
815    ///
816    /// # Returns
817    /// - `ContainerBuilder`: Returns `self`.
818    ///
819    /// # Example
820    /// ```rust
821    /// // Create a `Header` component.
822    /// let selector = Selector::new(...)?;
823    ///
824    /// // Set a preconstructed `Selector` component.
825    /// ContainerBuilder::new()
826    ///     .selector_expl(selector);
827    /// ```
828    pub fn selector_expl(mut self, selector: cpn::Selector) -> Self {
829        self.container.set_selector(selector);
830        self
831    }
832
833    /// Set a `Selector` for the `Container` to handle user navigation.
834    ///
835    /// # Parameters 
836    /// - `up_trig`: A `Trigger` that moves the selector up when activated.
837    /// - `down_trig`: A `Trigger` that moves the selector down when activated.
838    /// - `selc_trig`: A `Trigger` that confirms the selection when activated.
839    ///
840    /// # Returns
841    /// - `ContainerBuilder`: Returns `self`.
842    /// 
843    /// # Example
844    /// ```rust
845    /// // A `Trigger` that always activate.
846    /// tui::trg_new_trigger_func!(always_true_trigger, _arg, {
847    ///     Ok(true)
848    /// });
849    /// 
850    /// // A `Trigger` that never activate .
851    /// tui::trg_new_trigger_func!(always_false_trigger, _arg, {
852    ///     Ok(false)
853    /// });
854    ///
855    /// // Set a `Selector` that always moves down.
856    /// ContainerBuilder::new()
857    ///     .selector(
858    ///         Trigger::no_arg(always_false_trigger),
859    ///         Trigger::no_arg(always_true_trigger),
860    ///         Trigger::no_arg(always_false_trigger),
861    ///     );
862    /// ```
863    #[inline]
864    pub fn selector(
865        self, up_trig: Trigger, down_trig: Trigger, selc_trig: Trigger
866    ) -> Self {
867        self.selector_expl(cpn::Selector::new(up_trig, down_trig, selc_trig))
868    }
869
870    /// Sets a `Selector` with no `Trigger`s for the `Container`.
871    ///
872    /// In this case, navigation must be handled manually using the following methods:
873    /// - `Container::selector_up`
874    /// - `Container::selector_down`
875    /// - `Container::selector_select`
876    ///
877    /// # Returns
878    /// - `ContainerBuilder`: Returns `self`.
879    ///
880    /// # Example
881    /// ```rust
882    /// // Set a `Selector` with no `Trigger`s.
883    /// ContainerBuilder::new()
884    ///     .selector_no_triggers();
885    /// ```
886    pub fn selector_no_triggers(mut self) -> Self {
887        self.container.set_selector(cpn::Selector::no_triggers());
888        self
889    }
890
891    /// Finalizes the construction of a `Container`. This method should be called
892    /// after all desired components have been added using the builder pattern.
893    /// It consumes `self` and returns the completed `Container`.
894    ///
895    /// # Returns
896    /// - `Container`: Returns the created `Container`.
897    ///
898    /// # Example
899    /// ```rust
900    /// let container: Container = ContainerBuilder::new()
901    ///     .header(...)?
902    ///     .option(...)?
903    ///     .option(...)?
904    ///     .separator_normal(...)
905    ///     .text(...)?
906    ///     .selector(...)?
907    ///     .build(); // Finalize and retrieve the constructed container.
908    /// ```
909    pub fn build(self) -> Container {
910        self.container
911    }
912}