use ratatui::style::{Color, Modifier, Style};
#[derive(Debug, Clone)]
pub struct HotkeyDialogStyle {
pub title: String,
pub border_focused: Color,
pub border_unfocused: Color,
pub title_color: Color,
pub global_key_color: Color,
pub local_key_color: Color,
pub selected_bg: Color,
pub selected_fg: Color,
pub locked_color: Color,
pub cursor_color: Color,
pub placeholder_color: Color,
pub text_color: Color,
pub dim_color: Color,
pub width_percent: u16,
pub height_percent: u16,
pub max_width: u16,
pub max_height: u16,
pub min_width: u16,
pub min_height: u16,
pub category_width_percent: u16,
pub search_height: u16,
pub footer_height: u16,
pub global_indicator: String,
pub locked_indicator: String,
pub search_placeholder: String,
}
impl Default for HotkeyDialogStyle {
fn default() -> Self {
Self {
title: " Hotkey Configuration ".to_string(),
border_focused: Color::Yellow,
border_unfocused: Color::DarkGray,
title_color: Color::Yellow,
global_key_color: Color::Yellow,
local_key_color: Color::Cyan,
selected_bg: Color::Yellow,
selected_fg: Color::Black,
locked_color: Color::Red,
cursor_color: Color::Yellow,
placeholder_color: Color::DarkGray,
text_color: Color::White,
dim_color: Color::DarkGray,
width_percent: 85,
height_percent: 85,
max_width: 110,
max_height: 45,
min_width: 70,
min_height: 25,
category_width_percent: 28,
search_height: 3,
footer_height: 2,
global_indicator: "[G]".to_string(),
locked_indicator: "L".to_string(),
search_placeholder: "Type to filter hotkeys...".to_string(),
}
}
}
impl From<&crate::theme::Theme> for HotkeyDialogStyle {
fn from(theme: &crate::theme::Theme) -> Self {
let p = &theme.palette;
Self {
title: " Hotkey Configuration ".to_string(),
border_focused: p.border_focused,
border_unfocused: p.border_disabled,
title_color: p.primary,
global_key_color: p.primary,
local_key_color: p.secondary,
selected_bg: p.highlight_bg,
selected_fg: p.highlight_fg,
locked_color: p.error,
cursor_color: p.primary,
placeholder_color: p.text_placeholder,
text_color: p.text,
dim_color: p.text_disabled,
width_percent: 85,
height_percent: 85,
max_width: 110,
max_height: 45,
min_width: 70,
min_height: 25,
category_width_percent: 28,
search_height: 3,
footer_height: 2,
global_indicator: "[G]".to_string(),
locked_indicator: "L".to_string(),
search_placeholder: "Type to filter hotkeys...".to_string(),
}
}
}
impl HotkeyDialogStyle {
pub fn new() -> Self {
Self::default()
}
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = title.into();
self
}
pub fn border_focused(mut self, color: Color) -> Self {
self.border_focused = color;
self
}
pub fn border_unfocused(mut self, color: Color) -> Self {
self.border_unfocused = color;
self
}
pub fn size(
mut self,
width_percent: u16,
height_percent: u16,
max_width: u16,
max_height: u16,
) -> Self {
self.width_percent = width_percent;
self.height_percent = height_percent;
self.max_width = max_width;
self.max_height = max_height;
self
}
pub fn min_size(mut self, min_width: u16, min_height: u16) -> Self {
self.min_width = min_width;
self.min_height = min_height;
self
}
pub fn search_placeholder(mut self, text: impl Into<String>) -> Self {
self.search_placeholder = text.into();
self
}
pub fn focused_border_style(&self) -> Style {
Style::default().fg(self.border_focused)
}
pub fn unfocused_border_style(&self) -> Style {
Style::default().fg(self.border_unfocused)
}
pub fn title_style(&self) -> Style {
Style::default()
.fg(self.title_color)
.add_modifier(Modifier::BOLD)
}
pub fn selected_style(&self) -> Style {
Style::default()
.fg(self.selected_fg)
.bg(self.selected_bg)
.add_modifier(Modifier::BOLD)
}
pub fn selected_text_style(&self) -> Style {
Style::default().fg(self.selected_fg).bg(self.selected_bg)
}
pub fn global_key_style(&self) -> Style {
Style::default()
.fg(self.global_key_color)
.add_modifier(Modifier::BOLD)
}
pub fn local_key_style(&self) -> Style {
Style::default()
.fg(self.local_key_color)
.add_modifier(Modifier::BOLD)
}
pub fn locked_style(&self) -> Style {
Style::default().fg(self.locked_color)
}
pub fn text_style(&self) -> Style {
Style::default().fg(self.text_color)
}
pub fn dim_style(&self) -> Style {
Style::default().fg(self.dim_color)
}
pub fn placeholder_style(&self) -> Style {
Style::default().fg(self.placeholder_color)
}
pub fn cursor_style(&self) -> Style {
Style::default().fg(self.cursor_color)
}
pub fn calculate_modal_area(
&self,
screen_width: u16,
screen_height: u16,
) -> (u16, u16, u16, u16) {
let modal_width = (screen_width * self.width_percent / 100)
.min(self.max_width)
.max(self.min_width)
.min(screen_width.saturating_sub(4));
let modal_height = (screen_height * self.height_percent / 100)
.min(self.max_height)
.max(self.min_height)
.min(screen_height.saturating_sub(4));
let x = (screen_width.saturating_sub(modal_width)) / 2;
let y = (screen_height.saturating_sub(modal_height)) / 2;
(x, y, modal_width, modal_height)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_style() {
let style = HotkeyDialogStyle::default();
assert_eq!(style.width_percent, 85);
assert_eq!(style.height_percent, 85);
assert_eq!(style.border_focused, Color::Yellow);
}
#[test]
fn test_builder_pattern() {
let style = HotkeyDialogStyle::new()
.title("My Hotkeys")
.border_focused(Color::Cyan)
.size(80, 80, 100, 40);
assert_eq!(style.title, "My Hotkeys");
assert_eq!(style.border_focused, Color::Cyan);
assert_eq!(style.width_percent, 80);
}
#[test]
fn test_calculate_modal_area() {
let style = HotkeyDialogStyle::default();
let (x, _y, w, _h) = style.calculate_modal_area(120, 40);
assert!(w <= 110);
assert!(w >= 70);
assert_eq!(x, (120 - w) / 2);
}
}