Skip to main content

ohos_arkui_binding/api/
node_content.rs

1//! Module api::node_content wrappers and related types.
2
3use std::{os::raw::c_void, ptr::NonNull};
4
5use ohos_arkui_sys::{
6    ArkUI_NodeContentEvent, ArkUI_NodeContentHandle, OH_ArkUI_NodeContentEvent_GetEventType,
7    OH_ArkUI_NodeContentEvent_GetNodeContentHandle, OH_ArkUI_NodeContent_GetUserData,
8    OH_ArkUI_NodeContent_RegisterCallback, OH_ArkUI_NodeContent_SetUserData,
9};
10
11use crate::{check_arkui_status, ArkUINode, ArkUIResult};
12
13fn non_null_or_panic<T>(ptr: *mut T, name: &'static str) -> NonNull<T> {
14    NonNull::new(ptr).unwrap_or_else(|| panic!("{name} pointer is null"))
15}
16
17/// Event payload passed to node-content callbacks.
18pub struct NodeContentEvent {
19    raw: NonNull<ArkUI_NodeContentEvent>,
20}
21
22impl NodeContentEvent {
23    fn from_raw(raw: *mut ArkUI_NodeContentEvent) -> Option<Self> {
24        NonNull::new(raw).map(|raw| Self { raw })
25    }
26
27    fn raw(&self) -> *mut ArkUI_NodeContentEvent {
28        self.raw.as_ptr()
29    }
30
31    /// Returns the concrete event kind.
32    pub fn event_type(&self) -> crate::NodeContentEventType {
33        unsafe { OH_ArkUI_NodeContentEvent_GetEventType(self.raw()).into() }
34    }
35
36    /// Returns the `NodeContent` handle associated with this callback.
37    pub fn node_content_handle(&self) -> Option<crate::ArkUIHandle> {
38        let handle = unsafe { OH_ArkUI_NodeContentEvent_GetNodeContentHandle(self.raw()) };
39        crate::ArkUIHandle::from_raw(handle)
40    }
41}
42
43struct NodeContentCallbackContext {
44    callback: Box<dyn Fn(&mut NodeContentEvent)>,
45}
46
47/// Safe wrapper for `ArkUI_NodeContentHandle`.
48pub struct NodeContent {
49    handle: crate::ArkUIHandle,
50    callback_context: Option<NonNull<NodeContentCallbackContext>>,
51}
52
53impl NodeContent {
54    /// Wrap an existing node-content handle.
55    pub fn from_handle(handle: crate::ArkUIHandle) -> Self {
56        Self {
57            handle,
58            callback_context: None,
59        }
60    }
61
62    pub(crate) fn from_raw(raw: ArkUI_NodeContentHandle) -> Option<Self> {
63        crate::ArkUIHandle::from_raw(raw).map(Self::from_handle)
64    }
65
66    #[cfg(feature = "napi")]
67    pub fn from_napi_handle(handle: &crate::ArkUIHandle) -> Option<Self> {
68        Some(Self::from_handle(*handle))
69    }
70
71    /// Returns the underlying node-content handle wrapper.
72    pub fn handle(&self) -> crate::ArkUIHandle {
73        self.handle
74    }
75
76    /// Appends a child node to this content.
77    pub fn add_node(&self, node: &ArkUINode) -> ArkUIResult<()> {
78        self.handle.add_node(node)
79    }
80
81    /// Removes a child node from this content.
82    pub fn remove_node(&self, node: &ArkUINode) -> ArkUIResult<()> {
83        self.handle.remove_node(node)
84    }
85
86    /// Inserts a child node at the given position.
87    pub fn insert_node(&self, node: &ArkUINode, position: i32) -> ArkUIResult<()> {
88        self.handle.insert_node(node, position)
89    }
90
91    /// Registers a callback invoked when node-content events are emitted.
92    pub fn register_callback<T: Fn(&mut NodeContentEvent) + 'static>(
93        &mut self,
94        callback: T,
95    ) -> ArkUIResult<()> {
96        self.clear_callback()?;
97        let context = Box::into_raw(Box::new(NodeContentCallbackContext {
98            callback: Box::new(callback),
99        }));
100        let context = non_null_or_panic(context, "NodeContentCallbackContext");
101        if let Err(err) = self
102            .handle
103            .set_node_content_user_data(context.as_ptr().cast())
104        {
105            unsafe {
106                drop(Box::from_raw(context.as_ptr()));
107            }
108            return Err(err);
109        }
110        let register_result = self
111            .handle
112            .register_node_content_callback(Some(node_content_callback_trampoline));
113        if let Err(err) = register_result {
114            let _ = self.handle.set_node_content_user_data(std::ptr::null_mut());
115            unsafe {
116                drop(Box::from_raw(context.as_ptr()));
117            }
118            return Err(err);
119        }
120        self.callback_context = Some(context);
121        Ok(())
122    }
123
124    /// Unregisters the callback and releases callback resources.
125    pub fn clear_callback(&mut self) -> ArkUIResult<()> {
126        let unregister_result = self.handle.register_node_content_callback(None);
127        let clear_data_result = self.handle.set_node_content_user_data(std::ptr::null_mut());
128
129        if unregister_result.is_ok() || clear_data_result.is_ok() {
130            if let Some(context) = self.callback_context.take() {
131                unsafe {
132                    drop(Box::from_raw(context.as_ptr()));
133                }
134            }
135        }
136
137        unregister_result?;
138        clear_data_result?;
139        Ok(())
140    }
141}
142
143impl crate::ArkUIHandle {
144    pub(crate) fn register_node_content_callback(
145        &self,
146        callback: Option<unsafe extern "C" fn(event: *mut ArkUI_NodeContentEvent)>,
147    ) -> ArkUIResult<()> {
148        unsafe { check_arkui_status!(OH_ArkUI_NodeContent_RegisterCallback(self.raw(), callback)) }
149    }
150
151    pub(crate) fn set_node_content_user_data(&self, user_data: *mut c_void) -> ArkUIResult<()> {
152        unsafe { check_arkui_status!(OH_ArkUI_NodeContent_SetUserData(self.raw(), user_data)) }
153    }
154
155    pub(crate) fn node_content_user_data(&self) -> *mut c_void {
156        unsafe { OH_ArkUI_NodeContent_GetUserData(self.raw()) }
157    }
158}
159
160unsafe extern "C" fn node_content_callback_trampoline(event: *mut ArkUI_NodeContentEvent) {
161    let Some(mut event) = NodeContentEvent::from_raw(event) else {
162        return;
163    };
164    let Some(content_handle) = event.node_content_handle() else {
165        return;
166    };
167    let user_data = content_handle.node_content_user_data();
168    if user_data.is_null() {
169        return;
170    }
171    let callback = unsafe { &*(user_data as *mut NodeContentCallbackContext) };
172    (callback.callback)(&mut event);
173}