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::marker::PhantomData;
88use std::rc::Rc;
89
90#[derive(Debug, Clone)]
95pub struct Form<'a, W>
96where
97 W: Eq + Hash + Clone,
98{
99 style: Style,
100 block: Option<Block<'a>>,
101 layout: Option<GenericLayout<W>>,
102 pager: Pager<W>,
103 phantom: PhantomData<&'a ()>,
104}
105
106#[derive(Debug)]
113pub struct FormBuffer<'a, W>
114where
115 W: Eq + Hash + Clone,
116{
117 pager: PagerBuffer<'a, W>,
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 style: Default::default(),
141 block: Default::default(),
142 layout: Default::default(),
143 pager: Default::default(),
144 phantom: Default::default(),
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.style = style;
168 self.block = self.block.map(|v| v.style(style));
169 self.pager = self.pager.style(style);
170 self
171 }
172
173 pub fn label_style(mut self, style: Style) -> Self {
175 self.pager = self.pager.label_style(style);
176 self
177 }
178
179 pub fn label_alignment(mut self, alignment: Alignment) -> Self {
181 self.pager = self.pager.label_alignment(alignment);
182 self
183 }
184
185 pub fn styles(mut self, styles: PagerStyle) -> Self {
187 self.style = styles.style;
188 self.block = self.block.map(|v| v.style(styles.style));
189 self.pager = self.pager.styles(styles.clone());
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 layout_size(&self, area: Rect) -> Size {
201 self.block.inner_if_some(area).as_size()
202 }
203
204 pub fn inner(&self, area: Rect) -> Rect {
206 self.block.inner_if_some(area)
207 }
208
209 pub fn into_buffer(
212 self,
213 area: Rect,
214 buf: &'a mut Buffer,
215 state: &'a mut FormState<W>,
216 ) -> FormBuffer<'a, W> {
217 self.block.render(area, buf);
219 if let Some(layout) = self.layout {
221 state.layout = Rc::new(RefCell::new(layout));
222 }
223
224 FormBuffer {
225 pager: self
226 .pager .layout(state.layout.clone())
228 .page(0)
229 .into_buffer(area, Rc::new(RefCell::new(buf))),
230 }
231 }
232}
233
234impl<'a, W> FormBuffer<'a, W>
235where
236 W: Eq + Hash + Clone,
237{
238 pub fn is_visible(&self, widget: W) -> bool {
240 if let Some(idx) = self.pager.widget_idx(widget) {
241 self.pager.is_visible(idx)
242 } else {
243 false
244 }
245 }
246
247 pub fn render_block(&mut self) {
249 self.pager.render_block()
250 }
251
252 #[inline(always)]
254 pub fn render_label<FN, WW>(&mut self, widget: W, render_fn: FN) -> bool
255 where
256 FN: FnOnce(&Option<Cow<'static, str>>) -> WW,
257 WW: Widget,
258 {
259 let Some(idx) = self.pager.widget_idx(widget) else {
260 return false;
261 };
262 self.pager.render_label(idx, render_fn)
263 }
264
265 #[inline(always)]
267 pub fn render_widget<FN, WW>(&mut self, widget: W, render_fn: FN) -> bool
268 where
269 FN: FnOnce() -> WW,
270 WW: Widget,
271 {
272 let Some(idx) = self.pager.widget_idx(widget) else {
273 return false;
274 };
275 self.pager.render_auto_label(idx);
276 self.pager.render_widget(idx, render_fn)
277 }
278
279 #[inline(always)]
281 pub fn render_opt<FN, WW, SS>(&mut self, widget: W, render_fn: FN, state: &mut SS) -> bool
282 where
283 FN: FnOnce() -> Option<WW>,
284 WW: StatefulWidget<State = SS>,
285 SS: RelocatableState,
286 {
287 let Some(idx) = self.pager.widget_idx(widget) else {
288 return false;
289 };
290 self.pager.render_auto_label(idx);
291 if !self.pager.render_opt(idx, render_fn, state) {
292 self.hidden(state);
293 false
294 } else {
295 true
296 }
297 }
298
299 #[inline(always)]
301 pub fn render<FN, WW, SS>(&mut self, widget: W, render_fn: FN, state: &mut SS) -> bool
302 where
303 FN: FnOnce() -> WW,
304 WW: StatefulWidget<State = SS>,
305 SS: RelocatableState,
306 {
307 let Some(idx) = self.pager.widget_idx(widget) else {
308 return false;
309 };
310 self.pager.render_auto_label(idx);
311 if !self.pager.render(idx, render_fn, state) {
312 self.hidden(state);
313 false
314 } else {
315 true
316 }
317 }
318
319 #[inline(always)]
323 #[allow(clippy::question_mark)]
324 pub fn render2<FN, WW, SS, R>(&mut self, widget: W, render_fn: FN, state: &mut SS) -> Option<R>
325 where
326 FN: FnOnce() -> (WW, R),
327 WW: StatefulWidget<State = SS>,
328 SS: RelocatableState,
329 {
330 let Some(idx) = self.pager.widget_idx(widget) else {
331 return None;
332 };
333 self.pager.render_auto_label(idx);
334 if let Some(remainder) = self.pager.render2(idx, render_fn, state) {
335 Some(remainder)
336 } else {
337 self.hidden(state);
338 None
339 }
340 }
341
342 pub fn shift(&self) -> (i16, i16) {
348 (0, 0)
349 }
350
351 #[allow(clippy::question_mark)]
355 pub fn locate_widget(&self, widget: W) -> Option<Rect> {
356 let Some(idx) = self.pager.widget_idx(widget) else {
357 return None;
358 };
359 self.pager.locate_widget(idx)
360 }
361
362 #[allow(clippy::question_mark)]
366 pub fn locate_label(&self, widget: W) -> Option<Rect> {
367 let Some(idx) = self.pager.widget_idx(widget) else {
368 return None;
369 };
370 self.pager.locate_label(idx)
371 }
372
373 pub fn locate_area(&self, area: Rect) -> Option<Rect> {
378 self.pager.locate_area(area)
379 }
380
381 pub fn relocate<S>(&self, _state: &mut S)
384 where
385 S: RelocatableState,
386 {
387 }
388
389 pub fn hidden<S>(&self, state: &mut S)
392 where
393 S: RelocatableState,
394 {
395 state.relocate((0, 0), Rect::default())
396 }
397
398 pub fn buffer<'b>(&'b mut self) -> RefMut<'b, &'a mut Buffer> {
400 self.pager.buffer()
401 }
402}
403
404impl<W> Default for FormState<W>
405where
406 W: Eq + Hash + Clone,
407{
408 fn default() -> Self {
409 Self {
410 layout: Default::default(),
411 non_exhaustive: NonExhaustive,
412 }
413 }
414}
415
416impl<W> FormState<W>
417where
418 W: Eq + Hash + Clone,
419{
420 pub fn new() -> Self {
421 Self::default()
422 }
423
424 pub fn valid_layout(&self, size: Size) -> bool {
426 let layout = self.layout.borrow();
427 !layout.size_changed(size) && !layout.is_empty()
428 }
429
430 pub fn set_layout(&mut self, layout: GenericLayout<W>) {
432 self.layout = Rc::new(RefCell::new(layout));
433 }
434
435 pub fn layout(&self) -> Ref<'_, GenericLayout<W>> {
437 self.layout.borrow()
438 }
439
440 pub fn clear(&mut self) {
442 self.layout.borrow_mut().clear();
443 }
444}