1use crate::_private::NonExhaustive;
6use rat_reloc::{relocate_area, relocate_areas, RelocatableState};
7use ratatui::buffer::Buffer;
8use ratatui::layout::{Constraint, Layout, Rect};
9use ratatui::style::Style;
10use ratatui::text::Span;
11#[cfg(feature = "unstable-widget-ref")]
12use ratatui::widgets::StatefulWidgetRef;
13use ratatui::widgets::{StatefulWidget, Widget};
14use std::fmt::Debug;
15
16#[derive(Debug, Default, Clone)]
18pub struct StatusLine {
19 style: Vec<Style>,
20 widths: Vec<Constraint>,
21}
22
23#[derive(Debug, Clone)]
25pub struct StatusLineState {
26 pub area: Rect,
29 pub areas: Vec<Rect>,
32
33 pub status: Vec<String>,
36
37 pub non_exhaustive: NonExhaustive,
38}
39
40impl StatusLine {
41 pub fn new() -> Self {
43 Self {
44 style: Default::default(),
45 widths: Default::default(),
46 }
47 }
48
49 pub fn layout<It, Item>(mut self, widths: It) -> Self
54 where
55 It: IntoIterator<Item = Item>,
56 Item: Into<Constraint>,
57 {
58 self.widths = widths.into_iter().map(|v| v.into()).collect();
59 self
60 }
61
62 pub fn styles(mut self, style: impl IntoIterator<Item = impl Into<Style>>) -> Self {
64 self.style = style.into_iter().map(|v| v.into()).collect();
65 self
66 }
67}
68
69impl Default for StatusLineState {
70 fn default() -> Self {
71 Self {
72 area: Default::default(),
73 areas: Default::default(),
74 status: Default::default(),
75 non_exhaustive: NonExhaustive,
76 }
77 }
78}
79
80impl RelocatableState for StatusLineState {
81 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
82 self.area = relocate_area(self.area, shift, clip);
83 relocate_areas(self.areas.as_mut(), shift, clip);
84 }
85}
86
87impl StatusLineState {
88 pub fn new() -> Self {
89 Self::default()
90 }
91
92 pub fn clear_status(&mut self) {
94 self.status.clear();
95 }
96
97 pub fn status<S: Into<String>>(&mut self, idx: usize, msg: S) {
99 while self.status.len() <= idx {
100 self.status.push("".to_string());
101 }
102 self.status[idx] = msg.into();
103 }
104}
105
106#[cfg(feature = "unstable-widget-ref")]
107impl StatefulWidgetRef for StatusLine {
108 type State = StatusLineState;
109
110 fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
111 render_ref(self, area, buf, state);
112 }
113}
114
115impl StatefulWidget for StatusLine {
116 type State = StatusLineState;
117
118 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
119 render_ref(&self, area, buf, state);
120 }
121}
122
123fn render_ref(widget: &StatusLine, area: Rect, buf: &mut Buffer, state: &mut StatusLineState) {
124 state.area = area;
125
126 let layout = Layout::horizontal(widget.widths.iter()).split(state.area);
127
128 for (i, rect) in layout.iter().enumerate() {
129 let style = widget.style.get(i).copied().unwrap_or_default();
130 let txt = state.status.get(i).map(|v| v.as_str()).unwrap_or("");
131
132 buf.set_style(*rect, style);
133 Span::from(txt).render(*rect, buf);
134 }
135}