use ratatui::layout::{Constraint, Direction, Layout, Rect};
use ratatui::style::{Color, Modifier, Style};
use ratatui::widgets::{Scrollbar, ScrollbarOrientation};
use std::rc::Rc;
use crate::themes;
use crate::ui::UI_CONSTANTS;
#[must_use]
pub fn viewer_scrollbar() -> Scrollbar<'static> {
Scrollbar::new(ScrollbarOrientation::VerticalRight)
.begin_symbol(None)
.end_symbol(None)
}
#[must_use]
pub fn chrome_page_background(transparent_page: bool) -> Color {
if transparent_page {
Color::Reset
} else {
themes::current().background
}
}
#[must_use]
pub fn tab_row_padded(area: Rect) -> Rc<[Rect]> {
Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Length(UI_CONSTANTS.h_pad),
Constraint::Min(0),
Constraint::Length(UI_CONSTANTS.h_pad),
])
.split(area)
}
#[must_use]
pub fn split_vertical(area: Rect, constraints: &[Constraint]) -> Rc<[Rect]> {
Layout::default()
.direction(Direction::Vertical)
.constraints(constraints)
.split(area)
}
#[must_use]
pub fn split_main_tabs_and_body(area: Rect) -> (Rect, Option<Rect>, Rect) {
let gap = UI_CONSTANTS.tab_body_gap_height;
let tab_h = UI_CONSTANTS.tab_row_height;
let min_with_gap = tab_h + gap + 1;
if area.height >= min_with_gap {
let vs = split_vertical(area, &UI_CONSTANTS.tab_row_constraints());
(vs[0], Some(vs[1]), vs[2])
} else if area.height >= 2 {
let vs = split_vertical(area, &[Constraint::Length(tab_h), Constraint::Min(1)]);
(vs[0], None, vs[1])
} else {
(area, None, area)
}
}
#[must_use]
pub fn centered_popup_rect(
area: Rect,
content_w: usize,
content_h: usize,
padding_w: u16,
padding_h: u16,
) -> Rect {
let w = (content_w + usize::from(padding_w)).min(area.width as usize) as u16;
let h = (content_h + usize::from(padding_h)).min(area.height as usize) as u16;
let x = area.x + area.width.saturating_sub(w) / 2;
let y = area.y + area.height.saturating_sub(h) / 2;
Rect::new(x, y, w, h)
}
#[must_use]
pub fn rect_with_h_pad(area: Rect) -> Rect {
let pad = UI_CONSTANTS.h_pad;
let width = area.width.saturating_sub(2 * pad);
Rect {
x: area.x + pad,
y: area.y,
width,
height: area.height,
}
}
pub trait ThemeStyles {
fn palette() -> &'static themes::Palette
where
Self: Sized;
#[must_use]
fn panel_focused() -> Style
where
Self: Sized,
{
let t = Self::palette();
Style::default()
.fg(t.focused_border)
.add_modifier(Modifier::BOLD)
}
#[must_use]
fn panel_unfocused() -> Style
where
Self: Sized,
{
Style::default()
}
#[must_use]
fn text_style() -> Style
where
Self: Sized,
{
Style::default().fg(Self::palette().text)
}
#[must_use]
fn tab_active() -> Style
where
Self: Sized,
{
let t = Self::palette();
Style::default()
.fg(t.tab_active_fg)
.bg(t.tab_active_bg)
.add_modifier(Modifier::BOLD)
}
#[must_use]
fn tab_inactive() -> Style
where
Self: Sized,
{
let t = Self::palette();
Style::default().fg(t.text).bg(t.tab_inactive_bg)
}
#[must_use]
fn search_text() -> Style
where
Self: Sized,
{
let t = Self::palette();
Style::default().fg(t.search_text)
}
#[must_use]
fn hint_text() -> Style
where
Self: Sized,
{
let t = Self::palette();
Style::default().fg(t.hint).bg(t.popup_bg)
}
#[must_use]
fn delta_added() -> Style
where
Self: Sized,
{
Style::default().fg(Self::palette().delta_added)
}
#[must_use]
fn delta_mod() -> Style
where
Self: Sized,
{
Style::default().fg(Self::palette().delta_mod)
}
#[must_use]
fn delta_removed() -> Style
where
Self: Sized,
{
Style::default().fg(Self::palette().delta_removed)
}
#[must_use]
fn title_brand() -> Style
where
Self: Sized,
{
Style::default().fg(Self::palette().title_brand)
}
}
pub struct CurrentTheme;
impl ThemeStyles for CurrentTheme {
fn palette() -> &'static themes::Palette {
themes::current()
}
}
#[must_use]
pub fn list_highlight() -> Style {
Style::default().add_modifier(Modifier::REVERSED)
}
#[must_use]
pub fn panel_list_style(pane_focused: bool) -> Style {
let mut s = CurrentTheme::text_style();
if pane_focused {
s = s.add_modifier(Modifier::BOLD);
}
s
}
#[must_use]
pub fn panel_title_style(focused: bool) -> Style {
if focused {
CurrentTheme::panel_focused()
} else {
CurrentTheme::text_style()
}
}
macro_rules! theme_style_fn {
($name:ident) => {
pub fn $name() -> Style {
CurrentTheme::$name()
}
};
}
macro_rules! theme_style_fn_thru_list {
($($name:ident),* $(,)?) => {
$( theme_style_fn!($name); )*
};
}
theme_style_fn_thru_list!(
panel_focused,
panel_unfocused,
text_style,
tab_active,
tab_inactive,
search_text,
hint_text,
delta_added,
delta_mod,
delta_removed,
title_brand,
);
#[must_use]
pub fn viewer_find_match() -> Style {
CurrentTheme::search_text().add_modifier(Modifier::UNDERLINED)
}
#[must_use]
pub fn viewer_find_match_table_cell() -> Style {
let t = CurrentTheme::palette();
Style::default()
.fg(t.text)
.add_modifier(Modifier::BOLD | Modifier::UNDERLINED)
}
#[must_use]
pub fn viewer_find_match_current_table_cell() -> Style {
let t = CurrentTheme::palette();
Style::default()
.fg(t.search_text)
.add_modifier(Modifier::BOLD | Modifier::UNDERLINED)
}
#[must_use]
pub fn viewer_find_match_current_metadata_contrast() -> Style {
let t = CurrentTheme::palette();
let fg = match t.appearance {
crate::themes::Appearance::Dark => themes::DEFAULT_COLORS.white,
crate::themes::Appearance::Light => themes::DEFAULT_COLORS.black,
};
Style::default()
.fg(fg)
.add_modifier(Modifier::BOLD | Modifier::UNDERLINED)
}
#[must_use]
pub fn viewer_find_match_current() -> Style {
CurrentTheme::text_style().add_modifier(Modifier::BOLD | Modifier::REVERSED)
}