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