use crate::widgets::dataview::variant::Variant;
use std::any::Any;
use std::cell::RefCell;
use std::ffi::CString;
use std::os::raw::c_void;
use wxdragon_sys as ffi;
pub mod tree_helpers {
use std::ffi::c_void;
pub fn box_userdata<T>(v: T) -> *mut c_void {
Box::into_raw(Box::new(v)) as *mut c_void
}
pub unsafe fn userdata_as_mut<T>(ptr: *mut c_void) -> *mut T {
ptr as *mut T
}
pub fn ptr_to_id<T>(p: *mut T) -> *mut c_void {
p as *mut c_void
}
pub unsafe fn id_to_ptr<T>(id: *mut c_void) -> *mut T {
id as *mut T
}
pub fn leak_children_vec<T>(children: Vec<*mut T>) -> (*mut *mut c_void, i32) {
let mut arr: Vec<*mut c_void> = children.into_iter().map(|p| p as *mut c_void).collect();
let ptr = arr.as_mut_ptr();
let count = arr.len() as i32;
std::mem::forget(arr);
(ptr, count)
}
pub unsafe fn free_children_array(ptr: *mut *mut c_void, count: i32) {
if ptr.is_null() || count == 0 {
return;
}
let slice = unsafe { std::slice::from_raw_parts_mut(ptr, count as usize) };
let _ = unsafe { Vec::from_raw_parts(slice.as_mut_ptr(), count as usize, count as usize) };
}
}
type GetValueCallback = Box<dyn for<'a> Fn(&'a dyn Any, usize, usize) -> Variant>;
type SetValueCallback = Box<dyn for<'a, 'b> Fn(&'a dyn Any, usize, usize, &'b Variant) -> bool>;
type GetAttrCallback = Box<dyn for<'a> Fn(&'a dyn Any, usize, usize) -> Option<DataViewItemAttr>>;
type IsEnabledCallback = Box<dyn for<'a> Fn(&'a dyn Any, usize, usize) -> bool>;
#[derive(Debug, Clone, Default)]
pub struct DataViewItemAttr {
has_text_colour: bool,
text_colour_red: u8,
text_colour_green: u8,
text_colour_blue: u8,
text_colour_alpha: u8,
has_bg_colour: bool,
bg_colour_red: u8,
bg_colour_green: u8,
bg_colour_blue: u8,
bg_colour_alpha: u8,
bold: bool,
italic: bool,
}
impl DataViewItemAttr {
pub fn new() -> Self {
Default::default()
}
pub fn with_text_colour(mut self, r: u8, g: u8, b: u8, a: u8) -> Self {
self.has_text_colour = true;
self.text_colour_red = r;
self.text_colour_green = g;
self.text_colour_blue = b;
self.text_colour_alpha = a;
self
}
pub fn with_bg_colour(mut self, r: u8, g: u8, b: u8, a: u8) -> Self {
self.has_bg_colour = true;
self.bg_colour_red = r;
self.bg_colour_green = g;
self.bg_colour_blue = b;
self.bg_colour_alpha = a;
self
}
pub fn with_bold(mut self, bold: bool) -> Self {
self.bold = bold;
self
}
pub fn with_italic(mut self, italic: bool) -> Self {
self.italic = italic;
self
}
pub fn to_raw(&self) -> ffi::wxd_DataViewItemAttr_t {
ffi::wxd_DataViewItemAttr_t {
has_text_colour: self.has_text_colour,
text_colour_red: self.text_colour_red,
text_colour_green: self.text_colour_green,
text_colour_blue: self.text_colour_blue,
text_colour_alpha: self.text_colour_alpha,
has_bg_colour: self.has_bg_colour,
bg_colour_red: self.bg_colour_red,
bg_colour_green: self.bg_colour_green,
bg_colour_blue: self.bg_colour_blue,
bg_colour_alpha: self.bg_colour_alpha,
bold: self.bold,
italic: self.italic,
}
}
}
pub trait DataViewModel {
fn handle_ptr(&self) -> *mut ffi::wxd_DataViewModel_t;
}
pub struct DataViewListModel {
ptr: *mut ffi::wxd_DataViewModel_t,
}
impl_refcounted_object!(
DataViewListModel,
ptr,
ffi::wxd_DataViewModel_t,
ffi::wxd_DataViewModel_AddRef,
ffi::wxd_DataViewModel_GetRefCount,
ffi::wxd_DataViewModel_Release
);
impl DataViewListModel {
pub fn new() -> Self {
let ptr = unsafe { ffi::wxd_DataViewListModel_Create() };
Self { ptr }
}
pub fn append_column(&self, name: &str) -> bool {
let c_name = CString::new(name).unwrap_or_default();
unsafe { ffi::wxd_DataViewListModel_AppendColumn(self.ptr, c_name.as_ptr()) }
}
pub fn append_row(&self) -> bool {
unsafe { ffi::wxd_DataViewListModel_AppendRow(self.ptr) }
}
pub fn set_value<T: Into<Variant>>(&self, row: usize, col: usize, value: T) -> bool {
let v: Variant = value.into();
unsafe { ffi::wxd_DataViewListModel_SetValue(self.ptr, row, col, v.as_const_ptr()) }
}
pub fn get_item_count(&self) -> usize {
unsafe { ffi::wxd_DataViewListModel_GetItemCount(self.ptr) as usize }
}
pub fn prepend_row(&self) -> bool {
unsafe { ffi::wxd_DataViewListModel_PrependRow(self.ptr) }
}
pub fn insert_row(&self, pos: usize) -> bool {
unsafe { ffi::wxd_DataViewListModel_InsertRow(self.ptr, pos as u32) }
}
pub fn delete_item(&self, row: usize) -> bool {
unsafe { ffi::wxd_DataViewListModel_DeleteItem(self.ptr, row as u32) }
}
pub fn delete_all_items(&self) -> bool {
unsafe { ffi::wxd_DataViewListModel_DeleteAllItems(self.ptr) }
}
pub fn get_value(&self, row: usize, col: usize) -> Option<Variant> {
let ptr = unsafe { ffi::wxd_DataViewListModel_GetValue(self.ptr, row, col) };
if ptr.is_null() { None } else { Some(Variant::from(ptr)) }
}
}
impl DataViewModel for DataViewListModel {
fn handle_ptr(&self) -> *mut ffi::wxd_DataViewModel_t {
self.ptr
}
}
impl Default for DataViewListModel {
fn default() -> Self {
Self::new()
}
}
pub struct DataViewVirtualListModel {
ptr: *mut ffi::wxd_DataViewModel_t,
size: usize,
}
impl_refcounted_object!(
DataViewVirtualListModel,
ptr,
ffi::wxd_DataViewModel_t,
ffi::wxd_DataViewModel_AddRef,
ffi::wxd_DataViewModel_GetRefCount,
ffi::wxd_DataViewModel_Release,
size
);
impl DataViewVirtualListModel {
pub fn new(initial_size: usize) -> Self {
let ptr = unsafe { ffi::wxd_DataViewVirtualListModel_Create(initial_size as u64) };
Self { ptr, size: initial_size }
}
pub fn row_prepended(&mut self) {
unsafe { ffi::wxd_DataViewVirtualListModel_RowPrepended(self.ptr) };
self.size += 1;
}
pub fn row_inserted(&mut self, before: usize) {
unsafe { ffi::wxd_DataViewVirtualListModel_RowInserted(self.ptr, before as u64) };
self.size += 1;
}
pub fn row_appended(&mut self) {
unsafe { ffi::wxd_DataViewVirtualListModel_RowAppended(self.ptr) };
self.size += 1;
}
pub fn row_deleted(&mut self, row: usize) {
unsafe { ffi::wxd_DataViewVirtualListModel_RowDeleted(self.ptr, row as u64) };
if self.size > 0 {
self.size -= 1;
}
}
pub fn rows_deleted(&mut self, rows: &[i32]) {
let rows_ptr = rows.as_ptr() as *mut i32;
unsafe { ffi::wxd_DataViewVirtualListModel_RowsDeleted(self.ptr, rows_ptr, rows.len() as i32) };
if self.size >= rows.len() {
self.size -= rows.len();
} else {
self.size = 0;
}
}
pub fn row_changed(&self, row: usize) {
unsafe { ffi::wxd_DataViewVirtualListModel_RowChanged(self.ptr, row as u64) };
}
pub fn row_value_changed(&self, row: usize, col: usize) {
unsafe {
ffi::wxd_DataViewVirtualListModel_RowValueChanged(self.ptr, row as u64, col as u64);
};
}
pub fn reset(&mut self, new_size: usize) {
unsafe { ffi::wxd_DataViewVirtualListModel_Reset(self.ptr, new_size as u64) };
self.size = new_size;
}
pub fn get_item(&self, row: usize) -> *mut std::ffi::c_void {
unsafe { ffi::wxd_DataViewVirtualListModel_GetItem(self.ptr, row as u64) }
}
pub unsafe fn get_row(&self, item: *mut std::ffi::c_void) -> usize {
(unsafe { ffi::wxd_DataViewVirtualListModel_GetRow(self.ptr, item) }) as usize
}
pub fn size(&self) -> usize {
self.size
}
}
impl DataViewModel for DataViewVirtualListModel {
fn handle_ptr(&self) -> *mut ffi::wxd_DataViewModel_t {
self.ptr
}
}
impl Default for DataViewVirtualListModel {
fn default() -> Self {
Self::new(0)
}
}
pub struct CustomDataViewTreeModel {
model: *mut ffi::wxd_DataViewModel_t,
callbacks: *mut OwnedTreeCallbacks,
}
impl_refcounted_object!(
CustomDataViewTreeModel,
model,
ffi::wxd_DataViewModel_t,
ffi::wxd_DataViewModel_AddRef,
ffi::wxd_DataViewModel_GetRefCount,
ffi::wxd_DataViewModel_Release,
callbacks
);
#[allow(clippy::type_complexity)]
struct OwnedTreeCallbacks {
userdata: RefCell<Box<dyn Any>>,
get_parent: Box<dyn Fn(&dyn Any, *mut std::ffi::c_void) -> *mut std::ffi::c_void>,
is_container: Box<dyn Fn(&dyn Any, *mut std::ffi::c_void) -> bool>,
get_children: Box<dyn Fn(&dyn Any, *mut std::ffi::c_void) -> Vec<*mut std::ffi::c_void>>,
get_value: Box<dyn Fn(&dyn Any, *mut std::ffi::c_void, u32) -> Variant>,
set_value: Option<Box<dyn Fn(&dyn Any, *mut std::ffi::c_void, u32, &Variant) -> bool>>,
is_enabled: Option<Box<dyn Fn(&dyn Any, *mut std::ffi::c_void, u32) -> bool>>,
compare: Option<Box<dyn Fn(&dyn Any, *mut std::ffi::c_void, *mut std::ffi::c_void, u32, bool) -> i32>>,
}
impl CustomDataViewTreeModel {
#[allow(clippy::type_complexity, clippy::too_many_arguments)]
pub fn new<T, N, GP, IC, GC, GV, SV, IE, CMP>(
data: T,
get_parent: GP,
is_container: IC,
get_children: GC,
get_value: GV,
set_value: Option<SV>,
is_enabled: Option<IE>,
compare: Option<CMP>,
) -> Self
where
T: Any + 'static,
N: Any + 'static,
GP: for<'a> Fn(&T, Option<&'a N>) -> Option<*mut N> + 'static,
IC: for<'a> Fn(&T, Option<&'a N>) -> bool + 'static,
GC: for<'a> Fn(&T, Option<&'a N>) -> Vec<*mut N> + 'static,
GV: for<'a> Fn(&T, Option<&'a N>, u32) -> Variant + 'static,
SV: for<'a> Fn(&T, Option<&'a N>, u32, &Variant) -> bool + 'static,
IE: for<'a> Fn(&T, Option<&'a N>, u32) -> bool + 'static,
CMP: for<'a> Fn(&T, &'a N, &'a N, u32, bool) -> i32 + 'static,
{
let boxed_data: Box<dyn Any> = Box::new(data);
let any_get_parent: Box<dyn for<'a> Fn(&dyn Any, *mut std::ffi::c_void) -> *mut std::ffi::c_void> =
Box::new(move |any_data, item| {
let t = any_data.downcast_ref::<T>().unwrap();
let item_opt: Option<&N> = if item.is_null() {
None
} else {
Some(unsafe { &*(item as *mut N) })
};
let ret_opt = get_parent(t, item_opt);
match ret_opt {
Some(ptr) => ptr as *mut std::ffi::c_void,
None => std::ptr::null_mut(),
}
});
let any_is_container: Box<dyn for<'a> Fn(&dyn Any, *mut std::ffi::c_void) -> bool> = Box::new(move |any_data, item| {
let t = any_data.downcast_ref::<T>().unwrap();
let item_opt: Option<&N> = if item.is_null() {
None
} else {
Some(unsafe { &*(item as *mut N) })
};
is_container(t, item_opt)
});
let any_get_children: Box<dyn for<'a> Fn(&dyn Any, *mut std::ffi::c_void) -> Vec<*mut std::ffi::c_void>> =
Box::new(move |any_data, item| {
let t = any_data.downcast_ref::<T>().unwrap();
let item_opt: Option<&N> = if item.is_null() {
None
} else {
Some(unsafe { &*(item as *mut N) })
};
let vec_typed: Vec<*mut N> = get_children(t, item_opt);
vec_typed.into_iter().map(|p| p as *mut std::ffi::c_void).collect()
});
let any_get_value: Box<dyn for<'a> Fn(&dyn Any, *mut std::ffi::c_void, u32) -> Variant> =
Box::new(move |any_data, item, col| {
let t = any_data.downcast_ref::<T>().unwrap();
let item_opt: Option<&N> = if item.is_null() {
None
} else {
Some(unsafe { &*(item as *mut N) })
};
get_value(t, item_opt, col)
});
let any_set_value = set_value.map(|f| {
Box::new(move |any_data: &dyn Any, item: *mut std::ffi::c_void, col, var: &Variant| {
let t = any_data.downcast_ref::<T>().unwrap();
let item_opt: Option<&N> = if item.is_null() {
None
} else {
Some(unsafe { &*(item as *mut N) })
};
f(t, item_opt, col, var)
}) as Box<dyn for<'a> Fn(&dyn Any, *mut std::ffi::c_void, u32, &Variant) -> bool>
});
let any_is_enabled = is_enabled.map(|f| {
Box::new(move |any_data: &dyn Any, item: *mut std::ffi::c_void, col| {
let t = any_data.downcast_ref::<T>().unwrap();
let item_opt: Option<&N> = if item.is_null() {
None
} else {
Some(unsafe { &*(item as *mut N) })
};
f(t, item_opt, col)
}) as Box<dyn for<'a> Fn(&dyn Any, *mut std::ffi::c_void, u32) -> bool>
});
let any_compare = compare.map(|f| {
Box::new(
move |any_data: &dyn Any, a: *mut std::ffi::c_void, b: *mut std::ffi::c_void, col: u32, asc: bool| {
let t = any_data.downcast_ref::<T>().unwrap();
if a.is_null() || b.is_null() {
return 0;
}
let a_ref: &N = unsafe { &*(a as *mut N) };
let b_ref: &N = unsafe { &*(b as *mut N) };
f(t, a_ref, b_ref, col, asc)
},
) as Box<dyn for<'a> Fn(&dyn Any, *mut std::ffi::c_void, *mut std::ffi::c_void, u32, bool) -> i32>
});
let owned = Box::new(OwnedTreeCallbacks {
userdata: RefCell::new(boxed_data),
get_parent: any_get_parent,
is_container: any_is_container,
get_children: any_get_children,
get_value: any_get_value,
set_value: any_set_value,
is_enabled: any_is_enabled,
compare: any_compare,
});
let owned_raw = Box::into_raw(owned);
let cb = ffi::wxd_DataViewTreeModel_Callbacks {
userdata: owned_raw as *mut std::ffi::c_void,
userdata_free: Some(free_owned_tree_callbacks),
get_parent: Some(trampoline_get_parent),
is_container: Some(trampoline_is_container),
get_children: Some(trampoline_get_children),
free_children: Some(trampoline_free_children),
get_value: Some(trampoline_get_value),
set_value: Some(trampoline_set_value),
is_enabled: Some(trampoline_is_enabled),
compare: Some(trampoline_compare),
};
let cb_box = Box::new(cb);
let cb_raw = Box::into_raw(cb_box);
let model = unsafe { ffi::wxd_DataViewTreeModel_CreateWithCallbacks(cb_raw) };
Self {
model,
callbacks: owned_raw,
}
}
pub fn item_value_changed<N>(&self, item: *const N, col: u32) {
if self.model.is_null() {
return;
}
let item_id = item as *mut std::ffi::c_void;
unsafe { ffi::wxd_DataViewTreeModel_ItemValueChanged(self.model, item_id, col) };
}
pub fn item_changed<N>(&self, item: *const N) {
if self.model.is_null() {
return;
}
let item_id = item as *mut std::ffi::c_void;
unsafe { ffi::wxd_DataViewTreeModel_ItemChanged(self.model, item_id) };
}
pub fn item_added<N>(&self, parent: Option<*const N>, child: *const N) {
if self.model.is_null() {
return;
}
let parent_id = parent.map(|p| p as *mut std::ffi::c_void).unwrap_or(std::ptr::null_mut());
let child_id = child as *mut std::ffi::c_void;
unsafe { ffi::wxd_DataViewTreeModel_ItemAdded(self.model, parent_id, child_id) };
}
pub fn item_deleted<N>(&self, parent: Option<*const N>, child: *const N) {
if self.model.is_null() {
return;
}
let parent_id = parent.map(|p| p as *mut std::ffi::c_void).unwrap_or(std::ptr::null_mut());
let child_id = child as *mut std::ffi::c_void;
unsafe { ffi::wxd_DataViewTreeModel_ItemDeleted(self.model, parent_id, child_id) };
}
pub fn items_added<N>(&self, parent: Option<*const N>, children: &[*const N]) {
if self.model.is_null() {
return;
}
let parent_id = parent.map(|p| p as *mut std::ffi::c_void).unwrap_or(std::ptr::null_mut());
let child_ids: Vec<*const std::ffi::c_void> = children.iter().map(|&c| c as *const std::ffi::c_void).collect();
let ptr = child_ids.as_ptr();
let count = child_ids.len();
unsafe { ffi::wxd_DataViewTreeModel_ItemsAdded(self.model, parent_id, ptr, count) };
}
pub fn items_deleted<N>(&self, parent: Option<*const N>, children: &[*const N]) {
if self.model.is_null() {
return;
}
let parent_id = parent.map(|p| p as *mut std::ffi::c_void).unwrap_or(std::ptr::null_mut());
let child_ids: Vec<*const std::ffi::c_void> = children.iter().map(|&c| c as *const std::ffi::c_void).collect();
let ptr = child_ids.as_ptr();
let count = child_ids.len();
unsafe { ffi::wxd_DataViewTreeModel_ItemsDeleted(self.model, parent_id, ptr, count) };
}
pub fn items_changed<N>(&self, children: &[*const N]) {
if self.model.is_null() {
return;
}
let child_ids: Vec<*const std::ffi::c_void> = children.iter().map(|&c| c as *const std::ffi::c_void).collect();
let ptr = child_ids.as_ptr();
let count = child_ids.len();
unsafe { ffi::wxd_DataViewTreeModel_ItemsChanged(self.model, ptr, count) };
}
pub fn cleared(&self) {
if self.model.is_null() {
return;
}
unsafe { ffi::wxd_DataViewTreeModel_Cleared(self.model) };
}
pub fn with_userdata_mut<T: Any + 'static, R>(&self, f: impl FnOnce(&mut T) -> R) -> Option<R> {
if self.callbacks.is_null() {
return None;
}
let cb: &OwnedTreeCallbacks = unsafe { &*self.callbacks };
let mut guard = cb.userdata.borrow_mut();
let any_mut: &mut dyn Any = &mut **guard;
any_mut.downcast_mut::<T>().map(f)
}
}
extern "C" fn trampoline_free_children(items: *mut *mut std::ffi::c_void, count: i32) {
unsafe { tree_helpers::free_children_array(items, count) };
}
extern "C" fn trampoline_get_parent(userdata: *mut std::ffi::c_void, item: *mut std::ffi::c_void) -> *mut std::ffi::c_void {
if userdata.is_null() {
return std::ptr::null_mut();
}
let cb = unsafe { &*(userdata as *mut OwnedTreeCallbacks) };
let u = cb.userdata.borrow();
let any_ref: &dyn Any = &**u;
(cb.get_parent)(any_ref, item)
}
extern "C" fn trampoline_is_container(userdata: *mut std::ffi::c_void, item: *mut std::ffi::c_void) -> bool {
if userdata.is_null() {
return false;
}
let cb = unsafe { &*(userdata as *mut OwnedTreeCallbacks) };
let u = cb.userdata.borrow();
let any_ref: &dyn Any = &**u;
(cb.is_container)(any_ref, item)
}
extern "C" fn trampoline_get_children(
userdata: *mut std::ffi::c_void,
item: *mut std::ffi::c_void,
out_items: *mut *mut *mut std::ffi::c_void,
out_count: *mut i32,
) {
if userdata.is_null() {
unsafe { *out_items = std::ptr::null_mut() };
unsafe { *out_count = 0 };
return;
}
let cb = unsafe { &*(userdata as *mut OwnedTreeCallbacks) };
let u = cb.userdata.borrow();
let any_ref: &dyn Any = &**u;
let vec = (cb.get_children)(any_ref, item);
let (ptr, cnt) = tree_helpers::leak_children_vec(vec);
unsafe { *out_items = ptr };
unsafe { *out_count = cnt };
}
extern "C" fn trampoline_get_value(
userdata: *mut std::ffi::c_void,
item: *mut std::ffi::c_void,
col: u32,
) -> *mut ffi::wxd_Variant_t {
if userdata.is_null() {
return std::ptr::null_mut();
}
let cb = unsafe { &*(userdata as *mut OwnedTreeCallbacks) };
let u = cb.userdata.borrow();
let any_ref: &dyn Any = &**u;
let val = (cb.get_value)(any_ref, item, col);
match val.try_into() {
Ok(raw_ptr) => raw_ptr,
Err(_) => std::ptr::null_mut(),
}
}
extern "C" fn trampoline_set_value(
userdata: *mut std::ffi::c_void,
item: *mut std::ffi::c_void,
col: u32,
variant: *const ffi::wxd_Variant_t,
) -> bool {
if userdata.is_null() || variant.is_null() {
return false;
}
let cb = unsafe { &*(userdata as *mut OwnedTreeCallbacks) };
if let Some(f) = &cb.set_value {
let v = Variant::from(variant); let u = cb.userdata.borrow();
let any_ref: &dyn Any = &**u;
f(any_ref, item, col, &v)
} else {
false
}
}
extern "C" fn trampoline_is_enabled(userdata: *mut std::ffi::c_void, item: *mut std::ffi::c_void, col: u32) -> bool {
if userdata.is_null() {
return true;
}
let cb = unsafe { &*(userdata as *mut OwnedTreeCallbacks) };
if let Some(f) = &cb.is_enabled {
let u = cb.userdata.borrow();
let any_ref: &dyn Any = &**u;
f(any_ref, item, col)
} else {
true
}
}
extern "C" fn trampoline_compare(
userdata: *mut std::ffi::c_void,
a: *mut std::ffi::c_void,
b: *mut std::ffi::c_void,
col: u32,
asc: bool,
) -> i32 {
if userdata.is_null() {
return 0;
}
let cb = unsafe { &*(userdata as *mut OwnedTreeCallbacks) };
if let Some(f) = &cb.compare {
let u = cb.userdata.borrow();
let any_ref: &dyn Any = &**u;
f(any_ref, a, b, col, asc)
} else {
0
}
}
extern "C" fn free_owned_tree_callbacks(ptr: *mut std::ffi::c_void) {
if ptr.is_null() {
return;
}
let _ = unsafe { Box::from_raw(ptr as *mut OwnedTreeCallbacks) };
}
impl DataViewModel for CustomDataViewTreeModel {
fn handle_ptr(&self) -> *mut ffi::wxd_DataViewModel_t {
self.model
}
}
#[unsafe(no_mangle)]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub extern "C" fn wxd_Drop_Rust_DataViewTreeModelCallbacks(cb_ptr: *mut ffi::wxd_DataViewTreeModel_Callbacks) {
if cb_ptr.is_null() {
return;
}
let cb_box = unsafe { Box::from_raw(cb_ptr) };
if let Some(free_fn) = cb_box.userdata_free {
unsafe { free_fn(cb_box.userdata) };
}
drop(cb_box);
}
pub struct CustomDataViewVirtualListModel {
handle: *mut ffi::wxd_DataViewModel_t,
size: usize,
}
struct CustomModelCallbacks {
userdata: Box<dyn Any>,
get_value: GetValueCallback,
set_value: Option<SetValueCallback>,
get_attr: Option<GetAttrCallback>,
is_enabled: Option<IsEnabledCallback>,
}
impl Drop for CustomModelCallbacks {
fn drop(&mut self) {
log::debug!("CustomModelCallbacks dropped");
}
}
impl CustomDataViewVirtualListModel {
pub fn new<T, F, G, H, I>(
initial_size: usize,
data: T,
get_value: F,
set_value: Option<G>,
get_attr: Option<H>,
is_enabled: Option<I>,
) -> Self
where
T: Any + 'static,
F: for<'a> Fn(&'a T, usize, usize) -> Variant + 'static,
G: for<'a, 'b> Fn(&'a T, usize, usize, &'b Variant) -> bool + 'static,
H: for<'a> Fn(&'a T, usize, usize) -> Option<DataViewItemAttr> + 'static,
I: for<'a> Fn(&'a T, usize, usize) -> bool + 'static,
{
let any_data = Box::new(data);
let any_get_value: GetValueCallback = Box::new(move |any_data, row, col| {
let data = any_data.downcast_ref::<T>().unwrap();
get_value(data, row, col)
});
let any_set_value: Option<SetValueCallback> = if let Some(f) = set_value {
Some(Box::new(move |any_data: &dyn Any, row, col, value| {
let data = any_data.downcast_ref::<T>().unwrap();
f(data, row, col, value)
}))
} else {
None
};
let any_get_attr: Option<GetAttrCallback> = if let Some(f) = get_attr {
Some(Box::new(move |any_data: &dyn Any, row, col| {
let data = any_data.downcast_ref::<T>().unwrap();
f(data, row, col)
}))
} else {
None
};
let any_is_enabled: Option<IsEnabledCallback> = if let Some(f) = is_enabled {
Some(Box::new(move |any_data: &dyn Any, row, col| {
let data = any_data.downcast_ref::<T>().unwrap();
f(data, row, col)
}))
} else {
None
};
let callback_data = Box::new(CustomModelCallbacks {
userdata: any_data,
get_value: any_get_value,
set_value: any_set_value,
get_attr: any_get_attr,
is_enabled: any_is_enabled,
});
let raw_callback_data = Box::into_raw(callback_data);
let handle = unsafe {
ffi::wxd_DataViewVirtualListModel_CreateWithCallbacks(
initial_size as u64,
raw_callback_data as *mut ::std::os::raw::c_void,
Some(get_value_callback),
Some(set_value_callback),
Some(get_attr_callback),
Some(is_enabled_callback),
)
};
if handle.is_null() {
log::error!("Failed to create CustomDataViewVirtualListModel in C++");
drop(unsafe { Box::from_raw(raw_callback_data) });
return Self {
handle: std::ptr::null_mut(),
size: 0,
};
}
Self {
handle,
size: initial_size,
}
}
pub fn row_prepended(&mut self) {
unsafe { ffi::wxd_DataViewVirtualListModel_RowPrepended(self.handle) };
self.size += 1;
}
pub fn row_inserted(&mut self, before: usize) {
unsafe { ffi::wxd_DataViewVirtualListModel_RowInserted(self.handle, before as u64) };
self.size += 1;
}
pub fn row_appended(&mut self) {
unsafe { ffi::wxd_DataViewVirtualListModel_RowAppended(self.handle) };
self.size += 1;
}
pub fn row_deleted(&mut self, row: usize) {
unsafe { ffi::wxd_DataViewVirtualListModel_RowDeleted(self.handle, row as u64) };
if self.size > 0 {
self.size -= 1;
}
}
pub fn row_changed(&self, row: usize) {
unsafe { ffi::wxd_DataViewVirtualListModel_RowChanged(self.handle, row as u64) };
}
pub fn row_value_changed(&self, row: usize, col: usize) {
unsafe { ffi::wxd_DataViewVirtualListModel_RowValueChanged(self.handle, row as u64, col as u64) };
}
pub fn reset(&mut self, new_size: usize) {
unsafe { ffi::wxd_DataViewVirtualListModel_Reset(self.handle, new_size as u64) };
self.size = new_size;
}
pub fn size(&self) -> usize {
self.size
}
}
impl DataViewModel for CustomDataViewVirtualListModel {
fn handle_ptr(&self) -> *mut ffi::wxd_DataViewModel_t {
self.handle
}
}
impl_refcounted_object!(
CustomDataViewVirtualListModel,
handle,
ffi::wxd_DataViewModel_t,
ffi::wxd_DataViewModel_AddRef,
ffi::wxd_DataViewModel_GetRefCount,
ffi::wxd_DataViewModel_Release,
size
);
unsafe extern "C" fn get_value_callback(userdata: *mut ::std::os::raw::c_void, row: u64, col: u64) -> *mut ffi::wxd_Variant_t {
if userdata.is_null() {
let v = Variant::from_string("");
return v.try_into().unwrap();
}
let callbacks = unsafe { &*(userdata as *const CustomModelCallbacks) };
let value = (callbacks.get_value)(&*callbacks.userdata, row as usize, col as usize);
match value.try_into() {
Ok(raw_ptr) => raw_ptr,
Err(_) => std::ptr::null_mut(),
}
}
unsafe extern "C" fn set_value_callback(
userdata: *mut ::std::os::raw::c_void,
variant: *const ffi::wxd_Variant_t,
row: u64,
col: u64,
) -> bool {
let callbacks = unsafe { &*(userdata as *const CustomModelCallbacks) };
if let Some(set_value) = &callbacks.set_value {
let value = Variant::from(variant); (set_value)(&*callbacks.userdata, row as usize, col as usize, &value)
} else {
false
}
}
unsafe extern "C" fn get_attr_callback(
userdata: *mut ::std::os::raw::c_void,
row: u64,
col: u64,
attr: *mut ffi::wxd_DataViewItemAttr_t,
) -> bool {
let callbacks = unsafe { &*(userdata as *const CustomModelCallbacks) };
if let Some(get_attr) = &callbacks.get_attr {
if let Some(attrs) = (get_attr)(&*callbacks.userdata, row as usize, col as usize) {
unsafe { *attr = attrs.to_raw() };
true
} else {
false
}
} else {
false
}
}
unsafe extern "C" fn is_enabled_callback(userdata: *mut ::std::os::raw::c_void, row: u64, col: u64) -> bool {
let callbacks = unsafe { &*(userdata as *const CustomModelCallbacks) };
if let Some(is_enabled) = &callbacks.is_enabled {
(is_enabled)(&*callbacks.userdata, row as usize, col as usize)
} else {
true
}
}
#[unsafe(no_mangle)]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub extern "C" fn wxd_Drop_Rust_CustomModelCallbacks(ptr: *mut c_void) {
if !ptr.is_null() {
unsafe {
let _callback_box = Box::from_raw(ptr as *mut CustomModelCallbacks);
}
}
}