Compare commits
11 commits
master
...
model-remo
| Author | SHA1 | Date | |
|---|---|---|---|
| 914b2237ab | |||
| 4c0123e94e | |||
| 4ad86785e5 | |||
| 1f38ca9406 | |||
| fbc4606471 | |||
| ae1f2830e4 | |||
| 588ef0df56 | |||
| 69410e3b6e | |||
| 896eda5b9d | |||
| b05f29d7b5 | |||
| 11cca05de4 |
26 changed files with 3140 additions and 2711 deletions
3693
Cargo.lock
generated
3693
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -54,11 +54,11 @@ serde_json = "1.0.149"
|
||||||
[dependencies.libcosmic]
|
[dependencies.libcosmic]
|
||||||
git = "https://github.com/pop-os/libcosmic"
|
git = "https://github.com/pop-os/libcosmic"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["debug", "winit", "desktop", "winit_wgpu", "winit_tokio", "tokio", "wayland", "rfd", "dbus-config", "a11y", "wgpu", "multi-window", "process"]
|
features = ["debug", "winit", "desktop", "wayland", "tokio", "rfd", "dbus-config", "a11y", "wgpu", "multi-window", "process"]
|
||||||
|
|
||||||
[dependencies.iced_video_player]
|
[dependencies.iced_video_player]
|
||||||
git = "https://github.com/jackpot51/iced_video_player.git"
|
git = "https://github.com/wash2/iced_video_player.git"
|
||||||
branch = "cosmic"
|
branch = "iced-rebase"
|
||||||
features = ["wgpu"]
|
features = ["wgpu"]
|
||||||
|
|
||||||
# [profile.dev]
|
# [profile.dev]
|
||||||
|
|
|
||||||
2
TODO.org
2
TODO.org
|
|
@ -12,6 +12,8 @@ This is working but the right click context menu is all the way on the edge of t
|
||||||
|
|
||||||
Let's build some tests that ensure that these functions are working for the models. Make sure the models are built in such a way as to make sure that they are testable and work fast for the user.
|
Let's build some tests that ensure that these functions are working for the models. Make sure the models are built in such a way as to make sure that they are testable and work fast for the user.
|
||||||
|
|
||||||
|
Need to make some tests in the library module that can run things without needing messages as much. I'm redoing how the models work too such that the adding, updating, and removing of items all happens in just the model without needing to have a separate function to do the same action in the database.
|
||||||
|
|
||||||
* TODO [#B] Font in the song editor doesn't always use the original version
|
* TODO [#B] Font in the song editor doesn't always use the original version
|
||||||
There seems to be some issue with fontdb not able to decipher all the versions of some fonts that are OTF and then end up loading the wrong ones in some issues.
|
There seems to be some issue with fontdb not able to decipher all the versions of some fonts that are OTF and then end up loading the wrong ones in some issues.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@
|
||||||
libGL
|
libGL
|
||||||
cargo-flamegraph
|
cargo-flamegraph
|
||||||
bacon
|
bacon
|
||||||
|
openssl
|
||||||
|
|
||||||
fontconfig
|
fontconfig
|
||||||
glib
|
glib
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,17 @@ use super::{
|
||||||
service_items::ServiceTrait,
|
service_items::ServiceTrait,
|
||||||
};
|
};
|
||||||
use crisp::types::{Keyword, Symbol, Value};
|
use crisp::types::{Keyword, Symbol, Value};
|
||||||
use miette::{IntoDiagnostic, Result};
|
use miette::{IntoDiagnostic, Result, miette};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::{
|
use sqlx::{
|
||||||
Sqlite, SqliteConnection, SqlitePool, pool::PoolConnection,
|
Sqlite, SqliteConnection, SqlitePool, pool::PoolConnection,
|
||||||
query, query_as,
|
query, query_as,
|
||||||
};
|
};
|
||||||
use std::path::{Path, PathBuf};
|
use std::{
|
||||||
|
mem::replace,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
|
|
@ -156,6 +160,69 @@ impl ServiceTrait for Image {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Model<Image> {
|
impl Model<Image> {
|
||||||
|
pub async fn append_image(
|
||||||
|
&mut self,
|
||||||
|
image: Image,
|
||||||
|
db: &SqlitePool,
|
||||||
|
) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn new_image(
|
||||||
|
&mut self,
|
||||||
|
db: &SqlitePool,
|
||||||
|
) -> Result<Image> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
pub async fn update_image(
|
||||||
|
&mut self,
|
||||||
|
image: Image,
|
||||||
|
db: &SqlitePool,
|
||||||
|
) -> Result<()> {
|
||||||
|
let id = image.id;
|
||||||
|
self.update_item(image.clone(), |current_image| {
|
||||||
|
current_image.id == id
|
||||||
|
})?;
|
||||||
|
let path = image
|
||||||
|
.path
|
||||||
|
.to_str()
|
||||||
|
.map(std::string::ToString::to_string)
|
||||||
|
.unwrap_or_default();
|
||||||
|
debug!(?image, "should be been updated");
|
||||||
|
let result = query!(
|
||||||
|
r#"UPDATE images SET title = $2, file_path = $3 WHERE id = $1"#,
|
||||||
|
image.id,
|
||||||
|
image.title,
|
||||||
|
path,
|
||||||
|
)
|
||||||
|
.execute(db)
|
||||||
|
.await.into_diagnostic();
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(_) => {
|
||||||
|
debug!("should have been updated");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error! {?e};
|
||||||
|
Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn remove_image(
|
||||||
|
&mut self,
|
||||||
|
id: i32,
|
||||||
|
db: &SqlitePool,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.remove_item(|image| image.id == id)?;
|
||||||
|
query!("DELETE FROM images WHERE id = $1", id)
|
||||||
|
.execute(db)
|
||||||
|
.await
|
||||||
|
.into_diagnostic()
|
||||||
|
.map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn new_image_model(db: &mut SqlitePool) -> Self {
|
pub async fn new_image_model(db: &mut SqlitePool) -> Self {
|
||||||
let mut model = Self {
|
let mut model = Self {
|
||||||
items: vec![],
|
items: vec![],
|
||||||
|
|
@ -191,70 +258,84 @@ impl Model<Image> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn remove_from_db(
|
pub async fn remove_from_db(
|
||||||
db: PoolConnection<Sqlite>,
|
db: Arc<SqlitePool>,
|
||||||
|
mut images: Vec<Image>,
|
||||||
id: i32,
|
id: i32,
|
||||||
) -> Result<()> {
|
) -> Result<Vec<Image>> {
|
||||||
query!("DELETE FROM images WHERE id = $1", id)
|
query!("DELETE FROM images WHERE id = $1", id)
|
||||||
.execute(&mut db.detach())
|
.execute(&*db)
|
||||||
.await
|
.await
|
||||||
.into_diagnostic()
|
.into_diagnostic()
|
||||||
.map(|_| ())
|
.map(|_| ())?;
|
||||||
|
let index = images
|
||||||
|
.iter()
|
||||||
|
.position(|current_image| current_image.id == id)
|
||||||
|
.ok_or_else(|| miette!("Could not find image in model"))?;
|
||||||
|
images.remove(index);
|
||||||
|
Ok(images)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn add_image_to_db(
|
pub async fn add_to_db(
|
||||||
image: Image,
|
new_images: Vec<Image>,
|
||||||
db: PoolConnection<Sqlite>,
|
mut current_images: Vec<Image>,
|
||||||
) -> Result<()> {
|
db: Arc<SqlitePool>,
|
||||||
let path = image
|
) -> Result<Vec<Image>> {
|
||||||
.path
|
for image in new_images {
|
||||||
.to_str()
|
let path = image
|
||||||
.map(std::string::ToString::to_string)
|
.path
|
||||||
.unwrap_or_default();
|
.to_str()
|
||||||
let mut db = db.detach();
|
.map(std::string::ToString::to_string)
|
||||||
query!(
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
query!(
|
||||||
r#"INSERT INTO images (title, file_path) VALUES ($1, $2)"#,
|
r#"INSERT INTO images (title, file_path) VALUES ($1, $2)"#,
|
||||||
image.title,
|
image.title,
|
||||||
path,
|
path,
|
||||||
)
|
)
|
||||||
.execute(&mut db)
|
.execute(&*db)
|
||||||
.await
|
.await
|
||||||
.into_diagnostic()?;
|
.into_diagnostic()?;
|
||||||
Ok(())
|
|
||||||
|
current_images.push(image);
|
||||||
|
}
|
||||||
|
Ok(current_images)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_image_in_db(
|
pub async fn update_in_db(
|
||||||
image: Image,
|
image: Image,
|
||||||
db: PoolConnection<Sqlite>,
|
mut images: Vec<Image>,
|
||||||
) -> Result<()> {
|
db: Arc<SqlitePool>,
|
||||||
|
) -> Result<Vec<Image>> {
|
||||||
let path = image
|
let path = image
|
||||||
.path
|
.path
|
||||||
.to_str()
|
.to_str()
|
||||||
.map(std::string::ToString::to_string)
|
.map(std::string::ToString::to_string)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let mut db = db.detach();
|
|
||||||
debug!(?image, "should be been updated");
|
query!(
|
||||||
let result = query!(
|
|
||||||
r#"UPDATE images SET title = $2, file_path = $3 WHERE id = $1"#,
|
r#"UPDATE images SET title = $2, file_path = $3 WHERE id = $1"#,
|
||||||
image.id,
|
image.id,
|
||||||
image.title,
|
image.title,
|
||||||
path,
|
path,
|
||||||
)
|
)
|
||||||
.execute(&mut db)
|
.execute(&*db)
|
||||||
.await.into_diagnostic();
|
.await.into_diagnostic()?;
|
||||||
|
|
||||||
match result {
|
let current_image = images
|
||||||
Ok(_) => {
|
.iter()
|
||||||
debug!("should have been updated");
|
.position(|current_image| current_image.id == image.id)
|
||||||
Ok(())
|
.ok_or_else(|| miette!("Could not find image in model"))
|
||||||
}
|
.map(|index| {
|
||||||
Err(e) => {
|
images
|
||||||
error! {?e};
|
.get_mut(index)
|
||||||
Err(e)
|
.expect("We should have this image already")
|
||||||
}
|
})?;
|
||||||
}
|
|
||||||
|
replace(current_image, image);
|
||||||
|
Ok(images)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_image_from_db(
|
pub async fn get_from_db(
|
||||||
database_id: i32,
|
database_id: i32,
|
||||||
db: &mut SqliteConnection,
|
db: &mut SqliteConnection,
|
||||||
) -> Result<Image> {
|
) -> Result<Image> {
|
||||||
|
|
|
||||||
|
|
@ -83,30 +83,35 @@ impl<T> Model<T> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_item(&mut self, item: T, index: i32) -> Result<()> {
|
pub fn update_item<P>(
|
||||||
|
&mut self,
|
||||||
|
item: T,
|
||||||
|
predicate: P,
|
||||||
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
P: Fn(&T) -> bool,
|
||||||
|
{
|
||||||
self.items
|
self.items
|
||||||
.get_mut(
|
.iter()
|
||||||
usize::try_from(index)
|
.position(predicate)
|
||||||
.expect("Shouldn't be negative"),
|
.ok_or(miette!("Item cannot be found"))
|
||||||
)
|
.map(|index| self.items.get_mut(index).expect("Since we found position this should always exist"))
|
||||||
.map_or_else(
|
.map(|current_item| {
|
||||||
|| {
|
let _old_item = replace(current_item, item);
|
||||||
Err(miette!(
|
})
|
||||||
"Item doesn't exist in model. Id was {index}"
|
|
||||||
))
|
|
||||||
},
|
|
||||||
|current_item| {
|
|
||||||
let _old_item = replace(current_item, item);
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_item(&mut self, index: i32) -> Result<()> {
|
pub fn remove_item<P>(&mut self, predicate: P) -> Result<()>
|
||||||
self.items.remove(
|
where
|
||||||
usize::try_from(index).expect("Shouldn't be negative"),
|
P: Fn(&T) -> bool,
|
||||||
);
|
{
|
||||||
Ok(())
|
self.items
|
||||||
|
.iter()
|
||||||
|
.position(predicate)
|
||||||
|
.ok_or(miette!("Item cannot be found"))
|
||||||
|
.map(|index| {
|
||||||
|
self.items.remove(index);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
|
@ -123,11 +128,12 @@ impl<T> Model<T> {
|
||||||
self.items.iter().find(f)
|
self.items.iter().find(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_item(&mut self, item: T, index: i32) -> Result<()> {
|
pub fn insert_item(
|
||||||
self.items.insert(
|
&mut self,
|
||||||
usize::try_from(index).expect("Shouldn't be negative"),
|
item: T,
|
||||||
item,
|
index: usize,
|
||||||
);
|
) -> Result<()> {
|
||||||
|
self.items.insert(index, item);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
use cosmic::widget::image::Handle;
|
use cosmic::widget::image::Handle;
|
||||||
use crisp::types::{Keyword, Symbol, Value};
|
use crisp::types::{Keyword, Symbol, Value};
|
||||||
use miette::{IntoDiagnostic, Result};
|
use miette::{IntoDiagnostic, Result, miette};
|
||||||
use mupdf::{Colorspace, Document, Matrix};
|
use mupdf::{Colorspace, Document, Matrix};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::{
|
use sqlx::{
|
||||||
Row, Sqlite, SqliteConnection, SqlitePool, pool::PoolConnection,
|
Row, Sqlite, SqliteConnection, SqlitePool, pool::PoolConnection,
|
||||||
prelude::FromRow, query, sqlite::SqliteRow,
|
prelude::FromRow, query, sqlite::SqliteRow,
|
||||||
};
|
};
|
||||||
use std::path::{Path, PathBuf};
|
use std::{
|
||||||
|
mem::replace,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
|
|
||||||
use crate::{Background, Slide, SlideBuilder, TextAlignment};
|
use crate::{Background, Slide, SlideBuilder, TextAlignment};
|
||||||
|
|
@ -298,6 +302,35 @@ impl FromRow<'_, SqliteRow> for Presentation {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Model<Presentation> {
|
impl Model<Presentation> {
|
||||||
|
pub async fn append_presentation(
|
||||||
|
&mut self,
|
||||||
|
presentation: Presentation,
|
||||||
|
db: &SqlitePool,
|
||||||
|
) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn new_presentation(
|
||||||
|
&mut self,
|
||||||
|
db: &SqlitePool,
|
||||||
|
) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_presentation(
|
||||||
|
&mut self,
|
||||||
|
presentation: Presentation,
|
||||||
|
db: &SqlitePool,
|
||||||
|
) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
pub async fn remove_presentation(
|
||||||
|
&mut self,
|
||||||
|
id: i32,
|
||||||
|
db: &SqlitePool,
|
||||||
|
) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
pub async fn new_presentation_model(db: &mut SqlitePool) -> Self {
|
pub async fn new_presentation_model(db: &mut SqlitePool) -> Self {
|
||||||
let mut model = Self {
|
let mut model = Self {
|
||||||
items: vec![],
|
items: vec![],
|
||||||
|
|
@ -377,61 +410,77 @@ impl Model<Presentation> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn remove_from_db(
|
pub async fn remove_from_db(
|
||||||
db: PoolConnection<Sqlite>,
|
db: Arc<SqlitePool>,
|
||||||
|
mut presentations: Vec<Presentation>,
|
||||||
id: i32,
|
id: i32,
|
||||||
) -> Result<()> {
|
) -> Result<Vec<Presentation>> {
|
||||||
query!("DELETE FROM presentations WHERE id = $1", id)
|
query!("DELETE FROM presentations WHERE id = $1", id)
|
||||||
.execute(&mut db.detach())
|
.execute(&*db)
|
||||||
.await
|
.await
|
||||||
.into_diagnostic()
|
.into_diagnostic()
|
||||||
.map(|_| ())
|
.map(|_| ())?;
|
||||||
|
|
||||||
|
let index = presentations
|
||||||
|
.iter()
|
||||||
|
.position(|current_presentation| {
|
||||||
|
current_presentation.id == id
|
||||||
|
})
|
||||||
|
.ok_or_else(|| {
|
||||||
|
miette!("Could not find presentation in model")
|
||||||
|
})?;
|
||||||
|
presentations.remove(index);
|
||||||
|
Ok(presentations)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn add_presentation_to_db(
|
pub async fn add_to_db(
|
||||||
|
new_presentations: Vec<Presentation>,
|
||||||
|
mut current_presentations: Vec<Presentation>,
|
||||||
|
db: Arc<SqlitePool>,
|
||||||
|
) -> Result<Vec<Presentation>> {
|
||||||
|
for presentation in new_presentations {
|
||||||
|
let path = presentation
|
||||||
|
.path
|
||||||
|
.to_str()
|
||||||
|
.map(std::string::ToString::to_string)
|
||||||
|
.unwrap_or_default();
|
||||||
|
let html = presentation.kind == PresKind::Html;
|
||||||
|
let (starting_index, ending_index) = if let PresKind::Pdf {
|
||||||
|
starting_index,
|
||||||
|
ending_index,
|
||||||
|
} = presentation.kind
|
||||||
|
{
|
||||||
|
(starting_index, ending_index)
|
||||||
|
} else {
|
||||||
|
(0, 0)
|
||||||
|
};
|
||||||
|
query!(
|
||||||
|
r#"INSERT INTO presentations (title, file_path, html, starting_index, ending_index) VALUES ($1, $2, $3, $4, $5)"#,
|
||||||
|
presentation.title,
|
||||||
|
path,
|
||||||
|
html,
|
||||||
|
starting_index,
|
||||||
|
ending_index
|
||||||
|
)
|
||||||
|
.execute(&*db)
|
||||||
|
.await
|
||||||
|
.into_diagnostic()?;
|
||||||
|
|
||||||
|
current_presentations.push(presentation);
|
||||||
|
}
|
||||||
|
Ok(current_presentations)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_in_db(
|
||||||
presentation: Presentation,
|
presentation: Presentation,
|
||||||
db: PoolConnection<Sqlite>,
|
mut presentations: Vec<Presentation>,
|
||||||
) -> Result<()> {
|
db: Arc<SqlitePool>,
|
||||||
|
) -> Result<Vec<Presentation>> {
|
||||||
let path = presentation
|
let path = presentation
|
||||||
.path
|
.path
|
||||||
.to_str()
|
.to_str()
|
||||||
.map(std::string::ToString::to_string)
|
.map(std::string::ToString::to_string)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let html = presentation.kind == PresKind::Html;
|
let html = presentation.kind == PresKind::Html;
|
||||||
let mut db = db.detach();
|
|
||||||
let (starting_index, ending_index) = if let PresKind::Pdf {
|
|
||||||
starting_index,
|
|
||||||
ending_index,
|
|
||||||
} = presentation.kind
|
|
||||||
{
|
|
||||||
(starting_index, ending_index)
|
|
||||||
} else {
|
|
||||||
(0, 0)
|
|
||||||
};
|
|
||||||
query!(
|
|
||||||
r#"INSERT INTO presentations (title, file_path, html, starting_index, ending_index) VALUES ($1, $2, $3, $4, $5)"#,
|
|
||||||
presentation.title,
|
|
||||||
path,
|
|
||||||
html,
|
|
||||||
starting_index,
|
|
||||||
ending_index
|
|
||||||
)
|
|
||||||
.execute(&mut db)
|
|
||||||
.await
|
|
||||||
.into_diagnostic()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn update_presentation_in_db(
|
|
||||||
presentation: Presentation,
|
|
||||||
db: PoolConnection<Sqlite>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let path = presentation
|
|
||||||
.path
|
|
||||||
.to_str()
|
|
||||||
.map(std::string::ToString::to_string)
|
|
||||||
.unwrap_or_default();
|
|
||||||
let html = presentation.kind == PresKind::Html;
|
|
||||||
let mut db = db.detach();
|
|
||||||
let (starting_index, ending_index) = if let PresKind::Pdf {
|
let (starting_index, ending_index) = if let PresKind::Pdf {
|
||||||
starting_index: s_index,
|
starting_index: s_index,
|
||||||
ending_index: e_index,
|
ending_index: e_index,
|
||||||
|
|
@ -443,53 +492,8 @@ pub async fn update_presentation_in_db(
|
||||||
(0, 0)
|
(0, 0)
|
||||||
};
|
};
|
||||||
debug!(starting_index, ending_index);
|
debug!(starting_index, ending_index);
|
||||||
let id = presentation.id;
|
|
||||||
if let Err(e) =
|
|
||||||
query!("SELECT id FROM presentations where id = $1", id)
|
|
||||||
.fetch_one(&mut db)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
if let Ok(ids) = query!("SELECT id FROM presentations")
|
|
||||||
.fetch_all(&mut db)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
let Some(mut max) = ids.iter().map(|r| r.id).max() else {
|
|
||||||
return Err(miette::miette!("cannot find max id"));
|
|
||||||
};
|
|
||||||
debug!(
|
|
||||||
?e,
|
|
||||||
"Presentation not found adding a new presentation"
|
|
||||||
);
|
|
||||||
max += 1;
|
|
||||||
let result = query!(
|
|
||||||
r#"INSERT into presentations VALUES($1, $2, $3, $4, $5, $6)"#,
|
|
||||||
max,
|
|
||||||
presentation.title,
|
|
||||||
path,
|
|
||||||
html,
|
|
||||||
starting_index,
|
|
||||||
ending_index,
|
|
||||||
)
|
|
||||||
.execute(&mut db)
|
|
||||||
.await
|
|
||||||
.into_diagnostic();
|
|
||||||
|
|
||||||
return match result {
|
query!(
|
||||||
Ok(_) => {
|
|
||||||
debug!("presentation should have been added");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
error! {?e};
|
|
||||||
Err(e)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return Err(miette::miette!("cannot find ids"));
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!(?presentation, "should be been updated");
|
|
||||||
let result = query!(
|
|
||||||
r#"UPDATE presentations SET title = $2, file_path = $3, html = $4, starting_index = $5, ending_index = $6 WHERE id = $1"#,
|
r#"UPDATE presentations SET title = $2, file_path = $3, html = $4, starting_index = $5, ending_index = $6 WHERE id = $1"#,
|
||||||
presentation.id,
|
presentation.id,
|
||||||
presentation.title,
|
presentation.title,
|
||||||
|
|
@ -498,19 +502,25 @@ pub async fn update_presentation_in_db(
|
||||||
starting_index,
|
starting_index,
|
||||||
ending_index
|
ending_index
|
||||||
)
|
)
|
||||||
.execute(&mut db)
|
.execute(&*db)
|
||||||
.await.into_diagnostic();
|
.await.into_diagnostic()?;
|
||||||
|
|
||||||
match result {
|
let current_presentation = presentations
|
||||||
Ok(_) => {
|
.iter()
|
||||||
debug!("should have been updated");
|
.position(|current_presentation| {
|
||||||
Ok(())
|
current_presentation.id == presentation.id
|
||||||
}
|
})
|
||||||
Err(e) => {
|
.ok_or_else(|| {
|
||||||
error! {?e};
|
miette!("Could not find presentation in model")
|
||||||
Err(e)
|
})
|
||||||
}
|
.map(|index| {
|
||||||
}
|
presentations
|
||||||
|
.get_mut(index)
|
||||||
|
.expect("We should have this presentation already")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
replace(current_presentation, presentation);
|
||||||
|
Ok(presentations)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_presentation_from_db(
|
pub async fn get_presentation_from_db(
|
||||||
|
|
|
||||||
|
|
@ -114,10 +114,10 @@ pub async fn get_genius_lyrics(
|
||||||
root.inner_html()
|
root.inner_html()
|
||||||
})
|
})
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
let lyrics = lyrics.find("[").map_or_else(
|
let lyrics = lyrics.find('[').map_or_else(
|
||||||
|| {
|
|| {
|
||||||
lyrics.find("</div></div></div>").map_or(
|
lyrics.find("</div></div></div>").map_or_else(
|
||||||
lyrics.clone(),
|
|| lyrics.clone(),
|
||||||
|position| {
|
|position| {
|
||||||
lyrics.split_at(position + 18).1.to_string()
|
lyrics.split_at(position + 18).1.to_string()
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow, collections::HashMap, option::Option, path::PathBuf,
|
borrow::Cow, collections::HashMap, mem::replace, option::Option,
|
||||||
|
path::PathBuf, sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
|
|
@ -14,9 +15,8 @@ use itertools::Itertools;
|
||||||
use miette::{IntoDiagnostic, Result, miette};
|
use miette::{IntoDiagnostic, Result, miette};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::{
|
use sqlx::{
|
||||||
FromRow, Row, Sqlite, SqliteConnection, SqliteExecutor,
|
FromRow, Row, Sqlite, SqliteConnection, SqlitePool,
|
||||||
SqlitePool, Transaction, pool::PoolConnection, query,
|
pool::PoolConnection, query, sqlite::SqliteRow,
|
||||||
sqlite::SqliteRow,
|
|
||||||
};
|
};
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
|
|
||||||
|
|
@ -740,6 +740,187 @@ pub async fn get_song_from_db(
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Model<Song> {
|
impl Model<Song> {
|
||||||
|
// Not sure we will use this function. As it is, it makes more sense for
|
||||||
|
// a new song to be made within the model and then passed back out.
|
||||||
|
// But maybe for encapsulation reasons, it makes sense to have this?
|
||||||
|
pub async fn append_song(
|
||||||
|
&mut self,
|
||||||
|
song: Song,
|
||||||
|
db: &SqlitePool,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.add_item(song)?;
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn new_song(
|
||||||
|
&mut self,
|
||||||
|
db: Arc<SqlitePool>,
|
||||||
|
) -> Result<Song> {
|
||||||
|
let mut song = Song::default();
|
||||||
|
|
||||||
|
let verse_order = {
|
||||||
|
song.verse_order.clone().map_or_else(String::new, |vo| {
|
||||||
|
vo.into_iter()
|
||||||
|
.map(|mut s| {
|
||||||
|
s.push(' ');
|
||||||
|
s
|
||||||
|
})
|
||||||
|
.collect::<String>()
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let audio = song
|
||||||
|
.audio
|
||||||
|
.clone()
|
||||||
|
.map(|a| a.to_str().unwrap_or_default().to_string());
|
||||||
|
|
||||||
|
let background = song
|
||||||
|
.background
|
||||||
|
.clone()
|
||||||
|
.map(|b| b.path.to_str().unwrap_or_default().to_string());
|
||||||
|
|
||||||
|
let res = query!(
|
||||||
|
r#"INSERT INTO songs (title, lyrics, author, ccli, verse_order, audio, font, font_size, background) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)"#,
|
||||||
|
song.title,
|
||||||
|
song.lyrics,
|
||||||
|
song.author,
|
||||||
|
song.ccli,
|
||||||
|
verse_order,
|
||||||
|
audio,
|
||||||
|
song.font,
|
||||||
|
song.font_size,
|
||||||
|
background
|
||||||
|
)
|
||||||
|
.execute(&*db)
|
||||||
|
.await
|
||||||
|
.into_diagnostic()?;
|
||||||
|
song.id = i32::try_from(res.last_insert_rowid()).expect(
|
||||||
|
"Fairly confident that this number won't get that high",
|
||||||
|
);
|
||||||
|
self.add_item(song.clone())?;
|
||||||
|
Ok(song)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_song(
|
||||||
|
&mut self,
|
||||||
|
song: Song,
|
||||||
|
db: &SqlitePool,
|
||||||
|
) -> Result<()> {
|
||||||
|
let id = song.id;
|
||||||
|
self.update_item(song.clone(), |song| song.id == id)?;
|
||||||
|
// debug!(?item);
|
||||||
|
let verse_order =
|
||||||
|
ron::ser::to_string(&song.verses).into_diagnostic()?;
|
||||||
|
|
||||||
|
let audio = song
|
||||||
|
.audio
|
||||||
|
.map(|a| a.to_str().unwrap_or_default().to_string());
|
||||||
|
|
||||||
|
let background = song
|
||||||
|
.background
|
||||||
|
.map(|b| b.path.to_str().unwrap_or_default().to_string());
|
||||||
|
|
||||||
|
let lyrics = song.verse_map.map(|map| {
|
||||||
|
map.iter()
|
||||||
|
.map(|(name, lyric)| {
|
||||||
|
let lyric =
|
||||||
|
lyric.trim_end_matches('\n').to_string();
|
||||||
|
(name.to_owned(), lyric)
|
||||||
|
})
|
||||||
|
.collect::<HashMap<VerseName, String>>()
|
||||||
|
});
|
||||||
|
let lyrics =
|
||||||
|
ron::ser::to_string(&lyrics).into_diagnostic()?;
|
||||||
|
|
||||||
|
let (vertical_alignment, horizontal_alignment) =
|
||||||
|
song.text_alignment.map_or_else(
|
||||||
|
|| ("center", "center"),
|
||||||
|
|ta| match ta {
|
||||||
|
TextAlignment::TopLeft => ("top", "left"),
|
||||||
|
TextAlignment::TopCenter => ("top", "center"),
|
||||||
|
TextAlignment::TopRight => ("top", "right"),
|
||||||
|
TextAlignment::MiddleLeft => ("center", "left"),
|
||||||
|
TextAlignment::MiddleCenter => {
|
||||||
|
("center", "center")
|
||||||
|
}
|
||||||
|
TextAlignment::MiddleRight => ("center", "right"),
|
||||||
|
TextAlignment::BottomLeft => ("bottom", "left"),
|
||||||
|
TextAlignment::BottomCenter => {
|
||||||
|
("bottom", "center")
|
||||||
|
}
|
||||||
|
TextAlignment::BottomRight => ("bottom", "right"),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let stroke_size = song.stroke_size.unwrap_or_default();
|
||||||
|
let shadow_size = song.shadow_size.unwrap_or_default();
|
||||||
|
let (shadow_offset_x, shadow_offset_y) =
|
||||||
|
song.shadow_offset.unwrap_or_default();
|
||||||
|
|
||||||
|
let stroke_color = ron::ser::to_string(&song.stroke_color)
|
||||||
|
.into_diagnostic()?;
|
||||||
|
let shadow_color = ron::ser::to_string(&song.shadow_color)
|
||||||
|
.into_diagnostic()?;
|
||||||
|
|
||||||
|
let style = ron::ser::to_string(&song.font_style)
|
||||||
|
.into_diagnostic()?;
|
||||||
|
let weight = ron::ser::to_string(&song.font_weight)
|
||||||
|
.into_diagnostic()?;
|
||||||
|
|
||||||
|
// debug!(
|
||||||
|
// ?stroke_size,
|
||||||
|
// ?stroke_color,
|
||||||
|
// ?shadow_size,
|
||||||
|
// ?shadow_color,
|
||||||
|
// ?shadow_offset_x,
|
||||||
|
// ?shadow_offset_y
|
||||||
|
// );
|
||||||
|
|
||||||
|
let result = query!(
|
||||||
|
r#"UPDATE songs SET title = $2, lyrics = $3, author = $4, ccli = $5, verse_order = $6, audio = $7, font = $8, font_size = $9, background = $10, horizontal_text_alignment = $11, vertical_text_alignment = $12, stroke_color = $13, shadow_color = $14, stroke_size = $15, shadow_size = $16, shadow_offset_x = $17, shadow_offset_y = $18, style = $19, weight = $20 WHERE id = $1"#,
|
||||||
|
song.id,
|
||||||
|
song.title,
|
||||||
|
lyrics,
|
||||||
|
song.author,
|
||||||
|
song.ccli,
|
||||||
|
verse_order,
|
||||||
|
audio,
|
||||||
|
song.font,
|
||||||
|
song.font_size,
|
||||||
|
background,
|
||||||
|
horizontal_alignment,
|
||||||
|
vertical_alignment,
|
||||||
|
stroke_color,
|
||||||
|
shadow_color,
|
||||||
|
stroke_size,
|
||||||
|
shadow_size,
|
||||||
|
shadow_offset_x,
|
||||||
|
shadow_offset_y,
|
||||||
|
style,
|
||||||
|
weight
|
||||||
|
)
|
||||||
|
.execute(db)
|
||||||
|
.await
|
||||||
|
.into_diagnostic()?;
|
||||||
|
|
||||||
|
debug!(rows_affected = ?result.rows_affected());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn remove_song(
|
||||||
|
&mut self,
|
||||||
|
id: i32,
|
||||||
|
db: &SqlitePool,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.remove_item(|current_song| id == current_song.id)?;
|
||||||
|
query!("DELETE FROM songs WHERE id = $1", id)
|
||||||
|
.execute(db)
|
||||||
|
.await
|
||||||
|
.into_diagnostic()
|
||||||
|
.map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn new_song_model(db: &mut SqlitePool) -> Self {
|
pub async fn new_song_model(db: &mut SqlitePool) -> Self {
|
||||||
let mut model = Self {
|
let mut model = Self {
|
||||||
items: vec![],
|
items: vec![],
|
||||||
|
|
@ -779,19 +960,28 @@ impl Model<Song> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn remove_from_db(
|
pub async fn remove_from_db(
|
||||||
db: PoolConnection<Sqlite>,
|
db: Arc<SqlitePool>,
|
||||||
|
mut songs: Vec<Song>,
|
||||||
id: i32,
|
id: i32,
|
||||||
) -> Result<()> {
|
) -> Result<Vec<Song>> {
|
||||||
query!("DELETE FROM songs WHERE id = $1", id)
|
if let Some(index) =
|
||||||
.execute(&mut db.detach())
|
songs.iter().position(|current_song| current_song.id == id)
|
||||||
.await
|
{
|
||||||
.into_diagnostic()
|
songs.remove(index);
|
||||||
.map(|_| ())
|
query!("DELETE FROM songs WHERE id = $1", id)
|
||||||
|
.execute(&*db)
|
||||||
|
.await
|
||||||
|
.into_diagnostic()
|
||||||
|
.map(|_| songs)
|
||||||
|
} else {
|
||||||
|
Err(miette!("Couldn't find song in model"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn add_song_to_db(
|
pub async fn add_to_db(
|
||||||
db: PoolConnection<Sqlite>,
|
mut songs: Vec<Song>,
|
||||||
) -> Result<Song> {
|
db: Arc<SqlitePool>,
|
||||||
|
) -> Result<Vec<Song>> {
|
||||||
let mut song = Song::default();
|
let mut song = Song::default();
|
||||||
|
|
||||||
let verse_order = {
|
let verse_order = {
|
||||||
|
|
@ -827,34 +1017,38 @@ pub async fn add_song_to_db(
|
||||||
song.font_size,
|
song.font_size,
|
||||||
background
|
background
|
||||||
)
|
)
|
||||||
.execute(&mut db.detach())
|
.execute(&*db)
|
||||||
.await
|
.await
|
||||||
.into_diagnostic()?;
|
.into_diagnostic()?;
|
||||||
song.id = i32::try_from(res.last_insert_rowid()).expect(
|
song.id = i32::try_from(res.last_insert_rowid()).expect(
|
||||||
"Fairly confident that this number won't get that high",
|
"Fairly confident that this number won't get that high",
|
||||||
);
|
);
|
||||||
Ok(song)
|
songs.push(song);
|
||||||
|
Ok(songs)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_song_in_db(
|
pub async fn update_in_db(
|
||||||
item: Song,
|
song: Song,
|
||||||
db: PoolConnection<Sqlite>,
|
mut songs: Vec<Song>,
|
||||||
) -> Result<()> {
|
db: Arc<SqlitePool>,
|
||||||
|
) -> Result<Vec<Song>> {
|
||||||
// self.update_item(item.clone(), index)?;
|
// self.update_item(item.clone(), index)?;
|
||||||
|
|
||||||
// debug!(?item);
|
// debug!(?item);
|
||||||
let verse_order =
|
let verse_order =
|
||||||
ron::ser::to_string(&item.verses).into_diagnostic()?;
|
ron::ser::to_string(&song.verses).into_diagnostic()?;
|
||||||
|
|
||||||
let audio = item
|
let audio = song
|
||||||
.audio
|
.audio
|
||||||
|
.clone()
|
||||||
.map(|a| a.to_str().unwrap_or_default().to_string());
|
.map(|a| a.to_str().unwrap_or_default().to_string());
|
||||||
|
|
||||||
let background = item
|
let background = song
|
||||||
.background
|
.background
|
||||||
|
.clone()
|
||||||
.map(|b| b.path.to_str().unwrap_or_default().to_string());
|
.map(|b| b.path.to_str().unwrap_or_default().to_string());
|
||||||
|
|
||||||
let lyrics = item.verse_map.map(|map| {
|
let lyrics = song.verse_map.clone().map(|map| {
|
||||||
map.iter()
|
map.iter()
|
||||||
.map(|(name, lyric)| {
|
.map(|(name, lyric)| {
|
||||||
let lyric = lyric.trim_end_matches('\n').to_string();
|
let lyric = lyric.trim_end_matches('\n').to_string();
|
||||||
|
|
@ -865,7 +1059,7 @@ pub async fn update_song_in_db(
|
||||||
let lyrics = ron::ser::to_string(&lyrics).into_diagnostic()?;
|
let lyrics = ron::ser::to_string(&lyrics).into_diagnostic()?;
|
||||||
|
|
||||||
let (vertical_alignment, horizontal_alignment) =
|
let (vertical_alignment, horizontal_alignment) =
|
||||||
item.text_alignment.map_or_else(
|
song.text_alignment.map_or_else(
|
||||||
|| ("center", "center"),
|
|| ("center", "center"),
|
||||||
|ta| match ta {
|
|ta| match ta {
|
||||||
TextAlignment::TopLeft => ("top", "left"),
|
TextAlignment::TopLeft => ("top", "left"),
|
||||||
|
|
@ -880,20 +1074,20 @@ pub async fn update_song_in_db(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let stroke_size = item.stroke_size.unwrap_or_default();
|
let stroke_size = song.stroke_size.unwrap_or_default();
|
||||||
let shadow_size = item.shadow_size.unwrap_or_default();
|
let shadow_size = song.shadow_size.unwrap_or_default();
|
||||||
let (shadow_offset_x, shadow_offset_y) =
|
let (shadow_offset_x, shadow_offset_y) =
|
||||||
item.shadow_offset.unwrap_or_default();
|
song.shadow_offset.unwrap_or_default();
|
||||||
|
|
||||||
let stroke_color =
|
let stroke_color =
|
||||||
ron::ser::to_string(&item.stroke_color).into_diagnostic()?;
|
ron::ser::to_string(&song.stroke_color).into_diagnostic()?;
|
||||||
let shadow_color =
|
let shadow_color =
|
||||||
ron::ser::to_string(&item.shadow_color).into_diagnostic()?;
|
ron::ser::to_string(&song.shadow_color).into_diagnostic()?;
|
||||||
|
|
||||||
let style =
|
let style =
|
||||||
ron::ser::to_string(&item.font_style).into_diagnostic()?;
|
ron::ser::to_string(&song.font_style).into_diagnostic()?;
|
||||||
let weight =
|
let weight =
|
||||||
ron::ser::to_string(&item.font_weight).into_diagnostic()?;
|
ron::ser::to_string(&song.font_weight).into_diagnostic()?;
|
||||||
|
|
||||||
// debug!(
|
// debug!(
|
||||||
// ?stroke_size,
|
// ?stroke_size,
|
||||||
|
|
@ -906,15 +1100,15 @@ pub async fn update_song_in_db(
|
||||||
|
|
||||||
let result = query!(
|
let result = query!(
|
||||||
r#"UPDATE songs SET title = $2, lyrics = $3, author = $4, ccli = $5, verse_order = $6, audio = $7, font = $8, font_size = $9, background = $10, horizontal_text_alignment = $11, vertical_text_alignment = $12, stroke_color = $13, shadow_color = $14, stroke_size = $15, shadow_size = $16, shadow_offset_x = $17, shadow_offset_y = $18, style = $19, weight = $20 WHERE id = $1"#,
|
r#"UPDATE songs SET title = $2, lyrics = $3, author = $4, ccli = $5, verse_order = $6, audio = $7, font = $8, font_size = $9, background = $10, horizontal_text_alignment = $11, vertical_text_alignment = $12, stroke_color = $13, shadow_color = $14, stroke_size = $15, shadow_size = $16, shadow_offset_x = $17, shadow_offset_y = $18, style = $19, weight = $20 WHERE id = $1"#,
|
||||||
item.id,
|
song.id,
|
||||||
item.title,
|
song.title,
|
||||||
lyrics,
|
lyrics,
|
||||||
item.author,
|
song.author,
|
||||||
item.ccli,
|
song.ccli,
|
||||||
verse_order,
|
verse_order,
|
||||||
audio,
|
audio,
|
||||||
item.font,
|
song.font,
|
||||||
item.font_size,
|
song.font_size,
|
||||||
background,
|
background,
|
||||||
horizontal_alignment,
|
horizontal_alignment,
|
||||||
vertical_alignment,
|
vertical_alignment,
|
||||||
|
|
@ -927,13 +1121,23 @@ pub async fn update_song_in_db(
|
||||||
style,
|
style,
|
||||||
weight
|
weight
|
||||||
)
|
)
|
||||||
.execute(&mut db.detach())
|
.execute(&*db)
|
||||||
.await
|
.await
|
||||||
.into_diagnostic()?;
|
.into_diagnostic()?;
|
||||||
|
|
||||||
debug!(rows_affected = ?result.rows_affected());
|
debug!(rows_affected = ?result.rows_affected());
|
||||||
|
let index = songs
|
||||||
|
.iter()
|
||||||
|
.position(|current_song| current_song.id == song.id)
|
||||||
|
.ok_or_else(|| miette!("Could not find song in model"))?;
|
||||||
|
|
||||||
Ok(())
|
replace(
|
||||||
|
songs
|
||||||
|
.get_mut(index)
|
||||||
|
.expect("We have found the song so this shouldn't fail"),
|
||||||
|
song,
|
||||||
|
);
|
||||||
|
Ok(songs)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Song {
|
impl Song {
|
||||||
|
|
@ -1357,7 +1561,7 @@ You saved my soul"
|
||||||
async fn fill_db(db: &SqlitePool) -> Result<()> {
|
async fn fill_db(db: &SqlitePool) -> Result<()> {
|
||||||
for _ in 0..20 {
|
for _ in 0..20 {
|
||||||
let conn = db.acquire().await.into_diagnostic()?;
|
let conn = db.acquire().await.into_diagnostic()?;
|
||||||
let db_song = add_song_to_db(conn).await?;
|
let db_song = add_to_db(conn).await?;
|
||||||
let mut song = test_song();
|
let mut song = test_song();
|
||||||
song.id = db_song.id;
|
song.id = db_song.id;
|
||||||
let conn = db.acquire().await.into_diagnostic()?;
|
let conn = db.acquire().await.into_diagnostic()?;
|
||||||
|
|
@ -1429,7 +1633,9 @@ You saved my soul"
|
||||||
let test_song = song_model.get_item(2);
|
let test_song = song_model.get_item(2);
|
||||||
assert_ne!(test_song, Some(&cloned_song));
|
assert_ne!(test_song, Some(&cloned_song));
|
||||||
|
|
||||||
match song_model.update_item(song, 2) {
|
match song_model
|
||||||
|
.update_item(song, |song| song.id == cloned_song.id)
|
||||||
|
{
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
let updated_model_song =
|
let updated_model_song =
|
||||||
song_model.find(|s| s.id == 7).unwrap();
|
song_model.find(|s| s.id == 7).unwrap();
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,17 @@ use super::{
|
||||||
slide::Slide,
|
slide::Slide,
|
||||||
};
|
};
|
||||||
use crisp::types::{Keyword, Symbol, Value};
|
use crisp::types::{Keyword, Symbol, Value};
|
||||||
use miette::{IntoDiagnostic, Result};
|
use miette::{IntoDiagnostic, Result, miette};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::{
|
use sqlx::{
|
||||||
Sqlite, SqliteConnection, SqlitePool, pool::PoolConnection,
|
Sqlite, SqliteConnection, SqlitePool, pool::PoolConnection,
|
||||||
query, query_as,
|
query, query_as,
|
||||||
};
|
};
|
||||||
use std::path::{Path, PathBuf};
|
use std::{
|
||||||
|
mem::replace,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
|
|
@ -198,6 +202,33 @@ impl ServiceTrait for Video {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Model<Video> {
|
impl Model<Video> {
|
||||||
|
pub async fn append_video(
|
||||||
|
&mut self,
|
||||||
|
video: Video,
|
||||||
|
db: &SqlitePool,
|
||||||
|
) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn new_video(&mut self, db: &SqlitePool) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_video(
|
||||||
|
&mut self,
|
||||||
|
video: Video,
|
||||||
|
db: &SqlitePool,
|
||||||
|
) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn remove_video(
|
||||||
|
&mut self,
|
||||||
|
id: i32,
|
||||||
|
db: &SqlitePool,
|
||||||
|
) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
pub async fn new_video_model(db: &mut SqlitePool) -> Self {
|
pub async fn new_video_model(db: &mut SqlitePool) -> Self {
|
||||||
let mut model = Self {
|
let mut model = Self {
|
||||||
items: vec![],
|
items: vec![],
|
||||||
|
|
@ -227,52 +258,65 @@ impl Model<Video> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn remove_from_db(
|
pub async fn remove_from_db(
|
||||||
db: PoolConnection<Sqlite>,
|
db: Arc<SqlitePool>,
|
||||||
|
mut videos: Vec<Video>,
|
||||||
id: i32,
|
id: i32,
|
||||||
) -> Result<()> {
|
) -> Result<Vec<Video>> {
|
||||||
query!("DELETE FROM videos WHERE id = $1", id)
|
query!("DELETE FROM videos WHERE id = $1", id)
|
||||||
.execute(&mut db.detach())
|
.execute(&*db)
|
||||||
.await
|
.await
|
||||||
.into_diagnostic()
|
.into_diagnostic()
|
||||||
.map(|_| ())
|
.map(|_| ())?;
|
||||||
|
|
||||||
|
let index = videos
|
||||||
|
.iter()
|
||||||
|
.position(|current_video| current_video.id == id)
|
||||||
|
.ok_or_else(|| miette!("Could not find video in model"))?;
|
||||||
|
videos.remove(index);
|
||||||
|
Ok(videos)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn add_video_to_db(
|
pub async fn add_to_db(
|
||||||
|
new_videos: Vec<Video>,
|
||||||
|
mut current_videos: Vec<Video>,
|
||||||
|
db: Arc<SqlitePool>,
|
||||||
|
) -> Result<Vec<Video>> {
|
||||||
|
for video in new_videos {
|
||||||
|
let path = video
|
||||||
|
.path
|
||||||
|
.to_str()
|
||||||
|
.map(std::string::ToString::to_string)
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
query!(
|
||||||
|
r#"INSERT INTO videos (title, file_path, start_time, end_time, loop) VALUES ($1, $2, $3, $4, $5)"#,
|
||||||
|
video.title,
|
||||||
|
path,
|
||||||
|
video.start_time,
|
||||||
|
video.end_time,
|
||||||
|
video.looping
|
||||||
|
)
|
||||||
|
.execute(&*db)
|
||||||
|
.await
|
||||||
|
.into_diagnostic()?;
|
||||||
|
|
||||||
|
current_videos.push(video);
|
||||||
|
}
|
||||||
|
Ok(current_videos)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_in_db(
|
||||||
video: Video,
|
video: Video,
|
||||||
db: PoolConnection<Sqlite>,
|
mut videos: Vec<Video>,
|
||||||
) -> Result<()> {
|
db: Arc<SqlitePool>,
|
||||||
|
) -> Result<Vec<Video>> {
|
||||||
let path = video
|
let path = video
|
||||||
.path
|
.path
|
||||||
.to_str()
|
.to_str()
|
||||||
.map(std::string::ToString::to_string)
|
.map(std::string::ToString::to_string)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let mut db = db.detach();
|
|
||||||
query!(
|
query!(
|
||||||
r#"INSERT INTO videos (title, file_path, start_time, end_time, loop) VALUES ($1, $2, $3, $4, $5)"#,
|
|
||||||
video.title,
|
|
||||||
path,
|
|
||||||
video.start_time,
|
|
||||||
video.end_time,
|
|
||||||
video.looping
|
|
||||||
)
|
|
||||||
.execute(&mut db)
|
|
||||||
.await
|
|
||||||
.into_diagnostic()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn update_video_in_db(
|
|
||||||
video: Video,
|
|
||||||
db: PoolConnection<Sqlite>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let path = video
|
|
||||||
.path
|
|
||||||
.to_str()
|
|
||||||
.map(std::string::ToString::to_string)
|
|
||||||
.unwrap_or_default();
|
|
||||||
let mut db = db.detach();
|
|
||||||
debug!(?video, "should be been updated");
|
|
||||||
let result = query!(
|
|
||||||
r#"UPDATE videos SET title = $2, file_path = $3, start_time = $4, end_time = $5, loop = $6 WHERE id = $1"#,
|
r#"UPDATE videos SET title = $2, file_path = $3, start_time = $4, end_time = $5, loop = $6 WHERE id = $1"#,
|
||||||
video.id,
|
video.id,
|
||||||
video.title,
|
video.title,
|
||||||
|
|
@ -281,22 +325,24 @@ pub async fn update_video_in_db(
|
||||||
video.end_time,
|
video.end_time,
|
||||||
video.looping,
|
video.looping,
|
||||||
)
|
)
|
||||||
.execute(&mut db)
|
.execute(&*db)
|
||||||
.await.into_diagnostic();
|
.await.into_diagnostic()?;
|
||||||
|
|
||||||
match result {
|
let current_video = videos
|
||||||
Ok(_) => {
|
.iter()
|
||||||
debug!("should have been updated");
|
.position(|current_video| current_video.id == video.id)
|
||||||
Ok(())
|
.ok_or_else(|| miette!("Could not find video in model"))
|
||||||
}
|
.map(|index| {
|
||||||
Err(e) => {
|
videos
|
||||||
error! {?e};
|
.get_mut(index)
|
||||||
Err(e)
|
.expect("We should have this video already")
|
||||||
}
|
})?;
|
||||||
}
|
|
||||||
|
replace(current_video, video);
|
||||||
|
Ok(videos)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_video_from_db(
|
pub async fn get_from_db(
|
||||||
database_id: i32,
|
database_id: i32,
|
||||||
db: &mut SqliteConnection,
|
db: &mut SqliteConnection,
|
||||||
) -> Result<Video> {
|
) -> Result<Video> {
|
||||||
|
|
|
||||||
57
src/main.rs
57
src/main.rs
|
|
@ -10,7 +10,7 @@ use cosmic::cosmic_config::{Config, CosmicConfigEntry};
|
||||||
use cosmic::dialog::file_chooser::{open, save};
|
use cosmic::dialog::file_chooser::{open, save};
|
||||||
use cosmic::iced::alignment::Vertical;
|
use cosmic::iced::alignment::Vertical;
|
||||||
use cosmic::iced::keyboard::{Key, Modifiers};
|
use cosmic::iced::keyboard::{Key, Modifiers};
|
||||||
use cosmic::iced::window::{Mode, Position};
|
use cosmic::iced::window::Position;
|
||||||
use cosmic::iced::{
|
use cosmic::iced::{
|
||||||
self, Background as IcedBackground, Border, Color, Length, event,
|
self, Background as IcedBackground, Border, Color, Length, event,
|
||||||
window,
|
window,
|
||||||
|
|
@ -18,18 +18,19 @@ use cosmic::iced::{
|
||||||
use cosmic::iced_core::text::Wrapping;
|
use cosmic::iced_core::text::Wrapping;
|
||||||
use cosmic::iced_futures::Subscription;
|
use cosmic::iced_futures::Subscription;
|
||||||
use cosmic::iced_widget::{column, row, stack};
|
use cosmic::iced_widget::{column, row, stack};
|
||||||
|
use cosmic::prelude::*;
|
||||||
use cosmic::widget::dnd_destination::dnd_destination;
|
use cosmic::widget::dnd_destination::dnd_destination;
|
||||||
use cosmic::widget::menu::key_bind::Modifier;
|
use cosmic::widget::menu::key_bind::Modifier;
|
||||||
use cosmic::widget::menu::{ItemWidth, KeyBind};
|
use cosmic::widget::menu::{ItemWidth, KeyBind};
|
||||||
use cosmic::widget::nav_bar::nav_bar_style;
|
use cosmic::widget::nav_bar::nav_bar_style;
|
||||||
|
use cosmic::widget::space::horizontal;
|
||||||
use cosmic::widget::tooltip::Position as TPosition;
|
use cosmic::widget::tooltip::Position as TPosition;
|
||||||
use cosmic::widget::{
|
use cosmic::widget::{
|
||||||
Container, divider, menu, settings, text_input,
|
Container, divider, menu, settings, text_input,
|
||||||
};
|
};
|
||||||
use cosmic::widget::{
|
use cosmic::widget::{
|
||||||
Space, button, context_menu, horizontal_space, mouse_area,
|
Space, button, context_menu, mouse_area, nav_bar, nav_bar_toggle,
|
||||||
nav_bar, nav_bar_toggle, responsive, scrollable, search_input,
|
responsive, scrollable, search_input, tooltip,
|
||||||
tooltip,
|
|
||||||
};
|
};
|
||||||
use cosmic::widget::{container, text};
|
use cosmic::widget::{container, text};
|
||||||
use cosmic::widget::{icon, slider};
|
use cosmic::widget::{icon, slider};
|
||||||
|
|
@ -699,6 +700,7 @@ impl cosmic::Application for App {
|
||||||
// debug!(?platform_specific);
|
// debug!(?platform_specific);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
iced::Event::InputMethod(_event) => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
event::Status::Captured => None,
|
event::Status::Captured => None,
|
||||||
|
|
@ -736,7 +738,7 @@ impl cosmic::Application for App {
|
||||||
row![
|
row![
|
||||||
column![title, subtitle]
|
column![title, subtitle]
|
||||||
.spacing(space_xxs),
|
.spacing(space_xxs),
|
||||||
horizontal_space(),
|
horizontal(),
|
||||||
tooltip(
|
tooltip(
|
||||||
icon::from_name("add")
|
icon::from_name("add")
|
||||||
.symbolic(true).apply(button::icon)
|
.symbolic(true).apply(button::icon)
|
||||||
|
|
@ -785,7 +787,9 @@ impl cosmic::Application for App {
|
||||||
.center_x(Length::Fill)
|
.center_x(Length::Fill)
|
||||||
.align_top(Length::Fill);
|
.align_top(Length::Fill);
|
||||||
let mouse_stack = stack!(
|
let mouse_stack = stack!(
|
||||||
Space::new(Length::Fill, Length::Fill)
|
Space::new()
|
||||||
|
.height(Length::Fill)
|
||||||
|
.width(Length::Fill)
|
||||||
.apply(container)
|
.apply(container)
|
||||||
.style(|_| {
|
.style(|_| {
|
||||||
container::background(
|
container::background(
|
||||||
|
|
@ -824,7 +828,7 @@ impl cosmic::Application for App {
|
||||||
.padding(space_s)
|
.padding(space_s)
|
||||||
.align_right(Length::Fill)
|
.align_right(Length::Fill)
|
||||||
.align_top(60),
|
.align_top(60),
|
||||||
horizontal_space().height(space_xxl),
|
horizontal().height(space_xxl),
|
||||||
settings::section()
|
settings::section()
|
||||||
.title("Obs Settings")
|
.title("Obs Settings")
|
||||||
.add(obs_socket)
|
.add(obs_socket)
|
||||||
|
|
@ -845,7 +849,9 @@ impl cosmic::Application for App {
|
||||||
.apply(container)
|
.apply(container)
|
||||||
.padding([space_xxl, space_xxxl * 2]);
|
.padding([space_xxl, space_xxxl * 2]);
|
||||||
let mouse_stack = stack!(
|
let mouse_stack = stack!(
|
||||||
Space::new(Length::Fill, Length::Fill)
|
Space::new()
|
||||||
|
.height(Length::Fill)
|
||||||
|
.width(Length::Fill)
|
||||||
.apply(container)
|
.apply(container)
|
||||||
.style(|_| {
|
.style(|_| {
|
||||||
container::background(
|
container::background(
|
||||||
|
|
@ -1239,7 +1245,7 @@ impl cosmic::Application for App {
|
||||||
if let Some(video) = &mut self.presenter.video {
|
if let Some(video) = &mut self.presenter.video {
|
||||||
video.set_muted(false);
|
video.set_muted(false);
|
||||||
}
|
}
|
||||||
window::change_mode(id, Mode::Fullscreen)
|
window::maximize(id, true)
|
||||||
} else {
|
} else {
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
@ -1290,10 +1296,12 @@ impl cosmic::Application for App {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Message::HoveredServiceItem(index) => {
|
Message::HoveredServiceItem(index) => {
|
||||||
|
debug!(index);
|
||||||
self.hovered_item = index;
|
self.hovered_item = index;
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
Message::HoveredServiceDrop(index) => {
|
Message::HoveredServiceDrop(index) => {
|
||||||
|
debug!(index);
|
||||||
self.hovered_dnd = index;
|
self.hovered_dnd = index;
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
@ -1656,7 +1664,7 @@ impl cosmic::Application for App {
|
||||||
);
|
);
|
||||||
|
|
||||||
let slide_preview = column![
|
let slide_preview = column![
|
||||||
Space::with_height(Length::Fill),
|
Space::new().height(Length::Fill),
|
||||||
Container::new(
|
Container::new(
|
||||||
self.presenter.view_preview().map(Message::Present),
|
self.presenter.view_preview().map(Message::Present),
|
||||||
)
|
)
|
||||||
|
|
@ -1685,7 +1693,7 @@ impl cosmic::Application for App {
|
||||||
row![]
|
row![]
|
||||||
})
|
})
|
||||||
.center_x(Length::Fill),
|
.center_x(Length::Fill),
|
||||||
Space::with_height(Length::Fill),
|
Space::new().height(Length::Fill)
|
||||||
]
|
]
|
||||||
.spacing(3);
|
.spacing(3);
|
||||||
|
|
||||||
|
|
@ -1696,7 +1704,7 @@ impl cosmic::Application for App {
|
||||||
let library = if self.library_open {
|
let library = if self.library_open {
|
||||||
Container::new(
|
Container::new(
|
||||||
Container::new(self.library.as_ref().map_or_else(
|
Container::new(self.library.as_ref().map_or_else(
|
||||||
|| Element::from(Space::new(0, 0)),
|
|| Element::from(Space::new()),
|
||||||
|library| library.view().map(Message::Library),
|
|library| library.view().map(Message::Library),
|
||||||
))
|
))
|
||||||
.style(nav_bar_style),
|
.style(nav_bar_style),
|
||||||
|
|
@ -1704,11 +1712,11 @@ impl cosmic::Application for App {
|
||||||
.padding(space_s)
|
.padding(space_s)
|
||||||
.width(Length::FillPortion(2))
|
.width(Length::FillPortion(2))
|
||||||
} else {
|
} else {
|
||||||
Container::new(horizontal_space().width(0))
|
Container::new(horizontal().width(0))
|
||||||
};
|
};
|
||||||
|
|
||||||
let editor = self.editor_mode.as_ref().map_or_else(
|
let editor = self.editor_mode.as_ref().map_or_else(
|
||||||
|| Element::from(Space::new(0, 0)),
|
|| Element::from(Space::new()),
|
||||||
|mode| match mode {
|
|mode| match mode {
|
||||||
EditorMode::Song => {
|
EditorMode::Song => {
|
||||||
self.song_editor.view().map(Message::SongEditor)
|
self.song_editor.view().map(Message::SongEditor)
|
||||||
|
|
@ -1763,7 +1771,7 @@ impl cosmic::Application for App {
|
||||||
|
|
||||||
let preview_bar = if self.editor_mode.is_none() {
|
let preview_bar = if self.editor_mode.is_none() {
|
||||||
if self.service.is_empty() {
|
if self.service.is_empty() {
|
||||||
Container::new(horizontal_space())
|
Container::new(horizontal())
|
||||||
} else {
|
} else {
|
||||||
Container::new(
|
Container::new(
|
||||||
self.presenter
|
self.presenter
|
||||||
|
|
@ -1775,7 +1783,7 @@ impl cosmic::Application for App {
|
||||||
.center_y(180)
|
.center_y(180)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Container::new(horizontal_space())
|
Container::new(horizontal())
|
||||||
};
|
};
|
||||||
|
|
||||||
let main_area = self.editor_mode.as_ref().map_or_else(
|
let main_area = self.editor_mode.as_ref().map_or_else(
|
||||||
|
|
@ -1788,7 +1796,7 @@ impl cosmic::Application for App {
|
||||||
if self.library_open {
|
if self.library_open {
|
||||||
library.width(Length::FillPortion(1))
|
library.width(Length::FillPortion(1))
|
||||||
} else {
|
} else {
|
||||||
container(Space::new(0, 0))
|
container(Space::new())
|
||||||
},
|
},
|
||||||
main_area.width(Length::FillPortion(4))
|
main_area.width(Length::FillPortion(4))
|
||||||
]
|
]
|
||||||
|
|
@ -1850,6 +1858,7 @@ where
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
// if let Some(library) = self.library.clone() {
|
// if let Some(library) = self.library.clone() {
|
||||||
// Task::perform(
|
// Task::perform(
|
||||||
// async move { library.search_items(query).await },
|
// async move { library.search_items(query).await },
|
||||||
|
|
@ -1915,11 +1924,9 @@ where
|
||||||
) => self.update(Message::Present(
|
) => self.update(Message::Present(
|
||||||
presenter::Message::PrevSlide,
|
presenter::Message::PrevSlide,
|
||||||
)),
|
)),
|
||||||
(Key::Named(iced::keyboard::key::Named::Space), _) => {
|
(Key::Character(k), _) if k == *" " => self.update(
|
||||||
self.update(Message::Present(
|
Message::Present(presenter::Message::NextSlide),
|
||||||
presenter::Message::NextSlide,
|
),
|
||||||
))
|
|
||||||
}
|
|
||||||
(Key::Character(k), _) if k == *"j" || k == *"l" => self
|
(Key::Character(k), _) if k == *"j" || k == *"l" => self
|
||||||
.update(Message::Present(
|
.update(Message::Present(
|
||||||
presenter::Message::NextSlide,
|
presenter::Message::NextSlide,
|
||||||
|
|
@ -2001,11 +2008,11 @@ where
|
||||||
let color = t.cosmic().accent_color();
|
let color = t.cosmic().accent_color();
|
||||||
cosmic::iced_widget::rule::Style {
|
cosmic::iced_widget::rule::Style {
|
||||||
color: color.into(),
|
color: color.into(),
|
||||||
width: 2,
|
snap: true,
|
||||||
radius: t.cosmic().corner_radii.radius_xs.into(),
|
radius: t.cosmic().corner_radii.radius_xs.into(),
|
||||||
fill_mode: cosmic::iced_widget::rule::FillMode::Full,
|
fill_mode: cosmic::iced_widget::rule::FillMode::Full,
|
||||||
}
|
}
|
||||||
} ));
|
} )).width(2);
|
||||||
Container::new(column![divider, container].spacing(theme::spacing().space_s))
|
Container::new(column![divider, container].spacing(theme::spacing().space_s))
|
||||||
} else { container };
|
} else { container };
|
||||||
let mouse_area = mouse_area(visual_item)
|
let mouse_area = mouse_area(visual_item)
|
||||||
|
|
@ -2145,7 +2152,7 @@ where
|
||||||
text::heading("Service List")
|
text::heading("Service List")
|
||||||
.center()
|
.center()
|
||||||
.width(Length::Fill),
|
.width(Length::Fill),
|
||||||
iced::widget::horizontal_rule(1),
|
divider::horizontal::light(),
|
||||||
scrollable
|
scrollable
|
||||||
]
|
]
|
||||||
.padding(10)
|
.padding(10)
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ use cosmic::{
|
||||||
iced_widget::{column, row},
|
iced_widget::{column, row},
|
||||||
theme,
|
theme,
|
||||||
widget::{
|
widget::{
|
||||||
self, Space, button, container, horizontal_space, icon, text,
|
self, Space, button, container, icon, space::horizontal,
|
||||||
text_input,
|
text, text_input,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use tracing::{debug, error, warn};
|
use tracing::{debug, error, warn};
|
||||||
|
|
@ -94,7 +94,7 @@ impl ImageEditor {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn view(&self) -> Element<Message> {
|
pub fn view(&self) -> Element<Message> {
|
||||||
let container = self.image.as_ref().map_or_else(
|
let container = self.image.as_ref().map_or_else(
|
||||||
|| Space::new(0, 0).apply(container),
|
|| Space::new().apply(container),
|
||||||
|pic| widget::image(pic.path.clone()).apply(container),
|
|pic| widget::image(pic.path.clone()).apply(container),
|
||||||
);
|
);
|
||||||
let column = column![
|
let column = column![
|
||||||
|
|
@ -120,7 +120,7 @@ impl ImageEditor {
|
||||||
row![
|
row![
|
||||||
text::body("Title:"),
|
text::body("Title:"),
|
||||||
title_box,
|
title_box,
|
||||||
horizontal_space(),
|
horizontal(),
|
||||||
image_selector
|
image_selector
|
||||||
]
|
]
|
||||||
.align_y(Vertical::Center)
|
.align_y(Vertical::Center)
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -5,7 +5,7 @@ pub mod image_editor;
|
||||||
pub mod library;
|
pub mod library;
|
||||||
pub mod presentation_editor;
|
pub mod presentation_editor;
|
||||||
pub mod presenter;
|
pub mod presenter;
|
||||||
pub mod service;
|
// pub mod service;
|
||||||
pub mod slide_editor;
|
pub mod slide_editor;
|
||||||
pub mod song_editor;
|
pub mod song_editor;
|
||||||
pub mod text_svg;
|
pub mod text_svg;
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,11 @@ use cosmic::{
|
||||||
iced_widget::{column, row},
|
iced_widget::{column, row},
|
||||||
theme,
|
theme,
|
||||||
widget::{
|
widget::{
|
||||||
self, Space, button, container, context_menu,
|
self, Space, button, container, context_menu, icon,
|
||||||
horizontal_space, icon, image::Handle, menu, mouse_area,
|
image::Handle,
|
||||||
scrollable, text, text_input,
|
menu, mouse_area, scrollable,
|
||||||
|
space::{self, horizontal},
|
||||||
|
text, text_input,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use miette::{IntoDiagnostic, Result, miette};
|
use miette::{IntoDiagnostic, Result, miette};
|
||||||
|
|
@ -335,7 +337,7 @@ impl PresentationEditor {
|
||||||
|
|
||||||
pub fn view(&self) -> Element<Message> {
|
pub fn view(&self) -> Element<Message> {
|
||||||
let presentation = self.current_slide.as_ref().map_or_else(
|
let presentation = self.current_slide.as_ref().map_or_else(
|
||||||
|| container(Space::new(0, 0)),
|
|| container(Space::new()),
|
||||||
|slide| {
|
|slide| {
|
||||||
container(
|
container(
|
||||||
widget::image(slide)
|
widget::image(slide)
|
||||||
|
|
@ -350,7 +352,7 @@ impl PresentationEditor {
|
||||||
);
|
);
|
||||||
let pdf_pages: Vec<Element<Message>> =
|
let pdf_pages: Vec<Element<Message>> =
|
||||||
self.slides.as_ref().map_or_else(
|
self.slides.as_ref().map_or_else(
|
||||||
|| vec![horizontal_space().into()],
|
|| vec![horizontal().into()],
|
||||||
|pages| {
|
|pages| {
|
||||||
pages
|
pages
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -421,7 +423,7 @@ impl PresentationEditor {
|
||||||
let control_buttons = row![
|
let control_buttons = row![
|
||||||
button::standard("Previous Page")
|
button::standard("Previous Page")
|
||||||
.on_press(Message::PrevPage),
|
.on_press(Message::PrevPage),
|
||||||
horizontal_space(),
|
space::horizontal(),
|
||||||
button::standard("Next Page").on_press(Message::NextPage),
|
button::standard("Next Page").on_press(Message::NextPage),
|
||||||
];
|
];
|
||||||
let column =
|
let column =
|
||||||
|
|
@ -450,7 +452,7 @@ impl PresentationEditor {
|
||||||
row![
|
row![
|
||||||
text::body("Title:"),
|
text::body("Title:"),
|
||||||
title_box,
|
title_box,
|
||||||
horizontal_space(),
|
space::horizontal(),
|
||||||
presentation_selector
|
presentation_selector
|
||||||
]
|
]
|
||||||
.align_y(Vertical::Center)
|
.align_y(Vertical::Center)
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,13 @@ use cosmic::{
|
||||||
scrollable::{
|
scrollable::{
|
||||||
AbsoluteOffset, Direction, Scrollbar, scroll_to,
|
AbsoluteOffset, Direction, Scrollbar, scroll_to,
|
||||||
},
|
},
|
||||||
stack, vertical_rule,
|
stack,
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
widget::{
|
widget::{
|
||||||
Container, Id, Row, Space, container, context_menu, image,
|
Container, Id, Row, Space, container, context_menu,
|
||||||
menu, mouse_area, responsive, scrollable, text,
|
divider::vertical, image, menu, mouse_area, responsive,
|
||||||
|
scrollable, text,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use derive_more::Debug;
|
use derive_more::Debug;
|
||||||
|
|
@ -454,7 +455,10 @@ impl Presenter {
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut tasks = vec![];
|
let mut tasks = vec![];
|
||||||
tasks.push(scroll_to(self.scroll_id.clone(), offset));
|
tasks.push(scroll_to(
|
||||||
|
self.scroll_id.clone(),
|
||||||
|
offset.into(),
|
||||||
|
));
|
||||||
|
|
||||||
if self.slide_action_map.is_some() {
|
if self.slide_action_map.is_some() {
|
||||||
debug!("Found slide actions, running them");
|
debug!("Found slide actions, running them");
|
||||||
|
|
@ -723,7 +727,7 @@ impl Presenter {
|
||||||
.align_top(Length::Fill)
|
.align_top(Length::Fill)
|
||||||
.align_left(Length::Fill)
|
.align_left(Length::Fill)
|
||||||
.padding([0, 0, 0, 35]);
|
.padding([0, 0, 0, 35]);
|
||||||
let divider = vertical_rule(2);
|
let divider = vertical::light();
|
||||||
items.push(
|
items.push(
|
||||||
container(stack!(row, label_container))
|
container(stack!(row, label_container))
|
||||||
.padding([5, 2])
|
.padding([5, 2])
|
||||||
|
|
@ -981,7 +985,7 @@ pub(crate) fn slide_view<'a>(
|
||||||
) -> Element<'a, 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 black = Container::new(Space::new(0, 0))
|
let black = Container::new(Space::new())
|
||||||
.style(|_| {
|
.style(|_| {
|
||||||
container::background(Background::Color(Color::BLACK))
|
container::background(Background::Color(Color::BLACK))
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -121,10 +121,10 @@ impl<'a> Program<SlideWidget, cosmic::Theme, cosmic::Renderer>
|
||||||
fn update(
|
fn update(
|
||||||
&self,
|
&self,
|
||||||
_state: &mut Self::State,
|
_state: &mut Self::State,
|
||||||
event: canvas::Event,
|
event: &canvas::Event,
|
||||||
bounds: cosmic::iced::Rectangle,
|
bounds: cosmic::iced::Rectangle,
|
||||||
_cursor: cosmic::iced_core::mouse::Cursor,
|
_cursor: cosmic::iced_core::mouse::Cursor,
|
||||||
) -> (canvas::event::Status, Option<SlideWidget>) {
|
) -> Option<cosmic::iced_widget::Action<SlideWidget>> {
|
||||||
match event {
|
match event {
|
||||||
canvas::Event::Mouse(event) => match event {
|
canvas::Event::Mouse(event) => match event {
|
||||||
cosmic::iced::mouse::Event::CursorEntered => {
|
cosmic::iced::mouse::Event::CursorEntered => {
|
||||||
|
|
@ -157,8 +157,15 @@ impl<'a> Program<SlideWidget, cosmic::Theme, cosmic::Renderer>
|
||||||
},
|
},
|
||||||
canvas::Event::Touch(_event) => debug!("test"),
|
canvas::Event::Touch(_event) => debug!("test"),
|
||||||
canvas::Event::Keyboard(_event) => debug!("test"),
|
canvas::Event::Keyboard(_event) => debug!("test"),
|
||||||
|
canvas::Event::Window(_event) => todo!(),
|
||||||
|
canvas::Event::InputMethod(_event) => todo!(),
|
||||||
|
canvas::Event::A11y(_id, _action_request) => todo!(),
|
||||||
|
canvas::Event::Dnd(_dnd_event) => todo!(),
|
||||||
|
canvas::Event::PlatformSpecific(_platform_specific) => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
(canvas::event::Status::Ignored, None)
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mouse_interaction(
|
fn mouse_interaction(
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,9 @@ use cosmic::{
|
||||||
combo_box, container, divider, dnd_destination, dnd_source,
|
combo_box, container, divider, dnd_destination, dnd_source,
|
||||||
dropdown,
|
dropdown,
|
||||||
grid::{self},
|
grid::{self},
|
||||||
horizontal_space, icon, mouse_area, popover, progress_bar,
|
icon, mouse_area, popover, progress_bar, scrollable,
|
||||||
scrollable, text, text_editor, text_input, tooltip,
|
space::{self, horizontal},
|
||||||
|
text, text_editor, text_input, tooltip,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use derive_more::Debug;
|
use derive_more::Debug;
|
||||||
|
|
@ -900,7 +901,7 @@ impl SongEditor {
|
||||||
pub fn view(&self) -> Element<Message> {
|
pub fn view(&self) -> Element<Message> {
|
||||||
let video_elements: Element<Message> =
|
let video_elements: Element<Message> =
|
||||||
self.video.as_ref().map_or_else(
|
self.video.as_ref().map_or_else(
|
||||||
|| horizontal_space().into(),
|
|| horizontal().into(),
|
||||||
|video| {
|
|video| {
|
||||||
let play_button =
|
let play_button =
|
||||||
button::icon(if video.paused() {
|
button::icon(if video.paused() {
|
||||||
|
|
@ -913,8 +914,8 @@ impl SongEditor {
|
||||||
0.0..=video.duration().as_secs_f32(),
|
0.0..=video.duration().as_secs_f32(),
|
||||||
video.position().as_secs_f32(),
|
video.position().as_secs_f32(),
|
||||||
)
|
)
|
||||||
.height(cosmic::theme::spacing().space_s)
|
.girth(cosmic::theme::spacing().space_s)
|
||||||
.width(Length::Fill);
|
.length(Length::Fill);
|
||||||
container(
|
container(
|
||||||
row![play_button, video_track]
|
row![play_button, video_track]
|
||||||
.align_y(Vertical::Center)
|
.align_y(Vertical::Center)
|
||||||
|
|
@ -947,7 +948,7 @@ impl SongEditor {
|
||||||
|
|
||||||
fn slide_preview(&self) -> Element<Message> {
|
fn slide_preview(&self) -> Element<Message> {
|
||||||
self.song_slides.as_ref().map_or_else(
|
self.song_slides.as_ref().map_or_else(
|
||||||
|| horizontal_space().into(),
|
|| space::horizontal().into(),
|
||||||
|slides| {
|
|slides| {
|
||||||
let slides: Vec<Element<Message>> = slides
|
let slides: Vec<Element<Message>> = slides
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -1107,7 +1108,7 @@ impl SongEditor {
|
||||||
if let Some(hovered_chip) =
|
if let Some(hovered_chip) =
|
||||||
self.hovered_dnd_verse_chip
|
self.hovered_dnd_verse_chip
|
||||||
&& index == hovered_chip {
|
&& index == hovered_chip {
|
||||||
let phantom_chip = horizontal_space().width(60).height(19)
|
let phantom_chip = space::horizontal().width(60).height(19)
|
||||||
.apply(container)
|
.apply(container)
|
||||||
.padding(
|
.padding(
|
||||||
Padding::new(space_xxs.into())
|
Padding::new(space_xxs.into())
|
||||||
|
|
@ -1119,7 +1120,7 @@ impl SongEditor {
|
||||||
.background(ContainerBackground::Color(
|
.background(ContainerBackground::Color(
|
||||||
Color::from(t.cosmic().secondary.base).scale_alpha(0.5)
|
Color::from(t.cosmic().secondary.base).scale_alpha(0.5)
|
||||||
))
|
))
|
||||||
.border(Border::default().rounded(space_m).width(2))
|
.border(Border::default().rounded(space_m as u8).width(2))
|
||||||
})));
|
})));
|
||||||
chip = row![
|
chip = row![
|
||||||
phantom_chip,
|
phantom_chip,
|
||||||
|
|
@ -1166,7 +1167,7 @@ impl SongEditor {
|
||||||
|
|
||||||
let mut verse_order_row = if self.dragging_verse_chip {
|
let mut verse_order_row = if self.dragging_verse_chip {
|
||||||
let ending_dnd_dest = dnd_destination(
|
let ending_dnd_dest = dnd_destination(
|
||||||
horizontal_space().height(19),
|
space::horizontal().height(19),
|
||||||
vec!["application/verse".into()],
|
vec!["application/verse".into()],
|
||||||
)
|
)
|
||||||
.on_enter(|_, _, _| {
|
.on_enter(|_, _, _| {
|
||||||
|
|
@ -1213,7 +1214,7 @@ impl SongEditor {
|
||||||
if self.editing_verse_order {
|
if self.editing_verse_order {
|
||||||
Element::from(verse_options)
|
Element::from(verse_options)
|
||||||
} else {
|
} else {
|
||||||
Element::from(horizontal_space())
|
Element::from(space::horizontal())
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
.spacing(space_s),
|
.spacing(space_s),
|
||||||
|
|
@ -1236,7 +1237,7 @@ impl SongEditor {
|
||||||
.spacing(5);
|
.spacing(5);
|
||||||
|
|
||||||
let verse_list = self.verses.as_ref().map_or_else(
|
let verse_list = self.verses.as_ref().map_or_else(
|
||||||
|| Element::from(horizontal_space()),
|
|| Element::from(space::horizontal()),
|
||||||
|verse_list| {
|
|verse_list| {
|
||||||
Element::from(
|
Element::from(
|
||||||
column(verse_list.iter().enumerate().map(
|
column(verse_list.iter().enumerate().map(
|
||||||
|
|
@ -1347,7 +1348,7 @@ impl SongEditor {
|
||||||
.on_close(Message::FontSelectorOpen(false))
|
.on_close(Message::FontSelectorOpen(false))
|
||||||
.width(300),
|
.width(300),
|
||||||
container(if self.font_selector_open {
|
container(if self.font_selector_open {
|
||||||
Element::from(horizontal_space())
|
Element::from(space::horizontal())
|
||||||
} else {
|
} else {
|
||||||
Element::from(
|
Element::from(
|
||||||
icon::from_name("arrow-down").size(space_m),
|
icon::from_name("arrow-down").size(space_m),
|
||||||
|
|
@ -1383,7 +1384,7 @@ impl SongEditor {
|
||||||
.on_close(Message::FontSizeOpen(false))
|
.on_close(Message::FontSizeOpen(false))
|
||||||
.width(space_xxxl),
|
.width(space_xxxl),
|
||||||
container(if self.font_size_open {
|
container(if self.font_size_open {
|
||||||
Element::from(horizontal_space())
|
Element::from(space::horizontal())
|
||||||
} else {
|
} else {
|
||||||
Element::from(
|
Element::from(
|
||||||
icon::from_name("arrow-down").size(space_m),
|
icon::from_name("arrow-down").size(space_m),
|
||||||
|
|
@ -1797,7 +1798,7 @@ impl SongEditor {
|
||||||
shadow_tools_button,
|
shadow_tools_button,
|
||||||
divider::vertical::default().height(space_l),
|
divider::vertical::default().height(space_l),
|
||||||
text_alignment_popup,
|
text_alignment_popup,
|
||||||
horizontal_space(),
|
space::horizontal(),
|
||||||
background_selector
|
background_selector
|
||||||
]
|
]
|
||||||
.align_y(Vertical::Center)
|
.align_y(Vertical::Center)
|
||||||
|
|
@ -1974,7 +1975,9 @@ fn verse_chip(
|
||||||
))
|
))
|
||||||
.color(text_color)
|
.color(text_color)
|
||||||
.border(
|
.border(
|
||||||
Border::default().rounded(space_m).width(2),
|
Border::default()
|
||||||
|
.rounded(space_m as u8)
|
||||||
|
.width(2),
|
||||||
)
|
)
|
||||||
})));
|
})));
|
||||||
let button = button::icon(icon::from_name("view-close"))
|
let button = button::icon(icon::from_name("view-close"))
|
||||||
|
|
@ -2006,7 +2009,9 @@ fn verse_chip(
|
||||||
))
|
))
|
||||||
.color(text_color)
|
.color(text_color)
|
||||||
.border(
|
.border(
|
||||||
Border::default().rounded(space_m).width(2),
|
Border::default()
|
||||||
|
.rounded(space_m as u8)
|
||||||
|
.width(2),
|
||||||
)
|
)
|
||||||
})))
|
})))
|
||||||
.into()
|
.into()
|
||||||
|
|
|
||||||
|
|
@ -518,7 +518,13 @@ impl TextSvg {
|
||||||
|
|
||||||
pub fn view<'a>(&self) -> Element<'a, Message> {
|
pub fn view<'a>(&self) -> Element<'a, Message> {
|
||||||
self.handle.clone().map_or_else(
|
self.handle.clone().map_or_else(
|
||||||
|| Element::from(Space::new(Length::Fill, Length::Fill)),
|
|| {
|
||||||
|
Element::from(
|
||||||
|
Space::new()
|
||||||
|
.height(Length::Fill)
|
||||||
|
.width(Length::Fill),
|
||||||
|
)
|
||||||
|
},
|
||||||
|handle| {
|
|handle| {
|
||||||
Image::new(handle)
|
Image::new(handle)
|
||||||
.content_fit(ContentFit::Cover)
|
.content_fit(ContentFit::Cover)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,61 @@
|
||||||
// use iced_video_player::Video;
|
// use iced_video_player::Video;
|
||||||
|
|
||||||
// fn video_player(video: &Video) -> Element<Message> {}
|
// fn video_player(video: &Video) -> Element<Message> {}
|
||||||
|
|
||||||
|
use iced_video_player::Video;
|
||||||
|
use miette::{IntoDiagnostic, Result};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
pub fn create_video(url: &Url) -> Result<Video> {
|
||||||
|
// Based on `iced_video_player::Video::new`,
|
||||||
|
// but without a text sink so that the built-in subtitle functionality triggers.
|
||||||
|
use gstreamer as gst;
|
||||||
|
use gstreamer_app as gst_app;
|
||||||
|
use gstreamer_app::prelude::*;
|
||||||
|
|
||||||
|
gst::init().into_diagnostic()?;
|
||||||
|
|
||||||
|
let pipeline = format!(
|
||||||
|
r#"playbin uri="{}" video-sink="videoscale ! videoconvert ! videoflip method=automatic ! appsink name=lumina_video drop=true caps=video/x-raw,format=NV12,pixel-aspect-ratio=1/1""#,
|
||||||
|
url.as_str()
|
||||||
|
);
|
||||||
|
|
||||||
|
let pipeline =
|
||||||
|
gst::parse::launch(pipeline.as_ref()).into_diagnostic()?;
|
||||||
|
let pipeline = pipeline
|
||||||
|
.downcast::<gst::Pipeline>()
|
||||||
|
.map_err(|_| iced_video_player::Error::Cast)
|
||||||
|
.into_diagnostic()?;
|
||||||
|
|
||||||
|
let video_sink: gst::Element = pipeline.property("video-sink");
|
||||||
|
let pad = video_sink.pads().first().cloned().expect("first pad");
|
||||||
|
let pad = pad
|
||||||
|
.dynamic_cast::<gst::GhostPad>()
|
||||||
|
.map_err(|_| iced_video_player::Error::Cast)
|
||||||
|
.into_diagnostic()?;
|
||||||
|
let bin = pad
|
||||||
|
.parent_element()
|
||||||
|
.ok_or_else(|| {
|
||||||
|
iced_video_player::Error::AppSink(String::from(
|
||||||
|
"Should have a parent element here",
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.into_diagnostic()?
|
||||||
|
.downcast::<gst::Bin>()
|
||||||
|
.map_err(|_| iced_video_player::Error::Cast)
|
||||||
|
.into_diagnostic()?;
|
||||||
|
let video_sink = bin
|
||||||
|
.by_name("lumina_video")
|
||||||
|
.ok_or_else(|| {
|
||||||
|
iced_video_player::Error::AppSink(String::from(
|
||||||
|
"Can't find element lumina_video",
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.into_diagnostic()?;
|
||||||
|
let video_sink = video_sink
|
||||||
|
.downcast::<gst_app::AppSink>()
|
||||||
|
.map_err(|_| iced_video_player::Error::Cast)
|
||||||
|
.into_diagnostic()?;
|
||||||
|
let result = Video::from_gst_pipeline(pipeline, video_sink, None);
|
||||||
|
result.into_diagnostic()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,16 @@ use cosmic::{
|
||||||
iced_widget::{column, row},
|
iced_widget::{column, row},
|
||||||
theme,
|
theme,
|
||||||
widget::{
|
widget::{
|
||||||
Space, button, container, horizontal_space, icon,
|
Space, button, container, icon, progress_bar,
|
||||||
progress_bar, text, text_input,
|
space::{self, horizontal},
|
||||||
|
text, text_input,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use iced_video_player::{Video, VideoPlayer};
|
use iced_video_player::{Video, VideoPlayer};
|
||||||
use tracing::{debug, error, warn};
|
use tracing::{debug, error, warn};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::core::videos;
|
use crate::{core::videos, ui::video::create_video};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct VideoEditor {
|
pub struct VideoEditor {
|
||||||
|
|
@ -110,7 +111,7 @@ impl VideoEditor {
|
||||||
|
|
||||||
pub fn view(&self) -> Element<Message> {
|
pub fn view(&self) -> Element<Message> {
|
||||||
let video_elements = self.video.as_ref().map_or_else(
|
let video_elements = self.video.as_ref().map_or_else(
|
||||||
|| container(horizontal_space()),
|
|| container(horizontal()),
|
||||||
|video| {
|
|video| {
|
||||||
let play_button = button::icon(if video.paused() {
|
let play_button = button::icon(if video.paused() {
|
||||||
icon::from_name("media-playback-start")
|
icon::from_name("media-playback-start")
|
||||||
|
|
@ -122,8 +123,8 @@ impl VideoEditor {
|
||||||
0.0..=video.duration().as_secs_f32(),
|
0.0..=video.duration().as_secs_f32(),
|
||||||
video.position().as_secs_f32(),
|
video.position().as_secs_f32(),
|
||||||
)
|
)
|
||||||
.height(cosmic::theme::spacing().space_s)
|
.girth(cosmic::theme::spacing().space_s)
|
||||||
.width(Length::Fill);
|
.length(Length::Fill);
|
||||||
container(
|
container(
|
||||||
row![play_button, video_track]
|
row![play_button, video_track]
|
||||||
.align_y(Vertical::Center)
|
.align_y(Vertical::Center)
|
||||||
|
|
@ -135,7 +136,7 @@ impl VideoEditor {
|
||||||
);
|
);
|
||||||
|
|
||||||
let video_player = self.video.as_ref().map_or_else(
|
let video_player = self.video.as_ref().map_or_else(
|
||||||
|| Element::from(Space::new(0, 0)),
|
|| Element::from(Space::new()),
|
||||||
|video| Element::from(VideoPlayer::new(video)),
|
|video| Element::from(VideoPlayer::new(video)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -164,7 +165,7 @@ impl VideoEditor {
|
||||||
row![
|
row![
|
||||||
text::body("Title:"),
|
text::body("Title:"),
|
||||||
title_box,
|
title_box,
|
||||||
horizontal_space(),
|
space::horizontal(),
|
||||||
video_selector
|
video_selector
|
||||||
]
|
]
|
||||||
.align_y(Vertical::Center)
|
.align_y(Vertical::Center)
|
||||||
|
|
@ -177,9 +178,11 @@ impl VideoEditor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_entire_video(&mut self, video: &videos::Video) {
|
fn update_entire_video(&mut self, video: &videos::Video) {
|
||||||
|
debug!(?video);
|
||||||
let Ok(mut player_video) =
|
let Ok(mut player_video) =
|
||||||
Url::from_file_path(video.path.clone())
|
Url::from_file_path(video.path.clone()).map(|url| {
|
||||||
.map(|url| Video::new(&url).expect("Should be here"))
|
create_video(&url).expect("Shouldn't have probs")
|
||||||
|
})
|
||||||
else {
|
else {
|
||||||
self.video = None;
|
self.video = None;
|
||||||
self.title.clone_from(&video.title);
|
self.title.clone_from(&video.title);
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ use cosmic::iced::advanced::layout::{self, Layout};
|
||||||
use cosmic::iced::advanced::widget::{Operation, Tree, Widget, tree};
|
use cosmic::iced::advanced::widget::{Operation, Tree, Widget, tree};
|
||||||
use cosmic::iced::advanced::{Clipboard, Shell, overlay, renderer};
|
use cosmic::iced::advanced::{Clipboard, Shell, overlay, renderer};
|
||||||
use cosmic::iced::alignment::{self, Alignment};
|
use cosmic::iced::alignment::{self, Alignment};
|
||||||
use cosmic::iced::event::{self, Event};
|
use cosmic::iced::event::Event;
|
||||||
use cosmic::iced::{self, Transformation, mouse};
|
use cosmic::iced::{self, Transformation, mouse};
|
||||||
use cosmic::iced::{
|
use cosmic::iced::{
|
||||||
Background, Border, Color, Element, Length, Padding, Pixels,
|
Background, Border, Color, Element, Length, Padding, Pixels,
|
||||||
|
|
@ -376,7 +376,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&mut self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
limits: &layout::Limits,
|
limits: &layout::Limits,
|
||||||
|
|
@ -392,53 +392,49 @@ where
|
||||||
self.padding,
|
self.padding,
|
||||||
self.spacing,
|
self.spacing,
|
||||||
self.align,
|
self.align,
|
||||||
&self.children,
|
self.children.as_mut(),
|
||||||
&mut tree.children,
|
&mut tree.children,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operate(
|
fn operate(
|
||||||
&self,
|
&mut self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
operation: &mut dyn Operation,
|
operation: &mut dyn Operation,
|
||||||
) {
|
) {
|
||||||
operation.container(
|
operation.container(None, layout.bounds());
|
||||||
None,
|
|
||||||
layout.bounds(),
|
operation.traverse(&mut |operation| {
|
||||||
&mut |operation| {
|
self.children
|
||||||
self.children
|
.iter_mut()
|
||||||
.iter()
|
.zip(&mut tree.children)
|
||||||
.zip(&mut tree.children)
|
.zip(layout.children())
|
||||||
.zip(layout.children())
|
.for_each(|((child, state), c_layout)| {
|
||||||
.for_each(|((child, state), c_layout)| {
|
child.as_widget_mut().operate(
|
||||||
child.as_widget().operate(
|
state,
|
||||||
state,
|
c_layout.with_virtual_offset(
|
||||||
c_layout.with_virtual_offset(
|
layout.virtual_offset(),
|
||||||
layout.virtual_offset(),
|
),
|
||||||
),
|
renderer,
|
||||||
renderer,
|
operation,
|
||||||
operation,
|
);
|
||||||
);
|
});
|
||||||
});
|
});
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(
|
fn update(
|
||||||
&mut self,
|
&mut self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
event: Event,
|
event: &Event,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor: mouse::Cursor,
|
cursor: mouse::Cursor,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
clipboard: &mut dyn Clipboard,
|
clipboard: &mut dyn Clipboard,
|
||||||
shell: &mut Shell<'_, Message>,
|
shell: &mut Shell<'_, Message>,
|
||||||
viewport: &Rectangle,
|
viewport: &Rectangle,
|
||||||
) -> event::Status {
|
) {
|
||||||
let mut event_status = event::Status::Ignored;
|
|
||||||
|
|
||||||
let action = tree.state.downcast_mut::<Action>();
|
let action = tree.state.downcast_mut::<Action>();
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
|
|
@ -459,7 +455,7 @@ where
|
||||||
index,
|
index,
|
||||||
origin: cursor_position,
|
origin: cursor_position,
|
||||||
};
|
};
|
||||||
event_status = event::Status::Captured;
|
shell.capture_event();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -484,7 +480,7 @@ where
|
||||||
DragEvent::Picked { index },
|
DragEvent::Picked { index },
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
event_status = event::Status::Captured;
|
shell.capture_event();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Action::Dragging { origin, index, .. } => {
|
Action::Dragging { origin, index, .. } => {
|
||||||
|
|
@ -496,7 +492,7 @@ where
|
||||||
origin,
|
origin,
|
||||||
index,
|
index,
|
||||||
};
|
};
|
||||||
event_status = event::Status::Captured;
|
shell.capture_event();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
@ -529,8 +525,7 @@ where
|
||||||
drop_position,
|
drop_position,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
event_status =
|
shell.capture_event();
|
||||||
event::Status::Captured;
|
|
||||||
}
|
}
|
||||||
} else if let Some(on_reorder) =
|
} else if let Some(on_reorder) =
|
||||||
&self.on_drag
|
&self.on_drag
|
||||||
|
|
@ -538,8 +533,7 @@ where
|
||||||
shell.publish(on_reorder(
|
shell.publish(on_reorder(
|
||||||
DragEvent::Canceled { index },
|
DragEvent::Canceled { index },
|
||||||
));
|
));
|
||||||
event_status =
|
shell.capture_event();
|
||||||
event::Status::Captured;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*action = Action::Idle;
|
*action = Action::Idle;
|
||||||
|
|
@ -554,15 +548,14 @@ where
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let child_status = self
|
self.children
|
||||||
.children
|
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.zip(&mut tree.children)
|
.zip(&mut tree.children)
|
||||||
.zip(layout.children())
|
.zip(layout.children())
|
||||||
.map(|((child, state), c_layout)| {
|
.for_each(|((child, state), c_layout)| {
|
||||||
child.as_widget_mut().on_event(
|
child.as_widget_mut().update(
|
||||||
state,
|
state,
|
||||||
event.clone(),
|
&event.clone(),
|
||||||
c_layout
|
c_layout
|
||||||
.with_virtual_offset(layout.virtual_offset()),
|
.with_virtual_offset(layout.virtual_offset()),
|
||||||
cursor,
|
cursor,
|
||||||
|
|
@ -571,10 +564,7 @@ where
|
||||||
shell,
|
shell,
|
||||||
viewport,
|
viewport,
|
||||||
)
|
)
|
||||||
})
|
});
|
||||||
.fold(event::Status::Ignored, event::Status::merge);
|
|
||||||
|
|
||||||
event::Status::merge(event_status, child_status)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mouse_interaction(
|
fn mouse_interaction(
|
||||||
|
|
@ -807,8 +797,9 @@ where
|
||||||
fn overlay<'b>(
|
fn overlay<'b>(
|
||||||
&'b mut self,
|
&'b mut self,
|
||||||
tree: &'b mut Tree,
|
tree: &'b mut Tree,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'b>,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
|
viewport: &Rectangle,
|
||||||
translation: Vector,
|
translation: Vector,
|
||||||
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
|
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
|
||||||
overlay::from_children(
|
overlay::from_children(
|
||||||
|
|
@ -816,6 +807,7 @@ where
|
||||||
tree,
|
tree,
|
||||||
layout,
|
layout,
|
||||||
renderer,
|
renderer,
|
||||||
|
viewport,
|
||||||
translation,
|
translation,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ use cosmic::iced::advanced::layout::{self, Layout};
|
||||||
use cosmic::iced::advanced::widget::{Operation, Tree, Widget, tree};
|
use cosmic::iced::advanced::widget::{Operation, Tree, Widget, tree};
|
||||||
use cosmic::iced::advanced::{Clipboard, Shell, overlay, renderer};
|
use cosmic::iced::advanced::{Clipboard, Shell, overlay, renderer};
|
||||||
use cosmic::iced::alignment::{self, Alignment};
|
use cosmic::iced::alignment::{self, Alignment};
|
||||||
use cosmic::iced::event::{self, Event};
|
use cosmic::iced::event::Event;
|
||||||
use cosmic::iced::{self, Transformation, mouse};
|
use cosmic::iced::{self, Transformation, mouse};
|
||||||
use cosmic::iced::{
|
use cosmic::iced::{
|
||||||
Background, Border, Color, Element, Length, Padding, Pixels,
|
Background, Border, Color, Element, Length, Padding, Pixels,
|
||||||
|
|
@ -371,7 +371,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&mut self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
limits: &layout::Limits,
|
limits: &layout::Limits,
|
||||||
|
|
@ -385,48 +385,43 @@ where
|
||||||
self.padding,
|
self.padding,
|
||||||
self.spacing,
|
self.spacing,
|
||||||
self.align,
|
self.align,
|
||||||
&self.children,
|
&mut self.children,
|
||||||
&mut tree.children,
|
&mut tree.children,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operate(
|
fn operate(
|
||||||
&self,
|
&mut self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
operation: &mut dyn Operation,
|
operation: &mut dyn Operation,
|
||||||
) {
|
) {
|
||||||
operation.container(
|
operation.container(None, layout.bounds());
|
||||||
None,
|
operation.traverse(&mut |operation| {
|
||||||
layout.bounds(),
|
self.children
|
||||||
&mut |operation| {
|
.iter_mut()
|
||||||
self.children
|
.zip(&mut tree.children)
|
||||||
.iter()
|
.zip(layout.children())
|
||||||
.zip(&mut tree.children)
|
.for_each(|((child, state), layout)| {
|
||||||
.zip(layout.children())
|
child
|
||||||
.for_each(|((child, state), layout)| {
|
.as_widget_mut()
|
||||||
child.as_widget().operate(
|
.operate(state, layout, renderer, operation);
|
||||||
state, layout, renderer, operation,
|
});
|
||||||
);
|
});
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(
|
fn update(
|
||||||
&mut self,
|
&mut self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
event: Event,
|
event: &Event,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor: mouse::Cursor,
|
cursor: mouse::Cursor,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
clipboard: &mut dyn Clipboard,
|
clipboard: &mut dyn Clipboard,
|
||||||
shell: &mut Shell<'_, Message>,
|
shell: &mut Shell<'_, Message>,
|
||||||
viewport: &Rectangle,
|
viewport: &Rectangle,
|
||||||
) -> event::Status {
|
) {
|
||||||
let mut event_status = event::Status::Ignored;
|
|
||||||
|
|
||||||
let action = tree.state.downcast_mut::<Action>();
|
let action = tree.state.downcast_mut::<Action>();
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
|
|
@ -447,7 +442,7 @@ where
|
||||||
index,
|
index,
|
||||||
origin: cursor_position,
|
origin: cursor_position,
|
||||||
};
|
};
|
||||||
event_status = event::Status::Captured;
|
shell.capture_event();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -472,7 +467,7 @@ where
|
||||||
DragEvent::Picked { index },
|
DragEvent::Picked { index },
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
event_status = event::Status::Captured;
|
shell.capture_event();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Action::Dragging { origin, index, .. } => {
|
Action::Dragging { origin, index, .. } => {
|
||||||
|
|
@ -484,7 +479,7 @@ where
|
||||||
origin,
|
origin,
|
||||||
index,
|
index,
|
||||||
};
|
};
|
||||||
event_status = event::Status::Captured;
|
shell.capture_event();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
@ -517,8 +512,7 @@ where
|
||||||
drop_position,
|
drop_position,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
event_status =
|
shell.capture_event();
|
||||||
event::Status::Captured;
|
|
||||||
}
|
}
|
||||||
} else if let Some(on_reorder) =
|
} else if let Some(on_reorder) =
|
||||||
&self.on_drag
|
&self.on_drag
|
||||||
|
|
@ -526,8 +520,7 @@ where
|
||||||
shell.publish(on_reorder(
|
shell.publish(on_reorder(
|
||||||
DragEvent::Canceled { index },
|
DragEvent::Canceled { index },
|
||||||
));
|
));
|
||||||
event_status =
|
shell.capture_event();
|
||||||
event::Status::Captured;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*action = Action::Idle;
|
*action = Action::Idle;
|
||||||
|
|
@ -542,15 +535,14 @@ where
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let child_status = self
|
self.children
|
||||||
.children
|
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.zip(&mut tree.children)
|
.zip(&mut tree.children)
|
||||||
.zip(layout.children())
|
.zip(layout.children())
|
||||||
.map(|((child, state), layout)| {
|
.for_each(|((child, state), layout)| {
|
||||||
child.as_widget_mut().on_event(
|
child.as_widget_mut().update(
|
||||||
state,
|
state,
|
||||||
event.clone(),
|
&event.clone(),
|
||||||
layout,
|
layout,
|
||||||
cursor,
|
cursor,
|
||||||
renderer,
|
renderer,
|
||||||
|
|
@ -558,10 +550,7 @@ where
|
||||||
shell,
|
shell,
|
||||||
viewport,
|
viewport,
|
||||||
)
|
)
|
||||||
})
|
});
|
||||||
.fold(event::Status::Ignored, event::Status::merge);
|
|
||||||
|
|
||||||
event::Status::merge(event_status, child_status)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mouse_interaction(
|
fn mouse_interaction(
|
||||||
|
|
@ -769,8 +758,9 @@ where
|
||||||
fn overlay<'b>(
|
fn overlay<'b>(
|
||||||
&'b mut self,
|
&'b mut self,
|
||||||
tree: &'b mut Tree,
|
tree: &'b mut Tree,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'b>,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
|
viewport: &Rectangle,
|
||||||
translation: Vector,
|
translation: Vector,
|
||||||
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
|
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
|
||||||
overlay::from_children(
|
overlay::from_children(
|
||||||
|
|
@ -778,6 +768,7 @@ where
|
||||||
tree,
|
tree,
|
||||||
layout,
|
layout,
|
||||||
renderer,
|
renderer,
|
||||||
|
viewport,
|
||||||
translation,
|
translation,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -833,7 +824,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&mut self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
limits: &layout::Limits,
|
limits: &layout::Limits,
|
||||||
|
|
@ -875,8 +866,8 @@ where
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (i, child) in self.row.children.iter().enumerate() {
|
for (i, child) in self.row.children.iter_mut().enumerate() {
|
||||||
let node = child.as_widget().layout(
|
let node = child.as_widget_mut().layout(
|
||||||
&mut tree.children[i],
|
&mut tree.children[i],
|
||||||
renderer,
|
renderer,
|
||||||
&limits,
|
&limits,
|
||||||
|
|
@ -927,7 +918,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operate(
|
fn operate(
|
||||||
&self,
|
&mut self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
|
|
@ -936,18 +927,18 @@ where
|
||||||
self.row.operate(tree, layout, renderer, operation);
|
self.row.operate(tree, layout, renderer, operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(
|
fn update(
|
||||||
&mut self,
|
&mut self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
event: Event,
|
event: &Event,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor: mouse::Cursor,
|
cursor: mouse::Cursor,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
clipboard: &mut dyn Clipboard,
|
clipboard: &mut dyn Clipboard,
|
||||||
shell: &mut Shell<'_, Message>,
|
shell: &mut Shell<'_, Message>,
|
||||||
viewport: &Rectangle,
|
viewport: &Rectangle,
|
||||||
) -> event::Status {
|
) {
|
||||||
self.row.on_event(
|
self.row.update(
|
||||||
tree, event, layout, cursor, renderer, clipboard, shell,
|
tree, event, layout, cursor, renderer, clipboard, shell,
|
||||||
viewport,
|
viewport,
|
||||||
)
|
)
|
||||||
|
|
@ -984,11 +975,18 @@ where
|
||||||
fn overlay<'b>(
|
fn overlay<'b>(
|
||||||
&'b mut self,
|
&'b mut self,
|
||||||
tree: &'b mut Tree,
|
tree: &'b mut Tree,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'b>,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
|
viewport: &Rectangle,
|
||||||
translation: Vector,
|
translation: Vector,
|
||||||
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
|
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
|
||||||
self.row.overlay(tree, layout, renderer, translation)
|
self.row.overlay(
|
||||||
|
tree,
|
||||||
|
layout,
|
||||||
|
renderer,
|
||||||
|
viewport,
|
||||||
|
translation,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,5 +2,5 @@
|
||||||
#[allow(clippy::nursery)]
|
#[allow(clippy::nursery)]
|
||||||
#[allow(clippy::pedantic)]
|
#[allow(clippy::pedantic)]
|
||||||
pub mod draggable;
|
pub mod draggable;
|
||||||
pub mod slide_text;
|
// pub mod slide_text;
|
||||||
pub mod verse_editor;
|
pub mod verse_editor;
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&mut self,
|
||||||
_tree: &mut widget::Tree,
|
_tree: &mut widget::Tree,
|
||||||
_renderer: &Renderer,
|
_renderer: &Renderer,
|
||||||
_limits: &layout::Limits,
|
_limits: &layout::Limits,
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,7 @@ use cosmic::{
|
||||||
iced_widget::{column, row},
|
iced_widget::{column, row},
|
||||||
theme,
|
theme,
|
||||||
widget::{
|
widget::{
|
||||||
button, combo_box, container, horizontal_space, icon,
|
button, combo_box, container, icon, space, text_editor,
|
||||||
text_editor,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -62,12 +61,12 @@ impl VerseEditor {
|
||||||
let verse = self.verse_name;
|
let verse = self.verse_name;
|
||||||
Action::UpdateVerse((verse, lyrics))
|
Action::UpdateVerse((verse, lyrics))
|
||||||
}
|
}
|
||||||
text_editor::Action::Scroll { pixels } => {
|
text_editor::Action::Scroll { lines } => {
|
||||||
if self.content.line_count() > 6 {
|
if self.content.line_count() > 6 {
|
||||||
self.content.perform(action);
|
self.content.perform(action);
|
||||||
Action::None
|
Action::None
|
||||||
} else {
|
} else {
|
||||||
Action::ScrollVerses(pixels)
|
Action::ScrollVerses(lines as f32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
|
@ -109,12 +108,12 @@ impl VerseEditor {
|
||||||
);
|
);
|
||||||
|
|
||||||
let verse_title =
|
let verse_title =
|
||||||
row![combo, horizontal_space(), delete_button];
|
row![combo, space::horizontal(), delete_button];
|
||||||
|
|
||||||
let lyric: Element<Message> = if self.verse_name
|
let lyric: Element<Message> = if self.verse_name
|
||||||
== VerseName::Blank
|
== VerseName::Blank
|
||||||
{
|
{
|
||||||
horizontal_space().into()
|
space::horizontal().into()
|
||||||
} else {
|
} else {
|
||||||
text_editor(&self.content)
|
text_editor(&self.content)
|
||||||
.on_action(Message::UpdateLyric)
|
.on_action(Message::UpdateLyric)
|
||||||
|
|
@ -131,15 +130,11 @@ impl VerseEditor {
|
||||||
.into(),
|
.into(),
|
||||||
),
|
),
|
||||||
border: Border::default()
|
border: Border::default()
|
||||||
.rounded(space_s)
|
.rounded(space_s as u8)
|
||||||
.width(2)
|
.width(2)
|
||||||
.color(
|
.color(
|
||||||
t.cosmic().bg_component_divider(),
|
t.cosmic().bg_component_divider(),
|
||||||
),
|
),
|
||||||
icon: t
|
|
||||||
.cosmic()
|
|
||||||
.primary_component_color()
|
|
||||||
.into(),
|
|
||||||
placeholder: neutral
|
placeholder: neutral
|
||||||
.with_alpha(0.7)
|
.with_alpha(0.7)
|
||||||
.into(),
|
.into(),
|
||||||
|
|
@ -147,13 +142,15 @@ impl VerseEditor {
|
||||||
selection: t.cosmic().accent.base.into(),
|
selection: t.cosmic().accent.base.into(),
|
||||||
};
|
};
|
||||||
let hovered_border = Border::default()
|
let hovered_border = Border::default()
|
||||||
.rounded(space_s)
|
.rounded(space_s as u8)
|
||||||
.width(3)
|
.width(3)
|
||||||
.color(t.cosmic().accent.hover);
|
.color(t.cosmic().accent.hover);
|
||||||
match s {
|
match s {
|
||||||
text_editor::Status::Active => base_style,
|
text_editor::Status::Active => base_style,
|
||||||
text_editor::Status::Hovered
|
text_editor::Status::Hovered
|
||||||
| text_editor::Status::Focused => {
|
| text_editor::Status::Focused {
|
||||||
|
..
|
||||||
|
} => {
|
||||||
base_style.border = hovered_border;
|
base_style.border = hovered_border;
|
||||||
base_style
|
base_style
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue