noctua/src/app/view/panel_pages.rs
wfx 1182b7b55d feature: PDF and PDF thumbnails and refresh UI
- Implement PDF and PDF thumbnail generation with incremental loading
- Add UI refresh mechanism (tick counter + RefreshView message)
- Improve fl! macro with named parameters
- Clean up code organization (mod.rs: wiring, model.rs: state only)
2026-01-18 20:35:12 +01:00

90 lines
3 KiB
Rust

// SPDX-License-Identifier: GPL-3.0-or-later
// src/app/view/panel_pages.rs
//
// Page thumbnail panel for multi-page documents (PDF, multi-page TIFF).
use cosmic::iced::{Alignment, Length};
use cosmic::widget::{button, column, scrollable, text};
use cosmic::widget::image as cosmic_image;
use cosmic::Element;
use crate::app::{AppMessage, AppModel};
use crate::constant::THUMBNAIL_MAX_WIDTH;
use crate::fl;
/// Content for the page navigation panel (COSMIC nav_bar).
/// Returns None if the current document doesn't support multiple pages.
pub fn pages_panel(model: &AppModel) -> Option<Element<'static, AppMessage>> {
let doc = model.document.as_ref()?;
// Only show for multi-page documents.
if !doc.is_multi_page() {
return None;
}
let page_count = doc.page_count()?;
let loaded = doc.thumbnails_loaded();
let current_page = doc.current_page()?;
let mut content = column::with_capacity(page_count as usize + 1)
.spacing(12)
.padding([12, 8])
.align_x(Alignment::Center)
.width(Length::Fill);
// Show loading progress if not all thumbnails are ready.
if !doc.thumbnails_ready() {
let loading_msg = fl!("loading-thumbnails", current: loaded, total: page_count);
content = content.push(text::caption(loading_msg));
}
// Build thumbnail list for pages that are already loaded.
for page_index in 0..loaded {
let is_current = page_index == current_page;
// Get cached thumbnail handle.
let thumbnail_element: Element<'static, AppMessage> =
if let Some(handle) = doc.get_thumbnail(page_index) {
cosmic_image::Image::new(handle)
.width(Length::Fixed(THUMBNAIL_MAX_WIDTH))
.into()
} else {
// Fallback: show page number if no thumbnail.
text::body(format!("{}", page_index + 1)).into()
};
// Page number label.
let page_label = text::caption(format!("{}", page_index + 1));
// Combine thumbnail and label in a column.
let page_content = column::with_capacity(2)
.spacing(4)
.align_x(Alignment::Center)
.push(thumbnail_element)
.push(page_label);
// Wrap in button for navigation.
let page_button = if is_current {
// Current page: highlighted style.
button::custom(page_content)
.class(cosmic::theme::Button::Suggested)
.padding(4)
} else {
// Other pages: clickable with standard style.
button::custom(page_content)
.class(cosmic::theme::Button::Standard)
.padding(4)
.on_press(AppMessage::GotoPage(page_index))
};
content = content.push(page_button);
}
// Wrap in scrollable container.
Some(
scrollable(content)
.width(Length::Shrink)
.height(Length::Fill)
.into(),
)
}