use std::ffi::CStr; use std::marker::PhantomData; use bitflags::bitflags; use crate::sys; use crate::{Id, ImColor32, Ui}; bitflags! { /// Flags passed to `begin_table` methods. /// /// Important! Sizing policies have complex and subtle side effects, more so than you would expect. /// Read comments/demos carefully + experiment with live demos to get acquainted with them. /// - The DEFAULT sizing policies are: /// - Default to [SizingFixedFit] if [ScrollX] is on, or if host window has (WindowFlags::AlwaysAutoResize)[crate::WindowFlags::AlwaysAutoResize]. /// - Default to [SizingStretchSame] if [ScrollX] is off. /// - When [ScrollX] is off: /// - Table defaults to [SizingStretchSame] -> all Columns defaults to [TableColumnFlags::WidthStretch] with same weight. /// - Columns sizing policy allowed: [Stretch] (default), [Fixed]/Auto. /// - [Fixed] Columns will generally obtain their requested width (unless the table cannot fit them all). /// - [Stretch] Columns will share the remaining width. /// - Mixed [Fixed]/[Stretch] columns is possible but has various side-effects on resizing behaviors. /// The typical use of mixing sizing policies is: any number of LEADING [Fixed] columns, followed by one or two TRAILING [Stretch] columns. /// (this is because the visible order of columns have subtle but necessary effects on how they react to manual resizing). /// - When [ScrollX] is on: /// - Table defaults to [SizingFixedFit] -> all Columns defaults to [TableColumnFlags::WidthFixed] /// - Columns sizing policy allowed: [Fixed]/Auto mostly. /// - [Fixed] Columns can be enlarged as needed. Table will show an horizontal scrollbar if needed. /// - When using auto-resizing (non-resizable) fixed columns, querying the content width to use item right-alignment e.g. SetNextItemWidth(-FLT_MIN) doesn't make sense, would create a feedback loop. /// - Using [Stretch] columns OFTEN DOES NOT MAKE SENSE if [ScrollX] is on, UNLESS you have specified a value for `inner_width` in BeginTable(). /// If you specify a value for `inner_width` then effectively the scrolling space is known and [Stretch] or mixed [Fixed]/[Stretch] columns become meaningful again. /// - Read on documentation at the top of imgui_tables.cpp for more details. #[repr(transparent)] pub struct TableFlags: u32 { // Features /// Enable resizing columns. const RESIZABLE = sys::ImGuiTableFlags_Resizable; /// Enable reordering columns in header row, though you must set up a header row /// with `begin_table_header` or `table_setup_column`. const REORDERABLE =sys::ImGuiTableFlags_Reorderable; /// Enable hiding/disabling columns in context menu. const HIDEABLE = sys::ImGuiTableFlags_Hideable; /// Enable sorting. See `table_get_sort_specs` to object sort specs. Also see [SortMulti] /// and [SortTristate]. const SORTABLE = sys::ImGuiTableFlags_Sortable; /// Disable persisting columns order, width, and sort settings in the .ini file. const NO_SAVED_SETTINGS = sys::ImGuiTableFlags_NoSavedSettings; /// Right-click on columns body/contents will display table context menu. /// By default you can only right click in a headers row. const CONTEXT_MENU_IN_BODY = sys::ImGuiTableFlags_ContextMenuInBody; // Decorations /// Set each RowBg color with [table_row_bg] or [table_row_bg_alt] (equivalent of calling /// `table_set_bg_color` with `ROW_BG0` on each row manually) const ROW_BG = sys::ImGuiTableFlags_RowBg; /// Draw horizontal borders between rows. const BORDERS_INNER_H = sys::ImGuiTableFlags_BordersInnerH; /// Draw horizontal borders at the top and bottom. const BORDERS_OUTER_H = sys::ImGuiTableFlags_BordersOuterH; /// Draw vertical borders between columns. const BORDERS_INNER_V = sys::ImGuiTableFlags_BordersInnerV; /// Draw vertical borders on the left and right sides. const BORDERS_OUTER_V = sys::ImGuiTableFlags_BordersOuterV; /// Draw all horizontal borders (this is just [BORDERS_INNER_H] | [BORDERS_OUTER_H]). const BORDERS_H = sys::ImGuiTableFlags_BordersH; /// Draw all vertical borders (this is just [BORDERS_INNER_V] | [BORDERS_OUTER_V]). const BORDERS_V = sys::ImGuiTableFlags_BordersV; /// Draw all inner borders (this is just [BORDERS_INNER_H] | [BORDERS_INNER_V]). const BORDERS_INNER = sys::ImGuiTableFlags_BordersInner; /// Draw all outer borders (this is just [BORDERS_OUTER_H] | [BORDERS_OUTER_V]). const BORDERS_OUTER = sys::ImGuiTableFlags_BordersOuter; /// Draw all borders (this is just [BORDERS_INNER] | [BORDERS_OUTER]). const BORDERS = sys::ImGuiTableFlags_Borders; /// **ALPHA** Disable vertical borders in columns Body (borders will always appears in Headers). /// May move to Style const NO_BORDERS_IN_BODY = sys::ImGuiTableFlags_NoBordersInBody; /// **ALPHA** Disable vertical borders in columns Body until hovered for resize (borders will always appears in Headers). /// May move to style const NO_BORDERS_IN_BODY_UNTIL_RESIZE = sys::ImGuiTableFlags_NoBordersInBodyUntilResize; // Sizing Policy (read above for defaults) /// Columns default to [WidthFixed] or [WidthAuto] (if resizable or not resizable), /// matching contents width. const SIZING_FIXED_FIT = sys::ImGuiTableFlags_SizingFixedFit; /// Columns default to [WidthFixed] or [WidthAuto] (if resizable or not resizable), /// matching the maximum contents width of all columns. /// Implicitly enable [NoKeepColumnsVisible]. const SIZING_FIXED_SAME = sys::ImGuiTableFlags_SizingFixedSame; /// Columns default to [WidthStretch] with default weights proportional to each columns /// contents widths. const SIZING_STRETCH_PROP = sys::ImGuiTableFlags_SizingStretchProp; /// Columns default to [WidthStretch] with default weights all equal, unless overridden by /// a column's `TableHeader`. const SIZING_STRETCH_SAME = sys::ImGuiTableFlags_SizingStretchSame; // Sizing Extra Options /// Make outer width auto-fit to columns, overriding outer_size.x value. Only available when /// [ScrollX]/[ScrollY] are disabled and [Stretch] columns are not used. const NO_HOST_EXTEND_X = sys::ImGuiTableFlags_NoHostExtendX; /// Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). /// Only available when [ScrollX]/[ScrollY] are disabled. /// Data below the limit will be clipped and not visible. const NO_HOST_EXTEND_Y = sys::ImGuiTableFlags_NoHostExtendY; /// Disable keeping column always minimally visible when [ScrollX] is off and table /// gets too small. Not recommended if columns are resizable. const NO_KEEP_COLUMNS_VISIBLE = sys::ImGuiTableFlags_NoKeepColumnsVisible; /// Disable distributing remainder width to stretched columns (width allocation on a 100-wide /// table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). /// With larger number of columns, resizing will appear to be less smooth. const PRECISE_WIDTHS = sys::ImGuiTableFlags_PreciseWidths; // Clipping /// Disable clipping rectangle for every individual columns (reduce draw command count, items will /// be able to overflow into other columns). Generally incompatible with [table_setup_scroll_freeze]. const NO_CLIP = sys::ImGuiTableFlags_NoClip; // Padding /// Default if [BordersOuterV] is on. Enable outer-most padding. Generally desirable if you have headers. const PAD_OUTER_X = sys::ImGuiTableFlags_PadOuterX; /// Default if [BordersOuterV] is off. Disable outer-most padding. const NO_PAD_OUTER_X = sys::ImGuiTableFlags_NoPadOuterX; /// Disable inner padding between columns (double inner padding if [BordersOuterV] is on, single /// inner padding if BordersOuterV is off). const NO_PAD_INNER_X = sys::ImGuiTableFlags_NoPadInnerX; // Scrolling /// Enable horizontal scrolling. Require 'outer_size' parameter of [begin_table] to specify the /// container size. Changes default sizing policy. Because this create a child window, /// [ScrollY] is currently generally recommended when using [ScrollX]. const SCROLL_X = sys::ImGuiTableFlags_ScrollX; /// Enable vertical scrolling. Require 'outer_size' parameter of [begin_table] to specify the /// container size. const SCROLL_Y = sys::ImGuiTableFlags_ScrollY; // Sorting /// Hold shift when clicking headers to sort on multiple column. [table_get_sort_specs] may return specs where `[spec_count] > 1`. const SORT_MULTI = sys::ImGuiTableFlags_SortMulti; /// Allow no sorting, disable default sorting. `table_get_sort_specs` may return specs where `[specs_count] == 0`. const SORT_TRISTATE = sys::ImGuiTableFlags_SortTristate; } } bitflags! { /// Flags for [table_next_row_with_flags]. #[repr(transparent)] pub struct TableRowFlags: u32 { /// Identify header row (set default background color + width of its contents /// accounted different for auto column width) const HEADERS = sys::ImGuiTableRowFlags_Headers; } } bitflags! { /// Flags for [TableColumnSetup] and [table_setup_column_with]. #[repr(transparent)] #[derive(Default)] pub struct TableColumnFlags: u32 { // Input configuration flags /// Default as a hidden/disabled column. const DEFAULT_HIDE = sys::ImGuiTableColumnFlags_DefaultHide; /// Default as a sorting column. const DEFAULT_SORT = sys::ImGuiTableColumnFlags_DefaultSort; /// Column will stretch. Preferable with horizontal scrolling disabled (default /// if table sizing policy is [ImGuiTableFlags::SizingStretchSame] or /// [ImGuiTableFlags::SizingStretchProp]). const WIDTH_STRETCH = sys::ImGuiTableColumnFlags_WidthStretch; /// Column will not stretch. Preferable with horizontal scrolling enabled (default /// if table sizing policy is [ImGuiTableFlags::SizingFixedFit] and table is resizable). const WIDTH_FIXED = sys::ImGuiTableColumnFlags_WidthFixed; /// Disable manual resizing. const NO_RESIZE = sys::ImGuiTableColumnFlags_NoResize; /// Disable manual reordering this column, this will also prevent other columns from /// crossing over this column. const NO_REORDER = sys::ImGuiTableColumnFlags_NoReorder; /// Disable ability to hide/disable this column. const NO_HIDE = sys::ImGuiTableColumnFlags_NoHide; /// Disable clipping for this column (all [NO_CLIP] columns will render in a same /// draw command). const NO_CLIP = sys::ImGuiTableColumnFlags_NoClip; /// Disable ability to sort on this field (even if [ImGuiTableFlags::Sortable] is /// set on the table). const NO_SORT = sys::ImGuiTableColumnFlags_NoSort; /// Disable ability to sort in the ascending direction. const NO_SORT_ASCENDING = sys::ImGuiTableColumnFlags_NoSortAscending; /// Disable ability to sort in the descending direction. const NO_SORT_DESCENDING = sys::ImGuiTableColumnFlags_NoSortDescending; /// Disable header text width contribution to automatic column width. const NO_HEADER_WIDTH = sys::ImGuiTableColumnFlags_NoHeaderWidth; /// Make the initial sort direction Ascending when first sorting on this column (default). const PREFER_SORT_ASCENDING = sys::ImGuiTableColumnFlags_PreferSortAscending; /// Make the initial sort direction Descending when first sorting on this column. const PREFER_SORT_DESCENDING = sys::ImGuiTableColumnFlags_PreferSortDescending; /// Use current Indent value when entering cell (default for column 0). const INDENT_ENABLE = sys::ImGuiTableColumnFlags_IndentEnable; /// Ignore current Indent value when entering cell (default for columns > 0). /// Indentation changes _within_ the cell will still be honored. const INDENT_DISABLE = sys::ImGuiTableColumnFlags_IndentDisable; // Output status flags, read-only via [table_get_column_flags] /// Status: is enabled == not hidden by user/api (referred to as "Hide" in /// [DefaultHide] and [NoHide]) flags. const IS_ENABLED = sys::ImGuiTableColumnFlags_IsEnabled; /// Status: is visible == is enabled AND not clipped by scrolling. const IS_VISIBLE = sys::ImGuiTableColumnFlags_IsVisible; /// Status: is currently part of the sort specs const IS_SORTED = sys::ImGuiTableColumnFlags_IsSorted; /// Status: is hovered by mouse const IS_HOVERED = sys::ImGuiTableColumnFlags_IsHovered; } } bitflags! { /// Enum for [table_set_bg_color]. /// Background colors are rendering in 3 layers: /// - Layer 0: draw with RowBg0 color if set, otherwise draw with ColumnBg0 if set. /// - Layer 1: draw with RowBg1 color if set, otherwise draw with ColumnBg1 if set. /// - Layer 2: draw with CellBg color if set. /// The purpose of the two row/columns layers is to let you decide if a background color /// changes should override or blend with the existing color. /// When using [TableFlags::RowBg] on the table, each row has the RowBg0 color automatically /// set for odd/even rows. /// If you set the color of RowBg0 target, your color will override the existing RowBg0 color. /// If you set the color of RowBg1 or ColumnBg1 target, your color will blend over the RowBg0 color. #[repr(transparent)] pub struct TableBgTarget: u32 { /// Set row background color 0 (generally used for background, automatically set when /// [TableFlags::RowBg] is used) const ROW_BG0 = sys::ImGuiTableBgTarget_RowBg0; /// Set row background color 1 (generally used for selection marking) const ROW_BG1 = sys::ImGuiTableBgTarget_RowBg1; /// Set cell background color (top-most color) const CELL_BG = sys::ImGuiTableBgTarget_CellBg; } } #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum TableSortDirection { Ascending, Descending, } impl<'ui> Ui<'ui> { /// Begins a table with no flags and with standard sizing contraints. /// /// This does no work on styling the headers (the top row) -- see either /// [begin_table_header](Self::begin_table_header) or the more complex /// [table_setup_column](Self::table_setup_column). /// /// Nb: we take `column` as a usize, but it will be converted with `as i32` to an i32. /// If this makes a difference to you, you are probably trying to make too many columns. #[must_use = "if return is dropped immediately, table is ended immediately."] pub fn begin_table( &self, str_id: impl AsRef, column_count: usize, ) -> Option> { self.begin_table_with_flags(str_id, column_count, TableFlags::empty()) } /// Begins a table with flags and standard sizing contraints. /// /// This does no work on styling the headers (the top row) -- see either /// [begin_table_header](Self::begin_table_header) or the more complex /// [table_setup_column](Self::table_setup_column). /// /// Nb: we take `column` as a usize, but it will be converted with `as i32` to an i32. /// If this makes a difference to you, you are probably trying to make too many columns. #[must_use = "if return is dropped immediately, table is ended immediately."] pub fn begin_table_with_flags( &self, str_id: impl AsRef, column_count: usize, flags: TableFlags, ) -> Option> { self.begin_table_with_sizing(str_id, column_count, flags, [0.0, 0.0], 0.0) } /// Begins a table with all flags and sizing contraints. This is the base method, /// and gives users the most flexibility. /// /// This does no work on styling the headers (the top row) -- see either /// [begin_table_header](Self::begin_table_header) or the more complex /// [table_setup_column](Self::table_setup_column). /// /// Nb: we take `column` as a usize, but it will be converted with `as i32` to an i32. /// If this makes a difference to you, you are probably trying to make too many columns. #[must_use = "if return is dropped immediately, table is ended immediately."] pub fn begin_table_with_sizing( &self, str_id: impl AsRef, column: usize, flags: TableFlags, outer_size: [f32; 2], inner_width: f32, ) -> Option> { let should_render = unsafe { sys::igBeginTable( self.scratch_txt(str_id), column as i32, flags.bits() as i32, outer_size.into(), inner_width, ) }; // todo: once msrv is 1.54, convert this to .then(||) if should_render { Some(TableToken::new(self)) } else { None } } /// Begins a table with no flags and with standard sizing contraints. /// /// Takes an array of table header information, the length of which determines /// how many columns will be created. #[cfg(feature = "min-const-generics")] #[must_use = "if return is dropped immediately, table is ended immediately."] pub fn begin_table_header<'a, Name: AsRef, const N: usize>( &self, str_id: impl AsRef, column_data: [TableColumnSetup<'a, Name>; N], ) -> Option> { self.begin_table_header_with_flags(str_id, column_data, TableFlags::empty()) } /// Begins a table with flags and standard sizing contraints. /// /// Takes an array of table header information, the length of which determines /// how many columns will be created. #[cfg(feature = "min-const-generics")] #[must_use = "if return is dropped immediately, table is ended immediately."] pub fn begin_table_header_with_flags<'a, Name: AsRef, const N: usize>( &self, str_id: impl AsRef, column_data: [TableColumnSetup<'a, Name>; N], flags: TableFlags, ) -> Option> { self.begin_table_header_with_sizing(str_id, column_data, flags, [0.0, 0.0], 0.0) } /// Begins a table with all flags and sizing contraints. This is the base method, /// and gives users the most flexibility. /// Takes an array of table header information, the length of which determines /// how many columns will be created. #[cfg(feature = "min-const-generics")] #[must_use = "if return is dropped immediately, table is ended immediately."] pub fn begin_table_header_with_sizing<'a, Name: AsRef, const N: usize>( &self, str_id: impl AsRef, column_data: [TableColumnSetup<'a, Name>; N], flags: TableFlags, outer_size: [f32; 2], inner_width: f32, ) -> Option> { self.begin_table_with_sizing(str_id, N, flags, outer_size, inner_width) .map(|data| { for value in column_data { self.table_setup_column_with(value); } self.table_headers_row(); data }) } /// Moves a table to the next row (ie, down) with no flags, /// and with the next row having a standard computed height. /// /// If your table was made with [begin_table], this **must** be called /// before rendering any cells (along with [table_next_column]). /// If your table was made with [begin_table_header], this does not need to be called, /// though [table_next_column] still should be. /// /// [begin_table]: Self::begin_table /// [begin_table_header]: Self::begin_table_header /// [table_next_column]: Self::table_next_column #[inline] pub fn table_next_row(&self) { self.table_next_row_with_flags(TableRowFlags::empty()); } /// Moves a table to the next row (ie, down), with the given flags, /// and with the next row having a standard computed height. /// /// Setting a flag here will make the next row a "header" now, which may /// require setup of column data. /// /// See [table_next_row](Self::table_next_row) for information on how moving rows work. To set the row /// with a given height, see [table_next_row_with_height](Self::table_next_row_with_height). #[inline] pub fn table_next_row_with_flags(&self, flags: TableRowFlags) { self.table_next_row_with_height(flags, 0.0); } /// Moves a table to the next row (ie, down), with the given flags, /// and with the given minimum height. /// /// See [table_next_row](Self::table_next_row) for information on how moving rows work. #[inline] pub fn table_next_row_with_height(&self, flags: TableRowFlags, min_row_height: f32) { unsafe { sys::igTableNextRow(flags.bits() as i32, min_row_height); } } /// Moves onto the next column. If at `column_count`, this will move to the next row. /// In this way, you can use this function as an iterator over each cell in the table. /// /// # Example /// ```no_run /// # let mut ctx = imgui::Context::create(); /// # { let ui = ctx.frame(); /// if let Some(_t) = ui.begin_table("Basic-Table", 2) { /// // we have to call next_row because we didn't make headers.. /// ui.table_next_row(); /// /// // you always have to call this to start... /// // take advantage of this in loops! /// ui.table_next_column(); /// ui.text("x: 0, y: 0"); /// /// ui.table_next_column(); /// ui.text("x: 1, y: 0"); /// /// // notice that we go down a row here too. /// ui.table_next_column(); /// ui.text("x: 0, y: 1"); /// /// ui.table_next_column(); /// ui.text("x: 1, y: 1"); /// } /// # }; /// ``` /// /// This functions returns true if the given column is **visible.** It is not /// marked as must use, as you can still render commands into the not-visible column, /// though you can choose to not as an optimization. pub fn table_next_column(&self) -> bool { unsafe { sys::igTableNextColumn() } } /// Moves onto the given column. /// /// # Example /// ```no_run /// # let mut ctx = imgui::Context::create(); /// # { let ui = ctx.frame(); /// if let Some(_t) = ui.begin_table("Basic-Table", 2) { /// // we have to call next_row because we didn't make headers.. /// ui.table_next_row(); /// /// for i in 0..2 { /// ui.table_set_column_index(i); /// ui.text(format!("x: {}", i)); /// } /// /// // oops I just remembered, i need to add something on idx 0! /// ui.table_set_column_index(0); /// // if i uncomment this line, we'll write on top of our previous "x: 0" /// // line: /// // ui.text("hello from the future on top of the past"); /// // so we do a .newline(); /// ui.new_line(); /// ui.text("hello from the future"); /// /// // imgui will understand this and row spacing will be adjusted automatically. /// } /// # }; /// ``` /// /// This functions returns true if the given column is **visible.** It is not /// marked as must use, as you can still render commands into the not-visible column, /// though you can choose to not as an optimization. /// /// # Panics /// If `column_index >= ui.table_columm_count`, this function will panic. In `debug` releases, /// we will panic on the Rust side, for a nicer error message, though in release, we will /// panic in C++, which will result in an ugly stack overflow. pub fn table_set_column_index(&self, column_index: usize) -> bool { #[cfg(debug_assertions)] { let size = self.table_column_count() as usize; if column_index >= size { panic!( "column_index >= self.table_get_column_count().\ Requested {}, but only have {} columns.", column_index, size ); } } unsafe { sys::igTableSetColumnIndex(column_index as i32) } } /// Specify label per column, with no flags and default sizing. You can avoid calling /// this method entirely by using [begin_table_header](Self::begin_table_header). /// /// # Example /// ```no_run /// # let mut ctx = imgui::Context::create(); /// # { let ui = ctx.frame(); /// if let Some(_t) = ui.begin_table("My Table", 2) { /// ui.table_setup_column("One"); /// ui.table_setup_column("Two"); /// ui.table_setup_column("Three"); /// ui.table_headers_row(); /// /// // call next_column/set_column_index and proceed like normal. /// // the above code is the equivalent of just using `begin_table_header` /// // but does allow for some columns to have headers and others to not /// } /// # }; /// ``` /// /// Along with [table_headers_row](Self::table_headers_row), this method is used to create a header /// row and automatically submit a table header for each column. /// Headers are required to perform: reordering, sorting, and opening the context menu (though, /// the context menu can also be made available in columns body using [TableFlags::CONTEXT_MENU_IN_BODY]. pub fn table_setup_column(&self, str_id: impl AsRef) { self.table_setup_column_with(TableColumnSetup::new(str_id)) } /// Specify label per column, with data given in [TableColumnSetup]. You can avoid calling /// this method entirely by using [begin_table_header](Self::begin_table_header). /// /// See [table_setup_column](Self::table_setup_column) for an example of how to setup columns /// yourself. /// /// Along with [table_headers_row](Self::table_headers_row), this method is used to create a header /// row and automatically submit a table header for each column. /// Headers are required to perform: reordering, sorting, and opening the context menu (though, /// the context menu can also be made available in columns body using [TableFlags::CONTEXT_MENU_IN_BODY]. pub fn table_setup_column_with>(&self, data: TableColumnSetup<'_, N>) { unsafe { sys::igTableSetupColumn( self.scratch_txt(data.name), data.flags.bits() as i32, data.init_width_or_weight, data.user_id.as_imgui_id(), ) } } /// Locks columns/rows so they stay visible when scrolled. Generally, you will be calling this /// so that the header column is always visible (though go wild if you want). You can avoid /// calling this entirely by passing `true` to [begin_table_header](Self::begin_table_header). /// /// # Example /// ```no_run /// # let mut ctx = imgui::Context::create(); /// # { let ui = ctx.frame(); /// const COLUMN_COUNT: usize = 3; /// if let Some(_t) = ui.begin_table("scroll-freeze-example", COLUMN_COUNT) { /// // locks the header row. Notice how we need to call it BEFORE `table_headers_row`. /// ui.table_setup_scroll_freeze(1, COLUMN_COUNT); /// ui.table_setup_column("One"); /// ui.table_setup_column("Two"); /// ui.table_setup_column("Three"); /// ui.table_headers_row(); /// } /// # }; /// ``` /// /// Nb: we take `locked_columns` and `locked_rows` as a `usize`, but it will be converted /// with `as i32` to an i32. If this makes a difference to you, you are probably /// trying to make too many columns. pub fn table_setup_scroll_freeze(&self, locked_columns: usize, locked_rows: usize) { unsafe { sys::igTableSetupScrollFreeze(locked_columns as i32, locked_rows as i32); } } /// Along with [table_setup_column](Self::table_setup_column), this method is used /// to create a header row and automatically submit a table header for each column. /// /// For an example of using this method, see [table_setup_column](Self::table_setup_column). /// /// Headers are required to perform: reordering, sorting, and opening the context menu (though, /// the context menu can also be made available in columns body using [TableFlags::CONTEXT_MENU_IN_BODY]. /// /// You may manually submit headers using [table_next_column] + [table_header] calls, but this is /// only useful in some advanced use cases (e.g. adding custom widgets in header row). /// See [table_header](Self::table_header) for more information. /// /// [table_next_column]: Self::table_next_column /// [table_header]: Self::table_header pub fn table_headers_row(&self) { unsafe { sys::igTableHeadersRow(); } } /// Use this function to manually declare a column cell to be a header. /// /// You generally should avoid using this outside of specific cases, /// such as custom widgets. Instead, use [table_headers_row](Self::table_headers_row) /// and [table_setup_column](Self::table_setup_column). pub fn table_header(&self, label: impl AsRef) { unsafe { sys::igTableHeader(self.scratch_txt(label)); } } /// Gets the numbers of columns in the current table. pub fn table_column_count(&self) -> usize { unsafe { sys::igTableGetColumnCount() as usize } } /// Gets the current column index in the current table. pub fn table_column_index(&self) -> usize { unsafe { sys::igTableGetColumnIndex() as usize } } /// Gets the current row index in the current table. pub fn table_row_index(&self) -> usize { unsafe { sys::igTableGetRowIndex() as usize } } /// Gets the name of the current column. If there is no currently bound name /// for this column, we will return an empty string. /// /// Use [table_column_name_with_column](Self::table_column_name_with_column) /// for arbitrary indices. pub fn table_column_name(&mut self) -> &str { unsafe { // imgui uses utf8...though that is a continuous process there. CStr::from_ptr(sys::igTableGetColumnName(-1)) .to_str() .unwrap() } } /// Gets the name of a given column. If there is no currently bound name /// for this column, we will return an empty string. /// /// Use [table_column_name](Self::table_column_name) for the current column. pub fn table_column_name_with_column(&mut self, column: usize) -> &str { unsafe { // imgui uses utf8...though that is a continuous process there. CStr::from_ptr(sys::igTableGetColumnName(column as i32)) .to_str() .unwrap() } } /// Gets the flags on the current column in the current table. pub fn table_column_flags(&self) -> TableColumnFlags { unsafe { TableColumnFlags::from_bits(sys::igTableGetColumnFlags(-1) as u32) .expect("bad column flags") } } /// Gets the flags on the given column in the current table. To get the current column's /// flags without having to call [table_column_index](Self::table_column_index), use /// [table_column_flags](Self::table_column_flags). pub fn table_column_flags_with_column(&self, column_n: usize) -> TableColumnFlags { unsafe { TableColumnFlags::from_bits(sys::igTableGetColumnFlags(column_n as i32) as u32) .expect("bad column flags") } } /// Sets the given background color for this column. See [TableBgTarget] /// for more information on how colors work for tables. /// /// Use [table_set_bg_color_with_column](Self::table_set_bg_color_with_column) to set /// for arbitrary indices. pub fn table_set_bg_color(&self, target: TableBgTarget, color: impl Into) { unsafe { sys::igTableSetBgColor(target.bits() as i32, color.into().into(), -1); } } /// Sets the given background color for any column. See [TableBgTarget] /// for more information on how colors work for tables. /// /// Use [table_set_bg_color](Self::table_set_bg_color) for the current column. pub fn table_set_bg_color_with_column( &self, target: TableBgTarget, color: impl Into, column_index: usize, ) { unsafe { sys::igTableSetBgColor( target.bits() as i32, color.into().into(), column_index as i32, ); } } /// Change user accessible enabled/disabled state of the current column. /// /// Set to false to hide the column. Users can use the context menu to change /// this themselves by right-clicking in headers, or right-clicking in columns body /// if [TableFlags::CONTEXT_MENU_IN_BODY]. /// /// Use [table_set_enabled_with_column](Self::table_set_enabled_with_column) to set /// for arbitrary indices. pub fn table_set_enabled(&self, enabled: bool) { unsafe { sys::igTableSetColumnEnabled(-1, enabled) } } /// Change user accessible enabled/disabled state of the current column. /// /// Set to false to hide the column. Users can use the context menu to change /// this themselves by right-clicking in headers, or right-clicking in columns body /// if [TableFlags::CONTEXT_MENU_IN_BODY]. pub fn table_set_enabled_with_column(&self, enabled: bool, column_idx: usize) { unsafe { sys::igTableSetColumnEnabled(column_idx as i32, enabled) } } /// Gets the sorting data for a table. This will be `None` when not sorting. /// /// See the examples folder for how to use the sorting API. pub fn table_sort_specs_mut(&self) -> Option> { unsafe { let value = sys::igTableGetSortSpecs(); if value.is_null() { None } else { Some(TableSortSpecsMut(value, PhantomData)) } } } } /// A struct containing all the data needed to setup a table column header /// via [begin_table_header](Ui::begin_table_header) or [table_setup_column](Ui::table_setup_column). #[derive(Debug, Default)] pub struct TableColumnSetup<'a, Name> { /// The name of column to be displayed to users. pub name: Name, /// The flags this column will have. pub flags: TableColumnFlags, /// The width or weight of the given column. pub init_width_or_weight: f32, /// A user_id, primarily used in sorting operations. pub user_id: Id<'a>, } impl<'a, Name: AsRef> TableColumnSetup<'a, Name> { pub fn new(name: Name) -> Self { Self { name, flags: TableColumnFlags::empty(), init_width_or_weight: 0.0, user_id: Id::Int(0), } } } /// A wrapper around table sort specs. /// /// To use this simply, use [conditional_sort] and provide a closure -- /// if you should sort your data, then the closure will be ran and imgui /// will be informed that your data is sorted. /// /// For manual control (such as if sorting can fail), use [should_sort] to /// check if you should sort your data, sort your data using [specs] for information /// on how to sort it, and then [set_sorted] to indicate that the data is sorted. /// /// [conditional_sort]: Self::conditional_sort /// [should_sort]: Self::should_sort /// [specs]: Self::specs /// [set_sorted]: Self::set_sorted pub struct TableSortSpecsMut<'ui>(*mut sys::ImGuiTableSortSpecs, PhantomData>); impl TableSortSpecsMut<'_> { /// Gets the specs for a given sort. In most scenarios, this will be a slice of 1 entry. pub fn specs(&self) -> Specs<'_> { let value = unsafe { std::slice::from_raw_parts((*self.0).Specs, (*self.0).SpecsCount as usize) }; Specs(value) } /// Returns true if the data should be sorted. pub fn should_sort(&self) -> bool { unsafe { (*self.0).SpecsDirty } } /// Sets the internal flag that the data has been sorted. pub fn set_sorted(&mut self) { unsafe { (*self.0).SpecsDirty = false; } } /// Provide a closure, which will receive the Specs for a sort. /// /// If you should sort the data, the closure will run, and ImGui will be /// told that the data has been sorted. /// /// If you need manual control over sorting, consider using [should_sort], [specs], /// and [set_sorted] youself. /// /// [should_sort]: Self::should_sort /// [specs]: Self::specs /// [set_sorted]: Self::set_sorted pub fn conditional_sort(mut self, mut f: impl FnMut(Specs<'_>)) { let is_dirty = self.should_sort(); if is_dirty { f(self.specs()); } self.set_sorted(); } } /// A wrapper around a slice of [TableColumnSortSpecs]. /// /// This slice may be 0 if [TableFlags::SORT_TRISTATE] is true, may be > 1 is [TableFlags::SORT_MULTI] is true, /// but is generally == 1. /// /// Consume this struct as an iterator. pub struct Specs<'a>(&'a [sys::ImGuiTableColumnSortSpecs]); impl<'a> Specs<'a> { pub fn iter(self) -> impl Iterator> { self.0.iter().map(|v| TableColumnSortSpecs(v)) } } pub struct TableColumnSortSpecs<'a>(&'a sys::ImGuiTableColumnSortSpecs); impl<'a> TableColumnSortSpecs<'a> { /// User id of the column (if specified by a TableSetupColumn() call) pub fn column_user_id(&self) -> sys::ImGuiID { self.0.ColumnUserID } /// Index of the column pub fn column_idx(&self) -> usize { self.0.ColumnIndex as usize } /// Index within parent [Specs] slice where this was found -- always stored in order starting /// from 0, tables sorted on a single criteria will always have a 0 here. /// /// Generally, you don't need to access this, as it's the same as calling `specs.iter().enumerate()`. pub fn sort_order(&self) -> usize { self.0.SortOrder as usize } /// Gets the sort direction for the given column. This will nearly always be `Some` if you /// can access it. pub fn sort_direction(&self) -> Option { match self.0.SortDirection() { 0 => None, 1 => Some(TableSortDirection::Ascending), 2 => Some(TableSortDirection::Descending), _ => unimplemented!(), } } } create_token!( /// Tracks a table which can be rendered onto, ending with `.end()` /// or by dropping. pub struct TableToken<'ui>; /// Ends the table. drop { sys::igEndTable() } );