Use Luna tabbing.
Some checks failed
.NET Build / build (push) Has been cancelled

This commit is contained in:
Ottermandias 2025-11-03 20:54:54 +01:00
parent 5a2fddab89
commit 9aa1121410
28 changed files with 616 additions and 719 deletions

2
Luna

@ -1 +1 @@
Subproject commit 2e984d9c21370c778d172ab955def18c0dbe8c7d Subproject commit cb294f476476f7a3d8b56a0072dbd300b3d54c4f

View file

@ -10,7 +10,7 @@ public sealed class SelectTab(Logger log) : EventBase<SelectTab.Arguments, Selec
public enum Priority public enum Priority
{ {
/// <seealso cref="UI.Tabs.ConfigTabBar.OnSelectTab"/> /// <seealso cref="UI.Tabs.ConfigTabBar.OnSelectTab"/>
ConfigTabBar = 0, MainTabBar = 0,
} }
/// <summary> The arguments for a SelectTab event. </summary> /// <summary> The arguments for a SelectTab event. </summary>

View file

@ -10,6 +10,7 @@ using Penumbra.UI;
using Penumbra.UI.ResourceWatcher; using Penumbra.UI.ResourceWatcher;
using Penumbra.UI.Tabs; using Penumbra.UI.Tabs;
using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs; using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs;
using TabType = Penumbra.Api.Enums.TabType;
namespace Penumbra; namespace Penumbra;

View file

