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
{
/// <seealso cref="UI.Tabs.ConfigTabBar.OnSelectTab"/>
ConfigTabBar = 0,
MainTabBar = 0,
}
/// <summary> The arguments for a SelectTab event. </summary>

View file

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

View file

@ -134,7 +134,7 @@ public class Penumbra : IDalamudPlugin
AsyncTask.Run(() =>
{
var system = _services.GetService<PenumbraWindowSystem>();
system.Window.Setup(this, _services.GetService<ConfigTabBar>());
system.Window.Setup(this, _services.GetService<MainTabBar>());
_services.GetService<CommandHandler>();
if (!_disposed)
{
@ -199,8 +199,12 @@ public class Penumbra : IDalamudPlugin
{
ReadOnlySpan<string> relevantPlugins =
[
"Glamourer", "MareSynchronos", "CustomizePlus", "SimpleHeels", "VfxEditor", "heliosphere-plugin", "Ktisis", "Brio", "DynamicBridge",
"IllusioVitae", "Aetherment", "LoporritSync", "GagSpeak", "ProjectGagSpeak", "RoleplayingVoiceDalamud", "AQuestReborn",
"Glamourer", "CustomizePlus", "SimpleHeels",
"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
.GroupBy(p => p.InternalName)

View file

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

View file

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

View file

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

View file

@ -22,10 +22,10 @@ public class IncognitoService(TutorialService tutorial, Configuration config) :
config.Ephemeral.IncognitoMode = !IncognitoMode;
config.Ephemeral.Save();
}
}
if (!hold)
Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, $"\nHold {config.IncognitoModifier} while clicking to toggle.");
}
tutorial.OpenTutorial(BasicTutorialSteps.Incognito);
}

View file

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

View file

