use super::*;
use cpp::cpp;
use std::hash::{Hash, Hasher};
#[repr(C)]
pub struct SGNode<T> {
pub raw: *mut c_void,
_phantom: std::marker::PhantomData<T>,
}
impl<T> SGNode<T> {
pub unsafe fn from_raw(raw: *mut c_void) -> Self {
Self { raw, _phantom: Default::default() }
}
pub fn into_raw(self) -> *mut c_void {
let cpp = self.raw;
std::mem::forget(self);
cpp
}
pub fn reset(&mut self) {
*self = unsafe { Self::from_raw(std::ptr::null_mut()) };
}
}
impl<T> Drop for SGNode<T> {
fn drop(&mut self) {
let raw = self.raw;
cpp!(unsafe [raw as "QSGNode*"] { delete raw; });
}
}
pub enum ContainerNode {}
cpp! {{
struct ContainerNode : QSGNode {
quint64 type_id = 0;
std::size_t size = 0; quint64 mask = 0; ContainerNode(quint64 type_id, std::size_t size) : type_id(type_id), size(size) {}
};
}}
#[cfg_attr(feature = "cargo-clippy", allow(clippy::len_without_is_empty))]
pub trait UpdateNodeFnTuple<T> {
fn len(&self) -> u64;
unsafe fn update_fn(&self, i: u64, _: *mut c_void) -> *mut c_void;
}
macro_rules! declare_UpdateNodeFnTuple {
(@continue $T:ident : $A:ident : $N:tt $( $tail:tt )+) => { declare_UpdateNodeFnTuple![$( $tail )*]; };
(@continue $T:ident : $A:ident : $N:tt) => {};
($( $T:ident : $A:ident : $N:tt )+) => {
impl<$( $A, $T: Fn(SGNode<$A>) -> SGNode<$A> ),*> UpdateNodeFnTuple<($( $A, )*)> for ($( $T, )*)
{
fn len(&self) -> u64 { ($( $N, )* ).0 + 1 }
unsafe fn update_fn(&self, i: u64, n: *mut c_void) -> *mut c_void {
match i {
$(
$N => (self.$N)( SGNode::<_>::from_raw(n) ).into_raw(),
)*
_ => panic!("Out of range") }
}
}
declare_UpdateNodeFnTuple![@continue $( $T : $A : $N )*];
}
}
declare_UpdateNodeFnTuple![T9:A9:9 T8:A8:8 T7:A7:7 T6:A6:6 T5:A5:5 T4:A4:4 T3:A3:3 T2:A2:2 T1:A1:1 T0:A0:0];
impl<A, T: Fn(SGNode<A>) -> SGNode<A>> UpdateNodeFnTuple<(A,)> for T {
fn len(&self) -> u64 {
1
}
unsafe fn update_fn(&self, _i: u64, n: *mut c_void) -> *mut c_void {
self(SGNode::<A>::from_raw(n)).into_raw()
}
}
impl SGNode<ContainerNode> {
pub fn update_dynamic<T: std::any::Any, Iter: ExactSizeIterator, F>(
&mut self,
iter: Iter,
mut f: F,
) where
F: FnMut(<Iter as Iterator>::Item, SGNode<T>) -> SGNode<T>,
{
let mut raw = self.raw;
let type_id = get_type_hash::<T>();
let len = iter.len();
assert!(len <= 64, "There is a limit of 64 child nodes");
let mut mask = 0u64;
if raw.is_null() {
raw = cpp!(unsafe [type_id as "quint64", len as "std::size_t"] -> *mut c_void as "QSGNode*" {
return new ContainerNode(type_id, len);
});
self.raw = raw;
} else {
mask = cpp!(unsafe [raw as "ContainerNode*", type_id as "quint64", len as "std::size_t"] -> u64 as "quint64" {
if (raw->size != len || raw->type_id != type_id) {
rust!(sgnode_0 []{ panic!("update_dynamic must always be called with the same type and the same number of elements") });
}
return raw->mask;
});
}
let mut bit = 1u64;
let mut before_iter: *mut c_void = std::ptr::null_mut();
for i in iter {
before_iter = Self::iteration(raw, before_iter, &mut bit, &mut mask, |n| {
f(i, unsafe { SGNode::<T>::from_raw(n) }).into_raw()
});
}
cpp!(unsafe [raw as "ContainerNode*", mask as "quint64"] {
raw->mask = mask;
});
}
pub fn update_static<A: 'static, T: UpdateNodeFnTuple<A>>(&mut self, info: T) {
let type_id = get_type_hash::<A>();
let mut mask = 0u64;
if self.raw.is_null() {
self.raw = cpp!(unsafe [type_id as "quint64"] -> *mut c_void as "QSGNode*" {
return new ContainerNode(type_id, -1);
});
} else {
let raw = self.raw;
mask = cpp!(unsafe [raw as "ContainerNode*", type_id as "quint64"] -> u64 as "quint64" {
if (raw->size != std::size_t(-1) || raw->type_id != type_id) {
rust!(sgnode_3 []{ panic!("update_static must always be called with the same type of functions") });
}
return raw->mask;
});
}
let mut bit = 1u64;
let mut before_iter: *mut c_void = std::ptr::null_mut();
for i in 0..info.len() {
before_iter = Self::iteration(self.raw, before_iter, &mut bit, &mut mask, |n| unsafe {
info.update_fn(i, n)
});
}
let raw_ = self.raw;
cpp!(unsafe [raw_ as "ContainerNode*", mask as "quint64"] {
raw_->mask = mask;
});
}
fn iteration<F: FnOnce(*mut c_void) -> *mut c_void>(
raw: *mut c_void,
before_iter: *mut c_void,
bit: &mut u64,
mask: &mut u64,
update_fn: F,
) -> *mut c_void {
let node = if (*mask & *bit) == 0 {
std::ptr::null_mut()
} else {
cpp!(unsafe [raw as "QSGNode*", before_iter as "QSGNode*"] -> *mut c_void as "QSGNode*" {
auto node = before_iter ? before_iter->nextSibling() : raw->firstChild();
if (!node) rust!(sgnode_2 []{ panic!("There must be a node as the mask says so") });
node->setFlag(QSGNode::OwnedByParent, false); return node;
})
};
let node = update_fn(node);
*mask = if node.is_null() { *mask & !*bit } else { *mask | *bit };
if !node.is_null() {
cpp!(unsafe [raw as "QSGNode*", node as "QSGNode*", before_iter as "QSGNode*"] {
if (!node->parent()) {
if (before_iter)
raw->insertChildNodeAfter(node, before_iter);
else
raw->prependChildNode(node);
} else if (node->parent() != raw) {
rust!(sgnode_4 []{ panic!("Returned node from another parent") });
}
node->setFlag(QSGNode::OwnedByParent);
});
}
(*bit) <<= 1;
if node.is_null() {
before_iter
} else {
node
}
}
}
fn get_type_hash<T: std::any::Any>() -> u64 {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
std::any::TypeId::of::<T>().hash(&mut hasher);
hasher.finish()
}
#[cfg(qt_5_8)]
pub enum RectangleNode {}
cpp! {{
#if QT_VERSION < QT_VERSION_CHECK(5, 8, 0)
struct QSGRectangleNode{};
#endif
}}
#[cfg(qt_5_8)]
impl SGNode<RectangleNode> {
pub fn set_color(&mut self, color: QColor) {
let raw = self.raw;
cpp!(unsafe [raw as "QSGRectangleNode*", color as "QColor"] {
#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
if(raw) raw->setColor(color);
#endif
});
}
pub fn set_rect(&mut self, rect: QRectF) {
let raw = self.raw;
cpp!(unsafe [raw as "QSGRectangleNode*", rect as "QRectF"] {
#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
if (raw) raw->setRect(rect);
#endif
});
}
pub fn create(&mut self, item: &dyn QQuickItem) {
if !self.raw.is_null() {
return;
}
let item = item.get_cpp_object();
self.raw = cpp!(unsafe [item as "QQuickItem*"] -> *mut c_void as "void*" {
#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
if (!item) return nullptr;
if (auto window = item->window())
return window->createRectangleNode();
#endif
return nullptr;
});
}
}
pub enum TransformNode {}
impl SGNode<TransformNode> {
pub fn set_translation(&mut self, x: f64, y: f64) {
if self.raw.is_null() {
self.create();
}
let raw = self.raw;
cpp!(unsafe [raw as "QSGTransformNode*", x as "double", y as "double"] {
QMatrix4x4 m;
m.translate(x, y);
if (raw) raw->setMatrix(m);
});
}
pub fn create(&mut self) {
if !self.raw.is_null() {
return;
}
self.raw = cpp!(unsafe [] -> *mut c_void as "void*" { return new QSGTransformNode; });
}
pub fn update_sub_node<F: FnMut(SGNode<ContainerNode>) -> SGNode<ContainerNode>>(
&mut self,
mut f: F,
) {
if self.raw.is_null() {
self.create();
}
let raw = self.raw;
let sub = unsafe {
SGNode::<ContainerNode>::from_raw(
cpp!([raw as "QSGNode*"] -> *mut c_void as "QSGNode*" {
auto n = raw->firstChild();
if (n)
n->setFlag(QSGNode::OwnedByParent, false); return n;
}),
)
};
let sub = f(sub);
let node = sub.into_raw();
cpp!(unsafe [node as "QSGNode*", raw as "QSGNode*"] {
if (!node->parent()) {
raw->prependChildNode(node);
} else if (node->parent() != raw) {
rust!(sgnode_5 []{ panic!("Returned node from another parent") });
}
node->setFlag(QSGNode::OwnedByParent);
});
}
}