Compare commits
No commits in common. "1c403f7d81b9fd4e6dbbc808b2a0fe1c35657247" and "77d12b2b010f10e3312b94110e7c53a00e50c63c" have entirely different histories.
1c403f7d81
...
77d12b2b01
17 changed files with 1824 additions and 419 deletions
1769
Cargo.lock
generated
1769
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -26,10 +26,9 @@ crisp = { git = "https://git.tfcconnection.org/chris/crisp", version = "0.1.3" }
|
||||||
rodio = { version = "0.20.1", features = ["symphonia-all", "tracing"] }
|
rodio = { version = "0.20.1", features = ["symphonia-all", "tracing"] }
|
||||||
gstreamer = "0.23.3"
|
gstreamer = "0.23.3"
|
||||||
gstreamer-app = "0.23.3"
|
gstreamer-app = "0.23.3"
|
||||||
# cosmic-time = "0.2.0"
|
cosmic-time = "0.2.0"
|
||||||
url = "2"
|
url = "2"
|
||||||
colors-transform = "0.2.11"
|
colors-transform = "0.2.11"
|
||||||
# mupdf = "0.5.0"
|
|
||||||
# rfd = { version = "0.12.1", features = ["xdg-portal"], default-features = false }
|
# rfd = { version = "0.12.1", features = ["xdg-portal"], default-features = false }
|
||||||
|
|
||||||
[dependencies.iced_video_player]
|
[dependencies.iced_video_player]
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::{Background, Slide, SlideBuilder, TextAlignment};
|
||||||
use super::{
|
use super::{
|
||||||
content::Content,
|
content::Content,
|
||||||
kinds::ServiceItemKind,
|
kinds::ServiceItemKind,
|
||||||
model::{LibraryKind, Model},
|
model::{get_db, LibraryKind, Model},
|
||||||
service_items::ServiceTrait,
|
service_items::ServiceTrait,
|
||||||
};
|
};
|
||||||
use crisp::types::{Keyword, Symbol, Value};
|
use crisp::types::{Keyword, Symbol, Value};
|
||||||
|
@ -45,7 +45,13 @@ impl Content for Image {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn background(&self) -> Option<Background> {
|
fn background(&self) -> Option<Background> {
|
||||||
Background::try_from(self.path.clone()).ok()
|
if let Ok(background) =
|
||||||
|
Background::try_from(self.path.clone())
|
||||||
|
{
|
||||||
|
Some(background)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subtext(&self) -> String {
|
fn subtext(&self) -> String {
|
||||||
|
|
|
@ -133,6 +133,12 @@ pub(crate) fn get_lists(exp: &Value) -> Vec<Value> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use std::fs::read_to_string;
|
||||||
|
|
||||||
|
use lexpr::{parse::Options, Parser};
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
// #[test]
|
// #[test]
|
||||||
// fn test_list() {
|
// fn test_list() {
|
||||||
|
|
|
@ -4,6 +4,8 @@ use cosmic::iced::Executor;
|
||||||
use miette::{miette, Result};
|
use miette::{miette, Result};
|
||||||
use sqlx::{Connection, SqliteConnection};
|
use sqlx::{Connection, SqliteConnection};
|
||||||
|
|
||||||
|
use super::kinds::ServiceItemKind;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Model<T> {
|
pub struct Model<T> {
|
||||||
pub items: Vec<T>,
|
pub items: Vec<T>,
|
||||||
|
|
|
@ -13,7 +13,7 @@ use crate::{Background, Slide, SlideBuilder, TextAlignment};
|
||||||
use super::{
|
use super::{
|
||||||
content::Content,
|
content::Content,
|
||||||
kinds::ServiceItemKind,
|
kinds::ServiceItemKind,
|
||||||
model::{LibraryKind, Model},
|
model::{get_db, LibraryKind, Model},
|
||||||
service_items::ServiceTrait,
|
service_items::ServiceTrait,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -57,7 +57,13 @@ impl Content for Presentation {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn background(&self) -> Option<Background> {
|
fn background(&self) -> Option<Background> {
|
||||||
Background::try_from(self.path.clone()).ok()
|
if let Ok(background) =
|
||||||
|
Background::try_from(self.path.clone())
|
||||||
|
{
|
||||||
|
Some(background)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subtext(&self) -> String {
|
fn subtext(&self) -> String {
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::ops::Deref;
|
||||||
|
|
||||||
use cosmic::iced::clipboard::mime::{AllowedMimeTypes, AsMimeTypes};
|
use cosmic::iced::clipboard::mime::{AllowedMimeTypes, AsMimeTypes};
|
||||||
use crisp::types::{Keyword, Symbol, Value};
|
use crisp::types::{Keyword, Symbol, Value};
|
||||||
use miette::Result;
|
use miette::{miette, Result};
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
|
|
||||||
use crate::Slide;
|
use crate::Slide;
|
||||||
|
@ -153,48 +153,52 @@ impl From<&Value> for ServiceItem {
|
||||||
database_id: 0,
|
database_id: 0,
|
||||||
kind: ServiceItemKind::Content(slide),
|
kind: ServiceItemKind::Content(slide),
|
||||||
}
|
}
|
||||||
} else if let Some(background) =
|
|
||||||
list.get(background_pos)
|
|
||||||
{
|
|
||||||
match background {
|
|
||||||
Value::List(item) => match &item[0] {
|
|
||||||
Value::Symbol(Symbol(s))
|
|
||||||
if s == "image" =>
|
|
||||||
{
|
|
||||||
Self::from(&Image::from(
|
|
||||||
background,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
Value::Symbol(Symbol(s))
|
|
||||||
if s == "video" =>
|
|
||||||
{
|
|
||||||
Self::from(&Video::from(
|
|
||||||
background,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
Value::Symbol(Symbol(s))
|
|
||||||
if s == "presentation" =>
|
|
||||||
{
|
|
||||||
Self::from(&Presentation::from(
|
|
||||||
background,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
_ => todo!(),
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
error!(
|
|
||||||
"There is no background here: {:?}",
|
|
||||||
background
|
|
||||||
);
|
|
||||||
ServiceItem::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
error!(
|
if let Some(background) =
|
||||||
"There is no background here: {:?}",
|
list.get(background_pos)
|
||||||
background_pos
|
{
|
||||||
);
|
match background {
|
||||||
ServiceItem::default()
|
Value::List(item) => match &item[0] {
|
||||||
|
Value::Symbol(Symbol(s))
|
||||||
|
if s == "image" =>
|
||||||
|
{
|
||||||
|
Self::from(&Image::from(
|
||||||
|
background,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Value::Symbol(Symbol(s))
|
||||||
|
if s == "video" =>
|
||||||
|
{
|
||||||
|
Self::from(&Video::from(
|
||||||
|
background,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Value::Symbol(Symbol(s))
|
||||||
|
if s == "presentation" =>
|
||||||
|
{
|
||||||
|
Self::from(
|
||||||
|
&Presentation::from(
|
||||||
|
background,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
error!(
|
||||||
|
"There is no background here: {:?}",
|
||||||
|
background
|
||||||
|
);
|
||||||
|
ServiceItem::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!(
|
||||||
|
"There is no background here: {:?}",
|
||||||
|
background_pos
|
||||||
|
);
|
||||||
|
ServiceItem::default()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::Symbol(Symbol(s)) if s == "song" => {
|
Value::Symbol(Symbol(s)) if s == "song" => {
|
||||||
|
@ -338,7 +342,7 @@ mod test {
|
||||||
use crate::core::presentations::PresKind;
|
use crate::core::presentations::PresKind;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::{assert_eq, assert_ne};
|
||||||
|
|
||||||
fn test_song() -> Song {
|
fn test_song() -> Song {
|
||||||
Song {
|
Song {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// use cosmic::dialog::ashpd::url::Url;
|
// use cosmic::dialog::ashpd::url::Url;
|
||||||
use crisp::types::{Keyword, Symbol, Value};
|
use crisp::types::{Keyword, Symbol, Value};
|
||||||
|
use gstreamer::query::Uri;
|
||||||
use iced_video_player::Video;
|
use iced_video_player::Video;
|
||||||
use miette::{miette, Result};
|
use miette::{miette, Result};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -12,15 +13,7 @@ use tracing::error;
|
||||||
use super::songs::Song;
|
use super::songs::Song;
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone,
|
Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize,
|
||||||
Copy,
|
|
||||||
Debug,
|
|
||||||
Default,
|
|
||||||
PartialEq,
|
|
||||||
Eq,
|
|
||||||
Serialize,
|
|
||||||
Deserialize,
|
|
||||||
Hash,
|
|
||||||
)]
|
)]
|
||||||
pub enum TextAlignment {
|
pub enum TextAlignment {
|
||||||
TopLeft,
|
TopLeft,
|
||||||
|
|
|
@ -8,14 +8,14 @@ use sqlx::{
|
||||||
pool::PoolConnection, query, sqlite::SqliteRow, FromRow, Row,
|
pool::PoolConnection, query, sqlite::SqliteRow, FromRow, Row,
|
||||||
Sqlite, SqliteConnection, SqlitePool,
|
Sqlite, SqliteConnection, SqlitePool,
|
||||||
};
|
};
|
||||||
use tracing::error;
|
use tracing::{debug, error};
|
||||||
|
|
||||||
use crate::{core::slide, Slide, SlideBuilder};
|
use crate::{core::slide, Slide, SlideBuilder};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
content::Content,
|
content::Content,
|
||||||
kinds::ServiceItemKind,
|
kinds::ServiceItemKind,
|
||||||
model::{LibraryKind, Model},
|
model::{get_db, LibraryKind, Model},
|
||||||
service_items::ServiceTrait,
|
service_items::ServiceTrait,
|
||||||
slide::{Background, TextAlignment},
|
slide::{Background, TextAlignment},
|
||||||
};
|
};
|
||||||
|
@ -132,7 +132,10 @@ impl FromRow<'_, SqliteRow> for Song {
|
||||||
}),
|
}),
|
||||||
background: {
|
background: {
|
||||||
let string: String = row.try_get(7)?;
|
let string: String = row.try_get(7)?;
|
||||||
Background::try_from(string).ok()
|
match Background::try_from(string) {
|
||||||
|
Ok(background) => Some(background),
|
||||||
|
Err(_) => None,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
text_alignment: Some({
|
text_alignment: Some({
|
||||||
let horizontal_alignment: String = row.try_get(3)?;
|
let horizontal_alignment: String = row.try_get(3)?;
|
||||||
|
@ -420,7 +423,7 @@ pub async fn update_song_in_db(
|
||||||
if let Some(vo) = item.verse_order {
|
if let Some(vo) = item.verse_order {
|
||||||
vo.into_iter()
|
vo.into_iter()
|
||||||
.map(|mut s| {
|
.map(|mut s| {
|
||||||
s.push(' ');
|
s.push_str(" ");
|
||||||
s
|
s
|
||||||
})
|
})
|
||||||
.collect::<String>()
|
.collect::<String>()
|
||||||
|
@ -535,6 +538,7 @@ impl Song {
|
||||||
lyric_list.push(lyric.to_string());
|
lyric_list.push(lyric.to_string());
|
||||||
} else {
|
} else {
|
||||||
// error!("NOT WORKING!");
|
// error!("NOT WORKING!");
|
||||||
|
()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// for lyric in lyric_list.iter() {
|
// for lyric in lyric_list.iter() {
|
||||||
|
@ -552,7 +556,7 @@ mod test {
|
||||||
use std::fs::read_to_string;
|
use std::fs::read_to_string;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::{assert_eq, assert_ne};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn test_song_lyrics() {
|
pub fn test_song_lyrics() {
|
||||||
|
@ -720,7 +724,7 @@ You saved my soul"
|
||||||
let lisp = read_to_string("./test_song.lisp").expect("oops");
|
let lisp = read_to_string("./test_song.lisp").expect("oops");
|
||||||
let lisp_value = crisp::reader::read(&lisp);
|
let lisp_value = crisp::reader::read(&lisp);
|
||||||
match lisp_value {
|
match lisp_value {
|
||||||
Value::List(v) => v.first().unwrap().clone(),
|
Value::List(v) => v.get(0).unwrap().clone(),
|
||||||
_ => Value::Nil,
|
_ => Value::Nil,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::{Background, SlideBuilder, TextAlignment};
|
||||||
use super::{
|
use super::{
|
||||||
content::Content,
|
content::Content,
|
||||||
kinds::ServiceItemKind,
|
kinds::ServiceItemKind,
|
||||||
model::{LibraryKind, Model},
|
model::{get_db, LibraryKind, Model},
|
||||||
service_items::ServiceTrait,
|
service_items::ServiceTrait,
|
||||||
slide::Slide,
|
slide::Slide,
|
||||||
};
|
};
|
||||||
|
@ -50,7 +50,13 @@ impl Content for Video {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn background(&self) -> Option<Background> {
|
fn background(&self) -> Option<Background> {
|
||||||
Background::try_from(self.path.clone()).ok()
|
if let Ok(background) =
|
||||||
|
Background::try_from(self.path.clone())
|
||||||
|
{
|
||||||
|
Some(background)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subtext(&self) -> String {
|
fn subtext(&self) -> String {
|
||||||
|
|
|
@ -37,14 +37,17 @@ pub fn parse_lisp(value: Value) -> Vec<ServiceItem> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::{fs::read_to_string, path::PathBuf};
|
use std::{
|
||||||
|
fs::read_to_string,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{
|
core::{
|
||||||
images::Image, kinds::ServiceItemKind,
|
images::Image, kinds::ServiceItemKind,
|
||||||
service_items::ServiceTrait, songs::Song, videos::Video,
|
service_items::ServiceTrait, songs::Song, videos::Video,
|
||||||
},
|
},
|
||||||
Background, TextAlignment,
|
Background, Slide, SlideBuilder, TextAlignment,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
20
src/main.rs
20
src/main.rs
|
@ -1,4 +1,5 @@
|
||||||
use clap::{command, Parser};
|
use clap::{command, Parser};
|
||||||
|
use core::model::{get_db, LibraryKind};
|
||||||
use core::service_items::{ServiceItem, ServiceItemModel};
|
use core::service_items::{ServiceItem, ServiceItemModel};
|
||||||
use core::slide::*;
|
use core::slide::*;
|
||||||
use core::songs::Song;
|
use core::songs::Song;
|
||||||
|
@ -13,12 +14,13 @@ use cosmic::iced_widget::{column, row};
|
||||||
use cosmic::widget::dnd_destination::DragId;
|
use cosmic::widget::dnd_destination::DragId;
|
||||||
use cosmic::widget::nav_bar::nav_bar_style;
|
use cosmic::widget::nav_bar::nav_bar_style;
|
||||||
use cosmic::widget::segmented_button::Entity;
|
use cosmic::widget::segmented_button::Entity;
|
||||||
use cosmic::widget::text;
|
|
||||||
use cosmic::widget::tooltip::Position as TPosition;
|
use cosmic::widget::tooltip::Position as TPosition;
|
||||||
use cosmic::widget::{
|
use cosmic::widget::{
|
||||||
button, horizontal_space, nav_bar, search_input, tooltip, Space,
|
button, horizontal_space, nav_bar, search_input, text_input,
|
||||||
|
tooltip, Space,
|
||||||
};
|
};
|
||||||
use cosmic::widget::{icon, slider};
|
use cosmic::widget::{icon, slider};
|
||||||
|
use cosmic::widget::{text, toggler};
|
||||||
use cosmic::{executor, Application, ApplicationExt, Element};
|
use cosmic::{executor, Application, ApplicationExt, Element};
|
||||||
use cosmic::{prelude::*, theme};
|
use cosmic::{prelude::*, theme};
|
||||||
use cosmic::{widget::Container, Theme};
|
use cosmic::{widget::Container, Theme};
|
||||||
|
@ -330,7 +332,7 @@ impl cosmic::Application for App {
|
||||||
)
|
)
|
||||||
.class(cosmic::theme::style::Button::HeaderBar)
|
.class(cosmic::theme::style::Button::HeaderBar)
|
||||||
.on_press(Message::EditorToggle(
|
.on_press(Message::EditorToggle(
|
||||||
self.editor_mode.is_none(),
|
!self.editor_mode.is_some(),
|
||||||
)),
|
)),
|
||||||
"Enter Edit Mode",
|
"Enter Edit Mode",
|
||||||
TPosition::Bottom,
|
TPosition::Bottom,
|
||||||
|
@ -719,7 +721,7 @@ impl cosmic::Application for App {
|
||||||
|
|
||||||
let library = if self.library_open {
|
let library = if self.library_open {
|
||||||
Container::new(if let Some(library) = &self.library {
|
Container::new(if let Some(library) = &self.library {
|
||||||
library.view().map(Message::Library)
|
library.view().map(|m| Message::Library(m))
|
||||||
} else {
|
} else {
|
||||||
Space::new(0, 0).into()
|
Space::new(0, 0).into()
|
||||||
})
|
})
|
||||||
|
@ -730,7 +732,7 @@ impl cosmic::Application for App {
|
||||||
};
|
};
|
||||||
|
|
||||||
let song_editor =
|
let song_editor =
|
||||||
self.song_editor.view().map(Message::SongEditor);
|
self.song_editor.view().map(|m| Message::SongEditor(m));
|
||||||
|
|
||||||
let row = row![
|
let row = row![
|
||||||
Container::new(
|
Container::new(
|
||||||
|
@ -744,7 +746,8 @@ impl cosmic::Application for App {
|
||||||
.class(theme::style::Button::Transparent)
|
.class(theme::style::Button::Transparent)
|
||||||
)
|
)
|
||||||
.center_y(Length::Fill)
|
.center_y(Length::Fill)
|
||||||
.align_right(Length::FillPortion(1)),
|
.align_right(Length::Fill)
|
||||||
|
.width(Length::FillPortion(1)),
|
||||||
Container::new(slide_preview)
|
Container::new(slide_preview)
|
||||||
.center_y(Length::Fill)
|
.center_y(Length::Fill)
|
||||||
.width(Length::FillPortion(3)),
|
.width(Length::FillPortion(3)),
|
||||||
|
@ -759,7 +762,8 @@ impl cosmic::Application for App {
|
||||||
.class(theme::style::Button::Transparent)
|
.class(theme::style::Button::Transparent)
|
||||||
)
|
)
|
||||||
.center_y(Length::Fill)
|
.center_y(Length::Fill)
|
||||||
.align_left(Length::FillPortion(1)),
|
.align_left(Length::Fill)
|
||||||
|
.width(Length::FillPortion(1)),
|
||||||
library
|
library
|
||||||
]
|
]
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
|
@ -878,6 +882,8 @@ where
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
fn test_slide() -> String {
|
fn test_slide() -> String {
|
||||||
let slide = r#"(slide (image :source "./somehting.jpg" :fill cover
|
let slide = r#"(slide (image :source "./somehting.jpg" :fill cover
|
||||||
|
|
|
@ -1,22 +1,21 @@
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
iced::{
|
iced::{
|
||||||
alignment::Vertical, clipboard::dnd::DndAction,
|
alignment::Vertical, clipboard::dnd::DndAction,
|
||||||
futures::FutureExt, Background, Border, Color, Length,
|
futures::FutureExt, Background, Border, Color, Length,
|
||||||
},
|
},
|
||||||
iced_core::widget::tree::State,
|
|
||||||
iced_widget::{column, row as rowm, text as textm},
|
iced_widget::{column, row as rowm, text as textm},
|
||||||
theme,
|
theme,
|
||||||
widget::{
|
widget::{
|
||||||
button, container, horizontal_space, icon, mouse_area,
|
button, container, horizontal_space, icon, mouse_area,
|
||||||
responsive, row, scrollable, text, text_input, Container,
|
responsive, row, scrollable, text, text_input, Container,
|
||||||
DndSource, Space, Widget,
|
DndSource, Icon, Space, Widget,
|
||||||
},
|
},
|
||||||
Element, Task,
|
Element, Task,
|
||||||
};
|
};
|
||||||
use miette::{IntoDiagnostic, Result};
|
use miette::{miette, IntoDiagnostic, Result};
|
||||||
use sqlx::{pool::PoolConnection, Sqlite, SqlitePool};
|
use sqlx::{
|
||||||
|
pool::PoolConnection, Sqlite, SqliteConnection, SqlitePool,
|
||||||
|
};
|
||||||
use tracing::{debug, error, warn};
|
use tracing::{debug, error, warn};
|
||||||
|
|
||||||
use crate::core::{
|
use crate::core::{
|
||||||
|
@ -360,7 +359,7 @@ impl<'a> Library {
|
||||||
|(index, item)| {
|
|(index, item)| {
|
||||||
let service_item = item.to_service_item();
|
let service_item = item.to_service_item();
|
||||||
let drag_item =
|
let drag_item =
|
||||||
Box::new(self.single_item(index, item, &model));
|
self.single_item(index, item, model);
|
||||||
let visual_item = self
|
let visual_item = self
|
||||||
.single_item(index, item, model)
|
.single_item(index, item, model)
|
||||||
.map(|_| Message::None);
|
.map(|_| Message::None);
|
||||||
|
@ -387,25 +386,11 @@ impl<'a> Library {
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
.action(DndAction::Copy)
|
.action(DndAction::Copy)
|
||||||
.drag_icon({
|
// .drag_icon(move |i| {
|
||||||
let model = model.kind.clone();
|
// let state =
|
||||||
move |i| {
|
// drag_item.as_widget().state();
|
||||||
let state = State::None;
|
// (drag_item, state, i)
|
||||||
let icon = match model {
|
// })
|
||||||
LibraryKind::Song => icon::from_name(
|
|
||||||
"folder-music-symbolic",
|
|
||||||
)
|
|
||||||
,
|
|
||||||
LibraryKind::Video => icon::from_name("folder-videos-symbolic"),
|
|
||||||
LibraryKind::Image => icon::from_name("folder-pictures-symbolic"),
|
|
||||||
LibraryKind::Presentation => icon::from_name("x-office-presentation-symbolic"),
|
|
||||||
};
|
|
||||||
(
|
|
||||||
icon.into(),
|
|
||||||
state,
|
|
||||||
i,
|
|
||||||
)
|
|
||||||
}})
|
|
||||||
.drag_content(move || {
|
.drag_content(move || {
|
||||||
service_item.to_owned()
|
service_item.to_owned()
|
||||||
})
|
})
|
||||||
|
@ -413,11 +398,10 @@ impl<'a> Library {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.spacing(2)
|
.spacing(2)
|
||||||
.width(Length::Fill),
|
.width(Length::Fill),
|
||||||
)
|
)
|
||||||
.spacing(5)
|
.spacing(5);
|
||||||
.height(Length::Fill);
|
|
||||||
|
|
||||||
let library_toolbar = rowm!(
|
let library_toolbar = rowm!(
|
||||||
text_input("Search...", ""),
|
text_input("Search...", ""),
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use miette::{IntoDiagnostic, Result};
|
use miette::{miette, IntoDiagnostic, Result};
|
||||||
use std::{fs::File, io::BufReader, path::PathBuf, sync::Arc};
|
use std::{fs::File, io::BufReader, path::PathBuf, sync::Arc};
|
||||||
|
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
|
@ -13,7 +13,7 @@ use cosmic::{
|
||||||
scrollable::{
|
scrollable::{
|
||||||
scroll_to, AbsoluteOffset, Direction, Scrollbar,
|
scroll_to, AbsoluteOffset, Direction, Scrollbar,
|
||||||
},
|
},
|
||||||
span, stack,
|
span, stack, text,
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
widget::{
|
widget::{
|
||||||
|
@ -29,10 +29,13 @@ use url::Url;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{service_items::ServiceItemModel, slide::Slide},
|
core::{service_items::ServiceItemModel, slide::Slide},
|
||||||
ui::text_svg::{self, Font as SvgFont},
|
|
||||||
BackgroundKind,
|
BackgroundKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::text_svg::{
|
||||||
|
self, shadow, stroke, Font as SvgFont, TextSvg,
|
||||||
|
};
|
||||||
|
|
||||||
const REFERENCE_WIDTH: f32 = 1920.0;
|
const REFERENCE_WIDTH: f32 = 1920.0;
|
||||||
const REFERENCE_HEIGHT: f32 = 1080.0;
|
const REFERENCE_HEIGHT: f32 = 1080.0;
|
||||||
|
|
||||||
|
@ -525,43 +528,39 @@ async fn start_audio(sink: Arc<Sink>, audio: PathBuf) {
|
||||||
fn scale_font(font_size: f32, width: f32) -> f32 {
|
fn scale_font(font_size: f32, width: f32) -> f32 {
|
||||||
let scale_factor = (REFERENCE_WIDTH / width).sqrt();
|
let scale_factor = (REFERENCE_WIDTH / width).sqrt();
|
||||||
// debug!(scale_factor);
|
// debug!(scale_factor);
|
||||||
|
let font_size = if font_size > 0.0 {
|
||||||
if font_size > 0.0 {
|
|
||||||
font_size / scale_factor
|
font_size / scale_factor
|
||||||
} else {
|
} else {
|
||||||
50.0
|
50.0
|
||||||
}
|
};
|
||||||
|
font_size
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn slide_view(
|
pub(crate) fn slide_view<'a>(
|
||||||
slide: Slide,
|
slide: Slide,
|
||||||
video: &Option<Video>,
|
video: &'a Option<Video>,
|
||||||
font: Font,
|
font: Font,
|
||||||
delegate: bool,
|
delegate: bool,
|
||||||
hide_mouse: bool,
|
hide_mouse: bool,
|
||||||
) -> Element<'_, Message> {
|
) -> Element<'a, Message> {
|
||||||
responsive(move |size| {
|
responsive(move |size| {
|
||||||
let width = size.height * 16.0 / 9.0;
|
let width = size.height * 16.0 / 9.0;
|
||||||
let font_size = scale_font(slide.font_size() as f32, width);
|
let font_size = scale_font(slide.font_size() as f32, width);
|
||||||
let slide_text = slide.text();
|
|
||||||
|
|
||||||
// SVG based
|
|
||||||
// let font = SvgFont::from(font).size(font_size.floor() as u8);
|
// let font = SvgFont::from(font).size(font_size.floor() as u8);
|
||||||
|
let slide_text = slide.text();
|
||||||
// let text = text_svg::TextSvg::new()
|
// let text = text_svg::TextSvg::new()
|
||||||
// .text(&slide_text)
|
// .text(&slide_text)
|
||||||
// .fill("#fff")
|
// .fill("#fff")
|
||||||
// .shadow(text_svg::shadow(2, 2, 5, "#000000"))
|
// .shadow(shadow(2, 2, 5, "#000000"))
|
||||||
// .stroke(text_svg::stroke(1, "#000"))
|
// .stroke(stroke(1, "#000"))
|
||||||
// .font(font)
|
// .font(font)
|
||||||
// .view()
|
// .view()
|
||||||
// .map(|m| Message::None);
|
// .map(|m| Message::None);
|
||||||
|
|
||||||
// let text = text!("{}", &slide_text);
|
// let text = text!("{}", &slide_text);
|
||||||
// text widget based
|
|
||||||
let lines = slide_text.lines();
|
let lines = slide_text.lines();
|
||||||
let text: Vec<Element<Message>> = lines
|
let text: Vec<Element<Message>> = lines
|
||||||
.map(|t| {
|
.map(|t| {
|
||||||
rich_text([span(format!("{}\n", t))
|
rich_text([span(format!("{}\n", t.to_string()))
|
||||||
.background(
|
.background(
|
||||||
Background::Color(Color::BLACK)
|
Background::Color(Color::BLACK)
|
||||||
.scale_alpha(0.4),
|
.scale_alpha(0.4),
|
||||||
|
@ -574,8 +573,6 @@ pub(crate) fn slide_view(
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let text = Column::with_children(text).spacing(6);
|
let text = Column::with_children(text).spacing(6);
|
||||||
|
|
||||||
//Next
|
|
||||||
let text_container = Container::new(text)
|
let text_container = Container::new(text)
|
||||||
.center(Length::Fill)
|
.center(Length::Fill)
|
||||||
.align_x(Horizontal::Left);
|
.align_x(Horizontal::Left);
|
||||||
|
|
|
@ -31,10 +31,8 @@ use super::presenter::slide_view;
|
||||||
pub struct SongEditor {
|
pub struct SongEditor {
|
||||||
pub song: Option<Song>,
|
pub song: Option<Song>,
|
||||||
title: String,
|
title: String,
|
||||||
font_db: fontdb::Database,
|
fonts: combo_box::State<String>,
|
||||||
fonts: Vec<(fontdb::ID, String)>,
|
font_sizes: Vec<String>,
|
||||||
fonts_combo: combo_box::State<String>,
|
|
||||||
font_sizes: combo_box::State<String>,
|
|
||||||
font: String,
|
font: String,
|
||||||
author: String,
|
author: String,
|
||||||
audio: PathBuf,
|
audio: PathBuf,
|
||||||
|
@ -74,21 +72,42 @@ impl SongEditor {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let fonts = font_dir();
|
let fonts = font_dir();
|
||||||
debug!(?fonts);
|
debug!(?fonts);
|
||||||
let mut font_db = fontdb::Database::new();
|
let mut fontdb = fontdb::Database::new();
|
||||||
font_db.load_system_fonts();
|
fontdb.load_system_fonts();
|
||||||
let mut fonts: Vec<(fontdb::ID, String)> = font_db
|
let fonts: Vec<String> = fontdb
|
||||||
.faces()
|
.faces()
|
||||||
.map(|f| {
|
.map(|f| {
|
||||||
let id = f.id;
|
let mut font = f.to_owned().post_script_name;
|
||||||
let font_base_name: String =
|
if let Some(at) = font.find("-") {
|
||||||
f.families.iter().map(|f| f.0.clone()).collect();
|
let _ = font.split_off(at);
|
||||||
let font_weight = f.weight;
|
}
|
||||||
let font_style = f.style;
|
let indices: Vec<usize> = font
|
||||||
let font_stretch = f.stretch;
|
.chars()
|
||||||
(id, font_base_name)
|
.enumerate()
|
||||||
|
.filter(|(index, c)| {
|
||||||
|
c.is_uppercase() && *index != 0
|
||||||
|
})
|
||||||
|
.map(|(index, c)| index)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut font_parts = vec![];
|
||||||
|
for index in indices.iter().rev() {
|
||||||
|
let (first, last) = font.split_at(*index);
|
||||||
|
font_parts.push(first);
|
||||||
|
if !last.is_empty() {
|
||||||
|
font_parts.push(last);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
font_parts
|
||||||
|
.iter()
|
||||||
|
.map(|s| {
|
||||||
|
let mut s = s.to_string();
|
||||||
|
s.push(' ');
|
||||||
|
s
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
fonts.dedup();
|
|
||||||
// let fonts = vec![
|
// let fonts = vec![
|
||||||
// String::from("Quicksand"),
|
// String::from("Quicksand"),
|
||||||
// String::from("Noto Sans"),
|
// String::from("Noto Sans"),
|
||||||
|
@ -115,16 +134,13 @@ impl SongEditor {
|
||||||
"70".to_string(),
|
"70".to_string(),
|
||||||
"80".to_string(),
|
"80".to_string(),
|
||||||
];
|
];
|
||||||
let font_texts = fonts.iter().map(|f| f.1.clone()).collect();
|
|
||||||
Self {
|
Self {
|
||||||
song: None,
|
song: None,
|
||||||
font_db,
|
fonts: combo_box::State::new(fonts),
|
||||||
fonts,
|
|
||||||
fonts_combo: combo_box::State::new(font_texts),
|
|
||||||
title: "Death was Arrested".to_owned(),
|
title: "Death was Arrested".to_owned(),
|
||||||
font: "Quicksand".to_owned(),
|
font: "Quicksand".to_owned(),
|
||||||
font_size: 16,
|
font_size: 16,
|
||||||
font_sizes: combo_box::State::new(font_sizes),
|
font_sizes,
|
||||||
verse_order: "Death was Arrested".to_owned(),
|
verse_order: "Death was Arrested".to_owned(),
|
||||||
lyrics: text_editor::Content::new(),
|
lyrics: text_editor::Content::new(),
|
||||||
editing: false,
|
editing: false,
|
||||||
|
@ -151,7 +167,7 @@ impl SongEditor {
|
||||||
self.verse_order = verse_order
|
self.verse_order = verse_order
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|mut s| {
|
.map(|mut s| {
|
||||||
s.push(' ');
|
s.push_str(" ");
|
||||||
s
|
s
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -173,23 +189,11 @@ impl SongEditor {
|
||||||
self.background = song.background;
|
self.background = song.background;
|
||||||
}
|
}
|
||||||
Message::ChangeFont(font) => {
|
Message::ChangeFont(font) => {
|
||||||
let font_id = self
|
|
||||||
.fonts
|
|
||||||
.iter()
|
|
||||||
.filter(|f| f.1 == font)
|
|
||||||
.map(|f| f.0)
|
|
||||||
.next();
|
|
||||||
if let Some(id) = font_id {
|
|
||||||
if let Some(face) = self.font_db.face(id) {
|
|
||||||
self.font = face.post_script_name.clone();
|
|
||||||
// self.current_font = Font::from(face);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.font = font.clone();
|
self.font = font.clone();
|
||||||
|
|
||||||
let font_name = font.into_boxed_str();
|
let font_name = font.into_boxed_str();
|
||||||
let family = Family::Name(Box::leak(font_name));
|
let family = Family::Name(Box::leak(font_name));
|
||||||
let weight = Weight::Bold;
|
let weight = Weight::Normal;
|
||||||
let stretch = Stretch::Normal;
|
let stretch = Stretch::Normal;
|
||||||
let style = Style::Normal;
|
let style = Style::Normal;
|
||||||
let font = Font {
|
let font = Font {
|
||||||
|
@ -201,7 +205,15 @@ impl SongEditor {
|
||||||
self.current_font = font;
|
self.current_font = font;
|
||||||
// return self.update_song(song);
|
// return self.update_song(song);
|
||||||
}
|
}
|
||||||
Message::ChangeFontSize(size) => self.font_size = size,
|
Message::ChangeFontSize(size) => {
|
||||||
|
if let Some(size) = self.font_sizes.get(size) {
|
||||||
|
if let Ok(size) = size.parse() {
|
||||||
|
debug!(font_size = size);
|
||||||
|
self.font_size = size;
|
||||||
|
// return self.update_song(song);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Message::ChangeTitle(title) => {
|
Message::ChangeTitle(title) => {
|
||||||
self.title = title.clone();
|
self.title = title.clone();
|
||||||
if let Some(song) = &mut self.song {
|
if let Some(song) = &mut self.song {
|
||||||
|
@ -215,6 +227,7 @@ impl SongEditor {
|
||||||
if let Some(mut song) = self.song.clone() {
|
if let Some(mut song) = self.song.clone() {
|
||||||
let verse_order = verse_order
|
let verse_order = verse_order
|
||||||
.split(" ")
|
.split(" ")
|
||||||
|
.into_iter()
|
||||||
.map(|s| s.to_owned())
|
.map(|s| s.to_owned())
|
||||||
.collect();
|
.collect();
|
||||||
song.verse_order = Some(verse_order);
|
song.verse_order = Some(verse_order);
|
||||||
|
@ -292,25 +305,40 @@ impl SongEditor {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(index, slide)| {
|
.map(|(index, slide)| {
|
||||||
container(
|
let svg = Handle::from_memory(r#"<svg viewBox="0 0 1280 720" xmlns="http://www.w3.org/2000/svg">
|
||||||
slide_view(
|
<defs>
|
||||||
slide,
|
<filter id="shadow">
|
||||||
if index == 0 {
|
<feDropShadow dx="10" dy="10" stdDeviation="5" flood-color='#000' />
|
||||||
&self.video
|
</filter>
|
||||||
} else {
|
</defs>
|
||||||
&None
|
<text dominant-baseline="middle" text-anchor="middle" font-weight="bold" font-family="Quicksand" font-size="80" fill="white" stroke="black" stroke-width="2" style="filter:url(#shadow);">
|
||||||
},
|
<tspan x="50%" y="50" >Hello World this is</tspan>
|
||||||
self.current_font,
|
<tspan x="50%" y="140">longer chunks of text</tspan>
|
||||||
false,
|
<tspan x="50%" y="230">where we need to test whether the text</tspan>
|
||||||
false,
|
<tspan x="50%" y="320">will look ok!</tspan>
|
||||||
|
</text>
|
||||||
|
</svg>"#.as_bytes());
|
||||||
|
stack!(
|
||||||
|
container(
|
||||||
|
slide_view(
|
||||||
|
slide,
|
||||||
|
if index == 0 {
|
||||||
|
&self.video
|
||||||
|
} else {
|
||||||
|
&None
|
||||||
|
},
|
||||||
|
self.current_font,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.map(|_| Message::None),
|
||||||
)
|
)
|
||||||
.map(|_| Message::None),
|
.height(250)
|
||||||
)
|
.center_x(Length::Fill)
|
||||||
.height(250)
|
.padding([0, 20])
|
||||||
.center_x(Length::Fill)
|
.clip(true),
|
||||||
.padding([0, 20])
|
Svg::new(svg),
|
||||||
.clip(true)
|
).into()
|
||||||
.into()
|
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
scrollable(
|
scrollable(
|
||||||
|
@ -368,34 +396,22 @@ order",
|
||||||
|
|
||||||
fn toolbar(&self) -> Element<Message> {
|
fn toolbar(&self) -> Element<Message> {
|
||||||
let selected_font = &self.font;
|
let selected_font = &self.font;
|
||||||
let selected_font_size = {
|
let selected_font_size = self
|
||||||
let font_size_position = self
|
.font_sizes
|
||||||
.font_sizes
|
.iter()
|
||||||
.options()
|
.position(|s| *s == self.font_size.to_string());
|
||||||
.iter()
|
|
||||||
.position(|s| *s == self.font_size.to_string());
|
|
||||||
self.font_sizes
|
|
||||||
.options()
|
|
||||||
.get(font_size_position.unwrap_or_default())
|
|
||||||
};
|
|
||||||
let font_selector = combo_box(
|
let font_selector = combo_box(
|
||||||
&self.fonts_combo,
|
&self.fonts,
|
||||||
"Font",
|
"Font",
|
||||||
Some(selected_font),
|
Some(selected_font),
|
||||||
Message::ChangeFont,
|
Message::ChangeFont,
|
||||||
)
|
)
|
||||||
.width(200);
|
.width(200);
|
||||||
let font_size = combo_box(
|
let font_size = dropdown(
|
||||||
&self.font_sizes,
|
&self.font_sizes,
|
||||||
"Font Size",
|
|
||||||
selected_font_size,
|
selected_font_size,
|
||||||
|size| {
|
Message::ChangeFontSize,
|
||||||
Message::ChangeFontSize(
|
);
|
||||||
size.parse().expect("Should be a number"),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.width(theme::active().cosmic().space_xxl());
|
|
||||||
|
|
||||||
let background_selector = button::icon(
|
let background_selector = button::icon(
|
||||||
icon::from_name("folder-pictures-symbolic").scale(2),
|
icon::from_name("folder-pictures-symbolic").scale(2),
|
||||||
|
@ -411,7 +427,6 @@ order",
|
||||||
horizontal_space(),
|
horizontal_space(),
|
||||||
background_selector
|
background_selector
|
||||||
]
|
]
|
||||||
.spacing(10)
|
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
use std::{
|
use std::fmt::Display;
|
||||||
fmt::Display,
|
|
||||||
hash::{Hash, Hasher},
|
|
||||||
};
|
|
||||||
|
|
||||||
use colors_transform::Rgb;
|
use colors_transform::Rgb;
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
|
@ -10,9 +7,9 @@ use cosmic::{
|
||||||
Length,
|
Length,
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
widget::{container, lazy, responsive, svg::Handle, Svg},
|
widget::{container, responsive, svg::Handle, Svg},
|
||||||
};
|
};
|
||||||
use tracing::error;
|
use tracing::{debug, error};
|
||||||
|
|
||||||
use crate::TextAlignment;
|
use crate::TextAlignment;
|
||||||
|
|
||||||
|
@ -24,21 +21,9 @@ pub struct TextSvg {
|
||||||
stroke: Option<Stroke>,
|
stroke: Option<Stroke>,
|
||||||
fill: Color,
|
fill: Color,
|
||||||
alignment: TextAlignment,
|
alignment: TextAlignment,
|
||||||
handle: Option<Handle>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for TextSvg {
|
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
self.text.hash(state);
|
|
||||||
self.font.hash(state);
|
|
||||||
self.shadow.hash(state);
|
|
||||||
self.stroke.hash(state);
|
|
||||||
self.fill.hash(state);
|
|
||||||
self.alignment.hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
|
|
||||||
pub struct Font {
|
pub struct Font {
|
||||||
name: String,
|
name: String,
|
||||||
weight: Weight,
|
weight: Weight,
|
||||||
|
@ -76,11 +61,11 @@ impl Font {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_weight(&self) -> Weight {
|
pub fn get_weight(&self) -> Weight {
|
||||||
self.weight
|
self.weight.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_style(&self) -> Style {
|
pub fn get_style(&self) -> Style {
|
||||||
self.style
|
self.style.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn weight(mut self, weight: impl Into<Weight>) -> Self {
|
pub fn weight(mut self, weight: impl Into<Weight>) -> Self {
|
||||||
|
@ -107,12 +92,6 @@ impl Font {
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Color(Rgb);
|
pub struct Color(Rgb);
|
||||||
|
|
||||||
impl Hash for Color {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
self.0.to_css_hex_string().hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Color {
|
impl Color {
|
||||||
pub fn from_hex_str(color: impl AsRef<str>) -> Color {
|
pub fn from_hex_str(color: impl AsRef<str>) -> Color {
|
||||||
match Rgb::from_hex_str(color.as_ref()) {
|
match Rgb::from_hex_str(color.as_ref()) {
|
||||||
|
@ -149,7 +128,7 @@ impl Display for Color {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Hash)]
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||||
pub struct Shadow {
|
pub struct Shadow {
|
||||||
pub offset_x: i16,
|
pub offset_x: i16,
|
||||||
pub offset_y: i16,
|
pub offset_y: i16,
|
||||||
|
@ -157,7 +136,7 @@ pub struct Shadow {
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Hash)]
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||||
pub struct Stroke {
|
pub struct Stroke {
|
||||||
size: u16,
|
size: u16,
|
||||||
color: Color,
|
color: Color,
|
||||||
|
@ -174,8 +153,6 @@ impl TextSvg {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn build(self)
|
|
||||||
|
|
||||||
pub fn fill(mut self, color: impl Into<Color>) -> Self {
|
pub fn fill(mut self, color: impl Into<Color>) -> Self {
|
||||||
self.fill = color.into();
|
self.fill = color.into();
|
||||||
self
|
self
|
||||||
|
@ -249,11 +226,12 @@ impl TextSvg {
|
||||||
self.fill, stroke, text);
|
self.fill, stroke, text);
|
||||||
|
|
||||||
// debug!(final_svg);
|
// debug!(final_svg);
|
||||||
lazy(self.clone(), move |_s| Svg::new(Handle::from_memory(
|
|
||||||
Box::leak(<std::string::String as Clone>::clone(&final_svg).into_boxed_str()).as_bytes(),
|
Svg::new(Handle::from_memory(
|
||||||
|
Box::leak(final_svg.into_boxed_str()).as_bytes(),
|
||||||
))
|
))
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.height(Length::Fill))
|
.height(Length::Fill)
|
||||||
.into()
|
.into()
|
||||||
})).width(Length::Fill).height(Length::Fill).into()
|
})).width(Length::Fill).height(Length::Fill).into()
|
||||||
}
|
}
|
||||||
|
|
1
todo.org
1
todo.org
|
@ -1,7 +1,6 @@
|
||||||
#+TITLE: The Task list for Lumina
|
#+TITLE: The Task list for Lumina
|
||||||
|
|
||||||
|
|
||||||
* TODO Check into =mupdf-rs= for loading PDF's.
|
|
||||||
* TODO [#A] Text could be built by using SVG instead of the text element. Maybe I could construct my own text element even
|
* TODO [#A] Text could be built by using SVG instead of the text element. Maybe I could construct my own text element even
|
||||||
This does almost work. There is a clear amount of lag or rather hang up since switching to the =text_svg= element. I think I may only keep it till I can figure out how to do strokes and shadows in iced's normal text element.
|
This does almost work. There is a clear amount of lag or rather hang up since switching to the =text_svg= element. I think I may only keep it till I can figure out how to do strokes and shadows in iced's normal text element.
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue