From a3516ef70c7e41ed23ffbd59953cd8c609d4b4b1 Mon Sep 17 00:00:00 2001 From: Chris Cochrun Date: Sun, 28 Sep 2025 14:27:08 -0500 Subject: [PATCH 1/2] some more cleanup --- src/core/images.rs | 4 +- src/core/model.rs | 2 +- src/core/presentations.rs | 4 +- src/core/service_items.rs | 2 +- src/core/slide.rs | 2 +- src/core/songs.rs | 8 ++-- src/core/videos.rs | 4 +- src/lisp.rs | 2 +- src/main.rs | 61 +++++++++++------------------- src/ui/image_editor.rs | 10 ++--- src/ui/library.rs | 21 +++++----- src/ui/presentation_editor.rs | 10 ++--- src/ui/presenter.rs | 14 +++---- src/ui/service.rs | 7 ++-- src/ui/slide_editor.rs | 2 +- src/ui/song_editor.rs | 8 ++-- src/ui/text_svg.rs | 6 +-- src/ui/video_editor.rs | 10 ++--- src/ui/widgets/draggable/column.rs | 8 ++-- src/ui/widgets/draggable/row.rs | 6 +-- 20 files changed, 87 insertions(+), 104 deletions(-) diff --git a/src/core/images.rs b/src/core/images.rs index cd9b2e7..fda63f4 100644 --- a/src/core/images.rs +++ b/src/core/images.rs @@ -10,8 +10,8 @@ use crisp::types::{Keyword, Symbol, Value}; use miette::{IntoDiagnostic, Result}; use serde::{Deserialize, Serialize}; use sqlx::{ - Sqlite, SqliteConnection, SqlitePool, pool::PoolConnection, - query, query_as, + pool::PoolConnection, query, query_as, Sqlite, SqliteConnection, + SqlitePool, }; use std::path::{Path, PathBuf}; use tracing::{debug, error}; diff --git a/src/core/model.rs b/src/core/model.rs index 13ddda8..a778a14 100644 --- a/src/core/model.rs +++ b/src/core/model.rs @@ -1,6 +1,6 @@ use std::mem::replace; -use miette::{Result, miette}; +use miette::{miette, Result}; use sqlx::{Connection, SqliteConnection}; #[derive(Debug, Clone)] diff --git a/src/core/presentations.rs b/src/core/presentations.rs index 76d19ea..4ada88e 100644 --- a/src/core/presentations.rs +++ b/src/core/presentations.rs @@ -4,8 +4,8 @@ use miette::{IntoDiagnostic, Result}; use mupdf::{Colorspace, Document, Matrix}; use serde::{Deserialize, Serialize}; use sqlx::{ - Row, Sqlite, SqliteConnection, SqlitePool, pool::PoolConnection, - prelude::FromRow, query, sqlite::SqliteRow, + pool::PoolConnection, prelude::FromRow, query, sqlite::SqliteRow, + Row, Sqlite, SqliteConnection, SqlitePool, }; use std::path::{Path, PathBuf}; use tracing::{debug, error}; diff --git a/src/core/service_items.rs b/src/core/service_items.rs index 5b4aed7..3912fa0 100644 --- a/src/core/service_items.rs +++ b/src/core/service_items.rs @@ -12,7 +12,7 @@ use crate::Slide; use super::images::Image; use super::presentations::Presentation; -use super::songs::{Song, lisp_to_song}; +use super::songs::{lisp_to_song, Song}; use super::videos::Video; use super::kinds::ServiceItemKind; diff --git a/src/core/slide.rs b/src/core/slide.rs index 70e60c1..30bf847 100644 --- a/src/core/slide.rs +++ b/src/core/slide.rs @@ -2,7 +2,7 @@ use cosmic::widget::image::Handle; // use cosmic::dialog::ashpd::url::Url; use crisp::types::{Keyword, Symbol, Value}; use iced_video_player::Video; -use miette::{Result, miette}; +use miette::{miette, Result}; use serde::{Deserialize, Serialize}; use std::{ fmt::Display, diff --git a/src/core/songs.rs b/src/core/songs.rs index 2be3a66..feb9d65 100644 --- a/src/core/songs.rs +++ b/src/core/songs.rs @@ -1,15 +1,15 @@ use std::{collections::HashMap, option::Option, path::PathBuf}; use crisp::types::{Keyword, Symbol, Value}; -use miette::{IntoDiagnostic, Result, miette}; +use miette::{miette, IntoDiagnostic, Result}; use serde::{Deserialize, Serialize}; use sqlx::{ - Acquire, FromRow, Row, Sqlite, SqliteConnection, SqlitePool, - pool::PoolConnection, query, sqlite::SqliteRow, + pool::PoolConnection, query, sqlite::SqliteRow, Acquire, FromRow, + Row, Sqlite, SqliteConnection, SqlitePool, }; use tracing::error; -use crate::{Slide, SlideBuilder, core::slide}; +use crate::{core::slide, Slide, SlideBuilder}; use super::{ content::Content, diff --git a/src/core/videos.rs b/src/core/videos.rs index c881572..dc97db6 100644 --- a/src/core/videos.rs +++ b/src/core/videos.rs @@ -11,8 +11,8 @@ use crisp::types::{Keyword, Symbol, Value}; use miette::{IntoDiagnostic, Result}; use serde::{Deserialize, Serialize}; use sqlx::{ - Sqlite, SqliteConnection, SqlitePool, pool::PoolConnection, - query, query_as, + pool::PoolConnection, query, query_as, Sqlite, SqliteConnection, + SqlitePool, }; use std::path::{Path, PathBuf}; use tracing::{debug, error}; diff --git a/src/lisp.rs b/src/lisp.rs index c2f4c8f..2f803e4 100644 --- a/src/lisp.rs +++ b/src/lisp.rs @@ -40,11 +40,11 @@ mod test { use std::{fs::read_to_string, path::PathBuf}; use crate::{ - Background, TextAlignment, core::{ images::Image, kinds::ServiceItemKind, service_items::ServiceTrait, songs::Song, videos::Video, }, + Background, TextAlignment, }; use super::*; diff --git a/src/main.rs b/src/main.rs index 9cd9055..4114475 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use clap::{Parser, command}; +use clap::{command, Parser}; use core::service_items::ServiceItem; use core::slide::{ Background, BackgroundKind, Slide, SlideBuilder, TextAlignment, @@ -9,8 +9,8 @@ use cosmic::iced::alignment::Vertical; use cosmic::iced::keyboard::{Key, Modifiers}; use cosmic::iced::window::{Mode, Position}; use cosmic::iced::{ - self, Background as IcedBackground, Border, Color, Length, event, - window, + self, event, window, Background as IcedBackground, Border, Color, + Length, }; use cosmic::iced_core::text::Wrapping; use cosmic::iced_futures::Subscription; @@ -21,18 +21,18 @@ use cosmic::widget::menu::key_bind::Modifier; use cosmic::widget::menu::{ItemWidth, KeyBind}; use cosmic::widget::nav_bar::nav_bar_style; use cosmic::widget::tooltip::Position as TPosition; -use cosmic::widget::{Container, menu}; use cosmic::widget::{ - Space, button, context_menu, horizontal_space, mouse_area, - nav_bar, nav_bar_toggle, responsive, scrollable, search_input, - tooltip, vertical_space, + button, context_menu, horizontal_space, mouse_area, nav_bar, + nav_bar_toggle, responsive, scrollable, search_input, tooltip, + vertical_space, Space, }; use cosmic::widget::{container, text}; use cosmic::widget::{icon, slider}; -use cosmic::{Application, ApplicationExt, Element, executor}; +use cosmic::widget::{menu, Container}; +use cosmic::{executor, Application, ApplicationExt, Element}; use crisp::types::Value; use lisp::parse_lisp; -use miette::{Result, miette}; +use miette::{miette, Result}; use rayon::prelude::*; use resvg::usvg::fontdb; use std::collections::HashMap; @@ -42,10 +42,10 @@ use std::sync::Arc; use tracing::{debug, level_filters::LevelFilter}; use tracing::{error, warn}; use tracing_subscriber::EnvFilter; -use ui::EditorMode; use ui::library::{self, Library}; use ui::presenter::{self, Presenter}; use ui::song_editor::{self, SongEditor}; +use ui::EditorMode; use crate::core::kinds::ServiceItemKind; use crate::ui::image_editor::{self, ImageEditor}; @@ -1690,22 +1690,14 @@ where tooltip, vec!["application/service-item".into()], ) - .data_received_for::(move |item| { - if let Some(item) = item { - Message::AddServiceItem(index, item) - } else { - Message::None - } - }) - .forward_drag_as_cursor(true) - .on_finish(move |mime, data, action, x, y| { - debug!(mime, ?data, ?action, x, y); + .on_finish(move |mime, data, _, _, _| { let Ok(item) = ServiceItem::try_from((data, mime)) else { + error!("couldn't drag in Service item"); return Message::None; }; - debug!(?item); + debug!(?item, index, "adding Service item"); Message::AddServiceItem(index, item) }) .into() @@ -1763,24 +1755,15 @@ where column, vec!["application/service-item".into()], ) - .data_received_for::(|item| { - item.map_or_else( - || Message::None, - Message::AppendServiceItem, - ) - }) - .on_finish( - move |mime, data, action, x, y| { - debug!(mime, ?data, ?action, x, y); - let Ok(item) = - ServiceItem::try_from((data, mime)) - else { - return Message::None; - }; - debug!(?item); - Message::AppendServiceItem(item) - }, - ), + .on_finish(move |mime, data, _, _, _| { + let Ok(item) = ServiceItem::try_from((data, mime)) + else { + error!("couldn't drag in Service item"); + return Message::None; + }; + debug!(?item, "adding Service item"); + Message::AppendServiceItem(item) + }), ) .style(nav_bar_style); diff --git a/src/ui/image_editor.rs b/src/ui/image_editor.rs index 194484f..878fe8e 100644 --- a/src/ui/image_editor.rs +++ b/src/ui/image_editor.rs @@ -2,15 +2,15 @@ use std::{io, path::PathBuf}; use crate::core::images::Image; use cosmic::{ - Element, Task, - dialog::file_chooser::{FileFilter, open::Dialog}, - iced::{Length, alignment::Vertical}, + dialog::file_chooser::{open::Dialog, FileFilter}, + iced::{alignment::Vertical, Length}, iced_widget::{column, row}, theme, widget::{ - self, Space, button, container, horizontal_space, icon, text, - text_input, + self, button, container, horizontal_space, icon, text, + text_input, Space, }, + Element, Task, }; use tracing::{debug, error, warn}; diff --git a/src/ui/library.rs b/src/ui/library.rs index f4ca0b0..80c9fe7 100644 --- a/src/ui/library.rs +++ b/src/ui/library.rs @@ -1,35 +1,36 @@ use std::collections::HashMap; use cosmic::{ - Element, Task, dialog::file_chooser::open::Dialog, iced::{ - Background, Border, Color, Length, alignment::Vertical, - clipboard::dnd::DndAction, keyboard::Modifiers, + alignment::Vertical, clipboard::dnd::DndAction, + keyboard::Modifiers, Background, Border, Color, Length, }, iced_core::widget::tree::State, iced_widget::{column, row as rowm, text as textm}, theme, widget::{ - Container, DndSource, Space, button, container, context_menu, - dnd_destination, horizontal_space, icon, + button, container, context_menu, dnd_destination, + horizontal_space, icon, menu::{self, Action as MenuAction}, mouse_area, responsive, row, scrollable, text, text_input, + Container, DndSource, Space, }, + Element, Task, }; use miette::{IntoDiagnostic, Result}; use rapidfuzz::distance::levenshtein; -use sqlx::{SqlitePool, migrate}; +use sqlx::{migrate, SqlitePool}; use tracing::{debug, error, warn}; use crate::core::{ content::Content, - images::{self, Image, update_image_in_db}, + images::{self, update_image_in_db, Image}, model::{LibraryKind, Model}, - presentations::{self, Presentation, update_presentation_in_db}, + presentations::{self, update_presentation_in_db, Presentation}, service_items::ServiceItem, - songs::{self, Song, update_song_in_db}, - videos::{self, Video, update_video_in_db}, + songs::{self, update_song_in_db, Song}, + videos::{self, update_video_in_db, Video}, }; #[derive(Debug, Clone)] diff --git a/src/ui/presentation_editor.rs b/src/ui/presentation_editor.rs index 17f4a9f..7b181b0 100644 --- a/src/ui/presentation_editor.rs +++ b/src/ui/presentation_editor.rs @@ -5,15 +5,15 @@ use crate::core::{ slide::Slide, }; use cosmic::{ - Element, Task, - dialog::file_chooser::{FileFilter, open::Dialog}, - iced::{Length, alignment::Vertical}, + dialog::file_chooser::{open::Dialog, FileFilter}, + iced::{alignment::Vertical, Length}, iced_widget::{column, row}, theme, widget::{ - Space, button, container, horizontal_space, icon, text, - text_input, + button, container, horizontal_space, icon, text, text_input, + Space, }, + Element, Task, }; use tracing::{debug, error, warn}; diff --git a/src/ui/presenter.rs b/src/ui/presenter.rs index 84b9e18..a758311 100644 --- a/src/ui/presenter.rs +++ b/src/ui/presenter.rs @@ -7,32 +7,32 @@ use std::{ }; use cosmic::{ - Task, iced::{ + font::{Family, Stretch, Style, Weight}, Background, Border, Color, ContentFit, Font, Length, Shadow, Vector, - font::{Family, Stretch, Style, Weight}, }, iced_widget::{ scrollable::{ - AbsoluteOffset, Direction, Scrollbar, scroll_to, + scroll_to, AbsoluteOffset, Direction, Scrollbar, }, stack, vertical_rule, }, prelude::*, widget::{ - Container, Id, Row, Space, container, image, mouse_area, - responsive, scrollable, text, + container, image, mouse_area, responsive, scrollable, text, + Container, Id, Row, Space, }, + Task, }; -use iced_video_player::{Position, Video, VideoPlayer, gst_pbutils}; +use iced_video_player::{gst_pbutils, Position, Video, VideoPlayer}; use rodio::{Decoder, OutputStream, OutputStreamBuilder, Sink}; use tracing::{debug, error, info, warn}; use url::Url; use crate::{ - BackgroundKind, core::{service_items::ServiceItem, slide::Slide}, + BackgroundKind, }; const REFERENCE_WIDTH: f32 = 1920.0; diff --git a/src/ui/service.rs b/src/ui/service.rs index ceca63c..4a7dd81 100644 --- a/src/ui/service.rs +++ b/src/ui/service.rs @@ -2,16 +2,15 @@ use cosmic::iced::Size; use cosmic::iced_core::widget::tree; use cosmic::{ - Element, iced::{ - Event, Length, Point, Rectangle, Vector, clipboard::dnd::{DndEvent, SourceEvent}, - event, mouse, + event, mouse, Event, Length, Point, Rectangle, Vector, }, iced_core::{ - self, Clipboard, Shell, layout, renderer, widget::Tree, + self, layout, renderer, widget::Tree, Clipboard, Shell, }, widget::Widget, + Element, }; use tracing::debug; diff --git a/src/ui/slide_editor.rs b/src/ui/slide_editor.rs index ca6ce51..c9ae9cb 100644 --- a/src/ui/slide_editor.rs +++ b/src/ui/slide_editor.rs @@ -1,13 +1,13 @@ use std::{io, path::PathBuf}; use cosmic::{ - Renderer, iced::{Color, Font, Length, Size}, widget::{ self, canvas::{self, Program, Stroke}, container, }, + Renderer, }; use tracing::debug; diff --git a/src/ui/song_editor.rs b/src/ui/song_editor.rs index 309e86c..6a524bd 100644 --- a/src/ui/song_editor.rs +++ b/src/ui/song_editor.rs @@ -1,9 +1,8 @@ use std::{io, path::PathBuf, sync::Arc}; use cosmic::{ - Element, Task, - dialog::file_chooser::{FileFilter, open::Dialog}, - iced::{Length, alignment::Vertical}, + dialog::file_chooser::{open::Dialog, FileFilter}, + iced::{alignment::Vertical, Length}, iced_wgpu::graphics::text::cosmic_text::fontdb, iced_widget::{column, row}, theme, @@ -11,6 +10,7 @@ use cosmic::{ button, combo_box, container, horizontal_space, icon, progress_bar, scrollable, text, text_editor, text_input, }, + Element, Task, }; use dirs::font_dir; use iced_video_player::Video; @@ -18,11 +18,11 @@ use rayon::iter::{IntoParallelIterator, ParallelIterator}; use tracing::{debug, error}; use crate::{ - Background, BackgroundKind, core::{service_items::ServiceTrait, slide::Slide, songs::Song}, ui::{ presenter::slide_view, slide_editor::SlideEditor, text_svg, }, + Background, BackgroundKind, }; #[derive(Debug)] diff --git a/src/ui/text_svg.rs b/src/ui/text_svg.rs index 07b0e9a..58c574a 100644 --- a/src/ui/text_svg.rs +++ b/src/ui/text_svg.rs @@ -8,16 +8,16 @@ use std::{ use colors_transform::Rgb; use cosmic::{ iced::{ - ContentFit, Length, Size, font::{Style, Weight}, + ContentFit, Length, Size, }, prelude::*, - widget::{Image, image::Handle}, + widget::{image::Handle, Image}, }; use rapidhash::v3::rapidhash_v3; use resvg::{ tiny_skia::{self, Pixmap}, - usvg::{Tree, fontdb}, + usvg::{fontdb, Tree}, }; use tracing::{debug, error}; diff --git a/src/ui/video_editor.rs b/src/ui/video_editor.rs index f4a27c1..d0d4fd2 100644 --- a/src/ui/video_editor.rs +++ b/src/ui/video_editor.rs @@ -1,15 +1,15 @@ use std::{io, path::PathBuf}; use cosmic::{ - Element, Task, - dialog::file_chooser::{FileFilter, open::Dialog}, - iced::{Length, alignment::Vertical}, + dialog::file_chooser::{open::Dialog, FileFilter}, + iced::{alignment::Vertical, Length}, iced_widget::{column, row}, theme, widget::{ - Space, button, container, horizontal_space, icon, - progress_bar, text, text_input, + button, container, horizontal_space, icon, progress_bar, + text, text_input, Space, }, + Element, Task, }; use iced_video_player::{Video, VideoPlayer}; use tracing::{debug, error, warn}; diff --git a/src/ui/widgets/draggable/column.rs b/src/ui/widgets/draggable/column.rs index 2d77bcb..b35a2bc 100644 --- a/src/ui/widgets/draggable/column.rs +++ b/src/ui/widgets/draggable/column.rs @@ -22,17 +22,17 @@ // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -use cosmic::Theme; use cosmic::iced::advanced::layout::{self, Layout}; -use cosmic::iced::advanced::widget::{Operation, Tree, Widget, tree}; -use cosmic::iced::advanced::{Clipboard, Shell, overlay, renderer}; +use cosmic::iced::advanced::widget::{tree, Operation, Tree, Widget}; +use cosmic::iced::advanced::{overlay, renderer, Clipboard, Shell}; use cosmic::iced::alignment::{self, Alignment}; use cosmic::iced::event::{self, Event}; -use cosmic::iced::{self, Transformation, mouse}; +use cosmic::iced::{self, mouse, Transformation}; use cosmic::iced::{ Background, Border, Color, Element, Length, Padding, Pixels, Point, Rectangle, Size, Vector, }; +use cosmic::Theme; use tracing::debug; use super::{Action, DragEvent, DropPosition}; diff --git a/src/ui/widgets/draggable/row.rs b/src/ui/widgets/draggable/row.rs index 67f2212..4bbaf7d 100644 --- a/src/ui/widgets/draggable/row.rs +++ b/src/ui/widgets/draggable/row.rs @@ -23,11 +23,11 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. use cosmic::iced::advanced::layout::{self, Layout}; -use cosmic::iced::advanced::widget::{Operation, Tree, Widget, tree}; -use cosmic::iced::advanced::{Clipboard, Shell, overlay, renderer}; +use cosmic::iced::advanced::widget::{tree, Operation, Tree, Widget}; +use cosmic::iced::advanced::{overlay, renderer, Clipboard, Shell}; use cosmic::iced::alignment::{self, Alignment}; use cosmic::iced::event::{self, Event}; -use cosmic::iced::{self, Transformation, mouse}; +use cosmic::iced::{self, mouse, Transformation}; use cosmic::iced::{ Background, Border, Color, Element, Length, Padding, Pixels, Point, Rectangle, Size, Theme, Vector, From ee45a11a0f9a8fce0ea162766c695667dde45bfa Mon Sep 17 00:00:00 2001 From: Chris Cochrun Date: Mon, 29 Sep 2025 15:21:24 -0500 Subject: [PATCH 2/2] presentations now show up and reworked some of dnd for better --- src/core/model.rs | 51 +++++++++++- src/core/presentations.rs | 34 +++++++- src/main.rs | 123 ++++++++++++++++++++++------ src/ui/library.rs | 148 +++++++++++++++++++++++++++++----- src/ui/presentation_editor.rs | 24 ++++-- 5 files changed, 323 insertions(+), 57 deletions(-) diff --git a/src/core/model.rs b/src/core/model.rs index a778a14..53f6162 100644 --- a/src/core/model.rs +++ b/src/core/model.rs @@ -1,7 +1,10 @@ -use std::mem::replace; +use std::{borrow::Cow, mem::replace}; -use miette::{miette, Result}; +use cosmic::iced::clipboard::mime::{AllowedMimeTypes, AsMimeTypes}; +use miette::{miette, IntoDiagnostic, Result}; +use serde::{Deserialize, Serialize}; use sqlx::{Connection, SqliteConnection}; +use tracing::debug; #[derive(Debug, Clone)] pub struct Model { @@ -9,7 +12,9 @@ pub struct Model { pub kind: LibraryKind, } -#[derive(Debug, Clone, PartialEq, Eq, Copy)] +#[derive( + Debug, Clone, PartialEq, Eq, Copy, Hash, Serialize, Deserialize, +)] pub enum LibraryKind { Song, Video, @@ -17,6 +22,46 @@ pub enum LibraryKind { Presentation, } +#[derive( + Debug, Clone, PartialEq, Eq, Copy, Hash, Serialize, Deserialize, +)] +pub struct KindWrapper(pub (LibraryKind, i32)); + +impl TryFrom<(Vec, String)> for KindWrapper { + type Error = miette::Error; + + fn try_from( + value: (Vec, String), + ) -> std::result::Result { + debug!(?value); + ron::de::from_bytes(&value.0).into_diagnostic() + } +} + +impl AllowedMimeTypes for KindWrapper { + fn allowed() -> Cow<'static, [String]> { + Cow::from(vec!["application/service-item".to_string()]) + } +} + +impl AsMimeTypes for KindWrapper { + fn available(&self) -> Cow<'static, [String]> { + debug!(?self); + Cow::from(vec!["application/service-item".to_string()]) + } + + fn as_bytes( + &self, + mime_type: &str, + ) -> Option> { + debug!(?self); + debug!(mime_type); + let ron = ron::ser::to_string(self).ok()?; + debug!(ron); + Some(Cow::from(ron.into_bytes())) + } +} + impl Model { pub fn add_item(&mut self, item: T) -> Result<()> { self.items.push(item); diff --git a/src/core/presentations.rs b/src/core/presentations.rs index 4ada88e..a1d8b2a 100644 --- a/src/core/presentations.rs +++ b/src/core/presentations.rs @@ -82,7 +82,7 @@ impl From<&Path> for Presentation { } impl From<&Presentation> for Value { - fn from(value: &Presentation) -> Self { + fn from(_value: &Presentation) -> Self { Self::List(vec![Self::Symbol(Symbol("presentation".into()))]) } } @@ -307,6 +307,38 @@ pub async fn remove_from_db( .map(|_| ()) } +pub async fn add_presentation_to_db( + presentation: Presentation, + db: PoolConnection, + id: i32, +) -> Result<()> { + let path = presentation + .path + .to_str() + .map(std::string::ToString::to_string) + .unwrap_or_default(); + let html = presentation.kind == PresKind::Html; + let mut db = db.detach(); + let result = query!( + r#"INSERT into presentations VALUES($1, $2, $3, $4)"#, + id, + presentation.title, + path, + html, + ) + .execute(&mut db) + .await + .into_diagnostic()?; + if result.last_insert_rowid() != id as i64 { + let rowid = result.last_insert_rowid(); + error!( + rowid, + id, "It appears that rowid and id aren't the same" + ); + } + Ok(()) +} + pub async fn update_presentation_in_db( presentation: Presentation, db: PoolConnection, diff --git a/src/main.rs b/src/main.rs index 4114475..4fa4b1f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -47,8 +47,11 @@ use ui::presenter::{self, Presenter}; use ui::song_editor::{self, SongEditor}; use ui::EditorMode; +use crate::core::content::Content; use crate::core::kinds::ServiceItemKind; +use crate::core::model::KindWrapper; use crate::ui::image_editor::{self, ImageEditor}; +use crate::ui::presentation_editor::{self, PresentationEditor}; use crate::ui::text_svg::{self}; use crate::ui::video_editor::{self, VideoEditor}; use crate::ui::widgets::draggable; @@ -127,6 +130,7 @@ struct App { song_editor: SongEditor, video_editor: VideoEditor, image_editor: ImageEditor, + presentation_editor: PresentationEditor, searching: bool, search_query: String, search_results: Vec, @@ -145,6 +149,7 @@ enum Message { SongEditor(song_editor::Message), VideoEditor(video_editor::Message), ImageEditor(image_editor::Message), + PresentationEditor(presentation_editor::Message), File(PathBuf), OpenWindow, CloseWindow(Option), @@ -160,7 +165,7 @@ enum Message { SelectServiceItem(usize), AddSelectServiceItem(usize), HoveredServiceItem(Option), - AddServiceItem(usize, ServiceItem), + AddServiceItem(usize, KindWrapper), RemoveServiceItem(usize), AddServiceItemDrop(usize), AppendServiceItem(ServiceItem), @@ -333,6 +338,7 @@ impl cosmic::Application for App { song_editor, video_editor: VideoEditor::new(), image_editor: ImageEditor::new(), + presentation_editor: PresentationEditor::new(), searching: false, search_results: vec![], search_query: String::new(), @@ -818,6 +824,27 @@ impl cosmic::Application for App { video_editor::Action::None => Task::none(), } } + Message::PresentationEditor(message) => { + match self.presentation_editor.update(message) { + presentation_editor::Action::Task(task) => { + task.map(|m| { + cosmic::Action::App(Message::PresentationEditor( + m, + )) + }) + } + presentation_editor::Action::UpdatePresentation(presentation) => { + if self.library.is_some() { + self.update(Message::Library( + library::Message::UpdatePresentation(presentation), + )) + } else { + Task::none() + } + } + presentation_editor::Action::None => Task::none(), + } + } Message::Present(message) => { // debug!(?message); if self.presentation_open @@ -854,7 +881,7 @@ impl cosmic::Application for App { ) })); } - _ => todo!(), + _ => (), } self.current_item = (item_index, slide_index + 1); @@ -882,7 +909,7 @@ impl cosmic::Application for App { ) })); } - _ => todo!(), + _ => (), } } Task::batch(tasks) @@ -914,7 +941,7 @@ impl cosmic::Application for App { ) })); } - _ => todo!(), + _ => (), } self.current_item = (item_index, slide_index - 1); @@ -957,7 +984,7 @@ impl cosmic::Application for App { ) })); } - _ => todo!(), + _ => (), } } Task::batch(tasks) @@ -1013,7 +1040,14 @@ impl cosmic::Application for App { let image = lib_image.to_owned(); return self.update(Message::ImageEditor(image_editor::Message::ChangeImage(image))); }, - core::model::LibraryKind::Presentation => todo!(), + core::model::LibraryKind::Presentation => { + let Some(lib_presentation) = library.get_presentation(index) else { + return Task::none(); + }; + self.editor_mode = Some(kind.into()); + let presentation = lib_presentation.to_owned(); + return self.update(Message::PresentationEditor(presentation_editor::Message::ChangePresentation(presentation))); + }, } } library::Action::DraggedItem( @@ -1145,20 +1179,40 @@ impl cosmic::Application for App { } Task::none() } - Message::AddServiceItem(index, mut item) => { - item.slides = item - .slides - .into_par_iter() - .map(|mut slide| { - let fontdb = Arc::clone(&self.fontdb); - text_svg::text_svg_generator( - &mut slide, fontdb, - ); - slide - }) - .collect(); - self.service.insert(index, item); - self.presenter.update_items(self.service.clone()); + Message::AddServiceItem(index, item) => { + let item_index = item.0 .1; + let kind = item.0 .0; + match kind { + core::model::LibraryKind::Song => todo!(), + core::model::LibraryKind::Video => todo!(), + core::model::LibraryKind::Image => todo!(), + core::model::LibraryKind::Presentation => { + let Some(library) = self.library.as_mut() + else { + return Task::none(); + }; + let Some(presentation) = + library.get_presentation(item_index) + else { + return Task::none(); + }; + let mut item = presentation.to_service_item(); + item.slides = item + .slides + .into_par_iter() + .map(|mut slide| { + let fontdb = Arc::clone(&self.fontdb); + text_svg::text_svg_generator( + &mut slide, fontdb, + ); + slide + }) + .collect(); + self.service.insert(index, item); + self.presenter + .update_items(self.service.clone()); + } + } Task::none() } Message::RemoveServiceItem(index) => { @@ -1221,10 +1275,24 @@ impl cosmic::Application for App { song_editor::Message::ChangeSong(song), )) } - ServiceItemKind::Video(_video) => todo!(), - ServiceItemKind::Image(_image) => todo!(), - ServiceItemKind::Presentation(_presentation) => { - todo!() + ServiceItemKind::Video(video) => { + self.editor_mode = Some(EditorMode::Video); + self.update(Message::VideoEditor( + video_editor::Message::ChangeVideo(video), + )) + } + ServiceItemKind::Image(image) => { + self.editor_mode = Some(EditorMode::Image); + self.update(Message::ImageEditor( + image_editor::Message::ChangeImage(image), + )) + } + ServiceItemKind::Presentation(presentation) => { + self.editor_mode = + Some(EditorMode::Presentation); + self.update(Message::PresentationEditor( + presentation_editor::Message::ChangePresentation(presentation), + )) } ServiceItemKind::Content(_slide) => todo!(), } @@ -1371,7 +1439,10 @@ impl cosmic::Application for App { EditorMode::Video => { self.video_editor.view().map(Message::VideoEditor) } - EditorMode::Presentation => todo!(), + EditorMode::Presentation => self + .presentation_editor + .view() + .map(Message::PresentationEditor), EditorMode::Slide => todo!(), }, ); @@ -1692,7 +1763,7 @@ where ) .on_finish(move |mime, data, _, _, _| { let Ok(item) = - ServiceItem::try_from((data, mime)) + KindWrapper::try_from((data, mime)) else { error!("couldn't drag in Service item"); return Message::None; diff --git a/src/ui/library.rs b/src/ui/library.rs index 80c9fe7..a43196e 100644 --- a/src/ui/library.rs +++ b/src/ui/library.rs @@ -26,8 +26,11 @@ use tracing::{debug, error, warn}; use crate::core::{ content::Content, images::{self, update_image_in_db, Image}, - model::{LibraryKind, Model}, - presentations::{self, update_presentation_in_db, Presentation}, + model::{KindWrapper, LibraryKind, Model}, + presentations::{ + self, add_presentation_to_db, update_presentation_in_db, + Presentation, + }, service_items::ServiceItem, songs::{self, update_song_in_db, Song}, videos::{self, update_video_in_db, Video}, @@ -99,6 +102,7 @@ pub enum Message { None, AddImages(Option>), AddVideos(Option>), + AddPresentations(Option>), } impl<'a> Library { @@ -143,11 +147,10 @@ impl<'a> Library { let item = match kind { LibraryKind::Song => { let song = Song::default(); + let index = self.song_library.items.len(); self.song_library .add_item(song) .map(|_| { - let index = - self.song_library.items.len(); (LibraryKind::Song, index as i32) }) .ok() @@ -165,26 +168,17 @@ impl<'a> Library { )); } LibraryKind::Presentation => { - let presentation = Presentation::default(); - self.presentation_library - .add_item(presentation) - .map(|_| { - let index = self - .presentation_library - .items - .len(); - ( - LibraryKind::Presentation, - index as i32, - ) - }) - .ok() + return Action::Task(Task::perform( + add_presentations(), + Message::AddPresentations, + )); } }; return self.update(Message::OpenItem(item)); } Message::AddVideos(videos) => { debug!(?videos); + let mut index = self.video_library.items.len(); if let Some(videos) = videos { for video in videos { if let Err(e) = @@ -192,6 +186,7 @@ impl<'a> Library { { error!(?e); } + index += 1; } } return self.update(Message::OpenItem(Some(( @@ -199,8 +194,78 @@ impl<'a> Library { self.video_library.items.len() as i32 - 1, )))); } + Message::AddPresentations(presentations) => { + debug!(?presentations); + let mut index = self.presentation_library.items.len(); + // Check if empty + let mut tasks = Vec::new(); + if index == 0 { + if let Some(presentations) = presentations { + let len = presentations.len(); + for presentation in presentations { + if let Err(e) = self + .presentation_library + .add_item(presentation.clone()) + { + error!(?e); + } + let task = Task::future( + self.db.acquire(), + ) + .and_then(move |db| { + Task::perform( + add_presentation_to_db( + presentation.to_owned(), + db, + index as i32, + ), + move |res| { + debug!( + len, + index, "added to db" + ); + if let Err(e) = res { + error!(?e); + } + if len == index { + debug!("open the pres"); + Message::OpenItem(Some(( + LibraryKind::Presentation, + index as i32, + ))) + } else { + Message::None + } + }, + ) + }); + tasks.push(task); + index += 1; + } + } + return Action::Task(Task::batch(tasks)); + } else { + if let Some(presentations) = presentations { + for presentation in presentations { + if let Err(e) = self + .presentation_library + .add_item(presentation) + { + error!(?e); + } + index += 1; + } + } + return self.update(Message::OpenItem(Some(( + LibraryKind::Presentation, + self.presentation_library.items.len() as i32 + - 1, + )))); + } + } Message::AddImages(images) => { debug!(?images); + let mut index = self.image_library.items.len(); if let Some(images) = images { for image in images { if let Err(e) = @@ -208,6 +273,7 @@ impl<'a> Library { { error!(?e); } + index += 1; } } return self.update(Message::OpenItem(Some(( @@ -479,6 +545,21 @@ impl<'a> Library { Message::PresentationChanged => (), Message::Error(_) => (), Message::OpenContext(index) => { + let Some(kind) = self.library_open else { + return Action::None; + }; + let Some(items) = self.selected_items.as_mut() else { + self.selected_items = vec![(kind, index)].into(); + self.context_menu = Some(index); + return Action::None; + }; + + if items.contains(&(kind, index)) { + () + } else { + items.push((kind, index)); + } + self.selected_items = Some(items.to_vec()); self.context_menu = Some(index); } } @@ -597,13 +678,12 @@ impl<'a> Library { column({ model.items.iter().enumerate().map( |(index, item)| { - - let service_item = item.to_service_item(); + let kind = model.kind; let visual_item = self .single_item(index, item, model) .map(|()| Message::None); - DndSource::::new({ + DndSource::::new({ let mouse_area = mouse_area(visual_item); let mouse_area = mouse_area.on_enter(Message::HoverItem( Some(( @@ -669,7 +749,7 @@ impl<'a> Library { ) }}) .drag_content(move || { - service_item.clone() + KindWrapper((kind, index as i32)) }) .into() }, @@ -908,6 +988,13 @@ impl<'a> Library { self.image_library.get_item(index) } + pub fn get_presentation( + &self, + index: i32, + ) -> Option<&Presentation> { + self.presentation_library.get_item(index) + } + pub fn set_modifiers(&mut self, modifiers: Option) { self.modifiers_pressed = modifiers; } @@ -1083,6 +1170,23 @@ async fn add_videos() -> Option> { ) } +async fn add_presentations() -> Option> { + let paths = Dialog::new() + .title("pick presentation") + .open_files() + .await + .ok()?; + Some( + paths + .urls() + .iter() + .map(|path| { + Presentation::from(path.to_file_path().expect("oops")) + }) + .collect(), + ) +} + async fn add_db() -> Result { let mut data = dirs::data_local_dir().unwrap(); data.push("lumina"); diff --git a/src/ui/presentation_editor.rs b/src/ui/presentation_editor.rs index 7b181b0..b124665 100644 --- a/src/ui/presentation_editor.rs +++ b/src/ui/presentation_editor.rs @@ -6,12 +6,12 @@ use crate::core::{ }; use cosmic::{ dialog::file_chooser::{open::Dialog, FileFilter}, - iced::{alignment::Vertical, Length}, + iced::{alignment::Vertical, ContentFit, Length}, iced_widget::{column, row}, theme, widget::{ - button, container, horizontal_space, icon, text, text_input, - Space, + button, container, horizontal_space, icon, image, text, + text_input, Space, }, Element, Task, }; @@ -23,6 +23,7 @@ pub struct PresentationEditor { slides: Option>, title: String, editing: bool, + current_slide: i16, } pub enum Action { @@ -48,6 +49,7 @@ impl PresentationEditor { slides: None, title: "Death was Arrested".to_string(), editing: false, + current_slide: 0, } } pub fn update(&mut self, message: Message) -> Action { @@ -55,10 +57,12 @@ impl PresentationEditor { Message::ChangePresentation(presentation) => { self.presentation = Some(presentation.clone()); self.title = presentation.title.clone(); + warn!("changing presentation"); let Ok(slides) = presentation.to_slides() else { return Action::None; }; self.slides = Some(slides); + self.current_slide = 0; return self.update(Message::Update(presentation)); } Message::ChangeTitle(title) => { @@ -107,8 +111,18 @@ impl PresentationEditor { pub fn view(&self) -> Element { let container = if let Some(slides) = &self.slides { - todo!(); - // container(presentation) + if let Some(slide) = + slides.get(self.current_slide as usize) + { + container( + image(slide.pdf_page().unwrap_or( + image::Handle::from_path("res/chad.png"), + )) + .content_fit(ContentFit::Cover), + ) + } else { + container(Space::new(0, 0)) + } } else { container(Space::new(0, 0)) };