forked from liza/Questionable
Add search field to statistics/progress window
This commit is contained in:
parent
0d2ce03164
commit
65b7643b3e
@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
|
||||||
|
"Author": "liza",
|
||||||
|
"QuestSequence": [
|
||||||
|
{
|
||||||
|
"Sequence": 0,
|
||||||
|
"Steps": [
|
||||||
|
{
|
||||||
|
"DataId": 1026746,
|
||||||
|
"Position": {
|
||||||
|
"X": -55.588684,
|
||||||
|
"Y": 206.50021,
|
||||||
|
"Z": 18.57019
|
||||||
|
},
|
||||||
|
"TerritoryId": 478,
|
||||||
|
"InteractionType": "AcceptQuest"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Sequence": 1,
|
||||||
|
"Steps": [
|
||||||
|
{
|
||||||
|
"DataId": 1026747,
|
||||||
|
"Position": {
|
||||||
|
"X": 63.126587,
|
||||||
|
"Y": 205.63266,
|
||||||
|
"Z": 22.049255
|
||||||
|
},
|
||||||
|
"TerritoryId": 478,
|
||||||
|
"InteractionType": "Interact"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Sequence": 255,
|
||||||
|
"Steps": [
|
||||||
|
{
|
||||||
|
"DataId": 1026750,
|
||||||
|
"Position": {
|
||||||
|
"X": -62.6994,
|
||||||
|
"Y": 206.50021,
|
||||||
|
"Z": 16.617004
|
||||||
|
},
|
||||||
|
"TerritoryId": 478,
|
||||||
|
"InteractionType": "CompleteQuest"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -17,14 +17,14 @@ internal sealed class QuestInfo
|
|||||||
|
|
||||||
string suffix = QuestId switch
|
string suffix = QuestId switch
|
||||||
{
|
{
|
||||||
85 => " (LNC)",
|
85 => " (Lancer)",
|
||||||
108 => " (MRD)",
|
108 => " (Marauder)",
|
||||||
109 => " (ACN)",
|
109 => " (Arcanist)",
|
||||||
123 => " (ARC)",
|
123 => " (Archer)",
|
||||||
124 => " (CNJ)",
|
124 => " (Conjurer)",
|
||||||
568 => " (GLA)",
|
568 => " (Gladiator)",
|
||||||
569 => " (PGL)",
|
569 => " (Pugilist)",
|
||||||
570 => " (THM)",
|
570 => " (Thaumaturge)",
|
||||||
673 => " (Ul'dah)",
|
673 => " (Ul'dah)",
|
||||||
674 => " (Limsa/Gridania)",
|
674 => " (Limsa/Gridania)",
|
||||||
_ => "",
|
_ => "",
|
||||||
|
@ -6,6 +6,7 @@ using System.Numerics;
|
|||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Colors;
|
using Dalamud.Interface.Colors;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
|
using Dalamud.Plugin;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using LLib.ImGui;
|
using LLib.ImGui;
|
||||||
@ -23,6 +24,7 @@ internal sealed class JournalProgressWindow : LWindow, IDisposable
|
|||||||
private readonly GameFunctions _gameFunctions;
|
private readonly GameFunctions _gameFunctions;
|
||||||
private readonly UiUtils _uiUtils;
|
private readonly UiUtils _uiUtils;
|
||||||
private readonly QuestTooltipComponent _questTooltipComponent;
|
private readonly QuestTooltipComponent _questTooltipComponent;
|
||||||
|
private readonly IDalamudPluginInterface _pluginInterface;
|
||||||
private readonly IClientState _clientState;
|
private readonly IClientState _clientState;
|
||||||
private readonly ICommandManager _commandManager;
|
private readonly ICommandManager _commandManager;
|
||||||
|
|
||||||
@ -30,11 +32,15 @@ internal sealed class JournalProgressWindow : LWindow, IDisposable
|
|||||||
private readonly Dictionary<JournalData.Category, (int Available, int Completed)> _categoryCounts = new();
|
private readonly Dictionary<JournalData.Category, (int Available, int Completed)> _categoryCounts = new();
|
||||||
private readonly Dictionary<JournalData.Section, (int Available, int Completed)> _sectionCounts = new();
|
private readonly Dictionary<JournalData.Section, (int Available, int Completed)> _sectionCounts = new();
|
||||||
|
|
||||||
|
private List<FilteredSection> _filteredSections = [];
|
||||||
|
private string _searchText = string.Empty;
|
||||||
|
|
||||||
public JournalProgressWindow(JournalData journalData,
|
public JournalProgressWindow(JournalData journalData,
|
||||||
QuestRegistry questRegistry,
|
QuestRegistry questRegistry,
|
||||||
GameFunctions gameFunctions,
|
GameFunctions gameFunctions,
|
||||||
UiUtils uiUtils,
|
UiUtils uiUtils,
|
||||||
QuestTooltipComponent questTooltipComponent,
|
QuestTooltipComponent questTooltipComponent,
|
||||||
|
IDalamudPluginInterface pluginInterface,
|
||||||
IClientState clientState,
|
IClientState clientState,
|
||||||
ICommandManager commandManager)
|
ICommandManager commandManager)
|
||||||
: base("Journal Progress###QuestionableJournalProgress")
|
: base("Journal Progress###QuestionableJournalProgress")
|
||||||
@ -44,6 +50,7 @@ internal sealed class JournalProgressWindow : LWindow, IDisposable
|
|||||||
_gameFunctions = gameFunctions;
|
_gameFunctions = gameFunctions;
|
||||||
_uiUtils = uiUtils;
|
_uiUtils = uiUtils;
|
||||||
_questTooltipComponent = questTooltipComponent;
|
_questTooltipComponent = questTooltipComponent;
|
||||||
|
_pluginInterface = pluginInterface;
|
||||||
_clientState = clientState;
|
_clientState = clientState;
|
||||||
_commandManager = commandManager;
|
_commandManager = commandManager;
|
||||||
|
|
||||||
@ -59,9 +66,15 @@ internal sealed class JournalProgressWindow : LWindow, IDisposable
|
|||||||
|
|
||||||
private void OnQuestsReloaded(object? sender, EventArgs e) => RefreshCounts();
|
private void OnQuestsReloaded(object? sender, EventArgs e) => RefreshCounts();
|
||||||
|
|
||||||
public override void OnOpen() => RefreshCounts();
|
public override void OnOpen()
|
||||||
|
{
|
||||||
|
UpdateFilter();
|
||||||
|
RefreshCounts();
|
||||||
|
}
|
||||||
|
|
||||||
public override void Draw()
|
public override void Draw()
|
||||||
|
{
|
||||||
|
if (ImGui.CollapsingHeader("Explanation", ImGuiTreeNodeFlags.DefaultOpen))
|
||||||
{
|
{
|
||||||
ImGui.Text("The list below contains all quests that appear in your journal.");
|
ImGui.Text("The list below contains all quests that appear in your journal.");
|
||||||
ImGui.BulletText("'Supported' lists quests that Questionable can do for you");
|
ImGui.BulletText("'Supported' lists quests that Questionable can do for you");
|
||||||
@ -72,7 +85,14 @@ internal sealed class JournalProgressWindow : LWindow, IDisposable
|
|||||||
ImGui.Spacing();
|
ImGui.Spacing();
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
ImGui.Spacing();
|
ImGui.Spacing();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
|
||||||
|
if (ImGui.InputTextWithHint(string.Empty, "Search quests and categories", ref _searchText, 256))
|
||||||
|
UpdateFilter();
|
||||||
|
|
||||||
|
if (_filteredSections.Count > 0)
|
||||||
|
{
|
||||||
using var table = ImRaii.Table("Quests", 3, ImGuiTableFlags.NoSavedSettings);
|
using var table = ImRaii.Table("Quests", 3, ImGuiTableFlags.NoSavedSettings);
|
||||||
if (!table)
|
if (!table)
|
||||||
return;
|
return;
|
||||||
@ -82,84 +102,87 @@ internal sealed class JournalProgressWindow : LWindow, IDisposable
|
|||||||
ImGui.TableSetupColumn("Completed", ImGuiTableColumnFlags.WidthFixed, 120 * ImGui.GetIO().FontGlobalScale);
|
ImGui.TableSetupColumn("Completed", ImGuiTableColumnFlags.WidthFixed, 120 * ImGui.GetIO().FontGlobalScale);
|
||||||
ImGui.TableHeadersRow();
|
ImGui.TableHeadersRow();
|
||||||
|
|
||||||
foreach (var section in _journalData.Sections)
|
foreach (var section in _filteredSections)
|
||||||
{
|
{
|
||||||
DrawSection(section);
|
DrawSection(section);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
ImGui.Text("No quest or category matches your search text.");
|
||||||
|
}
|
||||||
|
|
||||||
private void DrawSection(JournalData.Section section)
|
private void DrawSection(FilteredSection filter)
|
||||||
{
|
{
|
||||||
if (section.QuestCount == 0)
|
if (filter.Section.QuestCount == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
(int supported, int completed) = _sectionCounts.GetValueOrDefault(section);
|
(int supported, int completed) = _sectionCounts.GetValueOrDefault(filter.Section);
|
||||||
|
|
||||||
ImGui.TableNextRow();
|
ImGui.TableNextRow();
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
|
|
||||||
bool open = ImGui.TreeNodeEx(section.Name, ImGuiTreeNodeFlags.SpanFullWidth);
|
bool open = ImGui.TreeNodeEx(filter.Section.Name, ImGuiTreeNodeFlags.SpanFullWidth);
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
DrawCount(supported, section.QuestCount);
|
DrawCount(supported, filter.Section.QuestCount);
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
DrawCount(completed, section.QuestCount);
|
DrawCount(completed, filter.Section.QuestCount);
|
||||||
|
|
||||||
if (open)
|
if (open)
|
||||||
{
|
{
|
||||||
foreach (var category in section.Categories)
|
foreach (var category in filter.Categories)
|
||||||
DrawCategory(category);
|
DrawCategory(category);
|
||||||
|
|
||||||
ImGui.TreePop();
|
ImGui.TreePop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawCategory(JournalData.Category category)
|
private void DrawCategory(FilteredCategory filter)
|
||||||
{
|
{
|
||||||
if (category.QuestCount == 0)
|
if (filter.Category.QuestCount == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
(int supported, int completed) = _categoryCounts.GetValueOrDefault(category);
|
(int supported, int completed) = _categoryCounts.GetValueOrDefault(filter.Category);
|
||||||
|
|
||||||
ImGui.TableNextRow();
|
ImGui.TableNextRow();
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
|
|
||||||
bool open = ImGui.TreeNodeEx(category.Name, ImGuiTreeNodeFlags.SpanFullWidth);
|
bool open = ImGui.TreeNodeEx(filter.Category.Name, ImGuiTreeNodeFlags.SpanFullWidth);
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
DrawCount(supported, category.QuestCount);
|
DrawCount(supported, filter.Category.QuestCount);
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
DrawCount(completed, category.QuestCount);
|
DrawCount(completed, filter.Category.QuestCount);
|
||||||
|
|
||||||
if (open)
|
if (open)
|
||||||
{
|
{
|
||||||
foreach (var genre in category.Genres)
|
foreach (var genre in filter.Genres)
|
||||||
DrawGenre(genre);
|
DrawGenre(genre);
|
||||||
|
|
||||||
ImGui.TreePop();
|
ImGui.TreePop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawGenre(JournalData.Genre genre)
|
private void DrawGenre(FilteredGenre filter)
|
||||||
{
|
{
|
||||||
if (genre.QuestCount == 0)
|
if (filter.Genre.QuestCount == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
(int supported, int completed) = _genreCounts.GetValueOrDefault(genre);
|
(int supported, int completed) = _genreCounts.GetValueOrDefault(filter.Genre);
|
||||||
|
|
||||||
ImGui.TableNextRow();
|
ImGui.TableNextRow();
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
|
|
||||||
bool open = ImGui.TreeNodeEx(genre.Name, ImGuiTreeNodeFlags.SpanFullWidth);
|
bool open = ImGui.TreeNodeEx(filter.Genre.Name, ImGuiTreeNodeFlags.SpanFullWidth);
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
DrawCount(supported, genre.QuestCount);
|
DrawCount(supported, filter.Genre.QuestCount);
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
DrawCount(completed, genre.QuestCount);
|
DrawCount(completed, filter.Genre.QuestCount);
|
||||||
|
|
||||||
if (open)
|
if (open)
|
||||||
{
|
{
|
||||||
foreach (var quest in genre.Quests)
|
foreach (var quest in filter.Quests)
|
||||||
DrawQuest(quest);
|
DrawQuest(quest);
|
||||||
|
|
||||||
ImGui.TreePop();
|
ImGui.TreePop();
|
||||||
@ -186,9 +209,15 @@ internal sealed class JournalProgressWindow : LWindow, IDisposable
|
|||||||
_questTooltipComponent.Draw(questInfo);
|
_questTooltipComponent.Draw(questInfo);
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
List<string> authors = quest?.Root.Author ?? [];
|
float spacing;
|
||||||
_uiUtils.ChecklistItem(authors.Count > 0 ? string.Join(", ", authors) : string.Empty,
|
// ReSharper disable once UnusedVariable
|
||||||
quest is { Root.Disabled: false });
|
using (var font = _pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
|
||||||
|
{
|
||||||
|
spacing = ImGui.GetColumnWidth() / 2 - ImGui.CalcTextSize(FontAwesomeIcon.Check.ToIconString()).X;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + spacing);
|
||||||
|
_uiUtils.ChecklistItem(string.Empty, quest is { Root.Disabled: false });
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
var (color, icon, text) = _uiUtils.GetQuestStyle(questInfo.QuestId);
|
var (color, icon, text) = _uiUtils.GetQuestStyle(questInfo.QuestId);
|
||||||
@ -210,6 +239,85 @@ internal sealed class JournalProgressWindow : LWindow, IDisposable
|
|||||||
ImGui.PopFont();
|
ImGui.PopFont();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateFilter()
|
||||||
|
{
|
||||||
|
Predicate<string> match;
|
||||||
|
if (string.IsNullOrWhiteSpace(_searchText))
|
||||||
|
match = _ => true;
|
||||||
|
else
|
||||||
|
match = x => x.Contains(_searchText, StringComparison.CurrentCultureIgnoreCase);
|
||||||
|
|
||||||
|
_filteredSections = _journalData.Sections
|
||||||
|
.Select(section => FilterSection(section, match))
|
||||||
|
.Where(x => x != null)
|
||||||
|
.Cast<FilteredSection>()
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FilteredSection? FilterSection(JournalData.Section section, Predicate<string> match)
|
||||||
|
{
|
||||||
|
if (match(section.Name))
|
||||||
|
{
|
||||||
|
return new FilteredSection(section,
|
||||||
|
section.Categories
|
||||||
|
.Select(x => FilterCategory(x, _ => true))
|
||||||
|
.Cast<FilteredCategory>()
|
||||||
|
.ToList());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
List<FilteredCategory> filteredCategories = section.Categories
|
||||||
|
.Select(category => FilterCategory(category, match))
|
||||||
|
.Where(x => x != null)
|
||||||
|
.Cast<FilteredCategory>()
|
||||||
|
.ToList();
|
||||||
|
if (filteredCategories.Count > 0)
|
||||||
|
return new FilteredSection(section, filteredCategories);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FilteredCategory? FilterCategory(JournalData.Category category, Predicate<string> match)
|
||||||
|
{
|
||||||
|
if (match(category.Name))
|
||||||
|
{
|
||||||
|
return new FilteredCategory(category,
|
||||||
|
category.Genres
|
||||||
|
.Select(x => FilterGenre(x, _ => true))
|
||||||
|
.Cast<FilteredGenre>()
|
||||||
|
.ToList());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
List<FilteredGenre> filteredGenres = category.Genres
|
||||||
|
.Select(genre => FilterGenre(genre, match))
|
||||||
|
.Where(x => x != null)
|
||||||
|
.Cast<FilteredGenre>()
|
||||||
|
.ToList();
|
||||||
|
if (filteredGenres.Count > 0)
|
||||||
|
return new FilteredCategory(category, filteredGenres);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FilteredGenre? FilterGenre(JournalData.Genre genre, Predicate<string> match)
|
||||||
|
{
|
||||||
|
if (match(genre.Name))
|
||||||
|
return new FilteredGenre(genre, genre.Quests);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
List<QuestInfo> filteredQuests = genre.Quests
|
||||||
|
.Where(x => match(x.Name))
|
||||||
|
.ToList();
|
||||||
|
if (filteredQuests.Count > 0)
|
||||||
|
return new FilteredGenre(genre, filteredQuests);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private void RefreshCounts()
|
private void RefreshCounts()
|
||||||
{
|
{
|
||||||
_genreCounts.Clear();
|
_genreCounts.Clear();
|
||||||
@ -265,4 +373,10 @@ internal sealed class JournalProgressWindow : LWindow, IDisposable
|
|||||||
_clientState.Logout -= ClearCounts;
|
_clientState.Logout -= ClearCounts;
|
||||||
_clientState.Login -= RefreshCounts;
|
_clientState.Login -= RefreshCounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sealed record FilteredSection(JournalData.Section Section, List<FilteredCategory> Categories);
|
||||||
|
|
||||||
|
private sealed record FilteredCategory(JournalData.Category Category, List<FilteredGenre> Genres);
|
||||||
|
|
||||||
|
private sealed record FilteredGenre(JournalData.Genre Genre, List<QuestInfo> Quests);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user