#nullable enable using T3.Core.Operator; namespace T3.Editor.UiModel.ProjectHandling; /// /// Manage the navigation between previously selected instances. /// This can also be used sort items by relevancy in search dialog. /// internal sealed class NavigationHistory { public NavigationHistory(Structure structure) { _structure = structure; } /// /// abC select D -> append... /// abcD select C -> keep order if new selection is next to current index /// abCd select A -> resort /// bcdA /// /// public void UpdateSelectedInstance(Instance instance) { var path = instance.InstancePath; var hash = GetHashForIdPath(path); if (!_idPathsByHash.TryGetValue(hash, out var previousPath)) { _idPathsByHash[hash] = path; _previousSelections.Insert(0, path); _currentIndex = 0; return; } var index = _previousSelections.IndexOf(previousPath); if (index == -1) { Log.Warning("Inconsistent navigation path"); return; } if (index == 0) { _currentIndex = 0; return; } // Keep order if (Math.Abs(index - _currentIndex) <= 1) { _currentIndex = index; return; } // Rearrange _previousSelections.RemoveAt(index); _previousSelections.Insert(0, previousPath); _currentIndex = 0; } public Instance? GetLastSelectedInstance() { if (_previousSelections.Count == 0) return null; return _previousSelections.Count < _currentIndex ? null : _structure.GetInstanceFromIdPath(_previousSelections[_currentIndex]); } public IEnumerable GetPreviouslySelectedInstances() { foreach (var path in _previousSelections) { var instance = _structure.GetInstanceFromIdPath(path); if (instance == null) continue; yield return instance; } } internal IReadOnlyList? NavigateBackwards() { while (_currentIndex < _previousSelections.Count - 1) { _currentIndex++; var path = _previousSelections[_currentIndex]; if (_structure.GetInstanceFromIdPath(path) == null) continue; return path; } return null; } internal IReadOnlyList? NavigateForward() { while (_currentIndex > 0) { _currentIndex--; var path = _previousSelections[_currentIndex]; if (_structure.GetInstanceFromIdPath(path) == null) continue; return path; } return null; } private long GetHashForIdPath(IReadOnlyList path) { long hash = 31.GetHashCode(); foreach (var id in path) { hash = hash * 31 + id.GetHashCode(); } return hash; } private int _currentIndex; private Structure _structure; private readonly List> _previousSelections = new(); private readonly Dictionary> _idPathsByHash = new(); }