This commit is contained in:
parent
42800c66ca
commit
61bba082aa
3 changed files with 127 additions and 75 deletions
|
|
@ -2,7 +2,7 @@ use std::{
|
|||
borrow::Cow, collections::HashMap, option::Option, path::PathBuf,
|
||||
};
|
||||
|
||||
use cosmic::iced::clipboard::mime::AsMimeTypes;
|
||||
use cosmic::iced::{Color, clipboard::mime::AsMimeTypes};
|
||||
use crisp::types::{Keyword, Symbol, Value};
|
||||
use miette::{IntoDiagnostic, Result, miette};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
@ -38,6 +38,10 @@ pub struct Song {
|
|||
pub font: Option<String>,
|
||||
pub font_size: Option<i32>,
|
||||
pub stroke_size: Option<i32>,
|
||||
pub stroke_color: Option<String>,
|
||||
pub shadow_size: Option<i32>,
|
||||
pub shadow_offset: Option<(i32, i32)>,
|
||||
pub shadow_color: Option<String>,
|
||||
pub verses: Option<Vec<VerseName>>,
|
||||
pub verse_map: Option<HashMap<VerseName, String>>,
|
||||
}
|
||||
|
|
@ -342,6 +346,7 @@ impl FromRow<'_, SqliteRow> for Song {
|
|||
stroke_size: None,
|
||||
verses,
|
||||
verse_map,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1133,6 +1138,7 @@ You saved my soul"
|
|||
stroke_size: None,
|
||||
verses: Some(vec![]),
|
||||
verse_map: None,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ impl Presenter {
|
|||
gst::init().into_diagnostic()?;
|
||||
|
||||
let pipeline = format!(
|
||||
r#"playbin uri="{}" video-sink="videoscale ! videoconvert ! appsink name=lumina_video drop=true caps=video/x-raw,format=NV12,pixel-aspect-ratio=1/1""#,
|
||||
r#"playbin uri="{}" video-sink="videoscale ! videoconvert ! videoflip method=automatic ! appsink name=lumina_video drop=true caps=video/x-raw,format=NV12,pixel-aspect-ratio=1/1""#,
|
||||
url.as_str()
|
||||
);
|
||||
|
||||
|
|
@ -1050,100 +1050,84 @@ pub(crate) fn slide_view<'a>(
|
|||
delegate: bool,
|
||||
hide_mouse: bool,
|
||||
) -> Element<'a, Message> {
|
||||
let res = responsive(move |size| {
|
||||
responsive(move |size| {
|
||||
let width = size.height * 16.0 / 9.0;
|
||||
|
||||
let text: Element<Message> =
|
||||
if let Some(text) = &slide.text_svg {
|
||||
if let Some(handle) = &text.handle {
|
||||
image(handle)
|
||||
.content_fit(ContentFit::ScaleDown)
|
||||
.width(Length::Shrink)
|
||||
.height(Length::Shrink)
|
||||
.into()
|
||||
} else {
|
||||
Space::with_width(0).into()
|
||||
}
|
||||
} else {
|
||||
Space::with_width(0).into()
|
||||
};
|
||||
let black = Container::new(Space::new(0, 0))
|
||||
.style(|_| {
|
||||
container::background(Background::Color(Color::BLACK))
|
||||
})
|
||||
.clip(true)
|
||||
.width(width)
|
||||
.height(size.height);
|
||||
let background = match slide.background().kind {
|
||||
.height(Length::Fill);
|
||||
let mut stack = stack(vec![black.into()]);
|
||||
|
||||
match slide.background().kind {
|
||||
BackgroundKind::Image => {
|
||||
let path = slide.background().path.clone();
|
||||
Container::new(
|
||||
image(path)
|
||||
.content_fit(ContentFit::Cover)
|
||||
.width(width)
|
||||
.height(size.height),
|
||||
)
|
||||
.center(Length::Shrink)
|
||||
.clip(true)
|
||||
}
|
||||
BackgroundKind::Video => {
|
||||
if delegate {
|
||||
Container::new(Space::new(0, 0))
|
||||
.style(|_| {
|
||||
container::background(Background::Color(
|
||||
Color::BLACK,
|
||||
))
|
||||
})
|
||||
.center(Length::Fill)
|
||||
.clip(true)
|
||||
.width(width)
|
||||
.height(size.height)
|
||||
} else if let Some(video) = &video {
|
||||
stack = stack.push(
|
||||
Container::new(
|
||||
VideoPlayer::new(video)
|
||||
.mouse_hidden(hide_mouse)
|
||||
image(path)
|
||||
.content_fit(ContentFit::Contain)
|
||||
.width(width)
|
||||
.height(size.height)
|
||||
.on_end_of_stream(Message::EndVideo)
|
||||
.on_new_frame(Message::VideoFrame)
|
||||
.on_missing_plugin(Message::MissingPlugin)
|
||||
.on_warning(|w| {
|
||||
Message::Error(w.to_string())
|
||||
})
|
||||
.on_error(|e| {
|
||||
Message::Error(e.to_string())
|
||||
})
|
||||
.content_fit(ContentFit::Cover),
|
||||
.height(Length::Fill),
|
||||
)
|
||||
.center(Length::Fill)
|
||||
.clip(true)
|
||||
// Container::new(Space::new(0, 0))
|
||||
} else {
|
||||
Container::new(Space::new(0, 0))
|
||||
.clip(true),
|
||||
);
|
||||
}
|
||||
BackgroundKind::Video => {
|
||||
if let Some(video) = &video
|
||||
&& !delegate
|
||||
{
|
||||
stack = stack.push(
|
||||
Container::new(
|
||||
VideoPlayer::new(video)
|
||||
.mouse_hidden(hide_mouse)
|
||||
.width(width)
|
||||
.height(Length::Fill)
|
||||
.on_end_of_stream(Message::EndVideo)
|
||||
.on_new_frame(Message::VideoFrame)
|
||||
.on_missing_plugin(
|
||||
Message::MissingPlugin,
|
||||
)
|
||||
.on_warning(|w| {
|
||||
Message::Error(w.to_string())
|
||||
})
|
||||
.on_error(|e| {
|
||||
Message::Error(e.to_string())
|
||||
})
|
||||
.content_fit(ContentFit::Contain),
|
||||
)
|
||||
.center(Length::Fill)
|
||||
.clip(true),
|
||||
);
|
||||
}
|
||||
}
|
||||
BackgroundKind::Pdf => {
|
||||
if let Some(pdf) = slide.pdf_page() {
|
||||
Container::new(
|
||||
image(pdf).content_fit(ContentFit::Contain),
|
||||
)
|
||||
.center(Length::Fill)
|
||||
.clip(true)
|
||||
} else {
|
||||
Container::new(Space::new(0.0, 0.0))
|
||||
stack = stack.push(
|
||||
Container::new(
|
||||
image(pdf)
|
||||
.content_fit(ContentFit::Contain),
|
||||
)
|
||||
.center(Length::Fill)
|
||||
.clip(true)
|
||||
.clip(true),
|
||||
);
|
||||
}
|
||||
}
|
||||
BackgroundKind::Html => todo!(),
|
||||
};
|
||||
let stack = stack!(
|
||||
black,
|
||||
background.center_x(Length::Fill),
|
||||
container(text).center(Length::Fill)
|
||||
);
|
||||
if let Some(text) = &slide.text_svg {
|
||||
if let Some(handle) = &text.handle {
|
||||
stack = stack.push(
|
||||
image(handle)
|
||||
.content_fit(ContentFit::ScaleDown)
|
||||
.width(Length::Shrink)
|
||||
.height(Length::Shrink),
|
||||
);
|
||||
}
|
||||
};
|
||||
Container::new(stack).center(Length::Fill).into()
|
||||
});
|
||||
|
||||
res.into()
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ pub enum Message {
|
|||
ChangeLyrics(text_editor::Action),
|
||||
ChangeBackground(Result<PathBuf, SongError>),
|
||||
UpdateSlides(Vec<Slide>),
|
||||
UpdateSlide((usize, Slide)),
|
||||
PickBackground,
|
||||
Edit(bool),
|
||||
None,
|
||||
|
|
@ -240,7 +241,28 @@ impl SongEditor {
|
|||
}
|
||||
self.background_video(&song.background);
|
||||
self.background = song.background.clone();
|
||||
self.song_slides = None;
|
||||
let font_db = Arc::clone(&self.font_db);
|
||||
|
||||
// let slides = song_slides.ok();
|
||||
// let mut task = Task::none();
|
||||
// if let Some(slides) = slides {
|
||||
// for (index, mut slide) in
|
||||
// slides.into_iter().enumerate()
|
||||
// {
|
||||
// let font_db = Arc::clone(&font_db);
|
||||
// task = task.chain(Task::perform(
|
||||
// async move {
|
||||
// text_svg::text_svg_generator(
|
||||
// &mut slide, font_db,
|
||||
// );
|
||||
// (index, slide)
|
||||
// },
|
||||
// Message::UpdateSlide,
|
||||
// ));
|
||||
// }
|
||||
// }
|
||||
|
||||
let task = Task::perform(
|
||||
async move {
|
||||
song_slides
|
||||
|
|
@ -260,6 +282,7 @@ impl SongEditor {
|
|||
},
|
||||
Message::UpdateSlides,
|
||||
);
|
||||
|
||||
self.verses = song.verse_map.map(|map| {
|
||||
map.into_iter()
|
||||
.sorted()
|
||||
|
|
@ -368,6 +391,18 @@ impl SongEditor {
|
|||
Message::UpdateSlides(slides) => {
|
||||
self.song_slides = Some(slides);
|
||||
}
|
||||
Message::UpdateSlide((index, slide)) => {
|
||||
if let Some(slides) = self.song_slides.as_mut() {
|
||||
if let Some(_old) = slides.get(index) {
|
||||
let _ = slides.remove(index);
|
||||
slides.insert(index, slide);
|
||||
} else {
|
||||
slides.push(slide);
|
||||
}
|
||||
} else {
|
||||
self.song_slides = Some(vec![slide]);
|
||||
}
|
||||
}
|
||||
Message::UpdateSong(song) => {
|
||||
self.song = Some(song.clone());
|
||||
return Action::UpdateSong(song);
|
||||
|
|
@ -1009,6 +1044,32 @@ impl SongEditor {
|
|||
let font_db = Arc::clone(&self.font_db);
|
||||
let update_task =
|
||||
Task::done(Message::UpdateSong(song.clone()));
|
||||
|
||||
// need to test to see which of these methods yields faster
|
||||
// text_svg slide creation. There is a small thought in me that
|
||||
// believes it's better for the user to see the slides being added
|
||||
// one by one, rather than all at once, but that isn't how
|
||||
// the task appears to happen.
|
||||
|
||||
// let slides = song.to_slides().ok();
|
||||
// let mut task = vec![];
|
||||
// if let Some(slides) = slides {
|
||||
// for (index, mut slide) in slides.into_iter().enumerate() {
|
||||
// let font_db = Arc::clone(&font_db);
|
||||
// task.push(Task::perform(
|
||||
// async move {
|
||||
// text_svg::text_svg_generator(
|
||||
// &mut slide, font_db,
|
||||
// );
|
||||
// (index, slide)
|
||||
// },
|
||||
// Message::UpdateSlide,
|
||||
// ));
|
||||
// }
|
||||
// }
|
||||
|
||||
// I think this implementation is faster
|
||||
|
||||
let task = Task::perform(
|
||||
async move {
|
||||
song.to_slides()
|
||||
|
|
@ -1028,6 +1089,7 @@ impl SongEditor {
|
|||
},
|
||||
Message::UpdateSlides,
|
||||
);
|
||||
|
||||
Action::Task(task.chain(update_task))
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue