Skip to main content

tui_rule/
lib.rs

1pub mod presets;
2use colorgrad::Gradient;
3use derive_builder::Builder;
4use getset::{Getters, Setters};
5use ratatui::{
6    layout::Margin,
7    prelude::{Alignment, Buffer, Rect},
8    style::Color,
9    text::Line,
10    widgets::{Padding, Widget, WidgetRef},
11};
12#[cfg(feature = "serde")]
13use serde::{Deserialize, Serialize};
14#[cfg(feature = "all")]
15/// ## The Rule widget
16/// ### Allows:
17///     - Vertical alignment
18///     - Horizontal alignment
19///     - Horizontal and vertical paddings
20///     - Center symbols
21///     - Horizontal and vertical orientation
22///     - Colorgrad gradients
23///     - Start and end symbols
24pub struct Rule {
25    pub gradient: Option<Box<dyn Gradient>>,
26    pub symbol_set: Set,
27    pub orientation: Orientation,
28    pub padding: Padding,
29    pub vertical_alignment: VerticalAlignment,
30    pub horizontal_alignment: Alignment,
31    pub extra_rep_1: usize,
32    pub extra_rep_2: usize,
33    pub bg: Bg,
34    pub area_margin: Margin,
35}
36pub enum Bg {
37    None,
38    Solid(Color),
39    Gradient,
40    GradientCustom(Box<dyn Gradient>),
41}
42#[macro_export]
43macro_rules! create_segment {
44    ($set:expr, $p_1:expr, $p_2:expr, $base_area:expr, $orientation:expr, $h_alignment:expr, $v_alignment:expr, $extra_rep_1:expr, $extra_rep_2:expr) => {{
45        let rep_count: f32 = ($base_area / 2.0) - 1.0;
46        let seg1 = $set.rep_1.to_string().repeat(
47            (rep_count.floor() as usize)
48                .saturating_sub($p_1)
49                .saturating_add($extra_rep_1),
50        );
51        let seg2 = $set.rep_2.to_string().repeat(
52            (rep_count.round() as usize)
53                .saturating_sub($p_2 + 1)
54                .saturating_add($extra_rep_2),
55        );
56        let mut ln = String::with_capacity(
57            $p_1 + $p_2
58                + 1
59                + seg1.len()
60                + 1
61                + seg2.len()
62                + 5,
63        );
64        ln.push_str(&String::from(" ").repeat(
65            match $orientation {
66                Orientation::Horizontal => {
67                    match $h_alignment {
68                        Alignment::Left => 0,
69                        Alignment::Center => $p_1,
70
71                        Alignment::Right => {
72                            $p_1.saturating_add($p_2)
73                        }
74                    }
75                }
76                Orientation::Vertical => match $v_alignment
77                {
78                    VerticalAlignment::Top => 0,
79                    VerticalAlignment::Center => $p_1,
80
81                    VerticalAlignment::Bottom => {
82                        $p_1.saturating_add($p_2)
83                    }
84                },
85            } as usize,
86        ));
87        ln.push($set.start);
88        ln.push_str(&seg1);
89        ln.push($set.center);
90        ln.push_str(&seg2);
91        ln.push($set.end);
92        ln.push_str(&String::from(" ").repeat(
93            match $orientation {
94                Orientation::Horizontal => {
95                    match $h_alignment {
96                        Alignment::Left => {
97                            $p_2.saturating_add($p_1)
98                        }
99                        Alignment::Center => $p_2,
100                        Alignment::Right => 0,
101                    }
102                }
103                Orientation::Vertical => match $v_alignment
104                {
105                    VerticalAlignment::Top => {
106                        $p_1.saturating_add($p_2)
107                    }
108                    VerticalAlignment::Center => $p_2,
109                    VerticalAlignment::Bottom => 0,
110                },
111            } as usize,
112        ));
113        ln
114    }};
115}
116/// ### Symbol set struct
117/// ```
118/// let set = Set {
119///     start: '+',
120///     rep_1: '─',
121///     center: '+',
122///     rep_2: '─',
123///     end: '+',
124/// };
125/// let rule = Rule::from_set(set);
126/// // Contents would be "+───+───+"
127/// frame.render_widget(rule, frame.area());
128/// ```
129#[cfg_attr(
130    feature = "serde",
131    derive(Serialize, Deserialize)
132)]
133#[derive(Builder, Getters, Setters, Debug, Clone)]
134pub struct Set {
135    #[builder(default = "'─'")]
136    pub start: char,
137    #[builder(default = "'─'")]
138    pub end: char,
139    #[builder(default = "'─'")]
140    pub rep_1: char,
141    #[builder(default = "'─'")]
142    pub rep_2: char,
143    #[builder(default = "'─'")]
144    pub center: char,
145}
146/// controls rule orientation
147#[cfg_attr(
148    feature = "serde",
149    derive(Serialize, Deserialize)
150)]
151#[derive(Clone, Debug, PartialEq, Hash)]
152pub enum Orientation {
153    Vertical,
154    Horizontal,
155}
156/// vertical version of the Alignment enum
157#[cfg_attr(
158    feature = "serde",
159    derive(Serialize, Deserialize)
160)]
161#[derive(Clone, Debug, PartialEq, Hash)]
162pub enum VerticalAlignment {
163    Top,
164    Center,
165    Bottom,
166}
167/// # Macro for generating gradient text that returns a `Vec<Span>` with the inputted gradient.
168/// # Parameters
169/// 1. any type that can be converted to Line (String, Line, &str, Vec<Span>)
170/// 2. a colorgrad gradient (can be either Box<dyn Gradient> or an owned type)
171///
172/// ```rust
173///     let gradient_text = generate_gradient_text!("Rainbow Text", colorgrad::preset::rainbow());
174///     // displays "Rainbow Text" with a rainbow gradient
175///     buf.set_line(1, 1, &gradient_text, gradient_text.width())
176/// ```
177#[macro_export]
178macro_rules! generate_gradient_text {
179    ($txt:expr, $gr:expr) => {{
180        use ratatui::prelude::{Color, Style};
181        let mut ln: Line = $txt.into();
182        ln.spans = create_raw_spans!(ln.spans[0].content);
183        let mut new_text = Vec::new();
184        for (s, c) in ln
185            .spans
186            .clone()
187            .into_iter()
188            .zip($gr.colors(ln.width()))
189        {
190            new_text.push(s.style(Style::new().fg(
191                Color::Rgb(
192                    (c.r * 255.0) as u8,
193                    (c.g * 255.0) as u8,
194                    (c.b * 255.0) as u8,
195                ),
196            )));
197        }
198        new_text
199    }};
200    ($txt:expr, $gr:expr, $bgtype:expr) => {{
201        use ratatui::prelude::{Color, Style};
202        let mut ln: Line = $txt.into();
203        ln.spans = create_raw_spans!(ln.spans[0].content);
204        let mut new_text = Vec::new();
205        match $bgtype {
206            Bg::GradientCustom(grad) => {
207                for (s, (c, c2)) in
208                    ln.spans.clone().into_iter().zip(
209                        $gr.colors(ln.width())
210                            .into_iter()
211                            .zip(grad.colors(ln.width())),
212                    )
213                {
214                    new_text.push(
215                        s.style(
216                            Style::new()
217                                .fg(Color::Rgb(
218                                    (c.r * 255.0) as u8,
219                                    (c.g * 255.0) as u8,
220                                    (c.b * 255.0) as u8,
221                                ))
222                                .bg(Color::Rgb(
223                                    (c2.r * 255.0) as u8,
224                                    (c2.g * 255.0) as u8,
225                                    (c2.b * 255.0) as u8,
226                                )),
227                        ),
228                    );
229                }
230            }
231            _ => {
232                for (s, c) in ln
233                    .spans
234                    .clone()
235                    .into_iter()
236                    .zip($gr.colors(ln.width()))
237                {
238                    let c = Color::Rgb(
239                        (c.r * 255.0) as u8,
240                        (c.g * 255.0) as u8,
241                        (c.b * 255.0) as u8,
242                    );
243                    new_text.push(s.style(
244                        Style::new().fg(c).bg(
245                            match $bgtype {
246                                Bg::Solid(color) => *color,
247                                Bg::Gradient => c,
248                                _ => c,
249                            },
250                        ),
251                    ));
252                }
253            }
254        }
255        new_text
256    }};
257}
258#[cfg(feature = "all")]
259impl Default for Rule {
260    fn default() -> Self {
261        Self::new()
262    }
263}
264#[cfg(feature = "all")]
265impl Rule {
266    /// generates a new rule that looks like `─────────────` with no gradient and no padding
267    /// centered horizontally and vertically by default
268    pub fn new() -> Self {
269        Self {
270            gradient: None,
271            symbol_set: Set {
272                start: '─',
273                end: '─',
274                center: '─',
275                rep_1: '─',
276                rep_2: '─',
277            },
278            padding: Padding::new(0, 0, 0, 0),
279            orientation: Orientation::Horizontal,
280            horizontal_alignment: Alignment::Center,
281            vertical_alignment: VerticalAlignment::Center,
282            bg: Bg::None,
283            area_margin: Margin::new(1, 1),
284            extra_rep_1: 0,
285            extra_rep_2: 0,
286        }
287    }
288    pub fn area_margin(mut self, margin: Margin) -> Self {
289        self.area_margin = margin;
290        self
291    }
292    /// makes the bg solid
293    pub fn bg_solid(mut self, c: Color) -> Self {
294        self.bg = Bg::Solid(c);
295        self
296    }
297    /// makes the bg use the same gradient as the fg
298    pub fn bg_same_gradient(mut self) -> Self {
299        self.bg = Bg::Gradient;
300        self
301    }
302    pub fn extra_rep_1(mut self, rep: usize) -> Self {
303        self.extra_rep_1 = rep;
304        self
305    }
306    pub fn extra_rep_2(mut self, rep: usize) -> Self {
307        self.extra_rep_2 = rep;
308        self
309    }
310    pub fn extra_rep(
311        mut self,
312        rep_1: usize,
313        rep_2: usize,
314    ) -> Self {
315        self.extra_rep_1 = rep_1;
316        self.extra_rep_2 = rep_2;
317        self
318    }
319    /// makes the bg a custom gradient
320    pub fn bg_gradient<G: Gradient + 'static>(
321        mut self,
322        g: G,
323    ) -> Self {
324        self.bg = Bg::GradientCustom(Box::<G>::new(g));
325        self
326    }
327    pub fn bg(mut self, bg: Bg) -> Self {
328        self.bg = bg;
329        self
330    }
331    /// creates a new vertical rule
332    pub fn new_vertical() -> Self {
333        Self::new().vertical()
334    }
335    /// Creates a new rule instance from a Set struct
336    /// ```
337    /// let rule = Rule::from_set(presets::horizontal::ASCII);
338    /// // Has the start, end, center, right, and left symbols from the horizontal ASCII preset
339    /// frame.render_widget(rule, frame.area())
340    /// ```
341    pub fn from_set(set: Set) -> Self {
342        Self::new().with_set(set)
343    }
344    /// the new function and the with_gradient function combined
345    /// ```rust
346    ///     // displays a new rule with a rainbow gradient
347    ///     Rule::new_with_gradient(colorgrad::preset::rainbow())
348    /// ```
349    pub fn new_with_gradient<G: Gradient + 'static>(
350        gradient: G,
351    ) -> Self {
352        Self::new().with_gradient(gradient)
353    }
354    /// sets gradient for rule. uses colorgrad gradients
355    /// ```rust
356    ///     // displays `+=====+=====+` with a rainbow gradient
357    ///     Rule::default().with_gradient(colorgrad::preset::rainbow())
358    /// ```
359    pub fn with_gradient<G: Gradient + 'static>(
360        mut self,
361        gradient: G,
362    ) -> Self {
363        self.gradient = Some(Box::<G>::new(gradient));
364        self
365    }
366    /// sets the horizontal padding
367    pub fn horizontal_padding(
368        mut self,
369        padding: u16,
370    ) -> Self {
371        self.padding.right = padding;
372        self.padding.left = padding;
373        self
374    }
375    /// sets the vertical padding
376    pub fn vertical_padding(
377        mut self,
378        padding: u16,
379    ) -> Self {
380        self.padding.bottom = padding;
381        self.padding.top = padding;
382        self
383    }
384    /// Sets the right padding
385    pub fn right_padding(mut self, padding: u16) -> Self {
386        self.padding.right = padding;
387        self
388    }
389
390    /// Sets the left padding
391    pub fn left_padding(mut self, padding: u16) -> Self {
392        self.padding.left = padding;
393        self
394    }
395
396    /// Sets the top padding
397    pub fn top_padding(mut self, padding: u16) -> Self {
398        self.padding.top = padding;
399        self
400    }
401
402    /// Sets the bottom padding
403    pub fn bottom_padding(mut self, padding: u16) -> Self {
404        self.padding.bottom = padding;
405        self
406    }
407    /// Sets the end, start, right, center, and left symbols from the Set struct
408    pub fn with_set(mut self, set: Set) -> Self {
409        self = self
410            .end(set.end)
411            .start(set.start)
412            .rep_2(set.rep_2)
413            .rep_1(set.rep_1)
414            .center(set.center);
415        self
416    }
417    /// makes the rule horizontal instead of vertical. Horizontal by default
418    pub fn horizontal(mut self) -> Self {
419        self.orientation = Orientation::Horizontal;
420        self
421    }
422    /// makes the rule a vertical rule instead of horizontal
423    pub fn vertical(mut self) -> Self {
424        self.orientation = Orientation::Vertical;
425        self
426    }
427    /// repeated symbol for right side
428    /// ```rust
429    ///     Rule::default().rep_2('-')
430    /// ```
431    /// `+=====+-----+`
432    pub fn rep_2(mut self, symb: char) -> Self {
433        self.symbol_set.rep_2 = symb;
434        self
435    }
436    /// repeated symbol for left side
437    /// ```rust
438    ///     Rule::default().rep_1('-')
439    /// ```
440    /// `+-----+=====+`
441    pub fn rep_1(mut self, symb: char) -> Self {
442        self.symbol_set.rep_1 = symb;
443        self
444    }
445    /// first symbol
446    /// ```rust
447    ///     Rule::default().start('%')
448    /// ```
449    /// `%=====+=====+`
450    pub fn start(mut self, symb: char) -> Self {
451        self.symbol_set.start = symb;
452        self
453    }
454    /// last symbol
455    ///```rust
456    ///     Rule::default().end('%');
457    ///```   
458    /// `+=====+=====%`
459    pub fn end(mut self, symb: char) -> Self {
460        self.symbol_set.end = symb;
461        self
462    }
463    /// center symbol  
464    ///```rust
465    ///     Rule::default().center('%')
466    ///```
467    /// `+=====%=====+`
468    pub fn center(mut self, symb: char) -> Self {
469        self.symbol_set.center = symb;
470        self
471    }
472    /// the rep_1 and the rep_2 functions in one
473    pub fn main_symbol(mut self, symb: char) -> Self {
474        self = self.rep_1(symb).rep_2(symb);
475        self
476    }
477    pub fn padding(mut self, padding: Padding) -> Self {
478        self.padding = padding;
479        self
480    }
481    /// sets rule orientation
482    /// ```rust
483    ///     // creates a vertical rule
484    ///     Rule::default().orientation(Orientation::Vertical)
485    /// ```
486    /// /// using the `.horizontal()` and `.vertical()` methods instead is recommended for simplicity
487    pub fn orientation(
488        mut self,
489        orientation: Orientation,
490    ) -> Self {
491        self.orientation = orientation;
492        self
493    }
494    /// sets vertical alignment
495    /// centered by default
496    /// ```rust
497    ///     // creates a rule thats vertically aligned to the top
498    ///     Rule::default().vertical_alignment(VerticalAlignment::Top)
499    /// ```
500    pub fn vertical_alignment(
501        mut self,
502        alignment: VerticalAlignment,
503    ) -> Self {
504        self.vertical_alignment = alignment;
505        self
506    }
507    // sets horizontal alignment
508    pub fn horizontal_alignment(
509        mut self,
510        alignment: Alignment,
511    ) -> Self {
512        self.horizontal_alignment = alignment;
513        self
514    }
515}
516#[cfg(feature = "all")]
517impl Widget for Rule {
518    fn render(self, area_old: Rect, buf: &mut Buffer) {
519        self.render_ref(area_old, buf);
520    }
521}
522#[cfg(test)]
523mod tests {
524    use ratatui::widgets::Block;
525    #[test]
526    pub fn test_hr() {
527        use super::presets::test_sets::HORIZONTAL;
528        use super::*;
529        let mut buffer =
530            Buffer::empty(Rect::new(0, 0, 49, 19));
531        Block::bordered()
532            .title_top(
533                Line::raw("Horizontal Rule").centered(),
534            )
535            .title_bottom(
536                Line::raw(" Vertical Alignment: Center ")
537                    .centered(),
538            )
539            .render(buffer.area, &mut buffer);
540        Rule::from_set(HORIZONTAL)
541            .horizontal_padding(1)
542            .vertical_alignment(VerticalAlignment::Center)
543            .horizontal()
544            .render(buffer.area, &mut buffer);
545        #[rustfmt::skip]
546        let expected = Buffer::with_lines([
547                "┌────────────────Horizontal Rule────────────────┐",
548                "│                                               │",
549                "│                                               │",
550                "│                                               │",
551                "│                                               │",
552                "│                                               │",
553                "│                                               │",
554                "│                                               │",
555                "│                                               │",
556                "│ +─────────────────────+─────────────────────+ │",
557                "│                                               │",
558                "│                                               │",
559                "│                                               │",
560                "│                                               │",
561                "│                                               │",
562                "│                                               │",
563                "│                                               │",
564                "│                                               │",
565                "└───────── Vertical Alignment: Center ──────────┘",
566            ]);
567        assert_eq!(buffer, expected);
568        buffer = Buffer::empty(Rect::new(0, 0, 49, 19));
569        Block::bordered()
570            .title_top(
571                Line::raw("Horizontal Rule").centered(),
572            )
573            .title_bottom(
574                Line::raw(" Vertical Alignment: Top ")
575                    .centered(),
576            )
577            .render(buffer.area, &mut buffer);
578        Rule::from_set(HORIZONTAL)
579            .horizontal_padding(1)
580            .vertical_alignment(VerticalAlignment::Top)
581            .horizontal()
582            .render(buffer.area, &mut buffer);
583        #[rustfmt::skip]
584        let expected = Buffer::with_lines([
585                "┌────────────────Horizontal Rule────────────────┐",
586                "│ +─────────────────────+─────────────────────+ │",
587                "│                                               │",
588                "│                                               │",
589                "│                                               │",
590                "│                                               │",
591                "│                                               │",
592                "│                                               │",
593                "│                                               │",
594                "│                                               │",
595                "│                                               │",
596                "│                                               │",
597                "│                                               │",
598                "│                                               │",
599                "│                                               │",
600                "│                                               │",
601                "│                                               │",
602                "│                                               │",
603                "└─────────── Vertical Alignment: Top ───────────┘",
604            ]);
605        assert_eq!(buffer, expected);
606        buffer = Buffer::empty(Rect::new(0, 0, 49, 19));
607        Block::bordered()
608            .title_top(
609                Line::raw("Horizontal Rule").centered(),
610            )
611            .title_bottom(
612                Line::raw(" Vertical Alignment: Bottom ")
613                    .centered(),
614            )
615            .render(buffer.area, &mut buffer);
616        Rule::from_set(HORIZONTAL)
617            .horizontal_padding(1)
618            .vertical_alignment(VerticalAlignment::Bottom)
619            .horizontal()
620            .render(buffer.area, &mut buffer);
621        #[rustfmt::skip]
622        let expected = Buffer::with_lines([
623                "┌────────────────Horizontal Rule────────────────┐",
624                "│                                               │",
625                "│                                               │",
626                "│                                               │",
627                "│                                               │",
628                "│                                               │",
629                "│                                               │",
630                "│                                               │",
631                "│                                               │",
632                "│                                               │",
633                "│                                               │",
634                "│                                               │",
635                "│                                               │",
636                "│                                               │",
637                "│                                               │",
638                "│                                               │",
639                "│                                               │",
640                "│ +─────────────────────+─────────────────────+ │",
641                "└───────── Vertical Alignment: Bottom ──────────┘",
642            ]);
643        assert_eq!(buffer, expected);
644    }
645    #[test]
646    pub fn test_vr() {
647        use super::presets::test_sets::VERTICAL;
648        use super::*;
649        let mut buffer =
650            Buffer::empty(Rect::new(0, 0, 49, 19));
651        Block::bordered()
652            .title_top(
653                Line::raw("Vertical Rule").centered(),
654            )
655            .title_bottom(
656                Line::raw(" Horizontal Alignment: Center ")
657                    .centered(),
658            )
659            .render(buffer.area, &mut buffer);
660        Rule::from_set(VERTICAL)
661            .vertical()
662            .vertical_padding(1)
663            .horizontal_alignment(Alignment::Center)
664            .render(buffer.area, &mut buffer);
665        #[rustfmt::skip]
666        let expected = Buffer::with_lines([
667            "┌─────────────────Vertical Rule─────────────────┐",
668            "│                                               │",
669            "│                       +                       │",
670            "│                       │                       │",
671            "│                       │                       │",
672            "│                       │                       │",
673            "│                       │                       │",
674            "│                       │                       │",
675            "│                       │                       │",
676            "│                       +                       │",
677            "│                       │                       │",
678            "│                       │                       │",
679            "│                       │                       │",
680            "│                       │                       │",
681            "│                       │                       │",
682            "│                       │                       │",
683            "│                       +                       │",
684            "│                                               │",
685            "└──────── Horizontal Alignment: Center ─────────┘",
686        ]);
687        assert_eq!(buffer, expected);
688        buffer = Buffer::empty(Rect::new(0, 0, 49, 19));
689        Block::bordered()
690            .title_top(
691                Line::raw("Vertical Rule").centered(),
692            )
693            .title_bottom(
694                Line::raw(" Horizontal Alignment: Left ")
695                    .centered(),
696            )
697            .render(buffer.area, &mut buffer);
698        Rule::from_set(VERTICAL)
699            .vertical()
700            .vertical_padding(1)
701            .horizontal_alignment(Alignment::Left)
702            .render(buffer.area, &mut buffer);
703        #[rustfmt::skip]
704        let expected = Buffer::with_lines([
705            "┌─────────────────Vertical Rule─────────────────┐",
706            "│                                               │",
707            "│+                                              │",
708            "││                                              │",
709            "││                                              │",
710            "││                                              │",
711            "││                                              │",
712            "││                                              │",
713            "││                                              │",
714            "│+                                              │",
715            "││                                              │",
716            "││                                              │",
717            "││                                              │",
718            "││                                              │",
719            "││                                              │",
720            "││                                              │",
721            "│+                                              │",
722            "│                                               │",
723            "└───────── Horizontal Alignment: Left ──────────┘",
724        ]);
725        assert_eq!(buffer, expected);
726        #[rustfmt::skip]
727        let expected = Buffer::with_lines([
728            "┌─────────────────Vertical Rule─────────────────┐",
729            "│                                               │",
730            "│                                              +│",
731            "│                                              ││",
732            "│                                              ││",
733            "│                                              ││",
734            "│                                              ││",
735            "│                                              ││",
736            "│                                              ││",
737            "│                                              +│",
738            "│                                              ││",
739            "│                                              ││",
740            "│                                              ││",
741            "│                                              ││",
742            "│                                              ││",
743            "│                                              ││",
744            "│                                              +│",
745            "│                                               │",
746            "└───────── Horizontal Alignment: Right ─────────┘",
747        ]);
748        buffer = Buffer::empty(Rect::new(0, 0, 49, 19));
749        Block::bordered()
750            .title_top(
751                Line::raw("Vertical Rule").centered(),
752            )
753            .title_bottom(
754                Line::raw(" Horizontal Alignment: Right ")
755                    .centered(),
756            )
757            .render(buffer.area, &mut buffer);
758        Rule::new()
759            .with_set(VERTICAL)
760            .vertical()
761            .vertical_padding(1)
762            .main_symbol('│')
763            .horizontal_alignment(Alignment::Right)
764            .render(buffer.area, &mut buffer);
765        assert_eq!(buffer, expected);
766    }
767}
768pub mod macros {
769    #[cfg(feature = "utils")]
770    #[macro_export]
771    macro_rules! gen_main {
772        () => {
773            fn main() -> io::Result<()> {
774                let mut terminal = ratatui::init();
775                let app_result = run(&mut terminal);
776                ratatui::restore();
777                app_result
778            }
779        };
780    }
781    #[cfg(feature = "utils")]
782    #[macro_export]
783    macro_rules! gen_example_code {
784        ($fun:item) => {
785            tui_rule::gen_use!();
786            tui_rule::gen_run!($fun);
787            tui_rule::gen_main!();
788        };
789    }
790    #[cfg(feature = "utils")]
791    #[macro_export]
792    macro_rules! gen_run {
793        ($fun:item) => {
794            $fun
795        };
796    }
797    #[cfg(feature = "utils")]
798    #[macro_export]
799    macro_rules! gen_use {
800        () => {
801            use colorgrad::Gradient;
802            use crossterm::event::{
803                self, Event, KeyCode, KeyEvent,
804                KeyEventKind,
805            };
806            use ratatui::{
807                buffer::Buffer,
808                layout::Rect,
809                prelude::{Alignment, Color, Style},
810                text::Line,
811                widgets::{Block, Widget},
812                DefaultTerminal, Frame,
813            };
814            use std::{io, rc::Rc};
815            use tui_rule::*;
816        };
817    }
818    #[macro_export]
819    macro_rules! create_raw_spans {
820        ($string:expr) => {
821            $string
822                .chars()
823                .map(String::from)
824                .map(ratatui::text::Span::from)
825                .collect::<Vec<ratatui::text::Span>>()
826        };
827    }
828}
829
830impl WidgetRef for Rule {
831    fn render_ref(
832        &self,
833        mut area_old: Rect,
834        buf: &mut Buffer,
835    ) {
836        let (p_l, p_r, p_t, p_b) = (
837            self.padding.left,
838            self.padding.right,
839            self.padding.top,
840            self.padding.bottom,
841        );
842        if self.orientation == Orientation::Horizontal {
843            area_old.y = match self.vertical_alignment {
844                VerticalAlignment::Top => area_old
845                    .y
846                    .saturating_sub(p_b)
847                    .saturating_add(p_t),
848                VerticalAlignment::Center => {
849                    (area_old.bottom() / 2)
850                        .saturating_sub(1 + p_b)
851                        .saturating_add(p_t)
852                }
853                VerticalAlignment::Bottom => area_old
854                    .bottom()
855                    .saturating_sub(
856                        1 + p_b
857                            + self.area_margin.vertical * 2,
858                    )
859                    .saturating_add(p_t),
860            }
861            .saturating_sub(self.extra_rep_1 as u16);
862        };
863        if self.orientation == Orientation::Vertical {
864            area_old.x = match self.horizontal_alignment {
865                Alignment::Left => area_old
866                    .x
867                    .saturating_sub(p_r)
868                    .saturating_add(p_l),
869                Alignment::Center => (area_old.right() / 2)
870                    .saturating_sub(1 + p_r)
871                    .saturating_add(p_l),
872
873                Alignment::Right => {
874                    area_old.right().saturating_sub(
875                        1 + p_r
876                            + self.area_margin.horizontal
877                                * 2,
878                    )
879                }
880            }
881            .saturating_sub(self.extra_rep_1 as u16);
882        };
883
884        let area = area_old.inner(self.area_margin);
885
886        let ln = create_segment!(
887            self.symbol_set,
888            match self.orientation {
889                Orientation::Vertical => p_t,
890                Orientation::Horizontal => p_l,
891            } as usize,
892            match self.orientation {
893                Orientation::Vertical => p_b,
894                Orientation::Horizontal => p_r,
895            } as usize,
896            match self.orientation {
897                Orientation::Horizontal =>
898                    area.width as f32,
899                Orientation::Vertical => area.height as f32,
900            },
901            self.orientation,
902            self.horizontal_alignment,
903            self.vertical_alignment,
904            self.extra_rep_1,
905            self.extra_rep_2
906        );
907
908        let ln = if let Some(boxed) = &self.gradient {
909            match self.bg {
910                Bg::None => Line::from(
911                    generate_gradient_text!(ln, boxed),
912                ),
913                _ => Line::from(generate_gradient_text!(
914                    ln, boxed, &self.bg
915                )),
916            }
917        } else {
918            Line::from(crate::create_raw_spans!(ln))
919        };
920        match self.orientation {
921            Orientation::Horizontal => {
922                buf.set_line(
923                    area.x,
924                    area.y,
925                    &ln,
926                    ln.spans.len() as u16 + 1,
927                );
928            }
929            Orientation::Vertical => {
930                for (y_n, s) in ln.iter().enumerate() {
931                    buf.set_span(
932                        area.x,
933                        area.y + y_n as u16,
934                        s,
935                        1,
936                    );
937                }
938            }
939        }
940    }
941}