From: Liza Carvelli Date: Sun, 13 Apr 2025 10:03:47 +0000 (+0200) Subject: Add plugin dependencies tab in config (which is the same as the setup window) X-Git-Tag: v5.7~3 X-Git-Url: https://git.jacobcasper.com/?a=commitdiff_plain;h=f887d5dd95d1f5bc5addaca3adcf0ae2c994e548;p=Questionable.git Add plugin dependencies tab in config (which is the same as the setup window) --- diff --git a/Questionable/QuestionablePlugin.cs b/Questionable/QuestionablePlugin.cs index f13f9069..df3229d2 100644 --- a/Questionable/QuestionablePlugin.cs +++ b/Questionable/QuestionablePlugin.cs @@ -301,6 +301,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/Questionable/Windows/ConfigComponents/GeneralConfigComponent.cs b/Questionable/Windows/ConfigComponents/GeneralConfigComponent.cs index 743ea7a9..243a4172 100644 --- a/Questionable/Windows/ConfigComponents/GeneralConfigComponent.cs +++ b/Questionable/Windows/ConfigComponents/GeneralConfigComponent.cs @@ -19,8 +19,6 @@ internal sealed class GeneralConfigComponent : ConfigComponent private static readonly List<(uint Id, string Name)> DefaultMounts = [(0, "Mount Roulette")]; private static readonly List<(EClassJob ClassJob, string Name)> DefaultClassJobs = [(EClassJob.Adventurer, "Auto (highest level/item level)")]; - private readonly CombatController _combatController; - private readonly uint[] _mountIds; private readonly string[] _mountNames; private readonly string[] _combatModuleNames = ["None", "Boss Mod (VBM)", "Wrath Combo", "Rotation Solver Reborn"]; @@ -34,13 +32,10 @@ internal sealed class GeneralConfigComponent : ConfigComponent public GeneralConfigComponent( IDalamudPluginInterface pluginInterface, Configuration configuration, - CombatController combatController, IDataManager dataManager, ClassJobUtils classJobUtils) : base(pluginInterface, configuration) { - _combatController = combatController; - var mounts = dataManager.GetExcelSheet() .Where(x => x is { RowId: > 0, Icon: > 0 }) .Select(x => (MountId: x.RowId, Name: x.Singular.ToString())) @@ -67,7 +62,7 @@ internal sealed class GeneralConfigComponent : ConfigComponent if (!tab) return; - using (ImRaii.Disabled(_combatController.IsRunning)) + { int selectedCombatModule = (int)Configuration.General.CombatModule; if (ImGui.Combo("Preferred Combat Module", ref selectedCombatModule, _combatModuleNames, diff --git a/Questionable/Windows/ConfigComponents/PluginConfigComponent.cs b/Questionable/Windows/ConfigComponents/PluginConfigComponent.cs new file mode 100644 index 00000000..b016b08f --- /dev/null +++ b/Questionable/Windows/ConfigComponents/PluginConfigComponent.cs @@ -0,0 +1,337 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using Dalamud.Interface; +using Dalamud.Interface.Colors; +using Dalamud.Interface.Components; +using Dalamud.Interface.Utility.Raii; +using Dalamud.Plugin; +using Dalamud.Plugin.Services; +using Dalamud.Utility; +using ImGuiNET; +using Questionable.Controller; +using Questionable.External; + +namespace Questionable.Windows.ConfigComponents; + +internal sealed class PluginConfigComponent : ConfigComponent +{ + private static readonly IReadOnlyList RequiredPlugins = + [ + new("vnavmesh", + "vnavmesh", + """ + vnavmesh handles the navigation within a zone, moving + your character to the next quest-related objective. + """, + new Uri("https://github.com/awgil/ffxiv_navmesh/"), + new Uri("https://puni.sh/api/repository/veyn")), + new("Lifestream", + "Lifestream", + """ + Used to travel to aethernet shards in cities. + """, + new Uri("https://github.com/NightmareXIV/Lifestream"), + new Uri("https://github.com/NightmareXIV/MyDalamudPlugins/raw/main/pluginmaster.json")), + new("TextAdvance", + "TextAdvance", + """ + Automatically accepts and turns in quests, skips cutscenes + and dialogue. + """, + new Uri("https://github.com/NightmareXIV/TextAdvance"), + new Uri("https://github.com/NightmareXIV/MyDalamudPlugins/raw/main/pluginmaster.json")), + ]; + + private static readonly ReadOnlyDictionary CombatPlugins = + new Dictionary + { + { + Configuration.ECombatModule.BossMod, + new("Boss Mod (VBM)", + "BossMod", + string.Empty, + new Uri("https://github.com/awgil/ffxiv_bossmod"), + new Uri("https://puni.sh/api/repository/veyn")) + }, + { + Configuration.ECombatModule.WrathCombo, + new PluginInfo("Wrath Combo", + "WrathCombo", + string.Empty, + new Uri("https://github.com/PunishXIV/WrathCombo"), + new Uri("https://puni.sh/api/plugins")) + }, + { + Configuration.ECombatModule.RotationSolverReborn, + new("Rotation Solver Reborn", + "RotationSolver", + string.Empty, + new Uri("https://github.com/FFXIV-CombatReborn/RotationSolverReborn"), + new Uri( + "https://raw.githubusercontent.com/FFXIV-CombatReborn/CombatRebornRepo/main/pluginmaster.json")) + }, + }.AsReadOnly(); + + private readonly IReadOnlyList _recommendedPlugins; + + private readonly Configuration _configuration; + private readonly CombatController _combatController; + private readonly IDalamudPluginInterface _pluginInterface; + private readonly UiUtils _uiUtils; + private readonly ICommandManager _commandManager; + + public PluginConfigComponent( + IDalamudPluginInterface pluginInterface, + Configuration configuration, + CombatController combatController, + UiUtils uiUtils, + ICommandManager commandManager, + AutomatonIpc automatonIpc, + PandorasBoxIpc pandorasBoxIpc) + : base(pluginInterface, configuration) + { + _configuration = configuration; + _combatController = combatController; + _pluginInterface = pluginInterface; + _uiUtils = uiUtils; + _commandManager = commandManager; + _recommendedPlugins = + [ + new PluginInfo("CBT (formerly known as Automaton)", + "Automaton", + """ + Automaton is a collection of automation-related tweaks. + """, + new Uri("https://github.com/Jaksuhn/Automaton"), + new Uri("https://puni.sh/api/repository/croizat"), + "/cbt", + [ + new PluginDetailInfo("'Sniper no sniping' enabled", + "Automatically completes sniping tasks introduced in Stormblood", + () => automatonIpc.IsAutoSnipeEnabled) + ]), + new PluginInfo("Pandora's Box", + "PandorasBox", + """ + Pandora's Box is a collection of tweaks. + """, + new Uri("https://github.com/PunishXIV/PandorasBox"), + new Uri("https://puni.sh/api/plugins"), + "/pandora", + [ + new PluginDetailInfo("'Auto Active Time Maneuver' enabled", + """ + Automatically completes active time maneuvers in + single player instances, trials and raids" + """, + () => pandorasBoxIpc.IsAutoActiveTimeManeuverEnabled) + ]), + new("NotificationMaster", + "NotificationMaster", + """ + Sends a configurable out-of-game notification if a quest + requires manual actions. + """, + new Uri("https://github.com/NightmareXIV/NotificationMaster"), + null), + ]; + } + + public override void DrawTab() + { + using var tab = ImRaii.TabItem("Dependencies###Plugins"); + if (!tab) + return; + + Draw(out bool allRequiredInstalled); + + ImGui.Spacing(); + ImGui.Separator(); + ImGui.Spacing(); + + if (allRequiredInstalled) + ImGui.TextColored(ImGuiColors.ParsedGreen, "All required plugins are installed."); + else + ImGui.TextColored(ImGuiColors.DalamudRed, + "Required plugins are missing, Questionable will not work properly."); + } + + public void Draw(out bool allRequiredInstalled) + { + float checklistPadding; + using (_pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push()) + { + checklistPadding = ImGui.CalcTextSize(FontAwesomeIcon.Check.ToIconString()).X + + ImGui.GetStyle().ItemSpacing.X; + } + + ImGui.Text("Questionable requires the following plugins to work:"); + allRequiredInstalled = true; + using (ImRaii.PushIndent()) + { + foreach (var plugin in RequiredPlugins) + allRequiredInstalled &= DrawPlugin(plugin, checklistPadding); + } + + ImGui.Spacing(); + ImGui.Separator(); + ImGui.Spacing(); + + ImGui.Text("Questionable supports multiple rotation/combat plugins, please pick the one\nyou want to use:"); + + using (ImRaii.PushIndent()) + { + using (ImRaii.Disabled(_combatController.IsRunning)) + { + if (ImGui.RadioButton("No rotation/combat plugin (combat must be done manually)", + _configuration.General.CombatModule == Configuration.ECombatModule.None)) + { + _configuration.General.CombatModule = Configuration.ECombatModule.None; + _pluginInterface.SavePluginConfig(_configuration); + } + + allRequiredInstalled &= DrawCombatPlugin(Configuration.ECombatModule.BossMod, checklistPadding); + allRequiredInstalled &= DrawCombatPlugin(Configuration.ECombatModule.WrathCombo, checklistPadding); + allRequiredInstalled &= + DrawCombatPlugin(Configuration.ECombatModule.RotationSolverReborn, checklistPadding); + } + } + + ImGui.Spacing(); + ImGui.Separator(); + ImGui.Spacing(); + + ImGui.Text("The following plugins are recommended, but not required:"); + using (ImRaii.PushIndent()) + { + foreach (var plugin in _recommendedPlugins) + DrawPlugin(plugin, checklistPadding); + } + } + + private bool DrawPlugin(PluginInfo plugin, float checklistPadding) + { + using (ImRaii.PushId("plugin_" + plugin.DisplayName)) + { + IExposedPlugin? installedPlugin = FindInstalledPlugin(plugin); + bool isInstalled = installedPlugin != null; + string label = plugin.DisplayName; + if (installedPlugin != null) + label += $" v{installedPlugin.Version}"; + + _uiUtils.ChecklistItem(label, isInstalled); + + DrawPluginDetails(plugin, checklistPadding, isInstalled); + return isInstalled; + } + } + + private bool DrawCombatPlugin(Configuration.ECombatModule combatModule, float checklistPadding) + { + ImGui.Spacing(); + + PluginInfo plugin = CombatPlugins[combatModule]; + using (ImRaii.PushId("plugin_" + plugin.DisplayName)) + { + IExposedPlugin? installedPlugin = FindInstalledPlugin(plugin); + bool isInstalled = installedPlugin != null; + string label = plugin.DisplayName; + if (installedPlugin != null) + label += $" v{installedPlugin.Version}"; + + if (ImGui.RadioButton(label, _configuration.General.CombatModule == combatModule)) + { + _configuration.General.CombatModule = combatModule; + _pluginInterface.SavePluginConfig(_configuration); + } + + ImGui.SameLine(0); + using (_pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push()) + { + var iconColor = isInstalled ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed; + var icon = isInstalled ? FontAwesomeIcon.Check : FontAwesomeIcon.Times; + + ImGui.AlignTextToFramePadding(); + ImGui.TextColored(iconColor, icon.ToIconString()); + } + + DrawPluginDetails(plugin, checklistPadding, isInstalled); + return isInstalled || _configuration.General.CombatModule != combatModule; + } + } + + private void DrawPluginDetails(PluginInfo plugin, float checklistPadding, bool isInstalled) + { + using (ImRaii.PushIndent(checklistPadding)) + { + if (!string.IsNullOrEmpty(plugin.Details)) + ImGui.TextUnformatted(plugin.Details); + + bool allDetailsOk = true; + if (plugin.DetailsToCheck != null) + { + foreach (var detail in plugin.DetailsToCheck) + { + bool detailOk = detail.Predicate(); + allDetailsOk &= detailOk; + + _uiUtils.ChecklistItem(detail.DisplayName, isInstalled && detailOk); + if (!string.IsNullOrEmpty(detail.Details)) + { + using (ImRaii.PushIndent(checklistPadding)) + { + ImGui.TextUnformatted(detail.Details); + } + } + } + } + + ImGui.Spacing(); + + if (isInstalled) + { + if (!allDetailsOk && plugin.ConfigCommand != null && plugin.ConfigCommand.StartsWith('/')) + { + if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Cog, "Open configuration")) + _commandManager.ProcessCommand(plugin.ConfigCommand); + } + } + else + { + if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Globe, "Open Website")) + Util.OpenLink(plugin.WebsiteUri.ToString()); + + ImGui.SameLine(); + if (plugin.DalamudRepositoryUri != null) + { + if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Code, "Open Repository")) + Util.OpenLink(plugin.DalamudRepositoryUri.ToString()); + } + else + { + ImGui.AlignTextToFramePadding(); + ImGuiComponents.HelpMarker("Available on official Dalamud Repository"); + } + } + } + } + + private IExposedPlugin? FindInstalledPlugin(PluginInfo pluginInfo) + { + return _pluginInterface.InstalledPlugins.FirstOrDefault(x => + x.InternalName == pluginInfo.InternalName && x.IsLoaded); + } + + private sealed record PluginInfo( + string DisplayName, + string InternalName, + string Details, + Uri WebsiteUri, + Uri? DalamudRepositoryUri, + string? ConfigCommand = null, + List? DetailsToCheck = null); + + private sealed record PluginDetailInfo(string DisplayName, string Details, Func Predicate); +} diff --git a/Questionable/Windows/ConfigWindow.cs b/Questionable/Windows/ConfigWindow.cs index f3611a5b..42f01dcd 100644 --- a/Questionable/Windows/ConfigWindow.cs +++ b/Questionable/Windows/ConfigWindow.cs @@ -11,6 +11,7 @@ internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig { private readonly IDalamudPluginInterface _pluginInterface; private readonly GeneralConfigComponent _generalConfigComponent; + private readonly PluginConfigComponent _pluginConfigComponent; private readonly DutyConfigComponent _dutyConfigComponent; private readonly SinglePlayerDutyConfigComponent _singlePlayerDutyConfigComponent; private readonly NotificationConfigComponent _notificationConfigComponent; @@ -20,6 +21,7 @@ internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig public ConfigWindow( IDalamudPluginInterface pluginInterface, GeneralConfigComponent generalConfigComponent, + PluginConfigComponent pluginConfigComponent, DutyConfigComponent dutyConfigComponent, SinglePlayerDutyConfigComponent singlePlayerDutyConfigComponent, NotificationConfigComponent notificationConfigComponent, @@ -29,6 +31,7 @@ internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig { _pluginInterface = pluginInterface; _generalConfigComponent = generalConfigComponent; + _pluginConfigComponent = pluginConfigComponent; _dutyConfigComponent = dutyConfigComponent; _singlePlayerDutyConfigComponent = singlePlayerDutyConfigComponent; _notificationConfigComponent = notificationConfigComponent; @@ -45,6 +48,7 @@ internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig return; _generalConfigComponent.DrawTab(); + _pluginConfigComponent.DrawTab(); _dutyConfigComponent.DrawTab(); _singlePlayerDutyConfigComponent.DrawTab(); _notificationConfigComponent.DrawTab(); diff --git a/Questionable/Windows/OneTimeSetupWindow.cs b/Questionable/Windows/OneTimeSetupWindow.cs index 37c151f3..5903ece6 100644 --- a/Questionable/Windows/OneTimeSetupWindow.cs +++ b/Questionable/Windows/OneTimeSetupWindow.cs @@ -1,136 +1,34 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using Dalamud.Interface; +using Dalamud.Interface; using Dalamud.Interface.Colors; using Dalamud.Interface.Components; using Dalamud.Interface.Utility.Raii; using Dalamud.Plugin; -using Dalamud.Plugin.Services; -using Dalamud.Utility; using ImGuiNET; using LLib.ImGui; using Microsoft.Extensions.Logging; -using Questionable.External; +using Questionable.Windows.ConfigComponents; namespace Questionable.Windows; internal sealed class OneTimeSetupWindow : LWindow { - private static readonly IReadOnlyList RequiredPlugins = - [ - new("vnavmesh", - "vnavmesh", - """ - vnavmesh handles the navigation within a zone, moving - your character to the next quest-related objective. - """, - new Uri("https://github.com/awgil/ffxiv_navmesh/"), - new Uri("https://puni.sh/api/repository/veyn")), - new("Lifestream", - "Lifestream", - """ - Used to travel to aethernet shards in cities. - """, - new Uri("https://github.com/NightmareXIV/Lifestream"), - new Uri("https://github.com/NightmareXIV/MyDalamudPlugins/raw/main/pluginmaster.json")), - new("TextAdvance", - "TextAdvance", - """ - Automatically accepts and turns in quests, skips cutscenes - and dialogue. - """, - new Uri("https://github.com/NightmareXIV/TextAdvance"), - new Uri("https://github.com/NightmareXIV/MyDalamudPlugins/raw/main/pluginmaster.json")), - ]; - - private static readonly ReadOnlyDictionary CombatPlugins = new Dictionary - { - { - Configuration.ECombatModule.BossMod, - new("Boss Mod (VBM)", - "BossMod", - string.Empty, - new Uri("https://github.com/awgil/ffxiv_bossmod"), - new Uri("https://puni.sh/api/repository/veyn")) - }, - { - Configuration.ECombatModule.WrathCombo, - new PluginInfo("Wrath Combo", - "WrathCombo", - string.Empty, - new Uri("https://github.com/PunishXIV/WrathCombo"), - new Uri("https://puni.sh/api/plugins")) - }, - { - Configuration.ECombatModule.RotationSolverReborn, - new("Rotation Solver Reborn", - "RotationSolver", - string.Empty, - new Uri("https://github.com/FFXIV-CombatReborn/RotationSolverReborn"), - new Uri( - "https://raw.githubusercontent.com/FFXIV-CombatReborn/CombatRebornRepo/main/pluginmaster.json")) - }, - }.AsReadOnly(); - - private readonly IReadOnlyList _recommendedPlugins; - + private readonly PluginConfigComponent _pluginConfigComponent; private readonly Configuration _configuration; private readonly IDalamudPluginInterface _pluginInterface; - private readonly UiUtils _uiUtils; private readonly ILogger _logger; - private readonly ICommandManager _commandManager; public OneTimeSetupWindow( + PluginConfigComponent pluginConfigComponent, Configuration configuration, IDalamudPluginInterface pluginInterface, - UiUtils uiUtils, - ILogger logger, - AutomatonIpc automatonIpc, - PandorasBoxIpc pandorasBoxIpc, - ICommandManager commandManager) + ILogger logger) : base("Questionable Setup###QuestionableOneTimeSetup", ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoSavedSettings, true) { + _pluginConfigComponent = pluginConfigComponent; _configuration = configuration; _pluginInterface = pluginInterface; - _uiUtils = uiUtils; _logger = logger; - _commandManager = commandManager; - _recommendedPlugins = - [ - new PluginInfo("CBT (formerly known as Automaton)", - "Automaton", - """ - Automaton is a collection of automation-related tweaks. - The 'Sniper no sniping' tweak can complete snipe tasks automatically. - """, - new Uri("https://github.com/Jaksuhn/Automaton"), - new Uri("https://puni.sh/api/repository/croizat"), - "/cbt", - [new PluginDetailInfo("'Sniper no sniping' enabled", () => automatonIpc.IsAutoSnipeEnabled)]), - new PluginInfo("Pandora's Box", - "PandorasBox", - """ - Pandora's Box is a collection of tweaks. - The 'Auto Active Time Maneuver' tweak can complete any - active time maneuvers in single player instances, trials and raids. - """, - new Uri("https://github.com/PunishXIV/PandorasBox"), - new Uri("https://puni.sh/api/plugins"), - "/pandora", - [new PluginDetailInfo("'Auto Active Time Maneuver' enabled", - () => pandorasBoxIpc.IsAutoActiveTimeManeuverEnabled)]), - new("NotificationMaster", - "NotificationMaster", - """ - Sends a configurable out-of-game notification if a quest - requires manual actions. - """, - new Uri("https://github.com/NightmareXIV/NotificationMaster"), - null), - ]; RespectCloseHotkey = false; ShowCloseButton = false; @@ -142,51 +40,7 @@ internal sealed class OneTimeSetupWindow : LWindow public override void Draw() { - float checklistPadding; - using (_pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push()) - { - checklistPadding = ImGui.CalcTextSize(FontAwesomeIcon.Check.ToIconString()).X + - ImGui.GetStyle().ItemSpacing.X; - } - - ImGui.Text("Questionable requires the following plugins to work:"); - bool allRequiredInstalled = true; - using (ImRaii.PushIndent()) - { - foreach (var plugin in RequiredPlugins) - allRequiredInstalled &= DrawPlugin(plugin, checklistPadding); - } - - ImGui.Spacing(); - ImGui.Separator(); - ImGui.Spacing(); - - ImGui.Text("Questionable supports multiple rotation/combat plugins, please pick the one\nyou want to use:"); - - using (ImRaii.PushIndent()) - { - if (ImGui.RadioButton("No rotation/combat plugin (combat must be done manually)", - _configuration.General.CombatModule == Configuration.ECombatModule.None)) - { - _configuration.General.CombatModule = Configuration.ECombatModule.None; - _pluginInterface.SavePluginConfig(_configuration); - } - - DrawCombatPlugin(Configuration.ECombatModule.BossMod, checklistPadding); - DrawCombatPlugin(Configuration.ECombatModule.WrathCombo, checklistPadding); - DrawCombatPlugin(Configuration.ECombatModule.RotationSolverReborn, checklistPadding); - } - - ImGui.Spacing(); - ImGui.Separator(); - ImGui.Spacing(); - - ImGui.Text("The following plugins are recommended, but not required:"); - using (ImRaii.PushIndent()) - { - foreach (var plugin in _recommendedPlugins) - DrawPlugin(plugin, checklistPadding); - } + _pluginConfigComponent.Draw(out bool allRequiredInstalled); ImGui.Spacing(); ImGui.Separator(); @@ -222,110 +76,4 @@ internal sealed class OneTimeSetupWindow : LWindow IsOpen = false; } } - - private bool DrawPlugin(PluginInfo plugin, float checklistPadding) - { - using (ImRaii.PushId("plugin_" + plugin.DisplayName)) - { - bool isInstalled = IsPluginInstalled(plugin); - _uiUtils.ChecklistItem(plugin.DisplayName, isInstalled); - - DrawPluginDetails(plugin, checklistPadding, isInstalled); - return isInstalled; - } - } - - private void DrawCombatPlugin(Configuration.ECombatModule combatModule, float checklistPadding) - { - ImGui.Spacing(); - - PluginInfo plugin = CombatPlugins[combatModule]; - using (ImRaii.PushId("plugin_" + plugin.DisplayName)) - { - bool isInstalled = IsPluginInstalled(plugin); - if (ImGui.RadioButton(plugin.DisplayName, _configuration.General.CombatModule == combatModule)) - { - _configuration.General.CombatModule = combatModule; - _pluginInterface.SavePluginConfig(_configuration); - } - - ImGui.SameLine(0); - using (_pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push()) - { - var iconColor = isInstalled ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed; - var icon = isInstalled ? FontAwesomeIcon.Check : FontAwesomeIcon.Times; - - ImGui.AlignTextToFramePadding(); - ImGui.TextColored(iconColor, icon.ToIconString()); - } - - - DrawPluginDetails(plugin, checklistPadding, isInstalled); - } - } - - private void DrawPluginDetails(PluginInfo plugin, float checklistPadding, bool isInstalled) - { - using (ImRaii.PushIndent(checklistPadding)) - { - if (!string.IsNullOrEmpty(plugin.Details)) - ImGui.TextUnformatted(plugin.Details); - - bool allDetailsOk = true; - if (plugin.DetailsToCheck != null) - { - foreach (var detail in plugin.DetailsToCheck) - { - bool detailOk = detail.Predicate(); - allDetailsOk &= detailOk; - - _uiUtils.ChecklistItem(detail.DisplayName, isInstalled && detailOk); - } - } - - ImGui.Spacing(); - - if (isInstalled) - { - if (!allDetailsOk && plugin.ConfigCommand != null && plugin.ConfigCommand.StartsWith('/')) - { - if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Cog, "Open configuration")) - _commandManager.ProcessCommand(plugin.ConfigCommand); - } - } - else - { - if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Globe, "Open Website")) - Util.OpenLink(plugin.WebsiteUri.ToString()); - - ImGui.SameLine(); - if (plugin.DalamudRepositoryUri != null) - { - if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Code, "Open Repository")) - Util.OpenLink(plugin.DalamudRepositoryUri.ToString()); - } - else - { - ImGui.AlignTextToFramePadding(); - ImGuiComponents.HelpMarker("Available on official Dalamud Repository"); - } - } - } - } - - private bool IsPluginInstalled(PluginInfo pluginInfo) - { - return _pluginInterface.InstalledPlugins.Any(x => x.InternalName == pluginInfo.InternalName && x.IsLoaded); - } - - private sealed record PluginInfo( - string DisplayName, - string InternalName, - string Details, - Uri WebsiteUri, - Uri? DalamudRepositoryUri, - string? ConfigCommand = null, - List? DetailsToCheck = null); - - private sealed record PluginDetailInfo(string DisplayName, Func Predicate); }