From a824d94ffe55e420f93c4dc61b0405f31ded123f Mon Sep 17 00:00:00 2001 From: azyges Date: Sat, 3 Jan 2026 10:20:07 +0900 Subject: [PATCH] slight adjustments and fixes --- .../Configurations/LightlessConfig.cs | 1 + .../PlayerData/Pairs/PairHandlerAdapter.cs | 7 +- LightlessSync/UI/DataAnalysisUi.cs | 135 ++++++++++++++++-- LightlessSync/UI/DtrEntry.cs | 13 +- .../UI/Models/TextureFormatSortMode.cs | 8 ++ 5 files changed, 148 insertions(+), 16 deletions(-) create mode 100644 LightlessSync/UI/Models/TextureFormatSortMode.cs diff --git a/LightlessSync/LightlessConfiguration/Configurations/LightlessConfig.cs b/LightlessSync/LightlessConfiguration/Configurations/LightlessConfig.cs index 737f9ee..c475e9e 100644 --- a/LightlessSync/LightlessConfiguration/Configurations/LightlessConfig.cs +++ b/LightlessSync/LightlessConfiguration/Configurations/LightlessConfig.cs @@ -51,6 +51,7 @@ public class LightlessConfig : ILightlessConfiguration public bool PreferNotesOverNamesForVisible { get; set; } = false; public VisiblePairSortMode VisiblePairSortMode { get; set; } = VisiblePairSortMode.Alphabetical; public OnlinePairSortMode OnlinePairSortMode { get; set; } = OnlinePairSortMode.Alphabetical; + public TextureFormatSortMode TextureFormatSortMode { get; set; } = TextureFormatSortMode.None; public float ProfileDelay { get; set; } = 1.5f; public bool ProfilePopoutRight { get; set; } = false; public bool ProfilesAllowNsfw { get; set; } = false; diff --git a/LightlessSync/PlayerData/Pairs/PairHandlerAdapter.cs b/LightlessSync/PlayerData/Pairs/PairHandlerAdapter.cs index 6d859ac..5e5beeb 100644 --- a/LightlessSync/PlayerData/Pairs/PairHandlerAdapter.cs +++ b/LightlessSync/PlayerData/Pairs/PairHandlerAdapter.cs @@ -644,9 +644,8 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa var dataApplied = !string.IsNullOrEmpty(dataHash) && string.Equals(dataHash, _lastSuccessfulDataHash ?? string.Empty, StringComparison.Ordinal); var needsApply = !dataApplied; - var hasModReplacements = sanitized.FileReplacements.Values.Any(list => list.Count > 0); - var needsModReapply = needsApply && hasModReplacements; - var shouldForceMods = shouldForce || needsModReapply; + var modFilesChanged = PlayerModFilesChanged(sanitized, _cachedData); + var shouldForceMods = shouldForce || modFilesChanged; forceApplyCustomization = forced || needsApply; var suppressForcedModRedraw = !forced && hasMissingCachedFiles && dataApplied; @@ -2192,7 +2191,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa if (ex is AggregateException aggr && aggr.InnerExceptions.Any(e => e is ArgumentNullException)) { IsVisible = false; - _forceApplyMods = true; + _forceApplyMods = true; _cachedData = charaData; _pairStateCache.Store(Ident, charaData); _forceFullReapply = true; diff --git a/LightlessSync/UI/DataAnalysisUi.cs b/LightlessSync/UI/DataAnalysisUi.cs index de6f697..e0bfcb1 100644 --- a/LightlessSync/UI/DataAnalysisUi.cs +++ b/LightlessSync/UI/DataAnalysisUi.cs @@ -11,6 +11,7 @@ using LightlessSync.LightlessConfiguration; using LightlessSync.Services; using LightlessSync.Services.Mediator; using LightlessSync.Services.TextureCompression; +using LightlessSync.UI.Models; using LightlessSync.Utils; using Microsoft.Extensions.Logging; using OtterTex; @@ -42,6 +43,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase private readonly Progress _conversionProgress = new(); private readonly IpcManager _ipcManager; private readonly UiSharedService _uiSharedService; + private readonly LightlessConfigService _configService; private readonly PlayerPerformanceConfigService _playerPerformanceConfig; private readonly TransientResourceManager _transientResourceManager; private readonly TransientConfigService _transientConfigService; @@ -106,10 +108,12 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase private TextureUsageCategory? _textureCategoryFilter = null; private TextureMapKind? _textureMapFilter = null; private TextureCompressionTarget? _textureTargetFilter = null; + private TextureFormatSortMode _textureFormatSortMode = TextureFormatSortMode.None; public DataAnalysisUi(ILogger logger, LightlessMediator mediator, CharacterAnalyzer characterAnalyzer, IpcManager ipcManager, PerformanceCollectorService performanceCollectorService, UiSharedService uiSharedService, + LightlessConfigService configService, PlayerPerformanceConfigService playerPerformanceConfig, TransientResourceManager transientResourceManager, TransientConfigService transientConfigService, TextureCompressionService textureCompressionService, TextureMetadataHelper textureMetadataHelper) @@ -118,6 +122,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase _characterAnalyzer = characterAnalyzer; _ipcManager = ipcManager; _uiSharedService = uiSharedService; + _configService = configService; _playerPerformanceConfig = playerPerformanceConfig; _transientResourceManager = transientResourceManager; _transientConfigService = transientConfigService; @@ -971,6 +976,13 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase #if DEBUG ResetDebugCompressionModalState(); #endif + var savedFormatSort = _configService.Current.TextureFormatSortMode; + if (!Enum.IsDefined(typeof(TextureFormatSortMode), savedFormatSort)) + { + savedFormatSort = TextureFormatSortMode.None; + } + + SetTextureFormatSortMode(savedFormatSort, persist: false); } protected override void Dispose(bool disposing) @@ -2198,26 +2210,56 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase ImGui.TableSetupColumn("Original", ImGuiTableColumnFlags.PreferSortDescending); ImGui.TableSetupColumn("Compressed", ImGuiTableColumnFlags.PreferSortDescending); ImGui.TableSetupScrollFreeze(0, 1); - ImGui.TableHeadersRow(); + DrawTextureTableHeaderRow(); var targets = _textureCompressionService.SelectableTargets; IEnumerable orderedRows = rows; var sortSpecs = ImGui.TableGetSortSpecs(); + var sizeSortColumn = -1; + var sizeSortDirection = ImGuiSortDirection.Ascending; if (sortSpecs.SpecsCount > 0) { var spec = sortSpecs.Specs[0]; - orderedRows = spec.ColumnIndex switch + if (spec.ColumnIndex is 7 or 8) { - 7 => spec.SortDirection == ImGuiSortDirection.Ascending - ? rows.OrderBy(r => r.OriginalSize) - : rows.OrderByDescending(r => r.OriginalSize), - 8 => spec.SortDirection == ImGuiSortDirection.Ascending - ? rows.OrderBy(r => r.CompressedSize) - : rows.OrderByDescending(r => r.CompressedSize), - _ => rows - }; + sizeSortColumn = spec.ColumnIndex; + sizeSortDirection = spec.SortDirection; + } + } + var hasSizeSort = sizeSortColumn != -1; + var indexedRows = rows.Select((row, idx) => (row, idx)); + + if (_textureFormatSortMode != TextureFormatSortMode.None) + { + bool compressedFirst = _textureFormatSortMode == TextureFormatSortMode.CompressedFirst; + int GroupKey(TextureRow row) => row.IsAlreadyCompressed == compressedFirst ? 0 : 1; + long SizeKey(TextureRow row) => sizeSortColumn == 7 ? row.OriginalSize : row.CompressedSize; + + var ordered = indexedRows.OrderBy(pair => GroupKey(pair.row)); + if (hasSizeSort) + { + ordered = sizeSortDirection == ImGuiSortDirection.Ascending + ? ordered.ThenBy(pair => SizeKey(pair.row)) + : ordered.ThenByDescending(pair => SizeKey(pair.row)); + } + + orderedRows = ordered + .ThenBy(pair => pair.idx) + .Select(pair => pair.row); + } + else if (hasSizeSort) + { + long SizeKey(TextureRow row) => sizeSortColumn == 7 ? row.OriginalSize : row.CompressedSize; + + orderedRows = sizeSortDirection == ImGuiSortDirection.Ascending + ? indexedRows.OrderBy(pair => SizeKey(pair.row)).ThenBy(pair => pair.idx).Select(pair => pair.row) + : indexedRows.OrderByDescending(pair => SizeKey(pair.row)).ThenBy(pair => pair.idx).Select(pair => pair.row); + } + + if (sortSpecs.SpecsCount > 0) + { sortSpecs.SpecsDirty = false; } @@ -2259,6 +2301,79 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase } } } + + private void DrawTextureTableHeaderRow() + { + ImGui.TableNextRow(ImGuiTableRowFlags.Headers); + + DrawHeaderCell(0, "##select"); + DrawHeaderCell(1, "Texture"); + DrawHeaderCell(2, "Slot"); + DrawHeaderCell(3, "Map"); + DrawFormatHeaderCell(); + DrawHeaderCell(5, "Recommended"); + DrawHeaderCell(6, "Target"); + DrawHeaderCell(7, "Original"); + DrawHeaderCell(8, "Compressed"); + } + + private static void DrawHeaderCell(int columnIndex, string label) + { + ImGui.TableSetColumnIndex(columnIndex); + ImGui.TableHeader(label); + } + + private void DrawFormatHeaderCell() + { + ImGui.TableSetColumnIndex(4); + ImGui.TableHeader(GetFormatHeaderLabel()); + + if (ImGui.IsItemClicked(ImGuiMouseButton.Left)) + { + CycleTextureFormatSortMode(); + } + + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip("Click to cycle sort: normal, compressed first, uncompressed first."); + } + } + + private string GetFormatHeaderLabel() + => _textureFormatSortMode switch + { + TextureFormatSortMode.CompressedFirst => "Format (C)##formatHeader", + TextureFormatSortMode.UncompressedFirst => "Format (U)##formatHeader", + _ => "Format##formatHeader" + }; + + private void SetTextureFormatSortMode(TextureFormatSortMode mode, bool persist = true) + { + if (_textureFormatSortMode == mode) + { + return; + } + + _textureFormatSortMode = mode; + if (persist) + { + _configService.Current.TextureFormatSortMode = mode; + _configService.Save(); + } + } + + private void CycleTextureFormatSortMode() + { + var nextMode = _textureFormatSortMode switch + { + TextureFormatSortMode.None => TextureFormatSortMode.CompressedFirst, + TextureFormatSortMode.CompressedFirst => TextureFormatSortMode.UncompressedFirst, + _ => TextureFormatSortMode.None + }; + + SetTextureFormatSortMode(nextMode); + } + private void StartTextureConversion() { if (_conversionTask != null && !_conversionTask.IsCompleted) diff --git a/LightlessSync/UI/DtrEntry.cs b/LightlessSync/UI/DtrEntry.cs index ae94d5e..5aa69eb 100644 --- a/LightlessSync/UI/DtrEntry.cs +++ b/LightlessSync/UI/DtrEntry.cs @@ -103,10 +103,19 @@ public sealed class DtrEntry : IDisposable, IHostedService public async Task StopAsync(CancellationToken cancellationToken) { - await _cancellationTokenSource.CancelAsync().ConfigureAwait(false); + _cancellationTokenSource.Cancel(); + + if (_dalamudUtilService.IsOnFrameworkThread) + { + _logger.LogDebug("Skipping Lightfinder DTR wait on framework thread during shutdown."); + _cancellationTokenSource.Dispose(); + return; + } + try { - await _runTask!.ConfigureAwait(false); + if (_runTask != null) + await _runTask.ConfigureAwait(false); } catch (OperationCanceledException) { diff --git a/LightlessSync/UI/Models/TextureFormatSortMode.cs b/LightlessSync/UI/Models/TextureFormatSortMode.cs new file mode 100644 index 0000000..165e10d --- /dev/null +++ b/LightlessSync/UI/Models/TextureFormatSortMode.cs @@ -0,0 +1,8 @@ +namespace LightlessSync.UI.Models; + +public enum TextureFormatSortMode +{ + None = 0, + CompressedFirst = 1, + UncompressedFirst = 2 +}