rat_widget/
util.rs

1//!
2//! Small helpers.
3//!
4use ratatui::buffer::Buffer;
5use ratatui::layout::{Rect, Size};
6use ratatui::prelude::{BlockExt, Widget};
7use ratatui::style::{Style, Stylize};
8use ratatui::widgets::{Block, Padding};
9use std::{fmt, mem};
10
11/// Union the areas, but regard only non-empty ones.
12///
13/// This can help if you want to union two non-adjacent areas.
14pub fn union_non_empty(area1: Rect, area2: Rect) -> Rect {
15    if area1.is_empty() && area2.is_empty() {
16        Rect::new(area1.x, area1.y, 0, 0)
17    } else if area1.is_empty() {
18        area2
19    } else if area2.is_empty() {
20        area1
21    } else {
22        area1.union(area2)
23    }
24}
25
26/// Returns a new style with fg and bg swapped.
27///
28/// This is not the same as setting Style::reversed().
29/// The latter sends special controls to the terminal,
30/// the former just swaps.
31pub fn revert_style(mut style: Style) -> Style {
32    if style.fg.is_some() || style.bg.is_some() {
33        mem::swap(&mut style.fg, &mut style.bg);
34        style
35    } else {
36        style.black().on_white()
37    }
38}
39
40/// Fallback for select style.
41pub fn fallback_select_style(style: Style) -> Style {
42    if style.fg.is_some() || style.bg.is_some() {
43        style
44    } else {
45        style.underlined()
46    }
47}
48
49/// Reset an area of the buffer.
50pub fn reset_buf_area(area: Rect, buf: &mut Buffer) {
51    for y in area.top()..area.bottom() {
52        for x in area.left()..area.right() {
53            if let Some(cell) = buf.cell_mut((x, y)) {
54                cell.reset();
55            }
56        }
57    }
58}
59
60/// Fill the given area of the buffer.
61pub fn fill_buf_area(buf: &mut Buffer, area: Rect, symbol: &str, style: impl Into<Style>) {
62    let style = style.into();
63
64    for y in area.top()..area.bottom() {
65        for x in area.left()..area.right() {
66            if let Some(cell) = buf.cell_mut((x, y)) {
67                cell.reset();
68                cell.set_symbol(symbol);
69                cell.set_style(style);
70            }
71        }
72    }
73}
74
75pub fn rect_dbg(area: Rect) -> String {
76    use fmt::Write;
77    let mut buf = String::new();
78    _ = write!(buf, "{}:{}+{}+{}", area.x, area.y, area.width, area.height);
79    buf
80}
81
82pub(crate) const DOUBLE_VERTICAL_SINGLE_LEFT: &str = "\u{2562}";
83pub(crate) const DOUBLE_VERTICAL_SINGLE_RIGHT: &str = "\u{255F}";
84pub(crate) const THICK_VERTICAL_SINGLE_LEFT: &str = "\u{2528}";
85pub(crate) const THICK_VERTICAL_SINGLE_RIGHT: &str = "\u{2520}";
86
87/// Get the padding the block imposes as Padding.
88pub fn block_padding(block: &Option<Block<'_>>) -> Padding {
89    let area = Rect::new(0, 0, 20, 20);
90    let inner = block.inner_if_some(area);
91    Padding {
92        left: inner.left() - area.left(),
93        right: area.right() - inner.right(),
94        top: inner.top() - area.top(),
95        bottom: area.bottom() - inner.bottom(),
96    }
97}
98
99/// Get the padding the block imposes as Padding.
100pub fn block_padding2(block: &Block<'_>) -> Padding {
101    let area = Rect::new(0, 0, 20, 20);
102    let inner = block.inner(area);
103    Padding {
104        left: inner.left() - area.left(),
105        right: area.right() - inner.right(),
106        top: inner.top() - area.top(),
107        bottom: area.bottom() - inner.bottom(),
108    }
109}
110
111/// Get the padding the block imposes as a Size.
112pub fn block_size(block: &Option<Block<'_>>) -> Size {
113    let area = Rect::new(0, 0, 20, 20);
114    let inner = block.inner_if_some(area);
115    Size {
116        width: (inner.left() - area.left()) + (area.right() - inner.right()),
117        height: (inner.top() - area.top()) + (area.bottom() - inner.bottom()),
118    }
119}
120
121pub(crate) fn block_left(block: &Block<'_>) -> String {
122    let area = Rect::new(0, 0, 3, 3);
123    let mut buf = Buffer::empty(area);
124    block.clone().render(area, &mut buf);
125    buf.cell((0, 1)).expect("cell").symbol().into()
126}
127
128pub(crate) fn block_right(block: &Block<'_>) -> String {
129    let area = Rect::new(0, 0, 3, 3);
130    let mut buf = Buffer::empty(area);
131    block.clone().render(area, &mut buf);
132    buf.cell((2, 1)).expect("cell").symbol().into()
133}
134
135#[allow(dead_code)]
136pub(crate) fn block_top(block: &Block<'_>) -> String {
137    let area = Rect::new(0, 0, 3, 3);
138    let mut buf = Buffer::empty(area);
139    block.clone().render(area, &mut buf);
140    buf.cell((1, 0)).expect("cell").symbol().into()
141}
142
143#[allow(dead_code)]
144pub(crate) fn block_bottom(block: &Block<'_>) -> String {
145    let area = Rect::new(0, 0, 3, 3);
146    let mut buf = Buffer::empty(area);
147    block.clone().render(area, &mut buf);
148    buf.cell((1, 2)).expect("cell").symbol().into()
149}