i_slint_backend_qt/
qt_widgets.rs1#![allow(non_upper_case_globals)]
19
20use crate::qt_window::QPainterPtr;
21use const_field_offset::FieldOffsets;
22use core::pin::Pin;
23use cpp::{cpp, cpp_class};
24use i_slint_core::graphics::Color;
25use i_slint_core::input::{
26 FocusEvent, InputEventFilterResult, InputEventResult, KeyEvent, KeyEventResult, MouseEvent,
27};
28use i_slint_core::item_rendering::{CachedRenderingData, ItemRenderer};
29use i_slint_core::items::{Item, ItemConsts, ItemRc, ItemVTable, RenderingResult, VoidArg};
30use i_slint_core::layout::{LayoutInfo, Orientation};
31use i_slint_core::lengths::{LogicalLength, LogicalPoint, LogicalRect, LogicalSize};
32#[cfg(feature = "rtti")]
33use i_slint_core::rtti::*;
34use i_slint_core::window::{WindowAdapter, WindowAdapterRc, WindowInner};
35use i_slint_core::{
36 declare_item_vtable, Callback, ItemVTable_static, Property, SharedString, SharedVector,
37};
38use i_slint_core_macros::*;
39use std::ptr::NonNull;
40use std::rc::Rc;
41
42type ItemRendererRef<'a> = &'a mut dyn ItemRenderer;
43
44macro_rules! get_size {
47 ($self:ident) => {{
48 let geo = $self.geometry();
49 let width = geo.width();
50 let height = geo.height();
51 if width < 1. || height < 1. {
52 return Default::default();
53 };
54 qttypes::QSize { width: width as _, height: height as _ }
55 }};
56}
57
58macro_rules! fn_render {
59 ($this:ident $dpr:ident $size:ident $painter:ident $widget:ident $initial_state:ident => $($tt:tt)*) => {
60 fn render(self: Pin<&Self>, backend: &mut &mut dyn ItemRenderer, item_rc: &ItemRc, size: LogicalSize) -> RenderingResult {
61 self.animation_tracker();
62 let $dpr: f32 = backend.scale_factor();
63
64 let active: bool = backend.window().active();
65 let $initial_state = cpp!(unsafe [ active as "bool" ] -> i32 as "int" {
68 QStyle::State state(QStyle::State_None);
69 if (active)
70 state |= QStyle::State_Active;
71 return (int)state;
72 });
73
74 let $widget: NonNull<()> = SlintTypeErasedWidgetPtr::qwidget_ptr(&self.widget_ptr);
75
76 if let Some(painter) = backend.as_any().and_then(|any| <dyn std::any::Any>::downcast_mut::<QPainterPtr>(any)) {
77 let width = size.width * $dpr;
78 let height = size.height * $dpr;
79 if width < 1. || height < 1. {
80 return Default::default();
81 };
82 let $size = qttypes::QSize { width: width as _, height: height as _ };
83 let $this = self;
84 let _workaround = unsafe { $crate::qt_widgets::PainterClipWorkaround::new(painter) };
85 painter.save();
86 let $painter = painter;
87 $($tt)*
88 $painter.restore();
89 } else {
90 backend.draw_cached_pixmap(
92 item_rc,
93 &|callback| {
94 let geo = item_rc.geometry();
95 let width = geo.width() * $dpr;
96 let height = geo.height() * $dpr;
97 if width < 1. || height < 1. {
98 return Default::default();
99 };
100 let $size = qttypes::QSize { width: width as _, height: height as _ };
101 let mut imgarray = QImageWrapArray::new($size, $dpr);
102 let img = &mut imgarray.img;
103 let mut painter = cpp!(unsafe [img as "QImage*"] -> QPainterPtr as "std::unique_ptr<QPainter>" {
104 auto painter = std::make_unique<QPainter>(img);
105 painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
106 return painter;
107 });
108 let $painter = &mut painter;
109 let $this = self;
110 $($tt)*
111 drop(painter);
112 imgarray.draw(callback);
113 },
114 );
115 }
116 RenderingResult::ContinueRenderingChildren
117 }
118 };
119}
120
121struct QImageWrapArray {
122 img: qttypes::QImage,
124 array: SharedVector<u8>,
125}
126
127impl QImageWrapArray {
128 pub fn new(size: qttypes::QSize, dpr: f32) -> Self {
129 let mut array = SharedVector::default();
130 array.resize((size.width * size.height * 4) as usize, 0);
131 let array_ptr = array.make_mut_slice().as_mut_ptr();
132 let img = cpp!(unsafe [size as "QSize", array_ptr as "uchar*", dpr as "float"] -> qttypes::QImage as "QImage" {
133 ensure_initialized();
134 QImage img(array_ptr, size.width(), size.height(), size.width() * 4, QImage::Format_RGBA8888_Premultiplied);
135 img.setDevicePixelRatio(dpr);
136 return img;
137 });
138 QImageWrapArray { img, array }
139 }
140
141 pub fn draw(&self, callback: &mut dyn FnMut(u32, u32, &[u8])) {
142 let size = self.img.size();
143 callback(size.width, size.height, self.array.as_slice());
144 }
145}
146
147cpp! {{
148 #include <QtWidgets/QApplication>
149 #include <QtWidgets/QStyle>
150 #include <QtWidgets/QStyleOption>
151 #include <QtWidgets/QStyleFactory>
152 #include <QtGui/QPainter>
153 #include <QtGui/QClipboard>
154 #include <QtGui/QPaintEngine>
155 #include <QtCore/QMimeData>
156 #include <QtCore/QDebug>
157 #include <QtCore/QScopeGuard>
158
159 using QPainterPtr = std::unique_ptr<QPainter>;
160
161 static bool g_lastWindowClosed = false; void ensure_initialized(bool from_qt_backend = false)
167 {
168 if (qApp) {
169 return;
170 }
171 if (!from_qt_backend) {
172 QCoreApplication::setAttribute(Qt::AA_PluginApplication, true);
175 }
176
177 static QByteArray executable = rust!(Slint_get_executable_name [] -> qttypes::QByteArray as "QByteArray" {
178 std::env::args().next().unwrap_or_default().as_bytes().into()
179 });
180
181 static int argc = 1;
182 static char *argv[] = { executable.data() };
183 new QApplication(argc, argv);
186 qApp->setQuitOnLastWindowClosed(false);
187 }
188
189 struct SlintTypeErasedWidget
191 {
192 virtual ~SlintTypeErasedWidget() = 0;
193 SlintTypeErasedWidget() = default;
194 SlintTypeErasedWidget(const SlintTypeErasedWidget&) = delete;
195 SlintTypeErasedWidget& operator=(const SlintTypeErasedWidget&) = delete;
196
197 virtual void *qwidget() = 0;
198 };
199
200 SlintTypeErasedWidget::~SlintTypeErasedWidget() = default;
201
202 template <typename Base>
203 struct SlintAnimatedWidget: public Base, public SlintTypeErasedWidget {
204 void *animation_update_property_ptr;
205 bool event(QEvent *event) override {
206 if (event->type() == QEvent::StyleAnimationUpdate || event->type() == QEvent::UpdateLater) {
209 rust!(Slint_AnimatedWidget_update [animation_update_property_ptr: Pin<&Property<i32>> as "void*"] {
210 animation_update_property_ptr.set(animation_update_property_ptr.get() + 1);
211 });
212 event->accept();
213 return true;
214 } else {
215 return Base::event(event);
216 }
217 }
218 void *qwidget() override { return static_cast<QWidget*>(this); }
220 };
221
222 template <typename Base>
223 std::unique_ptr<SlintTypeErasedWidget> make_unique_animated_widget(void *animation_update_property_ptr)
224 {
225 ensure_initialized();
226 auto ptr = std::make_unique<SlintAnimatedWidget<Base>>();
227 static QWidget globalParent;
229 ptr->setParent(&globalParent);
230 ptr->setAttribute(Qt::WA_WState_Visible, true);
232 ptr->setAttribute(Qt::WA_WState_InPaintEvent, true);
234 ptr->animation_update_property_ptr = animation_update_property_ptr;
235 return ptr;
236 }
237}}
238
239cpp_class!(pub unsafe struct SlintTypeErasedWidgetPtr as "std::unique_ptr<SlintTypeErasedWidget>");
240
241impl SlintTypeErasedWidgetPtr {
242 fn qwidget_ptr(this: &std::cell::Cell<Self>) -> NonNull<()> {
243 let widget_ptr: *mut SlintTypeErasedWidgetPtr = this.as_ptr();
244 cpp!(unsafe [widget_ptr as "std::unique_ptr<SlintTypeErasedWidget>*"] -> NonNull<()> as "void*" {
245 return (*widget_ptr)->qwidget();
246 })
247 }
248}
249
250cpp! {{
251 struct PainterClipWorkaround {
255 QPainter *painter;
256 QRegion old_clip;
257 explicit PainterClipWorkaround(QPainter *painter) : painter(painter) {
258 auto engine = painter->paintEngine();
259 old_clip = engine->systemClip();
260 auto new_clip = painter->clipRegion() * painter->transform();
261 if (!old_clip.isNull())
262 new_clip &= old_clip;
263 engine->setSystemClip(new_clip);
264 }
265 ~PainterClipWorkaround() {
266 auto engine = painter->paintEngine();
267 engine->setSystemClip(old_clip);
268 auto actual_clip = engine->systemClip();
270 if (actual_clip != old_clip) {
271 QSizeF s2 = actual_clip.boundingRect().size();
272 QSizeF s1 = old_clip.boundingRect().size();
273 engine->setSystemClip(old_clip * QTransform::fromScale(s1.width() / s2.width(), s1.height() / s2.height()));
274 }
275 }
276 PainterClipWorkaround(const PainterClipWorkaround&) = delete;
277 PainterClipWorkaround& operator=(const PainterClipWorkaround&) = delete;
278 };
279}}
280cpp_class!(pub(crate) unsafe struct PainterClipWorkaround as "PainterClipWorkaround");
281impl PainterClipWorkaround {
282 pub unsafe fn new(painter: &QPainterPtr) -> Self {
284 cpp!(unsafe [painter as "const QPainterPtr*"] -> PainterClipWorkaround as "PainterClipWorkaround" {
285 return PainterClipWorkaround(painter->get());
286 })
287 }
288}
289
290mod button;
291pub use button::*;
292
293mod checkbox;
294pub use checkbox::*;
295
296mod spinbox;
297pub use spinbox::*;
298
299mod slider;
300pub use slider::*;
301
302mod progress_indicator;
303pub use progress_indicator::*;
304
305mod groupbox;
306pub use groupbox::*;
307
308mod lineedit;
309pub use lineedit::*;
310
311mod scrollview;
312pub use scrollview::*;
313
314mod listviewitem;
315pub use listviewitem::*;
316
317mod combobox;
318pub use combobox::*;
319
320mod tabwidget;
321pub use tabwidget::*;
322
323mod stylemetrics;
324pub use stylemetrics::*;
325
326mod palette;
327pub use palette::*;
328
329mod tableheadersection;
330pub use tableheadersection::*;