From b88bba1a8758ea69a4aa6b3fad32761fa09fc744 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Sat, 21 Feb 2026 01:31:30 +0100 Subject: [PATCH] Current state. --- Glamourer.Api | 2 +- Glamourer/Api/DesignsApi.cs | 7 +- Glamourer/Api/GlamourerApi.cs | 3 +- Glamourer/Automation/AutoDesignApplier.cs | 27 +- Glamourer/Automation/FixedDesignMigrator.cs | 2 +- .../Configuration.cs | 56 +- .../DefaultDesignSettings.cs | 2 +- .../DesignPanelFlag.cs | 6 +- .../EphemeralConfig.cs | 4 +- .../HeightDisplayType.cs | 2 +- .../{Configuration => Config}/UiConfig.cs | 2 +- Glamourer/Designs/Design.cs | 21 +- Glamourer/Designs/DesignColors.cs | 3 +- Glamourer/Designs/DesignData.cs | 5 +- Glamourer/Designs/DesignEditor.cs | 6 +- Glamourer/Designs/DesignFileSystem.cs | 219 ++----- Glamourer/Designs/DesignFileSystemSaver.cs | 57 ++ Glamourer/Designs/DesignManager.cs | 4 +- Glamourer/Designs/Links/DesignMerger.cs | 3 +- Glamourer/Designs/SortMode.cs | 99 +++ .../Designs/Special/RandomDesignGenerator.cs | 4 +- Glamourer/Designs/Special/RandomPredicate.cs | 8 +- Glamourer/Events/DesignChanged.cs | 8 +- Glamourer/Events/TabSelected.cs | 3 +- Glamourer/Glamourer.cs | 3 +- Glamourer/Glamourer.csproj | 13 +- Glamourer/Glamourer.csproj.DotSettings | 2 + Glamourer/Gui/Colors.cs | 3 +- .../CustomizationDrawer.Simple.cs | 2 +- .../Gui/Customization/CustomizationDrawer.cs | 5 +- .../Customization/CustomizeParameterDrawer.cs | 7 +- Glamourer/Gui/DesignCombo.cs | 43 +- Glamourer/Gui/DesignQuickBar.cs | 25 +- Glamourer/Gui/Equipment/EquipmentDrawer.cs | 5 +- Glamourer/Gui/GenericPopupWindow.cs | 11 +- Glamourer/Gui/GlamourerChangelog.cs | 9 +- Glamourer/Gui/GlamourerWindowSystem.cs | 3 +- Glamourer/Gui/MainTabBar.cs | 4 +- Glamourer/Gui/MainWindow.cs | 15 +- Glamourer/Gui/Materials/AdvancedDyePopup.cs | 4 +- Glamourer/Gui/Materials/MaterialDrawer.cs | 5 +- Glamourer/Gui/Tabs/ActorTab/ActorPanel.cs | 6 +- Glamourer/Gui/Tabs/ActorTab/ActorSelector.cs | 2 +- Glamourer/Gui/Tabs/ActorTab/ActorTab.cs | 2 +- Glamourer/Gui/Tabs/ActorTab/ActorsHeader.cs | 4 +- .../Tabs/AutomationTab/AutomationButtons.cs | 5 +- .../Tabs/AutomationTab/AutomationHeader.cs | 27 +- .../Gui/Tabs/AutomationTab/AutomationTab.cs | 7 +- .../AutomationTab/RandomRestrictionDrawer.cs | 23 +- Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs | 3 +- .../Gui/Tabs/AutomationTab/SetSelector.cs | 6 +- Glamourer/Gui/Tabs/DebugTab/DebugTab.cs | 5 +- .../Gui/Tabs/DebugTab/DesignConverterPanel.cs | 2 +- .../Gui/Tabs/DebugTab/DesignManagerPanel.cs | 9 +- Glamourer/Gui/Tabs/DebugTab/FunPanel.cs | 5 +- .../Tabs/DesignTab/ApplyCharacterButton.cs | 55 ++ .../Gui/Tabs/DesignTab/DesignDetailTab.cs | 86 +-- .../DesignTab/DesignFileSystemSelector.cs | 405 ------------ Glamourer/Gui/Tabs/DesignTab/DesignHeader.cs | 102 ++- .../Gui/Tabs/DesignTab/DesignLinkDrawer.cs | 43 +- Glamourer/Gui/Tabs/DesignTab/DesignPanel.cs | 581 ++++++------------ .../Gui/Tabs/DesignTab/DesignSelection.cs | 12 - Glamourer/Gui/Tabs/DesignTab/DesignTab.cs | 59 +- .../Gui/Tabs/DesignTab/DesignUndoButton.cs | 39 ++ .../Tabs/DesignTab/ExportToClipboardButton.cs | 36 ++ Glamourer/Gui/Tabs/DesignTab/LockButton.cs | 28 + .../Gui/Tabs/DesignTab/ModAssociationsTab.cs | 33 +- Glamourer/Gui/Tabs/DesignTab/ModCombo.cs | 7 +- .../Gui/Tabs/DesignTab/MultiDesignPanel.cs | 276 ++++----- .../Selector/DeleteSelectionButton.cs | 45 ++ .../Selector/DesignFileSystemCache.cs | 112 ++++ .../Selector/DesignFileSystemDrawer.cs | 65 ++ .../Tabs/DesignTab/Selector/DesignFilter.cs | 150 +++++ .../DesignTab/Selector/DesignFilterToken.cs | 49 ++ .../Selector/DuplicateDesignButton.cs | 38 ++ .../DesignTab/Selector/ImportDesignButton.cs | 52 ++ .../DesignTab/Selector/MoveDesignInput.cs | 34 + .../DesignTab/Selector/NewDesignButton.cs | 28 + .../DesignTab/Selector/RenameDesignInput.cs | 34 + .../Tabs/DesignTab/SetFromClipboardButton.cs | 44 ++ Glamourer/Gui/Tabs/DesignTab/UndoButton.cs | 27 + Glamourer/Gui/Tabs/HeaderDrawer.cs | 80 --- Glamourer/Gui/Tabs/IncognitoButton.cs | 5 +- Glamourer/Gui/Tabs/NpcTab/NpcPanel.cs | 4 +- Glamourer/Gui/Tabs/NpcTab/NpcTab.cs | 2 +- Glamourer/Gui/Tabs/SettingsTab/CodeDrawer.cs | 73 +-- .../Gui/Tabs/SettingsTab/CollectionCombo.cs | 73 ++- .../SettingsTab/CollectionOverrideDrawer.cs | 18 +- Glamourer/Gui/Tabs/SettingsTab/SettingsTab.cs | 74 ++- .../Gui/Tabs/UnlocksTab/UnlockCacheItem.cs | 3 +- Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs | 66 +- Glamourer/Gui/Tabs/UnlocksTab/UnlocksTab.cs | 4 +- Glamourer/Interop/ContextMenuService.cs | 3 +- Glamourer/Interop/Material/MaterialManager.cs | 3 +- .../Interop/Penumbra/ModSettingApplier.cs | 13 +- .../Interop/Penumbra/PenumbraAutoRedraw.cs | 19 +- Glamourer/Interop/Penumbra/PenumbraService.cs | 5 +- Glamourer/Services/BackupService.cs | 27 +- Glamourer/Services/CodeService.cs | 28 +- Glamourer/Services/CommandService.cs | 37 +- Glamourer/Services/ConfigMigrationService.cs | 31 +- Glamourer/Services/DesignResolver.cs | 9 +- Glamourer/Services/FilenameService.cs | 22 +- Glamourer/Services/ItemManager.cs | 5 +- Glamourer/Services/PcpService.cs | 17 +- Glamourer/Services/ServiceManager.cs | 7 +- Glamourer/State/FunModule.cs | 29 +- Glamourer/State/StateEditor.cs | 16 +- Glamourer/State/StateListener.cs | 53 +- Glamourer/State/StateManager.cs | 3 +- Glamourer/packages.lock.json | 6 - Luna | 2 +- Penumbra.Api | 2 +- Penumbra.GameData | 2 +- Penumbra.String | 2 +- 115 files changed, 2225 insertions(+), 1776 deletions(-) rename Glamourer/{Configuration => Config}/Configuration.cs (75%) rename Glamourer/{Configuration => Config}/DefaultDesignSettings.cs (85%) rename Glamourer/{Configuration => Config}/DesignPanelFlag.cs (93%) rename Glamourer/{Configuration => Config}/EphemeralConfig.cs (93%) rename Glamourer/{Configuration => Config}/HeightDisplayType.cs (87%) rename Glamourer/{Configuration => Config}/UiConfig.cs (95%) create mode 100644 Glamourer/Designs/DesignFileSystemSaver.cs create mode 100644 Glamourer/Designs/SortMode.cs create mode 100644 Glamourer/Glamourer.csproj.DotSettings create mode 100644 Glamourer/Gui/Tabs/DesignTab/ApplyCharacterButton.cs delete mode 100644 Glamourer/Gui/Tabs/DesignTab/DesignFileSystemSelector.cs delete mode 100644 Glamourer/Gui/Tabs/DesignTab/DesignSelection.cs create mode 100644 Glamourer/Gui/Tabs/DesignTab/DesignUndoButton.cs create mode 100644 Glamourer/Gui/Tabs/DesignTab/ExportToClipboardButton.cs create mode 100644 Glamourer/Gui/Tabs/DesignTab/LockButton.cs create mode 100644 Glamourer/Gui/Tabs/DesignTab/Selector/DeleteSelectionButton.cs create mode 100644 Glamourer/Gui/Tabs/DesignTab/Selector/DesignFileSystemCache.cs create mode 100644 Glamourer/Gui/Tabs/DesignTab/Selector/DesignFileSystemDrawer.cs create mode 100644 Glamourer/Gui/Tabs/DesignTab/Selector/DesignFilter.cs create mode 100644 Glamourer/Gui/Tabs/DesignTab/Selector/DesignFilterToken.cs create mode 100644 Glamourer/Gui/Tabs/DesignTab/Selector/DuplicateDesignButton.cs create mode 100644 Glamourer/Gui/Tabs/DesignTab/Selector/ImportDesignButton.cs create mode 100644 Glamourer/Gui/Tabs/DesignTab/Selector/MoveDesignInput.cs create mode 100644 Glamourer/Gui/Tabs/DesignTab/Selector/NewDesignButton.cs create mode 100644 Glamourer/Gui/Tabs/DesignTab/Selector/RenameDesignInput.cs create mode 100644 Glamourer/Gui/Tabs/DesignTab/SetFromClipboardButton.cs create mode 100644 Glamourer/Gui/Tabs/DesignTab/UndoButton.cs delete mode 100644 Glamourer/Gui/Tabs/HeaderDrawer.cs diff --git a/Glamourer.Api b/Glamourer.Api index 51b3c72..941dc7e 160000 --- a/Glamourer.Api +++ b/Glamourer.Api @@ -1 +1 @@ -Subproject commit 51b3c72e91816af0002dd543d64944e777b246ba +Subproject commit 941dc7e1da694127a4405f4888ae162133131268 diff --git a/Glamourer/Api/DesignsApi.cs b/Glamourer/Api/DesignsApi.cs index d1f08e7..d2da5b1 100644 --- a/Glamourer/Api/DesignsApi.cs +++ b/Glamourer/Api/DesignsApi.cs @@ -2,7 +2,6 @@ using Glamourer.Api.Enums; using Glamourer.Designs; using Glamourer.State; -using ImSharp; using Luna; using Newtonsoft.Json.Linq; @@ -12,7 +11,6 @@ public class DesignsApi( ApiHelpers helpers, DesignManager designs, StateManager stateManager, - DesignFileSystem fileSystem, DesignColors color, DesignConverter converter) : IGlamourerApiDesigns, IApiService @@ -21,12 +19,11 @@ public class DesignsApi( => designs.Designs.ToDictionary(d => d.Identifier, d => d.Name.Text); public Dictionary GetDesignListExtended() - => fileSystem.ToDictionary(kvp => kvp.Key.Identifier, - kvp => (kvp.Key.Name.Text, kvp.Value.FullName(), color.GetColor(kvp.Key).Color, kvp.Key.QuickDesign)); + => designs.Designs.ToDictionary(d => d.Identifier, d => (d.DisplayName, d.Path.CurrentPath, color.GetColor(d).Color, d.QuickDesign)); public (string DisplayName, string FullPath, uint DisplayColor, bool ShowInQdb) GetExtendedDesignData(Guid designId) => designs.Designs.ByIdentifier(designId) is { } d - ? (d.Name.Text, fileSystem.TryGetValue(d, out var leaf) ? leaf.FullName() : d.Name.Text, color.GetColor(d).Color, d.QuickDesign) + ? (d.Name.Text, d.Path.CurrentPath, color.GetColor(d).Color, d.QuickDesign) : (string.Empty, string.Empty, 0, false); public GlamourerApiEc ApplyDesign(Guid designId, int objectIndex, uint key, ApplyFlag flags) diff --git a/Glamourer/Api/GlamourerApi.cs b/Glamourer/Api/GlamourerApi.cs index c176a30..5ff5d56 100644 --- a/Glamourer/Api/GlamourerApi.cs +++ b/Glamourer/Api/GlamourerApi.cs @@ -1,9 +1,10 @@ using Glamourer.Api.Api; +using Glamourer.Config; using Luna; namespace Glamourer.Api; -public class GlamourerApi(Configuration.Configuration config, DesignsApi designs, StateApi state, ItemsApi items) : IGlamourerApi, IApiService +public class GlamourerApi(Configuration config, DesignsApi designs, StateApi state, ItemsApi items) : IGlamourerApi, IApiService { public const int CurrentApiVersionMajor = 1; public const int CurrentApiVersionMinor = 7; diff --git a/Glamourer/Automation/AutoDesignApplier.cs b/Glamourer/Automation/AutoDesignApplier.cs index 71b76a3..f88fd69 100644 --- a/Glamourer/Automation/AutoDesignApplier.cs +++ b/Glamourer/Automation/AutoDesignApplier.cs @@ -1,5 +1,6 @@ using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.UI.Misc; +using Glamourer.Config; using Glamourer.Designs; using Glamourer.Designs.Links; using Glamourer.Events; @@ -16,22 +17,22 @@ namespace Glamourer.Automation; public sealed class AutoDesignApplier : IDisposable { - private readonly Configuration.Configuration _config; - private readonly AutoDesignManager _manager; - private readonly StateManager _state; - private readonly JobService _jobs; - private readonly EquippedGearset _equippedGearset; - private readonly ActorManager _actors; - private readonly AutomationChanged _event; - private readonly ActorObjectManager _objects; - private readonly WeaponLoading _weapons; - private readonly HumanModelList _humans; - private readonly DesignMerger _designMerger; - private readonly IClientState _clientState; + private readonly Configuration _config; + private readonly AutoDesignManager _manager; + private readonly StateManager _state; + private readonly JobService _jobs; + private readonly EquippedGearset _equippedGearset; + private readonly ActorManager _actors; + private readonly AutomationChanged _event; + private readonly ActorObjectManager _objects; + private readonly WeaponLoading _weapons; + private readonly HumanModelList _humans; + private readonly DesignMerger _designMerger; + private readonly IClientState _clientState; private readonly JobChangeState _jobChangeState; - public AutoDesignApplier(Configuration.Configuration config, AutoDesignManager manager, StateManager state, JobService jobs, ActorManager actors, + public AutoDesignApplier(Configuration config, AutoDesignManager manager, StateManager state, JobService jobs, ActorManager actors, AutomationChanged @event, ActorObjectManager objects, WeaponLoading weapons, HumanModelList humans, IClientState clientState, EquippedGearset equippedGearset, DesignMerger designMerger, JobChangeState jobChangeState) { diff --git a/Glamourer/Automation/FixedDesignMigrator.cs b/Glamourer/Automation/FixedDesignMigrator.cs index 1621588..3340457 100644 --- a/Glamourer/Automation/FixedDesignMigrator.cs +++ b/Glamourer/Automation/FixedDesignMigrator.cs @@ -47,7 +47,7 @@ public class FixedDesignMigrator(JobService jobs) var set = autoManager[^1]; foreach (var design in data.AsEnumerable().Reverse()) { - if (!designFileSystem.Find(design.Item1, out var child) || child is not DesignFileSystem.Leaf leaf) + if (!designFileSystem.Find(design.Item1, out var child) || child is not IFileSystemData leaf) { Glamourer.Messager.NotificationMessage($"Could not find design with path {design.Item1}, skipped fixed design.", NotificationType.Warning); diff --git a/Glamourer/Configuration/Configuration.cs b/Glamourer/Config/Configuration.cs similarity index 75% rename from Glamourer/Configuration/Configuration.cs rename to Glamourer/Config/Configuration.cs index 417850f..31452bc 100644 --- a/Glamourer/Configuration/Configuration.cs +++ b/Glamourer/Config/Configuration.cs @@ -7,14 +7,16 @@ using Glamourer.Gui.Tabs.DesignTab; using Glamourer.Services; using ImSharp; using Luna; +using Luna.Generators; using Newtonsoft.Json; -using OtterGui.Filesystem; using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs; -namespace Glamourer.Configuration; +namespace Glamourer.Config; -public class Configuration : IPluginConfiguration, ISavable +public sealed partial class Configuration : IPluginConfiguration, ISavable { + public const int CurrentVersion = 9; + [JsonIgnore] public readonly EphemeralConfig Ephemeral; @@ -58,8 +60,11 @@ public class Configuration : IPluginConfiguration, ISavable public DefaultDesignSettings DefaultDesignSettings { get; set; } = new(); - public HeightDisplayType HeightDisplayType { get; set; } = HeightDisplayType.Centimetre; - public RenameField ShowRename { get; set; } = RenameField.BothDataPrio; + public HeightDisplayType HeightDisplayType { get; set; } = HeightDisplayType.Centimetre; + + [ConfigProperty(EventName = "OnRenameChanged")] + private RenameField _showRename = RenameField.BothDataPrio; + public ModifiableHotkey ToggleQuickDesignBar { get; set; } = new(VirtualKey.NO_KEY); public DoubleModifier DeleteDesignModifier { get; set; } = new(ModifierHotkey.Control, ModifierHotkey.Shift); public DoubleModifier IncognitoModifier { get; set; } = new(ModifierHotkey.Control); @@ -70,7 +75,7 @@ public class Configuration : IPluginConfiguration, ISavable [JsonConverter(typeof(SortModeConverter))] [JsonProperty(Order = int.MaxValue)] - public ISortMode SortMode { get; set; } = ISortMode.FoldersFirst; + public ISortMode SortMode { get; set; } = ISortMode.FoldersFirst; public List<(string Code, bool Enabled)> Codes { get; set; } = []; @@ -80,7 +85,7 @@ public class Configuration : IPluginConfiguration, ISavable public bool DebugMode { get; set; } = false; #endif - public int Version { get; set; } = Constants.CurrentVersion; + public int Version { get; set; } = CurrentVersion; public Dictionary Colors { get; private set; } = ColorId.Values.ToDictionary(c => c, c => c.Data().DefaultColor); @@ -142,45 +147,22 @@ public class Configuration : IPluginConfiguration, ISavable serializer.Serialize(jWriter, this); } - public static class Constants - { - public const int CurrentVersion = 8; - - public static readonly ISortMode[] ValidSortModes = - [ - ISortMode.FoldersFirst, - ISortMode.Lexicographical, - new DesignFileSystem.CreationDate(), - new DesignFileSystem.InverseCreationDate(), - new DesignFileSystem.UpdateDate(), - new DesignFileSystem.InverseUpdateDate(), - ISortMode.InverseFoldersFirst, - ISortMode.InverseLexicographical, - ISortMode.FoldersLast, - ISortMode.InverseFoldersLast, - ISortMode.InternalOrder, - ISortMode.InverseInternalOrder, - ]; - } - /// Convert SortMode Types to their name. - private class SortModeConverter : JsonConverter> + private class SortModeConverter : JsonConverter { - public override void WriteJson(JsonWriter writer, ISortMode? value, JsonSerializer serializer) + public override void WriteJson(JsonWriter writer, ISortMode? value, JsonSerializer serializer) { - value ??= ISortMode.FoldersFirst; + value ??= ISortMode.FoldersFirst; serializer.Serialize(writer, value.GetType().Name); } - public override ISortMode ReadJson(JsonReader reader, Type objectType, ISortMode? existingValue, - bool hasExistingValue, + public override ISortMode ReadJson(JsonReader reader, Type objectType, ISortMode? existingValue, bool hasExistingValue, JsonSerializer serializer) { - var name = serializer.Deserialize(reader); - if (name == null || !Constants.ValidSortModes.FindFirst(s => s.GetType().Name == name, out var mode)) - return existingValue ?? ISortMode.FoldersFirst; + if (serializer.Deserialize(reader) is { } name) + return ISortMode.Valid.GetValueOrDefault(name, existingValue ?? ISortMode.FoldersFirst); - return mode; + return existingValue ?? ISortMode.FoldersFirst; } } } diff --git a/Glamourer/Configuration/DefaultDesignSettings.cs b/Glamourer/Config/DefaultDesignSettings.cs similarity index 85% rename from Glamourer/Configuration/DefaultDesignSettings.cs rename to Glamourer/Config/DefaultDesignSettings.cs index 4ed7dfe..b96cf85 100644 --- a/Glamourer/Configuration/DefaultDesignSettings.cs +++ b/Glamourer/Config/DefaultDesignSettings.cs @@ -1,4 +1,4 @@ -namespace Glamourer.Configuration; +namespace Glamourer.Config; public class DefaultDesignSettings { diff --git a/Glamourer/Configuration/DesignPanelFlag.cs b/Glamourer/Config/DesignPanelFlag.cs similarity index 93% rename from Glamourer/Configuration/DesignPanelFlag.cs rename to Glamourer/Config/DesignPanelFlag.cs index c381222..c3d1915 100644 --- a/Glamourer/Configuration/DesignPanelFlag.cs +++ b/Glamourer/Config/DesignPanelFlag.cs @@ -1,7 +1,7 @@ using ImSharp; using Luna.Generators; -namespace Glamourer.Configuration; +namespace Glamourer.Config; [Flags] [NamedEnum(Utf16: false)] @@ -40,9 +40,9 @@ public enum DesignPanelFlag : uint public static partial class DesignPanelFlagExtensions { - private static readonly StringU8 Expand = new("Expand"u8); + private static readonly StringU8 Expand = new("Expand"u8); - public static Im.HeaderDisposable Header(this DesignPanelFlag flag, global::Glamourer.Configuration.Configuration config) + public static Im.HeaderDisposable Header(this DesignPanelFlag flag, Configuration config) { if (config.HideDesignPanel.HasFlag(flag)) return default; diff --git a/Glamourer/Configuration/EphemeralConfig.cs b/Glamourer/Config/EphemeralConfig.cs similarity index 93% rename from Glamourer/Configuration/EphemeralConfig.cs rename to Glamourer/Config/EphemeralConfig.cs index ebf1c75..96699b5 100644 --- a/Glamourer/Configuration/EphemeralConfig.cs +++ b/Glamourer/Config/EphemeralConfig.cs @@ -6,11 +6,11 @@ using Luna.Generators; using Newtonsoft.Json; using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs; -namespace Glamourer.Configuration; +namespace Glamourer.Config; public partial class EphemeralConfig : ISavable { - public int Version { get; set; } = Configuration.Constants.CurrentVersion; + public int Version { get; set; } = Configuration.CurrentVersion; [ConfigProperty] private bool _incognitoMode; diff --git a/Glamourer/Configuration/HeightDisplayType.cs b/Glamourer/Config/HeightDisplayType.cs similarity index 87% rename from Glamourer/Configuration/HeightDisplayType.cs rename to Glamourer/Config/HeightDisplayType.cs index 5ff79e0..f16fdec 100644 --- a/Glamourer/Configuration/HeightDisplayType.cs +++ b/Glamourer/Config/HeightDisplayType.cs @@ -1,6 +1,6 @@ using Luna.Generators; -namespace Glamourer.Configuration; +namespace Glamourer.Config; [TooltipEnum] public enum HeightDisplayType diff --git a/Glamourer/Configuration/UiConfig.cs b/Glamourer/Config/UiConfig.cs similarity index 95% rename from Glamourer/Configuration/UiConfig.cs rename to Glamourer/Config/UiConfig.cs index c1ee1b4..e76266a 100644 --- a/Glamourer/Configuration/UiConfig.cs +++ b/Glamourer/Config/UiConfig.cs @@ -4,7 +4,7 @@ using Luna.Generators; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace Glamourer.Configuration; +namespace Glamourer.Config; public sealed partial class UiConfig : ConfigurationFile { diff --git a/Glamourer/Designs/Design.cs b/Glamourer/Designs/Design.cs index 69f4404..8804c8e 100644 --- a/Glamourer/Designs/Design.cs +++ b/Glamourer/Designs/Design.cs @@ -14,7 +14,7 @@ using Notification = Luna.Notification; namespace Glamourer.Designs; -public sealed class Design : DesignBase, ISavable, IDesignStandIn +public sealed class Design : DesignBase, ISavable, IDesignStandIn, IFileSystemValue { #region Data @@ -44,6 +44,7 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn public new const int FileVersion = 2; public Guid Identifier { get; internal init; } + public IFileSystemData? Node { get; set; } public DateTimeOffset CreationDate { get; internal init; } public DateTimeOffset LastEdit { get; internal set; } public LowerString Name { get; internal set; } = LowerString.Empty; @@ -57,6 +58,7 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn public string Color { get; internal set; } = string.Empty; public SortedList AssociatedMods { get; private set; } = []; public LinkContainer Links { get; private set; } = []; + public DataPath Path { get; } = new(); public string Incognito => Identifier.ToString()[..8]; @@ -124,6 +126,12 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn ["Mods"] = SerializeMods(), ["Links"] = Links.Serialize(), }; + if (Path.Folder.Length > 0) + ret["FileSystemFolder"] = Path.Folder; + if (Path.SortName is not null) + ret["SortOrderName"] = Path.SortName; + + return ret; } @@ -251,6 +259,9 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn }; if (design.LastEdit < creationDate) design.LastEdit = creationDate; + design.Path.Folder = json["FileSystemFolder"]?.Value() ?? string.Empty; + design.Path.SortName = json["SortOrderName"]?.Value()?.FixName(); + design.SetWriteProtected(json["WriteProtected"]?.ToObject() ?? false); LoadCustomize(customizations, json["Customize"], design, design.Name, true, false); LoadEquip(items, json["Equipment"], design, design.Name, true); @@ -340,7 +351,13 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn } public string LogName(string fileName) - => Path.GetFileNameWithoutExtension(fileName); + => System.IO.Path.GetFileNameWithoutExtension(fileName); #endregion + + string IFileSystemValue.Identifier + => Identifier.ToString(); + + public string DisplayName + => Name.Text; } diff --git a/Glamourer/Designs/DesignColors.cs b/Glamourer/Designs/DesignColors.cs index b9200eb..1dec82c 100644 --- a/Glamourer/Designs/DesignColors.cs +++ b/Glamourer/Designs/DesignColors.cs @@ -1,4 +1,5 @@ using Dalamud.Interface.ImGuiNotification; +using Glamourer.Config; using Glamourer.Gui; using Glamourer.Services; using ImSharp; @@ -8,7 +9,7 @@ using Newtonsoft.Json.Linq; namespace Glamourer.Designs; -public class DesignColorUi(DesignColors colors, Configuration.Configuration config) +public class DesignColorUi(DesignColors colors, Configuration config) { private string _newName = string.Empty; diff --git a/Glamourer/Designs/DesignData.cs b/Glamourer/Designs/DesignData.cs index c7ca8e5..5ace949 100644 --- a/Glamourer/Designs/DesignData.cs +++ b/Glamourer/Designs/DesignData.cs @@ -1,6 +1,5 @@ using Glamourer.GameData; using Glamourer.Services; -using OtterGui.Classes; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; using Penumbra.String.Functions; @@ -47,8 +46,8 @@ public unsafe struct DesignData { } [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)] - public readonly bool ContainsName(LowerString name) - => ItemNames.Any(name.IsContained); + public readonly bool ContainsName(string name) + => ItemNames.Any(i => i.Contains(name, StringComparison.OrdinalIgnoreCase)); public readonly StainIds Stain(EquipSlot slot) { diff --git a/Glamourer/Designs/DesignEditor.cs b/Glamourer/Designs/DesignEditor.cs index 828ed43..1bfc4d3 100644 --- a/Glamourer/Designs/DesignEditor.cs +++ b/Glamourer/Designs/DesignEditor.cs @@ -1,3 +1,4 @@ +using Glamourer.Config; using Glamourer.Designs.History; using Glamourer.Designs.Links; using Glamourer.Events; @@ -5,7 +6,6 @@ using Glamourer.GameData; using Glamourer.Interop.Material; using Glamourer.Services; using ImSharp; -using Luna; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; @@ -16,14 +16,14 @@ public class DesignEditor( DesignChanged designChanged, CustomizeService customizations, ItemManager items, - Configuration.Configuration config) + Configuration config) : IDesignEditor { protected readonly DesignChanged DesignChanged = designChanged; protected readonly SaveService SaveService = saveService; protected readonly ItemManager Items = items; protected readonly CustomizeService Customizations = customizations; - protected readonly Configuration.Configuration Config = config; + protected readonly Configuration Config = config; protected readonly Dictionary UndoStore = []; private bool _forceFullItemOff; diff --git a/Glamourer/Designs/DesignFileSystem.cs b/Glamourer/Designs/DesignFileSystem.cs index 9aa5fb8..4810174 100644 --- a/Glamourer/Designs/DesignFileSystem.cs +++ b/Glamourer/Designs/DesignFileSystem.cs @@ -3,195 +3,64 @@ using Glamourer.Designs.History; using Glamourer.Events; using Glamourer.Services; using Luna; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; namespace Glamourer.Designs; -public sealed class DesignFileSystem : OtterGui.Filesystem.FileSystem, IDisposable, ISavable +public sealed class DesignFileSystem : BaseFileSystem, IDisposable, IRequiredService { - private readonly DesignChanged _designChanged; + private readonly DesignFileSystemSaver _saver; + private readonly DesignChanged _designChanged; - private readonly SaveService _saveService; - private readonly DesignManager _designManager; - - public DesignFileSystem(DesignManager designManager, SaveService saveService, DesignChanged designChanged) + public DesignFileSystem(Logger log, SaveService saveService, DesignStorage designs, DesignChanged designChanged) + : base("DesignFileSystem", log, true) { - _designManager = designManager; - _saveService = saveService; _designChanged = designChanged; - _designChanged.Subscribe(OnDesignChange, DesignChanged.Priority.DesignFileSystem); - Changed += OnChange; - Reload(); + _saver = new DesignFileSystemSaver(log, this, saveService, designs); + + _saver.Load(); + _designChanged.Subscribe(OnDesignChanged, DesignChanged.Priority.DesignFileSystem); } - private void Reload() + private void OnDesignChanged(DesignChanged.Type type, Design design, ITransaction? _) { - if (Load(new FileInfo(_saveService.FileNames.DesignFileSystem), _designManager.Designs, DesignToIdentifier, DesignToName)) - _saveService.ImmediateSave(this); + switch (type) + { + case DesignChanged.Type.ReloadedAll: _saver.Load(); break; + case DesignChanged.Type.Created: + var parent = Root; + if (design.Path.Folder.Length > 0) + try + { + parent = FindOrCreateAllFolders(design.Path.Folder); + } + catch (Exception ex) + { + Glamourer.Messager.NotificationMessage(ex, + $"Could not move design to {design.Path} because the folder could not be created.", + NotificationType.Error); + } - Glamourer.Log.Debug("Reloaded design filesystem."); + var (data, _) = CreateDuplicateDataNode(parent, design.Path.SortName ?? design.Name, design); + Selection.Select(data); + break; + case DesignChanged.Type.Deleted: + if (design.Node is { } node) + { + if (node.Selected) + Selection.UnselectAll(); + Delete(node); + } + + break; + case DesignChanged.Type.Renamed when design.Path.SortName is null: + RenameWithDuplicates(design.Node!, design.Path.GetIntendedName(design.Name.Text)); + break; + // TODO: Maybe add path changes? + } } public void Dispose() { - _designChanged.Unsubscribe(OnDesignChange); - } - - public struct CreationDate : OtterGui.Filesystem.ISortMode - { - public ReadOnlySpan Name - => "Creation Date (Older First)"u8; - - public ReadOnlySpan Description - => "In each folder, sort all subfolders lexicographically, then sort all leaves using their creation date."u8; - - public IEnumerable GetChildren(Folder f) - => f.GetSubFolders().Cast().Concat(f.GetLeaves().OrderBy(l => l.Value.CreationDate)); - } - - public struct UpdateDate : OtterGui.Filesystem.ISortMode - { - public ReadOnlySpan Name - => "Update Date (Older First)"u8; - - public ReadOnlySpan Description - => "In each folder, sort all subfolders lexicographically, then sort all leaves using their last update date."u8; - - public IEnumerable GetChildren(Folder f) - => f.GetSubFolders().Cast().Concat(f.GetLeaves().OrderBy(l => l.Value.LastEdit)); - } - - public struct InverseCreationDate : OtterGui.Filesystem.ISortMode - { - public ReadOnlySpan Name - => "Creation Date (Newer First)"u8; - - public ReadOnlySpan Description - => "In each folder, sort all subfolders lexicographically, then sort all leaves using their inverse creation date."u8; - - public IEnumerable GetChildren(Folder f) - => f.GetSubFolders().Cast().Concat(f.GetLeaves().OrderByDescending(l => l.Value.CreationDate)); - } - - public struct InverseUpdateDate : OtterGui.Filesystem.ISortMode - { - public ReadOnlySpan Name - => "Update Date (Newer First)"u8; - - public ReadOnlySpan Description - => "In each folder, sort all subfolders lexicographically, then sort all leaves using their inverse last update date."u8; - - public IEnumerable GetChildren(Folder f) - => f.GetSubFolders().Cast().Concat(f.GetLeaves().OrderByDescending(l => l.Value.LastEdit)); - } - - private void OnChange(OtterGui.Filesystem.FileSystemChangeType type, IPath _1, IPath? _2, IPath? _3) - { - if (type != OtterGui.Filesystem.FileSystemChangeType.Reload) - _saveService.QueueSave(this); - } - - private void OnDesignChange(DesignChanged.Type type, Design design, ITransaction? data) - { - switch (type) - { - case DesignChanged.Type.Created: - var parent = Root; - if ((data as CreationTransaction?)?.Path is { } path) - try - { - parent = FindOrCreateAllFolders(path); - } - catch (Exception ex) - { - Glamourer.Messager.NotificationMessage(ex, $"Could not move design to {path} because the folder could not be created.", - NotificationType.Error); - } - - CreateDuplicateLeaf(parent, design.Name.Text, design); - - return; - case DesignChanged.Type.Deleted: - if (TryGetValue(design, out var leaf1)) - Delete(leaf1); - return; - case DesignChanged.Type.ReloadedAll: - Reload(); - return; - case DesignChanged.Type.Renamed when (data as RenameTransaction?)?.Old is { } oldName: - if (!TryGetValue(design, out var leaf2)) - return; - - var old = oldName.FixName(); - if (old == leaf2.Name || leaf2.Name.IsDuplicateName(out var baseName, out _) && baseName == old) - RenameWithDuplicates(leaf2, design.Name); - return; - } - } - - // Used for saving and loading. - private static string DesignToIdentifier(Design design) - => design.Identifier.ToString(); - - private static string DesignToName(Design design) - => design.Name.Text.FixName(); - - private static bool DesignHasDefaultPath(Design design, string fullPath) - { - var regex = new Regex($@"^{Regex.Escape(DesignToName(design))}( \(\d+\))?$"); - return regex.IsMatch(fullPath); - } - - private static (string, bool) SaveDesign(Design design, string fullPath) - // Only save pairs with non-default paths. - => DesignHasDefaultPath(design, fullPath) - ? (string.Empty, false) - : (DesignToIdentifier(design), true); - - internal static void MigrateOldPaths(SaveService saveService, Dictionary oldPaths) - { - if (oldPaths.Count == 0) - return; - - var file = saveService.FileNames.DesignFileSystem; - try - { - JObject jObject; - if (File.Exists(file)) - { - var text = File.ReadAllText(file); - jObject = JObject.Parse(text); - var dict = jObject["Data"]?.ToObject>(); - if (dict != null) - foreach (var (key, value) in dict) - oldPaths.TryAdd(key, value); - - jObject["Data"] = JToken.FromObject(oldPaths); - } - else - { - jObject = new JObject - { - ["Data"] = JToken.FromObject(oldPaths), - ["EmptyFolders"] = JToken.FromObject(Array.Empty()), - }; - } - - var data = jObject.ToString(Formatting.Indented); - File.WriteAllText(file, data); - } - catch (Exception ex) - { - Glamourer.Log.Error($"Could not migrate old folder paths to new version:\n{ex}"); - } - } - - public string ToFilePath(FilenameService fileNames) - => fileNames.DesignFileSystem; - - public void Save(StreamWriter writer) - { - SaveToFile(writer, SaveDesign, true); + _designChanged.Unsubscribe(OnDesignChanged); } } diff --git a/Glamourer/Designs/DesignFileSystemSaver.cs b/Glamourer/Designs/DesignFileSystemSaver.cs new file mode 100644 index 0000000..9899b28 --- /dev/null +++ b/Glamourer/Designs/DesignFileSystemSaver.cs @@ -0,0 +1,57 @@ +using Glamourer.Services; +using Luna; + +namespace Glamourer.Designs; + +public sealed class DesignFileSystemSaver(Logger log, BaseFileSystem fileSystem, SaveService saveService, DesignStorage designs) + : FileSystemSaver(log, fileSystem, saveService) +{ + protected override void SaveDataValue(IFileSystemValue value) + { + if (value is Design design) + SaveService.QueueSave(design); + } + + protected override string LockedFile(FilenameService provider) + => provider.FileSystemLockedNodes; + + protected override string ExpandedFile(FilenameService provider) + => provider.FileSystemExpandedFolders; + + protected override string EmptyFoldersFile(FilenameService provider) + => provider.FileSystemEmptyFolders; + + protected override string SelectionFile(FilenameService provider) + => provider.FileSystemSelectedNodes; + + protected override string MigrationFile(FilenameService provider) + => provider.MigrationDesignFileSystem; + + protected override bool GetValueFromIdentifier(ReadOnlySpan identifier, [NotNullWhen(true)] out IFileSystemValue? value) + { + if (!Guid.TryParse(identifier, out var guid)) + { + value = null; + return false; + } + + value = designs.FirstOrDefault(d => d.Identifier == guid); + return value is not null; + } + + protected override void CreateDataNodes() + { + foreach (var design in designs) + { + try + { + var folder = design.Path.Folder.Length is 0 ? FileSystem.Root : FileSystem.FindOrCreateAllFolders(design.Path.Folder); + FileSystem.CreateDuplicateDataNode(folder, design.Path.SortName ?? design.Name, design); + } + catch (Exception ex) + { + Log.Error($"Could not create folder structure for design {design.Name} at path {design.Path.Folder}: {ex}"); + } + } + } +} diff --git a/Glamourer/Designs/DesignManager.cs b/Glamourer/Designs/DesignManager.cs index 2e1a1f9..ab0551b 100644 --- a/Glamourer/Designs/DesignManager.cs +++ b/Glamourer/Designs/DesignManager.cs @@ -1,4 +1,5 @@ using Dalamud.Utility; +using Glamourer.Config; using Glamourer.Designs.History; using Glamourer.Designs.Links; using Glamourer.Events; @@ -21,7 +22,7 @@ public sealed class DesignManager : DesignEditor private readonly HumanModelList _humans; public DesignManager(SaveService saveService, ItemManager items, CustomizeService customizations, - DesignChanged @event, HumanModelList humans, DesignStorage storage, DesignLinkLoader designLinkLoader, Configuration.Configuration config) + DesignChanged @event, HumanModelList humans, DesignStorage storage, DesignLinkLoader designLinkLoader, Configuration config) : base(saveService, @event, customizations, items, config) { Designs = storage; @@ -545,7 +546,6 @@ public sealed class DesignManager : DesignEditor } } - DesignFileSystem.MigrateOldPaths(SaveService, migratedFileSystemPaths); Glamourer.Log.Information( $"Successfully migrated {successes} old designs. Skipped {skips} already migrated designs. Failed to migrate {errors} designs."); } diff --git a/Glamourer/Designs/Links/DesignMerger.cs b/Glamourer/Designs/Links/DesignMerger.cs index 709ae47..a75c7cb 100644 --- a/Glamourer/Designs/Links/DesignMerger.cs +++ b/Glamourer/Designs/Links/DesignMerger.cs @@ -1,5 +1,6 @@ using Glamourer.Api.Enums; using Glamourer.Automation; +using Glamourer.Config; using Glamourer.GameData; using Glamourer.Interop.Material; using Glamourer.Services; @@ -15,7 +16,7 @@ namespace Glamourer.Designs.Links; public class DesignMerger( DesignManager designManager, CustomizeService customizeService, - Configuration.Configuration config, + Configuration config, ItemUnlockManager itemUnlocks, CustomizeUnlockManager customizeUnlocks) : IService { diff --git a/Glamourer/Designs/SortMode.cs b/Glamourer/Designs/SortMode.cs new file mode 100644 index 0000000..db746b2 --- /dev/null +++ b/Glamourer/Designs/SortMode.cs @@ -0,0 +1,99 @@ +using System.Collections.Frozen; +using Luna; + +namespace Glamourer.Designs; + +public readonly struct CreationDate : ISortMode +{ + public static readonly CreationDate Instance = new(); + + public ReadOnlySpan Name + => "Creation Date (Older First)"u8; + + public ReadOnlySpan Description + => "In each folder, sort all subfolders lexicographically, then sort all leaves using their creation date."u8; + + public IEnumerable GetChildren(IFileSystemFolder f) + => f.GetSubFolders().Cast().Concat(f.GetLeaves().OfType>().OrderBy(l => l.Value.CreationDate)); +} + +public readonly struct UpdateDate : ISortMode +{ + public static readonly UpdateDate Instance = new(); + + public ReadOnlySpan Name + => "Update Date (Older First)"u8; + + public ReadOnlySpan Description + => "In each folder, sort all subfolders lexicographically, then sort all leaves using their last update date."u8; + + public IEnumerable GetChildren(IFileSystemFolder f) + => f.GetSubFolders().Cast().Concat(f.GetLeaves().OfType>().OrderBy(l => l.Value.LastEdit)); +} + +public readonly struct InverseCreationDate : ISortMode +{ + public static readonly InverseCreationDate Instance = new(); + + public ReadOnlySpan Name + => "Creation Date (Newer First)"u8; + + public ReadOnlySpan Description + => "In each folder, sort all subfolders lexicographically, then sort all leaves using their inverse creation date."u8; + + public IEnumerable GetChildren(IFileSystemFolder f) + => f.GetSubFolders().Cast() + .Concat(f.GetLeaves().OfType>().OrderByDescending(l => l.Value.CreationDate)); +} + +public readonly struct InverseUpdateDate : ISortMode +{ + public static readonly InverseUpdateDate Instance = new(); + + public ReadOnlySpan Name + => "Update Date (Newer First)"u8; + + public ReadOnlySpan Description + => "In each folder, sort all subfolders lexicographically, then sort all leaves using their inverse last update date."u8; + + public IEnumerable GetChildren(IFileSystemFolder f) + => f.GetSubFolders().Cast() + .Concat(f.GetLeaves().OfType>().OrderByDescending(l => l.Value.LastEdit)); +} + +public static class SortModeExtensions +{ + private static readonly FrozenDictionary ValidSortModes = new Dictionary + { + [nameof(ISortMode.FoldersFirst)] = ISortMode.FoldersFirst, + [nameof(ISortMode.Lexicographical)] = ISortMode.Lexicographical, + [nameof(CreationDate)] = ISortMode.CreationDate, + [nameof(InverseCreationDate)] = ISortMode.InverseCreationDate, + [nameof(UpdateDate)] = ISortMode.UpdateDate, + [nameof(InverseUpdateDate)] = ISortMode.InverseUpdateDate, + [nameof(ISortMode.InverseFoldersFirst)] = ISortMode.InverseFoldersFirst, + [nameof(ISortMode.InverseLexicographical)] = ISortMode.InverseLexicographical, + [nameof(ISortMode.FoldersLast)] = ISortMode.FoldersLast, + [nameof(ISortMode.InverseFoldersLast)] = ISortMode.InverseFoldersLast, + [nameof(ISortMode.InternalOrder)] = ISortMode.InternalOrder, + [nameof(ISortMode.InverseInternalOrder)] = ISortMode.InverseInternalOrder, + }.ToFrozenDictionary(); + + extension(ISortMode) + { + public static ISortMode CreationDate + => CreationDate.Instance; + + public static ISortMode InverseCreationDate + => InverseCreationDate.Instance; + + public static ISortMode UpdateDate + => UpdateDate.Instance; + + public static ISortMode InverseUpdateDate + => InverseUpdateDate.Instance; + + public static IReadOnlyDictionary Valid + => ValidSortModes; + } +} diff --git a/Glamourer/Designs/Special/RandomDesignGenerator.cs b/Glamourer/Designs/Special/RandomDesignGenerator.cs index ab736f4..0c62aa2 100644 --- a/Glamourer/Designs/Special/RandomDesignGenerator.cs +++ b/Glamourer/Designs/Special/RandomDesignGenerator.cs @@ -1,9 +1,9 @@ - +using Glamourer.Config; using Luna; namespace Glamourer.Designs.Special; -public class RandomDesignGenerator(DesignStorage designs, DesignFileSystem fileSystem, Configuration.Configuration config) : IService +public class RandomDesignGenerator(DesignStorage designs, DesignFileSystem fileSystem, Configuration config) : IService { private readonly Random _rng = new(); private readonly WeakReference _lastDesign = new(null!, false); diff --git a/Glamourer/Designs/Special/RandomPredicate.cs b/Glamourer/Designs/Special/RandomPredicate.cs index ae05f8f..172d249 100644 --- a/Glamourer/Designs/Special/RandomPredicate.cs +++ b/Glamourer/Designs/Special/RandomPredicate.cs @@ -10,19 +10,19 @@ public interface IDesignPredicate => Invoke(args.Design, args.LowerName, args.Identifier, args.LowerPath); public IEnumerable Get(IEnumerable designs, DesignFileSystem fileSystem) - => designs.Select(d => Transform(d, fileSystem)) + => designs.Select(Transform) .Where(Invoke) .Select(t => t.Design); public static IEnumerable Get(IReadOnlyList predicates, IEnumerable designs, DesignFileSystem fileSystem) => predicates.Count > 0 - ? designs.Select(d => Transform(d, fileSystem)) + ? designs.Select(Transform) .Where(t => predicates.Any(p => p.Invoke(t))) .Select(t => t.Design) : designs; - private static (Design Design, string LowerName, string Identifier, string LowerPath) Transform(Design d, DesignFileSystem fs) - => (d, d.Name.Lower, d.Identifier.ToString(), fs.TryGetValue(d, out var l) ? l.FullName().ToLowerInvariant() : string.Empty); + private static (Design Design, string LowerName, string Identifier, string LowerPath) Transform(Design d) + => (d, d.Name.Lower, d.Identifier.ToString(), d.Path.CurrentPath.ToLowerInvariant()); } public static class RandomPredicate diff --git a/Glamourer/Events/DesignChanged.cs b/Glamourer/Events/DesignChanged.cs index 04bb46a..83fc18b 100644 --- a/Glamourer/Events/DesignChanged.cs +++ b/Glamourer/Events/DesignChanged.cs @@ -1,6 +1,7 @@ using Glamourer.Designs; using Glamourer.Designs.History; using Glamourer.Gui; +using Glamourer.Gui.Tabs.DesignTab; using OtterGui.Classes; namespace Glamourer.Events; @@ -135,10 +136,13 @@ public sealed class DesignChanged() /// AutoDesignManager = 1, - /// + /// DesignFileSystem = 0, - /// + /// + DesignHeader = 0, + + /// DesignFileSystemSelector = -1, /// diff --git a/Glamourer/Events/TabSelected.cs b/Glamourer/Events/TabSelected.cs index 8dcf917..1880d37 100644 --- a/Glamourer/Events/TabSelected.cs +++ b/Glamourer/Events/TabSelected.cs @@ -1,5 +1,6 @@ using Glamourer.Designs; using Glamourer.Gui; +using Glamourer.Gui.Tabs.DesignTab; using OtterGui.Classes; namespace Glamourer.Events; @@ -16,7 +17,7 @@ public sealed class TabSelected() { public enum Priority { - /// + /// DesignSelector = 0, /// diff --git a/Glamourer/Glamourer.cs b/Glamourer/Glamourer.cs index c059b4f..bb6dd4f 100644 --- a/Glamourer/Glamourer.cs +++ b/Glamourer/Glamourer.cs @@ -1,6 +1,7 @@ using Dalamud.Plugin; using Glamourer.Api; using Glamourer.Automation; +using Glamourer.Config; using Glamourer.Designs; using Glamourer.Gui; using Glamourer.Interop; @@ -61,7 +62,7 @@ public class Glamourer : IDalamudPlugin public string GatherSupportInformation() { var sb = new StringBuilder(10240); - var config = _services.GetService(); + var config = _services.GetService(); sb.AppendLine("**Settings**"); sb.Append($"> **`Plugin Version: `** {Version}\n"); sb.Append($"> **`Commit Hash: `** {CommitHash}\n"); diff --git a/Glamourer/Glamourer.csproj b/Glamourer/Glamourer.csproj index 4bd1c25..0c1602c 100644 --- a/Glamourer/Glamourer.csproj +++ b/Glamourer/Glamourer.csproj @@ -1,4 +1,4 @@ - + Glamourer Glamourer @@ -22,6 +22,11 @@ + + false + false + + @@ -31,7 +36,8 @@ - + @@ -50,7 +56,8 @@ - + diff --git a/Glamourer/Glamourer.csproj.DotSettings b/Glamourer/Glamourer.csproj.DotSettings new file mode 100644 index 0000000..9252e38 --- /dev/null +++ b/Glamourer/Glamourer.csproj.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/Glamourer/Gui/Colors.cs b/Glamourer/Gui/Colors.cs index dab72db..a1e9780 100644 --- a/Glamourer/Gui/Colors.cs +++ b/Glamourer/Gui/Colors.cs @@ -1,3 +1,4 @@ +using Glamourer.Config; using ImSharp; namespace Glamourer.Gui; @@ -83,6 +84,6 @@ public static class Colors => _colors.TryGetValue(color, out var value) ? value : color.Data().DefaultColor; /// Set the configurable colors dictionary to a value. - public static void SetColors(Configuration.Configuration config) + public static void SetColors(Configuration config) => _colors = config.Colors; } diff --git a/Glamourer/Gui/Customization/CustomizationDrawer.Simple.cs b/Glamourer/Gui/Customization/CustomizationDrawer.Simple.cs index dd8a272..4665880 100644 --- a/Glamourer/Gui/Customization/CustomizationDrawer.Simple.cs +++ b/Glamourer/Gui/Customization/CustomizationDrawer.Simple.cs @@ -1,5 +1,5 @@ using System.Text.Unicode; -using Glamourer.Configuration; +using Glamourer.Config; using ImSharp; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; diff --git a/Glamourer/Gui/Customization/CustomizationDrawer.cs b/Glamourer/Gui/Customization/CustomizationDrawer.cs index d6ebf5d..a5b22b3 100644 --- a/Glamourer/Gui/Customization/CustomizationDrawer.cs +++ b/Glamourer/Gui/Customization/CustomizationDrawer.cs @@ -1,6 +1,7 @@ using Dalamud.Interface.Textures; using Dalamud.Interface.Textures.TextureWraps; using Dalamud.Plugin.Services; +using Glamourer.Config; using Glamourer.GameData; using Glamourer.Services; using Glamourer.Unlocks; @@ -14,7 +15,7 @@ namespace Glamourer.Gui.Customization; public partial class CustomizationDrawer( ITextureProvider textures, CustomizeService service, - Configuration.Configuration config, + Configuration config, FavoriteManager favorites, HeightService heightService) : IDisposable @@ -81,7 +82,7 @@ public partial class CustomizationDrawer( private CustomizeValue _currentByte = CustomizeValue.Zero; private bool _currentApply; private int _currentCount; - private StringU8 _currentOption = StringU8.Empty; + private StringU8 _currentOption = StringU8.Empty; // Prepare a new customization option. private Im.IdDisposable SetId(CustomizeIndex index) diff --git a/Glamourer/Gui/Customization/CustomizeParameterDrawer.cs b/Glamourer/Gui/Customization/CustomizeParameterDrawer.cs index 65a4e7d..e9004ac 100644 --- a/Glamourer/Gui/Customization/CustomizeParameterDrawer.cs +++ b/Glamourer/Gui/Customization/CustomizeParameterDrawer.cs @@ -1,4 +1,5 @@ -using Glamourer.Designs; +using Glamourer.Config; +using Glamourer.Designs; using Glamourer.GameData; using Glamourer.Interop.PalettePlus; using Glamourer.State; @@ -7,7 +8,7 @@ using Luna; namespace Glamourer.Gui.Customization; -public class CustomizeParameterDrawer(Configuration.Configuration config, PaletteImport import) : IService +public class CustomizeParameterDrawer(Configuration config, PaletteImport import) : IService { private readonly Dictionary _lastData = []; private StringU8 _paletteName = StringU8.Empty; @@ -125,7 +126,7 @@ public class CustomizeParameterDrawer(Configuration.Configuration config, Palett DrawColorFormatOptions(withApply); var value = config.ShowColorConfig; Im.Line.Same(); - if (Im.Checkbox("Show Config"u8, ref value)) + if (Im.Checkbox("Show Configuration"u8, ref value)) { config.ShowColorConfig = value; config.Save(); diff --git a/Glamourer/Gui/DesignCombo.cs b/Glamourer/Gui/DesignCombo.cs index cead406..6763cfe 100644 --- a/Glamourer/Gui/DesignCombo.cs +++ b/Glamourer/Gui/DesignCombo.cs @@ -9,7 +9,7 @@ using Luna; namespace Glamourer.Gui; public abstract class DesignComboBase( - Configuration.EphemeralConfig config, + Config.EphemeralConfig config, DesignManager designs, DesignChanged designChanged, DesignColors designColors, @@ -17,18 +17,18 @@ public abstract class DesignComboBase( DesignFileSystem designFileSystem) : FilterComboBase(new DesignFilter(), ConfigData.Default with { ComputeWidth = true }) { - protected readonly Configuration.EphemeralConfig Config = config; - protected readonly DesignChanged DesignChanged = designChanged; - protected readonly DesignColors DesignColors = designColors; - protected readonly DesignFileSystem DesignFileSystem = designFileSystem; - protected readonly TabSelected TabSelected = tabSelected; - protected readonly DesignManager Designs = designs; - protected IDesignStandIn? CurrentDesign; + protected readonly Config.EphemeralConfig Config = config; + protected readonly DesignChanged DesignChanged = designChanged; + protected readonly DesignColors DesignColors = designColors; + protected readonly DesignFileSystem DesignFileSystem = designFileSystem; + protected readonly TabSelected TabSelected = tabSelected; + protected readonly DesignManager Designs = designs; + protected IDesignStandIn? CurrentDesign; protected CacheItem CreateItem(IDesignStandIn design) { var color = design is Design d1 ? DesignColors.GetColor(d1).ToVector() : ColorId.NormalDesign.Value().ToVector(); - var path = design is Design d2 && DesignFileSystem.TryGetValue(d2, out var leaf) ? leaf.FullName() : string.Empty; + var path = design is Design d2 ? d2.Node!.FullPath : string.Empty; var name = design.ResolveName(false); if (path == name) path = string.Empty; @@ -118,7 +118,10 @@ public abstract class DesignComboBase( protected override void ComputeWidth() => ComboWidth = UnfilteredItems.Max(d - => d.Name.Utf8.CalculateSize(false).X + d.FullPath.Utf8.CalculateSize(false).X + 2 * Im.Style.ItemSpacing.X + Im.Style.ScrollbarSize); + => d.Name.Utf8.CalculateSize(false).X + + d.FullPath.Utf8.CalculateSize(false).X + + 2 * Im.Style.ItemSpacing.X + + Im.Style.ScrollbarSize); protected override void Dispose(bool disposing) { @@ -203,7 +206,7 @@ public sealed class QuickDesignCombo : DesignComboBase, IDisposable, IUiService } - public QuickDesignCombo(Configuration.EphemeralConfig config, DesignChanged designChanged, DesignColors designColors, TabSelected tabSelected, + public QuickDesignCombo(Config.EphemeralConfig config, DesignChanged designChanged, DesignColors designColors, TabSelected tabSelected, DesignFileSystem designFileSystem, DesignManager designs) : base(config, designs, designChanged, designColors, tabSelected, designFileSystem) { @@ -264,7 +267,7 @@ public sealed class LinkDesignCombo : DesignComboBase, IUiService, IDisposable { public Design? NewSelection { get; private set; } - public LinkDesignCombo(Configuration.EphemeralConfig config, DesignChanged designChanged, DesignColors designColors, TabSelected tabSelected, + public LinkDesignCombo(Config.EphemeralConfig config, DesignChanged designChanged, DesignColors designColors, TabSelected tabSelected, DesignFileSystem designFileSystem, DesignManager designs) : base(config, designs, designChanged, designColors, tabSelected, designFileSystem) { @@ -295,7 +298,7 @@ public sealed class LinkDesignCombo : DesignComboBase, IUiService, IDisposable } public sealed class RandomDesignCombo( - Configuration.EphemeralConfig config, + Config.EphemeralConfig config, DesignManager designs, DesignChanged designChanged, DesignColors designColors, @@ -307,14 +310,10 @@ public sealed class RandomDesignCombo( { return exact.Which switch { - RandomPredicate.Exact.Type.Name => Designs.Designs.FirstOrDefault(d => d.Name == exact.Value), - RandomPredicate.Exact.Type.Path => DesignFileSystem.Find(exact.Value.Text, out var c) && c is DesignFileSystem.Leaf l - ? l.Value - : null, - RandomPredicate.Exact.Type.Identifier => Designs.Designs.ByIdentifier(Guid.TryParse(exact.Value.Text, out var g) - ? g - : Guid.Empty), - _ => null, + RandomPredicate.Exact.Type.Name => Designs.Designs.FirstOrDefault(d => d.Name == exact.Value), + RandomPredicate.Exact.Type.Path => Designs.Designs.FirstOrDefault(d => d.Node!.FullPath == exact.Value.Text), + RandomPredicate.Exact.Type.Identifier => Designs.Designs.ByIdentifier(Guid.TryParse(exact.Value.Text, out var g) ? g : Guid.Empty), + _ => null, }; } @@ -346,7 +345,7 @@ public sealed class SpecialDesignCombo : DesignComboBase, IUiService private readonly CacheItem _revert; private readonly CacheItem _quick; - public SpecialDesignCombo(Configuration.EphemeralConfig config, + public SpecialDesignCombo(Config.EphemeralConfig config, DesignManager designs, DesignChanged designChanged, DesignColors designColors, diff --git a/Glamourer/Gui/DesignQuickBar.cs b/Glamourer/Gui/DesignQuickBar.cs index cd76118..30bab89 100644 --- a/Glamourer/Gui/DesignQuickBar.cs +++ b/Glamourer/Gui/DesignQuickBar.cs @@ -2,6 +2,7 @@ using Dalamud.Interface; using Dalamud.Plugin.Services; using Glamourer.Automation; +using Glamourer.Config; using Glamourer.Designs; using Glamourer.Interop.Penumbra; using Glamourer.State; @@ -33,19 +34,19 @@ public sealed class DesignQuickBar : Window, IDisposable ? WindowFlags.NoDecoration | WindowFlags.NoDocking | WindowFlags.NoFocusOnAppearing | WindowFlags.NoMove : WindowFlags.NoDecoration | WindowFlags.NoDocking | WindowFlags.NoFocusOnAppearing; - private readonly Configuration.Configuration _config; - private readonly QuickDesignCombo _designCombo; - private readonly StateManager _stateManager; - private readonly AutoDesignApplier _autoDesignApplier; - private readonly ActorObjectManager _objects; - private readonly PenumbraService _penumbra; - private readonly IKeyState _keyState; - private readonly Im.ColorStyleDisposable _style = new(); - private DateTime _keyboardToggle = DateTime.UnixEpoch; - private int _numButtons; - private readonly StringBuilder _tooltipBuilder = new(512); + private readonly Configuration _config; + private readonly QuickDesignCombo _designCombo; + private readonly StateManager _stateManager; + private readonly AutoDesignApplier _autoDesignApplier; + private readonly ActorObjectManager _objects; + private readonly PenumbraService _penumbra; + private readonly IKeyState _keyState; + private readonly Im.ColorStyleDisposable _style = new(); + private DateTime _keyboardToggle = DateTime.UnixEpoch; + private int _numButtons; + private readonly StringBuilder _tooltipBuilder = new(512); - public DesignQuickBar(Configuration.Configuration config, QuickDesignCombo designCombo, StateManager stateManager, IKeyState keyState, + public DesignQuickBar(Configuration config, QuickDesignCombo designCombo, StateManager stateManager, IKeyState keyState, ActorObjectManager objects, AutoDesignApplier autoDesignApplier, PenumbraService penumbra) : base("Glamourer Quick Bar", WindowFlags.NoDecoration | WindowFlags.NoDocking) { diff --git a/Glamourer/Gui/Equipment/EquipmentDrawer.cs b/Glamourer/Gui/Equipment/EquipmentDrawer.cs index 217d8c4..ba69c20 100644 --- a/Glamourer/Gui/Equipment/EquipmentDrawer.cs +++ b/Glamourer/Gui/Equipment/EquipmentDrawer.cs @@ -1,4 +1,5 @@ using Dalamud.Plugin.Services; +using Glamourer.Config; using Glamourer.Events; using Glamourer.Gui.Materials; using Glamourer.Services; @@ -22,7 +23,7 @@ public class EquipmentDrawer private readonly BonusItemCombo[] _bonusItemCombo; private readonly Dictionary _weaponCombo; private readonly TextureService _textures; - private readonly Configuration.Configuration _config; + private readonly Configuration _config; private readonly GPoseService _gPose; private readonly AdvancedDyePopup _advancedDyes; private readonly ItemCopyService _itemCopy; @@ -32,7 +33,7 @@ public class EquipmentDrawer private EquipSlot _dragTarget; public EquipmentDrawer(FavoriteManager favorites, IDataManager gameData, ItemManager items, TextureService textures, - Configuration.Configuration config, GPoseService gPose, AdvancedDyePopup advancedDyes, ItemCopyService itemCopy) + Configuration config, GPoseService gPose, AdvancedDyePopup advancedDyes, ItemCopyService itemCopy) { _items = items; _textures = textures; diff --git a/Glamourer/Gui/GenericPopupWindow.cs b/Glamourer/Gui/GenericPopupWindow.cs index cb13a18..f0a6441 100644 --- a/Glamourer/Gui/GenericPopupWindow.cs +++ b/Glamourer/Gui/GenericPopupWindow.cs @@ -1,17 +1,18 @@ using Dalamud.Game.ClientState.Conditions; using Dalamud.Plugin.Services; +using Glamourer.Config; using ImSharp; namespace Glamourer.Gui; public class GenericPopupWindow : Luna.Window { - private readonly Configuration.Configuration _config; - private readonly ICondition _condition; - private readonly IClientState _state; - public bool OpenFestivalPopup { get; internal set; } + private readonly Configuration _config; + private readonly ICondition _condition; + private readonly IClientState _state; + public bool OpenFestivalPopup { get; internal set; } - public GenericPopupWindow(Configuration.Configuration config, IClientState state, ICondition condition) + public GenericPopupWindow(Configuration config, IClientState state, ICondition condition) : base("Glamourer Popups", WindowFlags.NoBringToFrontOnFocus | WindowFlags.NoDecoration diff --git a/Glamourer/Gui/GlamourerChangelog.cs b/Glamourer/Gui/GlamourerChangelog.cs index a30dce6..a6c3e48 100644 --- a/Glamourer/Gui/GlamourerChangelog.cs +++ b/Glamourer/Gui/GlamourerChangelog.cs @@ -1,3 +1,4 @@ +using Glamourer.Config; using ImSharp; using Luna; @@ -5,11 +6,11 @@ namespace Glamourer.Gui; public class GlamourerChangelog { - public const int LastChangelogVersion = 0; - private readonly Configuration.Configuration _config; - public readonly Changelog Changelog; + public const int LastChangelogVersion = 0; + private readonly Configuration _config; + public readonly Changelog Changelog; - public GlamourerChangelog(Configuration.Configuration config) + public GlamourerChangelog(Configuration config) { _config = config; Changelog = new Changelog("Glamourer Changelog", ConfigData, Save); diff --git a/Glamourer/Gui/GlamourerWindowSystem.cs b/Glamourer/Gui/GlamourerWindowSystem.cs index 6379a4c..820927c 100644 --- a/Glamourer/Gui/GlamourerWindowSystem.cs +++ b/Glamourer/Gui/GlamourerWindowSystem.cs @@ -1,5 +1,6 @@ using Dalamud.Interface; using Dalamud.Interface.Windowing; +using Glamourer.Config; using Glamourer.Gui.Tabs.UnlocksTab; namespace Glamourer.Gui; @@ -11,7 +12,7 @@ public class GlamourerWindowSystem : IDisposable private readonly MainWindow _ui; public GlamourerWindowSystem(IUiBuilder uiBuilder, MainWindow ui, GenericPopupWindow popups, - Configuration.Configuration config, UnlocksTab unlocksTab, GlamourerChangelog changelog, DesignQuickBar quick) + Configuration config, UnlocksTab unlocksTab, GlamourerChangelog changelog, DesignQuickBar quick) { _uiBuilder = uiBuilder; _ui = ui; diff --git a/Glamourer/Gui/MainTabBar.cs b/Glamourer/Gui/MainTabBar.cs index 0862faa..23f5180 100644 --- a/Glamourer/Gui/MainTabBar.cs +++ b/Glamourer/Gui/MainTabBar.cs @@ -14,11 +14,11 @@ namespace Glamourer.Gui; public sealed class MainTabBar : TabBar { - private readonly Configuration.EphemeralConfig _config; + private readonly Config.EphemeralConfig _config; public readonly TabSelected Event; public readonly SettingsTab Settings; - public MainTabBar(Logger log, Configuration.EphemeralConfig config, SettingsTab settings, ActorTab actors, DesignTab designs, + public MainTabBar(Logger log, Config.EphemeralConfig config, SettingsTab settings, ActorTab actors, DesignTab designs, AutomationTab automation, UnlocksTab unlocks, NpcTab npcs, MessagesTab messages, DebugTab debug, TabSelected @event) : base("MainTabBar", log, settings, actors, designs, automation, unlocks, npcs, messages, debug) { diff --git a/Glamourer/Gui/MainWindow.cs b/Glamourer/Gui/MainWindow.cs index d1d5e3b..1952418 100644 --- a/Glamourer/Gui/MainWindow.cs +++ b/Glamourer/Gui/MainWindow.cs @@ -1,5 +1,6 @@ using Dalamud.Interface.ImGuiNotification; using Dalamud.Plugin; +using Glamourer.Config; using Glamourer.Interop.Penumbra; using ImSharp; using Luna; @@ -8,20 +9,20 @@ namespace Glamourer.Gui; public sealed class MainWindow : Window, IDisposable { - private readonly Configuration.Configuration _config; - private readonly PenumbraService _penumbra; - private readonly DesignQuickBar _quickBar; - private readonly MainTabBar _mainTabBar; - private bool _ignorePenumbra; + private readonly Configuration _config; + private readonly PenumbraService _penumbra; + private readonly DesignQuickBar _quickBar; + private readonly MainTabBar _mainTabBar; + private bool _ignorePenumbra; - public MainWindow(IDalamudPluginInterface pi, Configuration.Configuration config, PenumbraService penumbra, + public MainWindow(IDalamudPluginInterface pi, Configuration config, PenumbraService penumbra, MainTabBar mainTabBar, DesignQuickBar quickBar) : base("GlamourerMainWindow") { pi.UiBuilder.DisableGposeUiHide = true; SizeConstraints = new WindowSizeConstraints { - MinimumSize = new Vector2(700, 675), + MinimumSize = new Vector2(700, 675), MaximumSize = new Vector2(3840, 2160), }; _mainTabBar = mainTabBar; diff --git a/Glamourer/Gui/Materials/AdvancedDyePopup.cs b/Glamourer/Gui/Materials/AdvancedDyePopup.cs index 20d65dc..cd5da51 100644 --- a/Glamourer/Gui/Materials/AdvancedDyePopup.cs +++ b/Glamourer/Gui/Materials/AdvancedDyePopup.cs @@ -2,7 +2,7 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel; using FFXIVClientStructs.FFXIV.Client.Graphics.Render; using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle; -using Glamourer.Configuration; +using Glamourer.Config; using Glamourer.Designs; using Glamourer.Interop.Material; using Glamourer.State; @@ -17,7 +17,7 @@ using Notification = Luna.Notification; namespace Glamourer.Gui.Materials; public sealed unsafe class AdvancedDyePopup( - Configuration.Configuration config, + Configuration config, StateManager stateManager, LiveColorTablePreviewer preview, DirectXService directX) : IService diff --git a/Glamourer/Gui/Materials/MaterialDrawer.cs b/Glamourer/Gui/Materials/MaterialDrawer.cs index db9f8ce..d55666d 100644 --- a/Glamourer/Gui/Materials/MaterialDrawer.cs +++ b/Glamourer/Gui/Materials/MaterialDrawer.cs @@ -1,4 +1,5 @@ -using Glamourer.Designs; +using Glamourer.Config; +using Glamourer.Designs; using Glamourer.Interop.Material; using ImSharp; using Luna; @@ -7,7 +8,7 @@ using Penumbra.GameData.Files.MaterialStructs; namespace Glamourer.Gui.Materials; -public class MaterialDrawer(DesignManager designManager, Configuration.Configuration config) : IService +public class MaterialDrawer(DesignManager designManager, Configuration config) : IService { public const float GlossWidth = 100; public const float SpecularStrengthWidth = 125; diff --git a/Glamourer/Gui/Tabs/ActorTab/ActorPanel.cs b/Glamourer/Gui/Tabs/ActorTab/ActorPanel.cs index 0e8ef3c..f0f5be4 100644 --- a/Glamourer/Gui/Tabs/ActorTab/ActorPanel.cs +++ b/Glamourer/Gui/Tabs/ActorTab/ActorPanel.cs @@ -1,7 +1,7 @@ using Dalamud.Interface.ImGuiNotification; using FFXIVClientStructs.FFXIV.Client.Game; using Glamourer.Automation; -using Glamourer.Configuration; +using Glamourer.Config; using Glamourer.Designs; using Glamourer.Designs.History; using Glamourer.Gui.Customization; @@ -25,7 +25,7 @@ public sealed class ActorPanel : IPanel private readonly CustomizationDrawer _customizationDrawer; private readonly EquipmentDrawer _equipmentDrawer; private readonly AutoDesignApplier _autoDesignApplier; - private readonly Configuration.Configuration _config; + private readonly Configuration _config; private readonly DesignConverter _converter; private readonly ActorObjectManager _objects; private readonly ImportService _importService; @@ -37,7 +37,7 @@ public sealed class ActorPanel : IPanel CustomizationDrawer customizationDrawer, EquipmentDrawer equipmentDrawer, AutoDesignApplier autoDesignApplier, - Configuration.Configuration config, + Configuration config, DesignConverter converter, ActorObjectManager objects, DesignManager designManager, diff --git a/Glamourer/Gui/Tabs/ActorTab/ActorSelector.cs b/Glamourer/Gui/Tabs/ActorTab/ActorSelector.cs index 82d54ff..d4f3f9c 100644 --- a/Glamourer/Gui/Tabs/ActorTab/ActorSelector.cs +++ b/Glamourer/Gui/Tabs/ActorTab/ActorSelector.cs @@ -14,7 +14,7 @@ public readonly struct ActorCacheItem(ActorIdentifier identifier, ActorData data public readonly StringU8 IncognitoText = new(identifier.Incognito(data.Label)); } -public sealed class ActorSelector(ActorSelection selection, ActorObjectManager objects, ActorFilter filter, PenumbraService penumbra, Configuration.EphemeralConfig config) : IPanel +public sealed class ActorSelector(ActorSelection selection, ActorObjectManager objects, ActorFilter filter, PenumbraService penumbra, Config.EphemeralConfig config) : IPanel { public ReadOnlySpan Id => "ActorSelector"u8; diff --git a/Glamourer/Gui/Tabs/ActorTab/ActorTab.cs b/Glamourer/Gui/Tabs/ActorTab/ActorTab.cs index c250d7a..ed97ddc 100644 --- a/Glamourer/Gui/Tabs/ActorTab/ActorTab.cs +++ b/Glamourer/Gui/Tabs/ActorTab/ActorTab.cs @@ -1,4 +1,4 @@ -using Glamourer.Configuration; +using Glamourer.Config; using ImSharp; using Luna; diff --git a/Glamourer/Gui/Tabs/ActorTab/ActorsHeader.cs b/Glamourer/Gui/Tabs/ActorTab/ActorsHeader.cs index 907c1f4..4386d14 100644 --- a/Glamourer/Gui/Tabs/ActorTab/ActorsHeader.cs +++ b/Glamourer/Gui/Tabs/ActorTab/ActorsHeader.cs @@ -6,10 +6,10 @@ namespace Glamourer.Gui.Tabs.ActorTab; public sealed class ActorsHeader : SplitButtonHeader { private readonly ActorSelection _selection; - private readonly Configuration.EphemeralConfig _config; + private readonly Config.EphemeralConfig _config; public ActorsHeader(SetFromClipboardButton setFromClipboard, ExportToClipboardButton exportToClipboard, SaveAsDesignButton save, - UndoButton undo, LockedButton locked, IncognitoButton incognito, ActorSelection selection, Configuration.EphemeralConfig config) + UndoButton undo, LockedButton locked, IncognitoButton incognito, ActorSelection selection, Config.EphemeralConfig config) { _selection = selection; _config = config; diff --git a/Glamourer/Gui/Tabs/AutomationTab/AutomationButtons.cs b/Glamourer/Gui/Tabs/AutomationTab/AutomationButtons.cs index 6ef6583..8c0d1b6 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/AutomationButtons.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/AutomationButtons.cs @@ -1,4 +1,5 @@ using Glamourer.Automation; +using Glamourer.Config; using ImSharp; using Luna; using Penumbra.GameData.Actors; @@ -10,7 +11,7 @@ namespace Glamourer.Gui.Tabs.AutomationTab; public sealed class AutomationButtons : ButtonFooter { - public AutomationButtons(Configuration.Configuration config, AutoDesignManager manager, AutomationSelection selection, ActorObjectManager objects) + public AutomationButtons(Configuration config, AutoDesignManager manager, AutomationSelection selection, ActorObjectManager objects) { Buttons.AddButton(new AddButton(objects, manager), 100); Buttons.AddButton(new DuplicateButton(selection, manager), 90); @@ -134,7 +135,7 @@ public sealed class AutomationButtons : ButtonFooter } } - private sealed class DeleteButton(AutomationSelection selection, Configuration.Configuration config, AutoDesignManager manager) + private sealed class DeleteButton(AutomationSelection selection, Configuration config, AutoDesignManager manager) : BaseIconButton { private bool _enabled; diff --git a/Glamourer/Gui/Tabs/AutomationTab/AutomationHeader.cs b/Glamourer/Gui/Tabs/AutomationTab/AutomationHeader.cs index f5381e7..93c5dbb 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/AutomationHeader.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/AutomationHeader.cs @@ -1,13 +1,28 @@ -using ImSharp; +using Glamourer.Config; +using ImSharp; using Luna; namespace Glamourer.Gui.Tabs.AutomationTab; -public sealed class AutomationHeader(Configuration.Configuration config, AutomationSelection selection) : IHeader +public sealed class AutomationHeader : SplitButtonHeader { - public bool Collapsed - => false; + private readonly Configuration _config; + private readonly AutomationSelection _selection; - public void Draw(Vector2 size) - => ImEx.TextFramed(config.Ephemeral.IncognitoMode ? selection.Incognito : selection.Name, size with { Y = Im.Style.FrameHeight }); + public AutomationHeader(Configuration config, AutomationSelection selection, IncognitoButton incognito) + { + _config = config; + _selection = selection; + RightButtons.AddButton(incognito, 100); + } + + public override void Draw(Vector2 size) + { + var color = ColorId.HeaderButtons.Value(); + using var _ = ImGuiColor.Text.Push(color).Push(ImGuiColor.Border, color); + base.Draw(size with { Y = Im.Style.FrameHeight }); + } + + public override ReadOnlySpan Text + => _config.Ephemeral.IncognitoMode ? _selection.Incognito : _selection.Name; } diff --git a/Glamourer/Gui/Tabs/AutomationTab/AutomationTab.cs b/Glamourer/Gui/Tabs/AutomationTab/AutomationTab.cs index a2c601b..441ae26 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/AutomationTab.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/AutomationTab.cs @@ -1,14 +1,15 @@ -using ImSharp; +using Glamourer.Config; +using ImSharp; using Luna; namespace Glamourer.Gui.Tabs.AutomationTab; public class AutomationTab : TwoPanelLayout, ITab { - private readonly Configuration.Configuration _config; + private readonly Configuration _config; public AutomationTab(AutomationFilter filter, SetSelector selector, SetPanel panel, AutomationButtons buttons, AutomationHeader header, - Configuration.Configuration config) + Configuration config) { _config = config; LeftHeader = new FilterHeader(filter, new StringU8("Filter..."u8)); diff --git a/Glamourer/Gui/Tabs/AutomationTab/RandomRestrictionDrawer.cs b/Glamourer/Gui/Tabs/AutomationTab/RandomRestrictionDrawer.cs index 42cdc9c..87bf442 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/RandomRestrictionDrawer.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/RandomRestrictionDrawer.cs @@ -1,5 +1,6 @@ using Dalamud.Interface; using Glamourer.Automation; +using Glamourer.Config; using Glamourer.Designs; using Glamourer.Designs.Special; using Glamourer.Events; @@ -13,19 +14,19 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable private AutoDesignSet? _set; private int _designIndex = -1; - private readonly AutomationChanged _automationChanged; - private readonly Configuration.Configuration _config; - private readonly AutoDesignManager _autoDesignManager; - private readonly RandomDesignCombo _randomDesignCombo; - private readonly AutomationSelection _selection; - private readonly DesignStorage _designs; - private readonly DesignFileSystem _designFileSystem; + private readonly AutomationChanged _automationChanged; + private readonly Configuration _config; + private readonly AutoDesignManager _autoDesignManager; + private readonly RandomDesignCombo _randomDesignCombo; + private readonly AutomationSelection _selection; + private readonly DesignStorage _designs; + private readonly DesignFileSystem _designFileSystem; private string _newText = string.Empty; private string? _newDefinition; private Design? _newDesign; - public RandomRestrictionDrawer(AutomationChanged automationChanged, Configuration.Configuration config, AutoDesignManager autoDesignManager, + public RandomRestrictionDrawer(AutomationChanged automationChanged, Configuration config, AutoDesignManager autoDesignManager, RandomDesignCombo randomDesignCombo, AutomationSelection selection, DesignFileSystem designFileSystem, DesignStorage designs) { _automationChanged = automationChanged; @@ -268,19 +269,19 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable LookupTooltip(designs); } - private void LookupTooltip(IEnumerable designs) + private static void LookupTooltip(IEnumerable designs) { using var _ = Im.Tooltip.Begin(); using var enumerator = designs.GetEnumerator(); while (enumerator.MoveNext()) { Im.Text("Matches the following designs:"u8); - var name = _designFileSystem.TryGetValue(enumerator.Current, out var l) ? l.FullName() : enumerator.Current.Name.Text; + var name = enumerator.Current.Path.CurrentPath; Im.Separator(); Im.BulletText(name); while (enumerator.MoveNext()) { - name = _designFileSystem.TryGetValue(enumerator.Current, out l) ? l.FullName() : enumerator.Current.Name.Text; + name = enumerator.Current.Path.CurrentPath; Im.BulletText(name); } diff --git a/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs b/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs index bbd2fe5..e2ee40e 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs @@ -4,6 +4,7 @@ using Glamourer.Designs.Special; using Glamourer.Interop; using Glamourer.Services; using Glamourer.Unlocks; +using Glamourer.Config; using ImSharp; using Luna; using Penumbra.GameData.Enums; @@ -19,7 +20,7 @@ public class SetPanel( CustomizeUnlockManager customizeUnlocks, CustomizeService customizations, IdentifierDrawer identifierDrawer, - Configuration.Configuration config, + Configuration config, RandomRestrictionDrawer randomDrawer, AutomationSelection selection) : IPanel { diff --git a/Glamourer/Gui/Tabs/AutomationTab/SetSelector.cs b/Glamourer/Gui/Tabs/AutomationTab/SetSelector.cs index e61fdbc..10fb70d 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/SetSelector.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/SetSelector.cs @@ -1,4 +1,5 @@ using Glamourer.Automation; +using Glamourer.Config; using Glamourer.Events; using ImSharp; using Luna; @@ -8,7 +9,7 @@ namespace Glamourer.Gui.Tabs.AutomationTab; public sealed class SetSelector( AutomationSelection selection, - Configuration.Configuration config, + Configuration config, AutoDesignManager manager, AutomationFilter filter, ActorObjectManager objects, @@ -54,7 +55,8 @@ public sealed class SetSelector( var identifier = config.Ephemeral.IncognitoMode ? item.IdentifierIncognito : item.IdentifierString; var textSize = identifier.CalculateSize(); var textColor = item.Set.Identifiers.Any(objects.ContainsKey) ? cache.AutomationAvailable : cache.AutomationUnavailable; - Im.Cursor.Position = new Vector2(Im.ContentRegion.Available.X - textSize.X - Im.Style.FramePadding.X, Im.Cursor.Y - Im.Style.TextHeightWithSpacing); + Im.Cursor.Position = new Vector2(Im.ContentRegion.Available.X - textSize.X - Im.Style.FramePadding.X, + Im.Cursor.Y - Im.Style.TextHeightWithSpacing); Im.Text(identifier, textColor); } diff --git a/Glamourer/Gui/Tabs/DebugTab/DebugTab.cs b/Glamourer/Gui/Tabs/DebugTab/DebugTab.cs index 0e298bd..24b6fa6 100644 --- a/Glamourer/Gui/Tabs/DebugTab/DebugTab.cs +++ b/Glamourer/Gui/Tabs/DebugTab/DebugTab.cs @@ -1,11 +1,12 @@ -using ImSharp; +using Glamourer.Config; +using ImSharp; using Luna; namespace Glamourer.Gui.Tabs.DebugTab; public sealed class DebugTab(ServiceManager manager) : ITab { - private readonly Configuration.Configuration _config = manager.GetService(); + private readonly Configuration _config = manager.GetService(); public bool IsVisible => _config.DebugMode; diff --git a/Glamourer/Gui/Tabs/DebugTab/DesignConverterPanel.cs b/Glamourer/Gui/Tabs/DebugTab/DesignConverterPanel.cs index ef79666..ea8e97d 100644 --- a/Glamourer/Gui/Tabs/DebugTab/DesignConverterPanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/DesignConverterPanel.cs @@ -84,7 +84,7 @@ public sealed class DesignConverterPanel(DesignConverter designConverter) : IGam Im.Text("JSON Parsing Successful!"u8); if (_tmpDesign is not null) - DesignManagerPanel.DrawDesign(_tmpDesign, null); + DesignManagerPanel.DrawDesign(_tmpDesign); if (_clipboardProblem.Length > 0) { diff --git a/Glamourer/Gui/Tabs/DebugTab/DesignManagerPanel.cs b/Glamourer/Gui/Tabs/DebugTab/DesignManagerPanel.cs index 7da78dd..db45c52 100644 --- a/Glamourer/Gui/Tabs/DebugTab/DesignManagerPanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/DesignManagerPanel.cs @@ -24,7 +24,7 @@ public sealed class DesignManagerPanel(DesignManager designManager, DesignFileSy if (!t) continue; - DrawDesign(design, designFileSystem); + DrawDesign(design); var base64 = DesignBase64Migration.CreateOldBase64(design.DesignData, design.Application.Equip, design.Application.Customize, design.Application.Meta, design.WriteProtected()); @@ -50,12 +50,12 @@ public sealed class DesignManagerPanel(DesignManager designManager, DesignFileSy var designs = designManager.Designs.Where(d => d.Tags.Contains("_DebugTest")).ToArray(); foreach (var design in designs) designManager.Delete(design); - if (designFileSystem.Find("Test Designs", out var path) && path is DesignFileSystem.Folder { TotalChildren: 0 }) + if (designFileSystem.Find("Test Designs", out var path) && path is IFileSystemFolder { TotalDescendants: 0 }) designFileSystem.Delete(path); } } - public static void DrawDesign(DesignBase design, DesignFileSystem? fileSystem) + public static void DrawDesign(DesignBase design) { using var table = Im.Table.Begin("##equip"u8, 8, TableFlags.RowBackground | TableFlags.SizingFixedFit); if (design is Design d) @@ -70,8 +70,7 @@ public sealed class DesignManagerPanel(DesignManager designManager, DesignFileSy table.DrawDataPair("Identifier"u8, d.Identifier); table.NextRow(); table.DrawColumn("Design File System Path"u8); - if (fileSystem is not null) - table.DrawColumn(fileSystem.TryGetValue(d, out var leaf) ? leaf.FullName() : "No Path Known"u8); + table.DrawColumn(d.Path.CurrentPath); table.NextRow(); table.DrawDataPair("Creation"u8, d.CreationDate); diff --git a/Glamourer/Gui/Tabs/DebugTab/FunPanel.cs b/Glamourer/Gui/Tabs/DebugTab/FunPanel.cs index 10c5b1d..07071ef 100644 --- a/Glamourer/Gui/Tabs/DebugTab/FunPanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/FunPanel.cs @@ -1,10 +1,11 @@ -using Glamourer.State; +using Glamourer.Config; +using Glamourer.State; using ImSharp; using Penumbra.GameData.Gui.Debug; namespace Glamourer.Gui.Tabs.DebugTab; -public sealed class FunPanel(FunModule funModule, Configuration.Configuration config) : IGameDataDrawer +public sealed class FunPanel(FunModule funModule, Configuration config) : IGameDataDrawer { public ReadOnlySpan Label => "Fun Module"u8; diff --git a/Glamourer/Gui/Tabs/DesignTab/ApplyCharacterButton.cs b/Glamourer/Gui/Tabs/DesignTab/ApplyCharacterButton.cs new file mode 100644 index 0000000..bb728dc --- /dev/null +++ b/Glamourer/Gui/Tabs/DesignTab/ApplyCharacterButton.cs @@ -0,0 +1,55 @@ +using Dalamud.Interface; +using Dalamud.Interface.ImGuiNotification; +using Glamourer.Designs; +using Glamourer.State; +using ImSharp; +using Luna; +using Penumbra.GameData.Interop; + +namespace Glamourer.Gui.Tabs.DesignTab; + +public sealed class ApplyCharacterButton( + DesignFileSystem fileSystem, + DesignManager manager, + ActorObjectManager objects, + StateManager stateManager, + DesignConverter converter) : BaseIconButton +{ + private static readonly AwesomeIcon UserIcon = FontAwesomeIcon.UserEdit; + + public override bool IsVisible + => fileSystem.Selection.Selection is not null && objects.Player.Valid; + + public override AwesomeIcon Icon + => UserIcon; + + public override bool Enabled + => !((Design)fileSystem.Selection.Selection!.Value).WriteProtected(); + + public override bool HasTooltip + => true; + + public override void DrawTooltip() + => Im.Text("Overwrite this design with your character's current state."u8); + + public override void OnClick() + { + var selection = (Design)fileSystem.Selection.Selection!.Value; + try + { + var (player, actor) = objects.PlayerData; + if (!player.IsValid || !actor.Valid || !stateManager.GetOrCreate(player, actor.Objects[0], out var state)) + throw new Exception("No player state available."); + + var design = converter.Convert(state, ApplicationRules.FromModifiers(state)) + ?? throw new Exception("The clipboard did not contain valid data."); + selection.GetMaterialDataRef().Clear(); + manager.ApplyDesign(selection, design); + } + catch (Exception ex) + { + Glamourer.Messager.NotificationMessage(ex, $"Could not apply player state to {selection.Name}.", + $"Could not apply player state to design {selection.Identifier}", NotificationType.Error, false); + } + } +} diff --git a/Glamourer/Gui/Tabs/DesignTab/DesignDetailTab.cs b/Glamourer/Gui/Tabs/DesignTab/DesignDetailTab.cs index 3c5e6c0..f06cbd8 100644 --- a/Glamourer/Gui/Tabs/DesignTab/DesignDetailTab.cs +++ b/Glamourer/Gui/Tabs/DesignTab/DesignDetailTab.cs @@ -1,5 +1,5 @@ using Dalamud.Interface.ImGuiNotification; -using Glamourer.Configuration; +using Glamourer.Config; using Glamourer.Designs; using Glamourer.Services; using ImSharp; @@ -9,21 +9,19 @@ namespace Glamourer.Gui.Tabs.DesignTab; public class DesignDetailTab { - private readonly SaveService _saveService; - private readonly Configuration.Configuration _config; - private readonly DesignFileSystemSelector _selector; - private readonly DesignFileSystem _fileSystem; - private readonly DesignManager _manager; - private readonly DesignColors _colors; - private readonly DesignColorCombo _colorCombo; + private readonly SaveService _saveService; + private readonly Configuration _config; + private readonly DesignFileSystem _fileSystem; + private readonly DesignManager _manager; + private readonly DesignColors _colors; + private readonly DesignColorCombo _colorCombo; private bool _editDescriptionMode; - public DesignDetailTab(SaveService saveService, DesignFileSystemSelector selector, DesignManager manager, DesignFileSystem fileSystem, - DesignColors colors, Configuration.Configuration config) + public DesignDetailTab(SaveService saveService, DesignManager manager, DesignFileSystem fileSystem, + DesignColors colors, Configuration config) { _saveService = saveService; - _selector = selector; _manager = manager; _fileSystem = fileSystem; _colors = colors; @@ -42,6 +40,8 @@ public class DesignDetailTab Im.Line.New(); } + private Design Selected + => (Design) _fileSystem.Selection.Selection!.Value; private void DrawDesignInfoTable() { @@ -57,13 +57,13 @@ public class DesignDetailTab table.NextColumn(); var width = Im.ContentRegion.Available with { Y = 0 }; Im.Item.SetNextWidth(width.X); - if (ImEx.InputOnDeactivation.Text("##Name"u8, _selector.Selected!.Name.Text, out string newName)) - _manager.Rename(_selector.Selected!, newName); + if (ImEx.InputOnDeactivation.Text("##Name"u8, Selected.Name.Text, out string newName)) + _manager.Rename(Selected, newName); - var identifier = _selector.Selected!.Identifier.ToString(); + var identifier = Selected.Identifier.ToString(); table.DrawFrameColumn("Unique Identifier"u8); table.NextColumn(); - var fileName = _saveService.FileNames.DesignFile(_selector.Selected!); + var fileName = _saveService.FileNames.DesignFile(Selected); using (Im.Font.PushMono()) { if (Im.Button(identifier, width)) @@ -87,10 +87,10 @@ public class DesignDetailTab table.DrawFrameColumn("Full Selector Path"u8); table.NextColumn(); Im.Item.SetNextWidth(width.X); - if (ImEx.InputOnDeactivation.Text("##Path"u8, _selector.SelectedLeaf!.FullName(), out string newPath)) + if (ImEx.InputOnDeactivation.Text("##Path"u8, Selected.Path.CurrentPath, out string newPath)) try { - _fileSystem.RenameAndMove(_selector.SelectedLeaf, newPath); + _fileSystem.RenameAndMove(Selected.Node!, newPath); } catch (Exception ex) { @@ -99,56 +99,56 @@ public class DesignDetailTab table.DrawFrameColumn("Quick Design Bar"u8); table.NextColumn(); - if (Im.RadioButton("Display##qdb"u8, _selector.Selected.QuickDesign)) - _manager.SetQuickDesign(_selector.Selected!, true); + if (Im.RadioButton("Display##qdb"u8, Selected.QuickDesign)) + _manager.SetQuickDesign(Selected, true); var hovered = Im.Item.Hovered(); Im.Line.SameInner(); - if (Im.RadioButton("Hide##qdb"u8, !_selector.Selected.QuickDesign)) - _manager.SetQuickDesign(_selector.Selected!, false); + if (Im.RadioButton("Hide##qdb"u8, !Selected.QuickDesign)) + _manager.SetQuickDesign(Selected, false); if (hovered || Im.Item.Hovered()) Im.Tooltip.Set("Display or hide this design in your quick design bar."u8); - var forceRedraw = _selector.Selected!.ForcedRedraw; + var forceRedraw = Selected.ForcedRedraw; table.DrawFrameColumn("Force Redrawing"u8); table.NextColumn(); if (Im.Checkbox("##ForceRedraw"u8, ref forceRedraw)) - _manager.ChangeForcedRedraw(_selector.Selected!, forceRedraw); + _manager.ChangeForcedRedraw(Selected, forceRedraw); Im.Tooltip.OnHover("Set this design to always force a redraw when it is applied through any means."u8); - var resetAdvancedDyes = _selector.Selected!.ResetAdvancedDyes; + var resetAdvancedDyes = Selected.ResetAdvancedDyes; table.DrawFrameColumn("Reset Advanced Dyes"u8); table.NextColumn(); if (Im.Checkbox("##ResetAdvancedDyes"u8, ref resetAdvancedDyes)) - _manager.ChangeResetAdvancedDyes(_selector.Selected!, resetAdvancedDyes); + _manager.ChangeResetAdvancedDyes(Selected, resetAdvancedDyes); Im.Tooltip.OnHover("Set this design to reset any previously applied advanced dyes when it is applied through any means."u8); - var resetTemporarySettings = _selector.Selected!.ResetTemporarySettings; + var resetTemporarySettings = Selected.ResetTemporarySettings; table.DrawFrameColumn("Reset Temporary Settings"u8); table.NextColumn(); if (Im.Checkbox("##ResetTemporarySettings"u8, ref resetTemporarySettings)) - _manager.ChangeResetTemporarySettings(_selector.Selected!, resetTemporarySettings); + _manager.ChangeResetTemporarySettings(Selected, resetTemporarySettings); Im.Tooltip.OnHover( "Set this design to reset any temporary settings previously applied to the associated collection when it is applied through any means."u8); table.DrawFrameColumn("Color"u8); table.NextColumn(); - if (_colorCombo.Draw("##colorCombo"u8, _selector.Selected!.Color.Length is 0 ? DesignColors.AutomaticName : _selector.Selected!.Color, + if (_colorCombo.Draw("##colorCombo"u8, Selected.Color.Length is 0 ? DesignColors.AutomaticName : Selected.Color, "Associate a color with this design.\n"u8 + "Right-Click to revert to automatic coloring.\n"u8 + "Hold Control and scroll the mousewheel to scroll."u8, width.X - Im.Style.ItemSpacing.X - Im.Style.FrameHeight, out var newColorName)) - _manager.ChangeColor(_selector.Selected!, newColorName == DesignColors.AutomaticName ? string.Empty : newColorName); + _manager.ChangeColor(Selected, newColorName == DesignColors.AutomaticName ? string.Empty : newColorName); if (Im.Item.RightClicked()) - _manager.ChangeColor(_selector.Selected!, string.Empty); + _manager.ChangeColor(Selected, string.Empty); - if (_colors.TryGetValue(_selector.Selected!.Color, out var currentColor)) + if (_colors.TryGetValue(Selected.Color, out var currentColor)) { Im.Line.Same(); - if (DesignColorUi.DrawColorButton($"Color associated with {_selector.Selected!.Color}", currentColor, out var newColor)) - _colors.SetColor(_selector.Selected!.Color, newColor); + if (DesignColorUi.DrawColorButton($"Color associated with {Selected.Color}", currentColor, out var newColor)) + _colors.SetColor(Selected.Color, newColor); } - else if (_selector.Selected!.Color.Length != 0) + else if (Selected.Color.Length is not 0) { Im.Line.Same(); ImEx.Icon.Draw(LunaStyle.WarningIcon, _colors.MissingColor); @@ -157,11 +157,11 @@ public class DesignDetailTab table.DrawFrameColumn("Creation Date"u8); table.NextColumn(); - ImEx.TextFramed($"{_selector.Selected!.CreationDate.LocalDateTime:F}", width, 0); + ImEx.TextFramed($"{Selected.CreationDate.LocalDateTime:F}", width, 0); table.DrawFrameColumn("Last Update Date"u8); table.NextColumn(); - ImEx.TextFramed($"{_selector.Selected!.LastEdit.LocalDateTime:F}", width, 0); + ImEx.TextFramed($"{Selected.LastEdit.LocalDateTime:F}", width, 0); table.DrawFrameColumn("Tags"u8); table.NextColumn(); @@ -170,26 +170,26 @@ public class DesignDetailTab private void DrawTags() { - var idx = TagButtons.Draw(StringU8.Empty, StringU8.Empty, _selector.Selected!.Tags, out var editedTag); + var idx = TagButtons.Draw(StringU8.Empty, StringU8.Empty, Selected.Tags, out var editedTag); if (idx < 0) return; - if (idx < _selector.Selected!.Tags.Length) + if (idx < Selected.Tags.Length) { if (editedTag.Length is 0) - _manager.RemoveTag(_selector.Selected!, idx); + _manager.RemoveTag(Selected, idx); else - _manager.RenameTag(_selector.Selected!, idx, editedTag); + _manager.RenameTag(Selected, idx, editedTag); } else { - _manager.AddTag(_selector.Selected!, editedTag); + _manager.AddTag(Selected, editedTag); } } private void DrawDescription() { - var desc = _selector.Selected!.Description; + var desc = Selected.Description; var size = Im.ContentRegion.Available with { Y = 12 * Im.Style.TextHeightWithSpacing }; if (!_editDescriptionMode) { @@ -205,7 +205,7 @@ public class DesignDetailTab else { if (ImEx.InputOnDeactivation.MultiLine("##desc"u8, desc, out string newDescription, size)) - _manager.ChangeDescription(_selector.Selected!, newDescription); + _manager.ChangeDescription(Selected, newDescription); if (Im.Button("Stop Editing"u8)) _editDescriptionMode = false; diff --git a/Glamourer/Gui/Tabs/DesignTab/DesignFileSystemSelector.cs b/Glamourer/Gui/Tabs/DesignTab/DesignFileSystemSelector.cs deleted file mode 100644 index 33e35e2..0000000 --- a/Glamourer/Gui/Tabs/DesignTab/DesignFileSystemSelector.cs +++ /dev/null @@ -1,405 +0,0 @@ -using Dalamud.Interface; -using Dalamud.Interface.ImGuiNotification; -using Dalamud.Plugin.Services; -using Glamourer.Designs; -using Glamourer.Designs.History; -using Glamourer.Events; -using Glamourer.Services; -using Dalamud.Bindings.ImGui; -using ImSharp; -using OtterGui; -using OtterGui.Classes; -using OtterGui.Filesystem; -using OtterGui.FileSystem.Selector; -using OtterGui.Log; -using OtterGui.Raii; -using OtterGui.Text; -using Luna; - -namespace Glamourer.Gui.Tabs.DesignTab; - -public sealed class DesignFileSystemSelector : FileSystemSelector, IPanel -{ - private readonly DesignManager _designManager; - private readonly DesignChanged _event; - private readonly Configuration.Configuration _config; - private readonly DesignConverter _converter; - private readonly TabSelected _selectionEvent; - private readonly DesignColors _designColors; - private readonly DesignApplier _designApplier; - - private string? _clipboardText; - private Design? _cloneDesign; - private string _newName = string.Empty; - - public new DesignFileSystem.Leaf? SelectedLeaf - => base.SelectedLeaf; - - public record struct DesignState(Rgba32 Color) - { } - - protected override float CurrentWidth - => _config.Ephemeral.CurrentDesignSelectorWidth * Im.Style.GlobalScale; - - protected override float MinimumAbsoluteRemainder - => 470 * Im.Style.GlobalScale; - - protected override float MinimumScaling - => _config.Ephemeral.DesignSelectorMinimumScale; - - protected override float MaximumScaling - => _config.Ephemeral.DesignSelectorMaximumScale; - - protected override void SetSize(Vector2 size) - { - base.SetSize(size); - var adaptedSize = MathF.Round(size.X / Im.Style.GlobalScale); - if (adaptedSize == _config.Ephemeral.CurrentDesignSelectorWidth) - return; - - _config.Ephemeral.CurrentDesignSelectorWidth = adaptedSize; - _config.Ephemeral.Save(); - } - - public DesignFileSystemSelector(DesignManager designManager, DesignFileSystem fileSystem, IKeyState keyState, DesignChanged @event, - Configuration.Configuration config, DesignConverter converter, TabSelected selectionEvent, OtterGui.Log.Logger log, DesignColors designColors, - DesignApplier designApplier) - : base(fileSystem, keyState, log, allowMultipleSelection: true) - { - _designManager = designManager; - _event = @event; - _config = config; - _converter = converter; - _selectionEvent = selectionEvent; - _designColors = designColors; - _designApplier = designApplier; - _event.Subscribe(OnDesignChange, DesignChanged.Priority.DesignFileSystemSelector); - _selectionEvent.Subscribe(OnTabSelected, TabSelected.Priority.DesignSelector); - _designColors.ColorChanged += SetFilterDirty; - - AddButton(NewDesignButton, 0); - AddButton(ImportDesignButton, 10); - AddButton(CloneDesignButton, 20); - AddButton(DeleteButton, 1000); - UnsubscribeRightClickLeaf(RenameLeaf); - SetRenameSearchPath(_config.ShowRename); - SetFilterTooltip(); - - if (_config.Ephemeral.SelectedDesign == Guid.Empty) - return; - - var design = designManager.Designs.ByIdentifier(_config.Ephemeral.SelectedDesign); - if (design != null) - SelectByValue(design); - } - - public void SetRenameSearchPath(RenameField value) - { - switch (value) - { - case RenameField.RenameSearchPath: - SubscribeRightClickLeaf(RenameLeafDesign, 1000); - UnsubscribeRightClickLeaf(RenameDesign); - break; - case RenameField.RenameData: - UnsubscribeRightClickLeaf(RenameLeafDesign); - SubscribeRightClickLeaf(RenameDesign, 1000); - break; - case RenameField.BothSearchPathPrio: - UnsubscribeRightClickLeaf(RenameLeafDesign); - UnsubscribeRightClickLeaf(RenameDesign); - SubscribeRightClickLeaf(RenameLeafDesign, 1001); - SubscribeRightClickLeaf(RenameDesign, 1000); - break; - case RenameField.BothDataPrio: - UnsubscribeRightClickLeaf(RenameLeafDesign); - UnsubscribeRightClickLeaf(RenameDesign); - SubscribeRightClickLeaf(RenameLeafDesign, 1000); - SubscribeRightClickLeaf(RenameDesign, 1001); - break; - default: - UnsubscribeRightClickLeaf(RenameLeafDesign); - UnsubscribeRightClickLeaf(RenameDesign); - break; - } - } - - private void RenameLeafDesign(DesignFileSystem.Leaf leaf) - { - Im.Separator(); - RenameLeaf(leaf); - } - - private void RenameDesign(DesignFileSystem.Leaf leaf) - { - Im.Separator(); - var currentName = leaf.Value.Name.Text; - if (ImGui.IsWindowAppearing()) - ImGui.SetKeyboardFocusHere(0); - ImGui.TextUnformatted("Rename Design:"); - if (Im.Input.Text("##RenameDesign"u8, ref currentName, StringU8.Empty, InputTextFlags.EnterReturnsTrue)) - { - _designManager.Rename(leaf.Value, currentName); - ImGui.CloseCurrentPopup(); - } - - ImGuiUtil.HoverTooltip("Enter a new name here to rename the changed design."); - } - - protected override void Select(FileSystem.Leaf? leaf, bool clear, in DesignState storage = default) - { - base.Select(leaf, clear, storage); - var id = SelectedLeaf?.Value.Identifier ?? Guid.Empty; - if (id != _config.Ephemeral.SelectedDesign) - { - _config.Ephemeral.SelectedDesign = id; - _config.Ephemeral.Save(); - } - } - - protected override void DrawPopups() - { - DrawNewDesignPopup(); - } - - protected override void DrawLeafName(FileSystem.Leaf leaf, in DesignState state, bool selected) - { - var flag = selected ? ImGuiTreeNodeFlags.Selected | LeafFlags : LeafFlags; - var name = _config.Ephemeral.IncognitoMode ? leaf.Value.Incognito : leaf.Value.Name.Text; - using var color = ImGuiColor.Text.Push(state.Color); - using var _ = ImUtf8.TreeNode(name, flag); - if (_config.AllowDoubleClickToApply && ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(ImGuiMouseButton.Left)) - _designApplier.ApplyToPlayer(leaf.Value); - } - - public override void Dispose() - { - base.Dispose(); - _event.Unsubscribe(OnDesignChange); - _selectionEvent.Unsubscribe(OnTabSelected); - _designColors.ColorChanged -= SetFilterDirty; - } - - public override ISortMode SortMode - => _config.SortMode; - - protected override uint ExpandedFolderColor - => ColorId.FolderExpanded.Value().Color; - - protected override uint CollapsedFolderColor - => ColorId.FolderCollapsed.Value().Color; - - protected override uint FolderLineColor - => ColorId.FolderLine.Value().Color; - - protected override bool FoldersDefaultOpen - => _config.OpenFoldersByDefault; - - private void OnDesignChange(DesignChanged.Type type, Design design, ITransaction? _) - { - switch (type) - { - case DesignChanged.Type.ReloadedAll: - case DesignChanged.Type.Renamed: - case DesignChanged.Type.AddedTag: - case DesignChanged.Type.ChangedTag: - case DesignChanged.Type.RemovedTag: - case DesignChanged.Type.AddedMod: - case DesignChanged.Type.RemovedMod: - case DesignChanged.Type.Created: - case DesignChanged.Type.Deleted: - case DesignChanged.Type.ApplyCustomize: - case DesignChanged.Type.ApplyEquip: - case DesignChanged.Type.ApplyStain: - case DesignChanged.Type.ApplyCrest: - case DesignChanged.Type.Customize: - case DesignChanged.Type.Equip: - case DesignChanged.Type.ChangedColor: - SetFilterDirty(); - break; - } - } - - private void NewDesignButton(Vector2 size) - { - if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), size, "Create a new design with default configuration.", false, - true)) - { - _cloneDesign = null; - _clipboardText = null; - ImGui.OpenPopup("##NewDesign"); - } - } - - private void ImportDesignButton(Vector2 size) - { - if (!ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.FileImport.ToIconString(), size, "Try to import a design from your clipboard.", false, - true)) - return; - - try - { - _cloneDesign = null; - _clipboardText = ImGui.GetClipboardText(); - ImGui.OpenPopup("##NewDesign"); - } - catch - { - Glamourer.Messager.NotificationMessage("Could not import data from clipboard.", NotificationType.Error, false); - } - } - - private void CloneDesignButton(Vector2 size) - { - var tt = SelectedLeaf == null - ? "No design selected." - : "Clone the currently selected design to a duplicate"; - if (!ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Clone.ToIconString(), size, tt, SelectedLeaf == null, true)) - return; - - _clipboardText = null; - _cloneDesign = Selected!; - ImGui.OpenPopup("##NewDesign"); - } - - private void DeleteButton(Vector2 size) - => DeleteSelectionButton(size, - new OtterGui.Classes.DoubleModifier(new OtterGui.Classes.ModifierHotkey(_config.DeleteDesignModifier.Modifier1), - new OtterGui.Classes.ModifierHotkey(_config.DeleteDesignModifier.Modifier2)), "design", "designs", _designManager.Delete); - - private void DrawNewDesignPopup() - { - if (!ImGuiUtil.OpenNameField("##NewDesign", ref _newName)) - return; - - if (_clipboardText != null) - { - var design = _converter.FromBase64(_clipboardText, true, true, out _); - if (design is Design d) - _designManager.CreateClone(d, _newName, true); - else if (design != null) - _designManager.CreateClone(design, _newName, true); - else - Glamourer.Messager.NotificationMessage("Could not create a design, clipboard did not contain valid design data.", - NotificationType.Error, false); - _clipboardText = null; - } - else if (_cloneDesign != null) - { - _designManager.CreateClone(_cloneDesign, _newName, true); - _cloneDesign = null; - } - else - { - _designManager.CreateEmpty(_newName, true); - } - - _newName = string.Empty; - } - - private void OnTabSelected(MainTabType type, Design? design) - { - if (type == MainTabType.Designs && design != null) - SelectByValue(design); - } - - #region Filters - - private const StringComparison IgnoreCase = StringComparison.OrdinalIgnoreCase; - private LowerString _designFilter = LowerString.Empty; - private int _filterType = -1; - - private void SetFilterTooltip() - { - FilterTooltip = "Filter designs for those where their full paths or names contain the given substring.\n" - + "Enter m:[string] to filter for designs with with a mod association containing the string.\n" - + "Enter t:[string] to filter for designs set to specific tags.\n" - + "Enter c:[string] to filter for designs set to specific colors.\n" - + "Enter i:[string] to filter for designs containing specific items.\n" - + "Enter n:[string] to filter only for design names and no paths.\n\n" - + "Use None as a placeholder value that only matches empty lists or names."; - } - - /// Appropriately identify and set the string filter and its type. - protected override bool ChangeFilter(string filterValue) - { - (_designFilter, _filterType) = filterValue.Length switch - { - 0 => (LowerString.Empty, -1), - > 1 when filterValue[1] == ':' => - filterValue[0] switch - { - 'n' => filterValue.Length == 2 ? (LowerString.Empty, -1) : (new LowerString(filterValue[2..]), 1), - 'N' => filterValue.Length == 2 ? (LowerString.Empty, -1) : (new LowerString(filterValue[2..]), 1), - 'm' => filterValue.Length == 2 ? (LowerString.Empty, -1) : ParseFilter(filterValue, 2), - 'M' => filterValue.Length == 2 ? (LowerString.Empty, -1) : ParseFilter(filterValue, 2), - 't' => filterValue.Length == 2 ? (LowerString.Empty, -1) : ParseFilter(filterValue, 3), - 'T' => filterValue.Length == 2 ? (LowerString.Empty, -1) : ParseFilter(filterValue, 3), - 'i' => filterValue.Length == 2 ? (LowerString.Empty, -1) : (new LowerString(filterValue[2..]), 4), - 'I' => filterValue.Length == 2 ? (LowerString.Empty, -1) : (new LowerString(filterValue[2..]), 4), - 'c' => filterValue.Length == 2 ? (LowerString.Empty, -1) : (new LowerString(filterValue[2..]), 5), - 'C' => filterValue.Length == 2 ? (LowerString.Empty, -1) : (new LowerString(filterValue[2..]), 5), - _ => (new LowerString(filterValue), 0), - }, - _ => (new LowerString(filterValue), 0), - }; - - return true; - } - - private const int EmptyOffset = 128; - - private static (LowerString, int) ParseFilter(string value, int id) - { - value = value[2..]; - var lower = new LowerString(value); - return (lower, lower.Lower is "none" ? id + EmptyOffset : id); - } - - /// - /// The overwritten filter method also computes the state. - /// Folders have default state and are filtered out on the direct string instead of the other options. - /// If any filter is set, they should be hidden by default unless their children are visible, - /// or they contain the path search string. - /// - protected override bool ApplyFiltersAndState(FileSystem.IPath path, out DesignState state) - { - if (path is DesignFileSystem.Folder f) - { - state = default; - return FilterValue.Length > 0 && !f.FullName().Contains(FilterValue, IgnoreCase); - } - - return ApplyFiltersAndState((DesignFileSystem.Leaf)path, out state); - } - - /// Apply the string filters. - private bool ApplyStringFilters(DesignFileSystem.Leaf leaf, Design design) - { - return _filterType switch - { - -1 => false, - 0 => !(_designFilter.IsContained(leaf.FullName()) || design.Name.Contains(_designFilter)), - 1 => !design.Name.Contains(_designFilter), - 2 => !design.AssociatedMods.Any(kvp => _designFilter.IsContained(kvp.Key.Name)), - 3 => !design.Tags.Any(_designFilter.IsContained), - 4 => !design.DesignData.ContainsName(_designFilter), - 5 => !_designFilter.IsContained(design.Color.Length == 0 ? DesignColors.AutomaticName : design.Color), - 2 + EmptyOffset => design.AssociatedMods.Count > 0, - 3 + EmptyOffset => design.Tags.Length > 0, - _ => false, // Should never happen - }; - } - - /// Combined wrapper for handling all filters and setting state. - private bool ApplyFiltersAndState(DesignFileSystem.Leaf leaf, out DesignState state) - { - state = new DesignState(_designColors.GetColor(leaf.Value)); - return ApplyStringFilters(leaf, leaf.Value); - } - - #endregion - - public ReadOnlySpan Id - => "DesignSelector"u8; -} diff --git a/Glamourer/Gui/Tabs/DesignTab/DesignHeader.cs b/Glamourer/Gui/Tabs/DesignTab/DesignHeader.cs index b8baa4d..5b75fe3 100644 --- a/Glamourer/Gui/Tabs/DesignTab/DesignHeader.cs +++ b/Glamourer/Gui/Tabs/DesignTab/DesignHeader.cs @@ -1,21 +1,107 @@ -using Luna; +using System.Security.AccessControl; +using Glamourer.Config; +using Glamourer.Designs; +using Glamourer.Designs.History; +using Glamourer.Events; +using Glamourer.State; +using ImSharp; +using Luna; +using Penumbra.GameData.Interop; namespace Glamourer.Gui.Tabs.DesignTab; -public sealed class DesignHeader : SplitButtonHeader +public sealed class DesignHeader : SplitButtonHeader, IDisposable { - public DesignHeader(DesignSelection selection, IncognitoButton incognito) + private readonly DesignFileSystem _fileSystem; + private readonly DesignChanged _designChanged; + private readonly Configuration _config; + + private StringU8 _header = new("No Selection"u8); + private StringU8 _incognito = new("No Selection"u8); + + public DesignHeader(DesignFileSystem fileSystem, IncognitoButton incognito, DesignChanged designChanged, Configuration config, + DesignConverter converter, StateManager stateManager, EditorHistory history, DesignManager manager, ActorObjectManager objects) { - RightButtons.AddButton(incognito, 50); - RightButtons.AddButton(new LockedButton(selection), 100); + _fileSystem = fileSystem; + _designChanged = designChanged; + _config = config; + LeftButtons.AddButton(new SetFromClipboardButton(fileSystem, converter, manager), 100); + LeftButtons.AddButton(new DesignUndoButton(fileSystem, manager), 90); + LeftButtons.AddButton(new ExportToClipboardButton(fileSystem, converter), 80); + LeftButtons.AddButton(new ApplyCharacterButton(fileSystem, manager, objects, stateManager, converter), 70); + LeftButtons.AddButton(new UndoButton(fileSystem, history), 60); + + RightButtons.AddButton(incognito, 50); + RightButtons.AddButton(new LockedButton(fileSystem, manager), 100); + _fileSystem.Selection.Changed += OnSelectionChanged; + OnSelectionChanged(); + designChanged.Subscribe(OnDesignChanged, DesignChanged.Priority.DesignHeader); } - private sealed class LockedButton(DesignSelection selection) : BaseIconButton + private void OnDesignChanged(DesignChanged.Type arg1, Design arg2, ITransaction? arg3) + { + if (arg1 is not DesignChanged.Type.Renamed) + return; + + if (arg2 != _fileSystem.Selection.Selection?.Value) + return; + + _header = new StringU8(arg2.Name.Text); + } + + private void OnSelectionChanged() + { + if (_fileSystem.Selection.Selection?.GetValue() is { } selection) + { + _header = new StringU8(selection.Name.Text); + _incognito = new StringU8(selection.Incognito); + } + else if (_fileSystem.Selection.OrderedNodes.Count > 0) + { + _header = new StringU8($"{_fileSystem.Selection.OrderedNodes.Count} Objects Selected"); + _incognito = _header; + } + else + { + _header = new StringU8("No Selection"u8); + _incognito = _header; + } + } + + public override void Draw(Vector2 size) + { + var color = ColorId.HeaderButtons.Value(); + using var _ = ImGuiColor.Text.Push(color).Push(ImGuiColor.Border, color); + base.Draw(size with { Y = Im.Style.FrameHeight }); + } + + public override ReadOnlySpan Text + => _config.Ephemeral.IncognitoMode ? _incognito : _header; + + private sealed class LockedButton(DesignFileSystem fileSystem, DesignManager manager) : BaseIconButton { public override bool IsVisible - => selection.Design is not null; + => fileSystem.Selection.Selection is not null; public override AwesomeIcon Icon - => selection.Design!.WriteProtected() ? LunaStyle.LockedIcon : LunaStyle.UnlockedIcon; + => ((Design)fileSystem.Selection.Selection!.Value).WriteProtected() ? LunaStyle.LockedIcon : LunaStyle.UnlockedIcon; + + public override bool HasTooltip + => true; + + public override void DrawTooltip() + => Im.Text(((Design)fileSystem.Selection.Selection!.Value).WriteProtected() + ? "Make this design editable."u8 + : "Write-protect this design."u8); + + public override void OnClick() + => manager.SetWriteProtection((Design)fileSystem.Selection.Selection!.Value, + !((Design)fileSystem.Selection.Selection!.Value).WriteProtected()); + } + + public void Dispose() + { + _fileSystem.Selection.Changed -= OnSelectionChanged; + _designChanged.Unsubscribe(OnDesignChanged); } } diff --git a/Glamourer/Gui/Tabs/DesignTab/DesignLinkDrawer.cs b/Glamourer/Gui/Tabs/DesignTab/DesignLinkDrawer.cs index fd3ed75..ee568f7 100644 --- a/Glamourer/Gui/Tabs/DesignTab/DesignLinkDrawer.cs +++ b/Glamourer/Gui/Tabs/DesignTab/DesignLinkDrawer.cs @@ -1,6 +1,6 @@ using Dalamud.Interface; using Glamourer.Automation; -using Glamourer.Configuration; +using Glamourer.Config; using Glamourer.Designs; using Glamourer.Designs.Links; using ImSharp; @@ -10,16 +10,19 @@ namespace Glamourer.Gui.Tabs.DesignTab; public class DesignLinkDrawer( DesignLinkManager linkManager, - DesignFileSystemSelector selector, + DesignFileSystem fileSystem, LinkDesignCombo combo, DesignColors colorManager, - Configuration.Configuration config) : IUiService + Configuration config) : IUiService { private int _dragDropIndex = -1; private LinkOrder _dragDropOrder = LinkOrder.None; private int _dragDropTargetIndex = -1; private LinkOrder _dragDropTargetOrder = LinkOrder.None; + private Design Selected + => (Design)fileSystem.Selection.Selection!.Value; + public void Draw() { using var h = DesignPanelFlag.DesignLinks.Header(config); @@ -45,23 +48,23 @@ public class DesignLinkDrawer( switch (_dragDropTargetOrder) { case LinkOrder.Before: - for (var i = selector.Selected!.Links.Before.Count - 1; i >= _dragDropTargetIndex; --i) - linkManager.MoveDesignLink(selector.Selected!, i, LinkOrder.Before, 0, LinkOrder.After); + for (var i = Selected.Links.Before.Count - 1; i >= _dragDropTargetIndex; --i) + linkManager.MoveDesignLink(Selected, i, LinkOrder.Before, 0, LinkOrder.After); break; case LinkOrder.After: for (var i = 0; i <= _dragDropTargetIndex; ++i) { - linkManager.MoveDesignLink(selector.Selected!, 0, LinkOrder.After, selector.Selected!.Links.Before.Count, + linkManager.MoveDesignLink(Selected, 0, LinkOrder.After, Selected.Links.Before.Count, LinkOrder.Before); } break; } else if (_dragDropTargetOrder is LinkOrder.Self) - linkManager.MoveDesignLink(selector.Selected!, _dragDropIndex, _dragDropOrder, selector.Selected!.Links.Before.Count, + linkManager.MoveDesignLink(Selected, _dragDropIndex, _dragDropOrder, Selected.Links.Before.Count, LinkOrder.Before); else - linkManager.MoveDesignLink(selector.Selected!, _dragDropIndex, _dragDropOrder, _dragDropTargetIndex, _dragDropTargetOrder); + linkManager.MoveDesignLink(Selected, _dragDropIndex, _dragDropOrder, _dragDropTargetIndex, _dragDropTargetOrder); _dragDropIndex = -1; _dragDropTargetIndex = -1; @@ -81,9 +84,9 @@ public class DesignLinkDrawer( 6 * Im.Style.FrameHeight + 5 * Im.Style.ItemInnerSpacing.X); using var style = ImStyleDouble.ItemSpacing.Push(Im.Style.ItemInnerSpacing); - DrawSubList(table, selector.Selected!.Links.Before, LinkOrder.Before); + DrawSubList(table, Selected.Links.Before, LinkOrder.Before); DrawSelf(table); - DrawSubList(table, selector.Selected!.Links.After, LinkOrder.After); + DrawSubList(table, Selected.Links.After, LinkOrder.After); DrawNew(table); MoveLink(); } @@ -92,7 +95,7 @@ public class DesignLinkDrawer( { using var id = Im.Id.Push((int)LinkOrder.Self); table.NextColumn(); - var color = colorManager.GetColor(selector.Selected!); + var color = colorManager.GetColor(Selected); using (AwesomeIcon.Font.Push()) { using var c = ImGuiColor.Text.Push(color); @@ -104,11 +107,11 @@ public class DesignLinkDrawer( using (ImGuiColor.Text.Push(color)) { Im.Cursor.FrameAlign(); - Im.Selectable(config.Ephemeral.IncognitoMode ? selector.Selected!.Incognito : selector.Selected!.Name.Text); + Im.Selectable(config.Ephemeral.IncognitoMode ? Selected.Incognito : Selected.Name.Text); } Im.Tooltip.OnHover("Current Design"u8); - DrawDragDrop(selector.Selected!, LinkOrder.Self, 0); + DrawDragDrop(Selected, LinkOrder.Self, 0); table.NextColumn(); using (AwesomeIcon.Font.Push()) { @@ -144,7 +147,7 @@ public class DesignLinkDrawer( DrawApplicationBoxes(i, order, flags); if (delete) - linkManager.RemoveDesignLink(selector.Selected!, i--, order); + linkManager.RemoveDesignLink(Selected, i--, order); } } @@ -164,11 +167,11 @@ public class DesignLinkDrawer( } else { - canAddBefore = LinkContainer.CanAddLink(selector.Selected!, design, LinkOrder.Before, out var error); + canAddBefore = LinkContainer.CanAddLink(Selected, design, LinkOrder.Before, out var error); ttBefore = canAddBefore ? $"Add a link at the top of the list to {design.Name}." : $"Can not add a link to {design.Name}:\n{error}"; - canAddAfter = LinkContainer.CanAddLink(selector.Selected!, design, LinkOrder.After, out error); + canAddAfter = LinkContainer.CanAddLink(Selected, design, LinkOrder.After, out error); ttAfter = canAddAfter ? $"Add a link at the bottom of the list to {design.Name}." : $"Can not add a link to {design.Name}:\n{error}"; @@ -176,13 +179,13 @@ public class DesignLinkDrawer( if (ImEx.Icon.Button(FontAwesomeIcon.ArrowCircleUp.Icon(), ttBefore, !canAddBefore)) { - linkManager.AddDesignLink(selector.Selected!, design!, LinkOrder.Before); - linkManager.MoveDesignLink(selector.Selected!, selector.Selected!.Links.Before.Count - 1, LinkOrder.Before, 0, LinkOrder.Before); + linkManager.AddDesignLink(Selected, design!, LinkOrder.Before); + linkManager.MoveDesignLink(Selected, Selected.Links.Before.Count - 1, LinkOrder.Before, 0, LinkOrder.Before); } Im.Line.Same(); if (ImEx.Icon.Button(FontAwesomeIcon.ArrowCircleDown.Icon(), ttAfter, !canAddAfter)) - linkManager.AddDesignLink(selector.Selected!, design!, LinkOrder.After); + linkManager.AddDesignLink(Selected, design!, LinkOrder.After); } private void DrawDragDrop(Design design, LinkOrder order, int index) @@ -228,7 +231,7 @@ public class DesignLinkDrawer( Im.Line.Same(); Box(4); if (newType != current) - linkManager.ChangeApplicationType(selector.Selected!, idx, order, newType); + linkManager.ChangeApplicationType(Selected, idx, order, newType); return; void Box(int i) diff --git a/Glamourer/Gui/Tabs/DesignTab/DesignPanel.cs b/Glamourer/Gui/Tabs/DesignTab/DesignPanel.cs index 1a08fae..f04beed 100644 --- a/Glamourer/Gui/Tabs/DesignTab/DesignPanel.cs +++ b/Glamourer/Gui/Tabs/DesignTab/DesignPanel.cs @@ -1,51 +1,40 @@ -using Dalamud.Interface; -using Dalamud.Interface.ImGuiFileDialog; +using Dalamud.Interface.ImGuiFileDialog; using Dalamud.Interface.ImGuiNotification; using FFXIVClientStructs.FFXIV.Client.System.Framework; using Glamourer.Api.Enums; using Glamourer.Automation; +using Glamourer.Config; using Glamourer.Designs; -using Glamourer.Designs.History; using Glamourer.GameData; using Glamourer.Gui.Customization; using Glamourer.Gui.Equipment; using Glamourer.Gui.Materials; using Glamourer.Interop; using Glamourer.State; -using Dalamud.Bindings.ImGui; -using Glamourer.Configuration; using ImSharp; using Luna; -using OtterGui; -using OtterGui.Raii; -using OtterGui.Text; using Penumbra.GameData.Enums; using Penumbra.GameData.Interop; -using static Glamourer.Gui.Tabs.HeaderDrawer; namespace Glamourer.Gui.Tabs.DesignTab; public class DesignPanel : IPanel { - private readonly FileDialogManager _fileDialog = new(); - private readonly DesignSelection _selection; - private readonly CustomizationDrawer _customizationDrawer; - private readonly DesignManager _manager; - private readonly ActorObjectManager _objects; - private readonly StateManager _state; - private readonly EquipmentDrawer _equipmentDrawer; - private readonly ModAssociationsTab _modAssociations; - private readonly Configuration.Configuration _config; - private readonly DesignDetailTab _designDetails; - private readonly ImportService _importService; - private readonly DesignConverter _converter; - private readonly MultiDesignPanel _multiDesignPanel; - private readonly CustomizeParameterDrawer _parameterDrawer; - private readonly DesignLinkDrawer _designLinkDrawer; - private readonly MaterialDrawer _materials; - private readonly EditorHistory _history; - private readonly Button[] _leftButtons; - private readonly Button[] _rightButtons; + private readonly FileDialogManager _fileDialog = new(); + private readonly CustomizationDrawer _customizationDrawer; + private readonly DesignFileSystem _fileSystem; + private readonly DesignManager _manager; + private readonly ActorObjectManager _objects; + private readonly StateManager _state; + private readonly EquipmentDrawer _equipmentDrawer; + private readonly ModAssociationsTab _modAssociations; + private readonly Configuration _config; + private readonly DesignDetailTab _designDetails; + private readonly ImportService _importService; + private readonly MultiDesignPanel _multiDesignPanel; + private readonly CustomizeParameterDrawer _parameterDrawer; + private readonly DesignLinkDrawer _designLinkDrawer; + private readonly MaterialDrawer _materials; public DesignPanel(CustomizationDrawer customizationDrawer, @@ -54,7 +43,7 @@ public class DesignPanel : IPanel StateManager state, EquipmentDrawer equipmentDrawer, ModAssociationsTab modAssociations, - Configuration.Configuration config, + Configuration config, DesignDetailTab designDetails, DesignConverter converter, ImportService importService, @@ -62,7 +51,7 @@ public class DesignPanel : IPanel CustomizeParameterDrawer parameterDrawer, DesignLinkDrawer designLinkDrawer, MaterialDrawer materials, - EditorHistory history, DesignSelection selection) + DesignFileSystem fileSystem) { _customizationDrawer = customizationDrawer; _manager = manager; @@ -73,33 +62,16 @@ public class DesignPanel : IPanel _config = config; _designDetails = designDetails; _importService = importService; - _converter = converter; _multiDesignPanel = multiDesignPanel; _parameterDrawer = parameterDrawer; _designLinkDrawer = designLinkDrawer; _materials = materials; - _history = history; - _selection = selection; - _leftButtons = - [ - new SetFromClipboardButton(this), - new DesignUndoButton(this), - new ExportToClipboardButton(this), - new ApplyCharacterButton(this), - new UndoButton(this), - ]; - _rightButtons = - [ - new LockButton(this), - //new IncognitoButton(_config), - ]; + _fileSystem = fileSystem; } - private void DrawHeader() - => HeaderDrawer.Draw(SelectionName, 0, ImGuiColor.FrameBackground.Get().Color, _leftButtons, _rightButtons); - private string SelectionName - => _selection.Design == null ? "No Selection" : _config.Ephemeral.IncognitoMode ? _selection.Design.Incognito : _selection.Design.Name.Text; + private Design Selection + => (Design)_fileSystem.Selection.Selection!.Value; private void DrawEquipment() { @@ -109,22 +81,22 @@ public class DesignPanel : IPanel _equipmentDrawer.Prepare(); - var usedAllStain = _equipmentDrawer.DrawAllStain(out var newAllStain, _selection.Design!.WriteProtected()); + var usedAllStain = _equipmentDrawer.DrawAllStain(out var newAllStain, Selection.WriteProtected()); foreach (var slot in EquipSlotExtensions.EqdpSlots) { - var data = EquipDrawData.FromDesign(_manager, _selection.Design!, slot); + var data = EquipDrawData.FromDesign(_manager, Selection, slot); _equipmentDrawer.DrawEquip(data); if (usedAllStain) - _manager.ChangeStains(_selection.Design, slot, newAllStain); + _manager.ChangeStains(Selection, slot, newAllStain); } - var mainhand = EquipDrawData.FromDesign(_manager, _selection.Design!, EquipSlot.MainHand); - var offhand = EquipDrawData.FromDesign(_manager, _selection.Design!, EquipSlot.OffHand); + var mainhand = EquipDrawData.FromDesign(_manager, Selection, EquipSlot.MainHand); + var offhand = EquipDrawData.FromDesign(_manager, Selection, EquipSlot.OffHand); _equipmentDrawer.DrawWeapons(mainhand, offhand, true); foreach (var slot in BonusExtensions.AllFlags) { - var data = BonusDrawData.FromDesign(_manager, _selection.Design!, slot); + var data = BonusDrawData.FromDesign(_manager, Selection, slot); _equipmentDrawer.DrawBonusItem(data); } @@ -136,30 +108,30 @@ public class DesignPanel : IPanel private void DrawEquipmentMetaToggles() { - using (var _ = ImRaii.Group()) + using (Im.Group()) { - EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(MetaIndex.HatState, _manager, _selection.Design!)); - EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromDesign(CrestFlag.Head, _manager, _selection.Design!)); + EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(MetaIndex.HatState, _manager, Selection)); + EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromDesign(CrestFlag.Head, _manager, Selection)); } Im.Line.Same(); - using (var _ = ImRaii.Group()) + using (Im.Group()) { - EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(MetaIndex.VisorState, _manager, _selection.Design!)); - EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromDesign(CrestFlag.Body, _manager, _selection.Design!)); + EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(MetaIndex.VisorState, _manager, Selection)); + EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromDesign(CrestFlag.Body, _manager, Selection)); } Im.Line.Same(); - using (var _ = ImRaii.Group()) + using (Im.Group()) { - EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(MetaIndex.WeaponState, _manager, _selection.Design!)); - EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromDesign(CrestFlag.OffHand, _manager, _selection.Design!)); + EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(MetaIndex.WeaponState, _manager, Selection)); + EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromDesign(CrestFlag.OffHand, _manager, Selection)); } Im.Line.Same(); - using (var _ = ImRaii.Group()) + using (Im.Group()) { - EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(MetaIndex.EarState, _manager, _selection.Design!)); + EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(MetaIndex.EarState, _manager, Selection)); } } @@ -169,25 +141,25 @@ public class DesignPanel : IPanel return; var expand = _config.AutoExpandDesignPanel.HasFlag(DesignPanelFlag.Customization); - using var h = Im.Tree.HeaderId(_selection.Design!.DesignData.ModelId is 0 - ? "Customization" - : $"Customization (Model Id #{_selection.Design!.DesignData.ModelId})###Customization", + using var h = Im.Tree.HeaderId(Selection.DesignData.ModelId is 0 + ? "Customization"u8 + : $"Customization (Model Id #{Selection.DesignData.ModelId})###Customization", expand ? TreeNodeFlags.DefaultOpen : TreeNodeFlags.None); if (!h) return; - if (_customizationDrawer.Draw(_selection.Design!.DesignData.Customize, _selection.Design.Application.Customize, - _selection.Design!.WriteProtected(), false)) + if (_customizationDrawer.Draw(Selection.DesignData.Customize, Selection.Application.Customize, + Selection.WriteProtected(), false)) foreach (var idx in CustomizeIndex.Values) { var flag = idx.ToFlag(); var newValue = _customizationDrawer.ChangeApply.HasFlag(flag); - _manager.ChangeApplyCustomize(_selection.Design, idx, newValue); + _manager.ChangeApplyCustomize(Selection, idx, newValue); if (_customizationDrawer.Changed.HasFlag(flag)) - _manager.ChangeCustomize(_selection.Design, idx, _customizationDrawer.Customize[idx]); + _manager.ChangeCustomize(Selection, idx, _customizationDrawer.Customize[idx]); } - EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(MetaIndex.Wetness, _manager, _selection.Design!)); + EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(MetaIndex.Wetness, _manager, Selection)); Im.Dummy(new Vector2(Im.Style.TextHeight / 2)); } @@ -197,7 +169,7 @@ public class DesignPanel : IPanel if (!h) return; - _parameterDrawer.Draw(_manager, _selection.Design!); + _parameterDrawer.Draw(_manager, Selection); } private void DrawMaterialValues() @@ -206,52 +178,52 @@ public class DesignPanel : IPanel if (!h) return; - _materials.Draw(_selection.Design!); + _materials.Draw(Selection); } private void DrawCustomizeApplication() { - using var id = ImUtf8.PushId("Customizations"u8); - var set = _selection.Design!.CustomizeSet; + using var id = Im.Id.Push("Customizations"u8); + var set = Selection.CustomizeSet; var available = set.SettingAvailable | CustomizeFlag.Clan | CustomizeFlag.Gender | CustomizeFlag.BodyType; - var flags = _selection.Design!.ApplyCustomizeExcludingBodyType == 0 ? 0 : - (_selection.Design!.ApplyCustomize & available) == available ? 3 : 1; - if (ImGui.CheckboxFlags("Apply All Customizations", ref flags, 3)) + var flags = Selection.ApplyCustomizeExcludingBodyType is 0 ? 0ul : + (Selection.ApplyCustomize & available) == available ? 3ul : 1ul; + if (Im.Checkbox("Apply All Customizations"u8, ref flags, 3ul)) { - var newFlags = flags == 3; - _manager.ChangeApplyCustomize(_selection.Design!, CustomizeIndex.Clan, newFlags); - _manager.ChangeApplyCustomize(_selection.Design!, CustomizeIndex.Gender, newFlags); + var newFlags = flags is 3; + _manager.ChangeApplyCustomize(Selection, CustomizeIndex.Clan, newFlags); + _manager.ChangeApplyCustomize(Selection, CustomizeIndex.Gender, newFlags); foreach (var index in CustomizationExtensions.AllBasic) - _manager.ChangeApplyCustomize(_selection.Design!, index, newFlags); + _manager.ChangeApplyCustomize(Selection, index, newFlags); } - var applyClan = _selection.Design!.DoApplyCustomize(CustomizeIndex.Clan); - if (ImUtf8.Checkbox($"Apply {CustomizeIndex.Clan.ToNameU8()}", ref applyClan)) - _manager.ChangeApplyCustomize(_selection.Design!, CustomizeIndex.Clan, applyClan); + var applyClan = Selection.DoApplyCustomize(CustomizeIndex.Clan); + if (Im.Checkbox($"Apply {CustomizeIndex.Clan.ToNameU8()}", ref applyClan)) + _manager.ChangeApplyCustomize(Selection, CustomizeIndex.Clan, applyClan); - var applyGender = _selection.Design!.DoApplyCustomize(CustomizeIndex.Gender); - if (ImUtf8.Checkbox($"Apply {CustomizeIndex.Gender.ToNameU8()}", ref applyGender)) - _manager.ChangeApplyCustomize(_selection.Design!, CustomizeIndex.Gender, applyGender); + var applyGender = Selection.DoApplyCustomize(CustomizeIndex.Gender); + if (Im.Checkbox($"Apply {CustomizeIndex.Gender.ToNameU8()}", ref applyGender)) + _manager.ChangeApplyCustomize(Selection, CustomizeIndex.Gender, applyGender); foreach (var index in CustomizationExtensions.All.Where(set.IsAvailable)) { - var apply = _selection.Design!.DoApplyCustomize(index); - if (ImUtf8.Checkbox($"Apply {set.Option(index)}", ref apply)) - _manager.ChangeApplyCustomize(_selection.Design!, index, apply); + var apply = Selection.DoApplyCustomize(index); + if (Im.Checkbox($"Apply {set.Option(index)}", ref apply)) + _manager.ChangeApplyCustomize(Selection, index, apply); } } private void DrawCrestApplication() { - using var id = ImUtf8.PushId("Crests"u8); - var flags = (uint)_selection.Design!.Application.Crest; - var bigChange = ImGui.CheckboxFlags("Apply All Crests", ref flags, (uint)CrestExtensions.AllRelevant); + using var id = Im.Id.Push("Crests"u8); + var flags = (ulong)Selection.Application.Crest; + var bigChange = Im.Checkbox("Apply All Crests"u8, ref flags, (ulong)CrestExtensions.AllRelevant); foreach (var flag in CrestExtensions.AllRelevantSet) { - var apply = bigChange ? ((CrestFlag)flags & flag) == flag : _selection.Design!.DoApplyCrest(flag); - if (ImUtf8.Checkbox($"Apply {flag.ToLabel()} Crest", ref apply) || bigChange) - _manager.ChangeApplyCrest(_selection.Design!, flag, apply); + var apply = bigChange ? ((CrestFlag)flags & flag) == flag : Selection.DoApplyCrest(flag); + if (Im.Checkbox($"Apply {flag.ToLabel()} Crest", ref apply) || bigChange) + _manager.ChangeApplyCrest(Selection, flag, apply); } } @@ -261,63 +233,59 @@ public class DesignPanel : IPanel if (!h) return; - using var disabled = Im.Disabled(_selection.Design!.WriteProtected()); + using var disabled = Im.Disabled(Selection.WriteProtected()); DrawAllButtons(); - using (var _ = ImUtf8.Group()) + using (Im.Group()) { DrawCustomizeApplication(); - ImUtf8.IconDummy(); + Im.FrameDummy(); DrawCrestApplication(); - ImUtf8.IconDummy(); + Im.FrameDummy(); DrawMetaApplication(); } - ImGui.SameLine(210 * Im.Style.GlobalScale + Im.Style.ItemSpacing.X); - using (var _ = ImRaii.Group()) + Im.Line.Same(210 * Im.Style.GlobalScale + Im.Style.ItemSpacing.X); + using (Im.Group()) { void ApplyEquip(string label, EquipFlag allFlags, bool stain, IEnumerable slots) { - var flags = (uint)(allFlags & _selection.Design!.Application.Equip); - using var id = ImUtf8.PushId(label); - var bigChange = ImGui.CheckboxFlags($"Apply All {label}", ref flags, (uint)allFlags); + var flags = (ulong)(allFlags & Selection.Application.Equip); + using var id = Im.Id.Push(label); + var bigChange = Im.Checkbox($"Apply All {label}", ref flags, (ulong)allFlags); if (stain) foreach (var slot in slots) { - var apply = bigChange ? ((EquipFlag)flags).HasFlag(slot.ToStainFlag()) : _selection.Design!.DoApplyStain(slot); - if (ImUtf8.Checkbox($"Apply {slot.ToName()} Dye", ref apply) || bigChange) - _manager.ChangeApplyStains(_selection.Design!, slot, apply); + var apply = bigChange ? ((EquipFlag)flags).HasFlag(slot.ToStainFlag()) : Selection.DoApplyStain(slot); + if (Im.Checkbox($"Apply {slot.ToName()} Dye", ref apply) || bigChange) + _manager.ChangeApplyStains(Selection, slot, apply); } else foreach (var slot in slots) { - var apply = bigChange ? ((EquipFlag)flags).HasFlag(slot.ToFlag()) : _selection.Design!.DoApplyEquip(slot); - if (ImUtf8.Checkbox($"Apply {slot.ToName()}", ref apply) || bigChange) - _manager.ChangeApplyItem(_selection.Design!, slot, apply); + var apply = bigChange ? ((EquipFlag)flags).HasFlag(slot.ToFlag()) : Selection.DoApplyEquip(slot); + if (Im.Checkbox($"Apply {slot.ToName()}", ref apply) || bigChange) + _manager.ChangeApplyItem(Selection, slot, apply); } } - ApplyEquip("Weapons", ApplicationTypeExtensions.WeaponFlags, false, new[] - { - EquipSlot.MainHand, - EquipSlot.OffHand, - }); + ApplyEquip("Weapons", ApplicationTypeExtensions.WeaponFlags, false, [EquipSlot.MainHand, EquipSlot.OffHand]); - ImUtf8.IconDummy(); + Im.FrameDummy(); ApplyEquip("Armor", ApplicationTypeExtensions.ArmorFlags, false, EquipSlotExtensions.EquipmentSlots); - ImUtf8.IconDummy(); + Im.FrameDummy(); ApplyEquip("Accessories", ApplicationTypeExtensions.AccessoryFlags, false, EquipSlotExtensions.AccessorySlots); - ImUtf8.IconDummy(); + Im.FrameDummy(); ApplyEquip("Dyes", ApplicationTypeExtensions.StainFlags, true, EquipSlotExtensions.FullSlots); - ImUtf8.IconDummy(); + Im.FrameDummy(); DrawParameterApplication(); - ImUtf8.IconDummy(); + Im.FrameDummy(); DrawBonusSlotApplication(); } } @@ -327,9 +295,9 @@ public class DesignPanel : IPanel var enabled = _config.DeleteDesignModifier.IsActive(); bool? equip = null; bool? customize = null; - var size = new Vector2(210 * Im.Style.GlobalScale, 0); - if (ImUtf8.ButtonEx("Disable Everything"u8, - "Disable application of everything, including any existing advanced dyes, advanced customizations, crests and wetness."u8, size, + var size = ImEx.ScaledVectorX(210); + if (ImEx.Button("Disable Everything"u8, size, + "Disable application of everything, including any existing advanced dyes, advanced customizations, crests and wetness."u8, !enabled)) { equip = false; @@ -337,11 +305,11 @@ public class DesignPanel : IPanel } if (!enabled) - ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking."); + Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking."); Im.Line.Same(); - if (ImUtf8.ButtonEx("Enable Everything"u8, - "Enable application of everything, including any existing advanced dyes, advanced customizations, crests and wetness."u8, size, + if (ImEx.Button("Enable Everything"u8, size, + "Enable application of everything, including any existing advanced dyes, advanced customizations, crests and wetness."u8, !enabled)) { equip = true; @@ -349,10 +317,10 @@ public class DesignPanel : IPanel } if (!enabled) - ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking."); + Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking."); - if (ImUtf8.ButtonEx("Equipment Only"u8, - "Enable application of anything related to gear, disable anything that is not related to gear."u8, size, + if (ImEx.Button("Equipment Only"u8, size, + "Enable application of anything related to gear, disable anything that is not related to gear."u8, !enabled)) { equip = true; @@ -360,11 +328,11 @@ public class DesignPanel : IPanel } if (!enabled) - ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking."); + Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking."); Im.Line.Same(); - if (ImUtf8.ButtonEx("Customization Only"u8, - "Enable application of anything related to customization, disable anything that is not related to customization."u8, size, + if (ImEx.Button("Customization Only"u8, size, + "Enable application of anything related to customization, disable anything that is not related to customization."u8, !enabled)) { equip = false; @@ -372,85 +340,82 @@ public class DesignPanel : IPanel } if (!enabled) - ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking."); + Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking."); - if (ImUtf8.ButtonEx("Default Application"u8, + if (ImEx.Button("Default Application"u8, size, "Set the application rules to the default values as if the design was newly created, without any advanced features or wetness."u8, - size, !enabled)) { - _manager.ChangeApplyMulti(_selection.Design!, true, true, true, false, true, true, false, true); - _manager.ChangeApplyMeta(_selection.Design!, MetaIndex.Wetness, false); + _manager.ChangeApplyMulti(Selection, true, true, true, false, true, true, false, true); + _manager.ChangeApplyMeta(Selection, MetaIndex.Wetness, false); } if (!enabled) - ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking."); + Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking."); Im.Line.Same(); - if (ImUtf8.ButtonEx("Disable Advanced"u8, "Disable all advanced dyes and customizations but keep everything else as is."u8, - size, - !enabled)) - _manager.ChangeApplyMulti(_selection.Design!, null, null, null, false, null, null, false, null); + if (ImEx.Button("Disable Advanced"u8, size, "Disable all advanced dyes and customizations but keep everything else as is."u8, !enabled)) + _manager.ChangeApplyMulti(Selection, null, null, null, false, null, null, false, null); if (!enabled) - ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking."); + Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking."); if (equip is null && customize is null) return; - _manager.ChangeApplyMulti(_selection.Design!, equip, customize, equip, customize.HasValue && !customize.Value ? false : null, null, + _manager.ChangeApplyMulti(Selection, equip, customize, equip, customize.HasValue && !customize.Value ? false : null, null, equip, equip, equip); if (equip.HasValue) { - _manager.ChangeApplyMeta(_selection.Design!, MetaIndex.HatState, equip.Value); - _manager.ChangeApplyMeta(_selection.Design!, MetaIndex.VisorState, equip.Value); - _manager.ChangeApplyMeta(_selection.Design!, MetaIndex.WeaponState, equip.Value); - _manager.ChangeApplyMeta(_selection.Design!, MetaIndex.EarState, equip.Value); + _manager.ChangeApplyMeta(Selection, MetaIndex.HatState, equip.Value); + _manager.ChangeApplyMeta(Selection, MetaIndex.VisorState, equip.Value); + _manager.ChangeApplyMeta(Selection, MetaIndex.WeaponState, equip.Value); + _manager.ChangeApplyMeta(Selection, MetaIndex.EarState, equip.Value); } if (customize.HasValue) - _manager.ChangeApplyMeta(_selection.Design!, MetaIndex.Wetness, customize.Value); + _manager.ChangeApplyMeta(Selection, MetaIndex.Wetness, customize.Value); } - private static readonly IReadOnlyList MetaLabels = + private static readonly IReadOnlyList MetaLabels = [ - "Apply Wetness", - "Apply Hat Visibility", - "Apply Visor State", - "Apply Weapon Visibility", - "Apply Viera Ear Visibility", + new("Apply Wetness"u8), + new("Apply Hat Visibility"u8), + new("Apply Visor State"u8), + new("Apply Weapon Visibility"u8), + new("Apply Viera Ear Visibility"u8), ]; private void DrawMetaApplication() { - using var id = ImUtf8.PushId("Meta"); - const uint all = (uint)MetaExtensions.All; - var flags = (uint)_selection.Design!.Application.Meta; - var bigChange = ImGui.CheckboxFlags("Apply All Meta Changes", ref flags, all); + using var id = Im.Id.Push("Meta"u8); + const ulong all = (ulong)MetaExtensions.All; + var flags = (ulong)Selection.Application.Meta; + var bigChange = Im.Checkbox("Apply All Meta Changes"u8, ref flags, all); foreach (var (index, label) in MetaExtensions.AllRelevant.Zip(MetaLabels)) { - var apply = bigChange ? ((MetaFlag)flags).HasFlag(index.ToFlag()) : _selection.Design!.DoApplyMeta(index); - if (ImUtf8.Checkbox(label, ref apply) || bigChange) - _manager.ChangeApplyMeta(_selection.Design!, index, apply); + var apply = bigChange ? ((MetaFlag)flags).HasFlag(index.ToFlag()) : Selection.DoApplyMeta(index); + if (Im.Checkbox(label, ref apply) || bigChange) + _manager.ChangeApplyMeta(Selection, index, apply); } } - private static readonly IReadOnlyList BonusSlotLabels = + private static readonly IReadOnlyList BonusSlotLabels = [ - "Apply Facewear", + new("Apply Facewear"u8), ]; private void DrawBonusSlotApplication() { - using var id = ImUtf8.PushId("Bonus"u8); - var flags = _selection.Design!.Application.BonusItem; - var bigChange = BonusExtensions.AllFlags.Count > 1 && ImUtf8.Checkbox("Apply All Bonus Slots"u8, ref flags, BonusExtensions.All); + using var id = Im.Id.Push("Bonus"u8); + var flags = Selection.Application.BonusItem; + var bigChange = BonusExtensions.AllFlags.Count > 1 && Im.Checkbox("Apply All Bonus Slots"u8, ref flags, BonusExtensions.All); foreach (var (index, label) in BonusExtensions.AllFlags.Zip(BonusSlotLabels)) { - var apply = bigChange ? flags.HasFlag(index) : _selection.Design!.DoApplyBonusItem(index); - if (ImUtf8.Checkbox(label, ref apply) || bigChange) - _manager.ChangeApplyBonusItem(_selection.Design!, index, apply); + var apply = bigChange ? flags.HasFlag(index) : Selection.DoApplyBonusItem(index); + if (Im.Checkbox(label, ref apply) || bigChange) + _manager.ChangeApplyBonusItem(Selection, index, apply); } } @@ -458,69 +423,62 @@ public class DesignPanel : IPanel private void DrawParameterApplication() { using var id = Im.Id.Push("Parameter"u8); - var flags = (ulong)_selection.Design!.Application.Parameters; + var flags = (ulong)Selection.Application.Parameters; var bigChange = Im.Checkbox("Apply All Customize Parameters"u8, ref flags, (ulong)CustomizeParameterExtensions.All); foreach (var flag in CustomizeParameterExtensions.AllFlags) { - var apply = bigChange ? ((CustomizeParameterFlag)flags).HasFlag(flag) : _selection.Design!.DoApplyParameter(flag); + var apply = bigChange ? ((CustomizeParameterFlag)flags).HasFlag(flag) : Selection.DoApplyParameter(flag); if (Im.Checkbox($"Apply {flag.ToNameU8()}", ref apply) || bigChange) - _manager.ChangeApplyParameter(_selection.Design!, flag, apply); + _manager.ChangeApplyParameter(Selection, flag, apply); } } public ReadOnlySpan Id - => "Designs"u8; + => "DesignPanel"u8; public void Draw() { - using var group = ImUtf8.Group(); - //if (_selection.DesignPaths.Count > 1) - if (false) + _importService.CreateDatSource(); + if (_fileSystem.Selection.OrderedNodes.Count > 1) { _multiDesignPanel.Draw(); + return; } - else + + DrawPanel(); + + if (_fileSystem.Selection.Selection is null || Selection.WriteProtected()) + return; + + if (_importService.CreateDatTarget(out var dat)) { - DrawHeader(); - DrawPanel(); - - if (_selection.Design == null || _selection.Design.WriteProtected()) - return; - - if (_importService.CreateDatTarget(out var dat)) - { - _manager.ChangeCustomize(_selection.Design!, CustomizeIndex.Clan, dat.Customize[CustomizeIndex.Clan]); - _manager.ChangeCustomize(_selection.Design!, CustomizeIndex.Gender, dat.Customize[CustomizeIndex.Gender]); - foreach (var idx in CustomizationExtensions.AllBasic) - _manager.ChangeCustomize(_selection.Design!, idx, dat.Customize[idx]); - Glamourer.Messager.NotificationMessage( - $"Applied games .dat file {dat.Description} customizations to {_selection.Design.Name}.", NotificationType.Success, false); - } - else if (_importService.CreateCharaTarget(out var designBase, out var name)) - { - _manager.ApplyDesign(_selection.Design!, designBase); - Glamourer.Messager.NotificationMessage($"Applied Anamnesis .chara file {name} to {_selection.Design.Name}.", - NotificationType.Success, false); - } + _manager.ChangeCustomize(Selection, CustomizeIndex.Clan, dat.Customize[CustomizeIndex.Clan]); + _manager.ChangeCustomize(Selection, CustomizeIndex.Gender, dat.Customize[CustomizeIndex.Gender]); + foreach (var idx in CustomizationExtensions.AllBasic) + _manager.ChangeCustomize(Selection, idx, dat.Customize[idx]); + Glamourer.Messager.NotificationMessage( + $"Applied games .dat file {dat.Description} customizations to {Selection.Name}.", NotificationType.Success, false); + } + else if (_importService.CreateCharaTarget(out var designBase, out var name)) + { + _manager.ApplyDesign(Selection, designBase); + Glamourer.Messager.NotificationMessage($"Applied Anamnesis .chara file {name} to {Selection.Name}.", + NotificationType.Success, false); } - - _importService.CreateDatSource(); } private void DrawPanel() { - using var table = Im.Table.Begin("##Panel"u8, 1, TableFlags.BordersOuter | TableFlags.ScrollY, Im.ContentRegion.Available); - if (!table || _selection.Design is null) + using var table = Im.Table.Begin("##Panel"u8, 1, TableFlags.ScrollY, Im.ContentRegion.Available); + if (!table || _fileSystem.Selection.Selection is null) return; - ImGui.TableSetupScrollFreeze(0, 1); - ImGui.TableNextColumn(); - if (_selection.Design is null) - return; + table.SetupScrollFreeze(0, 1); + table.NextColumn(); Im.Dummy(Vector2.Zero); DrawButtonRow(); - ImGui.TableNextColumn(); + table.NextColumn(); DrawCustomize(); DrawEquipment(); @@ -547,15 +505,15 @@ public class DesignPanel : IPanel private void DrawApplyToSelf() { var (id, data) = _objects.PlayerData; - if (!ImGuiUtil.DrawDisabledButton("Apply to Yourself", Vector2.Zero, - "Apply the current design with its settings to your character.\nHold Control to only apply gear.\nHold Shift to only apply customizations.", + if (!ImEx.Button("Apply to Yourself"u8, Vector2.Zero, + "Apply the current design with its settings to your character.\nHold Control to only apply gear.\nHold Shift to only apply customizations."u8, !data.Valid)) return; if (_state.GetOrCreate(id, data.Objects[0], out var state)) { - using var _ = _selection.Design!.TemporarilyRestrictApplication(ApplicationCollection.FromKeys()); - _state.ApplyDesign(state, _selection.Design!, ApplySettings.ManualWithLinks with { IsFinal = true }); + using var _ = Selection.TemporarilyRestrictApplication(ApplicationCollection.FromKeys()); + _state.ApplyDesign(state, Selection, ApplySettings.ManualWithLinks with { IsFinal = true }); } } @@ -564,33 +522,33 @@ public class DesignPanel : IPanel var (id, data) = _objects.TargetData; var tt = id.IsValid ? data.Valid - ? "Apply the current design with its settings to your current target.\nHold Control to only apply gear.\nHold Shift to only apply customizations." - : "The current target can not be manipulated." - : "No valid target selected."; - if (!ImGuiUtil.DrawDisabledButton("Apply to Target", Vector2.Zero, tt, !data.Valid)) + ? "Apply the current design with its settings to your current target.\nHold Control to only apply gear.\nHold Shift to only apply customizations."u8 + : "The current target can not be manipulated."u8 + : "No valid target selected."u8; + if (!ImEx.Button("Apply to Target"u8, Vector2.Zero, tt, !data.Valid)) return; if (_state.GetOrCreate(id, data.Objects[0], out var state)) { - using var _ = _selection.Design!.TemporarilyRestrictApplication(ApplicationCollection.FromKeys()); - _state.ApplyDesign(state, _selection.Design!, ApplySettings.ManualWithLinks with { IsFinal = true }); + using var _ = Selection.TemporarilyRestrictApplication(ApplicationCollection.FromKeys()); + _state.ApplyDesign(state, Selection, ApplySettings.ManualWithLinks with { IsFinal = true }); } } private void DrawSaveToDat() { - var verified = _importService.Verify(_selection.Design!.DesignData.Customize, out _); + var verified = _importService.Verify(Selection.DesignData.Customize, out _); var tt = verified - ? "Export the currently configured customizations of this design to a character creation data file." - : "The current design contains customizations that can not be applied during character creation."; + ? "Export the currently configured customizations of this design to a character creation data file."u8 + : "The current design contains customizations that can not be applied during character creation."u8; var startPath = GetUserPath(); - if (startPath.Length == 0) + if (startPath.Length is 0) startPath = null; - if (ImGuiUtil.DrawDisabledButton("Export to Dat", Vector2.Zero, tt, !verified)) + if (ImEx.Button("Export to Dat"u8, Vector2.Zero, tt, !verified)) _fileDialog.SaveFileDialog("Save File...", ".dat", "FFXIV_CHARA_01.dat", ".dat", (v, path) => { - if (v && _selection.Design != null) - _importService.SaveDesignAsDat(path, _selection.Design!.DesignData.Customize, _selection.Design!.Name); + if (v && _fileSystem.Selection.Selection?.GetValue() is not null) + _importService.SaveDesignAsDat(path, Selection.DesignData.Customize, Selection.Name); }, startPath); _fileDialog.Draw(); @@ -598,165 +556,4 @@ public class DesignPanel : IPanel private static unsafe string GetUserPath() => Framework.Instance()->UserPathString; - - - - private sealed class LockButton(DesignPanel panel) : Button - { - public override bool Visible - => panel._selection.Design != null; - - protected override string Description - => panel._selection.Design!.WriteProtected() - ? "Make this design editable." - : "Write-protect this design."; - - protected override FontAwesomeIcon Icon - => panel._selection.Design!.WriteProtected() - ? FontAwesomeIcon.Lock - : FontAwesomeIcon.LockOpen; - - protected override void OnClick() - => panel._manager.SetWriteProtection(panel._selection.Design!, !panel._selection.Design!.WriteProtected()); - } - - private sealed class SetFromClipboardButton(DesignPanel panel) : Button - { - public override bool Visible - => panel._selection.Design != null; - - protected override bool Disabled - => panel._selection.Design?.WriteProtected() ?? true; - - protected override string Description - => "Try to apply a design from your clipboard over this design.\nHold Control to only apply gear.\nHold Shift to only apply customizations."; - - protected override FontAwesomeIcon Icon - => FontAwesomeIcon.Clipboard; - - protected override void OnClick() - { - try - { - var text = ImGui.GetClipboardText(); - var (applyEquip, applyCustomize) = UiHelpers.ConvertKeysToBool(); - var design = panel._converter.FromBase64(text, applyCustomize, applyEquip, out _) - ?? throw new Exception("The clipboard did not contain valid data."); - panel._manager.ApplyDesign(panel._selection.Design!, design); - } - catch (Exception ex) - { - Glamourer.Messager.NotificationMessage(ex, $"Could not apply clipboard to {panel._selection.Design!.Name}.", - $"Could not apply clipboard to design {panel._selection.Design!.Identifier}", NotificationType.Error, false); - } - } - } - - private sealed class DesignUndoButton(DesignPanel panel) : Button - { - public override bool Visible - => panel._selection.Design != null; - - protected override bool Disabled - => !panel._manager.CanUndo(panel._selection.Design) || (panel._selection.Design?.WriteProtected() ?? true); - - protected override string Description - => "Undo the last time you applied an entire design onto this design, if you accidentally overwrote your design with a different one."; - - protected override FontAwesomeIcon Icon - => FontAwesomeIcon.SyncAlt; - - protected override void OnClick() - { - try - { - panel._manager.UndoDesignChange(panel._selection.Design!); - } - catch (Exception ex) - { - Glamourer.Messager.NotificationMessage(ex, $"Could not undo last changes to {panel._selection.Design!.Name}.", - NotificationType.Error, - false); - } - } - } - - private sealed class ExportToClipboardButton(DesignPanel panel) : Button - { - public override bool Visible - => panel._selection.Design != null; - - protected override string Description - => "Copy the current design to your clipboard."; - - protected override FontAwesomeIcon Icon - => FontAwesomeIcon.Copy; - - protected override void OnClick() - { - try - { - var text = panel._converter.ShareBase64(panel._selection.Design!); - ImGui.SetClipboardText(text); - } - catch (Exception ex) - { - Glamourer.Messager.NotificationMessage(ex, $"Could not copy {panel._selection.Design!.Name} data to clipboard.", - $"Could not copy data from design {panel._selection.Design!.Identifier} to clipboard", NotificationType.Error, false); - } - } - } - - private sealed class ApplyCharacterButton(DesignPanel panel) : Button - { - public override bool Visible - => panel._selection.Design != null && panel._objects.Player.Valid; - - protected override string Description - => "Overwrite this design with your character's current state."; - - protected override bool Disabled - => panel._selection.Design?.WriteProtected() ?? true; - - protected override FontAwesomeIcon Icon - => FontAwesomeIcon.UserEdit; - - protected override void OnClick() - { - try - { - var (player, actor) = panel._objects.PlayerData; - if (!player.IsValid || !actor.Valid || !panel._state.GetOrCreate(player, actor.Objects[0], out var state)) - throw new Exception("No player state available."); - - var design = panel._converter.Convert(state, ApplicationRules.FromModifiers(state)) - ?? throw new Exception("The clipboard did not contain valid data."); - panel._selection.Design!.GetMaterialDataRef().Clear(); - panel._manager.ApplyDesign(panel._selection.Design!, design); - } - catch (Exception ex) - { - Glamourer.Messager.NotificationMessage(ex, $"Could not apply player state to {panel._selection.Design!.Name}.", - $"Could not apply player state to design {panel._selection.Design!.Identifier}", NotificationType.Error, false); - } - } - } - - private sealed class UndoButton(DesignPanel panel) : Button - { - protected override string Description - => "Undo the last change."; - - protected override FontAwesomeIcon Icon - => FontAwesomeIcon.Undo; - - public override bool Visible - => panel._selection.Design != null; - - protected override bool Disabled - => (panel._selection.Design?.WriteProtected() ?? true) || !panel._history.CanUndo(panel._selection.Design); - - protected override void OnClick() - => panel._history.Undo(panel._selection.Design!); - } } diff --git a/Glamourer/Gui/Tabs/DesignTab/DesignSelection.cs b/Glamourer/Gui/Tabs/DesignTab/DesignSelection.cs deleted file mode 100644 index f070208..0000000 --- a/Glamourer/Gui/Tabs/DesignTab/DesignSelection.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Glamourer.Designs; -using Luna; - -namespace Glamourer.Gui.Tabs.DesignTab; - -public sealed class DesignSelection : IUiService, IDisposable -{ - public Design? Design { get; private set; } - - public void Dispose() - { } -} diff --git a/Glamourer/Gui/Tabs/DesignTab/DesignTab.cs b/Glamourer/Gui/Tabs/DesignTab/DesignTab.cs index 4bef23b..839c392 100644 --- a/Glamourer/Gui/Tabs/DesignTab/DesignTab.cs +++ b/Glamourer/Gui/Tabs/DesignTab/DesignTab.cs @@ -1,5 +1,5 @@ using Dalamud.Interface.ImGuiNotification; -using Glamourer.Configuration; +using Glamourer.Config; using Glamourer.Designs; using Glamourer.Interop; using ImSharp; @@ -7,36 +7,55 @@ using Luna; namespace Glamourer.Gui.Tabs.DesignTab; -public sealed class DesignTab(DesignFileSystemSelector selector, DesignPanel panel, ImportService importService, DesignManager manager) - : ITab +public sealed class DesignTab : TwoPanelLayout, ITab { - public ReadOnlySpan Label + private readonly ImportService _importService; + private readonly DesignManager _manager; + private readonly UiConfig _uiConfig; + + public DesignTab(DesignFileSystemDrawer drawer, DesignPanel panel, ImportService importService, DesignManager manager, DesignFilter filter, + DesignHeader header, UiConfig uiConfig) + { + LeftHeader = drawer.Header; + LeftPanel = drawer; + LeftFooter = drawer.Footer; + + RightHeader = header; + RightPanel = panel; + RightFooter = NopHeaderFooter.Instance; + _importService = importService; + _manager = manager; + _uiConfig = uiConfig; + } + + public override ReadOnlySpan Label => "Designs"u8; public MainTabType Identifier => MainTabType.Designs; - public void DrawContent() + protected override void DrawLeftGroup(in TwoPanelWidth width) { - selector.Draw(); - if (importService.CreateCharaTarget(out var designBase, out var name)) + base.DrawLeftGroup(in width); + if (_importService.CreateCharaTarget(out var designBase, out var name)) { - var newDesign = manager.CreateClone(designBase, name, true); + var newDesign = _manager.CreateClone(designBase, name, true); Glamourer.Messager.NotificationMessage($"Imported Anamnesis .chara file {name} as new design {newDesign.Name}", NotificationType.Success, false); } - - Im.Line.Same(); - panel.Draw(); - importService.CreateCharaSource(); + _importService.CreateCharaSource(); } - //protected override void SetWidth(float width, ScalingMode mode) - // => _uiConfig.ActorsTabScale = new TwoPanelWidth(width, mode); - // - //protected override float MinimumWidth - // => LeftFooter.MinimumWidth; - // - //protected override float MaximumWidth - // => Im.Window.Width - 500 * Im.Style.GlobalScale; + protected override float MinimumWidth + => LeftFooter.MinimumWidth; + + protected override float MaximumWidth + => Im.Window.Width - 500 * Im.Style.GlobalScale; + + protected override void SetWidth(float width, ScalingMode mode) + => _uiConfig.DesignsTabScale = new TwoPanelWidth(width, mode); + + + public void DrawContent() + => Draw(_uiConfig.DesignsTabScale); } diff --git a/Glamourer/Gui/Tabs/DesignTab/DesignUndoButton.cs b/Glamourer/Gui/Tabs/DesignTab/DesignUndoButton.cs new file mode 100644 index 0000000..4242b0a --- /dev/null +++ b/Glamourer/Gui/Tabs/DesignTab/DesignUndoButton.cs @@ -0,0 +1,39 @@ +using Dalamud.Interface.ImGuiNotification; +using Glamourer.Designs; +using ImSharp; +using Luna; + +namespace Glamourer.Gui.Tabs.DesignTab; + +public sealed class DesignUndoButton(DesignFileSystem fileSystem, DesignManager manager) : BaseIconButton +{ + public override bool IsVisible + => fileSystem.Selection.Selection is not null; + + public override AwesomeIcon Icon + => LunaStyle.ResetIcon; + + public override bool Enabled + => !((Design)fileSystem.Selection.Selection!.Value).WriteProtected() && manager.CanUndo((Design)fileSystem.Selection.Selection!.Value); + + public override bool HasTooltip + => true; + + public override void DrawTooltip() + => Im.Text( + "Undo the last time you applied an entire design onto this design, if you accidentally overwrote your design with a different one."u8); + + public override void OnClick() + { + try + { + manager.UndoDesignChange((Design)fileSystem.Selection.Selection!.Value); + } + catch (Exception ex) + { + Glamourer.Messager.NotificationMessage(ex, + $"Could not undo last changes to {((Design)fileSystem.Selection.Selection!.Value).Name}.", + NotificationType.Error, false); + } + } +} diff --git a/Glamourer/Gui/Tabs/DesignTab/ExportToClipboardButton.cs b/Glamourer/Gui/Tabs/DesignTab/ExportToClipboardButton.cs new file mode 100644 index 0000000..990a3f2 --- /dev/null +++ b/Glamourer/Gui/Tabs/DesignTab/ExportToClipboardButton.cs @@ -0,0 +1,36 @@ +using Dalamud.Interface.ImGuiNotification; +using Glamourer.Designs; +using ImSharp; +using Luna; + +namespace Glamourer.Gui.Tabs.DesignTab; + +public sealed class ExportToClipboardButton(DesignFileSystem fileSystem, DesignConverter converter) : BaseIconButton +{ + public override bool IsVisible + => fileSystem.Selection.Selection is not null; + + public override AwesomeIcon Icon + => LunaStyle.ToClipboardIcon; + + public override bool HasTooltip + => true; + + public override void DrawTooltip() + => Im.Text("Copy the current design to your clipboard."u8); + + public override void OnClick() + { + var design = (Design)fileSystem.Selection.Selection!.Value; + try + { + var text = converter.ShareBase64(design); + Im.Clipboard.Set(text); + } + catch (Exception ex) + { + Glamourer.Messager.NotificationMessage(ex, $"Could not copy {design.Name} data to clipboard.", + $"Could not copy data from design {design.Identifier} to clipboard", NotificationType.Error, false); + } + } +} diff --git a/Glamourer/Gui/Tabs/DesignTab/LockButton.cs b/Glamourer/Gui/Tabs/DesignTab/LockButton.cs new file mode 100644 index 0000000..62225b8 --- /dev/null +++ b/Glamourer/Gui/Tabs/DesignTab/LockButton.cs @@ -0,0 +1,28 @@ +using Glamourer.Designs; +using ImSharp; +using Luna; + +namespace Glamourer.Gui.Tabs.DesignTab; + +public sealed class LockButton(DesignFileSystem fileSystem, DesignManager manager) : BaseIconButton +{ + public override bool IsVisible + => fileSystem.Selection.Selection is not null; + + public override AwesomeIcon Icon + => ((Design)fileSystem.Selection.Selection!.Value).WriteProtected() + ? LunaStyle.LockedIcon + : LunaStyle.UnlockedIcon; + + public override bool HasTooltip + => true; + + public override void DrawTooltip() + => Im.Text(((Design)fileSystem.Selection.Selection!.Value).WriteProtected() + ? "Make this design editable."u8 + : "Write-protect this design."u8); + + public override void OnClick() + => manager.SetWriteProtection((Design)fileSystem.Selection.Selection!.Value, + !((Design)fileSystem.Selection.Selection!.Value).WriteProtected()); +} diff --git a/Glamourer/Gui/Tabs/DesignTab/ModAssociationsTab.cs b/Glamourer/Gui/Tabs/DesignTab/ModAssociationsTab.cs index cfde48b..16d6f8d 100644 --- a/Glamourer/Gui/Tabs/DesignTab/ModAssociationsTab.cs +++ b/Glamourer/Gui/Tabs/DesignTab/ModAssociationsTab.cs @@ -1,5 +1,5 @@ using Dalamud.Interface.ImGuiNotification; -using Glamourer.Configuration; +using Glamourer.Config; using Glamourer.Designs; using Glamourer.Interop.Penumbra; using Glamourer.State; @@ -8,11 +8,14 @@ using Luna; namespace Glamourer.Gui.Tabs.DesignTab; -public class ModAssociationsTab(PenumbraService penumbra, DesignSelection selection, DesignManager manager, Configuration.Configuration config) +public class ModAssociationsTab(PenumbraService penumbra, DesignFileSystem fileSystem, DesignManager manager, Configuration config) { - private readonly ModCombo _modCombo = new(penumbra, selection); + private readonly ModCombo _modCombo = new(penumbra, fileSystem); private (Mod, ModSettings)[]? _copy; + private Design Selection + => (Design)fileSystem.Selection.Selection!.Value; + public void Draw() { using var h = DesignPanelFlag.ModAssociations.Header(config); @@ -36,7 +39,7 @@ public class ModAssociationsTab(PenumbraService penumbra, DesignSelection select { var size = new Vector2((Im.ContentRegion.Available.X - 2 * Im.Style.ItemSpacing.X) / 3, 0); if (Im.Button("Copy All to Clipboard"u8, size)) - _copy = selection.Design!.AssociatedMods.Select(kvp => (kvp.Key, kvp.Value)).ToArray(); + _copy = Selection.AssociatedMods.Select(kvp => (kvp.Key, kvp.Value)).ToArray(); Im.Line.Same(); @@ -45,7 +48,7 @@ public class ModAssociationsTab(PenumbraService penumbra, DesignSelection select ? $"Add {_copy.Length} mod association(s) from clipboard." : "Copy some mod associations to the clipboard, first."u8, _copy is null)) foreach (var (mod, setting) in _copy!) - manager.UpdateMod(selection.Design!, mod, setting); + manager.UpdateMod(Selection, mod, setting); Im.Line.Same(); @@ -54,10 +57,10 @@ public class ModAssociationsTab(PenumbraService penumbra, DesignSelection select ? $"Set {_copy.Length} mod association(s) from clipboard and discard existing." : "Copy some mod associations to the clipboard, first."u8, _copy is null)) { - while (selection.Design!.AssociatedMods.Count > 0) - manager.RemoveMod(selection.Design!, selection.Design!.AssociatedMods.Keys[0]); + while (Selection.AssociatedMods.Count > 0) + manager.RemoveMod(Selection, Selection.AssociatedMods.Keys[0]); foreach (var (mod, setting) in _copy!) - manager.AddMod(selection.Design!, mod, setting); + manager.AddMod(Selection, mod, setting); } } @@ -76,13 +79,13 @@ public class ModAssociationsTab(PenumbraService penumbra, DesignSelection select var (id, name) = penumbra.CurrentCollection; if (ImEx.Button("Apply Mod Associations"u8, Vector2.Zero, $"Try to apply all associated mod settings to Penumbras current collection {name}", - selection.Design!.AssociatedMods.Count is 0 || id == Guid.Empty)) + Selection.AssociatedMods.Count is 0 || id == Guid.Empty)) ApplyAll(); } public void ApplyAll() { - foreach (var (mod, settings) in selection.Design!.AssociatedMods) + foreach (var (mod, settings) in Selection.AssociatedMods) penumbra.SetMod(mod, settings, StateSource.Manual, false); } @@ -104,7 +107,7 @@ public class ModAssociationsTab(PenumbraService penumbra, DesignSelection select Mod? removedMod = null; (Mod mod, ModSettings settings)? updatedMod = null; - foreach (var (idx, (mod, settings)) in selection.Design!.AssociatedMods.Index()) + foreach (var (idx, (mod, settings)) in Selection.AssociatedMods.Index()) { using var id = Im.Id.Push(idx); DrawAssociatedModRow(table, mod, settings, out var removedModTmp, out var updatedModTmp); @@ -117,10 +120,10 @@ public class ModAssociationsTab(PenumbraService penumbra, DesignSelection select DrawNewModRow(table); if (removedMod.HasValue) - manager.RemoveMod(selection.Design!, removedMod.Value); + manager.RemoveMod(Selection, removedMod.Value); if (updatedMod.HasValue) - manager.UpdateMod(selection.Design!, updatedMod.Value.mod, updatedMod.Value.settings); + manager.UpdateMod(Selection, updatedMod.Value.mod, updatedMod.Value.settings); } private void DrawAssociatedModRow(in Im.TableDisposable table, Mod mod, ModSettings settings, out Mod? removedMod, @@ -245,12 +248,12 @@ public class ModAssociationsTab(PenumbraService penumbra, DesignSelection select table.NextColumn(); var tt = currentDir.Length is 0 ? "Please select a mod first."u8 - : selection.Design!.AssociatedMods.ContainsKey(new Mod(_modCombo.SelectionName, currentDir)) + : Selection.AssociatedMods.ContainsKey(new Mod(_modCombo.SelectionName, currentDir)) ? "The design already contains an association with the selected mod."u8 : StringU8.Empty; if (ImEx.Icon.Button(LunaStyle.AddObjectIcon, tt, tt.Length > 0)) - manager.AddMod(selection.Design!, new Mod(_modCombo.SelectionName, _modCombo.Selection), _modCombo.Settings); + manager.AddMod(Selection, new Mod(_modCombo.SelectionName, _modCombo.Selection), _modCombo.Settings); table.NextColumn(); _modCombo.Draw("##new"u8, Im.ContentRegion.Available.X); } diff --git a/Glamourer/Gui/Tabs/DesignTab/ModCombo.cs b/Glamourer/Gui/Tabs/DesignTab/ModCombo.cs index 58f5adb..98575a9 100644 --- a/Glamourer/Gui/Tabs/DesignTab/ModCombo.cs +++ b/Glamourer/Gui/Tabs/DesignTab/ModCombo.cs @@ -1,9 +1,10 @@ -using Glamourer.Interop.Penumbra; +using Glamourer.Designs; +using Glamourer.Interop.Penumbra; using ImSharp; namespace Glamourer.Gui.Tabs.DesignTab; -public sealed class ModCombo(PenumbraService penumbra, DesignSelection selection) : FilterComboBase(new ModFilter()) +public sealed class ModCombo(PenumbraService penumbra, DesignFileSystem fileSystem) : FilterComboBase(new ModFilter()) { public readonly struct CacheItem(in Mod mod, in ModSettings settings, int count) { @@ -42,7 +43,7 @@ public sealed class ModCombo(PenumbraService penumbra, DesignSelection selection => Im.Style.TextHeightWithSpacing; protected override IEnumerable GetItems() - => penumbra.GetMods(selection.Design?.FilteredItemNames.ToArray() ?? []).Select(t => new CacheItem(t.Mod, t.Settings, t.Count)); + => penumbra.GetMods(fileSystem.Selection.Selection?.GetValue()?.FilteredItemNames.ToArray() ?? []).Select(t => new CacheItem(t.Mod, t.Settings, t.Count)); protected override bool DrawItem(in CacheItem item, int globalIndex, bool selected) { diff --git a/Glamourer/Gui/Tabs/DesignTab/MultiDesignPanel.cs b/Glamourer/Gui/Tabs/DesignTab/MultiDesignPanel.cs index 472f571..71e0e30 100644 --- a/Glamourer/Gui/Tabs/DesignTab/MultiDesignPanel.cs +++ b/Glamourer/Gui/Tabs/DesignTab/MultiDesignPanel.cs @@ -1,4 +1,5 @@ -using Glamourer.Designs; +using Glamourer.Config; +using Glamourer.Designs; using Glamourer.Interop.Material; using ImSharp; using Luna; @@ -6,21 +7,21 @@ using Luna; namespace Glamourer.Gui.Tabs.DesignTab; public class MultiDesignPanel( - DesignFileSystemSelector selector, + DesignFileSystem fileSystem, DesignManager editor, DesignColors colors, - Configuration.Configuration config) + Configuration config) { private readonly DesignColorCombo _colorCombo = new(colors, true); public void Draw() { - if (selector.SelectedPaths.Count == 0) + if (fileSystem.Selection.OrderedNodes.Count is 0) return; var width = ImEx.ScaledVectorX(145); var treeNodePos = Im.Cursor.Position; - _numDesigns = DrawDesignList(); + DrawDesignList(); DrawCounts(treeNodePos); var offset = DrawMultiTagger(width); DrawMultiColor(width, offset); @@ -36,14 +37,15 @@ public class MultiDesignPanel( private void DrawCounts(Vector2 treeNodePos) { var startPos = Im.Cursor.Position; - var numFolders = selector.SelectedPaths.Count - _numDesigns; + var numDesigns = fileSystem.Selection.DataNodes.Count; + var numFolders = fileSystem.Selection.Folders.Count; Im.Cursor.Position = treeNodePos; - ImEx.TextRightAligned((_numDesigns, numFolders) switch + ImEx.TextRightAligned((numDesigns, numFolders) switch { - (0, 0) => StringU8.Empty, // should not happen - ( > 0, 0) => $"{_numDesigns} Designs", - (0, > 0) => $"{numFolders} Folders", - _ => $"{_numDesigns} Designs, {numFolders} Folders", + (0, 0) => StringU8.Empty, // should not happen + (> 0, 0) => $"{numDesigns} Designs", + (0, > 0) => $"{numFolders} Folders", + _ => $"{numDesigns} Designs, {numFolders} Folders", }); Im.Cursor.Position = startPos; } @@ -59,10 +61,10 @@ public class MultiDesignPanel( _numAdvancedDyes = 0; } - private bool CountLeaves(DesignFileSystem.IPath path) + private void CountLeaves(IFileSystemNode path) { - if (path is not DesignFileSystem.Leaf l) - return false; + if (path is not IFileSystemData l) + return; if (l.Value.QuickDesign) ++_numQuickDesignEnabled; @@ -79,55 +81,48 @@ public class MultiDesignPanel( ++_numDesignsWithAdvancedDyes; _numAdvancedDyes += l.Value.Materials.Count; } - - return true; } - private int DrawDesignList() + private void DrawDesignList() { ResetCounts(); using var tree = Im.Tree.Node("Currently Selected Objects"u8, TreeNodeFlags.DefaultOpen | TreeNodeFlags.NoTreePushOnOpen); Im.Separator(); if (!tree) - return selector.SelectedPaths.Count(CountLeaves); + return; var sizeType = new Vector2(Im.Style.FrameHeight); var availableSizePercent = (Im.ContentRegion.Available.X - sizeType.X - 4 * Im.Style.CellPadding.X) / 100; var sizeMods = availableSizePercent * 35; var sizeFolders = availableSizePercent * 65; - var numDesigns = 0; using (var table = Im.Table.Begin("mods"u8, 3, TableFlags.RowBackground)) { if (!table) - return selector.SelectedPaths.Count(l => l is DesignFileSystem.Leaf); + return; table.SetupColumn("type"u8, TableColumnFlags.WidthFixed, sizeType.X); table.SetupColumn("mod"u8, TableColumnFlags.WidthFixed, sizeMods); table.SetupColumn("path"u8, TableColumnFlags.WidthFixed, sizeFolders); - var i = 0; - foreach (var (fullName, path) in selector.SelectedPaths.Select(p => (p.FullName(), p)) - .OrderBy(p => p.Item1, StringComparer.OrdinalIgnoreCase)) + foreach (var (index, node) in fileSystem.Selection.OrderedNodes.Index()) { - using var id = Im.Id.Push(i++); - var (icon, text) = path is DesignFileSystem.Leaf l + using var id = Im.Id.Push(index); + var (icon, text) = node is IFileSystemData l ? (LunaStyle.RemoveFileIcon, l.Value.Name.Text) : (LunaStyle.RemoveFolderIcon, string.Empty); table.NextColumn(); if (ImEx.Icon.Button(icon, "Remove from selection."u8, sizeType)) - selector.RemovePathFromMultiSelection(path); + fileSystem.Selection.RemoveFromSelection(node); table.DrawFrameColumn(text); - table.DrawFrameColumn(fullName); + table.DrawFrameColumn(node.FullPath); - if (CountLeaves(path)) - ++numDesigns; + CountLeaves(node); } } Im.Separator(); - return numDesigns; } private string _tag = string.Empty; @@ -138,7 +133,6 @@ public class MultiDesignPanel( private int _numDesignsResetDyes; private int _numAdvancedDyes; private int _numDesignsWithAdvancedDyes; - private int _numDesigns; private readonly List _addDesigns = []; private readonly List<(Design, int)> _removeDesigns = []; @@ -153,23 +147,25 @@ public class MultiDesignPanel( UpdateTagCache(); Im.Line.Same(); if (ImEx.Button(_addDesigns.Count > 0 - ? $"Add to {_addDesigns.Count} Designs" - : "Add"u8, width, _addDesigns.Count is 0 - ? _tag.Length is 0 - ? "No tag specified."u8 - : $"All designs selected already contain the tag \"{_tag}\"." - : $"Add the tag \"{_tag}\" to {_addDesigns.Count} designs as a local tag:\n\n\t{StringU8.Join("\n\t"u8, _addDesigns.Select(m => m.Name.Text))}", _addDesigns.Count is 0)) + ? $"Add to {_addDesigns.Count} Designs" + : "Add"u8, width, _addDesigns.Count is 0 + ? _tag.Length is 0 + ? "No tag specified."u8 + : $"All designs selected already contain the tag \"{_tag}\"." + : $"Add the tag \"{_tag}\" to {_addDesigns.Count} designs as a local tag:\n\n\t{StringU8.Join("\n\t"u8, _addDesigns.Select(m => m.Name.Text))}", + _addDesigns.Count is 0)) foreach (var design in _addDesigns) editor.AddTag(design, _tag); Im.Line.Same(); if (ImEx.Button(_removeDesigns.Count > 0 - ? $"Remove from {_removeDesigns.Count} Designs" - : "Remove", width, _removeDesigns.Count is 0 - ? _tag.Length is 0 - ? "No tag specified."u8 - : $"No selected design contains the tag \"{_tag}\" locally." - : $"Remove the local tag \"{_tag}\" from {_removeDesigns.Count} designs:\n\n\t{string.Join("\n\t", _removeDesigns.Select(m => m.Item1.Name.Text))}", _removeDesigns.Count is 0)) + ? $"Remove from {_removeDesigns.Count} Designs" + : "Remove", width, _removeDesigns.Count is 0 + ? _tag.Length is 0 + ? "No tag specified."u8 + : $"No selected design contains the tag \"{_tag}\" locally." + : $"Remove the local tag \"{_tag}\" from {_removeDesigns.Count} designs:\n\n\t{string.Join("\n\t", _removeDesigns.Select(m => m.Item1.Name.Text))}", + _removeDesigns.Count is 0)) foreach (var (design, index) in _removeDesigns) editor.RemoveTag(design, index); Im.Separator(); @@ -181,23 +177,21 @@ public class MultiDesignPanel( ImEx.TextFrameAligned("Multi QDB:"u8); Im.Line.Same(offset, Im.Style.ItemSpacing.X); var buttonWidth = new Vector2((Im.ContentRegion.Available.X - Im.Style.ItemSpacing.X) / 2, 0); - var diff = _numDesigns - _numQuickDesignEnabled; + var diff = fileSystem.Selection.DataNodes.Count - _numQuickDesignEnabled; if (ImEx.Button("Display Selected Designs in QDB"u8, buttonWidth, diff is 0 - ? $"All {_numDesigns} selected designs are already displayed in the quick design bar." - : $"Display all {_numDesigns} selected designs in the quick design bar. Changes {diff} designs.", diff is 0)) - { - foreach (var design in selector.SelectedPaths.OfType()) - editor.SetQuickDesign(design.Value, true); - } + ? $"All {fileSystem.Selection.DataNodes.Count} selected designs are already displayed in the quick design bar." + : $"Display all {fileSystem.Selection.DataNodes.Count} selected designs in the quick design bar. Changes {diff} designs.", + diff is 0)) + foreach (var design in fileSystem.Selection.DataNodes) + editor.SetQuickDesign(design.GetValue()!, true); Im.Line.Same(); if (ImEx.Button("Hide Selected Designs in QDB"u8, buttonWidth, _numQuickDesignEnabled is 0 - ? $"All {_numDesigns} selected designs are already hidden in the quick design bar." - : $"Hide all {_numDesigns} selected designs in the quick design bar. Changes {_numQuickDesignEnabled} designs.", _numQuickDesignEnabled is 0)) - { - foreach (var design in selector.SelectedPaths.OfType()) - editor.SetQuickDesign(design.Value, false); - } + ? $"All {fileSystem.Selection.DataNodes.Count} selected designs are already hidden in the quick design bar." + : $"Hide all {fileSystem.Selection.DataNodes.Count} selected designs in the quick design bar. Changes {_numQuickDesignEnabled} designs.", + _numQuickDesignEnabled is 0)) + foreach (var design in fileSystem.Selection.DataNodes) + editor.SetQuickDesign(design.GetValue()!, false); Im.Separator(); } @@ -207,19 +201,20 @@ public class MultiDesignPanel( ImEx.TextFrameAligned("Multi Lock:"u8); Im.Line.Same(offset, Im.Style.ItemSpacing.X); var buttonWidth = new Vector2((Im.ContentRegion.Available.X - Im.Style.ItemSpacing.X) / 2, 0); - var diff = _numDesigns - _numDesignsLocked; + var diff = fileSystem.Selection.DataNodes.Count - _numDesignsLocked; if (ImEx.Button("Turn Write-Protected"u8, buttonWidth, diff is 0 - ? $"All {_numDesigns} selected designs are already write protected." - : $"Write-protect all {_numDesigns} designs. Changes {diff} designs.", diff is 0)) - foreach (var design in selector.SelectedPaths.OfType()) - editor.SetWriteProtection(design.Value, true); + ? $"All {fileSystem.Selection.DataNodes.Count} selected designs are already write protected." + : $"Write-protect all {fileSystem.Selection.DataNodes.Count} designs. Changes {diff} designs.", diff is 0)) + foreach (var design in fileSystem.Selection.DataNodes) + editor.SetWriteProtection(design.GetValue()!, true); Im.Line.Same(); if (ImEx.Button("Remove Write-Protection"u8, buttonWidth, _numDesignsLocked is 0 - ? $"None of the {_numDesigns} selected designs are write-protected." - : $"Remove the write protection of the {_numDesigns} selected designs. Changes {_numDesignsLocked} designs.", _numDesignsLocked is 0)) - foreach (var design in selector.SelectedPaths.OfType()) - editor.SetWriteProtection(design.Value, false); + ? $"None of the {fileSystem.Selection.DataNodes.Count} selected designs are write-protected." + : $"Remove the write protection of the {fileSystem.Selection.DataNodes.Count} selected designs. Changes {_numDesignsLocked} designs.", + _numDesignsLocked is 0)) + foreach (var design in fileSystem.Selection.DataNodes) + editor.SetWriteProtection(design.GetValue()!, false); Im.Separator(); } @@ -228,19 +223,21 @@ public class MultiDesignPanel( ImEx.TextFrameAligned("Settings:"u8); Im.Line.Same(offset, Im.Style.ItemSpacing.X); var buttonWidth = new Vector2((Im.ContentRegion.Available.X - Im.Style.ItemSpacing.X) / 2, 0); - var diff = _numDesigns - _numDesignsResetSettings; + var diff = fileSystem.Selection.DataNodes.Count - _numDesignsResetSettings; if (ImEx.Button("Set Reset Temp. Settings"u8, buttonWidth, diff is 0 - ? $"All {_numDesigns} selected designs already reset temporary settings." - : $"Make all {_numDesigns} selected designs reset temporary settings. Changes {diff} designs.", diff is 0)) - foreach (var design in selector.SelectedPaths.OfType()) - editor.ChangeResetTemporarySettings(design.Value, true); + ? $"All {fileSystem.Selection.DataNodes.Count} selected designs already reset temporary settings." + : $"Make all {fileSystem.Selection.DataNodes.Count} selected designs reset temporary settings. Changes {diff} designs.", + diff is 0)) + foreach (var design in fileSystem.Selection.DataNodes) + editor.ChangeResetTemporarySettings(design.GetValue()!, true); Im.Line.Same(); if (ImEx.Button("Remove Reset Temp. Settings"u8, buttonWidth, _numDesignsResetSettings is 0 - ? $"None of the {_numDesigns} selected designs reset temporary settings." - : $"Stop all {_numDesigns} selected designs from resetting temporary settings. Changes {_numDesignsResetSettings} designs.", _numDesignsResetSettings is 0)) - foreach (var design in selector.SelectedPaths.OfType()) - editor.ChangeResetTemporarySettings(design.Value, false); + ? $"None of the {fileSystem.Selection.DataNodes.Count} selected designs reset temporary settings." + : $"Stop all {fileSystem.Selection.DataNodes.Count} selected designs from resetting temporary settings. Changes {_numDesignsResetSettings} designs.", + _numDesignsResetSettings is 0)) + foreach (var design in fileSystem.Selection.DataNodes) + editor.ChangeResetTemporarySettings(design.GetValue()!, false); Im.Separator(); } @@ -249,19 +246,20 @@ public class MultiDesignPanel( ImEx.TextFrameAligned("Adv. Dyes:"u8); Im.Line.Same(offset, Im.Style.ItemSpacing.X); var buttonWidth = new Vector2((Im.ContentRegion.Available.X - Im.Style.ItemSpacing.X) / 2, 0); - var diff = _numDesigns - _numDesignsResetDyes; + var diff = fileSystem.Selection.DataNodes.Count - _numDesignsResetDyes; if (ImEx.Button("Set Reset Dyes"u8, buttonWidth, diff is 0 - ? $"All {_numDesigns} selected designs already reset advanced dyes." - : $"Make all {_numDesigns} selected designs reset advanced dyes. Changes {diff} designs.", diff is 0)) - foreach (var design in selector.SelectedPaths.OfType()) - editor.ChangeResetAdvancedDyes(design.Value, true); + ? $"All {fileSystem.Selection.DataNodes.Count} selected designs already reset advanced dyes." + : $"Make all {fileSystem.Selection.DataNodes.Count} selected designs reset advanced dyes. Changes {diff} designs.", diff is 0)) + foreach (var design in fileSystem.Selection.DataNodes) + editor.ChangeResetAdvancedDyes(design.GetValue()!, true); Im.Line.Same(); if (ImEx.Button("Remove Reset Dyes"u8, buttonWidth, _numDesignsLocked is 0 - ? $"None of the {_numDesigns} selected designs reset advanced dyes." - : $"Stop all {_numDesigns} selected designs from resetting advanced dyes. Changes {_numDesignsResetDyes} designs.", _numDesignsResetDyes is 0)) - foreach (var design in selector.SelectedPaths.OfType()) - editor.ChangeResetAdvancedDyes(design.Value, false); + ? $"None of the {fileSystem.Selection.DataNodes.Count} selected designs reset advanced dyes." + : $"Stop all {fileSystem.Selection.DataNodes.Count} selected designs from resetting advanced dyes. Changes {_numDesignsResetDyes} designs.", + _numDesignsResetDyes is 0)) + foreach (var design in fileSystem.Selection.DataNodes) + editor.ChangeResetAdvancedDyes(design.GetValue()!, false); Im.Separator(); } @@ -270,19 +268,20 @@ public class MultiDesignPanel( ImEx.TextFrameAligned("Redrawing:"u8); Im.Line.Same(offset, Im.Style.ItemSpacing.X); var buttonWidth = new Vector2((Im.ContentRegion.Available.X - Im.Style.ItemSpacing.X) / 2, 0); - var diff = _numDesigns - _numDesignsForcedRedraw; + var diff = fileSystem.Selection.DataNodes.Count - _numDesignsForcedRedraw; if (ImEx.Button("Force Redraws"u8, buttonWidth, diff is 0 - ? $"All {_numDesigns} selected designs already force redraws." - : $"Make all {_numDesigns} designs force redraws. Changes {diff} designs.", diff is 0)) - foreach (var design in selector.SelectedPaths.OfType()) - editor.ChangeForcedRedraw(design.Value, true); + ? $"All {fileSystem.Selection.DataNodes.Count} selected designs already force redraws." + : $"Make all {fileSystem.Selection.DataNodes.Count} designs force redraws. Changes {diff} designs.", diff is 0)) + foreach (var design in fileSystem.Selection.DataNodes) + editor.ChangeForcedRedraw(design.GetValue()!, true); Im.Line.Same(); if (ImEx.Button("Remove Forced Redraws"u8, buttonWidth, _numDesignsLocked is 0 - ? $"None of the {_numDesigns} selected designs force redraws." - : $"Stop all {_numDesigns} selected designs from forcing redraws. Changes {_numDesignsForcedRedraw} designs.", _numDesignsForcedRedraw is 0)) - foreach (var design in selector.SelectedPaths.OfType()) - editor.ChangeForcedRedraw(design.Value, false); + ? $"None of the {fileSystem.Selection.DataNodes.Count} selected designs force redraws." + : $"Stop all {fileSystem.Selection.DataNodes.Count} selected designs from forcing redraws. Changes {_numDesignsForcedRedraw} designs.", + _numDesignsForcedRedraw is 0)) + foreach (var design in fileSystem.Selection.DataNodes) + editor.ChangeForcedRedraw(design.GetValue()!, false); Im.Separator(); } @@ -299,30 +298,28 @@ public class MultiDesignPanel( UpdateColorCache(); Im.Line.Same(); if (ImEx.Button(_addDesigns.Count > 0 - ? $"Set for {_addDesigns.Count} Designs" - : "Set"u8, width, _addDesigns.Count is 0 - ? _colorComboSelection switch - { - null => "No color specified."u8, - DesignColors.AutomaticName => "Use the other button to set to automatic."u8, - _ => $"All designs selected are already set to the color \"{_colorComboSelection}\".", - } - : $"Set the color of {_addDesigns.Count} designs to \"{_colorComboSelection}\"\n\n\t{StringU8.Join("\n\t"u8, _addDesigns.Select(m => m.Name.Text))}", _addDesigns.Count is 0)) - { + ? $"Set for {_addDesigns.Count} Designs" + : "Set"u8, width, _addDesigns.Count is 0 + ? _colorComboSelection switch + { + null => "No color specified."u8, + DesignColors.AutomaticName => "Use the other button to set to automatic."u8, + _ => $"All designs selected are already set to the color \"{_colorComboSelection}\".", + } + : $"Set the color of {_addDesigns.Count} designs to \"{_colorComboSelection}\"\n\n\t{StringU8.Join("\n\t"u8, _addDesigns.Select(m => m.Name.Text))}", + _addDesigns.Count is 0)) foreach (var design in _addDesigns) editor.ChangeColor(design, _colorComboSelection!); - } Im.Line.Same(); if (ImEx.Button(_removeDesigns.Count > 0 - ? $"Unset {_removeDesigns.Count} Designs" - : "Unset"u8, width, _removeDesigns.Count is 0 - ? "No selected design is set to a non-automatic color."u8 - : $"Set {_removeDesigns.Count} designs to use automatic color again:\n\n\t{StringU8.Join("\n\t"u8, _removeDesigns.Select(m => m.Item1.Name.Text))}", _removeDesigns.Count is 0)) - { + ? $"Unset {_removeDesigns.Count} Designs" + : "Unset"u8, width, _removeDesigns.Count is 0 + ? "No selected design is set to a non-automatic color."u8 + : $"Set {_removeDesigns.Count} designs to use automatic color again:\n\n\t{StringU8.Join("\n\t"u8, _removeDesigns.Select(m => m.Item1.Name.Text))}", + _removeDesigns.Count is 0)) foreach (var (design, _) in _removeDesigns) editor.ChangeColor(design, string.Empty); - } Im.Separator(); } @@ -333,14 +330,15 @@ public class MultiDesignPanel( Im.Line.Same(offset, Im.Style.ItemSpacing.X); var enabled = config.DeleteDesignModifier.IsActive(); if (ImEx.Button("Delete All Advanced Dyes"u8, Im.ContentRegion.Available with { Y = 0 }, _numDesignsWithAdvancedDyes is 0 - ? "No selected designs contain any advanced dyes."u8 - : $"Delete {_numAdvancedDyes} advanced dyes from {_numDesignsWithAdvancedDyes} of the selected designs.", + ? "No selected designs contain any advanced dyes."u8 + : $"Delete {_numAdvancedDyes} advanced dyes from {_numDesignsWithAdvancedDyes} of the selected designs.", !enabled || _numDesignsWithAdvancedDyes is 0)) - foreach (var design in selector.SelectedPaths.OfType()) + foreach (var design in fileSystem.Selection.DataNodes) { - while (design.Value.Materials.Count > 0) - editor.ChangeMaterialValue(design.Value, MaterialValueIndex.FromKey(design.Value.Materials[0].Item1), null); + while (design.GetValue()!.Materials.Count > 0) + editor.ChangeMaterialValue(design.GetValue()!, + MaterialValueIndex.FromKey(design.GetValue()!.Materials[0].Item1), null); } if (!enabled && _numDesignsWithAdvancedDyes is not 0) @@ -359,8 +357,8 @@ public class MultiDesignPanel( using (Im.Group()) { if (ImEx.Button("Disable Everything"u8, width, - _numDesigns > 0 - ? $"Disable application of everything, including any existing advanced dyes, advanced customizations, crests and wetness for all {_numDesigns} designs." + fileSystem.Selection.DataNodes.Count > 0 + ? $"Disable application of everything, including any existing advanced dyes, advanced customizations, crests and wetness for all {fileSystem.Selection.DataNodes.Count} designs." : "No designs selected."u8, !enabled)) { equip = false; @@ -372,8 +370,8 @@ public class MultiDesignPanel( Im.Line.Same(); if (ImEx.Button("Enable Everything"u8, width, - _numDesigns > 0 - ? $"Enable application of everything, including any existing advanced dyes, advanced customizations, crests and wetness for all {_numDesigns} designs." + fileSystem.Selection.DataNodes.Count > 0 + ? $"Enable application of everything, including any existing advanced dyes, advanced customizations, crests and wetness for all {fileSystem.Selection.DataNodes.Count} designs." : "No designs selected."u8, !enabled)) { equip = true; @@ -384,8 +382,8 @@ public class MultiDesignPanel( Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, $"Hold {config.DeleteDesignModifier} while clicking."); if (ImEx.Button("Equipment Only"u8, width, - _numDesigns > 0 - ? $"Enable application of anything related to gear, disable anything that is not related to gear for all {_numDesigns} designs." + fileSystem.Selection.DataNodes.Count > 0 + ? $"Enable application of anything related to gear, disable anything that is not related to gear for all {fileSystem.Selection.DataNodes.Count} designs." : "No designs selected."u8, !enabled)) { equip = true; @@ -397,8 +395,8 @@ public class MultiDesignPanel( Im.Line.Same(); if (ImEx.Button("Customization Only"u8, width, - _numDesigns > 0 - ? $"Enable application of anything related to customization, disable anything that is not related to customization for all {_numDesigns} designs." + fileSystem.Selection.DataNodes.Count > 0 + ? $"Enable application of anything related to customization, disable anything that is not related to customization for all {fileSystem.Selection.DataNodes.Count} designs." : "No designs selected."u8, !enabled)) { equip = false; @@ -409,10 +407,10 @@ public class MultiDesignPanel( Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, $"Hold {config.DeleteDesignModifier} while clicking."); if (ImEx.Button("Default Application"u8, width, - _numDesigns > 0 - ? $"Set the application rules to the default values as if the {_numDesigns} were newly created,without any advanced features or wetness." + fileSystem.Selection.DataNodes.Count > 0 + ? $"Set the application rules to the default values as if the {fileSystem.Selection.DataNodes.Count} were newly created,without any advanced features or wetness." : "No designs selected."u8, !enabled)) - foreach (var design in selector.SelectedPaths.OfType().Select(l => l.Value)) + foreach (var design in fileSystem.Selection.DataNodes.Select(l => l.GetValue()!)) { editor.ChangeApplyMulti(design, true, true, true, false, true, true, false, true); editor.ChangeApplyMeta(design, MetaIndex.Wetness, false); @@ -422,10 +420,10 @@ public class MultiDesignPanel( Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, $"Hold {config.DeleteDesignModifier} while clicking."); Im.Line.Same(); - if (ImEx.Button("Disable Advanced"u8, width, _numDesigns > 0 - ? $"Disable all advanced dyes and customizations but keep everything else as is for all {_numDesigns} designs." + if (ImEx.Button("Disable Advanced"u8, width, fileSystem.Selection.DataNodes.Count > 0 + ? $"Disable all advanced dyes and customizations but keep everything else as is for all {fileSystem.Selection.DataNodes.Count} designs." : "No designs selected."u8, !enabled)) - foreach (var design in selector.SelectedPaths.OfType().Select(l => l.Value)) + foreach (var design in fileSystem.Selection.DataNodes.Select(l => l.GetValue()!)) editor.ChangeApplyMulti(design, null, null, null, false, null, null, false, null); if (!enabled) @@ -436,7 +434,7 @@ public class MultiDesignPanel( if (equip is null && customize is null) return; - foreach (var design in selector.SelectedPaths.OfType().Select(l => l.Value)) + foreach (var design in fileSystem.Selection.DataNodes.Select(l => l.GetValue()!)) { editor.ChangeApplyMulti(design, equip, customize, equip, customize.HasValue && !customize.Value ? false : null, null, equip, equip, equip); @@ -459,13 +457,14 @@ public class MultiDesignPanel( if (_tag.Length is 0) return; - foreach (var leaf in selector.SelectedPaths.OfType()) + foreach (var leaf in fileSystem.Selection.DataNodes) { - var index = leaf.Value.Tags.AsEnumerable().IndexOf(_tag); + var design = leaf.GetValue()!; + var index = design.Tags.AsEnumerable().IndexOf(_tag); if (index >= 0) - _removeDesigns.Add((leaf.Value, index)); + _removeDesigns.Add((design, index)); else - _addDesigns.Add(leaf.Value); + _addDesigns.Add(design); } } @@ -474,12 +473,13 @@ public class MultiDesignPanel( _addDesigns.Clear(); _removeDesigns.Clear(); var selection = string.IsNullOrEmpty(_colorComboSelection) ? DesignColors.AutomaticName : _colorComboSelection; - foreach (var leaf in selector.SelectedPaths.OfType()) + foreach (var leaf in fileSystem.Selection.DataNodes) { - if (leaf.Value.Color.Length > 0) - _removeDesigns.Add((leaf.Value, 0)); - if (selection != DesignColors.AutomaticName && leaf.Value.Color != selection) - _addDesigns.Add(leaf.Value); + var design = leaf.GetValue()!; + if (design.Color.Length > 0) + _removeDesigns.Add((design, 0)); + if (selection != DesignColors.AutomaticName && design.Color != selection) + _addDesigns.Add(design); } } } diff --git a/Glamourer/Gui/Tabs/DesignTab/Selector/DeleteSelectionButton.cs b/Glamourer/Gui/Tabs/DesignTab/Selector/DeleteSelectionButton.cs new file mode 100644 index 0000000..c35a57b --- /dev/null +++ b/Glamourer/Gui/Tabs/DesignTab/Selector/DeleteSelectionButton.cs @@ -0,0 +1,45 @@ +using Glamourer.Config; +using Glamourer.Designs; +using ImSharp; +using Luna; + +namespace Glamourer.Gui.Tabs.DesignTab; + +public sealed class DeleteSelectionButton(DesignFileSystem fileSystem, DesignManager manager, Configuration config) + : BaseIconButton +{ + /// + public override AwesomeIcon Icon + => LunaStyle.DeleteIcon; + + /// + public override bool HasTooltip + => true; + + /// + public override void DrawTooltip() + { + var anySelected = fileSystem.Selection.DataNodes.Count > 0; + var modifier = Enabled; + + Im.Text(anySelected + ? "Delete the currently selected designs entirely from your drive\nThis can not be undone."u8 + : "No designs selected."u8); + if (!modifier) + Im.Text($"\nHold {config.DeleteDesignModifier} while clicking to delete the designs."); + } + + /// + public override bool Enabled + => config.DeleteDesignModifier.IsActive() && fileSystem.Selection.DataNodes.Count > 0; + + /// + public override void OnClick() + { + foreach (var node in fileSystem.Selection.DataNodes.ToArray()) + { + if (node.GetValue() is { } design) + manager.Delete(design); + } + } +} diff --git a/Glamourer/Gui/Tabs/DesignTab/Selector/DesignFileSystemCache.cs b/Glamourer/Gui/Tabs/DesignTab/Selector/DesignFileSystemCache.cs new file mode 100644 index 0000000..91310b7 --- /dev/null +++ b/Glamourer/Gui/Tabs/DesignTab/Selector/DesignFileSystemCache.cs @@ -0,0 +1,112 @@ +using Glamourer.Designs; +using Glamourer.Designs.History; +using Glamourer.Events; +using ImSharp; +using Luna; + +namespace Glamourer.Gui.Tabs.DesignTab; + +public sealed class DesignFileSystemCache : FileSystemCache +{ + public DesignFileSystemCache(DesignFileSystemDrawer parent) + : base(parent) + { + parent.DesignChanged.Subscribe(OnDesignChanged, DesignChanged.Priority.DesignFileSystemSelector); + parent.DesignColors.ColorChanged += OnColorChanged; + } + + private void OnColorChanged() + { + foreach (var node in AllNodes.Values) + node.Dirty = true; + } + + private void OnDesignChanged(DesignChanged.Type type, Design design, ITransaction? _2) + { + switch (type) + { + case DesignChanged.Type.Created: + case DesignChanged.Type.Deleted: + case DesignChanged.Type.ReloadedAll: + case DesignChanged.Type.Renamed: + case DesignChanged.Type.ChangedDescription: + case DesignChanged.Type.ChangedColor: + case DesignChanged.Type.AddedTag: + case DesignChanged.Type.RemovedTag: + case DesignChanged.Type.ChangedTag: + case DesignChanged.Type.AddedMod: + case DesignChanged.Type.RemovedMod: + case DesignChanged.Type.UpdatedMod: + case DesignChanged.Type.ChangedLink: + case DesignChanged.Type.Equip: + case DesignChanged.Type.BonusItem: + case DesignChanged.Type.Weapon: + VisibleDirty = true; + break; + } + + if (design.Node is { } node && AllNodes.TryGetValue(node, out var cache)) + cache.Dirty = true; + } + + private new DesignFileSystemDrawer Parent + => (DesignFileSystemDrawer)base.Parent; + + public override void Update() + { + if (ColorsDirty) + { + CollapsedFolderColor = ColorId.FolderCollapsed.Value().ToVector(); + ExpandedFolderColor = ColorId.FolderExpanded.Value().ToVector(); + LineColor = ColorId.FolderLine.Value().ToVector(); + Dirty &= ~IManagedCache.DirtyFlags.Colors; + OnColorChanged(); + } + } + + protected override DesignData ConvertNode(in IFileSystemNode node) + => new((IFileSystemData)node); + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + Parent.DesignChanged.Unsubscribe(OnDesignChanged); + Parent.DesignColors.ColorChanged -= OnColorChanged; + } + + public sealed class DesignData(IFileSystemData node) : BaseFileSystemNodeCache + { + public readonly IFileSystemData Node = node; + public Vector4 Color; + public StringU8 Name = new(node.Value.Name.Text); + public StringU8 Incognito = new(node.Value.Incognito); + + public override void Update(FileSystemCache cache, IFileSystemNode node) + { + var drawer = (DesignFileSystemDrawer)cache.Parent; + Color = drawer.DesignColors.GetColor(Node.Value).ToVector(); + Name = new StringU8(Node.Value.Name.Text); + } + + protected override void DrawInternal(FileSystemCache cache, IFileSystemNode node) + { + var c = (DesignFileSystemCache)cache; + using var color = ImGuiColor.Text.Push(Color); + using var id = Im.Id.Push(Node.Value.Index); + var flags = node.Selected ? TreeNodeFlags.NoTreePushOnOpen | TreeNodeFlags.Selected : TreeNodeFlags.NoTreePushOnOpen; + Im.Tree.Leaf(c.Parent.Config.Ephemeral.IncognitoMode ? Incognito : Name, flags); + CheckDoubleClick(c); + } + + private void CheckDoubleClick(DesignFileSystemCache cache) + { + if (!cache.Parent.Config.AllowDoubleClickToApply) + return; + if (!Im.Item.Hovered()) + return; + + if (Im.Mouse.IsDoubleClicked(MouseButton.Left)) + cache.Parent.DesignApplier.ApplyToPlayer(Node.Value); + } + } +} diff --git a/Glamourer/Gui/Tabs/DesignTab/Selector/DesignFileSystemDrawer.cs b/Glamourer/Gui/Tabs/DesignTab/Selector/DesignFileSystemDrawer.cs new file mode 100644 index 0000000..9bb29da --- /dev/null +++ b/Glamourer/Gui/Tabs/DesignTab/Selector/DesignFileSystemDrawer.cs @@ -0,0 +1,65 @@ +using Glamourer.Config; +using Glamourer.Designs; +using Glamourer.Events; +using Glamourer.Services; +using Luna; + +namespace Glamourer.Gui.Tabs.DesignTab; + +public sealed class DesignFileSystemDrawer : FileSystemDrawer, IDisposable +{ + internal readonly Configuration Config; + internal readonly DesignApplier DesignApplier; + internal readonly DesignChanged DesignChanged; + internal readonly DesignColors DesignColors; + internal readonly DesignManager Manager; + + public DesignFileSystemDrawer(DesignFileSystem fileSystem, DesignManager manager, DesignConverter converter, Configuration config, + DesignApplier designApplier, DesignChanged designChanged, DesignColors designColors) + : base(fileSystem, new DesignFilter()) + { + Manager = manager; + Config = config; + DesignApplier = designApplier; + DesignChanged = designChanged; + DesignColors = designColors; + Footer.Buttons.AddButton(new NewDesignButton(manager), 1000); + Footer.Buttons.AddButton(new ImportDesignButton(converter, manager), 900); + Footer.Buttons.AddButton(new DuplicateDesignButton(fileSystem, manager), 800); + Footer.Buttons.AddButton(new DeleteSelectionButton(fileSystem, manager, config), -100); + + SortMode = Config.SortMode; + OnRenameChanged(Config.ShowRename, default); + Config.OnRenameChanged += OnRenameChanged; + } + + private void OnRenameChanged(RenameField newValue, RenameField _) + { + DataContext.RemoveButtons(); + DataContext.RemoveButtons(); + switch (newValue) + { + case RenameField.RenameSearchPath: DataContext.AddButton(new RenameDesignInput(this), -1000); break; + case RenameField.RenameData: DataContext.AddButton(new MoveDesignInput(this), -1000); break; + case RenameField.BothSearchPathPrio: + DataContext.AddButton(new RenameDesignInput(this), -1000); + DataContext.AddButton(new MoveDesignInput(this), -1001); + break; + case RenameField.BothDataPrio: + DataContext.AddButton(new RenameDesignInput(this), -1001); + DataContext.AddButton(new MoveDesignInput(this), -1000); + break; + } + } + + public void Dispose() + { + Config.OnRenameChanged -= OnRenameChanged; + } + + public override ReadOnlySpan Id + => "Designs"u8; + + protected override FileSystemCache CreateCache() + => new DesignFileSystemCache(this); +} diff --git a/Glamourer/Gui/Tabs/DesignTab/Selector/DesignFilter.cs b/Glamourer/Gui/Tabs/DesignTab/Selector/DesignFilter.cs new file mode 100644 index 0000000..e8feaac --- /dev/null +++ b/Glamourer/Gui/Tabs/DesignTab/Selector/DesignFilter.cs @@ -0,0 +1,150 @@ +using ImSharp; +using Luna; + +namespace Glamourer.Gui.Tabs.DesignTab; + +public sealed class DesignFilter : TokenizedFilter, + IFileSystemFilter, IUiService +{ + protected override void DrawTooltip() + { + if (!Im.Item.Hovered()) + return; + + using var tt = Im.Tooltip.Begin(); + var highlightColor = ColorId.EnabledAutoSet.Value().ToVector(); + Im.Text("Filter designs for those where their full paths or names contain the given strings, split by spaces."u8); + ImEx.TextMultiColored("Enter "u8).Then("m:[string]"u8, highlightColor) + .Then(" to filter for designs with a mod association containing the string."u8).End(); + ImEx.TextMultiColored("Enter "u8).Then("t:[string]"u8, highlightColor).Then(" to filter for designs set to specific tags."u8).End(); + ImEx.TextMultiColored("Enter "u8).Then("c:[string]"u8, highlightColor) + .Then(" to filter for designs set to specific colors."u8).End(); + ImEx.TextMultiColored("Enter "u8).Then("i:[string]"u8, highlightColor).Then(" to filter for designs containing specific items."u8) + .End(); + ImEx.TextMultiColored("Enter "u8).Then("n:[string]"u8, highlightColor).Then(" to filter only for design names, ignoring the paths."u8) + .End(); + ImEx.TextMultiColored("Enter "u8).Then("f:[string]"u8, highlightColor).Then( + " to filter for designs containing the text in name, path, description, tags, mod associations, colors or contained items."u8) + .End(); + Im.Line.New(); + ImEx.TextMultiColored("Use "u8).Then("None"u8, highlightColor).Then(" as a placeholder value that only matches empty lists or names."u8) + .End(); + Im.Text("Regularly, a design has to match all supplied criteria separately."u8); + ImEx.TextMultiColored("Put a "u8).Then("'-'"u8, highlightColor) + .Then(" in front of a search token to search only for designs not matching the criterion."u8).End(); + ImEx.TextMultiColored("Put a "u8).Then("'?'"u8, highlightColor) + .Then(" in front of a search token to search for designs matching at least one of the '?'-criteria."u8).End(); + ImEx.TextMultiColored("Wrap spaces in "u8).Then("\"[string with space]\""u8, highlightColor) + .Then(" to match this exact combination of words."u8).End(); + } + + protected override bool Matches(in DesignFilterToken token, in DesignFileSystemCache.DesignData cacheItem) + => token.Type switch + { + DesignFilterTokenType.Default => cacheItem.Node.FullPath.Contains(token.Needle, StringComparison.OrdinalIgnoreCase) + || cacheItem.Node.Value.Name.Text.Contains(token.Needle, StringComparison.OrdinalIgnoreCase), + DesignFilterTokenType.Mod => CheckMods(token.Needle, cacheItem), + DesignFilterTokenType.Tag => CheckTags(token.Needle, cacheItem), + DesignFilterTokenType.Color => cacheItem.Node.Value.Color.Contains(token.Needle, StringComparison.OrdinalIgnoreCase), + DesignFilterTokenType.Item => cacheItem.Node.Value.DesignData.ContainsName(token.Needle), + DesignFilterTokenType.Name => cacheItem.Node.Value.Name.Text.Contains(token.Needle, StringComparison.OrdinalIgnoreCase), + DesignFilterTokenType.FullContext => CheckFullContext(token.Needle, cacheItem), + _ => true, + }; + + protected override bool MatchesNone(DesignFilterTokenType type, bool negated, in DesignFileSystemCache.DesignData cacheItem) + => type switch + { + DesignFilterTokenType.Mod when negated => cacheItem.Node.Value.AssociatedMods.Count > 0, + DesignFilterTokenType.Mod => cacheItem.Node.Value.AssociatedMods.Count is 0, + DesignFilterTokenType.Tag when negated => cacheItem.Node.Value.Tags.Length > 0, + DesignFilterTokenType.Tag => cacheItem.Node.Value.Tags.Length is 0, + _ => true, + }; + + private static bool CheckMods(string needle, in DesignFileSystemCache.DesignData cacheItem) + => cacheItem.Node.Value.AssociatedMods.Any(kvp => kvp.Key.Name.Contains(needle, StringComparison.OrdinalIgnoreCase)); + + private static bool CheckTags(string needle, in DesignFileSystemCache.DesignData cacheItem) + => cacheItem.Node.Value.Tags.Any(t => t.Contains(needle, StringComparison.OrdinalIgnoreCase)); + + private static bool CheckFullContext(string needle, in DesignFileSystemCache.DesignData cacheItem) + { + if (needle.Length is 0) + return true; + + if (cacheItem.Node.FullPath.Contains(needle, StringComparison.OrdinalIgnoreCase)) + return true; + + var design = cacheItem.Node.Value; + if (design.Name.Text.Contains(needle, StringComparison.OrdinalIgnoreCase)) + return true; + + if (design.Description.Contains(needle, StringComparison.OrdinalIgnoreCase)) + return true; + + if (CheckTags(needle, cacheItem)) + return true; + + if (design.Color.Contains(needle, StringComparison.OrdinalIgnoreCase)) + return true; + + if (CheckMods(needle, cacheItem)) + return true; + + if (design.DesignData.ContainsName(needle)) + return true; + + if (design.Identifier.ToString().Contains(needle, StringComparison.OrdinalIgnoreCase)) + return true; + + return false; + } + + public bool WouldBeVisible(in FileSystemFolderCache folder) + { + switch (State) + { + case FilterState.NoFilters: return true; + case FilterState.NoMatches: return false; + } + + foreach (var token in Forced) + { + if (token.Type switch + { + DesignFilterTokenType.Name => !folder.Name.Contains(token.Needle, StringComparison.OrdinalIgnoreCase), + DesignFilterTokenType.Default => !folder.FullPath.Contains(token.Needle, StringComparison.OrdinalIgnoreCase), + DesignFilterTokenType.FullContext => !folder.FullPath.Contains(token.Needle, StringComparison.OrdinalIgnoreCase), + _ => true, + }) + return false; + } + + foreach (var token in Negated) + { + if (token.Type switch + { + DesignFilterTokenType.Name => folder.Name.Contains(token.Needle, StringComparison.OrdinalIgnoreCase), + DesignFilterTokenType.Default => folder.FullPath.Contains(token.Needle, StringComparison.OrdinalIgnoreCase), + DesignFilterTokenType.FullContext => folder.FullPath.Contains(token.Needle, StringComparison.OrdinalIgnoreCase), + _ => false, + }) + return false; + } + + foreach (var token in General) + { + if (token.Type switch + { + DesignFilterTokenType.Name => folder.Name.Contains(token.Needle, StringComparison.OrdinalIgnoreCase), + DesignFilterTokenType.Default => folder.FullPath.Contains(token.Needle, StringComparison.OrdinalIgnoreCase), + DesignFilterTokenType.FullContext => !folder.FullPath.Contains(token.Needle, StringComparison.OrdinalIgnoreCase), + _ => false, + }) + return true; + } + + return General.Count is 0; + } +} diff --git a/Glamourer/Gui/Tabs/DesignTab/Selector/DesignFilterToken.cs b/Glamourer/Gui/Tabs/DesignTab/Selector/DesignFilterToken.cs new file mode 100644 index 0000000..2df214f --- /dev/null +++ b/Glamourer/Gui/Tabs/DesignTab/Selector/DesignFilterToken.cs @@ -0,0 +1,49 @@ +using ImSharp; + +namespace Glamourer.Gui.Tabs.DesignTab; + +public enum DesignFilterTokenType +{ + Default, + Mod, + Tag, + Color, + Item, + Name, + FullContext, +} + +public readonly struct DesignFilterToken() : IFilterToken +{ + public string Needle { get; init; } = string.Empty; + public DesignFilterTokenType Type { get; init; } + + public bool Contains(DesignFilterToken other) + { + if (Type != other.Type) + return false; + + return Needle.Contains(other.Needle); + } + + public static bool ConvertToken(char tokenCharacter, out DesignFilterTokenType type) + { + type = tokenCharacter switch + { + 'm' or 'M' => DesignFilterTokenType.Mod, + 'n' or 'N' => DesignFilterTokenType.Name, + 't' or 'T' => DesignFilterTokenType.Tag, + 'i' or 'I' => DesignFilterTokenType.Item, + 'c' or 'C' => DesignFilterTokenType.Color, + 'f' or 'F' => DesignFilterTokenType.FullContext, + _ => DesignFilterTokenType.Default, + }; + return type is not DesignFilterTokenType.Default; + } + + public static bool AllowsNone(DesignFilterTokenType type) + => type is DesignFilterTokenType.Tag or DesignFilterTokenType.Mod; + + public static void ProcessList(List list) + { } +} diff --git a/Glamourer/Gui/Tabs/DesignTab/Selector/DuplicateDesignButton.cs b/Glamourer/Gui/Tabs/DesignTab/Selector/DuplicateDesignButton.cs new file mode 100644 index 0000000..e1e4405 --- /dev/null +++ b/Glamourer/Gui/Tabs/DesignTab/Selector/DuplicateDesignButton.cs @@ -0,0 +1,38 @@ +using Glamourer.Designs; +using ImSharp; +using Luna; + +namespace Glamourer.Gui.Tabs.DesignTab; + +public sealed class DuplicateDesignButton(DesignFileSystem fileSystem, DesignManager designManager) : BaseIconButton +{ + private readonly WeakReference _design = new(null!); + + public override AwesomeIcon Icon + => LunaStyle.DuplicateIcon; + + public override bool HasTooltip + => true; + + public override bool Enabled + => fileSystem.Selection.Selection is not null; + + public override void DrawTooltip() + => Im.Text(fileSystem.Selection.Selection is null ? "No design selected."u8 : "Clone the currently selected design to a duplicate."u8); + + public override void OnClick() + { + _design.SetTarget(fileSystem.Selection.Selection?.GetValue()!); + Im.Popup.Open("##CloneDesign"u8); + } + + protected override void PostDraw() + { + if (!InputPopup.OpenName("##CloneDesign"u8, out var newName)) + return; + + if (_design.TryGetTarget(out var design)) + designManager.CreateClone(design, newName, true); + _design.SetTarget(null!); + } +} diff --git a/Glamourer/Gui/Tabs/DesignTab/Selector/ImportDesignButton.cs b/Glamourer/Gui/Tabs/DesignTab/Selector/ImportDesignButton.cs new file mode 100644 index 0000000..cc5f03f --- /dev/null +++ b/Glamourer/Gui/Tabs/DesignTab/Selector/ImportDesignButton.cs @@ -0,0 +1,52 @@ +using Dalamud.Interface.ImGuiNotification; +using Glamourer.Designs; +using ImSharp; +using Luna; + +namespace Glamourer.Gui.Tabs.DesignTab; + +public sealed class ImportDesignButton(DesignConverter converter, DesignManager manager) : BaseIconButton +{ + private string _clipboardText = string.Empty; + + public override AwesomeIcon Icon + => LunaStyle.ImportIcon; + + public override bool HasTooltip + => true; + + public override void DrawTooltip() + => Im.Text("Try to import a design from your clipboard."u8); + + public override void OnClick() + { + try + { + _clipboardText = Im.Clipboard.GetUtf16(); + Im.Popup.Open("##ImportDesign"u8); + } + catch (Exception) + { + Glamourer.Messager.NotificationMessage("Could not import data from clipboard.", NotificationType.Error, false); + } + } + + protected override void PostDraw() + { + if (!InputPopup.OpenName("##ImportDesign"u8, out var newName)) + return; + + if (_clipboardText.Length is 0) + return; + + var design = converter.FromBase64(_clipboardText, true, true, out _); + if (design is Design d) + manager.CreateClone(d, newName, true); + else if (design is not null) + manager.CreateClone(design, newName, true); + else + Glamourer.Messager.NotificationMessage("Could not create a design, clipboard did not contain valid design data.", + NotificationType.Error, false); + _clipboardText = string.Empty; + } +} diff --git a/Glamourer/Gui/Tabs/DesignTab/Selector/MoveDesignInput.cs b/Glamourer/Gui/Tabs/DesignTab/Selector/MoveDesignInput.cs new file mode 100644 index 0000000..ca86538 --- /dev/null +++ b/Glamourer/Gui/Tabs/DesignTab/Selector/MoveDesignInput.cs @@ -0,0 +1,34 @@ +using ImSharp; +using Luna; + +namespace Glamourer.Gui.Tabs.DesignTab; + +public sealed class MoveDesignInput(DesignFileSystemDrawer fileSystem) : BaseButton +{ + /// + public override ReadOnlySpan Label(in IFileSystemData _) + => "##Move"u8; + + /// Replaces the normal menu item handling for a text input, so the other fields are not used. + /// + public override bool DrawMenuItem(in IFileSystemData data) + { + var currentPath = data.FullPath; + using var style = Im.Style.PushDefault(ImStyleDouble.FramePadding); + MenuSeparator.DrawSeparator(); + Im.Text("Move Design:"u8); + if (Im.Window.Appearing) + Im.Keyboard.SetFocusHere(); + var ret = Im.Input.Text(Label(data), ref currentPath, flags: InputTextFlags.EnterReturnsTrue); + Im.Tooltip.OnHover( + "Enter a full path here to move the design or change its search path. Creates all required parent directories, if possible."u8); + if (!ret) + return false; + + fileSystem.FileSystem.RenameAndMove(data, currentPath); + fileSystem.FileSystem.ExpandAllAncestors(data); + Im.Popup.CloseCurrent(); + + return ret; + } +} \ No newline at end of file diff --git a/Glamourer/Gui/Tabs/DesignTab/Selector/NewDesignButton.cs b/Glamourer/Gui/Tabs/DesignTab/Selector/NewDesignButton.cs new file mode 100644 index 0000000..258e077 --- /dev/null +++ b/Glamourer/Gui/Tabs/DesignTab/Selector/NewDesignButton.cs @@ -0,0 +1,28 @@ +using Glamourer.Designs; +using ImSharp; +using Luna; + +namespace Glamourer.Gui.Tabs.DesignTab; + +public sealed class NewDesignButton(DesignManager designManager) : BaseIconButton +{ + public override AwesomeIcon Icon + => LunaStyle.AddObjectIcon; + + public override bool HasTooltip + => true; + + public override void DrawTooltip() + => Im.Text("Create a new design with default configuration."u8); + + public override void OnClick() + => Im.Popup.Open("##NewDesign"u8); + + protected override void PostDraw() + { + if (!InputPopup.OpenName("##NewDesign"u8, out var newName)) + return; + + designManager.CreateEmpty(newName, true); + } +} diff --git a/Glamourer/Gui/Tabs/DesignTab/Selector/RenameDesignInput.cs b/Glamourer/Gui/Tabs/DesignTab/Selector/RenameDesignInput.cs new file mode 100644 index 0000000..2170c3a --- /dev/null +++ b/Glamourer/Gui/Tabs/DesignTab/Selector/RenameDesignInput.cs @@ -0,0 +1,34 @@ +using Glamourer.Designs; +using ImSharp; +using Luna; + +namespace Glamourer.Gui.Tabs.DesignTab; + +public sealed class RenameDesignInput(DesignFileSystemDrawer fileSystem) : BaseButton +{ + /// + public override ReadOnlySpan Label(in IFileSystemData _) + => "##Rename"u8; + + /// Replaces the normal menu item handling for a text input, so the other fields are not used. + /// + public override bool DrawMenuItem(in IFileSystemData data) + { + var design = (Design)data.Value; + var currentName = design.Name.Text; + using var style = Im.Style.PushDefault(ImStyleDouble.FramePadding); + MenuSeparator.DrawSeparator(); + Im.Text("Rename Design:"u8); + if (Im.Window.Appearing) + Im.Keyboard.SetFocusHere(); + var ret = Im.Input.Text(Label(data), ref currentName, flags: InputTextFlags.EnterReturnsTrue); + Im.Tooltip.OnHover("Enter a new name here to rename the changed design."u8); + if (!ret) + return false; + + fileSystem.Manager.Rename(design, currentName); + Im.Popup.CloseCurrent(); + + return ret; + } +} diff --git a/Glamourer/Gui/Tabs/DesignTab/SetFromClipboardButton.cs b/Glamourer/Gui/Tabs/DesignTab/SetFromClipboardButton.cs new file mode 100644 index 0000000..0e67431 --- /dev/null +++ b/Glamourer/Gui/Tabs/DesignTab/SetFromClipboardButton.cs @@ -0,0 +1,44 @@ +using Dalamud.Interface.ImGuiNotification; +using Glamourer.Designs; +using ImSharp; +using Luna; + +namespace Glamourer.Gui.Tabs.DesignTab; + +public sealed class SetFromClipboardButton(DesignFileSystem fileSystem, DesignConverter converter, DesignManager manager) + : BaseIconButton +{ + public override bool IsVisible + => fileSystem.Selection.Selection is not null; + + public override AwesomeIcon Icon + => LunaStyle.FromClipboardIcon; + + public override bool Enabled + => !((Design)fileSystem.Selection.Selection!.Value).WriteProtected(); + + public override bool HasTooltip + => true; + + public override void DrawTooltip() + => Im.Text( + "Try to apply a design from your clipboard over this design.\nHold Control to only apply gear.\nHold Shift to only apply customizations."u8); + + public override void OnClick() + { + try + { + var text = Im.Clipboard.GetUtf16(); + var (applyEquip, applyCustomize) = UiHelpers.ConvertKeysToBool(); + var design = converter.FromBase64(text, applyCustomize, applyEquip, out _) + ?? throw new Exception("The clipboard did not contain valid data."); + manager.ApplyDesign((Design)fileSystem.Selection.Selection!.Value, design); + } + catch (Exception ex) + { + Glamourer.Messager.NotificationMessage(ex, $"Could not apply clipboard to {((Design)fileSystem.Selection.Selection!.Value).Name}.", + $"Could not apply clipboard to design {((Design)fileSystem.Selection.Selection!.Value).Identifier}", NotificationType.Error, + false); + } + } +} diff --git a/Glamourer/Gui/Tabs/DesignTab/UndoButton.cs b/Glamourer/Gui/Tabs/DesignTab/UndoButton.cs new file mode 100644 index 0000000..889893a --- /dev/null +++ b/Glamourer/Gui/Tabs/DesignTab/UndoButton.cs @@ -0,0 +1,27 @@ +using Glamourer.Designs; +using Glamourer.Designs.History; +using ImSharp; +using Luna; + +namespace Glamourer.Gui.Tabs.DesignTab; + +public sealed class UndoButton(DesignFileSystem fileSystem, EditorHistory history) : BaseIconButton +{ + public override bool IsVisible + => fileSystem.Selection.Selection is not null; + + public override AwesomeIcon Icon + => LunaStyle.UndoIcon; + + public override bool Enabled + => !((Design)fileSystem.Selection.Selection!.Value).WriteProtected() && history.CanUndo((Design)fileSystem.Selection.Selection!.Value); + + public override bool HasTooltip + => true; + + public override void DrawTooltip() + => Im.Text("Undo the last change."u8); + + public override void OnClick() + => history.Undo((Design)fileSystem.Selection.Selection!.Value); +} diff --git a/Glamourer/Gui/Tabs/HeaderDrawer.cs b/Glamourer/Gui/Tabs/HeaderDrawer.cs deleted file mode 100644 index 6025b0c..0000000 --- a/Glamourer/Gui/Tabs/HeaderDrawer.cs +++ /dev/null @@ -1,80 +0,0 @@ -using Dalamud.Interface; -using Dalamud.Bindings.ImGui; -using ImSharp; -using OtterGui; -using OtterGui.Raii; - -namespace Glamourer.Gui.Tabs; - -public static class HeaderDrawer -{ - public abstract class Button - { - protected abstract void OnClick(); - - protected virtual string Description - => string.Empty; - - protected virtual Rgba32 BorderColor - => ColorId.HeaderButtons.Value(); - - protected virtual Rgba32 TextColor - => ColorId.HeaderButtons.Value(); - - protected virtual FontAwesomeIcon Icon - => FontAwesomeIcon.None; - - protected virtual bool Disabled - => false; - - public virtual bool Visible - => true; - - public void Draw(float width) - { - if (!Visible) - return; - - using var color = ImGuiColor.Border.Push(BorderColor) - .Push(ImGuiColor.Text, TextColor, TextColor.IsVisible); - if (ImGuiUtil.DrawDisabledButton(Icon.ToIconString(), new Vector2(width, Im.Style.FrameHeight), string.Empty, Disabled, true)) - OnClick(); - color.Pop(); - ImGuiUtil.HoverTooltip(Description); - } - } - - public static void Draw(string text, uint textColor, uint frameColor, Button[] leftButtons, Button[] rightButtons) - { - var width = Im.Style.FrameHeightWithSpacing; - using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero) - .Push(ImGuiStyleVar.FrameRounding, 0) - .Push(ImGuiStyleVar.FrameBorderSize, Im.Style.GlobalScale); - - var leftButtonSize = 0f; - foreach (var button in leftButtons.Where(b => b.Visible)) - { - button.Draw(width); - Im.Line.Same(); - leftButtonSize += width; - } - - var rightButtonSize = rightButtons.Count(b => b.Visible) * width; - var midSize = Im.ContentRegion.Available.X - rightButtonSize - Im.Style.GlobalScale; - - style.Pop(); - style.Push(ImGuiStyleVar.ButtonTextAlign, new Vector2(0.5f + (rightButtonSize - leftButtonSize) / midSize, 0.5f)); - if (textColor != 0) - ImGuiUtil.DrawTextButton(text, new Vector2(midSize, Im.Style.FrameHeight), frameColor, textColor); - else - ImGuiUtil.DrawTextButton(text, new Vector2(midSize, Im.Style.FrameHeight), frameColor); - style.Pop(); - style.Push(ImGuiStyleVar.FrameBorderSize, Im.Style.GlobalScale); - - foreach (var button in rightButtons.Where(b => b.Visible)) - { - Im.Line.Same(); - button.Draw(width); - } - } -} \ No newline at end of file diff --git a/Glamourer/Gui/Tabs/IncognitoButton.cs b/Glamourer/Gui/Tabs/IncognitoButton.cs index a7f3eb9..46f3394 100644 --- a/Glamourer/Gui/Tabs/IncognitoButton.cs +++ b/Glamourer/Gui/Tabs/IncognitoButton.cs @@ -1,9 +1,10 @@ -using ImSharp; +using Glamourer.Config; +using ImSharp; using Luna; namespace Glamourer.Gui.Tabs; -public sealed class IncognitoButton(Configuration.Configuration config) : BaseIconButton, IUiService +public sealed class IncognitoButton(Configuration config) : BaseIconButton, IUiService { public override AwesomeIcon Icon => config.Ephemeral.IncognitoMode diff --git a/Glamourer/Gui/Tabs/NpcTab/NpcPanel.cs b/Glamourer/Gui/Tabs/NpcTab/NpcPanel.cs index 4b0daa6..3c3376e 100644 --- a/Glamourer/Gui/Tabs/NpcTab/NpcPanel.cs +++ b/Glamourer/Gui/Tabs/NpcTab/NpcPanel.cs @@ -1,5 +1,5 @@ using FFXIVClientStructs.FFXIV.Client.Game.Object; -using Glamourer.Configuration; +using Glamourer.Config; using Glamourer.Designs; using Glamourer.Gui.Customization; using Glamourer.Gui.Equipment; @@ -13,7 +13,7 @@ using Penumbra.GameData.Interop; namespace Glamourer.Gui.Tabs.NpcTab; public sealed class NpcPanel( - Configuration.Configuration config, + Configuration config, NpcSelection selection, CustomizationDrawer customizeDrawer, EquipmentDrawer equipmentDrawer, diff --git a/Glamourer/Gui/Tabs/NpcTab/NpcTab.cs b/Glamourer/Gui/Tabs/NpcTab/NpcTab.cs index 60c64a9..4b420c6 100644 --- a/Glamourer/Gui/Tabs/NpcTab/NpcTab.cs +++ b/Glamourer/Gui/Tabs/NpcTab/NpcTab.cs @@ -1,4 +1,4 @@ -using Glamourer.Configuration; +using Glamourer.Config; using ImSharp; using Luna; diff --git a/Glamourer/Gui/Tabs/SettingsTab/CodeDrawer.cs b/Glamourer/Gui/Tabs/SettingsTab/CodeDrawer.cs index ca27f95..9beb9ff 100644 --- a/Glamourer/Gui/Tabs/SettingsTab/CodeDrawer.cs +++ b/Glamourer/Gui/Tabs/SettingsTab/CodeDrawer.cs @@ -1,16 +1,12 @@ -using Dalamud.Interface; +using Glamourer.Config; using Glamourer.Services; using Glamourer.State; using ImSharp; using Luna; -using OtterGui.Filesystem; -using OtterGui.Raii; -using OtterGui.Text; -using OtterGui.Text.EndObjects; namespace Glamourer.Gui.Tabs.SettingsTab; -public class CodeDrawer(Configuration.Configuration config, CodeService codeService, FunModule funModule) : IUiService +public class CodeDrawer(Configuration config, CodeService codeService, FunModule funModule) : IUiService { private static ReadOnlySpan Tooltip => "Cheat Codes are not actually for cheating in the game, but for 'cheating' in Glamourer. "u8 @@ -41,7 +37,7 @@ public class CodeDrawer(Configuration.Configuration config, CodeService codeServ private void DrawCodeInput() { var color = codeService.CheckCode(_currentCode).Item2 is not 0 ? ColorId.ActorAvailable : ColorId.ActorUnavailable; - using var border = ImRaii.PushFrameBorder(Im.Style.GlobalScale, color.Value().Color, _currentCode.Length > 0); + using var border = ImStyleBorder.Frame.Push(color.Value(), Im.Style.GlobalScale, _currentCode.Length > 0); Im.Item.SetNextWidth(500 * Im.Style.GlobalScale + Im.Style.ItemSpacing.X); if (Im.Input.Text("##Code"u8, ref _currentCode, "Enter Cheat Code..."u8, InputTextFlags.EnterReturnsTrue)) { @@ -50,24 +46,22 @@ public class CodeDrawer(Configuration.Configuration config, CodeService codeServ } Im.Line.Same(); - ImUtf8.Icon(FontAwesomeIcon.ExclamationCircle, ImGuiColor.TextDisabled.Get().Color); + ImEx.Icon.Draw(LunaStyle.WarningIcon, ImGuiColor.TextDisabled.Get()); DrawTooltip(); } private void DrawCopyButtons() { - var buttonSize = new Vector2(250 * Im.Style.GlobalScale, 0); - if (ImUtf8.Button("Who am I?!?"u8, buttonSize)) + var buttonSize = ImEx.ScaledVectorX(250); + if (Im.Button("Who am I?!?"u8, buttonSize)) funModule.WhoAmI(); - ImUtf8.HoverTooltip( - "Copy your characters actual current appearance including cheat codes or holiday events to the clipboard as a design."u8); + Im.Tooltip.OnHover("Copy your characters actual current appearance including cheat codes or holiday events to the clipboard as a design."u8); Im.Line.Same(); - if (ImUtf8.Button("Who is that!?!"u8, buttonSize)) + if (Im.Button("Who is that!?!"u8, buttonSize)) funModule.WhoIsThat(); - ImUtf8.HoverTooltip( - "Copy your targets actual current appearance including cheat codes or holiday events to the clipboard as a design."u8); + Im.Tooltip.OnHover("Copy your targets actual current appearance including cheat codes or holiday events to the clipboard as a design."u8); } private CodeService.CodeFlag DrawCodes() @@ -76,7 +70,7 @@ public class CodeDrawer(Configuration.Configuration config, CodeService codeServ CodeService.CodeFlag knownFlags = 0; for (var i = 0; i < config.Codes.Count; ++i) { - using var id = ImUtf8.PushId(i); + using var id = Im.Id.Push(i); var (code, state) = config.Codes[i]; var (action, flag) = codeService.CheckCode(code); if (flag is 0) @@ -84,8 +78,7 @@ public class CodeDrawer(Configuration.Configuration config, CodeService codeServ var data = CodeService.GetData(flag); - if (ImUtf8.IconButton(FontAwesomeIcon.Trash, - $"Delete this cheat code.{(canDelete ? string.Empty : $"\nHold {config.DeleteDesignModifier} while clicking to delete.")}", + if (ImEx.Icon.Button(LunaStyle.DeleteIcon, $"Delete this cheat code.{(canDelete ? StringU8.Empty : $"\nHold {config.DeleteDesignModifier} while clicking to delete.")}", disabled: !canDelete)) { action!(false); @@ -95,7 +88,7 @@ public class CodeDrawer(Configuration.Configuration config, CodeService codeServ knownFlags |= flag; Im.Line.SameInner(); - if (ImUtf8.Checkbox("\0"u8, ref state)) + if (Im.Checkbox(StringU8.Empty, ref state)) { action!(state); codeService.SaveState(); @@ -103,14 +96,14 @@ public class CodeDrawer(Configuration.Configuration config, CodeService codeServ var hovered = Im.Item.Hovered(); Im.Line.Same(); - ImUtf8.Selectable(code); + Im.Selectable(code); hovered |= Im.Item.Hovered(); DrawSource(i, code); DrawTarget(i); if (hovered) { - using var tt = ImUtf8.Tooltip(); - ImUtf8.Text(data.Effect); + using var tt = Im.Tooltip.Begin(); + Im.Text(data.Effect); } } @@ -119,22 +112,22 @@ public class CodeDrawer(Configuration.Configuration config, CodeService codeServ private void DrawSource(int idx, string code) { - using var source = ImUtf8.DragDropSource(); + using var source = Im.DragDrop.Source(); if (!source) return; - if (!DragDropSource.SetPayload(DragDropLabel)) + if (!source.SetPayload(DragDropLabel)) _dragCodeIdx = idx; - ImUtf8.Text($"Dragging {code}..."); + Im.Text($"Dragging {code}..."); } private void DrawTarget(int idx) { - using var target = ImUtf8.DragDropTarget(); - if (!target.IsDropping(DragDropLabel) || _dragCodeIdx == -1) + using var target = Im.DragDrop.Target(); + if (!target.IsDropping(DragDropLabel) || _dragCodeIdx is -1) return; - if (Extensions.Move(config.Codes, _dragCodeIdx, idx)) + if (config.Codes.Move(_dragCodeIdx, idx)) codeService.SaveState(); _dragCodeIdx = -1; } @@ -144,7 +137,7 @@ public class CodeDrawer(Configuration.Configuration config, CodeService codeServ if (knownFlags.HasFlag(CodeService.AllHintCodes)) return; - if (ImUtf8.Button(_showCodeHints ? "Hide Hints"u8 : "Show Hints"u8)) + if (Im.Button(_showCodeHints ? "Hide Hints"u8 : "Show Hints"u8)) _showCodeHints = !_showCodeHints; if (!_showCodeHints) @@ -162,23 +155,23 @@ public class CodeDrawer(Configuration.Configuration config, CodeService codeServ Im.Dummy(Vector2.Zero); Im.Separator(); Im.Dummy(Vector2.Zero); - ImUtf8.Text(data.Effect); - using var indent = ImRaii.PushIndent(2); - using (ImUtf8.Group()) + Im.Text(data.Effect); + using var indent = Im.Indent(2); + using (Im.Group()) { - ImUtf8.Text("Capitalized letters: "u8); - ImUtf8.Text("Punctuation: "u8); + Im.Text("Capitalized letters: "u8); + Im.Text("Punctuation: "u8); } Im.Line.SameInner(); - using (ImUtf8.Group()) + using (Im.Group()) { using var mono = Im.Font.PushMono(); - ImUtf8.Text($"{data.CapitalCount}"); - ImUtf8.Text($"{data.Punctuation}"); + Im.Text($"{data.CapitalCount}"); + Im.Text($"{data.Punctuation}"); } - ImUtf8.TextWrapped(data.Hint); + Im.TextWrapped(data.Hint); } } @@ -189,7 +182,7 @@ public class CodeDrawer(Configuration.Configuration config, CodeService codeServ return; Im.Window.SetNextSize(new Vector2(400, 0)); - using var tt = ImUtf8.Tooltip(); - ImUtf8.TextWrapped(Tooltip); + using var tt = Im.Tooltip.Begin(); + Im.TextWrapped(Tooltip); } } diff --git a/Glamourer/Gui/Tabs/SettingsTab/CollectionCombo.cs b/Glamourer/Gui/Tabs/SettingsTab/CollectionCombo.cs index 99b91a7..a8ebfb2 100644 --- a/Glamourer/Gui/Tabs/SettingsTab/CollectionCombo.cs +++ b/Glamourer/Gui/Tabs/SettingsTab/CollectionCombo.cs @@ -1,35 +1,82 @@ -using Dalamud.Interface; +using Glamourer.Config; using Glamourer.Interop.Penumbra; using ImSharp; using Luna; -using OtterGui.Widgets; -using Logger = OtterGui.Log.Logger; -using MouseWheelType = OtterGui.Widgets.MouseWheelType; namespace Glamourer.Gui.Tabs.SettingsTab; -public sealed class CollectionCombo(Configuration.Configuration config, PenumbraService penumbra, Logger log) - : FilterComboCache<(Guid Id, string IdShort, string Name)>( - () => penumbra.GetCollections().Select(kvp => (kvp.Key, kvp.Key.ToString()[..8], kvp.Value)).ToArray(), - MouseWheelType.Control, log), IUiService +public sealed class CollectionCombo(Configuration config, PenumbraService penumbra) + : FilterComboBase(new CollectionFilter()), IUiService { - protected override bool DrawSelectable(int globalIdx, bool selected) + private Guid _selected = Guid.Empty; + + public readonly struct CacheItem(Guid id, string name) + { + public readonly StringPair Name = new(name); + public readonly Guid Id = id; + public readonly StringU8 Incognito = id.ShortGuidU8(); + public readonly StringU8 ShortId = new($"({id.ShortGuidU8()})"); + } + + protected override float ItemHeight + => Im.Style.TextHeightWithSpacing; + + protected override IEnumerable GetItems() + => penumbra.GetCollections().Select(kvp => new CacheItem(kvp.Key, kvp.Value)); + + public bool Draw(Utf8StringHandler label, Utf8StringHandler preview, out string newName, + ref Guid id, float width) + { + _selected = id; + if (!base.Draw(label, preview, StringU8.Empty, width, out var ret)) + { + newName = string.Empty; + return false; + } + + newName = ret.Name.Utf16; + id = ret.Id; + return true; + } + + protected override bool DrawItem(in CacheItem item, int globalIndex, bool selected) { - var (_, idShort, name) = Items[globalIdx]; if (config.Ephemeral.IncognitoMode) using (Im.Font.PushMono()) { - return Im.Selectable(idShort); + return Im.Selectable(item.Incognito, selected); } - var ret = Im.Selectable(name, selected); + var ret = Im.Selectable(item.Name.Utf8, selected); Im.Line.Same(); + using (Im.Font.PushMono()) { using var color = ImGuiColor.Text.Push(ImGuiColor.TextDisabled.Get()); - ImEx.TextRightAligned($"({idShort})"); + ImEx.TextRightAligned(item.ShortId); } return ret; } + + protected override bool IsSelected(CacheItem item, int globalIndex) + => item.Id == _selected; + + private sealed class CollectionFilter : Utf8FilterBase + { + public override bool WouldBeVisible(in CacheItem item, int globalIndex) + => base.WouldBeVisible(in item, globalIndex) || WouldBeVisible(item.Incognito); + + protected override ReadOnlySpan ToFilterString(in CacheItem item, int globalIndex) + => item.Name; + } + + protected override FilterComboBaseCache CreateCache() + => new Cache(this); + + private sealed class Cache(CollectionCombo parent) : FilterComboBaseCache(parent) + { + protected override void ComputeWidth() + => ComboWidth = AllItems.Max(i => i.Name.Utf8.CalculateSize().X + Im.Style.ItemSpacing.X * 2 + i.ShortId.CalculateSize().X); + } } diff --git a/Glamourer/Gui/Tabs/SettingsTab/CollectionOverrideDrawer.cs b/Glamourer/Gui/Tabs/SettingsTab/CollectionOverrideDrawer.cs index a98eb71..dcbae3c 100644 --- a/Glamourer/Gui/Tabs/SettingsTab/CollectionOverrideDrawer.cs +++ b/Glamourer/Gui/Tabs/SettingsTab/CollectionOverrideDrawer.cs @@ -1,4 +1,5 @@ using Dalamud.Interface; +using Glamourer.Config; using Glamourer.Interop.Penumbra; using Glamourer.Services; using ImSharp; @@ -10,7 +11,7 @@ namespace Glamourer.Gui.Tabs.SettingsTab; public class CollectionOverrideDrawer( CollectionOverrideService collectionOverrides, - Configuration.Configuration config, + Configuration config, ActorObjectManager objects, ActorManager actors, PenumbraService penumbra, @@ -58,18 +59,15 @@ public class CollectionOverrideDrawer( DrawActorIdentifier(idx, actor); table.NextColumn(); - if (combo.Draw("##collection", name, "Select the overriding collection. Current GUID:", Im.ContentRegion.Available.X, - Im.Style.TextHeight)) - { - var (guid, _, newName) = combo.CurrentSelection; - collectionOverrides.ChangeOverride(idx, guid, newName); - } + if (combo.Draw("##collection"u8, name, out var newName, ref collection, Im.ContentRegion.Available.X)) + collectionOverrides.ChangeOverride(idx, collection, newName); if (Im.Item.Hovered()) { - using var tt = Im.Tooltip.Begin(); - using var font = Im.Font.PushMono(); - Im.Text($" {collection}"); + using var tt = Im.Tooltip.Begin(); + Im.Text("Select the overriding collection. Current GUID:"u8); + using var indent = Im.Indent(); + ImEx.MonoText($"{collection}"); } table.NextColumn(); diff --git a/Glamourer/Gui/Tabs/SettingsTab/SettingsTab.cs b/Glamourer/Gui/Tabs/SettingsTab/SettingsTab.cs index 97fa1e3..de01743 100644 --- a/Glamourer/Gui/Tabs/SettingsTab/SettingsTab.cs +++ b/Glamourer/Gui/Tabs/SettingsTab/SettingsTab.cs @@ -2,7 +2,7 @@ using Dalamud.Interface; using Dalamud.Plugin.Services; using Glamourer.Automation; -using Glamourer.Configuration; +using Glamourer.Config; using Glamourer.Designs; using Glamourer.Events; using Glamourer.Gui.Tabs.DesignTab; @@ -15,8 +15,8 @@ using Luna; namespace Glamourer.Gui.Tabs.SettingsTab; public sealed class SettingsTab( - Configuration.Configuration config, - DesignFileSystemSelector selector, + Configuration config, + DesignFileSystemDrawer drawer, ContextMenuService contextMenuService, IUiBuilder uiBuilder, GlamourerChangelog changelog, @@ -28,7 +28,8 @@ public sealed class SettingsTab( Glamourer glamourer, AutoDesignApplier autoDesignApplier, AutoRedrawChanged autoRedraw, - PcpService pcpService) + PcpService pcpService, + IgnoredMods ignoredMods) : ITab { private readonly VirtualKey[] _validKeys = keys.GetValidVirtualKeys().Prepend(VirtualKey.NO_KEY).ToArray(); @@ -61,6 +62,7 @@ public sealed class SettingsTab( DrawInterfaceSettings(); DrawColorSettings(); overrides.Draw(); + DrawIgnoredMods(); codeDrawer.Draw(); } @@ -256,7 +258,7 @@ public sealed class SettingsTab( Im.Line.New(); Im.Text("Show the following panels in their respective tabs:"u8); Im.Dummy(Vector2.Zero); - Configuration.DesignPanelFlagExtensions.DrawTable("##panelTable"u8, config.HideDesignPanel, config.AutoExpandDesignPanel, v => + DesignPanelFlagExtensions.DrawTable("##panelTable"u8, config.HideDesignPanel, config.AutoExpandDesignPanel, v => { config.HideDesignPanel = v; config.Save(); @@ -280,8 +282,8 @@ public sealed class SettingsTab( Checkbox("Show Unobtained Item Warnings"u8, "Show information whether you have unlocked all items and customizations in your automated design or not."u8, config.ShowUnlockedItemWarnings, v => config.ShowUnlockedItemWarnings = v); - Checkbox("Show Color Display Config"u8, "Show the Color Display configuration options in the Advanced Customization panels."u8, - config.ShowColorConfig, v => config.ShowColorConfig = v); + Checkbox("Show Color Display Configuration"u8, "Show the Color Display configuration options in the Advanced Customization panels."u8, + config.ShowColorConfig, v => config.ShowColorConfig = v); Checkbox("Show Palette+ Import Button"u8, "Show the import button that allows you to import Palette+ palettes onto a design in the Advanced Customization options section for designs."u8, config.ShowPalettePlusImport, v => config.ShowPalettePlusImport = v); @@ -376,7 +378,7 @@ public sealed class SettingsTab( if (Im.Button("Import Palette+ to Designs"u8)) paletteImport.ImportDesigns(); Im.Tooltip.OnHover( - $"Import all existing Palettes from your Palette+ Config into Designs at PalettePlus/[Name] if these do not exist. Existing Palettes are:\n\n\t - {string.Join("\n\t - ", paletteImport.Data.Keys)}"); + $"Import all existing Palettes from your Palette+ Configuration into Designs at PalettePlus/[Name] if these do not exist. Existing Palettes are:\n\n\t - {string.Join("\n\t - ", paletteImport.Data.Keys)}"); } /// Draw the entire Color subsection. @@ -402,6 +404,7 @@ public sealed class SettingsTab( continue; config.Colors[color] = newColor.Color; + CacheManager.Instance.SetColorsDirty(); config.Save(); } } @@ -445,20 +448,20 @@ public sealed class SettingsTab( using (var combo = Im.Combo.Begin("##sortMode"u8, sortMode.Name)) { if (combo) - foreach (var val in Configuration.Configuration.Constants.ValidSortModes) + foreach (var (_, value) in ISortMode.Valid) { - if (Im.Selectable(val.Name, val.GetType() == sortMode.GetType()) && val.GetType() != sortMode.GetType()) + if (Im.Selectable(value.Name, value.GetType() == sortMode.GetType()) && value.GetType() != sortMode.GetType()) { - config.SortMode = val; - selector.SetFilterDirty(); + config.SortMode = value; + drawer.SortMode = value; config.Save(); } - Im.Tooltip.OnHover(val.Description); + Im.Tooltip.OnHover(value.Description); } } - LunaStyle.DrawAlignedHelpMarkerLabel("Sort Mode"u8, "Choose the sort mode for the mod selector in the designs tab."u8); + LunaStyle.DrawAlignedHelpMarkerLabel("Sort Mode"u8, "Choose the sort mode for the design selector in the designs tab."u8); } private void DrawRenameSettings() @@ -472,7 +475,6 @@ public sealed class SettingsTab( if (Im.Selectable(value.ToNameU8(), config.ShowRename == value)) { config.ShowRename = value; - selector.SetRenameSearchPath(value); config.Save(); } @@ -503,4 +505,46 @@ public sealed class SettingsTab( LunaStyle.DrawAlignedHelpMarkerLabel("Character Height Display Type"u8, "Select how to display the height of characters in real-world units, if at all."u8); } + + private string _newIgnoredMod = string.Empty; + + private void DrawIgnoredMods() + { + using var header = Im.Tree.HeaderId("Ignored Mods"u8); + Im.Tooltip.OnHover("Add mods that are ignored for the 'modded' column in the Unlocks tab."u8); + if (!header) + return; + + using var listBox = Im.ListBox.Begin("##box"u8, new Vector2(0.4f * Im.ContentRegion.Available.X, Im.Style.FrameHeightWithSpacing * 10)); + if (!listBox) + return; + + var delete = string.Empty; + using var alignment = ImStyleDouble.ButtonTextAlign.PushX(0); + foreach (var (idx, mod) in ignoredMods.Index()) + { + using var id = Im.Id.Push(idx); + if (ImEx.Icon.Button(LunaStyle.DeleteIcon, "Delete this ignored mod."u8)) + delete = mod; + + Im.Line.SameInner(); + ImEx.TextFramed(mod, Im.ContentRegion.Available with { Y = Im.Style.FrameHeight}); + } + + if (delete.Length > 0) + ignoredMods.Remove(delete); + + var tt = _newIgnoredMod.Length is 0 ? "Please enter a new mod name or mod directory to ignore."u8 : + ignoredMods.Contains(_newIgnoredMod) ? "This mod is already ignored."u8 : + "Ignore all mods with this name or directory in the Unlocks tab."u8; + if (ImEx.Icon.Button(LunaStyle.AddObjectIcon, tt, tt[0] is not (byte)'I')) + { + ignoredMods.Add(_newIgnoredMod); + _newIgnoredMod = string.Empty; + } + + Im.Line.SameInner(); + Im.Item.SetNextWidthFull(); + Im.Input.Text("##newMod"u8, ref _newIgnoredMod, "Ignore this Mod..."u8); + } } diff --git a/Glamourer/Gui/Tabs/UnlocksTab/UnlockCacheItem.cs b/Glamourer/Gui/Tabs/UnlocksTab/UnlockCacheItem.cs index e59e8d0..fb305c6 100644 --- a/Glamourer/Gui/Tabs/UnlocksTab/UnlockCacheItem.cs +++ b/Glamourer/Gui/Tabs/UnlocksTab/UnlockCacheItem.cs @@ -38,7 +38,8 @@ public readonly struct UnlockCacheItem(in EquipItem item, in EquipItem offhand, public readonly StringPair OffhandModelString = offhand.Valid ? new StringPair(offhand.ModelString) : StringPair.Empty; public readonly StringPair GauntletModelString = gauntlets.Valid ? new StringPair(gauntlets.ModelString) : StringPair.Empty; public readonly StringPair RequiredLevel = new($"{item.Level.Value}"); - public required (string, string)[] Mods { get; init; } + public required (string, string)[] Mods { get; init; } + public int RelevantMods { get; init; } public readonly JobFlag Jobs = jobs.Flags; public readonly StringU8 JobText = jobs.Name.IsEmpty ? new StringU8($"Unknown {jobs.Id.Id}") : jobs.Name; public required bool Favorite { get; init; } diff --git a/Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs b/Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs index f6c747c..6d16d8b 100644 --- a/Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs +++ b/Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs @@ -1,6 +1,7 @@ using Dalamud.Game.Text.SeStringHandling; using Dalamud.Interface; using Dalamud.Interface.Utility.Table; +using Glamourer.Config; using Glamourer.Events; using Glamourer.Interop; using Glamourer.Interop.Penumbra; @@ -23,9 +24,10 @@ public sealed class UnlockTable : TableBase private readonly FavoriteManager _favorites; private readonly PenumbraService _penumbra; private readonly ObjectUnlocked _unlockEvent; + private readonly IgnoredMods _ignoredMods; public UnlockTable(JobService jobs, ItemManager items, ItemUnlockManager unlocks, PenumbraChangedItemTooltip tooltip, - ObjectUnlocked unlockEvent, FavoriteManager favorites, PenumbraService penumbra, TextureService textures) + ObjectUnlocked unlockEvent, FavoriteManager favorites, PenumbraService penumbra, TextureService textures, IgnoredMods ignoredMods) : base(new StringU8("Unlock Table"u8), new FavoriteColumn(favorites), new ModdedColumn(), new NameColumn(textures, tooltip), new SlotColumn(), new TypeColumn(), new UnlockDateColumn(), new ItemIdColumn(), new ModelDataColumn(), new JobColumn(jobs), new RequiredLevelColumn(), new DyableColumn(), new CrestColumn(), new TradableColumn()) @@ -36,6 +38,7 @@ public sealed class UnlockTable : TableBase _unlockEvent = unlockEvent; _favorites = favorites; _penumbra = penumbra; + _ignoredMods = ignoredMods; Flags |= TableFlags.Hideable | TableFlags.Reorderable | TableFlags.Resizable; } @@ -72,6 +75,7 @@ public sealed class UnlockTable : TableBase UnlockTimestamp = unlocked, Mods = mods, Favorite = favorite, + RelevantMods = mods.Count(m => !_ignoredMods.Contains(m.ModName) && !_ignoredMods.Contains(m.ModDirectory)), }; } @@ -94,24 +98,29 @@ public sealed class UnlockTable : TableBase => Im.Style.FrameHeightWithSpacing; public override void DrawColumn(in UnlockCacheItem item, int globalIndex) - { - Im.Cursor.FrameAlign(); - UiHelpers.DrawFavoriteStar(_favorites, item.Item); - } + => UiHelpers.DrawFavoriteStar(_favorites, item.Item); protected override bool GetValue(in UnlockCacheItem item, int globalIndex, int triEnumIndex) => item.Favorite; } - private sealed class ModdedColumn : YesNoColumn + private sealed class ModdedColumn : FlagColumn { - private static readonly AwesomeIcon Dot = FontAwesomeIcon.Circle; + [Flags] + public enum Modded + { + Relevant = 1, + Ignored = 2, + None = 4, + } + + private static readonly AwesomeIcon Dot = FontAwesomeIcon.Circle; + private static readonly AwesomeIcon Hollow = FontAwesomeIcon.DotCircle; public ModdedColumn() { - Flags |= TableColumnFlags.NoResize; - Label = new StringU8("M"); - FilterLabel = new StringU8("Modded"u8); + Flags |= TableColumnFlags.NoResize; + Label = new StringU8("M"); } public override float ComputeWidth(IEnumerable allItems) @@ -124,8 +133,11 @@ public sealed class UnlockTable : TableBase using (AwesomeIcon.Font.Push()) { - using var color = ImGuiColor.Text.Push(ColorId.ModdedItemMarker.Value()); - ImEx.TextCentered(Dot.Span); + var (color, text) = item.RelevantMods > 0 + ? (ColorId.ModdedItemMarker.Value(), Dot) + : (ColorId.ModdedItemMarker.Value().HalfTransparent(), Hollow); + using var c = ImGuiColor.Text.Push(color); + Im.Text(text.Span); } if (Im.Item.Hovered()) @@ -137,11 +149,29 @@ public sealed class UnlockTable : TableBase } } - protected override bool GetValue(in UnlockCacheItem item, int globalIndex, int triEnumIndex) - => item.Mods.Length > 0; + protected override Modded GetValue(in UnlockCacheItem item, int globalIndex) + => item.RelevantMods > 0 ? Modded.Relevant : item.Mods.Length > 0 ? Modded.Ignored : Modded.None; + + protected override StringU8 DisplayString(in UnlockCacheItem item, int globalIndex) + => StringU8.Empty; + + protected override IReadOnlyList<(Modded Value, StringU8 Name)> EnumData + => + [ + (Modded.Relevant, new StringU8("Any Relevant Mods"u8)), + (Modded.Ignored, new StringU8("Only Ignored Mods"u8)), + (Modded.None, new StringU8("Unmodded"u8)), + ]; + public override int Compare(in UnlockCacheItem lhs, int lhsGlobalIndex, in UnlockCacheItem rhs, int rhsGlobalIndex) - => lhs.Mods.Length.CompareTo(rhs.Mods.Length); + { + var relevant = lhs.RelevantMods.CompareTo(rhs.RelevantMods); + if (relevant is not 0) + return relevant; + + return lhs.Mods.Length.CompareTo(rhs.Mods.Length); + } } private sealed class NameColumn : TextColumn @@ -241,9 +271,9 @@ public sealed class UnlockTable : TableBase { public UnlockDateColumn() { - Flags &= ~TableColumnFlags.NoResize; - Label = new StringU8("Unlocked"u8); - FilterLabel = Label; + Flags &= ~TableColumnFlags.NoResize; + Label = new StringU8("Unlocked"u8); + FilterLabel = Label; } public override float ComputeWidth(IEnumerable allItems) diff --git a/Glamourer/Gui/Tabs/UnlocksTab/UnlocksTab.cs b/Glamourer/Gui/Tabs/UnlocksTab/UnlocksTab.cs index 3be59f7..3574be3 100644 --- a/Glamourer/Gui/Tabs/UnlocksTab/UnlocksTab.cs +++ b/Glamourer/Gui/Tabs/UnlocksTab/UnlocksTab.cs @@ -5,11 +5,11 @@ namespace Glamourer.Gui.Tabs.UnlocksTab; public sealed class UnlocksTab : Window, ITab { - private readonly Configuration.EphemeralConfig _config; + private readonly Config.EphemeralConfig _config; private readonly UnlockOverview _overview; private readonly UnlockTable _table; - public UnlocksTab(Configuration.EphemeralConfig config, UnlockOverview overview, UnlockTable table) + public UnlocksTab(Config.EphemeralConfig config, UnlockOverview overview, UnlockTable table) : base("Unlocked Equipment") { _config = config; diff --git a/Glamourer/Interop/ContextMenuService.cs b/Glamourer/Interop/ContextMenuService.cs index 32260ec..4805caf 100644 --- a/Glamourer/Interop/ContextMenuService.cs +++ b/Glamourer/Interop/ContextMenuService.cs @@ -1,6 +1,7 @@ using Dalamud.Game.Gui.ContextMenu; using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.UI.Agent; +using Glamourer.Config; using Glamourer.Designs; using Glamourer.Services; using Glamourer.State; @@ -23,7 +24,7 @@ public class ContextMenuService : IDisposable private readonly MenuItem _inventoryItem; - public ContextMenuService(ItemManager items, StateManager state, ActorObjectManager objects, Configuration.Configuration config, + public ContextMenuService(ItemManager items, StateManager state, ActorObjectManager objects, Configuration config, IContextMenu context) { _contextMenu = context; diff --git a/Glamourer/Interop/Material/MaterialManager.cs b/Glamourer/Interop/Material/MaterialManager.cs index d8c6ce9..04f4d2e 100644 --- a/Glamourer/Interop/Material/MaterialManager.cs +++ b/Glamourer/Interop/Material/MaterialManager.cs @@ -1,5 +1,6 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle; +using Glamourer.Config; using Glamourer.Designs; using Glamourer.Interop.Penumbra; using Glamourer.State; @@ -24,7 +25,7 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable private readonly ThreadLocal> _deleteList = new(() => []); public MaterialManager(PrepareColorSet prepareColorSet, StateManager stateManager, ActorManager actors, PenumbraService penumbra, - Configuration.Configuration config) + Configuration config) { _stateManager = stateManager; _actors = actors; diff --git a/Glamourer/Interop/Penumbra/ModSettingApplier.cs b/Glamourer/Interop/Penumbra/ModSettingApplier.cs index d12b2be..153ed0f 100644 --- a/Glamourer/Interop/Penumbra/ModSettingApplier.cs +++ b/Glamourer/Interop/Penumbra/ModSettingApplier.cs @@ -1,4 +1,5 @@ -using Glamourer.Designs.Links; +using Glamourer.Config; +using Glamourer.Designs.Links; using Glamourer.Services; using Glamourer.State; using Luna; @@ -7,14 +8,19 @@ using Penumbra.GameData.Structs; namespace Glamourer.Interop.Penumbra; -public class ModSettingApplier(PenumbraService penumbra, PenumbraAutoRedrawSkip autoRedrawSkip, Configuration.Configuration config, ActorObjectManager objects, CollectionOverrideService overrides) +public class ModSettingApplier( + PenumbraService penumbra, + PenumbraAutoRedrawSkip autoRedrawSkip, + Configuration config, + ActorObjectManager objects, + CollectionOverrideService overrides) : IService { private readonly HashSet _collectionTracker = []; public void HandleStateApplication(ActorState state, MergedDesign design, StateSource source, bool skipAutoRedraw, bool respectManual) { - if (!config.AlwaysApplyAssociatedMods || (design.AssociatedMods.Count == 0 && !design.ResetTemporarySettings)) + if (!config.AlwaysApplyAssociatedMods || design.AssociatedMods.Count == 0 && !design.ResetTemporarySettings) return; if (!objects.TryGetValue(state.Identifier, out var data)) @@ -90,6 +96,7 @@ public class ModSettingApplier(PenumbraService penumbra, PenumbraAutoRedrawSkip if (!respectManual && source.IsFixed()) penumbra.RemoveAllTemporarySettings(index.Value, StateSource.Manual); } + return index; } } diff --git a/Glamourer/Interop/Penumbra/PenumbraAutoRedraw.cs b/Glamourer/Interop/Penumbra/PenumbraAutoRedraw.cs index 406e830..ec81508 100644 --- a/Glamourer/Interop/Penumbra/PenumbraAutoRedraw.cs +++ b/Glamourer/Interop/Penumbra/PenumbraAutoRedraw.cs @@ -1,5 +1,6 @@ using Dalamud.Plugin.Services; using Glamourer.Api.Enums; +using Glamourer.Config; using Glamourer.Designs.History; using Glamourer.Events; using Glamourer.State; @@ -11,17 +12,17 @@ namespace Glamourer.Interop.Penumbra; public class PenumbraAutoRedraw : IDisposable, IRequiredService { - private const int WaitFrames = 5; - private readonly Configuration.Configuration _config; - private readonly PenumbraService _penumbra; - private readonly StateManager _state; - private readonly ActorObjectManager _objects; - private readonly IFramework _framework; - private readonly StateChanged _stateChanged; - private readonly PenumbraAutoRedrawSkip _skip; + private const int WaitFrames = 5; + private readonly Configuration _config; + private readonly PenumbraService _penumbra; + private readonly StateManager _state; + private readonly ActorObjectManager _objects; + private readonly IFramework _framework; + private readonly StateChanged _stateChanged; + private readonly PenumbraAutoRedrawSkip _skip; - public PenumbraAutoRedraw(PenumbraService penumbra, Configuration.Configuration config, StateManager state, ActorObjectManager objects, + public PenumbraAutoRedraw(PenumbraService penumbra, Configuration config, StateManager state, ActorObjectManager objects, IFramework framework, StateChanged stateChanged, PenumbraAutoRedrawSkip skip) { diff --git a/Glamourer/Interop/Penumbra/PenumbraService.cs b/Glamourer/Interop/Penumbra/PenumbraService.cs index 6759375..0438cbc 100644 --- a/Glamourer/Interop/Penumbra/PenumbraService.cs +++ b/Glamourer/Interop/Penumbra/PenumbraService.cs @@ -1,6 +1,7 @@ using Dalamud.Interface.ImGuiNotification; using Dalamud.Plugin; using Dalamud.Plugin.Ipc.Exceptions; +using Glamourer.Config; using Glamourer.Events; using Glamourer.State; using Luna; @@ -45,7 +46,7 @@ public class PenumbraService : IDisposable private const string NameManual = "Glamourer (Manually)"; private readonly IDalamudPluginInterface _pluginInterface; - private readonly Configuration.Configuration _config; + private readonly Configuration _config; private readonly EventSubscriber _tooltipSubscriber; private readonly EventSubscriber _clickSubscriber; private readonly EventSubscriber _creatingCharacterBase; @@ -96,7 +97,7 @@ public class PenumbraService : IDisposable public int CurrentMinor { get; private set; } public DateTime AttachTime { get; private set; } - public PenumbraService(IDalamudPluginInterface pi, PenumbraReloaded penumbraReloaded, Configuration.Configuration config) + public PenumbraService(IDalamudPluginInterface pi, PenumbraReloaded penumbraReloaded, Configuration config) { _pluginInterface = pi; _penumbraReloaded = penumbraReloaded; diff --git a/Glamourer/Services/BackupService.cs b/Glamourer/Services/BackupService.cs index 3beac71..1103b59 100644 --- a/Glamourer/Services/BackupService.cs +++ b/Glamourer/Services/BackupService.cs @@ -1,27 +1,9 @@ using Luna; -using Backup = OtterGui.Classes.Backup; -using Logger = OtterGui.Log.Logger; namespace Glamourer.Services; -public class BackupService : IAsyncService +public class BackupService(Logger log, FilenameService provider) : BaseBackupService(log, provider) { - private readonly Logger _logger; - private readonly DirectoryInfo _configDirectory; - private readonly IReadOnlyList _fileNames; - - public BackupService(Logger logger, FilenameService fileNames) - { - _logger = logger; - _fileNames = GlamourerFiles(fileNames); - _configDirectory = new DirectoryInfo(fileNames.ConfigurationDirectory); - Awaiter = Task.Run(() => Backup.CreateAutomaticBackup(logger, new DirectoryInfo(fileNames.ConfigurationDirectory), _fileNames)); - } - - /// Create a permanent backup with a given name for migrations. - public void CreateMigrationBackup(string name) - => Backup.CreatePermanentBackup(_logger, _configDirectory, _fileNames, name); - /// Collect all relevant files for glamourer configuration. private static IReadOnlyList GlamourerFiles(FilenameService fileNames) { @@ -29,7 +11,7 @@ public class BackupService : IAsyncService { new(fileNames.ConfigurationFile), new(fileNames.UiConfiguration), - new(fileNames.DesignFileSystem), + new(fileNames.MigrationDesignFileSystem), new(fileNames.MigrationDesignFile), new(fileNames.AutomationFile), new(fileNames.UnlockFileCustomize), @@ -42,9 +24,4 @@ public class BackupService : IAsyncService return list; } - - public Task Awaiter { get; } - - public bool Finished - => Awaiter.IsCompletedSuccessfully; } diff --git a/Glamourer/Services/CodeService.cs b/Glamourer/Services/CodeService.cs index bd1f9c3..750c335 100644 --- a/Glamourer/Services/CodeService.cs +++ b/Glamourer/Services/CodeService.cs @@ -1,13 +1,13 @@ +using Glamourer.Config; using ImSharp; -using Luna; using Penumbra.GameData.Enums; namespace Glamourer.Services; public class CodeService { - private readonly Configuration.Configuration _config; - private readonly SHA256 _hasher = SHA256.Create(); + private readonly Configuration _config; + private readonly SHA256 _hasher = SHA256.Create(); [Flags] public enum CodeFlag : ulong @@ -25,16 +25,17 @@ public class CodeService OopsAuRa = 0x000400, OopsHrothgar = 0x000800, OopsViera = 0x001000, + //Artisan = 0x002000, - SixtyThree = 0x004000, - Shirts = 0x008000, - World = 0x010000, - Elephants = 0x020000, - Crown = 0x040000, - Dolphins = 0x080000, - Face = 0x100000, - Manderville = 0x200000, - Smiles = 0x400000, + SixtyThree = 0x004000, + Shirts = 0x008000, + World = 0x010000, + Elephants = 0x020000, + Crown = 0x040000, + Dolphins = 0x080000, + Face = 0x100000, + Manderville = 0x200000, + Smiles = 0x400000, } public static readonly CodeFlag AllHintCodes = @@ -87,7 +88,7 @@ public class CodeService _ => Race.Unknown, }; - public CodeService(Configuration.Configuration config) + public CodeService(Configuration config) { _config = config; Load(); @@ -253,4 +254,3 @@ public class CodeService _ => (false, 0, string.Empty, string.Empty, string.Empty), }; } - diff --git a/Glamourer/Services/CommandService.cs b/Glamourer/Services/CommandService.cs index 8a7c086..019424c 100644 --- a/Glamourer/Services/CommandService.cs +++ b/Glamourer/Services/CommandService.cs @@ -2,6 +2,7 @@ using Dalamud.Game.Text.SeStringHandling; using Dalamud.Plugin.Services; using Glamourer.Automation; +using Glamourer.Config; using Glamourer.Designs; using Glamourer.Designs.Special; using Glamourer.GameData; @@ -24,27 +25,27 @@ public class CommandService : IDisposable, IApiService private const string MainCommandString = "/glamourer"; private const string ApplyCommandString = "/glamour"; - private readonly ICommandManager _commands; - private readonly MainWindow _mainWindow; - private readonly IChatGui _chat; - private readonly ActorManager _actors; - private readonly ActorObjectManager _objects; - private readonly StateManager _stateManager; - private readonly AutoDesignApplier _autoDesignApplier; - private readonly AutoDesignManager _autoDesignManager; - private readonly Configuration.Configuration _config; - private readonly ModSettingApplier _modApplier; - private readonly ItemManager _items; - private readonly CustomizeService _customizeService; - private readonly DesignManager _designManager; - private readonly DesignConverter _converter; - private readonly DesignResolver _resolver; - private readonly PenumbraService _penumbra; + private readonly ICommandManager _commands; + private readonly MainWindow _mainWindow; + private readonly IChatGui _chat; + private readonly ActorManager _actors; + private readonly ActorObjectManager _objects; + private readonly StateManager _stateManager; + private readonly AutoDesignApplier _autoDesignApplier; + private readonly AutoDesignManager _autoDesignManager; + private readonly Configuration _config; + private readonly ModSettingApplier _modApplier; + private readonly ItemManager _items; + private readonly CustomizeService _customizeService; + private readonly DesignManager _designManager; + private readonly DesignConverter _converter; + private readonly DesignResolver _resolver; + private readonly PenumbraService _penumbra; public CommandService(ICommandManager commands, MainWindow mainWindow, IChatGui chat, ActorManager actors, ActorObjectManager objects, AutoDesignApplier autoDesignApplier, StateManager stateManager, DesignManager designManager, DesignConverter converter, - DesignFileSystem designFileSystem, AutoDesignManager autoDesignManager, Configuration.Configuration config, ModSettingApplier modApplier, - ItemManager items, RandomDesignGenerator randomDesign, CustomizeService customizeService, DesignFileSystemSelector designSelector, + DesignFileSystem designFileSystem, AutoDesignManager autoDesignManager, Configuration config, ModSettingApplier modApplier, + ItemManager items, RandomDesignGenerator randomDesign, CustomizeService customizeService, DesignFileSystemDrawer designDrawer, QuickDesignCombo quickDesignCombo, DesignResolver resolver, PenumbraService penumbra) { _commands = commands; diff --git a/Glamourer/Services/ConfigMigrationService.cs b/Glamourer/Services/ConfigMigrationService.cs index dc6291f..2fefe15 100644 --- a/Glamourer/Services/ConfigMigrationService.cs +++ b/Glamourer/Services/ConfigMigrationService.cs @@ -1,20 +1,20 @@ using Glamourer.Automation; +using Glamourer.Config; using Glamourer.Gui; using ImSharp; -using Luna; using Newtonsoft.Json.Linq; namespace Glamourer.Services; public class ConfigMigrationService(SaveService saveService, FixedDesignMigrator fixedDesignMigrator, BackupService backupService) { - private Configuration.Configuration _config = null!; - private JObject _data = null!; + private Configuration _config = null!; + private JObject _data = null!; - public void Migrate(Configuration.Configuration config) + public void Migrate(Configuration config) { _config = config; - if (config.Version >= Configuration.Configuration.Constants.CurrentVersion || !File.Exists(saveService.FileNames.ConfigurationFile)) + if (config.Version >= Configuration.CurrentVersion || !File.Exists(saveService.FileNames.ConfigurationFile)) { AddColors(config, false); return; @@ -27,9 +27,22 @@ public class ConfigMigrationService(SaveService saveService, FixedDesignMigrator MigrateV5To6(); MigrateV6To7(); MigrateV7To8(); + MigrateV8To9(); AddColors(config, true); } + private void MigrateV8To9() + { + if (_config.Version > 8) + return; + + backupService.CreateMigrationBackup("pre_filesystem_update", saveService.FileNames.MigrationDesignFileSystem); + _config.Version = 9; + _config.Ephemeral.Version = 9; + _config.Save(); + _config.Ephemeral.Save(); + } + private void MigrateV7To8() { if (_config.Version > 7) @@ -60,7 +73,7 @@ public class ConfigMigrationService(SaveService saveService, FixedDesignMigrator _config.Version = 6; } - // Ephemeral Config. + // Ephemeral Configuration. private void MigrateV4To5() { if (_config.Version > 4) @@ -71,7 +84,7 @@ public class ConfigMigrationService(SaveService saveService, FixedDesignMigrator _config.Ephemeral.ShowDesignQuickBar = _data["ShowDesignQuickBar"]?.ToObject() ?? _config.Ephemeral.ShowDesignQuickBar; _config.Ephemeral.LockDesignQuickBar = _data["LockDesignQuickBar"]?.ToObject() ?? _config.Ephemeral.LockDesignQuickBar; _config.Ephemeral.LockMainWindow = _data["LockMainWindow"]?.ToObject() ?? _config.Ephemeral.LockMainWindow; - _config.Ephemeral.SelectedMainTab = _data["SelectedTab"]?.ToObject() ?? _config.Ephemeral.SelectedMainTab; + _config.Ephemeral.SelectedMainTab = _data["SelectedTab"]?.ToObject() ?? _config.Ephemeral.SelectedMainTab; _config.Ephemeral.LastSeenVersion = _data["LastSeenVersion"]?.ToObject() ?? _config.Ephemeral.LastSeenVersion; _config.Version = 5; _config.Ephemeral.Version = 5; @@ -83,7 +96,7 @@ public class ConfigMigrationService(SaveService saveService, FixedDesignMigrator if (_config.Version > 1) return; - backupService.CreateMigrationBackup("pre_v1_to_v2_migration"); + backupService.CreateMigrationBackup("pre_v1_to_v2_migration", saveService.FileNames.MigrationDesignFile); fixedDesignMigrator.Migrate(_data["FixedDesigns"]); _config.Version = 2; var customizationColor = _data["CustomizationColor"]?.ToObject() ?? ColorId.CustomizationDesign.Data().DefaultColor; @@ -103,7 +116,7 @@ public class ConfigMigrationService(SaveService saveService, FixedDesignMigrator _config.Codes = _config.Codes.DistinctBy(c => c.Code).ToList(); } - private static void AddColors(Configuration.Configuration config, bool forceSave) + private static void AddColors(Configuration config, bool forceSave) { var save = false; foreach (var color in ColorId.Values) diff --git a/Glamourer/Services/DesignResolver.cs b/Glamourer/Services/DesignResolver.cs index ef1e3b9..9849ad9 100644 --- a/Glamourer/Services/DesignResolver.cs +++ b/Glamourer/Services/DesignResolver.cs @@ -3,14 +3,13 @@ using Dalamud.Plugin.Services; using Glamourer.Designs; using Glamourer.Designs.Special; using Glamourer.Gui; -using Glamourer.Gui.Tabs.DesignTab; using Luna; using ImSharp; namespace Glamourer.Services; public class DesignResolver( - DesignFileSystemSelector designSelector, + DesignFileSystem fileSystem, QuickDesignCombo quickDesignCombo, DesignConverter converter, DesignManager manager, @@ -66,8 +65,8 @@ public class DesignResolver( [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool GetSelectedDesign(ref DesignBase? design, ref SeString? error) { - design = designSelector.Selected; - if (design != null) + design = (Design?)fileSystem.Selection.Selection?.Value; + if (design is not null) return true; error = "You do not have selected any design in the Designs Tab."; @@ -147,7 +146,7 @@ public class DesignResolver( // Search for design by name and partial identifier. design = manager.Designs.FirstOrDefault(MatchNameAndIdentifier(lower)); // Search for design by path, if nothing was found. - if (design == null && designFileSystem.Find(lower, out var child) && child is DesignFileSystem.Leaf leaf) + if (design is null && designFileSystem.Find(lower, out var child) && child is IFileSystemData leaf) design = leaf.Value; } diff --git a/Glamourer/Services/FilenameService.cs b/Glamourer/Services/FilenameService.cs index 60f1773..831c6ad 100644 --- a/Glamourer/Services/FilenameService.cs +++ b/Glamourer/Services/FilenameService.cs @@ -6,10 +6,11 @@ namespace Glamourer.Services; public class FilenameService(IDalamudPluginInterface pi) : BaseFilePathProvider(pi) { - public readonly string DesignFileSystem = Path.Combine(pi.ConfigDirectory.FullName, "sort_order.json"); + public readonly string MigrationDesignFileSystem = Path.Combine(pi.ConfigDirectory.FullName, "sort_order.json"); public readonly string MigrationDesignFile = Path.Combine(pi.ConfigDirectory.FullName, "Designs.json"); public readonly string DesignDirectory = Path.Combine(pi.ConfigDirectory.FullName, "designs"); public readonly string AutomationFile = Path.Combine(pi.ConfigDirectory.FullName, "automation.json"); + public readonly string IgnoredModsFile = Path.Combine(pi.ConfigDirectory.FullName, "ignored_mods.json"); public readonly string UnlockFileCustomize = Path.Combine(pi.ConfigDirectory.FullName, "unlocks_customize.json"); public readonly string UnlockFileItems = Path.Combine(pi.ConfigDirectory.FullName, "unlocks_items.json"); public readonly string FavoriteFile = Path.Combine(pi.ConfigDirectory.FullName, "favorites.json"); @@ -39,7 +40,22 @@ public class FilenameService(IDalamudPluginInterface pi) : BaseFilePathProvider( public string DesignFile(Design design) => DesignFile(design.Identifier.ToString()); - // TODO public override List GetBackupFiles() - => []; + { + var list = new List(16) + { + new(ConfigurationFile), + new(AutomationFile), + new(IgnoredModsFile), + new(UnlockFileCustomize), + new(UnlockFileItems), + new(FavoriteFile), + new(DesignColorFile), + new(FileSystemEmptyFolders), + new(FileSystemLockedNodes), + }; + // Do not back up expanded folders, selected nodes, ui configuration or ephemeral config. + list.AddRange(Designs()); + return list; + } } diff --git a/Glamourer/Services/ItemManager.cs b/Glamourer/Services/ItemManager.cs index d26a7f3..4f0a83d 100644 --- a/Glamourer/Services/ItemManager.cs +++ b/Glamourer/Services/ItemManager.cs @@ -1,4 +1,5 @@ using Dalamud.Plugin.Services; +using Glamourer.Config; using Lumina.Excel; using Lumina.Excel.Sheets; using Penumbra.GameData.Data; @@ -15,7 +16,7 @@ public class ItemManager public const string SmallClothesNpc = "Smallclothes (NPC)"; public const ushort SmallClothesNpcModel = 9903; - private readonly Configuration.Configuration _config; + private readonly Configuration _config; public readonly ObjectIdentification ObjectIdentification; public readonly ExcelSheet ItemSheet; @@ -26,7 +27,7 @@ public class ItemManager public readonly EquipItem DefaultSword; - public ItemManager(Configuration.Configuration config, IDataManager gameData, ObjectIdentification objectIdentification, + public ItemManager(Configuration config, IDataManager gameData, ObjectIdentification objectIdentification, ItemData itemData, DictStain stains, RestrictedGear restrictedGear, DictBonusItems dictBonusItems) { _config = config; diff --git a/Glamourer/Services/PcpService.cs b/Glamourer/Services/PcpService.cs index 639026a..cc8a54d 100644 --- a/Glamourer/Services/PcpService.cs +++ b/Glamourer/Services/PcpService.cs @@ -1,4 +1,5 @@ -using Glamourer.Designs; +using Glamourer.Config; +using Glamourer.Designs; using Glamourer.Interop.Penumbra; using Glamourer.State; using Luna; @@ -10,14 +11,14 @@ namespace Glamourer.Services; public class PcpService : IRequiredService { - private readonly Configuration.Configuration _config; - private readonly PenumbraService _penumbra; - private readonly ActorObjectManager _objects; - private readonly StateManager _state; - private readonly DesignConverter _designConverter; - private readonly DesignManager _designManager; + private readonly Configuration _config; + private readonly PenumbraService _penumbra; + private readonly ActorObjectManager _objects; + private readonly StateManager _state; + private readonly DesignConverter _designConverter; + private readonly DesignManager _designManager; - public PcpService(Configuration.Configuration config, PenumbraService penumbra, ActorObjectManager objects, StateManager state, + public PcpService(Configuration config, PenumbraService penumbra, ActorObjectManager objects, StateManager state, DesignConverter designConverter, DesignManager designManager) { _config = config; diff --git a/Glamourer/Services/ServiceManager.cs b/Glamourer/Services/ServiceManager.cs index e65f7ea..6b36d23 100644 --- a/Glamourer/Services/ServiceManager.cs +++ b/Glamourer/Services/ServiceManager.cs @@ -2,6 +2,7 @@ using Glamourer.Api; using Glamourer.Api.Api; using Glamourer.Automation; +using Glamourer.Config; using Glamourer.Designs; using Glamourer.Events; using Glamourer.Gui; @@ -73,8 +74,8 @@ public static class StaticServiceManager .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton() - .AddSingleton() + .AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton(); @@ -158,7 +159,7 @@ public static class StaticServiceManager .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() diff --git a/Glamourer/State/FunModule.cs b/Glamourer/State/FunModule.cs index 6199d33..26b6610 100644 --- a/Glamourer/State/FunModule.cs +++ b/Glamourer/State/FunModule.cs @@ -1,5 +1,6 @@ using Dalamud.Interface.ImGuiNotification; using FFXIVClientStructs.FFXIV.Client.Game.Object; +using Glamourer.Config; using Glamourer.Designs; using Glamourer.GameData; using Glamourer.Gui; @@ -23,19 +24,19 @@ public unsafe class FunModule : IDisposable AprilFirst, } - private readonly WorldSets _worldSets = new(); - private readonly ItemManager _items; - private readonly CustomizeService _customizations; - private readonly Configuration.Configuration _config; - private readonly CodeService _codes; - private readonly Random _rng; - private readonly GenericPopupWindow _popupWindow; - private readonly StateManager _stateManager; - private readonly DesignConverter _designConverter; - private readonly DesignManager _designManager; - private readonly ActorObjectManager _objects; - private readonly NpcCustomizeSet _npcs; - private readonly StainId[] _stains; + private readonly WorldSets _worldSets = new(); + private readonly ItemManager _items; + private readonly CustomizeService _customizations; + private readonly Configuration _config; + private readonly CodeService _codes; + private readonly Random _rng; + private readonly GenericPopupWindow _popupWindow; + private readonly StateManager _stateManager; + private readonly DesignConverter _designConverter; + private readonly DesignManager _designManager; + private readonly ActorObjectManager _objects; + private readonly NpcCustomizeSet _npcs; + private readonly StainId[] _stains; public FestivalType CurrentFestival { get; private set; } = FestivalType.None; private FunEquipSet? _festivalSet; @@ -66,7 +67,7 @@ public unsafe class FunModule : IDisposable internal void ResetFestival() => OnDayChange(DateTime.Now.Day, DateTime.Now.Month, DateTime.Now.Year); - public FunModule(CodeService codes, CustomizeService customizations, ItemManager items, Configuration.Configuration config, + public FunModule(CodeService codes, CustomizeService customizations, ItemManager items, Configuration config, GenericPopupWindow popupWindow, StateManager stateManager, ActorObjectManager objects, DesignConverter designConverter, DesignManager designManager, NpcCustomizeSet npcs) { diff --git a/Glamourer/State/StateEditor.cs b/Glamourer/State/StateEditor.cs index 305575f..1bfb66a 100644 --- a/Glamourer/State/StateEditor.cs +++ b/Glamourer/State/StateEditor.cs @@ -1,4 +1,5 @@ using Glamourer.Api.Enums; +using Glamourer.Config; using Glamourer.Designs; using Glamourer.Designs.History; using Glamourer.Designs.Links; @@ -6,7 +7,6 @@ using Glamourer.Events; using Glamourer.GameData; using Glamourer.Interop.Material; using Glamourer.Interop.Penumbra; -using Glamourer.Interop.Structs; using Glamourer.Services; using Penumbra.GameData.Enums; using Penumbra.GameData.Interop; @@ -20,18 +20,18 @@ public class StateEditor( StateChanged stateChanged, StateFinalized stateFinalized, JobChangeState jobChange, - Configuration.Configuration config, + Configuration config, ItemManager items, DesignMerger merger, ModSettingApplier modApplier, GPoseService gPose) : IDesignEditor { - protected readonly InternalStateEditor Editor = editor; - protected readonly StateApplier Applier = applier; - protected readonly StateChanged StateChanged = stateChanged; - protected readonly StateFinalized StateFinalized = stateFinalized; - protected readonly Configuration.Configuration Config = config; - protected readonly ItemManager Items = items; + protected readonly InternalStateEditor Editor = editor; + protected readonly StateApplier Applier = applier; + protected readonly StateChanged StateChanged = stateChanged; + protected readonly StateFinalized StateFinalized = stateFinalized; + protected readonly Configuration Config = config; + protected readonly ItemManager Items = items; /// Turn an actor to. public void ChangeModelId(ActorState state, uint modelId, CustomizeArray customize, nint equipData, StateSource source, diff --git a/Glamourer/State/StateListener.cs b/Glamourer/State/StateListener.cs index 85878ed..6856d4e 100644 --- a/Glamourer/State/StateListener.cs +++ b/Glamourer/State/StateListener.cs @@ -15,6 +15,7 @@ using Penumbra.GameData.DataContainers; using Glamourer.Designs; using Penumbra.GameData.Interop; using Glamourer.Api.Enums; +using Glamourer.Config; namespace Glamourer.State; @@ -25,31 +26,31 @@ namespace Glamourer.State; /// public class StateListener : IDisposable { - private readonly Configuration.Configuration _config; - private readonly ActorManager _actors; - private readonly ActorObjectManager _objects; - private readonly StateManager _manager; - private readonly StateApplier _applier; - private readonly ItemManager _items; - private readonly CustomizeService _customizations; - private readonly PenumbraService _penumbra; - private readonly EquipSlotUpdating _equipSlotUpdating; - private readonly BonusSlotUpdating _bonusSlotUpdating; - private readonly GearsetDataLoaded _gearsetDataLoaded; - private readonly WeaponLoading _weaponLoading; - private readonly HeadGearVisibilityChanged _headGearVisibility; - private readonly VisorStateChanged _visorState; - private readonly VieraEarStateChanged _vieraEarState; - private readonly WeaponVisibilityChanged _weaponVisibility; - private readonly StateFinalized _stateFinalized; - private readonly AutoDesignApplier _autoDesignApplier; - private readonly FunModule _funModule; - private readonly HumanModelList _humans; - private readonly MovedEquipment _movedEquipment; - private readonly GPoseService _gPose; - private readonly ChangeCustomizeService _changeCustomizeService; - private readonly CrestService _crestService; - private readonly ICondition _condition; + private readonly Configuration _config; + private readonly ActorManager _actors; + private readonly ActorObjectManager _objects; + private readonly StateManager _manager; + private readonly StateApplier _applier; + private readonly ItemManager _items; + private readonly CustomizeService _customizations; + private readonly PenumbraService _penumbra; + private readonly EquipSlotUpdating _equipSlotUpdating; + private readonly BonusSlotUpdating _bonusSlotUpdating; + private readonly GearsetDataLoaded _gearsetDataLoaded; + private readonly WeaponLoading _weaponLoading; + private readonly HeadGearVisibilityChanged _headGearVisibility; + private readonly VisorStateChanged _visorState; + private readonly VieraEarStateChanged _vieraEarState; + private readonly WeaponVisibilityChanged _weaponVisibility; + private readonly StateFinalized _stateFinalized; + private readonly AutoDesignApplier _autoDesignApplier; + private readonly FunModule _funModule; + private readonly HumanModelList _humans; + private readonly MovedEquipment _movedEquipment; + private readonly GPoseService _gPose; + private readonly ChangeCustomizeService _changeCustomizeService; + private readonly CrestService _crestService; + private readonly ICondition _condition; private readonly Dictionary _fistOffhands = []; @@ -58,7 +59,7 @@ public class StateListener : IDisposable private ActorState? _creatingState; private ActorState? _customizeState; - public StateListener(StateManager manager, ItemManager items, PenumbraService penumbra, ActorManager actors, Configuration.Configuration config, + public StateListener(StateManager manager, ItemManager items, PenumbraService penumbra, ActorManager actors, Configuration config, EquipSlotUpdating equipSlotUpdating, GearsetDataLoaded gearsetDataLoaded, WeaponLoading weaponLoading, VisorStateChanged visorState, WeaponVisibilityChanged weaponVisibility, HeadGearVisibilityChanged headGearVisibility, AutoDesignApplier autoDesignApplier, FunModule funModule, HumanModelList humans, StateApplier applier, MovedEquipment movedEquipment, ActorObjectManager objects, diff --git a/Glamourer/State/StateManager.cs b/Glamourer/State/StateManager.cs index c0fffab..b767f48 100644 --- a/Glamourer/State/StateManager.cs +++ b/Glamourer/State/StateManager.cs @@ -1,5 +1,6 @@ using Dalamud.Plugin.Services; using Glamourer.Api.Enums; +using Glamourer.Config; using Glamourer.Designs; using Glamourer.Designs.Links; using Glamourer.Events; @@ -27,7 +28,7 @@ public sealed class StateManager( InternalStateEditor editor, HumanModelList humans, IClientState clientState, - Configuration.Configuration config, + Configuration config, JobChangeState jobChange, DesignMerger merger, ModSettingApplier modApplier, diff --git a/Glamourer/packages.lock.json b/Glamourer/packages.lock.json index cf267ef..3268050 100644 --- a/Glamourer/packages.lock.json +++ b/Glamourer/packages.lock.json @@ -2,12 +2,6 @@ "version": 1, "dependencies": { "net10.0-windows7.0": { - "DalamudPackager": { - "type": "Direct", - "requested": "[14.0.1, )", - "resolved": "14.0.1", - "contentHash": "y0WWyUE6dhpGdolK3iKgwys05/nZaVf4ZPtIjpLhJBZvHxkkiE23zYRo7K7uqAgoK/QvK5cqF6l3VG5AbgC6KA==" - }, "DotNet.ReproducibleBuilds": { "type": "Direct", "requested": "[1.2.39, )", diff --git a/Luna b/Luna index bff400e..4a46e85 160000 --- a/Luna +++ b/Luna @@ -1 +1 @@ -Subproject commit bff400eb44aeded41fb6b1b3d35948881b578737 +Subproject commit 4a46e8551dd4438465c013c4a01d2936e5b4d4da diff --git a/Penumbra.Api b/Penumbra.Api index a79ff8d..9cfcaf3 160000 --- a/Penumbra.Api +++ b/Penumbra.Api @@ -1 +1 @@ -Subproject commit a79ff8d87c5b1ac12192f18563fb4247173ff4f0 +Subproject commit 9cfcaf39fef363e281f066b55811deef72908f2f diff --git a/Penumbra.GameData b/Penumbra.GameData index 223fb1b..22fc61d 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit 223fb1b04fee05c439b7679e7f62bc890e5d0885 +Subproject commit 22fc61d0298e13e18c97e9352b84a90353176c39 diff --git a/Penumbra.String b/Penumbra.String index 9bd016f..c13b802 160000 --- a/Penumbra.String +++ b/Penumbra.String @@ -1 +1 @@ -Subproject commit 9bd016fbef5fb2de467dd42165267fdd93cd9592 +Subproject commit c13b8020b2bb2e269c28a85f7d87fca86842fa88