Add favorite option to items.

This commit is contained in:
Ottermandias 2023-10-06 01:23:26 +02:00
parent 00883943cd
commit ea4fb49c0f
11 changed files with 211 additions and 31 deletions

View file

@ -22,6 +22,7 @@ public sealed class ObjectUnlocked : EventWrapper<Action<ObjectUnlocked.Type, ui
public enum Priority public enum Priority
{ {
/// <seealso cref="Gui.Tabs.UnlocksTab.UnlockTable.OnObjectUnlock"/> /// <seealso cref="Gui.Tabs.UnlocksTab.UnlockTable.OnObjectUnlock"/>
/// <remarks> Currently used as a hack to make the unlock table dirty in it. If anything else starts using this, rework. </remarks>
UnlockTable = 0, UnlockTable = 0,
} }

View file

@ -18,6 +18,9 @@ public enum ColorId
AutomationActorAvailable, AutomationActorAvailable,
AutomationActorUnavailable, AutomationActorUnavailable,
HeaderButtons, HeaderButtons,
FavoriteStarOn,
FavoriteStarHovered,
FavoriteStarOff,
} }
public static class Colors 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.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.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.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 ), _ => (0x00000000, string.Empty, string.Empty ),
// @formatter:on // @formatter:on
}; };

View file

