1use cpp::*;
7use i_slint_common::sharedfontique;
8use i_slint_core::graphics::rendering_metrics_collector::{
9 RenderingMetrics, RenderingMetricsCollector,
10};
11use i_slint_core::graphics::{
12 euclid, Brush, Color, FontRequest, IntRect, Point, Rgba8Pixel, SharedImageBuffer,
13 SharedPixelBuffer,
14};
15use i_slint_core::input::{KeyEvent, KeyEventType, MouseEvent};
16use i_slint_core::item_rendering::{
17 CachedRenderingData, ItemCache, ItemRenderer, RenderBorderRectangle, RenderImage,
18 RenderRectangle, RenderText,
19};
20use i_slint_core::item_tree::ParentItemTraversalMode;
21use i_slint_core::item_tree::{ItemTreeRc, ItemTreeRef, ItemTreeWeak};
22use i_slint_core::items::{
23 self, ColorScheme, FillRule, ImageRendering, ItemRc, ItemRef, Layer, LineCap, MouseCursor,
24 Opacity, PointerEventButton, RenderingResult, TextWrap,
25};
26use i_slint_core::layout::Orientation;
27use i_slint_core::lengths::{
28 LogicalBorderRadius, LogicalLength, LogicalPoint, LogicalRect, LogicalSize, LogicalVector,
29 PhysicalPx, ScaleFactor,
30};
31use i_slint_core::platform::{PlatformError, WindowEvent};
32use i_slint_core::textlayout::sharedparley::{self, parley, GlyphRenderer};
33use i_slint_core::window::{WindowAdapter, WindowAdapterInternal, WindowInner};
34use i_slint_core::{ImageInner, Property, SharedString};
35
36use std::cell::RefCell;
37use std::collections::HashMap;
38use std::pin::Pin;
39use std::ptr::NonNull;
40use std::rc::{Rc, Weak};
41
42use crate::key_generated;
43use i_slint_core::renderer::Renderer;
44use std::cell::OnceCell;
45
46cpp! {{
47 #include <QtWidgets/QtWidgets>
48 #include <QtWidgets/QGraphicsScene>
49 #include <QtWidgets/QGraphicsBlurEffect>
50 #include <QtWidgets/QGraphicsPixmapItem>
51 #include <QtGui/QAccessible>
52 #include <QtGui/QPainter>
53 #include <QtGui/QPaintEngine>
54 #include <QtGui/QPainterPath>
55 #include <QtGui/QWindow>
56 #include <QtGui/QResizeEvent>
57 #include <QtGui/QTextLayout>
58 #include <QtGui/QImageReader>
59 #include <QtGui/QCursor>
60 #include <QtCore/QBasicTimer>
61 #include <QtCore/QTimer>
62 #include <QtCore/QPointer>
63 #include <QtCore/QBuffer>
64 #include <QtCore/QEvent>
65 #include <QtCore/QFileInfo>
66
67 #include <memory>
68
69 void ensure_initialized(bool from_qt_backend);
70
71 using QPainterPtr = std::unique_ptr<QPainter>;
72
73 struct TimerHandler : QObject {
74 QBasicTimer timer;
75 static TimerHandler& instance() {
76 static TimerHandler instance;
77 return instance;
78 }
79
80 void timerEvent(QTimerEvent *event) override {
81 if (event->timerId() != timer.timerId()) {
82 QObject::timerEvent(event);
83 return;
84 }
85 timer.stop();
86 rust!(Slint_timerEvent [] { timer_event() });
87 }
88
89 };
90
91 struct SlintWidget : QWidget {
92 void *rust_window = nullptr;
93 bool isMouseButtonDown = false;
94 QRect ime_position;
95 QString ime_text;
96 int ime_cursor = 0;
97 int ime_anchor = 0;
98
99 SlintWidget() {
100 setMouseTracking(true);
101 setFocusPolicy(Qt::StrongFocus);
102 setAttribute(Qt::WA_TranslucentBackground);
103 setAttribute(Qt::WA_NoSystemBackground, false);
107 }
108
109 void paintEvent(QPaintEvent *) override {
110 if (!rust_window)
111 return;
112 auto painter = std::unique_ptr<QPainter>(new QPainter(this));
113 painter->setClipRect(rect());
114 painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
115 QPainterPtr *painter_ptr = &painter;
116 rust!(Slint_paintEvent [rust_window: &QtWindow as "void*", painter_ptr: &mut QPainterPtr as "QPainterPtr*"] {
117 rust_window.paint_event(std::mem::take(painter_ptr))
118 });
119 }
120
121 void resizeEvent(QResizeEvent *) override {
122 if (!rust_window)
123 return;
124
125 QSize size = this->size();
129 rust!(Slint_resizeEvent [rust_window: &QtWindow as "void*", size: qttypes::QSize as "QSize"] {
130 rust_window.resize_event(size)
131 });
132 }
133
134 std::tuple<QPoint, void*> adjust_mouse_event_to_popup_parent(QMouseEvent *event) {
137 auto pos = event->pos();
138 if (auto p = dynamic_cast<const SlintWidget*>(parent()); p && !rect().contains(pos)) {
139 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
140 QPoint eventPos = event->globalPosition().toPoint();
141 #else
142 QPoint eventPos = event->globalPos();
143 #endif
144 while (auto pp = dynamic_cast<const SlintWidget*>(p->parent())) {
145 if (p->rect().contains(p->mapFromGlobal(eventPos)))
146 break;
147 p = pp;
148 }
149 return { p->mapFromGlobal(eventPos), p->rust_window };
150 } else {
151 return { pos, rust_window };
152 }
153 }
154
155 void mousePressEvent(QMouseEvent *event) override {
156 isMouseButtonDown = true;
157 auto [pos, rust_window] = adjust_mouse_event_to_popup_parent(event);
158 if (!rust_window)
159 return;
160 int button = event->button();
161 rust!(Slint_mousePressEvent [rust_window: &QtWindow as "void*", pos: qttypes::QPoint as "QPoint", button: u32 as "int" ] {
162 let position = LogicalPoint::new(pos.x as _, pos.y as _);
163 let button = from_qt_button(button);
164 rust_window.mouse_event(MouseEvent::Pressed{ position, button, click_count: 0 })
165 });
166 }
167 void mouseReleaseEvent(QMouseEvent *event) override {
168 auto [pos, rust_window] = adjust_mouse_event_to_popup_parent(event);
169 if (!rust_window)
170 return;
171
172 if (!isMouseButtonDown && rust_window == this->rust_window) {
186 return;
187 }
188 isMouseButtonDown = event->button() != Qt::NoButton;
189
190 int button = event->button();
191 rust!(Slint_mouseReleaseEvent [rust_window: &QtWindow as "void*", pos: qttypes::QPoint as "QPoint", button: u32 as "int" ] {
192 let position = LogicalPoint::new(pos.x as _, pos.y as _);
193 let button = from_qt_button(button);
194 rust_window.mouse_event(MouseEvent::Released{ position, button, click_count: 0 })
195 });
196 }
197 void mouseMoveEvent(QMouseEvent *event) override {
198 auto [pos, rust_window] = adjust_mouse_event_to_popup_parent(event);
199 if (!rust_window)
200 return;
201 rust!(Slint_mouseMoveEvent [rust_window: &QtWindow as "void*", pos: qttypes::QPoint as "QPoint"] {
202 let position = LogicalPoint::new(pos.x as _, pos.y as _);
203 rust_window.mouse_event(MouseEvent::Moved{position})
204 });
205 }
206 void wheelEvent(QWheelEvent *event) override {
207 if (!rust_window)
208 return;
209 QPointF pos = event->position();
210 QPoint delta = event->pixelDelta();
211 if (delta.isNull()) {
212 delta = event->angleDelta();
213 }
214 rust!(Slint_mouseWheelEvent [rust_window: &QtWindow as "void*", pos: qttypes::QPointF as "QPointF", delta: qttypes::QPoint as "QPoint"] {
215 let position = LogicalPoint::new(pos.x as _, pos.y as _);
216 rust_window.mouse_event(MouseEvent::Wheel{position, delta_x: delta.x as _, delta_y: delta.y as _})
217 });
218 }
219 void leaveEvent(QEvent *) override {
220 if (!rust_window)
221 return;
222 rust!(Slint_mouseLeaveEvent [rust_window: &QtWindow as "void*"] {
223 rust_window.mouse_event(MouseEvent::Exit)
224 });
225 }
226
227 void keyPressEvent(QKeyEvent *event) override {
228 if (!rust_window)
229 return;
230 QString text = event->text();
231 int key = event->key();
232 bool repeat = event->isAutoRepeat();
233 rust!(Slint_keyPress [rust_window: &QtWindow as "void*", key: i32 as "int", text: qttypes::QString as "QString", repeat: bool as "bool"] {
234 rust_window.key_event(key, text.clone(), false, repeat);
235 });
236 }
237 void keyReleaseEvent(QKeyEvent *event) override {
238 if (!rust_window)
239 return;
240 if (event->isAutoRepeat())
243 return;
244
245 QString text = event->text();
246 int key = event->key();
247 rust!(Slint_keyRelease [rust_window: &QtWindow as "void*", key: i32 as "int", text: qttypes::QString as "QString"] {
248 rust_window.key_event(key, text.clone(), true, false);
249 });
250 }
251
252 void changeEvent(QEvent *event) override {
253 if (!rust_window)
254 return QWidget::changeEvent(event);
255
256 if (event->type() == QEvent::ActivationChange) {
257 bool active = isActiveWindow();
258 rust!(Slint_updateWindowActivation [rust_window: &QtWindow as "void*", active: bool as "bool"] {
259 rust_window.window.dispatch_event(WindowEvent::WindowActiveChanged(active));
260 });
261 } else if (event->type() == QEvent::PaletteChange || event->type() == QEvent::StyleChange) {
262 bool dark_color_scheme = qApp->palette().color(QPalette::Window).valueF() < 0.5;
263 rust!(Slint_updateWindowDarkColorScheme [rust_window: &QtWindow as "void*", dark_color_scheme: bool as "bool"] {
264 if let Some(ds) = rust_window.color_scheme.get() {
265 ds.as_ref().set(if dark_color_scheme {
266 ColorScheme::Dark
267 } else {
268 ColorScheme::Light
269 });
270 }
271 });
272 }
273
274 if (event->type() == QEvent::WindowStateChange)
278 {
279 rust!(Slint_syncWindowState [rust_window: &QtWindow as "void*"]{
280 rust_window.window_state_event();
281 });
282 }
283
284
285 QWidget::changeEvent(event);
286 }
287
288 void closeEvent(QCloseEvent *event) override {
289 if (!rust_window)
290 return;
291 rust!(Slint_requestClose [rust_window: &QtWindow as "void*"] {
292 rust_window.window.dispatch_event(WindowEvent::CloseRequested);
293 });
294 event->ignore();
295 }
296
297 QSize sizeHint() const override {
298 if (!rust_window)
299 return {};
300 auto preferred_size = rust!(Slint_sizeHint [rust_window: &QtWindow as "void*"] -> qttypes::QSize as "QSize" {
301 let component_rc = WindowInner::from_pub(&rust_window.window).component();
302 let component = ItemTreeRc::borrow_pin(&component_rc);
303 let layout_info_h = component.as_ref().layout_info(Orientation::Horizontal);
304 let layout_info_v = component.as_ref().layout_info(Orientation::Vertical);
305 qttypes::QSize {
306 width: layout_info_h.preferred_bounded() as _,
307 height: layout_info_v.preferred_bounded() as _,
308 }
309 });
310 if (!preferred_size.isEmpty()) {
311 return preferred_size;
312 } else {
313 return QWidget::sizeHint();
314 }
315 }
316
317 QVariant inputMethodQuery(Qt::InputMethodQuery query) const override {
318 switch (query) {
319 case Qt::ImCursorRectangle: return ime_position;
320 case Qt::ImCursorPosition: return ime_cursor;
321 case Qt::ImSurroundingText: return ime_text;
322 case Qt::ImCurrentSelection: return ime_text.mid(qMin(ime_cursor, ime_anchor), qAbs(ime_cursor - ime_anchor));
323 case Qt::ImAnchorPosition: return ime_anchor;
324 case Qt::ImTextBeforeCursor: return ime_text.left(ime_cursor);
325 case Qt::ImTextAfterCursor: return ime_text.right(ime_cursor);
326 default: break;
327 }
328 return QWidget::inputMethodQuery(query);
329 }
330
331 void inputMethodEvent(QInputMethodEvent *event) override {
332 if (!rust_window)
333 return;
334 QString commit_string = event->commitString();
335 QString preedit_string = event->preeditString();
336 int replacement_start = event->replacementStart();
337 QStringView ime_text(this->ime_text);
338 replacement_start = replacement_start < 0 ?
339 -ime_text.mid(ime_cursor,-replacement_start).toUtf8().size() :
340 ime_text.mid(ime_cursor,replacement_start).toUtf8().size();
341 int replacement_length = qMax(0, event->replacementLength());
342 ime_text.mid(ime_cursor + replacement_start, replacement_length).toUtf8().size();
343 int preedit_cursor = -1;
344 for (const QInputMethodEvent::Attribute &attribute: event->attributes()) {
345 if (attribute.type == QInputMethodEvent::Cursor) {
346 if (attribute.length > 0) {
347 preedit_cursor = QStringView(preedit_string).left(attribute.start).toUtf8().size();
348 }
349 }
350 }
351 event->accept();
352 rust!(Slint_inputMethodEvent [rust_window: &QtWindow as "void*", commit_string: qttypes::QString as "QString",
353 preedit_string: qttypes::QString as "QString", replacement_start: i32 as "int", replacement_length: i32 as "int",
354 preedit_cursor: i32 as "int"] {
355 let runtime_window = WindowInner::from_pub(&rust_window.window);
356
357 let event = KeyEvent {
358 event_type: KeyEventType::UpdateComposition,
359 text: i_slint_core::format!("{}", commit_string),
360 preedit_text: i_slint_core::format!("{}", preedit_string),
361 preedit_selection: (preedit_cursor >= 0).then_some(preedit_cursor..preedit_cursor),
362 replacement_range: (!commit_string.is_empty() || !preedit_string.is_empty() || preedit_cursor >= 0)
363 .then_some(replacement_start..replacement_start+replacement_length),
364 ..Default::default()
365 };
366 runtime_window.process_key_input(event);
367 });
368 }
369 };
370
371 QPainterPath to_painter_path(const QRectF &rect, qreal top_left_radius, qreal top_right_radius, qreal bottom_right_radius, qreal bottom_left_radius) {
372 QPainterPath path;
373 if (qFuzzyCompare(top_left_radius, top_right_radius) && qFuzzyCompare(top_left_radius, bottom_right_radius) && qFuzzyCompare(top_left_radius, bottom_left_radius)) {
374 path.addRoundedRect(rect, top_left_radius, top_left_radius);
375 } else {
376 QSizeF half = rect.size() / 2.0;
377
378 qreal tl_rx = qMin(top_left_radius, half.width());
379 qreal tl_ry = qMin(top_left_radius, half.height());
380 QRectF top_left(rect.left(), rect.top(), 2 * tl_rx, 2 * tl_ry);
381
382 qreal tr_rx = qMin(top_right_radius, half.width());
383 qreal tr_ry = qMin(top_right_radius, half.height());
384 QRectF top_right(rect.right() - 2 * tr_rx, rect.top(), 2 * tr_rx, 2 * tr_ry);
385
386 qreal br_rx = qMin(bottom_right_radius, half.width());
387 qreal br_ry = qMin(bottom_right_radius, half.height());
388 QRectF bottom_right(rect.right() - 2 * br_rx, rect.bottom() - 2 * br_ry, 2 * br_rx, 2 * br_ry);
389
390 qreal bl_rx = qMin(bottom_left_radius, half.width());
391 qreal bl_ry = qMin(bottom_left_radius, half.height());
392 QRectF bottom_left(rect.left(), rect.bottom() - 2 * bl_ry, 2 * bl_rx, 2 * bl_ry);
393
394 if (top_left.isNull()) {
395 path.moveTo(rect.topLeft());
396 } else {
397 path.arcMoveTo(top_left, 180);
398 path.arcTo(top_left, 180, -90);
399 }
400 if (top_right.isNull()) {
401 path.lineTo(rect.topRight());
402 } else {
403 path.arcTo(top_right, 90, -90);
404 }
405 if (bottom_right.isNull()) {
406 path.lineTo(rect.bottomRight());
407 } else {
408 path.arcTo(bottom_right, 0, -90);
409 }
410 if (bottom_left.isNull()) {
411 path.lineTo(rect.bottomLeft());
412 } else {
413 path.arcTo(bottom_left, -90, -90);
414 }
415 path.closeSubpath();
416 }
417 return path;
418 };
419}}
420
421cpp_class!(
422 pub unsafe struct QPainterPtr as "QPainterPtr"
425);
426impl QPainterPtr {
427 pub fn restore(&mut self) {
428 cpp!(unsafe [self as "QPainterPtr*"] {
429 (*self)->restore();
430 });
431 }
432
433 pub fn save(&mut self) {
434 cpp!(unsafe [self as "QPainterPtr*"] {
435 (*self)->save();
436 });
437 }
438}
439
440cpp_class! {pub unsafe struct QPainterPath as "QPainterPath"}
441
442impl QPainterPath {
443 pub fn move_to(&mut self, to: qttypes::QPointF) {
451 cpp! { unsafe [self as "QPainterPath*", to as "QPointF"] {
452 self->moveTo(to);
453 }}
454 }
455 pub fn line_to(&mut self, to: qttypes::QPointF) {
456 cpp! { unsafe [self as "QPainterPath*", to as "QPointF"] {
457 self->lineTo(to);
458 }}
459 }
460 pub fn quad_to(&mut self, ctrl: qttypes::QPointF, to: qttypes::QPointF) {
461 cpp! { unsafe [self as "QPainterPath*", ctrl as "QPointF", to as "QPointF"] {
462 self->quadTo(ctrl, to);
463 }}
464 }
465 pub fn cubic_to(
466 &mut self,
467 ctrl1: qttypes::QPointF,
468 ctrl2: qttypes::QPointF,
469 to: qttypes::QPointF,
470 ) {
471 cpp! { unsafe [self as "QPainterPath*", ctrl1 as "QPointF", ctrl2 as "QPointF", to as "QPointF"] {
472 self->cubicTo(ctrl1, ctrl2, to);
473 }}
474 }
475
476 pub fn close(&mut self) {
477 cpp! { unsafe [self as "QPainterPath*"] {
478 self->closeSubpath();
479 }}
480 }
481
482 pub fn set_fill_rule(&mut self, rule: key_generated::Qt_FillRule) {
483 cpp! { unsafe [self as "QPainterPath*", rule as "Qt::FillRule" ] {
484 self->setFillRule(rule);
485 }}
486 }
487}
488
489fn into_qbrush(
490 brush: i_slint_core::Brush,
491 width: qttypes::qreal,
492 height: qttypes::qreal,
493) -> qttypes::QBrush {
494 fn mangle_position(position: f32, idx: usize, count: usize) -> f32 {
496 if position < 0.54321 + 67.8 * f32::EPSILON {
500 position + f32::EPSILON * idx as f32
501 } else {
502 position - f32::EPSILON * (count - idx - 1) as f32
503 }
504 }
505 match brush {
506 i_slint_core::Brush::SolidColor(color) => {
507 let color: u32 = color.as_argb_encoded();
508 cpp!(unsafe [color as "QRgb"] -> qttypes::QBrush as "QBrush" {
509 return QBrush(QColor::fromRgba(color));
510 })
511 }
512 i_slint_core::Brush::LinearGradient(g) => {
513 let (start, end) = i_slint_core::graphics::line_for_angle(
514 g.angle(),
515 [width as f32, height as f32].into(),
516 );
517 let p1 = qttypes::QPointF { x: start.x as _, y: start.y as _ };
518 let p2 = qttypes::QPointF { x: end.x as _, y: end.y as _ };
519 cpp_class!(unsafe struct QLinearGradient as "QLinearGradient");
520 let mut qlg = cpp! {
521 unsafe [p1 as "QPointF", p2 as "QPointF"] -> QLinearGradient as "QLinearGradient" {
522 QLinearGradient qlg(p1, p2);
523 return qlg;
524 }
525 };
526 let count = g.stops().count();
527 for (idx, s) in g.stops().enumerate() {
528 let pos: f32 = mangle_position(s.position, idx, count);
529 let color: u32 = s.color.as_argb_encoded();
530 cpp! {unsafe [mut qlg as "QLinearGradient", pos as "float", color as "QRgb"] {
531 qlg.setColorAt(pos, QColor::fromRgba(color));
532 }};
533 }
534 cpp! {unsafe [qlg as "QLinearGradient"] -> qttypes::QBrush as "QBrush" {
535 return QBrush(qlg);
536 }}
537 }
538 i_slint_core::Brush::RadialGradient(g) => {
539 cpp_class!(unsafe struct QRadialGradient as "QRadialGradient");
540 let mut qrg = cpp! {
541 unsafe [width as "qreal", height as "qreal"] -> QRadialGradient as "QRadialGradient" {
542 QRadialGradient qrg(width / 2, height / 2, sqrt(width * width + height * height) / 2);
543 return qrg;
544 }
545 };
546 let count = g.stops().count();
547 for (idx, s) in g.stops().enumerate() {
548 let pos: f32 = mangle_position(s.position, idx, count);
549 let color: u32 = s.color.as_argb_encoded();
550 cpp! {unsafe [mut qrg as "QRadialGradient", pos as "float", color as "QRgb"] {
551 qrg.setColorAt(pos, QColor::fromRgba(color));
552 }};
553 }
554 cpp! {unsafe [qrg as "QRadialGradient"] -> qttypes::QBrush as "QBrush" {
555 return QBrush(qrg);
556 }}
557 }
558 i_slint_core::Brush::ConicGradient(g) => {
559 cpp_class!(unsafe struct QConicalGradient as "QConicalGradient");
560 let mut qcg = cpp! {
563 unsafe [width as "qreal", height as "qreal"] -> QConicalGradient as "QConicalGradient" {
564 QConicalGradient qcg(width / 2, height / 2, 90);
565 return qcg;
566 }
567 };
568 let count = g.stops().count();
569 for (idx, s) in g.stops().enumerate() {
570 let pos: f32 = 1.0 - mangle_position(s.position, idx, count);
573 let color: u32 = s.color.as_argb_encoded();
574 cpp! {unsafe [mut qcg as "QConicalGradient", pos as "float", color as "QRgb"] {
575 qcg.setColorAt(pos, QColor::fromRgba(color));
576 }};
577 }
578 cpp! {unsafe [qcg as "QConicalGradient"] -> qttypes::QBrush as "QBrush" {
579 return QBrush(qcg);
580 }}
581 }
582 _ => qttypes::QBrush::default(),
583 }
584}
585
586fn from_qt_button(qt_button: u32) -> PointerEventButton {
587 match qt_button {
588 1 => PointerEventButton::Left,
590 2 => PointerEventButton::Right,
591 4 => PointerEventButton::Middle,
592 8 => PointerEventButton::Back,
593 16 => PointerEventButton::Forward,
594 _ => PointerEventButton::Other,
595 }
596}
597
598macro_rules! check_geometry {
601 ($size:expr) => {{
602 let size = $size;
603 if size.width < 1. || size.height < 1. {
604 return Default::default();
605 };
606 qttypes::QRectF { x: 0., y: 0., width: size.width as _, height: size.height as _ }
607 }};
608}
609
610fn adjust_rect_and_border_for_inner_drawing(rect: &mut qttypes::QRectF, border_width: &mut f32) {
611 *border_width = border_width.min((rect.width as f32) / 2.);
613 rect.x += *border_width as f64 / 2.;
615 rect.y += *border_width as f64 / 2.;
616 rect.width -= *border_width as f64;
617 rect.height -= *border_width as f64;
618}
619
620struct QtItemRenderer<'a> {
621 painter: QPainterPtr,
622 cache: &'a ItemCache<qttypes::QPixmap>,
623 window: &'a i_slint_core::api::Window,
624 metrics: RenderingMetrics,
625}
626
627impl ItemRenderer for QtItemRenderer<'_> {
628 fn draw_rectangle(
629 &mut self,
630 rect_: Pin<&dyn RenderRectangle>,
631 _: &ItemRc,
632 size: LogicalSize,
633 _cache: &CachedRenderingData,
634 ) {
635 let rect: qttypes::QRectF = check_geometry!(size);
636 let brush: qttypes::QBrush = into_qbrush(rect_.background(), rect.width, rect.height);
637 let painter: &mut QPainterPtr = &mut self.painter;
638 cpp! { unsafe [painter as "QPainterPtr*", brush as "QBrush", rect as "QRectF"] {
639 (*painter)->fillRect(rect, brush);
640 }}
641 }
642
643 fn draw_border_rectangle(
644 &mut self,
645 rect: Pin<&dyn RenderBorderRectangle>,
646 _: &ItemRc,
647 size: LogicalSize,
648 _: &CachedRenderingData,
649 ) {
650 Self::draw_rectangle_impl(
651 &mut self.painter,
652 check_geometry!(size),
653 rect.background(),
654 rect.border_color(),
655 rect.border_width().get(),
656 rect.border_radius(),
657 );
658 }
659
660 fn draw_window_background(
661 &mut self,
662 _rect: Pin<&dyn RenderRectangle>,
663 _self_rc: &ItemRc,
664 _size: LogicalSize,
665 _cache: &CachedRenderingData,
666 ) {
667 }
669
670 fn draw_image(
671 &mut self,
672 image: Pin<&dyn RenderImage>,
673 item_rc: &ItemRc,
674 size: LogicalSize,
675 _: &CachedRenderingData,
676 ) {
677 self.draw_image_impl(item_rc, size, image);
678 }
679
680 fn draw_text(
681 &mut self,
682 text: Pin<&dyn RenderText>,
683 self_rc: &ItemRc,
684 size: LogicalSize,
685 _: &CachedRenderingData,
686 ) {
687 sharedparley::draw_text(self, text, Some(text.font_request(self_rc)), size);
688 }
689
690 fn draw_text_input(
691 &mut self,
692 text_input: Pin<&items::TextInput>,
693 self_rc: &ItemRc,
694 size: LogicalSize,
695 ) {
696 sharedparley::draw_text_input(
697 self,
698 text_input,
699 Some(text_input.font_request(self_rc)),
700 size,
701 Some(qt_password_character),
702 );
703 }
704
705 fn draw_path(&mut self, path: Pin<&items::Path>, item_rc: &ItemRc, size: LogicalSize) {
706 let (offset, path_events) = match path.fitted_path_events(item_rc) {
707 Some(offset_and_events) => offset_and_events,
708 None => return,
709 };
710 let rect: qttypes::QRectF = check_geometry!(size);
711 let fill_brush: qttypes::QBrush = into_qbrush(path.fill(), rect.width, rect.height);
712 let stroke_brush: qttypes::QBrush = into_qbrush(path.stroke(), rect.width, rect.height);
713 let stroke_width: f32 = path.stroke_width().get();
714 let stroke_pen_cap_style: i32 = match path.stroke_line_cap() {
715 LineCap::Butt => 0x00,
716 LineCap::Round => 0x20,
717 LineCap::Square => 0x10,
718 };
719 let pos = qttypes::QPoint { x: offset.x as _, y: offset.y as _ };
720 let mut painter_path = QPainterPath::default();
721
722 painter_path.set_fill_rule(match path.fill_rule() {
723 FillRule::Nonzero => key_generated::Qt_FillRule_WindingFill,
724 FillRule::Evenodd => key_generated::Qt_FillRule_OddEvenFill,
725 });
726
727 for x in path_events.iter() {
728 fn to_qpointf(p: Point) -> qttypes::QPointF {
729 qttypes::QPointF { x: p.x as _, y: p.y as _ }
730 }
731 match x {
732 lyon_path::Event::Begin { at } => {
733 painter_path.move_to(to_qpointf(at));
734 }
735 lyon_path::Event::Line { from: _, to } => {
736 painter_path.line_to(to_qpointf(to));
737 }
738 lyon_path::Event::Quadratic { from: _, ctrl, to } => {
739 painter_path.quad_to(to_qpointf(ctrl), to_qpointf(to));
740 }
741
742 lyon_path::Event::Cubic { from: _, ctrl1, ctrl2, to } => {
743 painter_path.cubic_to(to_qpointf(ctrl1), to_qpointf(ctrl2), to_qpointf(to));
744 }
745 lyon_path::Event::End { last: _, first: _, close } => {
746 if close {
748 painter_path.close()
749 }
750 }
751 }
752 }
753
754 let anti_alias: bool = path.anti_alias();
755
756 let painter: &mut QPainterPtr = &mut self.painter;
757 cpp! { unsafe [
758 painter as "QPainterPtr*",
759 pos as "QPoint",
760 mut painter_path as "QPainterPath",
761 fill_brush as "QBrush",
762 stroke_brush as "QBrush",
763 stroke_width as "float",
764 stroke_pen_cap_style as "int",
765 anti_alias as "bool"] {
766 (*painter)->save();
767 auto cleanup = qScopeGuard([&] { (*painter)->restore(); });
768 (*painter)->translate(pos);
769 (*painter)->setPen(stroke_width > 0 ? QPen(stroke_brush, stroke_width, Qt::SolidLine, Qt::PenCapStyle(stroke_pen_cap_style)) : Qt::NoPen);
770 (*painter)->setBrush(fill_brush);
771 (*painter)->setRenderHint(QPainter::Antialiasing, anti_alias);
772 (*painter)->drawPath(painter_path);
773 }}
774 }
775
776 fn draw_box_shadow(
777 &mut self,
778 box_shadow: Pin<&items::BoxShadow>,
779 item_rc: &ItemRc,
780 _size: LogicalSize,
781 ) {
782 let pixmap : qttypes::QPixmap = self.cache.get_or_update_cache_entry( item_rc, || {
783 let shadow_rect = check_geometry!(item_rc.geometry().size);
784
785 let source_size = qttypes::QSize {
786 width: shadow_rect.width.ceil() as _,
787 height: shadow_rect.height.ceil() as _,
788 };
789
790 let mut source_image =
791 qttypes::QImage::new(source_size, qttypes::ImageFormat::ARGB32_Premultiplied);
792 source_image.fill(qttypes::QColor::from_rgba_f(0., 0., 0., 0.));
793
794 let img = &mut source_image;
795 let mut painter_ = cpp!(unsafe [img as "QImage*"] -> QPainterPtr as "QPainterPtr" {
796 return std::make_unique<QPainter>(img);
797 });
798
799 Self::draw_rectangle_impl(
800 &mut painter_,
801 qttypes::QRectF { x: 0., y: 0., width: shadow_rect.width, height: shadow_rect.height },
802 Brush::SolidColor(box_shadow.color()),
803 Brush::default(),
804 0.,
805 LogicalBorderRadius::new_uniform(box_shadow.border_radius().get()),
806 );
807
808 drop(painter_);
809
810 let blur_radius = box_shadow.blur().get();
811
812 if blur_radius > 0. {
813 cpp! {
814 unsafe[img as "QImage*", blur_radius as "float"] -> qttypes::QPixmap as "QPixmap" {
815 QGraphicsScene scene;
816 auto pixmap_item = scene.addPixmap(QPixmap::fromImage(*img));
817
818 auto blur_effect = new QGraphicsBlurEffect;
819 blur_effect->setBlurRadius(blur_radius);
820 blur_effect->setBlurHints(QGraphicsBlurEffect::QualityHint);
821
822 pixmap_item->setGraphicsEffect(blur_effect);
825
826 QImage blurred_scene(img->width() + 2 * blur_radius, img->height() + 2 * blur_radius, QImage::Format_ARGB32_Premultiplied);
827 blurred_scene.fill(Qt::transparent);
828
829 QPainter p(&blurred_scene);
830 scene.render(&p,
831 QRectF(0, 0, blurred_scene.width(), blurred_scene.height()),
832 QRectF(-blur_radius, -blur_radius, blurred_scene.width(), blurred_scene.height()));
833 p.end();
834
835 return QPixmap::fromImage(blurred_scene);
836 }}
837 } else {
838 cpp! { unsafe[img as "QImage*"] -> qttypes::QPixmap as "QPixmap" {
839 return QPixmap::fromImage(*img);
840 }}
841 }
842 });
843
844 let blur_radius = box_shadow.blur();
845
846 let shadow_offset = qttypes::QPointF {
847 x: (box_shadow.offset_x() - blur_radius).get() as f64,
848 y: (box_shadow.offset_y() - blur_radius).get() as f64,
849 };
850
851 let painter: &mut QPainterPtr = &mut self.painter;
852 cpp! { unsafe [
853 painter as "QPainterPtr*",
854 shadow_offset as "QPointF",
855 pixmap as "QPixmap"
856 ] {
857 (*painter)->drawPixmap(shadow_offset, pixmap);
858 }}
859 }
860
861 fn visit_opacity(
862 &mut self,
863 opacity_item: Pin<&Opacity>,
864 item_rc: &ItemRc,
865 _size: LogicalSize,
866 ) -> RenderingResult {
867 let opacity = opacity_item.opacity();
868 if Opacity::need_layer(item_rc, opacity) {
869 self.render_and_blend_layer(opacity, item_rc)
870 } else {
871 self.apply_opacity(opacity);
872 self.cache.release(item_rc);
873 RenderingResult::ContinueRenderingChildren
874 }
875 }
876
877 fn visit_layer(
878 &mut self,
879 layer_item: Pin<&Layer>,
880 self_rc: &ItemRc,
881 _size: LogicalSize,
882 ) -> RenderingResult {
883 if layer_item.cache_rendering_hint() {
884 self.render_and_blend_layer(1.0, self_rc)
885 } else {
886 RenderingResult::ContinueRenderingChildren
887 }
888 }
889
890 fn combine_clip(
891 &mut self,
892 rect: LogicalRect,
893 radius: LogicalBorderRadius,
894 border_width: LogicalLength,
895 ) -> bool {
896 let mut border_width: f32 = border_width.get();
897 let mut clip_rect = qttypes::QRectF {
898 x: rect.min_x() as _,
899 y: rect.min_y() as _,
900 width: rect.width() as _,
901 height: rect.height() as _,
902 };
903 adjust_rect_and_border_for_inner_drawing(&mut clip_rect, &mut border_width);
904 let painter: &mut QPainterPtr = &mut self.painter;
905 let top_left_radius = radius.top_left;
906 let top_right_radius = radius.top_right;
907 let bottom_left_radius = radius.bottom_left;
908 let bottom_right_radius = radius.bottom_right;
909 cpp! { unsafe [
910 painter as "QPainterPtr*",
911 clip_rect as "QRectF",
912 top_left_radius as "float",
913 top_right_radius as "float",
914 bottom_right_radius as "float",
915 bottom_left_radius as "float"] -> bool as "bool" {
916 if (top_left_radius <= 0 && top_right_radius <= 0 && bottom_right_radius <= 0 && bottom_left_radius <= 0) {
917 (*painter)->setClipRect(clip_rect, Qt::IntersectClip);
918 } else {
919 QPainterPath path = to_painter_path(clip_rect, top_left_radius, top_right_radius, bottom_right_radius, bottom_left_radius);
920 (*painter)->setClipPath(path, Qt::IntersectClip);
921 }
922 return !(*painter)->clipBoundingRect().isEmpty();
923 }}
924 }
925
926 fn get_current_clip(&self) -> LogicalRect {
927 let painter: &QPainterPtr = &self.painter;
928 let res = cpp! { unsafe [painter as "const QPainterPtr*" ] -> qttypes::QRectF as "QRectF" {
929 return (*painter)->clipBoundingRect();
930 }};
931 LogicalRect::new(
932 LogicalPoint::new(res.x as _, res.y as _),
933 LogicalSize::new(res.width as _, res.height as _),
934 )
935 }
936
937 fn save_state(&mut self) {
938 self.painter.save()
939 }
940
941 fn restore_state(&mut self) {
942 self.painter.restore()
943 }
944
945 fn scale_factor(&self) -> f32 {
946 1.
947 }
951
952 fn draw_cached_pixmap(
953 &mut self,
954 _item_rc: &ItemRc,
955 update_fn: &dyn Fn(&mut dyn FnMut(u32, u32, &[u8])),
956 ) {
957 update_fn(&mut |width: u32, height: u32, data: &[u8]| {
958 let data = data.as_ptr();
959 let painter: &mut QPainterPtr = &mut self.painter;
960 cpp! { unsafe [painter as "QPainterPtr*", width as "int", height as "int", data as "const unsigned char *"] {
961 QImage img(data, width, height, width * 4, QImage::Format_RGBA8888_Premultiplied);
962 (*painter)->drawImage(QPoint(), img);
963 }}
964 })
965 }
966
967 fn draw_string(&mut self, string: &str, color: Color) {
968 sharedparley::draw_text(
969 self,
970 std::pin::pin!((SharedString::from(string), Brush::from(color))),
971 None,
972 LogicalSize::new(1., 1.), );
974 }
975
976 fn draw_image_direct(&mut self, _image: i_slint_core::graphics::Image) {
977 todo!()
978 }
979
980 fn window(&self) -> &i_slint_core::window::WindowInner {
981 i_slint_core::window::WindowInner::from_pub(self.window)
982 }
983
984 fn as_any(&mut self) -> Option<&mut dyn std::any::Any> {
985 Some(&mut self.painter)
986 }
987
988 fn translate(&mut self, distance: LogicalVector) {
989 let x: f32 = distance.x;
990 let y: f32 = distance.y;
991 let painter: &mut QPainterPtr = &mut self.painter;
992 cpp! { unsafe [painter as "QPainterPtr*", x as "float", y as "float"] {
993 (*painter)->translate(x, y);
994 }}
995 }
996
997 fn rotate(&mut self, angle_in_degrees: f32) {
998 let painter: &mut QPainterPtr = &mut self.painter;
999 cpp! { unsafe [painter as "QPainterPtr*", angle_in_degrees as "float"] {
1000 (*painter)->rotate(angle_in_degrees);
1001 }}
1002 }
1003
1004 fn scale(&mut self, x_factor: f32, y_factor: f32) {
1005 let painter: &mut QPainterPtr = &mut self.painter;
1006 cpp! { unsafe [painter as "QPainterPtr*", x_factor as "float", y_factor as "float"] {
1007 (*painter)->scale(x_factor, y_factor);
1008 }}
1009 }
1010
1011 fn apply_opacity(&mut self, opacity: f32) {
1012 let painter: &mut QPainterPtr = &mut self.painter;
1013 cpp! { unsafe [painter as "QPainterPtr*", opacity as "float"] {
1014 (*painter)->setOpacity((*painter)->opacity() * opacity);
1015 }}
1016 }
1017}
1018
1019#[derive(Clone)]
1020pub enum GlyphBrush {
1021 Fill(qttypes::QBrush),
1022 Stroke(qttypes::QPen),
1023}
1024
1025impl GlyphRenderer for QtItemRenderer<'_> {
1026 type PlatformBrush = GlyphBrush;
1027
1028 fn platform_text_fill_brush(
1029 &mut self,
1030 brush: i_slint_core::Brush,
1031 size: LogicalSize,
1032 ) -> Option<Self::PlatformBrush> {
1033 Some(GlyphBrush::Fill(into_qbrush(brush, size.width as _, size.height as _)))
1034 }
1035
1036 fn platform_brush_for_color(
1037 &mut self,
1038 color: &i_slint_core::Color,
1039 ) -> Option<Self::PlatformBrush> {
1040 let color: u32 = color.as_argb_encoded();
1041 Some(GlyphBrush::Fill(cpp!(unsafe [color as "QRgb"] -> qttypes::QBrush as "QBrush" {
1042 return QBrush(QColor::fromRgba(color));
1043 })))
1044 }
1045
1046 fn platform_text_stroke_brush(
1047 &mut self,
1048 brush: i_slint_core::Brush,
1049 physical_stroke_width: f32,
1050 size: LogicalSize,
1051 ) -> Option<Self::PlatformBrush> {
1052 let brush = into_qbrush(brush, size.width as _, size.height as _);
1053 Some(GlyphBrush::Stroke(
1054 cpp!(unsafe [brush as "QBrush", physical_stroke_width as "float"] -> qttypes::QPen as "QPen" {
1055 QPen pen(brush, physical_stroke_width);
1056 pen.setJoinStyle(Qt::MiterJoin);
1057 return pen;
1058 }),
1059 ))
1060 }
1061
1062 fn draw_glyph_run(
1063 &mut self,
1064 font: &sharedparley::parley::FontData,
1065 font_size: sharedparley::PhysicalLength,
1066 brush: Self::PlatformBrush,
1067 y_offset: sharedparley::PhysicalLength,
1068 glyphs_it: &mut dyn Iterator<Item = sharedparley::parley::layout::Glyph>,
1069 ) {
1070 let Some(mut raw_font) = FONT_CACHE.with(|cache| cache.borrow_mut().font(font)) else {
1071 return;
1072 };
1073
1074 raw_font.set_pixel_size(font_size.get());
1075
1076 let (glyph_indices, positions): (Vec<u32>, Vec<qttypes::QPointF>) = glyphs_it
1077 .into_iter()
1078 .map(|g| {
1079 (g.id, qttypes::QPointF { x: g.x as f64, y: g.y as f64 + y_offset.get() as f64 })
1080 })
1081 .unzip();
1082
1083 let glyph_indices_ptr = glyph_indices.as_ptr();
1084 let glyph_positions_ptr = positions.as_ptr();
1085 let size: u32 = glyph_indices.len() as u32;
1086 if size == 0 {
1087 return;
1088 }
1089
1090 let painter: &mut QPainterPtr = &mut self.painter;
1091
1092 match brush {
1093 GlyphBrush::Fill(qt_brush) => {
1094 cpp! { unsafe [painter as "QPainterPtr*", glyph_indices_ptr as "const quint32 *", glyph_positions_ptr as "const QPointF *", size as "int", raw_font as "QRawFont", qt_brush as "QBrush"] {
1095 (*painter)->setPen(QPen(qt_brush, 1));
1097 (*painter)->setBrush(Qt::NoBrush);
1098
1099 QGlyphRun glyphRun;
1100 glyphRun.setRawFont(raw_font);
1101 glyphRun.setRawData(glyph_indices_ptr, glyph_positions_ptr, size);
1102 (*painter)->drawGlyphRun(QPointF(0, 0), glyphRun);
1103 }}
1104 }
1105 GlyphBrush::Stroke(qt_pen) => {
1106 cpp! { unsafe [painter as "QPainterPtr*", glyph_indices_ptr as "const quint32 *", glyph_positions_ptr as "const QPointF *", size as "int", raw_font as "QRawFont", qt_pen as "QPen"] {
1107 (*painter)->setPen(qt_pen);
1108 (*painter)->setBrush(Qt::NoBrush);
1109
1110 QPainterPath path;
1111 for (int i = 0; i < size; i++) {
1112 QPainterPath glyphPath = raw_font.pathForGlyph(glyph_indices_ptr[i]);
1113 glyphPath.translate(glyph_positions_ptr[i]);
1114 path.addPath(glyphPath);
1115 }
1116 (*painter)->drawPath(path);
1117 }}
1118 }
1119 }
1120 }
1121
1122 fn fill_rectangle(
1123 &mut self,
1124 physical_rect: sharedparley::PhysicalRect,
1125 color: i_slint_core::Color,
1126 ) {
1127 let rect = qttypes::QRectF {
1128 x: physical_rect.min_x() as _,
1129 y: physical_rect.min_y() as _,
1130 width: physical_rect.width() as _,
1131 height: physical_rect.height() as _,
1132 };
1133 let color: u32 = color.as_argb_encoded();
1134 let painter: &mut QPainterPtr = &mut self.painter;
1135 cpp! { unsafe [painter as "QPainterPtr*", color as "QRgb", rect as "QRectF"] {
1136 (*painter)->fillRect(rect, QBrush(QColor::fromRgba(color)));
1137 }}
1138 }
1139}
1140
1141cpp_class! {pub unsafe struct QRawFont as "QRawFont"}
1142
1143impl QRawFont {
1144 pub fn load_from_data(&mut self, data: &[u8], pixel_size: f32) {
1145 let font_data = qttypes::QByteArray::from(data);
1146 cpp! { unsafe [ self as "QRawFont*", font_data as "QByteArray", pixel_size as "float"] {
1147 self->loadFromData(font_data, pixel_size, QFont::PreferDefaultHinting);
1148 }}
1149 }
1150
1151 pub fn set_pixel_size(&mut self, pixel_size: f32) {
1152 cpp! { unsafe [ self as "QRawFont*", pixel_size as "float"] {
1153 self->setPixelSize(pixel_size);
1154 }}
1155 }
1156
1157 pub fn is_valid(&self) -> bool {
1158 cpp! { unsafe [ self as "const QRawFont*"] -> bool as "bool" {
1159 return self->isValid();
1160 }}
1161 }
1162}
1163
1164pub struct FontCache {
1165 fonts: HashMap<(u64, u32), Option<QRawFont>>,
1167}
1168
1169impl Default for FontCache {
1170 fn default() -> Self {
1171 Self { fonts: Default::default() }
1172 }
1173}
1174
1175impl FontCache {
1176 pub fn font(&mut self, font: &parley::FontData) -> Option<QRawFont> {
1177 self.fonts
1178 .entry((font.data.id(), font.index))
1179 .or_insert_with(move || {
1180 let mut raw_font = QRawFont::default();
1181 raw_font.load_from_data(font.data.as_ref(), 12.0);
1182 if raw_font.is_valid() {
1183 Some(raw_font)
1184 } else {
1185 None
1186 }
1187 })
1188 .clone()
1189 }
1190}
1191
1192thread_local! {
1193 pub static FONT_CACHE: RefCell<FontCache> = RefCell::new(Default::default())
1194}
1195
1196fn shared_image_buffer_to_pixmap(buffer: &SharedImageBuffer) -> Option<qttypes::QPixmap> {
1197 let (format, bytes_per_line, buffer_ptr) = match buffer {
1198 SharedImageBuffer::RGBA8(img) => {
1199 (qttypes::ImageFormat::RGBA8888, img.width() * 4, img.as_bytes().as_ptr())
1200 }
1201 SharedImageBuffer::RGBA8Premultiplied(img) => {
1202 (qttypes::ImageFormat::RGBA8888_Premultiplied, img.width() * 4, img.as_bytes().as_ptr())
1203 }
1204 SharedImageBuffer::RGB8(img) => {
1205 (qttypes::ImageFormat::RGB888, img.width() * 3, img.as_bytes().as_ptr())
1206 }
1207 };
1208 let width: i32 = buffer.width() as _;
1209 let height: i32 = buffer.height() as _;
1210 let pixmap = cpp! { unsafe [format as "QImage::Format", width as "int", height as "int", bytes_per_line as "uint32_t", buffer_ptr as "const uchar *"] -> qttypes::QPixmap as "QPixmap" {
1211 QImage img(buffer_ptr, width, height, bytes_per_line, format);
1212 return QPixmap::fromImage(img);
1213 } };
1214 Some(pixmap)
1215}
1216
1217pub(crate) fn image_to_pixmap(
1218 image: &ImageInner,
1219 source_size: Option<euclid::Size2D<u32, PhysicalPx>>,
1220) -> Option<qttypes::QPixmap> {
1221 shared_image_buffer_to_pixmap(&image.render_to_buffer(source_size)?)
1222}
1223
1224impl QtItemRenderer<'_> {
1225 fn draw_image_impl(
1226 &mut self,
1227 item_rc: &ItemRc,
1228 size: LogicalSize,
1229 image: Pin<&dyn i_slint_core::item_rendering::RenderImage>,
1230 ) {
1231 let source_rect = image.source_clip();
1232
1233 let pixmap: qttypes::QPixmap = self.cache.get_or_update_cache_entry(item_rc, || {
1234 let source = image.source();
1235 let origin = source.size();
1236 let source: &ImageInner = (&source).into();
1237
1238 let scale_factor = ScaleFactor::new(self.scale_factor());
1240 let t = (image.target_size() * scale_factor).cast();
1241
1242 let source_size = if source.is_svg() {
1243 let has_source_clipping = source_rect.map_or(false, |rect| {
1244 rect.origin.x != 0
1245 || rect.origin.y != 0
1246 || !rect.size.width != t.width
1247 || !rect.size.height != t.height
1248 });
1249 if has_source_clipping {
1250 None
1252 } else {
1253 Some(
1254 i_slint_core::graphics::fit(
1255 image.image_fit(),
1256 t.cast(),
1257 IntRect::from_size(origin.cast()),
1258 scale_factor,
1259 Default::default(), image.tiling(),
1261 )
1262 .size
1263 .cast(),
1264 )
1265 }
1266 } else {
1267 None
1268 };
1269
1270 image_to_pixmap(source, source_size).map_or_else(
1271 Default::default,
1272 |mut pixmap: qttypes::QPixmap| {
1273 let colorize = image.colorize();
1274 if !colorize.is_transparent() {
1275 let pixmap_size = pixmap.size();
1276 let brush: qttypes::QBrush = into_qbrush(
1277 colorize,
1278 pixmap_size.width.into(),
1279 pixmap_size.height.into(),
1280 );
1281 cpp!(unsafe [mut pixmap as "QPixmap", brush as "QBrush"] {
1282 QPainter p(&pixmap);
1283 p.setCompositionMode(QPainter::CompositionMode_SourceIn);
1284 p.fillRect(QRect(QPoint(), pixmap.size()), brush);
1285 });
1286 }
1287 pixmap
1288 },
1289 )
1290 });
1291
1292 let image_size = pixmap.size();
1293 let source_rect = source_rect
1294 .unwrap_or_else(|| euclid::rect(0, 0, image_size.width as _, image_size.height as _));
1295 let scale_factor = ScaleFactor::new(self.scale_factor());
1296
1297 let fit = if let &i_slint_core::ImageInner::NineSlice(ref nine) = (&image.source()).into() {
1298 i_slint_core::graphics::fit9slice(
1299 nine.0.size(),
1300 nine.1,
1301 size * scale_factor,
1302 scale_factor,
1303 image.alignment(),
1304 image.tiling(),
1305 )
1306 .collect::<Vec<_>>()
1307 } else {
1308 vec![i_slint_core::graphics::fit(
1309 image.image_fit(),
1310 size * scale_factor,
1311 source_rect,
1312 scale_factor,
1313 image.alignment(),
1314 image.tiling(),
1315 )]
1316 };
1317
1318 for fit in fit {
1319 let dest_rect = qttypes::QRectF {
1320 x: fit.offset.x as _,
1321 y: fit.offset.y as _,
1322 width: fit.size.width as _,
1323 height: fit.size.height as _,
1324 };
1325 let source_rect = qttypes::QRectF {
1326 x: fit.clip_rect.origin.x as _,
1327 y: fit.clip_rect.origin.y as _,
1328 width: fit.clip_rect.size.width as _,
1329 height: fit.clip_rect.size.height as _,
1330 };
1331
1332 let painter: &mut QPainterPtr = &mut self.painter;
1333 let smooth: bool = image.rendering() == ImageRendering::Smooth;
1334 if let Some(offset) = fit.tiled {
1335 let scale_x: f32 = fit.source_to_target_x;
1336 let scale_y: f32 = fit.source_to_target_y;
1337 let offset = qttypes::QPoint { x: offset.x as _, y: offset.y as _ };
1338 cpp! { unsafe [
1339 painter as "QPainterPtr*", pixmap as "QPixmap", source_rect as "QRectF",
1340 dest_rect as "QRectF", smooth as "bool", scale_x as "float", scale_y as "float",
1341 offset as "QPoint"
1342 ] {
1343 (*painter)->save();
1344 (*painter)->setRenderHint(QPainter::SmoothPixmapTransform, smooth);
1345 auto transform = QTransform::fromScale(1 / scale_x, 1 / scale_y);
1346 auto scaled_destination = (dest_rect * transform).boundingRect();
1347 QPixmap source_pixmap = pixmap.copy(source_rect.toRect());
1348 (*painter)->scale(scale_x, scale_y);
1349 (*painter)->drawTiledPixmap(scaled_destination, source_pixmap, offset);
1350 (*painter)->restore();
1351 }
1352 };
1353 } else {
1354 cpp! { unsafe [
1355 painter as "QPainterPtr*",
1356 pixmap as "QPixmap",
1357 source_rect as "QRectF",
1358 dest_rect as "QRectF",
1359 smooth as "bool"] {
1360 (*painter)->save();
1361 (*painter)->setRenderHint(QPainter::SmoothPixmapTransform, smooth);
1362 (*painter)->drawPixmap(dest_rect, pixmap, source_rect);
1363 (*painter)->restore();
1364 }};
1365 }
1366 }
1367 }
1368
1369 fn draw_rectangle_impl(
1370 painter: &mut QPainterPtr,
1371 mut rect: qttypes::QRectF,
1372 brush: Brush,
1373 border_color: Brush,
1374 mut border_width: f32,
1375 border_radius: LogicalBorderRadius,
1376 ) {
1377 if border_color.is_transparent() {
1378 border_width = 0.;
1379 };
1380 let brush: qttypes::QBrush = into_qbrush(brush, rect.width, rect.height);
1381 let border_color: qttypes::QBrush = into_qbrush(border_color, rect.width, rect.height);
1382 let top_left_radius = border_radius.top_left;
1383 let top_right_radius = border_radius.top_right;
1384 let bottom_left_radius = border_radius.bottom_left;
1385 let bottom_right_radius = border_radius.bottom_right;
1386 border_width = border_width.min(rect.height.min(rect.width) as f32 / 2.);
1387 cpp! { unsafe [
1388 painter as "QPainterPtr*",
1389 brush as "QBrush",
1390 border_color as "QBrush",
1391 border_width as "float",
1392 top_left_radius as "float",
1393 top_right_radius as "float",
1394 bottom_left_radius as "float",
1395 bottom_right_radius as "float",
1396 mut rect as "QRectF"] {
1397 (*painter)->save();
1398 auto cleanup = qScopeGuard([&] { (*painter)->restore(); });
1399 (*painter)->setBrush(brush);
1400 QPen pen = border_width > 0 ? QPen(border_color, border_width, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin) : Qt::NoPen;
1401 if (top_left_radius <= 0 && top_right_radius <= 0 && bottom_left_radius <= 0 && bottom_right_radius <= 0) {
1402 if (!border_color.isOpaque() && border_width > 1) {
1403 (*painter)->setPen(Qt::NoPen);
1406 (*painter)->drawRect(rect);
1407 (*painter)->setBrush(QBrush());
1408 }
1409 rect.adjust(border_width / 2, border_width / 2, -border_width / 2, -border_width / 2);
1410 (*painter)->setPen(pen);
1411 (*painter)->drawRect(rect);
1412 } else {
1413 if (!border_color.isOpaque() && border_width > 1) {
1414 float tl_r = qFuzzyIsNull(top_left_radius) ? top_left_radius : qMax(border_width/2, top_left_radius);
1416 float tr_r = qFuzzyIsNull(top_right_radius) ? top_right_radius : qMax(border_width/2, top_right_radius);
1417 float br_r = qFuzzyIsNull(bottom_right_radius) ? bottom_right_radius : qMax(border_width/2, bottom_right_radius);
1418 float bl_r = qFuzzyIsNull(bottom_left_radius) ? bottom_left_radius : qMax(border_width/2, bottom_left_radius);
1419 (*painter)->setPen(Qt::NoPen);
1422 (*painter)->drawPath(to_painter_path(rect, tl_r, tr_r, br_r, bl_r));
1423 (*painter)->setBrush(QBrush());
1424 }
1425 float tl_r = qMax(0.0f, top_left_radius - border_width / 2);
1428 float tr_r = qMax(0.0f, top_right_radius - border_width / 2);
1429 float br_r = qMax(0.0f, bottom_right_radius - border_width / 2);
1430 float bl_r = qMax(0.0f, bottom_left_radius - border_width / 2);
1431 rect.adjust(border_width / 2, border_width / 2, -border_width / 2, -border_width / 2);
1432 (*painter)->setPen(pen);
1433 (*painter)->drawPath(to_painter_path(rect, tl_r, tr_r, br_r, bl_r));
1434 }
1435 }}
1436 }
1437
1438 fn render_layer(
1439 &mut self,
1440 item_rc: &ItemRc,
1441 layer_size_fn: &dyn Fn() -> LogicalSize,
1442 ) -> qttypes::QPixmap {
1443 self.cache.get_or_update_cache_entry(item_rc, || {
1444 let painter: &mut QPainterPtr = &mut self.painter;
1445 let dpr = cpp! { unsafe [painter as "QPainterPtr*"] -> f32 as "float" {
1446 return (*painter)->paintEngine()->paintDevice()->devicePixelRatioF();
1447 }};
1448
1449 let layer_size = layer_size_fn();
1450 let layer_size = qttypes::QSize {
1451 width: (layer_size.width * dpr) as _,
1452 height: (layer_size.height * dpr) as _,
1453 };
1454
1455 let mut layer_image = qttypes::QImage::new(layer_size, qttypes::ImageFormat::ARGB32_Premultiplied);
1456 layer_image.fill(qttypes::QColor::from_rgba_f(0., 0., 0., 0.));
1457
1458 *self.metrics.layers_created.as_mut().unwrap() += 1;
1459
1460 let img_ref: &mut qttypes::QImage = &mut layer_image;
1461 let mut layer_painter = cpp!(unsafe [img_ref as "QImage*", dpr as "float"] -> QPainterPtr as "QPainterPtr" {
1462 img_ref->setDevicePixelRatio(dpr);
1463 auto painter = std::make_unique<QPainter>(img_ref);
1464 painter->setClipRect(0, 0, img_ref->width(), img_ref->height());
1465 painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
1466 return painter;
1467 });
1468
1469 std::mem::swap(&mut self.painter, &mut layer_painter);
1470
1471 let window_adapter = self.window().window_adapter();
1472
1473 i_slint_core::item_rendering::render_item_children(
1474 self,
1475 &item_rc.item_tree(),
1476 item_rc.index() as isize, &window_adapter
1477 );
1478
1479 std::mem::swap(&mut self.painter, &mut layer_painter);
1480 drop(layer_painter);
1481
1482 qttypes::QPixmap::from(layer_image)
1483 })
1484 }
1485
1486 fn render_and_blend_layer(&mut self, alpha_tint: f32, self_rc: &ItemRc) -> RenderingResult {
1487 let current_clip = self.get_current_clip();
1488 let mut layer_image = self.render_layer(self_rc, &|| {
1489 let children_rect = i_slint_core::properties::evaluate_no_tracking(|| {
1491 self_rc.geometry().union(
1492 &i_slint_core::item_rendering::item_children_bounding_rect(
1493 &self_rc.item_tree(),
1494 self_rc.index() as isize,
1495 ¤t_clip,
1496 ),
1497 )
1498 });
1499 children_rect.size
1500 });
1501 self.save_state();
1502 self.apply_opacity(alpha_tint);
1503 {
1504 let painter: &mut QPainterPtr = &mut self.painter;
1505 let layer_image_ref: &mut qttypes::QPixmap = &mut layer_image;
1506 cpp! { unsafe [
1507 painter as "QPainterPtr*",
1508 layer_image_ref as "QPixmap*"
1509 ] {
1510 (*painter)->drawPixmap(0, 0, *layer_image_ref);
1511 }}
1512 }
1513 self.restore_state();
1514 RenderingResult::ContinueRenderingWithoutChildren
1515 }
1516}
1517
1518cpp! {{
1519 struct QWidgetDeleteLater
1520 {
1521 void operator()(QWidget *widget_ptr)
1522 {
1523 if (widget_ptr->parent()) {
1524 widget_ptr->hide();
1526 widget_ptr->deleteLater();
1527 } else {
1528 delete widget_ptr;
1530 }
1531 }
1532 };
1533}}
1534
1535cpp_class!(pub(crate) unsafe struct QWidgetPtr as "std::unique_ptr<QWidget, QWidgetDeleteLater>");
1536
1537pub struct QtWindow {
1538 widget_ptr: QWidgetPtr,
1539 pub(crate) window: i_slint_core::api::Window,
1540 self_weak: Weak<Self>,
1541
1542 rendering_metrics_collector: RefCell<Option<Rc<RenderingMetricsCollector>>>,
1543
1544 cache: ItemCache<qttypes::QPixmap>,
1545
1546 tree_structure_changed: RefCell<bool>,
1547
1548 color_scheme: OnceCell<Pin<Box<Property<ColorScheme>>>>,
1549}
1550
1551impl Drop for QtWindow {
1552 fn drop(&mut self) {
1553 let widget_ptr = self.widget_ptr();
1554 cpp! {unsafe [widget_ptr as "SlintWidget*"] {
1555 widget_ptr->rust_window = nullptr;
1557 }};
1558 }
1559}
1560
1561impl QtWindow {
1562 pub fn new() -> Rc<Self> {
1563 let rc = Rc::new_cyclic(|self_weak| {
1564 let window_ptr = self_weak.clone().into_raw();
1565 let widget_ptr = cpp! {unsafe [window_ptr as "void*"] -> QWidgetPtr as "std::unique_ptr<QWidget, QWidgetDeleteLater>" {
1566 ensure_initialized(true);
1567 auto widget = std::unique_ptr<SlintWidget, QWidgetDeleteLater>(new SlintWidget, QWidgetDeleteLater());
1568
1569 auto accessibility = new Slint_accessible_window(widget.get(), window_ptr);
1570 QAccessible::registerAccessibleInterface(accessibility);
1571
1572 return widget;
1573 }};
1574
1575 QtWindow {
1576 widget_ptr,
1577 window: i_slint_core::api::Window::new(self_weak.clone() as _),
1578 self_weak: self_weak.clone(),
1579 rendering_metrics_collector: Default::default(),
1580 cache: Default::default(),
1581 tree_structure_changed: RefCell::new(false),
1582 color_scheme: Default::default(),
1583 }
1584 });
1585 let widget_ptr = rc.widget_ptr();
1586 let rust_window = Rc::as_ptr(&rc);
1587 cpp! {unsafe [widget_ptr as "SlintWidget*", rust_window as "void*"] {
1588 widget_ptr->rust_window = rust_window;
1589 }};
1590 ALL_WINDOWS.with(|aw| aw.borrow_mut().push(rc.self_weak.clone()));
1591 rc
1592 }
1593
1594 pub fn widget_ptr(&self) -> NonNull<()> {
1596 unsafe { std::mem::transmute_copy::<QWidgetPtr, NonNull<_>>(&self.widget_ptr) }
1597 }
1598
1599 fn paint_event(&self, painter: QPainterPtr) {
1600 let runtime_window = WindowInner::from_pub(&self.window);
1601 let window_adapter = runtime_window.window_adapter();
1602 runtime_window.draw_contents(|components| {
1603 i_slint_core::animations::update_animations();
1604 let mut renderer = QtItemRenderer {
1605 painter,
1606 cache: &self.cache,
1607 window: &self.window,
1608 metrics: RenderingMetrics { layers_created: Some(0), ..Default::default() },
1609 };
1610
1611 for (component, origin) in components {
1612 if let Some(component) = ItemTreeWeak::upgrade(&component) {
1613 i_slint_core::item_rendering::render_component_items(
1614 &component,
1615 &mut renderer,
1616 *origin,
1617 &window_adapter,
1618 );
1619 }
1620 }
1621
1622 if let Some(collector) = &*self.rendering_metrics_collector.borrow() {
1623 let metrics = renderer.metrics.clone();
1624 collector.measure_frame_rendered(&mut renderer, metrics);
1625 }
1626
1627 if self.window.has_active_animations() {
1628 self.request_redraw();
1629 }
1630 });
1631
1632 if self.tree_structure_changed.replace(false) {
1634 let widget_ptr = self.widget_ptr();
1635 cpp! { unsafe [widget_ptr as "QWidget*"] {
1636 auto accessible = dynamic_cast<Slint_accessible_window*>(QAccessible::queryAccessibleInterface(widget_ptr));
1637 if (accessible->isUsed()) { accessible->updateAccessibilityTree(); }
1638 }};
1639 }
1640
1641 timer_event();
1642 }
1643
1644 fn resize_event(&self, size: qttypes::QSize) {
1645 self.window().dispatch_event(WindowEvent::Resized {
1646 size: i_slint_core::api::LogicalSize::new(size.width as _, size.height as _),
1647 });
1648 }
1649
1650 fn mouse_event(&self, event: MouseEvent) {
1651 WindowInner::from_pub(&self.window).process_mouse_input(event);
1652 timer_event();
1653 }
1654
1655 fn key_event(&self, key: i32, text: qttypes::QString, released: bool, repeat: bool) {
1656 i_slint_core::animations::update_animations();
1657 let text: String = text.into();
1658
1659 let text = qt_key_to_string(key as key_generated::Qt_Key, text);
1660
1661 let event = if released {
1662 WindowEvent::KeyReleased { text }
1663 } else if repeat {
1664 WindowEvent::KeyPressRepeated { text }
1665 } else {
1666 WindowEvent::KeyPressed { text }
1667 };
1668 self.window.dispatch_event(event);
1669
1670 timer_event();
1671 }
1672
1673 fn window_state_event(&self) {
1674 let widget_ptr = self.widget_ptr();
1675
1676 let minimized = cpp! { unsafe [widget_ptr as "QWidget*"] -> bool as "bool" {
1683 return widget_ptr->isMinimized();
1684 }};
1685
1686 if minimized != self.window().is_minimized() {
1687 self.window().set_minimized(minimized);
1688 }
1689
1690 let maximized = cpp! { unsafe [widget_ptr as "QWidget*"] -> bool as "bool" {
1691 return widget_ptr->isMaximized();
1692 }};
1693
1694 if maximized != self.window().is_maximized() {
1695 self.window().set_maximized(maximized);
1696 }
1697
1698 let fullscreen = cpp! { unsafe [widget_ptr as "QWidget*"] -> bool as "bool" {
1699 return widget_ptr->isFullScreen();
1700 }};
1701
1702 if fullscreen != self.window().is_fullscreen() {
1703 self.window().set_fullscreen(fullscreen);
1704 }
1705 }
1706}
1707
1708impl WindowAdapter for QtWindow {
1709 fn window(&self) -> &i_slint_core::api::Window {
1710 &self.window
1711 }
1712
1713 fn renderer(&self) -> &dyn Renderer {
1714 self
1715 }
1716
1717 fn set_visible(&self, visible: bool) -> Result<(), PlatformError> {
1718 if let Some(xdg_app_id) = WindowInner::from_pub(&self.window)
1719 .xdg_app_id()
1720 .map(|s| qttypes::QString::from(s.as_str()))
1721 {
1722 cpp! {unsafe [xdg_app_id as "QString"] {
1723 QGuiApplication::setDesktopFileName(xdg_app_id);
1724 }};
1725 }
1726
1727 if visible {
1728 let widget_ptr = self.widget_ptr();
1729 cpp! {unsafe [widget_ptr as "QWidget*"] {
1730 widget_ptr->show();
1731 }};
1732 let qt_platform_name = cpp! {unsafe [] -> qttypes::QString as "QString" {
1733 return QGuiApplication::platformName();
1734 }};
1735 *self.rendering_metrics_collector.borrow_mut() = RenderingMetricsCollector::new(
1736 &format!("Qt backend (platform {})", qt_platform_name),
1737 );
1738 Ok(())
1739 } else {
1740 self.rendering_metrics_collector.take();
1741 let widget_ptr = self.widget_ptr();
1742 cpp! {unsafe [widget_ptr as "QWidget*"] {
1743
1744 bool wasVisible = widget_ptr->isVisible();
1745
1746 widget_ptr->hide();
1747 if (wasVisible) {
1748 auto windows = QGuiApplication::topLevelWindows();
1751 bool visible_windows_left = std::any_of(windows.begin(), windows.end(), [](auto window) {
1752 return window->isVisible() || window->transientParent();
1753 });
1754 g_lastWindowClosed = !visible_windows_left;
1755 }
1756 }};
1757
1758 Ok(())
1759 }
1760 }
1761
1762 fn position(&self) -> Option<i_slint_core::api::PhysicalPosition> {
1763 let widget_ptr = self.widget_ptr();
1764 let qp = cpp! {unsafe [widget_ptr as "QWidget*"] -> qttypes::QPoint as "QPoint" {
1765 return widget_ptr->pos();
1766 }};
1767 i_slint_core::api::LogicalPosition::new(qp.x as _, qp.y as _)
1769 .to_physical(self.window().scale_factor())
1770 .into()
1771 }
1772
1773 fn set_position(&self, position: i_slint_core::api::WindowPosition) {
1774 let physical_position = position.to_physical(self.window().scale_factor());
1775 let widget_ptr = self.widget_ptr();
1776 let pos = qttypes::QPoint { x: physical_position.x as _, y: physical_position.y as _ };
1777 cpp! {unsafe [widget_ptr as "QWidget*", pos as "QPoint"] {
1778 widget_ptr->move(pos);
1779 }};
1780 }
1781
1782 fn set_size(&self, size: i_slint_core::api::WindowSize) {
1783 let logical_size = size.to_logical(self.window().scale_factor());
1784 let widget_ptr = self.widget_ptr();
1785 let sz: qttypes::QSize = into_qsize(logical_size);
1786
1787 cpp! {unsafe [widget_ptr as "QWidget*", sz as "QSize"] {
1789 widget_ptr->resize(sz);
1790 }};
1791
1792 self.resize_event(sz);
1793 }
1794
1795 fn size(&self) -> i_slint_core::api::PhysicalSize {
1796 let widget_ptr = self.widget_ptr();
1797 let s = cpp! {unsafe [widget_ptr as "QWidget*"] -> qttypes::QSize as "QSize" {
1798 return widget_ptr->size();
1799 }};
1800 i_slint_core::api::PhysicalSize::new(s.width as _, s.height as _)
1801 }
1802
1803 fn request_redraw(&self) {
1804 let widget_ptr = self.widget_ptr();
1805 cpp! {unsafe [widget_ptr as "QWidget*"] {
1806 if (widget_ptr->parentWidget()) {
1809 widget_ptr->update();
1810 } else if (auto w = widget_ptr->window()->windowHandle()) {
1811 w->requestUpdate();
1812 }
1813 }}
1814 }
1815
1816 fn update_window_properties(&self, properties: i_slint_core::window::WindowProperties<'_>) {
1818 let widget_ptr = self.widget_ptr();
1819 let title: qttypes::QString = properties.title().as_str().into();
1820 let Some(window_item) = WindowInner::from_pub(&self.window).window_item() else { return };
1821 let window_item = window_item.as_pin_ref();
1822 let no_frame = window_item.no_frame();
1823 let always_on_top = window_item.always_on_top();
1824 let mut size = qttypes::QSize {
1825 width: window_item.width().get().ceil() as _,
1826 height: window_item.height().get().ceil() as _,
1827 };
1828
1829 if size.width == 0 || size.height == 0 {
1830 let existing_size = cpp!(unsafe [widget_ptr as "QWidget*"] -> qttypes::QSize as "QSize" {
1831 return widget_ptr->size();
1832 });
1833 if size.width == 0 {
1834 window_item.width.set(LogicalLength::new(existing_size.width as _));
1835 size.width = existing_size.width;
1836 }
1837 if size.height == 0 {
1838 window_item.height.set(LogicalLength::new(existing_size.height as _));
1839 size.height = existing_size.height;
1840 }
1841 }
1842
1843 let background =
1844 into_qbrush(properties.background(), size.width.into(), size.height.into());
1845
1846 match (&window_item.icon()).into() {
1847 &ImageInner::None => (),
1848 r => {
1849 if let Some(pixmap) = image_to_pixmap(r, None) {
1850 cpp! {unsafe [widget_ptr as "QWidget*", pixmap as "QPixmap"] {
1851 widget_ptr->setWindowIcon(QIcon(pixmap));
1852 }};
1853 }
1854 }
1855 };
1856
1857 let fullscreen: bool = properties.is_fullscreen();
1858 let minimized: bool = properties.is_minimized();
1859 let maximized: bool = properties.is_maximized();
1860
1861 cpp! {unsafe [widget_ptr as "QWidget*", title as "QString", size as "QSize", background as "QBrush", no_frame as "bool", always_on_top as "bool",
1862 fullscreen as "bool", minimized as "bool", maximized as "bool"] {
1863
1864 if (size != widget_ptr->size()) {
1865 widget_ptr->resize(size.expandedTo({1, 1}));
1866 }
1867
1868 widget_ptr->setWindowFlag(Qt::FramelessWindowHint, no_frame);
1869 widget_ptr->setWindowFlag(Qt::WindowStaysOnTopHint, always_on_top);
1870
1871 {
1872 auto state = widget_ptr->windowState();
1875
1876 if (fullscreen != widget_ptr->isFullScreen()) {
1877 state = state ^ Qt::WindowFullScreen;
1878 }
1879 if (minimized != widget_ptr->isMinimized()) {
1880 state = state ^ Qt::WindowMinimized;
1881 }
1882 if (maximized != widget_ptr->isMaximized()) {
1883 state = state ^ Qt::WindowMaximized;
1884 }
1885
1886 widget_ptr->setWindowState(state);
1887 }
1888
1889 widget_ptr->setWindowTitle(title);
1890 auto pal = widget_ptr->palette();
1891
1892 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
1893 {
1899 pal.setResolveMask(~pal.resolveMask());
1900 pal.setResolveMask(~pal.resolveMask());
1901 }
1902 #endif
1903 pal.setBrush(QPalette::Window, background);
1904 widget_ptr->setPalette(pal);
1905 }};
1906
1907 let constraints = properties.layout_constraints();
1908
1909 let min_size: qttypes::QSize = constraints.min.map_or_else(
1910 || qttypes::QSize { width: 0, height: 0 }, into_qsize,
1912 );
1913
1914 const WIDGET_SIZE_MAX: u32 = 16_777_215;
1915
1916 let max_size: qttypes::QSize = constraints.max.map_or_else(
1917 || qttypes::QSize { width: WIDGET_SIZE_MAX, height: WIDGET_SIZE_MAX },
1918 into_qsize,
1919 );
1920
1921 cpp! {unsafe [widget_ptr as "QWidget*", min_size as "QSize", max_size as "QSize"] {
1922 widget_ptr->setMinimumSize(min_size);
1923 widget_ptr->setMaximumSize(max_size);
1924 }};
1925 }
1926
1927 fn internal(&self, _: i_slint_core::InternalToken) -> Option<&dyn WindowAdapterInternal> {
1928 Some(self)
1929 }
1930}
1931
1932fn into_qsize(logical_size: i_slint_core::api::LogicalSize) -> qttypes::QSize {
1933 qttypes::QSize {
1934 width: logical_size.width.round() as _,
1935 height: logical_size.height.round() as _,
1936 }
1937}
1938
1939impl WindowAdapterInternal for QtWindow {
1940 fn register_item_tree(&self) {
1941 self.tree_structure_changed.replace(true);
1942 }
1943
1944 fn unregister_item_tree(
1945 &self,
1946 _component: ItemTreeRef,
1947 _: &mut dyn Iterator<Item = Pin<ItemRef<'_>>>,
1948 ) {
1949 self.tree_structure_changed.replace(true);
1950 }
1951
1952 fn create_popup(&self, geometry: LogicalRect) -> Option<Rc<dyn WindowAdapter>> {
1953 let popup_window = QtWindow::new();
1954
1955 let size = qttypes::QSize { width: geometry.width() as _, height: geometry.height() as _ };
1956
1957 let popup_ptr = popup_window.widget_ptr();
1958 let pos = qttypes::QPoint { x: geometry.origin.x as _, y: geometry.origin.y as _ };
1959 let widget_ptr = self.widget_ptr();
1960 cpp! {unsafe [widget_ptr as "QWidget*", popup_ptr as "QWidget*", pos as "QPoint", size as "QSize"] {
1961 popup_ptr->setParent(widget_ptr, Qt::Popup);
1962 popup_ptr->setGeometry(QRect(pos + widget_ptr->mapToGlobal(QPoint(0,0)), size));
1963 popup_ptr->show();
1964 }};
1965 Some(popup_window as _)
1966 }
1967
1968 fn set_mouse_cursor(&self, cursor: MouseCursor) {
1969 let widget_ptr = self.widget_ptr();
1970 let cursor_shape = match cursor {
1972 MouseCursor::Default => key_generated::Qt_CursorShape_ArrowCursor,
1973 MouseCursor::None => key_generated::Qt_CursorShape_BlankCursor,
1974 MouseCursor::Help => key_generated::Qt_CursorShape_WhatsThisCursor,
1975 MouseCursor::Pointer => key_generated::Qt_CursorShape_PointingHandCursor,
1976 MouseCursor::Progress => key_generated::Qt_CursorShape_BusyCursor,
1977 MouseCursor::Wait => key_generated::Qt_CursorShape_WaitCursor,
1978 MouseCursor::Crosshair => key_generated::Qt_CursorShape_CrossCursor,
1979 MouseCursor::Text => key_generated::Qt_CursorShape_IBeamCursor,
1980 MouseCursor::Alias => key_generated::Qt_CursorShape_DragLinkCursor,
1981 MouseCursor::Copy => key_generated::Qt_CursorShape_DragCopyCursor,
1982 MouseCursor::Move => key_generated::Qt_CursorShape_DragMoveCursor,
1983 MouseCursor::NoDrop => key_generated::Qt_CursorShape_ForbiddenCursor,
1984 MouseCursor::NotAllowed => key_generated::Qt_CursorShape_ForbiddenCursor,
1985 MouseCursor::Grab => key_generated::Qt_CursorShape_OpenHandCursor,
1986 MouseCursor::Grabbing => key_generated::Qt_CursorShape_ClosedHandCursor,
1987 MouseCursor::ColResize => key_generated::Qt_CursorShape_SplitHCursor,
1988 MouseCursor::RowResize => key_generated::Qt_CursorShape_SplitVCursor,
1989 MouseCursor::NResize => key_generated::Qt_CursorShape_SizeVerCursor,
1990 MouseCursor::EResize => key_generated::Qt_CursorShape_SizeHorCursor,
1991 MouseCursor::SResize => key_generated::Qt_CursorShape_SizeVerCursor,
1992 MouseCursor::WResize => key_generated::Qt_CursorShape_SizeHorCursor,
1993 MouseCursor::NeResize => key_generated::Qt_CursorShape_SizeBDiagCursor,
1994 MouseCursor::NwResize => key_generated::Qt_CursorShape_SizeFDiagCursor,
1995 MouseCursor::SeResize => key_generated::Qt_CursorShape_SizeFDiagCursor,
1996 MouseCursor::SwResize => key_generated::Qt_CursorShape_SizeBDiagCursor,
1997 MouseCursor::EwResize => key_generated::Qt_CursorShape_SizeHorCursor,
1998 MouseCursor::NsResize => key_generated::Qt_CursorShape_SizeVerCursor,
1999 MouseCursor::NeswResize => key_generated::Qt_CursorShape_SizeBDiagCursor,
2000 MouseCursor::NwseResize => key_generated::Qt_CursorShape_SizeFDiagCursor,
2001 };
2002 cpp! {unsafe [widget_ptr as "QWidget*", cursor_shape as "Qt::CursorShape"] {
2003 widget_ptr->setCursor(QCursor{cursor_shape});
2004 }};
2005 }
2006
2007 fn input_method_request(&self, request: i_slint_core::window::InputMethodRequest) {
2008 let widget_ptr = self.widget_ptr();
2009 let props = match request {
2010 i_slint_core::window::InputMethodRequest::Enable(props) => {
2011 cpp! {unsafe [widget_ptr as "QWidget*"] {
2012 widget_ptr->setAttribute(Qt::WA_InputMethodEnabled, true);
2013 }};
2014 props
2015 }
2016 i_slint_core::window::InputMethodRequest::Disable => {
2017 cpp! {unsafe [widget_ptr as "SlintWidget*"] {
2018 widget_ptr->ime_text = "";
2019 widget_ptr->ime_cursor = 0;
2020 widget_ptr->ime_anchor = 0;
2021 widget_ptr->setAttribute(Qt::WA_InputMethodEnabled, false);
2022 }};
2023 return;
2024 }
2025 i_slint_core::window::InputMethodRequest::Update(props) => props,
2026 _ => return,
2027 };
2028
2029 let rect = qttypes::QRectF {
2030 x: props.cursor_rect_origin.x as _,
2031 y: props.cursor_rect_origin.y as _,
2032 width: props.cursor_rect_size.width as _,
2033 height: props.cursor_rect_size.height as _,
2034 };
2035 let cursor: i32 = props.text[..props.cursor_position].encode_utf16().count() as _;
2036 let anchor: i32 =
2037 props.anchor_position.map_or(cursor, |a| props.text[..a].encode_utf16().count() as _);
2038 let text: qttypes::QString = props.text.as_str().into();
2039 cpp! {unsafe [widget_ptr as "SlintWidget*", rect as "QRectF", cursor as "int", anchor as "int", text as "QString"] {
2040 widget_ptr->ime_position = rect.toRect();
2041 widget_ptr->ime_text = text;
2042 widget_ptr->ime_cursor = cursor;
2043 widget_ptr->ime_anchor = anchor;
2044 QGuiApplication::inputMethod()->update(Qt::ImQueryInput);
2045 }};
2046 }
2047
2048 fn as_any(&self) -> &dyn std::any::Any {
2049 self
2050 }
2051
2052 fn handle_focus_change(&self, _old: Option<ItemRc>, new: Option<ItemRc>) {
2053 let widget_ptr = self.widget_ptr();
2054 if let Some(ai) = accessible_item(new) {
2055 let item = &ai;
2056 cpp! {unsafe [widget_ptr as "QWidget*", item as "void*"] {
2057 auto accessible = QAccessible::queryAccessibleInterface(widget_ptr);
2058 if (auto slint_accessible = dynamic_cast<Slint_accessible*>(accessible)) {
2059 slint_accessible->clearFocus();
2060 slint_accessible->focusItem(item);
2061 }
2062 }};
2063 }
2064 }
2065
2066 fn color_scheme(&self) -> ColorScheme {
2067 let ds = self.color_scheme.get_or_init(|| {
2068 Box::pin(Property::new(
2069 if cpp! {unsafe [] -> bool as "bool" {
2070 return qApp->palette().color(QPalette::Window).valueF() < 0.5;
2071 }} {
2072 ColorScheme::Dark
2073 } else {
2074 ColorScheme::Light
2075 },
2076 ))
2077 });
2078 ds.as_ref().get()
2079 }
2080
2081 fn bring_to_front(&self) -> Result<(), i_slint_core::platform::PlatformError> {
2082 let widget_ptr = self.widget_ptr();
2083 cpp! {unsafe [widget_ptr as "QWidget*"] {
2084 widget_ptr->raise();
2085 widget_ptr->activateWindow();
2086 }};
2087 Ok(())
2088 }
2089}
2090
2091impl i_slint_core::renderer::RendererSealed for QtWindow {
2092 fn text_size(
2093 &self,
2094 font_request: FontRequest,
2095 text: &str,
2096 max_width: Option<LogicalLength>,
2097 scale_factor: ScaleFactor,
2098 text_wrap: TextWrap,
2099 ) -> LogicalSize {
2100 sharedparley::text_size(font_request, text, max_width, scale_factor, text_wrap)
2101 }
2102
2103 fn font_metrics(
2104 &self,
2105 font_request: i_slint_core::graphics::FontRequest,
2106 _scale_factor: ScaleFactor,
2107 ) -> i_slint_core::items::FontMetrics {
2108 sharedparley::font_metrics(font_request)
2109 }
2110
2111 fn text_input_byte_offset_for_position(
2112 &self,
2113 text_input: Pin<&i_slint_core::items::TextInput>,
2114 pos: LogicalPoint,
2115 font_request: FontRequest,
2116 scale_factor: ScaleFactor,
2117 ) -> usize {
2118 sharedparley::text_input_byte_offset_for_position(
2119 text_input,
2120 pos,
2121 font_request,
2122 scale_factor,
2123 )
2124 }
2125
2126 fn text_input_cursor_rect_for_byte_offset(
2127 &self,
2128 text_input: Pin<&i_slint_core::items::TextInput>,
2129 byte_offset: usize,
2130 font_request: FontRequest,
2131 scale_factor: ScaleFactor,
2132 ) -> LogicalRect {
2133 sharedparley::text_input_cursor_rect_for_byte_offset(
2134 text_input,
2135 byte_offset,
2136 font_request,
2137 scale_factor,
2138 )
2139 }
2140
2141 fn register_font_from_memory(
2142 &self,
2143 data: &'static [u8],
2144 ) -> Result<(), Box<dyn std::error::Error>> {
2145 sharedfontique::get_collection().register_fonts(data.to_vec().into(), None);
2146 Ok(())
2147 }
2148
2149 fn register_font_from_path(
2150 &self,
2151 path: &std::path::Path,
2152 ) -> Result<(), Box<dyn std::error::Error>> {
2153 let requested_path = path.canonicalize().unwrap_or_else(|_| path.into());
2154 let contents = std::fs::read(requested_path)?;
2155 sharedfontique::get_collection().register_fonts(contents.into(), None);
2156 Ok(())
2157 }
2158
2159 fn default_font_size(&self) -> LogicalLength {
2160 let default_font_size = cpp!(unsafe[] -> i32 as "int" {
2161 return QFontInfo(qApp->font()).pixelSize();
2162 });
2163 LogicalLength::new(default_font_size as f32)
2167 }
2168
2169 fn free_graphics_resources(
2170 &self,
2171 component: ItemTreeRef,
2172 _items: &mut dyn Iterator<Item = Pin<i_slint_core::items::ItemRef<'_>>>,
2173 ) -> Result<(), i_slint_core::platform::PlatformError> {
2174 self.cache.component_destroyed(component);
2176 Ok(())
2177 }
2178
2179 fn set_window_adapter(&self, _window_adapter: &Rc<dyn WindowAdapter>) {
2180 }
2182
2183 fn take_snapshot(&self) -> Result<SharedPixelBuffer<Rgba8Pixel>, PlatformError> {
2184 let widget_ptr = self.widget_ptr();
2185
2186 let size = cpp! {unsafe [widget_ptr as "QWidget*"] -> qttypes::QSize as "QSize" {
2187 return widget_ptr->size();
2188 }};
2189
2190 let rgba8_data = cpp! {unsafe [widget_ptr as "QWidget*"] -> qttypes::QByteArray as "QByteArray" {
2191 QPixmap pixmap = widget_ptr->grab();
2192 QImage image = pixmap.toImage();
2193 image.convertTo(QImage::Format_ARGB32);
2194 return QByteArray(reinterpret_cast<const char *>(image.constBits()), image.sizeInBytes());
2195 }};
2196
2197 let buffer = i_slint_core::graphics::SharedPixelBuffer::<i_slint_core::graphics::Rgba8Pixel>::clone_from_slice(
2198 rgba8_data.to_slice(),
2199 size.width,
2200 size.height,
2201 );
2202 Ok(buffer)
2203 }
2204
2205 fn supports_transformations(&self) -> bool {
2206 true
2207 }
2208}
2209
2210fn accessible_item(item: Option<ItemRc>) -> Option<ItemRc> {
2211 let mut current = item;
2212 while let Some(c) = current {
2213 if c.is_accessible() {
2214 return Some(c);
2215 } else {
2216 current = c.parent_item(ParentItemTraversalMode::StopAtPopups);
2217 }
2218 }
2219 None
2220}
2221
2222thread_local! {
2223 static ALL_WINDOWS: RefCell<Vec<Weak<QtWindow>>> = Default::default();
2225}
2226
2227pub(crate) fn timer_event() {
2229 i_slint_core::platform::update_timers_and_animations();
2230 restart_timer();
2231}
2232
2233pub(crate) fn restart_timer() {
2234 let timeout = i_slint_core::timers::TimerList::next_timeout().map(|instant| {
2235 let now = std::time::Instant::now();
2236 let instant: std::time::Instant = instant.into();
2237 if instant > now {
2238 instant.duration_since(now).as_millis() as i32
2239 } else {
2240 0
2241 }
2242 });
2243 if let Some(timeout) = timeout {
2244 cpp! { unsafe [timeout as "int"] {
2245 ensure_initialized(true);
2246 TimerHandler::instance().timer.start(timeout, &TimerHandler::instance());
2247 }}
2248 }
2249}
2250
2251mod key_codes {
2252 macro_rules! define_qt_key_to_string_fn {
2253 ($($char:literal # $name:ident # $($qt:ident)|* # $($winit:ident $(($_pos:ident))?)|* # $($_xkb:ident)|*;)*) => {
2254 use crate::key_generated;
2255 pub fn qt_key_to_string(key: key_generated::Qt_Key) -> Option<i_slint_core::SharedString> {
2256
2257 let char = match(key) {
2258 $($(key_generated::$qt => $char,)*)*
2259 _ => return None,
2260 };
2261 Some(char.into())
2262 }
2263 };
2264 }
2265
2266 i_slint_common::for_each_special_keys!(define_qt_key_to_string_fn);
2267}
2268
2269fn qt_key_to_string(key: key_generated::Qt_Key, event_text: String) -> SharedString {
2270 if let Some(special_key_code) = key_codes::qt_key_to_string(key) {
2276 return special_key_code;
2277 };
2278
2279 if !event_text.is_empty() && !event_text.chars().any(|ch| ch.is_control()) {
2283 return event_text.into();
2284 }
2285
2286 match key {
2287 key_generated::Qt_Key_Key_Space => " ",
2288 key_generated::Qt_Key_Key_Exclam => "!",
2289 key_generated::Qt_Key_Key_QuoteDbl => "\"",
2290 key_generated::Qt_Key_Key_NumberSign => "#",
2291 key_generated::Qt_Key_Key_Dollar => "$",
2292 key_generated::Qt_Key_Key_Percent => "%",
2293 key_generated::Qt_Key_Key_Ampersand => "&",
2294 key_generated::Qt_Key_Key_Apostrophe => "'",
2295 key_generated::Qt_Key_Key_ParenLeft => "(",
2296 key_generated::Qt_Key_Key_ParenRight => ")",
2297 key_generated::Qt_Key_Key_Asterisk => "*",
2298 key_generated::Qt_Key_Key_Plus => "+",
2299 key_generated::Qt_Key_Key_Comma => ",",
2300 key_generated::Qt_Key_Key_Minus => "-",
2301 key_generated::Qt_Key_Key_Period => ".",
2302 key_generated::Qt_Key_Key_Slash => "/",
2303 key_generated::Qt_Key_Key_0 => "0",
2304 key_generated::Qt_Key_Key_1 => "1",
2305 key_generated::Qt_Key_Key_2 => "2",
2306 key_generated::Qt_Key_Key_3 => "3",
2307 key_generated::Qt_Key_Key_4 => "4",
2308 key_generated::Qt_Key_Key_5 => "5",
2309 key_generated::Qt_Key_Key_6 => "6",
2310 key_generated::Qt_Key_Key_7 => "7",
2311 key_generated::Qt_Key_Key_8 => "8",
2312 key_generated::Qt_Key_Key_9 => "9",
2313 key_generated::Qt_Key_Key_Colon => ":",
2314 key_generated::Qt_Key_Key_Semicolon => ";",
2315 key_generated::Qt_Key_Key_Less => "<",
2316 key_generated::Qt_Key_Key_Equal => "=",
2317 key_generated::Qt_Key_Key_Greater => ">",
2318 key_generated::Qt_Key_Key_Question => "?",
2319 key_generated::Qt_Key_Key_At => "@",
2320 key_generated::Qt_Key_Key_A => "a",
2321 key_generated::Qt_Key_Key_B => "b",
2322 key_generated::Qt_Key_Key_C => "c",
2323 key_generated::Qt_Key_Key_D => "d",
2324 key_generated::Qt_Key_Key_E => "e",
2325 key_generated::Qt_Key_Key_F => "f",
2326 key_generated::Qt_Key_Key_G => "g",
2327 key_generated::Qt_Key_Key_H => "h",
2328 key_generated::Qt_Key_Key_I => "i",
2329 key_generated::Qt_Key_Key_J => "j",
2330 key_generated::Qt_Key_Key_K => "k",
2331 key_generated::Qt_Key_Key_L => "l",
2332 key_generated::Qt_Key_Key_M => "m",
2333 key_generated::Qt_Key_Key_N => "n",
2334 key_generated::Qt_Key_Key_O => "o",
2335 key_generated::Qt_Key_Key_P => "p",
2336 key_generated::Qt_Key_Key_Q => "q",
2337 key_generated::Qt_Key_Key_R => "r",
2338 key_generated::Qt_Key_Key_S => "s",
2339 key_generated::Qt_Key_Key_T => "t",
2340 key_generated::Qt_Key_Key_U => "u",
2341 key_generated::Qt_Key_Key_V => "v",
2342 key_generated::Qt_Key_Key_W => "w",
2343 key_generated::Qt_Key_Key_X => "x",
2344 key_generated::Qt_Key_Key_Y => "y",
2345 key_generated::Qt_Key_Key_Z => "z",
2346 key_generated::Qt_Key_Key_BracketLeft => "[",
2347 key_generated::Qt_Key_Key_Backslash => "\\",
2348 key_generated::Qt_Key_Key_BracketRight => "]",
2349 key_generated::Qt_Key_Key_AsciiCircum => "^",
2350 key_generated::Qt_Key_Key_Underscore => "_",
2351 key_generated::Qt_Key_Key_QuoteLeft => "`",
2352 key_generated::Qt_Key_Key_BraceLeft => "{",
2353 key_generated::Qt_Key_Key_Bar => "|",
2354 key_generated::Qt_Key_Key_BraceRight => "}",
2355 key_generated::Qt_Key_Key_AsciiTilde => "~",
2356 _ => "",
2357 }
2358 .into()
2359}
2360
2361pub(crate) mod ffi {
2362 use std::ffi::c_void;
2363
2364 use super::QtWindow;
2365
2366 #[unsafe(no_mangle)]
2367 pub extern "C" fn slint_qt_get_widget(
2368 window_adapter: &i_slint_core::window::WindowAdapterRc,
2369 ) -> *mut c_void {
2370 window_adapter
2371 .internal(i_slint_core::InternalToken)
2372 .and_then(|wa| <dyn std::any::Any>::downcast_ref(wa.as_any()))
2373 .map_or(std::ptr::null_mut(), |win: &QtWindow| {
2374 win.widget_ptr().cast::<c_void>().as_ptr()
2375 })
2376 }
2377}
2378
2379fn qt_password_character() -> char {
2380 char::from_u32(cpp! { unsafe [] -> i32 as "int" {
2381 return qApp->style()->styleHint(QStyle::SH_LineEdit_PasswordCharacter, nullptr, nullptr);
2382 }} as u32)
2383 .unwrap_or('●')
2384}