use crate::event::{Event, EventType, WxEvtHandler};
use crate::geometry::{Point, Size};
use crate::id::Id;
use crate::widgets::dataview::Variant;
use crate::window::{WindowHandle, WxWidget};
use std::ffi::{CStr, CString};
use std::fmt;
use std::os::raw::c_char;
use wxdragon_sys as ffi;
widget_style_enum!(
name: PropertyGridStyle,
doc: "Style flags controlling PropertyGrid layout and editing behavior.\n\nFlags can be combined with `|` and supplied through `PropertyGridBuilder::with_style`.\n\n# Example\n\n```no_run\nuse wxdragon::prelude::*;\n\nlet _ = wxdragon::main(|_| {\n let frame = Frame::builder().build();\n let grid = PropertyGrid::builder(&frame)\n .with_style(PropertyGridStyle::AutoSort | PropertyGridStyle::BoldModified | PropertyGridStyle::Tooltips)\n .build();\n frame.show(true);\n});\n```",
variants: {
Default: ffi::WXD_PG_DEFAULT_STYLE as i64, "Default property-grid style.",
AutoSort: ffi::WXD_PG_AUTO_SORT as i64, "Automatically sort properties after insertion.",
HideCategories: ffi::WXD_PG_HIDE_CATEGORIES as i64, "Hide category rows.",
AlphabeticMode: ffi::WXD_PG_ALPHABETIC_MODE as i64, "Hide categories and sort properties alphabetically.",
BoldModified: ffi::WXD_PG_BOLD_MODIFIED as i64, "Render modified values in bold.",
SplitterAutoCenter: ffi::WXD_PG_SPLITTER_AUTO_CENTER as i64, "Keep the splitter centered while resizing.",
Tooltips: ffi::WXD_PG_TOOLTIPS as i64, "Show tooltips for clipped cell text.",
HideMargin: ffi::WXD_PG_HIDE_MARGIN as i64, "Hide the margin and expand/collapse buttons.",
StaticSplitter: ffi::WXD_PG_STATIC_SPLITTER as i64, "Prevent users from moving the splitter.",
StaticLayout: ffi::WXD_PG_STATIC_LAYOUT as i64, "Use a fixed margin and splitter layout.",
LimitedEditing: ffi::WXD_PG_LIMITED_EDITING as i64, "Disable free-form text editors where another editor is available."
},
default_variant: Default
);
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PropertyId(String);
impl PropertyId {
pub fn new(name: impl Into<String>) -> Self {
Self(name.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
pub fn into_string(self) -> String {
self.0
}
}
impl AsRef<str> for PropertyId {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl fmt::Display for PropertyId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl From<PropertyId> for String {
fn from(value: PropertyId) -> Self {
value.into_string()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PropertyChoice {
pub label: String,
pub value: i32,
}
impl PropertyChoice {
pub fn new(label: impl Into<String>, value: i32) -> Self {
Self {
label: label.into(),
value,
}
}
}
impl<S: Into<String>> From<(S, i32)> for PropertyChoice {
fn from((label, value): (S, i32)) -> Self {
Self::new(label, value)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum PropertyKind {
Category,
String(String),
Int(i64),
UInt(u64),
Float(f64),
Bool(bool),
Enum {
choices: Vec<PropertyChoice>,
value: i32,
},
Flags {
choices: Vec<PropertyChoice>,
value: i32,
},
File(String),
Dir(String),
}
#[derive(Debug, Clone, PartialEq)]
pub struct Property {
label: String,
name: String,
parent: Option<String>,
kind: PropertyKind,
}
impl Property {
fn new(label: impl Into<String>, name: impl Into<String>, kind: PropertyKind) -> Self {
Self {
label: label.into(),
name: name.into(),
parent: None,
kind,
}
}
pub fn category(label: impl Into<String>, name: impl Into<String>) -> Self {
Self::new(label, name, PropertyKind::Category)
}
pub fn string(label: impl Into<String>, name: impl Into<String>, value: impl Into<String>) -> Self {
Self::new(label, name, PropertyKind::String(value.into()))
}
pub fn int(label: impl Into<String>, name: impl Into<String>, value: i64) -> Self {
Self::new(label, name, PropertyKind::Int(value))
}
pub fn uint(label: impl Into<String>, name: impl Into<String>, value: u64) -> Self {
Self::new(label, name, PropertyKind::UInt(value))
}
pub fn float(label: impl Into<String>, name: impl Into<String>, value: f64) -> Self {
Self::new(label, name, PropertyKind::Float(value))
}
pub fn boolean(label: impl Into<String>, name: impl Into<String>, value: bool) -> Self {
Self::new(label, name, PropertyKind::Bool(value))
}
pub fn enumeration<I, C>(label: impl Into<String>, name: impl Into<String>, choices: I, value: i32) -> Self
where
I: IntoIterator<Item = C>,
C: Into<PropertyChoice>,
{
Self::new(
label,
name,
PropertyKind::Enum {
choices: choices.into_iter().map(Into::into).collect(),
value,
},
)
}
pub fn flags<I, C>(label: impl Into<String>, name: impl Into<String>, choices: I, value: i32) -> Self
where
I: IntoIterator<Item = C>,
C: Into<PropertyChoice>,
{
Self::new(
label,
name,
PropertyKind::Flags {
choices: choices.into_iter().map(Into::into).collect(),
value,
},
)
}
pub fn file(label: impl Into<String>, name: impl Into<String>, value: impl Into<String>) -> Self {
Self::new(label, name, PropertyKind::File(value.into()))
}
pub fn dir(label: impl Into<String>, name: impl Into<String>, value: impl Into<String>) -> Self {
Self::new(label, name, PropertyKind::Dir(value.into()))
}
pub fn under(mut self, parent: impl AsRef<str>) -> Self {
self.parent = Some(parent.as_ref().to_owned());
self
}
pub fn label(&self) -> &str {
&self.label
}
pub fn name(&self) -> &str {
&self.name
}
pub fn parent(&self) -> Option<&str> {
self.parent.as_deref()
}
pub fn kind(&self) -> &PropertyKind {
&self.kind
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PropertyGridEvent {
Selected,
Changing,
Changed,
Highlighted,
RightClick,
PageChanged,
ItemCollapsed,
ItemExpanded,
DoubleClick,
LabelEditBegin,
LabelEditEnding,
ColumnBeginDrag,
ColumnDragging,
ColumnEndDrag,
}
#[derive(Debug)]
pub struct PropertyGridEventData {
event: Event,
}
impl PropertyGridEventData {
pub fn new(event: Event) -> Self {
Self { event }
}
pub fn get_id(&self) -> i32 {
self.event.get_id()
}
pub fn skip(&self, skip: bool) {
self.event.skip(skip);
}
pub fn property_name(&self) -> Option<String> {
if self.event.is_null() {
return None;
}
read_ffi_string(|out, out_len| unsafe { ffi::wxd_PropertyGridEvent_GetPropertyName(self.event.0, out, out_len) })
}
pub fn value(&self) -> Option<Variant> {
if self.event.is_null() {
return None;
}
let ptr = unsafe { ffi::wxd_PropertyGridEvent_GetValue(self.event.0) };
if ptr.is_null() { None } else { Some(Variant::from(ptr)) }
}
pub fn column(&self) -> u32 {
if self.event.is_null() {
return 0;
}
unsafe { ffi::wxd_PropertyGridEvent_GetColumn(self.event.0) }
}
pub fn can_veto(&self) -> bool {
!self.event.is_null() && unsafe { ffi::wxd_PropertyGridEvent_CanVeto(self.event.0) }
}
pub fn veto(&self, veto: bool) {
if !self.event.is_null() {
unsafe { ffi::wxd_PropertyGridEvent_Veto(self.event.0, veto) }
}
}
pub fn was_vetoed(&self) -> bool {
!self.event.is_null() && unsafe { ffi::wxd_PropertyGridEvent_WasVetoed(self.event.0) }
}
}
#[derive(Clone, Copy)]
pub struct PropertyGrid {
handle: WindowHandle,
}
impl PropertyGrid {
pub fn builder(parent: &dyn WxWidget) -> PropertyGridBuilder<'_> {
PropertyGridBuilder::new(parent)
}
fn new_impl(parent: *mut ffi::wxd_Window_t, id: Id, pos: Point, size: Size, style: i64) -> Self {
assert!(!parent.is_null(), "PropertyGrid requires a parent");
let ptr = unsafe { ffi::wxd_PropertyGrid_Create(parent, id, pos.into(), size.into(), style) };
assert!(!ptr.is_null(), "Failed to create PropertyGrid: FFI returned null");
Self {
handle: WindowHandle::new(ptr.cast()),
}
}
#[inline]
fn property_grid_ptr(&self) -> *mut ffi::wxd_PropertyGrid_t {
self.handle.get_ptr().map(|ptr| ptr.cast()).unwrap_or(std::ptr::null_mut())
}
pub fn window_handle(&self) -> WindowHandle {
self.handle
}
pub fn contains(&self, name: impl AsRef<str>) -> bool {
let Some(name) = to_cstring(name.as_ref()) else {
return false;
};
let ptr = self.property_grid_ptr();
!ptr.is_null() && unsafe { ffi::wxd_PropertyGrid_Contains(ptr, name.as_ptr()) }
}
pub fn append(&self, property: Property) -> Option<PropertyId> {
let ptr = self.property_grid_ptr();
if ptr.is_null() || property.name.is_empty() {
return None;
}
let label = to_cstring(&property.label)?;
let name = to_cstring(&property.name)?;
let parent = match property.parent.as_deref() {
Some(parent) => Some(to_cstring(parent)?),
None => None,
};
let parent_ptr = parent.as_ref().map_or(std::ptr::null(), |value| value.as_ptr());
let appended = match &property.kind {
PropertyKind::Category => unsafe {
ffi::wxd_PropertyGrid_AppendCategory(ptr, parent_ptr, label.as_ptr(), name.as_ptr())
},
PropertyKind::String(value) => {
let value = to_cstring(value)?;
unsafe { ffi::wxd_PropertyGrid_AppendString(ptr, parent_ptr, label.as_ptr(), name.as_ptr(), value.as_ptr()) }
}
PropertyKind::Int(value) => unsafe {
ffi::wxd_PropertyGrid_AppendInt(ptr, parent_ptr, label.as_ptr(), name.as_ptr(), *value)
},
PropertyKind::UInt(value) => unsafe {
ffi::wxd_PropertyGrid_AppendUInt(ptr, parent_ptr, label.as_ptr(), name.as_ptr(), *value)
},
PropertyKind::Float(value) => unsafe {
ffi::wxd_PropertyGrid_AppendFloat(ptr, parent_ptr, label.as_ptr(), name.as_ptr(), *value)
},
PropertyKind::Bool(value) => unsafe {
ffi::wxd_PropertyGrid_AppendBool(ptr, parent_ptr, label.as_ptr(), name.as_ptr(), *value)
},
PropertyKind::Enum { choices, value } => unsafe {
append_choices(ptr, parent_ptr, &label, &name, choices, *value, false)?
},
PropertyKind::Flags { choices, value } => unsafe {
append_choices(ptr, parent_ptr, &label, &name, choices, *value, true)?
},
PropertyKind::File(value) => {
let value = to_cstring(value)?;
unsafe { ffi::wxd_PropertyGrid_AppendFile(ptr, parent_ptr, label.as_ptr(), name.as_ptr(), value.as_ptr()) }
}
PropertyKind::Dir(value) => {
let value = to_cstring(value)?;
unsafe { ffi::wxd_PropertyGrid_AppendDir(ptr, parent_ptr, label.as_ptr(), name.as_ptr(), value.as_ptr()) }
}
};
appended.then(|| PropertyId::new(property.name))
}
pub fn get_value(&self, name: impl AsRef<str>) -> Option<Variant> {
let name = to_cstring(name.as_ref())?;
let ptr = self.property_grid_ptr();
if ptr.is_null() {
return None;
}
let value = unsafe { ffi::wxd_PropertyGrid_GetValue(ptr, name.as_ptr()) };
(!value.is_null()).then(|| Variant::from(value))
}
pub fn set_value(&self, name: impl AsRef<str>, value: &Variant) -> bool {
let Some(name) = to_cstring(name.as_ref()) else {
return false;
};
let ptr = self.property_grid_ptr();
!ptr.is_null() && unsafe { ffi::wxd_PropertyGrid_SetValue(ptr, name.as_ptr(), value.as_const_ptr()) }
}
pub fn set<V: Into<Variant>>(&self, name: impl AsRef<str>, value: V) -> bool {
let value = value.into();
self.set_value(name, &value)
}
pub fn change_value(&self, name: impl AsRef<str>, value: &Variant) -> bool {
let Some(name) = to_cstring(name.as_ref()) else {
return false;
};
let ptr = self.property_grid_ptr();
!ptr.is_null() && unsafe { ffi::wxd_PropertyGrid_ChangeValue(ptr, name.as_ptr(), value.as_const_ptr()) }
}
pub fn change<V: Into<Variant>>(&self, name: impl AsRef<str>, value: V) -> bool {
let value = value.into();
self.change_value(name, &value)
}
pub fn clear_value(&self, name: impl AsRef<str>) -> bool {
self.call_name_bool(name, |ptr, name| unsafe { ffi::wxd_PropertyGrid_ClearValue(ptr, name) })
}
pub fn get_value_as_string(&self, name: impl AsRef<str>) -> Option<String> {
let name = to_cstring(name.as_ref())?;
let ptr = self.property_grid_ptr();
if ptr.is_null() {
return None;
}
read_ffi_string(|out, out_len| unsafe { ffi::wxd_PropertyGrid_GetValueAsString(ptr, name.as_ptr(), out, out_len) })
}
pub fn get_label(&self, name: impl AsRef<str>) -> Option<String> {
let name = to_cstring(name.as_ref())?;
let ptr = self.property_grid_ptr();
if ptr.is_null() {
return None;
}
read_ffi_string(|out, out_len| unsafe { ffi::wxd_PropertyGrid_GetLabel(ptr, name.as_ptr(), out, out_len) })
}
pub fn set_label(&self, name: impl AsRef<str>, label: &str) -> bool {
let (Some(name), Some(label)) = (to_cstring(name.as_ref()), to_cstring(label)) else {
return false;
};
let ptr = self.property_grid_ptr();
!ptr.is_null() && unsafe { ffi::wxd_PropertyGrid_SetLabel(ptr, name.as_ptr(), label.as_ptr()) }
}
pub fn get_help_string(&self, name: impl AsRef<str>) -> Option<String> {
let name = to_cstring(name.as_ref())?;
let ptr = self.property_grid_ptr();
if ptr.is_null() {
return None;
}
read_ffi_string(|out, out_len| unsafe { ffi::wxd_PropertyGrid_GetHelpString(ptr, name.as_ptr(), out, out_len) })
}
pub fn set_help_string(&self, name: impl AsRef<str>, help: &str) -> bool {
let (Some(name), Some(help)) = (to_cstring(name.as_ref()), to_cstring(help)) else {
return false;
};
let ptr = self.property_grid_ptr();
!ptr.is_null() && unsafe { ffi::wxd_PropertyGrid_SetHelpString(ptr, name.as_ptr(), help.as_ptr()) }
}
pub fn set_attribute(&self, name: impl AsRef<str>, attribute: &str, value: &Variant) -> bool {
let (Some(name), Some(attribute)) = (to_cstring(name.as_ref()), to_cstring(attribute)) else {
return false;
};
let ptr = self.property_grid_ptr();
!ptr.is_null()
&& unsafe { ffi::wxd_PropertyGrid_SetAttribute(ptr, name.as_ptr(), attribute.as_ptr(), value.as_const_ptr()) }
}
pub fn enable_property(&self, name: impl AsRef<str>, enable: bool) -> bool {
self.call_name_bool(name, |ptr, name| unsafe {
ffi::wxd_PropertyGrid_EnableProperty(ptr, name, enable)
})
}
pub fn hide_property(&self, name: impl AsRef<str>, hide: bool) -> bool {
self.call_name_bool(name, |ptr, name| unsafe {
ffi::wxd_PropertyGrid_HideProperty(ptr, name, hide)
})
}
pub fn set_property_read_only(&self, name: impl AsRef<str>, read_only: bool) -> bool {
self.call_name_bool(name, |ptr, name| unsafe {
ffi::wxd_PropertyGrid_SetPropertyReadOnly(ptr, name, read_only)
})
}
pub fn is_property_enabled(&self, name: impl AsRef<str>) -> bool {
self.call_name_bool(name, |ptr, name| unsafe {
ffi::wxd_PropertyGrid_IsPropertyEnabled(ptr, name)
})
}
pub fn is_property_hidden(&self, name: impl AsRef<str>) -> bool {
self.call_name_bool(name, |ptr, name| unsafe { ffi::wxd_PropertyGrid_IsPropertyHidden(ptr, name) })
}
pub fn is_property_expanded(&self, name: impl AsRef<str>) -> bool {
self.call_name_bool(name, |ptr, name| unsafe {
ffi::wxd_PropertyGrid_IsPropertyExpanded(ptr, name)
})
}
pub fn is_property_category(&self, name: impl AsRef<str>) -> bool {
self.call_name_bool(name, |ptr, name| unsafe {
ffi::wxd_PropertyGrid_IsPropertyCategory(ptr, name)
})
}
pub fn is_property_modified(&self, name: impl AsRef<str>) -> bool {
self.call_name_bool(name, |ptr, name| unsafe {
ffi::wxd_PropertyGrid_IsPropertyModified(ptr, name)
})
}
pub fn expand(&self, name: impl AsRef<str>) -> bool {
self.call_name_bool(name, |ptr, name| unsafe { ffi::wxd_PropertyGrid_Expand(ptr, name) })
}
pub fn collapse(&self, name: impl AsRef<str>) -> bool {
self.call_name_bool(name, |ptr, name| unsafe { ffi::wxd_PropertyGrid_Collapse(ptr, name) })
}
pub fn expand_all(&self, expand: bool) -> bool {
let ptr = self.property_grid_ptr();
!ptr.is_null() && unsafe { ffi::wxd_PropertyGrid_ExpandAll(ptr, expand) }
}
pub fn select_property(&self, name: impl AsRef<str>, focus: bool) -> bool {
self.call_name_bool(name, |ptr, name| unsafe {
ffi::wxd_PropertyGrid_SelectProperty(ptr, name, focus)
})
}
pub fn get_selected_property_name(&self) -> Option<String> {
let ptr = self.property_grid_ptr();
if ptr.is_null() {
return None;
}
read_ffi_string(|out, out_len| unsafe { ffi::wxd_PropertyGrid_GetSelectedPropertyName(ptr, out, out_len) })
}
pub fn delete_property(&self, name: impl AsRef<str>) -> bool {
self.call_name_bool(name, |ptr, name| unsafe { ffi::wxd_PropertyGrid_DeleteProperty(ptr, name) })
}
pub fn clear(&self) {
let ptr = self.property_grid_ptr();
if !ptr.is_null() {
unsafe { ffi::wxd_PropertyGrid_Clear(ptr) }
}
}
pub fn clear_modified_status(&self) {
let ptr = self.property_grid_ptr();
if !ptr.is_null() {
unsafe { ffi::wxd_PropertyGrid_ClearModifiedStatus(ptr) }
}
}
pub fn get_splitter_position(&self, column: u32) -> i32 {
let ptr = self.property_grid_ptr();
if ptr.is_null() {
-1
} else {
unsafe { ffi::wxd_PropertyGrid_GetSplitterPosition(ptr, column) }
}
}
pub fn set_splitter_position(&self, position: i32, column: u32) {
let ptr = self.property_grid_ptr();
if !ptr.is_null() {
unsafe { ffi::wxd_PropertyGrid_SetSplitterPosition(ptr, position, column) }
}
}
pub fn get_column_proportion(&self, column: u32) -> i32 {
let ptr = self.property_grid_ptr();
if ptr.is_null() {
-1
} else {
unsafe { ffi::wxd_PropertyGrid_GetColumnProportion(ptr, column) }
}
}
pub fn set_column_proportion(&self, column: u32, proportion: i32) -> bool {
let ptr = self.property_grid_ptr();
!ptr.is_null() && unsafe { ffi::wxd_PropertyGrid_SetColumnProportion(ptr, column, proportion) }
}
pub fn center_splitter(&self, enable_auto_resizing: bool) {
let ptr = self.property_grid_ptr();
if !ptr.is_null() {
unsafe { ffi::wxd_PropertyGrid_CenterSplitter(ptr, enable_auto_resizing) }
}
}
pub fn refresh_grid(&self) {
let ptr = self.property_grid_ptr();
if !ptr.is_null() {
unsafe { ffi::wxd_PropertyGrid_Refresh(ptr) }
}
}
fn call_name_bool(
&self,
name: impl AsRef<str>,
call: impl FnOnce(*mut ffi::wxd_PropertyGrid_t, *const c_char) -> bool,
) -> bool {
let Some(name) = to_cstring(name.as_ref()) else {
return false;
};
let ptr = self.property_grid_ptr();
!ptr.is_null() && call(ptr, name.as_ptr())
}
}
impl WxWidget for PropertyGrid {
fn handle_ptr(&self) -> *mut ffi::wxd_Window_t {
self.handle.get_ptr().unwrap_or(std::ptr::null_mut())
}
fn is_valid(&self) -> bool {
self.handle.is_valid()
}
}
impl WxEvtHandler for PropertyGrid {
unsafe fn get_event_handler_ptr(&self) -> *mut ffi::wxd_EvtHandler_t {
self.handle.get_ptr().unwrap_or(std::ptr::null_mut()).cast()
}
}
widget_builder!(
name: PropertyGrid,
parent_type: &'a dyn WxWidget,
style_type: PropertyGridStyle,
fields: {},
build_impl: |slf| {
PropertyGrid::new_impl(
slf.parent.handle_ptr(),
slf.id,
slf.pos,
slf.size,
slf.style.bits(),
)
}
);
crate::implement_widget_local_event_handlers!(
PropertyGrid,
PropertyGridEvent,
PropertyGridEventData,
Selected => selected, EventType::PG_SELECTED,
Changing => changing, EventType::PG_CHANGING,
Changed => changed, EventType::PG_CHANGED,
Highlighted => highlighted, EventType::PG_HIGHLIGHTED,
RightClick => right_click, EventType::PG_RIGHT_CLICK,
PageChanged => page_changed, EventType::PG_PAGE_CHANGED,
ItemCollapsed => item_collapsed, EventType::PG_ITEM_COLLAPSED,
ItemExpanded => item_expanded, EventType::PG_ITEM_EXPANDED,
DoubleClick => double_click, EventType::PG_DOUBLE_CLICK,
LabelEditBegin => label_edit_begin, EventType::PG_LABEL_EDIT_BEGIN,
LabelEditEnding => label_edit_ending, EventType::PG_LABEL_EDIT_ENDING,
ColumnBeginDrag => column_begin_drag, EventType::PG_COL_BEGIN_DRAG,
ColumnDragging => column_dragging, EventType::PG_COL_DRAGGING,
ColumnEndDrag => column_end_drag, EventType::PG_COL_END_DRAG
);
impl crate::window::FromWindowWithClassName for PropertyGrid {
fn class_name() -> &'static str {
"wxPropertyGrid"
}
unsafe fn from_ptr(ptr: *mut ffi::wxd_Window_t) -> Self {
Self {
handle: WindowHandle::new(ptr),
}
}
}
#[cfg(feature = "xrc")]
impl crate::xrc::XrcSupport for PropertyGrid {
unsafe fn from_xrc_ptr(ptr: *mut ffi::wxd_Window_t) -> Self {
Self {
handle: WindowHandle::new(ptr),
}
}
}
fn to_cstring(value: &str) -> Option<CString> {
CString::new(value).ok()
}
fn read_ffi_string(reader: impl Fn(*mut c_char, usize) -> i32) -> Option<String> {
let needed = reader(std::ptr::null_mut(), 0);
if needed < 0 {
return None;
}
let mut buffer = vec![0; needed as usize + 1];
if reader(buffer.as_mut_ptr(), buffer.len()) < 0 {
return None;
}
Some(unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_string_lossy().into_owned())
}
unsafe fn append_choices(
grid: *mut ffi::wxd_PropertyGrid_t,
parent: *const c_char,
label: &CString,
name: &CString,
choices: &[PropertyChoice],
value: i32,
flags: bool,
) -> Option<bool> {
let labels: Option<Vec<CString>> = choices.iter().map(|choice| to_cstring(&choice.label)).collect();
let labels = labels?;
let label_ptrs: Vec<*const c_char> = labels.iter().map(|label| label.as_ptr()).collect();
let values: Vec<i32> = choices.iter().map(|choice| choice.value).collect();
if flags {
Some(unsafe {
ffi::wxd_PropertyGrid_AppendFlags(
grid,
parent,
label.as_ptr(),
name.as_ptr(),
label_ptrs.as_ptr(),
values.as_ptr(),
choices.len(),
value,
)
})
} else {
Some(unsafe {
ffi::wxd_PropertyGrid_AppendEnum(
grid,
parent,
label.as_ptr(),
name.as_ptr(),
label_ptrs.as_ptr(),
values.as_ptr(),
choices.len(),
value,
)
})
}
}