Skip to main content

ohos_xcomponent_binding/
native_xcomponent.rs

1#![allow(clippy::missing_safety_doc)]
2
3use std::rc::Rc;
4
5use napi_ohos::{Error, Result};
6use ohos_arkui_input_binding::ArkUIInputEvent;
7use ohos_xcomponent_sys::{
8    OH_NativeXComponent, OH_NativeXComponent_Callback, OH_NativeXComponent_ExpectedRateRange,
9    OH_NativeXComponent_MouseEvent_Callback, OH_NativeXComponent_RegisterCallback,
10    OH_NativeXComponent_RegisterKeyEventCallback, OH_NativeXComponent_RegisterMouseEventCallback,
11    OH_NativeXComponent_RegisterOnFrameCallback, OH_NativeXComponent_RegisterUIInputEventCallback,
12    OH_NativeXComponent_SetExpectedFrameRateRange,
13};
14
15use crate::{
16    code::XComponentResultCode, dispatch_touch_event, key_event, on_frame_change, on_hover_event,
17    on_mouse_event, on_surface_changed, on_surface_created, on_surface_destroyed,
18    on_ui_input_event, raw::XComponentRaw, tool::resolve_id, KeyEventData, MouseEventData,
19    RawWindow, TouchEventData, WindowRaw, XComponentOffset, XComponentSize, RAW_WINDOW,
20};
21
22#[cfg(not(feature = "multi_mode"))]
23use crate::X_COMPONENT_CALLBACKS;
24
25#[cfg(feature = "multi_mode")]
26use crate::X_COMPONENT_CALLBACKS_MAP;
27
28#[derive(Debug, Clone)]
29pub struct NativeXComponent {
30    pub raw: XComponentRaw,
31    pub(crate) id: Option<String>,
32}
33
34impl NativeXComponent {
35    pub fn new(raw: XComponentRaw) -> Self {
36        Self { raw, id: None }
37    }
38
39    pub fn with_id(raw: XComponentRaw, id: String) -> Self {
40        Self { raw, id: Some(id) }
41    }
42
43    /// Get current xcomponent instance's id
44    pub fn id(&self) -> Result<String> {
45        if let Some(id) = &self.id {
46            return Ok(id.clone());
47        }
48        let current_id = resolve_id(self.raw());
49        if let Some(id_str) = current_id {
50            return Ok(id_str);
51        }
52        Err(Error::from_reason("Get XComponent id failed."))
53    }
54
55    /// get raw point
56    pub fn raw(&self) -> *mut OH_NativeXComponent {
57        self.raw.0
58    }
59
60    pub fn native_window(&self) -> Option<RawWindow> {
61        let guard = (*RAW_WINDOW).read();
62        if let Ok(guard) = guard {
63            if let Some(win) = &*guard {
64                return Some(RawWindow::new(win.raw()));
65            }
66            return None;
67        }
68        None
69    }
70
71    /// Register callbacks   
72    /// For multi-mode, it will use hashmap to store all of your callbacks closure.   
73    /// This may cause xcomponent being slower, if you want to avoid this.    
74    /// You can disable feature with `callbacks` and use `register_native_callback`   
75    #[cfg(feature = "callbacks")]
76    pub fn register_callback(&self) -> Result<()> {
77        let cbs = Box::new(OH_NativeXComponent_Callback {
78            OnSurfaceCreated: Some(on_surface_created),
79            OnSurfaceChanged: Some(on_surface_changed),
80            OnSurfaceDestroyed: Some(on_surface_destroyed),
81            DispatchTouchEvent: Some(dispatch_touch_event),
82        });
83        let ret: XComponentResultCode = unsafe {
84            OH_NativeXComponent_RegisterCallback(self.raw(), Box::leak(cbs) as *mut _).into()
85        };
86        if ret != XComponentResultCode::Success {
87            return Err(Error::from_reason("XComponent register callbacks failed"));
88        }
89        Ok(())
90    }
91
92    pub fn on_surface_changed<T: Fn(XComponentRaw, WindowRaw) -> Result<()> + 'static>(
93        &self,
94        cb: T,
95    ) {
96        #[cfg(not(feature = "multi_mode"))]
97        X_COMPONENT_CALLBACKS.with_borrow_mut(|f| {
98            f.on_surface_changed = Some(Rc::new(cb));
99        });
100
101        #[cfg(feature = "multi_mode")]
102        {
103            let id = self.id().unwrap();
104            X_COMPONENT_CALLBACKS_MAP.with_borrow_mut(|f| {
105                f.entry(id).or_default().on_surface_changed = Some(Rc::new(cb));
106            });
107        }
108    }
109
110    pub fn on_surface_created<T: Fn(XComponentRaw, WindowRaw) -> Result<()> + 'static>(
111        &self,
112        cb: T,
113    ) {
114        #[cfg(not(feature = "multi_mode"))]
115        X_COMPONENT_CALLBACKS.with_borrow_mut(|f| {
116            f.on_surface_created = Some(Rc::new(cb));
117        });
118
119        #[cfg(feature = "multi_mode")]
120        {
121            let id = self.id().unwrap();
122            X_COMPONENT_CALLBACKS_MAP.with_borrow_mut(|f| {
123                f.entry(id).or_default().on_surface_created = Some(Rc::new(cb));
124            });
125        }
126    }
127
128    pub fn on_surface_destroyed<T: Fn(XComponentRaw, WindowRaw) -> Result<()> + 'static>(
129        &self,
130        cb: T,
131    ) {
132        #[cfg(not(feature = "multi_mode"))]
133        X_COMPONENT_CALLBACKS.with_borrow_mut(|f| {
134            f.on_surface_destroyed = Some(Rc::new(cb));
135        });
136
137        #[cfg(feature = "multi_mode")]
138        {
139            let id = self.id().unwrap();
140            X_COMPONENT_CALLBACKS_MAP.with_borrow_mut(|f| {
141                f.entry(id).or_default().on_surface_destroyed = Some(Rc::new(cb));
142            });
143        }
144    }
145
146    pub fn on_touch_event<
147        T: Fn(XComponentRaw, WindowRaw, TouchEventData) -> Result<()> + 'static,
148    >(
149        &self,
150        cb: T,
151    ) {
152        #[cfg(not(feature = "multi_mode"))]
153        X_COMPONENT_CALLBACKS.with_borrow_mut(|f| {
154            f.dispatch_touch_event = Some(Rc::new(cb));
155        });
156
157        #[cfg(feature = "multi_mode")]
158        {
159            let id = self.id().unwrap();
160            X_COMPONENT_CALLBACKS_MAP.with_borrow_mut(|f| {
161                f.entry(id).or_default().dispatch_touch_event = Some(Rc::new(cb));
162            });
163        }
164    }
165
166    /// Use ffi to register callbacks directly.
167    pub unsafe fn register_native_callback(
168        &self,
169        callbacks: Box<OH_NativeXComponent_Callback>,
170    ) -> Result<()> {
171        let ret: XComponentResultCode = unsafe {
172            OH_NativeXComponent_RegisterCallback(self.raw(), Box::leak(callbacks) as *mut _).into()
173        };
174        if ret != XComponentResultCode::Success {
175            return Err(Error::from_reason("XComponent register callbacks failed"));
176        }
177        Ok(())
178    }
179
180    /// Get current XComponent's size info include width and height.
181    pub fn size(&self, window: WindowRaw) -> Result<XComponentSize> {
182        self.raw.size(window)
183    }
184
185    pub fn offset(&self, window: WindowRaw) -> Result<XComponentOffset> {
186        self.raw.offset(window)
187    }
188
189    pub fn set_frame_rate(&self, min: i32, max: i32, expected: i32) -> Result<()> {
190        let mut range = OH_NativeXComponent_ExpectedRateRange { min, max, expected };
191        let ret: XComponentResultCode = unsafe {
192            OH_NativeXComponent_SetExpectedFrameRateRange(self.raw(), &mut range as *mut _).into()
193        };
194        if ret != XComponentResultCode::Success {
195            return Err(Error::from_reason("XComponent set frame rate failed"));
196        }
197        Ok(())
198    }
199
200    /// Register frame callback
201    pub fn on_frame_callback<T: Fn(XComponentRaw, u64, u64) -> Result<()> + 'static>(
202        &self,
203        cb: T,
204    ) -> Result<()> {
205        #[cfg(not(feature = "multi_mode"))]
206        X_COMPONENT_CALLBACKS.with_borrow_mut(|f| {
207            f.on_frame_change = Some(Rc::new(cb));
208        });
209
210        #[cfg(feature = "multi_mode")]
211        {
212            let id = self.id().unwrap();
213            X_COMPONENT_CALLBACKS_MAP.with_borrow_mut(|f| {
214                f.entry(id).or_default().on_frame_change = Some(Rc::new(cb));
215            });
216        }
217
218        let ret: XComponentResultCode = unsafe {
219            OH_NativeXComponent_RegisterOnFrameCallback(self.raw(), Some(on_frame_change)).into()
220        };
221        if ret != XComponentResultCode::Success {
222            return Err(Error::from_reason(
223                "XComponent register frame callback failed",
224            ));
225        }
226        Ok(())
227    }
228
229    pub fn on_key_event<T: Fn(XComponentRaw, WindowRaw, KeyEventData) -> Result<()> + 'static>(
230        &self,
231        cb: T,
232    ) -> Result<()> {
233        #[cfg(not(feature = "multi_mode"))]
234        X_COMPONENT_CALLBACKS.with_borrow_mut(|f| {
235            f.on_key_event = Some(Rc::new(cb));
236        });
237
238        #[cfg(feature = "multi_mode")]
239        {
240            let id = self.id().unwrap();
241            X_COMPONENT_CALLBACKS_MAP.with_borrow_mut(|f| {
242                f.entry(id).or_default().on_key_event = Some(Rc::new(cb));
243            });
244        }
245
246        let ret: XComponentResultCode = unsafe {
247            OH_NativeXComponent_RegisterKeyEventCallback(self.raw(), Some(key_event)).into()
248        };
249        if ret != XComponentResultCode::Success {
250            return Err(Error::from_reason(
251                "XComponent register key event callback failed",
252            ));
253        }
254        Ok(())
255    }
256
257    pub fn on_hover_event<T: Fn(XComponentRaw, bool) -> Result<()> + 'static>(
258        &self,
259        cb: T,
260    ) -> Result<()> {
261        #[cfg(not(feature = "multi_mode"))]
262        X_COMPONENT_CALLBACKS.with_borrow_mut(|f| {
263            f.on_hover_event = Some(Rc::new(cb));
264        });
265
266        #[cfg(feature = "multi_mode")]
267        {
268            let id = self.id().unwrap();
269            X_COMPONENT_CALLBACKS_MAP.with_borrow_mut(|f| {
270                f.entry(id).or_default().on_hover_event = Some(Rc::new(cb));
271            });
272        }
273        Ok(())
274    }
275
276    pub fn on_mouse_event<
277        T: Fn(XComponentRaw, WindowRaw, MouseEventData) -> Result<()> + 'static,
278    >(
279        &self,
280        cb: T,
281    ) -> Result<()> {
282        #[cfg(not(feature = "multi_mode"))]
283        X_COMPONENT_CALLBACKS.with_borrow_mut(|f| {
284            f.on_mouse_event = Some(Rc::new(cb));
285        });
286
287        #[cfg(feature = "multi_mode")]
288        {
289            let id = self.id().unwrap();
290            X_COMPONENT_CALLBACKS_MAP.with_borrow_mut(|f| {
291                f.entry(id).or_default().on_mouse_event = Some(Rc::new(cb));
292            });
293        }
294        Ok(())
295    }
296
297    pub fn register_mouse_event_callback(&self) -> Result<()> {
298        let callback = Box::new(OH_NativeXComponent_MouseEvent_Callback {
299            DispatchMouseEvent: Some(on_mouse_event),
300            DispatchHoverEvent: Some(on_hover_event),
301        });
302        let ret: XComponentResultCode = unsafe {
303            OH_NativeXComponent_RegisterMouseEventCallback(
304                self.raw(),
305                Box::leak(callback) as *mut _,
306            )
307            .into()
308        };
309        if ret != XComponentResultCode::Success {
310            return Err(Error::from_reason(
311                "XComponent register mouse event callback failed",
312            ));
313        }
314        Ok(())
315    }
316
317    pub fn on_ui_input_event<T: Fn(XComponentRaw, ArkUIInputEvent) -> Result<()> + 'static>(
318        &self,
319        cb: T,
320    ) -> Result<()> {
321        #[cfg(not(feature = "multi_mode"))]
322        X_COMPONENT_CALLBACKS.with_borrow_mut(|f| {
323            f.on_ui_input_event = Some(Rc::new(cb));
324        });
325
326        #[cfg(feature = "multi_mode")]
327        {
328            let id = self.id().unwrap();
329            X_COMPONENT_CALLBACKS_MAP.with_borrow_mut(|f| {
330                f.entry(id).or_default().on_ui_input_event = Some(Rc::new(cb));
331            });
332        }
333        let ret: XComponentResultCode = unsafe {
334            OH_NativeXComponent_RegisterUIInputEventCallback(
335                self.raw(),
336                Some(on_ui_input_event),
337                ohos_arkui_input_binding::UIInputEvent::Axis.into(),
338            )
339            .into()
340        };
341        if ret != XComponentResultCode::Success {
342            return Err(Error::from_reason(
343                "XComponent register ui input event callback failed",
344            ));
345        }
346        Ok(())
347    }
348}