@ -10,6 +10,7 @@ using Glamourer.Designs;
using Glamourer.Events; using Glamourer.Events;
using Glamourer.Services; using Glamourer.Services;
using Glamourer.Structs; using Glamourer.Structs;
using Glamourer.Unlocks;
using ImGuiNET; using ImGuiNET;
using OtterGui; using OtterGui;
using OtterGui.Raii; using OtterGui.Raii;
@ -37,7 +38,8 @@ public class EquipmentDrawer
private float _requiredComboWidthUnscaled; private float _requiredComboWidthUnscaled;
private float _requiredComboWidth; 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) GPoseService gPose)
{ {
_items = items; _items = items;
@ -48,7 +50,7 @@ public class EquipmentDrawer
_stainData = items.Stains; _stainData = items.Stains;
_stainCombo = new FilterComboColors(DefaultWidth - 20, _stainCombo = new FilterComboColors(DefaultWidth - 20,
_stainData.Data.Prepend(new KeyValuePair<byte, (string Name, uint Dye, bool Gloss)>(0, ("None", 0, false))), Glamourer.Log); _stainData.Data.Prepend(new KeyValuePair<byte, (string Name, uint Dye, bool Gloss)>(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<FullEquipType, WeaponCombo>(FullEquipTypeExtensions.WeaponTypes.Count * 2); _weaponCombo = new Dictionary<FullEquipType, WeaponCombo>(FullEquipTypeExtensions.WeaponTypes.Count * 2);
foreach (var type in Enum.GetValues<FullEquipType>()) foreach (var type in Enum.GetValues<FullEquipType>())
{ {

View file

@ -2,6 +2,7 @@
using System.Linq; using System.Linq;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Glamourer.Services; using Glamourer.Services;
using Glamourer.Unlocks;
using ImGuiNET; using ImGuiNET;
using Lumina.Excel.GeneratedSheets; using Lumina.Excel.GeneratedSheets;
using OtterGui; using OtterGui;
@ -16,13 +17,15 @@ namespace Glamourer.Gui.Equipment;
public sealed class ItemCombo : FilterComboCache<EquipItem> public sealed class ItemCombo : FilterComboCache<EquipItem>
{ {
public readonly string Label; private readonly FavoriteManager _favorites;
private ItemId _currentItem; public readonly string Label;
private float _innerWidth; private ItemId _currentItem;
private float _innerWidth;
public ItemCombo(IDataManager gameData, ItemManager items, EquipSlot slot, Logger log) public ItemCombo(IDataManager gameData, ItemManager items, EquipSlot slot, Logger log, FavoriteManager favorites)
: base(() => GetItems(items, slot), log) : base(() => GetItems(favorites, items, slot), log)
{ {
_favorites = favorites;
Label = GetLabel(gameData, slot); Label = GetLabel(gameData, slot);
_currentItem = ItemManager.NothingId(slot); _currentItem = ItemManager.NothingId(slot);
SearchByParts = true; SearchByParts = true;
@ -59,7 +62,15 @@ public sealed class ItemCombo : FilterComboCache<EquipItem>
{ {
var obj = Items[globalIdx]; var obj = Items[globalIdx];
var name = ToString(obj); 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(); ImGui.SameLine();
using var color = ImRaii.PushColor(ImGuiCol.Text, 0xFF808080); using var color = ImRaii.PushColor(ImGuiCol.Text, 0xFF808080);
ImGuiUtil.RightAlign($"({obj.ModelString})"); ImGuiUtil.RightAlign($"({obj.ModelString})");
@ -92,7 +103,7 @@ public sealed class ItemCombo : FilterComboCache<EquipItem>
}; };
} }
private static IReadOnlyList<EquipItem> GetItems(ItemManager items, EquipSlot slot) private static IReadOnlyList<EquipItem> GetItems(FavoriteManager favorites, ItemManager items, EquipSlot slot)
{ {
var nothing = ItemManager.NothingItem(slot); var nothing = ItemManager.NothingItem(slot);
if (!items.ItemService.AwaitedService.TryGetValue(slot.ToEquipType(), out var list)) if (!items.ItemService.AwaitedService.TryGetValue(slot.ToEquipType(), out var list))
@ -104,6 +115,6 @@ public sealed class ItemCombo : FilterComboCache<EquipItem>
var enumerable = list.AsEnumerable(); var enumerable = list.AsEnumerable();
if (slot.IsEquipment()) if (slot.IsEquipment())
enumerable = enumerable.Append(ItemManager.SmallClothesItem(slot)); 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();
} }
} }

View file

@ -1,6 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Interface.Utility; using Dalamud.Interface.Utility;
using Glamourer.Customization; using Glamourer.Customization;
using Glamourer.Interop; using Glamourer.Interop;
@ -24,6 +25,7 @@ public class UnlockOverview
private readonly TextureService _textures; private readonly TextureService _textures;
private readonly CodeService _codes; private readonly CodeService _codes;
private readonly JobService _jobs; private readonly JobService _jobs;
private readonly FavoriteManager _favorites;
private static readonly Vector4 UnavailableTint = new(0.3f, 0.3f, 0.3f, 1.0f); 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, public UnlockOverview(ItemManager items, CustomizationService customizations, ItemUnlockManager itemUnlocks,
CustomizeUnlockManager customizeUnlocks, PenumbraChangedItemTooltip tooltip, TextureService textures, CodeService codes, CustomizeUnlockManager customizeUnlocks, PenumbraChangedItemTooltip tooltip, TextureService textures, CodeService codes,
JobService jobs) JobService jobs, FavoriteManager favorites)
{ {
_items = items; _items = items;
_customizations = customizations; _customizations = customizations;
@ -80,6 +82,7 @@ public class UnlockOverview
_textures = textures; _textures = textures;
_codes = codes; _codes = codes;
_jobs = jobs; _jobs = jobs;
_favorites = favorites;
} }
public void Draw() public void Draw()
@ -166,10 +169,12 @@ public class UnlockOverview
var (icon, size) = (iconHandle.ImGuiHandle, new Vector2(iconHandle.Width, iconHandle.Height)); 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); 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()) if (ImGui.IsItemClicked())
{ Glamourer.Messager.Chat.Print(new SeStringBuilder().AddItemLink(item.ItemId.Id, false).BuiltString);
// TODO link
}
if (ImGui.IsItemClicked(ImGuiMouseButton.Right) && _tooltip.Player(out var state)) if (ImGui.IsItemClicked(ImGuiMouseButton.Right) && _tooltip.Player(out var state))
_tooltip.ApplyItem(state, item); _tooltip.ApplyItem(state, item);

View file

@ -3,6 +3,7 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Interface.Utility; using Dalamud.Interface.Utility;
using Glamourer.Events; using Glamourer.Events;
@ -24,19 +25,20 @@ public class UnlockTable : Table<EquipItem>, IDisposable
private readonly ObjectUnlocked _event; private readonly ObjectUnlocked _event;
public UnlockTable(ItemManager items, TextureService textures, ItemUnlockManager itemUnlocks, 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), : base("ItemUnlockTable", new ItemList(items),
new NameColumn(textures, tooltip) { Label = "Item Name..." }, new FavoriteColumn(favorites, @event) { Label = "F" },
new SlotColumn() { Label = "Equip Slot" }, new NameColumn(textures, tooltip) { Label = "Item Name..." },
new TypeColumn() { Label = "Item Type..." }, new SlotColumn() { Label = "Equip Slot" },
new UnlockDateColumn(itemUnlocks) { Label = "Unlocked" }, new TypeColumn() { Label = "Item Type..." },
new ItemIdColumn() { Label = "Item Id..." }, new UnlockDateColumn(itemUnlocks) { Label = "Unlocked" },
new ModelDataColumn(items) { Label = "Model Data..." }, new ItemIdColumn() { Label = "Item Id..." },
new JobColumn(jobs) { Label = "Jobs" }, new ModelDataColumn(items) { Label = "Model Data..." },
new LevelColumn() { Label = "Level..." }, new JobColumn(jobs) { Label = "Jobs" },
new DyableColumn() { Label = "Dye" }, new LevelColumn() { Label = "Level..." },
new CrestColumn() { Label = "Crest" }, new DyableColumn() { Label = "Dye" },
new TradableColumn() { Label = "Trade" } new CrestColumn() { Label = "Crest" },
new TradableColumn() { Label = "Trade" }
) )
{ {
_event = @event; _event = @event;
@ -48,6 +50,37 @@ public class UnlockTable : Table<EquipItem>, IDisposable
public void Dispose() public void Dispose()
=> _event.Unsubscribe(OnObjectUnlock); => _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<EquipItem> private sealed class NameColumn : ColumnString<EquipItem>
{ {
private readonly TextureService _textures; private readonly TextureService _textures;
@ -75,9 +108,7 @@ public class UnlockTable : Table<EquipItem>, IDisposable
ImGui.SameLine(); ImGui.SameLine();
ImGui.AlignTextToFramePadding(); ImGui.AlignTextToFramePadding();
if (ImGui.Selectable(item.Name)) if (ImGui.Selectable(item.Name))
{ Glamourer.Messager.Chat.Print(new SeStringBuilder().AddItemLink(item.ItemId.Id, false).BuiltString);
// TODO link
}
if (ImGui.IsItemClicked(ImGuiMouseButton.Right) && _tooltip.Player(out var state)) if (ImGui.IsItemClicked(ImGuiMouseButton.Right) && _tooltip.Player(out var state))
_tooltip.ApplyItem(state, item); _tooltip.ApplyItem(state, item);

View file

@ -1,9 +1,11 @@
using System; using System;
using System.Numerics; using System.Numerics;
using Dalamud.Interface;
using Dalamud.Interface.Utility; using Dalamud.Interface.Utility;
using Glamourer.Customization; using Glamourer.Customization;
using Glamourer.Services; using Glamourer.Services;
using Glamourer.Structs; using Glamourer.Structs;
using Glamourer.Unlocks;
using ImGuiNET; using ImGuiNET;
using Lumina.Misc; using Lumina.Misc;
using OtterGui; using OtterGui;
@ -119,4 +121,26 @@ public static class UiHelpers
(true, false) => (true, false), (true, false) => (true, false),
(false, true) => (false, true), (false, true) => (false, true),
}; };
}
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;
}
}

View file

@ -34,6 +34,7 @@ public class BackupService
new(fileNames.AutomationFile), new(fileNames.AutomationFile),
new(fileNames.UnlockFileCustomize), new(fileNames.UnlockFileCustomize),
new(fileNames.UnlockFileItems), new(fileNames.UnlockFileItems),
new(fileNames.FavoriteFile),
}; };
list.AddRange(fileNames.Designs()); list.AddRange(fileNames.Designs());

