This commit is contained in:
parent
1446e35c58
commit
4ccb186189
6 changed files with 112 additions and 75 deletions
|
@ -1,7 +1,7 @@
|
|||
use std::borrow::Cow;
|
||||
use std::cmp::Ordering;
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use cosmic::iced::clipboard::mime::{AllowedMimeTypes, AsMimeTypes};
|
||||
use crisp::types::{Keyword, Symbol, Value};
|
||||
|
@ -24,7 +24,7 @@ pub struct ServiceItem {
|
|||
pub title: String,
|
||||
pub database_id: i32,
|
||||
pub kind: ServiceItemKind,
|
||||
pub slides: Arc<[Slide]>,
|
||||
pub slides: Vec<Slide>,
|
||||
// pub item: Box<dyn ServiceTrait>,
|
||||
}
|
||||
|
||||
|
@ -122,7 +122,7 @@ impl Default for ServiceItem {
|
|||
title: String::default(),
|
||||
database_id: 0,
|
||||
kind: ServiceItemKind::Content(Slide::default()),
|
||||
slides: Arc::new([]),
|
||||
slides: vec![],
|
||||
// item: Box::new(Image::default()),
|
||||
}
|
||||
}
|
||||
|
@ -172,7 +172,7 @@ impl From<&Value> for ServiceItem {
|
|||
kind: ServiceItemKind::Content(
|
||||
slide.clone(),
|
||||
),
|
||||
slides: Arc::new([slide]),
|
||||
slides: vec![slide],
|
||||
}
|
||||
} else if let Some(background) =
|
||||
list.get(background_pos)
|
||||
|
|
|
@ -30,7 +30,7 @@ pub struct Slide {
|
|||
video_start_time: f32,
|
||||
video_end_time: f32,
|
||||
#[serde(skip)]
|
||||
pub text_svg: TextSvg,
|
||||
pub text_svg: Option<TextSvg>,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
|
@ -261,6 +261,11 @@ impl Slide {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn with_text_svg(mut self, text_svg: TextSvg) -> Self {
|
||||
self.text_svg = Some(text_svg);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_font(mut self, font: impl AsRef<str>) -> Self {
|
||||
self.font = font.as_ref().into();
|
||||
self
|
||||
|
@ -284,6 +289,10 @@ impl Slide {
|
|||
self.text.clone()
|
||||
}
|
||||
|
||||
pub fn text_alignment(&self) -> TextAlignment {
|
||||
self.text_alignment.clone()
|
||||
}
|
||||
|
||||
pub fn font_size(&self) -> i32 {
|
||||
self.font_size
|
||||
}
|
||||
|
@ -623,45 +632,19 @@ impl SlideBuilder {
|
|||
let Some(video_end_time) = self.video_end_time else {
|
||||
return Err(miette!("No video_end_time"));
|
||||
};
|
||||
if let Some(text_svg) = self.text_svg {
|
||||
Ok(Slide {
|
||||
background,
|
||||
text,
|
||||
font,
|
||||
font_size,
|
||||
text_alignment,
|
||||
audio: self.audio,
|
||||
video_loop,
|
||||
video_start_time,
|
||||
video_end_time,
|
||||
text_svg,
|
||||
..Default::default()
|
||||
})
|
||||
} else {
|
||||
let text_svg = TextSvg::new(text.clone())
|
||||
.alignment(text_alignment)
|
||||
.fill("#fff")
|
||||
.shadow(text_svg::shadow(2, 2, 5, "#000000"))
|
||||
.stroke(text_svg::stroke(3, "#000"))
|
||||
.font(
|
||||
text_svg::Font::from(font.clone())
|
||||
.size(font_size.try_into().unwrap()),
|
||||
)
|
||||
.build();
|
||||
Ok(Slide {
|
||||
background,
|
||||
text,
|
||||
font,
|
||||
font_size,
|
||||
text_alignment,
|
||||
audio: self.audio,
|
||||
video_loop,
|
||||
video_start_time,
|
||||
video_end_time,
|
||||
text_svg,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
Ok(Slide {
|
||||
background,
|
||||
text,
|
||||
font,
|
||||
font_size,
|
||||
text_alignment,
|
||||
audio: self.audio,
|
||||
video_loop,
|
||||
video_start_time,
|
||||
video_end_time,
|
||||
text_svg: self.text_svg,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
48
src/main.rs
48
src/main.rs
|
@ -28,8 +28,10 @@ use crisp::types::Value;
|
|||
use lisp::parse_lisp;
|
||||
use miette::{miette, Result};
|
||||
use rayon::prelude::*;
|
||||
use resvg::usvg::fontdb;
|
||||
use std::fs::read_to_string;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tracing::{debug, level_filters::LevelFilter};
|
||||
use tracing::{error, warn};
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
@ -38,6 +40,8 @@ use ui::presenter::{self, Presenter};
|
|||
use ui::song_editor::{self, SongEditor};
|
||||
use ui::EditorMode;
|
||||
|
||||
use crate::ui::text_svg;
|
||||
|
||||
pub mod core;
|
||||
pub mod lisp;
|
||||
pub mod ui;
|
||||
|
@ -111,6 +115,7 @@ struct App {
|
|||
song_editor: SongEditor,
|
||||
searching: bool,
|
||||
library_dragged_item: Option<ServiceItem>,
|
||||
fontdb: Arc<fontdb::Database>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -159,15 +164,19 @@ impl cosmic::Application for App {
|
|||
debug!("init");
|
||||
let nav_model = nav_bar::Model::default();
|
||||
|
||||
let mut fontdb = fontdb::Database::new();
|
||||
fontdb.load_system_fonts();
|
||||
let fontdb = Arc::new(fontdb);
|
||||
|
||||
let mut windows = vec![];
|
||||
|
||||
if input.ui {
|
||||
windows.push(core.main_window_id().unwrap());
|
||||
}
|
||||
|
||||
let items = match read_to_string(input.file) {
|
||||
let mut items = match read_to_string(input.file) {
|
||||
Ok(lisp) => {
|
||||
let mut slide_vector = vec![];
|
||||
let mut service_items = vec![];
|
||||
let lisp = crisp::reader::read(&lisp);
|
||||
match lisp {
|
||||
Value::List(vec) => {
|
||||
|
@ -178,12 +187,12 @@ impl cosmic::Application for App {
|
|||
// slide_vector.append(items);
|
||||
for value in vec {
|
||||
let mut inner_vector = parse_lisp(value);
|
||||
slide_vector.append(&mut inner_vector);
|
||||
service_items.append(&mut inner_vector);
|
||||
}
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
slide_vector
|
||||
service_items
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Missing file or could not read: {e}");
|
||||
|
@ -191,15 +200,32 @@ impl cosmic::Application for App {
|
|||
}
|
||||
};
|
||||
|
||||
let items: Vec<ServiceItem> = items
|
||||
.into_par_iter()
|
||||
.map(|mut item| {
|
||||
item.slides = item
|
||||
.slides
|
||||
.into_par_iter()
|
||||
.map(|mut slide| {
|
||||
text_svg::text_svg_generator(
|
||||
&mut slide,
|
||||
Arc::clone(&fontdb),
|
||||
);
|
||||
slide
|
||||
})
|
||||
.collect();
|
||||
item
|
||||
})
|
||||
.collect();
|
||||
|
||||
let presenter = Presenter::with_items(items.clone());
|
||||
let song_editor = SongEditor::new();
|
||||
let song_editor = SongEditor::new(Arc::clone(&fontdb));
|
||||
|
||||
// for item in items.iter() {
|
||||
// nav_model.insert().text(item.title()).data(item.clone());
|
||||
// }
|
||||
|
||||
// nav_model.activate_position(0);
|
||||
|
||||
let mut app = App {
|
||||
presenter,
|
||||
core,
|
||||
|
@ -217,6 +243,7 @@ impl cosmic::Application for App {
|
|||
searching: false,
|
||||
current_item: (0, 0),
|
||||
library_dragged_item: None,
|
||||
fontdb,
|
||||
};
|
||||
|
||||
let mut batch = vec![];
|
||||
|
@ -1289,11 +1316,10 @@ where
|
|||
vec!["application/service-item".into()]
|
||||
)
|
||||
.data_received_for::<ServiceItem>(|item| {
|
||||
if let Some(item) = item {
|
||||
Message::AppendServiceItem(item)
|
||||
} else {
|
||||
Message::None
|
||||
}
|
||||
item.map_or_else(
|
||||
|| Message::None,
|
||||
|item| Message::AppendServiceItem(item),
|
||||
)
|
||||
})
|
||||
.on_finish(
|
||||
move |mime, data, action, x, y| {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use miette::{IntoDiagnostic, Result};
|
||||
use resvg::usvg::fontdb;
|
||||
use std::{fs::File, io::BufReader, path::PathBuf, sync::Arc};
|
||||
|
||||
use cosmic::{
|
||||
|
@ -30,7 +31,7 @@ use url::Url;
|
|||
|
||||
use crate::{
|
||||
core::{service_items::ServiceItem, slide::Slide},
|
||||
// ui::widgets::slide_text,
|
||||
ui::text_svg,
|
||||
BackgroundKind,
|
||||
};
|
||||
|
||||
|
@ -148,6 +149,7 @@ impl Presenter {
|
|||
};
|
||||
let total_slides: usize =
|
||||
items.iter().fold(0, |a, item| a + item.slides.len());
|
||||
|
||||
Self {
|
||||
current_slide: items[0].slides[0].clone(),
|
||||
current_item: 0,
|
||||
|
@ -741,7 +743,12 @@ pub(crate) fn slide_view(
|
|||
.align_x(Horizontal::Left)
|
||||
} else {
|
||||
// SVG based
|
||||
let text = slide.text_svg.view().map(|m| Message::None);
|
||||
let text: Element<Message> =
|
||||
if let Some(text) = &slide.text_svg {
|
||||
text.view().map(|_| Message::None).into()
|
||||
} else {
|
||||
Space::with_width(0).into()
|
||||
};
|
||||
Container::new(text)
|
||||
.center(Length::Fill)
|
||||
.align_x(Horizontal::Left)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{io, path::PathBuf};
|
||||
use std::{io, path::PathBuf, sync::Arc};
|
||||
|
||||
use cosmic::{
|
||||
dialog::file_chooser::open::Dialog,
|
||||
|
@ -31,7 +31,7 @@ use super::presenter::slide_view;
|
|||
pub struct SongEditor {
|
||||
pub song: Option<Song>,
|
||||
title: String,
|
||||
font_db: fontdb::Database,
|
||||
font_db: Arc<fontdb::Database>,
|
||||
fonts: Vec<(fontdb::ID, String)>,
|
||||
fonts_combo: combo_box::State<String>,
|
||||
font_sizes: combo_box::State<String>,
|
||||
|
@ -72,11 +72,9 @@ pub enum Message {
|
|||
}
|
||||
|
||||
impl SongEditor {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(font_db: Arc<fontdb::Database>) -> Self {
|
||||
let fonts = font_dir();
|
||||
debug!(?fonts);
|
||||
let mut font_db = fontdb::Database::new();
|
||||
font_db.load_system_fonts();
|
||||
let mut fonts: Vec<(fontdb::ID, String)> = font_db
|
||||
.faces()
|
||||
.map(|f| {
|
||||
|
@ -447,7 +445,9 @@ order",
|
|||
|
||||
impl Default for SongEditor {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
let mut fontdb = fontdb::Database::new();
|
||||
fontdb.load_system_fonts();
|
||||
Self::new(Arc::new(fontdb))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ use cosmic::{
|
|||
};
|
||||
use resvg::{
|
||||
tiny_skia::{self, Pixmap},
|
||||
usvg::Tree,
|
||||
usvg::{fontdb, Tree},
|
||||
};
|
||||
use tracing::{debug, error};
|
||||
|
||||
|
@ -230,6 +230,11 @@ impl TextSvg {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn fontdb(mut self, fontdb: Arc<fontdb::Database>) -> Self {
|
||||
self.fontdb = fontdb;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn alignment(mut self, alignment: TextAlignment) -> Self {
|
||||
self.alignment = alignment;
|
||||
self
|
||||
|
@ -285,7 +290,7 @@ impl TextSvg {
|
|||
self.font.name,
|
||||
self.font.size,
|
||||
self.fill, stroke, text);
|
||||
debug!(?final_svg);
|
||||
// debug!(?final_svg);
|
||||
let resvg_tree = Tree::from_str(
|
||||
&final_svg,
|
||||
&resvg::usvg::Options {
|
||||
|
@ -301,21 +306,17 @@ impl TextSvg {
|
|||
.expect("opops");
|
||||
resvg::render(&resvg_tree, transform, &mut pixmap.as_mut());
|
||||
// debug!(?pixmap);
|
||||
let handle = Handle::from_bytes(pixmap.data().to_owned());
|
||||
let handle = Handle::from_bytes(pixmap.take());
|
||||
self.handle = Some(handle);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn view<'a>(&self) -> Element<'a, Message> {
|
||||
container(
|
||||
Image::new(self.handle.clone().unwrap())
|
||||
.content_fit(ContentFit::Contain)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.into()
|
||||
Image::new(self.handle.clone().unwrap())
|
||||
.content_fit(ContentFit::Contain)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn text_spans(&self) -> Vec<String> {
|
||||
|
@ -352,6 +353,26 @@ pub fn color(color: impl AsRef<str>) -> Color {
|
|||
Color::from_hex_str(color)
|
||||
}
|
||||
|
||||
pub fn text_svg_generator(
|
||||
slide: &mut crate::core::slide::Slide,
|
||||
fontdb: Arc<fontdb::Database>,
|
||||
) {
|
||||
if slide.text().len() > 0 {
|
||||
let text_svg = TextSvg::new(slide.text())
|
||||
.alignment(slide.text_alignment())
|
||||
.fill("#fff")
|
||||
.shadow(shadow(2, 2, 5, "#000000"))
|
||||
.stroke(stroke(3, "#000"))
|
||||
.font(
|
||||
Font::from(slide.font().clone())
|
||||
.size(slide.font_size().try_into().unwrap()),
|
||||
)
|
||||
.fontdb(Arc::clone(&fontdb))
|
||||
.build();
|
||||
slide.text_svg = Some(text_svg);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use pretty_assertions::assert_eq;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue