diff --git a/crates/cxx-qt-lib-extras-headers/include/gui/qapplication.h b/crates/cxx-qt-lib-extras-headers/include/gui/qapplication.h new file mode 100644 index 000000000..ec457569a --- /dev/null +++ b/crates/cxx-qt-lib-extras-headers/include/gui/qapplication.h @@ -0,0 +1,29 @@ +// clang-format off +// SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company +// clang-format on +// SPDX-FileContributor: Laurent Montel +// +// SPDX-License-Identifier: MIT OR Apache-2.0 +#pragma once + +#include + +#include +#include + +#include "rust/cxx.h" + +namespace rust { +namespace cxxqtlib1 { + +::std::unique_ptr +qapplicationNew(const QVector& args); + +void +qapplicationSetFont(QApplication& app, const QFont& font); + +QFont +qapplicationFont(const QApplication& app); + +} +} diff --git a/crates/cxx-qt-lib-extras-headers/src/lib.rs b/crates/cxx-qt-lib-extras-headers/src/lib.rs index bacaa1159..b6f50dba2 100644 --- a/crates/cxx-qt-lib-extras-headers/src/lib.rs +++ b/crates/cxx-qt-lib-extras-headers/src/lib.rs @@ -14,6 +14,10 @@ pub fn build_opts() -> cxx_qt_build::CxxQtBuildersOpts { include_str!("../include/core/qelapsedtimer.h"), "qelapsedtimer.h", ), + ( + include_str!("../include/gui/qapplication.h"), + "qapplication.h", + ), ( include_str!("../include/core/qcommandlineparser.h"), "qcommandlineparser.h", @@ -26,5 +30,5 @@ pub fn build_opts() -> cxx_qt_build::CxxQtBuildersOpts { opts = opts.header(file_contents, "cxx-qt-lib-extras", file_name); } - opts + opts.qt_module("Gui").qt_module("Widgets") } diff --git a/crates/cxx-qt-lib-extras/build.rs b/crates/cxx-qt-lib-extras/build.rs index 45a4c7efe..7e6b9c6aa 100644 --- a/crates/cxx-qt-lib-extras/build.rs +++ b/crates/cxx-qt-lib-extras/build.rs @@ -12,6 +12,7 @@ fn main() { "core/qelapsedtimer", "core/qcommandlineoption", "core/qcommandlineparser", + "gui/qapplication", ]; for rust_source in &rust_bridges { @@ -22,6 +23,7 @@ fn main() { "core/qelapsedtimer", "core/qcommandlineoption", "core/qcommandlineparser", + "gui/qapplication", ]; builder = builder.cc_builder(move |cc| { diff --git a/crates/cxx-qt-lib-extras/src/gui/qapplication.cpp b/crates/cxx-qt-lib-extras/src/gui/qapplication.cpp new file mode 100644 index 000000000..6a64ad836 --- /dev/null +++ b/crates/cxx-qt-lib-extras/src/gui/qapplication.cpp @@ -0,0 +1,44 @@ +// clang-format off +// SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company +// clang-format on +// SPDX-FileContributor: Laurent Montel +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +#include "cxx-qt-lib-extras/qapplication.h" + +#include "cxx-qt-lib/qcoreapplication.h" + +namespace rust { +namespace cxxqtlib1 { + +::std::unique_ptr +qapplicationNew(const QVector& args) +{ + // Ensure that our QVector has the same lifetime as the QApplication + // by storing it inside a QObject that has QApplication as it's parent + auto argsData = new ApplicationArgsData(args); + // Note that QApplication uses a reference to an int for the size here + // so we need to ensure that reference remains valid + auto ptr = + ::std::make_unique(argsData->size(), argsData->data()); + Q_ASSERT(ptr != nullptr); + argsData->setParent(ptr.get()); + + return ptr; +} + +void +qapplicationSetFont(QApplication& app, const QFont& font) +{ + app.setFont(font); +} + +QFont +qapplicationFont(const QApplication& app) +{ + return app.font(); +} + +} +} diff --git a/crates/cxx-qt-lib-extras/src/gui/qapplication.rs b/crates/cxx-qt-lib-extras/src/gui/qapplication.rs new file mode 100644 index 000000000..c63d1e089 --- /dev/null +++ b/crates/cxx-qt-lib-extras/src/gui/qapplication.rs @@ -0,0 +1,200 @@ +// SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company +// SPDX-FileContributor: Laurent Montel +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use crate::{QByteArray, QFont, QString, QStringList, QVector}; +use core::pin::Pin; + +#[cxx::bridge] +mod ffi { + unsafe extern "C++" { + include!("cxx-qt-lib/qbytearray.h"); + type QByteArray = crate::QByteArray; + include!("cxx-qt-lib/qstring.h"); + type QString = crate::QString; + include!("cxx-qt-lib/qstringlist.h"); + type QStringList = crate::QStringList; + include!("cxx-qt-lib/qvector.h"); + type QVector_QByteArray = crate::QVector; + include!("cxx-qt-lib/qfont.h"); + type QFont = crate::QFont; + + include!("cxx-qt-lib-extras/qapplication.h"); + type QApplication; + } + + #[namespace = "rust::cxxqtlib1"] + unsafe extern "C++" { + #[doc(hidden)] + #[rust_name = "qpplication_new"] + fn qapplicationNew(args: &QVector_QByteArray) -> UniquePtr; + } + + // These are all static, so we need to create bindings until CXX supports statics + #[namespace = "rust::cxxqtlib1"] + unsafe extern "C++" { + // Reuse the templated versions from QCoreApplication + include!("cxx-qt-lib/qcoreapplication.h"); + + #[doc(hidden)] + #[rust_name = "qapplication_add_library_path"] + fn qapplicationAddLibraryPath(app: Pin<&mut QApplication>, path: &QString); + #[doc(hidden)] + #[rust_name = "qapplication_application_name"] + fn qapplicationApplicationName(app: &QApplication) -> QString; + #[doc(hidden)] + #[rust_name = "qapplication_remove_library_path"] + fn qapplicationRemoveLibraryPath(app: &QApplication, path: &QString); + #[doc(hidden)] + #[rust_name = "qapplication_application_version"] + fn qapplicationApplicationVersion(app: &QApplication) -> QString; + #[doc(hidden)] + #[rust_name = "qapplication_exec"] + fn qapplicationExec(app: Pin<&mut QApplication>) -> i32; + #[doc(hidden)] + #[rust_name = "qapplication_library_paths"] + fn qapplicationLibraryPaths(app: &QApplication) -> QStringList; + #[doc(hidden)] + #[rust_name = "qapplication_organization_domain"] + fn qapplicationOrganizationDomain(app: &QApplication) -> QString; + #[doc(hidden)] + #[rust_name = "qapplication_organization_name"] + fn qapplicationOrganizationName(app: &QApplication) -> QString; + #[doc(hidden)] + #[rust_name = "qapplication_set_application_name"] + fn qapplicationSetApplicationName(app: Pin<&mut QApplication>, name: &QString); + #[doc(hidden)] + #[rust_name = "qapplication_set_application_version"] + fn qapplicationSetApplicationVersion(app: Pin<&mut QApplication>, version: &QString); + #[doc(hidden)] + #[rust_name = "qapplication_set_font"] + fn qapplicationSetFont(app: Pin<&mut QApplication>, font: &QFont); + #[doc(hidden)] + #[rust_name = "qapplication_font"] + fn qapplicationFont(app: &QApplication) -> QFont; + #[doc(hidden)] + #[rust_name = "qapplication_set_library_paths"] + fn qapplicationSetLibraryPaths(app: Pin<&mut QApplication>, paths: &QStringList); + #[doc(hidden)] + #[rust_name = "qapplication_set_organization_domain"] + fn qapplicationSetOrganizationDomain(app: Pin<&mut QApplication>, domain: &QString); + #[doc(hidden)] + #[rust_name = "qapplication_set_organization_name"] + fn qapplicationSetOrganizationName(app: Pin<&mut QApplication>, name: &QString); + } + + // QApplication is not a trivial to CXX and is not relocatable in Qt + // as the following fails in C++. So we cannot mark it as a trivial type + // and need to use references or pointers. + // static_assert(QTypeInfo::isRelocatable); + impl UniquePtr {} +} + +pub use ffi::QApplication; + +impl QApplication { + /// Prepends path to the beginning of the library path list, + /// ensuring that it is searched for libraries first. + /// If path is empty or already in the path list, the path list is not changed. + pub fn add_library_path(self: Pin<&mut Self>, path: &QString) { + ffi::qapplication_add_library_path(self, path); + } + + /// The name of this application + pub fn application_name(&self) -> QString { + ffi::qapplication_application_name(self) + } + + /// The version of this application + pub fn application_version(&self) -> QString { + ffi::qapplication_application_version(self) + } + + /// Enters the main event loop and waits until exit() is called, + /// and then returns the value that was set to exit() (which is 0 if exit() is called via quit()). + pub fn exec(self: Pin<&mut Self>) -> i32 { + ffi::qapplication_exec(self) + } + + /// Returns the default application font. + pub fn font(&self) -> QFont { + ffi::qapplication_font(self) + } + + /// Returns a list of paths that the application will search when dynamically loading libraries. + pub fn library_paths(&self) -> QStringList { + ffi::qapplication_library_paths(self) + } + + /// Initializes the window system and constructs an application object. + /// Standard [Qt command line arguments](https://doc.qt.io/qt-6/qapplication.html#supported-command-line-options) are handled automatically. + pub fn new() -> cxx::UniquePtr { + let mut vector = QVector::::default(); + + // Construct an owned QVector of the args + // as we need the args_os data to outlive this method + // so we pass a QVector to C++ which is then stored + for arg in std::env::args_os() { + // Unix OsStrings can be directly converted to bytes. + #[cfg(unix)] + use std::os::unix::ffi::OsStrExt; + + // Windows OsStrings are WTF-8 encoded, so they need to be + // converted to UTF-8 Strings before being converted to bytes. + // https://simonsapin.github.io/wtf-8/ + #[cfg(windows)] + let arg = arg.to_string_lossy(); + + vector.append(QByteArray::from(arg.as_bytes())); + } + + ffi::qapplication_new(&vector) + } + + /// The Internet domain of the organization that wrote this application + pub fn organization_domain(&self) -> QString { + ffi::qapplication_organization_domain(self) + } + + /// The name of the organization that wrote this application + pub fn organization_name(&self) -> QString { + ffi::qapplication_organization_name(self) + } + + /// Set the name of this application + pub fn set_application_name(self: Pin<&mut Self>, name: &QString) { + ffi::qapplication_set_application_name(self, name); + } + + /// Removes path from the library path list. If path is empty or not in the path list, the list is not changed. + pub fn remove_library_path(&self, path: &QString) { + ffi::qapplication_remove_library_path(self, path) + } + + /// Set the version of this application + pub fn set_application_version(self: Pin<&mut Self>, version: &QString) { + ffi::qapplication_set_application_version(self, version); + } + + /// Changes the default application font to font. + pub fn set_application_font(self: Pin<&mut Self>, font: &QFont) { + ffi::qapplication_set_font(self, font); + } + + /// Sets the list of directories to search when loading plugins with QLibrary to paths. + /// All existing paths will be deleted and the path list will consist of the paths given in paths and the path to the application. + pub fn set_library_paths(self: Pin<&mut Self>, paths: &QStringList) { + ffi::qapplication_set_library_paths(self, paths); + } + + /// Sets the Internet domain of the organization that wrote this application + pub fn set_organization_domain(self: Pin<&mut Self>, domain: &QString) { + ffi::qapplication_set_organization_domain(self, domain); + } + + /// Sets the name of the organization that wrote this application + pub fn set_organization_name(self: Pin<&mut Self>, name: &QString) { + ffi::qapplication_set_organization_name(self, name); + } +}