Feat: entretien de la codebase
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use ratatui::layout::{Constraint, Layout, Rect};
|
||||
use ratatui::style::{Modifier, Style};
|
||||
use ratatui::text::{Line as RLine, Span};
|
||||
use ratatui::widgets::{Block, Borders, List, ListItem, Paragraph};
|
||||
use ratatui::widgets::{Block, Borders, Paragraph};
|
||||
use ratatui::Frame;
|
||||
|
||||
use crate::app::App;
|
||||
@@ -9,6 +9,7 @@ use crate::model::categories::{get_category_name, CatEntry, CATEGORIES};
|
||||
use crate::model::{Word, WORDS};
|
||||
use crate::state::DictFocus;
|
||||
use crate::theme;
|
||||
use crate::widgets::{render_search_bar, CategoryItem, CategoryList};
|
||||
|
||||
use CatEntry::{Category, Section};
|
||||
|
||||
@@ -47,84 +48,35 @@ fn render_categories(frame: &mut Frame, app: &App, area: Rect, dimmed: bool) {
|
||||
let theme = theme::get();
|
||||
let focused = app.ui.dict_focus == DictFocus::Categories && !dimmed;
|
||||
|
||||
let visible_height = area.height.saturating_sub(2) as usize;
|
||||
let total_items = CATEGORIES.len();
|
||||
|
||||
// Find the visual index of the selected category (including sections)
|
||||
let selected_visual_idx = {
|
||||
let mut visual = 0;
|
||||
let mut cat_count = 0;
|
||||
for entry in CATEGORIES.iter() {
|
||||
if let Category(_) = entry {
|
||||
if cat_count == app.ui.dict_category {
|
||||
break;
|
||||
}
|
||||
cat_count += 1;
|
||||
}
|
||||
visual += 1;
|
||||
}
|
||||
visual
|
||||
};
|
||||
|
||||
// Calculate scroll to keep selection visible (centered when possible)
|
||||
let scroll = if selected_visual_idx < visible_height / 2 {
|
||||
0
|
||||
} else if selected_visual_idx > total_items.saturating_sub(visible_height / 2) {
|
||||
total_items.saturating_sub(visible_height)
|
||||
} else {
|
||||
selected_visual_idx.saturating_sub(visible_height / 2)
|
||||
};
|
||||
|
||||
// Count categories before the scroll offset to track cat_idx correctly
|
||||
let mut cat_idx = CATEGORIES
|
||||
let items: Vec<CategoryItem> = CATEGORIES
|
||||
.iter()
|
||||
.take(scroll)
|
||||
.filter(|e| matches!(e, Category(_)))
|
||||
.count();
|
||||
|
||||
let items: Vec<ListItem> = CATEGORIES
|
||||
.iter()
|
||||
.skip(scroll)
|
||||
.take(visible_height)
|
||||
.map(|entry| match entry {
|
||||
Section(name) => {
|
||||
let style = Style::new().fg(theme.ui.text_dim);
|
||||
ListItem::new(format!("─ {name} ─")).style(style)
|
||||
}
|
||||
Category(name) => {
|
||||
let is_selected = cat_idx == app.ui.dict_category;
|
||||
let style = if dimmed {
|
||||
Style::new().fg(theme.dict.category_dimmed)
|
||||
} else if is_selected && focused {
|
||||
Style::new()
|
||||
.fg(theme.dict.category_focused)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
} else if is_selected {
|
||||
Style::new().fg(theme.dict.category_selected)
|
||||
} else {
|
||||
Style::new().fg(theme.dict.category_normal)
|
||||
};
|
||||
let prefix = if is_selected && !dimmed { "> " } else { " " };
|
||||
cat_idx += 1;
|
||||
ListItem::new(format!("{prefix}{name}")).style(style)
|
||||
}
|
||||
Section(name) => CategoryItem {
|
||||
label: name,
|
||||
is_section: true,
|
||||
},
|
||||
Category(name) => CategoryItem {
|
||||
label: name,
|
||||
is_section: false,
|
||||
},
|
||||
})
|
||||
.collect();
|
||||
|
||||
let border_color = if focused { theme.dict.border_focused } else { theme.dict.border_normal };
|
||||
let block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_style(Style::new().fg(border_color))
|
||||
let mut list = CategoryList::new(&items, app.ui.dict_category)
|
||||
.focused(focused)
|
||||
.title("Categories");
|
||||
let list = List::new(items).block(block);
|
||||
frame.render_widget(list, area);
|
||||
|
||||
if dimmed {
|
||||
list = list.dimmed(theme.dict.category_dimmed);
|
||||
}
|
||||
|
||||
list.render(frame, area);
|
||||
}
|
||||
|
||||
fn render_words(frame: &mut Frame, app: &App, area: Rect, is_searching: bool) {
|
||||
let theme = theme::get();
|
||||
let focused = app.ui.dict_focus == DictFocus::Words;
|
||||
|
||||
// Filter words by search query or category
|
||||
let words: Vec<&Word> = if is_searching {
|
||||
let query = app.ui.dict_search_query.to_lowercase();
|
||||
WORDS
|
||||
@@ -142,7 +94,6 @@ fn render_words(frame: &mut Frame, app: &App, area: Rect, is_searching: bool) {
|
||||
.collect()
|
||||
};
|
||||
|
||||
// Split area for search bar when search is active or has query
|
||||
let show_search = app.ui.dict_search_active || is_searching;
|
||||
let (search_area, content_area) = if show_search {
|
||||
let [s, c] =
|
||||
@@ -152,9 +103,8 @@ fn render_words(frame: &mut Frame, app: &App, area: Rect, is_searching: bool) {
|
||||
(None, area)
|
||||
};
|
||||
|
||||
// Render search bar
|
||||
if let Some(sa) = search_area {
|
||||
render_search_bar(frame, app, sa);
|
||||
render_search_bar(frame, sa, &app.ui.dict_search_query, app.ui.dict_search_active);
|
||||
}
|
||||
|
||||
let content_width = content_area.width.saturating_sub(2) as usize;
|
||||
@@ -229,17 +179,3 @@ fn render_words(frame: &mut Frame, app: &App, area: Rect, is_searching: bool) {
|
||||
.block(block);
|
||||
frame.render_widget(para, content_area);
|
||||
}
|
||||
|
||||
fn render_search_bar(frame: &mut Frame, app: &App, area: Rect) {
|
||||
let theme = theme::get();
|
||||
let style = if app.ui.dict_search_active {
|
||||
Style::new().fg(theme.search.active)
|
||||
} else {
|
||||
Style::new().fg(theme.search.inactive)
|
||||
};
|
||||
let cursor = if app.ui.dict_search_active { "_" } else { "" };
|
||||
let text = format!(" /{}{}", app.ui.dict_search_query, cursor);
|
||||
let line = RLine::from(Span::styled(text, style));
|
||||
frame.render_widget(Paragraph::new(vec![line]), area);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user