细化书签功能:正文指示器、侧边栏书签列表、跳转功能
This commit is contained in:
48
src/app.rs
48
src/app.rs
@@ -275,9 +275,12 @@ BgType::Custom(ref path) if !path.is_empty() => {
|
||||
let theme_copy = self.settings.theme;
|
||||
let profile_names: Vec<String> = self.settings.profiles.iter().map(|p| p.name.clone()).collect();
|
||||
|
||||
let bookmarks = self.settings.bookmarks.get(&file_path).cloned().unwrap_or_default();
|
||||
let mut jump_to_bookmark: Option<usize> = None;
|
||||
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
let book = self.state.book.as_mut().unwrap();
|
||||
let action = crate::reader::reading_view(
|
||||
let (action, jump) = crate::reader::reading_view(
|
||||
ui,
|
||||
book,
|
||||
&mut self.state.current_section,
|
||||
@@ -288,7 +291,9 @@ BgType::Custom(ref path) if !path.is_empty() => {
|
||||
bg_type.clone(),
|
||||
&file_path,
|
||||
&profile_names,
|
||||
&bookmarks,
|
||||
);
|
||||
jump_to_bookmark = jump;
|
||||
|
||||
if action.go_back {
|
||||
self.save_reading_position();
|
||||
@@ -347,8 +352,49 @@ BgType::Custom(ref path) if !path.is_empty() => {
|
||||
if action.page_next {
|
||||
self.state.next_page();
|
||||
}
|
||||
|
||||
if action.toggle_bookmark {
|
||||
let file_path = file_path.clone();
|
||||
let section = self.state.current_section;
|
||||
let page = self.state.current_page;
|
||||
let book = self.state.book.as_ref().unwrap();
|
||||
let section_title = book.sections.get(section)
|
||||
.map(|s| s.title.clone())
|
||||
.unwrap_or_else(|| format!("第{}章", section + 1));
|
||||
|
||||
let bookmarks = self.settings.bookmarks.entry(file_path)
|
||||
.or_insert_with(Vec::new);
|
||||
|
||||
let existing_idx = bookmarks.iter()
|
||||
.position(|b: &theme::Bookmark| b.section == section && b.page == page);
|
||||
|
||||
if let Some(idx) = existing_idx {
|
||||
bookmarks.remove(idx);
|
||||
} else {
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
let timestamp = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map(|d| d.as_secs() as i64)
|
||||
.unwrap_or(0);
|
||||
bookmarks.push(theme::Bookmark {
|
||||
section,
|
||||
page,
|
||||
label: format!("{} - 第{}页", section_title, page + 1),
|
||||
timestamp,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(idx) = jump_to_bookmark {
|
||||
if let Some(bookmarks) = self.settings.bookmarks.get(&file_path) {
|
||||
if let Some(bm) = bookmarks.get(idx) {
|
||||
self.state.current_section = bm.section;
|
||||
self.state.current_page = bm.page;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sync style changes back to active profile (outside closure)
|
||||
if let Some(p) = self.settings.profiles.iter_mut()
|
||||
.find(|p| p.name == self.settings.active_profile)
|
||||
|
||||
@@ -42,7 +42,8 @@ pub fn reading_view(
|
||||
bg_type: BgType,
|
||||
_file_path: &str,
|
||||
profile_names: &[String],
|
||||
) -> ReaderAction {
|
||||
bookmarks: &[crate::theme::Bookmark],
|
||||
) -> (ReaderAction, Option<usize>) {
|
||||
let mut action = ReaderAction {
|
||||
go_back: false,
|
||||
toggle_theme: false,
|
||||
@@ -52,21 +53,35 @@ pub fn reading_view(
|
||||
page_next: false,
|
||||
page_prev: false,
|
||||
};
|
||||
let mut jump_to_bookmark: Option<usize> = None;
|
||||
|
||||
let panel_size = ui.available_size();
|
||||
recalculate_pages(book, style.font_size, style.line_height(), panel_size.x, panel_size.y);
|
||||
|
||||
let colors = theme::reader_colors(theme);
|
||||
let has_bookmark = bookmarks.iter().any(|b| b.section == *current_section && b.page == *current_page);
|
||||
|
||||
// --- Sidebar (TOC) ---
|
||||
// --- Sidebar (TOC + Bookmarks) ---
|
||||
let mut sidebar_tab: usize = 0;
|
||||
if *sidebar_open {
|
||||
egui::SidePanel::left("toc_sidebar")
|
||||
.resizable(true)
|
||||
.default_width(200.0)
|
||||
.default_width(240.0)
|
||||
.show_inside(ui, |ui| {
|
||||
ui.heading("目录");
|
||||
ui.horizontal(|ui| {
|
||||
let toc_response = ui.selectable_label(sidebar_tab == 0, "📋 目录");
|
||||
let bm_response = ui.selectable_label(sidebar_tab == 1,
|
||||
format!("🔖 书签 ({})", bookmarks.len())
|
||||
);
|
||||
if toc_response.clicked() { sidebar_tab = 0; }
|
||||
if bm_response.clicked() { sidebar_tab = 1; }
|
||||
});
|
||||
ui.separator();
|
||||
render_toc(ui, &book.toc, current_section, current_page);
|
||||
if sidebar_tab == 0 {
|
||||
render_toc(ui, &book.toc, current_section, current_page);
|
||||
} else {
|
||||
render_bookmarks(ui, bookmarks, &mut jump_to_bookmark);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -88,7 +103,9 @@ pub fn reading_view(
|
||||
if ui.button(theme_icon).on_hover_text(theme_hint).clicked() {
|
||||
action.toggle_theme = true;
|
||||
}
|
||||
if ui.button("🔖").on_hover_text("添加/移除书签").clicked() {
|
||||
let bookmark_icon = if has_bookmark { "🔴" } else { "🔖" };
|
||||
let bookmark_hint = if has_bookmark { "移除书签" } else { "添加书签" };
|
||||
if ui.button(bookmark_icon).on_hover_text(bookmark_hint).clicked() {
|
||||
action.toggle_bookmark = true;
|
||||
}
|
||||
if ui.button("A⁻").on_hover_text("缩小字体").clicked() {
|
||||
@@ -224,6 +241,18 @@ egui::ComboBox::from_id_salt("bg_type_selector")
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
if has_bookmark {
|
||||
let painter = ui.painter();
|
||||
let bookmark_pos = egui::pos2(rect.max.x - 30.0, rect.min.y + 10.0);
|
||||
painter.text(
|
||||
bookmark_pos,
|
||||
egui::Align2::RIGHT_TOP,
|
||||
"🔴",
|
||||
egui::FontId::proportional(18.0),
|
||||
egui::Color32::RED,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,7 +277,7 @@ egui::ComboBox::from_id_salt("bg_type_selector")
|
||||
}
|
||||
});
|
||||
|
||||
action
|
||||
(action, jump_to_bookmark)
|
||||
}
|
||||
|
||||
fn render_toc(
|
||||
@@ -282,6 +311,31 @@ fn render_toc(
|
||||
}
|
||||
}
|
||||
|
||||
fn render_bookmarks(
|
||||
ui: &mut egui::Ui,
|
||||
bookmarks: &[crate::theme::Bookmark],
|
||||
jump_to_bookmark: &mut Option<usize>,
|
||||
) {
|
||||
if bookmarks.is_empty() {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("暂无书签");
|
||||
});
|
||||
ui.label("点击工具栏 🔖 按钮添加书签");
|
||||
return;
|
||||
}
|
||||
|
||||
for (idx, bm) in bookmarks.iter().enumerate() {
|
||||
let label_text = egui::RichText::new(&bm.label).size(13.0);
|
||||
if ui.add(
|
||||
egui::Button::new(label_text)
|
||||
.frame(false)
|
||||
.wrap()
|
||||
).on_hover_text("点击跳转到该书签").clicked() {
|
||||
*jump_to_bookmark = Some(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calculate_pages(text: &str, chars_per_page: usize) -> Vec<usize> {
|
||||
let mut pages = Vec::new();
|
||||
pages.push(0);
|
||||
|
||||
Reference in New Issue
Block a user