use std::{os::raw::c_void, ptr::NonNull};
use ohos_arkui_sys::{
ArkUI_NodeContentEvent, ArkUI_NodeContentHandle, OH_ArkUI_NodeContentEvent_GetEventType,
OH_ArkUI_NodeContentEvent_GetNodeContentHandle, OH_ArkUI_NodeContent_GetUserData,
OH_ArkUI_NodeContent_RegisterCallback, OH_ArkUI_NodeContent_SetUserData,
};
use crate::{check_arkui_status, ArkUINode, ArkUIResult};
fn non_null_or_panic<T>(ptr: *mut T, name: &'static str) -> NonNull<T> {
NonNull::new(ptr).unwrap_or_else(|| panic!("{name} pointer is null"))
}
pub struct NodeContentEvent {
raw: NonNull<ArkUI_NodeContentEvent>,
}
impl NodeContentEvent {
fn from_raw(raw: *mut ArkUI_NodeContentEvent) -> Option<Self> {
NonNull::new(raw).map(|raw| Self { raw })
}
fn raw(&self) -> *mut ArkUI_NodeContentEvent {
self.raw.as_ptr()
}
pub fn event_type(&self) -> crate::NodeContentEventType {
unsafe { OH_ArkUI_NodeContentEvent_GetEventType(self.raw()).into() }
}
pub fn node_content_handle(&self) -> Option<crate::ArkUIHandle> {
let handle = unsafe { OH_ArkUI_NodeContentEvent_GetNodeContentHandle(self.raw()) };
crate::ArkUIHandle::from_raw(handle)
}
}
struct NodeContentCallbackContext {
callback: Box<dyn Fn(&mut NodeContentEvent)>,
}
pub struct NodeContent {
handle: crate::ArkUIHandle,
callback_context: Option<NonNull<NodeContentCallbackContext>>,
}
impl NodeContent {
pub fn from_handle(handle: crate::ArkUIHandle) -> Self {
Self {
handle,
callback_context: None,
}
}
pub(crate) fn from_raw(raw: ArkUI_NodeContentHandle) -> Option<Self> {
crate::ArkUIHandle::from_raw(raw).map(Self::from_handle)
}
#[cfg(feature = "napi")]
pub fn from_napi_handle(handle: &crate::ArkUIHandle) -> Option<Self> {
Some(Self::from_handle(*handle))
}
pub fn handle(&self) -> crate::ArkUIHandle {
self.handle
}
pub fn add_node(&self, node: &ArkUINode) -> ArkUIResult<()> {
self.handle.add_node(node)
}
pub fn remove_node(&self, node: &ArkUINode) -> ArkUIResult<()> {
self.handle.remove_node(node)
}
pub fn insert_node(&self, node: &ArkUINode, position: i32) -> ArkUIResult<()> {
self.handle.insert_node(node, position)
}
pub fn register_callback<T: Fn(&mut NodeContentEvent) + 'static>(
&mut self,
callback: T,
) -> ArkUIResult<()> {
self.clear_callback()?;
let context = Box::into_raw(Box::new(NodeContentCallbackContext {
callback: Box::new(callback),
}));
let context = non_null_or_panic(context, "NodeContentCallbackContext");
if let Err(err) = self
.handle
.set_node_content_user_data(context.as_ptr().cast())
{
unsafe {
drop(Box::from_raw(context.as_ptr()));
}
return Err(err);
}
let register_result = self
.handle
.register_node_content_callback(Some(node_content_callback_trampoline));
if let Err(err) = register_result {
let _ = self.handle.set_node_content_user_data(std::ptr::null_mut());
unsafe {
drop(Box::from_raw(context.as_ptr()));
}
return Err(err);
}
self.callback_context = Some(context);
Ok(())
}
pub fn clear_callback(&mut self) -> ArkUIResult<()> {
let unregister_result = self.handle.register_node_content_callback(None);
let clear_data_result = self.handle.set_node_content_user_data(std::ptr::null_mut());
if unregister_result.is_ok() || clear_data_result.is_ok() {
if let Some(context) = self.callback_context.take() {
unsafe {
drop(Box::from_raw(context.as_ptr()));
}
}
}
unregister_result?;
clear_data_result?;
Ok(())
}
}
impl crate::ArkUIHandle {
pub(crate) fn register_node_content_callback(
&self,
callback: Option<unsafe extern "C" fn(event: *mut ArkUI_NodeContentEvent)>,
) -> ArkUIResult<()> {
unsafe { check_arkui_status!(OH_ArkUI_NodeContent_RegisterCallback(self.raw(), callback)) }
}
pub(crate) fn set_node_content_user_data(&self, user_data: *mut c_void) -> ArkUIResult<()> {
unsafe { check_arkui_status!(OH_ArkUI_NodeContent_SetUserData(self.raw(), user_data)) }
}
pub(crate) fn node_content_user_data(&self) -> *mut c_void {
unsafe { OH_ArkUI_NodeContent_GetUserData(self.raw()) }
}
}
unsafe extern "C" fn node_content_callback_trampoline(event: *mut ArkUI_NodeContentEvent) {
let Some(mut event) = NodeContentEvent::from_raw(event) else {
return;
};
let Some(content_handle) = event.node_content_handle() else {
return;
};
let user_data = content_handle.node_content_user_data();
if user_data.is_null() {
return;
}
let callback = unsafe { &*(user_data as *mut NodeContentCallbackContext) };
(callback.callback)(&mut event);
}