use super::{DataViewAlign, DataViewCellMode, Variant, VariantType};
use std::ffi::CString;
use wxdragon_sys as ffi;
type GetSizeCallback = Box<dyn Fn(&super::Variant, crate::geometry::Size) -> crate::geometry::Size + 'static>;
type RenderCallback = Box<dyn Fn(crate::geometry::Rect, &RenderContext, i32, &super::Variant) -> bool + 'static>;
type SetValueCallback = Box<dyn Fn(&super::Variant) -> bool + 'static>;
type GetValueCallback = Box<dyn Fn() -> Option<super::Variant> + 'static>;
type HasEditorCallback = Box<dyn Fn() -> bool + 'static>;
type ActivateCellCallback = Box<dyn Fn(crate::geometry::Rect, i32) -> bool + 'static>;
type CreateEditorCallback =
Box<dyn Fn(&dyn crate::WxWidget, crate::geometry::Rect, &super::Variant) -> Option<Box<dyn crate::WxWidget>> + 'static>;
type GetValueFromEditorCallback = Box<dyn Fn(&dyn crate::WxWidget) -> Option<super::Variant> + 'static>;
#[repr(C)]
struct CustomRendererCallbacks {
get_size: Option<GetSizeCallback>,
render: Option<RenderCallback>,
set_value: Option<SetValueCallback>,
get_value: Option<GetValueCallback>,
has_editor: Option<HasEditorCallback>,
activate_cell: Option<ActivateCellCallback>,
create_editor: Option<CreateEditorCallback>,
get_value_from_editor: Option<GetValueFromEditorCallback>,
current_value: std::cell::RefCell<super::Variant>,
}
pub trait DataViewRenderer {
fn as_raw(&self) -> *mut ffi::wxd_DataViewRenderer_t;
}
pub struct DataViewTextRenderer {
handle: *mut ffi::wxd_DataViewRenderer_t,
}
impl DataViewTextRenderer {
pub fn new(variant_type: VariantType, mode: DataViewCellMode, align: DataViewAlign) -> Self {
let variant_type_str = variant_type.as_str();
let variant_type_cstr = CString::new(variant_type_str).unwrap();
let handle = unsafe { ffi::wxd_DataViewTextRenderer_Create(variant_type_cstr.as_ptr(), mode.bits(), align.bits()) };
Self { handle }
}
}
impl DataViewRenderer for DataViewTextRenderer {
fn as_raw(&self) -> *mut ffi::wxd_DataViewRenderer_t {
self.handle
}
}
pub struct DataViewToggleRenderer {
handle: *mut ffi::wxd_DataViewRenderer_t,
}
impl DataViewToggleRenderer {
pub fn new(variant_type: VariantType, mode: DataViewCellMode, align: DataViewAlign) -> Self {
let variant_type_str = variant_type.as_str();
let variant_type_cstr = CString::new(variant_type_str).unwrap();
let handle = unsafe { ffi::wxd_DataViewToggleRenderer_Create(variant_type_cstr.as_ptr(), mode.bits(), align.bits()) };
Self { handle }
}
}
impl DataViewRenderer for DataViewToggleRenderer {
fn as_raw(&self) -> *mut ffi::wxd_DataViewRenderer_t {
self.handle
}
}
pub struct DataViewProgressRenderer {
handle: *mut ffi::wxd_DataViewRenderer_t,
}
impl DataViewProgressRenderer {
pub fn new(variant_type: VariantType, mode: DataViewCellMode) -> Self {
let variant_type_str = variant_type.as_str();
let variant_type_cstr = CString::new(variant_type_str).unwrap();
let handle = unsafe {
ffi::wxd_DataViewProgressRenderer_Create(
variant_type_cstr.as_ptr(),
mode.bits(),
0, )
};
Self { handle }
}
}
impl DataViewRenderer for DataViewProgressRenderer {
fn as_raw(&self) -> *mut ffi::wxd_DataViewRenderer_t {
self.handle
}
}
pub struct DataViewIconTextRenderer {
handle: *mut ffi::wxd_DataViewRenderer_t,
}
impl DataViewIconTextRenderer {
pub fn new(variant_type: VariantType, mode: DataViewCellMode, align: DataViewAlign) -> Self {
let variant_type_str = variant_type.as_str();
let variant_type_cstr = CString::new(variant_type_str).unwrap_or_else(|_| CString::new("string").unwrap());
let handle = unsafe { ffi::wxd_DataViewIconTextRenderer_Create(variant_type_cstr.as_ptr(), mode.bits(), align.bits()) };
Self { handle }
}
}
impl DataViewRenderer for DataViewIconTextRenderer {
fn as_raw(&self) -> *mut ffi::wxd_DataViewRenderer_t {
self.handle
}
}
pub struct DataViewBitmapRenderer {
handle: *mut ffi::wxd_DataViewRenderer_t,
}
impl DataViewBitmapRenderer {
pub fn new(mode: DataViewCellMode, align: DataViewAlign) -> Self {
let variant_type_cstr = CString::new("wxBitmap").unwrap();
let handle = unsafe { ffi::wxd_DataViewBitmapRenderer_Create(variant_type_cstr.as_ptr(), mode.bits(), align.bits()) };
Self { handle }
}
}
impl DataViewRenderer for DataViewBitmapRenderer {
fn as_raw(&self) -> *mut ffi::wxd_DataViewRenderer_t {
self.handle
}
}
pub struct DataViewDateRenderer {
handle: *mut ffi::wxd_DataViewRenderer_t,
}
impl DataViewDateRenderer {
pub fn new(variant_type: VariantType, mode: DataViewCellMode, align: DataViewAlign) -> Self {
let variant_type_str = variant_type.as_str();
let variant_type_cstr = CString::new(variant_type_str).unwrap();
let handle = unsafe { ffi::wxd_DataViewDateRenderer_Create(variant_type_cstr.as_ptr(), mode.bits(), align.bits()) };
Self { handle }
}
}
impl DataViewRenderer for DataViewDateRenderer {
fn as_raw(&self) -> *mut ffi::wxd_DataViewRenderer_t {
self.handle
}
}
pub struct DataViewSpinRenderer {
handle: *mut ffi::wxd_DataViewRenderer_t,
}
impl DataViewSpinRenderer {
pub fn new(variant_type: VariantType, mode: DataViewCellMode, align: DataViewAlign, min: i32, max: i32, inc: i32) -> Self {
let variant_type_str = variant_type.as_str();
let variant_type_cstr = CString::new(variant_type_str).unwrap();
let handle =
unsafe { ffi::wxd_DataViewSpinRenderer_Create(variant_type_cstr.as_ptr(), mode.bits(), align.bits(), min, max, inc) };
Self { handle }
}
}
impl DataViewRenderer for DataViewSpinRenderer {
fn as_raw(&self) -> *mut ffi::wxd_DataViewRenderer_t {
self.handle
}
}
pub struct DataViewChoiceRenderer {
handle: *mut ffi::wxd_DataViewRenderer_t,
}
impl DataViewChoiceRenderer {
pub fn new(variant_type: VariantType, choices: &[&str], mode: DataViewCellMode, align: DataViewAlign) -> Self {
let choices_str = choices.join(",");
let choices_cstr = CString::new(choices_str).unwrap();
let variant_type_str = variant_type.as_str();
let variant_type_cstr = CString::new(variant_type_str).unwrap();
let handle = unsafe {
ffi::wxd_DataViewChoiceRenderer_Create(variant_type_cstr.as_ptr(), choices_cstr.as_ptr(), mode.bits(), align.bits())
};
Self { handle }
}
}
impl DataViewRenderer for DataViewChoiceRenderer {
fn as_raw(&self) -> *mut ffi::wxd_DataViewRenderer_t {
self.handle
}
}
pub struct DataViewCheckIconTextRenderer {
handle: *mut ffi::wxd_DataViewRenderer_t,
}
impl DataViewCheckIconTextRenderer {
pub fn new(mode: DataViewCellMode, align: DataViewAlign) -> Self {
let variant_type_cstr = CString::new("wxDataViewCheckIconText").unwrap();
let handle =
unsafe { ffi::wxd_DataViewCheckIconTextRenderer_Create(variant_type_cstr.as_ptr(), mode.bits(), align.bits()) };
Self { handle }
}
}
impl DataViewRenderer for DataViewCheckIconTextRenderer {
fn as_raw(&self) -> *mut ffi::wxd_DataViewRenderer_t {
self.handle
}
}
pub struct DataViewCustomRenderer {
raw: *mut ffi::wxd_DataViewRenderer_t,
}
impl DataViewRenderer for DataViewCustomRenderer {
fn as_raw(&self) -> *mut ffi::wxd_DataViewRenderer_t {
self.raw
}
}
impl DataViewCustomRenderer {
pub fn builder() -> DataViewCustomRendererBuilder {
DataViewCustomRendererBuilder::new()
}
}
pub struct DataViewCustomRendererBuilder {
variant_type: VariantType,
mode: DataViewCellMode,
align: DataViewAlign,
get_size: Option<GetSizeCallback>,
render: Option<RenderCallback>,
has_editor: Option<HasEditorCallback>,
create_editor: Option<CreateEditorCallback>,
get_value_from_editor: Option<GetValueFromEditorCallback>,
activate_cell: Option<ActivateCellCallback>,
}
impl DataViewCustomRendererBuilder {
fn new() -> Self {
Self {
variant_type: VariantType::String,
mode: DataViewCellMode::Inert,
align: DataViewAlign::Left,
get_size: None,
render: None,
has_editor: None,
create_editor: None,
get_value_from_editor: None,
activate_cell: None,
}
}
pub fn variant_type(mut self, variant_type: VariantType) -> Self {
self.variant_type = variant_type;
self
}
pub fn mode(mut self, mode: DataViewCellMode) -> Self {
self.mode = mode;
self
}
pub fn align(mut self, align: DataViewAlign) -> Self {
self.align = align;
self
}
pub fn with_get_size<F>(mut self, callback: F) -> Self
where
F: Fn(&super::Variant, crate::geometry::Size) -> crate::geometry::Size + 'static,
{
self.get_size = Some(Box::new(callback));
self
}
pub fn with_render<F>(mut self, callback: F) -> Self
where
F: Fn(crate::geometry::Rect, &RenderContext, i32, &super::Variant) -> bool + 'static,
{
self.render = Some(Box::new(callback));
self
}
pub fn with_has_editor<F>(mut self, callback: F) -> Self
where
F: Fn() -> bool + 'static,
{
self.has_editor = Some(Box::new(callback));
self
}
pub fn with_create_editor<F>(mut self, callback: F) -> Self
where
F: Fn(&dyn crate::WxWidget, crate::geometry::Rect, &super::Variant) -> Option<Box<dyn crate::WxWidget>> + 'static,
{
self.create_editor = Some(Box::new(callback));
self
}
pub fn with_get_value_from_editor<F>(mut self, callback: F) -> Self
where
F: Fn(&dyn crate::WxWidget) -> Option<super::Variant> + 'static,
{
self.get_value_from_editor = Some(Box::new(callback));
self
}
pub fn with_activate_cell<F>(mut self, callback: F) -> Self
where
F: Fn(crate::geometry::Rect, i32) -> bool + 'static,
{
self.activate_cell = Some(Box::new(callback));
self
}
pub fn build(self) -> DataViewCustomRenderer {
let callbacks = Box::new(CustomRendererCallbacks {
get_size: self.get_size,
render: self.render,
set_value: None, get_value: None, has_editor: self.has_editor,
activate_cell: self.activate_cell,
create_editor: self.create_editor,
get_value_from_editor: self.get_value_from_editor,
current_value: std::cell::RefCell::new(super::Variant::from_string("")),
});
unsafe {
let raw_callback_data = Box::into_raw(callbacks);
let variant_type_cstr = CString::new(self.variant_type.as_str()).unwrap();
let handle = ffi::wxd_DataViewCustomRenderer_Create(
variant_type_cstr.as_ptr(),
self.mode.bits(),
self.align.bits(),
raw_callback_data as *mut std::ffi::c_void,
Some(get_size_trampoline),
Some(render_trampoline),
Some(set_value_trampoline),
Some(get_value_trampoline),
Some(has_editor_trampoline),
Some(create_editor_trampoline),
Some(get_value_from_editor_trampoline),
Some(activate_cell_trampoline),
);
if handle.is_null() {
panic!("Failed to create custom renderer");
}
DataViewCustomRenderer { raw: handle }
}
}
}
pub struct RenderContext {
dc_ptr: *mut wxdragon_sys::wxd_DC_t,
}
impl RenderContext {
pub unsafe fn from_raw(dc: *mut std::ffi::c_void) -> Self {
Self {
dc_ptr: dc as *mut wxdragon_sys::wxd_DC_t,
}
}
}
impl crate::dc::DeviceContext for RenderContext {
fn dc_ptr(&self) -> *mut wxdragon_sys::wxd_DC_t {
self.dc_ptr
}
}
extern "C" fn get_size_trampoline(user_data: *mut std::ffi::c_void) -> ffi::wxd_Size_t {
if user_data.is_null() {
return ffi::wxd_Size_t { width: 50, height: 20 };
}
let callbacks = unsafe { &*(user_data as *const CustomRendererCallbacks) };
if let Some(ref callback) = callbacks.get_size {
let current_value = callbacks.current_value.borrow();
let default_size = crate::geometry::Size::new(80, 20);
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| callback(¤t_value, default_size)));
match result {
Ok(size) => ffi::wxd_Size_t {
width: size.width,
height: size.height,
},
Err(_) => ffi::wxd_Size_t { width: 50, height: 20 },
}
} else {
ffi::wxd_Size_t { width: 50, height: 20 }
}
}
extern "C" fn render_trampoline(
user_data: *mut std::ffi::c_void,
cell: ffi::wxd_Rect_t,
dc: *mut std::ffi::c_void,
state: i32,
) -> bool {
if user_data.is_null() || dc.is_null() {
return false;
}
let callbacks = unsafe { &*(user_data as *const CustomRendererCallbacks) };
if let Some(ref callback) = callbacks.render {
let rect = crate::geometry::Rect::new(cell.x, cell.y, cell.width, cell.height);
let context = unsafe { RenderContext::from_raw(dc) };
let current_value = callbacks.current_value.borrow();
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
callback(rect, &context, state, ¤t_value)
}));
result.unwrap_or(false)
} else {
false
}
}
extern "C" fn set_value_trampoline(user_data: *mut std::ffi::c_void, value: *const ffi::wxd_Variant_t) -> bool {
if user_data.is_null() || value.is_null() {
return false;
}
let callbacks = unsafe { &*(user_data as *const CustomRendererCallbacks) };
let variant = Variant::from(value).clone();
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
*callbacks.current_value.borrow_mut() = variant;
true
}));
result.unwrap_or(false)
}
extern "C" fn get_value_trampoline(user_data: *mut std::ffi::c_void) -> *mut ffi::wxd_Variant_t {
if user_data.is_null() {
return std::ptr::null_mut();
}
let callbacks = unsafe { &*(user_data as *const CustomRendererCallbacks) };
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let current_value = callbacks.current_value.borrow();
let owned = current_value.clone();
owned.try_into().unwrap()
}));
match result {
Ok(ptr) => ptr,
Err(_) => super::Variant::from_string("").try_into().unwrap(),
}
}
extern "C" fn has_editor_trampoline(user_data: *mut std::ffi::c_void) -> bool {
if user_data.is_null() {
return false;
}
let callbacks = unsafe { &*(user_data as *const CustomRendererCallbacks) };
if let Some(ref callback) = callbacks.has_editor {
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(callback));
result.unwrap_or(false)
} else {
false
}
}
extern "C" fn activate_cell_trampoline(
user_data: *mut std::ffi::c_void,
cell: ffi::wxd_Rect_t,
_model: *mut std::ffi::c_void,
_item: *mut std::ffi::c_void,
col: u32,
_mouse_event: *mut std::ffi::c_void,
) -> bool {
if user_data.is_null() {
return false;
}
let callbacks = unsafe { &*(user_data as *const CustomRendererCallbacks) };
if let Some(ref callback) = callbacks.activate_cell {
let rect = crate::geometry::Rect::new(cell.x, cell.y, cell.width, cell.height);
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| callback(rect, col as i32)));
result.unwrap_or(false)
} else {
false
}
}
extern "C" fn create_editor_trampoline(
user_data: *mut std::ffi::c_void,
parent: *mut std::ffi::c_void,
label_rect: ffi::wxd_Rect_t,
value: *const ffi::wxd_Variant_t,
) -> *mut std::ffi::c_void {
if user_data.is_null() || parent.is_null() || value.is_null() {
return std::ptr::null_mut();
}
let callbacks = unsafe { &*(user_data as *const CustomRendererCallbacks) };
if let Some(ref callback) = callbacks.create_editor {
let rect = crate::geometry::Rect::new(label_rect.x, label_rect.y, label_rect.width, label_rect.height);
let variant = Variant::from(value);
struct ParentWrapper {
ptr: *mut std::ffi::c_void,
}
impl crate::WxWidget for ParentWrapper {
fn handle_ptr(&self) -> *mut wxdragon_sys::wxd_Window_t {
self.ptr as *mut wxdragon_sys::wxd_Window_t
}
}
let parent_wrapper = ParentWrapper { ptr: parent };
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| callback(&parent_wrapper, rect, &variant)));
match result {
Ok(Some(editor)) => editor.handle_ptr() as *mut std::ffi::c_void,
_ => std::ptr::null_mut(),
}
} else {
std::ptr::null_mut()
}
}
extern "C" fn get_value_from_editor_trampoline(
user_data: *mut std::ffi::c_void,
editor: *mut std::ffi::c_void,
) -> *mut ffi::wxd_Variant_t {
if user_data.is_null() || editor.is_null() {
return std::ptr::null_mut();
}
let callbacks = unsafe { &*(user_data as *const CustomRendererCallbacks) };
if let Some(ref callback) = callbacks.get_value_from_editor {
struct EditorWrapper {
ptr: *mut std::ffi::c_void,
}
impl crate::WxWidget for EditorWrapper {
fn handle_ptr(&self) -> *mut wxdragon_sys::wxd_Window_t {
self.ptr as *mut wxdragon_sys::wxd_Window_t
}
}
let editor_wrapper = EditorWrapper { ptr: editor };
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| callback(&editor_wrapper)));
match result {
Ok(Some(variant)) => variant.try_into().unwrap_or(std::ptr::null_mut()),
_ => std::ptr::null_mut(),
}
} else {
std::ptr::null_mut()
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn drop_rust_custom_renderer_callbacks(ptr: *mut std::ffi::c_void) {
if !ptr.is_null() {
let _ = unsafe { Box::from_raw(ptr as *mut CustomRendererCallbacks) };
}
}