diff --git a/TODO.org b/TODO.org index 385bd72..653f411 100644 --- a/TODO.org +++ b/TODO.org @@ -353,5 +353,25 @@ or at least turns the entry above it into the same as itself while retaining it' ** DONE Fix file dialog using basic QT theme [[file:~/dev/church-presenter/src/qml/presenter/SongEditor.qml::FileDialog {]] +* Thoughts +I'm considering porting from using QML and CXX-QT to something like Iced or Slint and thus doing the entire app in a far more "Rust" way. The issues with this include: +- Rewrite almost the entire thing +- Most of the logic is fairly tied to the UI +- Will be using the current state of the app for a long time until it's nearly feature parity -* Inbox +Having rewritten a lot of the logic once already to Rust, I think it'll go faster, but I'm afraid of continuing to have FOMO in picking a library that isn't quite up to snuff. Iced seems to be a good contender to be around for a while since System 76 is taking it on as their library to build COSMIC in. However, I just don't think I like the way you build UI's in Iced. + +Slint on the other hand is basically like QML but for Rust, which is the whole reason this project started. QML and therefore Slint, are perfect for designing UI's. It's a powerful Object Tree that the code will nearly tell you in it's form how the UI should look since objects are nested. Slint also seems a bit more finished. + +Slint is also GPL3. So that means its protected. Good for me, not for others. Iced seems to have more community backing. Slint does have a dual license though and that is a bit scary. + +I think I might just use Slint. Maybe it's easier to contribute to than CXX-QT? + +Let's also list the problems I've had with CXX-QT... +- A lot of types that need to cross the boundary are specific to QT and therefore cause a lot of duplicated memory. +- The specific QT types are different and not "Rust" like. +- It's complicated to inherit from QT types to build models. And that's the only way to work with collections of data in QML. Models. +- Some things listed in the docs just don't seem to compile. +- APIs are changing. They are still trying to land on a model for how things should look to the user of the library. + +Slint and Iced both are a more nailed down API. However, QT is a far more mature system.... diff --git a/src/qml/presenter/MainWindow.qml b/src/qml/presenter/MainWindow.qml index abdb7d1..846d286 100644 --- a/src/qml/presenter/MainWindow.qml +++ b/src/qml/presenter/MainWindow.qml @@ -145,8 +145,8 @@ Controls.Page { FileHelper { id: fileHelper } SlideHelper { id: slideHelper } SongEditor { - id: songEditor - songModel: songProxyModel.songModel() + id: songEditorModel + /* songModel: songProxyModel.songModel() */ } function changeServiceItem(index) { diff --git a/src/qml/presenter/SongEditor.qml b/src/qml/presenter/SongEditor.qml index 23fcf68..6039bc4 100644 --- a/src/qml/presenter/SongEditor.qml +++ b/src/qml/presenter/SongEditor.qml @@ -10,7 +10,7 @@ Item { id: root property int songIndex - property var song + property var song: songEditorModel GridLayout { id: mainLayout @@ -445,8 +445,8 @@ Item { Presenter.SongEditorSlideList { id: songList - imageBackground: song.backgroundType === "image" ? song.background : "" - videoBackground: song.backgroundType === "video" ? song.background : "" + imageBackground: songEditorModel.backgroundType === "image" ? songEditor.background : "" + videoBackground: songEditorModel.backgroundType === "video" ? songEditor.background : "" Layout.preferredWidth: 500 Layout.fillWidth: true Layout.fillHeight: true @@ -471,54 +471,6 @@ Item { } } - FileDialog { - id: audioFileDialog - title: "Please choose an audio file" - folder: shortcuts.home - selectMultiple: false - nameFilters: ["Audio files (*.mp3 *.flac *.wav *.opus *.MP3 *.FLAC *.WAV *.OPUS)"] - onAccepted: { - updateAudioFile(audioFileDialog.fileUrls[0]); - console.log("audio = " + audioFileDialog.fileUrls[0]); - } - onRejected: { - console.log("Canceled") - } - - } - - FileDialog { - id: videoFileDialog - title: "Please choose a background" - folder: shortcuts.home - selectMultiple: false - nameFilters: ["Video files (*.mp4 *.mkv *.mov *.wmv *.avi *.MP4 *.MOV *.MKV)"] - onAccepted: { - updateBackground(videoFileDialog.fileUrls[0], "video"); - console.log("video background = " + videoFileDialog.fileUrls[0]); - } - onRejected: { - console.log("Canceled") - } - - } - - FileDialog { - id: imageFileDialog - title: "Please choose a background" - folder: shortcuts.home - selectMultiple: false - nameFilters: ["Image files (*.jpg *.jpeg *.png *.JPG *.JPEG *.PNG)"] - onAccepted: { - updateBackground(imageFileDialog.fileUrls[0], "image"); - console.log("image background = " + imageFileDialog.fileUrls[0]); - } - onRejected: { - console.log("Canceled") - } - - } - function newSong(index) { clearSlides(); song = songProxyModel.getSong(index); @@ -534,7 +486,20 @@ Item { function changeSong(index) { clearSlides(); - song = songProxyModel.getSong(index); + const updatedSong = songProxyModel.getSong(index); + console.log(updatedSong.verseOrder + " " + updatedSong.title); + songEditorModel.title = updatedSong.title; + songEditorModel.lyrics = updatedSong.lyrics; + songEditorModel.author = updatedSong.author; + songEditorModel.ccli = updatedSong.ccli; + songEditorModel.audio = updatedSong.audio; + songEditorModel.verseOrder = updatedSong.verseOrder; + songEditorModel.background = updatedSong.background; + songEditorModel.backgroundType = updatedSong.backgroundType; + songEditorModel.horizontalTextAlignment = updatedSong.horizontalTextAlignment; + songEditorModel.verticalTextAlignment = updatedSong.verticalTextAlignment; + songEditorModel.font = updatedSong.font; + songEditorModel.fontSize = updatedSong.fontSize; songIndex = song.id; changeSlideHAlignment(song.horizontalTextAlignment); @@ -577,26 +542,17 @@ Item { function updateAudioFile() { const file = fileHelper.loadFile("Pick Audio", "audio"); + songEditorModel.audio = file songProxyModel.songModel.updateAudio(songIndex, file); } function updateBackground(backgroundType) { - song.backgroundType = backgroundType; + songEditorModel.backgroundType = backgroundType; const file = fileHelper.loadFile("Pick Background", backgroundType); - song.background = file; + songEditorModel.background = file; songProxyModel.songModel.updateBackground(songIndex, file); songProxyModel.songModel.updateBackgroundType(songIndex, backgroundType); console.log("changed background"); - /* if (backgroundType === "image") { */ - /* //todo */ - /* songList.videoBackground = ""; */ - /* songList.imageBackground = background; */ - /* } else { */ - /* //todo */ - /* songList.imageBackground = ""; */ - /* songList.videoBackground = background; */ - /* songList.loadVideo(); */ - /* } */ } @@ -674,7 +630,6 @@ Item { } function changeSlideText(id) { - /* console.log("Here are the verses: " + verses); */ const verses = songProxyModel.getLyricList(id); verses.forEach(songList.appendVerse); /* songList.loadVideo(); */ diff --git a/src/rust/songs/song_editor.rs b/src/rust/songs/song_editor.rs index 76df1b0..835dafc 100644 --- a/src/rust/songs/song_editor.rs +++ b/src/rust/songs/song_editor.rs @@ -1,6 +1,5 @@ #[cxx_qt::bridge] -mod song_editor { - use crate::songs::song_model::qobject::SongModel; +pub mod song_editor { unsafe extern "C++" { include!("cxx-qt-lib/qmap.h"); @@ -16,8 +15,8 @@ mod song_editor { type QStringList = cxx_qt_lib::QStringList; include!("cxx-qt-lib/qlist.h"); type QList_QString = cxx_qt_lib::QList; - #[cxx_name = "SongModel"] - type CxxSongs = SongModel; + // #[cxx_name = "SongModel"] + // type CxxSongs = crate::songs::song_model::qobject::SongModel; } #[derive(Clone, Debug)] @@ -49,8 +48,8 @@ mod song_editor { font: QString, #[qproperty] font_size: i32, - #[qproperty] - song_model: *mut CxxSongs, + // #[qproperty] + // song_model: *mut CxxSongs, } impl Default for SongEditor { @@ -69,17 +68,50 @@ mod song_editor { vertical_text_alignment: QString::default(), font: QString::default(), font_size: 50, - song_model: std::ptr::null_mut(), + // song_model: std::ptr::null_mut(), } } } - impl SongEditor { + impl qobject::SongEditor { fn idk(mut self: Pin<&mut Self>) { - let mut model = self.song_model().as_mut().unwrap(); - let pinned_model = Pin::new_unchecked(model); - pinned_model.update_ccli(0, QString::from("idk")); - todo!(); + // let mut model = self.song_model().as_mut().unwrap(); + // let pinned_model = Pin::new_unchecked(model); + // pinned_model.update_ccli(0, QString::from("idk")); + todo!() } + // #[qinvokable] + // fn set_song( + // mut self: Pin<&mut Self>, + // title: QString, + // lyrics: QString, + // author: QString, + // ccli: QString, + // audio: QUrl, + // verse_order: QString, + // background: QUrl, + // background_type: QString, + // horizontal_text_alignment: QString, + // vertical_text_alignment: QString, + // font: QString, + // font_size: i32, + // ) -> bool { + // self.as_mut().set_title(title); + // self.as_mut().set_lyrics(lyrics); + // self.as_mut().set_author(author); + // self.as_mut().set_ccli(ccli); + // self.as_mut().set_audio(audio); + // self.as_mut().set_verse_order(verse_order); + // self.as_mut().set_background(background); + // self.as_mut().set_background_type(background_type); + // self.as_mut().set_horizontal_text_alignment( + // horizontal_text_alignment, + // ); + // self.as_mut() + // .set_vertical_text_alignment(vertical_text_alignment); + // self.as_mut().set_font(font); + // self.as_mut().set_font_size(font_size); + // true + // } } } diff --git a/src/rust/songs/song_model.rs b/src/rust/songs/song_model.rs index dfc2ba3..ac404c6 100644 --- a/src/rust/songs/song_model.rs +++ b/src/rust/songs/song_model.rs @@ -286,11 +286,11 @@ pub mod song_model { .get_mut(index as usize) { song.title = updated_title.to_string(); - self.as_mut().emit(Signals::DataChanged { - top_left: &model_index, - bottom_right: &model_index, - roles: &vector_roles, - }); + self.as_mut().emit_data_changed( + model_index, + model_index, + &vector_roles, + ); self.as_mut().emit_title_changed(); true } else { @@ -793,6 +793,7 @@ pub mod song_model { let role_names_iter = role_names.iter(); if let Some(song) = self.rust().songs.get(index as usize) { + debug!(song); for i in role_names_iter { qvariantmap.insert( QString::from(&i.1.to_string()),