diff --git a/Luna b/Luna index 2e984d9c..cb294f47 160000 --- a/Luna +++ b/Luna @@ -1 +1 @@ -Subproject commit 2e984d9c21370c778d172ab955def18c0dbe8c7d +Subproject commit cb294f476476f7a3d8b56a0072dbd300b3d54c4f diff --git a/Penumbra/Communication/SelectTab.cs b/Penumbra/Communication/SelectTab.cs index 144b362a..8030badd 100644 --- a/Penumbra/Communication/SelectTab.cs +++ b/Penumbra/Communication/SelectTab.cs @@ -10,7 +10,7 @@ public sealed class SelectTab(Logger log) : EventBase - ConfigTabBar = 0, + MainTabBar = 0, } /// The arguments for a SelectTab event. diff --git a/Penumbra/EphemeralConfig.cs b/Penumbra/EphemeralConfig.cs index caf34027..dba07d29 100644 --- a/Penumbra/EphemeralConfig.cs +++ b/Penumbra/EphemeralConfig.cs @@ -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; diff --git a/Penumbra/Penumbra.cs b/Penumbra/Penumbra.cs index b169a9a4..ee9c0d75 100644 --- a/Penumbra/Penumbra.cs +++ b/Penumbra/Penumbra.cs @@ -134,7 +134,7 @@ public class Penumbra : IDalamudPlugin AsyncTask.Run(() => { var system = _services.GetService(); - system.Window.Setup(this, _services.GetService()); + system.Window.Setup(this, _services.GetService()); _services.GetService(); if (!_disposed) { @@ -199,8 +199,12 @@ public class Penumbra : IDalamudPlugin { ReadOnlySpan 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().InstalledPlugins .GroupBy(p => p.InternalName) diff --git a/Penumbra/Services/ConfigMigrationService.cs b/Penumbra/Services/ConfigMigrationService.cs index 8369e1d6..36688027 100644 --- a/Penumbra/Services/ConfigMigrationService.cs +++ b/Penumbra/Services/ConfigMigrationService.cs @@ -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; diff --git a/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs b/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs index 57e72dd1..70a88c00 100644 --- a/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs +++ b/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs @@ -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; diff --git a/Penumbra/UI/ConfigWindow.cs b/Penumbra/UI/ConfigWindow.cs index 9f2c458a..f054a09b 100644 --- a/Penumbra/UI/ConfigWindow.cs +++ b/Penumbra/UI/ConfigWindow.cs @@ -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; - IsOpen = true; + _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; + _penumbra = penumbra; + _configTabs = configTabs; + _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; diff --git a/Penumbra/UI/IncognitoService.cs b/Penumbra/UI/IncognitoService.cs index 411a5145..9d930790 100644 --- a/Penumbra/UI/IncognitoService.cs +++ b/Penumbra/UI/IncognitoService.cs @@ -15,18 +15,18 @@ public class IncognitoService(TutorialService tutorial, Configuration config) : var color = ColorId.FolderExpanded.Value(); using (ImStyleBorder.Frame.Push(color)) { - var tt = IncognitoMode ? "Toggle incognito mode off."u8 : "Toggle incognito mode on."u8; - var icon = IncognitoMode ? LunaStyle.IncognitoOn : LunaStyle.IncognitoOff; + var tt = IncognitoMode ? "Toggle incognito mode off."u8 : "Toggle incognito mode on."u8; + var icon = IncognitoMode ? LunaStyle.IncognitoOn : LunaStyle.IncognitoOff; if (ImEx.Icon.Button(icon, tt, size: new Vector2(width, Im.Style.FrameHeight), textColor: color) && hold) { config.Ephemeral.IncognitoMode = !IncognitoMode; 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); } } diff --git a/Penumbra/UI/ModsTab/ModPanelChangedItemsTab.cs b/Penumbra/UI/ModsTab/ModPanelChangedItemsTab.cs index 55582130..d4e7a79e 100644 --- a/Penumbra/UI/ModsTab/ModPanelChangedItemsTab.cs +++ b/Penumbra/UI/ModsTab/ModPanelChangedItemsTab.cs @@ -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 { private readonly ImGuiCacheService.CacheId _cacheId = cacheService.GetNewId(); @@ -209,6 +208,9 @@ public class ModPanelChangedItemsTab( public ReadOnlySpan Label => "Changed Items"u8; + public ModPanelTab Identifier + => ModPanelTab.ChangedItems; + public bool IsVisible => selector.Selected!.ChangedItems.Count > 0; diff --git a/Penumbra/UI/ModsTab/ModPanelCollectionsTab.cs b/Penumbra/UI/ModsTab/ModPanelCollectionsTab.cs index 81615c32..52992deb 100644 --- a/Penumbra/UI/ModsTab/ModPanelCollectionsTab.cs +++ b/Penumbra/UI/ModsTab/ModPanelCollectionsTab.cs @@ -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 { private enum ModState { @@ -24,19 +21,22 @@ public class ModPanelCollectionsTab(CollectionManager manager, ModFileSystemSele public ReadOnlySpan 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); } } diff --git a/Penumbra/UI/ModsTab/ModPanelConflictsTab.cs b/Penumbra/UI/ModsTab/ModPanelConflictsTab.cs index 68e6912d..d8a6fc52 100644 --- a/Penumbra/UI/ModsTab/ModPanelConflictsTab.cs +++ b/Penumbra/UI/ModsTab/ModPanelConflictsTab.cs @@ -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 { public ReadOnlySpan Label => "Conflicts"u8; + public ModPanelTab Identifier + => ModPanelTab.Conflicts; + public bool IsVisible => collectionManager.Active.Current.Conflicts(selector.Selected!).Any(c => !GetPriority(c).IsHidden); diff --git a/Penumbra/UI/ModsTab/ModPanelDescriptionTab.cs b/Penumbra/UI/ModsTab/ModPanelDescriptionTab.cs index d956b004..a0fe226f 100644 --- a/Penumbra/UI/ModsTab/ModPanelDescriptionTab.cs +++ b/Penumbra/UI/ModsTab/ModPanelDescriptionTab.cs @@ -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,23 +11,25 @@ public class ModPanelDescriptionTab( TutorialService tutorial, ModManager modManager, PredefinedTagManager predefinedTagsConfig) - : ITab, Luna.IUiService + : ITab { private readonly TagButtons _localTags = new(); private readonly TagButtons _modTags = new(); public ReadOnlySpan 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); } } diff --git a/Penumbra/UI/ModsTab/ModPanelEditTab.cs b/Penumbra/UI/ModsTab/ModPanelEditTab.cs index 19d62b8d..42b9fee2 100644 --- a/Penumbra/UI/ModsTab/ModPanelEditTab.cs +++ b/Penumbra/UI/ModsTab/ModPanelEditTab.cs @@ -1,370 +1,335 @@ -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; - -public class ModPanelEditTab( - ModManager modManager, - ModFileSystemSelector selector, - ModFileSystem fileSystem, - Services.MessageService messager, - FilenameService filenames, - ModExportManager modExportManager, - Configuration config, - PredefinedTagManager predefinedTagManager, - ModGroupEditDrawer groupEditDrawer, - DescriptionEditPopup descriptionPopup, - AddGroupDrawer addGroupDrawer) - : ITab, IUiService -{ - private readonly TagButtons _modTags = new(); - - private ModFileSystem.Leaf _leaf = null!; - private Mod _mod = null!; - - public ReadOnlySpan Label - => "Edit Mod"u8; - - public void DrawContent() - { - using var child = ImRaii.Child("##editChild", -Vector2.One); - if (!child) - return; - - _leaf = selector.SelectedLeaf!; - _mod = selector.Selected!; - - EditButtons(); - EditRegularMeta(); - UiHelpers.DefaultLineSpace(); - EditLocalData(); - UiHelpers.DefaultLineSpace(); - - if (Input.Text("Mod Path", Input.Path, Input.None, _leaf.FullName(), out var newPath, 256, UiHelpers.InputTextWidth.X)) - try - { - fileSystem.RenameAndMove(_leaf, newPath); - } - catch (Exception e) - { - messager.NotificationMessage(e.Message, NotificationType.Warning, false); - } - - UiHelpers.DefaultLineSpace(); - - FeatureChecker.DrawFeatureFlagInput(modManager.DataEditor, _mod, UiHelpers.InputTextWidth.X); - - UiHelpers.DefaultLineSpace(); - var sharedTagsEnabled = predefinedTagManager.Enabled; - var sharedTagButtonOffset = sharedTagsEnabled ? Im.Style.FrameHeight + Im.Style.FramePadding.X : 0; - var tagIdx = _modTags.Draw("Mod Tags: ", "Edit tags by clicking them, or add new tags. Empty tags are removed.", _mod.ModTags, - out var editedTag, rightEndOffset: sharedTagButtonOffset); - if (tagIdx >= 0) - modManager.DataEditor.ChangeModTag(_mod, tagIdx, editedTag); - - if (sharedTagsEnabled) - predefinedTagManager.DrawAddFromSharedTagsAndUpdateTags(selector.Selected!.LocalTags, selector.Selected!.ModTags, false, - selector.Selected!); - - - UiHelpers.DefaultLineSpace(); - addGroupDrawer.Draw(_mod, UiHelpers.InputTextWidth.X); - UiHelpers.DefaultLineSpace(); - - groupEditDrawer.Draw(_mod); - descriptionPopup.Draw(); - } - - public void Reset() - { - MoveDirectory.Reset(); - Input.Reset(); - } - - /// The general edit row for non-detailed mod edits. - private void EditButtons() - { - var buttonSize = new Vector2(150 * Im.Style.GlobalScale, 0); - var folderExists = Directory.Exists(_mod.ModPath.FullName); - var tt = 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)) - 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.", - false)) - modManager.ReloadMod(_mod); - - BackupButtons(buttonSize); - MoveDirectory.Draw(modManager, _mod, buttonSize); - - UiHelpers.DefaultLineSpace(); - } - - private void BackupButtons(Vector2 buttonSize) - { - var backup = new ModBackup(modExportManager, _mod); - var tt = 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)) - backup.CreateAsync(); - - if (Im.Item.RightClicked()) - ImUtf8.OpenPopup("context"u8); - - Im.Line.Same(); - tt = 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())) - 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())) - backup.Restore(modManager); - if (backup.Exists) - { - Im.Line.Same(); - using (ImRaii.PushFont(UiBuilder.IconFont)) - { - ImUtf8.Text(FontAwesomeIcon.CheckCircle.ToIconString()); - } - - Im.Tooltip.OnHover($"Export exists in \"{backup.Name}\"."); - } - - using var context = ImUtf8.Popup("context"u8); - if (!context) - return; - - if (ImUtf8.Selectable("Open Backup Directory"u8)) - Process.Start(new ProcessStartInfo(modExportManager.ExportDirectory.FullName) { UseShellExecute = true }); - } - - /// Anything about editing the regular meta information about the mod. - private void EditRegularMeta() - { - if (Input.Text("Name", Input.Name, Input.None, _mod.Name, out var newName, 256, 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)) - modManager.DataEditor.ChangeModAuthor(_mod, newAuthor); - - if (Input.Text("Version", Input.Version, Input.None, _mod.Version, out var newVersion, 32, - UiHelpers.InputTextWidth.X)) - modManager.DataEditor.ChangeModVersion(_mod, newVersion); - - if (Input.Text("Website", Input.Website, Input.None, _mod.Website, out var newWebsite, 256, - 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)) - 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)) - Process.Start(new ProcessStartInfo(filenames.ModMetaPath(_mod)) { UseShellExecute = true }); - - DrawOpenDefaultMod(); - } - - private void EditLocalData() - { - DrawImportDate(); - DrawOpenLocalData(); - } - - private void DrawImportDate() - { - ImEx.TextFramed($"{DateTimeOffset.FromUnixTimeMilliseconds(_mod.ImportDate).ToLocalTime():yyyy/MM/dd HH:mm}", - new Vector2(UiHelpers.InputTextMinusButton3, 0), ImGuiColor.FrameBackground.Get(0.5f)); - 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)) - modManager.DataEditor.ResetModImportDate(_mod); - Im.Line.SameInner(); - ImUtf8.Text("Import Date"u8); - } - - private void DrawOpenLocalData() - { - var file = filenames.LocalDataFile(_mod); - var fileExists = File.Exists(file); - 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)) - Process.Start(new ProcessStartInfo(file) { UseShellExecute = true }); - } - - private void DrawOpenDefaultMod() - { - 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)) - Process.Start(new ProcessStartInfo(file) { UseShellExecute = true }); - } - - - /// A text input for the new directory name and a button to apply the move. - private static class MoveDirectory - { - private static string? _currentModDirectory; - private static NewDirectoryState _state = NewDirectoryState.Identical; - - public static void Reset() - { - _currentModDirectory = null; - _state = NewDirectoryState.Identical; - } - - public static void Draw(ModManager modManager, Mod mod, Vector2 buttonSize) - { - Im.Item.SetNextWidth(buttonSize.X * 2 + Im.Style.ItemSpacing.X); - var tmp = _currentModDirectory ?? mod.ModPath.Name; - if (ImGui.InputText("##newModMove", ref tmp, 64)) - { - _currentModDirectory = tmp; - _state = modManager.NewDirectoryValid(mod.ModPath.Name, _currentModDirectory, out _); - } - - var (disabled, tt) = _state switch - { - NewDirectoryState.Identical => (true, "Current directory name is identical to new one."), - NewDirectoryState.Empty => (true, "Please enter a new directory name first."), - NewDirectoryState.NonExisting => (false, $"Move mod from {mod.ModPath.Name} to {_currentModDirectory}."), - NewDirectoryState.ExistsEmpty => (false, $"Move mod from {mod.ModPath.Name} to {_currentModDirectory}."), - NewDirectoryState.ExistsNonEmpty => (true, $"{_currentModDirectory} already exists and is not empty."), - NewDirectoryState.ExistsAsFile => (true, $"{_currentModDirectory} exists as a file."), - NewDirectoryState.ContainsInvalidSymbols => (true, - $"{_currentModDirectory} contains invalid symbols for FFXIV."), - _ => (true, "Unknown error."), - }; - Im.Line.Same(); - if (ImGuiUtil.DrawDisabledButton("Rename Mod Directory", buttonSize, tt, disabled) && _currentModDirectory != 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."); - } - } - - /// Handles input text and integers in separate fields without buffers for every single one. - private static class Input - { - // Special field indices to reuse the same string buffer. - public const int None = -1; - public const int Name = -2; - public const int Author = -3; - 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) - { - var tmp = field == _currentField && option == _optionIndex ? _currentEdit ?? oldValue : oldValue; - Im.Item.SetNextWidth(width); - - if (ImGui.InputText(label, ref tmp)) - { - _currentEdit = tmp; - _optionIndex = option; - _currentField = field; - } - - if (ImGui.IsItemDeactivatedAfterEdit() && _currentEdit != null) - { - var ret = _currentEdit != oldValue; - value = _currentEdit; - Reset(); - return ret; - } - - 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; - } - } -} +using Dalamud.Interface; +using Dalamud.Interface.ImGuiNotification; +using ImSharp; +using Luna; +using OtterGui.Widgets; +using Penumbra.Mods; +using Penumbra.Mods.Editor; +using Penumbra.Mods.Manager; +using Penumbra.Services; +using Penumbra.UI.ModsTab.Groups; + +namespace Penumbra.UI.ModsTab; + +public class ModPanelEditTab( + ModManager modManager, + ModFileSystemSelector selector, + ModFileSystem fileSystem, + Services.MessageService messager, + FilenameService filenames, + ModExportManager modExportManager, + Configuration config, + PredefinedTagManager predefinedTagManager, + ModGroupEditDrawer groupEditDrawer, + DescriptionEditPopup descriptionPopup, + AddGroupDrawer addGroupDrawer) + : ITab +{ + private readonly TagButtons _modTags = new(); + + private ModFileSystem.Leaf _leaf = null!; + private Mod _mod = null!; + + public ReadOnlySpan Label + => "Edit Mod"u8; + + public ModPanelTab Identifier + => ModPanelTab.Edit; + + public void DrawContent() + { + using var child = Im.Child.Begin("##editChild"u8, Im.ContentRegion.Available); + if (!child) + return; + + _leaf = selector.SelectedLeaf!; + _mod = selector.Selected!; + + EditButtons(); + EditRegularMeta(); + UiHelpers.DefaultLineSpace(); + EditLocalData(); + UiHelpers.DefaultLineSpace(); + + if (Input.Text("Mod Path"u8, Input.Path, Input.None, _leaf.FullName(), out var newPath, UiHelpers.InputTextWidth.X)) + try + { + fileSystem.RenameAndMove(_leaf, newPath); + } + catch (Exception e) + { + messager.NotificationMessage(e.Message, NotificationType.Warning, false); + } + + UiHelpers.DefaultLineSpace(); + + FeatureChecker.DrawFeatureFlagInput(modManager.DataEditor, _mod, UiHelpers.InputTextWidth.X); + + UiHelpers.DefaultLineSpace(); + var sharedTagsEnabled = predefinedTagManager.Enabled; + var sharedTagButtonOffset = sharedTagsEnabled ? Im.Style.FrameHeight + Im.Style.FramePadding.X : 0; + var tagIdx = _modTags.Draw("Mod Tags: ", "Edit tags by clicking them, or add new tags. Empty tags are removed.", _mod.ModTags, + out var editedTag, rightEndOffset: sharedTagButtonOffset); + if (tagIdx >= 0) + modManager.DataEditor.ChangeModTag(_mod, tagIdx, editedTag); + + if (sharedTagsEnabled) + predefinedTagManager.DrawAddFromSharedTagsAndUpdateTags(selector.Selected!.LocalTags, selector.Selected!.ModTags, false, + selector.Selected!); + + + UiHelpers.DefaultLineSpace(); + addGroupDrawer.Draw(_mod, UiHelpers.InputTextWidth.X); + UiHelpers.DefaultLineSpace(); + + groupEditDrawer.Draw(_mod); + descriptionPopup.Draw(); + } + + public void Reset() + { + MoveDirectory.Reset(); + Input.Reset(); + } + + /// The general edit row for non-detailed mod edits. + private void EditButtons() + { + var buttonSize = new Vector2(150 * Im.Style.GlobalScale, 0); + var folderExists = Directory.Exists(_mod.ModPath.FullName); + 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.", !folderExists)) + Process.Start(new ProcessStartInfo(_mod.ModPath.FullName) { UseShellExecute = true }); + + Im.Line.Same(); + 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); + + BackupButtons(buttonSize); + MoveDirectory.Draw(modManager, _mod, buttonSize); + + UiHelpers.DefaultLineSpace(); + } + + private void BackupButtons(Vector2 buttonSize) + { + var backup = new ModBackup(modExportManager, _mod); + 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}\".", ModBackup.CreatingBackup)) + backup.CreateAsync(); + + if (Im.Item.RightClicked()) + Im.Popup.Open("context"u8); + + Im.Line.Same(); + 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.", !backup.Exists || !config.DeleteModModifier.IsActive())) + backup.Delete(); + + Im.Line.Same(); + 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(); + ImEx.Icon.Draw(FontAwesomeIcon.CheckCircle.Icon()); + Im.Tooltip.OnHover($"Export exists in \"{backup.Name}\"."); + } + + using var context = Im.Popup.Begin("context"u8); + if (!context) + return; + + if (Im.Selectable("Open Backup Directory"u8)) + Process.Start(new ProcessStartInfo(modExportManager.ExportDirectory.FullName) { UseShellExecute = true }); + } + + /// Anything about editing the regular meta information about the mod. + private void EditRegularMeta() + { + 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"u8, Input.Author, Input.None, _mod.Author, out var newAuthor, UiHelpers.InputTextWidth.X)) + modManager.DataEditor.ChangeModAuthor(_mod, newAuthor); + + 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"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 (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."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(); + } + + private void EditLocalData() + { + DrawImportDate(); + DrawOpenLocalData(); + } + + private void DrawImportDate() + { + ImEx.TextFramed($"{DateTimeOffset.FromUnixTimeMilliseconds(_mod.ImportDate).ToLocalTime():yyyy/MM/dd HH:mm}", + new Vector2(UiHelpers.InputTextMinusButton3, 0), ImGuiColor.FrameBackground.Get(0.5f)); + Im.Line.Same(0, 3 * Im.Style.GlobalScale); + + var canRefresh = config.DeleteModModifier.IsActive(); + 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(); + Im.Text("Import Date"u8); + } + + private void DrawOpenLocalData() + { + var file = filenames.LocalDataFile(_mod); + var fileExists = File.Exists(file); + 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 (ImEx.Button("Open Local Data"u8, UiHelpers.InputTextWidth, tt, !fileExists)) + Process.Start(new ProcessStartInfo(file) { UseShellExecute = true }); + } + + private void DrawOpenDefaultMod() + { + 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."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 }); + } + + + /// A text input for the new directory name and a button to apply the move. + private static class MoveDirectory + { + private static string? _currentModDirectory; + private static NewDirectoryState _state = NewDirectoryState.Identical; + + public static void Reset() + { + _currentModDirectory = null; + _state = NewDirectoryState.Identical; + } + + public static void Draw(ModManager modManager, Mod mod, Vector2 buttonSize) + { + Im.Item.SetNextWidth(buttonSize.X * 2 + Im.Style.ItemSpacing.X); + var tmp = _currentModDirectory ?? mod.ModPath.Name; + if (Im.Input.Text("##newModMove"u8, ref tmp)) + { + _currentModDirectory = tmp; + _state = modManager.NewDirectoryValid(mod.ModPath.Name, _currentModDirectory, out _); + } + + var (disabled, tt) = _state switch + { + NewDirectoryState.Identical => (true, "Current directory name is identical to new one."), + NewDirectoryState.Empty => (true, "Please enter a new directory name first."), + NewDirectoryState.NonExisting => (false, $"Move mod from {mod.ModPath.Name} to {_currentModDirectory}."), + NewDirectoryState.ExistsEmpty => (false, $"Move mod from {mod.ModPath.Name} to {_currentModDirectory}."), + NewDirectoryState.ExistsNonEmpty => (true, $"{_currentModDirectory} already exists and is not empty."), + NewDirectoryState.ExistsAsFile => (true, $"{_currentModDirectory} exists as a file."), + NewDirectoryState.ContainsInvalidSymbols => (true, + $"{_currentModDirectory} contains invalid symbols for FFXIV."), + _ => (true, "Unknown error."), + }; + Im.Line.Same(); + if (ImEx.Button("Rename Mod Directory"u8, buttonSize, tt, disabled) && _currentModDirectory is not null) + { + modManager.MoveModDirectory(mod, _currentModDirectory); + Reset(); + } + + Im.Line.Same(); + 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); + } + } + + /// Handles input text and integers in separate fields without buffers for every single one. + private static class Input + { + // Special field indices to reuse the same string buffer. + public const int None = -1; + public const int Name = -2; + public const int Author = -3; + public const int Version = -4; + public const int Website = -5; + public const int Path = -6; + + // Temporary strings + private static string? _currentEdit; + private static int _currentField = None; + private static int _optionIndex = None; + + public static void Reset() + { + _currentEdit = null; + _currentField = None; + _optionIndex = None; + } + + public static bool Text(ReadOnlySpan 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 (Im.Input.Text(label, ref tmp)) + { + _currentEdit = tmp; + _optionIndex = option; + _currentField = field; + } + + if (Im.Item.DeactivatedAfterEdit && _currentEdit is not null) + { + var ret = _currentEdit != oldValue; + value = _currentEdit; + Reset(); + return ret; + } + + value = string.Empty; + return false; + } + } +} diff --git a/Penumbra/UI/ModsTab/ModPanelSettingsTab.cs b/Penumbra/UI/ModsTab/ModPanelSettingsTab.cs index b17551f4..92343c63 100644 --- a/Penumbra/UI/ModsTab/ModPanelSettingsTab.cs +++ b/Penumbra/UI/ModsTab/ModPanelSettingsTab.cs @@ -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 { private bool _inherited; private bool _temporary; @@ -31,8 +31,11 @@ public class ModPanelSettingsTab( public ReadOnlySpan Label => "Settings"u8; + + public ModPanelTab Identifier + => ModPanelTab.Settings; - public void DrawHeader() + public void PostTabButton() => tutorial.OpenTutorial(BasicTutorialSteps.ModOptions); public void Reset() diff --git a/Penumbra/UI/ModsTab/ModPanelTabBar.cs b/Penumbra/UI/ModsTab/ModPanelTabBar.cs index daeb2394..3ebe7999 100644 --- a/Penumbra/UI/ModsTab/ModPanelTabBar.cs +++ b/Penumbra/UI/ModsTab/ModPanelTabBar.cs @@ -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,138 +7,88 @@ using ImGuiColor = ImSharp.ImGuiColor; namespace Penumbra.UI.ModsTab; -public class ModPanelTabBar : IUiService +public enum ModPanelTab { - private enum ModPanelTabType - { - Description, - Settings, - ChangedItems, - Conflicts, - Collections, - Edit, - }; + Description, + Settings, + ChangedItems, + Conflicts, + Collections, + Edit, +}; - 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 class ModPanelTabBar : TabBar +{ + public readonly ModPanelSettingsTab Settings; + public readonly ModPanelEditTab Edit; + private readonly ModManager _modManager; + private readonly TutorialService _tutorial; - public readonly ITab[] Tabs; - private ModPanelTabType _preferredTab = ModPanelTabType.Settings; - private Mod? _lastMod; + 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; - Settings = settings; - Description = description; - Conflicts = conflicts; - ChangedItems = changedItems; - Edit = edit; - _modManager = modManager; - _tutorial = tutorial; - Collections = collections; + Flags = TabBarFlags.NoTooltip; + Settings = settings; + Edit = edit; + _modManager = modManager; + _tutorial = tutorial; + 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 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) - { - _lastMod = mod; - TabBar.Draw(string.Empty, ImGuiTabBarFlags.NoTooltip, ToLabel(_preferredTab), out _, () => DrawAdvancedEditingButton(mod), Tabs); - } - else - { - TabBar.Draw(string.Empty, ImGuiTabBarFlags.NoTooltip, ReadOnlySpan.Empty, out var label, () => DrawAdvancedEditingButton(mod), - Tabs); - _preferredTab = ToType(label); - } + var tabBarHeight = Im.Cursor.Y; + _lastMod = mod; + base.Draw(); DrawFavoriteButton(mod, tabBarHeight); } - private ReadOnlySpan 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.Empty, - }; - - private ModPanelTabType ToType(ReadOnlySpan 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] : - mod.Favorite ? LunaStyle.FavoriteColor : Im.Style[ImGuiColor.TextDisabled]; + 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); } } diff --git a/Penumbra/UI/ResourceWatcher/ResourceWatcher.cs b/Penumbra/UI/ResourceWatcher/ResourceWatcher.cs index eede4474..ae43c0bd 100644 --- a/Penumbra/UI/ResourceWatcher/ResourceWatcher.cs +++ b/Penumbra/UI/ResourceWatcher/ResourceWatcher.cs @@ -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 { 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 Label => "Resource Logger"u8; + public TabType Identifier + => TabType.ResourceWatcher; + public void DrawContent() { UpdateRecords(); diff --git a/Penumbra/UI/ResourceWatcher/ResourceWatcherTable.cs b/Penumbra/UI/ResourceWatcher/ResourceWatcherTable.cs index ccc0d6d8..52fd14b9 100644 --- a/Penumbra/UI/ResourceWatcher/ResourceWatcherTable.cs +++ b/Penumbra/UI/ResourceWatcher/ResourceWatcherTable.cs @@ -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 => 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 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 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 @@ -153,13 +151,13 @@ internal sealed class ResourceWatcherTable : Table 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 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 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 => 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 => 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); diff --git a/Penumbra/UI/Tabs/ChangedItemsTab.cs b/Penumbra/UI/Tabs/ChangedItemsTab.cs index bb5193ea..3ff58171 100644 --- a/Penumbra/UI/Tabs/ChangedItemsTab.cs +++ b/Penumbra/UI/Tabs/ChangedItemsTab.cs @@ -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,19 +15,22 @@ 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 { public ReadOnlySpan Label => "Changed Items"u8; - private string _changedItemFilter = string.Empty; - private string _changedItemModFilter = string.Empty; - private Vector2 _buttonSize; + public TabType Identifier + => TabType.ChangedItems; + + private string _changedItemFilter = string.Empty; + private string _changedItemModFilter = string.Empty; + private Vector2 _buttonSize; public void DrawContent() { @@ -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; diff --git a/Penumbra/UI/Tabs/CollectionsTab.cs b/Penumbra/UI/Tabs/CollectionsTab.cs index e6025052..8af56cf7 100644 --- a/Penumbra/UI/Tabs/CollectionsTab.cs +++ b/Penumbra/UI/Tabs/CollectionsTab.cs @@ -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, 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) { diff --git a/Penumbra/UI/Tabs/ConfigTabBar.cs b/Penumbra/UI/Tabs/ConfigTabBar.cs deleted file mode 100644 index 52a95940..00000000 --- a/Penumbra/UI/Tabs/ConfigTabBar.cs +++ /dev/null @@ -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; - - /// The tab to select on the next Draw call, if any. - 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 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.Empty, - }; - - private TabType FromLabel(ReadOnlySpan 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; - } -} diff --git a/Penumbra/UI/Tabs/Debug/DebugTab.cs b/Penumbra/UI/Tabs/Debug/DebugTab.cs index c14662d4..d62328f5 100644 --- a/Penumbra/UI/Tabs/Debug/DebugTab.cs +++ b/Penumbra/UI/Tabs/Debug/DebugTab.cs @@ -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 { 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 diff --git a/Penumbra/UI/Tabs/EffectiveTab.cs b/Penumbra/UI/Tabs/EffectiveTab.cs index 33f5e120..98b53c11 100644 --- a/Penumbra/UI/Tabs/EffectiveTab.cs +++ b/Penumbra/UI/Tabs/EffectiveTab.cs @@ -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 { public ReadOnlySpan Label => "Effective Changes"u8; + public TabType Identifier + => TabType.EffectiveChanges; + public void DrawContent() { SetupEffectiveSizes(); diff --git a/Penumbra/UI/Tabs/MainTabBar.cs b/Penumbra/UI/Tabs/MainTabBar.cs new file mode 100644 index 00000000..60526d34 --- /dev/null +++ b/Penumbra/UI/Tabs/MainTabBar.cs @@ -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, 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(); + } +} diff --git a/Penumbra/UI/Tabs/MessagesTab.cs b/Penumbra/UI/Tabs/MessagesTab.cs index 4e0de2f2..de28ffa0 100644 --- a/Penumbra/UI/Tabs/MessagesTab.cs +++ b/Penumbra/UI/Tabs/MessagesTab.cs @@ -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 { public ReadOnlySpan 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; } diff --git a/Penumbra/UI/Tabs/ModsTab.cs b/Penumbra/UI/Tabs/ModsTab.cs index 988a8253..53ff8121 100644 --- a/Penumbra/UI/Tabs/ModsTab.cs +++ b/Penumbra/UI/Tabs/ModsTab.cs @@ -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 { private readonly ActiveCollections _activeCollections = collectionManager.Active; @@ -41,7 +41,10 @@ public class ModsTab( public ReadOnlySpan 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(); diff --git a/Penumbra/UI/Tabs/OnScreenTab.cs b/Penumbra/UI/Tabs/OnScreenTab.cs index 4125eb22..c39eaba8 100644 --- a/Penumbra/UI/Tabs/OnScreenTab.cs +++ b/Penumbra/UI/Tabs/OnScreenTab.cs @@ -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 { 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; } diff --git a/Penumbra/UI/Tabs/ResourceTab.cs b/Penumbra/UI/Tabs/ResourceTab.cs index 058ae1ed..909ff406 100644 --- a/Penumbra/UI/Tabs/ResourceTab.cs +++ b/Penumbra/UI/Tabs/ResourceTab.cs @@ -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 { + public TabType Identifier + => TabType.ResourceManager; + public ReadOnlySpan Label => "Resource Manager"u8; @@ -52,7 +56,8 @@ public class ResourceTab(Configuration config, ResourceManagerService resourceMa private string _resourceManagerFilter = string.Empty; /// Draw a single resource map. - private unsafe void DrawResourceMap(ResourceCategory category, uint ext, StdMap>* map) + private unsafe void DrawResourceMap(ResourceCategory category, uint ext, + StdMap>* map) { if (map == null) return; diff --git a/Penumbra/UI/Tabs/SettingsTab.cs b/Penumbra/UI/Tabs/SettingsTab.cs index ba014096..f1407bad 100644 --- a/Penumbra/UI/Tabs/SettingsTab.cs +++ b/Penumbra/UI/Tabs/SettingsTab.cs @@ -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 { public const int RootDirectoryMaxLength = 64; + public TabType Identifier + => TabType.Settings; + public ReadOnlySpan Label => "Settings"u8;