Added multi-column sorting to data tables.

This commit is contained in:
Patrick 2025-09-22 22:19:29 +02:00
parent 9d02d97af3
commit da8d1589b3

View File

@ -5,6 +5,7 @@
#define RAID_PUBLIC_RAID_IMRAID_HPP_INCLUDED 1 #define RAID_PUBLIC_RAID_IMRAID_HPP_INCLUDED 1
#include <algorithm> #include <algorithm>
#include <cstring>
#include <functional> #include <functional>
#include <span> #include <span>
#include <string> #include <string>
@ -73,8 +74,13 @@ inline bool BeginPopupButton(const char* label)
struct DataTableState struct DataTableState
{ {
std::vector<std::size_t> sortedIndices; std::vector<std::size_t> sortedIndices;
std::size_t sortColumn = 0;
bool sortDescending = false; struct SortColumn
{
std::size_t index;
bool sortDescending = false;
};
std::vector<SortColumn> sortColumns;
bool dirty = true; bool dirty = true;
}; };
@ -97,7 +103,8 @@ template<typename TObject>
struct DataTableOptions struct DataTableOptions
{ {
std::span<const DataTableColumn<TObject>> columns; std::span<const DataTableColumn<TObject>> columns;
ImGuiTableFlags tableFlags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Sortable | ImGuiTableFlags_ScrollY; ImGuiTableFlags tableFlags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Sortable
| ImGuiTableFlags_ScrollY | ImGuiTableFlags_SortMulti;
}; };
template<typename TObject,typename TData> template<typename TObject,typename TData>
@ -128,10 +135,23 @@ inline void DataTable(const char* strId, const DataTableOptions<TObject>& option
{ {
sortSpecs->SpecsDirty = false; sortSpecs->SpecsDirty = false;
const ImGuiTableColumnSortSpecs& specs = *sortSpecs->Specs; if (state.sortColumns.size() != static_cast<std::size_t>(sortSpecs->SpecsCount))
state.dirty |= (specs.ColumnIndex != static_cast<ImS16>(state.sortColumn)); {
state.sortColumn = specs.ColumnIndex; state.dirty = true;
state.sortDescending = specs.SortDirection == ImGuiSortDirection_Descending; state.sortColumns.resize(sortSpecs->SpecsCount);
}
for (int idx = 0; idx < sortSpecs->SpecsCount; ++idx)
{
const ImGuiTableColumnSortSpecs& specs = sortSpecs->Specs[idx];
DataTableState::SortColumn& column = state.sortColumns[idx];
state.dirty |= (static_cast<std::size_t>(specs.ColumnIndex) != column.index);
state.dirty |= column.sortDescending != (specs.SortDirection == ImGuiSortDirection_Descending);
column = {
.index = static_cast<std::size_t>(specs.ColumnIndex),
.sortDescending = (specs.SortDirection == ImGuiSortDirection_Descending)
};
}
} }
if (state.sortedIndices.size() != data.size()) if (state.sortedIndices.size() != data.size())
@ -145,16 +165,28 @@ inline void DataTable(const char* strId, const DataTableOptions<TObject>& option
for (std::size_t idx = 0; idx < data.size(); ++idx) { for (std::size_t idx = 0; idx < data.size(); ++idx) {
state.sortedIndices[idx] = idx; state.sortedIndices[idx] = idx;
} }
std::ranges::sort(state.sortedIndices, [&](std::size_t leftIdx, std::size_t rightIdx) { std::ranges::sort(state.sortedIndices, [&](std::size_t leftIdx, std::size_t rightIdx)
return options.columns[state.sortColumn].comparator(data[leftIdx], data[rightIdx]); {
for (const DataTableState::SortColumn& column : state.sortColumns)
{
const bool less = options.columns[column.index].comparator(data[leftIdx], data[rightIdx]);
if (less)
{ // left < right
return !column.sortDescending;
}
if (options.columns[column.index].comparator(data[rightIdx], data[leftIdx]))
{ // left > right
return column.sortDescending;
}
}
// left == right
return false;
}); });
state.dirty = false; state.dirty = false;
} }
for (std::size_t indexIdx = 0; indexIdx < state.sortedIndices.size(); ++indexIdx) for (const std::size_t dataIdx : state.sortedIndices)
{ {
const std::size_t dataIdx = state.sortDescending ? state.sortedIndices[state.sortedIndices.size() - indexIdx - 1]
: state.sortedIndices[indexIdx];
const TObject& object = data[dataIdx]; const TObject& object = data[dataIdx];
ImGui::TableNextRow(); ImGui::TableNextRow();