From 08f959444b04384d02f3016c21f424542913ea4f Mon Sep 17 00:00:00 2001 From: goaaats Date: Fri, 25 Apr 2025 22:12:39 +0200 Subject: [PATCH] Add "restart in safe mode" button to blocked message --- .../PluginInstaller/PluginInstallerWindow.cs | 26 ++- Dalamud/Interface/Utility/ImGuiHelpers.cs | 174 +++++++++++++++++- 2 files changed, 192 insertions(+), 8 deletions(-) diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs index 091142bc3..83adfa524 100644 --- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs +++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs @@ -639,11 +639,27 @@ internal class PluginInstallerWindow : Window, IDisposable throw new ArgumentOutOfRangeException(); } - if (DateTime.Now - this.timeLoaded > TimeSpan.FromSeconds(90) && !pluginManager.PluginsReady) + if (DateTime.Now - this.timeLoaded > TimeSpan.FromSeconds(30) && !pluginManager.PluginsReady) { + ImGuiHelpers.ScaledDummy(10); ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudRed); ImGuiHelpers.CenteredText("One of your plugins may be blocking the installer."); + ImGuiHelpers.CenteredText("You can try restarting in safe mode, and deleting the plugin."); ImGui.PopStyleColor(); + + ImGuiHelpers.BeginHorizontalButtonGroup() + .Add( + "Restart in Safe Mode", + () => + { + var config = Service.Get(); + config.PluginSafeMode = true; + config.ForceSave(); + Dalamud.RestartGame(); + }) + .SetCentered(true) + .WithHeight(30) + .Draw(); } } } @@ -3136,11 +3152,13 @@ internal class PluginInstallerWindow : Window, IDisposable { ImGuiComponents.DisabledToggleButton(toggleId, this.loadingIndicatorKind == LoadingIndicatorKind.EnablingSingle); } - else if (disabled || inMultipleProfiles || inSingleNonDefaultProfileWhichIsDisabled) + else if (disabled || inMultipleProfiles || inSingleNonDefaultProfileWhichIsDisabled || pluginManager.SafeMode) { ImGuiComponents.DisabledToggleButton(toggleId, isLoadedAndUnloadable); - if (inMultipleProfiles && ImGui.IsItemHovered()) + if (pluginManager.SafeMode && ImGui.IsItemHovered()) + ImGui.SetTooltip(Locs.PluginButtonToolTip_SafeMode); + else if (inMultipleProfiles && ImGui.IsItemHovered()) ImGui.SetTooltip(Locs.PluginButtonToolTip_NeedsToBeInSingleProfile); else if (inSingleNonDefaultProfileWhichIsDisabled && ImGui.IsItemHovered()) ImGui.SetTooltip(Locs.PluginButtonToolTip_SingleProfileDisabled(profilesThatWantThisPlugin.First().Name)); @@ -4220,6 +4238,8 @@ internal class PluginInstallerWindow : Window, IDisposable public static string PluginButtonToolTip_NeedsToBeInSingleProfile => Loc.Localize("InstallerUnloadNeedsToBeInSingleProfile", "This plugin is in more than one collection. If you want to enable or disable it, please do so by enabling or disabling the collections it is in.\nIf you want to manage it here, make sure it is only in a single collection."); + public static string PluginButtonToolTip_SafeMode => Loc.Localize("InstallerButtonSafeModeTooltip", "Cannot enable plugins in safe mode."); + public static string PluginButtonToolTip_SingleProfileDisabled(string name) => Loc.Localize("InstallerSingleProfileDisabled", "The collection '{0}' which contains this plugin is disabled.\nPlease enable it in the collections manager to toggle the plugin individually.").Format(name); #endregion diff --git a/Dalamud/Interface/Utility/ImGuiHelpers.cs b/Dalamud/Interface/Utility/ImGuiHelpers.cs index d9056fec4..8e9bf2c0f 100644 --- a/Dalamud/Interface/Utility/ImGuiHelpers.cs +++ b/Dalamud/Interface/Utility/ImGuiHelpers.cs @@ -111,7 +111,7 @@ public static class ImGuiHelpers /// /// The size of the indent. public static void ScaledIndent(float size) => ImGui.Indent(size * GlobalScale); - + /// /// Use a relative ImGui.SameLine() from your current cursor position, scaled by the Dalamud global scale. /// @@ -286,7 +286,7 @@ public static class ImGuiHelpers foreach (ref var kp in new Span((void*)font->KerningPairs.Data, font->KerningPairs.Size)) kp.AdvanceXAdjustment = rounder(kp.AdvanceXAdjustment * scale); - + foreach (ref var fkp in new Span((void*)font->FrequentKerningPairs.Data, font->FrequentKerningPairs.Size)) fkp = rounder(fkp * scale); } @@ -450,7 +450,7 @@ public static class ImGuiHelpers /// /// Center the ImGui cursor for a certain text. - /// + /// /// The text to center for. public static void CenterCursorForText(string text) => CenterCursorFor(ImGui.CalcTextSize(text).X); @@ -461,6 +461,12 @@ public static class ImGuiHelpers public static void CenterCursorFor(float itemWidth) => ImGui.SetCursorPosX((int)((ImGui.GetWindowWidth() - itemWidth) / 2)); + /// + /// Starts a new horizontal button group. + /// + /// The group. + public static HorizontalButtonGroup BeginHorizontalButtonGroup() => new(); + /// /// Allocates memory on the heap using
/// Memory must be freed using . @@ -535,7 +541,7 @@ public static class ImGuiHelpers builder.BuildRanges(out var vec); return new ReadOnlySpan((void*)vec.Data, vec.Size).ToArray(); } - + /// public static ushort[] CreateImGuiRangesFrom(params UnicodeRange[] ranges) => CreateImGuiRangesFrom((IEnumerable)ranges); @@ -618,7 +624,7 @@ public static class ImGuiHelpers ImGuiNative.ImGuiInputTextCallbackData_InsertChars(data, 0, pBuf, pBuf + len); ImGuiNative.ImGuiInputTextCallbackData_SelectAll(data); } - + /// /// Finds the corresponding ImGui viewport ID for the given window handle. /// @@ -891,4 +897,162 @@ public static class ImGuiHelpers set => this.TextureIndexAndGlyphId = (this.TextureIndexAndGlyphId & ~GlyphIdMask) | ((uint)value << GlyphIdShift); } } + + /// + /// Class helper for creating a horizontal button group. + /// + public class HorizontalButtonGroup + { + private readonly List buttons = []; + + /// + /// Gets or sets a value indicating whether the buttons should be centered horizontally. + /// + public bool IsCentered { get; set; } = false; + + /// + /// Gets or sets the height of the buttons. If null, the default frame height is used. + /// + public float? Height { get; set; } + + /// + /// Gets or sets the extra margin to add to the inside of each button, before and after the text. + /// If null, the default margin is used. + /// + public float? ExtraMargin { get; set; } + + /// + /// Gets or sets the padding between buttons. If null, the default item spacing is used. + /// + public float? PaddingBetweenButtons { get; set; } + + /// + /// Add a button to the group. + /// + /// The text of the button. + /// The action to perform when the button is pressed. + /// The group. + public HorizontalButtonGroup Add(string text, Action action) + { + this.buttons.Add(new ButtonDef(text, action)); + return this; + } + + /// + /// Sets whether the buttons should be centered horizontally. + /// + /// The value. + /// The group. + public HorizontalButtonGroup SetCentered(bool centered) + { + this.IsCentered = centered; + return this; + } + + /// + /// Sets the height of the buttons. + /// + /// The height. + /// The group. + public HorizontalButtonGroup WithHeight(float height) + { + this.Height = height; + return this; + } + + /// + /// Sets the extra margin to add to the inside of each button, before and after the text. + /// + /// The margin. + /// The group. + public HorizontalButtonGroup WithExtraMargin(float extraMargin) + { + this.ExtraMargin = extraMargin; + return this; + } + + /// + /// Sets the padding between buttons. + /// + /// The padding. + /// The group. + public HorizontalButtonGroup WithPaddingBetweenButtons(float padding) + { + this.PaddingBetweenButtons = padding; + return this; + } + + /// + /// Draw the button group at the current location. + /// + public void Draw() + { + var buttonHeight = this.Height * GlobalScale ?? ImGui.GetFrameHeight(); + var buttonCount = this.buttons.Count; + + if (buttonCount == 0) + return; + + var buttonWidths = new float[buttonCount]; + var totalContentWidth = 0f; + var extraMargin = this.ExtraMargin ?? 0f; + + for (var i = 0; i < buttonCount; i++) + { + var buttonText = this.buttons[i].Text; + buttonWidths[i] = ImGui.CalcTextSize(buttonText).X + (2 * extraMargin) + (2 * ImGui.GetStyle().FramePadding.X); + totalContentWidth += buttonWidths[i]; + } + + var buttonPadding = this.PaddingBetweenButtons ?? ImGui.GetStyle().ItemSpacing.X; + if (buttonCount > 1) + totalContentWidth += buttonPadding * (buttonCount - 1); + + var startX = ImGui.GetCursorPosX(); + if (this.IsCentered) + { + var availWidth = ImGui.GetContentRegionAvail().X; + startX += (availWidth - totalContentWidth) * 0.5f; + ImGui.SetCursorPosX(startX); + } + + var originalSpacing = ImGui.GetStyle().ItemSpacing; + if (this.PaddingBetweenButtons.HasValue) + { + var spacing = originalSpacing; + spacing.X = this.PaddingBetweenButtons.Value; + ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, spacing); + } + + for (var i = 0; i < buttonCount; i++) + { + var buttonDef = this.buttons[i]; + + if (this.ExtraMargin.HasValue) + ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(ImGui.GetStyle().FramePadding.X + extraMargin, ImGui.GetStyle().FramePadding.Y)); + + if (this.Height.HasValue) + { + if (ImGui.Button(buttonDef.Text, new Vector2(buttonWidths[i], buttonHeight))) + buttonDef.Action?.Invoke(); + } + else + { + if (ImGui.Button(buttonDef.Text, new Vector2(buttonWidths[i], -1))) + buttonDef.Action?.Invoke(); + } + + if (this.ExtraMargin.HasValue) + ImGui.PopStyleVar(); + + if (i < buttonCount - 1) + ImGui.SameLine(); + } + + if (this.PaddingBetweenButtons.HasValue) + ImGui.PopStyleVar(); + } + + private record ButtonDef(string Text, Action Action); + } }