#![allow(unsafe_op_in_unsafe_fn)]
use core::ptr::NonNull;
use std::ffi::{CString, c_void};
use crate::binding::Binding;
use crate::converters::{ConvertArg, Converted};
use crate::ffi::{
MultiValueConverterVTable, noesis_multi_binding_add_binding, noesis_multi_binding_create,
noesis_multi_binding_destroy, noesis_multi_binding_set_converter,
noesis_multi_binding_set_converter_parameter, noesis_multi_binding_set_mode,
noesis_multi_value_converter_create, noesis_multi_value_converter_destroy,
noesis_set_multi_binding,
};
use crate::view::FrameworkElement;
pub use crate::binding::BindingMode;
pub trait MultiValueConverter: Send + 'static {
fn convert(&self, values: &[ConvertArg], param: &ConvertArg) -> Option<Converted>;
}
impl<F> MultiValueConverter for F
where
F: Fn(&[ConvertArg], &ConvertArg) -> Option<Converted> + Send + 'static,
{
fn convert(&self, values: &[ConvertArg], param: &ConvertArg) -> Option<Converted> {
self(values, param)
}
}
static MULTI_CONVERTER_VTABLE: MultiValueConverterVTable = MultiValueConverterVTable {
convert: multi_convert_trampoline,
};
unsafe extern "C" fn multi_convert_trampoline(
userdata: *mut c_void,
values: *const *mut c_void,
count: u32,
_target_type: *const c_void,
parameter: *mut c_void,
out_result: *mut *mut c_void,
) -> bool {
crate::panic_guard::guard(|| {
let handler = &*userdata.cast::<Box<dyn MultiValueConverter>>();
let args: Vec<ConvertArg> = if values.is_null() || count == 0 {
Vec::new()
} else {
let slice = core::slice::from_raw_parts(values, count as usize);
slice.iter().map(|&p| ConvertArg::new(p)).collect()
};
let param = ConvertArg::new(parameter);
match handler.convert(&args, ¶m) {
Some(result) => {
if !out_result.is_null() {
*out_result = result.into_boxed();
}
true
}
None => false,
}
})
}
unsafe extern "C" fn multi_converter_free_trampoline(userdata: *mut c_void) {
crate::panic_guard::guard(|| {
if userdata.is_null() {
return;
}
drop(Box::from_raw(
userdata.cast::<Box<dyn MultiValueConverter>>(),
));
})
}
pub struct MultiConverter {
ptr: NonNull<c_void>,
}
unsafe impl Send for MultiConverter {}
impl MultiConverter {
#[must_use]
pub fn new<C: MultiValueConverter>(converter: C) -> Self {
let boxed: Box<Box<dyn MultiValueConverter>> = Box::new(Box::new(converter));
let userdata = Box::into_raw(boxed);
let ptr = unsafe {
noesis_multi_value_converter_create(
&MULTI_CONVERTER_VTABLE,
userdata.cast(),
multi_converter_free_trampoline,
)
};
match NonNull::new(ptr) {
Some(ptr) => MultiConverter { ptr },
None => {
unsafe { drop(Box::from_raw(userdata)) };
unreachable!(
"noesis_multi_value_converter_create returned null for a non-null vtable"
);
}
}
}
#[must_use]
pub fn raw(&self) -> *mut c_void {
self.ptr.as_ptr()
}
}
impl Drop for MultiConverter {
fn drop(&mut self) {
unsafe { noesis_multi_value_converter_destroy(self.ptr.as_ptr()) }
}
}
pub struct MultiBinding {
ptr: NonNull<c_void>,
}
unsafe impl Send for MultiBinding {}
impl Default for MultiBinding {
fn default() -> Self {
Self::new()
}
}
impl MultiBinding {
#[must_use]
pub fn new() -> Self {
let ptr = unsafe { noesis_multi_binding_create() };
Self {
ptr: NonNull::new(ptr).expect("noesis_multi_binding_create returned null"),
}
}
#[must_use]
pub fn add_binding(self, binding: Binding) -> Self {
unsafe { noesis_multi_binding_add_binding(self.ptr.as_ptr(), binding.raw()) };
self
}
#[must_use]
pub fn converter(self, converter: &MultiConverter) -> Self {
unsafe { noesis_multi_binding_set_converter(self.ptr.as_ptr(), converter.raw()) };
self
}
#[must_use]
pub fn converter_parameter(self, parameter: &crate::binding::Boxed) -> Self {
unsafe { noesis_multi_binding_set_converter_parameter(self.ptr.as_ptr(), parameter.raw()) };
self
}
#[must_use]
pub fn mode(self, mode: BindingMode) -> Self {
unsafe { noesis_multi_binding_set_mode(self.ptr.as_ptr(), mode as i32) };
self
}
#[must_use]
pub fn raw(&self) -> *mut c_void {
self.ptr.as_ptr()
}
#[must_use = "a false return means the property was not set (unknown name / type mismatch / read-only)"]
pub fn set_on(&self, element: &FrameworkElement, dp_name: &str) -> bool {
let c = CString::new(dp_name).expect("dp name contained interior NUL");
unsafe { noesis_set_multi_binding(element.raw(), c.as_ptr(), self.ptr.as_ptr()) }
}
}
impl Drop for MultiBinding {
fn drop(&mut self) {
unsafe { noesis_multi_binding_destroy(self.ptr.as_ptr()) }
}
}