mirror of
https://github.com/goatcorp/Dalamud.git
synced 2026-01-03 06:13:40 +01:00
chore: add raii, tables code from OtterGui into new Dalamud.Interface assembly
This commit is contained in:
parent
e0d4e60aad
commit
6bf1376515
22 changed files with 1356 additions and 0 deletions
34
Dalamud.Interface/Table/Column.cs
Normal file
34
Dalamud.Interface/Table/Column.cs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
using ImGuiNET;
|
||||
|
||||
namespace Dalamud.Interface.Table;
|
||||
|
||||
public class Column<TItem>
|
||||
{
|
||||
public string Label = string.Empty;
|
||||
public ImGuiTableColumnFlags Flags = ImGuiTableColumnFlags.NoResize;
|
||||
|
||||
public virtual float Width
|
||||
=> -1f;
|
||||
|
||||
public string FilterLabel
|
||||
=> $"##{this.Label}Filter";
|
||||
|
||||
public virtual bool DrawFilter()
|
||||
{
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted(this.Label);
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool FilterFunc(TItem item)
|
||||
=> true;
|
||||
|
||||
public virtual int Compare(TItem lhs, TItem rhs)
|
||||
=> 0;
|
||||
|
||||
public virtual void DrawColumn(TItem item, int idx)
|
||||
{ }
|
||||
|
||||
public int CompareInv(TItem lhs, TItem rhs)
|
||||
=> this.Compare(rhs, lhs);
|
||||
}
|
||||
65
Dalamud.Interface/Table/ColumnFlags.cs
Normal file
65
Dalamud.Interface/Table/ColumnFlags.cs
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
using ImGuiNET;
|
||||
using ImRaii = Dalamud.Interface.Raii.ImRaii;
|
||||
|
||||
namespace Dalamud.Interface.Table;
|
||||
|
||||
public class ColumnFlags<T, TItem> : Column<TItem> where T : struct, Enum
|
||||
{
|
||||
public T AllFlags = default;
|
||||
|
||||
protected virtual IReadOnlyList<T> Values
|
||||
=> Enum.GetValues<T>();
|
||||
|
||||
protected virtual string[] Names
|
||||
=> Enum.GetNames<T>();
|
||||
|
||||
public virtual T FilterValue
|
||||
=> default;
|
||||
|
||||
protected virtual void SetValue(T value, bool enable)
|
||||
{ }
|
||||
|
||||
public override bool DrawFilter()
|
||||
{
|
||||
using var id = ImRaii.PushId(this.FilterLabel);
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameRounding, 0);
|
||||
ImGui.SetNextItemWidth(-Table.ArrowWidth * InterfaceHelpers.GlobalScale);
|
||||
var all = this.FilterValue.HasFlag(this.AllFlags);
|
||||
using var color = ImRaii.PushColor(ImGuiCol.FrameBg, 0x803030A0, !all);
|
||||
using var combo = ImRaii.Combo(string.Empty, this.Label, ImGuiComboFlags.NoArrowButton);
|
||||
|
||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
|
||||
{
|
||||
this.SetValue(this.AllFlags, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!all && ImGui.IsItemHovered())
|
||||
ImGui.SetTooltip("Right-click to clear filters.");
|
||||
|
||||
if (!combo)
|
||||
return false;
|
||||
|
||||
color.Pop();
|
||||
|
||||
var ret = false;
|
||||
if (ImGui.Checkbox("Enable All", ref all))
|
||||
{
|
||||
this.SetValue(this.AllFlags, all);
|
||||
ret = true;
|
||||
}
|
||||
|
||||
using var indent = ImRaii.PushIndent(10f);
|
||||
for (var i = 0; i < this.Names.Length; ++i)
|
||||
{
|
||||
var tmp = this.FilterValue.HasFlag(this.Values[i]);
|
||||
if (!ImGui.Checkbox(this.Names[i], ref tmp))
|
||||
continue;
|
||||
|
||||
this.SetValue(this.Values[i], tmp);
|
||||
ret = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
47
Dalamud.Interface/Table/ColumnSelect.cs
Normal file
47
Dalamud.Interface/Table/ColumnSelect.cs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
using ImGuiNET;
|
||||
using ImRaii = Dalamud.Interface.Raii.ImRaii;
|
||||
|
||||
namespace Dalamud.Interface.Table;
|
||||
|
||||
public class ColumnSelect<T, TItem> : Column<TItem> where T : struct, Enum, IEquatable<T>
|
||||
{
|
||||
public ColumnSelect(T initialValue)
|
||||
=> this.FilterValue = initialValue;
|
||||
|
||||
protected virtual IReadOnlyList<T> Values
|
||||
=> Enum.GetValues<T>();
|
||||
|
||||
protected virtual string[] Names
|
||||
=> Enum.GetNames<T>();
|
||||
|
||||
protected virtual void SetValue(T value)
|
||||
=> this.FilterValue = value;
|
||||
|
||||
public T FilterValue;
|
||||
protected int Idx = -1;
|
||||
|
||||
public override bool DrawFilter()
|
||||
{
|
||||
using var id = ImRaii.PushId(this.FilterLabel);
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameRounding, 0);
|
||||
ImGui.SetNextItemWidth(-Table.ArrowWidth * InterfaceHelpers.GlobalScale);
|
||||
using var combo = ImRaii.Combo(string.Empty, this.Idx < 0 ? this.Label : this.Names[this.Idx]);
|
||||
if(!combo)
|
||||
return false;
|
||||
|
||||
var ret = false;
|
||||
for (var i = 0; i < this.Names.Length; ++i)
|
||||
{
|
||||
if (this.FilterValue.Equals(this.Values[i]))
|
||||
this.Idx = i;
|
||||
if (!ImGui.Selectable(this.Names[i], this.Idx == i) || this.Idx == i)
|
||||
continue;
|
||||
|
||||
this.Idx = i;
|
||||
this.SetValue(this.Values[i]);
|
||||
ret = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
56
Dalamud.Interface/Table/ColumnString.cs
Normal file
56
Dalamud.Interface/Table/ColumnString.cs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
using System.Text.RegularExpressions;
|
||||
using Dalamud.Interface.Raii;
|
||||
using ImGuiNET;
|
||||
|
||||
namespace Dalamud.Interface.Table;
|
||||
|
||||
public class ColumnString<TItem> : Column<TItem>
|
||||
{
|
||||
public ColumnString()
|
||||
=> this.Flags &= ~ImGuiTableColumnFlags.NoResize;
|
||||
|
||||
public string FilterValue = string.Empty;
|
||||
protected Regex? FilterRegex;
|
||||
|
||||
public virtual string ToName(TItem item)
|
||||
=> item!.ToString() ?? string.Empty;
|
||||
|
||||
public override int Compare(TItem lhs, TItem rhs)
|
||||
=> string.Compare(this.ToName(lhs), this.ToName(rhs), StringComparison.InvariantCulture);
|
||||
|
||||
public override bool DrawFilter()
|
||||
{
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameRounding, 0);
|
||||
|
||||
ImGui.SetNextItemWidth(-Table.ArrowWidth * InterfaceHelpers.GlobalScale);
|
||||
var tmp = this.FilterValue;
|
||||
if (!ImGui.InputTextWithHint(this.FilterLabel, this.Label, ref tmp, 256) || tmp == this.FilterValue)
|
||||
return false;
|
||||
|
||||
this.FilterValue = tmp;
|
||||
try
|
||||
{
|
||||
this.FilterRegex = new Regex(this.FilterValue, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
|
||||
}
|
||||
catch
|
||||
{
|
||||
this.FilterRegex = null;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool FilterFunc(TItem item)
|
||||
{
|
||||
var name = this.ToName(item);
|
||||
if (this.FilterValue.Length == 0)
|
||||
return true;
|
||||
|
||||
return this.FilterRegex?.IsMatch(name) ?? name.Contains(this.FilterValue, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public override void DrawColumn(TItem item, int _)
|
||||
{
|
||||
ImGui.TextUnformatted(this.ToName(item));
|
||||
}
|
||||
}
|
||||
172
Dalamud.Interface/Table/Table.cs
Normal file
172
Dalamud.Interface/Table/Table.cs
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
using System.Numerics;
|
||||
using ImGuiNET;
|
||||
using ImRaii = Dalamud.Interface.Raii.ImRaii;
|
||||
|
||||
namespace Dalamud.Interface.Table;
|
||||
|
||||
public static class Table
|
||||
{
|
||||
public const float ArrowWidth = 10;
|
||||
}
|
||||
|
||||
public class Table<T>
|
||||
{
|
||||
protected bool FilterDirty = true;
|
||||
protected bool SortDirty = true;
|
||||
protected readonly ICollection<T> Items;
|
||||
internal readonly List<(T, int)> FilteredItems;
|
||||
|
||||
protected readonly string Label;
|
||||
protected readonly Column<T>[] Headers;
|
||||
|
||||
protected float ItemHeight { get; set; }
|
||||
public float ExtraHeight { get; set; } = 0;
|
||||
|
||||
private int _currentIdx = 0;
|
||||
|
||||
protected bool Sortable
|
||||
{
|
||||
get => this.Flags.HasFlag(ImGuiTableFlags.Sortable);
|
||||
set => this.Flags = value ? this.Flags | ImGuiTableFlags.Sortable : this.Flags & ~ImGuiTableFlags.Sortable;
|
||||
}
|
||||
|
||||
protected int SortIdx = -1;
|
||||
|
||||
public ImGuiTableFlags Flags = ImGuiTableFlags.RowBg
|
||||
| ImGuiTableFlags.Sortable
|
||||
| ImGuiTableFlags.BordersOuter
|
||||
| ImGuiTableFlags.ScrollY
|
||||
| ImGuiTableFlags.ScrollX
|
||||
| ImGuiTableFlags.PreciseWidths
|
||||
| ImGuiTableFlags.BordersInnerV
|
||||
| ImGuiTableFlags.NoBordersInBodyUntilResize;
|
||||
|
||||
public int TotalItems
|
||||
=> this.Items.Count;
|
||||
|
||||
public int CurrentItems
|
||||
=> this.FilteredItems.Count;
|
||||
|
||||
public int TotalColumns
|
||||
=> this.Headers.Length;
|
||||
|
||||
public int VisibleColumns { get; private set; }
|
||||
|
||||
public Table(string label, ICollection<T> items, params Column<T>[] headers)
|
||||
{
|
||||
this.Label = label;
|
||||
this.Items = items;
|
||||
this.Headers = headers;
|
||||
this.FilteredItems = new List<(T, int)>(this.Items.Count);
|
||||
this.VisibleColumns = this.Headers.Length;
|
||||
}
|
||||
|
||||
public void Draw(float itemHeight)
|
||||
{
|
||||
this.ItemHeight = itemHeight;
|
||||
using var id = ImRaii.PushId(this.Label);
|
||||
this.UpdateFilter();
|
||||
this.DrawTableInternal();
|
||||
}
|
||||
|
||||
protected virtual void DrawFilters()
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
protected virtual void PreDraw()
|
||||
{ }
|
||||
|
||||
private void SortInternal()
|
||||
{
|
||||
if (!this.Sortable)
|
||||
return;
|
||||
|
||||
var sortSpecs = ImGui.TableGetSortSpecs();
|
||||
this.SortDirty |= sortSpecs.SpecsDirty;
|
||||
|
||||
if (!this.SortDirty)
|
||||
return;
|
||||
|
||||
this.SortIdx = sortSpecs.Specs.ColumnIndex;
|
||||
|
||||
if (this.Headers.Length <= this.SortIdx)
|
||||
this.SortIdx = 0;
|
||||
|
||||
if (sortSpecs.Specs.SortDirection == ImGuiSortDirection.Ascending)
|
||||
this.FilteredItems.StableSort((a, b) => this.Headers[this.SortIdx].Compare(a.Item1, b.Item1));
|
||||
else if (sortSpecs.Specs.SortDirection == ImGuiSortDirection.Descending)
|
||||
this.FilteredItems.StableSort((a, b) => this.Headers[this.SortIdx].CompareInv(a.Item1, b.Item1));
|
||||
else
|
||||
this.SortIdx = -1;
|
||||
this.SortDirty = false;
|
||||
sortSpecs.SpecsDirty = false;
|
||||
}
|
||||
|
||||
private void UpdateFilter()
|
||||
{
|
||||
if (!this.FilterDirty)
|
||||
return;
|
||||
|
||||
this.FilteredItems.Clear();
|
||||
var idx = 0;
|
||||
foreach (var item in this.Items)
|
||||
{
|
||||
if (this.Headers.All(header => header.FilterFunc(item)))
|
||||
this.FilteredItems.Add((item, idx));
|
||||
idx++;
|
||||
}
|
||||
|
||||
this.FilterDirty = false;
|
||||
this.SortDirty = true;
|
||||
}
|
||||
|
||||
private void DrawItem((T, int) pair)
|
||||
{
|
||||
var column = 0;
|
||||
using var id = ImRaii.PushId(this._currentIdx);
|
||||
this._currentIdx = pair.Item2;
|
||||
foreach (var header in this.Headers)
|
||||
{
|
||||
id.Push(column++);
|
||||
if (ImGui.TableNextColumn())
|
||||
header.DrawColumn(pair.Item1, pair.Item2);
|
||||
id.Pop();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawTableInternal()
|
||||
{
|
||||
using var table = ImRaii.Table("Table", this.Headers.Length, this.Flags,
|
||||
ImGui.GetContentRegionAvail() - this.ExtraHeight * Vector2.UnitY * InterfaceHelpers.GlobalScale);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
this.PreDraw();
|
||||
ImGui.TableSetupScrollFreeze(1, 1);
|
||||
|
||||
foreach (var header in this.Headers)
|
||||
ImGui.TableSetupColumn(header.Label, header.Flags, header.Width);
|
||||
|
||||
ImGui.TableNextRow(ImGuiTableRowFlags.Headers);
|
||||
var i = 0;
|
||||
this.VisibleColumns = 0;
|
||||
foreach (var header in this.Headers)
|
||||
{
|
||||
using var id = ImRaii.PushId(i);
|
||||
if (ImGui.TableGetColumnFlags(i).HasFlag(ImGuiTableColumnFlags.IsEnabled))
|
||||
++this.VisibleColumns;
|
||||
if (!ImGui.TableSetColumnIndex(i++))
|
||||
continue;
|
||||
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero);
|
||||
ImGui.TableHeader(string.Empty);
|
||||
ImGui.SameLine();
|
||||
style.Pop();
|
||||
if (header.DrawFilter())
|
||||
this.FilterDirty = true;
|
||||
}
|
||||
|
||||
this.SortInternal();
|
||||
this._currentIdx = 0;
|
||||
ImGuiClip.ClippedDraw(this.FilteredItems, this.DrawItem, this.ItemHeight);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue