mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 10:17:22 +01:00
Heavily improve changed item display.
This commit is contained in:
parent
1ebe4099d6
commit
deba8ac910
30 changed files with 360 additions and 126 deletions
2
OtterGui
2
OtterGui
|
|
@ -1 +1 @@
|
|||
Subproject commit 3bf047bfa293817a691b7f06032bae7aeb2e4dc7
|
||||
Subproject commit c347d29d980b0191d1d071170cf2ec229e3efdcf
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit bc339208d1d453582eb146533c572823146a4592
|
||||
Subproject commit 955c4e6b281bf0781689b15c01a868b0de5881b4
|
||||
|
|
@ -81,21 +81,21 @@ public class UiApi : IPenumbraApiUi, IApiService, IDisposable
|
|||
public void CloseMainWindow()
|
||||
=> _configWindow.IsOpen = false;
|
||||
|
||||
private void OnChangedItemClick(MouseButton button, IIdentifiedObjectData? data)
|
||||
private void OnChangedItemClick(MouseButton button, IIdentifiedObjectData data)
|
||||
{
|
||||
if (ChangedItemClicked == null)
|
||||
return;
|
||||
|
||||
var (type, id) = data?.ToApiObject() ?? (ChangedItemType.None, 0);
|
||||
var (type, id) = data.ToApiObject();
|
||||
ChangedItemClicked.Invoke(button, type, id);
|
||||
}
|
||||
|
||||
private void OnChangedItemHover(IIdentifiedObjectData? data)
|
||||
private void OnChangedItemHover(IIdentifiedObjectData data)
|
||||
{
|
||||
if (ChangedItemTooltip == null)
|
||||
return;
|
||||
|
||||
var (type, id) = data?.ToApiObject() ?? (ChangedItemType.None, 0);
|
||||
var (type, id) = data.ToApiObject();
|
||||
ChangedItemTooltip.Invoke(type, id);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ public sealed class ModChangedItemAdapter(WeakReference<ModStorage> storage)
|
|||
: throw new ObjectDisposedException("The underlying mod storage of this IPC container was disposed.");
|
||||
}
|
||||
|
||||
private sealed class ChangedItemDictionaryAdapter(SortedList<string, IIdentifiedObjectData?> data) : IReadOnlyDictionary<string, object?>
|
||||
private sealed class ChangedItemDictionaryAdapter(SortedList<string, IIdentifiedObjectData> data) : IReadOnlyDictionary<string, object?>
|
||||
{
|
||||
public IEnumerator<KeyValuePair<string, object?>> GetEnumerator()
|
||||
=> data.Select(d => new KeyValuePair<string, object?>(d.Key, d.Value?.ToInternalObject())).GetEnumerator();
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ public sealed class CollectionCache : IDisposable
|
|||
private readonly CollectionCacheManager _manager;
|
||||
private readonly ModCollection _collection;
|
||||
public readonly CollectionModData ModData = new();
|
||||
private readonly SortedList<string, (SingleArray<IMod>, IIdentifiedObjectData?)> _changedItems = [];
|
||||
private readonly SortedList<string, (SingleArray<IMod>, IIdentifiedObjectData)> _changedItems = [];
|
||||
public readonly ConcurrentDictionary<Utf8GamePath, ModPath> ResolvedFiles = new();
|
||||
public readonly CustomResourceCache CustomResources;
|
||||
public readonly MetaCache Meta;
|
||||
|
|
@ -43,7 +43,7 @@ public sealed class CollectionCache : IDisposable
|
|||
private int _changedItemsSaveCounter = -1;
|
||||
|
||||
// Obtain currently changed items. Computes them if they haven't been computed before.
|
||||
public IReadOnlyDictionary<string, (SingleArray<IMod>, IIdentifiedObjectData?)> ChangedItems
|
||||
public IReadOnlyDictionary<string, (SingleArray<IMod>, IIdentifiedObjectData)> ChangedItems
|
||||
{
|
||||
get
|
||||
{
|
||||
|
|
@ -441,7 +441,7 @@ public sealed class CollectionCache : IDisposable
|
|||
// Skip IMCs because they would result in far too many false-positive items,
|
||||
// since they are per set instead of per item-slot/item/variant.
|
||||
var identifier = _manager.MetaFileManager.Identifier;
|
||||
var items = new SortedList<string, IIdentifiedObjectData?>(512);
|
||||
var items = new SortedList<string, IIdentifiedObjectData>(512);
|
||||
|
||||
void AddItems(IMod mod)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@ public partial class ModCollection
|
|||
internal IReadOnlyDictionary<Utf8GamePath, ModPath> ResolvedFiles
|
||||
=> _cache?.ResolvedFiles ?? new ConcurrentDictionary<Utf8GamePath, ModPath>();
|
||||
|
||||
internal IReadOnlyDictionary<string, (SingleArray<IMod>, IIdentifiedObjectData?)> ChangedItems
|
||||
=> _cache?.ChangedItems ?? new Dictionary<string, (SingleArray<IMod>, IIdentifiedObjectData?)>();
|
||||
internal IReadOnlyDictionary<string, (SingleArray<IMod>, IIdentifiedObjectData)> ChangedItems
|
||||
=> _cache?.ChangedItems ?? new Dictionary<string, (SingleArray<IMod>, IIdentifiedObjectData)>();
|
||||
|
||||
internal IEnumerable<SingleArray<ModConflicts>> AllConflicts
|
||||
=> _cache?.AllConflicts ?? Array.Empty<SingleArray<ModConflicts>>();
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ namespace Penumbra.Communication;
|
|||
/// <item>Parameter is the clicked object data if any. </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public sealed class ChangedItemClick() : EventWrapper<MouseButton, IIdentifiedObjectData?, ChangedItemClick.Priority>(nameof(ChangedItemClick))
|
||||
public sealed class ChangedItemClick() : EventWrapper<MouseButton, IIdentifiedObjectData, ChangedItemClick.Priority>(nameof(ChangedItemClick))
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ namespace Penumbra.Communication;
|
|||
/// <item>Parameter is the hovered object data if any. </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public sealed class ChangedItemHover() : EventWrapper<IIdentifiedObjectData?, ChangedItemHover.Priority>(nameof(ChangedItemHover))
|
||||
public sealed class ChangedItemHover() : EventWrapper<IIdentifiedObjectData, ChangedItemHover.Priority>(nameof(ChangedItemHover))
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ public readonly record struct AtchIdentifier(AtchType Type, GenderRace GenderRac
|
|||
public override string ToString()
|
||||
=> $"Atch - {Type.ToAbbreviation()} - {GenderRace.ToName()} - {EntryIndex}";
|
||||
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData?> changedItems)
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData> changedItems)
|
||||
{
|
||||
// Nothing specific
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ public readonly record struct EqdpIdentifier(PrimaryId SetId, EquipSlot Slot, Ge
|
|||
public Gender Gender
|
||||
=> GenderRace.Split().Item1;
|
||||
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData?> changedItems)
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData> changedItems)
|
||||
=> identifier.Identify(changedItems, GamePaths.Mdl.Equipment(SetId, GenderRace, Slot));
|
||||
|
||||
public MetaIndex FileIndex()
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ namespace Penumbra.Meta.Manipulations;
|
|||
|
||||
public readonly record struct EqpIdentifier(PrimaryId SetId, EquipSlot Slot) : IMetaIdentifier, IComparable<EqpIdentifier>
|
||||
{
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData?> changedItems)
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData> changedItems)
|
||||
=> identifier.Identify(changedItems, GamePaths.Mdl.Equipment(SetId, GenderRace.MidlanderMale, Slot));
|
||||
|
||||
public MetaIndex FileIndex()
|
||||
|
|
|
|||
|
|
@ -24,17 +24,17 @@ public readonly record struct EstIdentifier(PrimaryId SetId, EstType Slot, Gende
|
|||
public Gender Gender
|
||||
=> GenderRace.Split().Item1;
|
||||
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData?> changedItems)
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData> changedItems)
|
||||
{
|
||||
switch (Slot)
|
||||
{
|
||||
case EstType.Hair:
|
||||
changedItems.TryAdd(
|
||||
$"Customization: {GenderRace.Split().Item2.ToName()} {GenderRace.Split().Item1.ToName()} Hair {SetId}", null);
|
||||
changedItems.UpdateCountOrSet(
|
||||
$"Customization: {GenderRace.Split().Item2.ToName()} {GenderRace.Split().Item1.ToName()} Hair {SetId}", () => new IdentifiedName());
|
||||
break;
|
||||
case EstType.Face:
|
||||
changedItems.TryAdd(
|
||||
$"Customization: {GenderRace.Split().Item2.ToName()} {GenderRace.Split().Item1.ToName()} Face {SetId}", null);
|
||||
changedItems.UpdateCountOrSet(
|
||||
$"Customization: {GenderRace.Split().Item2.ToName()} {GenderRace.Split().Item1.ToName()} Face {SetId}", () => new IdentifiedName());
|
||||
break;
|
||||
case EstType.Body:
|
||||
identifier.Identify(changedItems, GamePaths.Mdl.Equipment(SetId, GenderRace, EquipSlot.Body));
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ public readonly struct GlobalEqpManipulation : IMetaIdentifier
|
|||
public override string ToString()
|
||||
=> $"Global EQP - {Type}{(Condition != 0 ? $" - {Condition.Id}" : string.Empty)}";
|
||||
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData?> changedItems)
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData> changedItems)
|
||||
{
|
||||
var path = Type switch
|
||||
{
|
||||
|
|
@ -86,9 +86,9 @@ public readonly struct GlobalEqpManipulation : IMetaIdentifier
|
|||
if (path.Length > 0)
|
||||
identifier.Identify(changedItems, path);
|
||||
else if (Type is GlobalEqpType.DoNotHideVieraHats)
|
||||
changedItems["All Hats for Viera"] = null;
|
||||
changedItems.UpdateCountOrSet("All Hats for Viera", () => new IdentifiedName());
|
||||
else if (Type is GlobalEqpType.DoNotHideHrothgarHats)
|
||||
changedItems["All Hats for Hrothgar"] = null;
|
||||
changedItems.UpdateCountOrSet("All Hats for Hrothgar", () => new IdentifiedName());
|
||||
}
|
||||
|
||||
public MetaIndex FileIndex()
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ namespace Penumbra.Meta.Manipulations;
|
|||
|
||||
public readonly record struct GmpIdentifier(PrimaryId SetId) : IMetaIdentifier, IComparable<GmpIdentifier>
|
||||
{
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData?> changedItems)
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData> changedItems)
|
||||
=> identifier.Identify(changedItems, GamePaths.Mdl.Equipment(SetId, GenderRace.MidlanderMale, EquipSlot.Head));
|
||||
|
||||
public MetaIndex FileIndex()
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ public enum MetaManipulationType : byte
|
|||
|
||||
public interface IMetaIdentifier
|
||||
{
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData?> changedItems);
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData> changedItems);
|
||||
|
||||
public MetaIndex FileIndex();
|
||||
|
||||
|
|
|
|||
|
|
@ -27,10 +27,10 @@ public readonly record struct ImcIdentifier(
|
|||
: this(primaryId, variant, slot.IsAccessory() ? ObjectType.Accessory : ObjectType.Equipment, 0, slot, BodySlot.Unknown)
|
||||
{ }
|
||||
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData?> changedItems)
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData> changedItems)
|
||||
=> AddChangedItems(identifier, changedItems, false);
|
||||
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData?> changedItems, bool allVariants)
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData> changedItems, bool allVariants)
|
||||
{
|
||||
var path = ObjectType switch
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ namespace Penumbra.Meta.Manipulations;
|
|||
|
||||
public readonly record struct RspIdentifier(SubRace SubRace, RspAttribute Attribute) : IMetaIdentifier
|
||||
{
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData?> changedItems)
|
||||
=> changedItems.TryAdd($"{SubRace.ToName()} {Attribute.ToFullString()}", null);
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData> changedItems)
|
||||
=> changedItems.UpdateCountOrSet($"{SubRace.ToName()} {Attribute.ToFullString()}", () => new IdentifiedName());
|
||||
|
||||
public MetaIndex FileIndex()
|
||||
=> MetaIndex.HumanCmp;
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ public sealed class CombiningModGroup : IModGroup
|
|||
public void AddData(Setting setting, Dictionary<Utf8GamePath, FullPath> redirections, MetaDictionary manipulations)
|
||||
=> Data[setting.AsIndex].AddDataTo(redirections, manipulations);
|
||||
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData?> changedItems)
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData> changedItems)
|
||||
{
|
||||
foreach (var container in DataContainers)
|
||||
identifier.AddChangedItems(container, changedItems);
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ public interface IModGroup
|
|||
public IModGroupEditDrawer EditDrawer(ModGroupEditDrawer editDrawer);
|
||||
|
||||
public void AddData(Setting setting, Dictionary<Utf8GamePath, FullPath> redirections, MetaDictionary manipulations);
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData?> changedItems);
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData> changedItems);
|
||||
|
||||
/// <summary> Ensure that a value is valid for a group. </summary>
|
||||
public Setting FixSetting(Setting setting);
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ public class ImcModGroup(Mod mod) : IModGroup
|
|||
}
|
||||
}
|
||||
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData?> changedItems)
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData> changedItems)
|
||||
=> Identifier.AddChangedItems(identifier, changedItems, AllVariants);
|
||||
|
||||
public Setting FixSetting(Setting setting)
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ public sealed class MultiModGroup(Mod mod) : IModGroup, ITexToolsGroup
|
|||
}
|
||||
}
|
||||
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData?> changedItems)
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData> changedItems)
|
||||
{
|
||||
foreach (var container in DataContainers)
|
||||
identifier.AddChangedItems(container, changedItems);
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ public sealed class SingleModGroup(Mod mod) : IModGroup, ITexToolsGroup
|
|||
OptionData[setting.AsIndex].AddDataTo(redirections, manipulations);
|
||||
}
|
||||
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData?> changedItems)
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData> changedItems)
|
||||
{
|
||||
foreach (var container in DataContainers)
|
||||
identifier.AddChangedItems(container, changedItems);
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@ public class ModCacheManager : IDisposable, IService
|
|||
mod.ChangedItems.RemoveMachinistOffhands();
|
||||
|
||||
mod.LowerChangedItemsString = string.Join("\0", mod.ChangedItems.Keys.Select(k => k.ToLowerInvariant()));
|
||||
++mod.LastChangedItemsUpdate;
|
||||
}
|
||||
|
||||
private static void UpdateCounts(Mod mod)
|
||||
|
|
|
|||
|
|
@ -101,13 +101,14 @@ public sealed class Mod : IMod
|
|||
}
|
||||
|
||||
// Cache
|
||||
public readonly SortedList<string, IIdentifiedObjectData?> ChangedItems = new();
|
||||
public readonly SortedList<string, IIdentifiedObjectData> ChangedItems = new();
|
||||
|
||||
public string LowerChangedItemsString { get; internal set; } = string.Empty;
|
||||
public string AllTagsLower { get; internal set; } = string.Empty;
|
||||
|
||||
public int TotalFileCount { get; internal set; }
|
||||
public int TotalSwapCount { get; internal set; }
|
||||
public int TotalManipulations { get; internal set; }
|
||||
public int TotalFileCount { get; internal set; }
|
||||
public int TotalSwapCount { get; internal set; }
|
||||
public int TotalManipulations { get; internal set; }
|
||||
public ushort LastChangedItemsUpdate { get; internal set; }
|
||||
public bool HasOptions { get; internal set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ public class ResourceTreeViewer(
|
|||
private const ResourceTreeFactory.Flags ResourceTreeFactoryFlags =
|
||||
ResourceTreeFactory.Flags.RedactExternalPaths | ResourceTreeFactory.Flags.WithUiData | ResourceTreeFactory.Flags.WithOwnership;
|
||||
|
||||
private readonly CommunicatorService _communicator = communicator;
|
||||
private readonly HashSet<nint> _unfolded = [];
|
||||
|
||||
private readonly Dictionary<nint, NodeVisibility> _filterCache = [];
|
||||
|
|
@ -278,7 +277,7 @@ public class ResourceTreeViewer(
|
|||
if (ImGui.IsItemClicked())
|
||||
ImGui.SetClipboardText(resourceNode.FullPath.ToPath());
|
||||
if (hasMod && ImGui.IsItemClicked(ImGuiMouseButton.Right) && ImGui.GetIO().KeyCtrl)
|
||||
_communicator.SelectTab.Invoke(TabType.Mods, mod);
|
||||
communicator.SelectTab.Invoke(TabType.Mods, mod);
|
||||
|
||||
ImGuiUtil.HoverTooltip(
|
||||
$"{resourceNode.FullPath.ToPath()}\n\nClick to copy to clipboard.{(hasMod ? "\nControl + Right-Click to jump to mod." : string.Empty)}{GetAdditionalDataSuffix(resourceNode.AdditionalData)}");
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using OtterGui;
|
|||
using OtterGui.Classes;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Services;
|
||||
using OtterGui.Text;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.GameData.Data;
|
||||
using Penumbra.Services;
|
||||
|
|
@ -86,18 +87,20 @@ public class ChangedItemDrawer : IDisposable, IUiService
|
|||
}
|
||||
|
||||
/// <summary> Check if a changed item should be drawn based on its category. </summary>
|
||||
public bool FilterChangedItem(string name, IIdentifiedObjectData? data, LowerString filter)
|
||||
public bool FilterChangedItem(string name, IIdentifiedObjectData data, LowerString filter)
|
||||
=> (_config.Ephemeral.ChangedItemFilter == ChangedItemFlagExtensions.AllFlags
|
||||
|| _config.Ephemeral.ChangedItemFilter.HasFlag(data.GetIcon().ToFlag()))
|
||||
&& (filter.IsEmpty || !data.IsFilteredOut(name, filter));
|
||||
|
||||
/// <summary> Draw the icon corresponding to the category of a changed item. </summary>
|
||||
public void DrawCategoryIcon(IIdentifiedObjectData? data)
|
||||
=> DrawCategoryIcon(data.GetIcon().ToFlag());
|
||||
public void DrawCategoryIcon(IIdentifiedObjectData data, float height)
|
||||
=> DrawCategoryIcon(data.GetIcon().ToFlag(), height);
|
||||
|
||||
public void DrawCategoryIcon(ChangedItemIconFlag iconFlagType)
|
||||
=> DrawCategoryIcon(iconFlagType, ImGui.GetFrameHeight());
|
||||
|
||||
public void DrawCategoryIcon(ChangedItemIconFlag iconFlagType, float height)
|
||||
{
|
||||
var height = ImGui.GetFrameHeight();
|
||||
if (!_icons.TryGetValue(iconFlagType, out var icon))
|
||||
{
|
||||
ImGui.Dummy(new Vector2(height));
|
||||
|
|
@ -114,50 +117,50 @@ public class ChangedItemDrawer : IDisposable, IUiService
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw a changed item, invoking the Api-Events for clicks and tooltips.
|
||||
/// Also draw the item ID in grey if requested.
|
||||
/// </summary>
|
||||
public void DrawChangedItem(string name, IIdentifiedObjectData? data)
|
||||
public void ChangedItemHandling(IIdentifiedObjectData data, bool leftClicked)
|
||||
{
|
||||
name = data?.ToName(name) ?? name;
|
||||
using (ImRaii.PushStyle(ImGuiStyleVar.SelectableTextAlign, new Vector2(0, 0.5f))
|
||||
.Push(ImGuiStyleVar.ItemSpacing, new Vector2(ImGui.GetStyle().ItemSpacing.X, ImGui.GetStyle().CellPadding.Y * 2)))
|
||||
{
|
||||
var ret = ImGui.Selectable(name, false, ImGuiSelectableFlags.None, new Vector2(0, ImGui.GetFrameHeight()))
|
||||
? MouseButton.Left
|
||||
: MouseButton.None;
|
||||
ret = ImGui.IsItemClicked(ImGuiMouseButton.Right) ? MouseButton.Right : ret;
|
||||
ret = ImGui.IsItemClicked(ImGuiMouseButton.Middle) ? MouseButton.Middle : ret;
|
||||
if (ret != MouseButton.None)
|
||||
_communicator.ChangedItemClick.Invoke(ret, data);
|
||||
}
|
||||
var ret = leftClicked ? MouseButton.Left : MouseButton.None;
|
||||
ret = ImGui.IsItemClicked(ImGuiMouseButton.Right) ? MouseButton.Right : ret;
|
||||
ret = ImGui.IsItemClicked(ImGuiMouseButton.Middle) ? MouseButton.Middle : ret;
|
||||
if (ret != MouseButton.None)
|
||||
_communicator.ChangedItemClick.Invoke(ret, data);
|
||||
if (!ImGui.IsItemHovered())
|
||||
return;
|
||||
|
||||
if (_communicator.ChangedItemHover.HasTooltip && ImGui.IsItemHovered())
|
||||
{
|
||||
// We can not be sure that any subscriber actually prints something in any case.
|
||||
// Circumvent ugly blank tooltip with less-ugly useless tooltip.
|
||||
using var tt = ImRaii.Tooltip();
|
||||
using (ImRaii.Group())
|
||||
{
|
||||
_communicator.ChangedItemHover.Invoke(data);
|
||||
}
|
||||
|
||||
if (ImGui.GetItemRectSize() == Vector2.Zero)
|
||||
ImGui.TextUnformatted("No actions available.");
|
||||
}
|
||||
using var tt = ImUtf8.Tooltip();
|
||||
if (data.Count == 1)
|
||||
ImUtf8.Text("This item is changed through a single effective change.\n");
|
||||
else
|
||||
ImUtf8.Text($"This item is changed through {data.Count} distinct effective changes.\n");
|
||||
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + 3 * ImUtf8.GlobalScale);
|
||||
ImGui.Separator();
|
||||
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + 3 * ImUtf8.GlobalScale);
|
||||
_communicator.ChangedItemHover.Invoke(data);
|
||||
}
|
||||
|
||||
/// <summary> Draw the model information, right-justified. </summary>
|
||||
public static void DrawModelData(IIdentifiedObjectData? data)
|
||||
public static void DrawModelData(IIdentifiedObjectData data, float height)
|
||||
{
|
||||
var additionalData = data?.AdditionalData ?? string.Empty;
|
||||
var additionalData = data.AdditionalData;
|
||||
if (additionalData.Length == 0)
|
||||
return;
|
||||
|
||||
ImGui.SameLine(ImGui.GetContentRegionAvail().X);
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGuiUtil.RightJustify(additionalData, ColorId.ItemId.Value());
|
||||
ImGui.SameLine();
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Text, ColorId.ItemId.Value());
|
||||
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + (height - ImGui.GetTextLineHeight()) / 2);
|
||||
ImUtf8.TextRightAligned(additionalData, ImGui.GetStyle().ItemInnerSpacing.X);
|
||||
}
|
||||
|
||||
/// <summary> Draw the model information, right-justified. </summary>
|
||||
public static void DrawModelData(ReadOnlySpan<byte> text, float height)
|
||||
{
|
||||
if (text.Length == 0)
|
||||
return;
|
||||
|
||||
ImGui.SameLine();
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Text, ColorId.ItemId.Value());
|
||||
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + (height - ImGui.GetTextLineHeight()) / 2);
|
||||
ImUtf8.TextRightAligned(text, ImGui.GetStyle().ItemInnerSpacing.X);
|
||||
}
|
||||
|
||||
/// <summary> Draw a header line with the different icon types to filter them. </summary>
|
||||
|
|
@ -276,7 +279,7 @@ public class ChangedItemDrawer : IDisposable, IUiService
|
|||
return true;
|
||||
}
|
||||
|
||||
private static unsafe IDalamudTextureWrap? LoadUnknownTexture(IDataManager gameData, ITextureProvider textureProvider)
|
||||
private static IDalamudTextureWrap? LoadUnknownTexture(IDataManager gameData, ITextureProvider textureProvider)
|
||||
{
|
||||
var unk = gameData.GetFile<TexFile>("ui/uld/levelup2_hr1.tex");
|
||||
if (unk == null)
|
||||
|
|
|
|||
|
|
@ -1,47 +1,268 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Services;
|
||||
using OtterGui.Text;
|
||||
using OtterGui.Widgets;
|
||||
using Penumbra.GameData.Data;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Mods;
|
||||
using Penumbra.String;
|
||||
|
||||
namespace Penumbra.UI.ModsTab;
|
||||
|
||||
public class ModPanelChangedItemsTab(ModFileSystemSelector selector, ChangedItemDrawer drawer) : ITab, IUiService
|
||||
public class ModPanelChangedItemsTab(
|
||||
ModFileSystemSelector selector,
|
||||
ChangedItemDrawer drawer,
|
||||
ImGuiCacheService cacheService,
|
||||
EphemeralConfig config)
|
||||
: ITab, IUiService
|
||||
{
|
||||
private readonly ImGuiCacheService.CacheId _cacheId = cacheService.GetNewId();
|
||||
|
||||
private class ChangedItemsCache
|
||||
{
|
||||
private Mod? _lastSelected;
|
||||
private ushort _lastUpdate;
|
||||
private ChangedItemIconFlag _filter = ChangedItemFlagExtensions.DefaultFlags;
|
||||
private bool _reset;
|
||||
public readonly List<Container> Data = [];
|
||||
public bool AnyExpandable { get; private set; }
|
||||
|
||||
public record struct Container
|
||||
{
|
||||
public IIdentifiedObjectData Data;
|
||||
public ByteString Text;
|
||||
public ByteString ModelData;
|
||||
public uint Id;
|
||||
public int Children;
|
||||
public ChangedItemIconFlag Icon;
|
||||
public bool Expandable;
|
||||
public bool Expanded;
|
||||
public bool Child;
|
||||
|
||||
public static Container Single(string text, IIdentifiedObjectData data)
|
||||
=> new()
|
||||
{
|
||||
Child = false,
|
||||
Text = ByteString.FromStringUnsafe(data.ToName(text), false),
|
||||
ModelData = ByteString.FromStringUnsafe(data.AdditionalData, false),
|
||||
Icon = data.GetIcon().ToFlag(),
|
||||
Expandable = false,
|
||||
Expanded = false,
|
||||
Data = data,
|
||||
Id = 0,
|
||||
Children = 0,
|
||||
};
|
||||
|
||||
public static Container Parent(string text, IIdentifiedObjectData data, uint id, int children, bool expanded)
|
||||
=> new()
|
||||
{
|
||||
Child = false,
|
||||
Text = ByteString.FromStringUnsafe(data.ToName(text), false),
|
||||
ModelData = ByteString.FromStringUnsafe(data.AdditionalData, false),
|
||||
Icon = data.GetIcon().ToFlag(),
|
||||
Expandable = true,
|
||||
Expanded = expanded,
|
||||
Data = data,
|
||||
Id = id,
|
||||
Children = children,
|
||||
};
|
||||
|
||||
public static Container Indent(string text, IIdentifiedObjectData data)
|
||||
=> new()
|
||||
{
|
||||
Child = true,
|
||||
Text = ByteString.FromStringUnsafe(data.ToName(text), false),
|
||||
ModelData = ByteString.FromStringUnsafe(data.AdditionalData, false),
|
||||
Icon = data.GetIcon().ToFlag(),
|
||||
Expandable = false,
|
||||
Expanded = false,
|
||||
Data = data,
|
||||
Id = 0,
|
||||
Children = 0,
|
||||
};
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
=> _reset = true;
|
||||
|
||||
public void Update(Mod? mod, ChangedItemDrawer drawer, ChangedItemIconFlag filter)
|
||||
{
|
||||
if (mod == _lastSelected && _lastSelected!.LastChangedItemsUpdate == _lastUpdate && _filter == filter && !_reset)
|
||||
return;
|
||||
|
||||
_reset = false;
|
||||
Data.Clear();
|
||||
AnyExpandable = false;
|
||||
_lastSelected = mod;
|
||||
_filter = filter;
|
||||
if (_lastSelected == null)
|
||||
return;
|
||||
|
||||
_lastUpdate = _lastSelected.LastChangedItemsUpdate;
|
||||
var tmp = new Dictionary<(PrimaryId, FullEquipType), List<IdentifiedItem>>();
|
||||
|
||||
foreach (var (s, i) in _lastSelected.ChangedItems)
|
||||
{
|
||||
if (i is not IdentifiedItem item)
|
||||
continue;
|
||||
|
||||
if (!drawer.FilterChangedItem(s, item, LowerString.Empty))
|
||||
continue;
|
||||
|
||||
if (tmp.TryGetValue((item.Item.PrimaryId, item.Item.Type), out var p))
|
||||
p.Add(item);
|
||||
else
|
||||
tmp[(item.Item.PrimaryId, item.Item.Type)] = [item];
|
||||
}
|
||||
|
||||
foreach (var list in tmp.Values)
|
||||
{
|
||||
list.Sort((i1, i2) =>
|
||||
{
|
||||
// reversed
|
||||
var count = i2.Count.CompareTo(i1.Count);
|
||||
if (count != 0)
|
||||
return count;
|
||||
|
||||
return string.Compare(i1.Item.Name, i2.Item.Name, StringComparison.Ordinal);
|
||||
});
|
||||
}
|
||||
|
||||
var sortedTmp = tmp.Values.OrderBy(s => s[0].Item.Name).ToArray();
|
||||
|
||||
var sortedTmpIdx = 0;
|
||||
foreach (var (s, i) in _lastSelected.ChangedItems)
|
||||
{
|
||||
if (i is IdentifiedItem)
|
||||
continue;
|
||||
|
||||
if (!drawer.FilterChangedItem(s, i, LowerString.Empty))
|
||||
continue;
|
||||
|
||||
while (sortedTmpIdx < sortedTmp.Length
|
||||
&& string.Compare(sortedTmp[sortedTmpIdx][0].Item.Name, s, StringComparison.Ordinal) <= 0)
|
||||
AddList(sortedTmp[sortedTmpIdx++]);
|
||||
|
||||
Data.Add(Container.Single(s, i));
|
||||
}
|
||||
|
||||
for (; sortedTmpIdx < sortedTmp.Length; ++sortedTmpIdx)
|
||||
AddList(sortedTmp[sortedTmpIdx]);
|
||||
return;
|
||||
|
||||
void AddList(List<IdentifiedItem> list)
|
||||
{
|
||||
var mainItem = list[0];
|
||||
if (list.Count == 1)
|
||||
{
|
||||
Data.Add(Container.Single(mainItem.Item.Name, mainItem));
|
||||
}
|
||||
else
|
||||
{
|
||||
var id = ImUtf8.GetId($"{mainItem.Item.PrimaryId}{(int)mainItem.Item.Type}");
|
||||
var expanded = ImGui.GetStateStorage().GetBool(id, false);
|
||||
Data.Add(Container.Parent(mainItem.Item.Name, mainItem, id, list.Count - 1, expanded));
|
||||
AnyExpandable = true;
|
||||
if (!expanded)
|
||||
return;
|
||||
|
||||
foreach (var item in list.Skip(1))
|
||||
Data.Add(Container.Indent(item.Item.Name, item));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> Label
|
||||
=> "Changed Items"u8;
|
||||
|
||||
public bool IsVisible
|
||||
=> selector.Selected!.ChangedItems.Count > 0;
|
||||
|
||||
private ImGuiStoragePtr _stateStorage;
|
||||
|
||||
private Vector2 _buttonSize;
|
||||
|
||||
public void DrawContent()
|
||||
{
|
||||
if (cacheService.Cache(_cacheId, () => (new ChangedItemsCache(), "ModPanelChangedItemsCache")) is not { } cache)
|
||||
return;
|
||||
|
||||
drawer.DrawTypeFilter();
|
||||
|
||||
_stateStorage = ImGui.GetStateStorage();
|
||||
cache.Update(selector.Selected, drawer, config.ChangedItemFilter);
|
||||
ImGui.Separator();
|
||||
using var table = ImRaii.Table("##changedItems", 1, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY,
|
||||
_buttonSize = new Vector2(ImGui.GetStyle().ItemSpacing.Y + ImGui.GetFrameHeight());
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.CellPadding, Vector2.Zero)
|
||||
.Push(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
|
||||
.Push(ImGuiStyleVar.FramePadding, Vector2.Zero)
|
||||
.Push(ImGuiStyleVar.SelectableTextAlign, new Vector2(0.01f, 0.5f));
|
||||
|
||||
using var table = ImUtf8.Table("##changedItems"u8, cache.AnyExpandable ? 2 : 1, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY,
|
||||
new Vector2(ImGui.GetContentRegionAvail().X, -1));
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
var zipList = ZipList.FromSortedList(selector.Selected!.ChangedItems);
|
||||
var height = ImGui.GetFrameHeightWithSpacing();
|
||||
ImGui.TableNextColumn();
|
||||
var skips = ImGuiClip.GetNecessarySkips(height);
|
||||
var remainder = ImGuiClip.FilteredClippedDraw(zipList, skips, CheckFilter, DrawChangedItem);
|
||||
ImGuiClip.DrawEndDummy(remainder, height);
|
||||
if (cache.AnyExpandable)
|
||||
{
|
||||
ImUtf8.TableSetupColumn("##exp"u8, ImGuiTableColumnFlags.WidthFixed, _buttonSize.Y);
|
||||
ImUtf8.TableSetupColumn("##text"u8, ImGuiTableColumnFlags.WidthStretch);
|
||||
ImGuiClip.ClippedDraw(cache.Data, DrawContainerExpandable, _buttonSize.Y);
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGuiClip.ClippedDraw(cache.Data, DrawContainer, ImGui.GetFrameHeightWithSpacing());
|
||||
}
|
||||
}
|
||||
|
||||
private bool CheckFilter((string Name, IIdentifiedObjectData? Data) kvp)
|
||||
=> drawer.FilterChangedItem(kvp.Name, kvp.Data, LowerString.Empty);
|
||||
private void DrawContainerExpandable(ChangedItemsCache.Container obj, int idx)
|
||||
{
|
||||
using var id = ImUtf8.PushId(idx);
|
||||
ImGui.TableNextColumn();
|
||||
if (obj.Expandable)
|
||||
{
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Button, 0);
|
||||
if (ImUtf8.IconButton(obj.Expanded ? FontAwesomeIcon.CaretDown : FontAwesomeIcon.CaretRight,
|
||||
obj.Expanded ? "Hide the other items using the same model." :
|
||||
obj.Children > 1 ? $"Show {obj.Children} other items using the same model." :
|
||||
"Show one other item using the same model.",
|
||||
_buttonSize))
|
||||
{
|
||||
_stateStorage.SetBool(obj.Id, !obj.Expanded);
|
||||
if (cacheService.TryGetCache<ChangedItemsCache>(_cacheId, out var cache))
|
||||
cache.Reset();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.Dummy(_buttonSize);
|
||||
}
|
||||
|
||||
private void DrawChangedItem((string Name, IIdentifiedObjectData? Data) kvp)
|
||||
DrawBaseContainer(obj, idx);
|
||||
}
|
||||
|
||||
private void DrawContainer(ChangedItemsCache.Container obj, int idx)
|
||||
{
|
||||
using var id = ImUtf8.PushId(idx);
|
||||
DrawBaseContainer(obj, idx);
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void DrawBaseContainer(in ChangedItemsCache.Container obj, int idx)
|
||||
{
|
||||
ImGui.TableNextColumn();
|
||||
drawer.DrawCategoryIcon(kvp.Data);
|
||||
ImGui.SameLine();
|
||||
drawer.DrawChangedItem(kvp.Name, kvp.Data);
|
||||
ChangedItemDrawer.DrawModelData(kvp.Data);
|
||||
using var indent = ImRaii.PushIndent(1, obj.Child);
|
||||
drawer.DrawCategoryIcon(obj.Icon, _buttonSize.Y);
|
||||
ImGui.SameLine(0, 0);
|
||||
var clicked = ImUtf8.Selectable(obj.Text.Span, false, ImGuiSelectableFlags.None, _buttonSize with { X = 0 });
|
||||
drawer.ChangedItemHandling(obj.Data, clicked);
|
||||
ChangedItemDrawer.DrawModelData(obj.ModelData.Span, _buttonSize.Y);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using OtterGui;
|
|||
using OtterGui.Classes;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Services;
|
||||
using OtterGui.Text;
|
||||
using OtterGui.Widgets;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Collections.Manager;
|
||||
|
|
@ -26,30 +27,36 @@ public class ChangedItemsTab(
|
|||
|
||||
private LowerString _changedItemFilter = LowerString.Empty;
|
||||
private LowerString _changedItemModFilter = LowerString.Empty;
|
||||
private Vector2 _buttonSize;
|
||||
|
||||
public void DrawContent()
|
||||
{
|
||||
collectionHeader.Draw(true);
|
||||
drawer.DrawTypeFilter();
|
||||
var varWidth = DrawFilters();
|
||||
using var child = ImRaii.Child("##changedItemsChild", -Vector2.One);
|
||||
using var child = ImUtf8.Child("##changedItemsChild"u8, -Vector2.One);
|
||||
if (!child)
|
||||
return;
|
||||
|
||||
var height = ImGui.GetFrameHeightWithSpacing() + 2 * ImGui.GetStyle().CellPadding.Y;
|
||||
var skips = ImGuiClip.GetNecessarySkips(height);
|
||||
using var list = ImRaii.Table("##changedItems", 3, ImGuiTableFlags.RowBg, -Vector2.One);
|
||||
_buttonSize = new Vector2(ImGui.GetStyle().ItemSpacing.Y + ImGui.GetFrameHeight());
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.CellPadding, Vector2.Zero)
|
||||
.Push(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
|
||||
.Push(ImGuiStyleVar.FramePadding, Vector2.Zero)
|
||||
.Push(ImGuiStyleVar.SelectableTextAlign, new Vector2(0.01f, 0.5f));
|
||||
|
||||
var skips = ImGuiClip.GetNecessarySkips(_buttonSize.Y);
|
||||
using var list = ImUtf8.Table("##changedItems"u8, 3, ImGuiTableFlags.RowBg, -Vector2.One);
|
||||
if (!list)
|
||||
return;
|
||||
|
||||
const ImGuiTableColumnFlags flags = ImGuiTableColumnFlags.NoResize | ImGuiTableColumnFlags.WidthFixed;
|
||||
ImGui.TableSetupColumn("items", flags, 450 * UiHelpers.Scale);
|
||||
ImGui.TableSetupColumn("mods", flags, varWidth - 130 * UiHelpers.Scale);
|
||||
ImGui.TableSetupColumn("id", flags, 130 * UiHelpers.Scale);
|
||||
ImUtf8.TableSetupColumn("items"u8, flags, 450 * UiHelpers.Scale);
|
||||
ImUtf8.TableSetupColumn("mods"u8, flags, varWidth - 140 * UiHelpers.Scale);
|
||||
ImUtf8.TableSetupColumn("id"u8, flags, 140 * UiHelpers.Scale);
|
||||
|
||||
var items = collectionManager.Active.Current.ChangedItems;
|
||||
var rest = ImGuiClip.FilteredClippedDraw(items, skips, FilterChangedItem, DrawChangedItemColumn);
|
||||
ImGuiClip.DrawEndDummy(rest, height);
|
||||
ImGuiClip.DrawEndDummy(rest, _buttonSize.Y);
|
||||
}
|
||||
|
||||
/// <summary> Draw a pair of filters and return the variable width of the flexible column. </summary>
|
||||
|
|
@ -67,22 +74,25 @@ public class ChangedItemsTab(
|
|||
}
|
||||
|
||||
/// <summary> Apply the current filters. </summary>
|
||||
private bool FilterChangedItem(KeyValuePair<string, (SingleArray<IMod>, IIdentifiedObjectData?)> item)
|
||||
private bool FilterChangedItem(KeyValuePair<string, (SingleArray<IMod>, IIdentifiedObjectData)> item)
|
||||
=> drawer.FilterChangedItem(item.Key, item.Value.Item2, _changedItemFilter)
|
||||
&& (_changedItemModFilter.IsEmpty || item.Value.Item1.Any(m => m.Name.Contains(_changedItemModFilter)));
|
||||
|
||||
/// <summary> Draw a full column for a changed item. </summary>
|
||||
private void DrawChangedItemColumn(KeyValuePair<string, (SingleArray<IMod>, IIdentifiedObjectData?)> item)
|
||||
private void DrawChangedItemColumn(KeyValuePair<string, (SingleArray<IMod>, IIdentifiedObjectData)> item)
|
||||
{
|
||||
ImGui.TableNextColumn();
|
||||
drawer.DrawCategoryIcon(item.Value.Item2);
|
||||
ImGui.SameLine();
|
||||
drawer.DrawChangedItem(item.Key, item.Value.Item2);
|
||||
drawer.DrawCategoryIcon(item.Value.Item2, _buttonSize.Y);
|
||||
ImGui.SameLine(0, 0);
|
||||
var name = item.Value.Item2.ToName(item.Key);
|
||||
var clicked = ImUtf8.Selectable(name, false, ImGuiSelectableFlags.None, _buttonSize with { X = 0 });
|
||||
drawer.ChangedItemHandling(item.Value.Item2, clicked);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
DrawModColumn(item.Value.Item1);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ChangedItemDrawer.DrawModelData(item.Value.Item2);
|
||||
ChangedItemDrawer.DrawModelData(item.Value.Item2, _buttonSize.Y);
|
||||
}
|
||||
|
||||
private void DrawModColumn(SingleArray<IMod> mods)
|
||||
|
|
@ -90,19 +100,18 @@ public class ChangedItemsTab(
|
|||
if (mods.Count <= 0)
|
||||
return;
|
||||
|
||||
var first = mods[0];
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.SelectableTextAlign, new Vector2(0, 0.5f));
|
||||
if (ImGui.Selectable(first.Name, false, ImGuiSelectableFlags.None, new Vector2(0, ImGui.GetFrameHeight()))
|
||||
var first = mods[0];
|
||||
if (ImUtf8.Selectable(first.Name.Text, false, ImGuiSelectableFlags.None, _buttonSize with { X = 0 })
|
||||
&& ImGui.GetIO().KeyCtrl
|
||||
&& first is Mod mod)
|
||||
communicator.SelectTab.Invoke(TabType.Mods, mod);
|
||||
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
using var _ = ImRaii.Tooltip();
|
||||
ImGui.TextUnformatted("Hold Control and click to jump to mod.\n");
|
||||
if (mods.Count > 1)
|
||||
ImGui.TextUnformatted("Other mods affecting this item:\n" + string.Join("\n", mods.Skip(1).Select(m => m.Name)));
|
||||
}
|
||||
if (!ImGui.IsItemHovered())
|
||||
return;
|
||||
|
||||
using var _ = ImRaii.Tooltip();
|
||||
ImUtf8.Text("Hold Control and click to jump to mod.\n"u8);
|
||||
if (mods.Count > 1)
|
||||
ImUtf8.Text("Other mods affecting this item:\n" + string.Join("\n", mods.Skip(1).Select(m => m.Name)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -748,7 +748,7 @@ public class DebugTab : Window, ITab, IUiService
|
|||
}
|
||||
|
||||
private string _changedItemPath = string.Empty;
|
||||
private readonly Dictionary<string, IIdentifiedObjectData?> _changedItems = [];
|
||||
private readonly Dictionary<string, IIdentifiedObjectData> _changedItems = [];
|
||||
|
||||
private void DrawChangedItemTest()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ namespace Penumbra.Util;
|
|||
public static class IdentifierExtensions
|
||||
{
|
||||
public static void AddChangedItems(this ObjectIdentification identifier, IModDataContainer container,
|
||||
IDictionary<string, IIdentifiedObjectData?> changedItems)
|
||||
IDictionary<string, IIdentifiedObjectData> changedItems)
|
||||
{
|
||||
foreach (var gamePath in container.Files.Keys.Concat(container.FileSwaps.Keys))
|
||||
identifier.Identify(changedItems, gamePath.ToString());
|
||||
|
|
@ -19,7 +19,7 @@ public static class IdentifierExtensions
|
|||
manip.AddChangedItems(identifier, changedItems);
|
||||
}
|
||||
|
||||
public static void RemoveMachinistOffhands(this SortedList<string, IIdentifiedObjectData?> changedItems)
|
||||
public static void RemoveMachinistOffhands(this SortedList<string, IIdentifiedObjectData> changedItems)
|
||||
{
|
||||
for (var i = 0; i < changedItems.Count; i++)
|
||||
{
|
||||
|
|
@ -31,7 +31,7 @@ public static class IdentifierExtensions
|
|||
}
|
||||
}
|
||||
|
||||
public static void RemoveMachinistOffhands(this SortedList<string, (SingleArray<IMod>, IIdentifiedObjectData?)> changedItems)
|
||||
public static void RemoveMachinistOffhands(this SortedList<string, (SingleArray<IMod>, IIdentifiedObjectData)> changedItems)
|
||||
{
|
||||
for (var i = 0; i < changedItems.Count; i++)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue