diff --git a/.gitignore b/.gitignore index 5bbe257..e4c2232 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ /.fat32-epoch /.thumbnail-previews /.thumbnail-cache +/.cover-cache /.trash /css/*-user.css /hyphenation-patterns diff --git a/crates/core/src/library.rs b/crates/core/src/library.rs index db93da6..489e05c 100644 --- a/crates/core/src/library.rs +++ b/crates/core/src/library.rs @@ -19,6 +19,7 @@ pub const METADATA_FILENAME: &str = ".metadata.json"; pub const FAT32_EPOCH_FILENAME: &str = ".fat32-epoch"; pub const READING_STATES_DIRNAME: &str = ".reading-states"; pub const THUMBNAIL_PREVIEWS_DIRNAME: &str = ".thumbnail-previews"; +pub const COVER_CACHE_DIRNAME: &str = ".cover-cache"; pub struct Library { pub home: PathBuf, @@ -92,6 +93,11 @@ impl Library { fs::create_dir(&path).ok(); } + let path = home.as_ref().join(COVER_CACHE_DIRNAME); + if !path.exists() { + fs::create_dir(&path).ok(); + } + let paths = if mode == LibraryMode::Database { db.iter().map(|(fp, info)| (info.file.path.clone(), *fp)).collect() } else { @@ -261,10 +267,14 @@ impl Library { let rp1 = self.reading_state_path(fp2); let rp2 = self.reading_state_path(fp); fs::rename(rp1, rp2).ok(); - let tpp = self.thumbnail_preview_path(fp2); + let tpp = self.thumbnail_preview_path(fp2, false); if tpp.exists() { fs::remove_file(tpp).ok(); } + let tpp_large = self.thumbnail_preview_path(fp2, true); + if tpp_large.exists() { + fs::remove_file(tpp_large).ok(); + } self.has_db_changed = true; } else { let fp1 = self.fat32_epoch.checked_sub(Duration::from_secs(1)) @@ -291,9 +301,12 @@ impl Library { let rp1 = self.reading_state_path(nfp); let rp2 = self.reading_state_path(fp); fs::rename(rp1, rp2).ok(); - let tp1 = self.thumbnail_preview_path(nfp); - let tp2 = self.thumbnail_preview_path(fp); + let tp1 = self.thumbnail_preview_path(nfp, false); + let tp2 = self.thumbnail_preview_path(fp, false); fs::rename(tp1, tp2).ok(); + let tp1_large = self.thumbnail_preview_path(nfp, true); + let tp2_large = self.thumbnail_preview_path(fp, true); + fs::rename(tp1_large, tp2_large).ok(); if relat != self.db[&fp].file.path { println!("Update path for {}: {} → {}.", fp, self.db[&fp].file.path.display(), relat.display()); @@ -350,14 +363,17 @@ impl Library { let reading_states_dir = home.join(READING_STATES_DIRNAME); let thumbnail_previews_dir = home.join(THUMBNAIL_PREVIEWS_DIRNAME); + let cover_cache_dir = home.join(COVER_CACHE_DIRNAME); for entry in fs::read_dir(&reading_states_dir).unwrap() - .chain(fs::read_dir(&thumbnail_previews_dir).unwrap()) { + .chain(fs::read_dir(&thumbnail_previews_dir).unwrap()) + .chain(fs::read_dir(&cover_cache_dir).unwrap()) { if entry.is_err() { continue; } let entry = entry.unwrap(); if let Some(fp) = entry.path().file_stem() .and_then(|v| v.to_str()) + .map(|v| v.trim_end_matches("-large")) .and_then(|v| Fp::from_str(v).ok()) { if !self.db.contains_key(&fp) { fs::remove_file(entry.path()).ok(); @@ -434,10 +450,14 @@ impl Library { fs::remove_file(rsp)?; } - let tpp = self.thumbnail_preview_path(fp); + let tpp = self.thumbnail_preview_path(fp, false); if tpp.exists() { fs::remove_file(tpp)?; } + let tpp_large = self.thumbnail_preview_path(fp, true); + if tpp_large.exists() { + fs::remove_file(tpp_large)?; + } if self.mode == LibraryMode::Database { self.paths.remove(path.as_ref()); @@ -490,11 +510,16 @@ impl Library { fs::copy(&rsp_src, &rsp_dest)?; } - let tpp_src = self.thumbnail_preview_path(fp); + let tpp_src = self.thumbnail_preview_path(fp, false); if tpp_src.exists() { - let tpp_dest = other.thumbnail_preview_path(fp); + let tpp_dest = other.thumbnail_preview_path(fp, false); fs::copy(&tpp_src, &tpp_dest)?; } + let tpp_src_large = self.thumbnail_preview_path(fp, true); + if tpp_src_large.exists() { + let tpp_dest_large = other.thumbnail_preview_path(fp, true); + fs::copy(&tpp_src_large, &tpp_dest_large)?; + } if other.mode == LibraryMode::Database { let info = self.db.get(&fp).cloned() @@ -565,11 +590,16 @@ impl Library { fs::rename(&rsp_src, &rsp_dest)?; } - let tpp_src = self.thumbnail_preview_path(fp); + let tpp_src = self.thumbnail_preview_path(fp, false); if tpp_src.exists() { - let tpp_dest = other.thumbnail_preview_path(fp); + let tpp_dest = other.thumbnail_preview_path(fp, false); fs::rename(&tpp_src, &tpp_dest)?; } + let tpp_src_large = self.thumbnail_preview_path(fp, true); + if tpp_src_large.exists() { + let tpp_dest_large = other.thumbnail_preview_path(fp, true); + fs::rename(&tpp_src_large, &tpp_dest_large)?; + } if other.mode == LibraryMode::Database { let info = self.db.shift_remove(&fp) @@ -640,14 +670,17 @@ impl Library { let reading_states_dir = self.home.join(READING_STATES_DIRNAME); let thumbnail_previews_dir = self.home.join(THUMBNAIL_PREVIEWS_DIRNAME); + let cover_cache_dir = self.home.join(COVER_CACHE_DIRNAME); for entry in fs::read_dir(&reading_states_dir).unwrap() - .chain(fs::read_dir(&thumbnail_previews_dir).unwrap()) { + .chain(fs::read_dir(&thumbnail_previews_dir).unwrap()) + .chain(fs::read_dir(&cover_cache_dir).unwrap()) { if entry.is_err() { continue; } let entry = entry.unwrap(); if let Some(fp) = entry.path().file_stem() .and_then(|v| v.to_str()) + .map(|v| v.trim_end_matches("-large")) .and_then(|v| Fp::from_str(v).ok()) { if !fps.contains(&fp) { fs::remove_file(entry.path()).ok(); @@ -713,7 +746,33 @@ impl Library { .metadata().unwrap() .fingerprint(self.fat32_epoch).unwrap() }); - self.thumbnail_preview_path(fp) + self.thumbnail_preview_path(fp, false) + } + } + + pub fn thumbnail_preview_large>(&self, path: P) -> PathBuf { + if path.as_ref().starts_with(THUMBNAIL_PREVIEWS_DIRNAME) { + self.home.join(path.as_ref()) + } else { + let fp = self.paths.get(path.as_ref()).cloned().unwrap_or_else(|| { + self.home.join(path.as_ref()) + .metadata().unwrap() + .fingerprint(self.fat32_epoch).unwrap() + }); + self.thumbnail_preview_path(fp, true) + } + } + + pub fn cover_preview>(&self, path: P) -> PathBuf { + if path.as_ref().starts_with(COVER_CACHE_DIRNAME) { + self.home.join(path.as_ref()) + } else { + let fp = self.paths.get(path.as_ref()).cloned().unwrap_or_else(|| { + self.home.join(path.as_ref()) + .metadata().unwrap() + .fingerprint(self.fat32_epoch).unwrap() + }); + self.cover_preview_path(fp) } } @@ -841,9 +900,20 @@ impl Library { .join(format!("{}.json", fp)) } - fn thumbnail_preview_path(&self, fp: Fp) -> PathBuf { + fn thumbnail_preview_path(&self, fp: Fp, large: bool) -> PathBuf { + let filename = if large { + format!("{}-large.png", fp) + } else { + format!("{}.png", fp) + }; self.home .join(THUMBNAIL_PREVIEWS_DIRNAME) + .join(filename) + } + + fn cover_preview_path(&self, fp: Fp) -> PathBuf { + self.home + .join(COVER_CACHE_DIRNAME) .join(format!("{}.png", fp)) } } diff --git a/crates/core/src/settings/mod.rs b/crates/core/src/settings/mod.rs index 9ce5053..2ff92aa 100644 --- a/crates/core/src/settings/mod.rs +++ b/crates/core/src/settings/mod.rs @@ -144,6 +144,8 @@ pub struct LibrarySettings { pub first_column: FirstColumn, pub second_column: SecondColumn, pub thumbnail_previews: bool, + pub large_thumbnails: bool, + pub cover_view: bool, #[serde(skip_serializing_if = "Vec::is_empty")] pub hooks: Vec, } @@ -159,6 +161,8 @@ impl Default for LibrarySettings { first_column: FirstColumn::TitleAndAuthor, second_column: SecondColumn::Progress, thumbnail_previews: true, + large_thumbnails: false, + cover_view: false, hooks: Vec::new(), } } diff --git a/crates/core/src/view/home/book.rs b/crates/core/src/view/home/book.rs index 2cd1d12..8bb3854 100644 --- a/crates/core/src/view/home/book.rs +++ b/crates/core/src/view/home/book.rs @@ -25,13 +25,14 @@ pub struct Book { index: usize, first_column: FirstColumn, second_column: SecondColumn, + cover_view: bool, preview_path: Option, active: bool, } impl Book { pub fn new(rect: Rectangle, info: Info, index: usize, - first_column: FirstColumn, second_column: SecondColumn, preview_path: Option) -> Book { + first_column: FirstColumn, second_column: SecondColumn, cover_view: bool, preview_path: Option) -> Book { Book { id: ID_FEEDER.next(), rect, @@ -40,6 +41,7 @@ impl Book { index, first_column, second_column, + cover_view, preview_path, active: false, } @@ -93,6 +95,88 @@ impl View for Book { fb.draw_rectangle(&self.rect, scheme[0]); + if self.cover_view { + let indicator_height = scale_by_dpi(60.0, dpi) as i32; + let max_cover_height = (self.rect.height() as i32 - indicator_height).max(indicator_height); + let max_cover_width = self.rect.width() as i32; + + let mut cover_height = max_cover_height; + let mut cover_width = 3 * cover_height / 4; + + if cover_width > max_cover_width { + cover_width = max_cover_width; + cover_height = 4 * cover_width / 3; + } + + let content_height = cover_height + indicator_height; + let y_offset = ((self.rect.height() as i32 - content_height) / 2).max(0); + + // Draw cover image + if let Some(preview_path) = self.preview_path.as_ref() { + if preview_path.exists() { + if let Some((pixmap, _)) = PdfOpener::new().and_then(|opener| { + opener.open(preview_path) + }).and_then(|mut doc| { + doc.dims(0).and_then(|dims| { + let scale = (cover_width as f32 / dims.0).min(cover_height as f32 / dims.1); + doc.pixmap(Location::Exact(0), scale, CURRENT_DEVICE.color_samples()) + }) + }) { + let dx = ((self.rect.width() as i32 - pixmap.width as i32) / 2).max(0); + let dy = (cover_height - pixmap.height as i32) / 2; + let pt = pt!(self.rect.min.x + dx, self.rect.min.y + y_offset + dy); + fb.draw_pixmap(&pixmap, pt); + if fb.inverted() { + let rect = pixmap.rect() + pt; + fb.invert_region(&rect); + } + } + } + } + + // Draw second column indicator in remaining space below cover + let indicator_y = self.rect.min.y + y_offset + cover_height; + let indicator_center = pt!(self.rect.center().x, indicator_y + indicator_height / 2); + + match self.second_column { + SecondColumn::Year => { + let font = font_from_style(fonts, &MD_YEAR, dpi); + let plan = font.plan(&self.info.year, None, None); + let pt = pt!(indicator_center.x - plan.width / 2, + indicator_center.y + font.x_heights.1 as i32 / 2); + font.render(fb, scheme[1], &plan, pt); + }, + SecondColumn::Progress => { + let progress_height = scale_by_dpi(PROGRESS_HEIGHT, dpi) as i32; + let thickness = scale_by_dpi(THICKNESS_SMALL, dpi) as u16; + let (small_radius, big_radius) = halves(progress_height); + match self.info.status() { + Status::New | Status::Finished => { + let color = if self.info.reader.is_none() { WHITE } else { BLACK }; + fb.draw_rounded_rectangle_with_border(&rect![indicator_center - pt!(small_radius, small_radius), + indicator_center + pt!(big_radius, big_radius)], + &CornerSpec::Uniform(small_radius), + &BorderSpec { thickness, color: BLACK }, + &color); + }, + Status::Reading(progress) => { + let progress_width = self.rect.width() as i32 / 3; + let (small_progress_width, big_progress_width) = halves(progress_width); + let x_offset = indicator_center.x - progress_width / 2 + + (progress_width as f32 * progress.min(1.0)) as i32; + fb.draw_rounded_rectangle_with_border(&rect![indicator_center - pt!(small_progress_width, small_radius), + indicator_center + pt!(big_progress_width, big_radius)], + &CornerSpec::Uniform(small_radius), + &BorderSpec { thickness, color: BLACK }, + &|x, _| if x < x_offset { READING_PROGRESS } else { WHITE }); + } + } + }, + } + + return; + } + let (title, author) = if self.first_column == FirstColumn::TitleAndAuthor { (self.info.title(), self.info.author.as_str()) } else { diff --git a/crates/core/src/view/home/mod.rs b/crates/core/src/view/home/mod.rs index 952a75a..c33c8ac 100644 --- a/crates/core/src/view/home/mod.rs +++ b/crates/core/src/view/home/mod.rs @@ -160,8 +160,9 @@ impl Home { rect.max.x, rect.max.y - small_height - small_thickness], library_settings.first_column, library_settings.second_column, - library_settings.thumbnail_previews); - + library_settings.thumbnail_previews, + library_settings.large_thumbnails, + library_settings.cover_view); let max_lines = shelf.max_lines; let pages_count = (visible_books.len() as f32 / max_lines as f32).ceil() as usize; @@ -364,23 +365,31 @@ impl Home { fn update_thumbnail_previews(&mut self, hub: &Hub, rq: &mut RenderQueue, context: &mut Context) { let selected_library = context.settings.selected_library; - self.children[self.shelf_index].as_mut().downcast_mut::().unwrap() - .set_thumbnail_previews(context.settings.libraries[selected_library].thumbnail_previews); - self.update_shelf(false, hub, rq, context); + let settings = &context.settings.libraries[selected_library]; + let was_resized = { + let shelf = self.children[self.shelf_index].as_mut().downcast_mut::().unwrap(); + let old_max_lines = shelf.compute_max_lines(); + let thumbnail_previews = settings.thumbnail_previews && !settings.cover_view; + shelf.set_thumbnail_previews(thumbnail_previews); + shelf.set_cover_view(settings.cover_view); + shelf.set_large_thumbnails(settings.large_thumbnails); + shelf.compute_max_lines() != old_max_lines + }; + self.update_shelf(was_resized, hub, rq, context); + self.update_bottom_bar(rq, context); } + fn update_shelf(&mut self, was_resized: bool, hub: &Hub, rq: &mut RenderQueue, context: &mut Context) { - let dpi = CURRENT_DEVICE.dpi; - let big_height = scale_by_dpi(BIG_BAR_HEIGHT, dpi) as i32; - let thickness = scale_by_dpi(THICKNESS_MEDIUM, dpi) as i32; let shelf = self.children[self.shelf_index].as_mut().downcast_mut::().unwrap(); - let max_lines = ((shelf.rect.height() as i32 + thickness) / big_height) as usize; + let max_lines = shelf.compute_max_lines(); + shelf.max_lines = max_lines; if was_resized { let page_position = if self.visible_books.is_empty() { 0.0 } else { - self.current_page as f32 * (shelf.max_lines as f32 / + self.current_page as f32 * (max_lines as f32 / self.visible_books.len() as f32) }; @@ -1016,10 +1025,12 @@ impl Home { entries.push(EntryKind::Separator); - let first_column = library_settings.first_column; - entries.push(EntryKind::SubMenu("First Column".to_string(), - vec![EntryKind::RadioButton("Title and Author".to_string(), EntryId::FirstColumn(FirstColumn::TitleAndAuthor), first_column == FirstColumn::TitleAndAuthor), - EntryKind::RadioButton("File Name".to_string(), EntryId::FirstColumn(FirstColumn::FileName), first_column == FirstColumn::FileName)])); + if !library_settings.cover_view { + let first_column = library_settings.first_column; + entries.push(EntryKind::SubMenu("First Column".to_string(), + vec![EntryKind::RadioButton("Title and Author".to_string(), EntryId::FirstColumn(FirstColumn::TitleAndAuthor), first_column == FirstColumn::TitleAndAuthor), + EntryKind::RadioButton("File Name".to_string(), EntryId::FirstColumn(FirstColumn::FileName), first_column == FirstColumn::FileName)])); + } let second_column = library_settings.second_column; entries.push(EntryKind::SubMenu("Second Column".to_string(), @@ -1030,6 +1041,16 @@ impl Home { EntryId::ThumbnailPreviews, library_settings.thumbnail_previews)); + if library_settings.thumbnail_previews && !library_settings.cover_view { + entries.push(EntryKind::CheckBox("Large Thumbnails".to_string(), + EntryId::LargeThumbnails, + library_settings.large_thumbnails)); + } + + entries.push(EntryKind::CheckBox("Cover View".to_string(), + EntryId::CoverView, + library_settings.cover_view)); + let trash_path = context.library.home.join(TRASH_DIRNAME); if let Ok(trash) = Library::new(trash_path, LibraryMode::Database) .map_err(|e| eprintln!("Can't inspect trash: {:#?}.", e)) { @@ -1225,7 +1246,10 @@ impl Home { if let Some(shelf) = self.children[self.shelf_index].as_mut().downcast_mut::() { shelf.set_first_column(library_settings.first_column); shelf.set_second_column(library_settings.second_column); - shelf.set_thumbnail_previews(library_settings.thumbnail_previews); + let thumbnail_previews = library_settings.thumbnail_previews && !library_settings.cover_view; + shelf.set_thumbnail_previews(thumbnail_previews); + shelf.set_cover_view(library_settings.cover_view); + shelf.set_large_thumbnails(library_settings.large_thumbnails); } let home = context.library.home.clone(); @@ -1570,6 +1594,24 @@ impl View for Home { Event::Select(EntryId::ThumbnailPreviews) => { let selected_library = context.settings.selected_library; context.settings.libraries[selected_library].thumbnail_previews = !context.settings.libraries[selected_library].thumbnail_previews; + if context.settings.libraries[selected_library].thumbnail_previews { + context.settings.libraries[selected_library].cover_view = false; + } + self.update_thumbnail_previews(hub, rq, context); + true + }, + Event::Select(EntryId::CoverView) => { + let selected_library = context.settings.selected_library; + context.settings.libraries[selected_library].cover_view = !context.settings.libraries[selected_library].cover_view; + if context.settings.libraries[selected_library].cover_view { + context.settings.libraries[selected_library].thumbnail_previews = false; + } + self.update_thumbnail_previews(hub, rq, context); + true + }, + Event::Select(EntryId::LargeThumbnails) => { + let selected_library = context.settings.selected_library; + context.settings.libraries[selected_library].large_thumbnails = !context.settings.libraries[selected_library].large_thumbnails; self.update_thumbnail_previews(hub, rq, context); true }, diff --git a/crates/core/src/view/home/shelf.rs b/crates/core/src/view/home/shelf.rs index a4e1451..4f09ef9 100644 --- a/crates/core/src/view/home/shelf.rs +++ b/crates/core/src/view/home/shelf.rs @@ -31,23 +31,25 @@ pub struct Shelf { first_column: FirstColumn, second_column: SecondColumn, thumbnail_previews: bool, + cover_view: bool, + large_thumbnails: bool, } impl Shelf { - pub fn new(rect: Rectangle, first_column: FirstColumn, second_column: SecondColumn, thumbnail_previews: bool) -> Shelf { - let dpi = CURRENT_DEVICE.dpi; - let big_height = scale_by_dpi(BIG_BAR_HEIGHT, dpi) as i32; - let thickness = scale_by_dpi(THICKNESS_MEDIUM, dpi) as i32; - let max_lines = ((rect.height() as i32 + thickness) / big_height) as usize; - Shelf { + pub fn new(rect: Rectangle, first_column: FirstColumn, second_column: SecondColumn, thumbnail_previews: bool, large_thumbnails: bool, cover_view: bool) -> Shelf { + let mut shelf = Shelf { id: ID_FEEDER.next(), rect, children: Vec::new(), - max_lines, + max_lines: 0, first_column, second_column, thumbnail_previews, - } + cover_view, + large_thumbnails, + }; + shelf.max_lines = shelf.compute_max_lines(); + shelf } pub fn set_first_column(&mut self, first_column: FirstColumn) { @@ -62,16 +64,102 @@ impl Shelf { self.thumbnail_previews = thumbnail_previews; } + pub fn set_cover_view(&mut self, cover_view: bool) { + self.cover_view = cover_view; + } + + pub fn set_large_thumbnails(&mut self, large_thumbnails: bool) { + self.large_thumbnails = large_thumbnails; + } + + pub fn compute_max_lines(&self) -> usize { + let dpi = CURRENT_DEVICE.dpi; + let big_height = scale_by_dpi(BIG_BAR_HEIGHT, dpi) as i32; + if self.cover_view { + let cover_height = 3 * big_height; + let cover_width = 3 * cover_height / 4; + let cover_rows = (self.rect.height() as i32 / (3 * big_height)).max(1) as usize; + let cover_cols = (self.rect.width() as i32 / cover_width).max(1) as usize; + cover_rows * cover_cols + } else { + let row_height = if self.thumbnail_previews && self.large_thumbnails { 2 * big_height } else { big_height }; + let thickness = scale_by_dpi(THICKNESS_MEDIUM, dpi) as i32; + ((self.rect.height() as i32 + thickness) / row_height) as usize + } + } + pub fn update(&mut self, metadata: &[Info], hub: &Hub, rq: &mut RenderQueue, context: &Context) { self.children.clear(); let dpi = CURRENT_DEVICE.dpi; let big_height = scale_by_dpi(BIG_BAR_HEIGHT, dpi) as i32; let thickness = scale_by_dpi(THICKNESS_MEDIUM, dpi) as i32; + + if self.cover_view { + let cover_height = 3 * big_height; + let cover_width = 3 * cover_height / 4; + let cover_rows = (self.rect.height() as i32 / (3 * big_height)).max(1) as usize; + let cover_cols = (self.rect.width() as i32 / cover_width).max(1) as usize; + let max_items = cover_rows * cover_cols; + + if metadata.len() < max_items { + let filler = Filler::new(rect![self.rect.min.x, self.rect.min.y, + self.rect.max.x, self.rect.max.y], + WHITE); + self.children.push(Box::new(filler) as Box); + } + + let row_height = self.rect.height() as i32 / cover_rows as i32; + let col_width = self.rect.width() as i32 / cover_cols as i32; + let cover_th = 3 * big_height; + let cover_tw = 3 * cover_th / 4; + + for (index, info) in metadata.iter().enumerate() { + let row = index / cover_cols; + let col = index % cover_cols; + + let x_min = self.rect.min.x + (col as i32) * col_width; + let y_min = self.rect.min.y + (row as i32) * row_height; + + let preview_path: Option = { + let thumb_path = context.library.cover_preview(&info.file.path); + if !thumb_path.exists() { + let hub2 = hub.clone(); + let thumb_path2 = thumb_path.to_string_lossy().into_owned(); + let path2 = info.file.path.clone(); + let full_path = context.library.home.join(&path2); + thread::spawn(move || { + let _guard = EXCLUSIVE_ACCESS.lock().unwrap(); + open(full_path).and_then(|mut doc| { + doc.preview_pixmap(cover_tw as f32, cover_th as f32, CURRENT_DEVICE.color_samples()) + }).map(|pixmap| { + if pixmap.save(&thumb_path2).is_ok() { + hub2.send(Event::RefreshBookPreview(path2, Some(PathBuf::from(thumb_path2)))).ok(); + } + }) + }); + Some(PathBuf::default()) + } else { + Some(thumb_path) + } + }; + let book = Book::new(rect![x_min, y_min, + x_min + col_width, y_min + row_height], + info.clone(), + index, + self.first_column, + self.second_column, + true, + preview_path); + self.children.push(Box::new(book) as Box); + } + } else { + let row_height = if self.thumbnail_previews && self.large_thumbnails { 2 * big_height } else { big_height }; let (small_thickness, big_thickness) = halves(thickness); - let max_lines = ((self.rect.height() as i32 + thickness) / big_height) as usize; + self.max_lines = self.compute_max_lines(); + let max_lines = self.max_lines; let book_heights = divide(self.rect.height() as i32, max_lines as i32); let mut y_pos = self.rect.min.y; - let th = big_height; + let th = row_height; let tw = 3 * th / 4; for (index, info) in metadata.iter().enumerate() { @@ -79,7 +167,11 @@ impl Shelf { let y_max = y_pos + book_heights[index] - if index < max_lines - 1 { small_thickness } else { 0 }; let preview_path: Option = if self.thumbnail_previews { - let thumb_path = context.library.thumbnail_preview(&info.file.path); + let thumb_path = if self.large_thumbnails { + context.library.thumbnail_preview_large(&info.file.path) + } else { + context.library.thumbnail_preview(&info.file.path) + }; if !thumb_path.exists() { let hub2 = hub.clone(); let thumb_path2 = thumb_path.to_string_lossy().into_owned(); @@ -111,6 +203,7 @@ impl Shelf { index, self.first_column, self.second_column, + false, preview_path); self.children.push(Box::new(book) as Box); @@ -132,7 +225,7 @@ impl Shelf { self.children.push(Box::new(filler) as Box); } - self.max_lines = max_lines; + } rq.add(RenderData::new(self.id, self.rect, UpdateMode::Partial)); } } diff --git a/crates/core/src/view/mod.rs b/crates/core/src/view/mod.rs index 4e8a350..e15676e 100644 --- a/crates/core/src/view/mod.rs +++ b/crates/core/src/view/mod.rs @@ -530,6 +530,8 @@ pub enum EntryId { FirstColumn(FirstColumn), SecondColumn(SecondColumn), ThumbnailPreviews, + CoverView, + LargeThumbnails, ApplyCroppings(usize, PageScheme), RemoveCroppings, SetZoomMode(ZoomMode),