use crate::{
ThemeConfig,
components::{ConfirmModal, KeyShortcutInfo, ShortcutInfoModal, list_select::ListSelect},
};
use crossterm::event::{Event, KeyCode, KeyEventKind};
use ratatui::{
style::Color,
text::Line,
widgets::{Block, Padding, Widget, WidgetRef},
};
use ratatui_kit::prelude::*;
use tui_widget_list::ListBuildContext;
mod select_color;
use select_color::SelectColor;
#[derive(Debug, Clone)]
pub struct ListItem {
pub name: String,
pub color: Color,
pub selected: bool,
pub theme: ThemeConfig,
}
impl WidgetRef for ListItem {
fn render_ref(&self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer) {
let block = if self.selected {
Block::bordered()
.title(Line::from(self.name.clone()).style(self.theme.basic.border_title))
.padding(Padding::horizontal(0))
.style(self.theme.selected)
} else {
Block::bordered()
.title(Line::from(self.name.clone()).style(self.theme.basic.border_title))
.padding(Padding::horizontal(0))
};
let inner_area = block.inner(area);
block.render(area, buf);
let color = Line::from(format!("■ {}", self.color))
.centered()
.style(self.color);
color.render(inner_area, buf);
}
}
#[derive(Debug, Clone, PartialEq)]
enum ColorItem {
Text,
Primary,
Warning,
Error,
Success,
Info,
}
impl ColorItem {
fn name(&self) -> &'static str {
match self {
ColorItem::Text => "Text Color",
ColorItem::Primary => "Primary Color",
ColorItem::Warning => "Warning Color",
ColorItem::Error => "Error Color",
ColorItem::Success => "Success Color",
ColorItem::Info => "Info Color",
}
}
fn get_color(&self, theme: &ThemeConfig) -> Color {
match self {
ColorItem::Text => theme.colors.text_color,
ColorItem::Primary => theme.colors.primary_color,
ColorItem::Warning => theme.colors.warning_color,
ColorItem::Error => theme.colors.error_color,
ColorItem::Success => theme.colors.success_color,
ColorItem::Info => theme.colors.info_color,
}
}
fn set_color(&self, theme: &mut ThemeConfig, color: Color) {
match self {
ColorItem::Text => theme.colors.text_color = color,
ColorItem::Primary => theme.colors.primary_color = color,
ColorItem::Warning => theme.colors.warning_color = color,
ColorItem::Error => theme.colors.error_color = color,
ColorItem::Success => theme.colors.success_color = color,
ColorItem::Info => theme.colors.info_color = color,
}
}
fn all() -> Vec<ColorItem> {
vec![
ColorItem::Text,
ColorItem::Primary,
ColorItem::Warning,
ColorItem::Error,
ColorItem::Success,
ColorItem::Info,
]
}
}
#[component]
pub fn ThemeSetting(mut hooks: Hooks) -> impl Into<AnyElement<'static>> {
let mut info_modal_open = hooks.use_state(|| false);
let theme_config = *hooks.use_context::<State<ThemeConfig>>();
let theme = theme_config.read().clone();
let mut current = hooks.use_state(|| None::<ColorItem>);
let is_inputting = *hooks.use_context::<State<bool>>();
let mut reset_modal_open = hooks.use_state(|| false);
hooks.use_events(move |event| {
if let Event::Key(key) = event
&& key.kind == KeyEventKind::Press
&& !is_inputting.get()
{
match key.code {
KeyCode::Char('i') | KeyCode::Char('I') if current.read().is_none() => {
info_modal_open.set(!info_modal_open.get());
}
KeyCode::Char('d') | KeyCode::Char('D') if current.read().is_none() => {
reset_modal_open.set(!reset_modal_open.get());
}
KeyCode::Esc if current.read().is_some() => {
current.set(None);
}
_ => {}
}
}
});
let color_items: Vec<ColorItem> = ColorItem::all();
element!(Fragment{
ListSelect<ColorItem>(
items: ColorItem::all(),
default_value: 0,
top_title: Line::from("主题设置").style(theme.basic.border_title).centered(),
render_item:move|ctx:&ListBuildContext|{
let color_item=color_items[ctx.index].clone();
let name = color_item.name().to_string();
let color = color_item.get_color(&theme);
(ListItem {
name,
color,
selected: ctx.is_selected,
theme: theme.clone(),
}.into(),3)
},
is_editing: current.read().is_none() && !info_modal_open.get() && !reset_modal_open.get(),
on_select: move |item:ColorItem| {
current.set(Some(item));
},
)
#(if let Some(selected_item) = current.read().clone() {
element!(SelectColor(
color: selected_item.get_color(&theme_config.read().clone()),
is_editing: true,
on_change: move |color| {
let mut theme = theme_config.read().clone();
selected_item.set_color(&mut theme, color);
let new_theme = ThemeConfig::from_colors(theme.colors);
let _ = new_theme.save();
*theme_config.write() = new_theme;
current.set(None);
}
)).into_any()
}else{
element!(ShortcutInfoModal(
key_shortcut_info: KeyShortcutInfo::new(vec![
("选择下一个", "J / ▼"),
("选择上一个", "K / ▲"),
("确认选择", "Enter"),
("重置主题", "D"),
]),
open: info_modal_open.get(),
)).into_any()
})
ConfirmModal(
title: "重置主题",
content: "是否确定要重置主题?",
open: reset_modal_open.get(),
on_confirm: move |_| {
let new_theme = ThemeConfig::default();
let _ = new_theme.save();
*theme_config.write() = new_theme;
reset_modal_open.set(false);
},
on_cancel: move |_| {
reset_modal_open.set(false);
}
)
})
}