using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; using System.Text; using Dalamud.Bindings.ImGui; using Dalamud.Interface; using Dalamud.Interface.Utility.Raii; using Dalamud.Plugin; using Dalamud.Plugin.Services; using Dalamud.Utility; using LLib; using Questionable.Controller; using Questionable.External; namespace Questionable.Windows.ConfigComponents; internal sealed class PluginConfigComponent : ConfigComponent { private sealed record PluginProvider(string DisplayName, string InternalName, Uri WebsiteUri, Uri? DalamudRepositoryUri, IReadOnlyList? DalamudRepositoryAliases = null) { public IReadOnlyList AliasUrls { get { IReadOnlyList? readOnlyList = _aliasUrls; if (readOnlyList == null) { IReadOnlyList readOnlyList2 = DalamudRepositoryAliases?.Select((Uri u) => u.ToString()).ToList(); readOnlyList = (_aliasUrls = readOnlyList2 ?? Array.Empty()); } return readOnlyList; } } private IReadOnlyList? _aliasUrls; [CompilerGenerated] private bool PrintMembers(StringBuilder builder) { RuntimeHelpers.EnsureSufficientExecutionStack(); builder.Append("DisplayName = "); builder.Append((object?)DisplayName); builder.Append(", InternalName = "); builder.Append((object?)InternalName); builder.Append(", WebsiteUri = "); builder.Append(WebsiteUri); builder.Append(", DalamudRepositoryUri = "); builder.Append(DalamudRepositoryUri); builder.Append(", DalamudRepositoryAliases = "); builder.Append(DalamudRepositoryAliases); builder.Append(", AliasUrls = "); builder.Append(AliasUrls); return true; } } private sealed record PluginRequirement(string GroupName, string Description, IReadOnlyList Providers, string? ConfigCommand = null, List? DetailsToCheck = null, (string Label, Uri Uri)? ExtraLink = null); private sealed record PluginDetailInfo(string DisplayName, string Details, Func Predicate); private const string WigglyNavInternalName = "WigglyNav"; private static readonly IReadOnlyList RequiredPlugins = new global::_003C_003Ez__ReadOnlySingleElementList(new PluginRequirement("Quest Progression", "Automatically accepts and turns in quests, skips cutscenes and dialogue.", new global::_003C_003Ez__ReadOnlySingleElementList(new PluginProvider("TextAdvance", "TextAdvance", new Uri("https://github.com/NightmareXIV/TextAdvance"), new Uri("https://github.com/NightmareXIV/MyDalamudPlugins/raw/main/pluginmaster.json"), new global::_003C_003Ez__ReadOnlyArray(new Uri[3] { new Uri("https://raw.githubusercontent.com/NightmareXIV/MyDalamudPlugins/main/pluginmaster.json"), new Uri("https://github.com/NightmareXIV/MyDalamudPlugins/raw/refs/heads/main/pluginmaster.json"), new Uri("https://raw.githubusercontent.com/NightmareXIV/MyDalamudPlugins/refs/heads/main/pluginmaster.json") }))))); private static readonly ReadOnlyDictionary NavigationPlugins = new Dictionary { { Questionable.Configuration.ENavigationProvider.Vnavmesh, new PluginRequirement("vnavmesh", string.Empty, new global::_003C_003Ez__ReadOnlySingleElementList(new PluginProvider("vnavmesh", "vnavmesh", new Uri("https://github.com/awgil/ffxiv_navmesh/"), new Uri("https://puni.sh/api/repository/veyn")))) }, { Questionable.Configuration.ENavigationProvider.WigglyNav, new PluginRequirement("WigglyNav", string.Empty, new global::_003C_003Ez__ReadOnlySingleElementList(new PluginProvider("WigglyNav", "WigglyNav", new Uri("https://github.com/WigglyMuffin/WigglyNav"), null))) }, { Questionable.Configuration.ENavigationProvider.WigglyNavShim, new PluginRequirement("WigglyNav (shim)", string.Empty, new global::_003C_003Ez__ReadOnlySingleElementList(new PluginProvider("WigglyNav", "WigglyNav", new Uri("https://github.com/WigglyMuffin/WigglyNav"), null))) } }.AsReadOnly(); private static readonly ReadOnlyDictionary CombatPlugins = new Dictionary { { Questionable.Configuration.ECombatModule.BossMod, new PluginRequirement("Boss Mod", string.Empty, new global::_003C_003Ez__ReadOnlyArray(new PluginProvider[2] { new PluginProvider("Boss Mod", "BossMod", new Uri("https://github.com/awgil/ffxiv_bossmod"), new Uri("https://puni.sh/api/repository/veyn")), new PluginProvider("Boss Mod Reborn", "BossModReborn", new Uri("https://github.com/FFXIV-CombatReborn/BossmodReborn"), new Uri("https://raw.githubusercontent.com/FFXIV-CombatReborn/CombatRebornRepo/main/pluginmaster.json")) })) }, { Questionable.Configuration.ECombatModule.RotationSolverReborn, new PluginRequirement("Rotation Solver Reborn", string.Empty, new global::_003C_003Ez__ReadOnlySingleElementList(new PluginProvider("Rotation Solver Reborn", "RotationSolver", new Uri("https://github.com/FFXIV-CombatReborn/RotationSolverReborn"), new Uri("https://raw.githubusercontent.com/FFXIV-CombatReborn/CombatRebornRepo/main/pluginmaster.json")))) }, { Questionable.Configuration.ECombatModule.WrathCombo, new PluginRequirement("Wrath Combo", string.Empty, new global::_003C_003Ez__ReadOnlySingleElementList(new PluginProvider("Wrath Combo", "WrathCombo", new Uri("https://github.com/PunishXIV/WrathCombo"), new Uri("https://puni.sh/api/plugins"), new global::_003C_003Ez__ReadOnlySingleElementList(new Uri("https://love.puni.sh/ment.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; private readonly PluginInstaller _installer; private readonly WigglyNavAvailability _wigglyNavAvailability; private static readonly IReadOnlyList ConflictEligibleGroups = (from g in RequiredPlugins.Concat(CombatPlugins.Values) where g.Providers.Select((PluginProvider p) => p.InternalName).Distinct().Count() > 1 select g).ToList(); public PluginConfigComponent(IDalamudPluginInterface pluginInterface, Configuration configuration, CombatController combatController, UiUtils uiUtils, ICommandManager commandManager, PluginInstaller installer, WigglyNavAvailability wigglyNavAvailability) : base(pluginInterface, configuration) { _configuration = configuration; _combatController = combatController; _pluginInterface = pluginInterface; _uiUtils = uiUtils; _commandManager = commandManager; _installer = installer; _wigglyNavAvailability = wigglyNavAvailability; _recommendedPlugins = new global::_003C_003Ez__ReadOnlyArray(new PluginRequirement[3] { new PluginRequirement("Artisan", "Handles automatic crafting for quests that require crafted items.", new global::_003C_003Ez__ReadOnlySingleElementList(new PluginProvider("Artisan", "Artisan", new Uri("https://github.com/PunishXIV/Artisan"), new Uri("https://puni.sh/api/plugins"), new global::_003C_003Ez__ReadOnlySingleElementList(new Uri("https://love.puni.sh/ment.json"))))), new PluginRequirement("AutoDuty", "Automatically handles dungeon and trial completion during Main Scenario Quest progression.", new global::_003C_003Ez__ReadOnlySingleElementList(new PluginProvider("AutoDuty", "AutoDuty", new Uri("https://github.com/erdelf/AutoDuty"), new Uri("https://puni.sh/api/repository/erdelf"))), null, null, ("Duty Spreadsheet", new Uri("https://docs.google.com/spreadsheets/d/151RlpqRcCpiD_VbQn6Duf-u-S71EP7d0mx3j1PDNoNA/edit?pli=1#gid=0"))), new PluginRequirement("AutoHook", "Handles automatic fishing for Fisher class quests.", new global::_003C_003Ez__ReadOnlySingleElementList(new PluginProvider("AutoHook", "AutoHook", new Uri("https://github.com/PunishXIV/AutoHook"), new Uri("https://puni.sh/api/plugins"), new global::_003C_003Ez__ReadOnlySingleElementList(new Uri("https://love.puni.sh/ment.json"))))) }); } public override void DrawTab() { if (UiThemeUtils.IconTextButton(FontAwesomeIcon.Hammer, "Re-run setup wizard")) { _commandManager.ProcessCommand("/qst setup"); } ImGui.Spacing(); DrawSections(useCards: true, out var _); } public void Draw(out bool allRequiredInstalled) { DrawSections(useCards: false, out allRequiredInstalled); } private void DrawSections(bool useCards, out bool allRequiredInstalled) { float checklistPadding; using (_pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push()) { checklistPadding = ImGui.CalcTextSize(FontAwesomeIcon.Check.ToIconString()).X + ImGui.GetStyle().ItemSpacing.X; } allRequiredInstalled = true; Header("Required"); Vector2 contentStartPos; float availableWidth; ImDrawListPtr drawList; if (useCards) { (contentStartPos, availableWidth, drawList) = UiThemeUtils.BeginCard(); } else { contentStartPos = default(Vector2); availableWidth = 0f; drawList = default(ImDrawListPtr); } foreach (PluginRequirement requiredPlugin in RequiredPlugins) { allRequiredInstalled &= DrawRequirement(requiredPlugin, checklistPadding); } allRequiredInstalled &= DrawNavigationSelection(checklistPadding); if (useCards) { UiThemeUtils.EndCard(contentStartPos, availableWidth, drawList); } Between(); Header("Combat Plugin"); Vector2 contentStartPos2; float availableWidth2; ImDrawListPtr drawList2; if (useCards) { (contentStartPos2, availableWidth2, drawList2) = UiThemeUtils.BeginCard(); } else { contentStartPos2 = default(Vector2); availableWidth2 = 0f; drawList2 = default(ImDrawListPtr); } using (ImRaii.Disabled(_combatController.IsRunning)) { if (ImGui.RadioButton("No rotation/combat plugin (combat must be done manually)", _configuration.General.CombatModule == Questionable.Configuration.ECombatModule.None)) { _configuration.General.CombatModule = Questionable.Configuration.ECombatModule.None; _pluginInterface.SavePluginConfig(_configuration); } allRequiredInstalled &= DrawCombatPlugin(Questionable.Configuration.ECombatModule.BossMod, checklistPadding); allRequiredInstalled &= DrawCombatPlugin(Questionable.Configuration.ECombatModule.RotationSolverReborn, checklistPadding); allRequiredInstalled &= DrawCombatPlugin(Questionable.Configuration.ECombatModule.WrathCombo, checklistPadding); } if (useCards) { UiThemeUtils.EndCard(contentStartPos2, availableWidth2, drawList2); } Between(); Header("Recommended"); Vector2 contentStartPos3; float availableWidth3; ImDrawListPtr drawList3; if (useCards) { (contentStartPos3, availableWidth3, drawList3) = UiThemeUtils.BeginCard(); } else { contentStartPos3 = default(Vector2); availableWidth3 = 0f; drawList3 = default(ImDrawListPtr); } foreach (PluginRequirement recommendedPlugin in _recommendedPlugins) { DrawRequirement(recommendedPlugin, checklistPadding); } if (useCards) { UiThemeUtils.EndCard(contentStartPos3, availableWidth3, drawList3); } if (useCards) { UiThemeUtils.SectionSpacing(); } if (allRequiredInstalled) { Status("All required plugins installed", UiThemeUtils.StatusComplete, FontAwesomeIcon.CheckCircle); } else { Status("Required plugins missing", UiThemeUtils.StatusLocked, FontAwesomeIcon.ExclamationTriangle); } foreach (string item in DetectIpcConflicts()) { Status(item, UiThemeUtils.StatusActive, FontAwesomeIcon.ExclamationTriangle); } void Between() { if (useCards) { UiThemeUtils.SectionSpacing(); } else { ImGui.Spacing(); ImGui.Separator(); ImGui.Spacing(); } } void Header(string title) { if (useCards) { UiThemeUtils.SectionHeader(title); } else { ImGui.TextDisabled(title); ImGui.Spacing(); } } void Status(string text, Vector4 color, FontAwesomeIcon icon) { if (useCards) { UiThemeUtils.StatusBanner(text, color, icon); } else { ImGui.Spacing(); UiThemeUtils.StatusBadge(text, color, icon); } } } private bool IsWigglyNavInstalled() { return _pluginInterface.InstalledPlugins.Any((IExposedPlugin x) => x.InternalName == "WigglyNav"); } private IExposedPlugin? FindProvider(PluginProvider provider) { return _pluginInterface.InstalledPlugins.FirstOrDefault((IExposedPlugin x) => x.InternalName == provider.InternalName); } private List DetectIpcConflicts() { List list = new List(); foreach (PluginRequirement conflictEligibleGroup in ConflictEligibleGroups) { List list2 = (from p in conflictEligibleGroup.Providers where FindProvider(p)?.IsLoaded ?? false select p.DisplayName).ToList(); if (list2.Count > 1) { list.Add(string.Join(" and ", list2) + " are both loaded - disable one to avoid unpredictable behavior"); } } return list; } private bool DrawRequirement(PluginRequirement requirement, float checklistPadding) { bool flag = false; using (ImRaii.PushId("req_" + requirement.GroupName)) { if (requirement.Providers.Count > 1) { ImGui.TextUnformatted(requirement.GroupName); using (ImRaii.PushIndent(checklistPadding)) { if (!string.IsNullOrEmpty(requirement.Description)) { ImGui.PushTextWrapPos(0f); ImGui.TextColored(in UiThemeUtils.DimTextColor, requirement.Description); ImGui.PopTextWrapPos(); } foreach (PluginProvider provider in requirement.Providers) { flag |= DrawProvider(provider, checklistPadding); } DrawRequirementExtras(requirement, checklistPadding, flag); } } else { PluginProvider pluginProvider = requirement.Providers[0]; IExposedPlugin exposedPlugin = FindProvider(pluginProvider); bool flag2 = exposedPlugin?.IsLoaded ?? false; bool isDisabled = exposedPlugin != null && !exposedPlugin.IsLoaded; flag = flag2; _uiUtils.ChecklistItem(pluginProvider.DisplayName, flag2); if (exposedPlugin != null) { ImGui.SameLine(); ImU8String text = new ImU8String(1, 1); text.AppendLiteral("v"); text.AppendFormatted(exposedPlugin.Version); ImGui.TextColored(in UiThemeUtils.DimTextColor, text); } using (ImRaii.PushIndent(checklistPadding)) { if (!string.IsNullOrEmpty(requirement.Description)) { ImGui.PushTextWrapPos(0f); ImGui.TextColored(in UiThemeUtils.DimTextColor, requirement.Description); ImGui.PopTextWrapPos(); } DrawRequirementExtras(requirement, checklistPadding, flag2); DrawProviderActions(pluginProvider, exposedPlugin, flag2, isDisabled); } } ImGui.Spacing(); return flag; } } private bool DrawProvider(PluginProvider provider, float checklistPadding) { using (ImRaii.PushId("prov_" + provider.DisplayName)) { IExposedPlugin exposedPlugin = FindProvider(provider); bool flag = exposedPlugin?.IsLoaded ?? false; bool isDisabled = exposedPlugin != null && !exposedPlugin.IsLoaded; _uiUtils.ChecklistItem(provider.DisplayName, flag); if (exposedPlugin != null) { ImGui.SameLine(); ImU8String text = new ImU8String(1, 1); text.AppendLiteral("v"); text.AppendFormatted(exposedPlugin.Version); ImGui.TextColored(in UiThemeUtils.DimTextColor, text); } using (ImRaii.PushIndent(checklistPadding)) { DrawProviderActions(provider, exposedPlugin, flag, isDisabled); } return flag; } } private bool DrawNavigationSelection(float checklistPadding) { using (ImRaii.PushId("req_Navigation")) { bool flag = true; ImGui.TextUnformatted("Navigation"); using (ImRaii.PushIndent(checklistPadding)) { ImGui.PushTextWrapPos(0f); ImGui.TextColored(in UiThemeUtils.DimTextColor, "Handles navigation within a zone, moving your character to quest objectives."); ImGui.PopTextWrapPos(); flag &= DrawNavigationProvider(Questionable.Configuration.ENavigationProvider.Vnavmesh); Configuration.ENavigationProvider navigationProvider = _configuration.General.NavigationProvider; if (_wigglyNavAvailability.IsCompatible || navigationProvider == Questionable.Configuration.ENavigationProvider.WigglyNav) { flag &= DrawNavigationProvider(Questionable.Configuration.ENavigationProvider.WigglyNav); } if ((_configuration.Advanced.ShowWigglyNavShimProvider && IsWigglyNavInstalled()) || navigationProvider == Questionable.Configuration.ENavigationProvider.WigglyNavShim) { flag &= DrawNavigationProvider(Questionable.Configuration.ENavigationProvider.WigglyNavShim); } } ImGui.Spacing(); return flag; } } private bool DrawNavigationProvider(Configuration.ENavigationProvider navigationProvider) { PluginRequirement pluginRequirement = NavigationPlugins[navigationProvider]; using (ImRaii.PushId("nav_" + pluginRequirement.GroupName)) { if (ImGui.RadioButton(pluginRequirement.GroupName, _configuration.General.NavigationProvider == navigationProvider)) { _configuration.General.NavigationProvider = navigationProvider; _pluginInterface.SavePluginConfig(_configuration); } PluginProvider pluginProvider = pluginRequirement.Providers[0]; IExposedPlugin exposedPlugin = FindProvider(pluginProvider); bool flag = exposedPlugin?.IsLoaded ?? false; bool flag2 = exposedPlugin != null && !exposedPlugin.IsLoaded; if (exposedPlugin != null) { ImGui.SameLine(0f); ImGui.AlignTextToFramePadding(); ImU8String text = new ImU8String(1, 1); text.AppendLiteral("v"); text.AppendFormatted(exposedPlugin.Version); ImGui.TextColored(in UiThemeUtils.DimTextColor, text); } bool flag3 = navigationProvider == Questionable.Configuration.ENavigationProvider.WigglyNav && !_wigglyNavAvailability.IsCompatible; DrawProviderStatusIcon(flag && !flag3, flag2 || (flag && flag3)); if (!flag && pluginProvider.DalamudRepositoryUri != null) { DrawInlineProviderActions(pluginProvider, flag2); } if (navigationProvider == Questionable.Configuration.ENavigationProvider.WigglyNav && _configuration.General.NavigationProvider == navigationProvider && !_wigglyNavAvailability.IsCompatible) { ImGui.SameLine(); ImGui.AlignTextToFramePadding(); ImGui.TextColored(in UiThemeUtils.DimTextColor, "(unavailable - using vnavmesh)"); } return flag || _configuration.General.NavigationProvider != navigationProvider; } } private void DrawProviderActions(PluginProvider provider, IExposedPlugin? installed, bool isLoaded, bool isDisabled) { if (isLoaded) { return; } if (isDisabled) { ImGui.TextColored(in UiThemeUtils.StatusActive, "Installed but disabled"); DrawEnableButton(provider); DrawInstallError(provider); return; } DrawInstallButton(provider); ImGui.SameLine(); if (UiThemeUtils.IconTextButton(FontAwesomeIcon.Globe, "Website")) { Util.OpenLink(provider.WebsiteUri.ToString()); } if (provider.DalamudRepositoryUri != null) { ImGui.SameLine(); if (UiThemeUtils.IconTextButton(FontAwesomeIcon.Code, "Repo")) { Util.OpenLink(provider.DalamudRepositoryUri.ToString()); } } DrawInstallError(provider); } private void DrawRequirementExtras(PluginRequirement requirement, float checklistPadding, bool isLoaded) { (string, Uri)? extraLink = requirement.ExtraLink; if (extraLink.HasValue) { (string, Uri) valueOrDefault = extraLink.GetValueOrDefault(); if (UiThemeUtils.IconTextButton(FontAwesomeIcon.ExternalLinkAlt, valueOrDefault.Item1)) { Util.OpenLink(valueOrDefault.Item2.ToString()); } } if (requirement.DetailsToCheck != null) { foreach (PluginDetailInfo item in requirement.DetailsToCheck) { bool flag = item.Predicate(); _uiUtils.ChecklistItem(item.DisplayName, isLoaded && flag); if (!string.IsNullOrEmpty(item.Details)) { using (ImRaii.PushIndent(checklistPadding)) { ImGui.TextUnformatted(item.Details); } } } } if (isLoaded) { List? detailsToCheck = requirement.DetailsToCheck; if (detailsToCheck != null && detailsToCheck.Any((PluginDetailInfo d) => !d.Predicate()) && requirement.ConfigCommand != null && requirement.ConfigCommand.StartsWith('/') && UiThemeUtils.IconTextButton(FontAwesomeIcon.Cog, "Open configuration")) { _commandManager.ProcessCommand(requirement.ConfigCommand); } } } private bool DrawCombatPlugin(Configuration.ECombatModule combatModule, float checklistPadding) { ImGui.Spacing(); PluginRequirement pluginRequirement = CombatPlugins[combatModule]; using (ImRaii.PushId("combat_" + pluginRequirement.GroupName)) { bool flag = false; bool num = pluginRequirement.Providers.Count > 1; if (ImGui.RadioButton(pluginRequirement.GroupName, _configuration.General.CombatModule == combatModule)) { _configuration.General.CombatModule = combatModule; _pluginInterface.SavePluginConfig(_configuration); } if (!num) { PluginProvider provider = pluginRequirement.Providers[0]; IExposedPlugin exposedPlugin = FindProvider(provider); bool flag2 = exposedPlugin?.IsLoaded ?? false; bool isDisabled = exposedPlugin != null && !exposedPlugin.IsLoaded; flag = flag2; if (exposedPlugin != null) { ImGui.SameLine(0f); ImGui.AlignTextToFramePadding(); ImU8String text = new ImU8String(1, 1); text.AppendLiteral("v"); text.AppendFormatted(exposedPlugin.Version); ImGui.TextColored(in UiThemeUtils.DimTextColor, text); } DrawProviderStatusIcon(flag2, isDisabled); if (!flag2) { DrawInlineProviderActions(provider, isDisabled); } } else { using (ImRaii.PushIndent(checklistPadding)) { foreach (PluginProvider provider2 in pluginRequirement.Providers) { using (ImRaii.PushId("prov_" + provider2.DisplayName)) { IExposedPlugin exposedPlugin2 = FindProvider(provider2); bool flag3 = exposedPlugin2?.IsLoaded ?? false; bool isDisabled2 = exposedPlugin2 != null && !exposedPlugin2.IsLoaded; if (flag3) { flag = true; } _uiUtils.ChecklistItem(provider2.DisplayName, flag3); if (exposedPlugin2 != null) { ImGui.SameLine(); ImU8String text2 = new ImU8String(1, 1); text2.AppendLiteral("v"); text2.AppendFormatted(exposedPlugin2.Version); ImGui.TextColored(in UiThemeUtils.DimTextColor, text2); } if (!flag3) { DrawInlineProviderActions(provider2, isDisabled2); } } } } } return flag || _configuration.General.CombatModule != combatModule; } } private void DrawProviderStatusIcon(bool isLoaded, bool isDisabled) { ImGui.SameLine(0f); using (_pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push()) { Vector4 col; FontAwesomeIcon icon; if (isLoaded) { col = UiThemeUtils.StatusComplete; icon = FontAwesomeIcon.Check; } else if (isDisabled) { col = UiThemeUtils.StatusActive; icon = FontAwesomeIcon.ExclamationTriangle; } else { col = UiThemeUtils.StatusLocked; icon = FontAwesomeIcon.Times; } ImGui.AlignTextToFramePadding(); ImGui.TextColored(in col, icon.ToIconString()); } } private void DrawInlineProviderActions(PluginProvider provider, bool isDisabled) { if (isDisabled) { ImGui.SameLine(); ImGui.TextColored(in UiThemeUtils.StatusActive, "Installed but disabled"); ImGui.SameLine(); DrawEnableButton(provider); DrawInstallError(provider); return; } ImGui.SameLine(); DrawInstallButton(provider); ImGui.SameLine(); if (UiThemeUtils.IconTextButton(FontAwesomeIcon.Globe, "Website")) { Util.OpenLink(provider.WebsiteUri.ToString()); } if (provider.DalamudRepositoryUri != null) { ImGui.SameLine(); if (UiThemeUtils.IconTextButton(FontAwesomeIcon.Code, "Repo")) { Util.OpenLink(provider.DalamudRepositoryUri.ToString()); } } DrawInstallError(provider); } private void DrawInstallButton(PluginProvider provider) { string thirdPartyRepoUrl = provider.DalamudRepositoryUri?.ToString(); string text; bool flag; switch (_installer.GetState(provider.InternalName, thirdPartyRepoUrl).Status) { case PluginInstallStatus.InProgress: text = "Installing…"; flag = false; break; case PluginInstallStatus.Failed: text = "Retry Install"; flag = !_installer.IsAnyInstallInProgress; break; default: text = "Install"; flag = !_installer.IsAnyInstallInProgress; break; } using (ImRaii.Disabled(!flag)) { if (UiThemeUtils.IconTextButton(FontAwesomeIcon.Download, text)) { _installer.TryInstall(provider.InternalName, thirdPartyRepoUrl, provider.AliasUrls); } } } private void DrawEnableButton(PluginProvider provider) { string thirdPartyRepoUrl = provider.DalamudRepositoryUri?.ToString(); string text; bool flag; switch (_installer.GetState(provider.InternalName, thirdPartyRepoUrl).Status) { case PluginInstallStatus.InProgress: text = "Enabling…"; flag = false; break; case PluginInstallStatus.Failed: text = "Retry Enable"; flag = !_installer.IsAnyInstallInProgress; break; default: text = "Enable"; flag = !_installer.IsAnyInstallInProgress; break; } using (ImRaii.Disabled(!flag)) { if (UiThemeUtils.IconTextButton(FontAwesomeIcon.ToggleOn, text)) { _installer.TryEnable(provider.InternalName, thirdPartyRepoUrl, provider.AliasUrls); } } } private void DrawInstallError(PluginProvider provider) { string thirdPartyRepoUrl = provider.DalamudRepositoryUri?.ToString(); PluginInstallState state = _installer.GetState(provider.InternalName, thirdPartyRepoUrl); if (state.Status == PluginInstallStatus.Failed && !string.IsNullOrEmpty(state.ErrorMessage)) { ImGui.PushTextWrapPos(0f); ImU8String text = new ImU8String(8, 1); text.AppendLiteral("Failed: "); text.AppendFormatted(state.ErrorMessage); ImGui.TextColored(in UiThemeUtils.StatusLocked, text); ImGui.PopTextWrapPos(); } } }