From ea4fb49c0fe5c2e554271849cec36b6aab042183 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Fri, 6 Oct 2023 01:23:26 +0200 Subject: [PATCH] Add favorite option to items. --- Glamourer/Events/ObjectUnlocked.cs | 1 + Glamourer/Gui/Colors.cs | 6 ++ Glamourer/Gui/Equipment/EquipmentDrawer.cs | 6 +- Glamourer/Gui/Equipment/ItemCombo.cs | 27 ++++-- .../Gui/Tabs/UnlocksTab/UnlockOverview.cs | 13 ++- Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs | 61 +++++++++--- Glamourer/Gui/UiHelpers.cs | 26 ++++- Glamourer/Services/BackupService.cs | 1 + Glamourer/Services/FilenameService.cs | 2 + Glamourer/Services/ServiceManager.cs | 3 +- Glamourer/Unlocks/FavoriteManager.cs | 96 +++++++++++++++++++ 11 files changed, 211 insertions(+), 31 deletions(-) create mode 100644 Glamourer/Unlocks/FavoriteManager.cs diff --git a/Glamourer/Events/ObjectUnlocked.cs b/Glamourer/Events/ObjectUnlocked.cs index ccb4b18..7b8c120 100644 --- a/Glamourer/Events/ObjectUnlocked.cs +++ b/Glamourer/Events/ObjectUnlocked.cs @@ -22,6 +22,7 @@ public sealed class ObjectUnlocked : EventWrapper + /// Currently used as a hack to make the unlock table dirty in it. If anything else starts using this, rework. UnlockTable = 0, } diff --git a/Glamourer/Gui/Colors.cs b/Glamourer/Gui/Colors.cs index 2ece621..a665eee 100644 --- a/Glamourer/Gui/Colors.cs +++ b/Glamourer/Gui/Colors.cs @@ -18,6 +18,9 @@ public enum ColorId AutomationActorAvailable, AutomationActorUnavailable, HeaderButtons, + FavoriteStarOn, + FavoriteStarHovered, + FavoriteStarOff, } public static class Colors @@ -40,6 +43,9 @@ public static class Colors ColorId.AutomationActorAvailable => (0xFFFFFFFF, "Automation Actor Available", "A character associated with the given automated design set is currently visible." ), ColorId.AutomationActorUnavailable => (0xFF808080, "Automation Actor Unavailable", "No character associated with the given automated design set is currently visible." ), ColorId.HeaderButtons => (0xFFFFF0C0, "Header Buttons", "The text and border color of buttons in the header, like the Incognito toggle." ), + ColorId.FavoriteStarOn => (0xFF40D0D0, "Favored Item", "The color of the star for favored items and of the border in the unlock overview tab." ), + ColorId.FavoriteStarHovered => (0xFFD040D0, "Favorite Star Hovered", "The color of the star for favored items when it is hovered." ), + ColorId.FavoriteStarOff => (0x20808080, "Favorite Star Outline", "The color of the star for items that are not favored when it is not hovered." ), _ => (0x00000000, string.Empty, string.Empty ), // @formatter:on }; diff --git a/Glamourer/Gui/Equipment/EquipmentDrawer.cs b/Glamourer/Gui/Equipment/EquipmentDrawer.cs index de91fd0..75ca0ab 100644 --- a/Glamourer/Gui/Equipment/EquipmentDrawer.cs +++ b/Glamourer/Gui/Equipment/EquipmentDrawer.cs @@ -10,6 +10,7 @@ using Glamourer.Designs; using Glamourer.Events; using Glamourer.Services; using Glamourer.Structs; +using Glamourer.Unlocks; using ImGuiNET; using OtterGui; using OtterGui.Raii; @@ -37,7 +38,8 @@ public class EquipmentDrawer private float _requiredComboWidthUnscaled; private float _requiredComboWidth; - public EquipmentDrawer(IDataManager gameData, ItemManager items, CodeService codes, TextureService textures, Configuration config, + public EquipmentDrawer(FavoriteManager favorites, IDataManager gameData, ItemManager items, CodeService codes, TextureService textures, + Configuration config, GPoseService gPose) { _items = items; @@ -48,7 +50,7 @@ public class EquipmentDrawer _stainData = items.Stains; _stainCombo = new FilterComboColors(DefaultWidth - 20, _stainData.Data.Prepend(new KeyValuePair(0, ("None", 0, false))), Glamourer.Log); - _itemCombo = EquipSlotExtensions.EqdpSlots.Select(e => new ItemCombo(gameData, items, e, Glamourer.Log)).ToArray(); + _itemCombo = EquipSlotExtensions.EqdpSlots.Select(e => new ItemCombo(gameData, items, e, Glamourer.Log, favorites)).ToArray(); _weaponCombo = new Dictionary(FullEquipTypeExtensions.WeaponTypes.Count * 2); foreach (var type in Enum.GetValues()) { diff --git a/Glamourer/Gui/Equipment/ItemCombo.cs b/Glamourer/Gui/Equipment/ItemCombo.cs index 0430e94..5062949 100644 --- a/Glamourer/Gui/Equipment/ItemCombo.cs +++ b/Glamourer/Gui/Equipment/ItemCombo.cs @@ -2,6 +2,7 @@ using System.Linq; using Dalamud.Plugin.Services; using Glamourer.Services; +using Glamourer.Unlocks; using ImGuiNET; using Lumina.Excel.GeneratedSheets; using OtterGui; @@ -16,13 +17,15 @@ namespace Glamourer.Gui.Equipment; public sealed class ItemCombo : FilterComboCache { - public readonly string Label; - private ItemId _currentItem; - private float _innerWidth; + private readonly FavoriteManager _favorites; + public readonly string Label; + private ItemId _currentItem; + private float _innerWidth; - public ItemCombo(IDataManager gameData, ItemManager items, EquipSlot slot, Logger log) - : base(() => GetItems(items, slot), log) + public ItemCombo(IDataManager gameData, ItemManager items, EquipSlot slot, Logger log, FavoriteManager favorites) + : base(() => GetItems(favorites, items, slot), log) { + _favorites = favorites; Label = GetLabel(gameData, slot); _currentItem = ItemManager.NothingId(slot); SearchByParts = true; @@ -59,7 +62,15 @@ public sealed class ItemCombo : FilterComboCache { var obj = Items[globalIdx]; var name = ToString(obj); - var ret = ImGui.Selectable(name, selected); + if (UiHelpers.DrawFavoriteStar(_favorites, obj) && CurrentSelectionIdx == globalIdx) + { + CurrentSelectionIdx = -1; + _currentItem = obj.ItemId; + CurrentSelection = default; + } + + ImGui.SameLine(); + var ret = ImGui.Selectable(name, selected); ImGui.SameLine(); using var color = ImRaii.PushColor(ImGuiCol.Text, 0xFF808080); ImGuiUtil.RightAlign($"({obj.ModelString})"); @@ -92,7 +103,7 @@ public sealed class ItemCombo : FilterComboCache }; } - private static IReadOnlyList GetItems(ItemManager items, EquipSlot slot) + private static IReadOnlyList GetItems(FavoriteManager favorites, ItemManager items, EquipSlot slot) { var nothing = ItemManager.NothingItem(slot); if (!items.ItemService.AwaitedService.TryGetValue(slot.ToEquipType(), out var list)) @@ -104,6 +115,6 @@ public sealed class ItemCombo : FilterComboCache var enumerable = list.AsEnumerable(); if (slot.IsEquipment()) enumerable = enumerable.Append(ItemManager.SmallClothesItem(slot)); - return enumerable.OrderBy(i => i.Name).Prepend(nothing).ToList(); + return enumerable.OrderByDescending(favorites.Contains).ThenBy(i => i.Name).Prepend(nothing).ToList(); } } diff --git a/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs b/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs index 8c596ae..2bd79d8 100644 --- a/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs +++ b/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Numerics; +using Dalamud.Game.Text.SeStringHandling; using Dalamud.Interface.Utility; using Glamourer.Customization; using Glamourer.Interop; @@ -24,6 +25,7 @@ public class UnlockOverview private readonly TextureService _textures; private readonly CodeService _codes; private readonly JobService _jobs; + private readonly FavoriteManager _favorites; private static readonly Vector4 UnavailableTint = new(0.3f, 0.3f, 0.3f, 1.0f); @@ -70,7 +72,7 @@ public class UnlockOverview public UnlockOverview(ItemManager items, CustomizationService customizations, ItemUnlockManager itemUnlocks, CustomizeUnlockManager customizeUnlocks, PenumbraChangedItemTooltip tooltip, TextureService textures, CodeService codes, - JobService jobs) + JobService jobs, FavoriteManager favorites) { _items = items; _customizations = customizations; @@ -80,6 +82,7 @@ public class UnlockOverview _textures = textures; _codes = codes; _jobs = jobs; + _favorites = favorites; } public void Draw() @@ -166,10 +169,12 @@ public class UnlockOverview var (icon, size) = (iconHandle.ImGuiHandle, new Vector2(iconHandle.Width, iconHandle.Height)); ImGui.Image(icon, iconSize, Vector2.Zero, Vector2.One, unlocked || _codes.EnabledShirts ? Vector4.One : UnavailableTint); + if (_favorites.Contains(item)) + ImGui.GetWindowDrawList().AddRect(ImGui.GetItemRectMin(), ImGui.GetItemRectMax(), ColorId.FavoriteStarOn.Value(), + 2 * ImGuiHelpers.GlobalScale, ImDrawFlags.RoundCornersAll, 4 * ImGuiHelpers.GlobalScale); + if (ImGui.IsItemClicked()) - { - // TODO link - } + Glamourer.Messager.Chat.Print(new SeStringBuilder().AddItemLink(item.ItemId.Id, false).BuiltString); if (ImGui.IsItemClicked(ImGuiMouseButton.Right) && _tooltip.Player(out var state)) _tooltip.ApplyItem(state, item); diff --git a/Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs b/Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs index 7ac9989..5ccd037 100644 --- a/Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs +++ b/Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; using System.Linq; using System.Numerics; +using Dalamud.Game.Text.SeStringHandling; using Dalamud.Interface; using Dalamud.Interface.Utility; using Glamourer.Events; @@ -24,19 +25,20 @@ public class UnlockTable : Table, IDisposable private readonly ObjectUnlocked _event; public UnlockTable(ItemManager items, TextureService textures, ItemUnlockManager itemUnlocks, - PenumbraChangedItemTooltip tooltip, ObjectUnlocked @event, JobService jobs) + PenumbraChangedItemTooltip tooltip, ObjectUnlocked @event, JobService jobs, FavoriteManager favorites) : base("ItemUnlockTable", new ItemList(items), - new NameColumn(textures, tooltip) { Label = "Item Name..." }, - new SlotColumn() { Label = "Equip Slot" }, - new TypeColumn() { Label = "Item Type..." }, - new UnlockDateColumn(itemUnlocks) { Label = "Unlocked" }, - new ItemIdColumn() { Label = "Item Id..." }, - new ModelDataColumn(items) { Label = "Model Data..." }, - new JobColumn(jobs) { Label = "Jobs" }, - new LevelColumn() { Label = "Level..." }, - new DyableColumn() { Label = "Dye" }, - new CrestColumn() { Label = "Crest" }, - new TradableColumn() { Label = "Trade" } + new FavoriteColumn(favorites, @event) { Label = "F" }, + new NameColumn(textures, tooltip) { Label = "Item Name..." }, + new SlotColumn() { Label = "Equip Slot" }, + new TypeColumn() { Label = "Item Type..." }, + new UnlockDateColumn(itemUnlocks) { Label = "Unlocked" }, + new ItemIdColumn() { Label = "Item Id..." }, + new ModelDataColumn(items) { Label = "Model Data..." }, + new JobColumn(jobs) { Label = "Jobs" }, + new LevelColumn() { Label = "Level..." }, + new DyableColumn() { Label = "Dye" }, + new CrestColumn() { Label = "Crest" }, + new TradableColumn() { Label = "Trade" } ) { _event = @event; @@ -48,6 +50,37 @@ public class UnlockTable : Table, IDisposable public void Dispose() => _event.Unsubscribe(OnObjectUnlock); + private sealed class FavoriteColumn : YesNoColumn + { + public override float Width + => ImGui.GetFrameHeightWithSpacing(); + + private readonly FavoriteManager _favorites; + private readonly ObjectUnlocked _hackEvent; // used to trigger the table dirty. + + public FavoriteColumn(FavoriteManager favorites, ObjectUnlocked hackEvent) + { + _favorites = favorites; + _hackEvent = hackEvent; + } + + protected override bool GetValue(EquipItem item) + => _favorites.Contains(item); + + public override void DrawColumn(EquipItem item, int idx) + { + ImGui.AlignTextToFramePadding(); + if (UiHelpers.DrawFavoriteStar(_favorites, item)) + _hackEvent.Invoke(ObjectUnlocked.Type.Customization, 0, DateTimeOffset.Now); + } + + public override bool FilterFunc(EquipItem item) + => FilterValue.HasFlag(_favorites.Contains(item) ? YesNoFlag.Yes : YesNoFlag.No); + + public override int Compare(EquipItem lhs, EquipItem rhs) + => _favorites.Contains(rhs).CompareTo(_favorites.Contains(lhs)); + } + private sealed class NameColumn : ColumnString { private readonly TextureService _textures; @@ -75,9 +108,7 @@ public class UnlockTable : Table, IDisposable ImGui.SameLine(); ImGui.AlignTextToFramePadding(); if (ImGui.Selectable(item.Name)) - { - // TODO link - } + Glamourer.Messager.Chat.Print(new SeStringBuilder().AddItemLink(item.ItemId.Id, false).BuiltString); if (ImGui.IsItemClicked(ImGuiMouseButton.Right) && _tooltip.Player(out var state)) _tooltip.ApplyItem(state, item); diff --git a/Glamourer/Gui/UiHelpers.cs b/Glamourer/Gui/UiHelpers.cs index 93311b6..3bb4c3a 100644 --- a/Glamourer/Gui/UiHelpers.cs +++ b/Glamourer/Gui/UiHelpers.cs @@ -1,9 +1,11 @@ using System; using System.Numerics; +using Dalamud.Interface; using Dalamud.Interface.Utility; using Glamourer.Customization; using Glamourer.Services; using Glamourer.Structs; +using Glamourer.Unlocks; using ImGuiNET; using Lumina.Misc; using OtterGui; @@ -119,4 +121,26 @@ public static class UiHelpers (true, false) => (true, false), (false, true) => (false, true), }; -} \ No newline at end of file + + public static bool DrawFavoriteStar(FavoriteManager favorites, EquipItem item) + { + var favorite = favorites.Contains(item); + var hovering = ImGui.IsMouseHoveringRect(ImGui.GetCursorScreenPos(), + ImGui.GetCursorScreenPos() + new Vector2(ImGui.GetTextLineHeight())); + + using var font = ImRaii.PushFont(UiBuilder.IconFont); + using var c = ImRaii.PushColor(ImGuiCol.Text, + hovering ? ColorId.FavoriteStarHovered.Value() : favorite ? ColorId.FavoriteStarOn.Value() : ColorId.FavoriteStarOff.Value()); + ImGui.TextUnformatted(FontAwesomeIcon.Star.ToIconString()); + if (ImGui.IsItemClicked()) + { + if (favorite) + favorites.Remove(item); + else + favorites.TryAdd(item); + return true; + } + + return false; + } +} diff --git a/Glamourer/Services/BackupService.cs b/Glamourer/Services/BackupService.cs index 25a6e8c..dfccb2a 100644 --- a/Glamourer/Services/BackupService.cs +++ b/Glamourer/Services/BackupService.cs @@ -34,6 +34,7 @@ public class BackupService new(fileNames.AutomationFile), new(fileNames.UnlockFileCustomize), new(fileNames.UnlockFileItems), + new(fileNames.FavoriteFile), }; list.AddRange(fileNames.Designs()); diff --git a/Glamourer/Services/FilenameService.cs b/Glamourer/Services/FilenameService.cs index 5a5f554..7299d32 100644 --- a/Glamourer/Services/FilenameService.cs +++ b/Glamourer/Services/FilenameService.cs @@ -15,6 +15,7 @@ public class FilenameService public readonly string AutomationFile; public readonly string UnlockFileCustomize; public readonly string UnlockFileItems; + public readonly string FavoriteFile; public FilenameService(DalamudPluginInterface pi) { @@ -26,6 +27,7 @@ public class FilenameService UnlockFileCustomize = Path.Combine(ConfigDirectory, "unlocks_customize.json"); UnlockFileItems = Path.Combine(ConfigDirectory, "unlocks_items.json"); DesignDirectory = Path.Combine(ConfigDirectory, "designs"); + FavoriteFile = Path.Combine(ConfigDirectory, "favorites.json"); } diff --git a/Glamourer/Services/ServiceManager.cs b/Glamourer/Services/ServiceManager.cs index 1171388..916c679 100644 --- a/Glamourer/Services/ServiceManager.cs +++ b/Glamourer/Services/ServiceManager.cs @@ -56,7 +56,8 @@ public static class ServiceManager .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton(); + .AddSingleton() + .AddSingleton(); private static IServiceCollection AddEvents(this IServiceCollection services) => services.AddSingleton() diff --git a/Glamourer/Unlocks/FavoriteManager.cs b/Glamourer/Unlocks/FavoriteManager.cs new file mode 100644 index 0000000..4cd98c8 --- /dev/null +++ b/Glamourer/Unlocks/FavoriteManager.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Dalamud.Interface.Internal.Notifications; +using Glamourer.Services; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using OtterGui.Classes; +using Penumbra.GameData.Structs; + +namespace Glamourer.Unlocks; + +public class FavoriteManager : ISavable +{ + private readonly SaveService _saveService; + private readonly HashSet _favorites = new(); + + public FavoriteManager(SaveService saveService) + { + _saveService = saveService; + Load(); + } + + private void Load() + { + var file = _saveService.FileNames.FavoriteFile; + if (!File.Exists(file)) + return; + + try + { + var text = File.ReadAllText(file); + var array = JsonConvert.DeserializeObject(text) ?? Array.Empty(); + _favorites.UnionWith(array.Select(i => (ItemId)i)); + } + catch (Exception ex) + { + Glamourer.Messager.NotificationMessage(ex, "Could not read Favorite file.", NotificationType.Error); + } + } + + public string ToFilename(FilenameService fileNames) + => fileNames.FavoriteFile; + + private void Save() + => _saveService.DelaySave(this); + + public void Save(StreamWriter writer) + { + using var j = new JsonTextWriter(writer) + { + Formatting = Formatting.Indented, + }; + j.WriteStartArray(); + foreach (var item in _favorites) + j.WriteValue(item.Id); + j.WriteEndArray(); + } + + public bool TryAdd(EquipItem item) + => TryAdd(item.ItemId); + + public bool TryAdd(ItemId item) + { + if (item.Id == 0 || !_favorites.Add(item)) + return false; + + Save(); + return true; + } + + public bool Remove(EquipItem item) + => Remove(item.ItemId); + + public bool Remove(ItemId item) + { + if (!_favorites.Remove(item)) + return false; + + Save(); + return true; + } + + public IEnumerator GetEnumerator() + => _favorites.GetEnumerator(); + + public int Count + => _favorites.Count; + + public bool Contains(EquipItem item) + => _favorites.Contains(item.ItemId); + + public bool Contains(ItemId item) + => _favorites.Contains(item); +}