1pub mod help;
2mod side_menu;
3mod top_three;
4pub mod util;
5use crate::app::*;
6use ratatui::{
7 layout::{Alignment, Constraint, Direction, Layout, Margin, Rect},
8 style::Style,
9 text::{Line, Span},
10 widgets::{Block, BorderType, Borders, Paragraph},
11 Frame,
12};
13use util::get_color;
14mod display_block;
15
16pub fn draw_main_layout(f: &mut Frame, app: &mut App) {
17 let margin = util::get_main_layout_margin(app);
18 let app_area;
19 if app.app_config.behavior.show_logger {
20 let logger_area;
21 [app_area, logger_area] = Layout::default()
22 .direction(ratatui::layout::Direction::Horizontal)
23 .constraints([Constraint::Percentage(60), Constraint::Percentage(40)])
24 .areas(f.area());
25 app.render_logs(f, logger_area);
26 } else {
27 app_area = f.area();
28 }
29
30 let parent_layout = Layout::default()
33 .direction(Direction::Vertical)
34 .constraints([Constraint::Length(3), Constraint::Min(1)].as_ref())
35 .margin(margin)
36 .split(app_area);
37
38 draw_input_and_help_box(f, app, parent_layout[0]);
40
41 let chunk = side_menu::draw_routes(f, app, parent_layout[1]);
43 display_block::draw_display_layout(f, app, chunk);
44}
45
46pub fn draw_input_and_help_box(f: &mut Frame, app: &App, layout_chunk: Rect) {
47 let [search_chunk, title_chunk] = Layout::default()
48 .direction(Direction::Horizontal)
49 .constraints([Constraint::Percentage(18), Constraint::Percentage(82)])
50 .areas(layout_chunk);
51 let search_chunk = search_chunk.inner(Margin::new(1, 0));
53 let current_block = app.active_block;
54
55 let highlight_state = current_block == ActiveBlock::Input;
56
57 let input_string: String = app.input.iter().collect();
58 let lines = Span::from(input_string);
59 let input = Paragraph::new(lines).block(
60 Block::default()
61 .borders(Borders::ALL)
62 .border_type(BorderType::Rounded)
63 .title(Span::styled(
64 "Search",
65 get_color(highlight_state, app.app_config.theme),
66 ))
67 .border_style(get_color(highlight_state, app.app_config.theme)),
68 );
69 f.render_widget(input, search_chunk);
70
71 let mut title = app.display_block_title.clone();
72 if title.is_empty() {
73 title = "Home".to_string(); }
75 let block = Block::default()
76 .borders(Borders::ALL)
77 .border_type(BorderType::Rounded)
78 .border_style(Style::default().fg(app.app_config.theme.inactive));
79
80 let lines = Line::from(Span::from(title))
81 .alignment(Alignment::Center)
82 .style(Style::default().fg(app.app_config.theme.banner));
83
84 let help = Paragraph::new(lines)
85 .block(block)
86 .alignment(Alignment::Center)
87 .style(Style::default().fg(app.app_config.theme.banner));
88 f.render_widget(help, title_chunk);
89}
90
91pub fn format_number_with_commas(number: u64) -> String {
92 let num_str = number.to_string();
93 let mut result = String::new();
94 let mut count = 0;
95
96 for c in num_str.chars().rev() {
97 if count == 3 {
98 result.push(',');
99 count = 0;
100 }
101 result.push(c);
102 count += 1;
103 }
104
105 result.chars().rev().collect()
106}
107
108fn get_end_index(app: &App, typ: &str) -> usize {
109 match typ {
110 "anime" => {
111 let data_len = app.search_results.anime.as_ref().unwrap().data.len();
112 if app.start_card_list_index as usize + DISPLAY_RAWS_NUMBER * DISPLAY_COLUMN_NUMBER
113 > data_len - 1
114 {
116 data_len - 1
117 } else {
118 app.start_card_list_index as usize + DISPLAY_COLUMN_NUMBER * DISPLAY_RAWS_NUMBER - 1
120 }
121 }
122 "manga" => {
123 let data_len = app.search_results.manga.as_ref().unwrap().data.len();
124 if app.start_card_list_index as usize + DISPLAY_RAWS_NUMBER * DISPLAY_COLUMN_NUMBER
125 > data_len - 1
126 {
128 data_len - 1
129 } else {
130 app.start_card_list_index as usize + DISPLAY_COLUMN_NUMBER * DISPLAY_RAWS_NUMBER - 1
132 }
133 }
134 "anime_ranking" => {
136 let data_len = app.anime_ranking_data.as_ref().unwrap().data.len();
137 if app.start_card_list_index as usize + DISPLAY_RAWS_NUMBER * DISPLAY_COLUMN_NUMBER
138 > data_len - 1
139 {
141 data_len - 1
142 } else {
143 app.start_card_list_index as usize + DISPLAY_COLUMN_NUMBER * DISPLAY_RAWS_NUMBER - 1
145 }
146 }
147 "manga_ranking" => {
148 let data_len = app.manga_ranking_data.as_ref().unwrap().data.len();
149 if app.start_card_list_index as usize + DISPLAY_RAWS_NUMBER * DISPLAY_COLUMN_NUMBER
150 > data_len - 1
151 {
153 data_len - 1
154 } else {
155 app.start_card_list_index as usize + DISPLAY_COLUMN_NUMBER * DISPLAY_RAWS_NUMBER - 1
157 }
158 }
159 _ => panic!("Unknown type: {}", typ),
160 }
161}
162
163pub fn get_end_card_index(app: &App) -> usize {
164 match app.active_display_block {
165 ActiveDisplayBlock::SearchResultBlock => match app.search_results.selected_tab {
166 SelectedSearchTab::Manga => get_end_index(app, "manga"),
167 SelectedSearchTab::Anime => get_end_index(app, "anime"),
168 },
169 ActiveDisplayBlock::AnimeRanking => get_end_index(app, "anime_ranking"),
170 ActiveDisplayBlock::MangaRanking => get_end_index(app, "manga_ranking"),
171 _ => {
172 get_end_index(app, "anime")
174 }
175 }
176}