rat_widget/
util.rs

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