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}