Fix scrolling on lists.

This commit is contained in:
Ottermandias 2021-09-17 16:28:47 +02:00
parent c3d0d24713
commit fb909aaf87

View file

@ -8,17 +8,19 @@ namespace Glamourer.Gui
{ {
public class ComboWithFilter<T> public class ComboWithFilter<T>
{ {
private readonly string _label; private readonly string _label;
private readonly string _filterLabel; private readonly string _filterLabel;
private readonly string _listLabel; private readonly string _listLabel;
private string _currentFilter = string.Empty; private string _currentFilter = string.Empty;
private string _currentFilterLower = string.Empty; private string _currentFilterLower = string.Empty;
private bool _focus; private bool _focus;
private readonly float _size; private readonly float _size;
private float _previewSize; private float _previewSize;
private readonly IReadOnlyList<T> _items; private readonly IReadOnlyList<T> _items;
private readonly IReadOnlyList<string> _itemNamesLower; private readonly IReadOnlyList<(string, int)> _itemNamesLower;
private readonly Func<T, string> _itemToName; private readonly Func<T, string> _itemToName;
private IReadOnlyList<(string, int)> _currentItemNames;
private bool _needsClear;
public Action? PrePreview; public Action? PrePreview;
public Action? PostPreview; public Action? PostPreview;
@ -32,6 +34,22 @@ namespace Glamourer.Gui
public ImGuiComboFlags Flags { get; set; } = ImGuiComboFlags.None; public ImGuiComboFlags Flags { get; set; } = ImGuiComboFlags.None;
public int ItemsAtOnce { get; set; } = 12; public int ItemsAtOnce { get; set; } = 12;
private void UpdateFilter(string newFilter)
{
if (newFilter == _currentFilter)
return;
var lower = newFilter.ToLowerInvariant();
if (_currentFilterLower.Any() && lower.Contains(_currentFilterLower))
_currentItemNames = _currentItemNames.Where(p => p.Item1.Contains(lower)).ToArray();
else if (lower.Any())
_currentItemNames = _itemNamesLower.Where(p => p.Item1.Contains(lower)).ToArray();
else
_currentItemNames = _itemNamesLower;
_currentFilter = newFilter;
_currentFilterLower = lower;
}
public ComboWithFilter(string label, float size, float previewSize, IReadOnlyList<T> items, Func<T, string> itemToName) public ComboWithFilter(string label, float size, float previewSize, IReadOnlyList<T> items, Func<T, string> itemToName)
{ {
_label = label; _label = label;
@ -42,26 +60,28 @@ namespace Glamourer.Gui
_size = size; _size = size;
_previewSize = previewSize; _previewSize = previewSize;
_itemNamesLower = _items.Select(i => _itemToName(i).ToLowerInvariant()).ToList(); _itemNamesLower = _items.Select((i, idx) => (_itemToName(i).ToLowerInvariant(), idx)).ToArray();
_currentItemNames = _itemNamesLower;
} }
public ComboWithFilter(string label, ComboWithFilter<T> other) public ComboWithFilter(string label, ComboWithFilter<T> other)
{ {
_label = label; _label = label;
_filterLabel = $"##_{label}_filter"; _filterLabel = $"##_{label}_filter";
_listLabel = $"##_{label}_list"; _listLabel = $"##_{label}_list";
_itemToName = other._itemToName; _itemToName = other._itemToName;
_items = other._items; _items = other._items;
_itemNamesLower = other._itemNamesLower; _itemNamesLower = other._itemNamesLower;
_size = other._size; _currentItemNames = other._currentItemNames;
_previewSize = other._previewSize; _size = other._size;
PrePreview = other.PrePreview; _previewSize = other._previewSize;
PostPreview = other.PostPreview; PrePreview = other.PrePreview;
CreateSelectable = other.CreateSelectable; PostPreview = other.PostPreview;
PreList = other.PreList; CreateSelectable = other.CreateSelectable;
PostList = other.PostList; PreList = other.PreList;
HeightPerItem = other.HeightPerItem; PostList = other.PostList;
Flags = other.Flags; HeightPerItem = other.HeightPerItem;
Flags = other.Flags;
} }
private bool DrawList(string currentName, out int numItems, out int nodeIdx, ref T? value) private bool DrawList(string currentName, out int numItems, out int nodeIdx, ref T? value)
@ -83,7 +103,6 @@ namespace Glamourer.Gui
_focus = true; _focus = true;
} }
var scrollY = Math.Max((int) (ImGui.GetScrollY() / _heightPerItem) - 1, 0); var scrollY = Math.Max((int) (ImGui.GetScrollY() / _heightPerItem) - 1, 0);
var restHeight = scrollY * _heightPerItem; var restHeight = scrollY * _heightPerItem;
numItems = 0; numItems = 0;
@ -92,38 +111,34 @@ namespace Glamourer.Gui
if (restHeight > 0) if (restHeight > 0)
ImGui.Dummy(Vector2.UnitY * restHeight); ImGui.Dummy(Vector2.UnitY * restHeight);
for (var i = scrollY; i < _items.Count; ++i) for (var i = scrollY; i < _currentItemNames.Count; ++i)
{ {
if (!_itemNamesLower[i].Contains(_currentFilterLower)) if (++numItems > ItemsAtOnce + 2)
continue; continue;
++numItems; nodeIdx = _currentItemNames[i].Item2;
if (numItems <= ItemsAtOnce + 2) var item = _items[nodeIdx]!;
bool success;
if (CreateSelectable != null)
{ {
nodeIdx = i; success = CreateSelectable(item);
var item = _items[i]!; }
bool success; else
if (CreateSelectable != null) {
{ var name = _itemToName(item);
success = CreateSelectable(item); success = ImGui.Selectable(name, name == currentName);
} }
else
{
var name = _itemToName(item);
success = ImGui.Selectable(name, name == currentName);
}
if (success) if (success)
{ {
value = item; value = item;
ImGui.CloseCurrentPopup(); ImGui.CloseCurrentPopup();
ret = true; ret = true;
}
} }
} }
if (numItems > ItemsAtOnce + 2) if (_currentItemNames.Count > ItemsAtOnce + 2)
ImGui.Dummy(Vector2.UnitY * (numItems - ItemsAtOnce - 2) * _heightPerItem); ImGui.Dummy(Vector2.UnitY * (_currentItemNames.Count - ItemsAtOnce - 2 - scrollY) * _heightPerItem);
} }
finally finally
{ {
@ -143,13 +158,18 @@ namespace Glamourer.Gui
PrePreview?.Invoke(); PrePreview?.Invoke();
if (!ImGui.BeginCombo(_label, currentName, Flags)) if (!ImGui.BeginCombo(_label, currentName, Flags))
{ {
_focus = false; if (_needsClear)
_currentFilter = string.Empty; {
_currentFilterLower = string.Empty; _needsClear = false;
_focus = false;
UpdateFilter(string.Empty);
}
PostPreview?.Invoke(); PostPreview?.Invoke();
return false; return false;
} }
_needsClear = true;
PostPreview?.Invoke(); PostPreview?.Invoke();
_heightPerItem = HeightPerItem ?? ImGui.GetTextLineHeightWithSpacing(); _heightPerItem = HeightPerItem ?? ImGui.GetTextLineHeightWithSpacing();
@ -158,8 +178,9 @@ namespace Glamourer.Gui
try try
{ {
ImGui.SetNextItemWidth(-1); ImGui.SetNextItemWidth(-1);
if (ImGui.InputTextWithHint(_filterLabel, "Filter...", ref _currentFilter, 255)) var tmp = _currentFilter;
_currentFilterLower = _currentFilter.ToLowerInvariant(); if (ImGui.InputTextWithHint(_filterLabel, "Filter...", ref tmp, 255))
UpdateFilter(tmp);
var isFocused = ImGui.IsItemActive(); var isFocused = ImGui.IsItemActive();
if (!_focus) if (!_focus)