use std::ffi::CString;
use std::os::raw::c_char;
use crate::event::{Event, EventType, WxEvtHandler};
use crate::geometry::{Point, Size};
use crate::id::Id;
use crate::widgets::list_ctrl::ListColumnFormat;
use crate::window::{WindowHandle, WxWidget};
#[allow(unused_imports)]
use crate::window::Window;
use wxdragon_sys as ffi;
widget_style_enum!(
name: TreeListCtrlStyle,
doc: "Style flags for TreeListCtrl widget.",
variants: {
Default: 0, "Default style.",
Checkbox: 1, "Show checkboxes next to items.",
Three_State: 2, "Use 3-state checkboxes (checked, unchecked, undetermined)."
},
default_variant: Default
);
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CheckboxState {
Unchecked = 0,
Checked = 1,
Undetermined = 2,
}
impl From<CheckboxState> for i32 {
fn from(val: CheckboxState) -> Self {
val as i32
}
}
impl From<i32> for CheckboxState {
fn from(val: i32) -> Self {
match val {
0 => CheckboxState::Unchecked,
1 => CheckboxState::Checked,
2 => CheckboxState::Undetermined,
_ => CheckboxState::Unchecked,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TreeListItem {
id: i64,
}
impl TreeListItem {
pub fn new(id: i64) -> Self {
Self { id }
}
pub fn id(&self) -> i64 {
self.id
}
pub fn is_valid(&self) -> bool {
self.id != 0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TreeListCtrlEvent {
SelectionChanged,
ItemChecked,
ItemActivated,
ColumnSorted,
ItemExpanding,
ItemExpanded,
}
#[derive(Debug)]
pub struct TreeListCtrlEventData {
event: Event,
}
impl TreeListCtrlEventData {
pub fn new(event: Event) -> Self {
Self { event }
}
pub fn get_id(&self) -> i32 {
self.event.get_id()
}
pub fn get_item(&self) -> Option<TreeListItem> {
if self.event.is_null() {
return None;
}
let id = unsafe { ffi::wxd_TreeListEvent_GetItem(self.event._as_ptr()) };
if id != 0 { Some(TreeListItem::new(id)) } else { None }
}
pub fn get_column(&self) -> Option<i32> {
if self.event.is_null() {
return None;
}
let col = unsafe { ffi::wxd_TreeListEvent_GetColumn(self.event._as_ptr()) };
if col >= 0 { Some(col) } else { None }
}
pub fn get_string(&self) -> Option<String> {
self.event.get_string()
}
pub fn get_old_checked_state(&self) -> Option<CheckboxState> {
if self.event.is_null() {
return None;
}
let state = unsafe { ffi::wxd_TreeListEvent_GetOldCheckedState(self.event._as_ptr()) };
if state >= 0 { Some(CheckboxState::from(state)) } else { None }
}
pub fn is_checked(&self) -> Option<bool> {
self.event.is_checked()
}
pub fn skip(&self, skip: bool) {
self.event.skip(skip);
}
}
#[derive(Clone, Copy)]
pub struct TreeListCtrl {
handle: WindowHandle,
}
impl TreeListCtrl {
pub fn builder(parent: &dyn WxWidget) -> TreeListCtrlBuilder<'_> {
TreeListCtrlBuilder::new(parent)
}
fn new_impl(parent_ptr: *mut ffi::wxd_Window_t, id: Id, pos: Point, size: Size, style: i64) -> Self {
let ptr = unsafe { ffi::wxd_TreeListCtrl_Create(parent_ptr, id, pos.into(), size.into(), style as ffi::wxd_Style_t) };
if ptr.is_null() {
panic!("Failed to create TreeListCtrl widget");
}
TreeListCtrl {
handle: WindowHandle::new(ptr as *mut ffi::wxd_Window_t),
}
}
#[inline]
fn tree_list_ctrl_ptr(&self) -> *mut ffi::wxd_TreeListCtrl_t {
self.handle
.get_ptr()
.map(|p| p as *mut ffi::wxd_TreeListCtrl_t)
.unwrap_or(std::ptr::null_mut())
}
fn read_string_with_retry(mut getter: impl FnMut(*mut c_char, i32) -> i32) -> String {
let mut buffer: Vec<c_char> = vec![0; 1024];
let mut len = getter(buffer.as_mut_ptr(), buffer.len() as i32);
if len < 0 {
return String::new();
}
if len as usize >= buffer.len() {
buffer = vec![0; len as usize + 1];
len = getter(buffer.as_mut_ptr(), buffer.len() as i32);
if len < 0 {
return String::new();
}
}
let byte_slice = unsafe { std::slice::from_raw_parts(buffer.as_ptr() as *const u8, len as usize) };
String::from_utf8_lossy(byte_slice).to_string()
}
pub fn append_column(&self, text: &str, width: i32, align: ListColumnFormat) -> i32 {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return -1;
}
let c_text = CString::new(text).unwrap_or_default();
unsafe { ffi::wxd_TreeListCtrl_AppendColumn(ptr, c_text.as_ptr(), width, align.as_i32()) }
}
pub fn get_column_count(&self) -> i32 {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return 0;
}
unsafe { ffi::wxd_TreeListCtrl_GetColumnCount(ptr) }
}
pub fn set_column_width(&self, col: i32, width: i32) {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeListCtrl_SetColumnWidth(ptr, col, width) };
}
pub fn get_column_width(&self, col: i32) -> i32 {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return 0;
}
unsafe { ffi::wxd_TreeListCtrl_GetColumnWidth(ptr, col) }
}
pub fn delete_column(&self, col: u32) -> bool {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return false;
}
unsafe { ffi::wxd_TreeListCtrl_DeleteColumn(ptr, col) }
}
pub fn clear_columns(&self) {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeListCtrl_ClearColumns(ptr) };
}
pub fn width_for(&self, text: &str) -> i32 {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return 0;
}
let c_text = CString::new(text).unwrap_or_default();
unsafe { ffi::wxd_TreeListCtrl_WidthFor(ptr, c_text.as_ptr()) }
}
pub fn get_root_item(&self) -> TreeListItem {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return TreeListItem::new(0);
}
let id = unsafe { ffi::wxd_TreeListCtrl_GetRootItem(ptr) };
TreeListItem::new(id)
}
pub fn append_item(&self, parent: &TreeListItem, text: &str) -> Option<TreeListItem> {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return None;
}
let c_text = CString::new(text).unwrap_or_default();
let id = unsafe { ffi::wxd_TreeListCtrl_AppendItem(ptr, parent.id(), c_text.as_ptr()) };
if id != 0 { Some(TreeListItem::new(id)) } else { None }
}
pub fn insert_item(&self, parent: &TreeListItem, previous: &TreeListItem, text: &str) -> Option<TreeListItem> {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return None;
}
let c_text = CString::new(text).unwrap_or_default();
let id = unsafe { ffi::wxd_TreeListCtrl_InsertItem(ptr, parent.id(), previous.id(), c_text.as_ptr()) };
if id != 0 { Some(TreeListItem::new(id)) } else { None }
}
pub fn prepend_item(&self, parent: &TreeListItem, text: &str) -> Option<TreeListItem> {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return None;
}
let c_text = CString::new(text).unwrap_or_default();
let id = unsafe { ffi::wxd_TreeListCtrl_PrependItem(ptr, parent.id(), c_text.as_ptr()) };
if id != 0 { Some(TreeListItem::new(id)) } else { None }
}
pub fn delete_item(&self, item: &TreeListItem) {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeListCtrl_DeleteItem(ptr, item.id()) };
}
pub fn delete_all_items(&self) {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeListCtrl_DeleteAllItems(ptr) };
}
pub fn set_item_text(&self, item: &TreeListItem, col: i32, text: &str) {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return;
}
let c_text = CString::new(text).unwrap_or_default();
unsafe { ffi::wxd_TreeListCtrl_SetItemText(ptr, item.id(), col, c_text.as_ptr()) };
}
pub fn get_item_text(&self, item: &TreeListItem, col: i32) -> String {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return String::new();
}
unsafe { Self::read_string_with_retry(|buf, len| ffi::wxd_TreeListCtrl_GetItemText(ptr, item.id(), col, buf, len)) }
}
pub fn expand(&self, item: &TreeListItem) {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeListCtrl_Expand(ptr, item.id()) };
}
pub fn collapse(&self, item: &TreeListItem) {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeListCtrl_Collapse(ptr, item.id()) };
}
pub fn is_expanded(&self, item: &TreeListItem) -> bool {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return false;
}
unsafe { ffi::wxd_TreeListCtrl_IsExpanded(ptr, item.id()) }
}
pub fn get_selection(&self) -> Option<TreeListItem> {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return None;
}
let id = unsafe { ffi::wxd_TreeListCtrl_GetSelection(ptr) };
if id != 0 { Some(TreeListItem::new(id)) } else { None }
}
pub fn select_item(&self, item: &TreeListItem) {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeListCtrl_SelectItem(ptr, item.id()) };
}
pub fn unselect_all(&self) {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeListCtrl_UnselectAll(ptr) };
}
pub fn check_item(&self, item: &TreeListItem, state: CheckboxState) {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeListCtrl_CheckItem(ptr, item.id(), state.into()) };
}
pub fn get_checked_state(&self, item: &TreeListItem) -> CheckboxState {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return CheckboxState::Unchecked;
}
let state = unsafe { ffi::wxd_TreeListCtrl_GetCheckedState(ptr, item.id()) };
CheckboxState::from(state)
}
pub fn is_checked(&self, item: &TreeListItem) -> bool {
self.get_checked_state(item) == CheckboxState::Checked
}
pub fn check_item_recursively(&self, item: &TreeListItem, state: CheckboxState) {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeListCtrl_CheckItemRecursively(ptr, item.id(), state.into()) };
}
pub fn update_item_parent_state(&self, item: &TreeListItem) {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeListCtrl_UpdateItemParentState(ptr, item.id()) };
}
pub fn get_item_parent(&self, item: &TreeListItem) -> Option<TreeListItem> {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return None;
}
let id = unsafe { ffi::wxd_TreeListCtrl_GetItemParent(ptr, item.id()) };
if id != 0 { Some(TreeListItem::new(id)) } else { None }
}
pub fn get_first_child(&self, item: &TreeListItem) -> Option<TreeListItem> {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return None;
}
let id = unsafe { ffi::wxd_TreeListCtrl_GetFirstChild(ptr, item.id()) };
if id != 0 { Some(TreeListItem::new(id)) } else { None }
}
pub fn get_next_sibling(&self, item: &TreeListItem) -> Option<TreeListItem> {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return None;
}
let id = unsafe { ffi::wxd_TreeListCtrl_GetNextSibling(ptr, item.id()) };
if id != 0 { Some(TreeListItem::new(id)) } else { None }
}
pub fn get_next_item(&self, item: &TreeListItem) -> Option<TreeListItem> {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return None;
}
let id = unsafe { ffi::wxd_TreeListCtrl_GetNextItem(ptr, item.id()) };
if id != 0 { Some(TreeListItem::new(id)) } else { None }
}
pub fn get_first_item(&self) -> Option<TreeListItem> {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return None;
}
let id = unsafe { ffi::wxd_TreeListCtrl_GetFirstItem(ptr) };
if id != 0 { Some(TreeListItem::new(id)) } else { None }
}
pub fn set_item_image(&self, item: &TreeListItem, closed: i32, opened: i32) {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeListCtrl_SetItemImage(ptr, item.id(), closed, opened) };
}
pub fn get_selections(&self) -> Vec<TreeListItem> {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return Vec::new();
}
const MAX_SELECTIONS: usize = 1000;
let mut selections: Vec<i64> = vec![0; MAX_SELECTIONS];
let count = unsafe { ffi::wxd_TreeListCtrl_GetSelections(ptr, selections.as_mut_ptr(), MAX_SELECTIONS as u32) };
selections.truncate(count as usize);
selections.into_iter().map(TreeListItem::new).collect()
}
pub fn select(&self, item: &TreeListItem) {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeListCtrl_Select(ptr, item.id()) };
}
pub fn unselect(&self, item: &TreeListItem) {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeListCtrl_Unselect(ptr, item.id()) };
}
pub fn is_selected(&self, item: &TreeListItem) -> bool {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return false;
}
unsafe { ffi::wxd_TreeListCtrl_IsSelected(ptr, item.id()) }
}
pub fn select_all(&self) {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeListCtrl_SelectAll(ptr) };
}
pub fn ensure_visible(&self, item: &TreeListItem) {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeListCtrl_EnsureVisible(ptr, item.id()) };
}
pub fn uncheck_item(&self, item: &TreeListItem) {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeListCtrl_UncheckItem(ptr, item.id()) };
}
pub fn are_all_children_in_state(&self, item: &TreeListItem, state: CheckboxState) -> bool {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return false;
}
unsafe { ffi::wxd_TreeListCtrl_AreAllChildrenInState(ptr, item.id(), state as i32) }
}
pub fn set_sort_column(&self, col: u32, ascending: bool) {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return;
}
unsafe { ffi::wxd_TreeListCtrl_SetSortColumn(ptr, col, ascending) };
}
pub fn get_sort_column(&self) -> Option<(u32, bool)> {
let ptr = self.tree_list_ctrl_ptr();
if ptr.is_null() {
return None;
}
let mut col: u32 = 0;
let mut ascending: bool = true;
let has_sort = unsafe { ffi::wxd_TreeListCtrl_GetSortColumn(ptr, &mut col, &mut ascending) };
if has_sort { Some((col, ascending)) } else { None }
}
pub fn window_handle(&self) -> WindowHandle {
self.handle
}
}
impl WxWidget for TreeListCtrl {
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 TreeListCtrl {
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 TreeListCtrl {}
widget_builder!(
name: TreeListCtrl,
parent_type: &'a dyn WxWidget,
style_type: TreeListCtrlStyle,
fields: {},
build_impl: |slf| {
TreeListCtrl::new_impl(
slf.parent.handle_ptr(),
slf.id,
slf.pos,
slf.size,
slf.style.bits()
)
}
);
crate::implement_widget_local_event_handlers!(
TreeListCtrl,
TreeListCtrlEvent,
TreeListCtrlEventData,
SelectionChanged => selection_changed, EventType::TREELIST_SELECTION_CHANGED,
ItemChecked => item_checked, EventType::TREELIST_ITEM_CHECKED,
ItemActivated => item_activated, EventType::TREELIST_ITEM_ACTIVATED,
ColumnSorted => column_sorted, EventType::TREELIST_COLUMN_SORTED,
ItemExpanding => item_expanding, EventType::TREELIST_ITEM_EXPANDING,
ItemExpanded => item_expanded, EventType::TREELIST_ITEM_EXPANDED
);
#[cfg(feature = "xrc")]
impl crate::xrc::XrcSupport for TreeListCtrl {
unsafe fn from_xrc_ptr(ptr: *mut ffi::wxd_Window_t) -> Self {
TreeListCtrl {
handle: WindowHandle::new(ptr),
}
}
}
impl crate::window::FromWindowWithClassName for TreeListCtrl {
fn class_name() -> &'static str {
"wxTreeListCtrl"
}
unsafe fn from_ptr(ptr: *mut ffi::wxd_Window_t) -> Self {
TreeListCtrl {
handle: WindowHandle::new(ptr),
}
}
}