From 115960262a3f0a019c18f7010fa4cb4a085520c8 Mon Sep 17 00:00:00 2001 From: Nia292 <73426965+Nia292@users.noreply.github.com> Date: Sun, 22 Jan 2023 10:21:36 +0100 Subject: [PATCH] Feature: Pair Categories (#35) * Feature/pair categories re (#1) Implemented pair categories: - Paired users can now get tags - Tags get rendered into the main UI as groups - Tags are persistently stored on the local configuration * Added multi-server capabilities and cleaned up code - Tags and available tags are stored per API url - Added a few tooltips * Renamed both dictionary to reflect the fact that they are per-server dictionaries * Swapped icons and no longer renders groups that are empty after filter --- MareSynchronos/Configuration.cs | 16 ++ MareSynchronos/UI/CompactUI.cs | 76 ++++++--- MareSynchronos/UI/Components/PairGroupsUi.cs | 159 ++++++++++++++++++ .../UI/Components/SelectGroupForPairUi.cs | 155 +++++++++++++++++ MareSynchronos/UI/Handlers/TagHandler.cs | 135 +++++++++++++++ MareSynchronos/UI/UIShared.cs | 7 + 6 files changed, 529 insertions(+), 19 deletions(-) create mode 100644 MareSynchronos/UI/Components/PairGroupsUi.cs create mode 100644 MareSynchronos/UI/Components/SelectGroupForPairUi.cs create mode 100644 MareSynchronos/UI/Handlers/TagHandler.cs diff --git a/MareSynchronos/Configuration.cs b/MareSynchronos/Configuration.cs index c00c38f..fe8aae5 100644 --- a/MareSynchronos/Configuration.cs +++ b/MareSynchronos/Configuration.cs @@ -4,8 +4,10 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using MareSynchronos.API; using MareSynchronos.Utils; using MareSynchronos.WebAPI; +using Microsoft.Extensions.Primitives; namespace MareSynchronos; @@ -85,6 +87,20 @@ public class Configuration : IPluginConfiguration public Dictionary> GidServerComments { get; set; } = new(StringComparer.Ordinal); public Dictionary UidComments { get; set; } = new(StringComparer.Ordinal); + + /// + /// Each paired user can have multiple tags. Each tag will create a category, and the user will + /// be displayed into that category. + /// The dictionary first maps a server URL to a dictionary, and that + /// dictionary maps the OtherUID of the to a list of tags. + /// + public Dictionary>> UidServerPairedUserTags = new(StringComparer.Ordinal); + /// + /// A dictionary that maps a server URL to the tags the user has added for that server. + /// + public Dictionary> ServerAvailablePairTags = new(StringComparer.Ordinal); + + public HashSet OpenPairTags = new(StringComparer.Ordinal); public int Version { get; set; } = 5; public bool ShowTransferWindow { get; set; } = true; diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs index 57bf9c9..3f8c12f 100644 --- a/MareSynchronos/UI/CompactUI.cs +++ b/MareSynchronos/UI/CompactUI.cs @@ -5,7 +5,6 @@ using System.Globalization; using System.Linq; using System.Numerics; using System.Reflection; -using System.Runtime.Serialization.Formatters; using Dalamud.Interface; using Dalamud.Interface.Colors; using Dalamud.Interface.Components; @@ -13,6 +12,8 @@ using Dalamud.Interface.Windowing; using Dalamud.Utility; using ImGuiNET; using MareSynchronos.API; +using MareSynchronos.UI.Components; +using MareSynchronos.UI.Handlers; using MareSynchronos.Utils; using MareSynchronos.WebAPI; @@ -22,6 +23,7 @@ public class CompactUi : Window, IDisposable { private readonly ApiController _apiController; private readonly Configuration _configuration; + private readonly TagHandler _tagHandler; public readonly Dictionary ShowUidForEntry = new(StringComparer.Ordinal); private readonly UiShared _uiShared; private readonly WindowSystem _windowSystem; @@ -44,6 +46,9 @@ public class CompactUi : Window, IDisposable private ClientPairDto? _lastAddedUser; private string _lastAddedUserComment = string.Empty; + private readonly SelectGroupForPairUi _selectGroupForPairUi; + private readonly PairGroupsUi _pairGroupsUi; + public CompactUi(WindowSystem windowSystem, UiShared uiShared, Configuration configuration, ApiController apiController) : base("###MareSynchronosMainUI") { @@ -72,8 +77,11 @@ public class CompactUi : Window, IDisposable _uiShared = uiShared; _configuration = configuration; _apiController = apiController; + _tagHandler = new(_configuration); groupPanel = new(this, uiShared, configuration, apiController); + _selectGroupForPairUi = new(_tagHandler, configuration); + _pairGroupsUi = new(_tagHandler, DrawPairedClient, apiController); SizeConstraints = new WindowSizeConstraints() { @@ -149,6 +157,7 @@ public class CompactUi : Window, IDisposable } ImGui.Separator(); UiShared.DrawWithID("transfers", DrawTransfers); + UiShared.DrawWithID("grouping-popup", () => _selectGroupForPairUi.Draw(ShowUidForEntry)); TransferPartHeight = ImGui.GetCursorPosY() - TransferPartHeight; } @@ -300,15 +309,18 @@ public class CompactUi : Window, IDisposable private void DrawPairedClient(ClientPairDto entry) { var pauseIcon = entry.IsPaused ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause; - - var buttonSize = UiShared.GetIconButtonSize(pauseIcon); + var pauseIconSize = UiShared.GetIconButtonSize(pauseIcon); var trashButtonSize = UiShared.GetIconButtonSize(FontAwesomeIcon.Trash); + var barButtonSize = UiShared.GetIconButtonSize(FontAwesomeIcon.Bars); var entryUID = string.IsNullOrEmpty(entry.VanityUID) ? entry.OtherUID : entry.VanityUID; var textSize = ImGui.CalcTextSize(entryUID); var originalY = ImGui.GetCursorPosY(); - var buttonSizes = buttonSize.Y + trashButtonSize.Y; + var buttonSizes = pauseIconSize.Y + trashButtonSize.Y + barButtonSize.Y; + var spacingX = ImGui.GetStyle().ItemSpacing.X; + var windowEndX = ImGui.GetWindowContentRegionMin().X + UiShared.GetWindowContentRegionWidth(); + - var textPos = originalY + buttonSize.Y / 2 - textSize.Y / 2; + var textPos = originalY + pauseIconSize.Y / 2 - textSize.Y / 2; ImGui.SetCursorPosY(textPos); if (!entry.IsSynced) { @@ -387,7 +399,7 @@ public class CompactUi : Window, IDisposable { ImGui.SetCursorPosY(originalY); - ImGui.SetNextItemWidth(UiShared.GetWindowContentRegionWidth() - ImGui.GetCursorPosX() - buttonSizes - ImGui.GetStyle().ItemSpacing.X * 2); + ImGui.SetNextItemWidth(UiShared.GetWindowContentRegionWidth() - ImGui.GetCursorPosX() - buttonSizes - ImGui.GetStyle().ItemSpacing.X * 3); if (ImGui.InputTextWithHint("", "Nick/Notes", ref EditUserComment, 255, ImGuiInputTextFlags.EnterReturnsTrue)) { _configuration.SetCurrentServerUidComment(entry.OtherUID, EditUserComment); @@ -402,20 +414,10 @@ public class CompactUi : Window, IDisposable UiShared.AttachToolTip("Hit ENTER to save\nRight click to cancel"); } - ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiShared.GetWindowContentRegionWidth() - buttonSize.X); - ImGui.SetCursorPosY(originalY); - if (ImGuiComponents.IconButton(FontAwesomeIcon.Trash)) - { - if (UiShared.CtrlPressed()) - { - _ = _apiController.UserRemovePair(entry.OtherUID); - } - } - UiShared.AttachToolTip("Hold CTRL and click to unpair permanently from " + entryUID); - + // Pause Button if (entry.IsSynced) { - ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiShared.GetWindowContentRegionWidth() - buttonSize.X - ImGui.GetStyle().ItemSpacing.X - trashButtonSize.X); + ImGui.SameLine(windowEndX - barButtonSize.X - spacingX - pauseIconSize.X); ImGui.SetCursorPosY(originalY); if (ImGuiComponents.IconButton(pauseIcon)) { @@ -425,6 +427,39 @@ public class CompactUi : Window, IDisposable ? "Pause pairing with " + entryUID : "Resume pairing with " + entryUID); } + + // Flyout Menu + ImGui.SameLine(windowEndX - barButtonSize.X); + ImGui.SetCursorPosY(originalY); + + if (ImGuiComponents.IconButton(FontAwesomeIcon.Bars)) + { + ImGui.OpenPopup("User Flyout Menu"); + } + if (ImGui.BeginPopup("User Flyout Menu")) + { + UiShared.DrawWithID($"buttons-{entry.OtherUID}", () => DrawPairedClientMenu(entry)); + ImGui.EndPopup(); + } + } + + private void DrawPairedClientMenu(ClientPairDto entry) + { + var entryUID = string.IsNullOrEmpty(entry.VanityUID) ? entry.OtherUID : entry.VanityUID; + if (UiShared.IconTextButton(FontAwesomeIcon.Folder, "Pair Groups")) + { + _selectGroupForPairUi.Open(entry); + } + UiShared.AttachToolTip("Chose pair groups for " + entryUID); + + if (UiShared.IconTextButton(FontAwesomeIcon.Trash, "Unpair Permanently")) + { + if (UiShared.CtrlPressed()) + { + _ = _apiController.UserRemovePair(entry.OtherUID); + } + } + UiShared.AttachToolTip("Hold CTRL and click to unpair permanently from " + entryUID); } private void DrawPairList() @@ -446,7 +481,10 @@ public class CompactUi : Window, IDisposable if (_configuration.ReverseUserSort) users = users.Reverse(); ImGui.BeginChild("list", new Vector2(_windowContentWidth, ySize), false); - foreach (var entry in users.ToList()) + var allAvailablePairs = users.ToList(); + var pairsWithoutTags = allAvailablePairs.Where(pair => !_tagHandler.HasAnyTag(pair)); + _pairGroupsUi.Draw(allAvailablePairs); + foreach (var entry in pairsWithoutTags) { UiShared.DrawWithID(entry.OtherUID, () => DrawPairedClient(entry)); } diff --git a/MareSynchronos/UI/Components/PairGroupsUi.cs b/MareSynchronos/UI/Components/PairGroupsUi.cs new file mode 100644 index 0000000..53116ae --- /dev/null +++ b/MareSynchronos/UI/Components/PairGroupsUi.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Dalamud.Interface; +using Dalamud.Interface.Components; +using ImGuiNET; +using MareSynchronos.API; +using MareSynchronos.UI.Handlers; +using MareSynchronos.WebAPI; + +namespace MareSynchronos.UI.Components +{ + public class PairGroupsUi + { + private readonly Action _clientRenderFn; + private readonly TagHandler _tagHandler; + private readonly ApiController _apiController; + + public PairGroupsUi(TagHandler tagHandler, Action clientRenderFn, ApiController apiController) + { + _clientRenderFn = clientRenderFn; + _tagHandler = tagHandler; + _apiController = apiController; + } + + public void Draw(List availablePairs) + { + // Only render those tags that actually have pairs in them, otherwise + // we can end up with a bunch of useless pair groups + var tagsWithPairsInThem = _tagHandler.GetAllTagsSorted(); + foreach (var tag in tagsWithPairsInThem) + { + UiShared.DrawWithID($"group-{tag}", () => DrawCategory(tag, availablePairs)); + } + } + + public void DrawCategory(string tag, List availablePairs) + { + var otherUidsTaggedWithTag = _tagHandler.GetOtherUidsForTag(tag); + var availablePairsInThisTag = availablePairs + .Where(pair => otherUidsTaggedWithTag.Contains(pair.OtherUID)) + .ToList(); + if (availablePairsInThisTag.Any()) + { + DrawName(tag); + UiShared.DrawWithID($"group-{tag}-buttons", () => DrawButtons(tag, availablePairsInThisTag)); + if (_tagHandler.IsTagOpen(tag)) + { + DrawPairs(tag, availablePairsInThisTag); + } + } + } + + private void DrawName(string tag) + { + var resultFolderName = $"{tag}"; + + // FontAwesomeIcon.CaretSquareDown : FontAwesomeIcon.CaretSquareRight + var icon = _tagHandler.IsTagOpen(tag) ? FontAwesomeIcon.CaretSquareDown : FontAwesomeIcon.CaretSquareRight; + UiShared.FontText(icon.ToIconString(), UiBuilder.IconFont); + if (ImGui.IsItemClicked(ImGuiMouseButton.Left)) + { + ToggleTagOpen(tag); + } + ImGui.SameLine(); + UiShared.FontText(resultFolderName, UiBuilder.DefaultFont); + if (ImGui.IsItemClicked(ImGuiMouseButton.Left)) + { + ToggleTagOpen(tag); + } + } + + private void DrawButtons(string tag, List availablePairsInThisTag) + { + var allArePaused = availablePairsInThisTag.All(pair => pair.IsPaused); + var pauseButton = allArePaused ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause; + var trashButtonX = UiShared.GetIconButtonSize(FontAwesomeIcon.Trash).X; + var pauseButtonX = UiShared.GetIconButtonSize(pauseButton).X; + var windowX = ImGui.GetWindowContentRegionMin().X; + var windowWidth = UiShared.GetWindowContentRegionWidth(); + var spacingX = ImGui.GetStyle().ItemSpacing.X; + + var buttonPauseOffset = windowX + windowWidth - trashButtonX - spacingX - pauseButtonX; + ImGui.SameLine(buttonPauseOffset); + if (ImGuiComponents.IconButton(pauseButton)) + { + // If all of the currently visible pairs (after applying filters to the pairs) + // are paused we display a resume button to resume all currently visible (after filters) + // pairs. Otherwise, we just pause all the remaining pairs. + if (allArePaused) + { + // If all are paused => resume all + ResumeAllPairs(availablePairsInThisTag); + } + else + { + // otherwise pause all remaining + PauseRemainingPairs(availablePairsInThisTag); + } + } + if (allArePaused) + { + UiShared.AttachToolTip($"Resume pairing with all pairs in {tag}"); + } + else + { + UiShared.AttachToolTip($"Pause pairing with all pairs in {tag}"); + } + + var buttonDeleteOffset = windowX + windowWidth - trashButtonX; + ImGui.SameLine(buttonDeleteOffset); + if (ImGuiComponents.IconButton(FontAwesomeIcon.Trash)) + { + _tagHandler.RemoveTag(tag); + } + UiShared.AttachToolTip($"Delete Group {tag} (Will not delete the pairs)"); + } + + private void DrawPairs(string tag, List availablePairsInThisCategory) + { + ImGui.Separator(); + // These are all the OtherUIDs that are tagged with this tag + availablePairsInThisCategory + .ForEach(pair => UiShared.DrawWithID($"tag-{tag}-pair-${pair.OtherUID}", () => DrawPair(pair))); + ImGui.Separator(); + } + + private void DrawPair(ClientPairDto pair) + { + // This is probably just dumb. Somehow, just setting the cursor position to the icon lenght + // does not really push the child rendering further. So we'll just add two whitespaces and call it a day? + UiShared.FontText(" ", UiBuilder.DefaultFont); + ImGui.SameLine(); + _clientRenderFn(pair); + } + + private void ToggleTagOpen(string tag) + { + bool open = !_tagHandler.IsTagOpen(tag); + _tagHandler.SetTagOpen(tag, open); + } + + private void PauseRemainingPairs(List availablePairs) + { + foreach (var pairToPause in availablePairs.Where(pair => !pair.IsPaused)) + { + _ = _apiController.UserChangePairPauseStatus(pairToPause.OtherUID, paused: true); + } + } + + private void ResumeAllPairs(List availablePairs) + { + foreach (var pairToPause in availablePairs) + { + _ = _apiController.UserChangePairPauseStatus(pairToPause.OtherUID, paused: false); + } + } + } +} \ No newline at end of file diff --git a/MareSynchronos/UI/Components/SelectGroupForPairUi.cs b/MareSynchronos/UI/Components/SelectGroupForPairUi.cs new file mode 100644 index 0000000..137280e --- /dev/null +++ b/MareSynchronos/UI/Components/SelectGroupForPairUi.cs @@ -0,0 +1,155 @@ +using System.Collections.Generic; +using Dalamud.Interface; +using Dalamud.Interface.Components; +using Dalamud.Utility; +using ImGuiNET; +using MareSynchronos.API; +using MareSynchronos.UI.Handlers; + +namespace MareSynchronos.UI.Components +{ + public class SelectGroupForPairUi + { + /// + /// Should the panel show, yes/no + /// + private bool _show; + + /// + /// Has the panel already been opened? + /// This is used to prevent double opening + /// + private bool _opened; + + /// + /// The group UI is always open for a specific pair. This defines which pair the UI is open for. + /// + /// + private ClientPairDto? _pair; + + /// + /// For the add category option, this stores the currently typed in tag name + /// + private string _tagNameToAdd = ""; + + private readonly TagHandler _tagHandler; + private readonly Configuration _configuration; + + public SelectGroupForPairUi(TagHandler tagHandler, Configuration configuration) + { + _show = false; + _pair = null; + _tagHandler = tagHandler; + _configuration = configuration; + } + + public void Open(ClientPairDto pair) + { + _pair = pair; + // Using "_show" here to de-couple the opening of the popup + // The popup name is derived from the name the user currently sees, which is + // based on the showUidForEntry dictionary. + // We'd have to derive the name here to open it popup modal here, when the Open() is called + _show = true; + } + + + public void Draw(Dictionary showUidForEntry) + { + if (_pair == null) + { + return; + } + + var name = PairName(showUidForEntry, _pair.OtherUID); + var popupName = $"Chose Groups for {name}"; + // Is the popup supposed to show but did not open yet? Open it + if (_show && !_opened) + { + ImGui.OpenPopup(popupName); + _opened = true; + } + + // Is the popup not supposed to show? Set _opened to false so we can re-open it. + if (!_show) + { + _opened = false; + } + + if (ImGui.BeginPopupModal(popupName, ref _show, UiShared.PopupWindowFlags)) + { + UiShared.FontText($"Select the groups you want {name} to be in.", UiBuilder.DefaultFont); + foreach (var tag in _tagHandler.GetAllTagsSorted()) + { + UiShared.DrawWithID($"groups-pair-{_pair.OtherUID}-{tag}", () => DrawGroupName(_pair, tag)); + } + + UiShared.FontText($"Create a new group for {name}.", UiBuilder.DefaultFont); + if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus)) + { + HandleAddTag(); + } + ImGui.SameLine(); + ImGui.InputTextWithHint("##category_name", "New Group", ref _tagNameToAdd, 40); + { + if (ImGui.IsKeyDown(ImGuiKey.Enter)) + { + HandleAddTag(); + } + } + UiShared.SetScaledWindowSize(375); + ImGui.EndPopup(); + } + else + { + _show = false; + } + } + + private void DrawGroupName(ClientPairDto pair, string name) + { + bool hasTagBefore = _tagHandler.HasTag(pair, name); + bool hasTag = hasTagBefore; + if (ImGui.Checkbox(name, ref hasTag)) + { + if (hasTag) + { + _tagHandler.AddTagToPairedUid(pair, name); + } + else + { + _tagHandler.RemoveTagFromPairedUid(pair, name); + } + } + } + + private void HandleAddTag() + { + if (!_tagNameToAdd.IsNullOrWhitespace()) + { + _tagHandler.AddTag(_tagNameToAdd); + if (_pair != null) + { + _tagHandler.AddTagToPairedUid(_pair, _tagNameToAdd); + } + _tagNameToAdd = string.Empty; + } + } + + private string PairName(Dictionary showUidForEntry, string otherUid) + { + showUidForEntry.TryGetValue(otherUid, out var showUidInsteadOfName); + _configuration.GetCurrentServerUidComments().TryGetValue(otherUid, out var playerText); + if (showUidInsteadOfName) + { + playerText = otherUid; + } + else if (string.IsNullOrEmpty(playerText)) + { + playerText = otherUid; + } + return playerText; + } + + } +} \ No newline at end of file diff --git a/MareSynchronos/UI/Handlers/TagHandler.cs b/MareSynchronos/UI/Handlers/TagHandler.cs new file mode 100644 index 0000000..caff881 --- /dev/null +++ b/MareSynchronos/UI/Handlers/TagHandler.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using MareSynchronos.API; +using MareSynchronos.WebAPI; + +namespace MareSynchronos.UI.Handlers +{ + public class TagHandler + { + private readonly Configuration _configuration; + private readonly ApiController _apiController; + + public TagHandler(Configuration configuration) + { + _configuration = configuration; + } + + public void AddTag(string tag) + { + GetAvailableTagsForCurrentServer().Add(tag); + _configuration.Save(); + } + + public void RemoveTag(string tag) + { + // First remove the tag from teh available pair tags + GetAvailableTagsForCurrentServer().Remove(tag); + // Then also clean up the tag in all the pairs + GetUidTagDictionaryForCurrentServer().Keys + .ToList() + .ForEach(otherUid => RemoveTagFromPairedUid(otherUid, tag)); + _configuration.Save(); + } + + public void SetTagOpen(string tag, bool open) + { + if (open) + { + _configuration.OpenPairTags.Add(tag); + } + else + { + _configuration.OpenPairTags.Remove(tag); + } + _configuration.Save(); + } + + /// + /// Is this tag opened in the paired clients UI? + /// + /// the tag + /// open true/false + public bool IsTagOpen(string tag) + { + return _configuration.OpenPairTags.Contains(tag); + } + + public List GetAllTagsSorted() + { + return GetAvailableTagsForCurrentServer() + .OrderBy(s => s, StringComparer.OrdinalIgnoreCase) + .ToList(); + } + + public HashSet GetOtherUidsForTag(string tag) + { + return GetUidTagDictionaryForCurrentServer() + .Where(pair => pair.Value.Contains(tag, StringComparer.Ordinal)) + .Select(pair => pair.Key) + .ToHashSet(StringComparer.Ordinal); + } + + public void AddTagToPairedUid(ClientPairDto pair, string tagName) + { + var tagDictionary = GetUidTagDictionaryForCurrentServer(); + var tagsForPair = tagDictionary.GetValueOrDefault(pair.OtherUID, new List()); + tagsForPair.Add(tagName); + tagDictionary[pair.OtherUID] = tagsForPair; + _configuration.Save(); + } + + public void RemoveTagFromPairedUid(ClientPairDto pair, string tagName) + { + RemoveTagFromPairedUid(pair.OtherUID, tagName); + _configuration.Save(); + } + + public bool HasTag(ClientPairDto pair, string tagName) + { + var tagsForPair = GetUidTagDictionaryForCurrentServer().GetValueOrDefault(pair.OtherUID, new List()); + return tagsForPair.Contains(tagName, StringComparer.Ordinal); + } + + public bool HasAnyTag(ClientPairDto pair) + { + return GetUidTagDictionaryForCurrentServer().ContainsKey(pair.OtherUID); + } + + private void RemoveTagFromPairedUid(string otherUid, string tagName) + { + var tagsForPair = GetUidTagDictionaryForCurrentServer().GetValueOrDefault(otherUid, new List()); + tagsForPair.Remove(tagName); + if (!tagsForPair.Any()) + { + // No more entries in list -> we can kick out that entry completely + GetUidTagDictionaryForCurrentServer().Remove(otherUid); + } + else + { + GetUidTagDictionaryForCurrentServer()[otherUid] = tagsForPair; + } + } + + private Dictionary> GetUidTagDictionaryForCurrentServer() + { + if (!_configuration.UidServerPairedUserTags.ContainsKey(_configuration.ApiUri)) + { + _configuration.UidServerPairedUserTags.Add(_configuration.ApiUri, new(StringComparer.Ordinal)); + } + + return _configuration.UidServerPairedUserTags[_configuration.ApiUri]; + } + + private HashSet GetAvailableTagsForCurrentServer() + { + if (!_configuration.ServerAvailablePairTags.ContainsKey(_configuration.ApiUri)) + { + _configuration.ServerAvailablePairTags.Add(_configuration.ApiUri, new(StringComparer.Ordinal)); + } + + return _configuration.ServerAvailablePairTags[_configuration.ApiUri]; + } + } +} \ No newline at end of file diff --git a/MareSynchronos/UI/UIShared.cs b/MareSynchronos/UI/UIShared.cs index a817e3c..f8b7e5e 100644 --- a/MareSynchronos/UI/UIShared.cs +++ b/MareSynchronos/UI/UIShared.cs @@ -264,6 +264,13 @@ public class UiShared : IDisposable ImGui.TextUnformatted(text); ImGui.PopTextWrapPos(); } + + public static void FontText(string text, ImFontPtr font) + { + ImGui.PushFont(font); + ImGui.TextUnformatted(text); + ImGui.PopFont(); + } public static Vector4 GetCpuLoadColor(double input) => input < 50 ? ImGuiColors.ParsedGreen : input < 90 ? ImGuiColors.DalamudYellow : ImGuiColors.DalamudRed;