#[cxx_qt::bridge] pub mod image_model { unsafe extern "C++" { include!(< QAbstractListModel >); type QAbstractListModel; include!("cxx-qt-lib/qhash.h"); type QHash_i32_QByteArray = cxx_qt_lib::QHash; include!("cxx-qt-lib/qmap.h"); type QMap_QString_QVariant = cxx_qt_lib::QMap; include!("cxx-qt-lib/qvariant.h"); type QVariant = cxx_qt_lib::QVariant; include!("cxx-qt-lib/qstring.h"); type QString = cxx_qt_lib::QString; include!("cxx-qt-lib/qurl.h"); type QUrl = cxx_qt_lib::QUrl; include!("cxx-qt-lib/qmodelindex.h"); type QModelIndex = cxx_qt_lib::QModelIndex; include!("cxx-qt-lib/qvector.h"); type QVector_i32 = cxx_qt_lib::QVector; // include!("cxx-qt-lib/qstringlist.h"); // type QStringList = cxx_qt_lib::QStringList; // include!("cxx-qt-lib/qlist.h"); // type QList_QString = cxx_qt_lib::QList; } #[qenum(ImageModel)] enum ImageRoles { Id, Path, Title, } #[auto_cxx_name] #[auto_rust_name] unsafe extern "RustQt" { #[qobject] #[base = QAbstractListModel] #[qml_element] #[qproperty(i32, count)] type ImageModel = super::ImageModelRust; #[inherit] #[qsignal] #[cxx_name = "dataChanged"] fn data_changed( self: Pin<&mut ImageModel>, top_left: &QModelIndex, bottom_right: &QModelIndex, roles: &QVector_i32, ); #[qinvokable] fn clear(self: Pin<&mut ImageModel>); #[qinvokable] fn setup(self: Pin<&mut ImageModel>); #[qinvokable] fn remove_item( self: Pin<&mut ImageModel>, index: i32, ) -> bool; #[qinvokable] fn new_item(self: Pin<&mut ImageModel>, url: QUrl); #[qinvokable] fn update_title( self: Pin<&mut ImageModel>, index: i32, updated_title: QString, ) -> bool; #[qinvokable] fn update_file_path( self: Pin<&mut ImageModel>, index: i32, updated_file_path: QString, ) -> bool; #[qinvokable] fn get_item( self: Pin<&mut ImageModel>, index: i32, ) -> QMap_QString_QVariant; #[qinvokable] fn search(self: Pin<&mut ImageModel>, search_term: QString); } impl cxx_qt::Threading for ImageModel {} #[auto_cxx_name] #[auto_rust_name] unsafe extern "RustQt" { #[inherit] #[cxx_name = "beginInsertRows"] unsafe fn begin_insert_rows( self: Pin<&mut ImageModel>, parent: &QModelIndex, first: i32, last: i32, ); #[inherit] #[cxx_name = "endInsertRows"] unsafe fn end_insert_rows(self: Pin<&mut ImageModel>); #[inherit] #[cxx_name = "beginRemoveRows"] unsafe fn begin_remove_rows( self: Pin<&mut ImageModel>, parent: &QModelIndex, first: i32, last: i32, ); #[inherit] #[cxx_name = "beginMoveRows"] 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; #[inherit] #[cxx_name = "endMoveRows"] unsafe fn end_move_rows(self: Pin<&mut ImageModel>); #[inherit] #[cxx_name = "endRemoveRows"] unsafe fn end_remove_rows(self: Pin<&mut ImageModel>); #[inherit] #[cxx_name = "beginResetModel"] unsafe fn begin_reset_model(self: Pin<&mut ImageModel>); #[inherit] #[cxx_name = "endResetModel"] unsafe fn end_reset_model(self: Pin<&mut ImageModel>); #[inherit] #[cxx_name = "canFetchMore"] fn can_fetch_more( self: &ImageModel, parent: &QModelIndex, ) -> bool; #[inherit] fn index( self: &ImageModel, row: i32, column: i32, parent: &QModelIndex, ) -> QModelIndex; #[qinvokable] #[cxx_override] fn data( self: &ImageModel, index: &QModelIndex, role: i32, ) -> QVariant; #[qinvokable] #[cxx_override] #[cxx_name = "roleNames"] fn role_names(self: &ImageModel) -> QHash_i32_QByteArray; #[qinvokable] #[cxx_override] #[cxx_name = "rowCount"] fn row_count(self: &ImageModel, _parent: &QModelIndex) -> i32; } } use cxx_qt::CxxQtType; use cxx_qt_lib::{QModelIndex, QString, QUrl, QVariant}; use sqlx::{Connection, SqliteConnection, query, query_as}; use std::path::PathBuf; use std::pin::Pin; use tracing::{debug, error}; use self::image_model::{ ImageRoles, QHash_i32_QByteArray, QMap_QString_QVariant, QVector_i32, }; #[derive(Default, Clone, Debug)] pub struct Image { id: i32, title: String, pub path: String, } #[derive(Debug)] pub struct ImageModelRust { count: i32, highest_id: i32, images: Vec, inner_images: Vec, db: SqliteConnection, } impl Default for ImageModelRust { fn default() -> Self { Self { count: 0, highest_id: 0, images: vec![], inner_images: vec![], db: { let rt = tokio::runtime::Runtime::new().unwrap(); 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()); rt.block_on(async { SqliteConnection::connect(&db_url) .await .expect("problems") }) }, } } } pub fn get_image(index: i32) -> color_eyre::Result { let rt = tokio::runtime::Runtime::new().unwrap(); let mut db = { 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()); rt.block_on(async { SqliteConnection::connect(&db_url) .await .expect("problems") }) }; rt.block_on(async { let result = query_as!(Image, r#"SELECT id as "id: i32", title as "title!", file_path as "path!" from images where id = ?"#, index).fetch_one(&mut db).await?; Ok(result) }) } impl image_model::ImageModel { pub fn clear(mut self: Pin<&mut Self>) { unsafe { self.as_mut().begin_reset_model(); self.as_mut().rust_mut().images.clear(); self.as_mut().end_reset_model(); } } pub fn setup(mut self: Pin<&mut Self>) { let rt = tokio::runtime::Runtime::new().unwrap(); rt.block_on(async { let result = query_as!(Image, r#"SELECT id as "id: i32", title as "title!", file_path as "path!" from images"#).fetch_all(&mut self.as_mut().rust_mut().db).await; match result { Ok(i) => i.into_iter().for_each(|i| self.as_mut().add_image(i)), Err(e) => error!("There was an error in converting songs: {e}"), } }); } pub fn remove_item(mut self: Pin<&mut Self>, index: i32) -> bool { if index < 0 || (index as usize) >= self.images.len() { return false; } let rt = tokio::runtime::Runtime::new().unwrap(); rt.block_on(async { let result = query!("delete from images where id = ?", index) .execute(&mut self.as_mut().rust_mut().db) .await; match result { Ok(_i) => { unsafe { self.as_mut().begin_remove_rows( &QModelIndex::default(), index, index, ); self.as_mut() .rust_mut() .images .remove(index as usize); self.as_mut() .rust_mut() .inner_images .remove(index as usize); self.as_mut().end_remove_rows(); } debug!("removed-item-at-index: {:?}", index); true } Err(e) => { error!("Cannot connect to database: {e}"); false } } }); true } 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().rust_mut().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 rt = tokio::runtime::Runtime::new().unwrap(); rt.block_on(async { let image_title = image_title.to_string(); let image_path = image_path.to_string(); let result = query!(r#"INSERT into images (id, title, file_path) VALUES (?, ?, ?)"#, image_id, image_title, image_path) .execute(&mut self.as_mut().rust_mut().db).await; match result { Ok(_i) => { let image = Image { id: image_id, title: image_title.to_string(), path: image_path.to_string(), }; self.as_mut().add_image(image); debug!("{:?}", self.as_mut().images); true } Err(e) => { error!( "Cannot connect to database: {e}" ); false } } }); true } fn add_image(mut self: Pin<&mut Self>, image: self::Image) { let index = self.as_ref().images.len() as i32; println!("{:?}", image); let count = self.as_ref().count; self.as_mut().set_count(count + 1); unsafe { self.as_mut().begin_insert_rows( &QModelIndex::default(), index, index, ); self.as_mut().rust_mut().images.push(image.clone()); self.as_mut().rust_mut().inner_images.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(ImageRoles::Title)); let model_index = &self.as_ref().index(index, 0, &QModelIndex::default()); let rt = tokio::runtime::Runtime::new().unwrap(); rt.block_on(async { let title = updated_title.to_string(); let result = query!( "UPDATE images SET title = ? where id = ?", title, index ) .execute(&mut self.as_mut().rust_mut().db) .await; match result { Ok(_i) => { for image in self .as_mut() .rust_mut() .images .iter_mut() .filter(|x| x.id == index) { image.title = title.clone(); debug!( title = image.title, title = title, "updated image title" ); } self.as_mut().data_changed( model_index, model_index, &vector_roles, ); true } Err(e) => { error!("Error connecting to db: {e}"); false } } }); true } pub fn update_file_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_id(ImageRoles::Path)); let model_index = &self.as_ref().index(index, 0, &QModelIndex::default()); let rt = tokio::runtime::Runtime::new().unwrap(); rt.block_on(async { let updated_path = updated_path.to_string(); let result = query!( "UPDATE images SET file_path = ? where id = ?", updated_path, index ) .execute(&mut self.as_mut().rust_mut().db) .await; match result { Ok(_i) => { for image in self .as_mut() .rust_mut() .images .iter_mut() .filter(|x| x.id == index) { image.path = updated_path.clone(); debug!( title = image.title, path = updated_path, "updated image path" ); } self.as_mut().data_changed( model_index, model_index, &vector_roles, ); true } Err(e) => { error!("Error connecting to db: {e}"); false } } }); true } 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 search(mut self: Pin<&mut Self>, search_term: QString) { let search_term = search_term.to_string().to_lowercase(); debug!(search_term); let searched_images: Vec = self .inner_images .iter() .filter(|image| { image.title.to_lowercase().contains(&search_term) }) .cloned() .collect(); debug!(search = ?&searched_images); unsafe { self.as_mut().begin_reset_model(); self.as_mut().rust_mut().images = searched_images; self.as_mut().end_reset_model(); } } fn get_role_id(&self, role: ImageRoles) -> i32 { match role { ImageRoles::Id => 0, ImageRoles::Title => 1, ImageRoles::Path => 2, _ => 0, } } } // QAbstractListModel implementation impl image_model::ImageModel { fn data(&self, index: &QModelIndex, role: i32) -> QVariant { let role = ImageRoles { repr: role }; if let Some(image) = self.images.get(index.row() as usize) { return match role { ImageRoles::Id => QVariant::from(&image.id), ImageRoles::Title => { QVariant::from(&QString::from(&image.title)) } ImageRoles::Path => { QVariant::from(&QString::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( ImageRoles::Id.repr, cxx_qt_lib::QByteArray::from("id"), ); roles.insert( ImageRoles::Title.repr, cxx_qt_lib::QByteArray::from("title"), ); roles.insert( ImageRoles::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 } }