i_slint_backend_qt/
lib.rs1#![doc = include_str!("README.md")]
7#![doc(html_logo_url = "https://slint.dev/logo/slint-logo-square-light.svg")]
8#![recursion_limit = "2048"]
9#![cfg_attr(slint_nightly_test, feature(non_exhaustive_omitted_patterns_lint))]
10#![cfg_attr(slint_nightly_test, warn(non_exhaustive_omitted_patterns))]
11
12extern crate alloc;
13
14use i_slint_core::platform::PlatformError;
15use std::rc::Rc;
16
17#[cfg(not(no_qt))]
18mod qt_accessible;
19#[cfg(not(no_qt))]
20mod qt_widgets;
21#[cfg(not(no_qt))]
22mod qt_window;
23
24mod accessible_generated;
25mod key_generated;
26
27#[cfg(no_qt)]
28mod ffi {
29 #[unsafe(no_mangle)]
30 pub extern "C" fn slint_qt_get_widget(
31 _: &i_slint_core::window::WindowAdapterRc,
32 ) -> *mut std::ffi::c_void {
33 std::ptr::null_mut()
34 }
35}
36
37#[cfg(not(no_qt))]
58#[rustfmt::skip]
59pub type NativeWidgets =
60 (qt_widgets::NativeButton,
61 (qt_widgets::NativeCheckBox,
62 (qt_widgets::NativeSlider,
63 (qt_widgets::NativeProgressIndicator,
64 (qt_widgets::NativeSpinBox,
65 (qt_widgets::NativeGroupBox,
66 (qt_widgets::NativeLineEdit,
67 (qt_widgets::NativeScrollView,
68 (qt_widgets::NativeStandardListViewItem,
69 (qt_widgets::NativeTableHeaderSection,
70 (qt_widgets::NativeComboBox,
71 (qt_widgets::NativeComboBoxPopup,
72 (qt_widgets::NativeTabWidget,
73 (qt_widgets::NativeTab,
74 ()))))))))))))));
75
76#[cfg(not(no_qt))]
77#[rustfmt::skip]
78pub type NativeGlobals =
79 (qt_widgets::NativeStyleMetrics,
80 (qt_widgets::NativePalette,
81 ()));
82
83#[cfg(no_qt)]
84mod native_style_metrics_stub {
85 use const_field_offset::FieldOffsets;
86 use core::pin::Pin;
87 #[cfg(feature = "rtti")]
88 use i_slint_core::rtti::*;
89 use i_slint_core_macros::*;
90
91 #[repr(C)]
93 #[derive(FieldOffsets, SlintElement)]
94 #[pin]
95 #[pin_drop]
96 pub struct NativeStyleMetrics {}
97
98 impl const_field_offset::PinnedDrop for NativeStyleMetrics {
99 fn drop(self: Pin<&mut Self>) {}
100 }
101
102 #[repr(C)]
104 #[derive(FieldOffsets, SlintElement)]
105 #[pin]
106 #[pin_drop]
107 pub struct NativePalette {}
108
109 impl const_field_offset::PinnedDrop for NativePalette {
110 fn drop(self: Pin<&mut Self>) {}
111 }
112}
113
114pub mod native_widgets {
115 #[cfg(not(no_qt))]
116 pub use super::qt_widgets::*;
117
118 #[cfg(no_qt)]
119 pub use super::native_style_metrics_stub::NativeStyleMetrics;
120
121 #[cfg(no_qt)]
122 pub use super::native_style_metrics_stub::NativePalette;
123}
124
125#[cfg(no_qt)]
126pub type NativeWidgets = ();
127#[cfg(no_qt)]
128pub type NativeGlobals = ();
129
130pub const HAS_NATIVE_STYLE: bool = cfg!(not(no_qt));
131
132pub struct Backend;
133
134impl Backend {
135 pub fn new() -> Self {
136 #[cfg(not(no_qt))]
137 {
138 use cpp::cpp;
139 cpp! {unsafe[] {
142 ensure_initialized(true);
143 }}
144 }
145 Self {}
146 }
147}
148
149impl i_slint_core::platform::Platform for Backend {
150 fn create_window_adapter(
151 &self,
152 ) -> Result<Rc<dyn i_slint_core::window::WindowAdapter>, PlatformError> {
153 #[cfg(no_qt)]
154 return Err("Qt platform requested but Slint is compiled without Qt support".into());
155 #[cfg(not(no_qt))]
156 {
157 Ok(qt_window::QtWindow::new())
158 }
159 }
160
161 fn run_event_loop(&self) -> Result<(), PlatformError> {
162 #[cfg(not(no_qt))]
163 {
164 crate::qt_window::timer_event();
166 use cpp::cpp;
167 cpp! {unsafe [] {
168 ensure_initialized(true);
169 qApp->exec();
170 } }
171 Ok(())
172 }
173 #[cfg(no_qt)]
174 Err("Qt platform requested but Slint is compiled without Qt support".into())
175 }
176
177 fn process_events(
178 &self,
179 _timeout: core::time::Duration,
180 _: i_slint_core::InternalToken,
181 ) -> Result<core::ops::ControlFlow<()>, PlatformError> {
182 #[cfg(not(no_qt))]
183 {
184 crate::qt_window::timer_event();
186 use cpp::cpp;
187 let timeout_ms: i32 = _timeout.as_millis() as _;
188 let loop_was_quit = cpp! {unsafe [timeout_ms as "int"] -> bool as "bool" {
189 ensure_initialized(true);
190 qApp->processEvents(QEventLoop::AllEvents, timeout_ms);
191 return std::exchange(g_lastWindowClosed, false);
192 } };
193 Ok(if loop_was_quit {
194 core::ops::ControlFlow::Break(())
195 } else {
196 core::ops::ControlFlow::Continue(())
197 })
198 }
199 #[cfg(no_qt)]
200 Err("Qt platform requested but Slint is compiled without Qt support".into())
201 }
202
203 #[cfg(not(no_qt))]
204 fn new_event_loop_proxy(&self) -> Option<Box<dyn i_slint_core::platform::EventLoopProxy>> {
205 struct Proxy;
206 impl i_slint_core::platform::EventLoopProxy for Proxy {
207 fn quit_event_loop(&self) -> Result<(), i_slint_core::api::EventLoopError> {
208 use cpp::cpp;
209 cpp! {unsafe [] {
210 QCoreApplication::postEvent(qApp, new QEvent(QEvent::Quit));
215 } }
216 Ok(())
217 }
218
219 fn invoke_from_event_loop(
220 &self,
221 _event: Box<dyn FnOnce() + Send>,
222 ) -> Result<(), i_slint_core::api::EventLoopError> {
223 use cpp::cpp;
224 cpp! {{
225 struct TraitObject { void *a, *b; };
226 struct EventHolder {
227 TraitObject fnbox = {nullptr, nullptr};
228 ~EventHolder() {
229 if (fnbox.a != nullptr || fnbox.b != nullptr) {
230 rust!(Slint_delete_event_holder [fnbox: *mut dyn FnOnce() as "TraitObject"] {
231 unsafe { drop(Box::from_raw(fnbox)) }
232 });
233 }
234 }
235 EventHolder(TraitObject f) : fnbox(f) {}
236 EventHolder(const EventHolder&) = delete;
237 EventHolder& operator=(const EventHolder&) = delete;
238 EventHolder(EventHolder&& other) : fnbox(other.fnbox) {
239 other.fnbox = {nullptr, nullptr};
240 }
241 void operator()() {
242 if (fnbox.a != nullptr || fnbox.b != nullptr) {
243 TraitObject fnbox = std::move(this->fnbox);
244 this->fnbox = {nullptr, nullptr};
245 rust!(Slint_call_event_holder [fnbox: *mut dyn FnOnce() as "TraitObject"] {
246 let b = unsafe { Box::from_raw(fnbox) };
247 b();
248 crate::qt_window::restart_timer();
250 });
251 }
252
253 }
254 };
255 }};
256 let fnbox = Box::into_raw(_event);
257 cpp! {unsafe [fnbox as "TraitObject"] {
258 QTimer::singleShot(0, qApp, EventHolder{fnbox});
259 }}
260 Ok(())
261 }
262 }
263 Some(Box::new(Proxy))
264 }
265
266 #[cfg(not(no_qt))]
267 fn set_clipboard_text(&self, _text: &str, _clipboard: i_slint_core::platform::Clipboard) {
268 use cpp::cpp;
269 let is_selection: bool = match _clipboard {
270 i_slint_core::platform::Clipboard::DefaultClipboard => false,
271 i_slint_core::platform::Clipboard::SelectionClipboard => true,
272 _ => return,
273 };
274 let text: qttypes::QString = _text.into();
275 cpp! {unsafe [text as "QString", is_selection as "bool"] {
276 ensure_initialized();
277 if (is_selection && !QGuiApplication::clipboard()->supportsSelection())
278 return;
279 QGuiApplication::clipboard()->setText(text, is_selection ? QClipboard::Selection : QClipboard::Clipboard);
280 } }
281 }
282
283 #[cfg(not(no_qt))]
284 fn clipboard_text(&self, _clipboard: i_slint_core::platform::Clipboard) -> Option<String> {
285 use cpp::cpp;
286 let is_selection: bool = match _clipboard {
287 i_slint_core::platform::Clipboard::DefaultClipboard => false,
288 i_slint_core::platform::Clipboard::SelectionClipboard => true,
289 _ => return None,
290 };
291 let has_text = cpp! {unsafe [is_selection as "bool"] -> bool as "bool" {
292 ensure_initialized();
293 if (is_selection && !QGuiApplication::clipboard()->supportsSelection())
294 return false;
295 return QGuiApplication::clipboard()->mimeData(is_selection ? QClipboard::Selection : QClipboard::Clipboard)->hasText();
296 } };
297 if has_text {
298 return Some(
299 cpp! { unsafe [is_selection as "bool"] -> qttypes::QString as "QString" {
300 return QGuiApplication::clipboard()->text(is_selection ? QClipboard::Selection : QClipboard::Clipboard);
301 }}
302 .into(),
303 );
304 }
305 None
306 }
307
308 #[cfg(not(no_qt))]
309 fn click_interval(&self) -> core::time::Duration {
310 let duration_ms = unsafe {
311 cpp::cpp! {[] -> u32 as "int" { return qApp->doubleClickInterval(); }}
312 };
313 core::time::Duration::from_millis(duration_ms as u64)
314 }
315
316 #[cfg(not(no_qt))]
317 fn cursor_flash_cycle(&self) -> core::time::Duration {
318 let duration_ms = unsafe {
319 cpp::cpp! {[] -> i32 as "int" { return qApp->cursorFlashTime(); }}
320 };
321 if duration_ms > 0 {
322 core::time::Duration::from_millis(duration_ms as u64)
323 } else {
324 core::time::Duration::ZERO
325 }
326 }
327}
328
329#[cfg(not(no_qt))]
332pub trait QtWidgetAccessor {
333 fn qt_widget_ptr(&self) -> Option<std::ptr::NonNull<()>>;
334}
335
336#[cfg(not(no_qt))]
337impl QtWidgetAccessor for i_slint_core::api::Window {
338 fn qt_widget_ptr(&self) -> Option<std::ptr::NonNull<()>> {
339 i_slint_core::window::WindowInner::from_pub(self)
340 .window_adapter()
341 .internal(i_slint_core::InternalToken)
342 .and_then(|wa| (wa as &dyn core::any::Any).downcast_ref::<qt_window::QtWindow>())
343 .map(qt_window::QtWindow::widget_ptr)
344 }
345}