diff --git a/.envrc b/.envrc index 476cb0a..6c388c7 100644 --- a/.envrc +++ b/.envrc @@ -1,2 +1,4 @@ export NIXPKGS_ALLOW_INSECURE=1 +export QT_AUTO_SCREEN_SCALE_FACTOR=1 +export QT_SCALE_FACTOR=0.75 use flake . --impure diff --git a/build.rs b/build.rs index 49a6caf..39d94bd 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,9 @@ use cxx_qt_build::CxxQtBuilder; fn main() { - CxxQtBuilder::new().file("src/rust/cxxqt_object.rs").build(); - CxxQtBuilder::new().file("src/rust/service_thing.rs").build(); + // CxxQtBuilder::new().file("src/rust/my_object.rs").build(); + CxxQtBuilder::new() + .file("src/rust/service_thing.rs") + .file("src/rust/file_helper.rs") + .build(); } diff --git a/flake.nix b/flake.nix index 782fb65..6392bc5 100644 --- a/flake.nix +++ b/flake.nix @@ -18,15 +18,15 @@ src = ./.; rustPkgs = pkgs.rustBuilder.makePackageSet { rustVersion = "1.61.0"; - packageFun = import ./cargo.nix; + packageFun = import ./Cargo.nix; }; in rec { - # packages = { - # crate = (rustPkgs.workspace.qml-minimal { }).bin; - # default = packages.crate; - # }; + packages = { + crate = (rustPkgs.workspace.libre-presenter { }).bin; + default = packages.crate; + }; devShell = import ./shell.nix { inherit pkgs; }; defaultPackage = pkgs.libsForQt5.callPackage ./default.nix { inherit rustPkgs; }; } diff --git a/src/main.cpp b/src/main.cpp index ddaf219..8cbb1b4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -44,8 +44,9 @@ #include "cpp/slide.h" // RUST -#include "cxx-qt-gen/my_object.cxxqt.h" +// #include "cxx-qt-gen/my_object.cxxqt.h" #include "cxx-qt-gen/service_thing.cxxqt.h" +#include "cxx-qt-gen/file_helper.cxxqt.h" static void connectToDatabase() { // let's setup our sql database @@ -97,7 +98,7 @@ int main(int argc, char *argv[]) QCoreApplication::setOrganizationName(QStringLiteral("librepresenter")); QCoreApplication::setOrganizationDomain(QStringLiteral("tfcconnection.org")); QCoreApplication::setApplicationName(QStringLiteral("Libre Presenter")); - qSetMessagePattern("[%{type} %{time h:m:s ap}: %{function} in %{file}]: %{message}\n"); + // qSetMessagePattern("[%{type} %{time h:m:s ap}: %{function} in %{file}]: %{message}\n"); #ifdef Q_OS_WINDOWS QIcon::setFallbackThemeName("breeze"); @@ -132,7 +133,7 @@ int main(int argc, char *argv[]) qmlRegisterType("org.presenter", 1, 0, "ImageSqlModel"); qmlRegisterType("org.presenter", 1, 0, "PresentationSqlModel"); qmlRegisterType("org.presenter", 1, 0, "ServiceItemModel"); - qmlRegisterType("org.presenter", 1, 0, "MyObject"); + qmlRegisterType("org.presenter", 1, 0, "FileHelper"); qmlRegisterType("org.presenter", 1, 0, "ServiceThing"); qmlRegisterSingletonInstance("org.presenter", 1, 0, "SlideObject", slide.get()); qmlRegisterSingletonInstance("org.presenter", 1, 0, "FileManager", filemanager.get()); diff --git a/src/qml/main.qml b/src/qml/main.qml index 4da9bd5..c1313ee 100644 --- a/src/qml/main.qml +++ b/src/qml/main.qml @@ -129,6 +129,10 @@ Kirigami.ApplicationWindow { } } + FileHelper { + id: fileHelper + } + FileDialog { id: loadFileDialog title: "Load" @@ -180,12 +184,6 @@ Kirigami.ApplicationWindow { /* print(loaded[0].audio); */ } - MyObject { - id: myObject - number: 7 - string: "HI from rust in my proj: " + myObject.number - } - Component.onCompleted: { /* showPassiveNotification(Kirigami.Settings.style); */ /* Kirigami.Settings.style = "Plasma"; */ diff --git a/src/qml/presenter/MainWindow.qml b/src/qml/presenter/MainWindow.qml index 4824dc8..385c03b 100644 --- a/src/qml/presenter/MainWindow.qml +++ b/src/qml/presenter/MainWindow.qml @@ -149,6 +149,10 @@ Controls.Page { id: serviceItemModel } + ServiceThing { + id: serviceThing + } + function changeServiceItem(index) { const item = serviceItemModel.getItem(index); currentServiceItem = index; diff --git a/src/qml/presenter/Presentation.qml b/src/qml/presenter/Presentation.qml index ee6a4f2..1581a72 100644 --- a/src/qml/presenter/Presentation.qml +++ b/src/qml/presenter/Presentation.qml @@ -45,19 +45,19 @@ FocusScope { text: "Solo" icon.name: "viewimage" hoverEnabled: true - onClicked: myObject.slapVariantAround(imagesqlmodel.getImage(1).title); + onClicked: serviceThing.slapVariantAround(imagesqlmodel.getImage(1).title); } Controls.ToolButton { text: "Grid" icon.name: "view-app-grid-symbolic" hoverEnabled: true - onClicked: myObject.sayHi(myObject.string, myObject.number); + onClicked: serviceThing.checkActive(); } Controls.ToolButton { text: "Details" icon.name: "view-list-details" hoverEnabled: true - onClicked: showPassiveNotification(myObject.string); + onClicked: serviceThing.activate(); } Controls.ToolSeparator {} Item { Layout.fillWidth: true } diff --git a/src/rust/file_helper.rs b/src/rust/file_helper.rs new file mode 100644 index 0000000..fed85e1 --- /dev/null +++ b/src/rust/file_helper.rs @@ -0,0 +1,45 @@ +#[cxx_qt::bridge] +mod file_helper { + use cxx::{CxxString, CxxVector}; + unsafe extern "C++" { + 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/qvariant.h"); + type QVariant = cxx_qt_lib::QVariant; + } + + #[derive(Clone)] + #[cxx_qt::qobject] + pub struct FileHelper { + #[qproperty] + name: QString, + #[qproperty] + file_path: QString, + } + + impl Default for FileHelper { + fn default() -> Self { + Self { + name: QString::from(""), + file_path: QString::from(""), + } + } + } + + impl qobject::FileHelper { + #[qinvokable] + pub fn save(self: Pin<&mut Self>, file: QUrl, _service_list: QVariant) -> bool { + println!("{}", file); + return true; + } + + #[qinvokable] + pub fn load(self: Pin<&mut Self>, file: QUrl) -> Vec { + println!("{}", file); + let vc = vec![String::new()]; + return vc; + } + } +} diff --git a/src/rust/lib.rs b/src/rust/lib.rs index 380ac19..bcffb5b 100644 --- a/src/rust/lib.rs +++ b/src/rust/lib.rs @@ -13,5 +13,6 @@ mod tests { } } -mod cxxqt_object; +// mod my_object; +mod file_helper; mod service_thing; diff --git a/src/rust/cxxqt_object.rs b/src/rust/my_object.rs similarity index 100% rename from src/rust/cxxqt_object.rs rename to src/rust/my_object.rs diff --git a/src/rust/service_item_model.rs b/src/rust/service_item_model.rs new file mode 100644 index 0000000..1c02e3f --- /dev/null +++ b/src/rust/service_item_model.rs @@ -0,0 +1,142 @@ +use cxx::{type_id, ExternType}; +use std::mem::MaybeUninit; + +// Define a QModelIndex that is trivial for CXX +// +// TODO: later this will likely be in cxx-qt-lib +#[repr(C)] +pub struct QModelIndex { + _space: MaybeUninit<[usize; 3]>, +} + +// Safety: +// +// Static checks on the C++ side to ensure the size is the same. +// TODO: later this will likely be in cxx-qt-lib +unsafe impl ExternType for QModelIndex { + type Id = type_id!("QModelIndex"); + type Kind = cxx::kind::Trivial; +} + +// ANCHOR: book_macro_code +#[cxx_qt::bridge(cxx_file_stem = "service_item_model")] +mod service_item_model { + // ANCHOR: book_base_include + unsafe extern "C++" { + include!("serviceitemmodel.h"); + // ANCHOR_END: book_base_include + + include!("cxx-qt-lib/qvariant.h"); + type QVariant = cxx_qt_lib::QVariant; + + // Define the interface of the QModelIndex + type QModelIndex = super::QModelIndex; + fn row(self: &QModelIndex) -> i32; + + #[cxx_name = "beginInsertRows"] + fn begin_insert_rows(self: Pin<&mut CustomBaseClassQt>, first: i32, last: i32); + #[cxx_name = "endInsertRows"] + fn end_insert_rows(self: Pin<&mut CustomBaseClassQt>); + + #[cxx_name = "beginRemoveRows"] + fn begin_remove_rows(self: Pin<&mut CustomBaseClassQt>, first: i32, last: i32); + #[cxx_name = "endRemoveRows"] + fn end_remove_rows(self: Pin<&mut CustomBaseClassQt>); + + #[cxx_name = "beginResetModel"] + fn begin_reset_model(self: Pin<&mut CustomBaseClassQt>); + #[cxx_name = "endResetModel"] + fn end_reset_model(self: Pin<&mut CustomBaseClassQt>); + } + + // ANCHOR: book_qobject_base + #[cxx_qt::qobject(base = "ServiceItemModel")] + #[derive(Default)] + pub struct ServiceItemModel { + // ANCHOR_END: book_qobject_base + id: u32, + vector: Vec<(u32, f64)>, + } + + impl qobject::ServiceItemModel { + #[qinvokable] + pub fn add(self: Pin<&mut Self>) { + self.add_cpp_context(); + } + + #[qinvokable] + pub fn add_on_thread(self: Pin<&mut Self>, mut counter: i32) { + let qt_thread = self.qt_thread(); + + std::thread::spawn(move || { + while counter > 0 { + counter -= 1; + std::thread::sleep(std::time::Duration::from_millis(250)); + + // Use our add helper to add a row on the Qt event loop + // as seen in the threading demo channels could be used to pass info + qt_thread + .queue(|custom_base_class| { + custom_base_class.add_cpp_context(); + }) + .unwrap(); + } + }); + } + + fn add_cpp_context(mut self: Pin<&mut Self>) { + let count = self.vector().len(); + self.as_mut().begin_insert_rows(count as i32, count as i32); + let id = *self.id(); + self.as_mut().set_id(id + 1); + self.as_mut().vector_mut().push((id, (id as f64) / 3.0)); + self.as_mut().end_insert_rows(); + } + + #[qinvokable] + pub fn clear(mut self: Pin<&mut Self>) { + self.as_mut().begin_reset_model(); + self.as_mut().set_id(0); + self.as_mut().vector_mut().clear(); + self.as_mut().end_reset_model(); + } + + #[qinvokable] + pub fn remove(mut self: Pin<&mut Self>, index: i32) { + if index < 0 || (index as usize) >= self.vector().len() { + return; + } + + self.as_mut().begin_remove_rows(index, index); + self.as_mut().vector_mut().remove(index as usize); + self.as_mut().end_remove_rows(); + } + } + + // QAbstractListModel implementation + impl qobject::ServiceItemModel { + #[qinvokable(cxx_override)] + fn data(&self, index: &QModelIndex, role: i32) -> QVariant { + if let Some((id, value)) = self.rust().vector.get(index.row() as usize) { + return match role { + 0 => QVariant::from(*id), + 1 => QVariant::from(*value), + _ => QVariant::default(), + }; + } + + QVariant::default() + } + + #[qinvokable(cxx_override)] + pub fn role_names_as_vec(&self) -> Vec { + vec!["id".to_owned(), "value".to_owned()] + } + + #[qinvokable(cxx_override)] + pub fn row_count(&self, _parent: &QModelIndex) -> i32 { + self.rust().vector.len() as i32 + } + } +} +// ANCHOR_END: book_macro_code diff --git a/src/rust/service_thing.rs b/src/rust/service_thing.rs index a7b2d6f..e2b3935 100644 --- a/src/rust/service_thing.rs +++ b/src/rust/service_thing.rs @@ -9,6 +9,7 @@ mod service_thing { type QVariant = cxx_qt_lib::QVariant; } + #[derive(Clone)] #[cxx_qt::qobject] pub struct ServiceThing { #[qproperty] @@ -53,28 +54,30 @@ mod service_thing { impl qobject::ServiceThing { #[qinvokable] pub fn activate(self: Pin<&mut Self>) { - self.set_active(true); + println!("{}", self.active()); + let active: bool = *self.active(); + self.set_active(!active); + println!("{}", !active); } #[qinvokable] - pub fn say_hi(self: Pin<&mut Self>, string: &QString, number: i32) { - println!( - "Hi from Rust! String is '{}' and number is {}", - string, number - ); - println!("length is: {}", string.to_string().len()); - let mut nstr: String = string.to_string(); - nstr.push_str(" hi"); - self.set_name(QString::from(nstr.as_str())); + pub fn check_active(self: Pin<&mut Self>) { + println!("Are we active?: {}", self.active()); } #[qinvokable] pub fn slap_variant_around(self: Pin<&mut Self>, variant: &QVariant) { println!("wow!"); + let sname: String; match variant.value() { - QVariantValue::QString(string) => self.set_name(string), + QVariantValue::QString(string) => { + let nstr = string.to_string(); + self.set_name(QString::from(nstr.as_str())); + sname = nstr; + println!("New name is: {}", sname); + } _ => println!("Unknown QVariant type"), - } + }; } } }