mirror of
https://github.com/eliasstepanik/imgui-rs.git
synced 2026-01-11 05:28:35 +00:00
added controls for sorting...which should finish this guy off!
This commit is contained in:
parent
eb455f032c
commit
9d43206633
@ -5,6 +5,24 @@ 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
|
||||
@ -129,6 +147,70 @@ fn main() {
|
||||
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(
|
||||
im_str!("table-headers3"),
|
||||
[
|
||||
TableColumnSetup::new(im_str!("Name")),
|
||||
TableColumnSetup::new(im_str!("Favorite Number")),
|
||||
TableColumnSetup::new(im_str!("Favorite fruit")),
|
||||
],
|
||||
TableFlags::SORTABLE,
|
||||
) {
|
||||
if let Some(mut 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 i in 0..3 {
|
||||
ui.table_next_column();
|
||||
ui.text(humans[i].name);
|
||||
|
||||
ui.table_next_column();
|
||||
ui.text(humans[i].favorite_number.to_string());
|
||||
|
||||
ui.table_next_column();
|
||||
ui.text(humans[i].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!(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use bitflags::bitflags;
|
||||
|
||||
use crate::sys;
|
||||
@ -240,6 +242,12 @@ bitflags! {
|
||||
}
|
||||
}
|
||||
|
||||
#[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.
|
||||
///
|
||||
@ -294,13 +302,19 @@ impl<'ui> Ui<'ui> {
|
||||
)
|
||||
};
|
||||
|
||||
should_render.then(|| TableToken::new(self))
|
||||
// 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, const N: usize>(
|
||||
&self,
|
||||
str_id: &ImStr,
|
||||
@ -313,6 +327,7 @@ impl<'ui> Ui<'ui> {
|
||||
///
|
||||
/// 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, const N: usize>(
|
||||
&self,
|
||||
str_id: &ImStr,
|
||||
@ -326,6 +341,7 @@ impl<'ui> Ui<'ui> {
|
||||
/// 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, const N: usize>(
|
||||
&self,
|
||||
str_id: &ImStr,
|
||||
@ -562,19 +578,17 @@ impl<'ui> Ui<'ui> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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).
|
||||
/// 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: &ImStr) {
|
||||
unsafe {
|
||||
sys::igTableHeader(label.as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn table_get_sort_specs(&self) -> &TableSortSpecs {
|
||||
// unsafe { sys::igTableGetSortSpecs() }
|
||||
// }
|
||||
|
||||
/// Gets the numbers of columns in the current table.
|
||||
pub fn table_column_count(&self) -> usize {
|
||||
unsafe { sys::igTableGetColumnCount() as usize }
|
||||
@ -590,6 +604,23 @@ impl<'ui> Ui<'ui> {
|
||||
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) -> &ImStr {
|
||||
unsafe { ImStr::from_ptr_unchecked(sys::igTableGetColumnName(-1)) }
|
||||
}
|
||||
|
||||
/// 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) -> &ImStr {
|
||||
unsafe { ImStr::from_ptr_unchecked(sys::igTableGetColumnName(column as i32)) }
|
||||
}
|
||||
|
||||
/// Gets the flags on the current column in the current table.
|
||||
pub fn table_column_flags(&self) -> TableColumnFlags {
|
||||
unsafe {
|
||||
@ -608,23 +639,6 @@ impl<'ui> Ui<'ui> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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) -> &ImStr {
|
||||
unsafe { ImStr::from_ptr_unchecked(sys::igTableGetColumnName(-1)) }
|
||||
}
|
||||
|
||||
/// 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) -> &ImStr {
|
||||
unsafe { ImStr::from_ptr_unchecked(sys::igTableGetColumnName(column as i32)) }
|
||||
}
|
||||
|
||||
/// Sets the given background color for this column. See [TableBgTarget]
|
||||
/// for more information on how colors work for tables.
|
||||
///
|
||||
@ -654,6 +668,18 @@ impl<'ui> Ui<'ui> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the sorting data for a table. This will be `None` when not sorting.
|
||||
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
|
||||
@ -689,6 +715,102 @@ impl<'a> Default for TableColumnSetup<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
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.
|
||||
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 [SORT_TRISTATE] is true, may be > 1 is [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.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user