1use crate::_private::NonExhaustive;
76use crate::layout::GenericLayout;
77use crate::pager::{Pager, PagerBuffer, PagerStyle};
78use rat_reloc::RelocatableState;
79use ratatui::buffer::Buffer;
80use ratatui::layout::{Alignment, Rect, Size};
81use ratatui::prelude::BlockExt;
82use ratatui::style::Style;
83use ratatui::widgets::{Block, StatefulWidget, Widget};
84use std::borrow::Cow;
85use std::cell::{Ref, RefCell, RefMut};
86use std::hash::Hash;
87use std::rc::Rc;
88
89#[derive(Debug, Clone)]
94pub struct Form<'a, W>
95where
96 W: Eq + Hash + Clone,
97{
98 layout: Option<GenericLayout<W>>,
99 pager: Pager<W>,
100 style: Style,
101 block: Option<Block<'a>>,
102 auto_label: bool,
103}
104
105#[derive(Debug)]
112pub struct FormBuffer<'a, W>
113where
114 W: Eq + Hash + Clone,
115{
116 pager: PagerBuffer<'a, W>,
117 auto_label: bool,
118}
119
120#[derive(Debug, Clone)]
122pub struct FormState<W>
123where
124 W: Eq + Hash + Clone,
125{
126 pub layout: Rc<RefCell<GenericLayout<W>>>,
129
130 pub non_exhaustive: NonExhaustive,
132}
133
134impl<W> Default for Form<'_, W>
135where
136 W: Eq + Hash + Clone,
137{
138 fn default() -> Self {
139 Self {
140 layout: Default::default(),
141 pager: Default::default(),
142 style: Default::default(),
143 block: Default::default(),
144 auto_label: true,
145 }
146 }
147}
148
149impl<'a, W> Form<'a, W>
150where
151 W: Eq + Hash + Clone,
152{
153 pub fn new() -> Self {
155 Self::default()
156 }
157
158 pub fn layout(mut self, layout: GenericLayout<W>) -> Self {
161 self.layout = Some(layout);
162 self
163 }
164
165 pub fn style(mut self, style: Style) -> Self {
167 self.pager = self.pager.style(style);
168 self.style = style;
169 self.block = self.block.map(|v| v.style(style));
170 self
171 }
172
173 pub fn auto_label(mut self, auto: bool) -> Self {
177 self.auto_label = auto;
178 self
179 }
180
181 pub fn label_style(mut self, style: Style) -> Self {
183 self.pager = self.pager.label_style(style);
184 self
185 }
186
187 pub fn label_alignment(mut self, alignment: Alignment) -> Self {
189 self.pager = self.pager.label_alignment(alignment);
190 self
191 }
192
193 pub fn block(mut self, block: Block<'a>) -> Self {
195 self.block = Some(block.style(self.style));
196 self
197 }
198
199 pub fn styles(mut self, styles: PagerStyle) -> Self {
201 self.pager = self.pager.styles(styles.clone());
202 self.style = styles.style;
203 self.block = self.block.map(|v| v.style(styles.style));
204 self
205 }
206
207 pub fn layout_size(&self, area: Rect) -> Size {
209 self.block.inner_if_some(area).as_size()
210 }
211
212 pub fn inner(&self, area: Rect) -> Rect {
214 self.block.inner_if_some(area)
215 }
216
217 pub fn into_buffer(
220 self,
221 area: Rect,
222 buf: &'a mut Buffer,
223 state: &'a mut FormState<W>,
224 ) -> FormBuffer<'a, W> {
225 self.block.render(area, buf);
227 if let Some(layout) = self.layout {
229 state.layout = Rc::new(RefCell::new(layout));
230 }
231
232 FormBuffer {
233 pager: self
234 .pager .layout(state.layout.clone())
236 .page(0)
237 .into_buffer(area, Rc::new(RefCell::new(buf))),
238 auto_label: self.auto_label,
239 }
240 }
241}
242
243impl<'a, W> FormBuffer<'a, W>
244where
245 W: Eq + Hash + Clone,
246{
247 pub fn is_visible(&self, widget: W) -> bool {
249 if let Some(idx) = self.pager.widget_idx(widget) {
250 self.pager.is_visible(idx)
251 } else {
252 false
253 }
254 }
255
256 pub fn render_block(&mut self) {
258 self.pager.render_block()
259 }
260
261 #[inline(always)]
263 pub fn render_label<FN>(&mut self, widget: W, render_fn: FN) -> bool
264 where
265 FN: FnOnce(&Cow<'static, str>, Rect, &mut Buffer),
266 {
267 let Some(idx) = self.pager.widget_idx(widget) else {
268 return false;
269 };
270 self.pager.render_label(idx, render_fn)
271 }
272
273 #[inline(always)]
275 pub fn render_widget<FN, WW>(&mut self, widget: W, render_fn: FN) -> bool
276 where
277 FN: FnOnce() -> WW,
278 WW: Widget,
279 {
280 let Some(idx) = self.pager.widget_idx(widget) else {
281 return false;
282 };
283 if self.auto_label {
284 self.pager.render_auto_label(idx);
285 }
286 self.pager.render_widget(idx, render_fn)
287 }
288
289 #[inline(always)]
291 pub fn render_opt<FN, WW, SS>(&mut self, widget: W, render_fn: FN, state: &mut SS) -> bool
292 where
293 FN: FnOnce() -> Option<WW>,
294 WW: StatefulWidget<State = SS>,
295 SS: RelocatableState,
296 {
297 let Some(idx) = self.pager.widget_idx(widget) else {
298 return false;
299 };
300 if self.auto_label {
301 self.pager.render_auto_label(idx);
302 }
303 if !self.pager.render_opt(idx, render_fn, state) {
304 self.hidden(state);
305 false
306 } else {
307 true
308 }
309 }
310
311 #[inline(always)]
313 pub fn render<FN, WW, SS>(&mut self, widget: W, render_fn: FN, state: &mut SS) -> bool
314 where
315 FN: FnOnce() -> WW,
316 WW: StatefulWidget<State = SS>,
317 SS: RelocatableState,
318 {
319 let Some(idx) = self.pager.widget_idx(widget) else {
320 return false;
321 };
322 if self.auto_label {
323 self.pager.render_auto_label(idx);
324 }
325 if self.pager.render(idx, render_fn, state) {
326 true
327 } else {
328 self.hidden(state);
329 false
330 }
331 }
332
333 #[inline(always)]
337 #[allow(clippy::question_mark)]
338 pub fn render2<FN, WW, SS, R>(&mut self, widget: W, render_fn: FN, state: &mut SS) -> Option<R>
339 where
340 FN: FnOnce() -> (WW, R),
341 WW: StatefulWidget<State = SS>,
342 SS: RelocatableState,
343 {
344 let Some(idx) = self.pager.widget_idx(widget) else {
345 return None;
346 };
347 if self.auto_label {
348 self.pager.render_auto_label(idx);
349 }
350 if let Some(remainder) = self.pager.render2(idx, render_fn, state) {
351 Some(remainder)
352 } else {
353 self.hidden(state);
354 None
355 }
356 }
357
358 pub fn shift(&self) -> (i16, i16) {
364 (0, 0)
365 }
366
367 #[allow(clippy::question_mark)]
371 pub fn locate_widget(&self, widget: W) -> Option<Rect> {
372 let Some(idx) = self.pager.widget_idx(widget) else {
373 return None;
374 };
375 self.pager.locate_widget(idx)
376 }
377
378 #[allow(clippy::question_mark)]
382 pub fn locate_label(&self, widget: W) -> Option<Rect> {
383 let Some(idx) = self.pager.widget_idx(widget) else {
384 return None;
385 };
386 self.pager.locate_label(idx)
387 }
388
389 pub fn locate_area(&self, area: Rect) -> Option<Rect> {
394 self.pager.locate_area(area)
395 }
396
397 pub fn relocate<S>(&self, _state: &mut S)
400 where
401 S: RelocatableState,
402 {
403 }
404
405 pub fn hidden<S>(&self, state: &mut S)
408 where
409 S: RelocatableState,
410 {
411 state.relocate((0, 0), Rect::default())
412 }
413
414 pub fn buffer<'b>(&'b mut self) -> RefMut<'b, &'a mut Buffer> {
416 self.pager.buffer()
417 }
418}
419
420impl<W> Default for FormState<W>
421where
422 W: Eq + Hash + Clone,
423{
424 fn default() -> Self {
425 Self {
426 layout: Default::default(),
427 non_exhaustive: NonExhaustive,
428 }
429 }
430}
431
432impl<W> FormState<W>
433where
434 W: Eq + Hash + Clone,
435{
436 pub fn new() -> Self {
437 Self::default()
438 }
439
440 pub fn clear(&mut self) {
442 self.layout.borrow_mut().clear();
443 }
444
445 pub fn valid_layout(&self, size: Size) -> bool {
447 let layout = self.layout.borrow();
448 !layout.size_changed(size) && !layout.is_empty()
449 }
450
451 pub fn set_layout(&mut self, layout: GenericLayout<W>) {
453 self.layout = Rc::new(RefCell::new(layout));
454 }
455
456 pub fn layout(&self) -> Ref<'_, GenericLayout<W>> {
458 self.layout.borrow()
459 }
460}