use crossterm::event::{Event, KeyCode, KeyEventKind};
use ratatui::{
layout::{Constraint, Margin},
style::Style,
text::Line,
};
use ratatui_kit::prelude::*;
use crate::{
components::AdjustableSettingItem,
keymap::{ReaderAction, display_first_key},
pages::read_novel::visible_lines,
theme::AppChromeTheme,
};
const ITEM_COUNT: usize = 3;
const PANEL_HEIGHT: u16 = ITEM_COUNT as u16 * 3 + 4;
#[derive(Props, Default)]
pub struct ReaderSettingsModalProps {
pub open: bool,
pub is_editing: bool,
}
#[component]
pub fn ReaderSettingsModal(
props: &ReaderSettingsModalProps,
mut hooks: Hooks,
) -> impl Into<AnyElement<'static>> {
let theme = hooks.use_component_theme::<AppChromeTheme>();
let reader_keymap = hooks.use_atom(&crate::state::KEYMAP).read().reader.clone();
let reader_display = hooks.use_atom(&crate::state::READER_DISPLAY);
let (_, height) = hooks.use_terminal_size();
let mut index = hooks.use_state(|| 0usize);
let is_open = props.open;
let is_editing = props.is_editing;
hooks.use_event_handler(EventScope::Current, EventPriority::Normal, move |event| {
let Event::Key(key) = event else {
return EventResult::Ignored;
};
if key.kind != KeyEventKind::Press {
return EventResult::Ignored;
}
if !is_open || !is_editing {
return EventResult::Ignored;
}
match key.code {
KeyCode::Char('j') | KeyCode::Down => {
index.set((index.get() + 1).min(ITEM_COUNT - 1));
EventResult::Consumed
}
KeyCode::Char('k') | KeyCode::Up => {
index.set(index.get().saturating_sub(1));
EventResult::Consumed
}
_ => EventResult::Ignored,
}
});
if !is_open {
return element!(View).into_any();
}
let hint = Line::from(format!(
"↑/↓ 选择 · ←/→ 调整 · {} 关闭",
display_first_key(&reader_keymap, ReaderAction::ToggleReaderSettings)
))
.centered()
.style(theme.muted);
let config = *reader_display.read();
let overlap_value = format!(
"{} 行(每页滚动 {} 行)",
config.page_overlap,
config.page_step(visible_lines(height))
);
let title_value = if config.show_title { "开" } else { "关" };
let spacing_value = if config.paragraph_spacing {
"开"
} else {
"关"
};
element!(Modal(
width:Constraint::Percentage(60),
height: Constraint::Length(PANEL_HEIGHT),
open: is_open,
blocks_lower: false,
margin: Margin::new(1, 1),
style: Style::default().dim(),
) {
View(margin: Margin::new(1, 1)) {
Border(
border_style: theme.border.not_dim(),
top_title: Line::from("阅读设置").centered().style(theme.title),
bottom_title: hint,
) {
View(height: Constraint::Length(3)) {
AdjustableSettingItem(
is_editing: index.get() == 0 && is_editing,
label: "翻页重叠:".to_string(),
value: overlap_value,
on_decrease: move |_| reader_display.write().decrease_page_overlap(),
on_increase: move |_| reader_display.write().increase_page_overlap(),
)
}
View(height: Constraint::Length(3)) {
AdjustableSettingItem(
is_editing: index.get() == 1 && is_editing,
label: "显示标题:".to_string(),
value: title_value.to_string(),
on_decrease: move |_| reader_display.write().show_title = false,
on_increase: move |_| reader_display.write().show_title = true,
)
}
View(height: Constraint::Length(3)) {
AdjustableSettingItem(
is_editing: index.get() == 2 && is_editing,
label: "段落间距:".to_string(),
value: spacing_value.to_string(),
on_decrease: move |_| reader_display.write().paragraph_spacing = false,
on_increase: move |_| reader_display.write().paragraph_spacing = true,
)
}
}
}
})
.into_any()
}