View file

@ -15,6 +15,7 @@ public class FilenameService
public readonly string AutomationFile; public readonly string AutomationFile;
public readonly string UnlockFileCustomize; public readonly string UnlockFileCustomize;
public readonly string UnlockFileItems; public readonly string UnlockFileItems;
public readonly string FavoriteFile;
public FilenameService(DalamudPluginInterface pi) public FilenameService(DalamudPluginInterface pi)
{ {
@ -26,6 +27,7 @@ public class FilenameService
UnlockFileCustomize = Path.Combine(ConfigDirectory, "unlocks_customize.json"); UnlockFileCustomize = Path.Combine(ConfigDirectory, "unlocks_customize.json");
UnlockFileItems = Path.Combine(ConfigDirectory, "unlocks_items.json"); UnlockFileItems = Path.Combine(ConfigDirectory, "unlocks_items.json");
DesignDirectory = Path.Combine(ConfigDirectory, "designs"); DesignDirectory = Path.Combine(ConfigDirectory, "designs");
FavoriteFile = Path.Combine(ConfigDirectory, "favorites.json");
} }

View file

@ -56,7 +56,8 @@ public static class ServiceManager
.AddSingleton<CodeService>() .AddSingleton<CodeService>()
.AddSingleton<ConfigMigrationService>() .AddSingleton<ConfigMigrationService>()
.AddSingleton<Configuration>() .AddSingleton<Configuration>()
.AddSingleton<TextureService>(); .AddSingleton<TextureService>()
.AddSingleton<FavoriteManager>();
private static IServiceCollection AddEvents(this IServiceCollection services) private static IServiceCollection AddEvents(this IServiceCollection services)
=> services.AddSingleton<VisorStateChanged>() => services.AddSingleton<VisorStateChanged>()

View file

@ -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<ItemId> _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<uint[]>(text) ?? Array.Empty<uint>();
_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<ItemId> 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);
}