Skip to main content

qtbridge_runtime/
qapp.rs

1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only
3
4use cxx::UniquePtr;
5use qtbridge_type_lib::{QGuiApplication, QQmlApplicationEngine, QString, QVariant, QVariantMap};
6use crate::qml_register::QmlRegister;
7
8/// Entry point for a Qt QML application.
9///
10/// Wraps the Qt application and QML engine. Configure it with the builder
11/// methods and call [`run`](QApp::run) to start the event loop.
12///
13/// # Example
14///
15/// A minimal “Hello World” application without a Rust backend:
16///
17/// ```rust
18///# use qtbridge_runtime::QApp;
19/// QApp::new()
20///     .load_qml(br#"
21///         import QtQuick
22///         import QtQuick.Controls
23///         Text {
24///             text: "Hello Rust!"
25///#            Component.onCompleted: closeTimer.start()
26///#            Timer {
27///#                id: closeTimer
28///#                interval: 1
29///#                onTriggered: Qt.quit()
30///#            }
31///         }"#)
32///     .run();
33/// ```
34pub struct QApp {
35    engine: UniquePtr<QQmlApplicationEngine>, // engine must be first field so its dropped before app
36    #[allow(dead_code)]
37    app: UniquePtr<QGuiApplication>,
38    initial_properties: QVariantMap,
39}
40
41impl QApp {
42    /// Creates the Qt application and QML engine.
43    ///
44    /// Must be called before any QML or GUI functionality is used.
45    pub fn new() -> Self {
46        let app = QGuiApplication::new();
47        let engine = QQmlApplicationEngine::new();
48        Self {
49            engine: engine,
50            app: app,
51            initial_properties: QVariantMap::default(),
52        }
53    }
54
55    /// Enters the Qt main event loop.
56    ///
57    /// Blocks until the application exits and returns the exit code.
58    /// Usually the last call in `main`.
59    pub fn run(&mut self) -> i32 {
60        QGuiApplication::exec()
61    }
62
63    /// Queues an initial property to be set on the root QML object.
64    ///
65    /// Properties are applied when [`load_qml`](QApp::load_qml) or
66    /// [`load_qml_from_file`](QApp::load_qml_from_file) is called.
67    /// Call multiple times to set several properties.
68    ///
69    /// # Example
70    ///
71    /// ```rust
72    ///# use qtbridge_runtime::QApp;
73    /// let prop = 42;
74    ///
75    /// QApp::new()
76    /// .add_initial_property("answer", &prop.into())
77    /// .load_qml(br#"
78    ///     import QtQuick
79    ///     import QtQuick.Controls
80    ///     ApplicationWindow {
81    ///         required property var answer
82    ///#        Component.onCompleted: closeTimer.start()
83    ///#        Timer {
84    ///#            id: closeTimer
85    ///#            interval: 1
86    ///#            onTriggered: Qt.quit()
87    ///#        }
88    ///     }"#)
89    /// .run();
90    /// ```
91    pub fn add_initial_property(&mut self, id: &str, value: &QVariant) -> &mut Self {
92        self.initial_properties.insert(&QString::from(id), value);
93        self
94    }
95
96    /// Sets multiple initial properties on the root QML object at once.
97    ///
98    /// Must be called before [`load_qml`](QApp::load_qml) or
99    /// [`load_qml_from_file`](QApp::load_qml_from_file).
100    ///
101    /// # Example
102    ///
103    /// ```rust
104    ///# use qtbridge_runtime::QApp;
105    /// let prop = 42;
106    ///
107    /// QApp::new()
108    ///     .with_initial_properties(&[
109    ///         ("answer", prop.into()),
110    ///     ])
111    ///     .load_qml(br#"
112    ///         import QtQuick
113    ///         import QtQuick.Controls
114    ///         ApplicationWindow {
115    ///             required property var answer
116    ///#            Component.onCompleted: closeTimer.start()
117    ///#            Timer {
118    ///#                id: closeTimer
119    ///#                interval: 1
120    ///#                onTriggered: Qt.quit()
121    ///#            }
122    ///         }"#)
123    ///     .run();
124    /// ```
125    pub fn with_initial_properties(&mut self, properties: &[(&str, QVariant)]) -> &mut Self {
126        self.engine.pin_mut().set_initial_properties(&properties.into());
127        self
128    }
129
130    /// Loads QML source from an in-memory byte slice.
131    ///
132    /// Applies any properties queued with [`add_initial_property`](QApp::add_initial_property)
133    /// before loading.
134    pub fn load_qml(&mut self, code: &[u8]) -> &mut Self {
135        if !self.initial_properties.is_empty() {
136            self.engine.pin_mut().set_initial_properties(&self.initial_properties);
137        }
138        self.engine.pin_mut().load_data(code);
139        self
140    }
141
142    /// Loads the entry-point QML file by URL.
143    ///
144    /// Use this instead of [`load_qml`](QApp::load_qml) when the QML is
145    /// embedded in a Qt resource (`qrc:`) or accessible as a file path.
146    /// Accepts URLs such as `"qrc:/qt/qml/MyApp/Main.qml"` or
147    /// `"file:///path/to/main.qml"`.
148    ///
149    /// Import paths for any modules the file uses must be registered with
150    /// [`add_import_path`](QApp::add_import_path) before this call.
151    pub fn load_qml_from_file(&mut self, url: &str) -> &mut Self {
152        if !self.initial_properties.is_empty() {
153            self.engine.pin_mut().set_initial_properties(&self.initial_properties);
154        }
155        self.engine.pin_mut().load(url);
156        self
157    }
158
159    /// Adds a directory to the QML engine's module import search path.
160    ///
161    /// Call before [`load_qml_from_file`](QApp::load_qml_from_file) when the
162    /// loaded QML imports modules from a directory the engine would not
163    /// otherwise find. Accepts both URLs and file-system paths.
164    pub fn add_import_path(&mut self, path: &str) -> &mut Self {
165        self.engine.pin_mut().add_import_path(path);
166        self
167    }
168
169    /// Registers `T` with the QML type system, making it instantiable from QML.
170    ///
171    /// ```rust
172    ///# use qtbridge::{QApp, qobject};
173    /// #[derive(Default)]
174    /// pub struct Backend {
175    /// }
176    /// #[qobject]
177    /// impl Backend {
178    /// }
179    ///
180    /// QApp::new()
181    ///     .register::<Backend>()
182    ///     .load_qml(br#"
183    ///         import QtQuick
184    ///         import QtQuick.Controls
185    ///#        import qtbridge_runtime
186    ///         ApplicationWindow {
187    ///             Backend {}
188    ///#            Component.onCompleted: closeTimer.start()
189    ///#            Timer {
190    ///#                id: closeTimer
191    ///#                interval: 1
192    ///#                onTriggered: Qt.quit()
193    ///#            }
194    ///      }"#)
195    ///     .run();
196    /// ```
197    pub fn register<T: QmlRegister>(&mut self) -> &mut Self {
198        T::register();
199        self
200    }
201
202    /// Sets the application name reported to the OS.
203    pub fn application_name(&mut self, name: &str) -> &mut Self {
204        QGuiApplication::set_application_name(name);
205        self
206    }
207}