mirror of
https://github.com/eliasstepanik/imgui-rs.git
synced 2026-01-11 13:38:35 +00:00
Merge branch 'cfrantz-tables-api'
This commit is contained in:
commit
7d266d418c
212
imgui-examples/examples/tables_api.rs
Normal file
212
imgui-examples/examples/tables_api.rs
Normal file
@ -0,0 +1,212 @@
|
||||
use imgui::*;
|
||||
|
||||
mod support;
|
||||
|
||||
fn main() {
|
||||
let system = support::init(file!());
|
||||
|
||||
let mut humans = vec![
|
||||
HumanData {
|
||||
name: "Joonas",
|
||||
favorite_number: 102,
|
||||
favorite_fruit_maybe: "Dutch",
|
||||
},
|
||||
HumanData {
|
||||
name: "Thom",
|
||||
favorite_number: 314,
|
||||
favorite_fruit_maybe: "Rice",
|
||||
},
|
||||
HumanData {
|
||||
name: "Jack",
|
||||
favorite_number: 611,
|
||||
favorite_fruit_maybe: "Mangoes",
|
||||
},
|
||||
];
|
||||
|
||||
let mut t2_flags = TableFlags::REORDERABLE
|
||||
| TableFlags::HIDEABLE
|
||||
| TableFlags::RESIZABLE
|
||||
| TableFlags::NO_BORDERS_IN_BODY;
|
||||
|
||||
system.main_loop(move |_, ui| {
|
||||
Window::new("Input text callbacks")
|
||||
.size([800.0, 400.0], Condition::FirstUseEver)
|
||||
.build(ui, || {
|
||||
if let Some(_t) = ui.begin_table("Basic-Table", 3) {
|
||||
// we must also call `next_row` here, because we declined
|
||||
// to set up header rows. If we set up header rows ourselves,
|
||||
// we will call `table_header_rows` instead, and if we use
|
||||
// `begin_table_header`, then the initial call will be handled for us.
|
||||
|
||||
ui.table_next_row();
|
||||
|
||||
// note you MUST call `next_column` at least to START
|
||||
// Let's walk through a table like it's an iterator...
|
||||
ui.table_set_column_index(0);
|
||||
ui.text("x: 0, y: 0");
|
||||
|
||||
ui.table_next_column();
|
||||
ui.text("x: 1, y: 0");
|
||||
|
||||
ui.table_next_column();
|
||||
ui.text("x: 2, y: 0");
|
||||
|
||||
// // calling next column again will wrap us around to 0-1,
|
||||
// // since we've exhausted our 3 columns.
|
||||
ui.table_next_column();
|
||||
ui.text("x: 0, y: 1");
|
||||
|
||||
// // Let's do this manually now -- we can set each column ourselves...
|
||||
ui.table_set_column_index(1);
|
||||
ui.text("x: 1, y: 1");
|
||||
|
||||
ui.table_set_column_index(2);
|
||||
ui.text("x: 2, y: 1");
|
||||
|
||||
// you CAN go back...
|
||||
ui.table_set_column_index(1);
|
||||
// however, you should call `new_line`, since otherwise
|
||||
// we'd right on top of our `x: 1, y: 1` text.
|
||||
ui.new_line();
|
||||
ui.text("our of order txt");
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
ui.text("Let's add some headers");
|
||||
if let Some(_t) = ui.begin_table_header(
|
||||
"table-headers",
|
||||
[
|
||||
TableColumnSetup::new("Name"),
|
||||
TableColumnSetup::new("Age"),
|
||||
TableColumnSetup::new("Favorite fruit"),
|
||||
],
|
||||
) {
|
||||
// note that we DON'T have to call "table_next_row" here -- that's taken care
|
||||
// of for us by `begin_table_header`, since it actually calls `table_headers_row`
|
||||
|
||||
// but we DO need to call column!
|
||||
// but that's fine, we'll use a loop
|
||||
for i in 0..3 {
|
||||
let names = ["Joonas", "Thom", "Jack"];
|
||||
let fruit = ["Dutch", "Rice", "Mangoes"];
|
||||
|
||||
ui.table_next_column();
|
||||
ui.text(names[i]);
|
||||
|
||||
ui.table_next_column();
|
||||
ui.text((i * 9).to_string());
|
||||
|
||||
ui.table_next_column();
|
||||
ui.text(fruit[i]);
|
||||
}
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
ui.text("Let's do some context menus");
|
||||
ui.text(
|
||||
"context menus are created, by default, from the flags passed\
|
||||
while making the table, or each row.\n\
|
||||
Notice how toggling these checkboxes changes the context menu.",
|
||||
);
|
||||
|
||||
ui.checkbox_flags("Reorderable", &mut t2_flags, TableFlags::REORDERABLE);
|
||||
ui.same_line();
|
||||
ui.checkbox_flags("Hideable", &mut t2_flags, TableFlags::HIDEABLE);
|
||||
ui.same_line();
|
||||
ui.checkbox_flags("Resizable", &mut t2_flags, TableFlags::RESIZABLE);
|
||||
|
||||
if let Some(_t) = ui.begin_table_header_with_flags(
|
||||
"table-headers2",
|
||||
[
|
||||
TableColumnSetup::new("Name"),
|
||||
TableColumnSetup::new("Age"),
|
||||
TableColumnSetup::new("Favorite fruit"),
|
||||
],
|
||||
t2_flags,
|
||||
) {
|
||||
// note that we DON'T have to call "table_next_row" here -- that's taken care
|
||||
// of for us by `begin_table_header`, since it actually calls `table_headers_row`
|
||||
|
||||
// but we DO need to call column!
|
||||
// but that's fine, we'll use a loop
|
||||
for i in 0..3 {
|
||||
let names = ["Joonas", "Thom", "Jack"];
|
||||
let fruit = ["Dutch", "Rice", "Mangoes"];
|
||||
|
||||
ui.table_next_column();
|
||||
ui.text(names[i]);
|
||||
|
||||
ui.table_next_column();
|
||||
ui.text((i * 9).to_string());
|
||||
|
||||
ui.table_next_column();
|
||||
ui.text(fruit[i]);
|
||||
}
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.text("Here's a table you can sort!");
|
||||
ui.text("Check the code to see the two methods of doing it.");
|
||||
|
||||
if let Some(_t) = ui.begin_table_header_with_flags(
|
||||
"table-headers3",
|
||||
[
|
||||
TableColumnSetup::new("Name"),
|
||||
TableColumnSetup::new("Favorite Number"),
|
||||
TableColumnSetup::new("Favorite fruit"),
|
||||
],
|
||||
TableFlags::SORTABLE,
|
||||
) {
|
||||
if let Some(sort_data) = ui.table_sort_specs_mut() {
|
||||
sort_data
|
||||
.conditional_sort(|specs| HumanData::sort_humans(&mut humans, specs));
|
||||
|
||||
// Can also sort this other way...
|
||||
// if sort_data.should_sort() {
|
||||
// HumanData::sort_humans(&mut humans, sort_data.specs());
|
||||
// sort_data.set_sorted();
|
||||
// }
|
||||
}
|
||||
|
||||
for human in humans.iter() {
|
||||
ui.table_next_column();
|
||||
ui.text(human.name);
|
||||
|
||||
ui.table_next_column();
|
||||
ui.text(human.favorite_number.to_string());
|
||||
|
||||
ui.table_next_column();
|
||||
ui.text(human.favorite_fruit_maybe);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
struct HumanData {
|
||||
name: &'static str,
|
||||
favorite_number: usize,
|
||||
favorite_fruit_maybe: &'static str,
|
||||
}
|
||||
|
||||
impl HumanData {
|
||||
pub fn sort_humans(humans: &mut Vec<Self>, specs: Specs<'_>) {
|
||||
let spec = specs.iter().next().unwrap();
|
||||
if let Some(kind) = spec.sort_direction() {
|
||||
match kind {
|
||||
TableSortDirection::Ascending => match spec.column_idx() {
|
||||
0 => humans.sort_by_key(|h| h.name),
|
||||
1 => humans.sort_by_key(|h| h.favorite_number),
|
||||
2 => humans.sort_by_key(|h| h.favorite_fruit_maybe),
|
||||
_ => unimplemented!(),
|
||||
},
|
||||
TableSortDirection::Descending => match spec.column_idx() {
|
||||
0 => humans.sort_by_key(|h| std::cmp::Reverse(h.name)),
|
||||
1 => humans.sort_by_key(|h| std::cmp::Reverse(h.favorite_number)),
|
||||
2 => humans.sort_by_key(|h| std::cmp::Reverse(h.favorite_fruit_maybe)),
|
||||
_ => unimplemented!(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -31,6 +31,7 @@ pub use self::render::renderer::*;
|
||||
pub use self::stacks::*;
|
||||
pub use self::string::*;
|
||||
pub use self::style::*;
|
||||
pub use self::tables::*;
|
||||
pub use self::utils::*;
|
||||
pub use self::widget::color_editors::*;
|
||||
pub use self::widget::combo_box::*;
|
||||
@ -73,6 +74,7 @@ mod popups;
|
||||
mod render;
|
||||
mod stacks;
|
||||
mod style;
|
||||
mod tables;
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
mod utils;
|
||||
@ -288,6 +290,31 @@ impl<T> From<*mut T> for Id<'static> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Id<'a> {
|
||||
fn as_imgui_id(&self) -> sys::ImGuiID {
|
||||
unsafe {
|
||||
match self {
|
||||
Id::Ptr(p) => sys::igGetID_Ptr(*p),
|
||||
Id::Str(s) => {
|
||||
let s1 = s.as_ptr() as *const std::os::raw::c_char;
|
||||
let s2 = s1.add(s.len());
|
||||
sys::igGetID_StrStr(s1, s2)
|
||||
}
|
||||
Id::Int(i) => {
|
||||
let p = *i as *const std::os::raw::c_void;
|
||||
sys::igGetID_Ptr(p)
|
||||
} // Id::ImGuiID(n) => *n,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Default for Id<'a> {
|
||||
fn default() -> Self {
|
||||
Self::Int(0)
|
||||
}
|
||||
}
|
||||
|
||||
// Widgets: Input
|
||||
impl<'ui> Ui<'ui> {
|
||||
#[doc(alias = "InputText", alias = "InputTextWithHint")]
|
||||
|
||||
860
imgui/src/tables.rs
Normal file
860
imgui/src/tables.rs
Normal file
@ -0,0 +1,860 @@
|
||||
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:** after you begin a table (and after setting up )
|
||||
#[inline]
|
||||
#[must_use = "if return is dropped immediately, table is ended immediately."]
|
||||
pub fn begin_table(
|
||||
&self,
|
||||
str_id: impl AsRef<str>,
|
||||
column_count: i32,
|
||||
) -> Option<TableToken<'ui>> {
|
||||
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).
|
||||
#[inline]
|
||||
pub fn begin_table_with_flags(
|
||||
&self,
|
||||
str_id: impl AsRef<str>,
|
||||
column_count: i32,
|
||||
flags: TableFlags,
|
||||
) -> Option<TableToken<'ui>> {
|
||||
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).
|
||||
#[inline]
|
||||
pub fn begin_table_with_sizing(
|
||||
&self,
|
||||
str_id: impl AsRef<str>,
|
||||
column: i32,
|
||||
flags: TableFlags,
|
||||
outer_size: [f32; 2],
|
||||
inner_width: f32,
|
||||
) -> Option<TableToken<'ui>> {
|
||||
let should_render = unsafe {
|
||||
sys::igBeginTable(
|
||||
self.scratch_txt(str_id),
|
||||
column,
|
||||
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")]
|
||||
pub fn begin_table_header<'a, Name: AsRef<str>, const N: usize>(
|
||||
&self,
|
||||
str_id: impl AsRef<str>,
|
||||
column_data: [TableColumnSetup<'a, Name>; N],
|
||||
) -> Option<TableToken<'ui>> {
|
||||
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")]
|
||||
pub fn begin_table_header_with_flags<'a, Name: AsRef<str>, const N: usize>(
|
||||
&self,
|
||||
str_id: impl AsRef<str>,
|
||||
column_data: [TableColumnSetup<'a, Name>; N],
|
||||
flags: TableFlags,
|
||||
) -> Option<TableToken<'ui>> {
|
||||
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")]
|
||||
pub fn begin_table_header_with_sizing<'a, Name: AsRef<str>, const N: usize>(
|
||||
&self,
|
||||
str_id: impl AsRef<str>,
|
||||
column_data: [TableColumnSetup<'a, Name>; N],
|
||||
flags: TableFlags,
|
||||
outer_size: [f32; 2],
|
||||
inner_width: f32,
|
||||
) -> Option<TableToken<'ui>> {
|
||||
self.begin_table_with_sizing(str_id, N as i32, 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
|
||||
/// ```rs
|
||||
/// ### let ui: Ui<'static> = unimplemented!();
|
||||
/// if let Some(_t) = ui.begin_table(im_str!("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
|
||||
/// ```rs
|
||||
/// ### let ui: Ui<'static> = unimplemented!();
|
||||
/// if let Some(_t) = ui.begin_table(im_str!("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
|
||||
/// ```rs
|
||||
/// let ui: Ui<'static> = unimplemented!();
|
||||
///
|
||||
/// if let Some(_t) = ui.begin_table(im_str!("My Table"), 2) {
|
||||
/// ui.table_setup_column(im_str!("One"));
|
||||
/// ui.table_setup_column(im_str!("Two"));
|
||||
/// ui.table_setup_column(im_str!("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<str>) {
|
||||
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<N: AsRef<str>>(&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
|
||||
/// ```rs
|
||||
/// let ui: Ui<'static> = unimplemented!();
|
||||
///
|
||||
/// const COLUMN_COUNT: usize = 3;
|
||||
/// if let Some(_t) = ui.begin_table(im_str!("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(im_str!("One"));
|
||||
/// ui.table_setup_column(im_str!("Two"));
|
||||
/// ui.table_setup_column(im_str!("Three"));
|
||||
/// ui.table_headers_row();
|
||||
/// }
|
||||
/// ```
|
||||
pub fn table_setup_scroll_freeze(&self, locked_columns: i32, locked_rows: i32) {
|
||||
unsafe {
|
||||
sys::igTableSetupScrollFreeze(locked_columns, locked_rows);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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<str>) {
|
||||
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<ImColor32>) {
|
||||
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<ImColor32>,
|
||||
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<TableSortSpecsMut<'_>> {
|
||||
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<str>> 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<Ui<'ui>>);
|
||||
|
||||
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<Item = TableColumnSortSpecs<'a>> {
|
||||
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<TableSortDirection> {
|
||||
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() }
|
||||
);
|
||||
Loading…
x
Reference in New Issue
Block a user