diff --git a/src/core/presentations.rs b/src/core/presentations.rs index 19f9166..bf47b5b 100644 --- a/src/core/presentations.rs +++ b/src/core/presentations.rs @@ -1,12 +1,13 @@ +use cosmic::widget::image::Handle; use crisp::types::{Keyword, Symbol, Value}; use miette::{IntoDiagnostic, Result}; -use mupdf::{Document, Page}; +use mupdf::{Colorspace, Document, Matrix}; use serde::{Deserialize, Serialize}; use sqlx::{ pool::PoolConnection, prelude::FromRow, query, sqlite::SqliteRow, Row, Sqlite, SqliteConnection, SqlitePool, }; -use std::{path::PathBuf, sync::Arc}; +use std::path::PathBuf; use tracing::{debug, error}; use crate::{Background, Slide, SlideBuilder, TextAlignment}; @@ -131,13 +132,36 @@ impl ServiceTrait for Presentation { let background = Background::try_from(self.path.clone()) .into_diagnostic()?; debug!(?background); - let doc = Document::open(background.path.as_path()) + let document = Document::open(background.path.as_path()) .into_diagnostic()?; - debug!(?doc); - let pages = doc.page_count().into_diagnostic()?; + debug!(?document); + let pages = document.pages().into_diagnostic()?; debug!(?pages); + let pages: Vec = pages + .filter_map(|page| { + let Some(page) = page.ok() else { + return None; + }; + let matrix = Matrix::IDENTITY; + let colorspace = Colorspace::device_rgb(); + let Ok(pixmap) = page + .to_pixmap(&matrix, &colorspace, true, true) + .into_diagnostic() + else { + error!("Can't turn this page into pixmap"); + return None; + }; + debug!(?pixmap); + Some(Handle::from_rgba( + pixmap.width(), + pixmap.height(), + pixmap.samples().to_vec(), + )) + }) + .collect(); + let mut slides: Vec = vec![]; - for page in 0..pages { + for (index, page) in pages.into_iter().enumerate() { let slide = SlideBuilder::new() .background( Background::try_from(self.path.clone()) @@ -151,7 +175,8 @@ impl ServiceTrait for Presentation { .video_loop(false) .video_start_time(0.0) .video_end_time(0.0) - .pdf_index(page as u32) + .pdf_index(index as u32) + .pdf_page(page) .build()?; slides.push(slide); } diff --git a/src/core/service_items.rs b/src/core/service_items.rs index da6267d..91a1e82 100644 --- a/src/core/service_items.rs +++ b/src/core/service_items.rs @@ -1,12 +1,10 @@ use std::borrow::Cow; use std::cmp::Ordering; use std::ops::Deref; -use std::sync::{Arc, Mutex}; use cosmic::iced::clipboard::mime::{AllowedMimeTypes, AsMimeTypes}; use crisp::types::{Keyword, Symbol, Value}; use miette::{IntoDiagnostic, Result}; -use resvg::usvg::fontdb; use serde::{Deserialize, Serialize}; use tracing::{debug, error}; @@ -260,7 +258,7 @@ impl From<&Song> for ServiceItem { kind: ServiceItemKind::Song(song.clone()), database_id: song.id, title: song.title.clone(), - slides: slides.into(), + slides: slides, ..Default::default() } } else { @@ -281,7 +279,7 @@ impl From<&Video> for ServiceItem { kind: ServiceItemKind::Video(video.clone()), database_id: video.id, title: video.title.clone(), - slides: slides.into(), + slides: slides, ..Default::default() } } else { @@ -302,7 +300,7 @@ impl From<&Image> for ServiceItem { kind: ServiceItemKind::Image(image.clone()), database_id: image.id, title: image.title.clone(), - slides: slides.into(), + slides: slides, ..Default::default() } } else { @@ -325,7 +323,7 @@ impl From<&Presentation> for ServiceItem { ), database_id: presentation.id, title: presentation.title.clone(), - slides: slides.into(), + slides: slides, ..Default::default() }, Err(e) => { diff --git a/src/core/slide.rs b/src/core/slide.rs index 97ad589..b857afa 100644 --- a/src/core/slide.rs +++ b/src/core/slide.rs @@ -1,20 +1,16 @@ +use cosmic::widget::image::Handle; // use cosmic::dialog::ashpd::url::Url; use crisp::types::{Keyword, Symbol, Value}; use iced_video_player::Video; use miette::{miette, Result}; -use resvg::usvg::fontdb; use serde::{Deserialize, Serialize}; use std::{ fmt::Display, path::{Path, PathBuf}, - sync::Arc, }; use tracing::error; -use crate::ui::{ - pdf::PdfViewer, - text_svg::{self, TextSvg}, -}; +use crate::ui::text_svg::TextSvg; use super::songs::Song; @@ -34,6 +30,8 @@ pub struct Slide { video_end_time: f32, pdf_index: u32, #[serde(skip)] + pdf_page: Option, + #[serde(skip)] pub text_svg: Option, } @@ -307,7 +305,7 @@ impl Slide { } pub fn text_alignment(&self) -> TextAlignment { - self.text_alignment.clone() + self.text_alignment } pub fn font_size(&self) -> i32 { @@ -326,6 +324,10 @@ impl Slide { self.audio.clone() } + pub fn pdf_page(&self) -> Option { + self.pdf_page.clone() + } + pub fn pdf_index(&self) -> u32 { self.pdf_index } @@ -547,6 +549,8 @@ pub struct SlideBuilder { video_end_time: Option, pdf_index: Option, #[serde(skip)] + pdf_page: Option, + #[serde(skip)] text_svg: Option, } @@ -629,6 +633,11 @@ impl SlideBuilder { self } + pub(crate) fn pdf_page(mut self, pdf_page: Handle) -> Self { + let _ = self.pdf_page.insert(pdf_page); + self + } + pub(crate) fn pdf_index( mut self, pdf_index: impl Into, @@ -674,6 +683,7 @@ impl SlideBuilder { video_end_time, text_svg: self.text_svg, pdf_index: self.pdf_index.unwrap_or_default(), + pdf_page: self.pdf_page, ..Default::default() }) } diff --git a/src/main.rs b/src/main.rs index 930a524..9027860 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,7 @@ use cosmic::app::{Core, Settings, Task}; use cosmic::iced::alignment::Vertical; use cosmic::iced::keyboard::{Key, Modifiers}; use cosmic::iced::window::{Mode, Position}; -use cosmic::iced::{self, event, window, Color, Length, Point}; +use cosmic::iced::{self, event, window, Length, Point}; use cosmic::iced_futures::Subscription; use cosmic::iced_widget::{column, row, stack}; use cosmic::theme; @@ -40,7 +40,7 @@ use ui::song_editor::{self, SongEditor}; use ui::EditorMode; use crate::core::kinds::ServiceItemKind; -use crate::ui::text_svg::{self, shadow, stroke, TextSvg}; +use crate::ui::text_svg::{self}; pub mod core; pub mod lisp; @@ -985,7 +985,7 @@ impl cosmic::Application for App { } Message::Search(query) => { self.search_query = query.clone(); - return self.search(query); + self.search(query) } Message::UpdateSearchResults(items) => { self.search_results = items; @@ -1206,14 +1206,14 @@ where fn search(&self, query: String) -> Task { if let Some(library) = self.library.clone() { - return Task::perform( + Task::perform( async move { library.search_items(query).await }, |items| { cosmic::Action::App(Message::UpdateSearchResults( items, )) }, - ); + ) } else { Task::none() } @@ -1378,7 +1378,7 @@ where .data_received_for::(|item| { item.map_or_else( || Message::None, - |item| Message::AppendServiceItem(item), + Message::AppendServiceItem, ) }) .on_finish( @@ -1396,7 +1396,7 @@ where ] .padding(10) .spacing(10); - let mut container = Container::new(column) + let container = Container::new(column) // .height(Length::Fill) .style(nav_bar_style); diff --git a/src/ui/library.rs b/src/ui/library.rs index b8a9e8d..d8b18be 100644 --- a/src/ui/library.rs +++ b/src/ui/library.rs @@ -264,7 +264,8 @@ impl<'a> Library { let video_library = self.library_item(&self.video_library); let presentation_library = self.library_item(&self.presentation_library); - let column = column![ + + column![ text::heading("Library").center().width(Length::Fill), cosmic::iced::widget::horizontal_rule(1), song_library, @@ -275,8 +276,7 @@ impl<'a> Library { .height(Length::Fill) .padding(10) .spacing(10) - .into(); - column + .into() } pub fn library_item( @@ -481,25 +481,23 @@ impl<'a> Library { .accent_text_color() .into() } - } else { - if let Some((library, selected)) = self.selected_item + } else if let Some((library, selected)) = self.selected_item + { + if model.kind == library + && selected == index as i32 { - if model.kind == library - && selected == index as i32 - { - theme::active().cosmic().control_0().into() - } else { - theme::active() - .cosmic() - .destructive_text_color() - .into() - } + theme::active().cosmic().control_0().into() } else { theme::active() .cosmic() .destructive_text_color() .into() } + } else { + theme::active() + .cosmic() + .destructive_text_color() + .into() }; text::body(elide_text(item.subtext(), size.width)) .center() @@ -529,20 +527,18 @@ impl<'a> Library { && selected == index as i32 { t.cosmic().accent.selected.into() - } else { - if let Some((library, hovered)) = - self.hovered_item + } else if let Some((library, hovered)) = + self.hovered_item + { + if model.kind == library + && hovered == index as i32 { - if model.kind == library - && hovered == index as i32 - { - t.cosmic().button.hover.into() - } else { - t.cosmic().button.base.into() - } + t.cosmic().button.hover.into() } else { t.cosmic().button.base.into() } + } else { + t.cosmic().button.base.into() } } else if let Some((library, hovered)) = self.hovered_item diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 4719bc0..8372371 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -2,7 +2,6 @@ use crate::core::model::LibraryKind; pub mod double_ended_slider; pub mod library; -pub mod pdf; pub mod presenter; pub mod slide_editor; pub mod song_editor; diff --git a/src/ui/pdf.rs b/src/ui/pdf.rs deleted file mode 100644 index 60e9fe5..0000000 --- a/src/ui/pdf.rs +++ /dev/null @@ -1,161 +0,0 @@ -use std::path::Path; - -use cosmic::iced::ContentFit; -use cosmic::iced::Length; -use cosmic::widget::image; -use cosmic::widget::image::Handle; -use cosmic::Element; -use miette::IntoDiagnostic; -use miette::Result; -use miette::Severity; -use mupdf::Colorspace; -use mupdf::Document; -use mupdf::Matrix; -use tracing::debug; -use tracing::error; - -#[derive(Debug, Clone, Default)] -pub struct PdfViewer { - document: Option, - pages: Option>, - current_page: Option, - current_index: usize, -} - -pub enum Message {} - -impl PdfViewer { - pub fn with_pdf(pdf: impl AsRef) -> Result { - let pdf_path = pdf.as_ref(); - let document = Document::open(pdf_path).into_diagnostic()?; - let pages = document.pages().into_diagnostic()?; - let pages: Vec = pages - .filter_map(|page| { - let Some(page) = page.ok() else { - return None; - }; - let matrix = Matrix::IDENTITY; - let colorspace = Colorspace::device_rgb(); - let Ok(pixmap) = page - .to_pixmap(&matrix, &colorspace, true, true) - .into_diagnostic() - else { - error!("Can't turn this page into pixmap"); - return None; - }; - let handle = pixmap.samples().to_vec(); - Some(Handle::from_bytes(handle)) - }) - .collect(); - let Some(page) = document.pages().into_diagnostic()?.next() - else { - return Err(miette::miette!( - severity = Severity::Warning, - "There isn't a first page here" - )); - }; - let page = page.into_diagnostic()?; - let matrix = Matrix::IDENTITY; - let colorspace = Colorspace::device_rgb(); - let pixmap = page - .to_pixmap(&matrix, &colorspace, true, true) - .into_diagnostic()?; - let handle = pixmap.samples().to_vec(); - Ok(Self { - document: Some(document), - pages: Some(pages), - current_index: 0, - current_page: Some(Handle::from_bytes(handle)), - }) - } - - pub fn insert_pdf( - &mut self, - pdf: impl AsRef, - ) -> Result<()> { - let pdf_path = pdf.as_ref(); - let document = Document::open(pdf_path).into_diagnostic()?; - let pages = document.pages().into_diagnostic()?; - - let pages: Vec = pages - .filter_map(|page| { - let Some(page) = page.ok() else { - return None; - }; - let matrix = Matrix::IDENTITY; - let colorspace = Colorspace::device_rgb(); - let Ok(pixmap) = page - .to_pixmap(&matrix, &colorspace, true, true) - .into_diagnostic() - else { - error!("Can't turn this page into pixmap"); - return None; - }; - debug!(?pixmap); - Some(Handle::from_rgba( - pixmap.width(), - pixmap.height(), - pixmap.samples().to_vec(), - )) - }) - .collect(); - let Some(page) = document.pages().into_diagnostic()?.next() - else { - return Err(miette::miette!( - severity = Severity::Warning, - "There isn't a first page here" - )); - }; - self.current_page = pages.get(0).map(|h| h.to_owned()); - self.document = Some(document); - self.pages = Some(pages); - self.current_index = 0; - debug!(?self); - Ok(()) - } - - pub fn next_page(&mut self) -> Result<()> { - let Some(ref pages) = self.pages else { - return Err(miette::miette!("No pages in doc")); - }; - let Some(page) = pages.get(self.current_index + 1) else { - return Err(miette::miette!("There isn't a next page")); - }; - self.current_page = Some(page.to_owned()); - self.current_index += 1; - Ok(()) - } - - pub fn previous_page(&mut self) -> Result<()> { - if self.current_index == 0 { - return Err(miette::miette!("You are at the first page")); - } - let Some(ref pages) = self.pages else { - return Err(miette::miette!("No pages in doc")); - }; - let Some(page) = pages.get(self.current_index - 1) else { - return Err(miette::miette!( - "There isn't a previous page" - )); - }; - self.current_page = Some(page.to_owned()); - self.current_index -= 1; - Ok(()) - } - - pub fn view(&self, index: u32) -> Option> { - let Some(pages) = &self.pages else { - return None; - }; - let Some(handle) = pages.get(index as usize) else { - return None; - }; - Some( - image(handle) - .width(Length::Fill) - .height(Length::Fill) - .content_fit(ContentFit::Contain) - .into(), - ) - } -} diff --git a/src/ui/presenter.rs b/src/ui/presenter.rs index ea268cc..74b77d0 100644 --- a/src/ui/presenter.rs +++ b/src/ui/presenter.rs @@ -3,23 +3,18 @@ use std::{fs::File, io::BufReader, path::PathBuf, sync::Arc}; use cosmic::{ iced::{ - alignment::Horizontal, - border, font::{Family, Stretch, Style, Weight}, Background, Border, Color, ContentFit, Font, Length, Shadow, Vector, }, iced_widget::{ - rich_text, scrollable::{ scroll_to, AbsoluteOffset, Direction, Scrollbar, - }, - span, stack, vertical_rule, + }, stack, vertical_rule, }, prelude::*, widget::{ - container, image, mouse_area, responsive, scrollable, text, - Column, Container, Id, Image, Row, Space, + container, image, mouse_area, responsive, scrollable, text, Container, Id, Row, Space, }, Task, }; @@ -33,8 +28,6 @@ use crate::{ BackgroundKind, }; -use crate::ui::pdf::PdfViewer; - const REFERENCE_WIDTH: f32 = 1920.0; const REFERENCE_HEIGHT: f32 = 1080.0; @@ -53,7 +46,6 @@ pub(crate) struct Presenter { hovered_slide: Option<(usize, usize)>, scroll_id: Id, current_font: Font, - pdf_viewer: PdfViewer, } pub(crate) enum Action { @@ -174,7 +166,6 @@ impl Presenter { }, scroll_id: Id::unique(), current_font: cosmic::font::default(), - pdf_viewer: PdfViewer::default(), } } @@ -225,11 +216,6 @@ impl Presenter { self.reset_video(); } - if slide.background().kind == BackgroundKind::Pdf { - self.pdf_viewer - .insert_pdf(&slide.background().path); - } - let offset = AbsoluteOffset { x: { if self.current_slide_index > 2 { @@ -411,7 +397,6 @@ impl Presenter { slide_view( &self.current_slide, &self.video, - &self.pdf_viewer, self.current_font, false, true, @@ -422,7 +407,6 @@ impl Presenter { slide_view( &self.current_slide, &self.video, - &self.pdf_viewer, self.current_font, false, false, @@ -457,9 +441,8 @@ impl Presenter { ); let container = slide_view( - &slide, + slide, &self.video, - &self.pdf_viewer, font, true, false, @@ -722,7 +705,6 @@ fn scale_font(font_size: f32, width: f32) -> f32 { pub(crate) fn slide_view<'a>( slide: &'a Slide, video: &'a Option