a fledgling visible library system

Why do I write these weird commit messages....
This commit is contained in:
Chris Cochrun 2025-01-14 15:03:59 -06:00
parent 201e9dc925
commit 89294061b7
7 changed files with 188 additions and 55 deletions

View file

@ -1,6 +1,9 @@
use crate::{Background, Slide, SlideBuilder, TextAlignment}; use crate::{Background, Slide, SlideBuilder, TextAlignment};
use super::{model::Model, service_items::ServiceTrait}; use super::{
model::{get_db, LibraryKind, Model},
service_items::ServiceTrait,
};
use crisp::types::{Keyword, Value}; use crisp::types::{Keyword, Value};
use miette::{IntoDiagnostic, Result}; use miette::{IntoDiagnostic, Result};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -90,12 +93,22 @@ impl ServiceTrait for Image {
} }
impl Model<Image> { impl Model<Image> {
pub async fn load_from_db(&mut self) { pub async fn new_image_model(db: &mut SqliteConnection) -> Self {
let mut model = Self {
items: vec![],
kind: LibraryKind::Image,
};
model.load_from_db(db).await;
model
}
pub async fn load_from_db(&mut self, db: &mut SqliteConnection) {
let result = query_as!( let result = query_as!(
Image, Image,
r#"SELECT title as "title!", file_path as "path!", id as "id: i32" from images"# r#"SELECT title as "title!", file_path as "path!", id as "id: i32" from images"#
) )
.fetch_all(&mut self.db) .fetch_all(db)
.await; .await;
match result { match result {
Ok(v) => { Ok(v) => {

View file

@ -6,12 +6,20 @@ use sqlx::{Connection, SqliteConnection};
use super::kinds::ServiceItemKind; use super::kinds::ServiceItemKind;
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct Model<T> { pub struct Model<T> {
pub items: Vec<T>, pub items: Vec<T>,
pub db: SqliteConnection, pub kind: LibraryKind,
pub kind: ServiceItemKind,
} }
#[derive(Debug, Clone)]
pub enum LibraryKind {
Song,
Video,
Image,
Presentation,
}
impl<T> Model<T> { impl<T> Model<T> {
pub fn add_item(&mut self, item: T) -> Result<()> { pub fn add_item(&mut self, item: T) -> Result<()> {
self.items.push(item); self.items.push(item);

View file

@ -9,7 +9,10 @@ use tracing::error;
use crate::{Background, Slide, SlideBuilder, TextAlignment}; use crate::{Background, Slide, SlideBuilder, TextAlignment};
use super::{model::Model, service_items::ServiceTrait}; use super::{
model::{get_db, LibraryKind, Model},
service_items::ServiceTrait,
};
#[derive( #[derive(
Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize,
@ -131,11 +134,23 @@ impl FromRow<'_, SqliteRow> for Presentation {
} }
impl Model<Presentation> { impl Model<Presentation> {
pub async fn load_from_db(&mut self) { pub async fn new_presentation_model(
db: &mut SqliteConnection,
) -> Self {
let mut model = Self {
items: vec![],
kind: LibraryKind::Presentation,
};
model.load_from_db(db).await;
model
}
pub async fn load_from_db(&mut self, db: &mut SqliteConnection) {
let result = query!( let result = query!(
r#"SELECT id as "id: i32", title, file_path as "path", html from presentations"# r#"SELECT id as "id: i32", title, file_path as "path", html from presentations"#
) )
.fetch_all(&mut self.db) .fetch_all(db)
.await; .await;
match result { match result {
Ok(v) => { Ok(v) => {

View file

@ -12,7 +12,7 @@ use tracing::{debug, error};
use crate::{core::slide, Slide, SlideBuilder}; use crate::{core::slide, Slide, SlideBuilder};
use super::{ use super::{
model::Model, model::{get_db, LibraryKind, Model},
service_items::ServiceTrait, service_items::ServiceTrait,
slide::{Background, TextAlignment}, slide::{Background, TextAlignment},
}; };
@ -348,9 +348,19 @@ pub async fn get_song_from_db(
} }
impl Model<Song> { impl Model<Song> {
pub async fn load_from_db(&mut self) { pub async fn new_song_model(db: &mut SqliteConnection) -> Self {
let mut model = Self {
items: vec![],
kind: LibraryKind::Song,
};
model.load_from_db(db).await;
model
}
pub async fn load_from_db(&mut self, db: &mut SqliteConnection) {
// static DATABASE_URL: &str = "sqlite:///home/chris/.local/share/lumina/library-db.sqlite3"; // static DATABASE_URL: &str = "sqlite:///home/chris/.local/share/lumina/library-db.sqlite3";
let result = query(r#"SELECT verse_order as "verse_order!", font_size as "font_size!: i32", background_type as "background_type!", horizontal_text_alignment as "horizontal_text_alignment!", vertical_text_alignment as "vertical_text_alignment!", title as "title!", font as "font!", background as "background!", lyrics as "lyrics!", ccli as "ccli!", author as "author!", audio as "audio!", id as "id: i32" from songs"#).fetch_all(&mut self.db).await; let result = query(r#"SELECT verse_order as "verse_order!", font_size as "font_size!: i32", background_type as "background_type!", horizontal_text_alignment as "horizontal_text_alignment!", vertical_text_alignment as "vertical_text_alignment!", title as "title!", font as "font!", background as "background!", lyrics as "lyrics!", ccli as "ccli!", author as "author!", audio as "audio!", id as "id: i32" from songs"#).fetch_all(db).await;
match result { match result {
Ok(s) => { Ok(s) => {
for song in s.into_iter() { for song in s.into_iter() {

View file

@ -1,7 +1,9 @@
use crate::{Background, SlideBuilder, TextAlignment}; use crate::{Background, SlideBuilder, TextAlignment};
use super::{ use super::{
model::Model, service_items::ServiceTrait, slide::Slide, model::{get_db, LibraryKind, Model},
service_items::ServiceTrait,
slide::Slide,
}; };
use cosmic::iced::Executor; use cosmic::iced::Executor;
use crisp::types::{Keyword, Value}; use crisp::types::{Keyword, Value};
@ -136,8 +138,18 @@ impl ServiceTrait for Video {
} }
impl Model<Video> { impl Model<Video> {
pub async fn load_from_db(&mut self) { pub async fn new_video_model(db: &mut SqliteConnection) -> Self {
let result = query_as!(Video, r#"SELECT title as "title!", file_path as "path!", start_time as "start_time!: f32", end_time as "end_time!: f32", loop as "looping!", id as "id: i32" from videos"#).fetch_all(&mut self.db).await; let mut model = Self {
items: vec![],
kind: LibraryKind::Video,
};
model.load_from_db(db).await;
model
}
pub async fn load_from_db(&mut self, db: &mut SqliteConnection) {
let result = query_as!(Video, r#"SELECT title as "title!", file_path as "path!", start_time as "start_time!: f32", end_time as "end_time!: f32", loop as "looping!", id as "id: i32" from videos"#).fetch_all(db).await;
match result { match result {
Ok(v) => { Ok(v) => {
for video in v.into_iter() { for video in v.into_iter() {

View file

@ -23,7 +23,7 @@ use std::path::PathBuf;
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::library::Library; use ui::library::{self, Library};
pub mod core; pub mod core;
pub mod lisp; pub mod lisp;
@ -88,7 +88,7 @@ struct App {
current_slide: Slide, current_slide: Slide,
presentation_open: bool, presentation_open: bool,
cli_mode: bool, cli_mode: bool,
// library: Library, library: Option<Library>,
library_open: bool, library_open: bool,
library_width: f32, library_width: f32,
} }
@ -96,6 +96,7 @@ struct App {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
enum Message { enum Message {
Present(presenter::Message), Present(presenter::Message),
Library(library::Message),
File(PathBuf), File(PathBuf),
DndEnter(ServiceItem), DndEnter(ServiceItem),
DndDrop(ServiceItem), DndDrop(ServiceItem),
@ -103,6 +104,7 @@ enum Message {
CloseWindow(Option<window::Id>), CloseWindow(Option<window::Id>),
WindowOpened(window::Id, Option<Point>), WindowOpened(window::Id, Option<Point>),
WindowClosed(window::Id), WindowClosed(window::Id),
AddLibrary(Library),
Quit, Quit,
Key(Key, Modifiers), Key(Key, Modifiers),
None, None,
@ -174,21 +176,24 @@ impl cosmic::Application for App {
current_slide, current_slide,
presentation_open: false, presentation_open: false,
cli_mode: !input.ui, cli_mode: !input.ui,
// library: Library::new(&items), library: None,
library_open: true, library_open: true,
library_width: 60.0, library_width: 60.0,
}; };
let command; let mut batch = vec![];
if input.ui { if input.ui {
debug!("main view"); debug!("main view");
command = app.update_title() batch.push(app.update_title())
} else { } else {
debug!("window view"); debug!("window view");
command = app.show_window() batch.push(app.show_window())
}; };
(app, command) batch.push(app.add_library());
let batch = Task::batch(batch);
(app, batch)
} }
/// Allows COSMIC to integrate with your application's [`nav_bar::Model`]. /// Allows COSMIC to integrate with your application's [`nav_bar::Model`].
@ -412,6 +417,18 @@ impl cosmic::Application for App {
cosmic::app::Message::App(Message::None) cosmic::app::Message::App(Message::None)
}) })
} }
Message::Library(message) => {
// debug!(?message);
if let Some(library) = &mut self.library {
library.update(message).map(|x| {
debug!(?x);
cosmic::app::Message::App(Message::None)
})
} else {
Task::none()
}
}
Message::File(file) => { Message::File(file) => {
self.file = file; self.file = file;
Task::none() Task::none()
@ -483,6 +500,10 @@ impl cosmic::Application for App {
Message::Quit => cosmic::iced::exit(), Message::Quit => cosmic::iced::exit(),
Message::DndEnter(service_item) => todo!(), Message::DndEnter(service_item) => todo!(),
Message::DndDrop(service_item) => todo!(), Message::DndDrop(service_item) => todo!(),
Message::AddLibrary(library) => {
self.library = Some(library);
Task::none()
}
Message::None => Task::none(), Message::None => Task::none(),
} }
} }
@ -550,9 +571,14 @@ impl cosmic::Application for App {
] ]
.spacing(3); .spacing(3);
// let library = Container::new(self.library.view()) let library =
// .center(Length::Fill) Container::new(if let Some(library) = &self.library {
// .width(self.library_width); library.view().map(|m| Message::Library(m))
} else {
Space::new(0, 0).into()
})
.style(nav_bar_style)
.center(Length::Fill);
// let drag_handle = Container::new(Space::new(1, Length::Fill)) // let drag_handle = Container::new(Space::new(1, Length::Fill))
// .style(|t| nav_bar_style(t)); // .style(|t| nav_bar_style(t));
// let dragger = MouseArea::new(drag_handle) // let dragger = MouseArea::new(drag_handle)
@ -586,6 +612,7 @@ impl cosmic::Application for App {
.center_y(Length::Fill) .center_y(Length::Fill)
.align_left(Length::Fill) .align_left(Length::Fill)
.width(Length::FillPortion(2)), .width(Length::FillPortion(2)),
library
] ]
.width(Length::Fill) .width(Length::Fill)
.height(Length::Fill) .height(Length::Fill)
@ -645,6 +672,12 @@ where
cosmic::app::Message::App(Message::WindowOpened(id, None)) cosmic::app::Message::App(Message::WindowOpened(id, None))
}) })
} }
fn add_library(&mut self) -> Task<Message> {
Task::perform(async { Library::new().await }, |x| {
cosmic::app::Message::App(Message::AddLibrary(x))
})
}
} }
#[cfg(test)] #[cfg(test)]

View file

@ -1,25 +1,36 @@
use cosmic::{ use cosmic::{
iced::Length, iced::{
alignment::{Horizontal, Vertical},
Background, Border, Color, Length,
},
iced_widget::{column, text}, iced_widget::{column, text},
widget::{button, horizontal_space, icon, row}, widget::{
button, container, horizontal_space, icon, mouse_area, row,
Container, Space,
},
Element, Task, Element, Task,
}; };
use crate::core::{ use crate::core::{
images::Image, kinds::ServiceItemKind, model::Model, images::Image,
presentations::Presentation, service_items::ServiceItemModel, kinds::ServiceItemKind,
songs::Song, videos::Video, model::{get_db, LibraryKind, Model},
presentations::Presentation,
service_items::ServiceItemModel,
songs::Song,
videos::Video,
}; };
#[derive(Debug, Clone)]
pub(crate) struct Library { pub(crate) 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>,
presentation_library: Model<Presentation>, presentation_library: Model<Presentation>,
library_open: Option<ServiceItemKind>, library_open: Option<LibraryKind>,
} }
#[derive(Clone)] #[derive(Clone, Debug)]
pub(crate) enum Message { pub(crate) enum Message {
AddItem, AddItem,
RemoveItem, RemoveItem,
@ -28,15 +39,26 @@ pub(crate) enum Message {
} }
impl Library { impl Library {
pub fn new(service_items: &ServiceItemModel) -> Library { pub async fn new() -> Self {
todo!() let mut db = get_db().await;
Self {
song_library: Model::new_song_model(&mut db).await,
image_library: Model::new_image_model(&mut db).await,
video_library: Model::new_video_model(&mut db).await,
presentation_library: Model::new_presentation_model(
&mut db,
)
.await,
library_open: None,
}
} }
pub fn update(&mut self, message: Message) -> Task<Message> { pub fn update(&mut self, message: Message) -> Task<Message> {
match message { match message {
Message::AddItem => todo!(), Message::AddItem => Task::none(),
Message::None => todo!(), Message::None => Task::none(),
Message::RemoveItem => todo!(), Message::RemoveItem => Task::none(),
Message::OpenItem => todo!(), Message::OpenItem => Task::none(),
} }
} }
@ -47,36 +69,56 @@ impl Library {
self.library_item(&self.video_library), self.library_item(&self.video_library),
self.library_item(&self.presentation_library), self.library_item(&self.presentation_library),
]; ];
todo!() column.height(Length::Fill).spacing(5).into()
} }
pub fn library_item<T>( pub fn library_item<T>(
&self, &self,
model: &Model<T>, model: &Model<T>,
) -> Element<Message> { ) -> Element<Message> {
let mut row = row::<Message>(); let mut row = row::<Message>().spacing(5);
let title = match &model.kind { match &model.kind {
ServiceItemKind::Song(song) => { LibraryKind::Song => {
row = row.push(text!("Songs")); row = row
.push(text!("Songs").align_y(Vertical::Center));
} }
ServiceItemKind::Video(video) => { LibraryKind::Video => {
row = row.push(text!("Videos")); row = row
.push(text!("Videos").align_y(Vertical::Center));
} }
ServiceItemKind::Image(image) => { LibraryKind::Image => {
row = row.push(text!("Images")); row = row
.push(text!("Images").align_y(Vertical::Center));
} }
ServiceItemKind::Presentation(presentation) => { LibraryKind::Presentation => {
row = row.push(text!("Presentations")); row = row.push(
text!("Presentations").align_y(Vertical::Center),
);
} }
ServiceItemKind::Content(slide) => todo!(),
}; };
let item_count = model.items.len(); let item_count = model.items.len();
row = row.push(text!("{}", item_count)); row = row.push(horizontal_space());
row = row.push(horizontal_space().width(Length::Fill));
row = row.push( row = row.push(
button::icon(icon::from_name("arrow-down")) text!("{}", item_count)
.on_press(Message::None), .align_y(Vertical::Center)
.size(18),
); );
row.into() row = row.push(icon::from_name("arrow-down").size(20));
let row_container =
Container::new(row)
.padding(5)
.style(|t| {
container::Style::default()
.background(Background::Color(
t.cosmic().secondary.base.into(),
))
.border(Border::default().rounded(
t.cosmic().corner_radii.radius_s,
))
})
.center_x(Length::Fill)
.center_y(Length::Shrink);
let button = mouse_area(row_container);
button.into()
} }
} }