diff --git a/LightlessSync/Plugin.cs b/LightlessSync/Plugin.cs index 3529735..24f1745 100644 --- a/LightlessSync/Plugin.cs +++ b/LightlessSync/Plugin.cs @@ -147,7 +147,7 @@ public sealed class Plugin : IDalamudPlugin collection.AddSingleton(); collection.AddSingleton(); collection.AddSingleton(addonLifecycle); - collection.AddSingleton(p => new ContextMenu(contextMenu, pluginInterface, gameData, p.GetRequiredService>(), p.GetRequiredService(), p.GetRequiredService(), objectTable)); + collection.AddSingleton(p => new ContextMenu(contextMenu, pluginInterface, gameData, p.GetRequiredService>(), p.GetRequiredService(), p.GetRequiredService(), objectTable, p.GetRequiredService())); collection.AddSingleton((s) => new IpcCallerPenumbra(s.GetRequiredService>(), pluginInterface, s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService())); collection.AddSingleton((s) => new IpcCallerGlamourer(s.GetRequiredService>(), pluginInterface, diff --git a/LightlessSync/UI/BroadcastUI.cs b/LightlessSync/UI/BroadcastUI.cs index 4977fb6..eda5a53 100644 --- a/LightlessSync/UI/BroadcastUI.cs +++ b/LightlessSync/UI/BroadcastUI.cs @@ -22,7 +22,7 @@ namespace LightlessSync.UI private IReadOnlyList _allSyncshells; private string _userUid = string.Empty; - private List<(string Label, string? GID, bool IsAvailable)> _syncshellOptions = new(); + private readonly List<(string Label, string? GID, bool IsAvailable)> _syncshellOptions = new(); public BroadcastUI( ILogger logger, @@ -48,7 +48,7 @@ namespace LightlessSync.UI MaximumSize = new(750, 400) }; - mediator.Subscribe(this, async _ => await RefreshSyncshells()); + mediator.Subscribe(this, async _ => await RefreshSyncshells().ConfigureAwait(false)); } private void RebuildSyncshellDropdownOptions() @@ -62,7 +62,7 @@ namespace LightlessSync.UI _syncshellOptions.Clear(); _syncshellOptions.Add(("None", null, true)); - var addedGids = new HashSet(); + var addedGids = new HashSet(StringComparer.Ordinal); foreach (var shell in ownedSyncshells) { @@ -73,7 +73,7 @@ namespace LightlessSync.UI if (!string.IsNullOrEmpty(selectedGid) && !addedGids.Contains(selectedGid)) { - var matching = allSyncshells.FirstOrDefault(g => g.GID == selectedGid); + var matching = allSyncshells.FirstOrDefault(g => string.Equals(g.GID, selectedGid, StringComparison.Ordinal)); if (matching != null) { var label = matching.GroupAliasOrGID ?? matching.GID; @@ -97,7 +97,7 @@ namespace LightlessSync.UI { if (!_apiController.IsConnected) { - _allSyncshells = Array.Empty(); + _allSyncshells = []; RebuildSyncshellDropdownOptions(); return; } @@ -109,7 +109,7 @@ namespace LightlessSync.UI catch (Exception ex) { _logger.LogError(ex, "Failed to fetch Syncshells."); - _allSyncshells = Array.Empty(); + _allSyncshells = []; } RebuildSyncshellDropdownOptions(); @@ -260,14 +260,14 @@ namespace LightlessSync.UI } var selectedGid = _configService.Current.SelectedFinderSyncshell; - var currentOption = _syncshellOptions.FirstOrDefault(o => o.GID == selectedGid); + var currentOption = _syncshellOptions.FirstOrDefault(o => string.Equals(o.GID, selectedGid, StringComparison.Ordinal)); var preview = currentOption.Label ?? "Select a Syncshell..."; if (ImGui.BeginCombo("##SyncshellDropdown", preview)) { foreach (var (label, gid, available) in _syncshellOptions) { - bool isSelected = gid == selectedGid; + bool isSelected = string.Equals(gid, selectedGid, StringComparison.Ordinal); if (!available) ImGui.PushStyleColor(ImGuiCol.Text, UIColors.Get("DimRed")); @@ -310,6 +310,7 @@ namespace LightlessSync.UI ImGui.EndTabItem(); } + #if DEBUG if (ImGui.BeginTabItem("Debug")) { ImGui.Text("Broadcast Cache"); @@ -366,17 +367,12 @@ namespace LightlessSync.UI ImGui.EndTable(); } - ImGui.EndTabItem(); } + #endif ImGui.EndTabBar(); } } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - } } } diff --git a/LightlessSync/UI/ContextMenu.cs b/LightlessSync/UI/ContextMenu.cs index 1e1cfc3..b828cdd 100644 --- a/LightlessSync/UI/ContextMenu.cs +++ b/LightlessSync/UI/ContextMenu.cs @@ -1,14 +1,15 @@ using Dalamud.Game.ClientState.Objects.SubKinds; -using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.Gui.ContextMenu; using Dalamud.Plugin; using Dalamud.Plugin.Services; +using LightlessSync.LightlessConfiguration; using LightlessSync.Services; using LightlessSync.Utils; using LightlessSync.WebAPI; using Lumina.Excel.Sheets; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using System.Linq; namespace LightlessSync.UI; @@ -19,6 +20,7 @@ internal class ContextMenu : IHostedService private readonly IDataManager _gameData; private readonly ILogger _logger; private readonly DalamudUtilService _dalamudUtil; + private readonly LightlessConfigService _configService; private readonly ApiController _apiController; private readonly IObjectTable _objectTable; @@ -37,7 +39,8 @@ internal class ContextMenu : IHostedService ILogger logger, DalamudUtilService dalamudUtil, ApiController apiController, - IObjectTable objectTable) + IObjectTable objectTable, + LightlessConfigService configService) { _contextMenu = contextMenu; _pluginInterface = pluginInterface; @@ -46,6 +49,7 @@ internal class ContextMenu : IHostedService _dalamudUtil = dalamudUtil; _apiController = apiController; _objectTable = objectTable; + _configService = configService; } public Task StartAsync(CancellationToken cancellationToken) @@ -77,7 +81,7 @@ internal class ContextMenu : IHostedService if (!_pluginInterface.UiBuilder.ShouldModifyUi) return; - if (!ValidAddons.Contains(args.AddonName)) + if (!ValidAddons.Contains(args.AddonName, StringComparer.Ordinal)) return; if (args.Target is not MenuTargetDefault target) @@ -86,6 +90,10 @@ internal class ContextMenu : IHostedService if (string.IsNullOrEmpty(target.TargetName) || target.TargetObjectId == 0 || target.TargetHomeWorld.RowId == 0) return; + IPlayerCharacter? targetData = GetPlayerFromObjectTable(target); + if (targetData == null || targetData.Address == IntPtr.Zero) + return; + var world = GetWorld(target.TargetHomeWorld.RowId); if (!IsWorldValid(world)) return; @@ -96,7 +104,7 @@ internal class ContextMenu : IHostedService PrefixChar = 'L', UseDefaultPrefix = false, PrefixColor = 708, - OnClicked = async _ => await HandleSelection(args) + OnClicked = async _ => await HandleSelection(args).ConfigureAwait(false) }); } @@ -111,11 +119,7 @@ internal class ContextMenu : IHostedService try { - var targetData = _objectTable - .OfType() - .FirstOrDefault(p => - string.Equals(p.Name.TextValue, target.TargetName, StringComparison.OrdinalIgnoreCase) && - p.HomeWorld.RowId == target.TargetHomeWorld.RowId); + IPlayerCharacter? targetData = GetPlayerFromObjectTable(target); if (targetData == null || targetData.Address == IntPtr.Zero) { @@ -123,11 +127,11 @@ internal class ContextMenu : IHostedService return; } - var senderCid = (await _dalamudUtil.GetCIDAsync()).ToString().GetHash256(); + var senderCid = (await _dalamudUtil.GetCIDAsync().ConfigureAwait(false)).ToString().GetHash256(); var receiverCid = DalamudUtilService.GetHashedCIDFromPlayerPointer(targetData.Address); _logger.LogInformation("Sending pair request: sender {SenderCid}, receiver {ReceiverCid}", senderCid, receiverCid); - await _apiController.TryPairWithContentId(receiverCid, senderCid); + await _apiController.TryPairWithContentId(receiverCid, senderCid).ConfigureAwait(false); } catch (Exception ex) { @@ -135,12 +139,43 @@ internal class ContextMenu : IHostedService } } + private IPlayerCharacter? GetPlayerFromObjectTable(MenuTargetDefault target) + { + return _objectTable + .OfType() + .FirstOrDefault(p => + string.Equals(p.Name.TextValue, target.TargetName, StringComparison.OrdinalIgnoreCase) && + p.HomeWorld.RowId == target.TargetHomeWorld.RowId); + } + private World GetWorld(uint worldId) { var sheet = _gameData.GetExcelSheet()!; - return sheet.TryGetRow(worldId, out var world) ? world : sheet.First(); + var luminaWorlds = sheet.Where(x => + { + var dc = x.DataCenter.ValueNullable; + var name = x.Name.ExtractText(); + var internalName = x.InternalName.ExtractText(); + + if (dc == null || dc.Value.Region == 0 || string.IsNullOrWhiteSpace(dc.Value.Name.ExtractText())) + return false; + + if (string.IsNullOrWhiteSpace(name) || string.IsNullOrWhiteSpace(internalName)) + return false; + + if (name.Contains('-', StringComparison.Ordinal) || name.Contains('_', StringComparison.Ordinal)) + return false; + + return x.DataCenter.Value.Region != 5 || x.RowId > 3001 && x.RowId != 1200 && IsChineseJapaneseKoreanString(name); + }); + + return luminaWorlds.FirstOrDefault(x => x.RowId == worldId); } + private static bool IsChineseJapaneseKoreanString(string text) => text.All(IsChineseJapaneseKoreanCharacter); + + private static bool IsChineseJapaneseKoreanCharacter(char c) => (c >= 0x4E00 && c <= 0x9FFF); + public bool IsWorldValid(uint worldId) => IsWorldValid(GetWorld(worldId)); public static bool IsWorldValid(World world) diff --git a/LightlessSync/UI/DataAnalysisUi.cs b/LightlessSync/UI/DataAnalysisUi.cs index e1ef15c..5b750f3 100644 --- a/LightlessSync/UI/DataAnalysisUi.cs +++ b/LightlessSync/UI/DataAnalysisUi.cs @@ -547,73 +547,147 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase using var tab = ImRaii.TabItem(tabText + "###" + kvp.Key.ToString()); if (tab.Success) { - var groupedfiles = kvp.Value.Select(v => v.Value).GroupBy(f => f.FileType, StringComparer.Ordinal) - .OrderBy(k => k.Key, StringComparer.Ordinal).ToList(); + var groupedfiles = kvp.Value.Select(v => v.Value).GroupBy(f => f.FileType, StringComparer.Ordinal).OrderBy(k => k.Key, StringComparer.Ordinal).ToList(); - ImGui.TextUnformatted("Files for " + kvp.Key); - ImGui.SameLine(); - ImGui.TextUnformatted(kvp.Value.Count.ToString()); - ImGui.SameLine(); + ImGui.PushStyleVar(ImGuiStyleVar.CellPadding, new Vector2(1f, 1f)); + ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(1f, 1f)); - using (var font = ImRaii.PushFont(UiBuilder.IconFont)) + if (ImGui.BeginTable($"##fileStats_{kvp.Key}", 3, + ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.SizingFixedFit)) { - ImGui.TextUnformatted(FontAwesomeIcon.InfoCircle.ToIconString()); - } - if (ImGui.IsItemHovered()) - { - string text = ""; - text = string.Join(Environment.NewLine, groupedfiles - .Select(f => f.Key + ": " + f.Count() + " files, size: " + UiSharedService.ByteToString(f.Sum(v => v.OriginalSize)) - + ", compressed: " + UiSharedService.ByteToString(f.Sum(v => v.CompressedSize)))); - ImGui.SetTooltip(text); - } - ImGui.TextUnformatted($"{kvp.Key} size (actual):"); - ImGui.SameLine(); - ImGui.TextUnformatted(UiSharedService.ByteToString(kvp.Value.Sum(c => c.Value.OriginalSize))); - ImGui.TextUnformatted($"{kvp.Key} size (compressed for up/download only):"); - ImGui.SameLine(); - ImGui.TextUnformatted(UiSharedService.ByteToString(kvp.Value.Sum(c => c.Value.CompressedSize))); - ImGui.Separator(); - - var vramUsage = groupedfiles.SingleOrDefault(v => string.Equals(v.Key, "tex", StringComparison.Ordinal)); - if (vramUsage != null) - { - var actualVramUsage = vramUsage.Sum(f => f.OriginalSize); - ImGui.TextUnformatted($"{kvp.Key} VRAM usage:"); + ImGui.TableNextRow(); + ImGui.TableNextColumn(); + ImGui.TextUnformatted($"Files for {kvp.Key}"); + ImGui.TableNextColumn(); + ImGui.TextUnformatted(kvp.Value.Count.ToString()); ImGui.SameLine(); - ImGui.TextUnformatted(UiSharedService.ByteToString(actualVramUsage)); + using (var font = ImRaii.PushFont(UiBuilder.IconFont)) + ImGui.TextUnformatted(FontAwesomeIcon.InfoCircle.ToIconString()); + if (ImGui.IsItemHovered()) + { + string text = string.Join(Environment.NewLine, groupedfiles.Select(f => + $"{f.Key}: {f.Count()} files, size: {UiSharedService.ByteToString(f.Sum(v => v.OriginalSize))}, compressed: {UiSharedService.ByteToString(f.Sum(v => v.CompressedSize))}")); + ImGui.SetTooltip(text); + } + ImGui.TableNextColumn(); + + ImGui.TableNextRow(); + ImGui.TableNextColumn(); + ImGui.TextUnformatted($"{kvp.Key} size (actual):"); + ImGui.TableNextColumn(); + ImGui.TextUnformatted(UiSharedService.ByteToString(kvp.Value.Sum(c => c.Value.OriginalSize))); + ImGui.TableNextColumn(); + + ImGui.TableNextRow(); + ImGui.TableNextColumn(); + ImGui.TextUnformatted($"{kvp.Key} size (compressed for up/download only):"); + _uiSharedService.ColoredSeparator(UIColors.Get("LightlessPurpleDefault"), 1.5f); + ImGui.TableNextColumn(); + ImGui.TextUnformatted(UiSharedService.ByteToString(kvp.Value.Sum(c => c.Value.CompressedSize))); + _uiSharedService.ColoredSeparator(UIColors.Get("LightlessPurpleDefault"), 1.5f); + ImGui.TableNextColumn(); + + var vramUsage = groupedfiles.SingleOrDefault(v => string.Equals(v.Key, "tex", StringComparison.Ordinal)); + if (vramUsage != null) + { + var actualVramUsage = vramUsage.Sum(f => f.OriginalSize); + + ImGui.TableNextRow(); + ImGui.TableNextColumn(); + ImGui.TextUnformatted($"{kvp.Key} VRAM usage:"); + ImGui.TableNextColumn(); + ImGui.TextUnformatted(UiSharedService.ByteToString(actualVramUsage)); + ImGui.TableNextColumn(); + + if (_playerPerformanceConfig.Current.WarnOnExceedingThresholds + || _playerPerformanceConfig.Current.ShowPerformanceIndicator) + { + var currentVramWarning = _playerPerformanceConfig.Current.VRAMSizeWarningThresholdMiB; + + ImGui.TableNextRow(); + ImGui.TableNextColumn(); + ImGui.TextUnformatted("Configured VRAM threshold:"); + ImGui.TableNextColumn(); + ImGui.TextUnformatted($"{currentVramWarning} MiB."); + ImGui.TableNextColumn(); + if (currentVramWarning * 1024 * 1024 < actualVramUsage) + { + UiSharedService.ColorText( + $"You exceed your own threshold by {UiSharedService.ByteToString(actualVramUsage - (currentVramWarning * 1024 * 1024))}", + UIColors.Get("LightlessYellow")); + } + } + } + + var actualTriCount = kvp.Value.Sum(f => f.Value.Triangles); + ImGui.TableNextRow(); + ImGui.TableNextColumn(); + ImGui.TextUnformatted($"{kvp.Key} modded model triangles:"); + ImGui.TableNextColumn(); + ImGui.TextUnformatted(actualTriCount.ToString()); + ImGui.TableNextColumn(); + if (_playerPerformanceConfig.Current.WarnOnExceedingThresholds || _playerPerformanceConfig.Current.ShowPerformanceIndicator) { - using var _ = ImRaii.PushIndent(10f); - var currentVramWarning = _playerPerformanceConfig.Current.VRAMSizeWarningThresholdMiB; - ImGui.TextUnformatted($"Configured VRAM warning threshold: {currentVramWarning} MiB."); - if (currentVramWarning * 1024 * 1024 < actualVramUsage) + var currentTriWarning = _playerPerformanceConfig.Current.TrisWarningThresholdThousands; + + ImGui.TableNextRow(); + ImGui.TableNextColumn(); + ImGui.TextUnformatted("Configured triangle threshold:"); + ImGui.TableNextColumn(); + ImGui.TextUnformatted($"{currentTriWarning * 1000} triangles."); + ImGui.TableNextColumn(); + if (currentTriWarning * 1000 < actualTriCount) { - UiSharedService.ColorText($"You exceed your own threshold by " + - $"{UiSharedService.ByteToString(actualVramUsage - (currentVramWarning * 1024 * 1024))}.", + UiSharedService.ColorText( + $"You exceed your own threshold by {actualTriCount - (currentTriWarning * 1000)}", UIColors.Get("LightlessYellow")); } } + + ImGui.EndTable(); } - var actualTriCount = kvp.Value.Sum(f => f.Value.Triangles); - ImGui.TextUnformatted($"{kvp.Key} modded model triangles: {actualTriCount}"); - if (_playerPerformanceConfig.Current.WarnOnExceedingThresholds - || _playerPerformanceConfig.Current.ShowPerformanceIndicator) + ImGui.PopStyleVar(2); + + _uiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 2f); + + _uiSharedService.MediumText("Selected file:", UIColors.Get("LightlessBlue")); + ImGui.SameLine(); + _uiSharedService.MediumText(_selectedHash, UIColors.Get("LightlessYellow")); + + if (_cachedAnalysis[_selectedObjectTab].TryGetValue(_selectedHash, out CharacterAnalyzer.FileDataEntry? item)) { - using var _ = ImRaii.PushIndent(10f); - var currentTriWarning = _playerPerformanceConfig.Current.TrisWarningThresholdThousands; - ImGui.TextUnformatted($"Configured triangle warning threshold: {currentTriWarning * 1000} triangles."); - if (currentTriWarning * 1000 < actualTriCount) + var filePaths = item.FilePaths; + UiSharedService.ColorText("Local file path:", UIColors.Get("LightlessBlue")); + ImGui.SameLine(); + UiSharedService.TextWrapped(filePaths[0]); + if (filePaths.Count > 1) { - UiSharedService.ColorText($"You exceed your own threshold by " + - $"{actualTriCount - (currentTriWarning * 1000)} triangles.", - UIColors.Get("LightlessYellow")); + ImGui.SameLine(); + ImGui.TextUnformatted($"(and {filePaths.Count - 1} more)"); + ImGui.SameLine(); + _uiSharedService.IconText(FontAwesomeIcon.InfoCircle); + UiSharedService.AttachToolTip(string.Join(Environment.NewLine, filePaths.Skip(1))); + } + + var gamepaths = item.GamePaths; + UiSharedService.ColorText("Used by game path:", UIColors.Get("LightlessBlue")); + ImGui.SameLine(); + UiSharedService.TextWrapped(gamepaths[0]); + if (gamepaths.Count > 1) + { + ImGui.SameLine(); + ImGui.TextUnformatted($"(and {gamepaths.Count - 1} more)"); + ImGui.SameLine(); + _uiSharedService.IconText(FontAwesomeIcon.InfoCircle); + UiSharedService.AttachToolTip(string.Join(Environment.NewLine, gamepaths.Skip(1))); } } ImGui.Separator(); + if (_selectedObjectTab != kvp.Key) { _selectedHash = string.Empty; @@ -692,41 +766,6 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase } } } - - ImGui.Separator(); - - ImGui.TextUnformatted("Selected file:"); - ImGui.SameLine(); - UiSharedService.ColorText(_selectedHash, UIColors.Get("LightlessYellow")); - - if (_cachedAnalysis[_selectedObjectTab].TryGetValue(_selectedHash, out CharacterAnalyzer.FileDataEntry? item)) - { - var filePaths = item.FilePaths; - ImGui.TextUnformatted("Local file path:"); - ImGui.SameLine(); - UiSharedService.TextWrapped(filePaths[0]); - if (filePaths.Count > 1) - { - ImGui.SameLine(); - ImGui.TextUnformatted($"(and {filePaths.Count - 1} more)"); - ImGui.SameLine(); - _uiSharedService.IconText(FontAwesomeIcon.InfoCircle); - UiSharedService.AttachToolTip(string.Join(Environment.NewLine, filePaths.Skip(1))); - } - - var gamepaths = item.GamePaths; - ImGui.TextUnformatted("Used by game path:"); - ImGui.SameLine(); - UiSharedService.TextWrapped(gamepaths[0]); - if (gamepaths.Count > 1) - { - ImGui.SameLine(); - ImGui.TextUnformatted($"(and {gamepaths.Count - 1} more)"); - ImGui.SameLine(); - _uiSharedService.IconText(FontAwesomeIcon.InfoCircle); - UiSharedService.AttachToolTip(string.Join(Environment.NewLine, gamepaths.Skip(1))); - } - } } public override void OnOpen() @@ -855,7 +894,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase } var filePath = item.FilePaths[0]; bool toConvert = _texturesToConvert.ContainsKey(filePath); - if (ImGui.Checkbox("###convert" + item.Hash, ref toConvert)) + if (UiSharedService.CheckboxWithBorder("###convert" + item.Hash, ref toConvert, UIColors.Get("LightlessPurple"), 1.5f)) { if (toConvert && !_texturesToConvert.ContainsKey(filePath)) { diff --git a/LightlessSync/UI/SettingsUi.cs b/LightlessSync/UI/SettingsUi.cs index fdbe821..95bb2af 100644 --- a/LightlessSync/UI/SettingsUi.cs +++ b/LightlessSync/UI/SettingsUi.cs @@ -979,9 +979,17 @@ public class SettingsUi : WindowMediatorSubscriberBase var colorNames = new[] { ("LightlessPurple", "Lightless Purple", "Primary colors"), + ("LightlessPurpleActive", "Lightless Purple Active", "Primary colors"), + ("LightlessPurpleDefault", "Lightless Purple Inactive", "Primary colors"), ("LightlessBlue", "Lightless Blue", "Secondary colors"), + + ("LightlessGreen", "Lightless Green", "Active elements"), + ("LightlessYellow", "Lightless Yellow", "Warning colors"), + ("LightlessYellow2", "Lightless Yellow 2", "Warning colors"), + ("PairBlue", "Pair Blue", "Pair UI elements"), + ("DimRed", "Dim Red", "Error and offline") }; @@ -1020,6 +1028,8 @@ public class SettingsUi : WindowMediatorSubscriberBase ImGui.Spacing(); + _uiShared.ColoredSeparator(UIColors.Get("LightlessPurpleDefault"), 1.5f); + ImGui.TextUnformatted("Server Info Bar Colors"); if (ImGui.Checkbox("Color-code the Server Info Bar entry according to status", ref useColorsInDtr)) @@ -1054,6 +1064,9 @@ public class SettingsUi : WindowMediatorSubscriberBase } ImGui.Spacing(); + + _uiShared.ColoredSeparator(UIColors.Get("LightlessPurpleDefault"), 1.5f); + ImGui.TextUnformatted("Nameplate Colors"); var nameColorsEnabled = _configService.Current.IsNameplateColorsEnabled; @@ -1092,6 +1105,10 @@ public class SettingsUi : WindowMediatorSubscriberBase } } + ImGui.Spacing(); + + _uiShared.ColoredSeparator(UIColors.Get("LightlessPurpleDefault"), 1.5f); + if (ImGui.Checkbox("Use the complete redesign of the UI for Lightless client.", ref useLightlessRedesign)) { _configService.Current.UseLightlessRedesign = useLightlessRedesign; diff --git a/LightlessSync/UI/UISharedService.cs b/LightlessSync/UI/UISharedService.cs index e4753dc..24899ee 100644 --- a/LightlessSync/UI/UISharedService.cs +++ b/LightlessSync/UI/UISharedService.cs @@ -173,12 +173,14 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase public static string ByteToString(long bytes, bool addSuffix = true) { - string[] suffix = ["B", "KiB", "MiB", "GiB", "TiB"]; - int i; + string[] suffix = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}; + int i = 0; double dblSByte = bytes; - for (i = 0; i < suffix.Length && bytes >= 1024; i++, bytes /= 1024) + + while (dblSByte >= 1000 && i < suffix.Length - 1) { - dblSByte = bytes / 1024.0; + dblSByte /= 1000.0; + i++; } return addSuffix ? $"{dblSByte:0.00} {suffix[i]}" : $"{dblSByte:0.00}"; @@ -510,6 +512,21 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase ImGui.Dummy(new Vector2(0, thickness * scale)); } + public static bool CheckboxWithBorder(string label, ref bool value, Vector4? borderColor = null, float borderThickness = 1.0f, float rounding = 3.0f) + { + var pos = ImGui.GetCursorScreenPos(); + + bool changed = ImGui.Checkbox(label, ref value); + + var min = pos; + var max = ImGui.GetItemRectMax(); + + var col = ImGui.GetColorU32(borderColor ?? ImGuiColors.DalamudGrey); + ImGui.GetWindowDrawList().AddRect(min, max, col, rounding, ImDrawFlags.None, borderThickness); + + return changed; + } + public void MediumText(string text, Vector4? color = null) { FontText(text, MediumFont, color);