@ -1,9 +1,6 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Utility;
using ImSharp;
using OtterGui.Raii;
using OtterGui.Text;
using OtterGui.Widgets;
using Luna;
using Penumbra.Collections;
using Penumbra.Collections.Manager;
using Penumbra.Mods;
@ -11,7 +8,7 @@ using Penumbra.UI.Classes;
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
{
@ -25,18 +22,21 @@ public class ModPanelCollectionsTab(CollectionManager manager, ModFileSystemSele
public ReadOnlySpan<byte> Label
=> "Collections"u8;
public ModPanelTab Identifier
=> ModPanelTab.Collections;
public void DrawContent()
{
var (direct, inherited) = CountUsage(selector.Selected!);
Im.Line.New();
if (direct == 1)
ImUtf8.Text("This Mod is directly configured in 1 collection."u8);
else if (direct == 0)
ImUtf8.Text("This mod is entirely unused."u8, Colors.RegexWarningBorder);
else
ImUtf8.Text($"This Mod is directly configured in {direct} collections.");
switch (direct)
{
case 1: Im.Text("This Mod is directly configured in 1 collection."u8); break;
case 0: Im.Text("This mod is entirely unused."u8, Colors.RegexWarningBorder); break;
default: Im.Text($"This Mod is directly configured in {direct} collections."); break;
}
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.Separator();
@ -45,7 +45,7 @@ public class ModPanelCollectionsTab(CollectionManager manager, ModFileSystemSele
if (!table)
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;
table.SetupColumn("Collection"u8, TableColumnFlags.WidthFixed, collectionSize);
table.SetupColumn("State"u8, TableColumnFlags.WidthFixed, size);
@ -54,21 +54,21 @@ public class ModPanelCollectionsTab(CollectionManager manager, ModFileSystemSele
ImGui.TableHeadersRow();
foreach (var (idx, (collection, parent, color, state)) in _cache.Index())
{
using var id = ImUtf8.PushId(idx);
ImUtf8.DrawTableColumn(collection.Identity.Name);
using var id = Im.Id.Push(idx);
table.DrawColumn(collection.Identity.Name);
ImGui.TableNextColumn();
ImUtf8.Text(ToText(state), color);
table.NextColumn();
Im.Text(ToText(state), color);
using (var context = ImUtf8.PopupContextItem("Context"u8))
using (var context = Im.Popup.BeginContextItem("Context"u8))
{
if (context)
{
ImUtf8.Text(collection.Identity.Name);
Im.Text(collection.Identity.Name);
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)
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)
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);
}
}
}
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;
public class ModPanelConflictsTab(CollectionManager collectionManager, ModFileSystemSelector selector) : ITab, IUiService
public class ModPanelConflictsTab(CollectionManager collectionManager, ModFileSystemSelector selector) : ITab<ModPanelTab>
{
public ReadOnlySpan<byte> Label
=> "Conflicts"u8;
public ModPanelTab Identifier
=> ModPanelTab.Conflicts;
public bool IsVisible
=> collectionManager.Active.Current.Conflicts(selector.Selected!).Any(c => !GetPriority(c).IsHidden);

View file

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

View file

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

View file

@ -1,10 +1,5 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using ImSharp;
using Luna;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Widgets;
using Penumbra.Mods;
using Penumbra.Mods.Manager;
using Penumbra.UI.AdvancedWindow;
@ -12,9 +7,7 @@ using ImGuiColor = ImSharp.ImGuiColor;
namespace Penumbra.UI.ModsTab;
public class ModPanelTabBar : IUiService
{
private enum ModPanelTabType
public enum ModPanelTab
{
Description,
Settings,
@ -24,126 +17,78 @@ public class ModPanelTabBar : IUiService
Edit,
};
public class ModPanelTabBar : TabBar<ModPanelTab>
{
public readonly ModPanelSettingsTab Settings;
public readonly ModPanelDescriptionTab Description;
public readonly ModPanelCollectionsTab Collections;
public readonly ModPanelConflictsTab Conflicts;
public readonly ModPanelChangedItemsTab ChangedItems;
public readonly ModPanelEditTab Edit;
private readonly ModEditWindowFactory _modEditWindowFactory;
private readonly ModManager _modManager;
private readonly TutorialService _tutorial;
public readonly ITab[] Tabs;
private ModPanelTabType _preferredTab = ModPanelTabType.Settings;
private Mod? _lastMod;
public ModPanelTabBar(ModEditWindowFactory modEditWindowFactory, ModPanelSettingsTab settings, ModPanelDescriptionTab description,
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;
Description = description;
Conflicts = conflicts;
ChangedItems = changedItems;
Edit = edit;
_modManager = modManager;
_tutorial = tutorial;
Collections = collections;
Buttons.AddButton(new AdvancedEditingButton(this, modEditWindowFactory), 0);
}
Tabs =
[
Settings,
Description,
Conflicts,
ChangedItems,
Collections,
Edit,
];
private sealed class AdvancedEditingButton(ModPanelTabBar parent, ModEditWindowFactory editFactory) : BaseButton
{
public override ReadOnlySpan<byte> Label
=> "Advanced Editing"u8;
public override void OnClick()
{
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)
{
var tabBarHeight = ImGui.GetCursorPosY();
if (_lastMod != mod)
{
var tabBarHeight = Im.Cursor.Y;
_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);
}
base.Draw();
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)
{
var size = ImEx.Icon.CalculateSize(LunaStyle.FavoriteIcon) + Im.Style.FramePadding * 2;
var newPos = new Vector2(ImGui.GetWindowWidth() - size.X - Im.Style.ItemSpacing.X, height);
if (ImGui.GetScrollMaxX() > 0)
newPos.X += ImGui.GetScrollX();
var newPos = new Vector2(Im.Window.Width - size.X - Im.Style.ItemSpacing.X, height);
if (Im.Scroll.MaximumX > 0)
newPos.X += Im.Scroll.X;
var rectUpper = ImGui.GetWindowPos() + newPos;
var color = ImGui.IsMouseHoveringRect(rectUpper, rectUpper + size) ? Im.Style[ImGuiColor.Text] :
var rectUpper = Im.Window.Position + newPos;
var color = Im.Mouse.IsHoveringRectangle(rectUpper, rectUpper + size) ? Im.Style[ImGuiColor.Text] :
mod.Favorite ? LunaStyle.FavoriteColor : Im.Style[ImGuiColor.TextDisabled];
using var c = ImGuiColor.Text.Push(color)
.Push(ImGuiColor.Button, Vector4.Zero)
.Push(ImGuiColor.ButtonHovered, Vector4.Zero)
.Push(ImGuiColor.ButtonActive, Vector4.Zero);
ImGui.SetCursorPos(newPos);
Im.Cursor.Position = newPos;
if (ImEx.Icon.Button(LunaStyle.FavoriteIcon))
_modManager.DataEditor.ChangeModFavorite(mod, !mod.Favorite);
@ -151,6 +96,6 @@ public class ModPanelTabBar : IUiService
_tutorial.OpenTutorial(BasicTutorialSteps.Favorites);
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.System.Resource;
using ImSharp;
using OtterGui.Widgets;
using Luna;
using Penumbra.Api.Enums;
using Penumbra.Collections;
using Penumbra.GameData.Actors;
@ -16,7 +16,7 @@ using Penumbra.UI.Classes;
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 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
=> "Resource Logger"u8;
public TabType Identifier
=> TabType.ResourceWatcher;
public void DrawContent()
{
UpdateRecords();

View file

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

View file

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

View file

@ -2,8 +2,9 @@ using Dalamud.Bindings.ImGui;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Plugin;
using ImSharp;
using Luna;
using OtterGui.Raii;
using OtterGui.Widgets;
using Penumbra.Api.Enums;
using Penumbra.Collections.Manager;
using Penumbra.GameData.Actors;
using Penumbra.Mods.Manager;
@ -12,7 +13,7 @@ using Penumbra.UI.CollectionTab;
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 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,
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 OtterGui;
using OtterGui.Text;
using OtterGui.Widgets;
using Penumbra.Api;
using Penumbra.Api.Enums;
using Penumbra.Collections.Manager;
using Penumbra.GameData.Actors;
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 CollectionManager _collectionManager;
@ -173,6 +173,9 @@ public class DebugTab : Window, ITab
public bool IsVisible
=> _config is { DebugMode: true, Ephemeral.DebugSeparateWindow: false };
public TabType Identifier
=> TabType.Debug;
#if DEBUG
private const string DebugVersionString = "(Debug)";
#else

View file

@ -1,10 +1,11 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using ImSharp;
using Luna;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Text;
using OtterGui.Widgets;
using Penumbra.Api.Enums;
using Penumbra.Collections;
using Penumbra.Collections.Cache;
using Penumbra.Collections.Manager;
@ -15,12 +16,15 @@ using Penumbra.UI.Classes;
namespace Penumbra.UI.Tabs;
public class EffectiveTab(CollectionManager collectionManager, CollectionSelectHeader collectionHeader)
: ITab, Luna.IUiService
public sealed class EffectiveTab(CollectionManager collectionManager, CollectionSelectHeader collectionHeader)
: ITab<TabType>
{
public ReadOnlySpan<byte> Label
=> "Effective Changes"u8;
public TabType Identifier
=> TabType.EffectiveChanges;
public void DrawContent()
{
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 Penumbra.Services;
using Luna;
using Penumbra.Api.Enums;
using MessageService = Penumbra.Services.MessageService;
namespace Penumbra.UI.Tabs;
public class MessagesTab(MessageService messages) : ITab, Luna.IUiService
public sealed class MessagesTab(MessageService messages) : ITab<TabType>
{
public ReadOnlySpan<byte> Label
=> "Messages"u8;
@ -13,4 +14,7 @@ public class MessagesTab(MessageService messages) : ITab, Luna.IUiService
public void DrawContent()
=> messages.DrawNotificationLog();
public TabType Identifier
=> TabType.Messages;
}

View file

@ -7,7 +7,7 @@ using Dalamud.Interface;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using ImSharp;
using OtterGui.Widgets;
using Luna;
using Penumbra.Api.Enums;
using Penumbra.Interop.Services;
using Penumbra.Mods;
@ -19,7 +19,7 @@ using Penumbra.GameData.Interop;
namespace Penumbra.UI.Tabs;
public class ModsTab(
public sealed class ModsTab(
ModManager modManager,
CollectionManager collectionManager,
ModFileSystemSelector selector,
@ -31,7 +31,7 @@ public class ModsTab(
CollectionSelectHeader collectionHeader,
ITargetManager targets,
ObjectManager objects)
: ITab, Luna.IUiService
: ITab<TabType>
{
private readonly ActiveCollections _activeCollections = collectionManager.Active;
@ -41,7 +41,10 @@ public class ModsTab(
public ReadOnlySpan<byte> Label
=> "Mods"u8;
public void DrawHeader()
public TabType Identifier
=> TabType.Mods;
public void PostTabButton()
=> tutorial.OpenTutorial(BasicTutorialSteps.Mods);
public Mod SelectMod
@ -60,7 +63,8 @@ public class ModsTab(
collectionHeader.Draw(false);
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))
{
style.Pop();

View file

@ -1,9 +1,10 @@
using OtterGui.Widgets;
using Luna;
using Penumbra.Api.Enums;
using Penumbra.UI.AdvancedWindow;
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 { });
@ -12,4 +13,7 @@ public class OnScreenTab(ResourceTreeViewerFactory resourceTreeViewerFactory) :
public void DrawContent()
=> _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.STD;
using ImSharp;
using Luna;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Widgets;
using Penumbra.Api.Enums;
using Penumbra.Interop.Hooks.ResourceLoading;
using Penumbra.String.Classes;
namespace Penumbra.UI.Tabs;
public class ResourceTab(Configuration config, ResourceManagerService resourceManager, ISigScanner sigScanner)
: ITab, Luna.IUiService
public sealed class ResourceTab(Configuration config, ResourceManagerService resourceManager, ISigScanner sigScanner)
: ITab<TabType>
{
public TabType Identifier
=> TabType.ResourceManager;
public ReadOnlySpan<byte> Label
=> "Resource Manager"u8;
@ -52,7 +56,8 @@ public class ResourceTab(Configuration config, ResourceManagerService resourceMa
private string _resourceManagerFilter = string.Empty;
/// <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)
return;

View file

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