From fade3336347925b2e877cc16fc5f8f2925678044 Mon Sep 17 00:00:00 2001 From: Chris Cochrun Date: Sat, 16 Aug 2025 14:37:49 -0500 Subject: [PATCH] switching to a better layout for items and slides --- src/core/service_items.rs | 101 +++++++++++++++++------- src/main.rs | 159 +++++++++++++++++++++++--------------- src/ui/presenter.rs | 92 +++++++++++----------- test_presentation.lisp | 4 +- 4 files changed, 221 insertions(+), 135 deletions(-) diff --git a/src/core/service_items.rs b/src/core/service_items.rs index b08cc23..1d24cee 100644 --- a/src/core/service_items.rs +++ b/src/core/service_items.rs @@ -22,6 +22,7 @@ pub struct ServiceItem { pub title: String, pub database_id: i32, pub kind: ServiceItemKind, + pub slides: Vec, // pub item: Box, } @@ -120,6 +121,7 @@ impl Default for ServiceItem { title: String::default(), database_id: 0, kind: ServiceItemKind::Content(Slide::default()), + slides: vec![], // item: Box::new(Image::default()), } } @@ -166,7 +168,10 @@ impl From<&Value> for ServiceItem { id: 0, title, database_id: 0, - kind: ServiceItemKind::Content(slide), + kind: ServiceItemKind::Content( + slide.clone(), + ), + slides: vec![slide], } } else if let Some(background) = list.get(background_pos) @@ -224,11 +229,11 @@ impl From<&Value> for ServiceItem { } #[derive(Debug, Default, Clone)] -pub struct ServiceItemModel { +pub struct Service { items: Vec, } -impl Deref for ServiceItemModel { +impl Deref for Service { type Target = Vec; fn deref(&self) -> &Self::Target { @@ -244,7 +249,7 @@ impl Deref for ServiceItemModel { // } // } -impl From> for ServiceItemModel { +impl From> for Service { fn from(items: Vec) -> Self { Self { items } } @@ -252,49 +257,93 @@ impl From> for ServiceItemModel { impl From<&Song> for ServiceItem { fn from(song: &Song) -> Self { - Self { - kind: ServiceItemKind::Song(song.clone()), - database_id: song.id, - title: song.title.clone(), - ..Default::default() + if let Ok(slides) = song.to_slides() { + Self { + kind: ServiceItemKind::Song(song.clone()), + database_id: song.id, + title: song.title.clone(), + slides, + ..Default::default() + } + } else { + Self { + kind: ServiceItemKind::Song(song.clone()), + database_id: song.id, + title: song.title.clone(), + ..Default::default() + } } } } impl From<&Video> for ServiceItem { fn from(video: &Video) -> Self { - Self { - kind: ServiceItemKind::Video(video.clone()), - database_id: video.id, - title: video.title.clone(), - ..Default::default() + if let Ok(slides) = video.to_slides() { + Self { + kind: ServiceItemKind::Video(video.clone()), + database_id: video.id, + title: video.title.clone(), + slides, + ..Default::default() + } + } else { + Self { + kind: ServiceItemKind::Video(video.clone()), + database_id: video.id, + title: video.title.clone(), + ..Default::default() + } } } } impl From<&Image> for ServiceItem { fn from(image: &Image) -> Self { - Self { - kind: ServiceItemKind::Image(image.clone()), - database_id: image.id, - title: image.title.clone(), - ..Default::default() + if let Ok(slides) = image.to_slides() { + Self { + kind: ServiceItemKind::Image(image.clone()), + database_id: image.id, + title: image.title.clone(), + slides, + ..Default::default() + } + } else { + Self { + kind: ServiceItemKind::Image(image.clone()), + database_id: image.id, + title: image.title.clone(), + ..Default::default() + } } } } impl From<&Presentation> for ServiceItem { fn from(presentation: &Presentation) -> Self { - Self { - kind: ServiceItemKind::Presentation(presentation.clone()), - database_id: presentation.id, - title: presentation.title.clone(), - ..Default::default() + if let Ok(slides) = presentation.to_slides() { + Self { + kind: ServiceItemKind::Presentation( + presentation.clone(), + ), + database_id: presentation.id, + title: presentation.title.clone(), + slides, + ..Default::default() + } + } else { + Self { + kind: ServiceItemKind::Presentation( + presentation.clone(), + ), + database_id: presentation.id, + title: presentation.title.clone(), + ..Default::default() + } } } } -impl ServiceItemModel { +impl Service { fn add_item( &mut self, item: impl Into, @@ -379,7 +428,7 @@ mod test { let service_item = ServiceItem::from(&song); let pres = test_presentation(); let pres_item = ServiceItem::from(&pres); - let mut service_model = ServiceItemModel::default(); + let mut service_model = Service::default(); match service_model.add_item(&song) { Ok(_) => { assert_eq!( diff --git a/src/main.rs b/src/main.rs index 2b8d532..dd53574 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ use clap::{command, Parser}; -use core::service_items::{ServiceItem, ServiceItemModel}; +use core::service_items::{Service, ServiceItem}; use core::slide::*; use core::songs::Song; use cosmic::app::context_drawer::ContextDrawer; @@ -102,9 +102,7 @@ struct App { file: PathBuf, presenter: Presenter, windows: Vec, - service: BTreeMap>, - slides: Vec, - current_slide: Slide, + service: Vec, presentation_open: bool, cli_mode: bool, library: Option, @@ -136,7 +134,8 @@ enum Message { EditorToggle(bool), SearchFocus, ChangeServiceItem(usize), - AddServiceItem(Option), + AddServiceItem(usize, ServiceItem), + AppendServiceItem(ServiceItem), } const HEADER_SPACE: u16 = 6; @@ -191,27 +190,22 @@ impl cosmic::Application for App { } }; - let items = ServiceItemModel::from(items); let presenter = Presenter::with_items(items.clone()); - let slides = items.to_slides().unwrap_or_default(); - let current_slide = slides[0].clone(); let song_editor = SongEditor::new(); - for item in items.iter() { - nav_model.insert().text(item.title()).data(item.clone()); - } + // for item in items.iter() { + // nav_model.insert().text(item.title()).data(item.clone()); + // } - nav_model.activate_position(0); + // nav_model.activate_position(0); let mut app = App { presenter, core, nav_model, - service: BTreeMap::new(), + service: items, file: PathBuf::default(), windows, - slides, - current_slide, presentation_open: false, cli_mode: !input.ui, library: None, @@ -233,6 +227,7 @@ impl cosmic::Application for App { }; batch.push(app.add_library()); + // batch.push(app.add_service(items)); let batch = Task::batch(batch); (app, batch) } @@ -282,28 +277,37 @@ impl cosmic::Application for App { .service .iter() .enumerate() - .map(|(index, (item, slides))| { + .map(|(index, item)| { dnd_destination(tooltip( button::standard(item.title.clone()) - .leading_icon({ - match item.kind { - core::kinds::ServiceItemKind::Song(_) => { - icon::from_name("folder-music-symbolic") - }, - core::kinds::ServiceItemKind::Video(_) => { - icon::from_name("folder-videos-symbolic") - }, - core::kinds::ServiceItemKind::Image(_) => { - icon::from_name("folder-pictures-symbolic") - }, - core::kinds::ServiceItemKind::Presentation(_) => { - icon::from_name("x-office-presentation-symbolic") - }, - core::kinds::ServiceItemKind::Content(_) => todo!(), - } - }) - .class(cosmic::theme::style::Button::HeaderBar) - .on_press(cosmic::Action::App(Message::ChangeServiceItem(index))), text::body(item.kind.to_string()), TPosition::Right), vec!["application/service-item".into()]).data_received_for::(|item| { - cosmic::Action::App(Message::AddServiceItem(item)) + .leading_icon({ + match item.kind { + core::kinds::ServiceItemKind::Song(_) => { + icon::from_name("folder-music-symbolic") + }, + core::kinds::ServiceItemKind::Video(_) => { + icon::from_name("folder-videos-symbolic") + }, + core::kinds::ServiceItemKind::Image(_) => { + icon::from_name("folder-pictures-symbolic") + }, + core::kinds::ServiceItemKind::Presentation(_) => { + icon::from_name("x-office-presentation-symbolic") + }, + core::kinds::ServiceItemKind::Content(_) => { + icon::from_name("x-office-presentation-symbolic") + }, + } + }) + .class(cosmic::theme::style::Button::HeaderBar) + .padding(5) + .width(Length::Fill) + .on_press(cosmic::Action::App(Message::ChangeServiceItem(index))), + text::body(item.kind.to_string()), TPosition::Right), vec!["application/service-item".into()]).data_received_for::( move |item| { + if let Some(item) = item { + cosmic::Action::App(Message::AddServiceItem(index, item)) + } else { + cosmic::Action::None + } }) .into() }); @@ -311,18 +315,23 @@ impl cosmic::Application for App { let column = column![ text::heading("Service List").center().width(280), column(list).spacing(10), - text::heading("Service List").center().width(280), dnd_destination_for_data::< ServiceItem, cosmic::Action, >( Container::new(vertical_space()), |item, _| { - debug!("helloooooo"); - cosmic::Action::App(Message::AddServiceItem(item)) + if let Some(item) = item { + cosmic::Action::App( + Message::AppendServiceItem(item), + ) + } else { + cosmic::Action::None + } } ) ] + .padding(10) .spacing(10); let padding = Padding::new(0.0).top(20); let mut container = Container::new(column) @@ -655,16 +664,16 @@ impl cosmic::Application for App { Message::WindowOpened(id, _) => { debug!(?id, "Window opened"); if self.cli_mode - || id > self.core.main_window_id().expect("Cosmic core seems to be missing a main window, was this started in cli mode?") - { - self.presentation_open = true; - if let Some(video) = &mut self.presenter.video { - video.set_muted(false); - } - window::change_mode(id, Mode::Fullscreen) - } else { - Task::none() - } + || id > self.core.main_window_id().expect("Cosmic core seems to be missing a main window, was this started in cli mode?") + { + self.presentation_open = true; + if let Some(video) = &mut self.presenter.video { + video.set_muted(false); + } + window::change_mode(id, Mode::Fullscreen) + } else { + Task::none() + } } Message::WindowClosed(id) => { warn!("Closing window: {id}"); @@ -741,22 +750,23 @@ impl cosmic::Application for App { Task::none() } Message::ChangeServiceItem(index) => { - self.presenter.update( - presenter::Message::SlideChange(index as u16), - ); + if let Some(item) = self.service.get(index) { + if let Some(slide) = item.slides.first() { + self.presenter.update( + presenter::Message::SlideChange( + slide.clone(), + ), + ); + } + } Task::none() } - Message::AddServiceItem(item) => { - if let Some(item) = item { - let slides = match item.to_slides() { - Ok(s) => s, - Err(e) => { - error!("{e}"); - return Task::none(); - } - }; - self.service.insert(item, slides); - }; + Message::AddServiceItem(index, item) => { + self.service.insert(index, item); + Task::none() + } + Message::AppendServiceItem(item) => { + self.service.push(item); Task::none() } } @@ -939,6 +949,29 @@ where }) } + // fn add_service( + // &mut self, + // items: Vec, + // ) -> Task { + // Task::perform( + // async move { + // for item in items { + // debug!(?item, "Item to be appended"); + // let slides = item.to_slides().unwrap_or(vec![]); + // map.insert(item, slides); + // } + // let len = map.len(); + // debug!(len, "to be append: "); + // map + // }, + // |x| { + // let len = x.len(); + // debug!(len, "to append: "); + // cosmic::Action::App(Message::AppendService(x)) + // }, + // ) + // } + fn process_key_press( &mut self, key: Key, diff --git a/src/ui/presenter.rs b/src/ui/presenter.rs index d09be14..7780ed8 100644 --- a/src/ui/presenter.rs +++ b/src/ui/presenter.rs @@ -31,7 +31,10 @@ use tracing::{debug, error, info, warn}; use url::Url; use crate::{ - core::{service_items::ServiceItemModel, slide::Slide}, + core::{ + service_items::{Service, ServiceItem}, + slide::Slide, + }, ui::text_svg::{self, Font as SvgFont}, // ui::widgets::slide_text, BackgroundKind, @@ -43,8 +46,9 @@ const REFERENCE_HEIGHT: f32 = 1080.0; // #[derive(Default, Clone, Debug)] pub(crate) struct Presenter { pub slides: Vec, - pub items: ServiceItemModel, + pub service: Vec, pub current_slide: Slide, + pub current_item: usize, pub current_slide_index: u16, pub video: Option