This commit is contained in:
2025-12-16 06:31:29 +09:00
parent bdfcf254a8
commit 4444a88746
32 changed files with 1204 additions and 464 deletions

View File

@@ -1,5 +1,4 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Game.Text;
using Dalamud.Interface;
using Dalamud.Interface.Colors;
@@ -8,6 +7,7 @@ using Dalamud.Interface.Utility.Raii;
using Dalamud.Utility;
using LightlessSync.API.Data;
using LightlessSync.API.Data.Comparer;
using LightlessSync.API.Data.Enum;
using LightlessSync.API.Routes;
using LightlessSync.FileCache;
using LightlessSync.Interop.Ipc;
@@ -18,9 +18,11 @@ using LightlessSync.PlayerData.Handlers;
using LightlessSync.PlayerData.Pairs;
using LightlessSync.Services;
using LightlessSync.Services.ActorTracking;
using LightlessSync.Services.Events;
using LightlessSync.Services.Mediator;
using LightlessSync.Services.PairProcessing;
using LightlessSync.Services.ServerConfiguration;
using LightlessSync.UI.Models;
using LightlessSync.UI.Services;
using LightlessSync.UI.Style;
using LightlessSync.Utils;
@@ -39,7 +41,6 @@ using System.Net.Http.Json;
using System.Numerics;
using System.Text;
using System.Text.Json;
using static Penumbra.GameData.Files.ShpkFile;
namespace LightlessSync.UI;
@@ -62,6 +63,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
private readonly PerformanceCollectorService _performanceCollector;
private readonly PlayerPerformanceConfigService _playerPerformanceConfigService;
private readonly PairProcessingLimiter _pairProcessingLimiter;
private readonly EventAggregator _eventAggregator;
private readonly ServerConfigurationManager _serverConfigurationManager;
private readonly UiSharedService _uiShared;
private readonly IProgress<(int, int, FileCacheEntity)> _validationProgress;
@@ -75,11 +77,13 @@ public class SettingsUi : WindowMediatorSubscriberBase
private bool _readClearCache = false;
private int _selectedEntry = -1;
private string _uidToAddForIgnore = string.Empty;
private string _lightfinderIconInput = string.Empty;
private bool _lightfinderIconInputInitialized = false;
private int _lightfinderIconPresetIndex = -1;
private bool _selectGeneralTabOnNextDraw = false;
private string _pairDebugFilter = string.Empty;
private bool _pairDebugVisibleOnly = true;
private bool _pairDiagnosticsEnabled;
private string? _selectedPairDebugUid = null;
private static readonly LightlessConfig DefaultConfig = new();
private static readonly JsonSerializerOptions DebugJsonOptions = new() { WriteIndented = true };
private MainSettingsTab _selectedMainTab = MainSettingsTab.General;
private TransferSettingsTab _selectedTransferTab = TransferSettingsTab.Transfers;
private ServerSettingsTab _selectedServerTab = ServerSettingsTab.CharacterManagement;
@@ -143,15 +147,6 @@ public class SettingsUi : WindowMediatorSubscriberBase
PermissionSettings,
}
private static readonly (string Label, SeIconChar Icon)[] LightfinderIconPresets = new[]
{
("Link Marker", SeIconChar.LinkMarker), ("Hyadelyn", SeIconChar.Hyadelyn), ("Gil", SeIconChar.Gil),
("Quest Sync", SeIconChar.QuestSync), ("Glamoured", SeIconChar.Glamoured),
("Glamoured (Dyed)", SeIconChar.GlamouredDyed), ("Auto-Translate Open", SeIconChar.AutoTranslateOpen),
("Auto-Translate Close", SeIconChar.AutoTranslateClose), ("Boxed Star", SeIconChar.BoxedStar),
("Boxed Plus", SeIconChar.BoxedPlus)
};
private CancellationTokenSource? _validationCts;
private Task<List<FileCacheEntity>>? _validationTask;
private bool _wasOpen = false;
@@ -162,6 +157,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
ServerConfigurationManager serverConfigurationManager,
PlayerPerformanceConfigService playerPerformanceConfigService,
PairProcessingLimiter pairProcessingLimiter,
EventAggregator eventAggregator,
LightlessMediator mediator, PerformanceCollectorService performanceCollector,
FileUploadManager fileTransferManager,
FileTransferOrchestrator fileTransferOrchestrator,
@@ -179,6 +175,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
_serverConfigurationManager = serverConfigurationManager;
_playerPerformanceConfigService = playerPerformanceConfigService;
_pairProcessingLimiter = pairProcessingLimiter;
_eventAggregator = eventAggregator;
_performanceCollector = performanceCollector;
_fileTransferManager = fileTransferManager;
_fileTransferOrchestrator = fileTransferOrchestrator;
@@ -192,34 +189,14 @@ public class SettingsUi : WindowMediatorSubscriberBase
_uiShared = uiShared;
_nameplateService = nameplateService;
_actorObjectService = actorObjectService;
AllowClickthrough = false;
AllowPinning = true;
_validationProgress = new Progress<(int, int, FileCacheEntity)>(v => _currentProgress = v);
SizeConstraints = new WindowSizeConstraints()
{
MinimumSize = new Vector2(900f, 400f),
MaximumSize = new Vector2(900f, 2000f),
};
TitleBarButtons = new()
{
new TitleBarButton()
{
Icon = FontAwesomeIcon.FileAlt,
Click = (msg) =>
{
Mediator.Publish(new UiToggleMessage(typeof(UpdateNotesUi)));
},
IconOffset = new(2, 1),
ShowTooltip = () =>
{
ImGui.BeginTooltip();
ImGui.Text("View Update Notes");
ImGui.EndTooltip();
}
}
};
WindowBuilder.For(this)
.AllowPinning(true)
.AllowClickthrough(false)
.SetSizeConstraints(new Vector2(900f, 400f), new Vector2(900f, 2000f))
.AddTitleBarButton(FontAwesomeIcon.FileAlt, "View Update Notes", () => Mediator.Publish(new UiToggleMessage(typeof(UpdateNotesUi))))
.Apply();
Mediator.Subscribe<OpenSettingsUiMessage>(this, (_) => Toggle());
Mediator.Subscribe<OpenLightfinderSettingsMessage>(this, (_) =>
@@ -1309,9 +1286,425 @@ public class SettingsUi : WindowMediatorSubscriberBase
UiSharedService.TooltipSeparator
+ "Keeping LOD enabled can lead to more crashes. Use at your own risk.");
ImGuiHelpers.ScaledDummy(10f);
DrawPairDebugPanel();
UiSharedService.ColoredSeparator(UIColors.Get("LightlessYellow"), 2f);
}
private void DrawPairDebugPanel()
{
ImGui.PushStyleColor(ImGuiCol.Text, UIColors.Get("LightlessYellow"));
ImGui.TextUnformatted("Pair Diagnostics");
ImGui.PopStyleColor();
ImGuiHelpers.ScaledDummy(3f);
ImGui.Checkbox("Enable Pair Diagnostics", ref _pairDiagnosticsEnabled);
UiSharedService.AttachToolTip("When disabled the UI stops querying pair handlers and no diagnostics are processed.");
if (!_pairDiagnosticsEnabled)
{
UiSharedService.ColorTextWrapped("Diagnostics are disabled. Enable the toggle above to inspect active pairs.", UIColors.Get("LightlessYellow"));
return;
}
var snapshot = _pairUiService.GetSnapshot();
if (snapshot.PairsByUid.Count == 0)
{
UiSharedService.ColorTextWrapped("No pairs are currently tracked. Connect to the service and re-open this panel.", UIColors.Get("LightlessYellow"));
return;
}
ImGui.SetNextItemWidth(280f * ImGuiHelpers.GlobalScale);
ImGui.InputTextWithHint("##pairDebugFilter", "Search by UID, alias, or player name...", ref _pairDebugFilter, 96);
UiSharedService.AttachToolTip("Filters the list by UID, aliases, or currently cached player name.");
ImGui.SameLine();
ImGui.Checkbox("Visible pairs only", ref _pairDebugVisibleOnly);
UiSharedService.AttachToolTip("When enabled only currently visible pairs remain in the list.");
var pairs = snapshot.PairsByUid.Values;
var filteredPairs = pairs
.Where(p => !_pairDebugVisibleOnly || p.IsVisible)
.Where(p => PairMatchesFilter(p, _pairDebugFilter))
.OrderByDescending(p => p.IsVisible)
.ThenByDescending(p => p.IsOnline)
.ThenBy(p => p.UserData.AliasOrUID, StringComparer.OrdinalIgnoreCase)
.ToList();
if (filteredPairs.Count == 0)
{
UiSharedService.ColorTextWrapped("No pairs match the current filters.", UIColors.Get("LightlessYellow"));
_selectedPairDebugUid = null;
return;
}
if (_selectedPairDebugUid is null || !filteredPairs.Any(p => string.Equals(p.UserData.UID, _selectedPairDebugUid, StringComparison.Ordinal)))
{
_selectedPairDebugUid = filteredPairs[0].UserData.UID;
}
if (_selectedPairDebugUid is null || !snapshot.PairsByUid.TryGetValue(_selectedPairDebugUid, out var selectedPair))
{
selectedPair = filteredPairs[0];
}
var visibleCount = pairs.Count(p => p.IsVisible);
var onlineCount = pairs.Count(p => p.IsOnline);
var totalPairs = snapshot.PairsByUid.Count;
ImGui.TextUnformatted($"Visible: {visibleCount} / {totalPairs}; Online: {onlineCount}");
var mainChildHeight = MathF.Max(ImGui.GetTextLineHeightWithSpacing() * 12f, ImGui.GetContentRegionAvail().Y * 0.95f);
if (ImGui.BeginChild("##pairDebugPanel", new Vector2(-1, mainChildHeight), true, ImGuiWindowFlags.HorizontalScrollbar))
{
var childAvail = ImGui.GetContentRegionAvail();
var leftWidth = MathF.Max(220f * ImGuiHelpers.GlobalScale, childAvail.X * 0.35f);
leftWidth = MathF.Min(leftWidth, childAvail.X * 0.6f);
if (ImGui.BeginChild("##pairDebugList", new Vector2(leftWidth, 0), true, ImGuiWindowFlags.HorizontalScrollbar))
{
if (ImGui.BeginTable("##pairDebugTable", 3, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.ScrollY | ImGuiTableFlags.ScrollX))
{
ImGui.TableSetupScrollFreeze(0, 1);
ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed, 20f * ImGuiHelpers.GlobalScale);
ImGui.TableSetupColumn("Pair");
ImGui.TableSetupColumn("State", ImGuiTableColumnFlags.WidthFixed, 90f * ImGuiHelpers.GlobalScale);
ImGui.TableHeadersRow();
foreach (var entry in filteredPairs)
{
var isSelected = string.Equals(entry.UserData.UID, _selectedPairDebugUid, StringComparison.Ordinal);
ImGui.TableNextRow();
ImGui.TableNextColumn();
DrawPairStateIndicator(entry);
ImGui.TableNextColumn();
if (ImGui.Selectable($"{entry.UserData.AliasOrUID}##pairDebugSelect_{entry.UserData.UID}", isSelected, ImGuiSelectableFlags.SpanAllColumns))
{
_selectedPairDebugUid = entry.UserData.UID;
}
if (ImGui.IsItemHovered())
{
ImGui.SetTooltip($"UID: {entry.UserData.UID}\nVisible: {entry.IsVisible}\nOnline: {entry.IsOnline}\nDirect pair: {entry.IsDirectlyPaired}");
}
ImGui.TableNextColumn();
ImGui.TextUnformatted(entry.IsVisible ? "Visible" : entry.IsOnline ? "Online" : "Offline");
}
ImGui.EndTable();
}
}
ImGui.EndChild();
ImGui.SameLine();
if (ImGui.BeginChild("##pairDebugDetails", new Vector2(0, 0), true, ImGuiWindowFlags.HorizontalScrollbar))
{
DrawPairDebugDetails(selectedPair, snapshot);
}
ImGui.EndChild();
}
ImGui.EndChild();
}
private static bool PairMatchesFilter(Pair pair, string filter)
{
if (string.IsNullOrWhiteSpace(filter))
{
return true;
}
return pair.UserData.UID.Contains(filter, StringComparison.OrdinalIgnoreCase)
|| pair.UserData.AliasOrUID.Contains(filter, StringComparison.OrdinalIgnoreCase)
|| (!string.IsNullOrEmpty(pair.PlayerName) && pair.PlayerName.Contains(filter, StringComparison.OrdinalIgnoreCase))
|| (!string.IsNullOrEmpty(pair.Ident) && pair.Ident.Contains(filter, StringComparison.OrdinalIgnoreCase));
}
private static void DrawPairStateIndicator(Pair pair)
{
var color = pair.IsVisible
? UIColors.Get("LightlessGreen")
: pair.IsOnline ? UIColors.Get("LightlessYellow")
: UIColors.Get("DimRed");
var drawList = ImGui.GetWindowDrawList();
var cursor = ImGui.GetCursorScreenPos();
var radius = ImGui.GetTextLineHeight() * 0.35f;
var center = cursor + new Vector2(radius, radius);
drawList.AddCircleFilled(center, radius, ImGui.ColorConvertFloat4ToU32(color));
ImGui.Dummy(new Vector2(radius * 2f, radius * 2f));
}
private void DrawPairDebugDetails(Pair pair, PairUiSnapshot snapshot)
{
var debugInfo = pair.GetDebugInfo();
var statusColor = pair.IsVisible
? UIColors.Get("LightlessGreen")
: pair.IsOnline ? UIColors.Get("LightlessYellow")
: UIColors.Get("DimRed");
ImGui.TextColored(statusColor, pair.UserData.AliasOrUID);
ImGui.SameLine();
ImGui.TextColored(statusColor, $"[{(pair.IsVisible ? "Visible" : pair.IsOnline ? "Online" : "Offline")}]");
if (ImGui.BeginTable("##pairDebugProperties", 2, ImGuiTableFlags.SizingStretchProp))
{
DrawPairPropertyRow("UID", pair.UserData.UID);
DrawPairPropertyRow("Alias", string.IsNullOrEmpty(pair.UserData.Alias) ? "(none)" : pair.UserData.Alias!);
DrawPairPropertyRow("Player Name", pair.PlayerName ?? "(not cached)");
DrawPairPropertyRow("Handler Ident", string.IsNullOrEmpty(pair.Ident) ? "(not bound)" : pair.Ident);
DrawPairPropertyRow("Character Id", FormatCharacterId(pair.PlayerCharacterId));
DrawPairPropertyRow("Direct Pair", FormatBool(pair.IsDirectlyPaired));
DrawPairPropertyRow("Individual Status", pair.IndividualPairStatus.ToString());
DrawPairPropertyRow("Any Connection", FormatBool(pair.HasAnyConnection()));
DrawPairPropertyRow("Paused", FormatBool(pair.IsPaused));
DrawPairPropertyRow("Visible", FormatBool(pair.IsVisible), statusColor);
DrawPairPropertyRow("Online", FormatBool(pair.IsOnline));
DrawPairPropertyRow("Has Handler", FormatBool(debugInfo.HasHandler));
DrawPairPropertyRow("Handler Initialized", FormatBool(debugInfo.HandlerInitialized));
DrawPairPropertyRow("Handler Visible", FormatBool(debugInfo.HandlerVisible));
DrawPairPropertyRow("Handler Scheduled For Deletion", FormatBool(debugInfo.HandlerScheduledForDeletion));
DrawPairPropertyRow("Note", pair.GetNote() ?? "(none)");
ImGui.EndTable();
}
ImGui.Separator();
ImGui.TextUnformatted("Applied Data");
if (ImGui.BeginTable("##pairDebugDataStats", 2, ImGuiTableFlags.SizingStretchProp))
{
DrawPairPropertyRow("Last Data Size", FormatBytes(pair.LastAppliedDataBytes));
DrawPairPropertyRow("Approx. VRAM", FormatBytes(pair.LastAppliedApproximateVRAMBytes));
DrawPairPropertyRow("Effective VRAM", FormatBytes(pair.LastAppliedApproximateEffectiveVRAMBytes));
DrawPairPropertyRow("Last Triangles", pair.LastAppliedDataTris < 0 ? "n/a" : pair.LastAppliedDataTris.ToString(CultureInfo.InvariantCulture));
ImGui.EndTable();
}
var lastData = pair.LastReceivedCharacterData;
if (lastData is null)
{
ImGui.TextDisabled("No character data has been received for this pair.");
}
else
{
var fileReplacementCount = lastData.FileReplacements.Values.Sum(list => list?.Count ?? 0);
var totalGamePaths = lastData.FileReplacements.Values.Sum(list => list?.Sum(replacement => replacement.GamePaths.Length) ?? 0);
ImGui.BulletText($"File replacements: {fileReplacementCount} entries across {totalGamePaths} game paths.");
ImGui.BulletText($"Customize+: {lastData.CustomizePlusData.Count}, Glamourer entries: {lastData.GlamourerData.Count}");
ImGui.BulletText($"Manipulation length: {lastData.ManipulationData.Length}, Heels set: {FormatBool(!string.IsNullOrEmpty(lastData.HeelsData))}");
if (ImGui.TreeNode("Last Received Character Data (JSON)"))
{
DrawJsonBlob(lastData);
ImGui.TreePop();
}
}
ImGui.Separator();
ImGui.TextUnformatted("Application Timeline");
if (ImGui.BeginTable("##pairDebugTimeline", 2, ImGuiTableFlags.SizingStretchProp))
{
DrawPairPropertyRow("Last Data Received", FormatTimestamp(debugInfo.LastDataReceivedAt));
DrawPairPropertyRow("Last Apply Attempt", FormatTimestamp(debugInfo.LastApplyAttemptAt));
DrawPairPropertyRow("Last Successful Apply", FormatTimestamp(debugInfo.LastSuccessfulApplyAt));
ImGui.EndTable();
}
if (!string.IsNullOrEmpty(debugInfo.LastFailureReason))
{
UiSharedService.ColorTextWrapped($"Last failure: {debugInfo.LastFailureReason}", UIColors.Get("DimRed"));
if (debugInfo.BlockingConditions.Count > 0)
{
ImGui.TextUnformatted("Blocking conditions:");
foreach (var condition in debugInfo.BlockingConditions)
{
ImGui.BulletText(condition);
}
}
}
ImGui.Separator();
ImGui.TextUnformatted("Application & Download State");
if (ImGui.BeginTable("##pairDebugProcessing", 2, ImGuiTableFlags.SizingStretchProp))
{
DrawPairPropertyRow("Applying Data", FormatBool(debugInfo.IsApplying));
DrawPairPropertyRow("Downloading", FormatBool(debugInfo.IsDownloading));
DrawPairPropertyRow("Pending Downloads", debugInfo.PendingDownloadCount.ToString(CultureInfo.InvariantCulture));
DrawPairPropertyRow("Forbidden Downloads", debugInfo.ForbiddenDownloadCount.ToString(CultureInfo.InvariantCulture));
ImGui.EndTable();
}
ImGui.Separator();
ImGui.TextUnformatted("Syncshell Memberships");
if (snapshot.PairsWithGroups.TryGetValue(pair, out var groups) && groups.Count > 0)
{
foreach (var group in groups.OrderBy(g => g.Group.AliasOrGID, StringComparer.OrdinalIgnoreCase))
{
var flags = group.GroupPairUserInfos.TryGetValue(pair.UserData.UID, out var info) ? info : GroupPairUserInfo.None;
var flagLabel = flags switch
{
GroupPairUserInfo.None => string.Empty,
_ => $" ({string.Join(", ", GetGroupInfoFlags(flags))})"
};
ImGui.BulletText($"{group.Group.AliasOrGID} [{group.Group.GID}]{flagLabel}");
}
}
else
{
ImGui.TextDisabled("Not a member of any syncshells.");
}
if (pair.UserPair is null)
{
ImGui.TextDisabled("Pair DTO snapshot unavailable.");
}
else if (ImGui.TreeNode("Pair DTO Snapshot"))
{
DrawJsonBlob(pair.UserPair);
ImGui.TreePop();
}
ImGui.Separator();
DrawPairEventLog(pair);
}
private static IEnumerable<string> GetGroupInfoFlags(GroupPairUserInfo info)
{
if (info.HasFlag(GroupPairUserInfo.IsModerator))
{
yield return "Moderator";
}
if (info.HasFlag(GroupPairUserInfo.IsPinned))
{
yield return "Pinned";
}
}
private void DrawPairEventLog(Pair pair)
{
ImGui.TextUnformatted("Recent Events");
var events = _eventAggregator.EventList.Value;
var alias = pair.UserData.Alias;
var aliasOrUid = pair.UserData.AliasOrUID;
var rawUid = pair.UserData.UID;
var playerName = pair.PlayerName;
var relevantEvents = events.Where(e =>
EventMatchesIdentifier(e, rawUid)
|| EventMatchesIdentifier(e, aliasOrUid)
|| EventMatchesIdentifier(e, alias)
|| (!string.IsNullOrEmpty(playerName) && string.Equals(e.Character, playerName, StringComparison.OrdinalIgnoreCase)))
.OrderByDescending(e => e.EventTime)
.Take(40)
.ToList();
if (relevantEvents.Count == 0)
{
ImGui.TextDisabled("No recent events were logged for this pair.");
return;
}
var baseTableHeight = 300f * ImGuiHelpers.GlobalScale;
var tableHeight = MathF.Max(baseTableHeight, ImGui.GetContentRegionAvail().Y);
if (ImGui.BeginTable("##pairDebugEvents", 3,
ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.ScrollX | ImGuiTableFlags.ScrollY,
new Vector2(0f, tableHeight)))
{
ImGui.TableSetupScrollFreeze(0, 1);
ImGui.TableSetupColumn("Time", ImGuiTableColumnFlags.WidthFixed, 110f * ImGuiHelpers.GlobalScale);
ImGui.TableSetupColumn("Type", ImGuiTableColumnFlags.WidthFixed, 60f * ImGuiHelpers.GlobalScale);
ImGui.TableSetupColumn("Details");
ImGui.TableHeadersRow();
foreach (var ev in relevantEvents)
{
ImGui.TableNextRow();
ImGui.TableNextColumn();
ImGui.TextUnformatted(ev.EventTime.ToString("T", CultureInfo.CurrentCulture));
ImGui.TableNextColumn();
var (icon, color) = ev.EventSeverity switch
{
EventSeverity.Informational => (FontAwesomeIcon.InfoCircle, UIColors.Get("LightlessGreen")),
EventSeverity.Warning => (FontAwesomeIcon.ExclamationTriangle, UIColors.Get("LightlessYellow")),
EventSeverity.Error => (FontAwesomeIcon.ExclamationCircle, UIColors.Get("DimRed")),
_ => (FontAwesomeIcon.QuestionCircle, UIColors.Get("LightlessGrey"))
};
_uiShared.IconText(icon, color);
UiSharedService.AttachToolTip(ev.EventSeverity.ToString());
ImGui.TableNextColumn();
ImGui.TextWrapped($"[{ev.EventSource}] {ev.Message}");
}
ImGui.EndTable();
}
}
private static bool EventMatchesIdentifier(Event evt, string? identifier)
{
if (string.IsNullOrWhiteSpace(identifier))
{
return false;
}
return (!string.IsNullOrEmpty(evt.UserId) && string.Equals(evt.UserId, identifier, StringComparison.OrdinalIgnoreCase))
|| (!string.IsNullOrEmpty(evt.AliasOrUid) && string.Equals(evt.AliasOrUid, identifier, StringComparison.OrdinalIgnoreCase))
|| (!string.IsNullOrEmpty(evt.UID) && string.Equals(evt.UID, identifier, StringComparison.OrdinalIgnoreCase));
}
private static void DrawJsonBlob(object? value)
{
if (value is null)
{
ImGui.TextDisabled("(null)");
return;
}
try
{
var json = JsonSerializer.Serialize(value, DebugJsonOptions);
ImGui.PushStyleColor(ImGuiCol.Text, UIColors.Get("LightlessGrey"));
foreach (var line in json.Split('\n'))
{
ImGui.TextUnformatted(line);
}
ImGui.PopStyleColor();
}
catch (Exception ex)
{
UiSharedService.ColorTextWrapped($"Failed to serialize data: {ex.Message}", UIColors.Get("DimRed"));
}
}
private static void DrawPairPropertyRow(string label, string value, Vector4? colorOverride = null)
{
ImGui.TableNextRow();
ImGui.TableNextColumn();
ImGui.TextUnformatted(label);
ImGui.TableNextColumn();
if (colorOverride is { } color)
{
ImGui.TextColored(color, value);
}
else
{
ImGui.TextUnformatted(value);
}
}
private static string FormatTimestamp(DateTime? value)
{
return value is null ? "n/a" : value.Value.ToLocalTime().ToString("G", CultureInfo.CurrentCulture);
}
private static string FormatBytes(long value) => value < 0 ? "n/a" : UiSharedService.ByteToString(value);
private static string FormatCharacterId(uint id) => id == uint.MaxValue ? "n/a" : $"{id} (0x{id:X8})";
private static string FormatBool(bool value) => value ? "Yes" : "No";
private void DrawFileStorageSettings()
{
_lastTab = "FileCache";
@@ -2092,11 +2485,6 @@ public class SettingsUi : WindowMediatorSubscriberBase
{
// redo
}
else
{
_lightfinderIconInputInitialized = false;
_lightfinderIconPresetIndex = -1;
}
}
_uiShared.DrawHelpText("Switch between the Lightfinder text label and an icon on nameplates.");
@@ -2105,11 +2493,6 @@ public class SettingsUi : WindowMediatorSubscriberBase
{
//redo
}
else
{
_lightfinderIconInputInitialized = false;
_lightfinderIconPresetIndex = -1;
}
UiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.5f);
ImGui.TreePop();