#![allow(non_upper_case_globals)]
use crate::qt_window::QPainterPtr;
use const_field_offset::FieldOffsets;
use core::pin::Pin;
use cpp::{cpp, cpp_class};
use i_slint_core::graphics::Color;
use i_slint_core::input::{
FocusEvent, InputEventFilterResult, InputEventResult, KeyEvent, KeyEventResult, MouseEvent,
};
use i_slint_core::item_rendering::{CachedRenderingData, ItemRenderer};
use i_slint_core::items::{Item, ItemConsts, ItemRc, ItemVTable, RenderingResult, VoidArg};
use i_slint_core::layout::{LayoutInfo, Orientation};
use i_slint_core::lengths::{LogicalLength, LogicalPoint, LogicalRect, LogicalSize};
#[cfg(feature = "rtti")]
use i_slint_core::rtti::*;
use i_slint_core::window::{WindowAdapter, WindowAdapterRc, WindowInner};
use i_slint_core::{
declare_item_vtable, Callback, ItemVTable_static, Property, SharedString, SharedVector,
};
use i_slint_core_macros::*;
use std::ptr::NonNull;
use std::rc::Rc;
type ItemRendererRef<'a> = &'a mut dyn ItemRenderer;
macro_rules! get_size {
($self:ident) => {{
let geo = $self.geometry();
let width = geo.width();
let height = geo.height();
if width < 1. || height < 1. {
return Default::default();
};
qttypes::QSize { width: width as _, height: height as _ }
}};
}
macro_rules! fn_render {
($this:ident $dpr:ident $size:ident $painter:ident $widget:ident $initial_state:ident => $($tt:tt)*) => {
fn render(self: Pin<&Self>, backend: &mut &mut dyn ItemRenderer, item_rc: &ItemRc, size: LogicalSize) -> RenderingResult {
self.animation_tracker();
let $dpr: f32 = backend.scale_factor();
let active: bool = backend.window().active();
let $initial_state = cpp!(unsafe [ active as "bool" ] -> i32 as "int" {
QStyle::State state(QStyle::State_None);
if (active)
state |= QStyle::State_Active;
return (int)state;
});
let $widget: NonNull<()> = SlintTypeErasedWidgetPtr::qwidget_ptr(&self.widget_ptr);
if let Some(painter) = backend.as_any().and_then(|any| <dyn std::any::Any>::downcast_mut::<QPainterPtr>(any)) {
let width = size.width * $dpr;
let height = size.height * $dpr;
if width < 1. || height < 1. {
return Default::default();
};
let $size = qttypes::QSize { width: width as _, height: height as _ };
let $this = self;
let _workaround = unsafe { $crate::qt_widgets::PainterClipWorkaround::new(painter) };
painter.save();
let $painter = painter;
$($tt)*
$painter.restore();
} else {
backend.draw_cached_pixmap(
item_rc,
&|callback| {
let geo = item_rc.geometry();
let width = geo.width() * $dpr;
let height = geo.height() * $dpr;
if width < 1. || height < 1. {
return Default::default();
};
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*"] -> QPainterPtr as "std::unique_ptr<QPainter>" {
auto painter = std::make_unique<QPainter>(img);
painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
return painter;
});
let $painter = &mut painter;
let $this = self;
$($tt)*
drop(painter);
imgarray.draw(callback);
},
);
}
RenderingResult::ContinueRenderingChildren
}
};
}
struct QImageWrapArray {
img: qttypes::QImage,
array: SharedVector<u8>,
}
impl QImageWrapArray {
pub fn new(size: qttypes::QSize, dpr: f32) -> Self {
let mut array = SharedVector::default();
array.resize((size.width * size.height * 4) as usize, 0);
let array_ptr = array.make_mut_slice().as_mut_ptr();
let img = cpp!(unsafe [size as "QSize", array_ptr as "uchar*", dpr as "float"] -> qttypes::QImage as "QImage" {
ensure_initialized();
QImage img(array_ptr, size.width(), size.height(), size.width() * 4, QImage::Format_RGBA8888_Premultiplied);
img.setDevicePixelRatio(dpr);
return img;
});
QImageWrapArray { img, array }
}
pub fn draw(&self, callback: &mut dyn FnMut(u32, u32, &[u8])) {
let size = self.img.size();
callback(size.width, size.height, self.array.as_slice());
}
}
cpp! {{
#include <QtWidgets/QApplication>
#include <QtWidgets/QStyle>
#include <QtWidgets/QStyleOption>
#include <QtWidgets/QStyleFactory>
#include <QtGui/QPainter>
#include <QtGui/QClipboard>
#include <QtCore/QMimeData>
#include <QtCore/QDebug>
#include <QtCore/QScopeGuard>
using QPainterPtr = std::unique_ptr<QPainter>;
static bool g_lastWindowClosed = false;
void ensure_initialized(bool from_qt_backend = false)
{
if (qApp) {
return;
}
if (!from_qt_backend) {
QCoreApplication::setAttribute(Qt::AA_PluginApplication, true);
}
static QByteArray executable = rust!(Slint_get_executable_name [] -> qttypes::QByteArray as "QByteArray" {
std::env::args().next().unwrap_or_default().as_bytes().into()
});
static int argc = 1;
static char *argv[] = { executable.data() };
new QApplication(argc, argv);
qApp->setQuitOnLastWindowClosed(false);
}
struct SlintTypeErasedWidget
{
virtual ~SlintTypeErasedWidget() = 0;
SlintTypeErasedWidget() = default;
SlintTypeErasedWidget(const SlintTypeErasedWidget&) = delete;
SlintTypeErasedWidget& operator=(const SlintTypeErasedWidget&) = delete;
virtual void *qwidget() = 0;
};
SlintTypeErasedWidget::~SlintTypeErasedWidget() = default;
template <typename Base>
struct SlintAnimatedWidget: public Base, public SlintTypeErasedWidget {
void *animation_update_property_ptr;
bool event(QEvent *event) override {
if (event->type() == QEvent::StyleAnimationUpdate || event->type() == QEvent::UpdateLater) {
rust!(Slint_AnimatedWidget_update [animation_update_property_ptr: Pin<&Property<i32>> as "void*"] {
animation_update_property_ptr.set(animation_update_property_ptr.get() + 1);
});
event->accept();
return true;
} else {
return Base::event(event);
}
}
void *qwidget() override { return static_cast<QWidget*>(this); }
};
template <typename Base>
std::unique_ptr<SlintTypeErasedWidget> make_unique_animated_widget(void *animation_update_property_ptr)
{
ensure_initialized();
auto ptr = std::make_unique<SlintAnimatedWidget<Base>>();
static QWidget globalParent;
ptr->setParent(&globalParent);
ptr->setAttribute(Qt::WA_WState_Visible, true);
ptr->setAttribute(Qt::WA_WState_InPaintEvent, true);
ptr->animation_update_property_ptr = animation_update_property_ptr;
return ptr;
}
}}
cpp_class!(pub unsafe struct SlintTypeErasedWidgetPtr as "std::unique_ptr<SlintTypeErasedWidget>");
impl SlintTypeErasedWidgetPtr {
fn qwidget_ptr(this: &std::cell::Cell<Self>) -> NonNull<()> {
let widget_ptr: *mut SlintTypeErasedWidgetPtr = this.as_ptr();
cpp!(unsafe [widget_ptr as "std::unique_ptr<SlintTypeErasedWidget>*"] -> NonNull<()> as "void*" {
return (*widget_ptr)->qwidget();
})
}
}
cpp! {{
struct PainterClipWorkaround {
QPainter *painter;
QRegion old_clip;
explicit PainterClipWorkaround(QPainter *painter) : painter(painter) {
auto engine = painter->paintEngine();
old_clip = engine->systemClip();
auto new_clip = painter->clipRegion() * painter->transform();
if (!old_clip.isNull())
new_clip &= old_clip;
engine->setSystemClip(new_clip);
}
~PainterClipWorkaround() {
auto engine = painter->paintEngine();
engine->setSystemClip(old_clip);
auto actual_clip = engine->systemClip();
if (actual_clip != old_clip) {
QSizeF s2 = actual_clip.boundingRect().size();
QSizeF s1 = old_clip.boundingRect().size();
engine->setSystemClip(old_clip * QTransform::fromScale(s1.width() / s2.width(), s1.height() / s2.height()));
}
}
PainterClipWorkaround(const PainterClipWorkaround&) = delete;
PainterClipWorkaround& operator=(const PainterClipWorkaround&) = delete;
};
}}
cpp_class!(pub(crate) unsafe struct PainterClipWorkaround as "PainterClipWorkaround");
impl PainterClipWorkaround {
pub unsafe fn new(painter: &QPainterPtr) -> Self {
cpp!(unsafe [painter as "const QPainterPtr*"] -> PainterClipWorkaround as "PainterClipWorkaround" {
return PainterClipWorkaround(painter->get());
})
}
}
mod button;
pub use button::*;
mod checkbox;
pub use checkbox::*;
mod spinbox;
pub use spinbox::*;
mod slider;
pub use slider::*;
mod progress_indicator;
pub use progress_indicator::*;
mod groupbox;
pub use groupbox::*;
mod lineedit;
pub use lineedit::*;
mod scrollview;
pub use scrollview::*;
mod listviewitem;
pub use listviewitem::*;
mod combobox;
pub use combobox::*;
mod tabwidget;
pub use tabwidget::*;
mod stylemetrics;
pub use stylemetrics::*;
mod palette;
pub use palette::*;
mod tableheadersection;
pub use tableheadersection::*;