DalamudPluginInterface functions for opening Settings and DevMenu (#1795)

* Add functions for plugins to open the Developer Menu and Dalamud Settings windows

* Do not break ABI by instead declaring a new method OpenPluginsInstallerTo
Replace OpenDalamudSettings with OpenDalamudSettingsTo
This commit is contained in:
meoiswa 2024-04-30 00:30:09 +02:00 committed by GitHub
parent 5dd627d18e
commit 00311b4dca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 335 additions and 112 deletions

View file

@ -6,6 +6,7 @@ using System.Linq;
using System.Runtime.InteropServices;
using Dalamud.Game.Text;
using Dalamud.Interface;
using Dalamud.Interface.FontIdentifier;
using Dalamud.Interface.Internal.Windows.PluginInstaller;
using Dalamud.Interface.Style;
@ -452,7 +453,7 @@ internal sealed class DalamudConfiguration : IInternalDisposableService
/// <summary>
/// Gets or sets the page of the plugin installer that is shown by default when opened.
/// </summary>
public PluginInstallerWindow.PluginInstallerOpenKind PluginInstallerOpen { get; set; } = PluginInstallerWindow.PluginInstallerOpenKind.AllPlugins;
public PluginInstallerOpenKind PluginInstallerOpen { get; set; } = PluginInstallerOpenKind.AllPlugins;
/// <summary>
/// Load a configuration from the provided path.

View file

@ -12,6 +12,7 @@ using Dalamud.Game.Gui;
using Dalamud.Game.Text;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Interface;
using Dalamud.Interface.ImGuiNotification.Internal;
using Dalamud.Interface.Internal;
using Dalamud.Interface.Internal.Notifications;
@ -125,7 +126,7 @@ internal class ChatHandlers : IServiceType
this.openInstallerWindowLink = chatGui.AddChatLinkHandler("Dalamud", 1001, (i, m) =>
{
Service<DalamudInterface>.GetNullable()?.OpenPluginInstallerTo(PluginInstallerWindow.PluginInstallerOpenKind.InstalledPlugins);
Service<DalamudInterface>.GetNullable()?.OpenPluginInstallerTo(PluginInstallerOpenKind.InstalledPlugins);
});
}

View file

@ -0,0 +1,53 @@
namespace Dalamud.Interface;
/// <summary>
/// Enum describing pages the plugin installer can be opened to.
/// </summary>
public enum PluginInstallerOpenKind
{
/// <summary>
/// Open to the "All Plugins" page.
/// </summary>
AllPlugins,
/// <summary>
/// Open to the "Installed Plugins" page.
/// </summary>
InstalledPlugins,
/// <summary>
/// Open to the "Changelogs" page.
/// </summary>
Changelogs,
}
/// <summary>
/// Enum describing tabs the settings window can be opened to.
/// </summary>
public enum SettingsOpenKind
{
/// <summary>
/// Open to the "General" page.
/// </summary>
General,
/// <summary>
/// Open to the "Look &#038; Feel" page.
/// </summary>
LookAndFeel,
/// <summary>
/// Open to the "Server Info Bar" page.
/// </summary>
ServerInfoBar,
/// <summary>
/// Open to the "Experimental" page.
/// </summary>
Experimental,
/// <summary>
/// Open to the "About" page.
/// </summary>
About,
}

View file

@ -290,10 +290,10 @@ internal class DalamudInterface : IInternalDisposableService
}
/// <summary>
/// Opens the <see cref="PluginInstallerWindow"/> on the plugin installed.
/// Opens the <see cref="PluginInstallerWindow"/> on the specified page.
/// </summary>
/// <param name="kind">The page of the installer to open.</param>
public void OpenPluginInstallerTo(PluginInstallerWindow.PluginInstallerOpenKind kind)
public void OpenPluginInstallerTo(PluginInstallerOpenKind kind)
{
this.pluginWindow.OpenTo(kind);
this.pluginWindow.BringToFront();
@ -308,6 +308,16 @@ internal class DalamudInterface : IInternalDisposableService
this.settingsWindow.BringToFront();
}
/// <summary>
/// Opens the <see cref="SettingsWindow"/> on the specified tab.
/// </summary>
/// <param name="kind">The tab of the settings to open.</param>
public void OpenSettingsTo(SettingsOpenKind kind)
{
this.settingsWindow.OpenTo(kind);
this.settingsWindow.BringToFront();
}
/// <summary>
/// Opens the <see cref="SelfTestWindow"/>.
/// </summary>
@ -418,7 +428,7 @@ internal class DalamudInterface : IInternalDisposableService
/// Toggles the <see cref="PluginInstallerWindow"/>.
/// </summary>
/// <param name="kind">The page of the installer to open.</param>
public void TogglePluginInstallerWindowTo(PluginInstallerWindow.PluginInstallerOpenKind kind) => this.pluginWindow.ToggleTo(kind);
public void TogglePluginInstallerWindowTo(PluginInstallerOpenKind kind) => this.pluginWindow.ToggleTo(kind);
/// <summary>
/// Toggles the <see cref="SettingsWindow"/>.
@ -456,6 +466,15 @@ internal class DalamudInterface : IInternalDisposableService
this.pluginWindow.SetSearchText(text);
}
/// <summary>
/// Sets the current search text for the settings window.
/// </summary>
/// <param name="text">The search term.</param>
public void SetSettingsSearchText(string text)
{
this.settingsWindow.SetSearchText(text);
}
/// <summary>
/// Toggle the screen darkening effect used for the credits.
/// </summary>

