use crate::{Id, WxWidget};
use wxdragon_sys as ffi;
use super::enums::DataViewColumnFlags;
use super::renderer::DataViewIconTextRenderer;
use super::{
DataViewAlign, DataViewBitmapRenderer, DataViewCellMode, DataViewChoiceRenderer, DataViewColumn, DataViewDateRenderer,
DataViewItem, DataViewModel, DataViewProgressRenderer, DataViewSpinRenderer, DataViewTextRenderer, DataViewToggleRenderer,
VariantType,
};
use crate::color::Colour;
use crate::event::WxEvtHandler;
use crate::geometry::{Point, Size};
use crate::window::WindowHandle;
widget_style_enum!(
name: DataViewStyle,
doc: "Style flags for DataViewCtrl widgets.",
variants: {
Single: ffi::WXD_DV_SINGLE, "Single-selection mode.",
Multiple: ffi::WXD_DV_MULTIPLE, "Multiple-selection mode.",
RowLines: ffi::WXD_DV_ROW_LINES, "Display row dividers.",
HorizontalRules: ffi::WXD_DV_HORIZ_RULES, "Display horizontal rules.",
VerticalRules: ffi::WXD_DV_VERT_RULES, "Display vertical rules.",
VariableLineHeight: ffi::WXD_DV_VARIABLE_LINE_HEIGHT, "Enable variable line height.",
NoHeader: ffi::WXD_DV_NO_HEADER, "Hide column headers."
},
default_variant: Single
);
#[derive(Clone, Copy)]
pub struct DataViewCtrl {
handle: WindowHandle,
}
#[derive(Debug, Clone)]
pub struct SpinColumnConfig {
pub label: String,
pub model_column: usize,
pub width: i32,
pub align: DataViewAlign,
pub min: i32,
pub max: i32,
pub inc: i32,
pub flags: DataViewColumnFlags,
}
impl SpinColumnConfig {
pub fn new(label: &str, model_column: usize, min: i32, max: i32) -> Self {
Self {
label: label.to_string(),
model_column,
width: 80,
align: DataViewAlign::Left,
min,
max,
inc: 1,
flags: DataViewColumnFlags::Resizable,
}
}
pub fn with_width(mut self, width: i32) -> Self {
self.width = width;
self
}
pub fn with_align(mut self, align: DataViewAlign) -> Self {
self.align = align;
self
}
pub fn with_inc(mut self, inc: i32) -> Self {
self.inc = inc;
self
}
pub fn with_flags(mut self, flags: DataViewColumnFlags) -> Self {
self.flags = flags;
self
}
}
impl DataViewCtrl {
pub fn builder(parent: &dyn WxWidget) -> DataViewCtrlBuilder<'_> {
DataViewCtrlBuilder::new(parent)
}
fn new_impl(parent_ptr: *mut ffi::wxd_Window_t, id: i32, pos: Point, size: Size, style: i64) -> Self {
let ptr = unsafe {
ffi::wxd_DataViewCtrl_Create(
parent_ptr,
id as i64,
&pos as *const Point as *const ffi::wxd_Point,
&size as *const Size as *const ffi::wxd_Size,
style,
)
};
if ptr.is_null() {
panic!("Failed to create DataViewCtrl widget");
}
DataViewCtrl {
handle: WindowHandle::new(ptr),
}
}
#[inline]
fn dvc_ptr(&self) -> *mut ffi::wxd_Window_t {
self.handle.get_ptr().unwrap_or(std::ptr::null_mut())
}
pub fn window_handle(&self) -> WindowHandle {
self.handle
}
pub fn associate_model<M: DataViewModel>(&self, model: &M) -> bool {
let model_ptr = model.handle_ptr();
unsafe { ffi::wxd_DataViewCtrl_AssociateModel(self.dvc_ptr(), model_ptr) }
}
pub fn append_column(&self, column: &DataViewColumn) -> bool {
unsafe { ffi::wxd_DataViewCtrl_AppendColumn(self.dvc_ptr(), column.as_raw()) }
}
pub fn prepend_column(&self, column: &DataViewColumn) -> bool {
unsafe { ffi::wxd_DataViewCtrl_PrependColumn(self.dvc_ptr(), column.as_raw()) }
}
pub fn insert_column(&self, pos: usize, column: &DataViewColumn) -> bool {
unsafe { ffi::wxd_DataViewCtrl_InsertColumn(self.dvc_ptr(), pos as i64, column.as_raw()) }
}
pub fn select_row(&self, row: usize) -> bool {
unsafe { ffi::wxd_DataViewCtrl_SelectRow(self.dvc_ptr(), row as i64) }
}
pub fn get_selected_row(&self) -> Option<usize> {
let row = unsafe { ffi::wxd_DataViewCtrl_GetSelectedRow(self.dvc_ptr()) };
if row >= 0 { Some(row as usize) } else { None }
}
pub fn unselect_all(&self) {
unsafe { ffi::wxd_DataViewCtrl_UnselectAll(self.dvc_ptr()) }
}
pub fn append_text_column(
&self,
label: &str,
model_column: usize,
width: i32,
align: DataViewAlign,
flags: DataViewColumnFlags,
) -> bool {
let renderer = DataViewTextRenderer::new(VariantType::String, DataViewCellMode::Inert, align);
let column = DataViewColumn::new(label, &renderer, model_column, width, align, flags);
self.append_column(&column)
}
pub fn append_toggle_column(
&self,
label: &str,
model_column: usize,
width: i32,
align: DataViewAlign,
flags: DataViewColumnFlags,
) -> bool {
let renderer = DataViewToggleRenderer::new(VariantType::Bool, DataViewCellMode::Activatable, align);
let column = DataViewColumn::new(label, &renderer, model_column, width, align, flags);
self.append_column(&column)
}
pub fn append_progress_column(&self, label: &str, model_column: usize, width: i32, flags: DataViewColumnFlags) -> bool {
let renderer = DataViewProgressRenderer::new(VariantType::Int32, DataViewCellMode::Inert);
let column = DataViewColumn::new(label, &renderer, model_column, width, DataViewAlign::Center, flags);
self.append_column(&column)
}
pub fn append_bitmap_column(
&self,
label: &str,
model_column: usize,
width: i32,
align: DataViewAlign,
flags: DataViewColumnFlags,
) -> bool {
let renderer = DataViewBitmapRenderer::new(DataViewCellMode::Inert, align);
let column = DataViewColumn::new(label, &renderer, model_column, width, align, flags);
self.append_column(&column)
}
pub fn append_date_column(
&self,
label: &str,
model_column: usize,
width: i32,
align: DataViewAlign,
flags: DataViewColumnFlags,
) -> bool {
let renderer = DataViewDateRenderer::new(VariantType::DateTime, DataViewCellMode::Activatable, align);
let column = DataViewColumn::new(label, &renderer, model_column, width, align, flags);
self.append_column(&column)
}
pub fn append_choice_column(
&self,
label: &str,
model_column: usize,
width: i32,
align: DataViewAlign,
choices: &[&str],
flags: DataViewColumnFlags,
) -> bool {
let renderer = DataViewChoiceRenderer::new(VariantType::String, choices, DataViewCellMode::Editable, align);
let column = DataViewColumn::new(label, &renderer, model_column, width, align, flags);
self.append_column(&column)
}
pub fn append_spin_column(&self, config: SpinColumnConfig) -> bool {
let renderer = DataViewSpinRenderer::new(
VariantType::Int64,
DataViewCellMode::Editable,
config.align,
config.min,
config.max,
config.inc,
);
let column = DataViewColumn::new(
&config.label,
&renderer,
config.model_column,
config.width,
config.align,
config.flags,
);
self.append_column(&column)
}
pub fn append_icon_text_column(
&self,
label: &str,
model_column: usize,
width: i32,
align: DataViewAlign,
flags: DataViewColumnFlags,
) -> bool {
let renderer = DataViewIconTextRenderer::new(VariantType::String, DataViewCellMode::Inert, align);
let column = DataViewColumn::new(label, &renderer, model_column, width, align, flags);
self.append_column(&column)
}
pub fn get_column_count(&self) -> usize {
unsafe { ffi::wxd_DataViewCtrl_GetColumnCount(self.dvc_ptr()) as usize }
}
pub fn get_column(&self, pos: usize) -> Option<DataViewColumn> {
if pos >= self.get_column_count() {
return None; }
let raw_col = unsafe { ffi::wxd_DataViewCtrl_GetColumn(self.dvc_ptr(), pos as u32) };
if raw_col.is_null() {
None
} else {
unsafe { Some(DataViewColumn::from_ptr(raw_col)) }
}
}
pub fn get_column_position(&self, column: &DataViewColumn) -> i32 {
unsafe { ffi::wxd_DataViewCtrl_GetColumnPosition(self.dvc_ptr(), column.as_raw()) }
}
pub fn clear_columns(&self) -> bool {
unsafe { ffi::wxd_DataViewCtrl_ClearColumns(self.dvc_ptr()) }
}
pub fn select(&self, item: &DataViewItem) {
unsafe { ffi::wxd_DataViewCtrl_Select(self.dvc_ptr(), **item) };
}
pub fn unselect(&self, item: &DataViewItem) {
unsafe { ffi::wxd_DataViewCtrl_Unselect(self.dvc_ptr(), **item) };
}
pub fn select_all(&self) {
unsafe { ffi::wxd_DataViewCtrl_SelectAll(self.dvc_ptr()) }
}
pub fn is_selected(&self, item: &DataViewItem) -> bool {
unsafe { ffi::wxd_DataViewCtrl_IsSelected(self.dvc_ptr(), **item) }
}
pub fn get_selected_items_count(&self) -> usize {
unsafe { ffi::wxd_DataViewCtrl_GetSelectedItemsCount(self.dvc_ptr()) as usize }
}
pub fn has_selection(&self) -> bool {
self.get_selected_items_count() > 0
}
pub fn get_selections(&self) -> Vec<DataViewItem> {
let count = self.get_selected_items_count();
if count == 0 {
return Vec::new();
}
let mut items = Vec::with_capacity(count);
let mut items_raw: Vec<*const ffi::wxd_DataViewItem_t> = vec![std::ptr::null(); count];
let ptr = items_raw.as_mut_ptr();
unsafe { ffi::wxd_DataViewCtrl_GetSelections(self.dvc_ptr(), ptr, count as u32) };
for raw_ptr in items_raw {
if !raw_ptr.is_null() {
items.push(DataViewItem::from(raw_ptr));
}
}
items
}
pub fn set_selections(&self, items: &[DataViewItem]) {
let items_raw: Vec<_> = items.iter().map(|item| **item).collect();
unsafe { ffi::wxd_DataViewCtrl_SetSelections(self.dvc_ptr(), items_raw.as_ptr(), items_raw.len() as u32) };
}
pub fn get_current_item(&self) -> Option<DataViewItem> {
let item_ptr = unsafe { ffi::wxd_DataViewCtrl_GetCurrentItem(self.dvc_ptr()) };
if item_ptr.is_null() {
None
} else {
Some(DataViewItem::from(item_ptr))
}
}
pub fn get_nth_child(&self, parent: &DataViewItem, pos: u32) -> DataViewItem {
let item = unsafe { ffi::wxd_DataViewCtrl_GetNthChild(self.dvc_ptr(), **parent, pos) };
DataViewItem::from(item)
}
pub fn expand(&self, item: &DataViewItem) {
unsafe { ffi::wxd_DataViewCtrl_Expand(self.dvc_ptr(), **item) };
}
pub fn ensure_visible(&self, item: &DataViewItem) {
unsafe { ffi::wxd_DataViewCtrl_EnsureVisible(self.dvc_ptr(), **item) };
}
pub fn get_selection(&self) -> Option<DataViewItem> {
let item_ptr = unsafe { ffi::wxd_DataViewCtrl_GetSelection(self.dvc_ptr()) };
if item_ptr.is_null() {
None
} else {
Some(DataViewItem::from(item_ptr))
}
}
pub fn set_current_item(&self, item: &DataViewItem) {
unsafe { ffi::wxd_DataViewCtrl_SetCurrentItem(self.dvc_ptr(), **item) }
}
pub fn get_indent(&self) -> i32 {
unsafe { ffi::wxd_DataViewCtrl_GetIndent(self.dvc_ptr()) }
}
pub fn set_indent(&self, indent: i32) {
unsafe { ffi::wxd_DataViewCtrl_SetIndent(self.dvc_ptr(), indent) }
}
pub fn get_expander_column(&self) -> Option<DataViewColumn> {
let col_ptr = unsafe { ffi::wxd_DataViewCtrl_GetExpanderColumn(self.dvc_ptr()) };
if col_ptr.is_null() {
None
} else {
Some(unsafe { DataViewColumn::from_ptr(col_ptr) })
}
}
pub fn set_expander_column(&self, column: &DataViewColumn) {
unsafe { ffi::wxd_DataViewCtrl_SetExpanderColumn(self.dvc_ptr(), column.as_raw()) }
}
pub fn set_row_height(&self, height: i32) -> bool {
unsafe { ffi::wxd_DataViewCtrl_SetRowHeight(self.dvc_ptr(), height) }
}
pub fn set_alternate_row_colour(&self, colour: &Colour) -> bool {
let colour_raw = colour.to_raw();
unsafe { ffi::wxd_DataViewCtrl_SetAlternateRowColour(self.dvc_ptr(), &colour_raw) }
}
pub fn clear_sorting(&self) {
unsafe { ffi::wxd_DataViewCtrl_ClearSorting(self.dvc_ptr()) }
}
pub fn set_sorting_column(&self, column_index: usize, ascending: bool) -> bool {
unsafe { ffi::wxd_DataViewCtrl_SetSortingColumn(self.dvc_ptr(), column_index as i32, ascending) }
}
pub fn sorting_state(&self) -> Option<(usize, bool)> {
let mut col: i32 = -1;
let mut asc: bool = true;
let ok = unsafe { ffi::wxd_DataViewCtrl_GetSortingState(self.dvc_ptr(), &mut col, &mut asc) };
if ok && col >= 0 { Some((col as usize, asc)) } else { None }
}
}
impl WxWidget for DataViewCtrl {
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 DataViewCtrl {
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 DataViewCtrl {}
widget_builder!(
name: DataViewCtrl,
parent_type: &'a dyn WxWidget,
style_type: DataViewStyle,
fields: {},
build_impl: |slf| {
DataViewCtrl::new_impl(
slf.parent.handle_ptr(),
slf.id,
slf.pos,
slf.size,
slf.style.bits(),
)
}
);
impl crate::widgets::dataview::DataViewEventHandler for DataViewCtrl {}
impl crate::widgets::dataview::DataViewTreeEventHandler for DataViewCtrl {}