1use crate::_private::NonExhaustive;
38use rat_reloc::{RelocatableState, relocate_area, relocate_areas};
39use ratatui::buffer::Buffer;
40use ratatui::layout::{Constraint, Layout, Rect};
41use ratatui::style::Style;
42use ratatui::text::Span;
43use ratatui::widgets::{StatefulWidget, Widget};
44use std::fmt::Debug;
45
46#[derive(Debug, Default, Clone)]
48pub struct StatusLine {
49 style: Vec<Style>,
50 widths: Vec<Constraint>,
51}
52
53#[derive(Debug, Clone)]
55pub struct StatusLineState {
56 pub area: Rect,
59 pub areas: Vec<Rect>,
62
63 pub status: Vec<String>,
66
67 pub non_exhaustive: NonExhaustive,
68}
69
70impl StatusLine {
71 pub fn new() -> Self {
73 Self {
74 style: Default::default(),
75 widths: Default::default(),
76 }
77 }
78
79 pub fn layout<It, Item>(mut self, widths: It) -> Self
84 where
85 It: IntoIterator<Item = Item>,
86 Item: Into<Constraint>,
87 {
88 self.widths = widths.into_iter().map(|v| v.into()).collect();
89 self
90 }
91
92 pub fn styles(mut self, style: impl IntoIterator<Item = impl Into<Style>>) -> Self {
94 self.style = style.into_iter().map(|v| v.into()).collect();
95 self
96 }
97}
98
99impl Default for StatusLineState {
100 fn default() -> Self {
101 Self {
102 area: Default::default(),
103 areas: Default::default(),
104 status: Default::default(),
105 non_exhaustive: NonExhaustive,
106 }
107 }
108}
109
110impl RelocatableState for StatusLineState {
111 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
112 self.area = relocate_area(self.area, shift, clip);
113 relocate_areas(self.areas.as_mut(), shift, clip);
114 }
115}
116
117impl StatusLineState {
118 pub fn new() -> Self {
119 Self::default()
120 }
121
122 pub fn clear_status(&mut self) {
124 self.status.clear();
125 }
126
127 pub fn status<S: Into<String>>(&mut self, idx: usize, msg: S) {
129 while self.status.len() <= idx {
130 self.status.push("".to_string());
131 }
132 self.status[idx] = msg.into();
133 }
134}
135
136impl StatefulWidget for &StatusLine {
137 type State = StatusLineState;
138
139 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
140 render_ref(self, area, buf, state);
141 }
142}
143
144impl StatefulWidget for StatusLine {
145 type State = StatusLineState;
146
147 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
148 render_ref(&self, area, buf, state);
149 }
150}
151
152fn render_ref(widget: &StatusLine, area: Rect, buf: &mut Buffer, state: &mut StatusLineState) {
153 state.area = area;
154
155 let layout = Layout::horizontal(widget.widths.iter()).split(state.area);
156
157 for (i, rect) in layout.iter().enumerate() {
158 let style = widget.style.get(i).copied().unwrap_or_default();
159 let txt = state.status.get(i).map(|v| v.as_str()).unwrap_or("");
160
161 buf.set_style(*rect, style);
162 Span::from(txt).render(*rect, buf);
163 }
164}