diff --git a/Questionable/Data/JournalData.cs b/Questionable/Data/JournalData.cs index 52f37e22..c2983fe5 100644 --- a/Questionable/Data/JournalData.cs +++ b/Questionable/Data/JournalData.cs @@ -10,44 +10,30 @@ namespace Questionable.Data; internal sealed class JournalData { - private readonly IDataManager _dataManager; - private readonly QuestData _questData; public JournalData(IDataManager dataManager, QuestData questData) { - _dataManager = dataManager; - _questData = questData; - - Reload(); - } - - public List Genres { get; set; } - public List Categories { get; set; } - public List
Sections { get; set; } - - public void Reload() - { - var genres = _dataManager.GetExcelSheet() + var genres = dataManager.GetExcelSheet() .Where(x => x.RowId > 0 && x.Icon > 0) - .Select(x => new Genre(x, _questData.GetAllByJournalGenre(x.RowId))) + .Select(x => new Genre(x, questData.GetAllByJournalGenre(x.RowId))) .ToList(); - var limsaStart = _dataManager.GetExcelSheet().GetRow(1); - var gridaniaStart = _dataManager.GetExcelSheet().GetRow(2); - var uldahStart = _dataManager.GetExcelSheet().GetRow(3); + var limsaStart = dataManager.GetExcelSheet().GetRow(1); + var gridaniaStart = dataManager.GetExcelSheet().GetRow(2); + var uldahStart = dataManager.GetExcelSheet().GetRow(3); var genreLimsa = new Genre(uint.MaxValue - 3, "Starting in Limsa Lominsa", 1, new uint[] { 108, 109 }.Concat(limsaStart.QuestRedoParam.Select(x => x.Quest.RowId)) .Where(x => x != 0) - .Select(x => _questData.GetQuestInfo(new QuestId((ushort)(x & 0xFFFF)))) + .Select(x => questData.GetQuestInfo(new QuestId((ushort)(x & 0xFFFF)))) .ToList()); var genreGridania = new Genre(uint.MaxValue - 2, "Starting in Gridania", 1, new uint[] { 85, 123, 124 }.Concat(gridaniaStart.QuestRedoParam.Select(x => x.Quest.RowId)) .Where(x => x != 0) - .Select(x => _questData.GetQuestInfo(new QuestId((ushort)(x & 0xFFFF)))) + .Select(x => questData.GetQuestInfo(new QuestId((ushort)(x & 0xFFFF)))) .ToList()); var genreUldah = new Genre(uint.MaxValue - 1, "Starting in Ul'dah", 1, new uint[] { 568, 569, 570 }.Concat(uldahStart.QuestRedoParam.Select(x => x.Quest.RowId)) .Where(x => x != 0) - .Select(x => _questData.GetQuestInfo(new QuestId((ushort)(x & 0xFFFF)))) + .Select(x => questData.GetQuestInfo(new QuestId((ushort)(x & 0xFFFF)))) .ToList()); genres.InsertRange(0, [genreLimsa, genreGridania, genreUldah]); genres.Single(x => x.Id == 1) @@ -56,15 +42,19 @@ internal sealed class JournalData genreLimsa.Quests.Contains(x) || genreGridania.Quests.Contains(x) || genreUldah.Quests.Contains(x)); Genres = genres.ToList(); - Categories = _dataManager.GetExcelSheet() + Categories = dataManager.GetExcelSheet() .Where(x => x.RowId > 0) .Select(x => new Category(x, Genres.Where(y => y.CategoryId == x.RowId).ToList())) .ToList(); - Sections = _dataManager.GetExcelSheet() + Sections = dataManager.GetExcelSheet() .Select(x => new Section(x, Categories.Where(y => y.SectionId == x.RowId).ToList())) .ToList(); } + public List Genres { get; } + public List Categories { get; } + public List
Sections { get; } + internal sealed class Genre { public Genre(JournalGenre journalGenre, List quests) diff --git a/Questionable/Windows/JournalComponents/QuestJournalComponent.cs b/Questionable/Windows/JournalComponents/QuestJournalComponent.cs index de5a7bb8..437591ff 100644 --- a/Questionable/Windows/JournalComponents/QuestJournalComponent.cs +++ b/Questionable/Windows/JournalComponents/QuestJournalComponent.cs @@ -31,14 +31,12 @@ internal sealed class QuestJournalComponent private readonly IDalamudPluginInterface _pluginInterface; private readonly QuestJournalUtils _questJournalUtils; private readonly QuestValidator _questValidator; - private readonly IPluginLog _log; private List _filteredSections = []; - private string _searchText = string.Empty; public QuestJournalComponent(JournalData journalData, QuestRegistry questRegistry, QuestFunctions questFunctions, UiUtils uiUtils, QuestTooltipComponent questTooltipComponent, IDalamudPluginInterface pluginInterface, - QuestJournalUtils questJournalUtils, QuestValidator questValidator, IPluginLog log) + QuestJournalUtils questJournalUtils, QuestValidator questValidator) { _journalData = journalData; _questRegistry = questRegistry; @@ -48,9 +46,10 @@ internal sealed class QuestJournalComponent _pluginInterface = pluginInterface; _questJournalUtils = questJournalUtils; _questValidator = questValidator; - _log = log; } + internal FilterConfiguration Filter { get; } = new(); + public void DrawQuests() { using var tab = ImRaii.TabItem("Quests"); @@ -74,7 +73,7 @@ internal sealed class QuestJournalComponent ImGui.SameLine(); ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); - if (ImGui.InputTextWithHint(string.Empty, "Search quests and categories", ref _searchText, 256)) + if (ImGui.InputTextWithHint(string.Empty, "Search quests and categories", ref Filter.SearchText, 256)) UpdateFilter(); if (_filteredSections.Count > 0) @@ -92,7 +91,7 @@ internal sealed class QuestJournalComponent DrawSection(section); } else - ImGui.Text("No quest or category matches your search text."); + ImGui.Text("No quest or category matches your search."); } private void DrawSection(FilteredSection filter) @@ -239,104 +238,63 @@ internal sealed class QuestJournalComponent public void UpdateFilter() { - _journalData.Reload(); - Predicate match = string.IsNullOrWhiteSpace(_searchText) ? x => true : x => x.Contains(_searchText, StringComparison.CurrentCultureIgnoreCase); - _filteredSections = _journalData.Sections - .Select(section => FilterSection(section, match)) - .Where(x => x != null) - .Cast() + .Select(x => FilterSection(x, Filter)) + .Where(x => x.Categories.Count > 0) .ToList(); - for (int i = 0; i < _filteredSections.Count; i++) - { - var section = _filteredSections[i]; - for (int s = 0; s < section.Categories.Count; s++) - { - var category = section.Categories[s]; - for (int c = 0; c < category.Genres.Count; c++) - { - var genre = category.Genres[c]; - for (int g = 0; g < genre.Quests.Count; g++) - { - var quest = genre.Quests[g]; - - //All Quest Filter conditions checked here, cause we just love IEnumerable - if (QuestJournalUtils.AvailableOnly && !_questFunctions.IsReadyToAcceptQuest(quest.QuestId) || - QuestJournalUtils.HideNoPaths && !_questRegistry.TryGetQuest(quest.QuestId, out _)) - { - genre.Quests.Remove(quest); - g--; - } - } - } - } - } - RefreshCounts(); } - private static FilteredSection? FilterSection(JournalData.Section section, Predicate match) + private FilteredSection FilterSection(JournalData.Section section, FilterConfiguration filter) { - if (match(section.Name)) + IEnumerable filteredCategories; + if (IsCategorySectionGenreMatch(filter, section.Name)) { - return new FilteredSection(section, - section.Categories - .Select(x => FilterCategory(x, _ => true)) - .Cast() - .ToList()); + filteredCategories = section.Categories + .Select(x => FilterCategory(x, filter.WithoutName())); } else { - List filteredCategories = section.Categories - .Select(category => FilterCategory(category, match)) - .Where(x => x != null) - .Cast() - .ToList(); - if (filteredCategories.Count > 0) - return new FilteredSection(section, filteredCategories); - - return null; + filteredCategories = section.Categories + .Select(category => FilterCategory(category, filter)); } + + return new FilteredSection(section, filteredCategories.Where(x => x.Genres.Count > 0).ToList()); } - private static FilteredCategory? FilterCategory(JournalData.Category category, Predicate match) + private FilteredCategory FilterCategory(JournalData.Category category, FilterConfiguration filter) { - if (match(category.Name)) + IEnumerable filteredGenres; + if (IsCategorySectionGenreMatch(filter, category.Name)) { - return new FilteredCategory(category, - category.Genres - .Select(x => FilterGenre(x, _ => true)!) - .ToList()); + filteredGenres = category.Genres + .Select(x => FilterGenre(x, filter.WithoutName())); } else { - List filteredGenres = category.Genres - .Select(genre => FilterGenre(genre, match)) - .Where(x => x != null) - .Cast() - .ToList(); - if (filteredGenres.Count > 0) - return new FilteredCategory(category, filteredGenres); - - return null; + filteredGenres = category.Genres + .Select(genre => FilterGenre(genre, filter)); } + + return new FilteredCategory(category, filteredGenres.Where(x => x.Quests.Count > 0).ToList()); } - private static FilteredGenre? FilterGenre(JournalData.Genre genre, Predicate match) + private FilteredGenre FilterGenre(JournalData.Genre genre, FilterConfiguration filter) { - if (match(genre.Name)) - return new FilteredGenre(genre, genre.Quests); + IEnumerable filteredQuests; + if (IsCategorySectionGenreMatch(filter, genre.Name)) + { + filteredQuests = genre.Quests + .Where(x => IsQuestMatch(filter.WithoutName(), x)); + } else { - List filteredQuests = genre.Quests - .Where(x => match(x.Name)) - .ToList(); - if (filteredQuests.Count > 0) - return new FilteredGenre(genre, filteredQuests); + filteredQuests = genre.Quests + .Where(x => IsQuestMatch(filter, x)); } - return null; + return new FilteredGenre(genre, filteredQuests.ToList()); } internal void RefreshCounts() @@ -396,6 +354,28 @@ internal sealed class QuestJournalComponent _sectionCounts[sectionCount.Key] = sectionCount.Value with { Completed = 0 }; } + private static bool IsCategorySectionGenreMatch(FilterConfiguration filter, string name) + { + return string.IsNullOrEmpty(filter.SearchText) || + name.Contains(filter.SearchText, StringComparison.CurrentCultureIgnoreCase); + } + + private bool IsQuestMatch(FilterConfiguration filter, IQuestInfo questInfo) + { + if (!string.IsNullOrEmpty(filter.SearchText) && + !questInfo.Name.Contains(filter.SearchText, StringComparison.CurrentCultureIgnoreCase)) + return false; + + if (filter.AvailableOnly && !_questFunctions.IsReadyToAcceptQuest(questInfo.QuestId)) + return false; + + if (filter.HideNoPaths && + (!_questRegistry.TryGetQuest(questInfo.QuestId, out var quest) || quest.Root.Disabled)) + return false; + + return true; + } + private sealed record FilteredSection(JournalData.Section Section, List Categories); private sealed record FilteredCategory(JournalData.Category Category, List Genres); @@ -409,4 +389,19 @@ internal sealed class QuestJournalComponent { } } + + internal sealed class FilterConfiguration + { + public string SearchText = string.Empty; + public bool AvailableOnly; + public bool HideNoPaths; + + public bool AdvancedFiltersActive => AvailableOnly || HideNoPaths; + + public FilterConfiguration WithoutName() => new() + { + AvailableOnly = AvailableOnly, + HideNoPaths = HideNoPaths + }; + } } diff --git a/Questionable/Windows/JournalComponents/QuestJournalUtils.cs b/Questionable/Windows/JournalComponents/QuestJournalUtils.cs index 8d43d7f7..906dd9d2 100644 --- a/Questionable/Windows/JournalComponents/QuestJournalUtils.cs +++ b/Questionable/Windows/JournalComponents/QuestJournalUtils.cs @@ -7,6 +7,7 @@ using Questionable.Functions; using Questionable.Model; using Questionable.Model.Questing; using System; +using Dalamud.Interface.Colors; namespace Questionable.Windows.JournalComponents; @@ -16,9 +17,6 @@ internal sealed class QuestJournalUtils private readonly QuestFunctions _questFunctions; private readonly ICommandManager _commandManager; - public static bool AvailableOnly; - public static bool HideNoPaths; - public QuestJournalUtils(QuestController questController, QuestFunctions questFunctions, ICommandManager commandManager) { @@ -50,19 +48,17 @@ internal sealed class QuestJournalUtils } } - internal static void ShowFilterContextMenu(QuestJournalComponent journalUI) + internal static void ShowFilterContextMenu(QuestJournalComponent journalUi) { - if (ImGuiComponents.IconButton(Dalamud.Interface.FontAwesomeIcon.Filter)) - ImGui.OpenPopup($"##QuestFilters"); + if (ImGuiComponents.IconButtonWithText(Dalamud.Interface.FontAwesomeIcon.Filter, "Filter")) + ImGui.OpenPopup("##QuestFilters"); - using var popup = ImRaii.Popup($"##QuestFilters"); + using var popup = ImRaii.Popup("##QuestFilters"); if (!popup) return; - if (ImGui.Checkbox("Show only Available Quests", ref AvailableOnly) || - ImGui.Checkbox("Hide Quests Without Path", ref HideNoPaths)) - journalUI.UpdateFilter(); - - + if (ImGui.Checkbox("Show only Available Quests", ref journalUi.Filter.AvailableOnly) || + ImGui.Checkbox("Hide Quests Without Path", ref journalUi.Filter.HideNoPaths)) + journalUi.UpdateFilter(); } }