@ -134,7 +134,7 @@ public class Penumbra : IDalamudPlugin
AsyncTask.Run(() => AsyncTask.Run(() =>
{ {
var system = _services.GetService<PenumbraWindowSystem>(); var system = _services.GetService<PenumbraWindowSystem>();
system.Window.Setup(this, _services.GetService<ConfigTabBar>()); system.Window.Setup(this, _services.GetService<MainTabBar>());
_services.GetService<CommandHandler>(); _services.GetService<CommandHandler>();
if (!_disposed) if (!_disposed)
{ {
@ -199,8 +199,12 @@ public class Penumbra : IDalamudPlugin
{ {
ReadOnlySpan<string> relevantPlugins = ReadOnlySpan<string> relevantPlugins =
[ [
"Glamourer", "MareSynchronos", "CustomizePlus", "SimpleHeels", "VfxEditor", "heliosphere-plugin", "Ktisis", "Brio", "DynamicBridge", "Glamourer", "CustomizePlus", "SimpleHeels",
"IllusioVitae", "Aetherment", "LoporritSync", "GagSpeak", "ProjectGagSpeak", "RoleplayingVoiceDalamud", "AQuestReborn", "Ktisis", "Brio",
"heliosphere-plugin", "VfxEditor", "IllusioVitae", "Aetherment",
"DynamicBridge", "GagSpeak", "ProjectGagSpeak", "RoleplayingVoiceDalamud", "AQuestReborn",
"MareSynchronos", "LoporritSync", "KittenSync", "Snowcloak", "LightlessSync", "Sphene", "XivSync", "MareSempiterne" /* PlayerSync */, "AnatoliIliou", "LaciSynchroni"
]; ];
var plugins = _services.GetService<IDalamudPluginInterface>().InstalledPlugins var plugins = _services.GetService<IDalamudPluginInterface>().InstalledPlugins
.GroupBy(p => p.InternalName) .GroupBy(p => p.InternalName)

View file

@ -14,6 +14,7 @@ using Penumbra.UI;
using Penumbra.UI.Classes; using Penumbra.UI.Classes;
using Penumbra.UI.ResourceWatcher; using Penumbra.UI.ResourceWatcher;
using Penumbra.UI.Tabs; using Penumbra.UI.Tabs;
using TabType = Penumbra.Api.Enums.TabType;
namespace Penumbra.Services; namespace Penumbra.Services;

View file

@ -4,7 +4,6 @@ using ImSharp;
using Luna; using Luna;
using OtterGui; using OtterGui;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Text;
using OtterGui.Widgets; using OtterGui.Widgets;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
using Penumbra.Collections.Manager; using Penumbra.Collections.Manager;
@ -25,6 +24,7 @@ using Penumbra.Mods.SubMods;
using Penumbra.Services; using Penumbra.Services;
using Penumbra.UI.Classes; using Penumbra.UI.Classes;
using Penumbra.UI.ModsTab; using Penumbra.UI.ModsTab;
using ITab = OtterGui.Widgets.ITab;
using MouseWheelType = OtterGui.Widgets.MouseWheelType; using MouseWheelType = OtterGui.Widgets.MouseWheelType;
namespace Penumbra.UI.AdvancedWindow; namespace Penumbra.UI.AdvancedWindow;

View file

@ -1,10 +1,10 @@
using Dalamud.Plugin; using Dalamud.Plugin;
using ImSharp; using ImSharp;
using Luna; using Luna;
using Penumbra.Api.Enums;
using Penumbra.Services; using Penumbra.Services;
using Penumbra.UI.Classes; using Penumbra.UI.Classes;
using Penumbra.UI.Tabs; using Penumbra.UI.Tabs;
using TabType = Penumbra.Api.Enums.TabType;
namespace Penumbra.UI; namespace Penumbra.UI;
@ -14,7 +14,7 @@ public sealed class ConfigWindow : Window
private readonly Configuration _config; private readonly Configuration _config;
private readonly ValidityChecker _validityChecker; private readonly ValidityChecker _validityChecker;
private Penumbra? _penumbra; private Penumbra? _penumbra;
private ConfigTabBar _configTabs = null!; private MainTabBar _configTabs = null!;
private string? _lastException; private string? _lastException;
public ConfigWindow(IDalamudPluginInterface pi, Configuration config, ValidityChecker checker, public ConfigWindow(IDalamudPluginInterface pi, Configuration config, ValidityChecker checker,
@ -32,15 +32,15 @@ public sealed class ConfigWindow : Window
public void OpenSettings() public void OpenSettings()
{ {
_configTabs.SelectTab = TabType.Settings; _configTabs.NextTab = TabType.Settings;
IsOpen = true; IsOpen = true;
} }
public void Setup(Penumbra penumbra, ConfigTabBar configTabs) public void Setup(Penumbra penumbra, MainTabBar configTabs)
{ {
_penumbra = penumbra; _penumbra = penumbra;
_configTabs = configTabs; _configTabs = configTabs;
_configTabs.SelectTab = _config.Ephemeral.SelectedTab; _configTabs.NextTab = _config.Ephemeral.SelectedTab;
} }
public override bool DrawConditions() public override bool DrawConditions()
@ -98,12 +98,7 @@ public sealed class ConfigWindow : Window
} }
else else
{ {
var type = _configTabs.Draw(); _configTabs.Draw();
if (type != _config.Ephemeral.SelectedTab)
{
_config.Ephemeral.SelectedTab = type;
_config.Ephemeral.Save();
}
} }
_lastException = null; _lastException = null;

View file

@ -15,18 +15,18 @@ public class IncognitoService(TutorialService tutorial, Configuration config) :
var color = ColorId.FolderExpanded.Value(); var color = ColorId.FolderExpanded.Value();
using (ImStyleBorder.Frame.Push(color)) using (ImStyleBorder.Frame.Push(color))
{ {
var tt = IncognitoMode ? "Toggle incognito mode off."u8 : "Toggle incognito mode on."u8; var tt = IncognitoMode ? "Toggle incognito mode off."u8 : "Toggle incognito mode on."u8;
var icon = IncognitoMode ? LunaStyle.IncognitoOn : LunaStyle.IncognitoOff; var icon = IncognitoMode ? LunaStyle.IncognitoOn : LunaStyle.IncognitoOff;
if (ImEx.Icon.Button(icon, tt, size: new Vector2(width, Im.Style.FrameHeight), textColor: color) && hold) if (ImEx.Icon.Button(icon, tt, size: new Vector2(width, Im.Style.FrameHeight), textColor: color) && hold)
{ {
config.Ephemeral.IncognitoMode = !IncognitoMode; config.Ephemeral.IncognitoMode = !IncognitoMode;
config.Ephemeral.Save(); config.Ephemeral.Save();
} }
if (!hold)
Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, $"\nHold {config.IncognitoModifier} while clicking to toggle.");
} }
if (!hold)
Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, $"\nHold {config.IncognitoModifier} while clicking to toggle.");
tutorial.OpenTutorial(BasicTutorialSteps.Incognito); tutorial.OpenTutorial(BasicTutorialSteps.Incognito);
} }
} }

View file

@ -6,7 +6,6 @@ using Luna;
using OtterGui; using OtterGui;
using OtterGui.Services; using OtterGui.Services;
using OtterGui.Text; using OtterGui.Text;
using OtterGui.Widgets;
using Penumbra.GameData.Data; using Penumbra.GameData.Data;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
@ -23,7 +22,7 @@ public class ModPanelChangedItemsTab(
ImGuiCacheService cacheService, ImGuiCacheService cacheService,
Configuration config, Configuration config,
ModDataEditor dataEditor) ModDataEditor dataEditor)
: ITab, Luna.IUiService : ITab<ModPanelTab>
{ {
private readonly ImGuiCacheService.CacheId _cacheId = cacheService.GetNewId(); private readonly ImGuiCacheService.CacheId _cacheId = cacheService.GetNewId();
@ -209,6 +208,9 @@ public class ModPanelChangedItemsTab(
public ReadOnlySpan<byte> Label public ReadOnlySpan<byte> Label
=> "Changed Items"u8; => "Changed Items"u8;
public ModPanelTab Identifier
=> ModPanelTab.ChangedItems;
public bool IsVisible public bool IsVisible
=> selector.Selected!.ChangedItems.Count > 0; => selector.Selected!.ChangedItems.Count > 0;

View file

@ -1,9 +1,6 @@
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Utility;
using ImSharp; using ImSharp;
using OtterGui.Raii; using Luna;
using OtterGui.Text;
using OtterGui.Widgets;
using Penumbra.Collections; using Penumbra.Collections;
using Penumbra.Collections.Manager; using Penumbra.Collections.Manager;
using Penumbra.Mods; using Penumbra.Mods;
@ -11,7 +8,7 @@ using Penumbra.UI.Classes;
namespace Penumbra.UI.ModsTab; namespace Penumbra.UI.ModsTab;
public class ModPanelCollectionsTab(CollectionManager manager, ModFileSystemSelector selector) : ITab, Luna.IUiService public class ModPanelCollectionsTab(CollectionManager manager, ModFileSystemSelector selector) : ITab<ModPanelTab>
{ {
private enum ModState private enum ModState
{ {
@ -25,18 +22,21 @@ public class ModPanelCollectionsTab(CollectionManager manager, ModFileSystemSele
public ReadOnlySpan<byte> Label public ReadOnlySpan<byte> Label
=> "Collections"u8; => "Collections"u8;
public ModPanelTab Identifier
=> ModPanelTab.Collections;
public void DrawContent() public void DrawContent()
{ {
var (direct, inherited) = CountUsage(selector.Selected!); var (direct, inherited) = CountUsage(selector.Selected!);
Im.Line.New(); Im.Line.New();
if (direct == 1) switch (direct)
ImUtf8.Text("This Mod is directly configured in 1 collection."u8); {
else if (direct == 0) case 1: Im.Text("This Mod is directly configured in 1 collection."u8); break;
ImUtf8.Text("This mod is entirely unused."u8, Colors.RegexWarningBorder); case 0: Im.Text("This mod is entirely unused."u8, Colors.RegexWarningBorder); break;
else default: Im.Text($"This Mod is directly configured in {direct} collections."); break;
ImUtf8.Text($"This Mod is directly configured in {direct} collections."); }
if (inherited > 0) if (inherited > 0)
ImUtf8.Text($"It is also implicitly used in {inherited} {(inherited == 1 ? "collection" : "collections")} through inheritance."); Im.Text($"It is also implicitly used in {inherited} {(inherited == 1 ? "collection" : "collections")} through inheritance.");
Im.Line.New(); Im.Line.New();
Im.Separator(); Im.Separator();
@ -45,7 +45,7 @@ public class ModPanelCollectionsTab(CollectionManager manager, ModFileSystemSele
if (!table) if (!table)
return; return;
var size = ImUtf8.CalcTextSize(ToText(ModState.Unconfigured)).X + 20 * Im.Style.GlobalScale; var size = Im.Font.CalculateSize(ToText(ModState.Unconfigured)).X + 20 * Im.Style.GlobalScale;
var collectionSize = 200 * Im.Style.GlobalScale; var collectionSize = 200 * Im.Style.GlobalScale;
table.SetupColumn("Collection"u8, TableColumnFlags.WidthFixed, collectionSize); table.SetupColumn("Collection"u8, TableColumnFlags.WidthFixed, collectionSize);
table.SetupColumn("State"u8, TableColumnFlags.WidthFixed, size); table.SetupColumn("State"u8, TableColumnFlags.WidthFixed, size);
@ -54,21 +54,21 @@ public class ModPanelCollectionsTab(CollectionManager manager, ModFileSystemSele
ImGui.TableHeadersRow(); ImGui.TableHeadersRow();
foreach (var (idx, (collection, parent, color, state)) in _cache.Index()) foreach (var (idx, (collection, parent, color, state)) in _cache.Index())
{ {
using var id = ImUtf8.PushId(idx); using var id = Im.Id.Push(idx);
ImUtf8.DrawTableColumn(collection.Identity.Name); table.DrawColumn(collection.Identity.Name);
ImGui.TableNextColumn(); table.NextColumn();
ImUtf8.Text(ToText(state), color); Im.Text(ToText(state), color);
using (var context = ImUtf8.PopupContextItem("Context"u8)) using (var context = Im.Popup.BeginContextItem("Context"u8))
{ {
if (context) if (context)
{ {
ImUtf8.Text(collection.Identity.Name); Im.Text(collection.Identity.Name);
Im.Separator(); Im.Separator();
using (ImRaii.Disabled(state is ModState.Enabled && parent == collection)) using (Im.Disabled(state is ModState.Enabled && parent == collection))
{ {
if (ImUtf8.MenuItem("Enable"u8)) if (Im.Menu.Item("Enable"u8))
{ {
if (parent != collection) if (parent != collection)
manager.Editor.SetModInheritance(collection, selector.Selected!, false); manager.Editor.SetModInheritance(collection, selector.Selected!, false);
@ -76,9 +76,9 @@ public class ModPanelCollectionsTab(CollectionManager manager, ModFileSystemSele
} }
} }
using (ImRaii.Disabled(state is ModState.Disabled && parent == collection)) using (Im.Disabled(state is ModState.Disabled && parent == collection))
{ {
if (ImUtf8.MenuItem("Disable"u8)) if (Im.Menu.Item("Disable"u8))
{ {
if (parent != collection) if (parent != collection)
manager.Editor.SetModInheritance(collection, selector.Selected!, false); manager.Editor.SetModInheritance(collection, selector.Selected!, false);
@ -86,15 +86,15 @@ public class ModPanelCollectionsTab(CollectionManager manager, ModFileSystemSele
} }
} }
using (ImRaii.Disabled(parent != collection)) using (Im.Disabled(parent != collection))
{ {
if (ImUtf8.MenuItem("Inherit"u8)) if (Im.Menu.Item("Inherit"u8))
manager.Editor.SetModInheritance(collection, selector.Selected!, true); manager.Editor.SetModInheritance(collection, selector.Selected!, true);
} }
} }
} }
ImUtf8.DrawTableColumn(parent == collection ? string.Empty : parent.Identity.Name); table.DrawColumn(parent == collection ? StringU8.Empty : parent.Identity.Name);
} }
} }

View file

@ -13,11 +13,14 @@ using Penumbra.UI.Classes;
namespace Penumbra.UI.ModsTab; namespace Penumbra.UI.ModsTab;
public class ModPanelConflictsTab(CollectionManager collectionManager, ModFileSystemSelector selector) : ITab, IUiService public class ModPanelConflictsTab(CollectionManager collectionManager, ModFileSystemSelector selector) : ITab<ModPanelTab>
{ {
public ReadOnlySpan<byte> Label public ReadOnlySpan<byte> Label
=> "Conflicts"u8; => "Conflicts"u8;
public ModPanelTab Identifier
=> ModPanelTab.Conflicts;
public bool IsVisible public bool IsVisible
=> collectionManager.Active.Current.Conflicts(selector.Selected!).Any(c => !GetPriority(c).IsHidden); => collectionManager.Active.Current.Conflicts(selector.Selected!).Any(c => !GetPriority(c).IsHidden);

View file

@ -1,7 +1,6 @@
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using ImSharp; using ImSharp;
using OtterGui.Raii; using Luna;
using OtterGui;
using OtterGui.Widgets; using OtterGui.Widgets;
using Penumbra.Mods.Manager; using Penumbra.Mods.Manager;
@ -12,7 +11,7 @@ public class ModPanelDescriptionTab(
TutorialService tutorial, TutorialService tutorial,
ModManager modManager, ModManager modManager,
PredefinedTagManager predefinedTagsConfig) PredefinedTagManager predefinedTagsConfig)
: ITab, Luna.IUiService : ITab<ModPanelTab>
{ {
private readonly TagButtons _localTags = new(); private readonly TagButtons _localTags = new();
private readonly TagButtons _modTags = new(); private readonly TagButtons _modTags = new();
@ -20,15 +19,17 @@ public class ModPanelDescriptionTab(
public ReadOnlySpan<byte> Label public ReadOnlySpan<byte> Label
=> "Description"u8; => "Description"u8;
public ModPanelTab Identifier
=> ModPanelTab.Description;
public void DrawContent() public void DrawContent()
{ {
using var child = ImRaii.Child("##description"); using var child = Im.Child.Begin("##description"u8);
if (!child) if (!child)
return; return;
ImGui.Dummy(ImEx.ScaledVector(2)); Im.ScaledDummy(2, 2);
Im.ScaledDummy(2, 2);
ImGui.Dummy(ImEx.ScaledVector(2));
var (predefinedTagsEnabled, predefinedTagButtonOffset) = predefinedTagsConfig.Enabled var (predefinedTagsEnabled, predefinedTagButtonOffset) = predefinedTagsConfig.Enabled
? (true, Im.Style.FrameHeight + Im.Style.WindowPadding.X + (ImGui.GetScrollMaxY() > 0 ? Im.Style.ScrollbarSize : 0)) ? (true, Im.Style.FrameHeight + Im.Style.WindowPadding.X + (ImGui.GetScrollMaxY() > 0 ? Im.Style.ScrollbarSize : 0))
: (false, 0); : (false, 0);
@ -49,9 +50,9 @@ public class ModPanelDescriptionTab(
selector.Selected!.ModTags, out _, false, selector.Selected!.ModTags, out _, false,
ImGui.CalcTextSize("Local ").X - ImGui.CalcTextSize("Mod ").X); ImGui.CalcTextSize("Local ").X - ImGui.CalcTextSize("Mod ").X);
ImGui.Dummy(ImEx.ScaledVector(2)); Im.ScaledDummy(2, 2);
Im.Separator(); Im.Separator();
ImGuiUtil.TextWrapped(selector.Selected!.Description); Im.TextWrapped(selector.Selected!.Description);
} }
} }

View file

@ -1,18 +1,12 @@
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Interface.Components;
using Dalamud.Interface.ImGuiNotification; using Dalamud.Interface.ImGuiNotification;
using Dalamud.Bindings.ImGui;
using ImSharp; using ImSharp;
using Luna; using Luna;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Widgets; using OtterGui.Widgets;
using OtterGui.Text;
using Penumbra.Mods; using Penumbra.Mods;
using Penumbra.Mods.Editor; using Penumbra.Mods.Editor;
using Penumbra.Mods.Manager; using Penumbra.Mods.Manager;
using Penumbra.Services; using Penumbra.Services;
using Penumbra.Mods.Settings;
using Penumbra.UI.ModsTab.Groups; using Penumbra.UI.ModsTab.Groups;
namespace Penumbra.UI.ModsTab; namespace Penumbra.UI.ModsTab;
@ -29,7 +23,7 @@ public class ModPanelEditTab(
ModGroupEditDrawer groupEditDrawer, ModGroupEditDrawer groupEditDrawer,
DescriptionEditPopup descriptionPopup, DescriptionEditPopup descriptionPopup,
AddGroupDrawer addGroupDrawer) AddGroupDrawer addGroupDrawer)
: ITab, IUiService : ITab<ModPanelTab>
{ {
private readonly TagButtons _modTags = new(); private readonly TagButtons _modTags = new();
@ -39,9 +33,12 @@ public class ModPanelEditTab(
public ReadOnlySpan<byte> Label public ReadOnlySpan<byte> Label
=> "Edit Mod"u8; => "Edit Mod"u8;
public ModPanelTab Identifier
=> ModPanelTab.Edit;
public void DrawContent() public void DrawContent()
{ {
using var child = ImRaii.Child("##editChild", -Vector2.One); using var child = Im.Child.Begin("##editChild"u8, Im.ContentRegion.Available);
if (!child) if (!child)
return; return;
@ -54,7 +51,7 @@ public class ModPanelEditTab(
EditLocalData(); EditLocalData();
UiHelpers.DefaultLineSpace(); UiHelpers.DefaultLineSpace();
if (Input.Text("Mod Path", Input.Path, Input.None, _leaf.FullName(), out var newPath, 256, UiHelpers.InputTextWidth.X)) if (Input.Text("Mod Path"u8, Input.Path, Input.None, _leaf.FullName(), out var newPath, UiHelpers.InputTextWidth.X))
try try
{ {
fileSystem.RenameAndMove(_leaf, newPath); fileSystem.RenameAndMove(_leaf, newPath);
@ -100,15 +97,14 @@ public class ModPanelEditTab(
{ {
var buttonSize = new Vector2(150 * Im.Style.GlobalScale, 0); var buttonSize = new Vector2(150 * Im.Style.GlobalScale, 0);
var folderExists = Directory.Exists(_mod.ModPath.FullName); var folderExists = Directory.Exists(_mod.ModPath.FullName);
var tt = folderExists if (ImEx.Button("Open Mod Directory"u8, buttonSize, folderExists
? $"Open \"{_mod.ModPath.FullName}\" in the file explorer of your choice." ? $"Open \"{_mod.ModPath.FullName}\" in the file explorer of your choice."
: $"Mod directory \"{_mod.ModPath.FullName}\" does not exist."; : $"Mod directory \"{_mod.ModPath.FullName}\" does not exist.", !folderExists))
if (ImGuiUtil.DrawDisabledButton("Open Mod Directory", buttonSize, tt, !folderExists))
Process.Start(new ProcessStartInfo(_mod.ModPath.FullName) { UseShellExecute = true }); Process.Start(new ProcessStartInfo(_mod.ModPath.FullName) { UseShellExecute = true });
Im.Line.Same(); Im.Line.Same();
if (ImGuiUtil.DrawDisabledButton("Reload Mod", buttonSize, "Reload the current mod from its files.\n" if (ImEx.Button("Reload Mod"u8, buttonSize, "Reload the current mod from its files.\n"u8
+ "If the mod directory or meta file do not exist anymore or if the new mod name is empty, the mod is deleted instead.", + "If the mod directory or meta file do not exist anymore or if the new mod name is empty, the mod is deleted instead."u8,
false)) false))
modManager.ReloadMod(_mod); modManager.ReloadMod(_mod);
@ -121,81 +117,76 @@ public class ModPanelEditTab(
private void BackupButtons(Vector2 buttonSize) private void BackupButtons(Vector2 buttonSize)
{ {
var backup = new ModBackup(modExportManager, _mod); var backup = new ModBackup(modExportManager, _mod);
var tt = ModBackup.CreatingBackup if (ImEx.Button("Export Mod"u8, buttonSize, ModBackup.CreatingBackup
? "Already exporting a mod." ? "Already exporting a mod."
: backup.Exists : backup.Exists
? $"Overwrite current exported mod \"{backup.Name}\" with current mod." ? $"Overwrite current exported mod \"{backup.Name}\" with current mod."
: $"Create exported archive of current mod at \"{backup.Name}\"."; : $"Create exported archive of current mod at \"{backup.Name}\".", ModBackup.CreatingBackup))
if (ImUtf8.ButtonEx("Export Mod"u8, tt, buttonSize, ModBackup.CreatingBackup))
backup.CreateAsync(); backup.CreateAsync();
if (Im.Item.RightClicked()) if (Im.Item.RightClicked())
ImUtf8.OpenPopup("context"u8); Im.Popup.Open("context"u8);
Im.Line.Same(); Im.Line.Same();
tt = backup.Exists if (ImEx.Button("Delete Export"u8, buttonSize, backup.Exists
? $"Delete existing mod export \"{backup.Name}\" (hold {config.DeleteModModifier} while clicking)." ? $"Delete existing mod export \"{backup.Name}\" (hold {config.DeleteModModifier} while clicking)."
: $"Exported mod \"{backup.Name}\" does not exist."; : $"Exported mod \"{backup.Name}\" does not exist.", !backup.Exists || !config.DeleteModModifier.IsActive()))
if (ImUtf8.ButtonEx("Delete Export"u8, tt, buttonSize, !backup.Exists || !config.DeleteModModifier.IsActive()))
backup.Delete(); backup.Delete();
tt = backup.Exists
? $"Restore mod from exported file \"{backup.Name}\" (hold {config.DeleteModModifier} while clicking)."
: $"Exported mod \"{backup.Name}\" does not exist.";
Im.Line.Same(); Im.Line.Same();
if (ImUtf8.ButtonEx("Restore From Export"u8, tt, buttonSize, !backup.Exists || !config.DeleteModModifier.IsActive())) if (ImEx.Button("Restore From Export"u8, buttonSize, backup.Exists
? $"Restore mod from exported file \"{backup.Name}\" (hold {config.DeleteModModifier} while clicking)."
: $"Exported mod \"{backup.Name}\" does not exist.", !backup.Exists || !config.DeleteModModifier.IsActive()))
backup.Restore(modManager); backup.Restore(modManager);
if (backup.Exists) if (backup.Exists)
{ {
Im.Line.Same(); Im.Line.Same();
using (ImRaii.PushFont(UiBuilder.IconFont)) ImEx.Icon.Draw(FontAwesomeIcon.CheckCircle.Icon());
{
ImUtf8.Text(FontAwesomeIcon.CheckCircle.ToIconString());
}
Im.Tooltip.OnHover($"Export exists in \"{backup.Name}\"."); Im.Tooltip.OnHover($"Export exists in \"{backup.Name}\".");
} }
using var context = ImUtf8.Popup("context"u8); using var context = Im.Popup.Begin("context"u8);
if (!context) if (!context)
return; return;
if (ImUtf8.Selectable("Open Backup Directory"u8)) if (Im.Selectable("Open Backup Directory"u8))
Process.Start(new ProcessStartInfo(modExportManager.ExportDirectory.FullName) { UseShellExecute = true }); Process.Start(new ProcessStartInfo(modExportManager.ExportDirectory.FullName) { UseShellExecute = true });
} }
/// <summary> Anything about editing the regular meta information about the mod. </summary> /// <summary> Anything about editing the regular meta information about the mod. </summary>
private void EditRegularMeta() private void EditRegularMeta()
{ {
if (Input.Text("Name", Input.Name, Input.None, _mod.Name, out var newName, 256, UiHelpers.InputTextWidth.X)) if (Input.Text("Name"u8, Input.Name, Input.None, _mod.Name, out var newName, UiHelpers.InputTextWidth.X))
modManager.DataEditor.ChangeModName(_mod, newName); modManager.DataEditor.ChangeModName(_mod, newName);
if (Input.Text("Author", Input.Author, Input.None, _mod.Author, out var newAuthor, 256, UiHelpers.InputTextWidth.X)) if (Input.Text("Author"u8, Input.Author, Input.None, _mod.Author, out var newAuthor, UiHelpers.InputTextWidth.X))
modManager.DataEditor.ChangeModAuthor(_mod, newAuthor); modManager.DataEditor.ChangeModAuthor(_mod, newAuthor);
if (Input.Text("Version", Input.Version, Input.None, _mod.Version, out var newVersion, 32, if (Input.Text("Version"u8, Input.Version, Input.None, _mod.Version, out var newVersion,
UiHelpers.InputTextWidth.X)) UiHelpers.InputTextWidth.X))
modManager.DataEditor.ChangeModVersion(_mod, newVersion); modManager.DataEditor.ChangeModVersion(_mod, newVersion);
if (Input.Text("Website", Input.Website, Input.None, _mod.Website, out var newWebsite, 256, if (Input.Text("Website"u8, Input.Website, Input.None, _mod.Website, out var newWebsite,
UiHelpers.InputTextWidth.X)) UiHelpers.InputTextWidth.X))
modManager.DataEditor.ChangeModWebsite(_mod, newWebsite); modManager.DataEditor.ChangeModWebsite(_mod, newWebsite);
using var style = ImStyleDouble.ItemSpacing.Push(new Vector2(Im.Style.GlobalScale * 3)); using var style = ImStyleDouble.ItemSpacing.Push(new Vector2(Im.Style.GlobalScale * 3));
var reducedSize = new Vector2(UiHelpers.InputTextMinusButton3, 0); var reducedSize = new Vector2(UiHelpers.InputTextMinusButton3, 0);
if (ImGui.Button("Edit Description", reducedSize)) if (Im.Button("Edit Description"u8, reducedSize))
descriptionPopup.Open(_mod); descriptionPopup.Open(_mod);
Im.Line.Same(); Im.Line.Same();
var fileExists = File.Exists(filenames.ModMetaPath(_mod)); var fileExists = File.Exists(filenames.ModMetaPath(_mod));
var tt = fileExists var tt = fileExists
? "Open the metadata json file in the text editor of your choice." ? "Open the metadata json file in the text editor of your choice."u8
: "The metadata json file does not exist."; : "The metadata json file does not exist."u8;
if (ImGuiUtil.DrawDisabledButton($"{FontAwesomeIcon.FileExport.ToIconString()}##metaFile", UiHelpers.IconButtonSize, tt, using (Im.Id.Push("meta"))
!fileExists, true)) {
Process.Start(new ProcessStartInfo(filenames.ModMetaPath(_mod)) { UseShellExecute = true }); if (ImEx.Icon.Button(LunaStyle.FileExportIcon, tt, !fileExists))
Process.Start(new ProcessStartInfo(filenames.ModMetaPath(_mod)) { UseShellExecute = true });
}
DrawOpenDefaultMod(); DrawOpenDefaultMod();
} }
@ -213,14 +204,13 @@ public class ModPanelEditTab(
Im.Line.Same(0, 3 * Im.Style.GlobalScale); Im.Line.Same(0, 3 * Im.Style.GlobalScale);
var canRefresh = config.DeleteModModifier.IsActive(); var canRefresh = config.DeleteModModifier.IsActive();
var tt = canRefresh if (ImEx.Icon.Button(LunaStyle.RefreshIcon, canRefresh
? "Reset the import date to the current date and time." ? "Reset the import date to the current date and time."u8
: $"Reset the import date to the current date and time.\nHold {config.DeleteModModifier} while clicking to refresh."; : $"Reset the import date to the current date and time.\nHold {config.DeleteModModifier} while clicking to refresh.",
!canRefresh))
if (ImUtf8.IconButton(FontAwesomeIcon.Sync, tt, disabled: !canRefresh))
modManager.DataEditor.ResetModImportDate(_mod); modManager.DataEditor.ResetModImportDate(_mod);
Im.Line.SameInner(); Im.Line.SameInner();
ImUtf8.Text("Import Date"u8); Im.Text("Import Date"u8);
} }
private void DrawOpenLocalData() private void DrawOpenLocalData()
@ -230,7 +220,7 @@ public class ModPanelEditTab(
var tt = fileExists var tt = fileExists
? "Open the local mod data file in the text editor of your choice."u8 ? "Open the local mod data file in the text editor of your choice."u8
: "The local mod data file does not exist."u8; : "The local mod data file does not exist."u8;
if (ImUtf8.ButtonEx("Open Local Data"u8, tt, UiHelpers.InputTextWidth, !fileExists)) if (ImEx.Button("Open Local Data"u8, UiHelpers.InputTextWidth, tt, !fileExists))
Process.Start(new ProcessStartInfo(file) { UseShellExecute = true }); Process.Start(new ProcessStartInfo(file) { UseShellExecute = true });
} }
@ -239,9 +229,9 @@ public class ModPanelEditTab(
var file = filenames.OptionGroupFile(_mod, -1, false); var file = filenames.OptionGroupFile(_mod, -1, false);
var fileExists = File.Exists(file); var fileExists = File.Exists(file);
var tt = fileExists var tt = fileExists
? "Open the default mod data file in the text editor of your choice." ? "Open the default mod data file in the text editor of your choice."u8
: "The default mod data file does not exist."; : "The default mod data file does not exist."u8;
if (ImGuiUtil.DrawDisabledButton("Open Default Data", UiHelpers.InputTextWidth, tt, !fileExists)) if (ImEx.Button("Open Default Data"u8, UiHelpers.InputTextWidth, tt, !fileExists))
Process.Start(new ProcessStartInfo(file) { UseShellExecute = true }); Process.Start(new ProcessStartInfo(file) { UseShellExecute = true });
} }
@ -262,7 +252,7 @@ public class ModPanelEditTab(
{ {
Im.Item.SetNextWidth(buttonSize.X * 2 + Im.Style.ItemSpacing.X); Im.Item.SetNextWidth(buttonSize.X * 2 + Im.Style.ItemSpacing.X);
var tmp = _currentModDirectory ?? mod.ModPath.Name; var tmp = _currentModDirectory ?? mod.ModPath.Name;
if (ImGui.InputText("##newModMove", ref tmp, 64)) if (Im.Input.Text("##newModMove"u8, ref tmp))
{ {
_currentModDirectory = tmp; _currentModDirectory = tmp;
_state = modManager.NewDirectoryValid(mod.ModPath.Name, _currentModDirectory, out _); _state = modManager.NewDirectoryValid(mod.ModPath.Name, _currentModDirectory, out _);
@ -281,16 +271,17 @@ public class ModPanelEditTab(
_ => (true, "Unknown error."), _ => (true, "Unknown error."),
}; };
Im.Line.Same(); Im.Line.Same();
if (ImGuiUtil.DrawDisabledButton("Rename Mod Directory", buttonSize, tt, disabled) && _currentModDirectory != null) if (ImEx.Button("Rename Mod Directory"u8, buttonSize, tt, disabled) && _currentModDirectory is not null)
{ {
modManager.MoveModDirectory(mod, _currentModDirectory); modManager.MoveModDirectory(mod, _currentModDirectory);
Reset(); Reset();
} }
Im.Line.Same(); Im.Line.Same();
ImGuiComponents.HelpMarker( if (LunaStyle.DrawAlignedHelpMarker())
"The mod directory name is used to correspond stored settings and sort orders, otherwise it has no influence on anything that is displayed.\n" Im.Tooltip.Set(
+ "This can currently not be used on pre-existing folders and does not support merges or overwriting."); "The mod directory name is used to correspond stored settings and sort orders, otherwise it has no influence on anything that is displayed.\n"u8
+ "This can currently not be used on pre-existing folders and does not support merges or overwriting."u8);
} }
} }
@ -298,41 +289,38 @@ public class ModPanelEditTab(
private static class Input private static class Input
{ {
// Special field indices to reuse the same string buffer. // Special field indices to reuse the same string buffer.
public const int None = -1; public const int None = -1;
public const int Name = -2; public const int Name = -2;
public const int Author = -3; public const int Author = -3;
public const int Version = -4; public const int Version = -4;
public const int Website = -5; public const int Website = -5;
public const int Path = -6; public const int Path = -6;
public const int Description = -7;
// Temporary strings // Temporary strings
private static string? _currentEdit; private static string? _currentEdit;
private static ModPriority? _currentGroupPriority;
private static int _currentField = None; private static int _currentField = None;
private static int _optionIndex = None; private static int _optionIndex = None;
public static void Reset() public static void Reset()
{ {
_currentEdit = null; _currentEdit = null;
_currentGroupPriority = null;
_currentField = None; _currentField = None;
_optionIndex = None; _optionIndex = None;
} }
public static bool Text(string label, int field, int option, string oldValue, out string value, uint maxLength, float width) public static bool Text(ReadOnlySpan<byte> label, int field, int option, string oldValue, out string value, float width)
{ {
var tmp = field == _currentField && option == _optionIndex ? _currentEdit ?? oldValue : oldValue; var tmp = field == _currentField && option == _optionIndex ? _currentEdit ?? oldValue : oldValue;
Im.Item.SetNextWidth(width); Im.Item.SetNextWidth(width);
if (ImGui.InputText(label, ref tmp)) if (Im.Input.Text(label, ref tmp))
{ {
_currentEdit = tmp; _currentEdit = tmp;
_optionIndex = option; _optionIndex = option;
_currentField = field; _currentField = field;
} }
if (ImGui.IsItemDeactivatedAfterEdit() && _currentEdit != null) if (Im.Item.DeactivatedAfterEdit && _currentEdit is not null)
{ {
var ret = _currentEdit != oldValue; var ret = _currentEdit != oldValue;
value = _currentEdit; value = _currentEdit;
@ -343,28 +331,5 @@ public class ModPanelEditTab(
value = string.Empty; value = string.Empty;
return false; return false;
} }
public static bool Priority(string label, int field, int option, ModPriority oldValue, out ModPriority value, float width)
{
var tmp = (field == _currentField && option == _optionIndex ? _currentGroupPriority ?? oldValue : oldValue).Value;
Im.Item.SetNextWidth(width);
if (ImGui.InputInt(label, ref tmp, 0, 0))
{
_currentGroupPriority = new ModPriority(tmp);
_optionIndex = option;
_currentField = field;
}
if (ImGui.IsItemDeactivatedAfterEdit() && _currentGroupPriority != null)
{
var ret = _currentGroupPriority != oldValue;
value = _currentGroupPriority.Value;
Reset();
return ret;
}
value = ModPriority.Default;
return false;
}
} }
} }

View file

@ -1,8 +1,8 @@
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using ImSharp; using ImSharp;
using Luna;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Text; using OtterGui.Text;
using OtterGui.Widgets;
using Penumbra.UI.Classes; using Penumbra.UI.Classes;
using Penumbra.Collections.Manager; using Penumbra.Collections.Manager;
using Penumbra.Communication; using Penumbra.Communication;
@ -22,7 +22,7 @@ public class ModPanelSettingsTab(
CommunicatorService communicator, CommunicatorService communicator,
ModGroupDrawer modGroupDrawer, ModGroupDrawer modGroupDrawer,
Configuration config) Configuration config)
: ITab, Luna.IUiService : ITab<ModPanelTab>
{ {
private bool _inherited; private bool _inherited;
private bool _temporary; private bool _temporary;
@ -32,7 +32,10 @@ public class ModPanelSettingsTab(
public ReadOnlySpan<byte> Label public ReadOnlySpan<byte> Label
=> "Settings"u8; => "Settings"u8;
public void DrawHeader() public ModPanelTab Identifier
=> ModPanelTab.Settings;
public void PostTabButton()
=> tutorial.OpenTutorial(BasicTutorialSteps.ModOptions); => tutorial.OpenTutorial(BasicTutorialSteps.ModOptions);
public void Reset() public void Reset()

View file

@ -1,10 +1,5 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using ImSharp; using ImSharp;
using Luna; using Luna;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Widgets;
using Penumbra.Mods; using Penumbra.Mods;
using Penumbra.Mods.Manager; using Penumbra.Mods.Manager;
using Penumbra.UI.AdvancedWindow; using Penumbra.UI.AdvancedWindow;
@ -12,138 +7,88 @@ using ImGuiColor = ImSharp.ImGuiColor;
namespace Penumbra.UI.ModsTab; namespace Penumbra.UI.ModsTab;
public class ModPanelTabBar : IUiService public enum ModPanelTab
{ {
private enum ModPanelTabType Description,
{ Settings,
Description, ChangedItems,
Settings, Conflicts,
ChangedItems, Collections,
Conflicts, Edit,
Collections, };
Edit,
};
public readonly ModPanelSettingsTab Settings; public class ModPanelTabBar : TabBar<ModPanelTab>
public readonly ModPanelDescriptionTab Description; {
public readonly ModPanelCollectionsTab Collections; public readonly ModPanelSettingsTab Settings;
public readonly ModPanelConflictsTab Conflicts; public readonly ModPanelEditTab Edit;
public readonly ModPanelChangedItemsTab ChangedItems; private readonly ModManager _modManager;
public readonly ModPanelEditTab Edit; private readonly TutorialService _tutorial;
private readonly ModEditWindowFactory _modEditWindowFactory;
private readonly ModManager _modManager;
private readonly TutorialService _tutorial;
public readonly ITab[] Tabs; private Mod? _lastMod;
private ModPanelTabType _preferredTab = ModPanelTabType.Settings;
private Mod? _lastMod;
public ModPanelTabBar(ModEditWindowFactory modEditWindowFactory, ModPanelSettingsTab settings, ModPanelDescriptionTab description, public ModPanelTabBar(ModEditWindowFactory modEditWindowFactory, ModPanelSettingsTab settings, ModPanelDescriptionTab description,
ModPanelConflictsTab conflicts, ModPanelChangedItemsTab changedItems, ModPanelEditTab edit, ModManager modManager, ModPanelConflictsTab conflicts, ModPanelChangedItemsTab changedItems, ModPanelEditTab edit, ModManager modManager,
TutorialService tutorial, ModPanelCollectionsTab collections) TutorialService tutorial, ModPanelCollectionsTab collections, Logger log)
: base(nameof(ModPanelTabBar), log, settings, description, conflicts, changedItems, collections, edit)
{ {
_modEditWindowFactory = modEditWindowFactory; Flags = TabBarFlags.NoTooltip;
Settings = settings; Settings = settings;
Description = description; Edit = edit;
Conflicts = conflicts; _modManager = modManager;
ChangedItems = changedItems; _tutorial = tutorial;
Edit = edit; Buttons.AddButton(new AdvancedEditingButton(this, modEditWindowFactory), 0);
_modManager = modManager; }
_tutorial = tutorial;
Collections = collections;
Tabs = private sealed class AdvancedEditingButton(ModPanelTabBar parent, ModEditWindowFactory editFactory) : BaseButton
[ {
Settings, public override ReadOnlySpan<byte> Label
Description, => "Advanced Editing"u8;
Conflicts,
ChangedItems, public override void OnClick()
Collections, {
Edit, if (parent._lastMod is { } mod)
]; editFactory.OpenForMod(mod);
}
public override bool HasTooltip
=> true;
public override void DrawTooltip()
=> Im.Text(
"Clicking this will open a new window in which you can\nedit the following things per option for this mod:\n\n"u8
+ "\t\t- file redirections\n"u8
+ "\t\t- file swaps\n"u8
+ "\t\t- metadata manipulations\n"u8
+ "\t\t- model materials\n"u8
+ "\t\t- duplicates\n"u8
+ "\t\t- textures"u8);
} }
public void Draw(Mod mod) public void Draw(Mod mod)
{ {
var tabBarHeight = ImGui.GetCursorPosY(); var tabBarHeight = Im.Cursor.Y;
if (_lastMod != mod) _lastMod = mod;
{ base.Draw();
_lastMod = mod;
TabBar.Draw(string.Empty, ImGuiTabBarFlags.NoTooltip, ToLabel(_preferredTab), out _, () => DrawAdvancedEditingButton(mod), Tabs);
}
else
{
TabBar.Draw(string.Empty, ImGuiTabBarFlags.NoTooltip, ReadOnlySpan<byte>.Empty, out var label, () => DrawAdvancedEditingButton(mod),
Tabs);
_preferredTab = ToType(label);
}
DrawFavoriteButton(mod, tabBarHeight); DrawFavoriteButton(mod, tabBarHeight);
} }
private ReadOnlySpan<byte> ToLabel(ModPanelTabType type)
=> type switch
{
ModPanelTabType.Description => Description.Label,
ModPanelTabType.Settings => Settings.Label,
ModPanelTabType.ChangedItems => ChangedItems.Label,
ModPanelTabType.Conflicts => Conflicts.Label,
ModPanelTabType.Collections => Collections.Label,
ModPanelTabType.Edit => Edit.Label,
_ => ReadOnlySpan<byte>.Empty,
};
private ModPanelTabType ToType(ReadOnlySpan<byte> label)
{
if (label == Description.Label)
return ModPanelTabType.Description;
if (label == Settings.Label)
return ModPanelTabType.Settings;
if (label == ChangedItems.Label)
return ModPanelTabType.ChangedItems;
if (label == Conflicts.Label)
return ModPanelTabType.Conflicts;
if (label == Collections.Label)
return ModPanelTabType.Collections;
if (label == Edit.Label)
return ModPanelTabType.Edit;
return 0;
}
private void DrawAdvancedEditingButton(Mod mod)
{
if (ImGui.TabItemButton("Advanced Editing", ImGuiTabItemFlags.Trailing | ImGuiTabItemFlags.NoTooltip))
{
_modEditWindowFactory.OpenForMod(mod);
}
ImGuiUtil.HoverTooltip(
"Clicking this will open a new window in which you can\nedit the following things per option for this mod:\n\n"
+ "\t\t- file redirections\n"
+ "\t\t- file swaps\n"
+ "\t\t- metadata manipulations\n"
+ "\t\t- model materials\n"
+ "\t\t- duplicates\n"
+ "\t\t- textures");
}
private void DrawFavoriteButton(Mod mod, float height) private void DrawFavoriteButton(Mod mod, float height)
{ {
var size = ImEx.Icon.CalculateSize(LunaStyle.FavoriteIcon) + Im.Style.FramePadding * 2; var size = ImEx.Icon.CalculateSize(LunaStyle.FavoriteIcon) + Im.Style.FramePadding * 2;
var newPos = new Vector2(ImGui.GetWindowWidth() - size.X - Im.Style.ItemSpacing.X, height); var newPos = new Vector2(Im.Window.Width - size.X - Im.Style.ItemSpacing.X, height);
if (ImGui.GetScrollMaxX() > 0) if (Im.Scroll.MaximumX > 0)
newPos.X += ImGui.GetScrollX(); newPos.X += Im.Scroll.X;
var rectUpper = ImGui.GetWindowPos() + newPos; var rectUpper = Im.Window.Position + newPos;
var color = ImGui.IsMouseHoveringRect(rectUpper, rectUpper + size) ? Im.Style[ImGuiColor.Text] : var color = Im.Mouse.IsHoveringRectangle(rectUpper, rectUpper + size) ? Im.Style[ImGuiColor.Text] :
mod.Favorite ? LunaStyle.FavoriteColor : Im.Style[ImGuiColor.TextDisabled]; mod.Favorite ? LunaStyle.FavoriteColor : Im.Style[ImGuiColor.TextDisabled];
using var c = ImGuiColor.Text.Push(color) using var c = ImGuiColor.Text.Push(color)
.Push(ImGuiColor.Button, Vector4.Zero) .Push(ImGuiColor.Button, Vector4.Zero)
.Push(ImGuiColor.ButtonHovered, Vector4.Zero) .Push(ImGuiColor.ButtonHovered, Vector4.Zero)
.Push(ImGuiColor.ButtonActive, Vector4.Zero); .Push(ImGuiColor.ButtonActive, Vector4.Zero);
ImGui.SetCursorPos(newPos); Im.Cursor.Position = newPos;
if (ImEx.Icon.Button(LunaStyle.FavoriteIcon)) if (ImEx.Icon.Button(LunaStyle.FavoriteIcon))
_modManager.DataEditor.ChangeModFavorite(mod, !mod.Favorite); _modManager.DataEditor.ChangeModFavorite(mod, !mod.Favorite);
@ -151,6 +96,6 @@ public class ModPanelTabBar : IUiService
_tutorial.OpenTutorial(BasicTutorialSteps.Favorites); _tutorial.OpenTutorial(BasicTutorialSteps.Favorites);
if (hovered) if (hovered)
ImGui.SetTooltip("Favorite"); Im.Tooltip.Set("Favorite"u8);
} }
} }

View file

@ -2,7 +2,7 @@ using Dalamud.Bindings.ImGui;
using FFXIVClientStructs.FFXIV.Client.Game.Object; using FFXIVClientStructs.FFXIV.Client.Game.Object;
using FFXIVClientStructs.FFXIV.Client.System.Resource; using FFXIVClientStructs.FFXIV.Client.System.Resource;
using ImSharp; using ImSharp;
using OtterGui.Widgets; using Luna;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
using Penumbra.Collections; using Penumbra.Collections;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
@ -16,7 +16,7 @@ using Penumbra.UI.Classes;
namespace Penumbra.UI.ResourceWatcher; namespace Penumbra.UI.ResourceWatcher;
public sealed class ResourceWatcher : IDisposable, ITab, Luna.IUiService public sealed class ResourceWatcher : IDisposable, ITab<TabType>
{ {
public const int DefaultMaxEntries = 1024; public const int DefaultMaxEntries = 1024;
public const RecordType AllRecords = RecordType.Request | RecordType.ResourceLoad | RecordType.FileLoad | RecordType.Destruction; public const RecordType AllRecords = RecordType.Request | RecordType.ResourceLoad | RecordType.FileLoad | RecordType.Destruction;
@ -96,6 +96,9 @@ public sealed class ResourceWatcher : IDisposable, ITab, Luna.IUiService
public ReadOnlySpan<byte> Label public ReadOnlySpan<byte> Label
=> "Resource Logger"u8; => "Resource Logger"u8;
public TabType Identifier
=> TabType.ResourceWatcher;
public void DrawContent() public void DrawContent()
{ {
UpdateRecords(); UpdateRecords();

View file

@ -2,10 +2,7 @@ using Dalamud.Bindings.ImGui;
using Dalamud.Interface; using Dalamud.Interface;
using ImSharp; using ImSharp;
using Luna; using Luna;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Table; using OtterGui.Table;
using OtterGui.Text;
using Penumbra.Enums; using Penumbra.Enums;
using Penumbra.Interop.Structs; using Penumbra.Interop.Structs;
using Penumbra.String; using Penumbra.String;
@ -54,16 +51,16 @@ internal sealed class ResourceWatcherTable : Table<Record>
=> DrawByteString(item.Path, 280 * Im.Style.GlobalScale); => DrawByteString(item.Path, 280 * Im.Style.GlobalScale);
} }
private static unsafe void DrawByteString(CiByteString path, float length) private static void DrawByteString(CiByteString path, float length)
{ {
if (path.IsEmpty) if (path.IsEmpty)
return; return;
var size = ImUtf8.CalcTextSize(path.Span); var size = Im.Font.CalculateSize(path.Span);
var clicked = false; var clicked = false;
if (size.X <= length) if (size.X <= length)
{ {
clicked = ImUtf8.Selectable(path.Span); clicked = Im.Selectable(path.Span);
} }
else else
{ {
@ -71,10 +68,11 @@ internal sealed class ResourceWatcherTable : Table<Record>
using (Im.Group()) using (Im.Group())
{ {
CiByteString shortPath; CiByteString shortPath;
if (fileName != -1) var icon = FontAwesomeIcon.EllipsisH.Icon();
if (fileName is not -1)
{ {
using var font = ImRaii.PushFont(UiBuilder.IconFont); using var font = AwesomeIcon.Font.Push();
clicked = ImUtf8.Selectable(FontAwesomeIcon.EllipsisH.ToIconString()); clicked = Im.Selectable(icon.Span);
Im.Line.SameInner(); Im.Line.SameInner();
shortPath = path.Substring(fileName, path.Length - fileName); shortPath = path.Substring(fileName, path.Length - fileName);
} }
@ -83,14 +81,14 @@ internal sealed class ResourceWatcherTable : Table<Record>
shortPath = path; shortPath = path;
} }
clicked |= ImUtf8.Selectable(shortPath.Span, false, ImGuiSelectableFlags.AllowItemOverlap); clicked |= Im.Selectable(shortPath.Span, false, SelectableFlags.AllowOverlap);
} }
Im.Tooltip.OnHover(path.Span); Im.Tooltip.OnHover(path.Span);
} }
if (clicked) if (clicked)
ImUtf8.SetClipboardText(path.Span); Im.Clipboard.Set(path.Span);
} }
private sealed class RecordTypeColumn : ColumnFlags<RecordType, Record> private sealed class RecordTypeColumn : ColumnFlags<RecordType, Record>
@ -153,13 +151,13 @@ internal sealed class ResourceWatcherTable : Table<Record>
public override float Width public override float Width
=> UiBuilder.MonoFont.GetCharAdvance('0') * 17; => UiBuilder.MonoFont.GetCharAdvance('0') * 17;
public override unsafe string ToName(Record item) public override string ToName(Record item)
=> item.Crc64 != 0 ? $"{item.Crc64:X16}" : string.Empty; => item.Crc64 is not 0 ? $"{item.Crc64:X16}" : string.Empty;
public override unsafe void DrawColumn(Record item, int _) public override unsafe void DrawColumn(Record item, int _)
{ {
using var font = ImRaii.PushFont(UiBuilder.MonoFont, item.Handle != null); using var font = item.Handle is null ? null : Im.Font.PushMono();
ImUtf8.Text(ToName(item)); Im.Text(ToName(item));
} }
} }
@ -334,17 +332,17 @@ internal sealed class ResourceWatcherTable : Table<Record>
var (icon, color, tt) = item.LoadState switch var (icon, color, tt) = item.LoadState switch
{ {
LoadState.Success => (FontAwesomeIcon.CheckCircle, ColorId.IncreasedMetaValue.Value(), LoadState.Success => (FontAwesomeIcon.CheckCircle, ColorId.IncreasedMetaValue.Value(),
$"Successfully loaded ({(byte)item.LoadState})."), new StringU8($"Successfully loaded ({(byte)item.LoadState}).")),
LoadState.FailedSubResource => (FontAwesomeIcon.ExclamationCircle, ColorId.DecreasedMetaValue.Value(), LoadState.FailedSubResource => (FontAwesomeIcon.ExclamationCircle, ColorId.DecreasedMetaValue.Value(),
$"Dependencies failed to load ({(byte)item.LoadState})."), new StringU8($"Dependencies failed to load ({(byte)item.LoadState}).")),
<= LoadState.Constructed => (FontAwesomeIcon.QuestionCircle, ColorId.UndefinedMod.Value(), <= LoadState.Constructed => (FontAwesomeIcon.QuestionCircle, ColorId.UndefinedMod.Value(),
$"Not yet loaded ({(byte)item.LoadState})."), new StringU8($"Not yet loaded ({(byte)item.LoadState}).")),
< LoadState.Success => (FontAwesomeIcon.Clock, ColorId.FolderLine.Value(), $"Loading asynchronously ({(byte)item.LoadState})."), < LoadState.Success => (FontAwesomeIcon.Clock, ColorId.FolderLine.Value(), new StringU8($"Loading asynchronously ({(byte)item.LoadState}).")),
> LoadState.Success => (FontAwesomeIcon.Times, ColorId.DecreasedMetaValue.Value(), > LoadState.Success => (FontAwesomeIcon.Times, ColorId.DecreasedMetaValue.Value(),
$"Failed to load ({(byte)item.LoadState})."), new StringU8($"Failed to load ({(byte)item.LoadState}).")),
}; };
ImEx.Icon.Draw(icon.Icon(), color); ImEx.Icon.Draw(icon.Icon(), color);
ImGuiUtil.HoverTooltip(tt); Im.Tooltip.OnHover(tt);
} }
public override int Compare(Record lhs, Record rhs) public override int Compare(Record lhs, Record rhs)
@ -361,8 +359,8 @@ internal sealed class ResourceWatcherTable : Table<Record>
public override unsafe void DrawColumn(Record item, int _) public override unsafe void DrawColumn(Record item, int _)
{ {
using var font = ImRaii.PushFont(UiBuilder.MonoFont, item.Handle != null); using var font = item.Handle is null ? null : Im.Font.PushMono();
ImGuiUtil.RightAlign(ToName(item)); ImEx.TextRightAligned(ToName(item));
} }
} }
@ -447,7 +445,7 @@ internal sealed class ResourceWatcherTable : Table<Record>
=> 30 * Im.Style.GlobalScale; => 30 * Im.Style.GlobalScale;
public override void DrawColumn(Record item, int _) public override void DrawColumn(Record item, int _)
=> ImGuiUtil.RightAlign(item.RefCount.ToString()); => ImEx.TextRightAligned($"{item.RefCount}");
public override int Compare(Record lhs, Record rhs) public override int Compare(Record lhs, Record rhs)
=> lhs.RefCount.CompareTo(rhs.RefCount); => lhs.RefCount.CompareTo(rhs.RefCount);
@ -462,7 +460,7 @@ internal sealed class ResourceWatcherTable : Table<Record>
=> item.OsThreadId.ToString(); => item.OsThreadId.ToString();
public override void DrawColumn(Record item, int _) public override void DrawColumn(Record item, int _)
=> ImGuiUtil.RightAlign(ToName(item)); => ImEx.TextRightAligned($"{item.OsThreadId}");
public override int Compare(Record lhs, Record rhs) public override int Compare(Record lhs, Record rhs)
=> lhs.OsThreadId.CompareTo(rhs.OsThreadId); => lhs.OsThreadId.CompareTo(rhs.OsThreadId);

View file

@ -1,9 +1,9 @@
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using ImSharp; using ImSharp;
using Luna;
using OtterGui; using OtterGui;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Text; using OtterGui.Text;
using OtterGui.Widgets;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
using Penumbra.Collections.Manager; using Penumbra.Collections.Manager;
using Penumbra.Communication; using Penumbra.Communication;
@ -15,19 +15,22 @@ using Penumbra.UI.Classes;
namespace Penumbra.UI.Tabs; namespace Penumbra.UI.Tabs;
public class ChangedItemsTab( public sealed class ChangedItemsTab(
CollectionManager collectionManager, CollectionManager collectionManager,
CollectionSelectHeader collectionHeader, CollectionSelectHeader collectionHeader,
ChangedItemDrawer drawer, ChangedItemDrawer drawer,
CommunicatorService communicator) CommunicatorService communicator)
: ITab, Luna.IUiService : ITab<TabType>
{ {
public ReadOnlySpan<byte> Label public ReadOnlySpan<byte> Label
=> "Changed Items"u8; => "Changed Items"u8;
private string _changedItemFilter = string.Empty; public TabType Identifier
private string _changedItemModFilter = string.Empty; => TabType.ChangedItems;
private Vector2 _buttonSize;
private string _changedItemFilter = string.Empty;
private string _changedItemModFilter = string.Empty;
private Vector2 _buttonSize;
public void DrawContent() public void DrawContent()
{ {
@ -105,7 +108,7 @@ public class ChangedItemsTab(
if (ImUtf8.Selectable(first.Name, false, ImGuiSelectableFlags.None, _buttonSize with { X = 0 }) if (ImUtf8.Selectable(first.Name, false, ImGuiSelectableFlags.None, _buttonSize with { X = 0 })
&& ImGui.GetIO().KeyCtrl && ImGui.GetIO().KeyCtrl
&& first is Mod mod) && first is Mod mod)
communicator.SelectTab.Invoke(new SelectTab.Arguments(TabType.Mods, mod)); communicator.SelectTab.Invoke(new SelectTab.Arguments(Api.Enums.TabType.Mods, mod));
if (!Im.Item.Hovered()) if (!Im.Item.Hovered())
return; return;

View file

@ -2,8 +2,9 @@ using Dalamud.Bindings.ImGui;
using Dalamud.Game.ClientState.Objects; using Dalamud.Game.ClientState.Objects;
using Dalamud.Plugin; using Dalamud.Plugin;
using ImSharp; using ImSharp;
using Luna;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Widgets; using Penumbra.Api.Enums;
using Penumbra.Collections.Manager; using Penumbra.Collections.Manager;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.Mods.Manager; using Penumbra.Mods.Manager;
@ -12,7 +13,7 @@ using Penumbra.UI.CollectionTab;
namespace Penumbra.UI.Tabs; namespace Penumbra.UI.Tabs;
public sealed class CollectionsTab : IDisposable, ITab, Luna.IUiService public sealed class CollectionsTab : ITab<TabType>, IDisposable
{ {
private readonly EphemeralConfig _config; private readonly EphemeralConfig _config;
private readonly CollectionSelector _selector; private readonly CollectionSelector _selector;
@ -38,6 +39,9 @@ public sealed class CollectionsTab : IDisposable, ITab, Luna.IUiService
} }
} }
public TabType Identifier
=> TabType.Collections;
public CollectionsTab(IDalamudPluginInterface pi, Configuration configuration, CommunicatorService communicator, IncognitoService incognito, public CollectionsTab(IDalamudPluginInterface pi, Configuration configuration, CommunicatorService communicator, IncognitoService incognito,
CollectionManager collectionManager, ModStorage modStorage, ActorManager actors, ITargetManager targets, TutorialService tutorial, SaveService saveService) CollectionManager collectionManager, ModStorage modStorage, ActorManager actors, ITargetManager targets, TutorialService tutorial, SaveService saveService)
{ {

View file

@ -1,113 +0,0 @@
using Dalamud.Bindings.ImGui;
using OtterGui.Widgets;
using Penumbra.Api.Enums;
using Penumbra.Communication;
using Penumbra.Services;
using Penumbra.UI.Tabs.Debug;
using Watcher = Penumbra.UI.ResourceWatcher.ResourceWatcher;
namespace Penumbra.UI.Tabs;
public class ConfigTabBar : IDisposable, Luna.IUiService
{
private readonly CommunicatorService _communicator;
public readonly SettingsTab Settings;
public readonly ModsTab Mods;
public readonly CollectionsTab Collections;
public readonly ChangedItemsTab ChangedItems;
public readonly EffectiveTab Effective;
public readonly DebugTab Debug;
public readonly ResourceTab Resource;
public readonly Watcher Watcher;
public readonly OnScreenTab OnScreen;
public readonly MessagesTab Messages;
public readonly ITab[] Tabs;
/// <summary> The tab to select on the next Draw call, if any. </summary>
public TabType SelectTab = TabType.None;
public ConfigTabBar(CommunicatorService communicator, SettingsTab settings, ModsTab mods, CollectionsTab collections,
ChangedItemsTab changedItems, EffectiveTab effective, DebugTab debug, ResourceTab resource, Watcher watcher,
OnScreenTab onScreen, MessagesTab messages)
{
_communicator = communicator;
Settings = settings;
Mods = mods;
Collections = collections;
ChangedItems = changedItems;
Effective = effective;
Debug = debug;
Resource = resource;
Watcher = watcher;
OnScreen = onScreen;
Messages = messages;
Tabs =
[
Settings,
Collections,
Mods,
ChangedItems,
Effective,
OnScreen,
Debug,
Resource,
Watcher,
Messages,
];
_communicator.SelectTab.Subscribe(OnSelectTab, Communication.SelectTab.Priority.ConfigTabBar);
}
public void Dispose()
=> _communicator.SelectTab.Unsubscribe(OnSelectTab);
public TabType Draw()
{
if (TabBar.Draw(string.Empty, ImGuiTabBarFlags.NoTooltip, ToLabel(SelectTab), out var currentLabel, () => { }, Tabs))
SelectTab = TabType.None;
return FromLabel(currentLabel);
}
private ReadOnlySpan<byte> ToLabel(TabType type)
=> type switch
{
TabType.Settings => Settings.Label,
TabType.Mods => Mods.Label,
TabType.Collections => Collections.Label,
TabType.ChangedItems => ChangedItems.Label,
TabType.EffectiveChanges => Effective.Label,
TabType.OnScreen => OnScreen.Label,
TabType.ResourceWatcher => Watcher.Label,
TabType.Debug => Debug.Label,
TabType.ResourceManager => Resource.Label,
TabType.Messages => Messages.Label,
_ => ReadOnlySpan<byte>.Empty,
};
private TabType FromLabel(ReadOnlySpan<byte> label)
{
// @formatter:off
if (label == Mods.Label) return TabType.Mods;
if (label == Collections.Label) return TabType.Collections;
if (label == Settings.Label) return TabType.Settings;
if (label == ChangedItems.Label) return TabType.ChangedItems;
if (label == Effective.Label) return TabType.EffectiveChanges;
if (label == OnScreen.Label) return TabType.OnScreen;
if (label == Messages.Label) return TabType.Messages;
if (label == Watcher.Label) return TabType.ResourceWatcher;
if (label == Debug.Label) return TabType.Debug;
if (label == Resource.Label) return TabType.ResourceManager;
// @formatter:on
return TabType.None;
}
private void OnSelectTab(in SelectTab.Arguments arguments)
{
SelectTab = arguments.Tab;
if (arguments.Mod is not null)
Mods.SelectMod = arguments.Mod;
}
}

View file

@ -14,8 +14,8 @@ using Luna;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using OtterGui; using OtterGui;
using OtterGui.Text; using OtterGui.Text;
using OtterGui.Widgets;
using Penumbra.Api; using Penumbra.Api;
using Penumbra.Api.Enums;
using Penumbra.Collections.Manager; using Penumbra.Collections.Manager;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.DataContainers; using Penumbra.GameData.DataContainers;
@ -67,7 +67,7 @@ public class Diagnostics(ServiceManager provider) : IUiService
} }
} }
public class DebugTab : Window, ITab public sealed class DebugTab : Window, ITab<TabType>
{ {
private readonly Configuration _config; private readonly Configuration _config;
private readonly CollectionManager _collectionManager; private readonly CollectionManager _collectionManager;
@ -173,6 +173,9 @@ public class DebugTab : Window, ITab
public bool IsVisible public bool IsVisible
=> _config is { DebugMode: true, Ephemeral.DebugSeparateWindow: false }; => _config is { DebugMode: true, Ephemeral.DebugSeparateWindow: false };
public TabType Identifier
=> TabType.Debug;
#if DEBUG #if DEBUG
private const string DebugVersionString = "(Debug)"; private const string DebugVersionString = "(Debug)";
#else #else

View file

@ -1,10 +1,11 @@
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using Dalamud.Interface; using Dalamud.Interface;
using ImSharp; using ImSharp;
using Luna;
using OtterGui; using OtterGui;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Text; using OtterGui.Text;
using OtterGui.Widgets; using Penumbra.Api.Enums;
using Penumbra.Collections; using Penumbra.Collections;
using Penumbra.Collections.Cache; using Penumbra.Collections.Cache;
using Penumbra.Collections.Manager; using Penumbra.Collections.Manager;
@ -15,12 +16,15 @@ using Penumbra.UI.Classes;
namespace Penumbra.UI.Tabs; namespace Penumbra.UI.Tabs;
public class EffectiveTab(CollectionManager collectionManager, CollectionSelectHeader collectionHeader) public sealed class EffectiveTab(CollectionManager collectionManager, CollectionSelectHeader collectionHeader)
: ITab, Luna.IUiService : ITab<TabType>
{ {
public ReadOnlySpan<byte> Label public ReadOnlySpan<byte> Label
=> "Effective Changes"u8; => "Effective Changes"u8;
public TabType Identifier
=> TabType.EffectiveChanges;
public void DrawContent() public void DrawContent()
{ {
SetupEffectiveSizes(); SetupEffectiveSizes();

View file

@ -0,0 +1,55 @@
using Luna;
using Penumbra.Api.Enums;
using Penumbra.Communication;
using Penumbra.Services;
using Penumbra.UI.Tabs.Debug;
using Watcher = Penumbra.UI.ResourceWatcher.ResourceWatcher;
namespace Penumbra.UI.Tabs;
public sealed class MainTabBar : TabBar<TabType>, IDisposable
{
public readonly ModsTab Mods;
private readonly EphemeralConfig _config;
private readonly SelectTab _selectTab;
public MainTabBar(Logger log,
SettingsTab settings,
ModsTab mods,
CollectionsTab collections,
ChangedItemsTab changedItems,
EffectiveTab effectiveChanges,
DebugTab debug,
ResourceTab resources,
Watcher watcher,
OnScreenTab onScreen,
MessagesTab messages, EphemeralConfig config, CommunicatorService communicator)
: base(nameof(MainTabBar), log, settings, collections, mods, changedItems, effectiveChanges, onScreen,
resources, watcher, debug, messages)
{
Mods = mods;
_config = config;
_selectTab = communicator.SelectTab;
_selectTab.Subscribe(OnSelectTab, SelectTab.Priority.MainTabBar);
TabSelected.Subscribe(OnTabSelected, 0);
}
private void OnSelectTab(in SelectTab.Arguments arguments)
{
NextTab = arguments.Tab;
if (arguments.Mod is not null)
Mods.SelectMod = arguments.Mod;
}
public void Dispose()
{
_selectTab.Unsubscribe(OnSelectTab);
}
private void OnTabSelected(in TabType type)
{
_config.SelectedTab = type;
_config.Save();
}
}

View file

@ -1,9 +1,10 @@
using OtterGui.Widgets; using Luna;
using Penumbra.Services; using Penumbra.Api.Enums;
using MessageService = Penumbra.Services.MessageService;
namespace Penumbra.UI.Tabs; namespace Penumbra.UI.Tabs;
public class MessagesTab(MessageService messages) : ITab, Luna.IUiService public sealed class MessagesTab(MessageService messages) : ITab<TabType>
{ {
public ReadOnlySpan<byte> Label public ReadOnlySpan<byte> Label
=> "Messages"u8; => "Messages"u8;
@ -13,4 +14,7 @@ public class MessagesTab(MessageService messages) : ITab, Luna.IUiService
public void DrawContent() public void DrawContent()
=> messages.DrawNotificationLog(); => messages.DrawNotificationLog();
public TabType Identifier
=> TabType.Messages;
} }

View file

@ -7,7 +7,7 @@ using Dalamud.Interface;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game;
using ImSharp; using ImSharp;
using OtterGui.Widgets; using Luna;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
using Penumbra.Interop.Services; using Penumbra.Interop.Services;
using Penumbra.Mods; using Penumbra.Mods;
@ -19,7 +19,7 @@ using Penumbra.GameData.Interop;
namespace Penumbra.UI.Tabs; namespace Penumbra.UI.Tabs;
public class ModsTab( public sealed class ModsTab(
ModManager modManager, ModManager modManager,
CollectionManager collectionManager, CollectionManager collectionManager,
ModFileSystemSelector selector, ModFileSystemSelector selector,
@ -31,7 +31,7 @@ public class ModsTab(
CollectionSelectHeader collectionHeader, CollectionSelectHeader collectionHeader,
ITargetManager targets, ITargetManager targets,
ObjectManager objects) ObjectManager objects)
: ITab, Luna.IUiService : ITab<TabType>
{ {
private readonly ActiveCollections _activeCollections = collectionManager.Active; private readonly ActiveCollections _activeCollections = collectionManager.Active;
@ -41,7 +41,10 @@ public class ModsTab(
public ReadOnlySpan<byte> Label public ReadOnlySpan<byte> Label
=> "Mods"u8; => "Mods"u8;
public void DrawHeader() public TabType Identifier
=> TabType.Mods;
public void PostTabButton()
=> tutorial.OpenTutorial(BasicTutorialSteps.Mods); => tutorial.OpenTutorial(BasicTutorialSteps.Mods);
public Mod SelectMod public Mod SelectMod
@ -60,7 +63,8 @@ public class ModsTab(
collectionHeader.Draw(false); collectionHeader.Draw(false);
using var style = ImStyleDouble.ItemSpacing.Push(Vector2.Zero); using var style = ImStyleDouble.ItemSpacing.Push(Vector2.Zero);
using (var child = ImRaii.Child("##ModsTabMod", Im.ContentRegion.Available with { Y = config.HideRedrawBar ? 0 : -Im.Style.FrameHeight }, using (var child = ImRaii.Child("##ModsTabMod",
Im.ContentRegion.Available with { Y = config.HideRedrawBar ? 0 : -Im.Style.FrameHeight },
true, ImGuiWindowFlags.HorizontalScrollbar)) true, ImGuiWindowFlags.HorizontalScrollbar))
{ {
style.Pop(); style.Pop();

View file

@ -1,9 +1,10 @@
using OtterGui.Widgets; using Luna;
using Penumbra.Api.Enums;
using Penumbra.UI.AdvancedWindow; using Penumbra.UI.AdvancedWindow;
namespace Penumbra.UI.Tabs; namespace Penumbra.UI.Tabs;
public class OnScreenTab(ResourceTreeViewerFactory resourceTreeViewerFactory) : ITab, Luna.IUiService public sealed class OnScreenTab(ResourceTreeViewerFactory resourceTreeViewerFactory) : ITab<TabType>
{ {
private readonly ResourceTreeViewer _viewer = resourceTreeViewerFactory.Create(0, delegate { }, delegate { }); private readonly ResourceTreeViewer _viewer = resourceTreeViewerFactory.Create(0, delegate { }, delegate { });
@ -12,4 +13,7 @@ public class OnScreenTab(ResourceTreeViewerFactory resourceTreeViewerFactory) :
public void DrawContent() public void DrawContent()
=> _viewer.Draw(); => _viewer.Draw();
public TabType Identifier
=> TabType.OnScreen;
} }

View file

@ -4,17 +4,21 @@ using FFXIVClientStructs.FFXIV.Client.System.Resource;
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle; using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
using FFXIVClientStructs.STD; using FFXIVClientStructs.STD;
using ImSharp; using ImSharp;
using Luna;
using OtterGui; using OtterGui;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Widgets; using Penumbra.Api.Enums;
using Penumbra.Interop.Hooks.ResourceLoading; using Penumbra.Interop.Hooks.ResourceLoading;
using Penumbra.String.Classes; using Penumbra.String.Classes;
namespace Penumbra.UI.Tabs; namespace Penumbra.UI.Tabs;
public class ResourceTab(Configuration config, ResourceManagerService resourceManager, ISigScanner sigScanner) public sealed class ResourceTab(Configuration config, ResourceManagerService resourceManager, ISigScanner sigScanner)
: ITab, Luna.IUiService : ITab<TabType>
{ {
public TabType Identifier
=> TabType.ResourceManager;
public ReadOnlySpan<byte> Label public ReadOnlySpan<byte> Label
=> "Resource Manager"u8; => "Resource Manager"u8;
@ -52,7 +56,8 @@ public class ResourceTab(Configuration config, ResourceManagerService resourceMa
private string _resourceManagerFilter = string.Empty; private string _resourceManagerFilter = string.Empty;
/// <summary> Draw a single resource map. </summary> /// <summary> Draw a single resource map. </summary>
private unsafe void DrawResourceMap(ResourceCategory category, uint ext, StdMap<uint, FFXIVClientStructs.Interop.Pointer<ResourceHandle>>* map) private unsafe void DrawResourceMap(ResourceCategory category, uint ext,
StdMap<uint, FFXIVClientStructs.Interop.Pointer<ResourceHandle>>* map)
{ {
if (map == null) if (map == null)
return; return;

View file

@ -1,7 +1,6 @@
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Interface.Components; using Dalamud.Interface.Components;
using Dalamud.Interface.Utility;
using Dalamud.Plugin; using Dalamud.Plugin;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Dalamud.Utility; using Dalamud.Utility;
@ -12,6 +11,7 @@ using OtterGui.Raii;
using OtterGui.Text; using OtterGui.Text;
using OtterGui.Widgets; using OtterGui.Widgets;
using Penumbra.Api; using Penumbra.Api;
using Penumbra.Api.Enums;
using Penumbra.Collections; using Penumbra.Collections;
using Penumbra.Interop; using Penumbra.Interop;
using Penumbra.Interop.Hooks.PostProcessing; using Penumbra.Interop.Hooks.PostProcessing;
@ -23,10 +23,13 @@ using Penumbra.UI.ModsTab;
namespace Penumbra.UI.Tabs; namespace Penumbra.UI.Tabs;
public class SettingsTab : ITab, IUiService public sealed class SettingsTab : ITab<TabType>
{ {
public const int RootDirectoryMaxLength = 64; public const int RootDirectoryMaxLength = 64;
public TabType Identifier
=> TabType.Settings;
public ReadOnlySpan<byte> Label public ReadOnlySpan<byte> Label
=> "Settings"u8; => "Settings"u8;