use crossterm::event::KeyEvent;
use ratatui::layout::Rect;
use ratatui::text::Line;
use ratatui::Frame;
use super::types::{ModalKind, ModalRenderCtx};
use crate::ui::modal::LinkableResponse;
use crate::ui::modal_links::ModalLink;
use crate::ui::{ModalButton, ModalResponse, ModalState, ModalView};
pub fn body_columns(area: Rect) -> u16 {
area.width.saturating_sub(2 * crate::ui::MIN_PAD_H)
}
pub struct ModalChrome {
pub state: ModalState,
kind: ModalKind,
dismissable: bool,
max_content_w: Option<u16>,
}
impl ModalChrome {
pub fn new(kind: ModalKind, dismissable: bool) -> Self {
Self {
state: ModalState::new(),
kind,
dismissable,
max_content_w: None,
}
}
pub fn with_max_content_width(mut self, width: u16) -> Self {
self.max_content_w = Some(width);
self
}
pub fn kind(&self) -> ModalKind {
self.kind
}
pub fn dismissable(&self) -> bool {
self.dismissable
}
pub fn render(
&mut self,
frame: &mut Frame<'_>,
area: Rect,
ctx: &ModalRenderCtx<'_>,
title: &str,
body: &[Line<'_>],
buttons: &[ModalButton],
) {
self.render_with_links(frame, area, ctx, title, body, buttons, &[]);
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn render_with_links(
&mut self,
frame: &mut Frame<'_>,
area: Rect,
ctx: &ModalRenderCtx<'_>,
title: &str,
body: &[Line<'_>],
buttons: &[ModalButton],
links: &[ModalLink],
) {
let mut view = ModalView::new(title, body, buttons, ctx.theme, self.kind, self.dismissable);
if let Some(w) = self.max_content_w {
view = view.with_max_content_width(w);
}
if !links.is_empty() {
view = view.with_links(links);
}
frame.render_stateful_widget(view, area, &mut self.state);
}
pub fn on_key(&mut self, key: &KeyEvent, num_buttons: usize) -> ModalResponse {
self.state.handle_key(key, num_buttons, self.dismissable)
}
pub(crate) fn on_key_linkable(
&mut self,
key: &KeyEvent,
num_links: usize,
num_buttons: usize,
) -> LinkableResponse {
self.state
.handle_key_linkable(key, num_links, num_buttons, self.dismissable)
}
pub(crate) fn on_click_linkable(&self, col: u16, row: u16) -> LinkableResponse {
self.state.handle_click_linkable(col, row, self.dismissable)
}
pub(crate) fn focused_link(&self) -> Option<usize> {
self.state.focused_link
}
pub fn on_click(&self, col: u16, row: u16) -> ModalResponse {
self.state.handle_click(col, row, self.dismissable)
}
pub fn on_wheel(&mut self, delta: i32) {
self.state.scroll_by(delta);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ui::PROSE_CONTENT_WIDTH;
#[test]
fn the_width_cap_is_opt_in() {
assert_eq!(
ModalChrome::new(ModalKind::Normal, true).max_content_w,
None
);
}
#[test]
fn the_width_cap_builder_is_chainable_onto_new() {
let chrome =
ModalChrome::new(ModalKind::Warning, false).with_max_content_width(PROSE_CONTENT_WIDTH);
assert_eq!(chrome.max_content_w, Some(PROSE_CONTENT_WIDTH));
assert_eq!(chrome.kind(), ModalKind::Warning);
assert!(!chrome.dismissable());
}
}