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);
public Dictionary<Guid, (string DisplayName, string FullPath, uint DisplayColor, bool ShownInQdb)> GetDesignListExtended()
=> designs.Designs.ToDictionary(d => d.Identifier,
d => (d.Name.Text, fileSystem.FindLeaf(d, out var leaf) ? leaf.FullName() : d.Name.Text, color.GetColor(d), d.QuickDesign));
=> fileSystem.ToDictionary(kvp => kvp.Key.Identifier,
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)
=> 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);
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;
case DesignChanged.Type.Deleted:
if (FindLeaf(design, out var leaf1))
if (TryGetValue(design, out var leaf1))
Delete(leaf1);
return;
case DesignChanged.Type.ReloadedAll:
Reload();
return;
case DesignChanged.Type.Renamed when (data as RenameTransaction?)?.Old is { } oldName:
if (!FindLeaf(design, out var leaf2))
if (!TryGetValue(design, out var leaf2))
return;
var old = oldName.FixName();
@ -150,15 +150,6 @@ public sealed class DesignFileSystem : FileSystem<Design>, IDisposable, ISavable
? (string.Empty, false)
: (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)
{
if (oldPaths.Count == 0)

View file

@ -22,7 +22,7 @@ public interface IDesignPredicate
: designs;
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

View file

@ -10,7 +10,6 @@ using OtterGui;
using OtterGui.Classes;
using OtterGui.Extensions;
using OtterGui.Log;
using OtterGui.Services;
using OtterGui.Widgets;
namespace Glamourer.Gui;
@ -22,8 +21,8 @@ public abstract class DesignComboBase : FilterComboCache<Tuple<IDesignStandIn, s
protected readonly DesignColors DesignColors;
protected readonly TabSelected TabSelected;
protected float InnerWidth;
public bool IsListening { get; protected set; }
private IDesignStandIn? _currentDesign;
private bool _isCurrentSelectionDirty;
protected DesignComboBase(Func<IReadOnlyList<Tuple<IDesignStandIn, string>>> generator, Logger log, DesignChanged designChanged,
TabSelected tabSelected, EphemeralConfig config, DesignColors designColors)
@ -34,7 +33,6 @@ public abstract class DesignComboBase : FilterComboCache<Tuple<IDesignStandIn, s
Config = config;
DesignColors = designColors;
DesignChanged.Subscribe(OnDesignChanged, DesignChanged.Priority.DesignCombo);
IsListening = true;
}
public bool Incognito
@ -46,25 +44,6 @@ public abstract class DesignComboBase : FilterComboCache<Tuple<IDesignStandIn, s
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)
{
var (design, path) = Items[globalIdx];
@ -106,17 +85,11 @@ public abstract class DesignComboBase : FilterComboCache<Tuple<IDesignStandIn, s
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)
{
_currentDesign = currentDesign;
InnerWidth = 400 * ImGuiHelpers.GlobalScale;
UpdateCurrentSelection();
InnerWidth = 400 * ImGuiHelpers.GlobalScale;
var name = label ?? "Select Design Here...";
bool ret;
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));
}
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)
{
switch (type)
_isCurrentSelectionDirty = type switch
{
case DesignChanged.Type.Created:
case DesignChanged.Type.Renamed:
case DesignChanged.Type.ChangedColor:
case DesignChanged.Type.Deleted:
case DesignChanged.Type.QuickDesignBar:
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();
break;
}
DesignChanged.Type.Created => true,
DesignChanged.Type.Renamed => true,
DesignChanged.Type.ChangedColor => true,
DesignChanged.Type.Deleted => true,
DesignChanged.Type.QuickDesignBar => true,
_ => _isCurrentSelectionDirty,
};
}
private void QuickSelectedDesignTooltip(IDesignStandIn? design)
@ -248,8 +236,7 @@ public abstract class DesignCombo : DesignComboBase
public sealed class QuickDesignCombo : DesignCombo
{
public QuickDesignCombo(DesignManager designs,
DesignFileSystem fileSystem,
public QuickDesignCombo(DesignFileSystem fileSystem,
Logger log,
DesignChanged designChanged,
TabSelected tabSelected,
@ -257,9 +244,9 @@ public sealed class QuickDesignCombo : DesignCombo
DesignColors designColors)
: base(log, designChanged, tabSelected, config, designColors, () =>
[
.. designs.Designs
.Where(d => d.QuickDesign)
.Select(d => new Tuple<IDesignStandIn, string>(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty))
.. fileSystem
.Where(kvp => kvp.Key.QuickDesign)
.Select(kvp => new Tuple<IDesignStandIn, string>(kvp.Key, kvp.Value.FullName()))
.OrderBy(d => d.Item2),
])
{
@ -300,7 +287,6 @@ public sealed class QuickDesignCombo : DesignCombo
}
public sealed class LinkDesignCombo(
DesignManager designs,
DesignFileSystem fileSystem,
Logger log,
DesignChanged designChanged,
@ -309,8 +295,8 @@ public sealed class LinkDesignCombo(
DesignColors designColors)
: DesignCombo(log, designChanged, tabSelected, config, designColors, () =>
[
.. designs.Designs
.Select(d => new Tuple<IDesignStandIn, string>(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty))
.. fileSystem
.Select(kvp => new Tuple<IDesignStandIn, string>(kvp.Key, kvp.Value.FullName()))
.OrderBy(d => d.Item2),
]);
@ -324,8 +310,8 @@ public sealed class RandomDesignCombo(
DesignColors designColors)
: DesignCombo(log, designChanged, tabSelected, config, designColors, () =>
[
.. designs.Designs
.Select(d => new Tuple<IDesignStandIn, string>(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty))
.. fileSystem
.Select(kvp => new Tuple<IDesignStandIn, string>(kvp.Key, kvp.Value.FullName()))
.OrderBy(d => d.Item2),
])
{
@ -351,7 +337,6 @@ public sealed class RandomDesignCombo(
}
public sealed class SpecialDesignCombo(
DesignManager designs,
DesignFileSystem fileSystem,
TabSelected tabSelected,
DesignColors designColors,
@ -361,8 +346,8 @@ public sealed class SpecialDesignCombo(
EphemeralConfig config,
RandomDesignGenerator rng,
QuickSelectedDesign quickSelectedDesign)
: DesignComboBase(() => designs.Designs
.Select(d => new Tuple<IDesignStandIn, string>(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty))
: DesignComboBase(() => fileSystem
.Select(kvp => new Tuple<IDesignStandIn, string>(kvp.Key, kvp.Value.FullName()))
.OrderBy(d => d.Item2)
.Prepend(new Tuple<IDesignStandIn, string>(new RandomDesign(rng), string.Empty))
.Prepend(new Tuple<IDesignStandIn, string>(quickSelectedDesign, string.Empty))
@ -380,29 +365,3 @@ public sealed class SpecialDesignCombo(
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)
{
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
? "Matches no currently existing designs."
: "Matches the following designs:");

View file

@ -3,7 +3,9 @@ using Glamourer.Designs;
using ImGuiNET;
using OtterGui;
using OtterGui.Extensions;
using OtterGui.Filesystem;
using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Gui.Debug;
@ -19,6 +21,7 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _
public void Draw()
{
DrawButtons();
foreach (var (design, idx) in _designManager.Designs.WithIndex())
{
using var t = ImRaii.TreeNode($"{design.Name}##{idx}");
@ -26,7 +29,8 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _
continue;
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());
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
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)
{
using var table = ImRaii.Table("##equip", 8, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit);
@ -53,7 +77,7 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _
ImGui.TableNextRow();
ImGuiUtil.DrawTableColumn("Design File System Path");
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();
ImGuiUtil.DrawTableColumn("Creation");

View file

@ -14,8 +14,7 @@ public class MultiDesignPanel(
DesignFileSystemSelector selector,
DesignManager editor,
DesignColors colors,
Configuration config,
DesignComboWrapper combos)
Configuration config)
{
private readonly Button[] _leftButtons = [];
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.";
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>())
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.";
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>())
editor.SetQuickDesign(design.Value, false);
}
@ -339,7 +336,6 @@ public class MultiDesignPanel(
ImGui.SameLine();
if (ImUtf8.ButtonEx(label, tooltip, width, _addDesigns.Count == 0))
{
using var disableListener = combos.StopListening();
foreach (var design in _addDesigns)
editor.ChangeColor(design, _colorCombo.CurrentSelection!);
}
@ -353,7 +349,6 @@ public class MultiDesignPanel(
ImGui.SameLine();
if (ImUtf8.ButtonEx(label, tooltip, width, _removeDesigns.Count == 0))
{
using var disableListener = combos.StopListening();
foreach (var (design, _) in _removeDesigns)
editor.ChangeColor(design, string.Empty);
}

View file

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

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