#![allow(clippy::missing_safety_doc)]
use std::rc::Rc;
use napi_ohos::{Error, Result};
use ohos_xcomponent_sys::{
OH_NativeXComponent, OH_NativeXComponent_Callback, OH_NativeXComponent_ExpectedRateRange,
OH_NativeXComponent_RegisterCallback, OH_NativeXComponent_RegisterKeyEventCallback,
OH_NativeXComponent_RegisterOnFrameCallback, OH_NativeXComponent_SetExpectedFrameRateRange,
};
use crate::{
code::XComponentResultCode, dispatch_touch_event, key_event, on_frame_change,
on_surface_changed, on_surface_created, on_surface_destroyed, raw::XComponentRaw,
tool::resolve_id, KeyEventData, RawWindow, TouchEventData, WindowRaw, XComponentOffset,
XComponentSize, RAW_WINDOW,
};
#[cfg(not(feature = "multi_mode"))]
use crate::X_COMPONENT_CALLBACKS;
#[cfg(feature = "multi_mode")]
use crate::X_COMPONENT_CALLBACKS_MAP;
#[derive(Debug, Clone)]
pub struct NativeXComponent {
pub raw: XComponentRaw,
pub(crate) id: Option<String>,
}
impl NativeXComponent {
pub fn new(raw: XComponentRaw) -> Self {
Self { raw, id: None }
}
pub fn with_id(raw: XComponentRaw, id: String) -> Self {
Self { raw, id: Some(id) }
}
pub fn id(&self) -> Result<String> {
if let Some(id) = &self.id {
return Ok(id.clone());
}
let current_id = resolve_id(self.raw());
if let Some(id_str) = current_id {
return Ok(id_str);
}
Err(Error::from_reason("Get XComponent id failed."))
}
pub fn raw(&self) -> *mut OH_NativeXComponent {
self.raw.0
}
pub fn native_window(&self) -> Option<RawWindow> {
let guard = (*RAW_WINDOW).read();
if let Ok(guard) = guard {
if let Some(win) = &*guard {
return Some(RawWindow::new(win.raw()));
}
return None;
}
None
}
#[cfg(feature = "callbacks")]
pub fn register_callback(&self) -> Result<()> {
let cbs = Box::new(OH_NativeXComponent_Callback {
OnSurfaceCreated: Some(on_surface_created),
OnSurfaceChanged: Some(on_surface_changed),
OnSurfaceDestroyed: Some(on_surface_destroyed),
DispatchTouchEvent: Some(dispatch_touch_event),
});
let ret: XComponentResultCode = unsafe {
OH_NativeXComponent_RegisterCallback(self.raw(), Box::leak(cbs) as *mut _).into()
};
if ret != XComponentResultCode::Success {
return Err(Error::from_reason("XComponent register callbacks failed"));
}
Ok(())
}
pub fn on_surface_changed<T: Fn(XComponentRaw, WindowRaw) -> Result<()> + 'static>(
&self,
cb: T,
) {
#[cfg(not(feature = "multi_mode"))]
X_COMPONENT_CALLBACKS.with_borrow_mut(|f| {
f.on_surface_changed = Some(Rc::new(cb));
});
#[cfg(feature = "multi_mode")]
{
let id = self.id().unwrap();
X_COMPONENT_CALLBACKS_MAP.with_borrow_mut(|f| {
f.entry(id)
.or_insert_with(|| Default::default())
.on_surface_changed = Some(Rc::new(cb));
});
}
}
pub fn on_surface_created<T: Fn(XComponentRaw, WindowRaw) -> Result<()> + 'static>(
&self,
cb: T,
) {
#[cfg(not(feature = "multi_mode"))]
X_COMPONENT_CALLBACKS.with_borrow_mut(|f| {
f.on_surface_created = Some(Rc::new(cb));
});
#[cfg(feature = "multi_mode")]
{
let id = self.id().unwrap();
X_COMPONENT_CALLBACKS_MAP.with_borrow_mut(|f| {
f.entry(id)
.or_insert_with(|| Default::default())
.on_surface_created = Some(Rc::new(cb));
});
}
}
pub fn on_surface_destroyed<T: Fn(XComponentRaw, WindowRaw) -> Result<()> + 'static>(
&self,
cb: T,
) {
#[cfg(not(feature = "multi_mode"))]
X_COMPONENT_CALLBACKS.with_borrow_mut(|f| {
f.on_surface_destroyed = Some(Rc::new(cb));
});
#[cfg(feature = "multi_mode")]
{
let id = self.id().unwrap();
X_COMPONENT_CALLBACKS_MAP.with_borrow_mut(|f| {
f.entry(id)
.or_insert_with(|| Default::default())
.on_surface_destroyed = Some(Rc::new(cb));
});
}
}
pub fn on_touch_event<
T: Fn(XComponentRaw, WindowRaw, TouchEventData) -> Result<()> + 'static,
>(
&self,
cb: T,
) {
#[cfg(not(feature = "multi_mode"))]
X_COMPONENT_CALLBACKS.with_borrow_mut(|f| {
f.dispatch_touch_event = Some(Rc::new(cb));
});
#[cfg(feature = "multi_mode")]
{
let id = self.id().unwrap();
X_COMPONENT_CALLBACKS_MAP.with_borrow_mut(|f| {
f.entry(id)
.or_insert_with(|| Default::default())
.dispatch_touch_event = Some(Rc::new(cb));
});
}
}
pub unsafe fn register_native_callback(
&self,
callbacks: Box<OH_NativeXComponent_Callback>,
) -> Result<()> {
let ret: XComponentResultCode = unsafe {
OH_NativeXComponent_RegisterCallback(self.raw(), Box::leak(callbacks) as *mut _).into()
};
if ret != XComponentResultCode::Success {
return Err(Error::from_reason("XComponent register callbacks failed"));
}
Ok(())
}
pub fn size(&self, window: WindowRaw) -> Result<XComponentSize> {
self.raw.size(window)
}
pub fn offset(&self, window: WindowRaw) -> Result<XComponentOffset> {
self.raw.offset(window)
}
pub fn set_frame_rate(&self, min: i32, max: i32, expected: i32) -> Result<()> {
let mut range = OH_NativeXComponent_ExpectedRateRange { min, max, expected };
let ret: XComponentResultCode = unsafe {
OH_NativeXComponent_SetExpectedFrameRateRange(
self.raw(),
&mut range as *mut _ as *mut OH_NativeXComponent_ExpectedRateRange,
)
.into()
};
if ret != XComponentResultCode::Success {
return Err(Error::from_reason("XComponent set frame rate failed"));
}
Ok(())
}
pub fn on_frame_callback<T: Fn(XComponentRaw, u64, u64) -> Result<()> + 'static>(
&self,
cb: T,
) -> Result<()> {
#[cfg(not(feature = "multi_mode"))]
X_COMPONENT_CALLBACKS.with_borrow_mut(|f| {
f.on_frame_change = Some(Rc::new(cb));
});
#[cfg(feature = "multi_mode")]
{
let id = self.id().unwrap();
X_COMPONENT_CALLBACKS_MAP.with_borrow_mut(|f| {
f.entry(id)
.or_insert_with(|| Default::default())
.on_frame_change = Some(Rc::new(cb));
});
}
let ret: XComponentResultCode = unsafe {
OH_NativeXComponent_RegisterOnFrameCallback(self.raw(), Some(on_frame_change)).into()
};
if ret != XComponentResultCode::Success {
return Err(Error::from_reason(
"XComponent register frame callback failed",
));
}
Ok(())
}
pub fn on_key_event<T: Fn(XComponentRaw, WindowRaw, KeyEventData) -> Result<()> + 'static>(
&self,
cb: T,
) -> Result<()> {
#[cfg(not(feature = "multi_mode"))]
X_COMPONENT_CALLBACKS.with_borrow_mut(|f| {
f.on_key_event = Some(Rc::new(cb));
});
#[cfg(feature = "multi_mode")]
{
let id = self.id().unwrap();
X_COMPONENT_CALLBACKS_MAP.with_borrow_mut(|f| {
f.entry(id)
.or_insert_with(|| Default::default())
.on_key_event = Some(Rc::new(cb));
});
}
let ret: XComponentResultCode = unsafe {
OH_NativeXComponent_RegisterKeyEventCallback(self.raw(), Some(key_event)).into()
};
if ret != XComponentResultCode::Success {
return Err(Error::from_reason(
"XComponent register key event callback failed",
));
}
Ok(())
}
}