From fb909aaf87c8f24323d0705c508893053b101e60 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Fri, 17 Sep 2021 16:28:47 +0200 Subject: [PATCH] Fix scrolling on lists. --- Glamourer/Gui/ComboWithFilter.cs | 143 ++++++++++++++++++------------- 1 file changed, 82 insertions(+), 61 deletions(-) diff --git a/Glamourer/Gui/ComboWithFilter.cs b/Glamourer/Gui/ComboWithFilter.cs index f284437..9a63425 100644 --- a/Glamourer/Gui/ComboWithFilter.cs +++ b/Glamourer/Gui/ComboWithFilter.cs @@ -8,17 +8,19 @@ namespace Glamourer.Gui { public class ComboWithFilter { - private readonly string _label; - private readonly string _filterLabel; - private readonly string _listLabel; - private string _currentFilter = string.Empty; - private string _currentFilterLower = string.Empty; - private bool _focus; - private readonly float _size; - private float _previewSize; - private readonly IReadOnlyList _items; - private readonly IReadOnlyList _itemNamesLower; - private readonly Func _itemToName; + private readonly string _label; + private readonly string _filterLabel; + private readonly string _listLabel; + private string _currentFilter = string.Empty; + private string _currentFilterLower = string.Empty; + private bool _focus; + private readonly float _size; + private float _previewSize; + private readonly IReadOnlyList _items; + private readonly IReadOnlyList<(string, int)> _itemNamesLower; + private readonly Func _itemToName; + private IReadOnlyList<(string, int)> _currentItemNames; + private bool _needsClear; public Action? PrePreview; public Action? PostPreview; @@ -32,6 +34,22 @@ namespace Glamourer.Gui public ImGuiComboFlags Flags { get; set; } = ImGuiComboFlags.None; 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 items, Func itemToName) { _label = label; @@ -42,36 +60,38 @@ namespace Glamourer.Gui _size = size; _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 other) { - _label = label; - _filterLabel = $"##_{label}_filter"; - _listLabel = $"##_{label}_list"; - _itemToName = other._itemToName; - _items = other._items; - _itemNamesLower = other._itemNamesLower; - _size = other._size; - _previewSize = other._previewSize; - PrePreview = other.PrePreview; - PostPreview = other.PostPreview; - CreateSelectable = other.CreateSelectable; - PreList = other.PreList; - PostList = other.PostList; - HeightPerItem = other.HeightPerItem; - Flags = other.Flags; + _label = label; + _filterLabel = $"##_{label}_filter"; + _listLabel = $"##_{label}_list"; + _itemToName = other._itemToName; + _items = other._items; + _itemNamesLower = other._itemNamesLower; + _currentItemNames = other._currentItemNames; + _size = other._size; + _previewSize = other._previewSize; + PrePreview = other.PrePreview; + PostPreview = other.PostPreview; + CreateSelectable = other.CreateSelectable; + PreList = other.PreList; + PostList = other.PostList; + HeightPerItem = other.HeightPerItem; + Flags = other.Flags; } private bool DrawList(string currentName, out int numItems, out int nodeIdx, ref T? value) { numItems = ItemsAtOnce; nodeIdx = -1; - if (!ImGui.BeginChild(_listLabel, new Vector2(_size, ItemsAtOnce * _heightPerItem))) - { - ImGui.EndChild(); - return false; + if (!ImGui.BeginChild(_listLabel, new Vector2(_size, ItemsAtOnce * _heightPerItem))) + { + ImGui.EndChild(); + return false; } var ret = false; @@ -83,7 +103,6 @@ namespace Glamourer.Gui _focus = true; } - var scrollY = Math.Max((int) (ImGui.GetScrollY() / _heightPerItem) - 1, 0); var restHeight = scrollY * _heightPerItem; numItems = 0; @@ -92,38 +111,34 @@ namespace Glamourer.Gui if (restHeight > 0) 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; - ++numItems; - if (numItems <= ItemsAtOnce + 2) + nodeIdx = _currentItemNames[i].Item2; + var item = _items[nodeIdx]!; + bool success; + if (CreateSelectable != null) { - nodeIdx = i; - var item = _items[i]!; - bool success; - if (CreateSelectable != null) - { - success = CreateSelectable(item); - } - else - { - var name = _itemToName(item); - success = ImGui.Selectable(name, name == currentName); - } + success = CreateSelectable(item); + } + else + { + var name = _itemToName(item); + success = ImGui.Selectable(name, name == currentName); + } - if (success) - { - value = item; - ImGui.CloseCurrentPopup(); - ret = true; - } + if (success) + { + value = item; + ImGui.CloseCurrentPopup(); + ret = true; } } - if (numItems > ItemsAtOnce + 2) - ImGui.Dummy(Vector2.UnitY * (numItems - ItemsAtOnce - 2) * _heightPerItem); + if (_currentItemNames.Count > ItemsAtOnce + 2) + ImGui.Dummy(Vector2.UnitY * (_currentItemNames.Count - ItemsAtOnce - 2 - scrollY) * _heightPerItem); } finally { @@ -143,13 +158,18 @@ namespace Glamourer.Gui PrePreview?.Invoke(); if (!ImGui.BeginCombo(_label, currentName, Flags)) { - _focus = false; - _currentFilter = string.Empty; - _currentFilterLower = string.Empty; + if (_needsClear) + { + _needsClear = false; + _focus = false; + UpdateFilter(string.Empty); + } + PostPreview?.Invoke(); return false; } + _needsClear = true; PostPreview?.Invoke(); _heightPerItem = HeightPerItem ?? ImGui.GetTextLineHeightWithSpacing(); @@ -158,8 +178,9 @@ namespace Glamourer.Gui try { ImGui.SetNextItemWidth(-1); - if (ImGui.InputTextWithHint(_filterLabel, "Filter...", ref _currentFilter, 255)) - _currentFilterLower = _currentFilter.ToLowerInvariant(); + var tmp = _currentFilter; + if (ImGui.InputTextWithHint(_filterLabel, "Filter...", ref tmp, 255)) + UpdateFilter(tmp); var isFocused = ImGui.IsItemActive(); if (!_focus)