Optimize design combos and file system.

This commit is contained in:
Ottermandias 2025-06-13 17:17:58 +02:00
parent 282935c6d6
commit 75c76a92b9
9 changed files with 92 additions and 124 deletions

View file

@ -20,12 +20,12 @@ public class DesignsApi(
=> designs.Designs.ToDictionary(d => d.Identifier, d => d.Name.Text); => designs.Designs.ToDictionary(d => d.Identifier, d => d.Name.Text);
public Dictionary<Guid, (string DisplayName, string FullPath, uint DisplayColor, bool ShownInQdb)> GetDesignListExtended() public Dictionary<Guid, (string DisplayName, string FullPath, uint DisplayColor, bool ShownInQdb)> GetDesignListExtended()
=> designs.Designs.ToDictionary(d => d.Identifier, => fileSystem.ToDictionary(kvp => kvp.Key.Identifier,
d => (d.Name.Text, fileSystem.FindLeaf(d, out var leaf) ? leaf.FullName() : d.Name.Text, color.GetColor(d), d.QuickDesign)); kvp => (kvp.Key.Name.Text, kvp.Value.FullName(), color.GetColor(kvp.Key), kvp.Key.QuickDesign));
public (string DisplayName, string FullPath, uint DisplayColor, bool ShowInQdb) GetExtendedDesignData(Guid designId) public (string DisplayName, string FullPath, uint DisplayColor, bool ShowInQdb) GetExtendedDesignData(Guid designId)
=> designs.Designs.ByIdentifier(designId) is { } d => designs.Designs.ByIdentifier(designId) is { } d
? (d.Name.Text, fileSystem.FindLeaf(d, out var leaf) ? leaf.FullName() : d.Name.Text, color.GetColor(d), d.QuickDesign) ? (d.Name.Text, fileSystem.TryGetValue(d, out var leaf) ? leaf.FullName() : d.Name.Text, color.GetColor(d), d.QuickDesign)
: (string.Empty, string.Empty, 0, false); : (string.Empty, string.Empty, 0, false);
public GlamourerApiEc ApplyDesign(Guid designId, int objectIndex, uint key, ApplyFlag flags) public GlamourerApiEc ApplyDesign(Guid designId, int objectIndex, uint key, ApplyFlag flags)

View file

@ -114,14 +114,14 @@ public sealed class DesignFileSystem : FileSystem<Design>, IDisposable, ISavable
return; return;
case DesignChanged.Type.Deleted: case DesignChanged.Type.Deleted:
if (FindLeaf(design, out var leaf1)) if (TryGetValue(design, out var leaf1))
Delete(leaf1); Delete(leaf1);
return; return;
case DesignChanged.Type.ReloadedAll: case DesignChanged.Type.ReloadedAll:
Reload(); Reload();
return; return;
case DesignChanged.Type.Renamed when (data as RenameTransaction?)?.Old is { } oldName: case DesignChanged.Type.Renamed when (data as RenameTransaction?)?.Old is { } oldName:
if (!FindLeaf(design, out var leaf2)) if (!TryGetValue(design, out var leaf2))
return; return;
var old = oldName.FixName(); var old = oldName.FixName();
@ -150,15 +150,6 @@ public sealed class DesignFileSystem : FileSystem<Design>, IDisposable, ISavable
? (string.Empty, false) ? (string.Empty, false)
: (DesignToIdentifier(design), true); : (DesignToIdentifier(design), true);
// Search the entire filesystem for the leaf corresponding to a design.
public bool FindLeaf(Design design, [NotNullWhen(true)] out Leaf? leaf)
{
leaf = Root.GetAllDescendants(ISortMode<Design>.Lexicographical)
.OfType<Leaf>()
.FirstOrDefault(l => l.Value == design);
return leaf != null;
}
internal static void MigrateOldPaths(SaveService saveService, Dictionary<string, string> oldPaths) internal static void MigrateOldPaths(SaveService saveService, Dictionary<string, string> oldPaths)
{ {
if (oldPaths.Count == 0) if (oldPaths.Count == 0)

View file

@ -22,7 +22,7 @@ public interface IDesignPredicate
: designs; : designs;
private static (Design Design, string LowerName, string Identifier, string LowerPath) Transform(Design d, DesignFileSystem fs) private static (Design Design, string LowerName, string Identifier, string LowerPath) Transform(Design d, DesignFileSystem fs)
=> (d, d.Name.Lower, d.Identifier.ToString(), fs.FindLeaf(d, out var l) ? l.FullName().ToLowerInvariant() : string.Empty); => (d, d.Name.Lower, d.Identifier.ToString(), fs.TryGetValue(d, out var l) ? l.FullName().ToLowerInvariant() : string.Empty);
} }
public static class RandomPredicate public static class RandomPredicate

View file

@ -10,7 +10,6 @@ using OtterGui;
using OtterGui.Classes; using OtterGui.Classes;
using OtterGui.Extensions; using OtterGui.Extensions;
using OtterGui.Log; using OtterGui.Log;
using OtterGui.Services;
using OtterGui.Widgets; using OtterGui.Widgets;
namespace Glamourer.Gui; namespace Glamourer.Gui;
@ -22,8 +21,8 @@ public abstract class DesignComboBase : FilterComboCache<Tuple<IDesignStandIn, s
protected readonly DesignColors DesignColors; protected readonly DesignColors DesignColors;
protected readonly TabSelected TabSelected; protected readonly TabSelected TabSelected;
protected float InnerWidth; protected float InnerWidth;
public bool IsListening { get; protected set; }
private IDesignStandIn? _currentDesign; private IDesignStandIn? _currentDesign;
private bool _isCurrentSelectionDirty;
protected DesignComboBase(Func<IReadOnlyList<Tuple<IDesignStandIn, string>>> generator, Logger log, DesignChanged designChanged, protected DesignComboBase(Func<IReadOnlyList<Tuple<IDesignStandIn, string>>> generator, Logger log, DesignChanged designChanged,
TabSelected tabSelected, EphemeralConfig config, DesignColors designColors) TabSelected tabSelected, EphemeralConfig config, DesignColors designColors)
@ -34,7 +33,6 @@ public abstract class DesignComboBase : FilterComboCache<Tuple<IDesignStandIn, s
Config = config; Config = config;
DesignColors = designColors; DesignColors = designColors;
DesignChanged.Subscribe(OnDesignChanged, DesignChanged.Priority.DesignCombo); DesignChanged.Subscribe(OnDesignChanged, DesignChanged.Priority.DesignCombo);
IsListening = true;
} }
public bool Incognito public bool Incognito
@ -46,25 +44,6 @@ public abstract class DesignComboBase : FilterComboCache<Tuple<IDesignStandIn, s
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
public void StopListening()
{
if (!IsListening)
return;
DesignChanged.Unsubscribe(OnDesignChanged);
IsListening = false;
}
public void StartListening()
{
if (IsListening)
return;
DesignChanged.Subscribe(OnDesignChanged, DesignChanged.Priority.DesignCombo);
OnDesignChanged(DesignChanged.Type.Deleted, null);
IsListening = true;
}
protected override bool DrawSelectable(int globalIdx, bool selected) protected override bool DrawSelectable(int globalIdx, bool selected)
{ {
var (design, path) = Items[globalIdx]; var (design, path) = Items[globalIdx];
@ -106,17 +85,11 @@ public abstract class DesignComboBase : FilterComboCache<Tuple<IDesignStandIn, s
DrawRightAligned(quickDesign.ResolveName(false), "[Nothing]", DesignColors.MissingColor); DrawRightAligned(quickDesign.ResolveName(false), "[Nothing]", DesignColors.MissingColor);
} }
protected override int UpdateCurrentSelected(int currentSelected)
{
CurrentSelectionIdx = Items.IndexOf(p => _currentDesign == p.Item1);
UpdateSelection(CurrentSelectionIdx >= 0 ? Items[CurrentSelectionIdx] : null);
return CurrentSelectionIdx;
}
protected bool Draw(IDesignStandIn? currentDesign, string? label, float width) protected bool Draw(IDesignStandIn? currentDesign, string? label, float width)
{ {
_currentDesign = currentDesign; _currentDesign = currentDesign;
InnerWidth = 400 * ImGuiHelpers.GlobalScale; UpdateCurrentSelection();
InnerWidth = 400 * ImGuiHelpers.GlobalScale;
var name = label ?? "Select Design Here..."; var name = label ?? "Select Design Here...";
bool ret; bool ret;
using (_ = currentDesign != null ? ImRaii.PushColor(ImGuiCol.Text, DesignColors.GetColor(currentDesign as Design)) : null) using (_ = currentDesign != null ? ImRaii.PushColor(ImGuiCol.Text, DesignColors.GetColor(currentDesign as Design)) : null)
@ -150,37 +123,52 @@ public abstract class DesignComboBase : FilterComboCache<Tuple<IDesignStandIn, s
return filter.IsContained(path) || filter.IsContained(design.ResolveName(false)); return filter.IsContained(path) || filter.IsContained(design.ResolveName(false));
} }
private void UpdateCurrentSelection()
{
if (!_isCurrentSelectionDirty)
return;
var priorState = IsInitialized;
if (priorState)
Cleanup();
CurrentSelectionIdx = Items.IndexOf(s => ReferenceEquals(s.Item1, CurrentSelection?.Item1));
if (CurrentSelectionIdx >= 0)
{
UpdateSelection(Items[CurrentSelectionIdx]);
}
else if (Items.Count > 0)
{
CurrentSelectionIdx = 0;
UpdateSelection(Items[0]);
}
else
{
UpdateSelection(null);
}
if (!priorState)
Cleanup();
_isCurrentSelectionDirty = false;
}
protected override int UpdateCurrentSelected(int currentSelected)
{
CurrentSelectionIdx = Items.IndexOf(p => _currentDesign == p.Item1);
UpdateSelection(CurrentSelectionIdx >= 0 ? Items[CurrentSelectionIdx] : null);
return CurrentSelectionIdx;
}
private void OnDesignChanged(DesignChanged.Type type, Design? _1, ITransaction? _2 = null) private void OnDesignChanged(DesignChanged.Type type, Design? _1, ITransaction? _2 = null)
{ {
switch (type) _isCurrentSelectionDirty = type switch
{ {
case DesignChanged.Type.Created: DesignChanged.Type.Created => true,
case DesignChanged.Type.Renamed: DesignChanged.Type.Renamed => true,
case DesignChanged.Type.ChangedColor: DesignChanged.Type.ChangedColor => true,
case DesignChanged.Type.Deleted: DesignChanged.Type.Deleted => true,
case DesignChanged.Type.QuickDesignBar: DesignChanged.Type.QuickDesignBar => true,
var priorState = IsInitialized; _ => _isCurrentSelectionDirty,
if (priorState) };
Cleanup();
CurrentSelectionIdx = Items.IndexOf(s => ReferenceEquals(s.Item1, CurrentSelection?.Item1));
if (CurrentSelectionIdx >= 0)
{
UpdateSelection(Items[CurrentSelectionIdx]);
}
else if (Items.Count > 0)
{
CurrentSelectionIdx = 0;
UpdateSelection(Items[0]);
}
else
{
UpdateSelection(null);
}
if (!priorState)
Cleanup();
break;
}
} }
private void QuickSelectedDesignTooltip(IDesignStandIn? design) private void QuickSelectedDesignTooltip(IDesignStandIn? design)
@ -248,8 +236,7 @@ public abstract class DesignCombo : DesignComboBase
public sealed class QuickDesignCombo : DesignCombo public sealed class QuickDesignCombo : DesignCombo
{ {
public QuickDesignCombo(DesignManager designs, public QuickDesignCombo(DesignFileSystem fileSystem,
DesignFileSystem fileSystem,
Logger log, Logger log,
DesignChanged designChanged, DesignChanged designChanged,
TabSelected tabSelected, TabSelected tabSelected,
@ -257,9 +244,9 @@ public sealed class QuickDesignCombo : DesignCombo
DesignColors designColors) DesignColors designColors)
: base(log, designChanged, tabSelected, config, designColors, () => : base(log, designChanged, tabSelected, config, designColors, () =>
[ [
.. designs.Designs .. fileSystem
.Where(d => d.QuickDesign) .Where(kvp => kvp.Key.QuickDesign)
.Select(d => new Tuple<IDesignStandIn, string>(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty)) .Select(kvp => new Tuple<IDesignStandIn, string>(kvp.Key, kvp.Value.FullName()))
.OrderBy(d => d.Item2), .OrderBy(d => d.Item2),
]) ])
{ {
@ -300,7 +287,6 @@ public sealed class QuickDesignCombo : DesignCombo
} }
public sealed class LinkDesignCombo( public sealed class LinkDesignCombo(
DesignManager designs,
DesignFileSystem fileSystem, DesignFileSystem fileSystem,
Logger log, Logger log,
DesignChanged designChanged, DesignChanged designChanged,
@ -309,8 +295,8 @@ public sealed class LinkDesignCombo(
DesignColors designColors) DesignColors designColors)
: DesignCombo(log, designChanged, tabSelected, config, designColors, () => : DesignCombo(log, designChanged, tabSelected, config, designColors, () =>
[ [
.. designs.Designs .. fileSystem
.Select(d => new Tuple<IDesignStandIn, string>(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty)) .Select(kvp => new Tuple<IDesignStandIn, string>(kvp.Key, kvp.Value.FullName()))
.OrderBy(d => d.Item2), .OrderBy(d => d.Item2),
]); ]);
@ -324,8 +310,8 @@ public sealed class RandomDesignCombo(
DesignColors designColors) DesignColors designColors)
: DesignCombo(log, designChanged, tabSelected, config, designColors, () => : DesignCombo(log, designChanged, tabSelected, config, designColors, () =>
[ [
.. designs.Designs .. fileSystem
.Select(d => new Tuple<IDesignStandIn, string>(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty)) .Select(kvp => new Tuple<IDesignStandIn, string>(kvp.Key, kvp.Value.FullName()))
.OrderBy(d => d.Item2), .OrderBy(d => d.Item2),
]) ])
{ {
@ -351,7 +337,6 @@ public sealed class RandomDesignCombo(
} }
public sealed class SpecialDesignCombo( public sealed class SpecialDesignCombo(
DesignManager designs,
DesignFileSystem fileSystem, DesignFileSystem fileSystem,
TabSelected tabSelected, TabSelected tabSelected,
DesignColors designColors, DesignColors designColors,
@ -361,8 +346,8 @@ public sealed class SpecialDesignCombo(
EphemeralConfig config, EphemeralConfig config,
RandomDesignGenerator rng, RandomDesignGenerator rng,
QuickSelectedDesign quickSelectedDesign) QuickSelectedDesign quickSelectedDesign)
: DesignComboBase(() => designs.Designs : DesignComboBase(() => fileSystem
.Select(d => new Tuple<IDesignStandIn, string>(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty)) .Select(kvp => new Tuple<IDesignStandIn, string>(kvp.Key, kvp.Value.FullName()))
.OrderBy(d => d.Item2) .OrderBy(d => d.Item2)
.Prepend(new Tuple<IDesignStandIn, string>(new RandomDesign(rng), string.Empty)) .Prepend(new Tuple<IDesignStandIn, string>(new RandomDesign(rng), string.Empty))
.Prepend(new Tuple<IDesignStandIn, string>(quickSelectedDesign, string.Empty)) .Prepend(new Tuple<IDesignStandIn, string>(quickSelectedDesign, string.Empty))
@ -380,29 +365,3 @@ public sealed class SpecialDesignCombo(
autoDesignManager.AddDesign(set, CurrentSelection!.Item1); autoDesignManager.AddDesign(set, CurrentSelection!.Item1);
} }
} }
public class DesignComboWrapper(ServiceManager services)
{
public readonly IReadOnlyList<DesignComboBase> Combos = services.GetServicesImplementing<DesignComboBase>().ToArray();
internal DesignComboListener StopListening()
{
var list = new List<DesignComboBase>(Combos.Count);
foreach (var combo in Combos.Where(c => c.IsListening))
{
combo.StopListening();
list.Add(combo);
}
return new DesignComboListener(list);
}
internal readonly struct DesignComboListener(List<DesignComboBase> combos) : IDisposable
{
public void Dispose()
{
foreach (var combo in combos)
combo.StartListening();
}
}
}

View file

@ -278,7 +278,7 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable
private void LookupTooltip(IEnumerable<Design> designs) private void LookupTooltip(IEnumerable<Design> designs)
{ {
using var _ = ImRaii.Tooltip(); using var _ = ImRaii.Tooltip();
var tt = string.Join('\n', designs.Select(d => _designFileSystem.FindLeaf(d, out var l) ? l.FullName() : d.Name.Text).OrderBy(t => t)); var tt = string.Join('\n', designs.Select(d => _designFileSystem.TryGetValue(d, out var l) ? l.FullName() : d.Name.Text).OrderBy(t => t));
ImGui.TextUnformatted(tt.Length == 0 ImGui.TextUnformatted(tt.Length == 0
? "Matches no currently existing designs." ? "Matches no currently existing designs."
: "Matches the following designs:"); : "Matches the following designs:");

View file

@ -3,7 +3,9 @@ using Glamourer.Designs;
using ImGuiNET; using ImGuiNET;
using OtterGui; using OtterGui;
using OtterGui.Extensions; using OtterGui.Extensions;
using OtterGui.Filesystem;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Gui.Debug; using Penumbra.GameData.Gui.Debug;
@ -19,6 +21,7 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _
public void Draw() public void Draw()
{ {
DrawButtons();
foreach (var (design, idx) in _designManager.Designs.WithIndex()) foreach (var (design, idx) in _designManager.Designs.WithIndex())
{ {
using var t = ImRaii.TreeNode($"{design.Name}##{idx}"); using var t = ImRaii.TreeNode($"{design.Name}##{idx}");
@ -26,7 +29,8 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _
continue; continue;
DrawDesign(design, _designFileSystem); DrawDesign(design, _designFileSystem);
var base64 = DesignBase64Migration.CreateOldBase64(design.DesignData, design.Application.Equip, design.Application.Customize, design.Application.Meta, var base64 = DesignBase64Migration.CreateOldBase64(design.DesignData, design.Application.Equip, design.Application.Customize,
design.Application.Meta,
design.WriteProtected()); design.WriteProtected());
using var font = ImRaii.PushFont(UiBuilder.MonoFont); using var font = ImRaii.PushFont(UiBuilder.MonoFont);
ImGuiUtil.TextWrapped(base64); ImGuiUtil.TextWrapped(base64);
@ -35,6 +39,26 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _
} }
} }
private void DrawButtons()
{
if (ImUtf8.Button("Generate 500 Test Designs"u8))
for (var i = 0; i < 500; ++i)
{
var design = _designManager.CreateEmpty($"Test Designs/Test Design {i}", true);
_designManager.AddTag(design, "_DebugTest");
}
ImUtf8.SameLineInner();
if (ImUtf8.Button("Remove All Test Designs"u8))
{
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 })
_designFileSystem.Delete(path);
}
}
public static void DrawDesign(DesignBase design, DesignFileSystem? fileSystem) public static void DrawDesign(DesignBase design, DesignFileSystem? fileSystem)
{ {
using var table = ImRaii.Table("##equip", 8, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit); using var table = ImRaii.Table("##equip", 8, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit);
@ -53,7 +77,7 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _
ImGui.TableNextRow(); ImGui.TableNextRow();
ImGuiUtil.DrawTableColumn("Design File System Path"); ImGuiUtil.DrawTableColumn("Design File System Path");
if (fileSystem != null) if (fileSystem != null)
ImGuiUtil.DrawTableColumn(fileSystem.FindLeaf(d, out var leaf) ? leaf.FullName() : "No Path Known"); ImGuiUtil.DrawTableColumn(fileSystem.TryGetValue(d, out var leaf) ? leaf.FullName() : "No Path Known");
ImGui.TableNextRow(); ImGui.TableNextRow();
ImGuiUtil.DrawTableColumn("Creation"); ImGuiUtil.DrawTableColumn("Creation");

View file

@ -14,8 +14,7 @@ public class MultiDesignPanel(
DesignFileSystemSelector selector, DesignFileSystemSelector selector,
DesignManager editor, DesignManager editor,
DesignColors colors, DesignColors colors,
Configuration config, Configuration config)
DesignComboWrapper combos)
{ {
private readonly Button[] _leftButtons = []; private readonly Button[] _leftButtons = [];
private readonly Button[] _rightButtons = [new IncognitoButton(config)]; private readonly Button[] _rightButtons = [new IncognitoButton(config)];
@ -206,7 +205,6 @@ public class MultiDesignPanel(
: $"Display all {_numDesigns} selected designs in the quick design bar. Changes {diff} designs."; : $"Display all {_numDesigns} selected designs in the quick design bar. Changes {diff} designs.";
if (ImUtf8.ButtonEx("Display Selected Designs in QDB"u8, tt, buttonWidth, diff == 0)) if (ImUtf8.ButtonEx("Display Selected Designs in QDB"u8, tt, buttonWidth, diff == 0))
{ {
using var disableListener = combos.StopListening();
foreach (var design in selector.SelectedPaths.OfType<DesignFileSystem.Leaf>()) foreach (var design in selector.SelectedPaths.OfType<DesignFileSystem.Leaf>())
editor.SetQuickDesign(design.Value, true); editor.SetQuickDesign(design.Value, true);
} }
@ -217,7 +215,6 @@ public class MultiDesignPanel(
: $"Hide all {_numDesigns} selected designs in the quick design bar. Changes {_numQuickDesignEnabled} designs."; : $"Hide all {_numDesigns} selected designs in the quick design bar. Changes {_numQuickDesignEnabled} designs.";
if (ImUtf8.ButtonEx("Hide Selected Designs in QDB"u8, tt, buttonWidth, _numQuickDesignEnabled == 0)) if (ImUtf8.ButtonEx("Hide Selected Designs in QDB"u8, tt, buttonWidth, _numQuickDesignEnabled == 0))
{ {
using var disableListener = combos.StopListening();
foreach (var design in selector.SelectedPaths.OfType<DesignFileSystem.Leaf>()) foreach (var design in selector.SelectedPaths.OfType<DesignFileSystem.Leaf>())
editor.SetQuickDesign(design.Value, false); editor.SetQuickDesign(design.Value, false);
} }
@ -339,7 +336,6 @@ public class MultiDesignPanel(
ImGui.SameLine(); ImGui.SameLine();
if (ImUtf8.ButtonEx(label, tooltip, width, _addDesigns.Count == 0)) if (ImUtf8.ButtonEx(label, tooltip, width, _addDesigns.Count == 0))
{ {
using var disableListener = combos.StopListening();
foreach (var design in _addDesigns) foreach (var design in _addDesigns)
editor.ChangeColor(design, _colorCombo.CurrentSelection!); editor.ChangeColor(design, _colorCombo.CurrentSelection!);
} }
@ -353,7 +349,6 @@ public class MultiDesignPanel(
ImGui.SameLine(); ImGui.SameLine();
if (ImUtf8.ButtonEx(label, tooltip, width, _removeDesigns.Count == 0)) if (ImUtf8.ButtonEx(label, tooltip, width, _removeDesigns.Count == 0))
{ {
using var disableListener = combos.StopListening();
foreach (var (design, _) in _removeDesigns) foreach (var (design, _) in _removeDesigns)
editor.ChangeColor(design, string.Empty); editor.ChangeColor(design, string.Empty);
} }

View file

@ -169,6 +169,5 @@ public static class StaticServiceManager
.AddSingleton<DesignQuickBar>() .AddSingleton<DesignQuickBar>()
.AddSingleton<DesignColorUi>() .AddSingleton<DesignColorUi>()
.AddSingleton<NpcCombo>() .AddSingleton<NpcCombo>()
.AddSingleton<TextureCache>() .AddSingleton<TextureCache>();
.AddSingleton<DesignComboWrapper>();
} }

@ -1 +1 @@
Subproject commit cee50c3fe97a03ca7445c81de651b609620da526 Subproject commit 78528f93ac253db0061d9a8244cfa0cee5c2f873