rat_widget/
util.rs

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