feat: 响应式分页引擎 - 基于egui Galley精确测量替代字符计数
This commit is contained in:
47
src/app.rs
47
src/app.rs
@@ -22,6 +22,7 @@ pub struct AppState {
|
||||
pub sidebar_open: bool,
|
||||
pub file_path: Option<PathBuf>,
|
||||
pub error_message: Option<String>,
|
||||
pub pending_anchor: Option<theme::ReadingPosition>,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
@@ -32,7 +33,7 @@ impl AppState {
|
||||
} else if self.current_section > 0 {
|
||||
self.current_section -= 1;
|
||||
self.current_page = book.sections[self.current_section]
|
||||
.pages.len().saturating_sub(2);
|
||||
.page_block_ranges.len().saturating_sub(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,7 +41,7 @@ impl AppState {
|
||||
pub fn next_page(&mut self) {
|
||||
if let Some(ref book) = self.book {
|
||||
if self.current_page + 1 < book.sections[self.current_section]
|
||||
.pages.len().saturating_sub(1)
|
||||
.page_block_ranges.len()
|
||||
{
|
||||
self.current_page += 1;
|
||||
} else if self.current_section + 1 < book.sections.len() {
|
||||
@@ -64,6 +65,7 @@ impl App {
|
||||
sidebar_open: false,
|
||||
file_path: None,
|
||||
error_message: None,
|
||||
pending_anchor: None,
|
||||
},
|
||||
settings,
|
||||
settings_dir,
|
||||
@@ -78,7 +80,7 @@ impl App {
|
||||
match crate::book::load_epub(&path) {
|
||||
Ok(book) => {
|
||||
let path_str = path.to_string_lossy().to_string();
|
||||
let pos = self.settings.reading_positions.get(&path_str).copied();
|
||||
let pos = self.settings.reading_positions.get(&path_str).cloned();
|
||||
let mut recent = Vec::new();
|
||||
recent.push(path_str.clone());
|
||||
for f in &self.settings.recent_files {
|
||||
@@ -91,8 +93,9 @@ impl App {
|
||||
}
|
||||
self.settings.recent_files = recent;
|
||||
self.state.book = Some(book);
|
||||
self.state.current_section = pos.map(|p| p.section).unwrap_or(0);
|
||||
self.state.current_page = pos.map(|p| p.page).unwrap_or(0);
|
||||
self.state.current_section = pos.as_ref().map(|p| p.section).unwrap_or(0);
|
||||
self.state.current_page = pos.as_ref().map(|p| p.page).unwrap_or(0);
|
||||
self.state.pending_anchor = pos;
|
||||
self.state.sidebar_open = false;
|
||||
self.state.file_path = Some(path);
|
||||
self.state.error_message = None;
|
||||
@@ -117,11 +120,27 @@ impl App {
|
||||
fn save_reading_position(&mut self) {
|
||||
if let Some(ref path) = self.state.file_path {
|
||||
let path_str = path.to_string_lossy().to_string();
|
||||
let (block_index, text_snippet) = if let Some(ref book) = self.state.book {
|
||||
let section = &book.sections[self.state.current_section];
|
||||
if let Some(&(start, _end)) = section.page_block_ranges.get(self.state.current_page) {
|
||||
let first_block = start;
|
||||
let snippet = section.blocks.get(first_block)
|
||||
.map(|b| b.text.chars().take(30).collect())
|
||||
.unwrap_or_default();
|
||||
(Some(first_block), Some(snippet))
|
||||
} else {
|
||||
(None, None)
|
||||
}
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
self.settings.reading_positions.insert(
|
||||
path_str,
|
||||
theme::ReadingPosition {
|
||||
section: self.state.current_section,
|
||||
page: self.state.current_page,
|
||||
block_index,
|
||||
text_snippet,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -395,6 +414,24 @@ BgType::Custom(ref path) if !path.is_empty() => {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(anchor) = self.state.pending_anchor.take() {
|
||||
if let Some(ref book) = self.state.book {
|
||||
if anchor.section < book.sections.len() {
|
||||
let section = &book.sections[anchor.section];
|
||||
let restored = if let Some(block_idx) = anchor.block_index {
|
||||
crate::reader::find_page_for_block(section, block_idx)
|
||||
} else if let Some(ref snippet) = anchor.text_snippet {
|
||||
crate::reader::find_page_by_snippet(section, snippet)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(page) = restored {
|
||||
self.state.current_page = 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)
|
||||
|
||||
Reference in New Issue
Block a user