more things, but mostly starting to add library management
Some checks are pending
/ test (push) Waiting to run
Some checks are pending
/ test (push) Waiting to run
This commit is contained in:
parent
3fe77c93e2
commit
645411b59c
13 changed files with 341 additions and 260 deletions
22
flake.nix
22
flake.nix
|
@ -8,16 +8,18 @@
|
||||||
fenix.url = "github:nix-community/fenix";
|
fenix.url = "github:nix-community/fenix";
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = inputs: with inputs;
|
outputs =
|
||||||
flake-utils.lib.eachDefaultSystem
|
inputs:
|
||||||
(system:
|
with inputs;
|
||||||
|
flake-utils.lib.eachDefaultSystem (
|
||||||
|
system:
|
||||||
let
|
let
|
||||||
pkgs = import nixpkgs {
|
pkgs = import nixpkgs {
|
||||||
inherit system;
|
inherit system;
|
||||||
overlays = [fenix.overlays.default];
|
overlays = [ fenix.overlays.default ];
|
||||||
# overlays = [cargo2nix.overlays.default];
|
# overlays = [cargo2nix.overlays.default];
|
||||||
};
|
};
|
||||||
naersk' = pkgs.callPackage naersk {};
|
naersk' = pkgs.callPackage naersk { };
|
||||||
nbi = with pkgs; [
|
nbi = with pkgs; [
|
||||||
# Rust tools
|
# Rust tools
|
||||||
alejandra
|
alejandra
|
||||||
|
@ -52,6 +54,7 @@
|
||||||
vulkan-tools
|
vulkan-tools
|
||||||
libGL
|
libGL
|
||||||
cargo-flamegraph
|
cargo-flamegraph
|
||||||
|
bacon
|
||||||
|
|
||||||
fontconfig
|
fontconfig
|
||||||
glib
|
glib
|
||||||
|
@ -74,11 +77,14 @@
|
||||||
sqlx-cli
|
sqlx-cli
|
||||||
cargo-watch
|
cargo-watch
|
||||||
];
|
];
|
||||||
in rec
|
in
|
||||||
|
rec {
|
||||||
|
devShell =
|
||||||
|
pkgs.mkShell.override
|
||||||
{
|
{
|
||||||
devShell = pkgs.mkShell.override {
|
|
||||||
# stdenv = pkgs.stdenvAdapters.useMoldLinker pkgs.clangStdenv;
|
# stdenv = pkgs.stdenvAdapters.useMoldLinker pkgs.clangStdenv;
|
||||||
} {
|
}
|
||||||
|
{
|
||||||
nativeBuildInputs = nbi;
|
nativeBuildInputs = nbi;
|
||||||
buildInputs = bi;
|
buildInputs = bi;
|
||||||
LD_LIBRARY_PATH = "$LD_LIBRARY_PATH:${
|
LD_LIBRARY_PATH = "$LD_LIBRARY_PATH:${
|
||||||
|
|
|
@ -52,7 +52,9 @@ impl Content for Image {
|
||||||
if self.path.exists() {
|
if self.path.exists() {
|
||||||
self.path
|
self.path
|
||||||
.file_name()
|
.file_name()
|
||||||
.map_or("Missing image".into(), |f| f.to_string_lossy().to_string())
|
.map_or("Missing image".into(), |f| {
|
||||||
|
f.to_string_lossy().to_string()
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
"Missing image".into()
|
"Missing image".into()
|
||||||
}
|
}
|
||||||
|
@ -158,7 +160,9 @@ impl Model<Image> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("There was an error in converting images: {e}");
|
error!(
|
||||||
|
"There was an error in converting images: {e}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,11 +21,11 @@ pub enum ServiceItemKind {
|
||||||
impl std::fmt::Display for ServiceItemKind {
|
impl std::fmt::Display for ServiceItemKind {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
let s = match self {
|
let s = match self {
|
||||||
Self::Song(s) => "song".to_owned(),
|
Self::Song(_) => "song".to_owned(),
|
||||||
Self::Image(i) => "image".to_owned(),
|
Self::Image(_) => "image".to_owned(),
|
||||||
Self::Video(v) => "video".to_owned(),
|
Self::Video(_) => "video".to_owned(),
|
||||||
Self::Presentation(p) => "html".to_owned(),
|
Self::Presentation(_) => "html".to_owned(),
|
||||||
Self::Content(s) => "content".to_owned(),
|
Self::Content(_) => "content".to_owned(),
|
||||||
};
|
};
|
||||||
write!(f, "{s}")
|
write!(f, "{s}")
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,9 @@ impl Display for ParseError {
|
||||||
f: &mut std::fmt::Formatter<'_>,
|
f: &mut std::fmt::Formatter<'_>,
|
||||||
) -> std::fmt::Result {
|
) -> std::fmt::Result {
|
||||||
let message = match self {
|
let message = match self {
|
||||||
Self::UnknownType => "The type does not exist. It needs to be one of 'song', 'video', 'image', 'presentation', or 'content'",
|
Self::UnknownType => {
|
||||||
|
"The type does not exist. It needs to be one of 'song', 'video', 'image', 'presentation', or 'content'"
|
||||||
|
}
|
||||||
};
|
};
|
||||||
write!(f, "Error: {message}")
|
write!(f, "Error: {message}")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use std::mem::replace;
|
use std::mem::replace;
|
||||||
|
|
||||||
use cosmic::iced::Executor;
|
use miette::{Result, miette};
|
||||||
use miette::{miette, Result};
|
|
||||||
use sqlx::{Connection, SqliteConnection};
|
use sqlx::{Connection, SqliteConnection};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -46,7 +45,8 @@ impl<T> Model<T> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use] pub fn get_item(&self, index: i32) -> Option<&T> {
|
#[must_use]
|
||||||
|
pub fn get_item(&self, index: i32) -> Option<&T> {
|
||||||
self.items.get(index as usize)
|
self.items.get(index as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,9 @@ impl Content for Presentation {
|
||||||
if self.path.exists() {
|
if self.path.exists() {
|
||||||
self.path
|
self.path
|
||||||
.file_name()
|
.file_name()
|
||||||
.map_or("Missing presentation".into(), |f| f.to_string_lossy().to_string())
|
.map_or("Missing presentation".into(), |f| {
|
||||||
|
f.to_string_lossy().to_string()
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
"Missing presentation".into()
|
"Missing presentation".into()
|
||||||
}
|
}
|
||||||
|
@ -189,14 +191,16 @@ impl ServiceTrait for Presentation {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Presentation {
|
impl Presentation {
|
||||||
#[must_use] pub fn new() -> Self {
|
#[must_use]
|
||||||
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
title: String::new(),
|
title: String::new(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use] pub const fn get_kind(&self) -> &PresKind {
|
#[must_use]
|
||||||
|
pub const fn get_kind(&self) -> &PresKind {
|
||||||
&self.kind
|
&self.kind
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,7 +172,8 @@ impl From<&Value> for ServiceItem {
|
||||||
} else if let Some(background) =
|
} else if let Some(background) =
|
||||||
list.get(background_pos)
|
list.get(background_pos)
|
||||||
{
|
{
|
||||||
if let Value::List(item) = background { match &item[0] {
|
if let Value::List(item) = background {
|
||||||
|
match &item[0] {
|
||||||
Value::Symbol(Symbol(s))
|
Value::Symbol(Symbol(s))
|
||||||
if s == "image" =>
|
if s == "image" =>
|
||||||
{
|
{
|
||||||
|
@ -195,7 +196,8 @@ impl From<&Value> for ServiceItem {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
} } else {
|
}
|
||||||
|
} else {
|
||||||
error!(
|
error!(
|
||||||
"There is no background here: {:?}",
|
"There is no background here: {:?}",
|
||||||
background
|
background
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
use std::{collections::HashMap, option::Option, path::PathBuf};
|
use std::{collections::HashMap, option::Option, path::PathBuf};
|
||||||
|
|
||||||
use cosmic::iced::Executor;
|
|
||||||
use crisp::types::{Keyword, Symbol, Value};
|
use crisp::types::{Keyword, Symbol, Value};
|
||||||
use miette::{miette, IntoDiagnostic, Result};
|
use miette::{IntoDiagnostic, Result, miette};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::{
|
use sqlx::{
|
||||||
pool::PoolConnection, query, sqlite::SqliteRow, FromRow, Row,
|
FromRow, Row, Sqlite, SqliteConnection, SqlitePool,
|
||||||
Sqlite, SqliteConnection, SqlitePool,
|
pool::PoolConnection, query, sqlite::SqliteRow,
|
||||||
};
|
};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
use crate::{core::slide, Slide, SlideBuilder};
|
use crate::{Slide, SlideBuilder, core::slide};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
content::Content,
|
content::Content,
|
||||||
|
@ -128,7 +127,9 @@ impl FromRow<'_, SqliteRow> for Song {
|
||||||
})),
|
})),
|
||||||
verse_order: Some({
|
verse_order: Some({
|
||||||
let str: &str = row.try_get(0)?;
|
let str: &str = row.try_get(0)?;
|
||||||
str.split(' ').map(std::string::ToString::to_string).collect()
|
str.split(' ')
|
||||||
|
.map(std::string::ToString::to_string)
|
||||||
|
.collect()
|
||||||
}),
|
}),
|
||||||
background: {
|
background: {
|
||||||
let string: String = row.try_get(7)?;
|
let string: String = row.try_get(7)?;
|
||||||
|
@ -250,8 +251,7 @@ pub fn lisp_to_song(list: Vec<Value>) -> Song {
|
||||||
.position(|v| v == &Value::Keyword(Keyword::from("title")))
|
.position(|v| v == &Value::Keyword(Keyword::from("title")))
|
||||||
{
|
{
|
||||||
let pos = key_pos + 1;
|
let pos = key_pos + 1;
|
||||||
list.get(pos)
|
list.get(pos).map_or(String::from("song"), String::from)
|
||||||
.map_or(String::from("song"), String::from)
|
|
||||||
} else {
|
} else {
|
||||||
String::from("song")
|
String::from("song")
|
||||||
};
|
};
|
||||||
|
@ -625,7 +625,27 @@ You saved my soul"
|
||||||
let lyrics = song.get_lyrics();
|
let lyrics = song.get_lyrics();
|
||||||
match lyrics {
|
match lyrics {
|
||||||
Ok(lyrics) => {
|
Ok(lyrics) => {
|
||||||
assert_eq!(vec!["From the Day\nI Am They", "When You found me,\nI was so blind\nMy sin was before me,\nI was swallowed by pride", "But out of the darkness,\nYou brought me to Your light\nYou showed me new mercy\nAnd opened up my eyes", "From the day\nYou saved my soul\n'Til the very moment\nWhen I come home", "I'll sing, I'll dance,\nMy heart will overflow\nFrom the day\nYou saved my soul", "Where brilliant light\nIs all around\nAnd endless joy\nIs the only sound", "Oh, rest my heart\nForever now\nOh, in Your arms\nI'll always be found", "From the day\nYou saved my soul\n'Til the very moment\nWhen I come home", "I'll sing, I'll dance,\nMy heart will overflow\nFrom the day\nYou saved my soul", "My love is Yours\nMy heart is Yours\nMy life is Yours\nForever", "My love is Yours\nMy heart is Yours\nMy life is Yours\nForever", "From the day\nYou saved my soul\n'Til the very moment\nWhen I come home", "I'll sing, I'll dance,\nMy heart will overflow\nFrom the day\nYou saved my soul", "From the day\nYou saved my soul\n'Til the very moment\nWhen I come home", "I'll sing, I'll dance,\nMy heart will overflow\nFrom the day\nYou saved my soul", "Oh Oh Oh\nFrom the day\nYou saved my soul\n"], lyrics);
|
assert_eq!(
|
||||||
|
vec![
|
||||||
|
"From the Day\nI Am They",
|
||||||
|
"When You found me,\nI was so blind\nMy sin was before me,\nI was swallowed by pride",
|
||||||
|
"But out of the darkness,\nYou brought me to Your light\nYou showed me new mercy\nAnd opened up my eyes",
|
||||||
|
"From the day\nYou saved my soul\n'Til the very moment\nWhen I come home",
|
||||||
|
"I'll sing, I'll dance,\nMy heart will overflow\nFrom the day\nYou saved my soul",
|
||||||
|
"Where brilliant light\nIs all around\nAnd endless joy\nIs the only sound",
|
||||||
|
"Oh, rest my heart\nForever now\nOh, in Your arms\nI'll always be found",
|
||||||
|
"From the day\nYou saved my soul\n'Til the very moment\nWhen I come home",
|
||||||
|
"I'll sing, I'll dance,\nMy heart will overflow\nFrom the day\nYou saved my soul",
|
||||||
|
"My love is Yours\nMy heart is Yours\nMy life is Yours\nForever",
|
||||||
|
"My love is Yours\nMy heart is Yours\nMy life is Yours\nForever",
|
||||||
|
"From the day\nYou saved my soul\n'Til the very moment\nWhen I come home",
|
||||||
|
"I'll sing, I'll dance,\nMy heart will overflow\nFrom the day\nYou saved my soul",
|
||||||
|
"From the day\nYou saved my soul\n'Til the very moment\nWhen I come home",
|
||||||
|
"I'll sing, I'll dance,\nMy heart will overflow\nFrom the day\nYou saved my soul",
|
||||||
|
"Oh Oh Oh\nFrom the day\nYou saved my soul\n"
|
||||||
|
],
|
||||||
|
lyrics
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
assert!(false, "{:?}", e)
|
assert!(false, "{:?}", e)
|
||||||
|
|
|
@ -7,13 +7,12 @@ use super::{
|
||||||
service_items::ServiceTrait,
|
service_items::ServiceTrait,
|
||||||
slide::Slide,
|
slide::Slide,
|
||||||
};
|
};
|
||||||
use cosmic::iced::Executor;
|
|
||||||
use crisp::types::{Keyword, Symbol, Value};
|
use crisp::types::{Keyword, Symbol, Value};
|
||||||
use miette::{IntoDiagnostic, Result};
|
use miette::{IntoDiagnostic, Result};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::{
|
use sqlx::{
|
||||||
pool::PoolConnection, query, query_as, Sqlite, SqliteConnection,
|
Sqlite, SqliteConnection, SqlitePool, pool::PoolConnection,
|
||||||
SqlitePool,
|
query, query_as,
|
||||||
};
|
};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
@ -57,7 +56,9 @@ impl Content for Video {
|
||||||
if self.path.exists() {
|
if self.path.exists() {
|
||||||
self.path
|
self.path
|
||||||
.file_name()
|
.file_name()
|
||||||
.map_or("Missing video".into(), |f| f.to_string_lossy().to_string())
|
.map_or("Missing video".into(), |f| {
|
||||||
|
f.to_string_lossy().to_string()
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
"Missing video".into()
|
"Missing video".into()
|
||||||
}
|
}
|
||||||
|
@ -196,7 +197,9 @@ impl Model<Video> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("There was an error in converting videos: {e}");
|
error!(
|
||||||
|
"There was an error in converting videos: {e}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
80
src/main.rs
80
src/main.rs
|
@ -1,31 +1,32 @@
|
||||||
use clap::{command, Parser};
|
use clap::{Parser, command};
|
||||||
use core::service_items::ServiceItem;
|
use core::service_items::ServiceItem;
|
||||||
use core::slide::{Background, Slide, SlideBuilder, TextAlignment, BackgroundKind};
|
use core::slide::{
|
||||||
|
Background, BackgroundKind, Slide, SlideBuilder, TextAlignment,
|
||||||
|
};
|
||||||
use core::songs::Song;
|
use core::songs::Song;
|
||||||
use cosmic::app::context_drawer::ContextDrawer;
|
use cosmic::app::context_drawer::ContextDrawer;
|
||||||
use cosmic::app::{Core, Settings, Task};
|
use cosmic::app::{Core, Settings, Task};
|
||||||
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::{Mode, Position};
|
||||||
use cosmic::iced::{self, event, window, Length, Point};
|
use cosmic::iced::{self, Length, Point, event, window};
|
||||||
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::theme;
|
use cosmic::theme;
|
||||||
|
use cosmic::widget::Container;
|
||||||
use cosmic::widget::dnd_destination::dnd_destination;
|
use cosmic::widget::dnd_destination::dnd_destination;
|
||||||
use cosmic::widget::nav_bar::nav_bar_style;
|
use cosmic::widget::nav_bar::nav_bar_style;
|
||||||
use cosmic::widget::segmented_button::Entity;
|
|
||||||
use cosmic::widget::text;
|
use cosmic::widget::text;
|
||||||
use cosmic::widget::tooltip::Position as TPosition;
|
use cosmic::widget::tooltip::Position as TPosition;
|
||||||
use cosmic::widget::{
|
use cosmic::widget::{
|
||||||
button, horizontal_space, mouse_area, nav_bar, search_input,
|
Space, button, horizontal_space, mouse_area, nav_bar,
|
||||||
tooltip, vertical_space, Space,
|
search_input, tooltip, vertical_space,
|
||||||
};
|
};
|
||||||
use cosmic::widget::{icon, slider};
|
use cosmic::widget::{icon, slider};
|
||||||
use cosmic::{executor, Application, ApplicationExt, Element};
|
use cosmic::{Application, ApplicationExt, Element, executor};
|
||||||
use cosmic::{widget::Container, Theme};
|
|
||||||
use crisp::types::Value;
|
use crisp::types::Value;
|
||||||
use lisp::parse_lisp;
|
use lisp::parse_lisp;
|
||||||
use miette::{miette, Result};
|
use miette::{Result, miette};
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use resvg::usvg::fontdb;
|
use resvg::usvg::fontdb;
|
||||||
use std::fs::read_to_string;
|
use std::fs::read_to_string;
|
||||||
|
@ -34,10 +35,10 @@ use std::sync::Arc;
|
||||||
use tracing::{debug, level_filters::LevelFilter};
|
use tracing::{debug, level_filters::LevelFilter};
|
||||||
use tracing::{error, warn};
|
use tracing::{error, warn};
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
|
use ui::EditorMode;
|
||||||
use ui::library::{self, Library};
|
use ui::library::{self, Library};
|
||||||
use ui::presenter::{self, Presenter};
|
use ui::presenter::{self, Presenter};
|
||||||
use ui::song_editor::{self, SongEditor};
|
use ui::song_editor::{self, SongEditor};
|
||||||
use ui::EditorMode;
|
|
||||||
|
|
||||||
use crate::core::kinds::ServiceItemKind;
|
use crate::core::kinds::ServiceItemKind;
|
||||||
use crate::ui::text_svg::{self};
|
use crate::ui::text_svg::{self};
|
||||||
|
@ -94,9 +95,9 @@ fn main() -> Result<()> {
|
||||||
.map_err(|e| miette!("Invalid things... {}", e))
|
.map_err(|e| miette!("Invalid things... {}", e))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn theme(_state: &App) -> Theme {
|
// fn theme(_state: &App) -> Theme {
|
||||||
Theme::dark()
|
// Theme::dark()
|
||||||
}
|
// }
|
||||||
|
|
||||||
struct App {
|
struct App {
|
||||||
core: Core,
|
core: Core,
|
||||||
|
@ -135,9 +136,6 @@ enum Message {
|
||||||
Quit,
|
Quit,
|
||||||
Key(Key, Modifiers),
|
Key(Key, Modifiers),
|
||||||
None,
|
None,
|
||||||
DndLeave(Entity),
|
|
||||||
DndEnter(Entity, Vec<String>),
|
|
||||||
DndDrop,
|
|
||||||
EditorToggle(bool),
|
EditorToggle(bool),
|
||||||
ChangeServiceItem(usize),
|
ChangeServiceItem(usize),
|
||||||
AddServiceItem(usize, ServiceItem),
|
AddServiceItem(usize, ServiceItem),
|
||||||
|
@ -816,10 +814,8 @@ impl cosmic::Application for App {
|
||||||
});
|
});
|
||||||
|
|
||||||
self.windows.push(id);
|
self.windows.push(id);
|
||||||
_ = self.set_window_title(
|
_ = self
|
||||||
format!("window_{count}"),
|
.set_window_title(format!("window_{count}"), id);
|
||||||
id,
|
|
||||||
);
|
|
||||||
|
|
||||||
spawn_window.map(|id| {
|
spawn_window.map(|id| {
|
||||||
cosmic::Action::App(Message::WindowOpened(
|
cosmic::Action::App(Message::WindowOpened(
|
||||||
|
@ -873,34 +869,6 @@ impl cosmic::Application for App {
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
Message::Quit => cosmic::iced::exit(),
|
Message::Quit => cosmic::iced::exit(),
|
||||||
Message::DndEnter(entity, data) => {
|
|
||||||
debug!(?entity);
|
|
||||||
debug!(?data);
|
|
||||||
Task::none()
|
|
||||||
}
|
|
||||||
Message::DndDrop => {
|
|
||||||
// debug!(?entity);
|
|
||||||
// debug!(?action);
|
|
||||||
// debug!(?service_item);
|
|
||||||
|
|
||||||
if let Some(library) = &self.library
|
|
||||||
&& let Some((lib, item)) = library.dragged_item
|
|
||||||
{
|
|
||||||
// match lib {
|
|
||||||
// core::model::LibraryKind::Song => ,
|
|
||||||
// core::model::LibraryKind::Video => todo!(),
|
|
||||||
// core::model::LibraryKind::Image => todo!(),
|
|
||||||
// core::model::LibraryKind::Presentation => todo!(),
|
|
||||||
// }
|
|
||||||
let item = library.get_song(item).unwrap();
|
|
||||||
let item = ServiceItem::from(item);
|
|
||||||
self.nav_model
|
|
||||||
.insert()
|
|
||||||
.text(item.title.clone())
|
|
||||||
.data(item);
|
|
||||||
}
|
|
||||||
Task::none()
|
|
||||||
}
|
|
||||||
Message::AddLibrary(library) => {
|
Message::AddLibrary(library) => {
|
||||||
self.library = Some(library);
|
self.library = Some(library);
|
||||||
Task::none()
|
Task::none()
|
||||||
|
@ -910,10 +878,6 @@ impl cosmic::Application for App {
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
Message::None => Task::none(),
|
Message::None => Task::none(),
|
||||||
Message::DndLeave(entity) => {
|
|
||||||
// debug!(?entity);
|
|
||||||
Task::none()
|
|
||||||
}
|
|
||||||
Message::EditorToggle(edit) => {
|
Message::EditorToggle(edit) => {
|
||||||
if edit {
|
if edit {
|
||||||
self.editor_mode = Some(EditorMode::Song);
|
self.editor_mode = Some(EditorMode::Song);
|
||||||
|
@ -1006,12 +970,12 @@ impl cosmic::Application for App {
|
||||||
song_editor::Message::ChangeSong(song),
|
song_editor::Message::ChangeSong(song),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
ServiceItemKind::Video(video) => todo!(),
|
ServiceItemKind::Video(_video) => todo!(),
|
||||||
ServiceItemKind::Image(image) => todo!(),
|
ServiceItemKind::Image(_image) => todo!(),
|
||||||
ServiceItemKind::Presentation(presentation) => {
|
ServiceItemKind::Presentation(_presentation) => {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
ServiceItemKind::Content(slide) => todo!(),
|
ServiceItemKind::Content(_slide) => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1198,7 +1162,7 @@ where
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_library(&mut self) -> Task<Message> {
|
fn add_library(&self) -> Task<Message> {
|
||||||
Task::perform(async move { Library::new().await }, |x| {
|
Task::perform(async move { Library::new().await }, |x| {
|
||||||
cosmic::Action::App(Message::AddLibrary(x))
|
cosmic::Action::App(Message::AddLibrary(x))
|
||||||
})
|
})
|
||||||
|
@ -1220,7 +1184,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_service(
|
fn add_service(
|
||||||
&mut self,
|
&self,
|
||||||
items: Vec<ServiceItem>,
|
items: Vec<ServiceItem>,
|
||||||
fontdb: Arc<fontdb::Database>,
|
fontdb: Arc<fontdb::Database>,
|
||||||
) -> Task<Message> {
|
) -> Task<Message> {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
iced::{
|
iced::{
|
||||||
alignment::Vertical, clipboard::dnd::DndAction,
|
alignment::Vertical, clipboard::dnd::DndAction,
|
||||||
|
@ -7,9 +9,10 @@ use cosmic::{
|
||||||
iced_widget::{column, row as rowm, text as textm},
|
iced_widget::{column, row as rowm, text as textm},
|
||||||
theme,
|
theme,
|
||||||
widget::{
|
widget::{
|
||||||
button, container, horizontal_space, icon, mouse_area,
|
button, container, context_menu, horizontal_space, icon,
|
||||||
responsive, row, scrollable, text, text_input, Container,
|
menu::{self, Action as MenuAction},
|
||||||
DndSource, Space, Widget,
|
mouse_area, responsive, row, scrollable, text, text_input,
|
||||||
|
Container, DndSource, Space,
|
||||||
},
|
},
|
||||||
Element, Task,
|
Element, Task,
|
||||||
};
|
};
|
||||||
|
@ -41,6 +44,29 @@ pub(crate) struct Library {
|
||||||
pub dragged_item: Option<(LibraryKind, i32)>,
|
pub dragged_item: Option<(LibraryKind, i32)>,
|
||||||
editing_item: Option<(LibraryKind, i32)>,
|
editing_item: Option<(LibraryKind, i32)>,
|
||||||
db: SqlitePool,
|
db: SqlitePool,
|
||||||
|
menu_keys: std::collections::HashMap<menu::KeyBind, MenuMessage>,
|
||||||
|
context_menu: Option<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Copy)]
|
||||||
|
enum MenuMessage {
|
||||||
|
Delete((LibraryKind, i32)),
|
||||||
|
Open,
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MenuAction for MenuMessage {
|
||||||
|
type Message = Message;
|
||||||
|
|
||||||
|
fn message(&self) -> Self::Message {
|
||||||
|
match self {
|
||||||
|
MenuMessage::Delete((kind, index)) => {
|
||||||
|
Message::DeleteItem((*kind, *index))
|
||||||
|
}
|
||||||
|
MenuMessage::Open => todo!(),
|
||||||
|
MenuMessage::None => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) enum Action {
|
pub(crate) enum Action {
|
||||||
|
@ -53,7 +79,7 @@ pub(crate) enum Action {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) enum Message {
|
pub(crate) enum Message {
|
||||||
AddItem,
|
AddItem,
|
||||||
RemoveItem,
|
DeleteItem((LibraryKind, i32)),
|
||||||
OpenItem(Option<(LibraryKind, i32)>),
|
OpenItem(Option<(LibraryKind, i32)>),
|
||||||
HoverLibrary(Option<LibraryKind>),
|
HoverLibrary(Option<LibraryKind>),
|
||||||
OpenLibrary(Option<LibraryKind>),
|
OpenLibrary(Option<LibraryKind>),
|
||||||
|
@ -69,6 +95,7 @@ pub(crate) enum Message {
|
||||||
UpdatePresentation(Presentation),
|
UpdatePresentation(Presentation),
|
||||||
PresentationChanged,
|
PresentationChanged,
|
||||||
Error(String),
|
Error(String),
|
||||||
|
OpenContext(i32),
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,6 +117,8 @@ impl<'a> Library {
|
||||||
dragged_item: None,
|
dragged_item: None,
|
||||||
editing_item: None,
|
editing_item: None,
|
||||||
db,
|
db,
|
||||||
|
menu_keys: HashMap::new(),
|
||||||
|
context_menu: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +130,16 @@ impl<'a> Library {
|
||||||
match message {
|
match message {
|
||||||
Message::AddItem => (),
|
Message::AddItem => (),
|
||||||
Message::None => (),
|
Message::None => (),
|
||||||
Message::RemoveItem => (),
|
Message::DeleteItem((kind, index)) => {
|
||||||
|
match kind {
|
||||||
|
LibraryKind::Song => todo!(),
|
||||||
|
LibraryKind::Video => todo!(),
|
||||||
|
LibraryKind::Image => todo!(),
|
||||||
|
LibraryKind::Presentation => {
|
||||||
|
self.presentation_library.remove_item(index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
Message::OpenItem(item) => {
|
Message::OpenItem(item) => {
|
||||||
debug!(?item);
|
debug!(?item);
|
||||||
self.editing_item = item;
|
self.editing_item = item;
|
||||||
|
@ -144,7 +182,7 @@ impl<'a> Library {
|
||||||
Task::future(self.db.acquire()).and_then(
|
Task::future(self.db.acquire()).and_then(
|
||||||
move |conn| update_in_db(&song, conn),
|
move |conn| update_in_db(&song, conn),
|
||||||
),
|
),
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
Err(_) => todo!(),
|
Err(_) => todo!(),
|
||||||
}
|
}
|
||||||
|
@ -181,7 +219,7 @@ impl<'a> Library {
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
Err(_) => todo!(),
|
Err(_) => todo!(),
|
||||||
}
|
}
|
||||||
|
@ -215,7 +253,7 @@ impl<'a> Library {
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
Err(_) => todo!(),
|
Err(_) => todo!(),
|
||||||
}
|
}
|
||||||
|
@ -254,6 +292,9 @@ impl<'a> Library {
|
||||||
}
|
}
|
||||||
Message::PresentationChanged => (),
|
Message::PresentationChanged => (),
|
||||||
Message::Error(_) => (),
|
Message::Error(_) => (),
|
||||||
|
Message::OpenContext(index) => {
|
||||||
|
self.context_menu = Some(index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Action::None
|
Action::None
|
||||||
}
|
}
|
||||||
|
@ -374,8 +415,8 @@ impl<'a> Library {
|
||||||
let visual_item = self
|
let visual_item = self
|
||||||
.single_item(index, item, model)
|
.single_item(index, item, model)
|
||||||
.map(|()| Message::None);
|
.map(|()| Message::None);
|
||||||
DndSource::<Message, ServiceItem>::new(
|
DndSource::<Message, ServiceItem>::new({
|
||||||
mouse_area(visual_item)
|
let mouse_area = Element::from(mouse_area(visual_item)
|
||||||
.on_drag(Message::DragItem(service_item.clone()))
|
.on_drag(Message::DragItem(service_item.clone()))
|
||||||
.on_enter(Message::HoverItem(
|
.on_enter(Message::HoverItem(
|
||||||
Some((
|
Some((
|
||||||
|
@ -389,13 +430,31 @@ impl<'a> Library {
|
||||||
index as i32,
|
index as i32,
|
||||||
))),
|
))),
|
||||||
)
|
)
|
||||||
|
.on_right_press(Message::OpenContext(index as i32))
|
||||||
.on_exit(Message::HoverItem(None))
|
.on_exit(Message::HoverItem(None))
|
||||||
.on_press(Message::SelectItem(
|
.on_press(Message::SelectItem(
|
||||||
Some((
|
Some((
|
||||||
model.kind,
|
model.kind,
|
||||||
index as i32,
|
index as i32,
|
||||||
)),
|
)),
|
||||||
)),
|
)));
|
||||||
|
if let Some(context_id) = self.context_menu {
|
||||||
|
if index == context_id as usize {
|
||||||
|
let context_menu = context_menu(
|
||||||
|
mouse_area,
|
||||||
|
self.context_menu.map_or_else(|| None, |id| {
|
||||||
|
Some(menu::items(&self.menu_keys,
|
||||||
|
vec![menu::Item::Button("Delete", None, MenuMessage::Delete((model.kind, index as i32)))]))
|
||||||
|
})
|
||||||
|
);
|
||||||
|
Element::from(context_menu)
|
||||||
|
} else {
|
||||||
|
Element::from(mouse_area)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Element::from(mouse_area)
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
.action(DndAction::Copy)
|
.action(DndAction::Copy)
|
||||||
.drag_icon({
|
.drag_icon({
|
||||||
|
@ -481,11 +540,10 @@ impl<'a> Library {
|
||||||
.accent_text_color()
|
.accent_text_color()
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
} else if let Some((library, selected)) = self.selected_item
|
} else if let Some((library, selected)) =
|
||||||
{
|
self.selected_item
|
||||||
if model.kind == library
|
|
||||||
&& selected == index as i32
|
|
||||||
{
|
{
|
||||||
|
if model.kind == library && selected == index as i32 {
|
||||||
theme::active().cosmic().control_0().into()
|
theme::active().cosmic().control_0().into()
|
||||||
} else {
|
} else {
|
||||||
theme::active()
|
theme::active()
|
||||||
|
@ -563,6 +621,7 @@ impl<'a> Library {
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unused_async)]
|
||||||
pub async fn search_items(
|
pub async fn search_items(
|
||||||
&self,
|
&self,
|
||||||
query: String,
|
query: String,
|
||||||
|
@ -573,14 +632,18 @@ impl<'a> Library {
|
||||||
.items
|
.items
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|song| song.title.to_lowercase().contains(&query))
|
.filter(|song| song.title.to_lowercase().contains(&query))
|
||||||
.map(super::super::core::content::Content::to_service_item)
|
.map(
|
||||||
|
super::super::core::content::Content::to_service_item,
|
||||||
|
)
|
||||||
.collect();
|
.collect();
|
||||||
let videos: Vec<ServiceItem> = self
|
let videos: Vec<ServiceItem> = self
|
||||||
.video_library
|
.video_library
|
||||||
.items
|
.items
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|vid| vid.title.to_lowercase().contains(&query))
|
.filter(|vid| vid.title.to_lowercase().contains(&query))
|
||||||
.map(super::super::core::content::Content::to_service_item)
|
.map(
|
||||||
|
super::super::core::content::Content::to_service_item,
|
||||||
|
)
|
||||||
.collect();
|
.collect();
|
||||||
let images: Vec<ServiceItem> = self
|
let images: Vec<ServiceItem> = self
|
||||||
.image_library
|
.image_library
|
||||||
|
@ -589,14 +652,18 @@ impl<'a> Library {
|
||||||
.filter(|image| {
|
.filter(|image| {
|
||||||
image.title.to_lowercase().contains(&query)
|
image.title.to_lowercase().contains(&query)
|
||||||
})
|
})
|
||||||
.map(super::super::core::content::Content::to_service_item)
|
.map(
|
||||||
|
super::super::core::content::Content::to_service_item,
|
||||||
|
)
|
||||||
.collect();
|
.collect();
|
||||||
let presentations: Vec<ServiceItem> = self
|
let presentations: Vec<ServiceItem> = self
|
||||||
.presentation_library
|
.presentation_library
|
||||||
.items
|
.items
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|pres| pres.title.to_lowercase().contains(&query))
|
.filter(|pres| pres.title.to_lowercase().contains(&query))
|
||||||
.map(super::super::core::content::Content::to_service_item)
|
.map(
|
||||||
|
super::super::core::content::Content::to_service_item,
|
||||||
|
)
|
||||||
.collect();
|
.collect();
|
||||||
items.extend(videos);
|
items.extend(videos);
|
||||||
items.extend(images);
|
items.extend(images);
|
||||||
|
|
|
@ -221,7 +221,8 @@ impl Presenter {
|
||||||
let offset = AbsoluteOffset {
|
let offset = AbsoluteOffset {
|
||||||
x: {
|
x: {
|
||||||
if self.current_slide_index > 2 {
|
if self.current_slide_index > 2 {
|
||||||
(self.current_slide_index as f32).mul_add(187.5, -187.5)
|
(self.current_slide_index as f32)
|
||||||
|
.mul_add(187.5, -187.5)
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,29 @@
|
||||||
use std::{io, path::PathBuf, sync::Arc};
|
use std::{io, path::PathBuf, sync::Arc};
|
||||||
|
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
|
Element, Task,
|
||||||
dialog::file_chooser::open::Dialog,
|
dialog::file_chooser::open::Dialog,
|
||||||
iced::{
|
iced::{
|
||||||
font::{Family, Stretch, Style, Weight},
|
|
||||||
Font, Length,
|
Font, Length,
|
||||||
|
font::{Family, Stretch, Style, Weight},
|
||||||
},
|
},
|
||||||
iced_wgpu::graphics::text::cosmic_text::fontdb,
|
iced_wgpu::graphics::text::cosmic_text::fontdb,
|
||||||
iced_widget::row,
|
iced_widget::row,
|
||||||
theme,
|
theme,
|
||||||
widget::{
|
widget::{
|
||||||
button, column, combo_box, container, horizontal_space, icon, text, text_editor, text_input,
|
button, column, combo_box, container, horizontal_space, icon,
|
||||||
|
text, text_editor, text_input,
|
||||||
},
|
},
|
||||||
Element, Task,
|
|
||||||
};
|
};
|
||||||
use dirs::font_dir;
|
use dirs::font_dir;
|
||||||
use iced_video_player::Video;
|
use iced_video_player::Video;
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{service_items::ServiceTrait, songs::Song},
|
Background, BackgroundKind, core::songs::Song,
|
||||||
ui::slide_editor::SlideEditor,
|
ui::slide_editor::SlideEditor,
|
||||||
Background, BackgroundKind,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SongEditor {
|
pub struct SongEditor {
|
||||||
pub song: Option<Song>,
|
pub song: Option<Song>,
|
||||||
|
@ -244,8 +243,7 @@ impl SongEditor {
|
||||||
Message::ChangeBackground(Ok(path)) => {
|
Message::ChangeBackground(Ok(path)) => {
|
||||||
debug!(?path);
|
debug!(?path);
|
||||||
if let Some(mut song) = self.song.clone() {
|
if let Some(mut song) = self.song.clone() {
|
||||||
let background =
|
let background = Background::try_from(path).ok();
|
||||||
Background::try_from(path).ok();
|
|
||||||
self.background_video(&background);
|
self.background_video(&background);
|
||||||
song.background = background;
|
song.background = background;
|
||||||
return self.update_song(song);
|
return self.update_song(song);
|
||||||
|
@ -258,7 +256,7 @@ impl SongEditor {
|
||||||
return Action::Task(Task::perform(
|
return Action::Task(Task::perform(
|
||||||
pick_background(),
|
pick_background(),
|
||||||
Message::ChangeBackground,
|
Message::ChangeBackground,
|
||||||
))
|
));
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,15 +121,18 @@ impl From<&str> for Font {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Font {
|
impl Font {
|
||||||
#[must_use] pub fn get_name(&self) -> String {
|
#[must_use]
|
||||||
|
pub fn get_name(&self) -> String {
|
||||||
self.name.clone()
|
self.name.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use] pub const fn get_weight(&self) -> Weight {
|
#[must_use]
|
||||||
|
pub const fn get_weight(&self) -> Weight {
|
||||||
self.weight
|
self.weight
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use] pub const fn get_style(&self) -> Style {
|
#[must_use]
|
||||||
|
pub const fn get_style(&self) -> Style {
|
||||||
self.style
|
self.style
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,7 +151,8 @@ impl Font {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use] pub const fn size(mut self, size: u8) -> Self {
|
#[must_use]
|
||||||
|
pub const fn size(mut self, size: u8) -> Self {
|
||||||
self.size = size;
|
self.size = size;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -236,7 +240,10 @@ impl TextSvg {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn alignment(mut self, alignment: TextAlignment) -> Self {
|
pub const fn alignment(
|
||||||
|
mut self,
|
||||||
|
alignment: TextAlignment,
|
||||||
|
) -> Self {
|
||||||
self.alignment = alignment;
|
self.alignment = alignment;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -272,8 +279,8 @@ impl TextSvg {
|
||||||
let middle_position = size.height / 2.0;
|
let middle_position = size.height / 2.0;
|
||||||
let line_spacing = 10.0;
|
let line_spacing = 10.0;
|
||||||
let text_and_line_spacing = font_size + line_spacing;
|
let text_and_line_spacing = font_size + line_spacing;
|
||||||
let starting_y_position =
|
let starting_y_position = half_lines
|
||||||
half_lines.mul_add(-text_and_line_spacing, middle_position);
|
.mul_add(-text_and_line_spacing, middle_position);
|
||||||
|
|
||||||
let text_pieces: Vec<String> = self
|
let text_pieces: Vec<String> = self
|
||||||
.text
|
.text
|
||||||
|
@ -282,7 +289,10 @@ impl TextSvg {
|
||||||
.map(|(index, text)| {
|
.map(|(index, text)| {
|
||||||
format!(
|
format!(
|
||||||
"<tspan x=\"50%\" y=\"{}\">{}</tspan>",
|
"<tspan x=\"50%\" y=\"{}\">{}</tspan>",
|
||||||
(index as f32).mul_add(text_and_line_spacing, starting_y_position),
|
(index as f32).mul_add(
|
||||||
|
text_and_line_spacing,
|
||||||
|
starting_y_position
|
||||||
|
),
|
||||||
text
|
text
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue