Compare commits
2 commits
5e3ab8a22b
...
ee45a11a0f
| Author | SHA1 | Date | |
|---|---|---|---|
| ee45a11a0f | |||
| a3516ef70c |
20 changed files with 405 additions and 156 deletions
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
use std::mem::replace;
|
||||
use std::{borrow::Cow, mem::replace};
|
||||
|
||||
use miette::{Result, miette};
|
||||
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<T> {
|
||||
|
|
@ -9,7 +12,9 @@ pub struct Model<T> {
|
|||
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<u8>, String)> for KindWrapper {
|
||||
type Error = miette::Error;
|
||||
|
||||
fn try_from(
|
||||
value: (Vec<u8>, String),
|
||||
) -> std::result::Result<Self, Self::Error> {
|
||||
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<std::borrow::Cow<'static, [u8]>> {
|
||||
debug!(?self);
|
||||
debug!(mime_type);
|
||||
let ron = ron::ser::to_string(self).ok()?;
|
||||
debug!(ron);
|
||||
Some(Cow::from(ron.into_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Model<T> {
|
||||
pub fn add_item(&mut self, item: T) -> Result<()> {
|
||||
self.items.push(item);
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
@ -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<Sqlite>,
|
||||
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<Sqlite>,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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::*;
|
||||
|
|
|
|||
184
src/main.rs
184
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,13 +42,16 @@ 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::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<ServiceItem>,
|
||||
|
|
@ -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<window::Id>),
|
||||
|
|
@ -160,7 +165,7 @@ enum Message {
|
|||
SelectServiceItem(usize),
|
||||
AddSelectServiceItem(usize),
|
||||
HoveredServiceItem(Option<usize>),
|
||||
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!(),
|
||||
},
|
||||
);
|
||||
|
|
@ -1690,22 +1761,14 @@ where
|
|||
tooltip,
|
||||
vec!["application/service-item".into()],
|
||||
)
|
||||
.data_received_for::<ServiceItem>(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))
|
||||
KindWrapper::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 +1826,15 @@ where
|
|||
column,
|
||||
vec!["application/service-item".into()],
|
||||
)
|
||||
.data_received_for::<ServiceItem>(|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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,35 +1,39 @@
|
|||
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},
|
||||
model::{LibraryKind, Model},
|
||||
presentations::{self, Presentation, update_presentation_in_db},
|
||||
images::{self, update_image_in_db, Image},
|
||||
model::{KindWrapper, LibraryKind, Model},
|
||||
presentations::{
|
||||
self, add_presentation_to_db, 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)]
|
||||
|
|
@ -98,6 +102,7 @@ pub enum Message {
|
|||
None,
|
||||
AddImages(Option<Vec<Image>>),
|
||||
AddVideos(Option<Vec<Video>>),
|
||||
AddPresentations(Option<Vec<Presentation>>),
|
||||
}
|
||||
|
||||
impl<'a> Library {
|
||||
|
|
@ -142,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()
|
||||
|
|
@ -164,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) =
|
||||
|
|
@ -191,6 +186,7 @@ impl<'a> Library {
|
|||
{
|
||||
error!(?e);
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
return self.update(Message::OpenItem(Some((
|
||||
|
|
@ -198,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) =
|
||||
|
|
@ -207,6 +273,7 @@ impl<'a> Library {
|
|||
{
|
||||
error!(?e);
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
return self.update(Message::OpenItem(Some((
|
||||
|
|
@ -478,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);
|
||||
}
|
||||
}
|
||||
|
|
@ -596,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::<Message, ServiceItem>::new({
|
||||
DndSource::<Message, KindWrapper>::new({
|
||||
let mouse_area = mouse_area(visual_item);
|
||||
let mouse_area = mouse_area.on_enter(Message::HoverItem(
|
||||
Some((
|
||||
|
|
@ -668,7 +749,7 @@ impl<'a> Library {
|
|||
)
|
||||
}})
|
||||
.drag_content(move || {
|
||||
service_item.clone()
|
||||
KindWrapper((kind, index as i32))
|
||||
})
|
||||
.into()
|
||||
},
|
||||
|
|
@ -907,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<Modifiers>) {
|
||||
self.modifiers_pressed = modifiers;
|
||||
}
|
||||
|
|
@ -1082,6 +1170,23 @@ async fn add_videos() -> Option<Vec<Video>> {
|
|||
)
|
||||
}
|
||||
|
||||
async fn add_presentations() -> Option<Vec<Presentation>> {
|
||||
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<SqlitePool> {
|
||||
let mut data = dirs::data_local_dir().unwrap();
|
||||
data.push("lumina");
|
||||
|
|
|
|||
|
|
@ -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, ContentFit, Length},
|
||||
iced_widget::{column, row},
|
||||
theme,
|
||||
widget::{
|
||||
Space, button, container, horizontal_space, icon, text,
|
||||
text_input,
|
||||
button, container, horizontal_space, icon, image, text,
|
||||
text_input, Space,
|
||||
},
|
||||
Element, Task,
|
||||
};
|
||||
use tracing::{debug, error, warn};
|
||||
|
||||
|
|
@ -23,6 +23,7 @@ pub struct PresentationEditor {
|
|||
slides: Option<Vec<Slide>>,
|
||||
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<Message> {
|
||||
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))
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue