Compare commits
10 commits
043a90485c
...
38b12c2681
Author | SHA1 | Date | |
---|---|---|---|
|
38b12c2681 | ||
|
b95e76eff4 | ||
|
1a2ff0a4bc | ||
|
44e8bc4683 | ||
|
625988820c | ||
|
ae84870ef1 | ||
|
5ed7f6b837 | ||
|
8cf2d48a16 | ||
|
e6621072cd | ||
|
77d12b2b01 |
|
@ -3,7 +3,7 @@ use crate::{Background, Slide, SlideBuilder, TextAlignment};
|
||||||
use super::{
|
use super::{
|
||||||
content::Content,
|
content::Content,
|
||||||
kinds::ServiceItemKind,
|
kinds::ServiceItemKind,
|
||||||
model::{get_db, LibraryKind, Model},
|
model::{LibraryKind, Model},
|
||||||
service_items::ServiceTrait,
|
service_items::ServiceTrait,
|
||||||
};
|
};
|
||||||
use crisp::types::{Keyword, Symbol, Value};
|
use crisp::types::{Keyword, Symbol, Value};
|
||||||
|
@ -45,13 +45,7 @@ impl Content for Image {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn background(&self) -> Option<Background> {
|
fn background(&self) -> Option<Background> {
|
||||||
if let Ok(background) =
|
Background::try_from(self.path.clone()).ok()
|
||||||
Background::try_from(self.path.clone())
|
|
||||||
{
|
|
||||||
Some(background)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subtext(&self) -> String {
|
fn subtext(&self) -> String {
|
||||||
|
|
|
@ -133,12 +133,6 @@ 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,8 +4,6 @@ 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::{get_db, LibraryKind, Model},
|
model::{LibraryKind, Model},
|
||||||
service_items::ServiceTrait,
|
service_items::ServiceTrait,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -57,13 +57,7 @@ impl Content for Presentation {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn background(&self) -> Option<Background> {
|
fn background(&self) -> Option<Background> {
|
||||||
if let Ok(background) =
|
Background::try_from(self.path.clone()).ok()
|
||||||
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::{miette, Result};
|
use miette::Result;
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
|
|
||||||
use crate::Slide;
|
use crate::Slide;
|
||||||
|
@ -153,52 +153,48 @@ impl From<&Value> for ServiceItem {
|
||||||
database_id: 0,
|
database_id: 0,
|
||||||
kind: ServiceItemKind::Content(slide),
|
kind: ServiceItemKind::Content(slide),
|
||||||
}
|
}
|
||||||
} else {
|
} else if let Some(background) =
|
||||||
if let Some(background) =
|
list.get(background_pos)
|
||||||
list.get(background_pos)
|
{
|
||||||
{
|
match background {
|
||||||
match background {
|
Value::List(item) => match &item[0] {
|
||||||
Value::List(item) => match &item[0] {
|
Value::Symbol(Symbol(s))
|
||||||
Value::Symbol(Symbol(s))
|
if s == "image" =>
|
||||||
if s == "image" =>
|
{
|
||||||
{
|
Self::from(&Image::from(
|
||||||
Self::from(&Image::from(
|
background,
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
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()
|
|
||||||
}
|
}
|
||||||
|
} 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" => {
|
||||||
|
@ -342,7 +338,7 @@ mod test {
|
||||||
use crate::core::presentations::PresKind;
|
use crate::core::presentations::PresKind;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use pretty_assertions::{assert_eq, assert_ne};
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
fn test_song() -> Song {
|
fn test_song() -> Song {
|
||||||
Song {
|
Song {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// 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};
|
||||||
|
@ -13,7 +12,15 @@ use tracing::error;
|
||||||
use super::songs::Song;
|
use super::songs::Song;
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize,
|
Clone,
|
||||||
|
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::{debug, error};
|
use tracing::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::{get_db, LibraryKind, Model},
|
model::{LibraryKind, Model},
|
||||||
service_items::ServiceTrait,
|
service_items::ServiceTrait,
|
||||||
slide::{Background, TextAlignment},
|
slide::{Background, TextAlignment},
|
||||||
};
|
};
|
||||||
|
@ -132,10 +132,7 @@ impl FromRow<'_, SqliteRow> for Song {
|
||||||
}),
|
}),
|
||||||
background: {
|
background: {
|
||||||
let string: String = row.try_get(7)?;
|
let string: String = row.try_get(7)?;
|
||||||
match Background::try_from(string) {
|
Background::try_from(string).ok()
|
||||||
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)?;
|
||||||
|
@ -423,7 +420,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_str(" ");
|
s.push(' ');
|
||||||
s
|
s
|
||||||
})
|
})
|
||||||
.collect::<String>()
|
.collect::<String>()
|
||||||
|
@ -538,7 +535,6 @@ 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() {
|
||||||
|
@ -556,7 +552,7 @@ mod test {
|
||||||
use std::fs::read_to_string;
|
use std::fs::read_to_string;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use pretty_assertions::{assert_eq, assert_ne};
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn test_song_lyrics() {
|
pub fn test_song_lyrics() {
|
||||||
|
@ -724,7 +720,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.get(0).unwrap().clone(),
|
Value::List(v) => v.first().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::{get_db, LibraryKind, Model},
|
model::{LibraryKind, Model},
|
||||||
service_items::ServiceTrait,
|
service_items::ServiceTrait,
|
||||||
slide::Slide,
|
slide::Slide,
|
||||||
};
|
};
|
||||||
|
@ -50,13 +50,7 @@ impl Content for Video {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn background(&self) -> Option<Background> {
|
fn background(&self) -> Option<Background> {
|
||||||
if let Ok(background) =
|
Background::try_from(self.path.clone()).ok()
|
||||||
Background::try_from(self.path.clone())
|
|
||||||
{
|
|
||||||
Some(background)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subtext(&self) -> String {
|
fn subtext(&self) -> String {
|
||||||
|
|
|
@ -37,17 +37,14 @@ pub fn parse_lisp(value: Value) -> Vec<ServiceItem> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::{
|
use std::{fs::read_to_string, path::PathBuf};
|
||||||
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, Slide, SlideBuilder, TextAlignment,
|
Background, TextAlignment,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
63
src/main.rs
63
src/main.rs
|
@ -1,5 +1,4 @@
|
||||||
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;
|
||||||
|
@ -14,13 +13,12 @@ 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, text_input,
|
button, horizontal_space, nav_bar, search_input, tooltip, Space,
|
||||||
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};
|
||||||
|
@ -130,7 +128,7 @@ enum Message {
|
||||||
EditorToggle(bool),
|
EditorToggle(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
const HEADER_SPACE: u16 = 20;
|
const HEADER_SPACE: u16 = 6;
|
||||||
|
|
||||||
impl cosmic::Application for App {
|
impl cosmic::Application for App {
|
||||||
type Executor = executor::Default;
|
type Executor = executor::Default;
|
||||||
|
@ -300,11 +298,11 @@ impl cosmic::Application for App {
|
||||||
.into()]
|
.into()]
|
||||||
}
|
}
|
||||||
fn header_end(&self) -> Vec<Element<Self::Message>> {
|
fn header_end(&self) -> Vec<Element<Self::Message>> {
|
||||||
let editor_toggle = toggler(self.editor_mode.is_some())
|
// let editor_toggle = toggler(self.editor_mode.is_some())
|
||||||
.label("Editor")
|
// .label("Editor")
|
||||||
.spacing(10)
|
// .spacing(10)
|
||||||
.width(Length::Shrink)
|
// .width(Length::Shrink)
|
||||||
.on_toggle(Message::EditorToggle);
|
// .on_toggle(Message::EditorToggle);
|
||||||
|
|
||||||
let presenter_window = self.windows.get(1);
|
let presenter_window = self.windows.get(1);
|
||||||
let text = if self.presentation_open {
|
let text = if self.presentation_open {
|
||||||
|
@ -314,7 +312,30 @@ impl cosmic::Application for App {
|
||||||
};
|
};
|
||||||
|
|
||||||
vec![
|
vec![
|
||||||
editor_toggle.into(),
|
tooltip(
|
||||||
|
button::custom(
|
||||||
|
row!(
|
||||||
|
Container::new(
|
||||||
|
icon::from_name("document-edit-symbolic")
|
||||||
|
.scale(3)
|
||||||
|
)
|
||||||
|
.center_y(Length::Fill),
|
||||||
|
text::body(if self.editor_mode.is_some() {
|
||||||
|
"Present Mode"
|
||||||
|
} else {
|
||||||
|
"Edit Mode"
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.spacing(5),
|
||||||
|
)
|
||||||
|
.class(cosmic::theme::style::Button::HeaderBar)
|
||||||
|
.on_press(Message::EditorToggle(
|
||||||
|
self.editor_mode.is_none(),
|
||||||
|
)),
|
||||||
|
"Enter Edit Mode",
|
||||||
|
TPosition::Bottom,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
horizontal_space().width(HEADER_SPACE).into(),
|
horizontal_space().width(HEADER_SPACE).into(),
|
||||||
tooltip(
|
tooltip(
|
||||||
button::custom(
|
button::custom(
|
||||||
|
@ -353,12 +374,8 @@ impl cosmic::Application for App {
|
||||||
button::custom(
|
button::custom(
|
||||||
row!(
|
row!(
|
||||||
Container::new(
|
Container::new(
|
||||||
icon::from_name(if self.library_open {
|
icon::from_name("view-list-symbolic")
|
||||||
"arrow-right"
|
.scale(3)
|
||||||
} else {
|
|
||||||
"view-list-symbolic"
|
|
||||||
})
|
|
||||||
.scale(3)
|
|
||||||
)
|
)
|
||||||
.center_y(Length::Fill),
|
.center_y(Length::Fill),
|
||||||
text::body(if self.library_open {
|
text::body(if self.library_open {
|
||||||
|
@ -702,7 +719,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(|m| Message::Library(m))
|
library.view().map(Message::Library)
|
||||||
} else {
|
} else {
|
||||||
Space::new(0, 0).into()
|
Space::new(0, 0).into()
|
||||||
})
|
})
|
||||||
|
@ -713,7 +730,7 @@ impl cosmic::Application for App {
|
||||||
};
|
};
|
||||||
|
|
||||||
let song_editor =
|
let song_editor =
|
||||||
self.song_editor.view().map(|m| Message::SongEditor(m));
|
self.song_editor.view().map(Message::SongEditor);
|
||||||
|
|
||||||
let row = row![
|
let row = row![
|
||||||
Container::new(
|
Container::new(
|
||||||
|
@ -727,8 +744,7 @@ 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::Fill)
|
.align_right(Length::FillPortion(1)),
|
||||||
.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)),
|
||||||
|
@ -743,8 +759,7 @@ 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::Fill)
|
.align_left(Length::FillPortion(1)),
|
||||||
.width(Length::FillPortion(1)),
|
|
||||||
library
|
library
|
||||||
]
|
]
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
|
@ -863,8 +878,6 @@ 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,21 +1,22 @@
|
||||||
|
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, Icon, Space, Widget,
|
DndSource, Space, Widget,
|
||||||
},
|
},
|
||||||
Element, Task,
|
Element, Task,
|
||||||
};
|
};
|
||||||
use miette::{miette, IntoDiagnostic, Result};
|
use miette::{IntoDiagnostic, Result};
|
||||||
use sqlx::{
|
use sqlx::{pool::PoolConnection, Sqlite, SqlitePool};
|
||||||
pool::PoolConnection, Sqlite, SqliteConnection, SqlitePool,
|
|
||||||
};
|
|
||||||
use tracing::{debug, error, warn};
|
use tracing::{debug, error, warn};
|
||||||
|
|
||||||
use crate::core::{
|
use crate::core::{
|
||||||
|
@ -359,7 +360,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 =
|
||||||
self.single_item(index, item, model);
|
Box::new(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);
|
||||||
|
@ -386,11 +387,25 @@ impl<'a> Library {
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
.action(DndAction::Copy)
|
.action(DndAction::Copy)
|
||||||
// .drag_icon(move |i| {
|
.drag_icon({
|
||||||
// let state =
|
let model = model.kind.clone();
|
||||||
// drag_item.as_widget().state();
|
move |i| {
|
||||||
// (drag_item, state, i)
|
let state = State::None;
|
||||||
// })
|
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()
|
||||||
})
|
})
|
||||||
|
@ -398,10 +413,11 @@ 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::{miette, IntoDiagnostic, Result};
|
use 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, text,
|
span, stack,
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
widget::{
|
widget::{
|
||||||
|
@ -29,13 +29,10 @@ 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;
|
||||||
|
|
||||||
|
@ -528,39 +525,43 @@ 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<'a>(
|
pub(crate) fn slide_view(
|
||||||
slide: Slide,
|
slide: Slide,
|
||||||
video: &'a Option<Video>,
|
video: &Option<Video>,
|
||||||
font: Font,
|
font: Font,
|
||||||
delegate: bool,
|
delegate: bool,
|
||||||
hide_mouse: bool,
|
hide_mouse: bool,
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'_, 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 font = SvgFont::from(font).size(font_size.floor() as u8);
|
|
||||||
let slide_text = slide.text();
|
let slide_text = slide.text();
|
||||||
|
|
||||||
|
// SVG based
|
||||||
|
// let font = SvgFont::from(font).size(font_size.floor() as u8);
|
||||||
// let text = text_svg::TextSvg::new()
|
// let text = text_svg::TextSvg::new()
|
||||||
// .text(&slide_text)
|
// .text(&slide_text)
|
||||||
// .fill("#fff")
|
// .fill("#fff")
|
||||||
// .shadow(shadow(2, 2, 5, "#000000"))
|
// .shadow(text_svg::shadow(2, 2, 5, "#000000"))
|
||||||
// .stroke(stroke(1, "#000"))
|
// .stroke(text_svg::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.to_string()))
|
rich_text([span(format!("{}\n", t))
|
||||||
.background(
|
.background(
|
||||||
Background::Color(Color::BLACK)
|
Background::Color(Color::BLACK)
|
||||||
.scale_alpha(0.4),
|
.scale_alpha(0.4),
|
||||||
|
@ -573,6 +574,8 @@ pub(crate) fn slide_view<'a>(
|
||||||
})
|
})
|
||||||
.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,8 +31,10 @@ use super::presenter::slide_view;
|
||||||
pub struct SongEditor {
|
pub struct SongEditor {
|
||||||
pub song: Option<Song>,
|
pub song: Option<Song>,
|
||||||
title: String,
|
title: String,
|
||||||
fonts: combo_box::State<String>,
|
font_db: fontdb::Database,
|
||||||
font_sizes: Vec<String>,
|
fonts: Vec<(fontdb::ID, 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,
|
||||||
|
@ -72,42 +74,21 @@ impl SongEditor {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let fonts = font_dir();
|
let fonts = font_dir();
|
||||||
debug!(?fonts);
|
debug!(?fonts);
|
||||||
let mut fontdb = fontdb::Database::new();
|
let mut font_db = fontdb::Database::new();
|
||||||
fontdb.load_system_fonts();
|
font_db.load_system_fonts();
|
||||||
let fonts: Vec<String> = fontdb
|
let mut fonts: Vec<(fontdb::ID, String)> = font_db
|
||||||
.faces()
|
.faces()
|
||||||
.map(|f| {
|
.map(|f| {
|
||||||
let mut font = f.to_owned().post_script_name;
|
let id = f.id;
|
||||||
if let Some(at) = font.find("-") {
|
let font_base_name: String =
|
||||||
let _ = font.split_off(at);
|
f.families.iter().map(|f| f.0.clone()).collect();
|
||||||
}
|
let font_weight = f.weight;
|
||||||
let indices: Vec<usize> = font
|
let font_style = f.style;
|
||||||
.chars()
|
let font_stretch = f.stretch;
|
||||||
.enumerate()
|
(id, font_base_name)
|
||||||
.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"),
|
||||||
|
@ -134,13 +115,16 @@ 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,
|
||||||
fonts: combo_box::State::new(fonts),
|
font_db,
|
||||||
|
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,
|
font_sizes: combo_box::State::new(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,
|
||||||
|
@ -167,7 +151,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_str(" ");
|
s.push(' ');
|
||||||
s
|
s
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -189,11 +173,23 @@ 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::Normal;
|
let weight = Weight::Bold;
|
||||||
let stretch = Stretch::Normal;
|
let stretch = Stretch::Normal;
|
||||||
let style = Style::Normal;
|
let style = Style::Normal;
|
||||||
let font = Font {
|
let font = Font {
|
||||||
|
@ -205,15 +201,7 @@ impl SongEditor {
|
||||||
self.current_font = font;
|
self.current_font = font;
|
||||||
// return self.update_song(song);
|
// return self.update_song(song);
|
||||||
}
|
}
|
||||||
Message::ChangeFontSize(size) => {
|
Message::ChangeFontSize(size) => self.font_size = 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 {
|
||||||
|
@ -227,7 +215,6 @@ 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);
|
||||||
|
@ -305,40 +292,25 @@ impl SongEditor {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(index, slide)| {
|
.map(|(index, slide)| {
|
||||||
let svg = Handle::from_memory(r#"<svg viewBox="0 0 1280 720" xmlns="http://www.w3.org/2000/svg">
|
container(
|
||||||
<defs>
|
slide_view(
|
||||||
<filter id="shadow">
|
slide,
|
||||||
<feDropShadow dx="10" dy="10" stdDeviation="5" flood-color='#000' />
|
if index == 0 {
|
||||||
</filter>
|
&self.video
|
||||||
</defs>
|
} else {
|
||||||
<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);">
|
&None
|
||||||
<tspan x="50%" y="50" >Hello World this is</tspan>
|
},
|
||||||
<tspan x="50%" y="140">longer chunks of text</tspan>
|
self.current_font,
|
||||||
<tspan x="50%" y="230">where we need to test whether the text</tspan>
|
false,
|
||||||
<tspan x="50%" y="320">will look ok!</tspan>
|
false,
|
||||||
</text>
|
|
||||||
</svg>"#.as_bytes());
|
|
||||||
stack!(
|
|
||||||
container(
|
|
||||||
slide_view(
|
|
||||||
slide,
|
|
||||||
if index == 0 {
|
|
||||||
&self.video
|
|
||||||
} else {
|
|
||||||
&None
|
|
||||||
},
|
|
||||||
self.current_font,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.map(|_| Message::None),
|
|
||||||
)
|
)
|
||||||
.height(250)
|
.map(|_| Message::None),
|
||||||
.center_x(Length::Fill)
|
)
|
||||||
.padding([0, 20])
|
.height(250)
|
||||||
.clip(true),
|
.center_x(Length::Fill)
|
||||||
Svg::new(svg),
|
.padding([0, 20])
|
||||||
).into()
|
.clip(true)
|
||||||
|
.into()
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
scrollable(
|
scrollable(
|
||||||
|
@ -396,22 +368,34 @@ 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 = self
|
let selected_font_size = {
|
||||||
.font_sizes
|
let font_size_position = self
|
||||||
.iter()
|
.font_sizes
|
||||||
.position(|s| *s == self.font_size.to_string());
|
.options()
|
||||||
|
.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,
|
&self.fonts_combo,
|
||||||
"Font",
|
"Font",
|
||||||
Some(selected_font),
|
Some(selected_font),
|
||||||
Message::ChangeFont,
|
Message::ChangeFont,
|
||||||
)
|
)
|
||||||
.width(200);
|
.width(200);
|
||||||
let font_size = dropdown(
|
let font_size = combo_box(
|
||||||
&self.font_sizes,
|
&self.font_sizes,
|
||||||
|
"Font Size",
|
||||||
selected_font_size,
|
selected_font_size,
|
||||||
Message::ChangeFontSize,
|
|size| {
|
||||||
);
|
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),
|
||||||
|
@ -427,6 +411,7 @@ order",
|
||||||
horizontal_space(),
|
horizontal_space(),
|
||||||
background_selector
|
background_selector
|
||||||
]
|
]
|
||||||
|
.spacing(10)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
use std::fmt::Display;
|
use std::{
|
||||||
|
fmt::Display,
|
||||||
|
hash::{Hash, Hasher},
|
||||||
|
};
|
||||||
|
|
||||||
use colors_transform::Rgb;
|
use colors_transform::Rgb;
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
|
@ -7,9 +10,9 @@ use cosmic::{
|
||||||
Length,
|
Length,
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
widget::{container, responsive, svg::Handle, Svg},
|
widget::{container, lazy, responsive, svg::Handle, Svg},
|
||||||
};
|
};
|
||||||
use tracing::{debug, error};
|
use tracing::error;
|
||||||
|
|
||||||
use crate::TextAlignment;
|
use crate::TextAlignment;
|
||||||
|
|
||||||
|
@ -21,9 +24,21 @@ pub struct TextSvg {
|
||||||
stroke: Option<Stroke>,
|
stroke: Option<Stroke>,
|
||||||
fill: Color,
|
fill: Color,
|
||||||
alignment: TextAlignment,
|
alignment: TextAlignment,
|
||||||
|
handle: Option<Handle>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
impl Hash for TextSvg {
|
||||||
|
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,
|
||||||
|
@ -61,11 +76,11 @@ impl Font {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_weight(&self) -> Weight {
|
pub fn get_weight(&self) -> Weight {
|
||||||
self.weight.clone()
|
self.weight
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_style(&self) -> Style {
|
pub fn get_style(&self) -> Style {
|
||||||
self.style.clone()
|
self.style
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn weight(mut self, weight: impl Into<Weight>) -> Self {
|
pub fn weight(mut self, weight: impl Into<Weight>) -> Self {
|
||||||
|
@ -92,6 +107,12 @@ 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()) {
|
||||||
|
@ -128,7 +149,7 @@ impl Display for Color {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq)]
|
#[derive(Clone, Debug, Default, PartialEq, Hash)]
|
||||||
pub struct Shadow {
|
pub struct Shadow {
|
||||||
pub offset_x: i16,
|
pub offset_x: i16,
|
||||||
pub offset_y: i16,
|
pub offset_y: i16,
|
||||||
|
@ -136,7 +157,7 @@ pub struct Shadow {
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq)]
|
#[derive(Clone, Debug, Default, PartialEq, Hash)]
|
||||||
pub struct Stroke {
|
pub struct Stroke {
|
||||||
size: u16,
|
size: u16,
|
||||||
color: Color,
|
color: Color,
|
||||||
|
@ -153,6 +174,8 @@ 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
|
||||||
|
@ -226,12 +249,11 @@ 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(
|
||||||
Svg::new(Handle::from_memory(
|
Box::leak(<std::string::String as Clone>::clone(&final_svg).into_boxed_str()).as_bytes(),
|
||||||
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,6 +1,7 @@
|
||||||
#+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…
Reference in a new issue