use core::ptr::NonNull;
use std::ffi::{CStr, c_void};
use crate::binding::ObservableCollection;
use crate::ffi::{
ClickFn, SubscriptionFreeFn, noesis_base_component_release, noesis_collection_view_count,
noesis_collection_view_current_item, noesis_collection_view_current_position,
noesis_collection_view_is_current_after_last, noesis_collection_view_is_current_before_first,
noesis_collection_view_move_current_to_first, noesis_collection_view_move_current_to_last,
noesis_collection_view_move_current_to_next, noesis_collection_view_move_current_to_position,
noesis_collection_view_move_current_to_previous, noesis_collection_view_refresh,
noesis_collection_view_source_create, noesis_collection_view_source_get_view,
noesis_collection_view_source_set_source, noesis_collection_view_subscribe_current_changed,
noesis_collection_view_unsubscribe_current_changed, noesis_unbox_bool, noesis_unbox_double,
noesis_unbox_int32, noesis_unbox_string,
};
pub struct CollectionViewSource {
ptr: NonNull<c_void>,
}
unsafe impl Send for CollectionViewSource {}
impl Default for CollectionViewSource {
fn default() -> Self {
Self::new()
}
}
impl CollectionViewSource {
#[must_use]
pub fn new() -> Self {
let ptr = unsafe { noesis_collection_view_source_create() };
Self {
ptr: NonNull::new(ptr).expect("noesis_collection_view_source_create returned null"),
}
}
#[must_use]
pub fn raw(&self) -> *mut c_void {
self.ptr.as_ptr()
}
pub fn set_source(&mut self, source: &ObservableCollection) -> bool {
unsafe { noesis_collection_view_source_set_source(self.ptr.as_ptr(), source.raw()) }
}
pub unsafe fn set_source_raw(&mut self, source: *mut c_void) -> bool {
unsafe { noesis_collection_view_source_set_source(self.ptr.as_ptr(), source) }
}
#[must_use]
pub fn view(&self) -> Option<CollectionView> {
let p = unsafe { noesis_collection_view_source_get_view(self.ptr.as_ptr()) };
NonNull::new(p).map(|ptr| CollectionView { ptr })
}
}
impl Drop for CollectionViewSource {
fn drop(&mut self) {
unsafe { noesis_base_component_release(self.ptr.as_ptr()) }
}
}
pub struct CollectionView {
ptr: NonNull<c_void>,
}
unsafe impl Send for CollectionView {}
impl CollectionView {
#[must_use]
pub fn raw(&self) -> *mut c_void {
self.ptr.as_ptr()
}
#[must_use]
pub fn count(&self) -> u32 {
let n = unsafe { noesis_collection_view_count(self.ptr.as_ptr()) };
u32::try_from(n.max(0)).unwrap_or(0)
}
#[must_use]
pub fn current_position(&self) -> i32 {
unsafe { noesis_collection_view_current_position(self.ptr.as_ptr()) }
}
#[must_use]
pub fn current_item(&self) -> Option<CurrentItem> {
let p = unsafe { noesis_collection_view_current_item(self.ptr.as_ptr()) };
NonNull::new(p).map(|ptr| CurrentItem { ptr })
}
#[must_use]
pub fn is_current_before_first(&self) -> bool {
unsafe { noesis_collection_view_is_current_before_first(self.ptr.as_ptr()) }
}
#[must_use]
pub fn is_current_after_last(&self) -> bool {
unsafe { noesis_collection_view_is_current_after_last(self.ptr.as_ptr()) }
}
pub fn move_current_to_first(&self) -> bool {
unsafe { noesis_collection_view_move_current_to_first(self.ptr.as_ptr()) }
}
pub fn move_current_to_last(&self) -> bool {
unsafe { noesis_collection_view_move_current_to_last(self.ptr.as_ptr()) }
}
pub fn move_current_to_next(&self) -> bool {
unsafe { noesis_collection_view_move_current_to_next(self.ptr.as_ptr()) }
}
pub fn move_current_to_previous(&self) -> bool {
unsafe { noesis_collection_view_move_current_to_previous(self.ptr.as_ptr()) }
}
pub fn move_current_to_position(&self, position: i32) -> bool {
unsafe { noesis_collection_view_move_current_to_position(self.ptr.as_ptr(), position) }
}
pub fn refresh(&self) {
unsafe { noesis_collection_view_refresh(self.ptr.as_ptr()) }
}
#[must_use]
pub fn subscribe_current_changed<H: CurrentChangedHandler>(
&self,
handler: H,
) -> Option<CurrentChangedSubscription> {
let outer: Box<Box<dyn CurrentChangedHandler>> = Box::new(Box::new(handler));
let userdata = Box::into_raw(outer);
let token = unsafe {
noesis_collection_view_subscribe_current_changed(
self.ptr.as_ptr(),
current_changed_trampoline,
userdata.cast(),
current_changed_free,
)
};
if let Some(token) = NonNull::new(token) {
Some(CurrentChangedSubscription { token })
} else {
drop(unsafe { Box::from_raw(userdata) });
None
}
}
}
impl Drop for CollectionView {
fn drop(&mut self) {
unsafe { noesis_base_component_release(self.ptr.as_ptr()) }
}
}
pub struct CurrentItem {
ptr: NonNull<c_void>,
}
unsafe impl Send for CurrentItem {}
impl CurrentItem {
#[must_use]
pub fn raw(&self) -> *mut c_void {
self.ptr.as_ptr()
}
#[must_use]
pub fn as_string(&self) -> Option<String> {
let p = unsafe { noesis_unbox_string(self.ptr.as_ptr()) };
if p.is_null() {
return None;
}
Some(unsafe { CStr::from_ptr(p) }.to_string_lossy().into_owned())
}
#[must_use]
pub fn as_bool(&self) -> Option<bool> {
let mut out = false;
let ok = unsafe { noesis_unbox_bool(self.ptr.as_ptr(), &mut out) };
ok.then_some(out)
}
#[must_use]
pub fn as_i32(&self) -> Option<i32> {
let mut out = 0i32;
let ok = unsafe { noesis_unbox_int32(self.ptr.as_ptr(), &mut out) };
ok.then_some(out)
}
#[must_use]
pub fn as_f64(&self) -> Option<f64> {
let mut out = 0.0f64;
let ok = unsafe { noesis_unbox_double(self.ptr.as_ptr(), &mut out) };
ok.then_some(out)
}
}
impl Drop for CurrentItem {
fn drop(&mut self) {
unsafe { noesis_base_component_release(self.ptr.as_ptr()) }
}
}
pub trait CurrentChangedHandler: Send + 'static {
fn on_current_changed(&self);
}
impl<F: Fn() + Send + 'static> CurrentChangedHandler for F {
fn on_current_changed(&self) {
self();
}
}
#[must_use = "dropping the subscription immediately unsubscribes the handler"]
pub struct CurrentChangedSubscription {
token: NonNull<c_void>,
}
unsafe impl Send for CurrentChangedSubscription {}
impl Drop for CurrentChangedSubscription {
fn drop(&mut self) {
unsafe { noesis_collection_view_unsubscribe_current_changed(self.token.as_ptr()) }
}
}
unsafe extern "C" fn current_changed_trampoline(userdata: *mut c_void) {
crate::panic_guard::guard(|| {
if userdata.is_null() {
return;
}
let handler = unsafe { &*userdata.cast::<Box<dyn CurrentChangedHandler>>() };
handler.on_current_changed();
});
}
unsafe extern "C" fn current_changed_free(userdata: *mut c_void) {
crate::panic_guard::guard(|| {
if userdata.is_null() {
return;
}
drop(unsafe { Box::from_raw(userdata.cast::<Box<dyn CurrentChangedHandler>>()) });
});
}
const _: ClickFn = current_changed_trampoline;
const _: SubscriptionFreeFn = current_changed_free;