use ratatui::{
buffer::Buffer,
layout::{Constraint, Layout, Rect},
style::{Color, Style},
widgets::{Borders, StatefulWidget},
};
use crate::popup::{Popup, PopupSize};
use crate::popups::AutoSized;
use crate::text_input::{TextInput, TextInputState};
use crate::{BadgeStack, BorderType, DotPattern};
#[derive(Clone)]
pub struct TextInputPopup<'a> {
popup: Popup<'a>,
text_input: TextInput<'a>,
}
impl<'a> TextInputPopup<'a> {
pub fn new() -> Self {
Self {
popup: Popup::new(Color::White)
.padding(0)
.border_type(BorderType::Rounded),
text_input: TextInput::new(),
}
}
pub fn title(mut self, title: &'a str) -> Self {
self.popup = self.popup.title(title);
self
}
pub fn cursor_style(mut self, style: Style) -> Self {
self.text_input = self.text_input.cursor_style(style);
self
}
pub fn text_style(mut self, style: Style) -> Self {
self.text_input = self.text_input.text_style(style);
self
}
pub fn border_style(mut self, style: Style) -> Self {
self.text_input = self.text_input.border_style(style);
self
}
pub fn borders(mut self, borders: Borders) -> Self {
self.text_input = self.text_input.borders(borders);
self
}
pub fn input_bg_color(mut self, color: Color) -> Self {
self.text_input = self.text_input.bg_color(color);
self
}
pub fn bg_color(mut self, color: Color) -> Self {
self.popup = self.popup.bg_color(color);
self
}
pub fn scroll_padding(mut self, padding: u16) -> Self {
self.text_input = self.text_input.scroll_padding(padding);
self
}
pub fn scroll_reserve(mut self, reserve: u16) -> Self {
self.text_input = self.text_input.scroll_reserve(reserve);
self
}
pub fn rows(mut self, rows: u16) -> Self {
self.text_input = self.text_input.rows(rows);
self
}
pub fn placeholder(mut self, placeholder: &'a str) -> Self {
self.text_input = self.text_input.placeholder(placeholder);
self
}
pub fn border_color(mut self, color: Color) -> Self {
self.popup = self.popup.border_color(color);
self
}
pub fn border_type(mut self, bt: BorderType) -> Self {
self.popup = self.popup.border_type(bt);
self
}
pub fn width(mut self, w: PopupSize) -> Self {
self.popup = self.popup.width(w);
self
}
pub fn height(mut self, h: PopupSize) -> Self {
self.popup = self.popup.height(h);
self
}
pub fn position(mut self, x: u16, y: u16) -> Self {
self.popup = self.popup.position(x, y);
self
}
pub fn origin(mut self, x: u16, y: u16) -> Self {
self.popup = self.popup.origin(x, y);
self
}
pub fn header(mut self) -> Self {
self.popup = self.popup.header();
self
}
pub fn no_background(mut self) -> Self {
self.popup = self.popup.no_background();
self
}
pub fn background_dots(mut self, color: Color, symbol: &str, density: u16) -> Self {
self.popup = self.popup.background_dots(color, symbol, density);
self
}
pub fn background_pattern(mut self, pattern: DotPattern) -> Self {
self.popup = self.popup.background_pattern(pattern);
self
}
pub fn background_spacing(mut self, density_x: u16, density_y: u16) -> Self {
self.popup = self.popup.background_spacing(density_x, density_y);
self
}
pub fn badges(mut self, badges: BadgeStack<'a>) -> Self {
self.popup = self.popup.badges(badges);
self
}
}
impl AutoSized for TextInputPopup<'_> {
type State = TextInputState;
fn auto_height(&self, _state: &Self::State, _area: Rect) -> u16 {
let header_extra = if self.popup.has_header() { 2 } else { 0 };
self.text_input.required_height() + 2 + header_extra
}
}
impl<'a> TextInputPopup<'a> {
pub fn resolve_rect(&self, area: Rect, state: &TextInputState) -> Rect {
if self.popup.get_height() == PopupSize::Auto {
self.popup
.clone()
.height(PopupSize::Fixed(self.auto_height(state, area)))
.resolve_rect(area)
} else {
self.popup.resolve_rect(area)
}
}
}
impl StatefulWidget for TextInputPopup<'_> {
type State = TextInputState;
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
let input_height = self.text_input.required_height();
let auto_h = if self.popup.get_height() == PopupSize::Auto {
Some(self.auto_height(state, area))
} else {
None
};
let mut popup = self.popup;
if let Some(h) = auto_h {
popup = popup.height(PopupSize::Fixed(h));
}
let inner = popup.render_inner(area, buf);
let chunks = Layout::vertical([
Constraint::Min(0),
Constraint::Length(input_height),
Constraint::Min(0),
]).split(inner);
self.text_input.render(chunks[1], buf, state);
}
}
#[cfg(test)]
mod tests;