diff --git a/TODO.org b/TODO.org index 013fe67..edf8d95 100644 --- a/TODO.org +++ b/TODO.org @@ -1,9 +1,21 @@ #+TITLE: Todo List +#+filetags: :projectdev: :PROPERTIES: :CATEGORY: dev :END: -* Tasks [61%] [47/76] +* Tasks [0%] [0/0] +** TODO Port to CXX-QT 6.0 [37%] [3/8] +[[file:~/dev/lumina/src/rust/lib.rs]] + +*** DONE image_model.rs +*** DONE video_model.rs +*** DONE presentation_model.rs +*** TODO songs/song_model.rs +*** TODO songs/song_editor.rs +*** TODO ytdl.rs +*** TODO utils.rs +*** TODO obs.rs ** TODO Write a function to handle switching to the next fragment in revealjs [[file:~/dev/lumina/src/qml/presenter/Slide.qml::WebEngineView {]] diff --git a/src/rust/image_model.rs b/src/rust/image_model.rs index fb217b8..873adda 100644 --- a/src/rust/image_model.rs +++ b/src/rust/image_model.rs @@ -1,11 +1,5 @@ #[cxx_qt::bridge] mod image_model { - use crate::image_model::image_model::Image; - use crate::schema::images::dsl::*; - use diesel::sqlite::SqliteConnection; - use diesel::{delete, insert_into, prelude::*, update}; - use std::path::PathBuf; - unsafe extern "C++" { include!(< QAbstractListModel >); include!("cxx-qt-lib/qhash.h"); @@ -30,431 +24,468 @@ mod image_model { type QList_QString = cxx_qt_lib::QList; } - #[derive(Default, Clone, Debug)] - pub struct Image { - id: i32, - title: QString, - path: QString, - } - - #[cxx_qt::qobject(base = "QAbstractListModel")] - #[derive(Default, Debug)] - pub struct ImageModel { - #[qproperty] - count_rows: i32, - highest_id: i32, - images: Vec, - } - - #[cxx_qt::qsignals(ImageModel)] - pub enum Signals<'a> { - #[inherit] - DataChanged { - top_left: &'a QModelIndex, - bottom_right: &'a QModelIndex, - roles: &'a QVector_i32, - }, - } - + #[qenum(ImageModel)] enum Role { - IdRole, - PathRole, - TitleRole, + Id, + Path, + Title, } - // impl FromStr for Role { - // type Err = (); - // fn from_str(input: &str) -> Result { - // match input { - // "id" => Ok(Role::IdRole), - // "title" => Ok(Role::TitleRole), - // "path" => Ok(Role::PathRole), - // _ => Err(()), - // } - // } - // } + unsafe extern "RustQt" { + #[qobject] + #[base = "QAbstractListModel"] + #[qml_element] + #[qproperty(i32, count_rows)] + type ImageModel = super::ImageModelRust; - // use crate::entities::{images, prelude::Images}; - // use sea_orm::{ConnectionTrait, Database, DbBackend, DbErr, Statement, ActiveValue}; - impl qobject::ImageModel { - #[qinvokable] - pub fn clear(mut self: Pin<&mut Self>) { - unsafe { - self.as_mut().begin_reset_model(); - self.as_mut().images_mut().clear(); - self.as_mut().end_reset_model(); - } - } + #[inherit] + #[qsignal] + fn data_changed( + self: Pin<&mut ImageModel>, + top_left: &QModelIndex, + bottom_right: &QModelIndex, + roles: &QVector_i32, + ); #[qinvokable] - pub fn setup(mut self: Pin<&mut Self>) { - let db = &mut self.as_mut().get_db(); - let results = images - .load::(db) - .expect("Error loading images"); - self.as_mut().set_highest_id(0); - - println!("SHOWING IMAGES"); - println!("--------------"); - for image in results { - println!("{}", image.title); - println!("{}", image.id); - println!("{}", image.path); - println!("--------------"); - if self.as_mut().highest_id() < &image.id { - self.as_mut().set_highest_id(image.id); - } - - let img = self::Image { - id: image.id, - title: QString::from(&image.title), - path: QString::from(&image.path), - }; - - self.as_mut().add_image(img); - } - println!("--------------------------------------"); - println!("{:?}", self.as_mut().images()); - println!("--------------------------------------"); - } - + fn clear(self: Pin<&mut ImageModel>); #[qinvokable] - pub fn remove_item( - mut self: Pin<&mut Self>, + fn setup(self: Pin<&mut ImageModel>); + #[qinvokable] + fn remove_item( + self: Pin<&mut ImageModel>, index: i32, - ) -> bool { - if index < 0 || (index as usize) >= self.images().len() { - return false; - } - let db = &mut self.as_mut().get_db(); - - let image_id = - self.images().get(index as usize).unwrap().id; - - let result = - delete(images.filter(id.eq(image_id))).execute(db); - - match result { - Ok(_i) => { - unsafe { - self.as_mut().begin_remove_rows( - &QModelIndex::default(), - index, - index, - ); - self.as_mut() - .images_mut() - .remove(index as usize); - self.as_mut().end_remove_rows(); - } - println!("removed-item-at-index: {:?}", image_id); - println!("new-Vec: {:?}", self.as_mut().images()); - true - } - Err(_e) => { - println!("Cannot connect to database"); - false - } - } - } - - fn get_db(self: Pin<&mut Self>) -> SqliteConnection { - let mut data = dirs::data_local_dir().unwrap(); - data.push("lumina"); - data.push("library-db.sqlite3"); - let mut db_url = String::from("sqlite://"); - db_url.push_str(data.to_str().unwrap()); - println!("DB: {:?}", db_url); - - SqliteConnection::establish(&db_url).unwrap_or_else( - |_| panic!("error connecting to {}", db_url), - ) - } - + ) -> bool; #[qinvokable] - pub fn new_item(mut self: Pin<&mut Self>, url: QUrl) { - println!("LETS INSERT THIS SUCKER!"); - let file_path = PathBuf::from(url.path().to_string()); - let name = - file_path.file_stem().unwrap().to_str().unwrap(); - let image_id = self.rust().highest_id + 1; - let image_title = QString::from(name); - let image_path = url.to_qstring(); - - if self.as_mut().add_item( - image_id, - image_title, - image_path, - ) { - println!("filename: {:?}", name); - self.as_mut().set_highest_id(image_id); - } else { - println!("Error in inserting item"); - } - } - - fn add_item( - mut self: Pin<&mut Self>, - image_id: i32, - image_title: QString, - image_path: QString, - ) -> bool { - let db = &mut self.as_mut().get_db(); - // println!("{:?}", db); - let image = self::Image { - id: image_id, - title: image_title.clone(), - path: image_path.clone(), - }; - println!("{:?}", image); - - let result = insert_into(images) - .values(( - id.eq(&image_id), - title.eq(&image_title.to_string()), - path.eq(&image_path.to_string()), - )) - .execute(db); - println!("{:?}", result); - - match result { - Ok(_i) => { - self.as_mut().add_image(image); - println!("{:?}", self.as_mut().images()); - true - } - Err(_e) => { - println!("Cannot connect to database"); - false - } - } - } - - fn add_image(mut self: Pin<&mut Self>, image: self::Image) { - let index = self.as_ref().images().len() as i32; - println!("{:?}", image); - unsafe { - self.as_mut().begin_insert_rows( - &QModelIndex::default(), - index, - index, - ); - self.as_mut().images_mut().push(image); - self.as_mut().end_insert_rows(); - } - } - + fn new_item(self: Pin<&mut ImageModel>, url: QUrl); #[qinvokable] - pub fn update_title( - mut self: Pin<&mut Self>, + fn update_title( + self: Pin<&mut ImageModel>, index: i32, updated_title: QString, - ) -> bool { - let mut vector_roles = QVector_i32::default(); - vector_roles - .append(self.as_ref().get_role_id(Role::TitleRole)); - let model_index = &self.as_ref().index( - index, - 0, - &QModelIndex::default(), - ); - - let db = &mut self.as_mut().get_db(); - let result = update(images.filter(id.eq(index))) - .set(title.eq(updated_title.to_string())) - .execute(db); - match result { - Ok(_i) => { - for image in self - .as_mut() - .images_mut() - .iter_mut() - .filter(|x| x.id == index) - { - image.title = updated_title.clone(); - println!("rust-title: {:?}", image.title); - } - self.as_mut().emit_data_changed( - model_index, - model_index, - &vector_roles, - ); - true - } - Err(_e) => false, - } - } - + ) -> bool; #[qinvokable] - pub fn update_file_path( - mut self: Pin<&mut Self>, + fn update_file_path( + self: Pin<&mut ImageModel>, index: i32, updated_file_path: QString, - ) -> bool { - let mut vector_roles = QVector_i32::default(); - vector_roles - .append(self.as_ref().get_role_id(Role::PathRole)); - let model_index = &self.as_ref().index( - index, - 0, - &QModelIndex::default(), - ); - - let db = &mut self.as_mut().get_db(); - let result = update(images.filter(id.eq(index))) - .set(path.eq(updated_file_path.to_string())) - .execute(db); - match result { - Ok(_i) => { - for image in self - .as_mut() - .images_mut() - .iter_mut() - .filter(|x| x.id == index) - { - image.path = updated_file_path.clone(); - println!("rust-title: {:?}", image.path); - } - self.as_mut().emit_data_changed( - model_index, - model_index, - &vector_roles, - ); - true - } - Err(_e) => false, - } - } - + ) -> bool; #[qinvokable] - pub fn get_item( - self: Pin<&mut Self>, + fn get_item( + self: Pin<&mut ImageModel>, index: i32, - ) -> QMap_QString_QVariant { - println!("{index}"); - let mut qvariantmap = QMap_QString_QVariant::default(); - let idx = self.index(index, 0, &QModelIndex::default()); - if !idx.is_valid() { - return qvariantmap; - } - let role_names = self.as_ref().role_names(); - let role_names_iter = role_names.iter(); - if let Some(image) = - self.rust().images.get(index as usize) - { - for i in role_names_iter { - qvariantmap.insert( - QString::from(&i.1.to_string()), - self.as_ref().data(&idx, *i.0), - ); - } - }; - qvariantmap - } - - fn get_role_id(&self, role: Role) -> i32 { - match role { - Role::IdRole => 0, - Role::TitleRole => 1, - Role::PathRole => 2, - _ => 0, - } - } + ) -> QMap_QString_QVariant; } - // Create Rust bindings for C++ functions of the base class (QAbstractItemModel) - #[cxx_qt::inherit] - extern "C++" { + impl cxx_qt::Threading for ImageModel {} + + unsafe extern "RustQt" { + #[inherit] unsafe fn begin_insert_rows( - self: Pin<&mut qobject::ImageModel>, + self: Pin<&mut ImageModel>, parent: &QModelIndex, first: i32, last: i32, ); - unsafe fn end_insert_rows( - self: Pin<&mut qobject::ImageModel>, - ); + #[inherit] + unsafe fn end_insert_rows(self: Pin<&mut ImageModel>); + + #[inherit] unsafe fn begin_remove_rows( - self: Pin<&mut qobject::ImageModel>, + self: Pin<&mut ImageModel>, parent: &QModelIndex, first: i32, last: i32, ); - unsafe fn end_remove_rows( - self: Pin<&mut qobject::ImageModel>, - ); - unsafe fn begin_reset_model( - self: Pin<&mut qobject::ImageModel>, - ); - unsafe fn end_reset_model( - self: Pin<&mut qobject::ImageModel>, - ); - } + #[inherit] + unsafe fn begin_move_rows( + self: Pin<&mut ImageModel>, + source_parent: &QModelIndex, + source_first: i32, + source_last: i32, + destination_parent: &QModelIndex, + destination_child: i32, + ) -> bool; - #[cxx_qt::inherit] - unsafe extern "C++" { - #[cxx_name = "canFetchMore"] - fn base_can_fetch_more( - self: &qobject::ImageModel, + #[inherit] + unsafe fn end_move_rows(self: Pin<&mut ImageModel>); + + #[inherit] + unsafe fn end_remove_rows(self: Pin<&mut ImageModel>); + + #[inherit] + unsafe fn begin_reset_model(self: Pin<&mut ImageModel>); + + #[inherit] + unsafe fn end_reset_model(self: Pin<&mut ImageModel>); + + #[inherit] + fn can_fetch_more( + self: &ImageModel, parent: &QModelIndex, ) -> bool; + #[inherit] fn index( - self: &qobject::ImageModel, + self: &ImageModel, row: i32, column: i32, parent: &QModelIndex, ) -> QModelIndex; - } - - // QAbstractListModel implementation - impl qobject::ImageModel { - #[qinvokable(cxx_override)] - fn data(&self, index: &QModelIndex, role: i32) -> QVariant { - if let Some(image) = - self.images().get(index.row() as usize) - { - return match role { - 0 => QVariant::from(&image.id), - 1 => QVariant::from(&image.title), - 2 => QVariant::from(&image.path), - _ => QVariant::default(), - }; - } - - QVariant::default() - } - - // Example of overriding a C++ virtual method and calling the base class implementation. - #[qinvokable(cxx_override)] - pub fn can_fetch_more(&self, parent: &QModelIndex) -> bool { - self.base_can_fetch_more(parent) - } - - #[qinvokable(cxx_override)] - pub fn role_names(&self) -> QHash_i32_QByteArray { - let mut roles = QHash_i32_QByteArray::default(); - roles.insert(0, cxx_qt_lib::QByteArray::from("id")); - roles.insert(1, cxx_qt_lib::QByteArray::from("title")); - roles.insert(2, cxx_qt_lib::QByteArray::from("filePath")); - roles - } - - #[qinvokable(cxx_override)] - pub fn row_count(&self, _parent: &QModelIndex) -> i32 { - let cnt = self.rust().images.len() as i32; - // self.as_mut().set_count(cnt); - // println!("row count is {cnt}"); - cnt - } #[qinvokable] - pub fn count(mut self: Pin<&mut Self>) -> i32 { - let cnt = self.rust().images.len() as i32; - self.as_mut().set_count_rows(cnt); - cnt + #[cxx_override] + fn data( + self: &ImageModel, + index: &QModelIndex, + role: i32, + ) -> QVariant; + + #[qinvokable] + #[cxx_override] + fn role_names(self: &ImageModel) -> QHash_i32_QByteArray; + + #[qinvokable] + #[cxx_override] + fn row_count(self: &ImageModel, _parent: &QModelIndex) + -> i32; + + #[qinvokable] + fn count(self: &ImageModel) -> i32; + } +} + +use crate::image_model::image_model::Image; +use crate::schema::images::dsl::*; +use diesel::sqlite::SqliteConnection; +use diesel::{delete, insert_into, prelude::*, update}; +use std::path::PathBuf; + +#[derive(Default, Clone, Debug)] +pub struct Image { + id: i32, + title: QString, + path: QString, +} + +#[derive(Default, Debug)] +pub struct ImageModelRust { + count_rows: i32, + highest_id: i32, + images: Vec, +} + +impl qobject::ImageModel { + pub fn clear(mut self: Pin<&mut Self>) { + unsafe { + self.as_mut().begin_reset_model(); + self.as_mut().images_mut().clear(); + self.as_mut().end_reset_model(); + } + } + + pub fn setup(mut self: Pin<&mut Self>) { + let db = &mut self.as_mut().get_db(); + let results = images + .load::(db) + .expect("Error loading images"); + self.as_mut().set_highest_id(0); + + println!("SHOWING IMAGES"); + println!("--------------"); + for image in results { + println!("{}", image.title); + println!("{}", image.id); + println!("{}", image.path); + println!("--------------"); + if self.as_mut().highest_id() < &image.id { + self.as_mut().set_highest_id(image.id); + } + + let img = self::Image { + id: image.id, + title: QString::from(&image.title), + path: QString::from(&image.path), + }; + + self.as_mut().add_image(img); + } + println!("--------------------------------------"); + println!("{:?}", self.as_mut().images()); + println!("--------------------------------------"); + } + + pub fn remove_item(mut self: Pin<&mut Self>, index: i32) -> bool { + if index < 0 || (index as usize) >= self.images().len() { + return false; + } + let db = &mut self.as_mut().get_db(); + + let image_id = self.images().get(index as usize).unwrap().id; + + let result = + delete(images.filter(id.eq(image_id))).execute(db); + + match result { + Ok(_i) => { + unsafe { + self.as_mut().begin_remove_rows( + &QModelIndex::default(), + index, + index, + ); + self.as_mut().images_mut().remove(index as usize); + self.as_mut().end_remove_rows(); + } + println!("removed-item-at-index: {:?}", image_id); + println!("new-Vec: {:?}", self.as_mut().images()); + true + } + Err(_e) => { + println!("Cannot connect to database"); + false + } + } + } + + fn get_db(self: Pin<&mut Self>) -> SqliteConnection { + let mut data = dirs::data_local_dir().unwrap(); + data.push("lumina"); + data.push("library-db.sqlite3"); + let mut db_url = String::from("sqlite://"); + db_url.push_str(data.to_str().unwrap()); + println!("DB: {:?}", db_url); + + SqliteConnection::establish(&db_url).unwrap_or_else(|_| { + panic!("error connecting to {}", db_url) + }) + } + + pub fn new_item(mut self: Pin<&mut Self>, url: QUrl) { + println!("LETS INSERT THIS SUCKER!"); + let file_path = PathBuf::from(url.path().to_string()); + let name = file_path.file_stem().unwrap().to_str().unwrap(); + let image_id = self.rust().highest_id + 1; + let image_title = QString::from(name); + let image_path = url.to_qstring(); + + if self.as_mut().add_item(image_id, image_title, image_path) { + println!("filename: {:?}", name); + self.as_mut().set_highest_id(image_id); + } else { + println!("Error in inserting item"); + } + } + + fn add_item( + mut self: Pin<&mut Self>, + image_id: i32, + image_title: QString, + image_path: QString, + ) -> bool { + let db = &mut self.as_mut().get_db(); + // println!("{:?}", db); + let image = self::Image { + id: image_id, + title: image_title.clone(), + path: image_path.clone(), + }; + println!("{:?}", image); + + let result = insert_into(images) + .values(( + id.eq(&image_id), + title.eq(&image_title.to_string()), + path.eq(&image_path.to_string()), + )) + .execute(db); + println!("{:?}", result); + + match result { + Ok(_i) => { + self.as_mut().add_image(image); + println!("{:?}", self.as_mut().images()); + true + } + Err(_e) => { + println!("Cannot connect to database"); + false + } + } + } + + fn add_image(mut self: Pin<&mut Self>, image: self::Image) { + let index = self.as_ref().images().len() as i32; + println!("{:?}", image); + unsafe { + self.as_mut().begin_insert_rows( + &QModelIndex::default(), + index, + index, + ); + self.as_mut().images_mut().push(image); + self.as_mut().end_insert_rows(); + } + } + + pub fn update_title( + mut self: Pin<&mut Self>, + index: i32, + updated_title: QString, + ) -> bool { + let mut vector_roles = QVector_i32::default(); + vector_roles + .append(self.as_ref().get_role_id(Role::TitleRole)); + let model_index = + &self.as_ref().index(index, 0, &QModelIndex::default()); + + let db = &mut self.as_mut().get_db(); + let result = update(images.filter(id.eq(index))) + .set(title.eq(updated_title.to_string())) + .execute(db); + match result { + Ok(_i) => { + for image in self + .as_mut() + .images_mut() + .iter_mut() + .filter(|x| x.id == index) + { + image.title = updated_title.clone(); + println!("rust-title: {:?}", image.title); + } + self.as_mut().emit_data_changed( + model_index, + model_index, + &vector_roles, + ); + true + } + Err(_e) => false, + } + } + + pub fn update_file_path( + mut self: Pin<&mut Self>, + index: i32, + updated_file_path: QString, + ) -> bool { + let mut vector_roles = QVector_i32::default(); + vector_roles + .append(self.as_ref().get_role_id(Role::PathRole)); + let model_index = + &self.as_ref().index(index, 0, &QModelIndex::default()); + + let db = &mut self.as_mut().get_db(); + let result = update(images.filter(id.eq(index))) + .set(path.eq(updated_file_path.to_string())) + .execute(db); + match result { + Ok(_i) => { + for image in self + .as_mut() + .images_mut() + .iter_mut() + .filter(|x| x.id == index) + { + image.path = updated_file_path.clone(); + println!("rust-title: {:?}", image.path); + } + self.as_mut().emit_data_changed( + model_index, + model_index, + &vector_roles, + ); + true + } + Err(_e) => false, + } + } + + pub fn get_item( + self: Pin<&mut Self>, + index: i32, + ) -> QMap_QString_QVariant { + println!("{index}"); + let mut qvariantmap = QMap_QString_QVariant::default(); + let idx = self.index(index, 0, &QModelIndex::default()); + if !idx.is_valid() { + return qvariantmap; + } + let role_names = self.as_ref().role_names(); + let role_names_iter = role_names.iter(); + if let Some(image) = self.rust().images.get(index as usize) { + for i in role_names_iter { + qvariantmap.insert( + QString::from(&i.1.to_string()), + self.as_ref().data(&idx, *i.0), + ); + } + }; + qvariantmap + } + + fn get_role_id(&self, role: Role) -> i32 { + match role { + qobject::Role::Id => 0, + qobject::Role::Title => 1, + qobject::Role::Path => 2, + _ => 0, } } } + +// QAbstractListModel implementation +impl qobject::ImageModel { + fn data(&self, index: &QModelIndex, role: i32) -> QVariant { + let role = qobject::Roles { repr: role }; + if let Some(image) = self.images().get(index.row() as usize) { + return match role { + qobject::Roles::Id => QVariant::from(&image.id), + qobject::Roles::Title => QVariant::from(&image.title), + qobject::Roles::Path => QVariant::from(&image.path), + _ => QVariant::default(), + }; + } + + QVariant::default() + } + + // Example of overriding a C++ virtual method and calling the base class implementation. + + pub fn can_fetch_more(&self, parent: &QModelIndex) -> bool { + self.base_can_fetch_more(parent) + } + + pub fn role_names(&self) -> QHash_i32_QByteArray { + let mut roles = QHash_i32_QByteArray::default(); + roles.insert( + qobject::Roles::Id.repr, + cxx_qt_lib::QByteArray::from("id"), + ); + roles.insert( + qobject::Roles::Title.repr, + cxx_qt_lib::QByteArray::from("title"), + ); + roles.insert( + qobject::Roles::Path.repr, + cxx_qt_lib::QByteArray::from("filePath"), + ); + roles + } + + pub fn row_count(&self, _parent: &QModelIndex) -> i32 { + let cnt = self.rust().images.len() as i32; + // self.as_mut().set_count(cnt); + // println!("row count is {cnt}"); + cnt + } + + pub fn count(mut self: Pin<&mut Self>) -> i32 { + let cnt = self.rust().images.len() as i32; + self.as_mut().set_count_rows(cnt); + cnt + } +} diff --git a/src/rust/lib.rs b/src/rust/lib.rs index 5325844..dfbe86d 100644 --- a/src/rust/lib.rs +++ b/src/rust/lib.rs @@ -16,3 +16,4 @@ pub mod utils; pub mod video_model; pub mod ytdl; // mod video_thumbnail; + diff --git a/src/rust/presentation_model.rs b/src/rust/presentation_model.rs index 4560713..1e9bb0c 100644 --- a/src/rust/presentation_model.rs +++ b/src/rust/presentation_model.rs @@ -1,14 +1,5 @@ #[cxx_qt::bridge] mod presentation_model { - use crate::presentation_model::presentation_model::Presentation; - use crate::reveal_js; - use crate::schema::presentations::dsl::*; - use diesel::sqlite::SqliteConnection; - use diesel::{delete, insert_into, prelude::*, update}; - // use sqlx::Connection; - use std::path::PathBuf; - use tracing::debug; - unsafe extern "C++" { include!(< QAbstractListModel >); include!("cxx-qt-lib/qhash.h"); @@ -33,493 +24,581 @@ mod presentation_model { type QList_QString = cxx_qt_lib::QList; } - #[derive(Default, Clone, Debug)] - pub struct Presentation { - id: i32, - title: String, - html: bool, - path: String, - page_count: i32, - } - - #[cxx_qt::qobject(base = "QAbstractListModel")] - #[derive(Default, Debug)] - pub struct PresentationModel { - highest_id: i32, - presentations: Vec, - } - - #[cxx_qt::qsignals(PresentationModel)] - pub enum Signals<'a> { - #[inherit] - DataChanged { - top_left: &'a QModelIndex, - bottom_right: &'a QModelIndex, - roles: &'a QVector_i32, - }, - } - + #[qenum(PresentationModel)] enum Role { - IdRole, - PathRole, - TitleRole, - HtmlRole, - PageCountRole, + Id, + Title, + Path, + Html, + PageCount, } - // use crate::entities::{presentations, prelude::Presentations}; - // use sea_orm::{ConnectionTrait, Database, DbBackend, DbErr, Statement, ActiveValue}; - impl qobject::PresentationModel { - #[qinvokable] - pub fn clear(mut self: Pin<&mut Self>) { - unsafe { - self.as_mut().begin_reset_model(); - self.as_mut().presentations_mut().clear(); - self.as_mut().end_reset_model(); - } - } + unsafe extern "RustQt" { + #[qobject] + #[base = "QAbstractListModel"] + #[qml_element] + #[qproperty(i32, count_rows)] + type PresentationModel = super::PresentationModelRust; + + #[inherit] + #[qsignal] + fn data_changed( + self: Pin<&mut PresentationModel>, + top_left: &QModelIndex, + bottom_right: &QModelIndex, + roles: &QVector_i32, + ); #[qinvokable] - pub fn setup(mut self: Pin<&mut Self>) { - let db = &mut self.as_mut().get_db(); - // let table_info = diesel::sql_query("PRAGMA table_info(presentations)").load(db); - // println!("{:?}", table_info); - let results = presentations - .load::(db) - .expect("Error loading presentations"); - self.as_mut().set_highest_id(0); - - println!("SHOWING PRESENTATIONS"); - println!("--------------"); - for presentation in results { - println!("{}", presentation.title); - println!("{}", presentation.id); - println!("{}", presentation.path); - println!("{}", presentation.html); - println!("--------------"); - if self.as_mut().highest_id() < &presentation.id { - self.as_mut().set_highest_id(presentation.id); - } - - let pres = self::Presentation { - id: presentation.id, - title: presentation.title, - html: presentation.path.ends_with(".html"), - path: presentation.path, - page_count: presentation.page_count.unwrap(), - }; - - self.as_mut().add_presentation(pres); - } - println!("--------------------------------------"); - println!("{:?}", self.as_mut().presentations()); - println!("--------------------------------------"); - } - + fn clear(self: Pin<&mut PresentationModel>); #[qinvokable] - pub fn remove_item( - mut self: Pin<&mut Self>, + fn setup(self: Pin<&mut PresentationModel>); + #[qinvokable] + fn remove_item( + self: Pin<&mut PresentationModel>, index: i32, - ) -> bool { - if index < 0 - || (index as usize) >= self.presentations().len() - { - return false; - } - let db = &mut self.as_mut().get_db(); - - let presentation_id = - self.presentations().get(index as usize).unwrap().id; - - let result = - delete(presentations.filter(id.eq(presentation_id))) - .execute(db); - - match result { - Ok(_i) => { - unsafe { - self.as_mut().begin_remove_rows( - &QModelIndex::default(), - index, - index, - ); - self.as_mut() - .presentations_mut() - .remove(index as usize); - self.as_mut().end_remove_rows(); - } - println!( - "removed-item-at-index: {:?}", - presentation_id - ); - println!( - "new-Vec: {:?}", - self.as_mut().presentations() - ); - true - } - Err(_e) => { - println!("Cannot connect to database"); - false - } - } - } - - fn get_db(self: Pin<&mut Self>) -> SqliteConnection { - let mut data = dirs::data_local_dir().unwrap(); - data.push("lumina"); - data.push("library-db.sqlite3"); - let mut db_url = String::from("sqlite://"); - db_url.push_str(data.to_str().unwrap()); - println!("DB: {:?}", db_url); - - SqliteConnection::establish(&db_url).unwrap_or_else( - |_| panic!("error connecting to {}", db_url), - ) - } - + ) -> bool; #[qinvokable] - pub fn new_item( - mut self: Pin<&mut Self>, + fn new_item( + self: Pin<&mut PresentationModel>, url: QUrl, new_page_count: i32, - ) { - println!("LETS INSERT THIS SUCKER!"); - let file_path = PathBuf::from(url.path().to_string()); - let name = - file_path.file_stem().unwrap().to_str().unwrap(); - let presentation_id = self.rust().highest_id + 1; - let presentation_title = QString::from(name); - let presentation_path = url; - let presentation_html = - file_path.extension().unwrap() == "html"; - debug!(html = presentation_html, ?file_path, extension = ?file_path.extension()); - - if self.as_mut().add_item( - presentation_id, - presentation_title, - presentation_path, - presentation_html, - new_page_count, - ) { - println!("filename: {:?}", name); - self.as_mut().set_highest_id(presentation_id); - } else { - println!("Error in inserting item"); - } - } - + ); #[qinvokable] - pub fn add_item( - mut self: Pin<&mut Self>, - presentation_id: i32, - presentation_title: QString, - presentation_path: QUrl, - presentation_html: bool, - new_page_count: i32, - ) -> bool { - let db = &mut self.as_mut().get_db(); - // println!("{:?}", db); - let mut actual_page_count = new_page_count; - if presentation_html { - let actual_path = PathBuf::from( - presentation_path.path().to_string(), - ); - actual_page_count = - reveal_js::count_slides_and_fragments( - &actual_path, - ); - } - debug!( - page_count = actual_page_count, - html = presentation_html - ); - - let presentation = self::Presentation { - id: presentation_id, - title: presentation_title.clone().to_string(), - html: presentation_html, - path: presentation_path.clone().to_string(), - page_count: actual_page_count, - }; - println!("{:?}", presentation); - - let result = insert_into(presentations) - .values(( - id.eq(&presentation_id), - title.eq(&presentation_title.to_string()), - path.eq(&presentation_path.to_string()), - html.eq(&presentation_html), - page_count.eq(&presentation.page_count), - )) - .execute(db); - println!("{:?}", result); - - match result { - Ok(_i) => { - self.as_mut().add_presentation(presentation); - println!("{:?}", self.as_mut().presentations()); - true - } - Err(_e) => { - println!("Cannot connect to database"); - false - } - } - } - - fn add_presentation( - mut self: Pin<&mut Self>, - presentation: self::Presentation, - ) { - let index = self.as_ref().presentations().len() as i32; - println!("{:?}", presentation); - unsafe { - self.as_mut().begin_insert_rows( - &QModelIndex::default(), - index, - index, - ); - self.as_mut().presentations_mut().push(presentation); - self.as_mut().end_insert_rows(); - } - } - - #[qinvokable] - pub fn get_item( - self: Pin<&mut Self>, + fn update_path( + self: Pin<&mut PresentationModel>, index: i32, - ) -> QMap_QString_QVariant { - println!("{index}"); - let mut qvariantmap = QMap_QString_QVariant::default(); - let idx = self.index(index, 0, &QModelIndex::default()); - if !idx.is_valid() { - return qvariantmap; - } - let role_names = self.as_ref().role_names(); - let role_names_iter = role_names.iter(); - if let Some(presentation) = - self.rust().presentations.get(index as usize) - { - for i in role_names_iter { - qvariantmap.insert( - QString::from(&i.1.to_string()), - self.as_ref().data(&idx, *i.0), - ); - } - }; - qvariantmap - } - + updated_path: QString, + ) -> bool; #[qinvokable] - pub fn update_title( - mut self: Pin<&mut Self>, + fn get_item( + self: Pin<&mut PresentationModel>, + index: i32, + ) -> QMap_QString_QVariant; + #[qinvokable] + fn update_loop( + self: Pin<&mut PresentationModel>, + index: i32, + loop_value: bool, + ) -> bool; + #[qinvokable] + fn update_title( + self: Pin<&mut PresentationModel>, index: i32, updated_title: QString, - ) -> bool { - let mut vector_roles = QVector_i32::default(); - vector_roles - .append(self.as_ref().get_role(Role::TitleRole)); - let model_index = &self.as_ref().index( - index, - 0, - &QModelIndex::default(), - ); - - let db = &mut self.as_mut().get_db(); - let result = update(presentations.filter(id.eq(index))) - .set(title.eq(updated_title.to_string())) - .execute(db); - match result { - Ok(_i) => { - for presentation in self - .as_mut() - .presentations_mut() - .iter_mut() - .filter(|x| x.id == index) - { - presentation.title = - updated_title.to_string(); - println!( - "rust-title: {:?}", - presentation.title - ); - } - // TODO this seems to not be updating in the actual list - self.as_mut().emit_data_changed( - model_index, - model_index, - &vector_roles, - ); - // self.as_mut().emit_title_changed(); - println!("rust-title: {:?}", updated_title); - true - } - Err(_e) => false, - } - } - + ) -> bool; #[qinvokable] - pub fn update_page_count( - mut self: Pin<&mut Self>, + fn update_page_count( + self: Pin<&mut PresentationModel>, index: i32, updated_page_count: i32, - ) -> bool { - let mut vector_roles = QVector_i32::default(); - vector_roles - .append(self.as_ref().get_role(Role::PageCountRole)); - let model_index = &self.as_ref().index( - index, - 0, - &QModelIndex::default(), - ); - - let db = &mut self.as_mut().get_db(); - let result = update(presentations.filter(id.eq(index))) - .set(page_count.eq(updated_page_count)) - .execute(db); - match result { - Ok(_i) => { - for presentation in self - .as_mut() - .presentations_mut() - .iter_mut() - .filter(|x| x.id == index) - { - presentation.page_count = updated_page_count; - println!( - "rust-page_count: {:?}", - presentation.page_count - ); - } - // TODO this seems to not be updating in the actual list - self.as_mut().emit_data_changed( - model_index, - model_index, - &vector_roles, - ); - // self.as_mut().emit_page_count_changed(); - println!( - "rust-page_count: {:?}", - updated_page_count - ); - true - } - Err(_e) => false, - } - } - - fn get_role(&self, role: Role) -> i32 { - match role { - Role::IdRole => 0, - Role::TitleRole => 1, - Role::PathRole => 2, - Role::HtmlRole => 3, - Role::PageCountRole => 4, - _ => 0, - } - } + ) -> bool; + #[qinvokable] + fn update_end_time( + self: Pin<&mut PresentationModel>, + index: i32, + updated_end_stime: QString, + ) -> bool; } - // Create Rust bindings for C++ functions of the base class (QAbstractItemModel) - #[cxx_qt::inherit] - extern "C++" { + impl cxx_qt::Threading for PresentationModel {} + + unsafe extern "RustQt" { + #[inherit] unsafe fn begin_insert_rows( - self: Pin<&mut qobject::PresentationModel>, + self: Pin<&mut PresentationModel>, parent: &QModelIndex, first: i32, last: i32, ); - unsafe fn end_insert_rows( - self: Pin<&mut qobject::PresentationModel>, - ); + #[inherit] + unsafe fn end_insert_rows(self: Pin<&mut PresentationModel>); + + #[inherit] unsafe fn begin_remove_rows( - self: Pin<&mut qobject::PresentationModel>, + self: Pin<&mut PresentationModel>, parent: &QModelIndex, first: i32, last: i32, ); - unsafe fn end_remove_rows( - self: Pin<&mut qobject::PresentationModel>, - ); + #[inherit] + unsafe fn begin_move_rows( + self: Pin<&mut PresentationModel>, + source_parent: &QModelIndex, + source_first: i32, + source_last: i32, + destination_parent: &QModelIndex, + destination_child: i32, + ) -> bool; + + #[inherit] + unsafe fn end_move_rows(self: Pin<&mut PresentationModel>); + + #[inherit] + unsafe fn end_remove_rows(self: Pin<&mut PresentationModel>); + + #[inherit] unsafe fn begin_reset_model( - self: Pin<&mut qobject::PresentationModel>, + self: Pin<&mut PresentationModel>, ); - unsafe fn end_reset_model( - self: Pin<&mut qobject::PresentationModel>, - ); - } - #[cxx_qt::inherit] - unsafe extern "C++" { - #[cxx_name = "canFetchMore"] - fn base_can_fetch_more( - self: &qobject::PresentationModel, + #[inherit] + unsafe fn end_reset_model(self: Pin<&mut PresentationModel>); + + #[inherit] + fn can_fetch_more( + self: &PresentationModel, parent: &QModelIndex, ) -> bool; + #[inherit] fn index( - self: &qobject::PresentationModel, + self: &PresentationModel, row: i32, column: i32, parent: &QModelIndex, ) -> QModelIndex; - } - - // QAbstractListModel implementation - impl qobject::PresentationModel { - #[qinvokable(cxx_override)] - fn data(&self, index: &QModelIndex, role: i32) -> QVariant { - if let Some(presentation) = - self.presentations().get(index.row() as usize) - { - return match role { - 0 => QVariant::from(&presentation.id), - 1 => QVariant::from(&QString::from( - &presentation.title, - )), - 2 => QVariant::from(&QString::from( - &presentation.path, - )), - 3 => QVariant::from(&presentation.html), - 4 => QVariant::from(&presentation.page_count), - _ => QVariant::default(), - }; - } - - QVariant::default() - } - - // Example of overriding a C++ virtual method and calling the base class implementation. - #[qinvokable(cxx_override)] - pub fn can_fetch_more(&self, parent: &QModelIndex) -> bool { - self.base_can_fetch_more(parent) - } - - #[qinvokable(cxx_override)] - pub fn role_names(&self) -> QHash_i32_QByteArray { - let mut roles = QHash_i32_QByteArray::default(); - roles.insert(0, cxx_qt_lib::QByteArray::from("id")); - roles.insert(1, cxx_qt_lib::QByteArray::from("title")); - roles.insert(2, cxx_qt_lib::QByteArray::from("filePath")); - roles.insert(3, cxx_qt_lib::QByteArray::from("html")); - roles - .insert(4, cxx_qt_lib::QByteArray::from("pageCount")); - roles - } - - #[qinvokable(cxx_override)] - pub fn row_count(&self, _parent: &QModelIndex) -> i32 { - let cnt = self.rust().presentations.len() as i32; - // println!("row count is {cnt}"); - cnt - } #[qinvokable] - pub fn count(&self) -> i32 { - self.rust().presentations.len() as i32 + #[cxx_override] + fn data( + self: &PresentationModel, + index: &QModelIndex, + role: i32, + ) -> QVariant; + + #[qinvokable] + #[cxx_override] + fn role_names( + self: &PresentationModel, + ) -> QHash_i32_QByteArray; + + #[qinvokable] + #[cxx_override] + fn row_count( + self: &PresentationModel, + _parent: &QModelIndex, + ) -> i32; + + #[qinvokable] + fn count(self: &PresentationModel) -> i32; + } +} + +use crate::presentation_model::presentation_model::Presentation; +use crate::reveal_js; +use crate::schema::presentations::dsl::*; +use diesel::sqlite::SqliteConnection; +use diesel::{delete, insert_into, prelude::*, update}; +// use sqlx::Connection; +use std::path::PathBuf; +use tracing::debug; + +#[derive(Default, Clone, Debug)] +pub struct Presentation { + id: i32, + title: String, + html: bool, + path: String, + page_count: i32, +} + +#[derive(Default, Debug)] +pub struct PresentationModelRust { + highest_id: i32, + presentations: Vec, +} + +impl qobject::PresentationModel { + pub fn clear(mut self: Pin<&mut Self>) { + unsafe { + self.as_mut().begin_reset_model(); + self.as_mut().presentations_mut().clear(); + self.as_mut().end_reset_model(); + } + } + + pub fn setup(mut self: Pin<&mut Self>) { + let db = &mut self.as_mut().get_db(); + // let table_info = diesel::sql_query("PRAGMA table_info(presentations)").load(db); + // println!("{:?}", table_info); + let results = presentations + .load::(db) + .expect("Error loading presentations"); + self.as_mut().set_highest_id(0); + + println!("SHOWING PRESENTATIONS"); + println!("--------------"); + for presentation in results { + println!("{}", presentation.title); + println!("{}", presentation.id); + println!("{}", presentation.path); + println!("{}", presentation.html); + println!("--------------"); + if self.as_mut().highest_id() < &presentation.id { + self.as_mut().set_highest_id(presentation.id); + } + + let pres = self::Presentation { + id: presentation.id, + title: presentation.title, + html: presentation.path.ends_with(".html"), + path: presentation.path, + page_count: presentation.page_count.unwrap(), + }; + + self.as_mut().add_presentation(pres); + } + println!("--------------------------------------"); + println!("{:?}", self.as_mut().presentations()); + println!("--------------------------------------"); + } + + pub fn remove_item(mut self: Pin<&mut Self>, index: i32) -> bool { + if index < 0 || (index as usize) >= self.presentations().len() + { + return false; + } + let db = &mut self.as_mut().get_db(); + + let presentation_id = + self.presentations().get(index as usize).unwrap().id; + + let result = + delete(presentations.filter(id.eq(presentation_id))) + .execute(db); + + match result { + Ok(_i) => { + unsafe { + self.as_mut().begin_remove_rows( + &QModelIndex::default(), + index, + index, + ); + self.as_mut() + .presentations_mut() + .remove(index as usize); + self.as_mut().end_remove_rows(); + } + println!( + "removed-item-at-index: {:?}", + presentation_id + ); + println!( + "new-Vec: {:?}", + self.as_mut().presentations() + ); + true + } + Err(_e) => { + println!("Cannot connect to database"); + false + } + } + } + + fn get_db(self: Pin<&mut Self>) -> SqliteConnection { + let mut data = dirs::data_local_dir().unwrap(); + data.push("lumina"); + data.push("library-db.sqlite3"); + let mut db_url = String::from("sqlite://"); + db_url.push_str(data.to_str().unwrap()); + println!("DB: {:?}", db_url); + + SqliteConnection::establish(&db_url).unwrap_or_else(|_| { + panic!("error connecting to {}", db_url) + }) + } + + pub fn new_item( + mut self: Pin<&mut Self>, + url: QUrl, + new_page_count: i32, + ) { + println!("LETS INSERT THIS SUCKER!"); + let file_path = PathBuf::from(url.path().to_string()); + let name = file_path.file_stem().unwrap().to_str().unwrap(); + let presentation_id = self.rust().highest_id + 1; + let presentation_title = QString::from(name); + let presentation_path = url; + let presentation_html = + file_path.extension().unwrap() == "html"; + debug!(html = presentation_html, ?file_path, extension = ?file_path.extension()); + + if self.as_mut().add_item( + presentation_id, + presentation_title, + presentation_path, + presentation_html, + new_page_count, + ) { + println!("filename: {:?}", name); + self.as_mut().set_highest_id(presentation_id); + } else { + println!("Error in inserting item"); + } + } + + pub fn add_item( + mut self: Pin<&mut Self>, + presentation_id: i32, + presentation_title: QString, + presentation_path: QUrl, + presentation_html: bool, + new_page_count: i32, + ) -> bool { + let db = &mut self.as_mut().get_db(); + // println!("{:?}", db); + let mut actual_page_count = new_page_count; + if presentation_html { + let actual_path = + PathBuf::from(presentation_path.path().to_string()); + actual_page_count = + reveal_js::count_slides_and_fragments(&actual_path); + } + debug!( + page_count = actual_page_count, + html = presentation_html + ); + + let presentation = self::Presentation { + id: presentation_id, + title: presentation_title.clone().to_string(), + html: presentation_html, + path: presentation_path.clone().to_string(), + page_count: actual_page_count, + }; + println!("{:?}", presentation); + + let result = insert_into(presentations) + .values(( + id.eq(&presentation_id), + title.eq(&presentation_title.to_string()), + path.eq(&presentation_path.to_string()), + html.eq(&presentation_html), + page_count.eq(&presentation.page_count), + )) + .execute(db); + println!("{:?}", result); + + match result { + Ok(_i) => { + self.as_mut().add_presentation(presentation); + println!("{:?}", self.as_mut().presentations()); + true + } + Err(_e) => { + println!("Cannot connect to database"); + false + } + } + } + + fn add_presentation( + mut self: Pin<&mut Self>, + presentation: self::Presentation, + ) { + let index = self.as_ref().presentations().len() as i32; + println!("{:?}", presentation); + unsafe { + self.as_mut().begin_insert_rows( + &QModelIndex::default(), + index, + index, + ); + self.as_mut().presentations_mut().push(presentation); + self.as_mut().end_insert_rows(); + } + } + + pub fn get_item( + self: Pin<&mut Self>, + index: i32, + ) -> QMap_QString_QVariant { + println!("{index}"); + let mut qvariantmap = QMap_QString_QVariant::default(); + let idx = self.index(index, 0, &QModelIndex::default()); + if !idx.is_valid() { + return qvariantmap; + } + let role_names = self.as_ref().role_names(); + let role_names_iter = role_names.iter(); + if let Some(presentation) = + self.rust().presentations.get(index as usize) + { + for i in role_names_iter { + qvariantmap.insert( + QString::from(&i.1.to_string()), + self.as_ref().data(&idx, *i.0), + ); + } + }; + qvariantmap + } + + pub fn update_title( + mut self: Pin<&mut Self>, + index: i32, + updated_title: QString, + ) -> bool { + let mut vector_roles = QVector_i32::default(); + vector_roles.append(self.as_ref().get_role(Role::TitleRole)); + let model_index = + &self.as_ref().index(index, 0, &QModelIndex::default()); + + let db = &mut self.as_mut().get_db(); + let result = update(presentations.filter(id.eq(index))) + .set(title.eq(updated_title.to_string())) + .execute(db); + match result { + Ok(_i) => { + for presentation in self + .as_mut() + .presentations_mut() + .iter_mut() + .filter(|x| x.id == index) + { + presentation.title = updated_title.to_string(); + println!("rust-title: {:?}", presentation.title); + } + // TODO this seems to not be updating in the actual list + self.as_mut().emit_data_changed( + model_index, + model_index, + &vector_roles, + ); + // self.as_mut().emit_title_changed(); + println!("rust-title: {:?}", updated_title); + true + } + Err(_e) => false, + } + } + + pub fn update_page_count( + mut self: Pin<&mut Self>, + index: i32, + updated_page_count: i32, + ) -> bool { + let mut vector_roles = QVector_i32::default(); + vector_roles + .append(self.as_ref().get_role(Role::PageCountRole)); + let model_index = + &self.as_ref().index(index, 0, &QModelIndex::default()); + + let db = &mut self.as_mut().get_db(); + let result = update(presentations.filter(id.eq(index))) + .set(page_count.eq(updated_page_count)) + .execute(db); + match result { + Ok(_i) => { + for presentation in self + .as_mut() + .presentations_mut() + .iter_mut() + .filter(|x| x.id == index) + { + presentation.page_count = updated_page_count; + println!( + "rust-page_count: {:?}", + presentation.page_count + ); + } + // TODO this seems to not be updating in the actual list + self.as_mut().emit_data_changed( + model_index, + model_index, + &vector_roles, + ); + // self.as_mut().emit_page_count_changed(); + println!("rust-page_count: {:?}", updated_page_count); + true + } + Err(_e) => false, + } + } + + fn get_role(&self, role: Role) -> i32 { + match role { + Role::IdRole => 0, + Role::TitleRole => 1, + Role::PathRole => 2, + Role::HtmlRole => 3, + Role::PageCountRole => 4, + _ => 0, } } } + +// QAbstractListModel implementation +impl qobject::PresentationModel { + fn data(&self, index: &QModelIndex, role: i32) -> QVariant { + let role = qobject::Roles { repr: role }; + if let Some(presentation) = + self.presentations().get(index.row() as usize) + { + return match role { + qobject::Role::Id => QVariant::from(&presentation.id), + qobject::Role::Title => QVariant::from( + &QString::from(&presentation.title), + ), + qobject::Role::Path => { + QVariant::from(&QString::from(&presentation.path)) + } + qobject::Role::Html => { + QVariant::from(&presentation.html) + } + qobject::Role::PageCount => { + QVariant::from(&presentation.page_count) + } + _ => QVariant::default(), + }; + } + + QVariant::default() + } + + // Example of overriding a C++ virtual method and calling the base class implementation. + + pub fn can_fetch_more(&self, parent: &QModelIndex) -> bool { + self.base_can_fetch_more(parent) + } + + pub fn role_names(&self) -> QHash_i32_QByteArray { + let mut roles = QHash_i32_QByteArray::default(); + roles.insert( + qobject::Roles::Id.repr, + cxx_qt_lib::QByteArray::from("id"), + ); + roles.insert( + qobject::Roles::Title.repr, + cxx_qt_lib::QByteArray::from("title"), + ); + roles.insert( + qobject::Roles::Path.repr, + cxx_qt_lib::QByteArray::from("filePath"), + ); + roles.insert( + qobject::Roles::Html.repr, + cxx_qt_lib::QByteArray::from("html"), + ); + roles.insert( + qobject::Roles::PageCount.repr, + cxx_qt_lib::QByteArray::from("pageCount"), + ); + roles + } + + pub fn row_count(&self, _parent: &QModelIndex) -> i32 { + let cnt = self.rust().presentations.len() as i32; + // println!("row count is {cnt}"); + cnt + } + + pub fn count(&self) -> i32 { + self.rust().presentations.len() as i32 + } +} diff --git a/src/rust/service_item_model.rs b/src/rust/service_item_model.rs index ecc52e4..bc8b933 100644 --- a/src/rust/service_item_model.rs +++ b/src/rust/service_item_model.rs @@ -24,200 +24,63 @@ mod service_item_model { type QUrl = cxx_qt_lib::QUrl; } - use crate::obs::Obs; - use serde::{Deserialize, Serialize}; - #[cxx_qt::qobject] - #[derive(Clone, Debug)] - pub struct ServiceItm { - #[qproperty] - name: QString, - #[qproperty] - ty: QString, - #[qproperty] - audio: QString, - #[qproperty] - background: QString, - #[qproperty] - background_type: QString, - #[qproperty] - text: QStringList, - #[qproperty] - font: QString, - #[qproperty] - font_size: i32, - #[qproperty] - slide_count: i32, - #[qproperty] - active: bool, - #[qproperty] - selected: bool, - #[qproperty] - looping: bool, - #[qproperty] - video_start_time: f32, - #[qproperty] - video_end_time: f32, - #[qproperty] - obs_scene: QString, - } - - impl Default for ServiceItm { - fn default() -> Self { - Self { - name: QString::default(), - ty: QString::default(), - audio: QString::default(), - background: QString::default(), - background_type: QString::default(), - text: QStringList::default(), - font: QString::default(), - font_size: 50, - slide_count: 1, - active: false, - selected: false, - looping: false, - video_start_time: 0.0, - video_end_time: 0.0, - obs_scene: QString::default(), - } - } - } - - #[cxx_qt::qobject(base = "QAbstractListModel")] - #[derive(Debug)] - pub struct ServiceItemMod { - id: i32, - service_items: Vec, - obs: Option, - } - - impl Default for ServiceItemMod { - fn default() -> Self { - let obs = tokio::runtime::Runtime::new() - .unwrap() - .block_on(async { - match Obs::new().await { - Ok(o) => Some(o), - Err(e) => { - error!(e); - None - } - } - }); - Self { - id: 0, - service_items: Vec::new(), - obs, - } - } - } - - #[cxx_qt::qsignals(ServiceItemMod)] - pub enum Signals<'a> { - #[inherit] - DataChanged { - top_left: &'a QModelIndex, - bottom_right: &'a QModelIndex, - roles: &'a QVector_i32, - }, - ActiveChanged { - index: &'a i32, - }, - SelectedChanged, - ItemInserted { - index: &'a i32, - item: &'a QMap_QString_QVariant, - }, - ItemAdded { - index: &'a i32, - item: &'a QMap_QString_QVariant, - }, - ItemRemoved { - index: &'a i32, - item: &'a QMap_QString_QVariant, - }, - ItemMoved { - source_index: &'a i32, - dest_index: &'a i32, - // index: &'a i32, - item: &'a QMap_QString_QVariant, - }, - Cleared {}, - } - + #[qenum(ServiceItemModel)] enum Role { - NameRole, - TyRole, - AudioRole, - BackgroundRole, - BackgroundTypeRole, - TextRole, - FontRole, - FontSizeRole, - SlideCountRole, - ActiveRole, - SelectedRole, - LoopingRole, - VideoStartTimeRole, - VideoEndTimeRole, + Name, + Type, + Audio, + Background, + BackgroundType, + Text, + Font, + FontSize, + SlideCount, + Active, + Selected, + Looping, + VideoStartTime, + VideoEndTime, } - // use crate::video_thumbnail; - // use image::{ImageBuffer, Rgba}; - use dirs; - use serde_json::{json, Deserializer, Map, Serializer, Value}; - use std::ffi::{OsStr, OsString}; - use std::io::{self, Read, Write}; - use std::iter; - use std::path::{Path, PathBuf}; - use std::str; - use std::{fs, println}; - use tar::{Archive, Builder}; - use tracing::{debug, debug_span, error, info, instrument}; - use zstd::{Decoder, Encoder}; + unsafe extern "RustQt" { + #[qobject] + #[base = "QAbstractListModel"] + #[qml_element] + type ServiceItemModel = super::ServiceItemModelRust; - impl qobject::ServiceItemMod { - pub fn setup(mut self: Pin<&mut Self>) { - todo!() - } + #[inherit] + #[qsignal] + fn data_changed( + self: Pin<&mut ServiceItemModel>, + top_left: &QModelIndex, + bottom_right: &QModelIndex, + roles: &QVector_i32, + ); + + #[qsignal] + fn active_changed(self: Pin<&mut ServiceItemModel>, index: &i32); + #[qsignal] + fn selected_changed(self: Pin<&mut ServiceItemModel>); + #[qsignal] + fn item_inserted(self: Pin<&mut ServiceItemModel>, index: &i32, item: &QMap_QString_QVariant); + #[qsignal] + fn item_added(self: Pin<&mut ServiceItemModel>, index: &i32, item: &QMap_QString_QVariant); + #[qsignal] + fn item_removed(self: Pin<&mut ServiceItemModel>, index: &i32, item: &QMap_QString_QVariant); + #[qsignal] + fn item_moved(self: Pin<&mut ServiceItemModel>, source_index: &i32, dest_index: &i32, item: &QMap_QString_QVariant); + #[qsignal] + fn cleared(self: Pin<&mut ServiceItemModel>); #[qinvokable] - pub fn clear(mut self: Pin<&mut Self>) { - println!("CLEARING ALL ITEMS"); - unsafe { - self.as_mut().begin_reset_model(); - self.as_mut().service_items_mut().clear(); - self.as_mut().end_reset_model(); - } - self.emit(Signals::Cleared {}); - } + fn clear(self: Pin<&mut ServiceItemModel>); #[qinvokable] - pub fn remove_item(mut self: Pin<&mut Self>, index: i32) { - if index < 0 - || (index as usize) >= self.service_items().len() - { - return; - } - - unsafe { - self.as_mut().begin_remove_rows( - &QModelIndex::default(), - index, - index, - ); - self.as_mut() - .service_items_mut() - .remove(index as usize); - self.as_mut().end_remove_rows(); - } - let item = self.as_mut().get_item(index); - self.as_mut().emit_item_removed(&index, &item); - } + fn remove_item(self: Pin<&mut ServiceItemModel>, index: i32); #[qinvokable] - pub fn add_item( - mut self: Pin<&mut Self>, + fn add_item( + self: Pin<&mut ServiceItemModel>, name: QString, ty: QString, background: QString, @@ -230,49 +93,11 @@ mod service_item_model { looping: bool, video_start_time: f32, video_end_time: f32, - ) { - let service_item = ServiceItm { - name, - ty, - text, - background, - background_type, - audio, - font, - font_size, - slide_count, - looping, - video_start_time, - video_end_time, - ..Default::default() - }; - - self.as_mut().add_service_item(&service_item); - } - - fn add_service_item( - mut self: Pin<&mut Self>, - service_item: &ServiceItm, - ) { - let index = self.as_ref().service_items().len() as i32; - println!("{:?}", service_item); - let service_item = service_item.clone(); - unsafe { - self.as_mut().begin_insert_rows( - &QModelIndex::default(), - index, - index, - ); - self.as_mut().service_items_mut().push(service_item); - self.as_mut().end_insert_rows(); - } - let item = self.as_mut().get_item(index); - self.as_mut().emit_item_added(&index, &item); - } + ); #[qinvokable] - pub fn insert_item( - mut self: Pin<&mut Self>, + fn insert_item( + self: Pin<&mut ServiceItemModel>, index: i32, name: QString, text: QStringList, @@ -286,279 +111,530 @@ mod service_item_model { looping: bool, video_start_time: f32, video_end_time: f32, - ) { - let service_item = ServiceItm { - name, - ty, - text, - background, - background_type, - audio, - font, - font_size, - slide_count, - looping, - video_start_time, - video_end_time, - ..Default::default() - }; - - self.as_mut().insert_service_item(&service_item, index); - } - - fn insert_service_item( - mut self: Pin<&mut Self>, - service_item: &ServiceItm, - id: i32, - ) { - let service_item = service_item.clone(); - unsafe { - self.as_mut().begin_insert_rows( - &QModelIndex::default(), - id, - id, - ); - self.as_mut() - .service_items_mut() - .insert(id as usize, service_item); - self.as_mut().end_insert_rows(); - } - let item = self.as_mut().get_item(id); - self.as_mut().emit_item_inserted(&id, &item); - } - + ); + #[qinvokable] - pub fn get_item( - self: Pin<&mut Self>, + fn get_item( + self: Pin<&mut ServiceItemModel>, index: i32, - ) -> QMap_QString_QVariant { - println!("{index}"); - let mut map = QMap_QString_QVariant::default(); - let idx = self.index(index, 0, &QModelIndex::default()); - if !idx.is_valid() { - return map; - } - let rn = self.as_ref().role_names(); - let rn_iter = rn.iter(); - if let Some(service_item) = - self.rust().service_items.get(index as usize) - { - for i in rn_iter { - map.insert( - QString::from(&i.1.to_string()), - self.as_ref().data(&idx, *i.0), - ); - } - }; - map - } + ) -> QMap_QString_QVariant; #[qinvokable] - pub fn move_rows( - mut self: Pin<&mut Self>, + fn move_rows( + self: Pin<&mut ServiceItemModel>, source_index: i32, dest_index: i32, count: i32, - ) -> bool { - debug!( - source = source_index, - dest = dest_index, - count = count - ); - let model_index = - self.index(source_index, 0, &QModelIndex::default()); - let parent = model_index.parent(); - let source_id = source_index as usize; - let dest_id = dest_index as usize; - let cnt = count as usize; - let end_service_item = source_id + cnt - 1; - // This needs to point to the index above the intended position if moving - // up. Qt's begin_move_rows requires that knowledge for some reason. - let qt_dest_index = if source_index < dest_index { - dest_index + 1 - } else { - dest_index - }; - - println!("rust-end-service_item: {:?}", end_service_item); - println!("qt-dest-service_item: {:?}", qt_dest_index); - unsafe { - // this function doesn't build - self.as_mut().begin_move_rows( - &parent, - source_index, - source_index + count - 1, - &parent, - qt_dest_index, - ); - - if source_id < dest_id { - let move_amount = dest_id - source_id - cnt + 1; - self.as_mut().service_items_mut() - [source_id..=dest_id] - .rotate_right(move_amount); - println!("rust-move_amount: {:?}", move_amount); - } else { - let move_amount = - end_service_item - dest_id - cnt + 1; - println!("rust-move_amount: {:?}", move_amount); - self.as_mut().service_items_mut() - [dest_id..=end_service_item] - .rotate_left(move_amount); - } - - self.as_mut().end_move_rows(); - let item = self.as_mut().get_item(dest_index); - self.as_mut().emit_item_moved( - &source_index, - &dest_index, - &item, - ); - true - } - } + ) -> bool; #[qinvokable] - pub fn move_up(self: Pin<&mut Self>, index: i32) -> bool { - self.move_rows(index, index - 1, 1) - } + fn move_up(self: Pin<&mut ServiceItemModel>, index: i32) -> bool; #[qinvokable] - pub fn move_down(self: Pin<&mut Self>, index: i32) -> bool { - self.move_rows(index, index + 1, 1) - } + fn move_down(self: Pin<&mut ServiceItemModel>, index: i32) -> bool; #[qinvokable] - pub fn select(mut self: Pin<&mut Self>, index: i32) -> bool { - let rc = self.as_ref().count() - 1; - let tl = - &self.as_ref().index(0, 0, &QModelIndex::default()); - let br = - &self.as_ref().index(rc, 0, &QModelIndex::default()); - let mut vector_roles = QVector_i32::default(); - vector_roles.append(self.get_role(Role::SelectedRole)); - for service_item in - self.as_mut().service_items_mut().iter_mut() - { - debug!(deselecting = ?service_item); - service_item.selected = false; - } - if let Some(service_item) = self - .as_mut() - .service_items_mut() - .get_mut(index as usize) - { - debug!(selecting_item = index, item = ?service_item); - service_item.selected = true; - self.as_mut().emit_data_changed( - tl, - br, - &vector_roles, - ); - // We use this signal generated by our signals enum to tell QML that - // the selected service_item has changed which is used to reposition views. - // self.as_mut().emit_selected_changed(); - true - } else { - false - } - } + fn select(self: Pin<&mut ServiceItemModel>, index: i32) -> bool; #[qinvokable] - pub fn select_items( - mut self: Pin<&mut Self>, + fn select_items( + self: Pin<&mut ServiceItemModel>, final_index: i32, - ) -> bool { - // setup the roles we are using so that we can tell QML - // which properties to get again. - let mut vector_roles = QVector_i32::default(); - vector_roles.append(self.get_role(Role::SelectedRole)); + ) -> bool; - if let Some(current_index) = self - .as_ref() - .service_items() - .iter() - .position(|i| i.selected == true) - { - // Here we will need to branch to get the selected items - debug!(first_item = ?current_index); - debug!(final_item = final_index); - // Let's return early to prevent needing to do anything else - if final_index == current_index as i32 { - return false; - } + #[qinvokable] + pub fn activate( + self: Pin<&mut ServiceItemModel>, + index: i32, + ) -> bool; - let lower = final_index > current_index as i32; - if lower { - let top_left = &self.as_ref().index( - current_index as i32, - 0, - &QModelIndex::default(), - ); - let bottom_right = &self.as_ref().index( - final_index, - 0, - &QModelIndex::default(), - ); - for (index, item) in self - .as_mut() - .service_items_mut() - .iter_mut() - .enumerate() - .filter(|i| { - i.0 >= current_index - && i.0 <= final_index as usize - }) - { - item.selected = true; - debug!(selected_item = ?item, index = index); + #[qinvokable] + pub fn deactivate( + self: Pin<&mut ServiceItemModel>, + index: i32, + ) -> bool; + + #[qinvokable] + fn save(self: Pin<&mut ServiceItemModel>, file: QUrl) -> bool; + + #[qinvokable] + fn load(self: Pin<&mut ServiceItemModel>, file: QUrl) -> bool; + } + + impl cxx_qt::Threading for ServiceItemModel {} + + unsafe extern "RustQt" { + #[inherit] + unsafe fn begin_insert_rows( + self: Pin<&mut ServiceItemModel>, + parent: &QModelIndex, + first: i32, + last: i32, + ); + + #[inherit] + unsafe fn end_insert_rows(self: Pin<&mut ServiceItemModel>); + + #[inherit] + unsafe fn begin_remove_rows( + self: Pin<&mut ServiceItemModel>, + parent: &QModelIndex, + first: i32, + last: i32, + ); + + + #[inherit] + unsafe fn begin_move_rows( + self: Pin<&mut ServiceItemModel>, + source_parent: &QModelIndex, + source_first: i32, + source_last: i32, + destination_parent: &QModelIndex, + destination_child: i32, + ) -> bool; + + #[inherit] + unsafe fn end_move_rows( + self: Pin<&mut ServiceItemModel>, + ); + + + #[inherit] + unsafe fn end_remove_rows(self: Pin<&mut ServiceItemModel>); + + #[inherit] + unsafe fn begin_reset_model(self: Pin<&mut ServiceItemModel>); + + #[inherit] + unsafe fn end_reset_model(self: Pin<&mut ServiceItemModel>); + + #[inherit] + fn can_fetch_more( + self: &ServiceItemModel, + parent: &QModelIndex, + ) -> bool; + + #[inherit] + fn index( + self: &ServiceItemModel, + row: i32, + column: i32, + parent: &QModelIndex, + ) -> QModelIndex; + + #[qinvokable] + #[cxx_override] + fn data( + self: &ServiceItemModel, + index: &QModelIndex, + role: i32, + ) -> QVariant; + + #[qinvokable] + #[cxx_override] + fn role_names(self: &ServiceItemModel) -> QHash_i32_QByteArray; + + #[qinvokable] + #[cxx_override] + fn row_count(self: &ServiceItemModel, _parent: &QModelIndex) + -> i32; + + #[qinvokable] + fn count(self: &ServiceItemModel) -> i32; + } +} + + +use dirs; +use serde_json::{json, Deserializer, Map, Serializer, Value}; +use std::ffi::{OsStr, OsString}; +use std::io::{self, Read, Write}; +use std::iter; +use std::path::{Path, PathBuf}; +use std::str; +use std::{fs, println}; +use tar::{Archive, Builder}; +use tracing::{debug, debug_span, error, info, instrument}; +use zstd::{Decoder, Encoder}; +use crate::obs::Obs; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug)] +pub struct ServiceItm { + name: QString, + ty: QString, + audio: QString, + background: QString, + background_type: QString, + text: QStringList, + font: QString, + font_size: i32, + slide_count: i32, + active: bool, + selected: bool, + looping: bool, + video_start_time: f32, + video_end_time: f32, + obs_scene: QString, +} + +impl Default for ServiceItm { + fn default() -> Self { + Self { + name: QString::default(), + ty: QString::default(), + audio: QString::default(), + background: QString::default(), + background_type: QString::default(), + text: QStringList::default(), + font: QString::default(), + font_size: 50, + slide_count: 1, + active: false, + selected: false, + looping: false, + video_start_time: 0.0, + video_end_time: 0.0, + obs_scene: QString::default(), + } + } +} + +#[derive(Debug)] +pub struct ServiceItemModelRust { + id: i32, + service_items: Vec, + obs: Option, +} + +impl Default for ServiceItemModelRust { + fn default() -> Self { + let obs = tokio::runtime::Runtime::new() + .unwrap() + .block_on(async { + match Obs::new().await { + Ok(o) => Some(o), + Err(e) => { + error!(e); + None } - self.as_mut().emit_data_changed( - top_left, - bottom_right, - &vector_roles, - ); - // self.as_mut().emit_selected_changed(); - } else { - let top_left = &self.as_ref().index( - final_index, - 0, - &QModelIndex::default(), - ); - let bottom_right = &self.as_ref().index( - current_index as i32, - 0, - &QModelIndex::default(), - ); - for (index, item) in self - .as_mut() - .service_items_mut() - .iter_mut() - .enumerate() - .filter(|i| { - i.0 >= final_index as usize - && i.0 <= current_index - }) - { - item.selected = true; - debug!(selected_item = ?item, index = index); - } - self.as_mut().emit_data_changed( - top_left, - bottom_right, - &vector_roles, - ); - // self.as_mut().emit_selected_changed(); } + }); + Self { + id: 0, + service_items: Vec::new(), + obs, + } + } +} - true +impl qobject::ServiceItemModel { + pub fn setup(mut self: Pin<&mut Self>) { + todo!() + } + + pub fn clear(mut self: Pin<&mut Self>) { + println!("CLEARING ALL ITEMS"); + unsafe { + self.as_mut().begin_reset_model(); + self.as_mut().service_items_mut().clear(); + self.as_mut().end_reset_model(); + } + self.emit(Signals::Cleared {}); + } + + pub fn remove_item(mut self: Pin<&mut Self>, index: i32) { + if index < 0 + || (index as usize) >= self.service_items().len() + { + return; + } + + unsafe { + self.as_mut().begin_remove_rows( + &QModelIndex::default(), + index, + index, + ); + self.as_mut() + .service_items_mut() + .remove(index as usize); + self.as_mut().end_remove_rows(); + } + let item = self.as_mut().get_item(index); + self.as_mut().emit_item_removed(&index, &item); + } + + pub fn add_item( + mut self: Pin<&mut Self>, + name: QString, + ty: QString, + background: QString, + background_type: QString, + text: QStringList, + audio: QString, + font: QString, + font_size: i32, + slide_count: i32, + looping: bool, + video_start_time: f32, + video_end_time: f32, + ) { + let service_item = ServiceItm { + name, + ty, + text, + background, + background_type, + audio, + font, + font_size, + slide_count, + looping, + video_start_time, + video_end_time, + ..Default::default() + }; + + self.as_mut().add_service_item(&service_item); + } + + fn add_service_item( + mut self: Pin<&mut Self>, + service_item: &ServiceItm, + ) { + let index = self.as_ref().service_items().len() as i32; + println!("{:?}", service_item); + let service_item = service_item.clone(); + unsafe { + self.as_mut().begin_insert_rows( + &QModelIndex::default(), + index, + index, + ); + self.as_mut().service_items_mut().push(service_item); + self.as_mut().end_insert_rows(); + } + let item = self.as_mut().get_item(index); + self.as_mut().emit_item_added(&index, &item); + } + + pub fn insert_item( + mut self: Pin<&mut Self>, + index: i32, + name: QString, + text: QStringList, + ty: QString, + background: QString, + background_type: QString, + audio: QString, + font: QString, + font_size: i32, + slide_count: i32, + looping: bool, + video_start_time: f32, + video_end_time: f32, + ) { + let service_item = ServiceItm { + name, + ty, + text, + background, + background_type, + audio, + font, + font_size, + slide_count, + looping, + video_start_time, + video_end_time, + ..Default::default() + }; + + self.as_mut().insert_service_item(&service_item, index); + } + + fn insert_service_item( + mut self: Pin<&mut Self>, + service_item: &ServiceItm, + id: i32, + ) { + let service_item = service_item.clone(); + unsafe { + self.as_mut().begin_insert_rows( + &QModelIndex::default(), + id, + id, + ); + self.as_mut() + .service_items_mut() + .insert(id as usize, service_item); + self.as_mut().end_insert_rows(); + } + let item = self.as_mut().get_item(id); + self.as_mut().emit_item_inserted(&id, &item); + } + + pub fn get_item( + self: Pin<&mut Self>, + index: i32, + ) -> QMap_QString_QVariant { + println!("{index}"); + let mut map = QMap_QString_QVariant::default(); + let idx = self.index(index, 0, &QModelIndex::default()); + if !idx.is_valid() { + return map; + } + let rn = self.as_ref().role_names(); + let rn_iter = rn.iter(); + if let Some(service_item) = + self.rust().service_items.get(index as usize) + { + for i in rn_iter { + map.insert( + QString::from(&i.1.to_string()), + self.as_ref().data(&idx, *i.0), + ); + } + }; + map + } + + pub fn move_rows( + mut self: Pin<&mut Self>, + source_index: i32, + dest_index: i32, + count: i32, + ) -> bool { + debug!( + source = source_index, + dest = dest_index, + count = count + ); + let model_index = + self.index(source_index, 0, &QModelIndex::default()); + let parent = model_index.parent(); + let source_id = source_index as usize; + let dest_id = dest_index as usize; + let cnt = count as usize; + let end_service_item = source_id + cnt - 1; + // This needs to point to the index above the intended position if moving + // up. Qt's begin_move_rows requires that knowledge for some reason. + let qt_dest_index = if source_index < dest_index { + dest_index + 1 + } else { + dest_index + }; + + println!("rust-end-service_item: {:?}", end_service_item); + println!("qt-dest-service_item: {:?}", qt_dest_index); + unsafe { + // this function doesn't build + self.as_mut().begin_move_rows( + &parent, + source_index, + source_index + count - 1, + &parent, + qt_dest_index, + ); + + if source_id < dest_id { + let move_amount = dest_id - source_id - cnt + 1; + self.as_mut().service_items_mut() + [source_id..=dest_id] + .rotate_right(move_amount); + println!("rust-move_amount: {:?}", move_amount); } else { - // Here let's branch to select from the first item to the - // final item. Since we don't know which one is selected, - // assume that the first one is "selected" + let move_amount = + end_service_item - dest_id - cnt + 1; + println!("rust-move_amount: {:?}", move_amount); + self.as_mut().service_items_mut() + [dest_id..=end_service_item] + .rotate_left(move_amount); + } + self.as_mut().end_move_rows(); + let item = self.as_mut().get_item(dest_index); + self.as_mut().emit_item_moved( + &source_index, + &dest_index, + &item, + ); + true + } + } + + pub fn move_up(self: Pin<&mut Self>, index: i32) -> bool { + self.move_rows(index, index - 1, 1) + } + + pub fn move_down(self: Pin<&mut Self>, index: i32) -> bool { + self.move_rows(index, index + 1, 1) + } + + pub fn select(mut self: Pin<&mut Self>, index: i32) -> bool { + let rc = self.as_ref().count() - 1; + let tl = + &self.as_ref().index(0, 0, &QModelIndex::default()); + let br = + &self.as_ref().index(rc, 0, &QModelIndex::default()); + let mut vector_roles = QVector_i32::default(); + vector_roles.append(self.get_role(Role::SelectedRole)); + for service_item in + self.as_mut().service_items_mut().iter_mut() + { + debug!(deselecting = ?service_item); + service_item.selected = false; + } + if let Some(service_item) = self + .as_mut() + .service_items_mut() + .get_mut(index as usize) + { + debug!(selecting_item = index, item = ?service_item); + service_item.selected = true; + self.as_mut().emit_data_changed( + tl, + br, + &vector_roles, + ); + // We use this signal generated by our signals enum to tell QML that + // the selected service_item has changed which is used to reposition views. + // self.as_mut().emit_selected_changed(); + true + } else { + false + } + } + + pub fn select_items( + mut self: Pin<&mut Self>, + final_index: i32, + ) -> bool { + // setup the roles we are using so that we can tell QML + // which properties to get again. + let mut vector_roles = QVector_i32::default(); + vector_roles.append(self.get_role(Role::SelectedRole)); + + if let Some(current_index) = self + .as_ref() + .service_items() + .iter() + .position(|i| i.selected == true) + { + // Here we will need to branch to get the selected items + debug!(first_item = ?current_index); + debug!(final_item = final_index); + // Let's return early to prevent needing to do anything else + if final_index == current_index as i32 { + return false; + } + + let lower = final_index > current_index as i32; + if lower { let top_left = &self.as_ref().index( - 0, + current_index as i32, 0, &QModelIndex::default(), ); @@ -572,7 +648,10 @@ mod service_item_model { .service_items_mut() .iter_mut() .enumerate() - .filter(|i| i.0 <= final_index as usize) + .filter(|i| { + i.0 >= current_index + && i.0 <= final_index as usize + }) { item.selected = true; debug!(selected_item = ?item, index = index); @@ -583,729 +662,723 @@ mod service_item_model { &vector_roles, ); // self.as_mut().emit_selected_changed(); - debug!( - first_item = 0, - final_item = final_index, - "couldn't find first selected item" - ); - false - } - } - - #[qinvokable] - pub fn activate( - mut self: Pin<&mut Self>, - index: i32, - ) -> bool { - let rc = self.as_ref().count() - 1; - let tl = - &self.as_ref().index(0, 0, &QModelIndex::default()); - let br = - &self.as_ref().index(rc, 0, &QModelIndex::default()); - let mut vector_roles = QVector_i32::default(); - vector_roles.append(self.get_role(Role::ActiveRole)); - for service_item in - self.as_mut().service_items_mut().iter_mut() - { - // println!("service_item is deactivating {:?}", i); - service_item.active = false; - } - let obs = self.as_mut().obs_mut().clone(); - - if let Some(service_item) = self - .as_mut() - .service_items_mut() - .get_mut(index as usize) - { - debug!(activating_item = index, - title = ?service_item.name, - background = ?service_item.background, - background_type = ?service_item.background_type); - service_item.active = true; - // if let Some(obs) = obs { - // match obs - // .set_scene(service_item.obs_scene.to_string()) - // { - // Ok(()) => debug!("Successfully set scene"), - // Err(e) => error!(e), - // } - // } - self.as_mut().emit_data_changed( - tl, - br, - &vector_roles, - ); - // We use this signal generated by our signals enum to tell QML that - // the active service_item has changed which is used to reposition views. - self.as_mut().emit_active_changed(&index); - true } else { - false - } - } - - #[qinvokable] - pub fn deactivate( - mut self: Pin<&mut Self>, - index: i32, - ) -> bool { - todo!(); - let rc = self.as_ref().count() - 1; - let tl = - &self.as_ref().index(0, 0, &QModelIndex::default()); - let br = - &self.as_ref().index(rc, 0, &QModelIndex::default()); - let mut vector_roles = QVector_i32::default(); - vector_roles.append(self.get_role(Role::ActiveRole)); - if let Some(service_item) = self - .as_mut() - .service_items_mut() - .get_mut(index as usize) - { - println!("service_item is activating {:?}", index); - println!( - "service_item_title: {:?}", - service_item.name + let top_left = &self.as_ref().index( + final_index, + 0, + &QModelIndex::default(), ); - println!( - "service_item_background: {:?}", - service_item.background + let bottom_right = &self.as_ref().index( + current_index as i32, + 0, + &QModelIndex::default(), ); - println!( - "service_item_background_type: {:?}", - service_item.background_type - ); - service_item.active = false; - self.as_mut().emit_data_changed( - tl, - br, - &vector_roles, - ); - // We use this signal generated by our signals enum to tell QML that - // the active service_item has changed which is used to reposition views. - // self.as_mut().emit_active_changed(index); - true - } else { - false - } - } - #[qinvokable] - pub fn save(mut self: Pin<&mut Self>, file: QUrl) -> bool { - println!("rust-save-file: {file}"); - let path = - file.to_local_file().unwrap_or_default().to_string(); - println!("path: {:?}", path); - let lfr = fs::File::create(&path); - if let Ok(lf) = &lfr { - println!("archive: {:?}", lf); - let encoder = Encoder::new(lf, 3).unwrap(); - let mut tar = Builder::new(encoder); - let items = self.service_items(); - let mut temp_dir = dirs::data_dir().unwrap(); - temp_dir.push("lumina"); - let mut s: String = - iter::repeat_with(fastrand::alphanumeric) - .take(5) - .collect(); - s.insert_str(0, "temp_"); - temp_dir.push(s); - match fs::create_dir_all(&temp_dir) { - Ok(f) => { - println!("created_temp_dir: {:?}", &temp_dir) - } - Err(e) => println!("temp-dir-error: {e}"), + for (index, item) in self + .as_mut() + .service_items_mut() + .iter_mut() + .enumerate() + .filter(|i| { + i.0 >= final_index as usize + && i.0 <= current_index + }) + { + item.selected = true; + debug!(selected_item = ?item, index = index); } - let mut temp_service_file = temp_dir.clone(); - temp_service_file.push("serviceitems.json"); - let mut service_json: Vec = vec![]; + self.as_mut().emit_data_changed( + top_left, + bottom_right, + &vector_roles, + ); + // self.as_mut().emit_selected_changed(); + } - for item in items { - let text_list = QList_QString::from(&item.text); - let mut text_vec = Vec::::default(); + true + } else { + // Here let's branch to select from the first item to the + // final item. Since we don't know which one is selected, + // assume that the first one is "selected" - let bg_string_path = item.background.to_string(); - let background_path = PathBuf::from( - bg_string_path - .to_string() - .strip_prefix("file://") - .unwrap_or(""), - ); - println!("bg_path: {:?}", background_path); - let flat_background_name = - &background_path.file_name(); - let flat_background; - match flat_background_name { - Some(name) => { - println!("bg: {:?}", &name); - if name.to_str().unwrap() != "temp" { - flat_background = - name.to_str().unwrap() - } else { - flat_background = ""; - } - } - _ => { - println!( - "save-background: no background" - ); + let top_left = &self.as_ref().index( + 0, + 0, + &QModelIndex::default(), + ); + let bottom_right = &self.as_ref().index( + final_index, + 0, + &QModelIndex::default(), + ); + for (index, item) in self + .as_mut() + .service_items_mut() + .iter_mut() + .enumerate() + .filter(|i| i.0 <= final_index as usize) + { + item.selected = true; + debug!(selected_item = ?item, index = index); + } + self.as_mut().emit_data_changed( + top_left, + bottom_right, + &vector_roles, + ); + // self.as_mut().emit_selected_changed(); + debug!( + first_item = 0, + final_item = final_index, + "couldn't find first selected item" + ); + false + } + } + + pub fn activate( + mut self: Pin<&mut Self>, + index: i32, + ) -> bool { + let rc = self.as_ref().count() - 1; + let tl = + &self.as_ref().index(0, 0, &QModelIndex::default()); + let br = + &self.as_ref().index(rc, 0, &QModelIndex::default()); + let mut vector_roles = QVector_i32::default(); + vector_roles.append(self.get_role(Role::ActiveRole)); + for service_item in + self.as_mut().service_items_mut().iter_mut() + { + // println!("service_item is deactivating {:?}", i); + service_item.active = false; + } + let obs = self.as_mut().obs_mut().clone(); + + if let Some(service_item) = self + .as_mut() + .service_items_mut() + .get_mut(index as usize) + { + debug!(activating_item = index, + title = ?service_item.name, + background = ?service_item.background, + background_type = ?service_item.background_type); + service_item.active = true; + // if let Some(obs) = obs { + // match obs + // .set_scene(service_item.obs_scene.to_string()) + // { + // Ok(()) => debug!("Successfully set scene"), + // Err(e) => error!(e), + // } + // } + self.as_mut().emit_data_changed( + tl, + br, + &vector_roles, + ); + // We use this signal generated by our signals enum to tell QML that + // the active service_item has changed which is used to reposition views. + self.as_mut().emit_active_changed(&index); + true + } else { + false + } + } + + pub fn deactivate( + mut self: Pin<&mut Self>, + index: i32, + ) -> bool { + let rc = self.as_ref().count() - 1; + let tl = + &self.as_ref().index(0, 0, &QModelIndex::default()); + let br = + &self.as_ref().index(rc, 0, &QModelIndex::default()); + let mut vector_roles = QVector_i32::default(); + vector_roles.append(self.get_role(Role::ActiveRole)); + if let Some(service_item) = self + .as_mut() + .service_items_mut() + .get_mut(index as usize) + { + println!("service_item is activating {:?}", index); + println!( + "service_item_title: {:?}", + service_item.name + ); + println!( + "service_item_background: {:?}", + service_item.background + ); + println!( + "service_item_background_type: {:?}", + service_item.background_type + ); + service_item.active = false; + self.as_mut().emit_data_changed( + tl, + br, + &vector_roles, + ); + // We use this signal generated by our signals enum to tell QML that + // the active service_item has changed which is used to reposition views. + // self.as_mut().emit_active_changed(index); + true + } else { + false + } + } + + pub fn save(mut self: Pin<&mut Self>, file: QUrl) -> bool { + println!("rust-save-file: {file}"); + let path = + file.to_local_file().unwrap_or_default().to_string(); + println!("path: {:?}", path); + let lfr = fs::File::create(&path); + if let Ok(lf) = &lfr { + println!("archive: {:?}", lf); + let encoder = Encoder::new(lf, 3).unwrap(); + let mut tar = Builder::new(encoder); + let items = self.service_items(); + let mut temp_dir = dirs::data_dir().unwrap(); + temp_dir.push("lumina"); + let mut s: String = + iter::repeat_with(fastrand::alphanumeric) + .take(5) + .collect(); + s.insert_str(0, "temp_"); + temp_dir.push(s); + match fs::create_dir_all(&temp_dir) { + Ok(f) => { + println!("created_temp_dir: {:?}", &temp_dir) + } + Err(e) => println!("temp-dir-error: {e}"), + } + let mut temp_service_file = temp_dir.clone(); + temp_service_file.push("serviceitems.json"); + let mut service_json: Vec = vec![]; + + for item in items { + let text_list = QList_QString::from(&item.text); + let mut text_vec = Vec::::default(); + + let bg_string_path = item.background.to_string(); + let background_path = PathBuf::from( + bg_string_path + .to_string() + .strip_prefix("file://") + .unwrap_or(""), + ); + println!("bg_path: {:?}", background_path); + let flat_background_name = + &background_path.file_name(); + let flat_background; + match flat_background_name { + Some(name) => { + println!("bg: {:?}", &name); + if name.to_str().unwrap() != "temp" { + flat_background = + name.to_str().unwrap() + } else { flat_background = ""; } } - let mut temp_bg_path = temp_dir.clone(); - temp_bg_path.push(flat_background); - match fs::copy(&background_path, &temp_bg_path) { - Ok(s) => println!( - "background-copied: of size: {:?}", - s - ), - Err(e) => println!("bg-copy-error: {e}"), + _ => { + println!( + "save-background: no background" + ); + flat_background = ""; } + } + let mut temp_bg_path = temp_dir.clone(); + temp_bg_path.push(flat_background); + match fs::copy(&background_path, &temp_bg_path) { + Ok(s) => println!( + "background-copied: of size: {:?}", + s + ), + Err(e) => println!("bg-copy-error: {e}"), + } - let audio_path_str = item.audio.to_string(); - let audio_path = PathBuf::from( - audio_path_str - .strip_prefix("file://") - .unwrap_or(""), - ); - println!("audio_path: {:?}", audio_path); - let flat_audio_name = audio_path.file_name(); - let flat_audio; - match flat_audio_name { - Some(name) => { - println!("audio: {:?}", &name); - if name.to_str().unwrap() != "temp" { - flat_audio = name.to_str().unwrap() - } else { - flat_audio = ""; - } - } - _ => { - println!("save-audio: no audio"); + let audio_path_str = item.audio.to_string(); + let audio_path = PathBuf::from( + audio_path_str + .strip_prefix("file://") + .unwrap_or(""), + ); + println!("audio_path: {:?}", audio_path); + let flat_audio_name = audio_path.file_name(); + let flat_audio; + match flat_audio_name { + Some(name) => { + println!("audio: {:?}", &name); + if name.to_str().unwrap() != "temp" { + flat_audio = name.to_str().unwrap() + } else { flat_audio = ""; } } - let mut temp_aud_path = temp_dir.clone(); - temp_aud_path.push(flat_audio); - match fs::copy(&audio_path, temp_aud_path) { - Ok(s) => { - println!("audio-copied: of size: {:?}", s) - } - Err(e) => println!("audio-copy-error: {e}"), + _ => { + println!("save-audio: no audio"); + flat_audio = ""; } - - for (index, line) in text_list.iter().enumerate() - { - text_vec.insert(index, line.to_string()) + } + let mut temp_aud_path = temp_dir.clone(); + temp_aud_path.push(flat_audio); + match fs::copy(&audio_path, temp_aud_path) { + Ok(s) => { + println!("audio-copied: of size: {:?}", s) } + Err(e) => println!("audio-copy-error: {e}"), + } - let item_json = json!({"name".to_owned(): Value::from(item.name.to_string()), - "type".to_owned(): Value::from(item.ty.to_string()), - "audio".to_owned(): Value::from(item.audio.to_string()), - "background".to_owned(): Value::from(item.background.to_string()), - "backgroundType".to_owned(): Value::from(item.background_type.to_string()), - "font".to_owned(): Value::from(item.font.to_string()), - "fontSize".to_owned(): Value::from(item.font_size), - "flatAudio".to_owned(): Value::from(flat_audio), - "flatBackground".to_owned(): Value::from(flat_background), - "loop".to_owned(): Value::from(item.looping), - "slideNumber".to_owned(): Value::from(item.slide_count), - "text".to_owned(): Value::from(text_vec)}); - println!("item-json: {item_json}"); - service_json.push(item_json); - } - println!("{:?}", &temp_service_file); - match fs::File::create(&temp_service_file) { - Ok(o) => println!("created: {:?}", o), - Err(e) => println!( - "error-creating-service-file: {:?}", - e - ), - } - match fs::File::options() - .write(true) - .read(true) - .open(&temp_service_file) + for (index, line) in text_list.iter().enumerate() { - Ok(service_file) => { - match serde_json::to_writer( - service_file, - &service_json, - ) { - Ok(e) => { - println!("json: file written"); - match tar - .append_dir_all("./", &temp_dir) - { - Ok(i) => { - println!("idk"); + text_vec.insert(index, line.to_string()) + } - match tar.finish() { - Ok(i) => { - println!("tar-written: {:?}", &lf); - fs::remove_dir_all( - &temp_dir, - ); - true - } - Err(e) => { - println!( - "tar-error: {:?}", - e - ); - fs::remove_dir_all( - &temp_dir, - ); - false - } + let item_json = json!({"name".to_owned(): Value::from(item.name.to_string()), + "type".to_owned(): Value::from(item.ty.to_string()), + "audio".to_owned(): Value::from(item.audio.to_string()), + "background".to_owned(): Value::from(item.background.to_string()), + "backgroundType".to_owned(): Value::from(item.background_type.to_string()), + "font".to_owned(): Value::from(item.font.to_string()), + "fontSize".to_owned(): Value::from(item.font_size), + "flatAudio".to_owned(): Value::from(flat_audio), + "flatBackground".to_owned(): Value::from(flat_background), + "loop".to_owned(): Value::from(item.looping), + "slideNumber".to_owned(): Value::from(item.slide_count), + "text".to_owned(): Value::from(text_vec)}); + println!("item-json: {item_json}"); + service_json.push(item_json); + } + println!("{:?}", &temp_service_file); + match fs::File::create(&temp_service_file) { + Ok(o) => println!("created: {:?}", o), + Err(e) => println!( + "error-creating-service-file: {:?}", + e + ), + } + match fs::File::options() + .write(true) + .read(true) + .open(&temp_service_file) + { + Ok(service_file) => { + match serde_json::to_writer( + service_file, + &service_json, + ) { + Ok(e) => { + println!("json: file written"); + match tar + .append_dir_all("./", &temp_dir) + { + Ok(i) => { + println!("idk"); + + match tar.finish() { + Ok(i) => { + println!("tar-written: {:?}", &lf); + fs::remove_dir_all( + &temp_dir, + ); + true + } + Err(e) => { + println!( + "tar-error: {:?}", + e + ); + fs::remove_dir_all( + &temp_dir, + ); + false } } - Err(e) => { - println!("err: {:?}", e); - fs::remove_dir_all(&temp_dir); - false - } + } + Err(e) => { + println!("err: {:?}", e); + fs::remove_dir_all(&temp_dir); + false } } - Err(e) => { - println!("json: error: {:?}", e); - fs::remove_dir_all(&temp_dir); - false - } + } + Err(e) => { + println!("json: error: {:?}", e); + fs::remove_dir_all(&temp_dir); + false } } - Err(e) => { - println!( - "json: service_file isn't open: {:?}", - e - ); - fs::remove_dir_all(&temp_dir); - false - } } - } else { - println!("rust-save-file-failed: {:?}", lfr); - false + Err(e) => { + println!( + "json: service_file isn't open: {:?}", + e + ); + fs::remove_dir_all(&temp_dir); + false + } } + } else { + println!("rust-save-file-failed: {:?}", lfr); + false } + } - #[qinvokable] - pub fn load(mut self: Pin<&mut Self>, file: QUrl) -> bool { - self.as_mut().clear(); - println!("file is: {file}"); - let lfr = fs::File::open( - file.to_local_file().unwrap_or_default().to_string(), - ); + pub fn load(mut self: Pin<&mut Self>, file: QUrl) -> bool { + self.as_mut().clear(); + println!("file is: {file}"); + let lfr = fs::File::open( + file.to_local_file().unwrap_or_default().to_string(), + ); - let mut datadir = dirs::data_dir().unwrap(); - datadir.push("lumina"); - datadir.push("temp"); - println!("datadir: {:?}", datadir); - fs::remove_dir_all(&datadir); - fs::create_dir_all(&datadir); + let mut datadir = dirs::data_dir().unwrap(); + datadir.push("lumina"); + datadir.push("temp"); + println!("datadir: {:?}", datadir); + fs::remove_dir_all(&datadir); + fs::create_dir_all(&datadir); - if let Ok(lf) = &lfr { - println!("archive: {:?}", lf); - let dec = Decoder::new(lf).unwrap(); - let mut tar = Archive::new(dec); - for mut file in - tar.entries().unwrap().filter_map(|e| e.ok()) - { - let mut file_path = datadir.clone(); - file_path.push(file.path().unwrap()); - // Inspect metadata about each file - println!("filename: {:?}", file.path().unwrap()); - println!("size: {:?}", file.size()); - if !file_path.exists() { - file.unpack_in(&datadir); - } - } - - // older save files use servicelist.json instead of serviceitems.json - // Let's check to see if that's the case and change it's name in the - // temp dir. - for mut file in fs::read_dir(datadir.clone()) - .unwrap() - .filter(|f| { - f.as_ref() - .map(|e| { - String::from( - e.file_name().to_str().unwrap(), - ) - }) - .unwrap_or(String::from("")) - == "servicelist.json" - }) - { - let mut service_path = datadir.clone(); - service_path.push("serviceitems.json"); - match fs::rename( - file.unwrap().path(), - service_path, - ) { - Ok(i) => println!("We did it captain"), - Err(e) => println!("error: {:?}", e), - } + if let Ok(lf) = &lfr { + println!("archive: {:?}", lf); + let dec = Decoder::new(lf).unwrap(); + let mut tar = Archive::new(dec); + for mut file in + tar.entries().unwrap().filter_map(|e| e.ok()) + { + let mut file_path = datadir.clone(); + file_path.push(file.path().unwrap()); + // Inspect metadata about each file + println!("filename: {:?}", file.path().unwrap()); + println!("size: {:?}", file.size()); + if !file_path.exists() { + file.unpack_in(&datadir); } + } + // older save files use servicelist.json instead of serviceitems.json + // Let's check to see if that's the case and change it's name in the + // temp dir. + for mut file in fs::read_dir(datadir.clone()) + .unwrap() + .filter(|f| { + f.as_ref() + .map(|e| { + String::from( + e.file_name().to_str().unwrap(), + ) + }) + .unwrap_or(String::from("")) + == "servicelist.json" + }) + { let mut service_path = datadir.clone(); service_path.push("serviceitems.json"); - // let mut service_list = - // fs::File::open(service_path).unwrap(); + match fs::rename( + file.unwrap().path(), + service_path, + ) { + Ok(i) => println!("We did it captain"), + Err(e) => println!("error: {:?}", e), + } + } - let mut s = fs::read_to_string(service_path).unwrap(); - // service_list.read_to_string(&mut s); - let ds: Value = serde_json::from_str(&s).unwrap(); - for obj in ds.as_array().unwrap() { - println!( - "objname: {:?}", - obj.get("name").unwrap().as_str().unwrap() - ); - println!( - "objtype: {:?}", - obj.get("type").unwrap().as_str().unwrap() - ); - let name = QString::from( - obj.get("name").unwrap().as_str().unwrap(), - ); - let ty = QString::from( - obj.get("type").unwrap().as_str().unwrap(), - ); - // both audio and background will need to know if - // it exists on disk, if not use the flat version - let audio_string = - obj.get("audio").unwrap().as_str().unwrap(); - let mut audio; - println!("audio_on_disk: {audio_string}"); + let mut service_path = datadir.clone(); + service_path.push("serviceitems.json"); + // let mut service_list = + // fs::File::open(service_path).unwrap(); - if !Path::new(&audio_string).exists() { - println!("#$#$#$#$#$#"); - println!("The audio doesn't exist"); - println!("#$#$#$#$#$#"); - let string = obj - .get("flatAudio") - .unwrap() - .as_str() - .unwrap(); - if !string.is_empty() { - println!( - "before_audio_str: {:?}", - string - ); - let mut audio_path = datadir.clone(); - audio_path.push(string); - // Needed to ensure QML images and mpv will find the audio - let mut final_string = audio_path - .to_str() - .unwrap() - .to_owned(); - final_string.insert_str(0, "file://"); - audio = QString::from(&final_string); - println!( - "after_audio_str: {:?}", - final_string - ); - } else { - audio = QString::default(); - } - } else { - audio = QString::from(audio_string); - } + let mut s = fs::read_to_string(service_path).unwrap(); + // service_list.read_to_string(&mut s); + let ds: Value = serde_json::from_str(&s).unwrap(); + for obj in ds.as_array().unwrap() { + println!( + "objname: {:?}", + obj.get("name").unwrap().as_str().unwrap() + ); + println!( + "objtype: {:?}", + obj.get("type").unwrap().as_str().unwrap() + ); + let name = QString::from( + obj.get("name").unwrap().as_str().unwrap(), + ); + let ty = QString::from( + obj.get("type").unwrap().as_str().unwrap(), + ); + // both audio and background will need to know if + // it exists on disk, if not use the flat version + let audio_string = + obj.get("audio").unwrap().as_str().unwrap(); + let mut audio; + println!("audio_on_disk: {audio_string}"); - let bgstr = obj - .get("background") + if !Path::new(&audio_string).exists() { + println!("#$#$#$#$#$#"); + println!("The audio doesn't exist"); + println!("#$#$#$#$#$#"); + let string = obj + .get("flatAudio") .unwrap() .as_str() .unwrap(); - let mut background; - println!("background_on_disk: {bgstr}"); - let bgpath = - bgstr.strip_prefix("file://").unwrap_or(""); - - // lets test to see if the background exists on disk. - // if not we can use the flat version - if !Path::new(&bgpath).exists() { - println!("#$#$#$#$#$#"); - println!("The background doesn't exist"); - println!("#$#$#$#$#$#"); - let string = obj - .get("flatBackground") + if !string.is_empty() { + println!( + "before_audio_str: {:?}", + string + ); + let mut audio_path = datadir.clone(); + audio_path.push(string); + // Needed to ensure QML images and mpv will find the audio + let mut final_string = audio_path + .to_str() .unwrap() - .as_str() - .unwrap(); - if !string.is_empty() { - println!("before_bgstr: {:?}", string); - let mut bgpath = datadir.clone(); - bgpath.push(string); - // Needed to ensure QML images and mpv will find the background - let mut final_string = - bgpath.to_str().unwrap().to_owned(); - final_string.insert_str(0, "file://"); - background = QString::from(&final_string); - println!( - "after_bgstr: {:?}", - final_string - ); - } else { - background = QString::default(); - } + .to_owned(); + final_string.insert_str(0, "file://"); + audio = QString::from(&final_string); + println!( + "after_audio_str: {:?}", + final_string + ); } else { - background = QString::from(bgstr); + audio = QString::default(); } - println!("realbg: {:?}", background); - - let background_type = QString::from( - obj.get("backgroundType") - .unwrap() - .as_str() - .unwrap(), - ); - let font = QString::from( - obj.get("font").unwrap().as_str().unwrap(), - ); - let font_size = obj - .get("fontSize") - .unwrap() - .as_i64() - .unwrap() - as i32; - let looping; - if let Some(lp) = obj.get("loop") { - looping = lp.as_bool().unwrap(); - } else { - looping = false; - } - let slide_count; - if let Some(sc) = obj.get("slideNumber") { - slide_count = sc.as_i64().unwrap() as i32; - } else { - slide_count = i32::default(); - } - let mut video_start_time = f32::default(); - if let Some(video_start_value) = - obj.get("video_start_time") - { - video_start_time = - video_start_value.as_f64().unwrap() - as f32; - } - let mut video_end_time = f32::default(); - if let Some(video_end_value) = - obj.get("video_end_time") - { - video_end_time = - video_end_value.as_f64().unwrap() as f32; - } - let text_array = - obj.get("text").unwrap().as_array().unwrap(); - let mut text_list = QList_QString::default(); - for txt in text_array { - text_list.append(QString::from( - txt.as_str().unwrap(), - )); - } - let text = QStringList::from(&text_list); - - let service_item = ServiceItm { - name, - ty, - text, - background, - background_type, - audio, - font, - font_size, - slide_count, - looping, - video_start_time, - video_end_time, - ..Default::default() - }; - self.as_mut().add_service_item(&service_item); - println!("Loaded Service: {:?}", ds); - // // files implement the Read trait + } else { + audio = QString::from(audio_string); } - true - } else { - println!("There is no file here: {file}"); - println!("Loading default service"); - false - } - } - #[qinvokable] - pub fn load_last_saved(mut self: Pin<&mut Self>) -> bool { - todo!(); - // Don't actually need - } + let bgstr = obj + .get("background") + .unwrap() + .as_str() + .unwrap(); + let mut background; + println!("background_on_disk: {bgstr}"); + let bgpath = + bgstr.strip_prefix("file://").unwrap_or(""); - fn get_role(&self, role: Role) -> i32 { - match role { - Role::NameRole => 0, - Role::TyRole => 1, - Role::AudioRole => 2, - Role::BackgroundRole => 3, - Role::BackgroundTypeRole => 4, - Role::TextRole => 5, - Role::FontRole => 6, - Role::FontSizeRole => 7, - Role::SlideCountRole => 8, - Role::ActiveRole => 9, - Role::SelectedRole => 10, - Role::LoopingRole => 11, - Role::VideoStartTimeRole => 12, - Role::VideoEndTimeRole => 13, - _ => 0, - } - } - } - - // Create Rust bindings for C++ functions of the base class (QAbstractItem_Modelel) - #[cxx_qt::inherit] - extern "C++" { - unsafe fn begin_insert_rows( - self: Pin<&mut qobject::ServiceItemMod>, - parent: &QModelIndex, - first: i32, - last: i32, - ); - unsafe fn end_insert_rows( - self: Pin<&mut qobject::ServiceItemMod>, - ); - - unsafe fn begin_move_rows( - self: Pin<&mut qobject::ServiceItemMod>, - source_parent: &QModelIndex, - source_first: i32, - source_last: i32, - destination_parent: &QModelIndex, - destination_child: i32, - ) -> bool; - - unsafe fn end_move_rows( - self: Pin<&mut qobject::ServiceItemMod>, - ); - - unsafe fn begin_remove_rows( - self: Pin<&mut qobject::ServiceItemMod>, - parent: &QModelIndex, - first: i32, - last: i32, - ); - unsafe fn end_remove_rows( - self: Pin<&mut qobject::ServiceItemMod>, - ); - - unsafe fn begin_reset_model( - self: Pin<&mut qobject::ServiceItemMod>, - ); - unsafe fn end_reset_model( - self: Pin<&mut qobject::ServiceItemMod>, - ); - } - - #[cxx_qt::inherit] - unsafe extern "C++" { - #[cxx_name = "canFetchMore"] - fn base_can_fetch_more( - self: &qobject::ServiceItemMod, - parent: &QModelIndex, - ) -> bool; - - fn index( - self: &qobject::ServiceItemMod, - row: i32, - column: i32, - parent: &QModelIndex, - ) -> QModelIndex; - } - - // QAbstractListModel implementation - impl qobject::ServiceItemMod { - #[qinvokable(cxx_override)] - fn data(&self, index: &QModelIndex, role: i32) -> QVariant { - if let Some(service_item) = - self.service_items().get(index.row() as usize) - { - return match role { - 0 => QVariant::from(&service_item.name), - 1 => QVariant::from(&service_item.ty), - 2 => QVariant::from(&service_item.audio), - 3 => QVariant::from(&service_item.background), - 4 => { - QVariant::from(&service_item.background_type) + // lets test to see if the background exists on disk. + // if not we can use the flat version + if !Path::new(&bgpath).exists() { + println!("#$#$#$#$#$#"); + println!("The background doesn't exist"); + println!("#$#$#$#$#$#"); + let string = obj + .get("flatBackground") + .unwrap() + .as_str() + .unwrap(); + if !string.is_empty() { + println!("before_bgstr: {:?}", string); + let mut bgpath = datadir.clone(); + bgpath.push(string); + // Needed to ensure QML images and mpv will find the background + let mut final_string = + bgpath.to_str().unwrap().to_owned(); + final_string.insert_str(0, "file://"); + background = QString::from(&final_string); + println!( + "after_bgstr: {:?}", + final_string + ); + } else { + background = QString::default(); } - 5 => QVariant::from(&service_item.text), - 6 => QVariant::from(&service_item.font), - 7 => QVariant::from(&service_item.font_size), - 8 => QVariant::from(&service_item.slide_count), - 9 => QVariant::from(&service_item.active), - 10 => QVariant::from(&service_item.selected), - 11 => QVariant::from(&service_item.looping), - 12 => { - QVariant::from(&service_item.video_start_time) - } - 13 => { - QVariant::from(&service_item.video_end_time) - } - _ => QVariant::default(), + } else { + background = QString::from(bgstr); + } + println!("realbg: {:?}", background); + + let background_type = QString::from( + obj.get("backgroundType") + .unwrap() + .as_str() + .unwrap(), + ); + let font = QString::from( + obj.get("font").unwrap().as_str().unwrap(), + ); + let font_size = obj + .get("fontSize") + .unwrap() + .as_i64() + .unwrap() + as i32; + let looping; + if let Some(lp) = obj.get("loop") { + looping = lp.as_bool().unwrap(); + } else { + looping = false; + } + let slide_count; + if let Some(sc) = obj.get("slideNumber") { + slide_count = sc.as_i64().unwrap() as i32; + } else { + slide_count = i32::default(); + } + let mut video_start_time = f32::default(); + if let Some(video_start_value) = + obj.get("video_start_time") + { + video_start_time = + video_start_value.as_f64().unwrap() + as f32; + } + let mut video_end_time = f32::default(); + if let Some(video_end_value) = + obj.get("video_end_time") + { + video_end_time = + video_end_value.as_f64().unwrap() as f32; + } + let text_array = + obj.get("text").unwrap().as_array().unwrap(); + let mut text_list = QList_QString::default(); + for txt in text_array { + text_list.append(QString::from( + txt.as_str().unwrap(), + )); + } + let text = QStringList::from(&text_list); + + let service_item = ServiceItm { + name, + ty, + text, + background, + background_type, + audio, + font, + font_size, + slide_count, + looping, + video_start_time, + video_end_time, + ..Default::default() }; + self.as_mut().add_service_item(&service_item); + println!("Loaded Service: {:?}", ds); + // // files implement the Read trait } - - QVariant::default() + true + } else { + println!("There is no file here: {file}"); + println!("Loading default service"); + false } + } - // Example of overriding a C++ virtual method and calling the base class implementation. - #[qinvokable(cxx_override)] - pub fn can_fetch_more(&self, parent: &QModelIndex) -> bool { - self.base_can_fetch_more(parent) - } + pub fn load_last_saved(mut self: Pin<&mut Self>) -> bool { + todo!(); + // Don't actually need + } - #[qinvokable(cxx_override)] - pub fn role_names(&self) -> QHash_i32_QByteArray { - let mut roles = QHash_i32_QByteArray::default(); - roles.insert(0, cxx_qt_lib::QByteArray::from("name")); - roles.insert(1, cxx_qt_lib::QByteArray::from("ty")); - roles.insert(2, cxx_qt_lib::QByteArray::from("audio")); - roles.insert( - 3, - cxx_qt_lib::QByteArray::from("background"), - ); - roles.insert( - 4, - cxx_qt_lib::QByteArray::from("backgroundType"), - ); - roles.insert(5, cxx_qt_lib::QByteArray::from("text")); - roles.insert(6, cxx_qt_lib::QByteArray::from("font")); - roles.insert(7, cxx_qt_lib::QByteArray::from("fontSize")); - roles.insert( - 8, - cxx_qt_lib::QByteArray::from("slideCount"), - ); - roles.insert(9, cxx_qt_lib::QByteArray::from("active")); - roles - .insert(10, cxx_qt_lib::QByteArray::from("selected")); - roles.insert(11, cxx_qt_lib::QByteArray::from("looping")); - roles.insert( - 12, - cxx_qt_lib::QByteArray::from("videoStartTime"), - ); - roles.insert( - 13, - cxx_qt_lib::QByteArray::from("videoEndTime"), - ); - roles - } - - #[qinvokable(cxx_override)] - pub fn row_count(&self, _parent: &QModelIndex) -> i32 { - let cnt = self.rust().service_items.len() as i32; - // println!("row count is {cnt}"); - cnt - } - - #[qinvokable] - pub fn count(&self) -> i32 { - self.rust().service_items.len() as i32 + fn get_role(&self, role: Role) -> i32 { + match role { + Role::NameRole => 0, + Role::TyRole => 1, + Role::AudioRole => 2, + Role::BackgroundRole => 3, + Role::BackgroundTypeRole => 4, + Role::TextRole => 5, + Role::FontRole => 6, + Role::FontSizeRole => 7, + Role::SlideCountRole => 8, + Role::ActiveRole => 9, + Role::SelectedRole => 10, + Role::LoopingRole => 11, + Role::VideoStartTimeRole => 12, + Role::VideoEndTimeRole => 13, + _ => 0, } } } + +// QAbstractListModel implementation +impl qobject::ServiceItemMod { + fn data(&self, index: &QModelIndex, role: i32) -> QVariant { + let role = qobject::Roles { repr: role }; + if let Some(service_item) = + self.service_items().get(index.row() as usize) + { + return match role { + qobject::Roles::Name => QVariant::from(&service_item.name), + qobject::Roles::Type => QVariant::from(&service_item.ty), + qobject::Roles::Audio => QVariant::from(&service_item.audio), + qobject::Roles::Background => QVariant::from(&service_item.background), + qobject::Roles::BackgroundType => { + QVariant::from(&service_item.background_type) + } + qobject::Roles::Text => QVariant::from(&service_item.text), + qobject::Roles::Font => QVariant::from(&service_item.font), + qobject::Roles::FontSize => QVariant::from(&service_item.font_size), + qobject::Roles::SlideCount => QVariant::from(&service_item.slide_count), + qobject::Roles::Active => QVariant::from(&service_item.active), + qobject::Roles::Selected => QVariant::from(&service_item.selected), + qobject::Roles::Looping => QVariant::from(&service_item.looping), + qobject::Roles::VideoStartTime => { + QVariant::from(&service_item.video_start_time) + } + qobject::Roles::VideoEndTime => { + QVariant::from(&service_item.video_end_time) + } + _ => QVariant::default(), + }; + } + + QVariant::default() + } + + // Example of overriding a C++ virtual method and calling the base class implementation. + pub fn can_fetch_more(&self, parent: &QModelIndex) -> bool { + self.base_can_fetch_more(parent) + } + + pub fn role_names(&self) -> QHash_i32_QByteArray { + let mut roles = QHash_i32_QByteArray::default(); + roles.insert(qobject::Roles::Name.repr, cxx_qt_lib::QByteArray::from("name")); + roles.insert(qobject::Roles::Type.repr, cxx_qt_lib::QByteArray::from("ty")); + roles.insert(qobject::Roles::Audio.repr, cxx_qt_lib::QByteArray::from("audio")); + roles.insert( + qobject::Roles::Background.repr, + cxx_qt_lib::QByteArray::from("background"), + ); + roles.insert( + qobject::Roles::BackgroundType, + cxx_qt_lib::QByteArray::from("backgroundType"), + ); + roles.insert(qobject::Roles::Text.repr, cxx_qt_lib::QByteArray::from("text")); + roles.insert(qobject::Roles::Font.repr, cxx_qt_lib::QByteArray::from("font")); + roles.insert(qobject::Roles::FontSize.repr, cxx_qt_lib::QByteArray::from("fontSize")); + roles.insert( + qobject::Roles::SlideCount.repr, + cxx_qt_lib::QByteArray::from("slideCount"), + ); + roles.insert(qobject::Roles::Active.repr, cxx_qt_lib::QByteArray::from("active")); + roles + .insert(qobject::Roles::Selected.repr, cxx_qt_lib::QByteArray::from("selected")); + roles.insert(qobject::Roles::Looping.repr, cxx_qt_lib::QByteArray::from("looping")); + roles.insert( + qobject::Roles::VideoStartTime.repr, + cxx_qt_lib::QByteArray::from("videoStartTime"), + ); + roles.insert( + qobject::Roles::VideoEndTime.repr, + cxx_qt_lib::QByteArray::from("videoEndTime"), + ); + roles + } + + pub fn row_count(&self, _parent: &QModelIndex) -> i32 { + let cnt = self.rust().service_items.len() as i32; + // println!("row count is {cnt}"); + cnt + } + + pub fn count(&self) -> i32 { + self.rust().service_items.len() as i32 + } +} diff --git a/src/rust/slide_model.rs b/src/rust/slide_model.rs index bceb436..5ee22f5 100644 --- a/src/rust/slide_model.rs +++ b/src/rust/slide_model.rs @@ -253,7 +253,6 @@ pub struct SlideModelRust { } impl qobject::SlideModel { - #[qinvokable] pub fn add_video_thumbnail( mut self: Pin<&mut Self>, index: i32, @@ -285,7 +284,6 @@ impl qobject::SlideModel { true } - #[qinvokable] pub fn clear(mut self: Pin<&mut Self>) { println!("CLEARING ALL SLIDES"); unsafe { @@ -295,7 +293,6 @@ impl qobject::SlideModel { } } - #[qinvokable] pub fn remove_item_from_service( mut self: Pin<&mut Self>, index: i32, @@ -324,7 +321,6 @@ impl qobject::SlideModel { } } - #[qinvokable] pub fn remove_item(mut self: Pin<&mut Self>, index: i32) { if index < 0 || (index as usize) >= self.slides().len() { return; @@ -394,7 +390,6 @@ impl qobject::SlideModel { }); } - #[qinvokable] pub fn insert_item_from_service( mut self: Pin<&mut Self>, index: i32, @@ -586,7 +581,6 @@ impl qobject::SlideModel { println!("Item added in slide model!"); } - #[qinvokable] pub fn add_item_from_service( mut self: Pin<&mut Self>, index: i32, @@ -742,7 +736,6 @@ impl qobject::SlideModel { println!("Item added in rust model!"); } - #[qinvokable] pub fn move_item_from_service( mut self: Pin<&mut Self>, source_index: i32, @@ -948,7 +941,6 @@ impl qobject::SlideModel { } } - #[qinvokable] pub fn get_item( self: Pin<&mut Self>, index: i32, @@ -972,7 +964,6 @@ impl qobject::SlideModel { qvariantmap } - #[qinvokable] pub fn get_slide_from_service( self: Pin<&mut Self>, index: i32, @@ -992,7 +983,6 @@ impl qobject::SlideModel { id } - #[qinvokable] pub fn activate(mut self: Pin<&mut Self>, index: i32) -> bool { let rc = self.as_ref().count() - 1; let tl = &self.as_ref().index(0, 0, &QModelIndex::default()); diff --git a/src/rust/video_model.rs b/src/rust/video_model.rs index 38ffeb7..2c92b1e 100644 --- a/src/rust/video_model.rs +++ b/src/rust/video_model.rs @@ -1,12 +1,5 @@ #[cxx_qt::bridge] mod video_model { - use crate::models::*; - use crate::schema::videos::dsl::*; - use crate::video_model::video_model::Video; - use diesel::sqlite::SqliteConnection; - use diesel::{delete, insert_into, prelude::*, update}; - use std::path::{Path, PathBuf}; - unsafe extern "C++" { include!(< QAbstractListModel >); include!("cxx-qt-lib/qhash.h"); @@ -31,574 +24,641 @@ mod video_model { type QList_QString = cxx_qt_lib::QList; } - #[derive(Default, Clone, Debug)] - pub struct Video { - id: i32, - title: QString, - path: QString, - start_time: f32, - end_time: f32, - looping: bool, - } - - #[cxx_qt::qobject(base = "QAbstractListModel")] - #[derive(Default, Debug)] - pub struct VideoModel { - highest_id: i32, - videos: Vec, - } - - #[cxx_qt::qsignals(VideoModel)] - pub enum Signals<'a> { - #[inherit] - DataChanged { - top_left: &'a QModelIndex, - bottom_right: &'a QModelIndex, - roles: &'a QVector_i32, - }, - } - + #[qenum(VideoModel)] enum Role { - IdRole, - TitleRole, - PathRole, - StartTimeRole, - EndTimeRole, - LoopingRole, + Id, + Title, + Path, + StartTime, + EndTime, + Looping, } - impl qobject::VideoModel { - #[qinvokable] - pub fn clear(mut self: Pin<&mut Self>) { - unsafe { - self.as_mut().begin_reset_model(); - self.as_mut().videos_mut().clear(); - self.as_mut().end_reset_model(); - } - } + unsafe extern "RustQt" { + #[qobject] + #[base = "QAbstractListModel"] + #[qml_element] + #[qproperty(i32, count_rows)] + type VideoModel = super::VideoModelRust; + + #[inherit] + #[qsignal] + fn data_changed( + self: Pin<&mut VideoModel>, + top_left: &QModelIndex, + bottom_right: &QModelIndex, + roles: &QVector_i32, + ); #[qinvokable] - pub fn setup(mut self: Pin<&mut Self>) { - let db = &mut self.as_mut().get_db(); - let results = videos - .load::(db) - .expect("Error loading videos"); - self.as_mut().set_highest_id(0); - - println!("SHOWING VIDEOS"); - println!("--------------"); - for video in results { - println!("{}", video.title); - println!("{}", video.id); - println!("{}", video.path); - println!("--------------"); - if self.as_mut().highest_id() < &video.id { - self.as_mut().set_highest_id(video.id); - } - - let img = self::Video { - id: video.id, - title: QString::from(&video.title), - path: QString::from(&video.path), - start_time: video.start_time.unwrap_or(0.0), - end_time: video.end_time.unwrap_or(0.0), - looping: video.looping, - }; - - self.as_mut().add_video(img); - } - println!("--------------------------------------"); - println!("{:?}", self.as_mut().videos()); - println!("--------------------------------------"); - } - + fn clear(self: Pin<&mut VideoModel>); #[qinvokable] - pub fn remove_item( - mut self: Pin<&mut Self>, + fn setup(self: Pin<&mut VideoModel>); + #[qinvokable] + fn remove_item( + self: Pin<&mut VideoModel>, index: i32, - ) -> bool { - if index < 0 || (index as usize) >= self.videos().len() { - return false; - } - let db = &mut self.as_mut().get_db(); - - let video_id = - self.videos().get(index as usize).unwrap().id; - - let result = - delete(videos.filter(id.eq(video_id))).execute(db); - - match result { - Ok(_i) => { - unsafe { - self.as_mut().begin_remove_rows( - &QModelIndex::default(), - index, - index, - ); - self.as_mut() - .videos_mut() - .remove(index as usize); - self.as_mut().end_remove_rows(); - } - println!("removed-item-at-index: {:?}", video_id); - println!("new-Vec: {:?}", self.as_mut().videos()); - true - } - Err(_e) => { - println!("Cannot connect to database"); - false - } - } - } - - fn get_db(self: Pin<&mut Self>) -> SqliteConnection { - let mut data = dirs::data_local_dir().unwrap(); - data.push("lumina"); - data.push("library-db.sqlite3"); - let mut db_url = String::from("sqlite://"); - db_url.push_str(data.to_str().unwrap()); - println!("DB: {:?}", db_url); - - SqliteConnection::establish(&db_url).unwrap_or_else( - |_| panic!("error connecting to {}", db_url), - ) - } - + ) -> bool; #[qinvokable] - pub fn new_item(mut self: Pin<&mut Self>, url: QUrl) { - println!("LETS INSERT THIS SUCKER!"); - let file_path = PathBuf::from(url.path().to_string()); - let name = - file_path.file_stem().unwrap().to_str().unwrap(); - let video_id = self.rust().highest_id + 1; - let video_title = QString::from(name); - let video_path = url.to_qstring(); - - if self.as_mut().add_item( - video_id, - video_title, - video_path, - ) { - println!("filename: {:?}", name); - self.as_mut().set_highest_id(video_id); - } else { - println!("Error in inserting item"); - } - } - + fn new_item(self: Pin<&mut VideoModel>, url: QUrl); #[qinvokable] - pub fn add_item( - mut self: Pin<&mut Self>, - video_id: i32, - video_title: QString, - video_path: QString, - ) -> bool { - let db = &mut self.as_mut().get_db(); - // println!("{:?}", db); - let video = self::Video { - id: video_id, - title: video_title.clone(), - path: video_path.clone(), - start_time: 0.0, - end_time: 0.0, - looping: false, - }; - println!("{:?}", video); - - let result = insert_into(videos) - .values(( - id.eq(&video_id), - title.eq(&video_title.to_string()), - path.eq(&video_path.to_string()), - start_time.eq(&video.start_time), - end_time.eq(&video.end_time), - looping.eq(&video.looping), - )) - .execute(db); - println!("{:?}", result); - - match result { - Ok(_i) => { - self.as_mut().add_video(video); - println!("{:?}", self.as_mut().videos()); - true - } - Err(_e) => { - println!( - "Cannot connect to database or there was an error in inserting the video" - ); - false - } - } - } - - fn add_video(mut self: Pin<&mut Self>, video: self::Video) { - let index = self.as_ref().videos().len() as i32; - println!("{:?}", video); - unsafe { - self.as_mut().begin_insert_rows( - &QModelIndex::default(), - index, - index, - ); - self.as_mut().videos_mut().push(video); - self.as_mut().end_insert_rows(); - } - } - - #[qinvokable] - pub fn get_item( - self: Pin<&mut Self>, - index: i32, - ) -> QMap_QString_QVariant { - println!("{index}"); - let mut qvariantmap = QMap_QString_QVariant::default(); - let idx = self.index(index, 0, &QModelIndex::default()); - if !idx.is_valid() { - return qvariantmap; - } - let role_names = self.as_ref().role_names(); - let role_names_iter = role_names.iter(); - if let Some(video) = - self.rust().videos.get(index as usize) - { - for i in role_names_iter { - qvariantmap.insert( - QString::from(&i.1.to_string()), - self.as_ref().data(&idx, *i.0), - ); - } - println!("gotted-video: {:?}", video); - }; - qvariantmap - } - - fn get_role(&self, role: Role) -> i32 { - match role { - Role::IdRole => 0, - Role::TitleRole => 1, - Role::PathRole => 2, - Role::StartTimeRole => 3, - Role::EndTimeRole => 4, - Role::LoopingRole => 5, - _ => 0, - } - } - - #[qinvokable] - pub fn update_loop( - mut self: Pin<&mut Self>, - index: i32, - loop_value: bool, - ) -> bool { - let mut vector_roles = QVector_i32::default(); - vector_roles - .append(self.as_ref().get_role(Role::LoopingRole)); - let model_index = &self.as_ref().index( - index, - 0, - &QModelIndex::default(), - ); - println!("rust-video: {:?}", index); - println!("rust-loop: {:?}", loop_value); - - let db = &mut self.as_mut().get_db(); - let result = update(videos.filter(id.eq(index))) - .set(looping.eq(loop_value)) - .execute(db); - match result { - Ok(_i) => { - for video in self - .as_mut() - .videos_mut() - .iter_mut() - .filter(|x| x.id == index) - { - video.looping = loop_value.clone(); - println!("rust-video: {:?}", video.title); - } - self.as_mut().emit_data_changed( - model_index, - model_index, - &vector_roles, - ); - println!("rust-looping: {:?}", loop_value); - true - } - Err(_e) => false, - } - } - - #[qinvokable] - pub fn update_end_time( - mut self: Pin<&mut Self>, - index: i32, - updated_end_time: f32, - ) -> bool { - let mut vector_roles = QVector_i32::default(); - vector_roles - .append(self.as_ref().get_role(Role::EndTimeRole)); - let model_index = &self.as_ref().index( - index, - 0, - &QModelIndex::default(), - ); - - let db = &mut self.as_mut().get_db(); - let result = update(videos.filter(id.eq(index))) - .set(end_time.eq(updated_end_time)) - .execute(db); - match result { - Ok(_i) => { - for video in self - .as_mut() - .videos_mut() - .iter_mut() - .filter(|x| x.id == index) - { - video.end_time = updated_end_time.clone(); - } - self.as_mut().emit_data_changed( - model_index, - model_index, - &vector_roles, - ); - println!("rust-end-time: {:?}", updated_end_time); - true - } - Err(_e) => false, - } - } - - #[qinvokable] - pub fn update_start_time( - mut self: Pin<&mut Self>, - index: i32, - updated_start_time: f32, - ) -> bool { - let mut vector_roles = QVector_i32::default(); - vector_roles - .append(self.as_ref().get_role(Role::StartTimeRole)); - let model_index = &self.as_ref().index( - index, - 0, - &QModelIndex::default(), - ); - - let db = &mut self.as_mut().get_db(); - let result = update(videos.filter(id.eq(index))) - .set(start_time.eq(updated_start_time)) - .execute(db); - match result { - Ok(_i) => { - for video in self - .as_mut() - .videos_mut() - .iter_mut() - .filter(|x| x.id == index) - { - video.start_time = updated_start_time.clone(); - } - self.as_mut().emit_data_changed( - model_index, - model_index, - &vector_roles, - ); - println!( - "rust-start-time: {:?}", - updated_start_time - ); - true - } - Err(_e) => false, - } - } - - #[qinvokable] - pub fn update_title( - mut self: Pin<&mut Self>, - index: i32, - updated_title: QString, - ) -> bool { - let mut vector_roles = QVector_i32::default(); - vector_roles - .append(self.as_ref().get_role(Role::TitleRole)); - let model_index = &self.as_ref().index( - index, - 0, - &QModelIndex::default(), - ); - - let db = &mut self.as_mut().get_db(); - let result = update(videos.filter(id.eq(index))) - .set(title.eq(updated_title.to_string())) - .execute(db); - match result { - Ok(_i) => { - for video in self - .as_mut() - .videos_mut() - .iter_mut() - .filter(|x| x.id == index) - { - video.title = updated_title.clone(); - println!("rust-title: {:?}", video.title); - } - // TODO this seems to not be updating in the actual list - self.as_mut().emit_data_changed( - model_index, - model_index, - &vector_roles, - ); - // self.as_mut().emit_title_changed(); - println!("rust-title: {:?}", updated_title); - true - } - Err(_e) => false, - } - } - - #[qinvokable] - pub fn update_path( - mut self: Pin<&mut Self>, + fn update_path( + self: Pin<&mut VideoModel>, index: i32, updated_path: QString, - ) -> bool { - let mut vector_roles = QVector_i32::default(); - vector_roles - .append(self.as_ref().get_role(Role::PathRole)); - let model_index = &self.as_ref().index( - index, - 0, - &QModelIndex::default(), - ); - - let db = &mut self.as_mut().get_db(); - let result = update(videos.filter(id.eq(index))) - .set(path.eq(updated_path.to_string())) - .execute(db); - match result { - Ok(_i) => { - for video in self - .as_mut() - .videos_mut() - .iter_mut() - .filter(|x| x.id == index) - { - video.path = updated_path.clone(); - println!("rust-title: {:?}", video.title); - } - self.as_mut().emit_data_changed( - model_index, - model_index, - &vector_roles, - ); - println!("rust-path: {:?}", updated_path); - true - } - Err(_e) => false, - } - } + ) -> bool; + #[qinvokable] + fn get_item( + self: Pin<&mut VideoModel>, + index: i32, + ) -> QMap_QString_QVariant; + #[qinvokable] + fn update_loop( + self: Pin<&mut VideoModel>, + index: i32, + loop_value: bool, + ) -> bool; + #[qinvokable] + fn update_title( + self: Pin<&mut VideoModel>, + index: i32, + updated_title: QString, + ) -> bool; + #[qinvokable] + fn update_start_time( + self: Pin<&mut VideoModel>, + index: i32, + updated_start_time: QString, + ) -> bool; + #[qinvokable] + fn update_end_time( + self: Pin<&mut VideoModel>, + index: i32, + updated_end_time: QString, + ) -> bool; } - // Create Rust bindings for C++ functions of the base class (QAbstractItemModel) - #[cxx_qt::inherit] - extern "C++" { + impl cxx_qt::Threading for VideoModel {} + + unsafe extern "RustQt" { + #[inherit] unsafe fn begin_insert_rows( - self: Pin<&mut qobject::VideoModel>, + self: Pin<&mut VideoModel>, parent: &QModelIndex, first: i32, last: i32, ); - unsafe fn end_insert_rows( - self: Pin<&mut qobject::VideoModel>, - ); + #[inherit] + unsafe fn end_insert_rows(self: Pin<&mut VideoModel>); + + #[inherit] unsafe fn begin_remove_rows( - self: Pin<&mut qobject::VideoModel>, + self: Pin<&mut VideoModel>, parent: &QModelIndex, first: i32, last: i32, ); - unsafe fn end_remove_rows( - self: Pin<&mut qobject::VideoModel>, - ); - unsafe fn begin_reset_model( - self: Pin<&mut qobject::VideoModel>, - ); - unsafe fn end_reset_model( - self: Pin<&mut qobject::VideoModel>, - ); - } + #[inherit] + unsafe fn begin_move_rows( + self: Pin<&mut VideoModel>, + source_parent: &QModelIndex, + source_first: i32, + source_last: i32, + destination_parent: &QModelIndex, + destination_child: i32, + ) -> bool; - #[cxx_qt::inherit] - unsafe extern "C++" { - #[cxx_name = "canFetchMore"] - fn base_can_fetch_more( - self: &qobject::VideoModel, + #[inherit] + unsafe fn end_move_rows(self: Pin<&mut VideoModel>); + + #[inherit] + unsafe fn end_remove_rows(self: Pin<&mut VideoModel>); + + #[inherit] + unsafe fn begin_reset_model(self: Pin<&mut VideoModel>); + + #[inherit] + unsafe fn end_reset_model(self: Pin<&mut VideoModel>); + + #[inherit] + fn can_fetch_more( + self: &VideoModel, parent: &QModelIndex, ) -> bool; + #[inherit] fn index( - self: &qobject::VideoModel, + self: &VideoModel, row: i32, column: i32, parent: &QModelIndex, ) -> QModelIndex; - } - - // QAbstractListModel implementation - impl qobject::VideoModel { - #[qinvokable(cxx_override)] - fn data(&self, index: &QModelIndex, role: i32) -> QVariant { - if let Some(video) = - self.videos().get(index.row() as usize) - { - return match role { - 0 => QVariant::from(&video.id), - 1 => QVariant::from(&video.title), - 2 => QVariant::from(&video.path), - 3 => QVariant::from(&video.start_time), - 4 => QVariant::from(&video.end_time), - 5 => QVariant::from(&video.looping), - _ => QVariant::default(), - }; - } - - QVariant::default() - } - - // Example of overriding a C++ virtual method and calling the base class implementation. - #[qinvokable(cxx_override)] - pub fn can_fetch_more(&self, parent: &QModelIndex) -> bool { - self.base_can_fetch_more(parent) - } - - #[qinvokable(cxx_override)] - pub fn role_names(&self) -> QHash_i32_QByteArray { - let mut roles = QHash_i32_QByteArray::default(); - roles.insert(0, cxx_qt_lib::QByteArray::from("id")); - roles.insert(1, cxx_qt_lib::QByteArray::from("title")); - roles.insert(2, cxx_qt_lib::QByteArray::from("filePath")); - roles - .insert(3, cxx_qt_lib::QByteArray::from("startTime")); - roles.insert(4, cxx_qt_lib::QByteArray::from("endTime")); - roles.insert(5, cxx_qt_lib::QByteArray::from("loop")); - roles - } - - #[qinvokable(cxx_override)] - pub fn row_count(&self, _parent: &QModelIndex) -> i32 { - let cnt = self.rust().videos.len() as i32; - // println!("row count is {cnt}"); - cnt - } #[qinvokable] - pub fn count(&self) -> i32 { - self.rust().videos.len() as i32 + #[cxx_override] + fn data( + self: &VideoModel, + index: &QModelIndex, + role: i32, + ) -> QVariant; + + #[qinvokable] + #[cxx_override] + fn role_names(self: &VideoModel) -> QHash_i32_QByteArray; + + #[qinvokable] + #[cxx_override] + fn row_count(self: &VideoModel, _parent: &QModelIndex) + -> i32; + + #[qinvokable] + fn count(self: &VideoModel) -> i32; + } +} + +use crate::models::*; +use crate::schema::videos::dsl::*; +use crate::video_model::video_model::Video; +use diesel::sqlite::SqliteConnection; +use diesel::{delete, insert_into, prelude::*, update}; +use std::path::{Path, PathBuf}; + +#[derive(Default, Clone, Debug)] +pub struct Video { + id: i32, + title: QString, + path: QString, + start_time: f32, + end_time: f32, + looping: bool, +} + +#[derive(Default, Debug)] +pub struct VideoModelRust { + highest_id: i32, + videos: Vec, +} + +impl qobject::VideoModel { + pub fn clear(mut self: Pin<&mut Self>) { + unsafe { + self.as_mut().begin_reset_model(); + self.as_mut().videos_mut().clear(); + self.as_mut().end_reset_model(); + } + } + + pub fn setup(mut self: Pin<&mut Self>) { + let db = &mut self.as_mut().get_db(); + let results = videos + .load::(db) + .expect("Error loading videos"); + self.as_mut().set_highest_id(0); + + println!("SHOWING VIDEOS"); + println!("--------------"); + for video in results { + println!("{}", video.title); + println!("{}", video.id); + println!("{}", video.path); + println!("--------------"); + if self.as_mut().highest_id() < &video.id { + self.as_mut().set_highest_id(video.id); + } + + let img = self::Video { + id: video.id, + title: QString::from(&video.title), + path: QString::from(&video.path), + start_time: video.start_time.unwrap_or(0.0), + end_time: video.end_time.unwrap_or(0.0), + looping: video.looping, + }; + + self.as_mut().add_video(img); + } + println!("--------------------------------------"); + println!("{:?}", self.as_mut().videos()); + println!("--------------------------------------"); + } + + pub fn remove_item(mut self: Pin<&mut Self>, index: i32) -> bool { + if index < 0 || (index as usize) >= self.videos().len() { + return false; + } + let db = &mut self.as_mut().get_db(); + + let video_id = self.videos().get(index as usize).unwrap().id; + + let result = + delete(videos.filter(id.eq(video_id))).execute(db); + + match result { + Ok(_i) => { + unsafe { + self.as_mut().begin_remove_rows( + &QModelIndex::default(), + index, + index, + ); + self.as_mut().videos_mut().remove(index as usize); + self.as_mut().end_remove_rows(); + } + println!("removed-item-at-index: {:?}", video_id); + println!("new-Vec: {:?}", self.as_mut().videos()); + true + } + Err(_e) => { + println!("Cannot connect to database"); + false + } + } + } + + fn get_db(self: Pin<&mut Self>) -> SqliteConnection { + let mut data = dirs::data_local_dir().unwrap(); + data.push("lumina"); + data.push("library-db.sqlite3"); + let mut db_url = String::from("sqlite://"); + db_url.push_str(data.to_str().unwrap()); + println!("DB: {:?}", db_url); + + SqliteConnection::establish(&db_url).unwrap_or_else(|_| { + panic!("error connecting to {}", db_url) + }) + } + + pub fn new_item(mut self: Pin<&mut Self>, url: QUrl) { + println!("LETS INSERT THIS SUCKER!"); + let file_path = PathBuf::from(url.path().to_string()); + let name = file_path.file_stem().unwrap().to_str().unwrap(); + let video_id = self.rust().highest_id + 1; + let video_title = QString::from(name); + let video_path = url.to_qstring(); + + if self.as_mut().add_item(video_id, video_title, video_path) { + println!("filename: {:?}", name); + self.as_mut().set_highest_id(video_id); + } else { + println!("Error in inserting item"); + } + } + + pub fn add_item( + mut self: Pin<&mut Self>, + video_id: i32, + video_title: QString, + video_path: QString, + ) -> bool { + let db = &mut self.as_mut().get_db(); + // println!("{:?}", db); + let video = self::Video { + id: video_id, + title: video_title.clone(), + path: video_path.clone(), + start_time: 0.0, + end_time: 0.0, + looping: false, + }; + println!("{:?}", video); + + let result = insert_into(videos) + .values(( + id.eq(&video_id), + title.eq(&video_title.to_string()), + path.eq(&video_path.to_string()), + start_time.eq(&video.start_time), + end_time.eq(&video.end_time), + looping.eq(&video.looping), + )) + .execute(db); + println!("{:?}", result); + + match result { + Ok(_i) => { + self.as_mut().add_video(video); + println!("{:?}", self.as_mut().videos()); + true + } + Err(_e) => { + println!( + "Cannot connect to database or there was an error in inserting the video" + ); + false + } + } + } + + fn add_video(mut self: Pin<&mut Self>, video: self::Video) { + let index = self.as_ref().videos().len() as i32; + println!("{:?}", video); + unsafe { + self.as_mut().begin_insert_rows( + &QModelIndex::default(), + index, + index, + ); + self.as_mut().videos_mut().push(video); + self.as_mut().end_insert_rows(); + } + } + + pub fn get_item( + self: Pin<&mut Self>, + index: i32, + ) -> QMap_QString_QVariant { + println!("{index}"); + let mut qvariantmap = QMap_QString_QVariant::default(); + let idx = self.index(index, 0, &QModelIndex::default()); + if !idx.is_valid() { + return qvariantmap; + } + let role_names = self.as_ref().role_names(); + let role_names_iter = role_names.iter(); + if let Some(video) = self.rust().videos.get(index as usize) { + for i in role_names_iter { + qvariantmap.insert( + QString::from(&i.1.to_string()), + self.as_ref().data(&idx, *i.0), + ); + } + println!("gotted-video: {:?}", video); + }; + qvariantmap + } + + fn get_role(&self, role: Role) -> i32 { + match role { + Role::IdRole => 0, + Role::TitleRole => 1, + Role::PathRole => 2, + Role::StartTimeRole => 3, + Role::EndTimeRole => 4, + Role::LoopingRole => 5, + _ => 0, + } + } + + pub fn update_loop( + mut self: Pin<&mut Self>, + index: i32, + loop_value: bool, + ) -> bool { + let mut vector_roles = QVector_i32::default(); + vector_roles + .append(self.as_ref().get_role(Role::LoopingRole)); + let model_index = + &self.as_ref().index(index, 0, &QModelIndex::default()); + println!("rust-video: {:?}", index); + println!("rust-loop: {:?}", loop_value); + + let db = &mut self.as_mut().get_db(); + let result = update(videos.filter(id.eq(index))) + .set(looping.eq(loop_value)) + .execute(db); + match result { + Ok(_i) => { + for video in self + .as_mut() + .videos_mut() + .iter_mut() + .filter(|x| x.id == index) + { + video.looping = loop_value.clone(); + println!("rust-video: {:?}", video.title); + } + self.as_mut().emit_data_changed( + model_index, + model_index, + &vector_roles, + ); + println!("rust-looping: {:?}", loop_value); + true + } + Err(_e) => false, + } + } + + pub fn update_end_time( + mut self: Pin<&mut Self>, + index: i32, + updated_end_time: f32, + ) -> bool { + let mut vector_roles = QVector_i32::default(); + vector_roles + .append(self.as_ref().get_role(Role::EndTimeRole)); + let model_index = + &self.as_ref().index(index, 0, &QModelIndex::default()); + + let db = &mut self.as_mut().get_db(); + let result = update(videos.filter(id.eq(index))) + .set(end_time.eq(updated_end_time)) + .execute(db); + match result { + Ok(_i) => { + for video in self + .as_mut() + .videos_mut() + .iter_mut() + .filter(|x| x.id == index) + { + video.end_time = updated_end_time.clone(); + } + self.as_mut().emit_data_changed( + model_index, + model_index, + &vector_roles, + ); + println!("rust-end-time: {:?}", updated_end_time); + true + } + Err(_e) => false, + } + } + + pub fn update_start_time( + mut self: Pin<&mut Self>, + index: i32, + updated_start_time: f32, + ) -> bool { + let mut vector_roles = QVector_i32::default(); + vector_roles + .append(self.as_ref().get_role(Role::StartTimeRole)); + let model_index = + &self.as_ref().index(index, 0, &QModelIndex::default()); + + let db = &mut self.as_mut().get_db(); + let result = update(videos.filter(id.eq(index))) + .set(start_time.eq(updated_start_time)) + .execute(db); + match result { + Ok(_i) => { + for video in self + .as_mut() + .videos_mut() + .iter_mut() + .filter(|x| x.id == index) + { + video.start_time = updated_start_time.clone(); + } + self.as_mut().emit_data_changed( + model_index, + model_index, + &vector_roles, + ); + println!("rust-start-time: {:?}", updated_start_time); + true + } + Err(_e) => false, + } + } + + pub fn update_title( + mut self: Pin<&mut Self>, + index: i32, + updated_title: QString, + ) -> bool { + let mut vector_roles = QVector_i32::default(); + vector_roles.append(self.as_ref().get_role(Role::TitleRole)); + let model_index = + &self.as_ref().index(index, 0, &QModelIndex::default()); + + let db = &mut self.as_mut().get_db(); + let result = update(videos.filter(id.eq(index))) + .set(title.eq(updated_title.to_string())) + .execute(db); + match result { + Ok(_i) => { + for video in self + .as_mut() + .videos_mut() + .iter_mut() + .filter(|x| x.id == index) + { + video.title = updated_title.clone(); + println!("rust-title: {:?}", video.title); + } + // TODO this seems to not be updating in the actual list + self.as_mut().emit_data_changed( + model_index, + model_index, + &vector_roles, + ); + // self.as_mut().emit_title_changed(); + println!("rust-title: {:?}", updated_title); + true + } + Err(_e) => false, + } + } + + pub fn update_path( + mut self: Pin<&mut Self>, + index: i32, + updated_path: QString, + ) -> bool { + let mut vector_roles = QVector_i32::default(); + vector_roles.append(self.as_ref().get_role(Role::PathRole)); + let model_index = + &self.as_ref().index(index, 0, &QModelIndex::default()); + + let db = &mut self.as_mut().get_db(); + let result = update(videos.filter(id.eq(index))) + .set(path.eq(updated_path.to_string())) + .execute(db); + match result { + Ok(_i) => { + for video in self + .as_mut() + .videos_mut() + .iter_mut() + .filter(|x| x.id == index) + { + video.path = updated_path.clone(); + println!("rust-title: {:?}", video.title); + } + self.as_mut().emit_data_changed( + model_index, + model_index, + &vector_roles, + ); + println!("rust-path: {:?}", updated_path); + true + } + Err(_e) => false, } } } + +// QAbstractListModel implementation +impl qobject::VideoModel { + fn data(&self, index: &QModelIndex, role: i32) -> QVariant { + let role = qobject::Roles { repr: role }; + if let Some(video) = self.videos().get(index.row() as usize) { + return match role { + qobject::Role::Id => QVariant::from(&video.id), + qobject::Role::Title => QVariant::from(&video.title), + qobject::Role::Path => QVariant::from(&video.path), + qobject::Role::StartTime => { + QVariant::from(&video.start_time) + } + qobject::Role::EndTime => { + QVariant::from(&video.end_time) + } + qobject::Role::Looping => { + QVariant::from(&video.looping) + } + _ => QVariant::default(), + }; + } + + QVariant::default() + } + + // Example of overriding a C++ virtual method and calling the base class implementation. + + pub fn can_fetch_more(&self, parent: &QModelIndex) -> bool { + self.base_can_fetch_more(parent) + } + + pub fn role_names(&self) -> QHash_i32_QByteArray { + let mut roles = QHash_i32_QByteArray::default(); + roles.insert( + qobject::Roles::Id.repr, + cxx_qt_lib::QByteArray::from("id"), + ); + roles.insert( + qobject::Roles::Title.repr, + cxx_qt_lib::QByteArray::from("title"), + ); + roles.insert( + qobject::Roles::Path.repr, + cxx_qt_lib::QByteArray::from("filePath"), + ); + roles.insert( + qobject::Roles::StartTime.repr, + cxx_qt_lib::QByteArray::from("startTime"), + ); + roles.insert( + qobject::Roles::EndTime.repr, + cxx_qt_lib::QByteArray::from("endTime"), + ); + roles.insert( + qobject::Roles::Looping.repr, + cxx_qt_lib::QByteArray::from("loop"), + ); + roles + } + + pub fn row_count(&self, _parent: &QModelIndex) -> i32 { + let cnt = self.rust().videos.len() as i32; + // println!("row count is {cnt}"); + cnt + } + + pub fn count(&self) -> i32 { + self.rust().videos.len() as i32 + } +}