diff --git a/CHANGELOG.md b/CHANGELOG.md index cb1104bb000..919c99f7e47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,16 @@ # Changelog All notable changes to this project are documented in this file. +## Unreleased + +### Changed + +### Added + +### Fixed + + - Fixed crashes with the Qt backend in release mode. + ## [0.2.2] - 2022-05-04 ### Changed diff --git a/internal/backends/qt/qt_widgets.rs b/internal/backends/qt/qt_widgets.rs index 6e4bf4e388d..ddd65765994 100644 --- a/internal/backends/qt/qt_widgets.rs +++ b/internal/backends/qt/qt_widgets.rs @@ -17,6 +17,7 @@ it needs to be kept in sync with different place. #![allow(non_upper_case_globals)] +use crate::qt_window::QPainterPtr; use const_field_offset::FieldOffsets; use core::pin::Pin; use cpp::cpp; @@ -38,8 +39,6 @@ use std::rc::Rc; type ItemRendererRef<'a> = &'a mut dyn ItemRenderer; -use qttypes::QPainter; - /// Helper macro to get the size from the width and height property, /// and return Default::default in case the size is too small macro_rules! get_size { @@ -69,12 +68,12 @@ macro_rules! fn_render { return (int)state; }); - if let Some(painter) = ::downcast_mut::(backend.as_any()) { + if let Some(painter) = ::downcast_mut::(backend.as_any()) { let $size: qttypes::QSize = get_size!(self); let $this = self; painter.save(); - let $widget = cpp!(unsafe [painter as "QPainter*"] -> * const () as "QWidget*" { - return painter->device()->devType() == QInternal::Widget ? static_cast(painter->device()) : nullptr; + let $widget = cpp!(unsafe [painter as "QPainterPtr*"] -> * const () as "QWidget*" { + return (*painter)->device()->devType() == QInternal::Widget ? static_cast((*painter)->device()) : nullptr; }); let $painter = painter; $($tt)* @@ -92,12 +91,14 @@ macro_rules! fn_render { let $size = qttypes::QSize { width: width as _, height: height as _ }; let mut imgarray = QImageWrapArray::new($size, $dpr); let img = &mut imgarray.img; - let mut painter_ = cpp!(unsafe [img as "QImage*"] -> QPainter as "QPainter" { return QPainter(img); }); + let mut painter = cpp!(unsafe [img as "QImage*"] -> QPainterPtr as "std::unique_ptr" { + return std::make_unique(img); + }); let $widget: * const () = core::ptr::null(); - let $painter = &mut painter_; + let $painter = &mut painter; let $this = self; $($tt)* - drop(painter_); + drop(painter); imgarray.draw(callback); }, ); @@ -144,6 +145,8 @@ cpp! {{ #include #include + using QPainterPtr = std::unique_ptr; + /// Make sure there is an instance of QApplication. /// The `from_qt_backend` argument specifies if we know that we are running /// the Qt backend, or if we are just drawing widgets diff --git a/internal/backends/qt/qt_widgets/button.rs b/internal/backends/qt/qt_widgets/button.rs index 473b072f5a4..9715febeccd 100644 --- a/internal/backends/qt/qt_widgets/button.rs +++ b/internal/backends/qt/qt_widgets/button.rs @@ -298,7 +298,7 @@ impl Item for NativeButton { let has_focus = this.has_focus(); cpp!(unsafe [ - painter as "QPainter*", + painter as "QPainterPtr*", widget as "QWidget*", text as "QString", icon as "QPixmap", @@ -328,7 +328,7 @@ impl Item for NativeButton { if (has_focus) { option.state |= QStyle::State_HasFocus | QStyle::State_KeyboardFocusChange | QStyle::State_Item; } - qApp->style()->drawControl(QStyle::CE_PushButton, &option, painter, widget); + qApp->style()->drawControl(QStyle::CE_PushButton, &option, painter->get(), widget); }); } } diff --git a/internal/backends/qt/qt_widgets/checkbox.rs b/internal/backends/qt/qt_widgets/checkbox.rs index a1ec97e797d..3c70db55f52 100644 --- a/internal/backends/qt/qt_widgets/checkbox.rs +++ b/internal/backends/qt/qt_widgets/checkbox.rs @@ -109,7 +109,7 @@ impl Item for NativeCheckBox { let text: qttypes::QString = this.text().as_str().into(); cpp!(unsafe [ - painter as "QPainter*", + painter as "QPainterPtr*", widget as "QWidget*", enabled as "bool", text as "QString", @@ -132,7 +132,7 @@ impl Item for NativeCheckBox { if (has_focus) { option.state |= QStyle::State_HasFocus | QStyle::State_KeyboardFocusChange | QStyle::State_Item; } - qApp->style()->drawControl(QStyle::CE_CheckBox, &option, painter, widget); + qApp->style()->drawControl(QStyle::CE_CheckBox, &option, painter->get(), widget); }); } } diff --git a/internal/backends/qt/qt_widgets/combobox.rs b/internal/backends/qt/qt_widgets/combobox.rs index 3110ce81ec0..b3b7d70ebda 100644 --- a/internal/backends/qt/qt_widgets/combobox.rs +++ b/internal/backends/qt/qt_widgets/combobox.rs @@ -103,7 +103,7 @@ impl Item for NativeComboBox { this.current_value().as_str().into(); let enabled = this.enabled(); cpp!(unsafe [ - painter as "QPainter*", + painter as "QPainterPtr*", widget as "QWidget*", text as "QString", enabled as "bool", @@ -132,8 +132,8 @@ impl Item for NativeComboBox { // option.state |= QStyle::State_On; } option.subControls = QStyle::SC_All; - qApp->style()->drawComplexControl(QStyle::CC_ComboBox, &option, painter, widget); - qApp->style()->drawControl(QStyle::CE_ComboBoxLabel, &option, painter, widget); + qApp->style()->drawComplexControl(QStyle::CC_ComboBox, &option, painter->get(), widget); + qApp->style()->drawControl(QStyle::CE_ComboBoxLabel, &option, painter->get(), widget); }); } } diff --git a/internal/backends/qt/qt_widgets/groupbox.rs b/internal/backends/qt/qt_widgets/groupbox.rs index 2eaca2ddfad..641ce7f9d7e 100644 --- a/internal/backends/qt/qt_widgets/groupbox.rs +++ b/internal/backends/qt/qt_widgets/groupbox.rs @@ -162,7 +162,7 @@ impl Item for NativeGroupBox { let enabled = this.enabled(); cpp!(unsafe [ - painter as "QPainter*", + painter as "QPainterPtr*", widget as "QWidget*", text as "QString", enabled as "bool", @@ -187,7 +187,7 @@ impl Item for NativeGroupBox { } option.textColor = QColor(qApp->style()->styleHint( QStyle::SH_GroupBox_TextLabelColor, &option)); - qApp->style()->drawComplexControl(QStyle::CC_GroupBox, &option, painter, widget); + qApp->style()->drawComplexControl(QStyle::CC_GroupBox, &option, painter->get(), widget); }); } } diff --git a/internal/backends/qt/qt_widgets/lineedit.rs b/internal/backends/qt/qt_widgets/lineedit.rs index 4e3fec27b16..8b7de9df437 100644 --- a/internal/backends/qt/qt_widgets/lineedit.rs +++ b/internal/backends/qt/qt_widgets/lineedit.rs @@ -115,7 +115,7 @@ impl Item for NativeLineEdit { let enabled: bool = this.enabled(); cpp!(unsafe [ - painter as "QPainter*", + painter as "QPainterPtr*", widget as "QWidget*", size as "QSize", dpr as "float", @@ -135,7 +135,7 @@ impl Item for NativeLineEdit { } else { option.palette.setCurrentColorGroup(QPalette::Disabled); } - qApp->style()->drawPrimitive(QStyle::PE_PanelLineEdit, &option, painter, widget); + qApp->style()->drawPrimitive(QStyle::PE_PanelLineEdit, &option, painter->get(), widget); }); } } diff --git a/internal/backends/qt/qt_widgets/listviewitem.rs b/internal/backends/qt/qt_widgets/listviewitem.rs index 2daf4a07939..cfe347e5b4c 100644 --- a/internal/backends/qt/qt_widgets/listviewitem.rs +++ b/internal/backends/qt/qt_widgets/listviewitem.rs @@ -90,7 +90,7 @@ impl Item for NativeStandardListViewItem { let item = this.item(); let text: qttypes::QString = item.text.as_str().into(); cpp!(unsafe [ - painter as "QPainter*", + painter as "QPainterPtr*", widget as "QWidget*", size as "QSize", dpr as "float", @@ -120,14 +120,14 @@ impl Item for NativeStandardListViewItem { option.features |= QStyleOptionViewItem::HasDisplay; option.text = text; // CE_ItemViewItem in QCommonStyle calls setClipRect on the painter and replace the clips. So we need to cheat. - auto engine = painter->paintEngine(); + auto engine = (*painter)->paintEngine(); auto old_clip = engine->systemClip(); - auto new_clip = old_clip & (painter->clipRegion() * painter->transform()); + auto new_clip = old_clip & ((*painter)->clipRegion() * (*painter)->transform()); if (new_clip.isEmpty()) return; engine->setSystemClip(new_clip); - qApp->style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &option, painter, widget); - qApp->style()->drawControl(QStyle::CE_ItemViewItem, &option, painter, widget); + qApp->style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &option, painter->get(), widget); + qApp->style()->drawControl(QStyle::CE_ItemViewItem, &option, painter->get(), widget); engine->setSystemClip(old_clip); }); } diff --git a/internal/backends/qt/qt_widgets/scrollview.rs b/internal/backends/qt/qt_widgets/scrollview.rs index 8294ae51c31..1dc01747744 100644 --- a/internal/backends/qt/qt_widgets/scrollview.rs +++ b/internal/backends/qt/qt_widgets/scrollview.rs @@ -272,7 +272,7 @@ impl Item for NativeScrollView { let enabled: bool = this.enabled(); let has_focus: bool = this.has_focus(); let frame_around_contents = cpp!(unsafe [ - painter as "QPainter*", + painter as "QPainterPtr*", widget as "QWidget*", size as "QSize", dpr as "float", @@ -303,13 +303,13 @@ impl Item for NativeScrollView { QSize corner_size = QSize(margins.right() - margins.left(), margins.bottom() - margins.top()); if (foac) { frameOption.rect = QRect(QPoint(), (size / dpr) - corner_size); - qApp->style()->drawControl(QStyle::CE_ShapedFrame, &frameOption, painter, widget); + qApp->style()->drawControl(QStyle::CE_ShapedFrame, &frameOption, painter->get(), widget); frameOption.rect = QRect(frameOption.rect.bottomRight() + QPoint(1, 1), corner_size); - qApp->style()->drawPrimitive(QStyle::PE_PanelScrollAreaCorner, &frameOption, painter, widget); + qApp->style()->drawPrimitive(QStyle::PE_PanelScrollAreaCorner, &frameOption, painter->get(), widget); } else { - qApp->style()->drawControl(QStyle::CE_ShapedFrame, &frameOption, painter, widget); + qApp->style()->drawControl(QStyle::CE_ShapedFrame, &frameOption, painter->get(), widget); frameOption.rect = QRect(frameOption.rect.bottomRight() + QPoint(1, 1) - QPoint(margins.right(), margins.bottom()), corner_size); - qApp->style()->drawPrimitive(QStyle::PE_PanelScrollAreaCorner, &frameOption, painter, widget); + qApp->style()->drawPrimitive(QStyle::PE_PanelScrollAreaCorner, &frameOption, painter->get(), widget); } return foac; }); @@ -323,7 +323,7 @@ impl Item for NativeScrollView { pressed: bool, initial_state: i32| { cpp!(unsafe [ - painter as "QPainter*", + painter as "QPainterPtr*", widget as "QWidget*", value as "int", page_size as "int", @@ -336,6 +336,7 @@ impl Item for NativeScrollView { has_focus as "bool", initial_state as "int" ] { + QPainter *painter_ = painter->get(); auto r = rect.toAlignedRect(); // The mac style may crash on invalid rectangles (#595) if (!r.isValid()) @@ -345,11 +346,11 @@ impl Item for NativeScrollView { #if defined(Q_OS_MAC) QImage scrollbar_image(r.size(), QImage::Format_ARGB32_Premultiplied); scrollbar_image.fill(Qt::transparent); - {QPainter p(&scrollbar_image); QPainter *painter = &p; + {QPainter p(&scrollbar_image); QPainter *painter_ = &p; #else - painter->save(); - auto cleanup = qScopeGuard([&] { painter->restore(); }); - painter->translate(r.topLeft()); // There is bugs in the styles if the scrollbar is not in (0,0) + painter_->save(); + auto cleanup = qScopeGuard([&] { painter_->restore(); }); + painter_->translate(r.topLeft()); // There is bugs in the styles if the scrollbar is not in (0,0) #endif QStyleOptionSlider option; option.state |= QStyle::State(initial_state); @@ -366,10 +367,10 @@ impl Item for NativeScrollView { } auto style = qApp->style(); - style->drawComplexControl(QStyle::CC_ScrollBar, &option, painter, widget); + style->drawComplexControl(QStyle::CC_ScrollBar, &option, painter_, widget); #if defined(Q_OS_MAC) } - painter->drawImage(r.topLeft(), scrollbar_image); + (painter_)->drawImage(r.topLeft(), scrollbar_image); #endif }); }; diff --git a/internal/backends/qt/qt_widgets/slider.rs b/internal/backends/qt/qt_widgets/slider.rs index a0226cbe5d1..c6780a5dee8 100644 --- a/internal/backends/qt/qt_widgets/slider.rs +++ b/internal/backends/qt/qt_widgets/slider.rs @@ -207,7 +207,7 @@ impl Item for NativeSlider { let pressed = data.pressed; cpp!(unsafe [ - painter as "QPainter*", + painter as "QPainterPtr*", widget as "QWidget*", enabled as "bool", value as "int", @@ -224,7 +224,7 @@ impl Item for NativeSlider { option.rect = QRect(QPoint(), size / dpr); initQSliderOptions(option, pressed, enabled, active_controls, min, max, value); auto style = qApp->style(); - style->drawComplexControl(QStyle::CC_Slider, &option, painter, widget); + style->drawComplexControl(QStyle::CC_Slider, &option, painter->get(), widget); }); } } diff --git a/internal/backends/qt/qt_widgets/spinbox.rs b/internal/backends/qt/qt_widgets/spinbox.rs index b90dff55813..a048c29a4d8 100644 --- a/internal/backends/qt/qt_widgets/spinbox.rs +++ b/internal/backends/qt/qt_widgets/spinbox.rs @@ -231,7 +231,7 @@ impl Item for NativeSpinBox { let pressed = data.pressed; cpp!(unsafe [ - painter as "QPainter*", + painter as "QPainterPtr*", widget as "QWidget*", value as "int", enabled as "bool", @@ -250,7 +250,7 @@ impl Item for NativeSpinBox { } option.rect = QRect(QPoint(), size / dpr); initQSpinBoxOptions(option, pressed, enabled, active_controls); - style->drawComplexControl(QStyle::CC_SpinBox, &option, painter, widget); + style->drawComplexControl(QStyle::CC_SpinBox, &option, painter->get(), widget); QStyleOptionFrame frame; frame.state = option.state; @@ -259,11 +259,11 @@ impl Item for NativeSpinBox { : style->pixelMetric(QStyle::PM_DefaultFrameWidth, &option, widget); frame.midLineWidth = 0; frame.rect = style->subControlRect(QStyle::CC_SpinBox, &option, QStyle::SC_SpinBoxEditField, widget); - style->drawPrimitive(QStyle::PE_PanelLineEdit, &frame, painter, widget); + style->drawPrimitive(QStyle::PE_PanelLineEdit, &frame, painter->get(), widget); QRect text_rect = qApp->style()->subElementRect(QStyle::SE_LineEditContents, &frame, widget); text_rect.adjust(1, 2, 1, 2); - painter->setPen(option.palette.color(QPalette::Text)); - painter->drawText(text_rect, QString::number(value)); + (*painter)->setPen(option.palette.color(QPalette::Text)); + (*painter)->drawText(text_rect, QString::number(value)); }); } } diff --git a/internal/backends/qt/qt_widgets/tabwidget.rs b/internal/backends/qt/qt_widgets/tabwidget.rs index 585069bd488..2ae9b58985e 100644 --- a/internal/backends/qt/qt_widgets/tabwidget.rs +++ b/internal/backends/qt/qt_widgets/tabwidget.rs @@ -252,7 +252,7 @@ impl Item for NativeTabWidget { height: this.tabbar_preferred_height() as _, }; cpp!(unsafe [ - painter as "QPainter*", + painter as "QPainterPtr*", widget as "QWidget*", size as "QSize", dpr as "float", @@ -275,7 +275,7 @@ impl Item for NativeTabWidget { option.leftCornerWidgetSize = QSize(0, 0); option.tabBarRect = style->subElementRect(QStyle::SE_TabWidgetTabBar, &option, widget); option.rect = style->subElementRect(QStyle::SE_TabWidgetTabPane, &option, widget); - style->drawPrimitive(QStyle::PE_FrameTabWidget, &option, painter, widget); + style->drawPrimitive(QStyle::PE_FrameTabWidget, &option, painter->get(), widget); /* -- we don't need to draw the base since we already draw the frame QStyleOptionTab tabOverlap; @@ -290,7 +290,7 @@ impl Item for NativeTabWidget { } optTabBase.tabBarRect = option.tabBarRect; optTabBase.selectedTabRect = option.selectedTabRect; - style->drawPrimitive(QStyle::PE_FrameTabBarBase, &optTabBase, painter, widget);*/ + style->drawPrimitive(QStyle::PE_FrameTabBarBase, &optTabBase, painter->get(), widget);*/ }); } } @@ -449,7 +449,7 @@ impl Item for NativeTab { let num_tabs: i32 = this.num_tabs(); cpp!(unsafe [ - painter as "QPainter*", + painter as "QPainterPtr*", widget as "QWidget*", text as "QString", icon as "QPixmap", @@ -492,7 +492,7 @@ impl Item for NativeTab { option.state |= QStyle::State_HasFocus | QStyle::State_KeyboardFocusChange | QStyle::State_Item; } option.features |= QStyleOptionTab::HasFrame; - qApp->style()->drawControl(QStyle::CE_TabBarTab, &option, painter, widget); + qApp->style()->drawControl(QStyle::CE_TabBarTab, &option, painter->get(), widget); }); } } diff --git a/internal/backends/qt/qt_window.rs b/internal/backends/qt/qt_window.rs index 53be87d4eb6..53ab97f8202 100644 --- a/internal/backends/qt/qt_window.rs +++ b/internal/backends/qt/qt_window.rs @@ -22,7 +22,6 @@ use i_slint_core::window::{PlatformWindow, PopupWindow, PopupWindowLocation, Win use i_slint_core::{component::ComponentRc, SharedString}; use i_slint_core::{ImageInner, PathData, Property}; use items::{ImageFit, TextHorizontalAlignment, TextVerticalAlignment}; -use qttypes::QPainter; use std::cell::RefCell; use std::pin::Pin; @@ -53,6 +52,8 @@ cpp! {{ #include void ensure_initialized(bool from_qt_backend); + using QPainterPtr = std::unique_ptr; + struct TimerHandler : QObject { QBasicTimer timer; static TimerHandler& instance() { @@ -81,12 +82,12 @@ cpp! {{ } void paintEvent(QPaintEvent *) override { - QPainter painter(this); - painter.setClipRect(rect()); - painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); - auto painter_ptr = &painter; - rust!(Slint_paintEvent [rust_window: &QtWindow as "void*", painter_ptr: &mut QPainter as "QPainter*"] { - rust_window.paint_event(painter_ptr) + auto painter = std::unique_ptr(new QPainter(this)); + painter->setClipRect(rect()); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + QPainterPtr *painter_ptr = &painter; + rust!(Slint_paintEvent [rust_window: &QtWindow as "void*", painter_ptr: &mut QPainterPtr as "QPainterPtr*"] { + rust_window.paint_event(std::mem::take(painter_ptr)) }); } @@ -270,6 +271,25 @@ cpp! {{ } }} +cpp_class!( + /// Wrapper around a pointer to a QPainter. + // We can't use [`qttypes::QPainter`] because it is not sound + pub unsafe struct QPainterPtr as "QPainterPtr" +); +impl QPainterPtr { + pub fn restore(&mut self) { + cpp!(unsafe [self as "QPainterPtr*"] { + (*self)->restore(); + }); + } + + pub fn save(&mut self) { + cpp!(unsafe [self as "QPainterPtr*"] { + (*self)->save(); + }); + } +} + cpp_class! {pub unsafe struct QPainterPath as "QPainterPath"} impl QPainterPath { @@ -401,27 +421,27 @@ impl Default for QtRenderingCacheItem { type QtRenderingCache = Rc>>; -struct QtItemRenderer<'a> { - painter: &'a mut QPainter, +struct QtItemRenderer { + painter: QPainterPtr, cache: QtRenderingCache, default_font_properties: FontRequest, window: WindowRc, metrics: RenderingMetrics, } -impl ItemRenderer for QtItemRenderer<'_> { +impl ItemRenderer for QtItemRenderer { fn draw_rectangle(&mut self, rect: Pin<&items::Rectangle>) { let brush: qttypes::QBrush = into_qbrush(rect.background()); let rect: qttypes::QRectF = get_geometry!(items::Rectangle, rect); - let painter: &mut QPainter = &mut *self.painter; - cpp! { unsafe [painter as "QPainter*", brush as "QBrush", rect as "QRectF"] { - painter->fillRect(rect, brush); + let painter: &mut QPainterPtr = &mut self.painter; + cpp! { unsafe [painter as "QPainterPtr*", brush as "QBrush", rect as "QRectF"] { + (*painter)->fillRect(rect, brush); }} } fn draw_border_rectangle(&mut self, rect: std::pin::Pin<&items::BorderRectangle>) { Self::draw_rectangle_impl( - self.painter, + &mut self.painter, get_geometry!(items::BorderRectangle, rect), rect.background(), rect.border_color(), @@ -485,13 +505,13 @@ impl ItemRenderer for QtItemRenderer<'_> { TextWrap::word_wrap => key_generated::Qt_TextFlag_TextWordWrap, }; let elide = text.overflow() == TextOverflow::elide; - let painter: &mut QPainter = &mut *self.painter; - cpp! { unsafe [painter as "QPainter*", rect as "QRectF", fill_brush as "QBrush", mut string as "QString", flags as "int", font as "QFont", elide as "bool"] { - painter->setFont(font); - painter->setPen(QPen(fill_brush, 0)); - painter->setBrush(Qt::NoBrush); + let painter: &mut QPainterPtr = &mut self.painter; + cpp! { unsafe [painter as "QPainterPtr*", rect as "QRectF", fill_brush as "QBrush", mut string as "QString", flags as "int", font as "QFont", elide as "bool"] { + (*painter)->setFont(font); + (*painter)->setPen(QPen(fill_brush, 0)); + (*painter)->setBrush(Qt::NoBrush); if (!elide) { - painter->drawText(rect, flags, string); + (*painter)->drawText(rect, flags, string); } else if (!(flags & Qt::TextWordWrap)) { QString elided; QFontMetrics fm(font); @@ -506,7 +526,7 @@ impl ItemRenderer for QtItemRenderer<'_> { elided += '\n'; string = string.mid(pos + 1); } - painter->drawText(rect, flags, elided); + (*painter)->drawText(rect, flags, elided); } else { // elide and word wrap: we need to add the ellipsis manually on the last line string.replace(QChar('\n'), QChar::LineSeparator); @@ -540,7 +560,7 @@ impl ItemRenderer for QtItemRenderer<'_> { QString to_elide = QStringView(string).mid(last_line_begin, last_line_size).trimmed() % QStringView(QT_UNICODE_LITERAL("…")); elided += fm.elidedText(to_elide, Qt::ElideRight, rect.width()); } - painter->drawText(rect, flags, elided); + (*painter)->drawText(rect, flags, elided); } }} } @@ -602,9 +622,9 @@ impl ItemRenderer for QtItemRenderer<'_> { let single_line: bool = text_input.single_line(); - let painter: &mut QPainter = &mut *self.painter; + let painter: &mut QPainterPtr = &mut self.painter; cpp! { unsafe [ - painter as "QPainter*", + painter as "QPainterPtr*", rect as "QRectF", fill_brush as "QBrush", selection_foreground_color as "QRgb", @@ -621,7 +641,7 @@ impl ItemRenderer for QtItemRenderer<'_> { } QTextLayout layout(string, font); do_text_layout(layout, flags, rect); - painter->setPen(QPen(fill_brush, 0)); + (*painter)->setPen(QPen(fill_brush, 0)); QVector selections; if (anchor_position != cursor_position) { QTextCharFormat fmt; @@ -633,9 +653,9 @@ impl ItemRenderer for QtItemRenderer<'_> { fmt }; } - layout.draw(painter, rect.topLeft(), selections); + layout.draw(painter->get(), rect.topLeft(), selections); if (text_cursor_width > 0) { - layout.drawCursor(painter, rect.topLeft(), cursor_position, text_cursor_width); + layout.drawCursor(painter->get(), rect.topLeft(), cursor_position, text_cursor_width); } }} } @@ -686,20 +706,20 @@ impl ItemRenderer for QtItemRenderer<'_> { } } - let painter: &mut QPainter = &mut *self.painter; + let painter: &mut QPainterPtr = &mut self.painter; cpp! { unsafe [ - painter as "QPainter*", + painter as "QPainterPtr*", pos as "QPoint", mut painter_path as "QPainterPath", fill_brush as "QBrush", stroke_brush as "QBrush", stroke_width as "float"] { - painter->save(); - auto cleanup = qScopeGuard([&] { painter->restore(); }); - painter->translate(pos); - painter->setPen(stroke_width > 0 ? QPen(stroke_brush, stroke_width) : Qt::NoPen); - painter->setBrush(fill_brush); - painter->drawPath(painter_path); + (*painter)->save(); + auto cleanup = qScopeGuard([&] { (*painter)->restore(); }); + (*painter)->translate(pos); + (*painter)->setPen(stroke_width > 0 ? QPen(stroke_brush, stroke_width) : Qt::NoPen); + (*painter)->setBrush(fill_brush); + (*painter)->drawPath(painter_path); }} } @@ -719,8 +739,9 @@ impl ItemRenderer for QtItemRenderer<'_> { source_image.fill(qttypes::QColor::from_rgba_f(0., 0., 0., 0.)); let img = &mut source_image; - let mut painter_ = - cpp!(unsafe [img as "QImage*"] -> QPainter as "QPainter" { return QPainter(img); }); + let mut painter_ = cpp!(unsafe [img as "QImage*"] -> QPainterPtr as "QPainterPtr" { + return std::make_unique(img); + }); Self::draw_rectangle_impl( &mut painter_, @@ -787,13 +808,13 @@ impl ItemRenderer for QtItemRenderer<'_> { y: (box_shadow.offset_y() - blur_radius) as f64, }; - let painter: &mut QPainter = &mut *self.painter; + let painter: &mut QPainterPtr = &mut self.painter; cpp! { unsafe [ - painter as "QPainter*", + painter as "QPainterPtr*", shadow_offset as "QPointF", pixmap as "QPixmap*" ] { - painter->drawPixmap(shadow_offset, *pixmap); + (*painter)->drawPixmap(shadow_offset, *pixmap); }} } @@ -824,22 +845,22 @@ impl ItemRenderer for QtItemRenderer<'_> { height: rect.height() as _, }; adjust_rect_and_border_for_inner_drawing(&mut clip_rect, &mut border_width); - let painter: &mut QPainter = &mut *self.painter; - cpp! { unsafe [painter as "QPainter*", clip_rect as "QRectF", radius as "float"] { + let painter: &mut QPainterPtr = &mut self.painter; + cpp! { unsafe [painter as "QPainterPtr*", clip_rect as "QRectF", radius as "float"] { if (radius <= 0) { - painter->setClipRect(clip_rect, Qt::IntersectClip); + (*painter)->setClipRect(clip_rect, Qt::IntersectClip); } else { QPainterPath path; path.addRoundedRect(clip_rect, radius, radius); - painter->setClipPath(path, Qt::IntersectClip); + (*painter)->setClipPath(path, Qt::IntersectClip); } }} } fn get_current_clip(&self) -> Rect { - let painter: &QPainter = self.painter; - let res = cpp! { unsafe [painter as "const QPainter*" ] -> qttypes::QRectF as "QRectF" { - return painter->clipBoundingRect(); + let painter: &QPainterPtr = &self.painter; + let res = cpp! { unsafe [painter as "const QPainterPtr*" ] -> qttypes::QRectF as "QRectF" { + return (*painter)->clipBoundingRect(); }}; Rect::new(Point::new(res.x as _, res.y as _), Size::new(res.width as _, res.height as _)) } @@ -854,8 +875,8 @@ impl ItemRenderer for QtItemRenderer<'_> { fn scale_factor(&self) -> f32 { 1. - /* cpp! { unsafe [painter as "QPainter*"] -> f32 as "float" { - return painter->paintEngine()->paintDevice()->devicePixelRatioF(); + /* cpp! { unsafe [painter as "QPainterPtr*"] -> f32 as "float" { + return (*painter)->paintEngine()->paintDevice()->devicePixelRatioF(); }} */ } @@ -866,10 +887,10 @@ impl ItemRenderer for QtItemRenderer<'_> { ) { update_fn(&mut |width: u32, height: u32, data: &[u8]| { let data = data.as_ptr(); - let painter: &mut QPainter = &mut *self.painter; - cpp! { unsafe [painter as "QPainter*", width as "int", height as "int", data as "const unsigned char *"] { + let painter: &mut QPainterPtr = &mut self.painter; + cpp! { unsafe [painter as "QPainterPtr*", width as "int", height as "int", data as "const unsigned char *"] { QImage img(data, width, height, width * 4, QImage::Format_RGBA8888_Premultiplied); - painter->drawImage(QPoint(), img); + (*painter)->drawImage(QPoint(), img); }} }) } @@ -878,12 +899,12 @@ impl ItemRenderer for QtItemRenderer<'_> { let fill_brush: qttypes::QBrush = into_qbrush(color.into()); let mut string: qttypes::QString = string.into(); let font: QFont = get_font(self.default_font_properties.clone()); - let painter: &mut QPainter = &mut *self.painter; - cpp! { unsafe [painter as "QPainter*", fill_brush as "QBrush", mut string as "QString", font as "QFont"] { - painter->setFont(font); - painter->setPen(QPen(fill_brush, 0)); - painter->setBrush(Qt::NoBrush); - painter->drawText(0, QFontMetrics(painter->font()).ascent(), string); + let painter: &mut QPainterPtr = &mut self.painter; + cpp! { unsafe [painter as "QPainterPtr*", fill_brush as "QBrush", mut string as "QString", font as "QFont"] { + (*painter)->setFont(font); + (*painter)->setPen(QPen(fill_brush, 0)); + (*painter)->setBrush(Qt::NoBrush); + (*painter)->drawText(0, QFontMetrics((*painter)->font()).ascent(), string); }} } @@ -892,27 +913,27 @@ impl ItemRenderer for QtItemRenderer<'_> { } fn as_any(&mut self) -> &mut dyn std::any::Any { - self.painter + &mut self.painter } fn translate(&mut self, x: f32, y: f32) { - let painter: &mut QPainter = &mut *self.painter; - cpp! { unsafe [painter as "QPainter*", x as "float", y as "float"] { - painter->translate(x, y); + let painter: &mut QPainterPtr = &mut self.painter; + cpp! { unsafe [painter as "QPainterPtr*", x as "float", y as "float"] { + (*painter)->translate(x, y); }} } fn rotate(&mut self, angle_in_degrees: f32) { - let painter: &mut QPainter = &mut *self.painter; - cpp! { unsafe [painter as "QPainter*", angle_in_degrees as "float"] { - painter->rotate(angle_in_degrees); + let painter: &mut QPainterPtr = &mut self.painter; + cpp! { unsafe [painter as "QPainterPtr*", angle_in_degrees as "float"] { + (*painter)->rotate(angle_in_degrees); }} } fn apply_opacity(&mut self, opacity: f32) { - let painter: &mut QPainter = &mut *self.painter; - cpp! { unsafe [painter as "QPainter*", opacity as "float"] { - painter->setOpacity(painter->opacity() * opacity); + let painter: &mut QPainterPtr = &mut self.painter; + cpp! { unsafe [painter as "QPainterPtr*", opacity as "float"] { + (*painter)->setOpacity((*painter)->opacity() * opacity); }} } @@ -1059,7 +1080,7 @@ fn is_svg(resource: &ImageInner) -> bool { } } -impl QtItemRenderer<'_> { +impl QtItemRenderer { fn draw_image_impl( &mut self, item_cache: &CachedRenderingData, @@ -1122,23 +1143,23 @@ impl QtItemRenderer<'_> { }); let mut dest_rect = dest_rect; adjust_to_image_fit(image_fit, &mut source_rect, &mut dest_rect); - let painter: &mut QPainter = &mut *self.painter; + let painter: &mut QPainterPtr = &mut self.painter; let smooth: bool = rendering == ImageRendering::smooth; cpp! { unsafe [ - painter as "QPainter*", + painter as "QPainterPtr*", pixmap as "QPixmap*", source_rect as "QRectF", dest_rect as "QRectF", smooth as "bool"] { - painter->save(); - painter->setRenderHint(QPainter::SmoothPixmapTransform, smooth); - painter->drawPixmap(dest_rect, *pixmap, source_rect); - painter->restore(); + (*painter)->save(); + (*painter)->setRenderHint(QPainter::SmoothPixmapTransform, smooth); + (*painter)->drawPixmap(dest_rect, *pixmap, source_rect); + (*painter)->restore(); }}; } fn draw_rectangle_impl( - painter: &mut QPainter, + painter: &mut QPainterPtr, mut rect: qttypes::QRectF, brush: Brush, border_color: Brush, @@ -1148,13 +1169,13 @@ impl QtItemRenderer<'_> { let brush: qttypes::QBrush = into_qbrush(brush); let border_color: qttypes::QBrush = into_qbrush(border_color); adjust_rect_and_border_for_inner_drawing(&mut rect, &mut border_width); - cpp! { unsafe [painter as "QPainter*", brush as "QBrush", border_color as "QBrush", border_width as "float", border_radius as "float", rect as "QRectF"] { - painter->setPen(border_width > 0 ? QPen(border_color, border_width) : Qt::NoPen); - painter->setBrush(brush); + cpp! { unsafe [painter as "QPainterPtr*", brush as "QBrush", border_color as "QBrush", border_width as "float", border_radius as "float", rect as "QRectF"] { + (*painter)->setPen(border_width > 0 ? QPen(border_color, border_width) : Qt::NoPen); + (*painter)->setBrush(brush); if (border_radius > 0) { - painter->drawRoundedRect(rect, border_radius, border_radius); + (*painter)->drawRoundedRect(rect, border_radius, border_radius); } else { - painter->drawRect(rect); + (*painter)->drawRect(rect); } }} } @@ -1172,22 +1193,14 @@ impl QtItemRenderer<'_> { *self.metrics.layers_created.as_mut().unwrap() += 1; - let mut layer_painter = { - let img_ref: &mut qttypes::QImage = &mut layer_image; - cpp!(unsafe [img_ref as "QImage*"] -> QPainter as "QPainter" { return QPainter(img_ref); }) - }; - - std::mem::swap(self.painter, &mut layer_painter); + let img_ref: &mut qttypes::QImage = &mut layer_image; + let mut layer_painter = cpp!(unsafe [img_ref as "QImage*"] -> QPainterPtr as "QPainterPtr" { + auto painter = std::make_unique(img_ref); + painter->setClipRect(0, 0, img_ref->width(), img_ref->height()); + return painter; + }); - { - let painter: &mut QPainter = &mut *self.painter; - cpp! { unsafe [ - painter as "QPainter*", - layer_size as "QSize" - ] { - painter->setClipRect(0, 0, layer_size.width(), layer_size.height()); - }} - } + std::mem::swap(&mut self.painter, &mut layer_painter); i_slint_core::item_rendering::render_item_children( self, @@ -1195,11 +1208,10 @@ impl QtItemRenderer<'_> { item_rc.index() as isize, ); - std::mem::swap(self.painter, &mut layer_painter); + std::mem::swap(&mut self.painter, &mut layer_painter); drop(layer_painter); - let img_ref = &mut layer_image; - QtRenderingCacheItem::Pixmap(cpp!(unsafe [img_ref as "QImage*"] -> qttypes::QPixmap as "QPixmap" { return QPixmap::fromImage(*img_ref); })) + QtRenderingCacheItem::Pixmap(qttypes::QPixmap::from(layer_image)) }); match &cache_entry { QtRenderingCacheItem::Pixmap(pixmap) => Some(pixmap.clone()), @@ -1234,13 +1246,13 @@ impl QtItemRenderer<'_> { self.save_state(); self.apply_opacity(alpha_tint); { - let painter: &mut QPainter = &mut *self.painter; + let painter: &mut QPainterPtr = &mut self.painter; let layer_image_ref: &mut qttypes::QPixmap = &mut layer_image; cpp! { unsafe [ - painter as "QPainter*", + painter as "QPainterPtr*", layer_image_ref as "QPixmap*" ] { - painter->drawPixmap(0, 0, *layer_image_ref); + (*painter)->drawPixmap(0, 0, *layer_image_ref); }} } self.restore_state(); @@ -1287,7 +1299,7 @@ impl QtWindow { unsafe { std::mem::transmute_copy::>(&self.widget_ptr) } } - fn paint_event(&self, painter: &mut QPainter) { + fn paint_event(&self, painter: QPainterPtr) { let runtime_window = self.self_weak.upgrade().unwrap(); runtime_window.clone().draw_contents(|components| { i_slint_core::animations::update_animations();