use std::any::Any;
use std::ffi::CString;
use std::ptr;
use std::sync::Arc;
use crate::event::{TreeEvents, WxEvtHandler};
use crate::geometry::{Point, Size};
use crate::id::Id;
use crate::widgets::imagelist::ImageList;
use crate::widgets::item_data::{HasItemData, get_item_data, remove_item_data, store_item_data};
use crate::window::{WindowHandle, WxWidget};
use wxdragon_sys as ffi;
widget_style_enum!(
name: TreeCtrlStyle,
doc: "Style flags for TreeCtrl widget.",
variants: {
Default: ffi::WXD_TR_DEFAULT_STYLE, "Default style. Combines `HasButtons` and `LinesAtRoot`.",
HasButtons: ffi::WXD_TR_HAS_BUTTONS, "Use buttons to show expand/collapse state.",
LinesAtRoot: ffi::WXD_TR_LINES_AT_ROOT, "Use lines to show hierarchy at the root level.",
NoLines: ffi::WXD_TR_NO_LINES, "Don't show any lines.",
Single: ffi::WXD_TR_SINGLE, "Only allow a single item to be selected.",
HideRoot: ffi::WXD_TR_HIDE_ROOT, "Hide the root item, making its children appear as top-level items.",
EditLabels: ffi::WXD_TR_EDIT_LABELS, "Allow editing of item labels."
},
default_variant: Default
);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)] pub enum TreeItemIcon {
Normal = ffi::wxd_TreeItemIconType_t_WXD_TreeItemIcon_Normal,
Selected = ffi::wxd_TreeItemIconType_t_WXD_TreeItemIcon_Selected,
Expanded = ffi::wxd_TreeItemIconType_t_WXD_TreeItemIcon_Expanded,
SelectedExpanded = ffi::wxd_TreeItemIconType_t_WXD_TreeItemIcon_SelectedExpanded,
}
impl From<TreeItemIcon> for ffi::wxd_TreeItemIconType_t {
fn from(icon: TreeItemIcon) -> Self {
icon as ffi::wxd_TreeItemIconType_t
}
}
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TreeHitTestFlags: u32 {
const ABOVE = 0x0001;
const BELOW = 0x0002;
const NOWHERE = 0x0004;
const ON_ITEM_BUTTON = 0x0008;
const ON_ITEM_ICON = 0x0010;
const ON_ITEM_INDENT = 0x0020;
const ON_ITEM_LABEL = 0x0040;
const ON_ITEM_RIGHT = 0x0080;
const ON_ITEM_STATE_ICON = 0x0100;
const TO_LEFT = 0x0200;
const TO_RIGHT = 0x0400;
const ON_ITEM_UPPER_PART = 0x0800;
const ON_ITEM_LOWER_PART = 0x1000;
const ON_ITEM = Self::ON_ITEM_ICON.bits() | Self::ON_ITEM_LABEL.bits();
}
}
#[derive(Debug)]
pub struct TreeItemId {
ptr: *mut ffi::wxd_TreeItemId_t,
}
impl TreeItemId {
pub(crate) unsafe fn from_ptr(ptr: *mut ffi::wxd_TreeItemId_t) -> Option<Self> {
if ptr.is_null() {
None
} else {
let ptr_value = ptr as usize;
if ptr_value.is_multiple_of(std::mem::align_of::<*mut std::ffi::c_void>()) && ptr_value > 0x1000 && ptr_value < (usize::MAX / 2)
{
if unsafe { ffi::wxd_TreeItemId_IsOk(ptr) } {
Some(TreeItemId { ptr })
} else {
log::warn!("Warning: C++ returned invalid TreeItemId pointer {ptr:p}, rejecting");
unsafe { ffi::wxd_TreeItemId_Free(ptr) };
None
}
} else {
log::warn!("Warning: C++ returned corrupted TreeItemId pointer {ptr:p}, rejecting");
None
}
}
}
pub fn is_ok(&self) -> bool {
unsafe { ffi::wxd_TreeItemId_IsOk(self.ptr) }
}
pub(crate) fn as_ptr(&self) -> *mut ffi::wxd_TreeItemId_t {
self.ptr
}
}
impl Clone for TreeItemId {
fn clone(&self) -> Self {
let clone_ptr = unsafe { ffi::wxd_TreeItemId_Clone(self.ptr) };
TreeItemId { ptr: clone_ptr }
}
}
impl From<&TreeItemId> for u64 {
fn from(tree_item: &TreeItemId) -> Self {
tree_item as *const _ as usize as u64
}
}
impl Drop for TreeItemId {
fn drop(&mut self) {
if !self.ptr.is_null() {
unsafe {
let ptr_value = self.ptr as usize;
if ptr_value.is_multiple_of(std::mem::align_of::<*mut std::ffi::c_void>()) && ptr_value > 0x1000 && ptr_value < (usize::MAX / 2)
{
if ffi::wxd_TreeItemId_IsOk(self.ptr) {
ffi::wxd_TreeItemId_Free(self.ptr);
} else {
ffi::wxd_TreeItemId_Free(self.ptr);
}
} else {
log::warn!(
"Warning: Dropping TreeItemId with corrupted pointer {:p}, not freeing to avoid crash",
self.ptr
);
}
}
self.ptr = ptr::null_mut();
}
}
}
pub struct TreeIterationCookie {
cookie_ptr: *mut std::ffi::c_void,
}
impl TreeIterationCookie {
fn new(cookie_ptr: *mut std::ffi::c_void) -> Self {
Self { cookie_ptr }
}
fn as_ptr_mut(&mut self) -> *mut *mut std::ffi::c_void {
&mut self.cookie_ptr as *mut *mut std::ffi::c_void
}
}
impl Drop for TreeIterationCookie {
fn drop(&mut self) {
self.cookie_ptr = ptr::null_mut();
}
}
#[derive(Clone, Copy)]
pub struct TreeCtrl {
handle: WindowHandle,
}
impl TreeCtrl {
pub fn builder(parent: &dyn WxWidget) -> TreeCtrlBuilder<'_> {
TreeCtrlBuilder::new(parent)
}
fn new_impl(parent_ptr: *mut ffi::wxd_Window_t, id: Id, pos: Point, size: Size, style: i64) -> Self {
assert!(!parent_ptr.is_null(), "TreeCtrl parent cannot be null");
let ctrl_ptr = unsafe { ffi::wxd_TreeCtrl_Create(parent_ptr, id, pos.into(), size.into(), style as ffi::wxd_Style_t) };
if ctrl_ptr.is_null() {
panic!("Failed to create wxTreeCtrl");
}
let tree_ctrl = TreeCtrl {
handle: WindowHandle::new(ctrl_ptr as *mut ffi::wxd_Window_t),
};
tree_ctrl.setup_cleanup();
tree_ctrl
}
#[inline]
fn treectrl_ptr(&self) -> *mut ffi::wxd_TreeCtrl_t {
self.handle
.get_ptr()
.map(|p| p as *mut ffi::wxd_TreeCtrl_t)
.unwrap_or(std::ptr::null_mut())
}
pub fn add_root(&self, text: &str, image: Option<i32>, selected_image: Option<i32>) -> Option<TreeItemId> {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return None;
}
let c_text = CString::new(text).unwrap_or_default();
let img = image.unwrap_or(-1);
let sel_img = selected_image.unwrap_or(-1);
let item_ptr = unsafe { ffi::wxd_TreeCtrl_AddRoot(ptr, c_text.as_ptr(), img, sel_img, ptr::null_mut()) };
unsafe { TreeItemId::from_ptr(item_ptr) }
}
pub fn add_root_with_data<T: Any + Send + Sync + 'static>(
&self,
text: &str,
data: T,
image: Option<i32>,
selected_image: Option<i32>,
) -> Option<TreeItemId> {
let root_item = self.add_root(text, image, selected_image)?;
self.set_custom_data_direct(&root_item, data);
Some(root_item)
}
pub fn append_item(
&self,
parent: &TreeItemId,
text: &str,
image: Option<i32>,
selected_image: Option<i32>,
) -> Option<TreeItemId> {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return None;
}
let t = CString::new(text).unwrap_or_default();
let img = image.unwrap_or(-1);
let sel_img = selected_image.unwrap_or(-1);
let item_ptr = unsafe { ffi::wxd_TreeCtrl_AppendItem(ptr, parent.as_ptr(), t.as_ptr(), img, sel_img, ptr::null_mut()) };
unsafe { TreeItemId::from_ptr(item_ptr) }
}
pub fn append_item_with_data<T: Any + Send + Sync + 'static>(
&self,
parent: &TreeItemId,
text: &str,
data: T,
image: Option<i32>,
selected_image: Option<i32>,
) -> Option<TreeItemId> {
let item = self.append_item(parent, text, image, selected_image)?;
self.set_custom_data_direct(&item, data);
Some(item)
}
pub fn delete(&self, item: &TreeItemId) {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return;
}
let _ = self.clear_custom_data(item);
unsafe { ffi::wxd_TreeCtrl_Delete(ptr, item.as_ptr()) };
}
pub fn get_selection(&self) -> Option<TreeItemId> {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return None;
}
let item_ptr = unsafe { ffi::wxd_TreeCtrl_GetSelection(ptr) };
unsafe { TreeItemId::from_ptr(item_ptr) }
}
pub fn select_item(&self, item: &TreeItemId) {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return;
}
unsafe {
ffi::wxd_TreeCtrl_SelectItem(ptr, item.as_ptr());
}
}
pub fn expand(&self, item: &TreeItemId) {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeCtrl_Expand(ptr, item.as_ptr()) };
}
fn setup_cleanup(&self) {
use crate::event::EventType;
let tree_ctrl_clone = *self;
self.bind_internal(EventType::DESTROY, move |_event| {
tree_ctrl_clone.cleanup_all_custom_data();
});
}
pub fn cleanup_custom_data(&self) {
self.cleanup_all_custom_data();
}
pub fn get_root_item(&self) -> Option<TreeItemId> {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return None;
}
let item_ptr = unsafe { ffi::wxd_TreeCtrl_GetRootItem(ptr) };
unsafe { TreeItemId::from_ptr(item_ptr) }
}
pub fn get_first_child(&self, item: &TreeItemId) -> Option<(TreeItemId, TreeIterationCookie)> {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return None;
}
let mut cookie_ptr = ptr::null_mut();
let child_ptr =
unsafe { ffi::wxd_TreeCtrl_GetFirstChild(ptr, item.as_ptr(), &mut cookie_ptr as *mut *mut std::ffi::c_void) };
let child = unsafe { TreeItemId::from_ptr(child_ptr) }?;
let cookie = TreeIterationCookie::new(cookie_ptr);
Some((child, cookie))
}
pub fn get_next_child(&self, item: &TreeItemId, cookie: &mut TreeIterationCookie) -> Option<TreeItemId> {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return None;
}
let child_ptr = unsafe { ffi::wxd_TreeCtrl_GetNextChild(ptr, item.as_ptr(), cookie.as_ptr_mut()) };
unsafe { TreeItemId::from_ptr(child_ptr) }
}
pub fn get_next_sibling(&self, item: &TreeItemId) -> Option<TreeItemId> {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return None;
}
let sibling_ptr = unsafe { ffi::wxd_TreeCtrl_GetNextSibling(ptr, item.as_ptr()) };
unsafe { TreeItemId::from_ptr(sibling_ptr) }
}
pub fn get_children_count(&self, item: &TreeItemId, recursively: bool) -> usize {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return 0;
}
unsafe { ffi::wxd_TreeCtrl_GetChildrenCount(ptr, item.as_ptr(), recursively) as usize }
}
pub fn set_image_list(&self, image_list: ImageList) {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return;
}
unsafe {
ffi::wxd_TreeCtrl_SetImageList(ptr, image_list.as_ptr());
}
std::mem::forget(image_list);
}
pub fn get_image_list(&self) -> Option<ImageList> {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return None;
}
let img_ptr = unsafe { ffi::wxd_TreeCtrl_GetImageList(ptr) };
if img_ptr.is_null() {
None
} else {
Some(unsafe { ImageList::from_ptr_unowned(img_ptr) })
}
}
pub fn set_item_image(&self, item: &TreeItemId, image_index: i32, icon_type: TreeItemIcon) {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeCtrl_SetItemImage(ptr, item.as_ptr(), image_index, icon_type.into()) };
}
pub fn get_item_image(&self, item: &TreeItemId, icon_type: TreeItemIcon) -> i32 {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return -1;
}
unsafe { ffi::wxd_TreeCtrl_GetItemImage(ptr, item.as_ptr(), icon_type.into()) }
}
pub fn get_item_text(&self, item: &TreeItemId) -> Option<String> {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return None;
}
let required_size = unsafe { ffi::wxd_TreeCtrl_GetItemText(ptr, item.as_ptr(), std::ptr::null_mut(), 0) };
if required_size < 0 {
return None;
}
let buffer_size = (required_size as usize) + 1;
let mut buffer: Vec<u8> = vec![0; buffer_size];
let result = unsafe { ffi::wxd_TreeCtrl_GetItemText(ptr, item.as_ptr(), buffer.as_mut_ptr() as *mut i8, buffer_size) };
if result < 0 {
return None;
}
let text_len = result as usize;
buffer.truncate(text_len);
String::from_utf8(buffer).ok()
}
pub fn ensure_visible(&self, item: &TreeItemId) {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeCtrl_EnsureVisible(ptr, item.as_ptr()) }
}
pub fn set_focused_item(&self, item: &TreeItemId) {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeCtrl_SetFocusedItem(ptr, item.as_ptr()) }
}
pub fn expand_all(&self) {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeCtrl_ExpandAll(ptr) }
}
pub fn set_item_text(&self, item: &TreeItemId, text: &str) {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return;
}
let c_text = CString::new(text).unwrap_or_default();
unsafe { ffi::wxd_TreeCtrl_SetItemText(ptr, item.as_ptr(), c_text.as_ptr()) }
}
pub fn get_focused_item(&self) -> Option<TreeItemId> {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return None;
}
let item_ptr = unsafe { ffi::wxd_TreeCtrl_GetFocusedItem(ptr) };
unsafe { TreeItemId::from_ptr(item_ptr) }
}
pub fn collapse(&self, item: &TreeItemId) {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeCtrl_Collapse(ptr, item.as_ptr()) }
}
pub fn collapse_all(&self) {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeCtrl_CollapseAll(ptr) }
}
pub fn collapse_all_children(&self, item: &TreeItemId) {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeCtrl_CollapseAllChildren(ptr, item.as_ptr()) }
}
pub fn collapse_and_reset(&self, item: &TreeItemId) {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeCtrl_CollapseAndReset(ptr, item.as_ptr()) }
}
pub fn toggle(&self, item: &TreeItemId) {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeCtrl_Toggle(ptr, item.as_ptr()) }
}
pub fn is_expanded(&self, item: &TreeItemId) -> bool {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return false;
}
unsafe { ffi::wxd_TreeCtrl_IsExpanded(ptr, item.as_ptr()) }
}
pub fn is_selected(&self, item: &TreeItemId) -> bool {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return false;
}
unsafe { ffi::wxd_TreeCtrl_IsSelected(ptr, item.as_ptr()) }
}
pub fn unselect_all(&self) {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeCtrl_UnselectAll(ptr) }
}
pub fn unselect_item(&self, item: &TreeItemId) {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeCtrl_UnselectItem(ptr, item.as_ptr()) }
}
pub fn select_all(&self) {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeCtrl_SelectAll(ptr) }
}
pub fn get_selections(&self) -> Vec<TreeItemId> {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return Vec::new();
}
let count = unsafe { ffi::wxd_TreeCtrl_GetSelections(ptr, std::ptr::null_mut(), 0) };
if count == 0 {
return Vec::new();
}
let mut items: Vec<*mut ffi::wxd_TreeItemId_t> = vec![std::ptr::null_mut(); count];
unsafe { ffi::wxd_TreeCtrl_GetSelections(ptr, items.as_mut_ptr(), count) };
items.into_iter().filter_map(|p| unsafe { TreeItemId::from_ptr(p) }).collect()
}
pub fn get_item_parent(&self, item: &TreeItemId) -> Option<TreeItemId> {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return None;
}
let item_ptr = unsafe { ffi::wxd_TreeCtrl_GetItemParent(ptr, item.as_ptr()) };
unsafe { TreeItemId::from_ptr(item_ptr) }
}
pub fn get_prev_sibling(&self, item: &TreeItemId) -> Option<TreeItemId> {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return None;
}
let item_ptr = unsafe { ffi::wxd_TreeCtrl_GetPrevSibling(ptr, item.as_ptr()) };
unsafe { TreeItemId::from_ptr(item_ptr) }
}
pub fn get_last_child(&self, item: &TreeItemId) -> Option<TreeItemId> {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return None;
}
let item_ptr = unsafe { ffi::wxd_TreeCtrl_GetLastChild(ptr, item.as_ptr()) };
unsafe { TreeItemId::from_ptr(item_ptr) }
}
pub fn is_visible(&self, item: &TreeItemId) -> bool {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return false;
}
unsafe { ffi::wxd_TreeCtrl_IsVisible(ptr, item.as_ptr()) }
}
pub fn item_has_children(&self, item: &TreeItemId) -> bool {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return false;
}
unsafe { ffi::wxd_TreeCtrl_ItemHasChildren(ptr, item.as_ptr()) }
}
pub fn is_bold(&self, item: &TreeItemId) -> bool {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return false;
}
unsafe { ffi::wxd_TreeCtrl_IsBold(ptr, item.as_ptr()) }
}
pub fn set_item_bold(&self, item: &TreeItemId, bold: bool) {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeCtrl_SetItemBold(ptr, item.as_ptr(), bold) }
}
pub fn set_item_text_colour(&self, item: &TreeItemId, colour: crate::color::Colour) {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeCtrl_SetItemTextColour(ptr, item.as_ptr(), colour.into()) }
}
pub fn get_item_text_colour(&self, item: &TreeItemId) -> crate::color::Colour {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return crate::color::Colour::new(0, 0, 0, 255);
}
let c = unsafe { ffi::wxd_TreeCtrl_GetItemTextColour(ptr, item.as_ptr()) };
crate::color::Colour::new(c.r, c.g, c.b, c.a)
}
pub fn set_item_background_colour(&self, item: &TreeItemId, colour: crate::color::Colour) {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeCtrl_SetItemBackgroundColour(ptr, item.as_ptr(), colour.into()) }
}
pub fn get_item_background_colour(&self, item: &TreeItemId) -> crate::color::Colour {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return crate::color::Colour::new(255, 255, 255, 255);
}
let c = unsafe { ffi::wxd_TreeCtrl_GetItemBackgroundColour(ptr, item.as_ptr()) };
crate::color::Colour::new(c.r, c.g, c.b, c.a)
}
pub fn insert_item(
&self,
parent: &TreeItemId,
previous: &TreeItemId,
text: &str,
image: Option<i32>,
selected_image: Option<i32>,
) -> Option<TreeItemId> {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return None;
}
let c_text = CString::new(text).unwrap_or_default();
let img = image.unwrap_or(-1);
let sel_img = selected_image.unwrap_or(-1);
let item_ptr = unsafe {
ffi::wxd_TreeCtrl_InsertItem(
ptr,
parent.as_ptr(),
previous.as_ptr(),
c_text.as_ptr(),
img,
sel_img,
ptr::null_mut(),
)
};
unsafe { TreeItemId::from_ptr(item_ptr) }
}
pub fn insert_item_before(
&self,
parent: &TreeItemId,
pos: usize,
text: &str,
image: Option<i32>,
selected_image: Option<i32>,
) -> Option<TreeItemId> {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return None;
}
let c_text = CString::new(text).unwrap_or_default();
let img = image.unwrap_or(-1);
let sel_img = selected_image.unwrap_or(-1);
let item_ptr = unsafe {
ffi::wxd_TreeCtrl_InsertItemBefore(ptr, parent.as_ptr(), pos, c_text.as_ptr(), img, sel_img, ptr::null_mut())
};
unsafe { TreeItemId::from_ptr(item_ptr) }
}
pub fn prepend_item(
&self,
parent: &TreeItemId,
text: &str,
image: Option<i32>,
selected_image: Option<i32>,
) -> Option<TreeItemId> {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return None;
}
let c_text = CString::new(text).unwrap_or_default();
let img = image.unwrap_or(-1);
let sel_img = selected_image.unwrap_or(-1);
let item_ptr =
unsafe { ffi::wxd_TreeCtrl_PrependItem(ptr, parent.as_ptr(), c_text.as_ptr(), img, sel_img, ptr::null_mut()) };
unsafe { TreeItemId::from_ptr(item_ptr) }
}
pub fn delete_all_items(&self) {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeCtrl_DeleteAllItems(ptr) }
}
pub fn delete_children(&self, item: &TreeItemId) {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeCtrl_DeleteChildren(ptr, item.as_ptr()) }
}
pub fn get_count(&self) -> usize {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return 0;
}
unsafe { ffi::wxd_TreeCtrl_GetCount(ptr) }
}
pub fn scroll_to(&self, item: &TreeItemId) {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeCtrl_ScrollTo(ptr, item.as_ptr()) }
}
pub fn sort_children(&self, item: &TreeItemId) {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeCtrl_SortChildren(ptr, item.as_ptr()) }
}
pub fn set_item_has_children(&self, item: &TreeItemId, has: bool) {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeCtrl_SetItemHasChildren(ptr, item.as_ptr(), has) }
}
pub fn hit_test(&self, point: Point) -> (Option<TreeItemId>, TreeHitTestFlags) {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return (None, TreeHitTestFlags::NOWHERE);
}
let mut flags: i32 = 0;
let item_ptr = unsafe { ffi::wxd_TreeCtrl_HitTest(ptr, point.into(), &mut flags) };
let item = unsafe { TreeItemId::from_ptr(item_ptr) };
(item, TreeHitTestFlags::from_bits_truncate(flags as u32))
}
pub fn get_bounding_rect(&self, item: &TreeItemId, text_only: bool) -> Option<crate::geometry::Rect> {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return None;
}
let mut rect = ffi::wxd_Rect {
x: 0,
y: 0,
width: 0,
height: 0,
};
let success = unsafe { ffi::wxd_TreeCtrl_GetBoundingRect(ptr, item.as_ptr(), &mut rect, text_only) };
if success {
Some(crate::geometry::Rect::new(rect.x, rect.y, rect.width, rect.height))
} else {
None
}
}
}
impl HasItemData for TreeCtrl {
fn set_custom_data<T: Any + Send + Sync + 'static>(&self, item_id: impl Into<u64>, data: T) -> u64 {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return 0;
}
let item_id = item_id.into();
if let Some(tree_item) = self.get_concrete_tree_item_id(item_id) {
let existing_data_id = unsafe { ffi::wxd_TreeCtrl_GetItemData(ptr, tree_item.as_ptr()) as u64 };
if existing_data_id != 0 {
let _ = remove_item_data(existing_data_id);
}
let data_id = store_item_data(data);
unsafe {
ffi::wxd_TreeCtrl_SetItemData(ptr, tree_item.as_ptr(), data_id as i64);
}
return data_id;
}
0
}
fn get_custom_data(&self, item_id: impl Into<u64>) -> Option<Arc<dyn Any + Send + Sync>> {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return None;
}
let item_id = item_id.into();
let tree_item = self.get_concrete_tree_item_id(item_id)?;
let data_id = unsafe { ffi::wxd_TreeCtrl_GetItemData(ptr, tree_item.as_ptr()) as u64 };
if data_id == 0 {
return None;
}
get_item_data(data_id)
}
fn has_custom_data(&self, item_id: impl Into<u64>) -> bool {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return false;
}
let item_id = item_id.into();
if let Some(tree_item) = self.get_concrete_tree_item_id(item_id) {
let data_id = unsafe { ffi::wxd_TreeCtrl_GetItemData(ptr, tree_item.as_ptr()) as u64 };
return data_id != 0 && get_item_data(data_id).is_some();
}
false
}
fn clear_custom_data(&self, item_id: impl Into<u64>) -> bool {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return false;
}
let item_id = item_id.into();
if let Some(tree_item) = self.get_concrete_tree_item_id(item_id) {
let data_id = unsafe { ffi::wxd_TreeCtrl_GetItemData(ptr, tree_item.as_ptr()) as u64 };
if data_id != 0 {
let _ = remove_item_data(data_id);
unsafe {
ffi::wxd_TreeCtrl_SetItemData(ptr, tree_item.as_ptr(), 0);
}
return true;
}
}
false
}
fn cleanup_all_custom_data(&self) {
let root = match self.get_root_item() {
Some(root) => root,
None => {
return;
}
};
self.clean_item_and_children(&root);
}
}
impl TreeCtrl {
fn get_concrete_tree_item_id(&self, _id: u64) -> Option<TreeItemId> {
if _id > u32::MAX as u64 {
let ptr = _id as usize as *const TreeItemId;
if !ptr.is_null() {
let ptr_value = ptr as usize;
if ptr_value.is_multiple_of(std::mem::align_of::<TreeItemId>()) && ptr_value > 0x1000 && ptr_value < (usize::MAX / 2)
{
unsafe {
let possible_tree_item = &*ptr;
let internal_ptr = possible_tree_item.ptr as usize;
if !possible_tree_item.ptr.is_null()
&& internal_ptr.is_multiple_of(std::mem::align_of::<*mut std::ffi::c_void>())
&& internal_ptr > 0x1000
&& internal_ptr < (usize::MAX / 2)
{
if ffi::wxd_TreeItemId_IsOk(possible_tree_item.ptr) {
let clone_ptr = ffi::wxd_TreeItemId_Clone(possible_tree_item.ptr);
if !clone_ptr.is_null() {
return Some(TreeItemId { ptr: clone_ptr });
}
}
}
}
}
}
}
match _id {
1 => self.get_root_item(),
2 => self.get_selection(),
_ => None,
}
}
fn clean_item_and_children(&self, item: &TreeItemId) {
if self.get_children_count(item, false) == 0 {
return;
}
if let Some((first_child, mut cookie)) = self.get_first_child(item) {
self.clean_item_and_children(&first_child);
while let Some(next_child) = self.get_next_child(item, &mut cookie) {
self.clean_item_and_children(&next_child);
}
}
}
pub fn set_custom_data_direct<T: Any + Send + Sync + 'static>(&self, item_id: &TreeItemId, data: T) -> u64 {
let ptr = self.treectrl_ptr();
if ptr.is_null() {
return 0;
}
let existing_data_id = unsafe { ffi::wxd_TreeCtrl_GetItemData(ptr, item_id.as_ptr()) as u64 };
if existing_data_id != 0 {
let _ = remove_item_data(existing_data_id);
}
let data_id = store_item_data(data);
unsafe {
ffi::wxd_TreeCtrl_SetItemData(ptr, item_id.as_ptr(), data_id as i64);
}
data_id
}
}
impl WxWidget for TreeCtrl {
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 TreeCtrl {
unsafe fn get_event_handler_ptr(&self) -> *mut ffi::wxd_EvtHandler_t {
self.handle.get_ptr().unwrap_or(std::ptr::null_mut()) as *mut ffi::wxd_EvtHandler_t
}
}
impl crate::event::WindowEvents for TreeCtrl {}
impl crate::scrollable::WxScrollable for TreeCtrl {}
widget_builder!(
name: TreeCtrl,
parent_type: &'a dyn WxWidget,
style_type: TreeCtrlStyle,
fields: {},
build_impl: |slf| {
TreeCtrl::new_impl(
slf.parent.handle_ptr(),
slf.id,
slf.pos,
slf.size,
slf.style.bits()
)
}
);
impl TreeEvents for TreeCtrl {}
#[cfg(feature = "xrc")]
impl crate::xrc::XrcSupport for TreeCtrl {
unsafe fn from_xrc_ptr(ptr: *mut ffi::wxd_Window_t) -> Self {
TreeCtrl {
handle: WindowHandle::new(ptr),
}
}
}
impl crate::window::FromWindowWithClassName for TreeCtrl {
fn class_name() -> &'static str {
"wxTreeCtrl"
}
unsafe fn from_ptr(ptr: *mut ffi::wxd_Window_t) -> Self {
TreeCtrl {
handle: WindowHandle::new(ptr),
}
}
}