use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use qtbridge_type_lib::{QMetaObject, QObject, QVariantList};
use crate::QObjectHolder;
#[cxx::bridge]
pub mod ffi {
unsafe extern "C++" {
include!("qtbridge-type-lib/src/generated/core/qobject/cpp/qobject.h");
type QObject = qtbridge_type_lib::QObject;
include!("cpp/qml_method_invoker.h");
unsafe fn connect_destroyed_callback(obj: *mut QObject, flag_ptr: usize);
}
extern "Rust" {
fn on_qobject_destroyed(flag_ptr: usize);
}
}
fn on_qobject_destroyed(flag_ptr: usize) {
let arc = unsafe { Arc::from_raw(flag_ptr as *const AtomicBool) };
arc.store(false, Ordering::Release);
}
#[macro_export]
macro_rules! invoke_method {
($invoker:expr, $name:expr $(,)?) => {{
$invoker.invoke_method($name)
}};
($invoker:expr, $name:expr, $($arg:expr),+ $(,)?) => {{
let args = qtbridge::qtbridge_type_lib::QVariantList::from([
$(qtbridge::qtbridge_type_lib::QVariant::from($arg)),+
]);
$invoker.invoke_method_with_args($name, &args)
}};
}
pub struct QmlMethodInvoker {
obj: *mut QObject,
alive: Arc<AtomicBool>,
}
unsafe impl Send for QmlMethodInvoker {}
impl QmlMethodInvoker {
pub fn new<T: QObjectHolder>(target: &T) -> Self {
let obj = target.get_qobject_ptr();
let alive = Arc::new(AtomicBool::new(true));
let flag_ptr = Arc::into_raw(alive.clone()) as usize;
unsafe { ffi::connect_destroyed_callback(obj, flag_ptr) };
Self { obj, alive }
}
fn is_alive(&self) -> bool {
self.alive.load(Ordering::Acquire)
}
pub fn invoke_method(&self, name: &str) -> bool {
if !self.is_alive() {
return false;
}
QMetaObject::invoke_method(self.obj, name)
}
pub fn invoke_method_with_args(&self, name: &str, args: &QVariantList) -> bool {
if !self.is_alive() {
return false;
}
QMetaObject::invoke_method_with_args(self.obj, name, args)
}
}