View file

@ -10,6 +10,7 @@ using System.Threading;
using System.Threading.Tasks;
using CheapLoc;
using Dalamud.Configuration.Internal;
using Dalamud.Game.Command;
using Dalamud.Interface.Animation.EasingFunctions;
@ -29,6 +30,7 @@ using Dalamud.Plugin.Internal.Types;
using Dalamud.Plugin.Internal.Types.Manifest;
using Dalamud.Support;
using Dalamud.Utility;
using ImGuiNET;
namespace Dalamud.Interface.Internal.Windows.PluginInstaller;
@ -175,27 +177,6 @@ internal class PluginInstallerWindow : Window, IDisposable
this.profileManagerWidget = new(this);
}
/// <summary>
/// Enum describing pages the plugin installer can be opened to.
/// </summary>
public enum PluginInstallerOpenKind
{
/// <summary>
/// Open to the "All Plugins" page.
/// </summary>
AllPlugins,
/// <summary>
/// Open to the "Installed Plugins" page.
/// </summary>
InstalledPlugins,
/// <summary>
/// Open to the "Changelogs" page.
/// </summary>
Changelogs,
}
private enum OperationStatus
{
Idle,
@ -329,8 +310,16 @@ internal class PluginInstallerWindow : Window, IDisposable
/// <param name="text">The search term.</param>
public void SetSearchText(string text)
{
this.isSearchTextPrefilled = true;
this.searchText = text;
if (string.IsNullOrEmpty(text))
{
this.isSearchTextPrefilled = false;
this.searchText = string.Empty;
}
else
{
this.isSearchTextPrefilled = true;
this.searchText = text;
}
}
/// <summary>

View file

@ -2,6 +2,7 @@ using System.Linq;
using System.Numerics;
using CheapLoc;
using Dalamud.Configuration.Internal;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Internal.Windows.Settings.Tabs;
@ -10,6 +11,7 @@ using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;
using Dalamud.Utility;
using ImGuiNET;
namespace Dalamud.Interface.Internal.Windows.Settings;
@ -22,6 +24,9 @@ internal class SettingsWindow : Window
private SettingsTab[]? tabs;
private string searchInput = string.Empty;
private bool isSearchInputPrefilled = false;
private SettingsTab setActiveTab = null!;
/// <summary>
/// Initializes a new instance of the <see cref="SettingsWindow"/> class.
@ -39,6 +44,34 @@ internal class SettingsWindow : Window
this.SizeCondition = ImGuiCond.FirstUseEver;
}
/// <summary>
/// Open the settings window to the tab specified by <paramref name="kind"/>.
/// </summary>
/// <param name="kind">The tab of the settings window to open.</param>
public void OpenTo(SettingsOpenKind kind)
{
this.IsOpen = true;
this.SetOpenTab(kind);
}
/// <summary>
/// Sets the current search text and marks it as prefilled.
/// </summary>
/// <param name="text">The search term.</param>
public void SetSearchText(string text)
{
if (string.IsNullOrEmpty(text))
{
this.isSearchInputPrefilled = false;
this.searchInput = string.Empty;
}
else
{
this.isSearchInputPrefilled = true;
this.searchInput = text;
}
}
/// <inheritdoc/>
public override void OnOpen()
{
@ -56,7 +89,7 @@ internal class SettingsWindow : Window
settingsTab.Load();
}
this.searchInput = string.Empty;
if (!this.isSearchInputPrefilled) this.searchInput = string.Empty;
base.OnOpen();
}
@ -84,6 +117,12 @@ internal class SettingsWindow : Window
settingsTab.IsOpen = false;
}
if (this.isSearchInputPrefilled)
{
this.isSearchInputPrefilled = false;
this.searchInput = string.Empty;
}
}
/// <inheritdoc/>
@ -97,7 +136,15 @@ internal class SettingsWindow : Window
{
foreach (var settingsTab in this.tabs.Where(x => x.IsVisible))
{
if (ImGui.BeginTabItem(settingsTab.Title))
var flags = ImGuiTabItemFlags.NoCloseWithMiddleMouseButton;
if (this.setActiveTab == settingsTab)
{
flags |= ImGuiTabItemFlags.SetSelected;
this.setActiveTab = null;
}
using var tab = ImRaii.TabItem(settingsTab.Title, flags);
if (tab)
{
if (!settingsTab.IsOpen)
{
@ -231,4 +278,28 @@ internal class SettingsWindow : Window
Service<InterfaceManager>.Get().RebuildFonts();
}
private void SetOpenTab(SettingsOpenKind kind)
{
switch (kind)
{
case SettingsOpenKind.General:
this.setActiveTab = this.tabs[0];
break;
case SettingsOpenKind.LookAndFeel:
this.setActiveTab = this.tabs[1];
break;
case SettingsOpenKind.ServerInfoBar:
this.setActiveTab = this.tabs[2];
break;
case SettingsOpenKind.Experimental:
this.setActiveTab = this.tabs[3];
break;
case SettingsOpenKind.About:
this.setActiveTab = this.tabs[4];
break;
default:
throw new ArgumentOutOfRangeException(nameof(kind), kind, null);
}
}
}

View file

@ -128,8 +128,8 @@ public class SettingsTabLook : SettingsTab
new SettingsEntry<bool>(
Loc.Localize("DalamudSettingInstallerOpenDefault", "Open the Plugin Installer to the \"Installed Plugins\" tab by default"),
Loc.Localize("DalamudSettingInstallerOpenDefaultHint", "This will allow you to open the Plugin Installer to the \"Installed Plugins\" tab by default, instead of the \"Available Plugins\" tab."),
c => c.PluginInstallerOpen == PluginInstallerWindow.PluginInstallerOpenKind.InstalledPlugins,
(v, c) => c.PluginInstallerOpen = v ? PluginInstallerWindow.PluginInstallerOpenKind.InstalledPlugins : PluginInstallerWindow.PluginInstallerOpenKind.AllPlugins),
c => c.PluginInstallerOpen == PluginInstallerOpenKind.InstalledPlugins,
(v, c) => c.PluginInstallerOpen = v ? PluginInstallerOpenKind.InstalledPlugins : PluginInstallerOpenKind.AllPlugins),
new SettingsEntry<bool>(
Loc.Localize("DalamudSettingReducedMotion", "Reduce motions"),

View file

@ -1,4 +1,5 @@
using System.Numerics;
using System.Text;
using ImGuiNET;
@ -103,8 +104,43 @@ public static partial class ImRaii
public static IEndObject TabItem(string label)
=> new EndConditionally(ImGui.EndTabItem, ImGui.BeginTabItem(label));
public static unsafe IEndObject TabItem(byte* label, ImGuiTabItemFlags flags)
=> new EndConditionally(ImGuiNative.igEndTabItem, ImGuiNative.igBeginTabItem(label, null, flags) != 0);
public static unsafe IEndObject TabItem(string label, ImGuiTabItemFlags flags)
{
// I am so sorry for this.
const int ImGuiNET_Util_StackAllocationSizeLimit = 2048;
byte* native_label;
int label_byteCount = 0;
if (label != null)
{
label_byteCount = Encoding.UTF8.GetByteCount(label);
if (label_byteCount > ImGuiNET_Util_StackAllocationSizeLimit)
{
throw new ArgumentOutOfRangeException("label", "Label is too long. (Longer than 2048 bytes)");
}
byte* native_label_stackBytes = stackalloc byte[label_byteCount + 1];
native_label = native_label_stackBytes;
int native_label_offset;
fixed (char* utf16Ptr = label)
{
native_label_offset = Encoding.UTF8.GetBytes(utf16Ptr, label.Length, native_label, label_byteCount);
}
native_label[native_label_offset] = 0;
}
else
{
native_label = null;
}
byte* p_open = null;
byte ret = ImGuiNative.igBeginTabItem(native_label, p_open, flags);
return new EndUnconditionally(ImGuiNative.igEndTabItem, ret != 0);
}
public static IEndObject TabItem(string label, ref bool open)
=> new EndConditionally(ImGui.EndTabItem, ImGui.BeginTabItem(label, ref open));
@ -210,15 +246,15 @@ public static partial class ImRaii
{
private Action EndAction { get; }
public bool Success { get; }
public bool Success { get; }
public bool Disposed { get; private set; }
public bool Disposed { get; private set; }
public EndUnconditionally(Action endAction, bool success)
{
this.EndAction = endAction;
this.Success = success;
this.Disposed = false;
this.Success = success;
this.Disposed = false;
}
public void Dispose()

View file

@ -18,12 +18,16 @@ using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Interface;
using Dalamud.Interface.Internal;
using Dalamud.Interface.Internal.Windows.PluginInstaller;
using Dalamud.Interface.Internal.Windows.Settings;
using Dalamud.Plugin.Internal;
using Dalamud.Plugin.Internal.Types;
using Dalamud.Plugin.Internal.Types.Manifest;
using Dalamud.Plugin.Ipc;
using Dalamud.Plugin.Ipc.Exceptions;
using Dalamud.Plugin.Ipc.Internal;
using Dalamud.Utility;
using static Dalamud.Interface.Internal.Windows.PluginInstaller.PluginInstallerWindow;
namespace Dalamud.Plugin;
@ -218,7 +222,19 @@ public sealed class DalamudPluginInterface : IDisposable
/// Opens the <see cref="PluginInstallerWindow"/> with the plugin name set as search target.
/// </summary>
/// <returns>Returns false if the DalamudInterface was null.</returns>
[Api10ToDo(Api10ToDoAttribute.DeleteCompatBehavior)]
public bool OpenPluginInstaller()
{
return this.OpenPluginInstallerTo(PluginInstallerOpenKind.InstalledPlugins, this.plugin.InternalName);
}
/// <summary>
/// Opens the <see cref="PluginInstallerWindow"/>, with an optional search term.
/// </summary>
/// <param name="openTo">The page to open the installer to. Defaults to the "All Plugins" page.</param>
/// <param name="searchText">An optional search text to input in the search box.</param>
/// <returns>Returns false if the DalamudInterface was null.</returns>
public bool OpenPluginInstallerTo(PluginInstallerOpenKind openTo = PluginInstallerOpenKind.AllPlugins, string searchText = null)
{
var dalamudInterface = Service<DalamudInterface>.GetNullable(); // Can be null during boot
if (dalamudInterface == null)
@ -226,12 +242,48 @@ public sealed class DalamudPluginInterface : IDisposable
return false;
}
dalamudInterface.OpenPluginInstallerTo(PluginInstallerWindow.PluginInstallerOpenKind.InstalledPlugins);
dalamudInterface.SetPluginInstallerSearchText(this.plugin.InternalName);
dalamudInterface.OpenPluginInstallerTo(openTo);
dalamudInterface.SetPluginInstallerSearchText(searchText);
return true;
}
/// <summary>
/// Opens the <see cref="SettingsWindow"/>, with an optional search term.
/// </summary>
/// <param name="openTo">The tab to open the settings to. Defaults to the "General" tab.</param>
/// <param name="searchText">An optional search text to input in the search box.</param>
/// <returns>Returns false if the DalamudInterface was null.</returns>
public bool OpenDalamudSettingsTo(SettingsOpenKind openTo = SettingsOpenKind.General, string searchText = null)
{
var dalamudInterface = Service<DalamudInterface>.GetNullable(); // Can be null during boot
if (dalamudInterface == null)
{
return false;
}
dalamudInterface.OpenSettingsTo(openTo);
dalamudInterface.SetSettingsSearchText(searchText);
return true;
}
/// <summary>
/// Opens the dev menu bar.
/// </summary>
/// <returns>Returns false if the DalamudInterface was null.</returns>
public bool OpenDeveloperMenu()
{
var dalamudInterface = Service<DalamudInterface>.GetNullable(); // Can be null during boot
if (dalamudInterface == null)
{
return false;
}
dalamudInterface.OpenDevMenu();
return true;
}
#region IPC
/// <inheritdoc cref="DataShare.GetOrCreateData{T}"/>

View file

@ -19,6 +19,7 @@ using Dalamud.Game.Gui.Dtr;
using Dalamud.Game.Text;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Interface;
using Dalamud.Interface.Internal;
using Dalamud.Interface.Internal.Windows.PluginInstaller;
using Dalamud.IoC;
@ -129,7 +130,7 @@ internal class PluginManager : IInternalDisposableService
(_, _) =>
{
Service<DalamudInterface>.GetNullable()?.OpenPluginInstallerTo(
PluginInstallerWindow.PluginInstallerOpenKind.Changelogs);
PluginInstallerOpenKind.Changelogs);
}));
this.configuration.PluginTestingOptIns ??= new();