use crate::{Contrast, Palette, SalsaTheme};
use rat_widget::button::ButtonStyle;
use rat_widget::calendar::CalendarStyle;
use rat_widget::checkbox::CheckboxStyle;
use rat_widget::choice::ChoiceStyle;
use rat_widget::clipper::ClipperStyle;
use rat_widget::file_dialog::FileDialogStyle;
use rat_widget::form::FormStyle;
use rat_widget::line_number::LineNumberStyle;
use rat_widget::list::ListStyle;
use rat_widget::menu::MenuStyle;
use rat_widget::msgdialog::MsgDialogStyle;
use rat_widget::paragraph::ParagraphStyle;
use rat_widget::popup::PopupStyle;
use rat_widget::radio::{RadioLayout, RadioStyle};
use rat_widget::scrolled::ScrollStyle;
use rat_widget::shadow::{ShadowDirection, ShadowStyle};
use rat_widget::slider::SliderStyle;
use rat_widget::splitter::SplitStyle;
use rat_widget::statusline::StatusLineStyle;
use rat_widget::tabbed::TabbedStyle;
use rat_widget::table::TableStyle;
use rat_widget::text::TextStyle;
use rat_widget::view::ViewStyle;
use ratatui_core::layout::Alignment;
use ratatui_core::style::{Color, Style};
use ratatui_widgets::block::Block;
use ratatui_widgets::borders::Borders;
use std::borrow::Cow;
use std::time::Duration;
#[derive(Debug, Clone)]
pub struct ShellTheme {
p: Palette,
name: Box<str>,
}
impl ShellTheme {
pub fn new(name: &str, p: Palette) -> Self {
Self {
p,
name: Box::from(name),
}
}
fn fg_style(&self, color: Color) -> Style {
Style::new().fg(color)
}
}
impl SalsaTheme for ShellTheme {
fn name(&self) -> &str {
&self.name
}
fn palette(&self) -> &Palette {
&self.p
}
fn white(&self, n: usize) -> Style {
self.p.white(n, Contrast::Normal)
}
fn black(&self, n: usize) -> Style {
self.p.black(n, Contrast::Normal)
}
fn gray(&self, n: usize) -> Style {
self.p.gray(n, Contrast::Normal)
}
fn red(&self, n: usize) -> Style {
self.p.red(n, Contrast::Normal)
}
fn orange(&self, n: usize) -> Style {
self.p.orange(n, Contrast::Normal)
}
fn yellow(&self, n: usize) -> Style {
self.p.yellow(n, Contrast::Normal)
}
fn limegreen(&self, n: usize) -> Style {
self.p.limegreen(n, Contrast::Normal)
}
fn green(&self, n: usize) -> Style {
self.p.green(n, Contrast::Normal)
}
fn bluegreen(&self, n: usize) -> Style {
self.p.bluegreen(n, Contrast::Normal)
}
fn cyan(&self, n: usize) -> Style {
self.p.cyan(n, Contrast::Normal)
}
fn blue(&self, n: usize) -> Style {
self.p.blue(n, Contrast::Normal)
}
fn deepblue(&self, n: usize) -> Style {
self.p.deepblue(n, Contrast::Normal)
}
fn purple(&self, n: usize) -> Style {
self.p.purple(n, Contrast::Normal)
}
fn magenta(&self, n: usize) -> Style {
self.p.magenta(n, Contrast::Normal)
}
fn redpink(&self, n: usize) -> Style {
self.p.redpink(n, Contrast::Normal)
}
fn primary(&self, n: usize) -> Style {
self.p.primary(n, Contrast::Normal)
}
fn secondary(&self, n: usize) -> Style {
self.p.secondary(n, Contrast::Normal)
}
fn focus(&self) -> Style {
self.p.high_contrast(self.p.primary[Palette::BRIGHT_2])
}
fn select(&self) -> Style {
self.p.high_contrast(self.p.secondary[Palette::BRIGHT_2])
}
fn text_input(&self) -> Style {
self.p.normal_contrast(self.p.gray[Palette::BRIGHT_0])
}
fn text_focus(&self) -> Style {
self.p.normal_contrast(self.p.gray[Palette::BRIGHT_3])
}
fn text_select(&self) -> Style {
self.p.normal_contrast(self.p.secondary[Palette::BRIGHT_0])
}
fn container_base(&self) -> Style {
Default::default()
}
fn container_border(&self) -> Style {
Default::default()
}
fn container_arrow(&self) -> Style {
Default::default()
}
fn popup_base(&self) -> Style {
self.p
.style(self.p.gray[Palette::BRIGHT_0], Contrast::Normal)
}
fn popup_border(&self) -> Style {
self.popup_base().fg(self.p.gray[Palette::BRIGHT_0])
}
fn popup_arrow(&self) -> Style {
self.popup_base().fg(self.p.gray[Palette::BRIGHT_0])
}
fn dialog_base(&self) -> Style {
self.p
.style(self.p.gray[Palette::BRIGHT_1], Contrast::Normal)
}
fn dialog_border(&self) -> Style {
self.dialog_base().fg(self.p.white[Palette::BRIGHT_0])
}
fn dialog_arrow(&self) -> Style {
self.dialog_base().fg(self.p.white[Palette::BRIGHT_0])
}
fn status_base(&self) -> Style {
Default::default()
}
fn button_base(&self) -> Style {
self.p
.style(self.p.gray[Palette::BRIGHT_2], Contrast::Normal)
}
fn button_armed(&self) -> Style {
self.p
.style(self.p.secondary[Palette::BRIGHT_0], Contrast::Normal)
}
fn month_style(&self) -> CalendarStyle {
CalendarStyle {
style: Default::default(),
title: None,
weeknum: Some(Style::new().fg(self.p.limegreen[Palette::BRIGHT_0])),
weekday: Some(Style::new().fg(self.p.limegreen[Palette::BRIGHT_0])),
day: None,
select: Some(self.select()),
focus: Some(self.focus()),
..CalendarStyle::default()
}
}
fn shadow_style(&self) -> ShadowStyle {
ShadowStyle {
style: Style::new().bg(self.p.black[Palette::DARK_0]),
dir: ShadowDirection::BottomRight,
..ShadowStyle::default()
}
}
fn line_nr_style(&self) -> LineNumberStyle {
LineNumberStyle {
style: self.container_base(),
cursor: Some(self.text_select()),
..LineNumberStyle::default()
}
}
fn textarea_style(&self) -> TextStyle {
TextStyle {
style: self.text_input(),
select: Some(self.text_select()),
scroll: Some(self.scroll_style()),
border_style: Some(self.container_border()),
..TextStyle::default()
}
}
fn text_style(&self) -> TextStyle {
TextStyle {
style: self.text_input(),
focus: Some(self.text_focus()),
select: Some(self.text_select()),
invalid: Some(self.fg_style(self.p.red[Palette::BRIGHT_3])),
..TextStyle::default()
}
}
fn label_style(&self) -> Style {
self.container_base()
}
fn paragraph_style(&self) -> ParagraphStyle {
ParagraphStyle {
style: self.container_base(),
focus: Some(self.focus()),
scroll: Some(self.scroll_style()),
..Default::default()
}
}
fn choice_style(&self) -> ChoiceStyle {
ChoiceStyle {
style: self.text_input(),
select: Some(self.text_select()),
focus: Some(self.text_focus()),
popup: PopupStyle::default(),
popup_style: Some(self.popup_base()),
popup_border: Some(self.popup_border()),
popup_scroll: Some(self.popup_scroll_style()),
popup_block: Some(
Block::bordered()
.borders(Borders::LEFT)
.border_style(self.popup_border()),
),
..Default::default()
}
}
fn radio_style(&self) -> RadioStyle {
RadioStyle {
layout: Some(RadioLayout::Stacked),
style: self.text_input(),
focus: Some(self.text_focus()),
..Default::default()
}
}
fn checkbox_style(&self) -> CheckboxStyle {
CheckboxStyle {
style: self.text_input(),
focus: Some(self.text_focus()),
..Default::default()
}
}
fn slider_style(&self) -> SliderStyle {
SliderStyle {
style: self.text_input(),
bounds: Some(self.gray(2)),
knob: Some(self.select()),
focus: Some(self.focus()),
text_align: Some(Alignment::Center),
..Default::default()
}
}
fn menu_style(&self) -> MenuStyle {
MenuStyle {
style: self.status_base(),
title: Some(self.fg_style(self.p.yellow[Palette::BRIGHT_2])),
focus: Some(self.focus()),
right: Some(self.fg_style(self.p.green[Palette::BRIGHT_3])),
disabled: Some(self.fg_style(self.p.gray[Palette::BRIGHT_2])),
highlight: Some(Style::default().underlined()),
popup_block: Some(Block::bordered().style(self.popup_border())),
popup_focus: Some(self.focus()),
popup_right: Some(self.fg_style(self.p.green[Palette::BRIGHT_3])),
popup_disabled: Some(self.fg_style(self.p.gray[Palette::BRIGHT_2])),
popup_highlight: Some(Style::default().underlined()),
..Default::default()
}
}
fn button_style(&self) -> ButtonStyle {
ButtonStyle {
style: self.button_base(),
focus: Some(self.focus()),
armed: Some(self.select()),
armed_delay: Some(Duration::from_millis(50)),
..Default::default()
}
}
fn table_style(&self) -> TableStyle {
TableStyle {
style: self.container_base(),
select_row: Some(self.select()),
show_row_focus: true,
focus_style: Some(self.focus()),
border_style: Some(self.container_border()),
scroll: Some(self.scroll_style()),
header: Some(self.fg_style(self.p.green[Palette::BRIGHT_2])),
footer: Some(self.fg_style(self.p.green[Palette::BRIGHT_2])),
..Default::default()
}
}
fn list_style(&self) -> ListStyle {
ListStyle {
style: self.container_base(),
select: Some(self.select()),
focus: Some(self.focus()),
scroll: Some(self.scroll_style()),
..Default::default()
}
}
fn scroll_style(&self) -> ScrollStyle {
ScrollStyle {
thumb_style: Some(self.container_border()),
track_style: Some(self.container_border()),
min_style: Some(self.container_border()),
begin_style: Some(self.container_arrow()),
end_style: Some(self.container_arrow()),
..Default::default()
}
}
fn popup_scroll_style(&self) -> ScrollStyle {
ScrollStyle {
thumb_style: Some(self.popup_border()),
track_style: Some(self.popup_border()),
min_style: Some(self.popup_border()),
begin_style: Some(self.popup_arrow()),
end_style: Some(self.popup_arrow()),
..Default::default()
}
}
fn dialog_scroll_style(&self) -> ScrollStyle {
ScrollStyle {
thumb_style: Some(self.dialog_border()),
track_style: Some(self.dialog_border()),
min_style: Some(self.dialog_border()),
begin_style: Some(self.dialog_arrow()),
end_style: Some(self.dialog_arrow()),
..Default::default()
}
}
fn split_style(&self) -> SplitStyle {
SplitStyle {
style: self.container_border(),
arrow_style: Some(self.container_arrow()),
drag_style: Some(self.focus()),
..Default::default()
}
}
fn view_style(&self) -> ViewStyle {
ViewStyle {
scroll: Some(self.scroll_style()),
..Default::default()
}
}
fn tabbed_style(&self) -> TabbedStyle {
TabbedStyle {
style: self.container_base(),
tab: Some(self.button_base()),
select: Some(self.button_armed()),
focus: Some(self.focus()),
..Default::default()
}
}
fn statusline_style(&self) -> Vec<Style> {
vec![
self.status_base(),
self.fg_style(self.p.blue[Palette::BRIGHT_2]),
self.fg_style(self.p.blue[Palette::BRIGHT_2]),
self.fg_style(self.p.blue[Palette::BRIGHT_2]),
]
}
fn statusline_style_ext(&self) -> StatusLineStyle {
StatusLineStyle {
sep: Some(Cow::Borrowed("|")),
styles: vec![
self.status_base(),
self.status_base().fg(self.p.blue[Palette::BRIGHT_2]),
self.status_base().fg(self.p.blue[Palette::BRIGHT_2]),
self.status_base().fg(self.p.blue[Palette::BRIGHT_2]),
],
..Default::default()
}
}
fn file_dialog_style(&self) -> FileDialogStyle {
FileDialogStyle {
style: self.dialog_base(),
list: Some(self.list_style()),
roots: Some(ListStyle {
style: self.dialog_base(),
..self.list_style()
}),
text: Some(self.text_style()),
button: Some(self.button_style()),
block: Some(Block::bordered()),
..Default::default()
}
}
fn msg_dialog_style(&self) -> MsgDialogStyle {
MsgDialogStyle {
style: self.dialog_base(),
button: Some(self.button_style()),
..Default::default()
}
}
fn form_style(&self) -> FormStyle {
FormStyle {
style: self.container_base(),
navigation: Some(self.container_arrow()),
block: Some(
Block::default()
.borders(Borders::TOP)
.border_style(self.container_border()),
),
..Default::default()
}
}
fn clipper_style(&self) -> ClipperStyle {
ClipperStyle {
style: self.container_base(),
scroll: Some(self.scroll_style()),
..Default::default()
}
}
fn textview_style(&self) -> TextStyle {
TextStyle {
style: self.container_base(),
focus: Some(self.container_base()),
select: Some(self.text_select()),
scroll: Some(self.scroll_style()),
border_style: Some(self.container_border()),
..TextStyle::default()
}
}
}