Merge branch 'master' of git.tfcconnection.org:chris/lumina-iced
This commit is contained in:
commit
ac00afbfe6
7 changed files with 395 additions and 357 deletions
563
Cargo.lock
generated
563
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -20,10 +20,10 @@ strum = "0.26.3"
|
||||||
strum_macros = "0.26.4"
|
strum_macros = "0.26.4"
|
||||||
ron = "0.8.1"
|
ron = "0.8.1"
|
||||||
sqlx = { version = "0.8.2", features = ["sqlite", "runtime-tokio"] }
|
sqlx = { version = "0.8.2", features = ["sqlite", "runtime-tokio"] }
|
||||||
dirs = "5.0.1"
|
dirs = "6.0.0"
|
||||||
tokio = "1.41.1"
|
tokio = "1.41.1"
|
||||||
crisp = { git = "https://git.tfcconnection.org/chris/crisp", version = "0.1.3" }
|
crisp = { git = "https://git.tfcconnection.org/chris/crisp", version = "0.1.3" }
|
||||||
rodio = { version = "0.20.1", features = ["symphonia-all", "tracing"] }
|
rodio = { version = "0.21.1", features = ["symphonia-all", "tracing"] }
|
||||||
gstreamer = "0.23"
|
gstreamer = "0.23"
|
||||||
gstreamer-app = "0.23"
|
gstreamer-app = "0.23"
|
||||||
# gstreamer-video = "0.23"
|
# gstreamer-video = "0.23"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::mem::replace;
|
use std::mem::replace;
|
||||||
|
|
||||||
use miette::{Result, miette};
|
use miette::{miette, Result};
|
||||||
use sqlx::{Connection, SqliteConnection};
|
use sqlx::{Connection, SqliteConnection};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
|
||||||
22
src/main.rs
22
src/main.rs
|
|
@ -1,4 +1,4 @@
|
||||||
use clap::{Parser, command};
|
use clap::{command, Parser};
|
||||||
use core::service_items::ServiceItem;
|
use core::service_items::ServiceItem;
|
||||||
use core::slide::{
|
use core::slide::{
|
||||||
Background, BackgroundKind, Slide, SlideBuilder, TextAlignment,
|
Background, BackgroundKind, Slide, SlideBuilder, TextAlignment,
|
||||||
|
|
@ -9,24 +9,24 @@ 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, Length, Point, event, window};
|
use cosmic::iced::{self, event, window, Length, Point};
|
||||||
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::text;
|
|
||||||
use cosmic::widget::tooltip::Position as TPosition;
|
use cosmic::widget::tooltip::Position as TPosition;
|
||||||
|
use cosmic::widget::Container;
|
||||||
use cosmic::widget::{
|
use cosmic::widget::{
|
||||||
Space, button, horizontal_space, mouse_area, nav_bar,
|
button, horizontal_space, mouse_area, nav_bar, search_input,
|
||||||
search_input, tooltip, vertical_space,
|
tooltip, vertical_space, Space,
|
||||||
};
|
};
|
||||||
|
use cosmic::widget::{container, text};
|
||||||
use cosmic::widget::{icon, slider};
|
use cosmic::widget::{icon, slider};
|
||||||
use cosmic::{Application, ApplicationExt, Element, executor};
|
use cosmic::{executor, Application, ApplicationExt, Element};
|
||||||
use crisp::types::Value;
|
use crisp::types::Value;
|
||||||
use lisp::parse_lisp;
|
use lisp::parse_lisp;
|
||||||
use miette::{Result, miette};
|
use miette::{miette, Result};
|
||||||
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;
|
||||||
|
|
@ -35,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};
|
||||||
|
|
@ -1115,7 +1115,9 @@ impl cosmic::Application for App {
|
||||||
];
|
];
|
||||||
|
|
||||||
if let Some(_editor) = &self.editor_mode {
|
if let Some(_editor) = &self.editor_mode {
|
||||||
Element::from(song_editor)
|
container(song_editor)
|
||||||
|
.padding(cosmic::theme::spacing().space_xxl)
|
||||||
|
.into()
|
||||||
} else {
|
} else {
|
||||||
Element::from(column)
|
Element::from(column)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ use cosmic::{
|
||||||
};
|
};
|
||||||
use miette::{IntoDiagnostic, Result};
|
use miette::{IntoDiagnostic, Result};
|
||||||
use rapidfuzz::distance::levenshtein;
|
use rapidfuzz::distance::levenshtein;
|
||||||
use sqlx::{pool::PoolConnection, Sqlite, SqlitePool};
|
use sqlx::{migrate, pool::PoolConnection, Sqlite, SqlitePool};
|
||||||
use tracing::{debug, error, warn};
|
use tracing::{debug, error, warn};
|
||||||
|
|
||||||
use crate::core::{
|
use crate::core::{
|
||||||
|
|
@ -33,7 +33,7 @@ use crate::core::{
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct Library {
|
pub struct Library {
|
||||||
song_library: Model<Song>,
|
song_library: Model<Song>,
|
||||||
image_library: Model<Image>,
|
image_library: Model<Image>,
|
||||||
video_library: Model<Video>,
|
video_library: Model<Video>,
|
||||||
|
|
@ -42,7 +42,6 @@ pub(crate) struct Library {
|
||||||
library_hovered: Option<LibraryKind>,
|
library_hovered: Option<LibraryKind>,
|
||||||
selected_item: Option<(LibraryKind, i32)>,
|
selected_item: Option<(LibraryKind, i32)>,
|
||||||
hovered_item: Option<(LibraryKind, i32)>,
|
hovered_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>,
|
menu_keys: std::collections::HashMap<menu::KeyBind, MenuMessage>,
|
||||||
|
|
@ -70,7 +69,7 @@ impl MenuAction for MenuMessage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) enum Action {
|
pub enum Action {
|
||||||
OpenItem(Option<(LibraryKind, i32)>),
|
OpenItem(Option<(LibraryKind, i32)>),
|
||||||
DraggedItem(ServiceItem),
|
DraggedItem(ServiceItem),
|
||||||
Task(Task<Message>),
|
Task(Task<Message>),
|
||||||
|
|
@ -103,6 +102,9 @@ pub(crate) enum Message {
|
||||||
impl<'a> Library {
|
impl<'a> Library {
|
||||||
pub async fn new() -> Self {
|
pub async fn new() -> Self {
|
||||||
let mut db = add_db().await.expect("probs");
|
let mut db = add_db().await.expect("probs");
|
||||||
|
if let Err(e) = migrate!("./migrations").run(&db).await {
|
||||||
|
error!(?e)
|
||||||
|
}
|
||||||
Self {
|
Self {
|
||||||
song_library: Model::new_song_model(&mut db).await,
|
song_library: Model::new_song_model(&mut db).await,
|
||||||
image_library: Model::new_image_model(&mut db).await,
|
image_library: Model::new_image_model(&mut db).await,
|
||||||
|
|
@ -115,7 +117,6 @@ impl<'a> Library {
|
||||||
library_hovered: None,
|
library_hovered: None,
|
||||||
selected_item: None,
|
selected_item: None,
|
||||||
hovered_item: None,
|
hovered_item: None,
|
||||||
dragged_item: None,
|
|
||||||
editing_item: None,
|
editing_item: None,
|
||||||
db,
|
db,
|
||||||
menu_keys: HashMap::new(),
|
menu_keys: HashMap::new(),
|
||||||
|
|
@ -298,6 +299,27 @@ impl<'a> Library {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Message::AddItem => {
|
||||||
|
let kind =
|
||||||
|
self.library_open.unwrap_or(LibraryKind::Song);
|
||||||
|
let item = match kind {
|
||||||
|
LibraryKind::Song => {
|
||||||
|
let song = Song::default();
|
||||||
|
self.song_library
|
||||||
|
.add_item(song)
|
||||||
|
.map(|_| {
|
||||||
|
let index =
|
||||||
|
self.song_library.items.len();
|
||||||
|
(LibraryKind::Song, index as i32)
|
||||||
|
})
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
LibraryKind::Video => todo!(),
|
||||||
|
LibraryKind::Image => todo!(),
|
||||||
|
LibraryKind::Presentation => todo!(),
|
||||||
|
};
|
||||||
|
return self.update(Message::OpenItem(item));
|
||||||
|
}
|
||||||
Message::OpenItem(item) => {
|
Message::OpenItem(item) => {
|
||||||
debug!(?item);
|
debug!(?item);
|
||||||
self.editing_item = item;
|
self.editing_item = item;
|
||||||
|
|
@ -600,7 +622,7 @@ impl<'a> Library {
|
||||||
if index == context_id as usize {
|
if index == context_id as usize {
|
||||||
let context_menu = context_menu(
|
let context_menu = context_menu(
|
||||||
mouse_area,
|
mouse_area,
|
||||||
self.context_menu.map_or_else(|| None, |id| {
|
self.context_menu.map_or_else(|| None, |_| {
|
||||||
Some(menu::items(&self.menu_keys,
|
Some(menu::items(&self.menu_keys,
|
||||||
vec![menu::Item::Button("Delete", None, MenuMessage::Delete((model.kind, index as i32)))]))
|
vec![menu::Item::Button("Delete", None, MenuMessage::Delete((model.kind, index as i32)))]))
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ use cosmic::{
|
||||||
Task,
|
Task,
|
||||||
};
|
};
|
||||||
use iced_video_player::{gst_pbutils, Position, Video, VideoPlayer};
|
use iced_video_player::{gst_pbutils, Position, Video, VideoPlayer};
|
||||||
use rodio::{Decoder, OutputStream, Sink};
|
use rodio::{Decoder, OutputStream, OutputStreamBuilder, Sink};
|
||||||
use tracing::{debug, error, info, warn};
|
use tracing::{debug, error, info, warn};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
|
@ -44,7 +44,7 @@ pub(crate) struct Presenter {
|
||||||
pub video: Option<Video>,
|
pub video: Option<Video>,
|
||||||
pub video_position: f32,
|
pub video_position: f32,
|
||||||
pub audio: Option<PathBuf>,
|
pub audio: Option<PathBuf>,
|
||||||
sink: (OutputStream, Arc<Sink>),
|
sink: (Arc<Sink>, OutputStream),
|
||||||
hovered_slide: Option<(usize, usize)>,
|
hovered_slide: Option<(usize, usize)>,
|
||||||
scroll_id: Id,
|
scroll_id: Id,
|
||||||
current_font: Font,
|
current_font: Font,
|
||||||
|
|
@ -159,11 +159,14 @@ impl Presenter {
|
||||||
video_position: 0.0,
|
video_position: 0.0,
|
||||||
hovered_slide: None,
|
hovered_slide: None,
|
||||||
sink: {
|
sink: {
|
||||||
let (stream, stream_handle) =
|
let stream_handle =
|
||||||
OutputStream::try_default().unwrap();
|
OutputStreamBuilder::open_default_stream()
|
||||||
|
.expect("Can't open default rodio stream");
|
||||||
(
|
(
|
||||||
stream,
|
Arc::new(Sink::connect_new(
|
||||||
Arc::new(Sink::try_new(&stream_handle).unwrap()),
|
&stream_handle.mixer(),
|
||||||
|
)),
|
||||||
|
stream_handle,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
scroll_id: Id::unique(),
|
scroll_id: Id::unique(),
|
||||||
|
|
@ -385,7 +388,7 @@ impl Presenter {
|
||||||
return Action::Task(self.start_audio());
|
return Action::Task(self.start_audio());
|
||||||
}
|
}
|
||||||
Message::EndAudio => {
|
Message::EndAudio => {
|
||||||
self.sink.1.stop();
|
self.sink.0.stop();
|
||||||
}
|
}
|
||||||
Message::None => debug!("Presenter Message::None"),
|
Message::None => debug!("Presenter Message::None"),
|
||||||
Message::Error(error) => {
|
Message::Error(error) => {
|
||||||
|
|
@ -397,9 +400,8 @@ impl Presenter {
|
||||||
|
|
||||||
pub fn view(&self) -> Element<Message> {
|
pub fn view(&self) -> Element<Message> {
|
||||||
slide_view(
|
slide_view(
|
||||||
&self.current_slide,
|
self.current_slide.clone(),
|
||||||
&self.video,
|
&self.video,
|
||||||
self.current_font,
|
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
|
|
@ -407,9 +409,8 @@ impl Presenter {
|
||||||
|
|
||||||
pub fn view_preview(&self) -> Element<Message> {
|
pub fn view_preview(&self) -> Element<Message> {
|
||||||
slide_view(
|
slide_view(
|
||||||
&self.current_slide,
|
self.current_slide.clone(),
|
||||||
&self.video,
|
&self.video,
|
||||||
self.current_font,
|
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
|
|
@ -443,9 +444,8 @@ impl Presenter {
|
||||||
);
|
);
|
||||||
|
|
||||||
let container = slide_view(
|
let container = slide_view(
|
||||||
slide,
|
slide.clone(),
|
||||||
&self.video,
|
&self.video,
|
||||||
font,
|
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
@ -663,7 +663,7 @@ impl Presenter {
|
||||||
debug!(?audio, "This is where audio should be changing");
|
debug!(?audio, "This is where audio should be changing");
|
||||||
let audio = audio.clone();
|
let audio = audio.clone();
|
||||||
Task::perform(
|
Task::perform(
|
||||||
start_audio(Arc::clone(&self.sink.1), audio),
|
start_audio(Arc::clone(&self.sink.0), audio),
|
||||||
|()| Message::None,
|
|()| Message::None,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -705,9 +705,8 @@ fn scale_font(font_size: f32, width: f32) -> f32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn slide_view<'a>(
|
pub(crate) fn slide_view<'a>(
|
||||||
slide: &'a Slide,
|
slide: Slide,
|
||||||
video: &'a Option<Video>,
|
video: &'a Option<Video>,
|
||||||
font: Font,
|
|
||||||
delegate: bool,
|
delegate: bool,
|
||||||
hide_mouse: bool,
|
hide_mouse: bool,
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,28 @@
|
||||||
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, Length,
|
|
||||||
font::{Family, Stretch, Style, Weight},
|
font::{Family, Stretch, Style, Weight},
|
||||||
|
Font, Length,
|
||||||
},
|
},
|
||||||
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,
|
button, column, combo_box, container, horizontal_space, icon,
|
||||||
text, text_editor, text_input,
|
scrollable, 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::{
|
||||||
Background, BackgroundKind, core::songs::Song,
|
core::{service_items::ServiceTrait, songs::Song},
|
||||||
ui::slide_editor::SlideEditor,
|
ui::{presenter::slide_view, slide_editor::SlideEditor},
|
||||||
|
Background, BackgroundKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -282,49 +283,48 @@ impl SongEditor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn slide_preview(&self) -> Element<Message> {
|
fn slide_preview(&self) -> Element<Message> {
|
||||||
// if let Some(song) = &self.song {
|
if let Some(song) = &self.song {
|
||||||
// if let Ok(slides) = song.to_slides() {
|
if let Ok(slides) = song.to_slides() {
|
||||||
// let slides = slides
|
let slides: Vec<Element<Message>> = slides
|
||||||
// .iter()
|
.into_iter()
|
||||||
// .enumerate()
|
.enumerate()
|
||||||
// .map(|(index, slide)| {
|
.map(|(index, slide)| {
|
||||||
// container(
|
container(
|
||||||
// slide_view(
|
slide_view(
|
||||||
// slide.clone(),
|
slide,
|
||||||
// if index == 0 {
|
if index == 0 {
|
||||||
// &self.video
|
&self.video
|
||||||
// } else {
|
} else {
|
||||||
// &None
|
&None
|
||||||
// },
|
},
|
||||||
// self.current_font,
|
false,
|
||||||
// false,
|
false,
|
||||||
// false,
|
)
|
||||||
// )
|
.map(|_| Message::None),
|
||||||
// .map(|_| Message::None),
|
)
|
||||||
// )
|
.height(250)
|
||||||
// .height(250)
|
.center_x(Length::Fill)
|
||||||
// .center_x(Length::Fill)
|
.padding([0, 20])
|
||||||
// .padding([0, 20])
|
.clip(true)
|
||||||
// .clip(true)
|
.into()
|
||||||
// .into()
|
})
|
||||||
// })
|
.collect();
|
||||||
// .collect();
|
scrollable(
|
||||||
// scrollable(
|
column::with_children(slides)
|
||||||
// column::with_children(slides)
|
.spacing(theme::active().cosmic().space_l()),
|
||||||
// .spacing(theme::active().cosmic().space_l()),
|
)
|
||||||
// )
|
.height(Length::Fill)
|
||||||
// .height(Length::Fill)
|
.width(Length::Fill)
|
||||||
// .width(Length::Fill)
|
.into()
|
||||||
// .into()
|
} else {
|
||||||
// } else {
|
horizontal_space().into()
|
||||||
// horizontal_space().into()
|
}
|
||||||
// }
|
} else {
|
||||||
// } else {
|
horizontal_space().into()
|
||||||
// horizontal_space().into()
|
}
|
||||||
// }
|
// self.slide_state
|
||||||
self.slide_state
|
// .view(Font::with_name("Quicksand Bold"))
|
||||||
.view(Font::with_name("Quicksand Bold"))
|
// .map(|_s| Message::None)
|
||||||
.map(|_s| Message::None)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn left_column(&self) -> Element<Message> {
|
fn left_column(&self) -> Element<Message> {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue