tui_gradient_block/lib.rs
1use bitflags::bitflags;
2use ratatui::{
3 buffer::Buffer,
4 layout::Rect,
5 style::{Color, Style},
6 symbols::border::{DOUBLE, PLAIN, ROUNDED, THICK},
7 text::{Line, Span},
8 widgets::{Block, Borders, Paragraph, Widget, Wrap},
9};
10use std::rc::Rc;
11use unicode_width::UnicodeWidthChar;
12/// A struct that represents a customizable block with gradient text, borders, and other visual elements.
13///
14/// This struct allows you to create and manage blocks that have a gradient color effect for text,
15/// customizable borders, and areas with specific alignments and fill styles.
16///
17/// # Fields
18/// - `bordertype`: Specifies the type of border style used for the block.
19/// - `fill`: Defines the fill style for the block's area (e.g., solid or gradient).
20/// - `top_titles`: A vector of tuples where each tuple contains:
21/// - A `String` representing the title text.
22/// - A `TitleAlignment` indicating the alignment of the title.
23/// - An optional tuple of gradient colors (represented as a vector of `(u8, u8, u8)` tuples) and
24/// a factor that controls the spread of the gradient effect.
25/// - `bottom_titles`: A similar vector to `top_titles`, but for titles placed at the bottom of the block.
26/// - `border_symbols`: Defines the symbols used for the block's borders.
27/// - `border_segments`: Defines the segments of the block's border.
28/// - `split_segments`: Specifies how the block's border is split into different sections.
29/// - `area`: A `Rect` representing the block's area, typically used for positioning and layout.
30///
31/// # Example
32/// ```rust
33/// let gradient_block = TuiGradientblock {
34/// bordertype: BorderStyle::Solid,
35/// fill: Fill::SolidColor((255, 0, 0)),
36/// top_titles: vec![("Top Title".to_owned(), TitleAlignment::Center, None)],
37/// bottom_titles: vec![("Bottom Title".to_owned(), TitleAlignment::Right, None)],
38/// border_symbols: BorderSymbols::Default,
39/// border_segments: BorderSegments::Full,
40/// split_segments: SplitBorderSegments::None,
41/// area: Rect::new(0, 0, 10, 5),
42/// };
43/// ```
44
45#[derive(Clone)]
46pub struct TuiGradientblock {
47 bordertype: BorderStyle,
48 fill: Fill,
49 top_titles: Vec<(String, TitleAlignment, Option<(Vec<(u8, u8, u8)>, f32)>)>,
50 bottom_titles: Vec<(String, TitleAlignment, Option<(Vec<(u8, u8, u8)>, f32)>)>,
51 border_symbols: BorderSymbols,
52 border_segments: BorderSegments,
53 split_segments: SplitBorderSegments,
54 area: Rect,
55}
56/// A constant error message displayed when there are not enough colors provided for a gradient.
57/// It encourages the user to use at least two colors for a gradient effect or repeat the same color
58/// if a solid color is desired.
59///
60/// Example of how to provide colors for a solid color:
61/// ❌ `[(250, 2, 238)]`
62/// ✅ `[(250, 2, 238), (250, 2, 238)]`
63pub const ERROR_MESSAGE: &str = "
64╓───── IMPORTANT ─────╖
65║ ║
66║ Use at least two ║
67║ colors. ║
68║ ║
69║ If you want to use ║
70║ a solid color, ║
71║ enter the same ║
72║ color more than once║
73║ ║
74║ Example: Solid pink ║
75║ ║
76║ ❌ [(250, 2, 238)] ║
77║ ✅ [ ║
78║ (250, 2, 238), ║
79║ (250, 2, 238), ║
80║ ] ║
81║ ║
82╙─────────────────────╜
83";
84
85/// A set of predefined border styles for different visual aesthetics. Each `BorderStyleInfo`
86/// instance defines the characters to be used for different parts of the border (corners, sides, and centers).
87///
88/// # Variants:
89/// - `MISC1`: A style with standard "+" corners, and "=" for top/bottom edges, with "|" for side edges.
90/// - `MISC2`: A style with "╘" and "╛" for the bottom corners, and "=" for top and bottom edges.
91/// - `MISC3`: A style with "╬" for the corners and sides, and "═" for the top and bottom edges.
92/// - `MISC4`: A unique style with "$" corners, "~" for center sides, and "─" for top and bottom edges.
93///
94/// These styles can be used to customize the appearance of borders for blocks, areas, or text layouts.
95pub const MISC1: BorderStyleInfo = BorderStyleInfo {
96 top_left: '+',
97 bottom_left: '+',
98 top_right: '+',
99 bottom_right: '+',
100 top: '=',
101 bottom: '=',
102 left: '|',
103 right: '|',
104 bottom_center: '=',
105 top_center: '=',
106 right_center: '|',
107 left_center: '|',
108};
109
110/// A border style with special bottom corners "╘" and "╛" and "═" edges for a different aesthetic.
111pub const MISC2: BorderStyleInfo = BorderStyleInfo {
112 top_left: '╔',
113 bottom_left: '╚',
114 top_right: '╗',
115 bottom_right: '╝',
116 top: '═',
117 bottom: '═',
118 right_center: '╬',
119 left_center: '╬',
120 left: '║',
121 right: '║',
122 bottom_center: '╬',
123 top_center: '╬',
124};
125
126/// A border style with "╬" for the corners and sides, giving a more solid and ornate border.
127pub const MISC3: BorderStyleInfo = BorderStyleInfo {
128 top_left: '╬',
129 bottom_left: '╬',
130 top_right: '╬',
131 bottom_right: '╬',
132 top: '═',
133 bottom: '═',
134 left: '║',
135 right: '║',
136 bottom_center: '═',
137 top_center: '═',
138 right_center: '║',
139 left_center: '║',
140};
141
142/// A more unique border style featuring "$" for the corners, "~" for the center sides, and "─" for the edges.
143pub const MISC4: BorderStyleInfo = BorderStyleInfo {
144 top_left: '$',
145 bottom_left: '$',
146 top_right: '$',
147 bottom_right: '$',
148 top: '─',
149 bottom: '─',
150 left: '│',
151 right: '│',
152 bottom_center: '$',
153 top_center: '~',
154 right_center: '~',
155 left_center: '~',
156};
157
158/// A struct that defines the characters used for different parts of a border's visual appearance.
159/// Each field specifies a character to represent a particular section of the border.
160///
161/// # Fields
162/// - `top_left`: The character used for the top-left corner of the border.
163/// - `bottom_left`: The character used for the bottom-left corner of the border.
164/// - `top_right`: The character used for the top-right corner of the border.
165/// - `bottom_right`: The character used for the bottom-right corner of the border.
166/// - `top`: The character used for the top edge of the border.
167/// - `bottom`: The character used for the bottom edge of the border.
168/// - `left`: The character used for the left side of the border.
169/// - `right`: The character used for the right side of the border.
170/// - `bottom_center`: The character used for the bottom center section of the border.
171/// - `top_center`: The character used for the top center section of the border.
172/// - `right_center`: The character used for the right center section of the border.
173/// - `left_center`: The character used for the left center section of the border.
174pub struct BorderStyleInfo {
175 pub top_left: char,
176 pub bottom_left: char,
177 pub top_right: char,
178 pub bottom_right: char,
179 pub top: char,
180 pub bottom: char,
181 pub left: char,
182 pub right: char,
183 pub bottom_center: char,
184 pub top_center: char,
185 pub right_center: char,
186 pub left_center: char,
187}
188
189impl TuiGradientblock {
190 pub fn new(area: &Rect, split_segments: SplitBorderSegments) -> Self {
191 Self {
192 bordertype: BorderStyle::Plain,
193 fill: Fill {
194 fill_string: None,
195 gradient: None,
196 },
197 top_titles: Vec::new(),
198 bottom_titles: Vec::new(),
199 border_symbols: BorderSymbols {
200 top_left: None,
201 top_right: None,
202 bottom_left: None,
203 bottom_right: None,
204 top_center: None,
205 bottom_center: None,
206 left_center: None,
207 right_center: None,
208 bottom_horizontal: None,
209 top_horizontal: None,
210 left_vertical: None,
211 right_vertical: None,
212 top_horizontal_right: None,
213 bottom_horizontal_right: None,
214 top_horizontal_left: None,
215 bottom_horizontal_left: None,
216 top_vertical_right: None,
217 bottom_vertical_right: None,
218 top_vertical_left: None,
219 bottom_vertical_left: None,
220 },
221 border_segments: BorderSegments::new(area),
222 split_segments,
223 area: *area,
224 }
225 }
226 /// Interpolates between two colors based on a factor and a time value (t).
227 /// This function computes a smooth transition between the `start` and `end` RGB colors
228 /// using the given parameter `t` (which represents the position between the colors)
229 /// and `factor` (which controls the curve of the interpolation).
230 ///
231 /// # Parameters
232 /// - `start`: A tuple representing the RGB values of the starting color (each value is a `u8`).
233 /// - `end`: A tuple representing the RGB values of the ending color (each value is a `u8`).
234 /// - `t`: A floating-point value (`f32`) between 0 and 1 that represents the interpolation factor,
235 /// where `t = 0` gives the `start` color, and `t = 1` gives the `end` color.
236 /// - `factor`: A floating-point value (`f32`) that influences the smoothness or intensity of the interpolation.
237 /// Higher values make the interpolation curve sharper, while lower values create a more gradual transition.
238 ///
239 /// # Returns
240 /// A tuple `(u8, u8, u8)` representing the interpolated RGB color, where each value is a byte (0-255).
241 ///
242 /// # Example
243 /// ```rust
244 /// let start_color = (255, 0, 0); // Red
245 /// let end_color = (0, 0, 255); // Blue
246 /// let t = 0.5; // Midway between the colors
247 /// let factor = 1.0; // Linear interpolation
248 /// let result = interpolate_color(start_color, end_color, t, factor);
249 /// ```
250 /// In this example, `result` will contain an RGB value that is a mix of red and blue, producing purple.
251 /// # Note
252 /// - The interpolation is calculated using a smooth function based on `t` raised to the power of `factor`,
253 /// which controls the rate of transition between the `start` and `end` colors.
254 pub fn interpolate_color(
255 start: (u8, u8, u8),
256 end: (u8, u8, u8),
257 t: f32,
258 factor: f32,
259 ) -> (u8, u8, u8) {
260 let lerp = |s, e| ((1.0 - t.powf(factor)) * (s as f32) + t.powf(factor) * (e as f32)) as u8;
261 (
262 lerp(start.0, end.0),
263 lerp(start.1, end.1),
264 lerp(start.2, end.2),
265 )
266 }
267 /// Creates a gradient effect on a given text by interpolating between a list of colors
268 /// based on the position of each character in the string.
269 ///
270 /// # Parameters
271 /// - `text`: A reference to the input `String` for which the gradient effect will be applied.
272 /// - `colors`: A vector of tuples representing the RGB values of the gradient colors. Each tuple
273 /// contains three `u8` values representing the Red, Green, and Blue components of a color.
274 /// - `factor`: A floating-point value (`f32`) that determines the intensity or spread of the gradient.
275 /// Higher values will increase the spread of the gradient colors.
276 ///
277 /// # Returns
278 /// A `Vec<Span<'static>>` containing `Span` elements, where each `Span` represents a styled portion
279 /// of the input text with the corresponding color from the gradient.
280 ///
281 /// # Panics
282 /// This function will panic if the number of colors in `colors` is less than 2. A gradient requires
283 /// at least two colors to interpolate between. If fewer colors are provided, the following error message
284 /// will be displayed:
285 ///
286 /// ```text
287 /// ╓───── IMPORTANT ─────╖
288 /// ║ ║
289 /// ║ Use at least two ║
290 /// ║ colors. ║
291 /// ║ ║
292 /// ║ If you want to use ║
293 /// ║ a solid color, ║
294 /// ║ enter the same ║
295 /// ║ color more than once║
296 /// ║ ║
297 /// ║ Example: Solid pink ║
298 /// ║ ║
299 /// ║ ❌ [(250, 2, 238)] ║
300 /// ║ ✅ [ ║
301 /// ║ (250, 2, 238), ║
302 /// ║ (250, 2, 238), ║
303 /// ║ ] ║
304 /// ║ ║
305 /// ╙─────────────────────╜
306 /// ```
307
308 /// # Example
309 /// ```rust
310 /// let text = "Hello, World!".to_string();
311 /// let colors = vec![(255, 0, 0), (0, 255, 0), (0, 0, 255)];
312 /// let factor = 1.0;
313 /// let gradient_text = create_gradient_text(&text, colors, factor);
314 /// ```
315 /// In the above example, the `gradient_text` will be a vector of `Span`s with the text "Hello, World!"
316 /// styled with a gradient transitioning from red to green to blue.
317 /// # Note
318 /// - The `interpolate_color` function is used internally to calculate the intermediate colors based on the
319 /// position of the character relative to the total width of the text.
320
321 pub fn create_gradient_text(
322 text: &str,
323 colors: Vec<(u8, u8, u8)>,
324 factor: f32,
325 ) -> Vec<Span<'static>> {
326 let mut gradient_colors = Vec::new();
327
328 let char_widths: Vec<usize> = text.chars().map(|c| c.width().unwrap_or(1)).collect();
329 let total_width: usize = char_widths.iter().sum();
330
331 let num_colors = colors.len();
332
333 if num_colors < 2 {
334 ratatui::restore();
335 panic!("{}", ERROR_MESSAGE);
336 }
337 let mut accumulated_width = 0.0;
338 for (i, _) in text.chars().enumerate() {
339 let char_width = char_widths[i] as f32;
340 let relative_pos = accumulated_width / total_width as f32;
341 let section_index = (relative_pos * (num_colors - 1) as f32).floor() as usize;
342 let t = (relative_pos * (num_colors - 1) as f32) % 1.0;
343 let next_color_index = (section_index + 1).min(num_colors - 1);
344 gradient_colors.push(Self::interpolate_color(
345 colors[section_index],
346 colors[next_color_index],
347 t,
348 factor,
349 ));
350 accumulated_width += char_width;
351 }
352 let mut gradient_text = Vec::new();
353 for (i, c) in text.chars().zip(gradient_colors) {
354 gradient_text.push(Span::styled(
355 i.to_string(),
356 Style::default().fg(Color::Rgb(c.0, c.1, c.2)),
357 ));
358 }
359 gradient_text
360 }
361 /// Sets gradient colors for specific segments of the border.
362 ///
363 /// # Parameters
364 /// - `gradientlist`: A vector of tuples where each tuple contains:
365 /// - A `GradientSegments` enum value specifying which border segment to apply the gradient to.
366 /// - A vector of RGB color tuples (`Vec<(u8, u8, u8)>`) representing the gradient colors.
367 /// - A `f32` value representing the gradient factor (e.g., intensity or blending weight).
368 ///
369 /// # Notes
370 /// - If a gradient should be a solid color, provide the same RGB value twice.
371 /// - Gradients must have at least two different colors to transition properly.
372 ///
373 /// # Example 1: Applying a gradient to the top border
374 /// ```
375 /// let border = TuiGradientblock::new().set_gradients(vec![
376 /// (GradientSegments::Top, vec![(255, 0, 0), (0, 0, 255)], 0.5),
377 /// ]);
378 /// ```
379 ///
380 /// # Example 2: Applying a solid color to the right border
381 /// ```
382 /// let border = TuiGradientblock::new().set_gradients(vec![
383 /// (GradientSegments::Right, vec![(50, 50, 50), (50, 50, 50)], 1.0),
384 /// ]);
385 /// ```
386 pub fn set_gradients(
387 mut self,
388 gradientlist: Vec<(GradientSegments, Vec<(u8, u8, u8)>, f32)>,
389 ) -> Self {
390 for (segment, colors, factor) in gradientlist {
391 let gradient_data = Some((colors, factor));
392
393 match segment {
394 GradientSegments::Top => {
395 self.border_segments.top_ln.gradient = gradient_data;
396 self.border_segments.top_ln.should_use_gradient = true;
397 }
398 GradientSegments::Bottom => {
399 self.border_segments.bottom_ln.gradient = gradient_data;
400 self.border_segments.bottom_ln.should_use_gradient = true;
401 }
402 GradientSegments::Left => {
403 self.border_segments.left_ln.gradient = gradient_data;
404 self.border_segments.left_ln.should_use_gradient = true;
405 }
406 GradientSegments::Right => {
407 self.border_segments.right_ln.gradient = gradient_data;
408 self.border_segments.right_ln.should_use_gradient = true;
409 }
410 GradientSegments::TopHorizontalLeftLn => {
411 self.border_segments.top_horizontal_left_ln.gradient = gradient_data;
412 self.border_segments
413 .top_horizontal_left_ln
414 .should_use_gradient = true;
415 }
416 GradientSegments::TopHorizontalRightLn => {
417 self.border_segments.top_horizontal_right_ln.gradient = gradient_data;
418 self.border_segments
419 .top_horizontal_right_ln
420 .should_use_gradient = true;
421 }
422 GradientSegments::BottomHorizontalLeftLn => {
423 self.border_segments.bottom_horizontal_left_ln.gradient = gradient_data;
424 self.border_segments
425 .bottom_horizontal_left_ln
426 .should_use_gradient = true;
427 }
428 GradientSegments::BottomHorizontalRightLn => {
429 self.border_segments.bottom_horizontal_right_ln.gradient = gradient_data;
430 self.border_segments
431 .bottom_horizontal_right_ln
432 .should_use_gradient = true;
433 }
434 GradientSegments::TopVerticalLeftLn => {
435 self.border_segments.top_vertical_left_ln.gradient = gradient_data;
436 self.border_segments
437 .top_vertical_left_ln
438 .should_use_gradient = true;
439 }
440 GradientSegments::TopVerticalRightLn => {
441 self.border_segments.top_vertical_right_ln.gradient = gradient_data;
442 self.border_segments
443 .top_vertical_right_ln
444 .should_use_gradient = true;
445 }
446 GradientSegments::BottomVerticalLeftLn => {
447 self.border_segments.bottom_vertical_left_ln.gradient = gradient_data;
448 self.border_segments
449 .bottom_vertical_left_ln
450 .should_use_gradient = true;
451 }
452 GradientSegments::BottomVerticalRightLn => {
453 self.border_segments.bottom_vertical_right_ln.gradient = gradient_data;
454 self.border_segments
455 .bottom_vertical_right_ln
456 .should_use_gradient = true;
457 }
458 }
459 }
460 self
461 }
462
463 /// Sets the border style for the block.
464 ///
465 /// If this function is not called, the border will be plain by default.
466 ///
467 /// # Parameters
468 /// - `style`: A `BorderStyle` enum value that determines the appearance of the border.
469 /// - `BorderStyle::Plain`: A simple, unstyled border.
470 /// - `BorderStyle::Double`: A double-lined border.
471 /// - `BorderStyle::Thick`: A thick-stroked border.
472 /// - `BorderStyle::Rounded`: A border with rounded corners.
473 /// - `BorderStyle::MiscBorder(MiscBorderTypes)`: A selection of miscellaneous predefined border styles.
474 /// - `BorderStyle::CustomBorderType`: Allows custom border symbols to be set manually.
475 ///
476 /// # Example 1: Using a standard border style
477 /// ```
478 /// let border = TuiGradientblock::new().border_style(BorderStyle::Double);
479 /// ```
480 ///
481 /// # Example 2: Using a miscellaneous border style
482 /// ```
483 /// let border = TuiGradientblock::new().border_style(BorderStyle::MiscBorder(MiscBorderTypes::Misc2));
484 /// ```
485 ///
486 /// # Example 3: Using a custom border type
487 /// ```
488 /// let border = TuiGradientblock::new()
489 /// .border_style(BorderStyle::CustomBorderType)
490 /// .top_left('╔')
491 /// .top_right('╗')
492 /// .bottom_left('╚')
493 /// .bottom_right('╝');
494 /// ```
495 /// Sets the border style of the block.
496 ///
497 /// This function allows setting a predefined border style or a custom one.
498 ///
499 /// # Parameters
500 /// - `style`: A `BorderStyle` enum variant specifying the desired border style.
501 ///
502 /// # Behavior
503 /// - `BorderStyle::CustomBorderType`: Does not set predefined symbols, allowing manual customization.
504 /// - `BorderStyle::MiscBorder(MiscBorderTypes)`: Uses a predefined miscellaneous border style.
505 /// - `BorderStyle::Plain`, `BorderStyle::Double`, `BorderStyle::Thick`, `BorderStyle::Rounded`:
506 /// Sets the block's borders to one of these predefined styles.
507 ///
508 /// # Example
509 /// ```
510 /// let block = TuiGradientblock::new().border_style(BorderStyle::Double);
511 /// ```
512 pub fn border_style(mut self, style: BorderStyle) -> Self {
513 match &style {
514 BorderStyle::CustomBorderType => {
515 // Does not set predefined symbols, allowing manual customization.
516 }
517 BorderStyle::MiscBorder(t) => {
518 let miscborder = match t {
519 MiscBorderTypes::Misc1 => MISC1,
520 MiscBorderTypes::Misc2 => MISC2,
521 MiscBorderTypes::Misc3 => MISC3,
522 MiscBorderTypes::Misc4 => MISC4,
523 };
524 self.border_symbols.top_left = Some(miscborder.top_left);
525 self.border_symbols.bottom_left = Some(miscborder.bottom_left);
526 self.border_symbols.top_right = Some(miscborder.top_right);
527 self.border_symbols.bottom_right = Some(miscborder.bottom_right);
528 self.border_symbols.top_horizontal = Some(miscborder.top);
529 self.border_symbols.bottom_horizontal = Some(miscborder.bottom);
530 self.border_symbols.left_vertical = Some(miscborder.left);
531 self.border_symbols.right_vertical = Some(miscborder.right);
532 self.border_symbols.bottom_center = Some(miscborder.bottom_center);
533 self.border_symbols.top_center = Some(miscborder.top_center);
534 self.border_symbols.right_center = Some(miscborder.right_center);
535 self.border_symbols.left_center = Some(miscborder.left_center);
536 }
537 regborder => {
538 let reg = match regborder {
539 BorderStyle::Plain => PLAIN,
540 BorderStyle::Double => DOUBLE,
541 BorderStyle::Thick => THICK,
542 BorderStyle::Rounded => ROUNDED,
543 _ => PLAIN, // Fallback to plain border.
544 };
545 self.border_symbols.top_left = Some(reg.top_left.parse().unwrap());
546 self.border_symbols.bottom_left = Some(reg.bottom_left.parse().unwrap());
547 self.border_symbols.top_right = Some(reg.top_right.parse().unwrap());
548 self.border_symbols.bottom_right = Some(reg.bottom_right.parse().unwrap());
549 self.border_symbols.top_horizontal = Some(reg.horizontal_top.parse().unwrap());
550 self.border_symbols.bottom_horizontal =
551 Some(reg.horizontal_bottom.parse().unwrap());
552 self.border_symbols.left_vertical = Some(reg.vertical_left.parse().unwrap());
553 self.border_symbols.right_vertical = Some(reg.vertical_right.parse().unwrap());
554 self.border_symbols.bottom_center = Some(reg.horizontal_bottom.parse().unwrap());
555 self.border_symbols.top_center = Some(reg.horizontal_top.parse().unwrap());
556 self.border_symbols.right_center = Some(reg.vertical_right.parse().unwrap());
557 self.border_symbols.left_center = Some(reg.vertical_left.parse().unwrap());
558 }
559 };
560 self.bordertype = style;
561 self
562 }
563
564 /// Sets the titles that appear at the bottom of the border.
565 ///
566 /// # Parameters
567 /// - `titles`: A vector of tuples where each tuple contains:
568 /// - A `String` representing the title text.
569 /// - A `TitleAlignment` indicating how the title should be aligned (e.g., left, center, right).
570 /// - An optional tuple containing a vector of RGB colors and a gradient factor (f32).
571 ///
572 /// # Example
573 /// ```
574 /// let border = Border::new().bottom_titles(vec![
575 /// ("Footer", TitleAlignment::Center, Some((vec![(255, 0, 0), (190, 3, 252)], 0.5))),
576 /// ]);
577 /// ```
578 pub fn bottom_titles(
579 mut self,
580 titles: Vec<(String, TitleAlignment, Option<(Vec<(u8, u8, u8)>, f32)>)>,
581 ) -> Self {
582 for (title, align, colors) in titles {
583 self.bottom_titles.push((title, align, colors));
584 }
585 self
586 }
587
588 /// Sets the titles that appear at the top of the border.
589 ///
590 /// # Parameters
591 /// - `titles`: A vector of tuples where each tuple contains:
592 /// - A `String` representing the title text.
593 /// - A `TitleAlignment` indicating how the title should be aligned (e.g., left, center, right).
594 /// - An optional tuple containing a vector of RGB colors and a gradient factor (f32).
595 ///
596 /// # Example 1: Without Gradient
597 /// ```
598 /// let border = TuiGradientblock::new().top_titles(vec![
599 /// ("Header", TitleAlignment::Left, None),
600 /// ]);
601 /// ```
602 ///
603 /// # Example 2: With Gradient
604 /// In this example, we use two different colors for the gradient (Red to Blue).
605 /// ```
606 /// let border = TuiGradientblock::new().top_titles(vec![
607 /// ("Header", TitleAlignment::Center, Some((vec![(255, 0, 0), (0, 0, 255)], 0.5))),
608 /// ]);
609 /// ```
610 pub fn top_titles(
611 mut self,
612 titles: Vec<(String, TitleAlignment, Option<(Vec<(u8, u8, u8)>, f32)>)>,
613 ) -> Self {
614 for (title, align, colors) in titles {
615 self.top_titles.push((title, align, colors));
616 }
617 self
618 }
619
620 /// Sets the symbol for the top-right corner of the border.
621 ///
622 /// # Parameters
623 /// - `symb`: A `char` representing the symbol to be used in the top-right corner.
624 ///
625 /// # Example
626 /// ```
627 /// let border = TuiGradientblock::new().top_right('#');
628 /// ```
629 pub fn top_right(mut self, symb: char) -> Self {
630 self.border_symbols.top_right = Some(symb);
631 self
632 }
633
634 /// Sets the symbol for the top-left corner of the border.
635 ///
636 /// # Parameters
637 /// - `symb`: A `char` representing the symbol to be used in the top-left corner.
638 ///
639 /// # Example
640 /// ```
641 /// let border = TuiGradientblock::new().top_left('*');
642 /// ```
643 pub fn top_left(mut self, symb: char) -> Self {
644 self.border_symbols.top_left = Some(symb);
645 self
646 }
647
648 /// Sets the symbol for the bottom-right corner of the border.
649 ///
650 /// # Parameters
651 /// - `symb`: A `char` representing the symbol to be used in the bottom-right corner.
652 ///
653 /// # Example
654 /// ```
655 /// let border = TuiGradientblock::new().bottom_right('%');
656 /// ```
657 pub fn bottom_right(mut self, symb: char) -> Self {
658 self.border_symbols.bottom_right = Some(symb);
659 self
660 }
661
662 /// Sets the symbol for the bottom-left corner of the border.
663 ///
664 /// # Parameters
665 /// - `symb`: A `char` representing the symbol to be used in the bottom-left corner.
666 ///
667 /// # Example
668 /// ```
669 /// let border = TuiGradientblock::new().bottom_left('@');
670 /// ```
671 pub fn bottom_left(mut self, symb: char) -> Self {
672 self.border_symbols.bottom_left = Some(symb);
673 self
674 }
675
676 /// Sets the symbol for the bottom horizontal segment.
677 ///
678 /// # Parameters
679 /// - `symb`: A `char` representing the symbol to be used for the bottom horizontal border.
680 ///
681 /// # Example
682 /// ```
683 /// let border = TuiGradientblockr::new().bottom_horizontal_symbol('-');
684 /// ```
685 pub fn bottom_horizontal_symbol(mut self, symb: char) -> Self {
686 self.border_symbols.bottom_horizontal = Some(symb);
687 self
688 }
689
690 /// Sets the symbol for the top horizontal border segment.
691 ///
692 /// # Parameters
693 /// - `symb`: A `char` representing the symbol to be used for the top horizontal border.
694 ///
695 /// # Example
696 /// ```
697 /// let border = Border::new().top_horizontal_symbol('=');
698 /// ```
699 pub fn top_horizontal_symbol(mut self, symb: char) -> Self {
700 self.border_symbols.top_horizontal = Some(symb);
701 self
702 }
703
704 /// Sets the symbol for the right vertical border segment.
705 ///
706 /// # Parameters
707 /// - `symb`: A `char` representing the symbol to be used for the right vertical border.
708 ///
709 /// # Example
710 /// ```
711 /// let border = TuiGradientblock::new().right_vertical_symbol('|');
712 /// ```
713 pub fn right_vertical_symbol(mut self, symb: char) -> Self {
714 self.border_symbols.right_vertical = Some(symb);
715 self
716 }
717 /// Sets the left vertical border symbol.
718 ///
719 /// # Example
720 /// ```
721 /// let widget = TuiGradientblock::new().left_vertical_symbol('|');
722 /// ```
723 pub fn left_vertical_symbol(mut self, symb: char) -> Self {
724 self.border_symbols.left_vertical = Some(symb);
725 self
726 }
727
728 /// Sets the top center border symbol.
729 ///
730 /// # Example
731 /// ```
732 /// let widget = TuiGradientblock::new().top_center_symbol('─');
733 /// ```
734 pub fn top_center_symbol(mut self, symb: char) -> Self {
735 self.border_symbols.top_center = Some(symb);
736 self
737 }
738
739 /// Sets the bottom center border symbol.
740 ///
741 /// # Example
742 /// ```
743 /// let widget = TuiGradientblock::new().bottom_center_symbol('═');
744 /// ```
745 pub fn bottom_center_symbol(mut self, symb: char) -> Self {
746 self.border_symbols.bottom_center = Some(symb);
747 self
748 }
749
750 /// Sets the left center vertical border symbol.
751 ///
752 /// # Example
753 /// ```
754 /// let widget = TuiGradientblock::new().left_center_symbol('+');
755 /// ```
756 pub fn left_center_symbol(mut self, symb: char) -> Self {
757 self.border_symbols.left_center = Some(symb);
758 self
759 }
760
761 /// Sets the right center vertical border symbol.
762 ///
763 /// # Example
764 /// ```
765 /// let widget = TuiGradientblock::new().right_center_symbol('+');
766 /// ```
767 pub fn right_center_symbol(mut self, symb: char) -> Self {
768 self.border_symbols.right_center = Some(symb);
769 self
770 }
771
772 /// Sets the top right horizontal border symbol.
773 ///
774 /// # Example
775 /// ```
776 /// let widget = TuiGradientblock::new().top_horizontal_right_symbol('┐');
777 /// ```
778 pub fn top_horizontal_right_symbol(mut self, symb: char) -> Self {
779 self.border_symbols.top_horizontal_right = Some(symb);
780 self
781 }
782 /// Sets the symbol used for the repeated section of the bottom horizontal border (right side).
783 ///
784 /// # Example
785 /// ```
786 /// let block = TuiGradientblock::new().bottom_horizontal_right_symbol('*');
787 /// ```
788 pub fn bottom_horizontal_right_symbol(mut self, symb: char) -> Self {
789 self.border_symbols.bottom_horizontal_right = Some(symb);
790 self
791 }
792
793 /// Sets the symbol for the top horizontal left connector.
794 ///
795 /// # Example
796 /// ```
797 /// let block = TuiGradientblock::new().top_horizontal_left_symbol('=');
798 /// ```
799 pub fn top_horizontal_left_symbol(mut self, symb: char) -> Self {
800 self.border_symbols.top_horizontal_left = Some(symb);
801 self
802 }
803
804 /// Sets the symbol for the bottom horizontal left connector.
805 ///
806 /// # Example
807 /// ```
808 /// let block = TuiGradientblock::new().bottom_horizontal_left_symbol('=');
809 /// ```
810 pub fn bottom_horizontal_left_symbol(mut self, symb: char) -> Self {
811 self.border_symbols.bottom_horizontal_left = Some(symb);
812 self
813 }
814
815 /// Sets the symbol for the top vertical right connector.
816 ///
817 /// # Example
818 /// ```
819 /// let block = TuiGradientblock::new().top_vertical_right_symbol('|');
820 /// ```
821 pub fn top_vertical_right_symbol(mut self, symb: char) -> Self {
822 self.border_symbols.top_vertical_right = Some(symb);
823 self
824 }
825
826 /// Sets the symbol for the bottom vertical right connector.
827 ///
828 /// # Example
829 /// ```
830 /// let block = TuiGradientblock::new().bottom_vertical_right_symbol('|');
831 /// ```
832 pub fn bottom_vertical_right_symbol(mut self, symb: char) -> Self {
833 self.border_symbols.bottom_vertical_right = Some(symb);
834 self
835 }
836
837 /// Sets the symbol for the top vertical left connector.
838 ///
839 /// # Example
840 /// ```
841 /// let block = TuiGradientblock::new().top_vertical_left_symbol('|');
842 /// ```
843 pub fn top_vertical_left_symbol(mut self, symb: char) -> Self {
844 self.border_symbols.top_vertical_left = Some(symb);
845 self
846 }
847
848 /// Sets the symbol for the bottom vertical left connector.
849 ///
850 /// # Example
851 /// ```
852 /// let block = TuiGradientblock::new().bottom_vertical_left_symbol('|');
853 /// ```
854 pub fn bottom_vertical_left_symbol(mut self, symb: char) -> Self {
855 self.border_symbols.bottom_vertical_left = Some(symb);
856 self
857 }
858
859 /// Sets the fill string for the block.
860 ///
861 /// This string is used to fill the inner area of the block.
862 ///
863 /// # Example
864 /// ```
865 /// let block = TuiGradientblock::new().fill_string(String::from("Hello"));
866 /// ```
867 pub fn fill_string(mut self, string: String) -> Self {
868 self.fill.fill_string = Some(string);
869 self
870 }
871
872 /// Sets the fill gradient for the block.
873 ///
874 /// The gradient is defined as a list of RGB colors and a factor to control the blending effect.
875 ///
876 /// # Example
877 /// ```
878 /// let colors = vec![(255, 0, 0), (0, 255, 0), (0, 0, 255)];
879 /// let block = TuiGradientblock::new().fill_gradient(colors, 0.5);
880 /// ```
881 pub fn fill_gradient(mut self, colors: Vec<(u8, u8, u8)>, factor: f32) -> Self {
882 self.fill.gradient = Some((colors, factor));
883 self
884 }
885
886 /// Sets the border line segments based on the area and border symbols.
887 ///
888 /// This method configures the border segments (top, bottom, left, right) and any possible splits
889 /// within the block. It calculates and sets the text for each border line segment using the provided
890 /// border symbols and the block's area. The function supports setting up horizontal and vertical
891 /// lines, as well as handling special cases where the border is split into smaller sections.
892 ///
893 /// **Important:**
894 /// - This function should be called **last** after all other block properties are set, as it depends
895 /// on the final values of the area and border symbols.
896 ///
897 /// # Behavior
898 /// - The function calculates the appropriate border segments, including top, bottom, left, and right borders.
899 /// - It uses the provided `border_symbols` to determine the characters used for the borders.
900 /// - If the `split_segments` attribute contains any of `TOP`, `BOTTOM`, `LEFT`, or `RIGHT`, the respective
901 /// segments are split into smaller parts, and the relevant segments are marked for rendering.
902 /// - In the case of the `ALL` split, all border lines are broken into smaller segments and set to be rendered.
903 /// - If no split is needed (`NONE`), the function disables the rendering of split border lines.
904 ///
905 /// # Parameters
906 /// - This method takes no parameters and modifies the internal state of the struct it's called on.
907 ///
908 /// # Returns
909 /// - A modified instance of the struct (self), with the border segments set according to the configurations.
910 pub fn set_lines(mut self) -> Self {
911 let border_symbols = &self.border_symbols;
912 let area = &self.area;
913 let top_horizontal = border_symbols
914 .top_horizontal
915 .unwrap_or(PLAIN.horizontal_top.parse().unwrap());
916 let left_vertical = border_symbols
917 .left_vertical
918 .unwrap_or(PLAIN.vertical_left.parse().unwrap());
919 let bottom_horizontal = border_symbols
920 .bottom_horizontal
921 .unwrap_or(PLAIN.horizontal_top.parse().unwrap());
922 let right_vertical = border_symbols
923 .right_vertical
924 .unwrap_or(PLAIN.vertical_right.parse().unwrap());
925 let top_center_char = border_symbols.top_center.unwrap_or(top_horizontal);
926 let bottom_center_char = border_symbols.bottom_center.unwrap_or(bottom_horizontal);
927 let left_center_char = border_symbols.left_center.unwrap_or(left_vertical);
928 let right_center_char = border_symbols.right_center.unwrap_or(right_vertical);
929 let top_right = border_symbols
930 .top_right
931 .unwrap_or(PLAIN.top_right.parse().unwrap());
932 let top_left: char = border_symbols
933 .top_left
934 .unwrap_or(PLAIN.top_left.parse().unwrap());
935 let bottom_right = border_symbols
936 .bottom_right
937 .unwrap_or(PLAIN.bottom_right.parse().unwrap());
938 let bottom_left = border_symbols
939 .bottom_left
940 .unwrap_or(PLAIN.bottom_left.parse().unwrap());
941 let top_horizontal_right = border_symbols
942 .top_horizontal_right
943 .unwrap_or(top_horizontal);
944 let bottom_horizontal_right = border_symbols
945 .bottom_horizontal_right
946 .unwrap_or(bottom_horizontal);
947 let top_horizontal_left = border_symbols.top_horizontal_left.unwrap_or(top_horizontal);
948 let bottom_horizontal_left = border_symbols
949 .bottom_horizontal_left
950 .unwrap_or(bottom_horizontal);
951 let top_vertical_right = border_symbols.top_vertical_right.unwrap_or(right_vertical);
952 let bottom_vertical_right = border_symbols
953 .bottom_vertical_right
954 .unwrap_or(right_vertical);
955 let top_vertical_left = border_symbols.top_vertical_left.unwrap_or(left_vertical);
956 let bottom_vertical_left = border_symbols.bottom_vertical_left.unwrap_or(left_vertical);
957 let top_horizontal_right_ln = format!(
958 "{}{}{}",
959 top_center_char,
960 top_horizontal_right
961 .to_string()
962 .repeat((area.width as usize - 1) / 2 - 1),
963 top_right,
964 );
965 let top_horizontal_left_ln = format!(
966 "{}{}{}",
967 top_left,
968 top_horizontal_left
969 .to_string()
970 .repeat((area.width as usize - 1) / 2 - 1),
971 top_center_char
972 );
973 let bottom_horizontal_left_ln = format!(
974 "{}{}{}",
975 bottom_left,
976 bottom_horizontal_left
977 .to_string()
978 .repeat((area.width as usize - 1) / 2 - 1),
979 bottom_center_char,
980 );
981 let bottom_horizontal_right_ln = format!(
982 "{}{}{}",
983 bottom_center_char,
984 bottom_horizontal_right
985 .to_string()
986 .repeat((area.width as usize - 1) / 2 - 1),
987 bottom_right,
988 );
989 let top_vertical_right_ln = format!(
990 "{}{}{}",
991 top_right,
992 top_vertical_right
993 .to_string()
994 .repeat(((area.height as usize + 1) / 2).saturating_sub(2)),
995 right_center_char
996 );
997 let top_vertical_left_ln = format!(
998 "{}{}{}",
999 top_left,
1000 top_vertical_left
1001 .to_string()
1002 .repeat(((area.height as usize + 1) / 2).saturating_sub(2)),
1003 left_center_char,
1004 );
1005 let bottom_vertical_left_ln = format!(
1006 "{}{}{}",
1007 left_center_char,
1008 bottom_vertical_left
1009 .to_string()
1010 .repeat((area.height as usize / 2).saturating_sub(1)),
1011 bottom_left,
1012 );
1013 let bottom_vertical_right_ln = format!(
1014 "{}{}{}",
1015 right_center_char,
1016 bottom_vertical_right
1017 .to_string()
1018 .repeat((area.height as usize / 2).saturating_sub(1)),
1019 bottom_right,
1020 );
1021 let top_ln = format!(
1022 "{}{}{}{}{}",
1023 top_left,
1024 top_horizontal_left
1025 .to_string()
1026 .repeat(((area.width as usize + 1) / 2).saturating_sub(2)),
1027 top_center_char,
1028 top_horizontal_right
1029 .to_string()
1030 .repeat((area.width as usize / 2).saturating_sub(1)),
1031 top_right,
1032 );
1033 let bottom_ln = format!(
1034 "{}{}{}{}{}",
1035 bottom_left,
1036 bottom_horizontal_left
1037 .to_string()
1038 .repeat(((area.width as usize + 1) / 2).saturating_sub(1)),
1039 bottom_center_char,
1040 bottom_horizontal_right
1041 .to_string()
1042 .repeat((area.width as usize / 2).saturating_sub(1)),
1043 bottom_right,
1044 );
1045 let right_ln = format!(
1046 "{}{}{}{}{}",
1047 top_right,
1048 top_vertical_right
1049 .to_string()
1050 .repeat(((area.height as usize + 1) / 2).saturating_sub(2)),
1051 right_center_char,
1052 bottom_vertical_right
1053 .to_string()
1054 .repeat((area.height as usize / 2).saturating_sub(1)),
1055 bottom_right,
1056 );
1057 let left_ln = format!(
1058 "{}{}{}{}{}",
1059 top_left,
1060 top_vertical_left
1061 .to_string()
1062 .repeat(((area.height as usize + 1) / 2).saturating_sub(2)),
1063 left_center_char,
1064 bottom_vertical_left
1065 .to_string()
1066 .repeat((area.height as usize / 2).saturating_sub(1)),
1067 bottom_left
1068 );
1069 self.border_segments.top_ln.should_be_rendered = true;
1070 self.border_segments.top_ln.segment_text = top_ln;
1071 self.border_segments.bottom_ln.should_be_rendered = true;
1072 self.border_segments.bottom_ln.segment_text = bottom_ln;
1073 self.border_segments.left_ln.should_be_rendered = true;
1074 self.border_segments.left_ln.segment_text = left_ln;
1075 self.border_segments.right_ln.should_be_rendered = true;
1076 self.border_segments.right_ln.segment_text = right_ln;
1077 if self.split_segments.contains(SplitBorderSegments::TOP) {
1078 self.border_segments.top_ln.should_be_rendered = false;
1079 self.border_segments
1080 .top_horizontal_left_ln
1081 .should_be_rendered = true;
1082 self.border_segments.top_horizontal_left_ln.segment_text = top_horizontal_left_ln;
1083 self.border_segments
1084 .top_horizontal_right_ln
1085 .should_be_rendered = true;
1086 self.border_segments.top_horizontal_right_ln.segment_text = top_horizontal_right_ln;
1087 }
1088
1089 if self.split_segments.contains(SplitBorderSegments::BOTTOM) {
1090 self.border_segments.bottom_ln.should_be_rendered = false;
1091 self.border_segments.bottom_horizontal_left_ln.segment_text = bottom_horizontal_left_ln;
1092 self.border_segments
1093 .bottom_horizontal_left_ln
1094 .should_be_rendered = true;
1095
1096 self.border_segments.bottom_horizontal_right_ln.segment_text =
1097 bottom_horizontal_right_ln;
1098 self.border_segments
1099 .bottom_horizontal_right_ln
1100 .should_be_rendered = true;
1101 }
1102 if self.split_segments.contains(SplitBorderSegments::LEFT) {
1103 self.border_segments.left_ln.should_be_rendered = false;
1104 self.border_segments.top_vertical_left_ln.should_be_rendered = true;
1105 self.border_segments.top_vertical_left_ln.segment_text = top_vertical_left_ln;
1106 self.border_segments
1107 .bottom_vertical_left_ln
1108 .should_be_rendered = true;
1109 self.border_segments.bottom_vertical_left_ln.segment_text = bottom_vertical_left_ln;
1110 }
1111 if self.split_segments.contains(SplitBorderSegments::RIGHT) {
1112 self.border_segments.right_ln.should_be_rendered = false;
1113 self.border_segments
1114 .top_vertical_right_ln
1115 .should_be_rendered = true;
1116 self.border_segments.top_vertical_right_ln.segment_text = top_vertical_right_ln;
1117 self.border_segments
1118 .bottom_vertical_right_ln
1119 .should_be_rendered = true;
1120 self.border_segments.bottom_vertical_right_ln.segment_text = bottom_vertical_right_ln;
1121 }
1122 if self.split_segments == SplitBorderSegments::ALL {
1123 self.border_segments.top_ln.should_be_rendered = false;
1124 self.border_segments.bottom_ln.should_be_rendered = false;
1125 self.border_segments.left_ln.should_be_rendered = false;
1126 self.border_segments.right_ln.should_be_rendered = false;
1127 self.border_segments
1128 .top_horizontal_left_ln
1129 .should_be_rendered = true;
1130 self.border_segments
1131 .top_horizontal_right_ln
1132 .should_be_rendered = true;
1133 self.border_segments
1134 .bottom_horizontal_left_ln
1135 .should_be_rendered = true;
1136 self.border_segments
1137 .bottom_horizontal_right_ln
1138 .should_be_rendered = true;
1139 self.border_segments.top_vertical_left_ln.should_be_rendered = true;
1140 self.border_segments
1141 .top_vertical_right_ln
1142 .should_be_rendered = true;
1143 self.border_segments
1144 .bottom_vertical_left_ln
1145 .should_be_rendered = true;
1146 self.border_segments
1147 .bottom_vertical_right_ln
1148 .should_be_rendered = true;
1149 }
1150
1151 if self.split_segments == SplitBorderSegments::NONE {
1152 self.border_segments
1153 .top_horizontal_left_ln
1154 .should_be_rendered = false;
1155 self.border_segments
1156 .top_horizontal_right_ln
1157 .should_be_rendered = false;
1158 self.border_segments
1159 .bottom_horizontal_left_ln
1160 .should_be_rendered = false;
1161 self.border_segments
1162 .bottom_horizontal_right_ln
1163 .should_be_rendered = false;
1164 }
1165 self
1166 }
1167 /// Renders the border segments of the block.
1168 ///
1169 /// This private function iterates through all defined border segments and renders only those
1170 /// that are marked to be rendered (`should_be_rendered == true`).
1171 ///
1172 /// It uses a list of tuples where each tuple contains a border segment and its corresponding
1173 /// render function. If a segment is marked as `should_be_rendered`, the associated render function
1174 /// is invoked to render that segment.
1175 ///
1176 /// # Parameters
1177 /// - `buf`: A mutable reference to the [`Buffer`] where the border segments will be drawn.
1178 ///
1179 /// # Behavior
1180 /// - The function checks each border segment and invokes the corresponding render function if the
1181 /// segment is set to be rendered.
1182 ///
1183 /// # Note
1184 /// This function is called internally by the program when rendering the custom block, and isn't meant
1185 /// to be directly called by users.
1186 fn render_block(&self, buf: &mut Buffer) {
1187 let segments: &[(&BorderSegment, fn(&Self, &mut Buffer))] = &[
1188 (&self.border_segments.top_ln, Self::render_top_ln),
1189 (&self.border_segments.bottom_ln, Self::render_bottom_ln),
1190 (&self.border_segments.left_ln, Self::render_left_ln),
1191 (&self.border_segments.right_ln, Self::render_right_ln),
1192 (
1193 &self.border_segments.top_horizontal_left_ln,
1194 Self::render_top_horizontal_left_ln,
1195 ),
1196 (
1197 &self.border_segments.top_horizontal_right_ln,
1198 Self::render_top_horizontal_right_ln,
1199 ),
1200 (
1201 &self.border_segments.bottom_vertical_left_ln,
1202 Self::render_bottom_vertical_left_ln,
1203 ),
1204 (
1205 &self.border_segments.bottom_vertical_right_ln,
1206 Self::render_bottom_vertical_right_ln,
1207 ),
1208 (
1209 &self.border_segments.bottom_horizontal_left_ln,
1210 Self::render_bottom_horizontal_left_ln,
1211 ),
1212 (
1213 &self.border_segments.bottom_horizontal_right_ln,
1214 Self::render_bottom_horizontal_right_ln,
1215 ),
1216 (
1217 &self.border_segments.top_vertical_left_ln,
1218 Self::render_top_vertical_left_ln,
1219 ),
1220 (
1221 &self.border_segments.top_vertical_right_ln,
1222 Self::render_top_vertical_right_ln,
1223 ),
1224 ];
1225
1226 for (segment, render_fn) in segments {
1227 if segment.should_be_rendered {
1228 render_fn(self, buf);
1229 }
1230 }
1231 }
1232
1233 /// Renders the top horizontal line of the border with an optional gradient.
1234 ///
1235 /// This function renders the top border line. If the `top_ln` segment should use a gradient,
1236 /// it applies the gradient to the segment text. Otherwise, it renders the text as-is.
1237 ///
1238 /// # Parameters:
1239 /// - `buf`: A mutable reference to the `Buffer` where the rendered output will be stored.
1240 ///
1241 /// ## Visual Representation:
1242 /// Without the function:
1243 /// ```
1244 /// +-----+
1245 /// | |
1246 /// | |
1247 /// + +
1248 /// | |
1249 /// | |
1250 /// +-----+
1251 /// ```
1252 fn render_top_ln(&self, buf: &mut Buffer) {
1253 let top_ln = self.border_segments.top_ln.clone();
1254 let used_top_ln = match top_ln.should_use_gradient {
1255 true => &Line::from(Self::create_gradient_text(
1256 &top_ln.segment_text,
1257 top_ln.gradient.clone().unwrap().0,
1258 top_ln.gradient.unwrap().1,
1259 )),
1260 false => &Line::from(top_ln.segment_text),
1261 };
1262 buf.set_line(top_ln.x, top_ln.y, used_top_ln, self.area.width);
1263 }
1264
1265 /// Renders the left vertical line of the border with an optional gradient.
1266 ///
1267 /// This function renders the left border line. If the `left_ln` segment should use a gradient,
1268 /// it applies the gradient to the segment text. Otherwise, it renders the text as-is.
1269 ///
1270 /// # Parameters:
1271 /// - `buf`: A mutable reference to the `Buffer` where the rendered output will be stored.
1272 ///
1273 /// ## Visual Representation:
1274 /// Without the function:
1275 /// ```
1276 /// +-----+
1277 /// | |
1278 /// | |
1279 /// + +
1280 /// | |
1281 /// | |
1282 /// +-----+
1283 /// ```
1284 fn render_left_ln(&self, buf: &mut Buffer) {
1285 let left_ln = self.border_segments.left_ln.clone();
1286 let used_left_ln: &Vec<Span> = match left_ln.should_use_gradient {
1287 true => &Self::create_gradient_text(
1288 &left_ln.segment_text,
1289 left_ln.gradient.clone().unwrap().0,
1290 left_ln.gradient.unwrap().1,
1291 ),
1292 false => &left_ln
1293 .segment_text
1294 .chars()
1295 .map(|i| Span::raw(i.to_string()))
1296 .collect(),
1297 };
1298 for (i, ln) in used_left_ln.iter().enumerate() {
1299 buf.set_span(left_ln.x, left_ln.y + i as u16, ln, 1);
1300 }
1301 }
1302
1303 /// Renders the bottom horizontal line of the border with an optional gradient.
1304 ///
1305 /// This function renders the bottom border line. If the `bottom_ln` segment should use a gradient,
1306 /// it applies the gradient to the segment text. Otherwise, it renders the text as-is.
1307 ///
1308 /// # Parameters:
1309 /// - `buf`: A mutable reference to the `Buffer` where the rendered output will be stored.
1310 ///
1311 /// ## Visual Representation:
1312 /// Without the function:
1313 /// ```
1314 /// +-----+
1315 /// | |
1316 /// | |
1317 /// + +
1318 /// | |
1319 /// | |
1320 /// +-----+
1321 /// ```
1322 fn render_bottom_ln(&self, buf: &mut Buffer) {
1323 let bottom_ln = self.border_segments.bottom_ln.clone();
1324 let used_bottom_ln = match bottom_ln.should_use_gradient {
1325 true => &Line::from(Self::create_gradient_text(
1326 &bottom_ln.segment_text,
1327 bottom_ln.gradient.clone().unwrap().0,
1328 bottom_ln.gradient.unwrap().1,
1329 )),
1330 false => &Line::from(bottom_ln.segment_text),
1331 };
1332 buf.set_line(bottom_ln.x, bottom_ln.y, used_bottom_ln, self.area.width);
1333 }
1334
1335 /// Renders the right vertical line of the border with an optional gradient.
1336 ///
1337 /// This function renders the right border line. If the `right_ln` segment should use a gradient,
1338 /// it applies the gradient to the segment text. Otherwise, it renders the text as-is.
1339 ///
1340 /// # Parameters:
1341 /// - `buf`: A mutable reference to the `Buffer` where the rendered output will be stored.
1342 ///
1343 /// ## Visual Representation:
1344 /// Without the function:
1345 /// ```
1346 /// +-----+
1347 /// | |
1348 /// | |
1349 /// + +
1350 /// | |
1351 /// | |
1352 /// +-----+
1353 /// ```
1354 fn render_right_ln(&self, buf: &mut Buffer) {
1355 let mut right_ln = self.border_segments.right_ln.clone();
1356
1357 let used_right_ln: Vec<Span> = if right_ln.should_use_gradient {
1358 Self::create_gradient_text(
1359 &right_ln.clone().segment_text,
1360 right_ln.gradient.clone().unwrap().0,
1361 right_ln.gradient.as_mut().unwrap().1,
1362 )
1363 } else {
1364 right_ln
1365 .clone()
1366 .segment_text
1367 .chars()
1368 .map(|i| Span::raw(i.to_string()))
1369 .collect()
1370 };
1371 for (i, span) in used_right_ln.iter().enumerate() {
1372 buf.set_span(right_ln.x, right_ln.y + i as u16, span, 1);
1373 }
1374 }
1375
1376 /// Renders the left side of the top horizontal line of the border with an optional gradient.
1377 ///
1378 /// This function renders the left part of the top horizontal line. If the `top_horizontal_left_ln` segment
1379 /// should use a gradient, it applies the gradient to the segment text. Otherwise, it renders the text as-is.
1380 ///
1381 /// # Parameters:
1382 /// - `buf`: A mutable reference to the `Buffer` where the rendered output will be stored.
1383 ///
1384 /// ## Visual Representation:
1385 /// Without the function:
1386 /// ```
1387 /// ****~~~
1388 /// ~ ~
1389 /// ~ ~
1390 /// ~ ~
1391 /// ~ ~
1392 /// ~ ~
1393 /// ~~~~~~~
1394 /// ```
1395 fn render_top_horizontal_left_ln(&self, buf: &mut Buffer) {
1396 let top_horizontal_left_ln = self.border_segments.top_horizontal_left_ln.clone();
1397 let used_top_horizontal_left_ln = match top_horizontal_left_ln.should_use_gradient {
1398 true => &Line::from(Self::create_gradient_text(
1399 &top_horizontal_left_ln.segment_text,
1400 top_horizontal_left_ln.gradient.clone().unwrap().0,
1401 top_horizontal_left_ln.gradient.unwrap().1,
1402 )),
1403 false => &Line::from(top_horizontal_left_ln.segment_text),
1404 };
1405 buf.set_line(
1406 top_horizontal_left_ln.x,
1407 top_horizontal_left_ln.y,
1408 used_top_horizontal_left_ln,
1409 self.area.width,
1410 );
1411 }
1412
1413 /// Renders the right side of the top horizontal line of the border with an optional gradient.
1414 ///
1415 /// This function renders the right part of the top horizontal line. If the `top_horizontal_right_ln` segment
1416 /// should use a gradient, it applies the gradient to the segment text. Otherwise, it renders the text as-is.
1417 ///
1418 /// # Parameters:
1419 /// - `buf`: A mutable reference to the `Buffer` where the rendered output will be stored.
1420 ///
1421 /// ## Visual Representation:
1422 /// Without the function:
1423 /// ```
1424 /// +--- +
1425 /// | |
1426 /// | |
1427 /// + +
1428 /// | |
1429 /// | |
1430 /// +-----+
1431 /// ```
1432 fn render_top_horizontal_right_ln(&self, buf: &mut Buffer) {
1433 let top_horizontal_right_ln = self.border_segments.top_horizontal_right_ln.clone();
1434 let used_top_horizontal_right_ln = match top_horizontal_right_ln.should_use_gradient {
1435 true => &Line::from(Self::create_gradient_text(
1436 &top_horizontal_right_ln.segment_text,
1437 top_horizontal_right_ln.gradient.clone().unwrap().0,
1438 top_horizontal_right_ln.gradient.unwrap().1,
1439 )),
1440 false => &Line::from(top_horizontal_right_ln.segment_text),
1441 };
1442 buf.set_line(
1443 top_horizontal_right_ln.x,
1444 top_horizontal_right_ln.y,
1445 used_top_horizontal_right_ln,
1446 self.area.width,
1447 );
1448 }
1449
1450 /// Renders the left side of the bottom horizontal line of the border with an optional gradient.
1451 ///
1452 /// This function renders the left part of the bottom horizontal line. If the segment should use a gradient,
1453 /// it applies the gradient to the segment text. Otherwise, it renders the text as-is.
1454 ///
1455 /// # Parameters:
1456 /// - `buf`: A mutable reference to the `Buffer` where the rendered output will be stored.
1457 ///
1458 /// ## Visual Representation:
1459 /// Without the function:
1460 /// ```
1461 /// +-----+
1462 /// | |
1463 /// | |
1464 /// + +
1465 /// | |
1466 /// | |
1467 /// + ---+
1468 /// ```
1469 fn render_bottom_horizontal_left_ln(&self, buf: &mut Buffer) {
1470 let bottom_horizontal_left_ln = self.border_segments.bottom_horizontal_left_ln.clone();
1471 let used_bottom_horizontal_left_ln = match bottom_horizontal_left_ln.should_use_gradient {
1472 true => &Line::from(Self::create_gradient_text(
1473 &bottom_horizontal_left_ln.segment_text,
1474 bottom_horizontal_left_ln.gradient.clone().unwrap().0,
1475 bottom_horizontal_left_ln.gradient.unwrap().1,
1476 )),
1477 false => &Line::from(bottom_horizontal_left_ln.segment_text),
1478 };
1479 buf.set_line(
1480 bottom_horizontal_left_ln.x,
1481 bottom_horizontal_left_ln.y,
1482 used_bottom_horizontal_left_ln,
1483 self.area.width,
1484 );
1485 }
1486
1487 /// Renders the right side of the bottom horizontal line of the border with an optional gradient.
1488 ///
1489 /// This function renders the right part of the bottom horizontal line. If the segment should use a gradient,
1490 /// it applies the gradient to the segment text. Otherwise, it renders the text as-is.
1491 ///
1492 /// # Parameters:
1493 /// - `buf`: A mutable reference to the `Buffer` where the rendered output will be stored.
1494 ///
1495 /// ## Visual Representation:
1496 /// Without the function:
1497 /// ```
1498 /// +-----+
1499 /// | |
1500 /// | |
1501 /// + +
1502 /// | |
1503 /// | |
1504 /// +--- +
1505 /// ```
1506 fn render_bottom_horizontal_right_ln(&self, buf: &mut Buffer) {
1507 let bottom_horizontal_right_ln = self.border_segments.bottom_horizontal_right_ln.clone();
1508 let used_bottom_horizontal_right_ln = match bottom_horizontal_right_ln.should_use_gradient {
1509 true => &Line::from(Self::create_gradient_text(
1510 &bottom_horizontal_right_ln.segment_text,
1511 bottom_horizontal_right_ln.gradient.clone().unwrap().0,
1512 bottom_horizontal_right_ln.gradient.unwrap().1,
1513 )),
1514 false => &Line::from(bottom_horizontal_right_ln.segment_text),
1515 };
1516 buf.set_line(
1517 bottom_horizontal_right_ln.x,
1518 bottom_horizontal_right_ln.y,
1519 used_bottom_horizontal_right_ln,
1520 self.area.width,
1521 );
1522 }
1523
1524 /// Renders the top side of the left vertical line of the border with an optional gradient.
1525 ///
1526 /// This function renders the left part of the top vertical line. If the segment should use a gradient,
1527 /// it applies the gradient to the segment text. Otherwise, it renders the text as-is.
1528 ///
1529 /// # Parameters:
1530 /// - `buf`: A mutable reference to the `Buffer` where the rendered output will be stored.
1531 ///
1532 /// ## Visual Representation:
1533 /// Without the function:
1534 /// ```
1535 /// +-----+
1536 /// |
1537 /// |
1538 /// + +
1539 /// | |
1540 /// | |
1541 /// +-----+
1542 /// ```
1543 fn render_top_vertical_left_ln(&self, buf: &mut Buffer) {
1544 let mut top_vertical_left_ln = self.border_segments.top_vertical_left_ln.clone();
1545
1546 let used_top_vertical_left_ln: Vec<Span> = if top_vertical_left_ln.should_use_gradient {
1547 Self::create_gradient_text(
1548 &top_vertical_left_ln.clone().segment_text,
1549 top_vertical_left_ln.gradient.clone().unwrap().0,
1550 top_vertical_left_ln.gradient.as_mut().unwrap().1,
1551 )
1552 } else {
1553 top_vertical_left_ln
1554 .clone()
1555 .segment_text
1556 .chars()
1557 .map(|i| Span::raw(i.to_string()))
1558 .collect()
1559 };
1560 for (i, span) in used_top_vertical_left_ln.iter().enumerate() {
1561 buf.set_span(
1562 top_vertical_left_ln.x,
1563 top_vertical_left_ln.y + i as u16,
1564 span,
1565 1,
1566 );
1567 }
1568 }
1569
1570 /// Renders the top side of the right vertical line of the border with an optional gradient.
1571 ///
1572 /// # Parameters:
1573 /// - `buf`: A mutable reference to the `Buffer` where the rendered output will be stored.
1574 ///
1575 /// This function renders the top-right vertical line of the border. If the segment associated with
1576 /// this line should use a gradient, it applies the gradient to the segment text using the `create_gradient_text`
1577 /// function. Otherwise, the function renders the segment text as-is.
1578 ///
1579 /// # Example:
1580 /// ```rust
1581 /// let widget = TuiGradientblock::new();
1582 /// let mut buffer = Buffer::new();
1583 /// widget.render_top_vertical_right_ln(&mut buffer);
1584 /// ```
1585 ///
1586 /// ## Visual Representation:
1587 /// Without the function:
1588 /// ```
1589 /// +-----+
1590 /// | |
1591 /// | |
1592 /// + +
1593 /// | |
1594 /// | |
1595 /// +-----+
1596 /// ```
1597 fn render_top_vertical_right_ln(&self, buf: &mut Buffer) {
1598 let mut top_vertical_right_ln = self.border_segments.top_vertical_right_ln.clone();
1599
1600 let used_top_vertical_right_ln: Vec<Span> = if top_vertical_right_ln.should_use_gradient {
1601 Self::create_gradient_text(
1602 &top_vertical_right_ln.clone().segment_text,
1603 top_vertical_right_ln.gradient.clone().unwrap().0,
1604 top_vertical_right_ln.gradient.as_mut().unwrap().1,
1605 )
1606 } else {
1607 top_vertical_right_ln
1608 .clone()
1609 .segment_text
1610 .chars()
1611 .map(|i| Span::raw(i.to_string()))
1612 .collect()
1613 };
1614 for (i, span) in used_top_vertical_right_ln.iter().enumerate() {
1615 buf.set_span(
1616 top_vertical_right_ln.x,
1617 top_vertical_right_ln.y + i as u16,
1618 span,
1619 1,
1620 );
1621 }
1622 }
1623
1624 /// Renders the bottom vertical right line of the border with an optional gradient.
1625 ///
1626 /// # Parameters:
1627 /// - `buf`: A mutable reference to the `Buffer` where the rendered output will be stored.
1628 ///
1629 /// This function renders the bottom-right vertical line of the border. If the segment associated with
1630 /// this line should use a gradient, it applies the gradient to the segment text using the `create_gradient_text`
1631 /// function. Otherwise, the function renders the segment text as-is.
1632 ///
1633 /// # Example:
1634 /// ```rust
1635 /// let widget = TuiGradientblock::new();
1636 /// let mut buffer = Buffer::new();
1637 /// widget.render_bottom_vertical_right_ln(&mut buffer);
1638 /// ```
1639 ///
1640 /// ## Visual Representation:
1641 /// Without the function:
1642 /// ```
1643 /// +-----+
1644 /// | |
1645 /// | |
1646 /// + +
1647 /// |
1648 /// |
1649 /// +-----+
1650 /// ```
1651 fn render_bottom_vertical_right_ln(&self, buf: &mut Buffer) {
1652 let mut bottom_vertical_right_ln = self.border_segments.bottom_vertical_right_ln.clone();
1653 let used_bottom_vertical_right_ln: Vec<Span> =
1654 if bottom_vertical_right_ln.should_use_gradient {
1655 Self::create_gradient_text(
1656 &bottom_vertical_right_ln.clone().segment_text,
1657 bottom_vertical_right_ln.gradient.clone().unwrap().0,
1658 bottom_vertical_right_ln.gradient.as_mut().unwrap().1,
1659 )
1660 } else {
1661 bottom_vertical_right_ln
1662 .clone()
1663 .segment_text
1664 .chars()
1665 .map(|i| Span::raw(i.to_string()))
1666 .collect()
1667 };
1668 for (i, span) in used_bottom_vertical_right_ln.iter().enumerate() {
1669 buf.set_span(
1670 bottom_vertical_right_ln.x,
1671 bottom_vertical_right_ln.y + i as u16,
1672 span,
1673 1,
1674 );
1675 }
1676 }
1677
1678 /// Renders the bottom vertical left segment of the border with an optional gradient.
1679 ///
1680 /// # Parameters:
1681 /// - `buf`: A mutable reference to the `Buffer` where the rendered output will be stored.
1682 ///
1683 /// This function renders the bottom-left vertical segment of the border. If the segment associated with
1684 /// this line should use a gradient, it applies the gradient to the segment text using the `create_gradient_text`
1685 /// function. Otherwise, the function renders the segment text as-is.
1686 /// ## Visual Representation:
1687 /// Without the function:
1688 /// ```
1689 /// +-----+
1690 /// | |
1691 /// | |
1692 /// + +
1693 /// |
1694 /// |
1695 /// +-----+
1696 /// ```
1697 fn render_bottom_vertical_left_ln(&self, buf: &mut Buffer) {
1698 let mut bottom_vertical_left_ln = self.border_segments.bottom_vertical_left_ln.clone();
1699 let used_bottom_vertical_left_ln: Vec<Span> = if bottom_vertical_left_ln.should_use_gradient
1700 {
1701 Self::create_gradient_text(
1702 &bottom_vertical_left_ln.clone().segment_text,
1703 bottom_vertical_left_ln.gradient.clone().unwrap().0,
1704 bottom_vertical_left_ln.gradient.as_mut().unwrap().1,
1705 )
1706 } else {
1707 bottom_vertical_left_ln
1708 .clone()
1709 .segment_text
1710 .chars()
1711 .map(|i| Span::raw(i.to_string()))
1712 .collect()
1713 };
1714 for (i, span) in used_bottom_vertical_left_ln.iter().enumerate() {
1715 buf.set_span(
1716 bottom_vertical_left_ln.x,
1717 bottom_vertical_left_ln.y + i as u16,
1718 span,
1719 1,
1720 );
1721 }
1722 }
1723
1724 /// Renders the bottom titles for the `TuiGradientblock` widget, with optional gradient support.
1725 ///
1726 /// # Parameters:
1727 /// - `area`: A `Rc<Rect>` that defines the area where the bottom titles will be rendered.
1728 /// - `buf`: A mutable reference to the `Buffer` where the rendered output will be stored.
1729 ///
1730 /// This function renders each title in the `bottom_titles` collection with a specified alignment:
1731 /// - **Left-aligned**: The title is rendered at the left edge of the bottom row of the area.
1732 /// - **Right-aligned**: The title is rendered at the right edge of the bottom row of the area.
1733 /// - **Centered**: The title is centered within the bottom row of the area.
1734 ///
1735 /// If a gradient is specified for a title, it is applied by calling `create_gradient_text`
1736 /// with the gradient’s start and end colors. The resulting text is then rendered with the specified alignment.
1737 fn render_bottom_titles(&self, area: Rc<Rect>, buf: &mut Buffer) {
1738 for title in &self.bottom_titles {
1739 let ln = match &title.2 {
1740 Some(s) => &Line::from(Self::create_gradient_text(&title.0, s.0.clone(), s.1)),
1741 None => &Line::from(title.0.clone()),
1742 };
1743 match title.1 {
1744 TitleAlignment::LeftAligned => {
1745 buf.set_line(area.left() + 1, area.bottom() - 1, ln, title.0.len() as u16);
1746 }
1747 TitleAlignment::RightAligned => {
1748 buf.set_line(
1749 (area.right() - 1).saturating_sub(title.0.len() as u16),
1750 area.bottom() - 1,
1751 ln,
1752 title.0.len() as u16,
1753 );
1754 }
1755 TitleAlignment::Centered => {
1756 buf.set_line(
1757 (area.right() / 2).saturating_sub(title.0.len() as u16 / 2),
1758 area.bottom() - 1,
1759 ln,
1760 title.0.len() as u16,
1761 );
1762 }
1763 }
1764 }
1765 }
1766
1767 /// Renders the top titles for the `TuiGradientblock` widget, with optional gradient support.
1768 ///
1769 /// # Parameters:
1770 /// - `area`: A `Rc<Rect>` that defines the area where the top titles will be rendered.
1771 /// - `buf`: A mutable reference to the `Buffer` where the rendered output will be stored.
1772 ///
1773 /// This function renders each title in the `top_titles` collection with a specified alignment:
1774 /// - **Left-aligned**: The title is rendered at the left edge of the area.
1775 /// - **Right-aligned**: The title is rendered at the right edge of the area.
1776 /// - **Centered**: The title is centered within the area.
1777 ///
1778 /// If a gradient is specified for a title, it is applied by calling `create_gradient_text`
1779 /// with the gradient’s start and end colors. The resulting text is then rendered with the specified alignment.
1780 ///
1781 /// # Example:
1782 /// ```rust
1783 /// let widget = TuiGradientblock::new();
1784 /// let area = Rc::new(Rect::new(0, 0, 20, 5));
1785 /// let mut buffer = Buffer::new();
1786 /// widget.render_top_titles(area, &mut buffer);
1787 /// ```
1788 fn render_top_titles(&self, area: Rc<Rect>, buf: &mut Buffer) {
1789 for title in &self.top_titles {
1790 let ln = match &title.2 {
1791 Some(s) => &Line::from(Self::create_gradient_text(&title.0, s.0.clone(), s.1)),
1792 None => &Line::from(title.0.clone()),
1793 };
1794 match title.1 {
1795 TitleAlignment::LeftAligned => {
1796 buf.set_line(area.left() + 1, area.top(), ln, title.0.len() as u16);
1797 }
1798 TitleAlignment::RightAligned => {
1799 buf.set_line(
1800 (area.right() - 1).saturating_sub(title.0.len() as u16),
1801 area.top(),
1802 ln,
1803 title.0.len() as u16,
1804 );
1805 }
1806 TitleAlignment::Centered => {
1807 buf.set_line(
1808 (area.right() / 2).saturating_sub(title.0.len() as u16 / 2),
1809 area.top(),
1810 ln,
1811 title.0.len() as u16,
1812 );
1813 }
1814 }
1815 }
1816 }
1817
1818 /// Renders the fill for the `TuiGradientblock` widget, including optional gradient rendering.
1819 ///
1820 /// # Parameters:
1821 /// - `area`: A `Rc<Rect>` that defines the area in which to render the fill.
1822 /// - `buf`: A mutable reference to the `Buffer` where the rendered output will be stored.
1823 ///
1824 /// This function performs the following:
1825 /// - If a `fill_string` is provided (via `self.fill.fill_string`), it generates a repeated string
1826 /// pattern to fill the given `area`.
1827 /// - If no gradient is specified (`self.fill.gradient` is `None`), it renders the repeated string
1828 /// in the specified area using a simple block with borders.
1829 /// - If a gradient is provided, the function applies the gradient to the fill and renders the
1830 /// resulting text using a centered block with borders.
1831 ///
1832 /// The function uses `Paragraph::new` to render the text and applies wrapping and border options.
1833 /// It also calls `create_gradient_text` to generate the text with the applied gradient if a gradient
1834 /// is specified.
1835 ///
1836 /// # Example:
1837 /// ```rust
1838 /// let widget = TuiGradientblock::new();
1839 /// let area = Rc::new(Rect::new(0, 0, 10, 5));
1840 /// let mut buffer = Buffer::new();
1841 /// widget.render_fill(area, &mut buffer);
1842 /// ```
1843 fn render_fill(&self, area: Rc<Rect>, buf: &mut Buffer) {
1844 let mut string = self.fill.fill_string.clone().unwrap();
1845 string.push(' ');
1846 if self.fill.gradient.is_none() {
1847 Paragraph::new(string.repeat(area.width as usize * string.len()).to_owned())
1848 .wrap(Wrap { trim: true })
1849 .block(Block::default().borders(Borders::ALL))
1850 .render(*area, buf);
1851 } else {
1852 // Get a reference to the gradient and its color values
1853 let gradient = self.fill.gradient.as_ref().unwrap();
1854 let gradient_text = Self::create_gradient_text(
1855 &string
1856 .repeat(area.width as usize * string.len())
1857 .to_string(),
1858 gradient.0.clone(),
1859 gradient.1,
1860 );
1861
1862 let text = Line::from(gradient_text);
1863 Paragraph::new(text)
1864 .centered()
1865 .wrap(Wrap { trim: true })
1866 .block(Block::default().borders(Borders::ALL))
1867 .render(*area, buf);
1868 }
1869 }
1870
1871 /// Renders the `TuiGradientblock` widget, including optional fill and custom block rendering,
1872 /// along with top and bottom titles.
1873 ///
1874 /// # Parameters:
1875 /// - `area`: A reference to a `Rect` that specifies the area to render the widget in.
1876 /// - `buf`: A mutable reference to the `Buffer` where the rendered output will be stored.
1877 ///
1878 /// This function:
1879 /// - Checks if there is a fill string and calls `render_fill` if present.
1880 /// - Renders the custom block using `render_custom_block`.
1881 /// - Renders the top titles using `render_top_titles`.
1882 /// - Renders the bottom titles using `render_bottom_titles`.
1883 pub fn main(&self, area: &Rect, buf: &mut Buffer) {
1884 let area_rc = Rc::new(*area);
1885 if self.fill.fill_string.is_some() {
1886 Self::render_fill(self, Rc::clone(&area_rc), buf);
1887 }
1888 self.render_block(buf);
1889 Self::render_top_titles(self, Rc::clone(&area_rc), buf);
1890 Self::render_bottom_titles(self, Rc::clone(&area_rc), buf);
1891 }
1892}
1893impl Widget for TuiGradientblock {
1894 /// Renders the `TuiGradientblock` widget using the `main` function.
1895 ///
1896 /// This is part of the `Widget` trait implementation. The `render` function takes an
1897 /// `area` and a mutable reference to the `Buffer`, and delegates rendering to the `main` function.
1898 ///
1899 /// # Parameters:
1900 /// - `area`: A `Rect` that defines the area for rendering the widget.
1901 /// - `buf`: A mutable reference to the `Buffer` where the rendered output will be stored.
1902 ///
1903 /// # Example:
1904 /// ```
1905 /// let widget = TuiGradientblock::new();
1906 /// let area = Rect::new(0, 0, 10, 10);
1907 /// let mut buffer = Buffer::new();
1908 /// widget.render(area, &mut buffer);
1909 /// ```
1910 fn render(self, area: Rect, buf: &mut Buffer) {
1911 self.main(&area, buf);
1912 }
1913}
1914#[derive(Clone)]
1915/// Contains miscellaneous border types
1916pub enum MiscBorderTypes {
1917 /// ```
1918 /// +=====+
1919 /// | |
1920 /// | |
1921 /// | |
1922 /// +=====+
1923 /// ```
1924 Misc1,
1925 /// ```
1926 /// +=====+
1927 /// | |
1928 /// + +
1929 /// | |
1930 /// ╘═════╛
1931 /// ```
1932 Misc2,
1933 /// ``````
1934 /// ╬═════╬
1935 /// ║ ║
1936 /// ║ ║
1937 /// ║ ║
1938 /// ╬═════╬
1939 /// ```
1940 Misc3,
1941 /// ```
1942 /// $──~──$
1943 /// | |
1944 /// ~ ~
1945 /// | |
1946 /// $──~──$
1947 /// ```
1948 Misc4,
1949}
1950#[derive(Clone)]
1951/// contains the custom border types
1952/// Defines different border styles that can be applied.
1953pub enum BorderStyle {
1954 /// A simple, single-line border (e.g., `│─┌┐└┘`).
1955 Plain,
1956 /// A double-line border for a more structured appearance (e.g., `║═╔╗╚╝`).
1957 Double,
1958 /// A thick border for strong emphasis (may vary depending on rendering support).
1959 Thick,
1960 /// A rounded border with smooth corners (e.g., `╭╮╰╯`).
1961 Rounded,
1962 /// A completely empty, user-defined custom border.
1963 CustomBorderType,
1964 /// A collection of miscellaneous border types.
1965 MiscBorder(MiscBorderTypes),
1966}
1967
1968#[derive(Debug, Clone)]
1969/// A set of symbols that define the appearance of a border.
1970/// Most symbols are optional, allowing for customization of corners, edges, and intersections.
1971struct BorderSymbols {
1972 /// The character used for the top-left corner of the border (e.g., '┌' for a box).
1973 /// If not set, a default may be used.
1974 top_left: Option<char>,
1975
1976 /// The character used for the top-right corner of the border (e.g., '┐' for a box).
1977 top_right: Option<char>,
1978
1979 /// The character used for the bottom-left corner of the border (e.g., '└' for a box).
1980 bottom_left: Option<char>,
1981
1982 /// The character used for the bottom-right corner of the border (e.g., '┘' for a box).
1983 bottom_right: Option<char>,
1984
1985 /// The character placed at the center of the top border.
1986 /// Defaults to `top_horizontal` if not specified.
1987 top_center: Option<char>,
1988
1989 /// The character placed at the center of the bottom border.
1990 /// Defaults to `bottom_horizontal` if not specified.
1991 bottom_center: Option<char>,
1992
1993 /// The character placed at the center of the left border.
1994 /// Defaults to `left_vertical` if not specified.
1995 left_center: Option<char>,
1996
1997 /// The character placed at the center of the right border.
1998 /// Defaults to `right_vertical` if not specified.
1999 right_center: Option<char>,
2000
2001 /// The character used for the horizontal segments of the top border (e.g., '─' for a solid line).
2002 /// Defaults to '─' if not set.
2003 top_horizontal: Option<char>,
2004
2005 /// The character used for the vertical segments of the left border (e.g., '│' for a solid line).
2006 /// Defaults to '│' if not set.
2007 left_vertical: Option<char>,
2008
2009 /// The character used for the horizontal segments of the bottom border (e.g., '─' for a solid line).
2010 /// Defaults to '─' if not set.
2011 bottom_horizontal: Option<char>,
2012
2013 /// The character used for the vertical segments of the right border (e.g., '│' for a solid line).
2014 /// Defaults to '│' if not set.
2015 right_vertical: Option<char>,
2016
2017 /// The character repeated for the right side of the top border.
2018 /// Defaults to `top_horizontal` if not set.
2019 top_horizontal_right: Option<char>,
2020
2021 /// The character repeated for the right side of the bottom border.
2022 /// Defaults to `bottom_horizontal` if not set.
2023 bottom_horizontal_right: Option<char>,
2024
2025 /// The character repeated for the left side of the top border.
2026 /// Defaults to `top_horizontal` if not set.
2027 top_horizontal_left: Option<char>,
2028
2029 /// The character repeated for the left side of the bottom border.
2030 /// Defaults to `bottom_horizontal` if not set.
2031 bottom_horizontal_left: Option<char>,
2032
2033 /// The character repeated for the top section of the right border.
2034 /// Defaults to `right_vertical` if not set.
2035 top_vertical_right: Option<char>,
2036
2037 /// The character repeated for the bottom section of the right border.
2038 /// Defaults to `right_vertical` if not set.
2039 bottom_vertical_right: Option<char>,
2040
2041 /// The character repeated for the top section of the left border.
2042 /// Defaults to `left_vertical` if not set.
2043 top_vertical_left: Option<char>,
2044
2045 /// The character repeated for the bottom section of the left border.
2046 /// Defaults to `left_vertical` if not set.
2047 bottom_vertical_left: Option<char>,
2048}
2049
2050#[derive(Debug, Clone)]
2051/// controls the optional fill with an optional gradient
2052struct Fill {
2053 fill_string: Option<String>,
2054 gradient: Option<(Vec<(u8, u8, u8)>, f32)>,
2055}
2056#[derive(Debug, Clone)]
2057struct BorderSegment {
2058 /// The text representation of this border segment.
2059 /// - For a top border, this might be `"┌──────┐"`.
2060 /// - For a right border, this might be `"┐││││┘"` (each character is rendered on a separate line from top to bottom, as newlines cannot be rendered directly).
2061 segment_text: String,
2062
2063 /// An optional gradient applied to this border segment.
2064 /// The gradient consists of a vector of RGB color values and a scaling factor.
2065 gradient: Option<(Vec<(u8, u8, u8)>, f32)>,
2066
2067 /// Determines whether this segment should be rendered with a gradient.
2068 /// If `false`, the gradient (if present) will be ignored.
2069 should_use_gradient: bool,
2070
2071 /// Determines whether the segment should be rendered at all.
2072 /// If `false`, this segment will not be displayed.
2073 should_be_rendered: bool,
2074
2075 /// The X-coordinate of the segment’s position.
2076 x: u16,
2077
2078 /// The Y-coordinate of the segment’s position.
2079 y: u16,
2080}
2081impl BorderSegment {
2082 /// Creates a new, empty border segment at the specified position.
2083 ///
2084 /// The segment starts with no text, no gradient, and is not set to be rendered by default.
2085 ///
2086 /// # Parameters
2087 /// - `x`: The X-coordinate of the segment’s position.
2088 /// - `y`: The Y-coordinate of the segment’s position.
2089 ///
2090 /// # Returns
2091 /// A `BorderSegment` instance with default values.
2092 pub fn new(x: u16, y: u16) -> Self {
2093 Self {
2094 segment_text: String::new(),
2095 gradient: None,
2096 should_use_gradient: false,
2097 should_be_rendered: false,
2098 x,
2099 y,
2100 }
2101 }
2102}
2103
2104#[derive(Debug, Clone)]
2105
2106/// A collection of border segments representing different parts of a bordered structure.
2107///
2108/// This struct holds individual `BorderSegment` instances for each section of the border,
2109/// ensuring flexibility in rendering complex layouts.
2110struct BorderSegments {
2111 /// The left portion of the top horizontal border.
2112 top_horizontal_left_ln: BorderSegment,
2113 /// The right portion of the top horizontal border.
2114 top_horizontal_right_ln: BorderSegment,
2115 /// The left portion of the bottom horizontal border.
2116 bottom_horizontal_left_ln: BorderSegment,
2117 /// The right portion of the bottom horizontal border.
2118 bottom_horizontal_right_ln: BorderSegment,
2119 /// The upper portion of the left vertical border.
2120 top_vertical_left_ln: BorderSegment,
2121 /// The upper portion of the right vertical border.
2122 top_vertical_right_ln: BorderSegment,
2123 /// The lower portion of the left vertical border.
2124 bottom_vertical_left_ln: BorderSegment,
2125 /// The lower portion of the right vertical border.
2126 bottom_vertical_right_ln: BorderSegment,
2127 /// The full top border segment.
2128 top_ln: BorderSegment,
2129 /// The full bottom border segment.
2130 bottom_ln: BorderSegment,
2131 /// The full left border segment.
2132 left_ln: BorderSegment,
2133 /// The full right border segment.
2134 right_ln: BorderSegment,
2135}
2136impl BorderSegments {
2137 /// Creates a new set of `BorderSegments` for a given rectangular area.
2138 ///
2139 /// This constructor initializes border segments based on the provided `Rect` dimensions,
2140 /// ensuring correct positioning for all parts of the border.
2141 ///
2142 /// # Arguments
2143 /// * `area` - A reference to a `Rect` defining the bounds of the border.
2144 ///
2145 /// # Returns
2146 /// A `BorderSegments` instance with all segments initialized at their respective positions.
2147 pub fn new(area: &Rect) -> Self {
2148 Self {
2149 top_horizontal_left_ln: BorderSegment::new(area.left(), area.top()),
2150 top_horizontal_right_ln: BorderSegment::new(area.right() / 2, area.top()),
2151 bottom_horizontal_left_ln: BorderSegment::new(area.left(), area.bottom() - 1),
2152 bottom_horizontal_right_ln: BorderSegment::new(area.right() / 2, area.bottom() - 1),
2153 top_vertical_left_ln: BorderSegment::new(area.left(), area.top()),
2154 top_vertical_right_ln: BorderSegment::new(area.right() - 1, area.top()),
2155 bottom_vertical_left_ln: BorderSegment::new(
2156 area.left(),
2157 ((area.height as usize + 1) / 2) as u16,
2158 ),
2159 bottom_vertical_right_ln: BorderSegment::new(
2160 area.right() - 1,
2161 ((area.height as usize + 1) / 2) as u16,
2162 ),
2163 top_ln: BorderSegment::new(area.left(), area.top()),
2164 bottom_ln: BorderSegment::new(area.left(), area.bottom() - 1),
2165 left_ln: BorderSegment::new(area.left(), area.top()),
2166 right_ln: BorderSegment::new(area.right() - 1, area.top()),
2167 }
2168 }
2169}
2170
2171bitflags! {
2172 /// Represents individual border segments that can be split or modified.
2173 #[derive(PartialEq, Clone)]
2174 pub struct SplitBorderSegments: u8 {
2175 /// No border segments are split.
2176 const NONE = 0b0000;
2177 /// The left border segment is split.
2178 const LEFT = 0b0001;
2179 /// The right border segment is split.
2180 const RIGHT = 0b0010;
2181 /// The top border segment is split.
2182 const TOP = 0b0100;
2183 /// The bottom border segment is split.
2184 const BOTTOM = 0b1000;
2185 /// All border segments are split.
2186 const ALL = Self::LEFT.bits() | Self::RIGHT.bits() | Self::TOP.bits() | Self::BOTTOM.bits();
2187 }
2188}
2189#[derive(Clone)]
2190/// Represents different segments of a border where gradients can be applied.
2191///
2192/// This enum is used to control gradient effects for specific border sections,
2193/// allowing for finer customization of visual styles.
2194pub enum GradientSegments {
2195 /// The top border segment.
2196 Top,
2197 /// The bottom border segment.
2198 Bottom,
2199 /// The left border segment.
2200 Left,
2201 /// The right border segment.
2202 Right,
2203 /// The right portion of the top horizontal border.
2204 TopHorizontalRightLn,
2205 /// The left portion of the top horizontal border.
2206 TopHorizontalLeftLn,
2207 /// The right portion of the bottom horizontal border.
2208 BottomHorizontalRightLn,
2209 /// The left portion of the bottom horizontal border.
2210 BottomHorizontalLeftLn,
2211 /// The right portion of the top vertical border.
2212 TopVerticalRightLn,
2213 /// The left portion of the top vertical border.
2214 TopVerticalLeftLn,
2215 /// The right portion of the bottom vertical border.
2216 BottomVerticalRightLn,
2217 /// The left portion of the bottom vertical border.
2218 BottomVerticalLeftLn,
2219}
2220#[derive(Clone)]
2221/// Defines the alignment options for a title within a bordered area.
2222pub enum TitleAlignment {
2223 /// Centers the title within the border.
2224 Centered,
2225 /// Aligns the title to the left within the border.
2226 LeftAligned,
2227 /// Aligns the title to the right within the border.
2228 RightAligned,
2229}