From fdc0d4bf6f26076421bc7a528aa6e99a61fc7d27 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Tue, 17 Oct 2023 22:26:48 +0200 Subject: [PATCH 01/56] remove logspam --- MareSynchronos/UI/DrawEntityFactory.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/MareSynchronos/UI/DrawEntityFactory.cs b/MareSynchronos/UI/DrawEntityFactory.cs index 92dffe0..c9002fe 100644 --- a/MareSynchronos/UI/DrawEntityFactory.cs +++ b/MareSynchronos/UI/DrawEntityFactory.cs @@ -37,7 +37,6 @@ public class DrawEntityFactory public DrawFolderGroup CreateDrawGroupFolder(GroupFullInfoDto groupFullInfoDto, Dictionary> pairs) { - _logger.LogTrace("Creating new DrawGroupFolder for {gid}", groupFullInfoDto.GID); return new DrawFolderGroup(groupFullInfoDto.Group.GID, groupFullInfoDto, _apiController, pairs.Select(p => CreateDrawPair(groupFullInfoDto.Group.GID + p.Key.UserData.UID, p.Key, p.Value)).ToList(), _tagHandler, _uidDisplayHandler, _mediator); @@ -45,16 +44,12 @@ public class DrawEntityFactory public DrawFolderTag CreateDrawTagFolder(string tag, Dictionary> pairs) { - _logger.LogTrace("Creating new DrawTagFolder for {tag}", tag); - return new(tag, pairs.Select(u => CreateDrawPair(tag, u.Key, u.Value)).ToList(), _tagHandler, _apiController, _selectPairForTagUi); } public DrawUserPair CreateDrawPair(string id, Pair user, List groups) { - _logger.LogTrace("Creating new DrawPair for {id}", id + user.UserData.UID); - return new DrawUserPair(id + user.UserData.UID, user, groups, _apiController, _uidDisplayHandler, _mediator, _selectTagForPairUi, _serverConfigurationManager); } From 5a62012d2aa3f40bb5b785c72b8f5f81aa7766bc Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Tue, 17 Oct 2023 22:33:38 +0200 Subject: [PATCH 02/56] fix icons and text of buttons being static in place --- MareSynchronos/UI/UISharedService.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/MareSynchronos/UI/UISharedService.cs b/MareSynchronos/UI/UISharedService.cs index 5f79201..341e7b8 100644 --- a/MareSynchronos/UI/UISharedService.cs +++ b/MareSynchronos/UI/UISharedService.cs @@ -360,6 +360,8 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase var cursor = ImGui.GetCursorPos(); var drawList = ImGui.GetWindowDrawList(); var pos = ImGui.GetWindowPos(); + var scrollPosY = ImGui.GetScrollY(); + var scrollPosX = ImGui.GetScrollX(); Vector2 buttonSize; var buttonSizeY = textSize.Y + padding.Y * 2; @@ -370,7 +372,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase if (width == null || width <= 0) { - var buttonSizeX = (iconScaling == 1 ? iconSize.Y : (iconSize.X * iconScaling)) + var buttonSizeX = (iconScaling == 1 ? iconSize.Y : (iconSize.X * iconScaling)) + textSize.X + padding.X * 2 + spacing.X + (iconXoffset * 2); buttonSize = new Vector2(buttonSizeX + iconExtraSpacing, buttonSizeY); } @@ -388,13 +390,13 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase } drawList.AddText(UiBuilder.IconFont, ImGui.GetFontSize() * iconScaling, - new(pos.X + cursor.X + iconXoffset + padding.X, - pos.Y + cursor.Y + (buttonSizeY - (iconSize.Y * iconScaling)) / 2f), + new(pos.X - scrollPosX + cursor.X + iconXoffset + padding.X, + pos.Y - scrollPosY + cursor.Y + (buttonSizeY - (iconSize.Y * iconScaling)) / 2f), ImGui.GetColorU32(ImGuiCol.Text), icon.ToIconString()); drawList.AddText(UiBuilder.DefaultFont, ImGui.GetFontSize(), - new(pos.X + cursor.X + (padding.X) + spacing.X + (iconSize.X * iconScaling) + (iconXoffset * 2) + iconExtraSpacing, - pos.Y + cursor.Y + ((buttonSizeY - textSize.Y) / 2f)), + new(pos.X - scrollPosX + cursor.X + (padding.X) + spacing.X + (iconSize.X * iconScaling) + (iconXoffset * 2) + iconExtraSpacing, + pos.Y - scrollPosY + cursor.Y + ((buttonSizeY - textSize.Y) / 2f)), ImGui.GetColorU32(ImGuiCol.Text), text); return wasClicked; From 3a7e3e71772fe2ad4f4f0b684f507facbe63979a Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Wed, 18 Oct 2023 00:18:02 +0200 Subject: [PATCH 03/56] add more options for the compactui --- .../Configurations/MareConfig.cs | 2 ++ .../PlayerData/Pairs/PairManager.cs | 2 +- MareSynchronos/UI/CompactUI.cs | 15 +++++++++-- MareSynchronos/UI/Components/DrawFolderTag.cs | 5 ++++ MareSynchronos/UI/Handlers/TagHandler.cs | 1 + MareSynchronos/UI/SettingsUi.cs | 25 +++++++++++++++++++ 6 files changed, 47 insertions(+), 3 deletions(-) diff --git a/MareSynchronos/MareConfiguration/Configurations/MareConfig.cs b/MareSynchronos/MareConfiguration/Configurations/MareConfig.cs index 61cd581..168ad78 100644 --- a/MareSynchronos/MareConfiguration/Configurations/MareConfig.cs +++ b/MareSynchronos/MareConfiguration/Configurations/MareConfig.cs @@ -27,8 +27,10 @@ public class MareConfig : IMareConfiguration public bool ProfilePopoutRight { get; set; } = false; public bool ProfilesAllowNsfw { get; set; } = false; public bool ProfilesShow { get; set; } = true; + public bool ShowSyncshellUsersInVisible { get; set; } = true; public bool ShowCharacterNameInsteadOfNotesForVisible { get; set; } = false; public bool ShowOfflineUsersSeparately { get; set; } = true; + public bool ShowSyncshellOfflineUsersSeparately { get; set; } = true; public bool GroupUpSyncshells { get; set; } = true; public bool ShowOnlineNotifications { get; set; } = false; public bool ShowOnlineNotificationsOnlyForIndividualPairs { get; set; } = true; diff --git a/MareSynchronos/PlayerData/Pairs/PairManager.cs b/MareSynchronos/PlayerData/Pairs/PairManager.cs index f1ac38d..02448d3 100644 --- a/MareSynchronos/PlayerData/Pairs/PairManager.cs +++ b/MareSynchronos/PlayerData/Pairs/PairManager.cs @@ -137,7 +137,7 @@ public sealed class PairManager : DisposableMediatorSubscriberBase } if (sendNotif && _configurationService.Current.ShowOnlineNotifications - && (_configurationService.Current.ShowOnlineNotificationsOnlyForIndividualPairs && pair.UserPair != null + && (_configurationService.Current.ShowOnlineNotificationsOnlyForIndividualPairs && pair.IsDirectlyPaired && !pair.IsOneSidedPair || !_configurationService.Current.ShowOnlineNotificationsOnlyForIndividualPairs) && (_configurationService.Current.ShowOnlineNotificationsOnlyForNamedPairs && !string.IsNullOrEmpty(pair.GetNote()) || !_configurationService.Current.ShowOnlineNotificationsOnlyForNamedPairs)) diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs index b6c8737..87ba4e2 100644 --- a/MareSynchronos/UI/CompactUI.cs +++ b/MareSynchronos/UI/CompactUI.cs @@ -484,7 +484,8 @@ public class CompactUi : WindowMediatorSubscriberBase if (_configService.Current.ShowVisibleUsersSeparately) { - var visibleUsers = users.Where(u => u.Key.IsVisible) + var visibleUsers = users.Where(u => u.Key.IsVisible && + (_configService.Current.ShowSyncshellUsersInVisible || !(!_configService.Current.ShowSyncshellUsersInVisible && !u.Key.IsDirectlyPaired))) .OrderBy( u => _configService.Current.ShowCharacterNameInsteadOfNotesForVisible && !string.IsNullOrEmpty(u.Key.PlayerName) ? (_configService.Current.PreferNotesOverNamesForVisible ? u.Key.GetNote() : u.Key.PlayerName) @@ -563,13 +564,23 @@ public class CompactUi : WindowMediatorSubscriberBase if (_configService.Current.ShowOfflineUsersSeparately) { - var offlineUsersEntries = users.Where(u => (!u.Key.IsOneSidedPair || u.Value.Any()) && !u.Key.IsOnline && !u.Key.UserPair.OwnPermissions.IsPaused()).OrderBy( + var offlineUsersEntries = users.Where(u => + ((u.Key.IsDirectlyPaired && _configService.Current.ShowSyncshellOfflineUsersSeparately) || !_configService.Current.ShowSyncshellOfflineUsersSeparately) + && (!u.Key.IsOneSidedPair || u.Value.Any()) && !u.Key.IsOnline && !u.Key.UserPair.OwnPermissions.IsPaused()).OrderBy( u => _configService.Current.ShowCharacterNameInsteadOfNotesForVisible && !string.IsNullOrEmpty(u.Key.PlayerName) ? (_configService.Current.PreferNotesOverNamesForVisible ? u.Key.GetNote() : u.Key.PlayerName) : (u.Key.GetNote() ?? u.Key.UserData.AliasOrUID), StringComparer.OrdinalIgnoreCase) .ToDictionary(u => u.Key, u => u.Value); drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomOfflineTag, offlineUsersEntries)); + if (_configService.Current.ShowSyncshellOfflineUsersSeparately) + { + var offlineSyncshellUsers = users.Where(u => !u.Key.IsDirectlyPaired && !u.Key.IsOnline && !u.Key.UserPair.OwnPermissions.IsPaused()).OrderBy( + u => _configService.Current.ShowCharacterNameInsteadOfNotesForVisible && !string.IsNullOrEmpty(u.Key.PlayerName) + ? (_configService.Current.PreferNotesOverNamesForVisible ? u.Key.GetNote() : u.Key.PlayerName) + : (u.Key.GetNote() ?? u.Key.UserData.AliasOrUID), StringComparer.OrdinalIgnoreCase); + drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomOfflineSyncshellTag, offlineSyncshellUsers.ToDictionary(k => k.Key, k => k.Value))); + } } drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomUnpairedTag, users.Where(u => u.Key.IsOneSidedPair).ToDictionary(u => u.Key, u => u.Value))); diff --git a/MareSynchronos/UI/Components/DrawFolderTag.cs b/MareSynchronos/UI/Components/DrawFolderTag.cs index 3db2caa..a61cdca 100644 --- a/MareSynchronos/UI/Components/DrawFolderTag.cs +++ b/MareSynchronos/UI/Components/DrawFolderTag.cs @@ -27,6 +27,7 @@ public class DrawFolderTag : DrawFolderBase TagHandler.CustomOfflineTag => false, TagHandler.CustomVisibleTag => false, TagHandler.CustomAllTag => true, + TagHandler.CustomOfflineSyncshellTag => false, _ => true, }; @@ -37,6 +38,7 @@ public class DrawFolderTag : DrawFolderBase TagHandler.CustomOfflineTag => false, TagHandler.CustomVisibleTag => false, TagHandler.CustomAllTag => false, + TagHandler.CustomOfflineSyncshellTag => false, _ => true, }; @@ -47,6 +49,7 @@ public class DrawFolderTag : DrawFolderBase TagHandler.CustomOfflineTag => false, TagHandler.CustomVisibleTag => false, TagHandler.CustomAllTag => false, + TagHandler.CustomOfflineSyncshellTag => false, _ => true, } && _drawPairs.Any(); @@ -58,6 +61,7 @@ public class DrawFolderTag : DrawFolderBase TagHandler.CustomUnpairedTag => FontAwesomeIcon.ArrowsLeftRight.ToIconString(), TagHandler.CustomOnlineTag => FontAwesomeIcon.Link.ToIconString(), TagHandler.CustomOfflineTag => FontAwesomeIcon.Unlink.ToIconString(), + TagHandler.CustomOfflineSyncshellTag => FontAwesomeIcon.Unlink.ToIconString(), TagHandler.CustomVisibleTag => FontAwesomeIcon.Eye.ToIconString(), TagHandler.CustomAllTag => FontAwesomeIcon.User.ToIconString(), _ => FontAwesomeIcon.Folder.ToIconString() @@ -93,6 +97,7 @@ public class DrawFolderTag : DrawFolderBase TagHandler.CustomUnpairedTag => "One-sided Individual Pairs", TagHandler.CustomOnlineTag => "Online / Paused by you", TagHandler.CustomOfflineTag => "Offline / Paused by other", + TagHandler.CustomOfflineSyncshellTag => "Offline Syncshell Users", TagHandler.CustomVisibleTag => "Visible", TagHandler.CustomAllTag => "Users", _ => _id diff --git a/MareSynchronos/UI/Handlers/TagHandler.cs b/MareSynchronos/UI/Handlers/TagHandler.cs index f02f9fe..8f3c0fd 100644 --- a/MareSynchronos/UI/Handlers/TagHandler.cs +++ b/MareSynchronos/UI/Handlers/TagHandler.cs @@ -6,6 +6,7 @@ public class TagHandler { public const string CustomAllTag = "Mare_All"; public const string CustomOfflineTag = "Mare_Offline"; + public const string CustomOfflineSyncshellTag = "Mare_OfflineSyncshell"; public const string CustomOnlineTag = "Mare_Online"; public const string CustomUnpairedTag = "Mare_Unpaired"; public const string CustomVisibleTag = "Mare_Visible"; diff --git a/MareSynchronos/UI/SettingsUi.cs b/MareSynchronos/UI/SettingsUi.cs index b002ec9..9453d50 100644 --- a/MareSynchronos/UI/SettingsUi.cs +++ b/MareSynchronos/UI/SettingsUi.cs @@ -1,5 +1,6 @@ using Dalamud.Interface; using Dalamud.Interface.Colors; +using Dalamud.Interface.Utility.Raii; using Dalamud.Utility; using ImGuiNET; using MareSynchronos.API.Data; @@ -560,6 +561,8 @@ public class SettingsUi : WindowMediatorSubscriberBase var enableDtrEntry = _configService.Current.EnableDtrEntry; var preferNotesInsteadOfName = _configService.Current.PreferNotesOverNamesForVisible; var groupUpSyncshells = _configService.Current.GroupUpSyncshells; + var groupInVisible = _configService.Current.ShowSyncshellUsersInVisible; + var syncshellOfflineSeparate = _configService.Current.ShowSyncshellOfflineUsersSeparately; if (ImGui.Checkbox("Enable Game Right Click Menu Entries", ref enableRightClickMenu)) { @@ -583,6 +586,17 @@ public class SettingsUi : WindowMediatorSubscriberBase } UiSharedService.DrawHelpText("This will show all currently visible users in a special 'Visible' group in the main UI."); + using (ImRaii.Disabled(!showVisibleSeparate)) + { + using var indent = ImRaii.PushIndent(); + if (ImGui.Checkbox("Show Syncshell Users in Visible Group", ref groupInVisible)) + { + _configService.Current.ShowSyncshellUsersInVisible = groupInVisible; + _configService.Save(); + Mediator.Publish(new RefreshUiMessage()); + } + } + if (ImGui.Checkbox("Show separate Offline group", ref showOfflineSeparate)) { _configService.Current.ShowOfflineUsersSeparately = showOfflineSeparate; @@ -591,6 +605,17 @@ public class SettingsUi : WindowMediatorSubscriberBase } UiSharedService.DrawHelpText("This will show all currently offline users in a special 'Offline' group in the main UI."); + using (ImRaii.Disabled(!showOfflineSeparate)) + { + using var indent = ImRaii.PushIndent(); + if (ImGui.Checkbox("Show separate Offline group for Syncshell users", ref syncshellOfflineSeparate)) + { + _configService.Current.ShowSyncshellOfflineUsersSeparately = syncshellOfflineSeparate; + _configService.Save(); + Mediator.Publish(new RefreshUiMessage()); + } + } + if (ImGui.Checkbox("Group up all syncshells in one folder", ref groupUpSyncshells)) { _configService.Current.GroupUpSyncshells = groupUpSyncshells; From b9843015cfab3a124c961b022bf76cf5b3d7ac0e Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Wed, 18 Oct 2023 02:22:05 +0200 Subject: [PATCH 04/56] fix broken font in header --- MareSynchronos/UI/CompactUI.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs index 87ba4e2..4a27a18 100644 --- a/MareSynchronos/UI/CompactUI.cs +++ b/MareSynchronos/UI/CompactUI.cs @@ -102,7 +102,6 @@ public class CompactUi : WindowMediatorSubscriberBase if (!_apiController.IsCurrentVersion) { var ver = _apiController.CurrentClientVersion; - if (_uiShared.UidFontBuilt) ImGui.PushFont(_uiShared.UidFont); var unsupported = "UNSUPPORTED VERSION"; var uidTextSize = ImGui.CalcTextSize(unsupported); ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMax().X + ImGui.GetWindowContentRegionMin().X) / 2 - uidTextSize.X / 2); From c237ae53e6184ea313f17acccf463cfc1477a189 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Wed, 18 Oct 2023 02:23:16 +0200 Subject: [PATCH 05/56] argh --- MareSynchronos/UI/CompactUI.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs index 4a27a18..ebcef03 100644 --- a/MareSynchronos/UI/CompactUI.cs +++ b/MareSynchronos/UI/CompactUI.cs @@ -103,10 +103,10 @@ public class CompactUi : WindowMediatorSubscriberBase { var ver = _apiController.CurrentClientVersion; var unsupported = "UNSUPPORTED VERSION"; - var uidTextSize = ImGui.CalcTextSize(unsupported); - ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMax().X + ImGui.GetWindowContentRegionMin().X) / 2 - uidTextSize.X / 2); using (ImRaii.PushFont(_uiShared.UidFont, _uiShared.UidFontBuilt)) { + var uidTextSize = ImGui.CalcTextSize(unsupported); + ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMax().X + ImGui.GetWindowContentRegionMin().X) / 2 - uidTextSize.X / 2); ImGui.AlignTextToFramePadding(); ImGui.TextColored(ImGuiColors.DalamudRed, unsupported); } From d7bfa2147f7bdd1ae0642ee0e826efd61c8486c1 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Wed, 18 Oct 2023 17:49:24 +0200 Subject: [PATCH 06/56] fix token for character change, add online count to syncshells and groups --- MareSynchronos/UI/CompactUI.cs | 4 +- .../UI/Components/DrawFolderBase.cs | 1 + .../UI/Components/DrawFolderGroup.cs | 14 +++++- MareSynchronos/UI/Components/DrawFolderTag.cs | 44 ++++++++++++++----- .../UI/Components/DrawGroupedGroupFolder.cs | 7 +++ MareSynchronos/UI/Components/IDrawFolder.cs | 1 + .../WebAPI/SignalR/TokenProvider.cs | 11 ++++- 7 files changed, 64 insertions(+), 18 deletions(-) diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs index ebcef03..23e4c2e 100644 --- a/MareSynchronos/UI/CompactUI.cs +++ b/MareSynchronos/UI/CompactUI.cs @@ -91,8 +91,8 @@ public class CompactUi : WindowMediatorSubscriberBase SizeConstraints = new WindowSizeConstraints() { - MinimumSize = new Vector2(350, 400), - MaximumSize = new Vector2(350, 2000), + MinimumSize = new Vector2(375, 400), + MaximumSize = new Vector2(375, 2000), }; } diff --git a/MareSynchronos/UI/Components/DrawFolderBase.cs b/MareSynchronos/UI/Components/DrawFolderBase.cs index 7c505a8..475fa3f 100644 --- a/MareSynchronos/UI/Components/DrawFolderBase.cs +++ b/MareSynchronos/UI/Components/DrawFolderBase.cs @@ -12,6 +12,7 @@ public abstract class DrawFolderBase : IDrawFolder protected readonly string _id; protected readonly TagHandler _tagHandler; private float _menuWidth = -1; + public int OnlinePairs => _drawPairs.Count(u => u.Pair.IsOnline); protected DrawFolderBase(string id, IEnumerable drawPairs, TagHandler tagHandler) { _id = id; diff --git a/MareSynchronos/UI/Components/DrawFolderGroup.cs b/MareSynchronos/UI/Components/DrawFolderGroup.cs index c77dc3b..0c7e45c 100644 --- a/MareSynchronos/UI/Components/DrawFolderGroup.cs +++ b/MareSynchronos/UI/Components/DrawFolderGroup.cs @@ -39,16 +39,26 @@ public class DrawFolderGroup : DrawFolderBase protected override float DrawIcon(float textPosY, float originalY) { ImGui.SetCursorPosY(textPosY); + using (ImRaii.PushFont(UiBuilder.IconFont)) ImGui.TextUnformatted(_groupFullInfoDto.GroupPermissions.IsDisableInvites() ? FontAwesomeIcon.Lock.ToIconString() : FontAwesomeIcon.Users.ToIconString()); if (_groupFullInfoDto.GroupPermissions.IsDisableInvites()) { UiSharedService.AttachToolTip("Syncshell " + _groupFullInfoDto.GroupAliasOrGID + " is closed for invites"); } - if (IsOwner) + + using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing with { X = ImGui.GetStyle().ItemSpacing.X / 2f })) { ImGui.SameLine(); ImGui.SetCursorPosY(textPosY); + ImGui.TextUnformatted("[" + OnlinePairs.ToString() + "]"); + } + UiSharedService.AttachToolTip(OnlinePairs + " online in this syncshell"); + + ImGui.SameLine(); + if (IsOwner) + { + ImGui.SameLine(); using (ImRaii.PushFont(UiBuilder.IconFont)) ImGui.TextUnformatted(FontAwesomeIcon.Crown.ToIconString()); UiSharedService.AttachToolTip("You are the owner of " + _groupFullInfoDto.GroupAliasOrGID); @@ -136,7 +146,7 @@ public class DrawFolderGroup : DrawFolderBase ImGui.CloseCurrentPopup(); } - if (UiSharedService.IconTextButton(disableVfx ? FontAwesomeIcon.Sun : FontAwesomeIcon.Circle, disableVfx ? "Enable VFX Sync" : "Disable VFX Sync", + if (UiSharedService.IconTextButton(disableVfx ? FontAwesomeIcon.Sun : FontAwesomeIcon.Circle, disableVfx ? "Enable VFX Sync" : "Disable VFX Sync", menuWidth, true)) { perm.SetDisableVFX(!disableVfx); diff --git a/MareSynchronos/UI/Components/DrawFolderTag.cs b/MareSynchronos/UI/Components/DrawFolderTag.cs index a61cdca..a71c472 100644 --- a/MareSynchronos/UI/Components/DrawFolderTag.cs +++ b/MareSynchronos/UI/Components/DrawFolderTag.cs @@ -53,22 +53,42 @@ public class DrawFolderTag : DrawFolderBase _ => true, } && _drawPairs.Any(); + private bool RenderCount => _id switch + { + TagHandler.CustomOfflineSyncshellTag => false, + TagHandler.CustomOfflineTag => false, + TagHandler.CustomUnpairedTag => false, + _ => true + }; + protected override float DrawIcon(float textPosY, float originalY) { - using var font = ImRaii.PushFont(UiBuilder.IconFont); - var icon = _id switch + using (ImRaii.PushFont(UiBuilder.IconFont)) { - TagHandler.CustomUnpairedTag => FontAwesomeIcon.ArrowsLeftRight.ToIconString(), - TagHandler.CustomOnlineTag => FontAwesomeIcon.Link.ToIconString(), - TagHandler.CustomOfflineTag => FontAwesomeIcon.Unlink.ToIconString(), - TagHandler.CustomOfflineSyncshellTag => FontAwesomeIcon.Unlink.ToIconString(), - TagHandler.CustomVisibleTag => FontAwesomeIcon.Eye.ToIconString(), - TagHandler.CustomAllTag => FontAwesomeIcon.User.ToIconString(), - _ => FontAwesomeIcon.Folder.ToIconString() - }; + var icon = _id switch + { + TagHandler.CustomUnpairedTag => FontAwesomeIcon.ArrowsLeftRight.ToIconString(), + TagHandler.CustomOnlineTag => FontAwesomeIcon.Link.ToIconString(), + TagHandler.CustomOfflineTag => FontAwesomeIcon.Unlink.ToIconString(), + TagHandler.CustomOfflineSyncshellTag => FontAwesomeIcon.Unlink.ToIconString(), + TagHandler.CustomVisibleTag => FontAwesomeIcon.Eye.ToIconString(), + TagHandler.CustomAllTag => FontAwesomeIcon.User.ToIconString(), + _ => FontAwesomeIcon.Folder.ToIconString() + }; - ImGui.SetCursorPosY(textPosY); - ImGui.TextUnformatted(icon); + ImGui.SetCursorPosY(textPosY); + ImGui.TextUnformatted(icon); + } + if (RenderCount) + { + using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing with { X = ImGui.GetStyle().ItemSpacing.X / 2f })) + { + ImGui.SameLine(); + ImGui.SetCursorPosY(textPosY); + ImGui.TextUnformatted("[" + OnlinePairs.ToString() + "]"); + } + UiSharedService.AttachToolTip(OnlinePairs + " online in this group"); + } ImGui.SameLine(); return ImGui.GetCursorPosX(); } diff --git a/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs b/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs index ee4432a..d1c3e74 100644 --- a/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs +++ b/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs @@ -9,6 +9,7 @@ public class DrawGroupedGroupFolder : IDrawFolder { private readonly IEnumerable _groups; private readonly TagHandler _tagHandler; + public int OnlinePairs => _groups.Sum(g => g.OnlinePairs); public DrawGroupedGroupFolder(IEnumerable groups, TagHandler tagHandler) { @@ -33,6 +34,12 @@ public class DrawGroupedGroupFolder : IDrawFolder ImGui.SameLine(); using (ImRaii.PushFont(UiBuilder.IconFont)) ImGui.TextUnformatted(FontAwesomeIcon.UsersRectangle.ToIconString()); + using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing with { X = ImGui.GetStyle().ItemSpacing.X / 2f })) + { + ImGui.SameLine(); + ImGui.TextUnformatted("[" + OnlinePairs.ToString() + "]"); + } + UiSharedService.AttachToolTip(OnlinePairs + " online all of your syncshells"); ImGui.SameLine(); ImGui.TextUnformatted("All Syncshells"); ImGui.Separator(); diff --git a/MareSynchronos/UI/Components/IDrawFolder.cs b/MareSynchronos/UI/Components/IDrawFolder.cs index ead3bb2..78fceb5 100644 --- a/MareSynchronos/UI/Components/IDrawFolder.cs +++ b/MareSynchronos/UI/Components/IDrawFolder.cs @@ -2,5 +2,6 @@ public interface IDrawFolder { + int OnlinePairs { get; } void Draw(); } diff --git a/MareSynchronos/WebAPI/SignalR/TokenProvider.cs b/MareSynchronos/WebAPI/SignalR/TokenProvider.cs index 5940979..2d0f6e8 100644 --- a/MareSynchronos/WebAPI/SignalR/TokenProvider.cs +++ b/MareSynchronos/WebAPI/SignalR/TokenProvider.cs @@ -1,5 +1,6 @@ using MareSynchronos.API.Routes; using MareSynchronos.Services; +using MareSynchronos.Services.Mediator; using MareSynchronos.Services.ServerConfiguration; using MareSynchronos.Utils; using Microsoft.Extensions.Logging; @@ -10,7 +11,7 @@ using System.Reflection; namespace MareSynchronos.WebAPI.SignalR; -public sealed class TokenProvider : IDisposable +public sealed class TokenProvider : IDisposable, IMediatorSubscriber { private readonly DalamudUtilService _dalamudUtil; private readonly HttpClient _httpClient; @@ -18,20 +19,26 @@ public sealed class TokenProvider : IDisposable private readonly ServerConfigurationManager _serverManager; private readonly ConcurrentDictionary _tokenCache = new(); - public TokenProvider(ILogger logger, ServerConfigurationManager serverManager, DalamudUtilService dalamudUtil) + public TokenProvider(ILogger logger, ServerConfigurationManager serverManager, DalamudUtilService dalamudUtil, MareMediator mareMediator) { _logger = logger; _serverManager = serverManager; _dalamudUtil = dalamudUtil; _httpClient = new(); var ver = Assembly.GetExecutingAssembly().GetName().Version; + Mediator = mareMediator; + Mediator.Subscribe(this, (_) => _tokenCache.Clear()); + Mediator.Subscribe(this, (_) => _tokenCache.Clear()); _httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("MareSynchronos", ver!.Major + "." + ver!.Minor + "." + ver!.Build)); } + public MareMediator Mediator { get; } + private JwtIdentifier CurrentIdentifier => new(_serverManager.CurrentApiUrl, _serverManager.GetSecretKey()!); public void Dispose() { + Mediator.UnsubscribeAll(this); _httpClient.Dispose(); } From 984ee08a2b6ccb0984d4e786f18fabe2196b7ef9 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Fri, 20 Oct 2023 16:53:56 +0200 Subject: [PATCH 07/56] add total count on mouseover, make syncshell windows non-blocking --- MareSynchronos/Plugin.cs | 5 ++- MareSynchronos/Services/Mediator/Messages.cs | 2 - MareSynchronos/UI/CompactUI.cs | 9 ++-- .../UI/Components/DrawFolderBase.cs | 4 +- .../UI/Components/DrawFolderGroup.cs | 6 +-- MareSynchronos/UI/Components/DrawFolderTag.cs | 13 +++--- .../UI/Components/DrawGroupedGroupFolder.cs | 4 +- MareSynchronos/UI/Components/IDrawFolder.cs | 1 + .../UI/Components/Popup/PopupHandler.cs | 16 ------- ...llPopupHandler.cs => CreateSyncshellUI.cs} | 24 ++++++++--- MareSynchronos/UI/DrawEntityFactory.cs | 8 ++-- ...hellPopupHandler.cs => JoinSyncshellUI.cs} | 43 +++++++++++-------- 12 files changed, 73 insertions(+), 62 deletions(-) rename MareSynchronos/UI/{Components/Popup/CreateSyncshellPopupHandler.cs => CreateSyncshellUI.cs} (84%) rename MareSynchronos/UI/{Components/Popup/JoinSyncshellPopupHandler.cs => JoinSyncshellUI.cs} (90%) diff --git a/MareSynchronos/Plugin.cs b/MareSynchronos/Plugin.cs index e688f83..cb9cb70 100644 --- a/MareSynchronos/Plugin.cs +++ b/MareSynchronos/Plugin.cs @@ -112,14 +112,15 @@ public sealed class Plugin : IDalamudPlugin collection.AddScoped(); collection.AddScoped(); collection.AddScoped(); + collection.AddScoped(); + collection.AddScoped(); + collection.AddScoped((s) => new EditProfileUi(s.GetRequiredService>(), s.GetRequiredService(), s.GetRequiredService(), pluginInterface.UiBuilder, s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService())); collection.AddScoped(); collection.AddScoped(); collection.AddScoped(); - collection.AddScoped(); - collection.AddScoped(); collection.AddScoped(); collection.AddScoped(); collection.AddScoped(); diff --git a/MareSynchronos/Services/Mediator/Messages.cs b/MareSynchronos/Services/Mediator/Messages.cs index adbc2d9..2901258 100644 --- a/MareSynchronos/Services/Mediator/Messages.cs +++ b/MareSynchronos/Services/Mediator/Messages.cs @@ -73,8 +73,6 @@ public record PairHandlerVisibleMessage(PairHandler Player) : MessageBase; public record RefreshUiMessage : MessageBase; public record OpenReportPopupMessage(Pair PairToReport) : MessageBase; public record OpenBanUserPopupMessage(Pair PairToBan, GroupFullInfoDto GroupFullInfoDto) : MessageBase; -public record JoinSyncshellPopupMessage() : MessageBase; -public record OpenCreateSyncshellPopupMessage() : MessageBase; public record OpenSyncshellAdminPanelPopupMessage(GroupFullInfoDto GroupInfo) : MessageBase; #pragma warning restore S2094 diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs index 23e4c2e..5a3035b 100644 --- a/MareSynchronos/UI/CompactUI.cs +++ b/MareSynchronos/UI/CompactUI.cs @@ -232,7 +232,7 @@ public class CompactUi : WindowMediatorSubscriberBase { if (UiSharedService.IconTextButton(FontAwesomeIcon.Plus, "Create new Syncshell", _syncshellMenuSize, true)) { - Mediator.Publish(new OpenCreateSyncshellPopupMessage()); + Mediator.Publish(new UiToggleMessage(typeof(CreateSyncshellUI))); ImGui.CloseCurrentPopup(); } } @@ -241,7 +241,7 @@ public class CompactUi : WindowMediatorSubscriberBase { if (UiSharedService.IconTextButton(FontAwesomeIcon.Users, "Join existing Syncshell", _syncshellMenuSize, true)) { - Mediator.Publish(new JoinSyncshellPopupMessage()); + Mediator.Publish(new UiToggleMessage(typeof(JoinSyncshellUI))); ImGui.CloseCurrentPopup(); } } @@ -517,7 +517,8 @@ public class CompactUi : WindowMediatorSubscriberBase : (u.Key.GetNote() ?? u.Key.UserData.AliasOrUID), StringComparer.Ordinal) .ToDictionary(k => k.Key, k => k.Value); - groupFolders.Add(_drawEntityFactory.CreateDrawGroupFolder(group, groupUsers2)); + groupFolders.Add(_drawEntityFactory.CreateDrawGroupFolder(group, groupUsers2, + users.Count(v => v.Value.Exists(g => string.Equals(g.GID, group.GID, StringComparison.Ordinal))))); } if (_configService.Current.GroupUpSyncshells) @@ -544,7 +545,7 @@ public class CompactUi : WindowMediatorSubscriberBase { alreadyInTags.Add(u.Key); return (u.Key, u.Value); - }).ToDictionary(u => u.Key, u => u.Value))); + }).ToDictionary(u => u.Key, u => u.Value), users.Count(u => _tagHandler.HasTag(u.Key.UserData.UID, tag)))); } var onlineDirectPairedUsersNotInTags = users.Where(u => u.Key.IsDirectlyPaired && !u.Key.IsOneSidedPair && !_tagHandler.HasAnyTag(u.Key.UserData.UID) diff --git a/MareSynchronos/UI/Components/DrawFolderBase.cs b/MareSynchronos/UI/Components/DrawFolderBase.cs index 475fa3f..2acc636 100644 --- a/MareSynchronos/UI/Components/DrawFolderBase.cs +++ b/MareSynchronos/UI/Components/DrawFolderBase.cs @@ -13,11 +13,13 @@ public abstract class DrawFolderBase : IDrawFolder protected readonly TagHandler _tagHandler; private float _menuWidth = -1; public int OnlinePairs => _drawPairs.Count(u => u.Pair.IsOnline); - protected DrawFolderBase(string id, IEnumerable drawPairs, TagHandler tagHandler) + public int TotalPairs { get; } + protected DrawFolderBase(string id, IEnumerable drawPairs, TagHandler tagHandler, int totalPairs) { _id = id; _drawPairs = drawPairs; _tagHandler = tagHandler; + TotalPairs = totalPairs; } protected abstract bool RenderIfEmpty { get; } diff --git a/MareSynchronos/UI/Components/DrawFolderGroup.cs b/MareSynchronos/UI/Components/DrawFolderGroup.cs index 0c7e45c..1043cd5 100644 --- a/MareSynchronos/UI/Components/DrawFolderGroup.cs +++ b/MareSynchronos/UI/Components/DrawFolderGroup.cs @@ -21,8 +21,8 @@ public class DrawFolderGroup : DrawFolderBase public DrawFolderGroup(string id, GroupFullInfoDto groupFullInfoDto, ApiController apiController, IEnumerable drawPairs, TagHandler tagHandler, IdDisplayHandler idDisplayHandler, - MareMediator mareMediator) : - base(id, drawPairs, tagHandler) + MareMediator mareMediator, int totalPairs) : + base(id, drawPairs, tagHandler, totalPairs) { _groupFullInfoDto = groupFullInfoDto; _apiController = apiController; @@ -53,7 +53,7 @@ public class DrawFolderGroup : DrawFolderBase ImGui.SetCursorPosY(textPosY); ImGui.TextUnformatted("[" + OnlinePairs.ToString() + "]"); } - UiSharedService.AttachToolTip(OnlinePairs + " online in this syncshell"); + UiSharedService.AttachToolTip(OnlinePairs + " online" + Environment.NewLine + TotalPairs + " total"); ImGui.SameLine(); if (IsOwner) diff --git a/MareSynchronos/UI/Components/DrawFolderTag.cs b/MareSynchronos/UI/Components/DrawFolderTag.cs index a71c472..42b4cec 100644 --- a/MareSynchronos/UI/Components/DrawFolderTag.cs +++ b/MareSynchronos/UI/Components/DrawFolderTag.cs @@ -13,8 +13,8 @@ public class DrawFolderTag : DrawFolderBase private readonly ApiController _apiController; private readonly SelectPairForTagUi _selectPairForTagUi; - public DrawFolderTag(string id, IEnumerable drawPairs, TagHandler tagHandler, ApiController apiController, SelectPairForTagUi selectPairForTagUi) - : base(id, drawPairs, tagHandler) + public DrawFolderTag(string id, IEnumerable drawPairs, TagHandler tagHandler, ApiController apiController, SelectPairForTagUi selectPairForTagUi, int totalPairs) + : base(id, drawPairs, tagHandler, totalPairs) { _apiController = apiController; _selectPairForTagUi = selectPairForTagUi; @@ -55,9 +55,12 @@ public class DrawFolderTag : DrawFolderBase private bool RenderCount => _id switch { - TagHandler.CustomOfflineSyncshellTag => false, - TagHandler.CustomOfflineTag => false, TagHandler.CustomUnpairedTag => false, + TagHandler.CustomOnlineTag => false, + TagHandler.CustomOfflineTag => false, + TagHandler.CustomVisibleTag => false, + TagHandler.CustomAllTag => false, + TagHandler.CustomOfflineSyncshellTag => false, _ => true }; @@ -87,7 +90,7 @@ public class DrawFolderTag : DrawFolderBase ImGui.SetCursorPosY(textPosY); ImGui.TextUnformatted("[" + OnlinePairs.ToString() + "]"); } - UiSharedService.AttachToolTip(OnlinePairs + " online in this group"); + UiSharedService.AttachToolTip(OnlinePairs + " online" + Environment.NewLine + TotalPairs + " total"); } ImGui.SameLine(); return ImGui.GetCursorPosX(); diff --git a/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs b/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs index d1c3e74..f8ee004 100644 --- a/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs +++ b/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs @@ -10,6 +10,7 @@ public class DrawGroupedGroupFolder : IDrawFolder private readonly IEnumerable _groups; private readonly TagHandler _tagHandler; public int OnlinePairs => _groups.Sum(g => g.OnlinePairs); + public int TotalPairs => _groups.Sum(g => g.TotalPairs); public DrawGroupedGroupFolder(IEnumerable groups, TagHandler tagHandler) { @@ -39,7 +40,8 @@ public class DrawGroupedGroupFolder : IDrawFolder ImGui.SameLine(); ImGui.TextUnformatted("[" + OnlinePairs.ToString() + "]"); } - UiSharedService.AttachToolTip(OnlinePairs + " online all of your syncshells"); + UiSharedService.AttachToolTip(OnlinePairs + " online in all of your joined syncshells" + Environment.NewLine + + TotalPairs + " pairs combined in all of your joined syncshells"); ImGui.SameLine(); ImGui.TextUnformatted("All Syncshells"); ImGui.Separator(); diff --git a/MareSynchronos/UI/Components/IDrawFolder.cs b/MareSynchronos/UI/Components/IDrawFolder.cs index 78fceb5..13563ca 100644 --- a/MareSynchronos/UI/Components/IDrawFolder.cs +++ b/MareSynchronos/UI/Components/IDrawFolder.cs @@ -2,6 +2,7 @@ public interface IDrawFolder { + int TotalPairs { get; } int OnlinePairs { get; } void Draw(); } diff --git a/MareSynchronos/UI/Components/Popup/PopupHandler.cs b/MareSynchronos/UI/Components/Popup/PopupHandler.cs index b278014..8359e97 100644 --- a/MareSynchronos/UI/Components/Popup/PopupHandler.cs +++ b/MareSynchronos/UI/Components/Popup/PopupHandler.cs @@ -36,14 +36,6 @@ public class PopupHandler : WindowMediatorSubscriberBase IsOpen = true; }); - Mediator.Subscribe(this, (msg) => - { - _openPopup = true; - _currentHandler = _handlers.OfType().Single(); - ((CreateSyncshellPopupHandler)_currentHandler).Open(); - IsOpen = true; - }); - Mediator.Subscribe(this, (msg) => { _openPopup = true; @@ -52,14 +44,6 @@ public class PopupHandler : WindowMediatorSubscriberBase IsOpen = true; }); - Mediator.Subscribe(this, (_) => - { - _openPopup = true; - _currentHandler = _handlers.OfType().Single(); - ((JoinSyncshellPopupHandler)_currentHandler).Open(); - IsOpen = true; - }); - Mediator.Subscribe(this, (msg) => { IsOpen = true; diff --git a/MareSynchronos/UI/Components/Popup/CreateSyncshellPopupHandler.cs b/MareSynchronos/UI/CreateSyncshellUI.cs similarity index 84% rename from MareSynchronos/UI/Components/Popup/CreateSyncshellPopupHandler.cs rename to MareSynchronos/UI/CreateSyncshellUI.cs index 4b9f0b9..97c3d3f 100644 --- a/MareSynchronos/UI/Components/Popup/CreateSyncshellPopupHandler.cs +++ b/MareSynchronos/UI/CreateSyncshellUI.cs @@ -4,27 +4,37 @@ using Dalamud.Interface.Utility.Raii; using ImGuiNET; using MareSynchronos.API.Data.Extensions; using MareSynchronos.API.Dto.Group; +using MareSynchronos.Services.Mediator; using MareSynchronos.WebAPI; +using Microsoft.Extensions.Logging; using System.Numerics; -namespace MareSynchronos.UI.Components.Popup; +namespace MareSynchronos.UI; -public class CreateSyncshellPopupHandler : IPopupHandler +public class CreateSyncshellUI : WindowMediatorSubscriberBase { private readonly ApiController _apiController; private readonly UiSharedService _uiSharedService; private bool _errorGroupCreate; private GroupJoinDto? _lastCreatedGroup; - public CreateSyncshellPopupHandler(ApiController apiController, UiSharedService uiSharedService) + public CreateSyncshellUI(ILogger logger, MareMediator mareMediator, ApiController apiController, UiSharedService uiSharedService) + : base(logger, mareMediator, "Create new Syncshell###MareSynchronosCreateSyncshell") { _apiController = apiController; _uiSharedService = uiSharedService; + SizeConstraints = new() + { + MinimumSize = new(550, 330), + MaximumSize = new(550, 330) + }; + + Flags = ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoCollapse; + + Mediator.Subscribe(this, (_) => IsOpen = false); } - public Vector2 PopupSize => new(500, 300); - - public void DrawContent() + public override void Draw() { using (ImRaii.PushFont(_uiSharedService.UidFont)) ImGui.TextUnformatted("Create new Syncshell"); @@ -93,7 +103,7 @@ public class CreateSyncshellPopupHandler : IPopupHandler } } - public void Open() + public override void OnOpen() { _lastCreatedGroup = null; } diff --git a/MareSynchronos/UI/DrawEntityFactory.cs b/MareSynchronos/UI/DrawEntityFactory.cs index c9002fe..320c9b2 100644 --- a/MareSynchronos/UI/DrawEntityFactory.cs +++ b/MareSynchronos/UI/DrawEntityFactory.cs @@ -35,17 +35,17 @@ public class DrawEntityFactory _serverConfigurationManager = serverConfigurationManager; } - public DrawFolderGroup CreateDrawGroupFolder(GroupFullInfoDto groupFullInfoDto, Dictionary> pairs) + public DrawFolderGroup CreateDrawGroupFolder(GroupFullInfoDto groupFullInfoDto, Dictionary> pairs, int totalPairs = -1) { return new DrawFolderGroup(groupFullInfoDto.Group.GID, groupFullInfoDto, _apiController, pairs.Select(p => CreateDrawPair(groupFullInfoDto.Group.GID + p.Key.UserData.UID, p.Key, p.Value)).ToList(), - _tagHandler, _uidDisplayHandler, _mediator); + _tagHandler, _uidDisplayHandler, _mediator, totalPairs); } - public DrawFolderTag CreateDrawTagFolder(string tag, Dictionary> pairs) + public DrawFolderTag CreateDrawTagFolder(string tag, Dictionary> pairs, int totalPairs = -1) { return new(tag, pairs.Select(u => CreateDrawPair(tag, u.Key, u.Value)).ToList(), - _tagHandler, _apiController, _selectPairForTagUi); + _tagHandler, _apiController, _selectPairForTagUi, totalPairs); } public DrawUserPair CreateDrawPair(string id, Pair user, List groups) diff --git a/MareSynchronos/UI/Components/Popup/JoinSyncshellPopupHandler.cs b/MareSynchronos/UI/JoinSyncshellUI.cs similarity index 90% rename from MareSynchronos/UI/Components/Popup/JoinSyncshellPopupHandler.cs rename to MareSynchronos/UI/JoinSyncshellUI.cs index 8c73de4..339baa9 100644 --- a/MareSynchronos/UI/Components/Popup/JoinSyncshellPopupHandler.cs +++ b/MareSynchronos/UI/JoinSyncshellUI.cs @@ -5,13 +5,14 @@ using MareSynchronos.API.Data.Enum; using MareSynchronos.API.Data.Extensions; using MareSynchronos.API.Dto; using MareSynchronos.API.Dto.Group; +using MareSynchronos.Services.Mediator; using MareSynchronos.Utils; using MareSynchronos.WebAPI; -using System.Numerics; +using Microsoft.Extensions.Logging; -namespace MareSynchronos.UI.Components.Popup; +namespace MareSynchronos.UI; -internal class JoinSyncshellPopupHandler : IPopupHandler +internal class JoinSyncshellUI : WindowMediatorSubscriberBase { private readonly ApiController _apiController; private readonly UiSharedService _uiSharedService; @@ -21,18 +22,35 @@ internal class JoinSyncshellPopupHandler : IPopupHandler private string _previousPassword = string.Empty; private string _syncshellPassword = string.Empty; - public JoinSyncshellPopupHandler(UiSharedService uiSharedService, ApiController apiController) + public JoinSyncshellUI(ILogger logger, MareMediator mediator, + UiSharedService uiSharedService, ApiController apiController) : base(logger, mediator, "Join existing Syncshell###MareSynchronosJoinSyncshell") { _uiSharedService = uiSharedService; _apiController = apiController; + SizeConstraints = new() + { + MinimumSize = new(700, 400), + MaximumSize = new(700, 400) + }; + + Mediator.Subscribe(this, (_) => IsOpen = false); + + Flags = ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoResize; } - public Vector2 PopupSize => new(700, 400); + public override void OnOpen() + { + _desiredSyncshellToJoin = string.Empty; + _syncshellPassword = string.Empty; + _previousPassword = string.Empty; + _groupJoinInfo = null; + _ownPermissions = _apiController.DefaultPermissions.DeepClone()!; + } - public void DrawContent() + public override void Draw() { using (ImRaii.PushFont(_uiSharedService.UidFont)) - ImGui.TextUnformatted((_groupJoinInfo == null || !_groupJoinInfo.Success) ? "Join Syncshell" : ("Finalize join Syncshell " + _groupJoinInfo.GroupAliasOrGID)); + ImGui.TextUnformatted(_groupJoinInfo == null || !_groupJoinInfo.Success ? "Join Syncshell" : "Finalize join Syncshell " + _groupJoinInfo.GroupAliasOrGID); ImGui.Separator(); if (_groupJoinInfo == null || !_groupJoinInfo.Success) @@ -147,17 +165,8 @@ internal class JoinSyncshellPopupHandler : IPopupHandler joinPermissions.SetDisableAnimations(_ownPermissions.DisableGroupAnimations); joinPermissions.SetDisableVFX(_ownPermissions.DisableGroupVFX); _ = _apiController.GroupJoinFinalize(new GroupJoinDto(_groupJoinInfo.Group, _previousPassword, joinPermissions)); - ImGui.CloseCurrentPopup(); + IsOpen = false; } } } - - public void Open() - { - _desiredSyncshellToJoin = string.Empty; - _syncshellPassword = string.Empty; - _previousPassword = string.Empty; - _groupJoinInfo = null; - _ownPermissions = _apiController.DefaultPermissions.DeepClone()!; - } } \ No newline at end of file From 33344386c4a1b6f9851d671b8eda58a76b0f05dc Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Sat, 21 Oct 2023 18:29:20 +0200 Subject: [PATCH 08/56] rework main ui --- MareAPI | 2 +- .../Services/CommandManagerService.cs | 2 +- MareSynchronos/Services/Mediator/Messages.cs | 1 - MareSynchronos/UI/CompactUI.cs | 148 ++--- MareSynchronos/UI/Components/DrawFolderTag.cs | 24 +- MareSynchronos/UI/DataAnalysisUi.cs | 1 - MareSynchronos/UI/JoinSyncshellUI.cs | 2 +- MareSynchronos/UI/TopTabMenu.cs | 557 ++++++++++++++++++ MareSynchronos/UI/UISharedService.cs | 27 +- .../SignalR/ApIController.Functions.Users.cs | 19 +- .../SignalR/ApiController.Functions.Groups.cs | 5 +- 11 files changed, 656 insertions(+), 132 deletions(-) create mode 100644 MareSynchronos/UI/TopTabMenu.cs diff --git a/MareAPI b/MareAPI index 9199d9c..d04781f 160000 --- a/MareAPI +++ b/MareAPI @@ -1 +1 @@ -Subproject commit 9199d9c66721a225e32083e76c44df0f8fdabd01 +Subproject commit d04781fddf11e3cece03d5bdd7acf249c453664c diff --git a/MareSynchronos/Services/CommandManagerService.cs b/MareSynchronos/Services/CommandManagerService.cs index db9285e..1c2688a 100644 --- a/MareSynchronos/Services/CommandManagerService.cs +++ b/MareSynchronos/Services/CommandManagerService.cs @@ -100,7 +100,7 @@ public sealed class CommandManagerService : IDisposable } else if (string.Equals(splitArgs[0], "analyze", StringComparison.OrdinalIgnoreCase)) { - _mediator.Publish(new OpenDataAnalysisUiMessage()); + _mediator.Publish(new UiToggleMessage(typeof(DataAnalysisUi))); } } } \ No newline at end of file diff --git a/MareSynchronos/Services/Mediator/Messages.cs b/MareSynchronos/Services/Mediator/Messages.cs index 2901258..05d0a4a 100644 --- a/MareSynchronos/Services/Mediator/Messages.cs +++ b/MareSynchronos/Services/Mediator/Messages.cs @@ -15,7 +15,6 @@ namespace MareSynchronos.Services.Mediator; public record SwitchToIntroUiMessage : MessageBase; public record SwitchToMainUiMessage : MessageBase; public record OpenSettingsUiMessage : MessageBase; -public record OpenDataAnalysisUiMessage : MessageBase; public record DalamudLoginMessage : MessageBase; public record DalamudLogoutMessage : MessageBase; public record FrameworkUpdateMessage : SameThreadMessage; diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs index 5a3035b..e6715f1 100644 --- a/MareSynchronos/UI/CompactUI.cs +++ b/MareSynchronos/UI/CompactUI.cs @@ -38,20 +38,20 @@ public class CompactUi : WindowMediatorSubscriberBase private readonly PairManager _pairManager; private readonly SelectTagForPairUi _selectGroupForPairUi; private readonly SelectPairForTagUi _selectPairsForGroupUi; + private readonly TopTabMenu _tabMenu; private readonly ServerConfigurationManager _serverManager; private readonly TagHandler _tagHandler; private readonly UiSharedService _uiShared; - private string _characterOrCommentFilter = string.Empty; private List _drawFolders; private Pair? _lastAddedUser; private string _lastAddedUserComment = string.Empty; private Vector2 _lastPosition = Vector2.One; private Vector2 _lastSize = Vector2.One; - private string _pairToAdd = string.Empty; private int _secretKeyIdx = -1; private bool _showModalForUserAddition; private bool _wasOpen; + public CompactUi(ILogger logger, UiSharedService uiShared, MareConfigService configService, ApiController apiController, PairManager pairManager, ServerConfigurationManager serverManager, MareMediator mediator, FileUploadManager fileTransferManager, TagHandler tagHandler, DrawEntityFactory drawEntityFactory, SelectTagForPairUi selectTagForPairUi, SelectPairForTagUi selectPairForTagUi) @@ -67,6 +67,7 @@ public class CompactUi : WindowMediatorSubscriberBase _drawEntityFactory = drawEntityFactory; _selectGroupForPairUi = selectTagForPairUi; _selectPairsForGroupUi = selectPairForTagUi; + _tabMenu = new TopTabMenu(Mediator, _apiController, _pairManager); _drawFolders = GetDrawFolders().ToList(); @@ -121,11 +122,12 @@ public class CompactUi : WindowMediatorSubscriberBase if (_apiController.ServerState is ServerState.Connected) { + UiSharedService.DrawWithID("global-topmenu", () => _tabMenu.Draw()); UiSharedService.DrawWithID("pairlist", DrawPairList); ImGui.Separator(); UiSharedService.DrawWithID("transfers", DrawTransfers); - TransferPartHeight = ImGui.GetCursorPosY() - TransferPartHeight; + TransferPartHeight = ImGui.GetCursorPosY() - TransferPartHeight - ImGui.GetTextLineHeight(); UiSharedService.DrawWithID("group-user-popup", () => _selectPairsForGroupUi.Draw(_pairManager.DirectPairs)); UiSharedService.DrawWithID("grouping-popup", () => _selectGroupForPairUi.Draw()); } @@ -171,6 +173,28 @@ public class CompactUi : WindowMediatorSubscriberBase } } + private void DrawPairList() + { + UiSharedService.DrawWithID("pairs", DrawPairs); + TransferPartHeight = ImGui.GetCursorPosY(); + } + + private void DrawPairs() + { + var ySize = TransferPartHeight == 0 + ? 1 + : (ImGui.GetWindowContentRegionMax().Y - ImGui.GetWindowContentRegionMin().Y) - TransferPartHeight - ImGui.GetCursorPosY(); + + ImGui.BeginChild("list", new Vector2(WindowContentWidth, ySize), border: false); + + foreach (var item in _drawFolders) + { + item.Draw(); + } + + ImGui.EndChild(); + } + private void DrawAddCharacter() { ImGui.Dummy(new(10)); @@ -200,93 +224,6 @@ public class CompactUi : WindowMediatorSubscriberBase } } - private void DrawAddPair() - { - var buttonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.UserPlus); - var usersButtonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Users); - ImGui.SetNextItemWidth(UiSharedService.GetWindowContentRegionWidth() - ImGui.GetWindowContentRegionMin().X - buttonSize.X - ImGui.GetStyle().ItemSpacing.X - usersButtonSize.X); - ImGui.InputTextWithHint("##otheruid", "Other players UID/Alias", ref _pairToAdd, 20); - ImGui.SameLine(); - var alreadyExisting = _pairManager.DirectPairs.Exists(p => string.Equals(p.UserData.UID, _pairToAdd, StringComparison.Ordinal) || string.Equals(p.UserData.Alias, _pairToAdd, StringComparison.Ordinal)); - using (ImRaii.Disabled(alreadyExisting || string.IsNullOrEmpty(_pairToAdd))) - { - if (ImGuiComponents.IconButton(FontAwesomeIcon.UserPlus)) - { - _ = _apiController.UserAddPair(new(new(_pairToAdd))); - _pairToAdd = string.Empty; - } - UiSharedService.AttachToolTip("Pair with " + (_pairToAdd.IsNullOrEmpty() ? "other user" : _pairToAdd)); - } - - ImGui.SameLine(); - if (ImGuiComponents.IconButton(FontAwesomeIcon.Users)) - { - ImGui.OpenPopup("Syncshell Menu"); - } - UiSharedService.AttachToolTip("Syncshell Menu"); - - if (ImGui.BeginPopup("Syncshell Menu")) - { - using (ImRaii.Disabled(_pairManager.GroupPairs.Select(k => k.Key).Distinct() - .Count(g => string.Equals(g.OwnerUID, _apiController.UID, StringComparison.Ordinal)) >= _apiController.ServerInfo.MaxGroupsCreatedByUser)) - { - if (UiSharedService.IconTextButton(FontAwesomeIcon.Plus, "Create new Syncshell", _syncshellMenuSize, true)) - { - Mediator.Publish(new UiToggleMessage(typeof(CreateSyncshellUI))); - ImGui.CloseCurrentPopup(); - } - } - - using (ImRaii.Disabled(_pairManager.GroupPairs.Select(k => k.Key).Distinct().Count() >= _apiController.ServerInfo.MaxGroupsJoinedByUser)) - { - if (UiSharedService.IconTextButton(FontAwesomeIcon.Users, "Join existing Syncshell", _syncshellMenuSize, true)) - { - Mediator.Publish(new UiToggleMessage(typeof(JoinSyncshellUI))); - ImGui.CloseCurrentPopup(); - } - } - _syncshellMenuSize = ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X; - ImGui.EndPopup(); - } - - ImGuiHelpers.ScaledDummy(2); - } - - private float _syncshellMenuSize = 0; - - private void DrawFilter() - { - ImGui.SetNextItemWidth(WindowContentWidth); - if (ImGui.InputTextWithHint("##filter", "Filter for UID/notes", ref _characterOrCommentFilter, 255)) - { - Mediator.Publish(new RefreshUiMessage()); - } - } - - private void DrawPairList() - { - UiSharedService.DrawWithID("addpair", DrawAddPair); - UiSharedService.DrawWithID("pairs", DrawPairs); - TransferPartHeight = ImGui.GetCursorPosY(); - UiSharedService.DrawWithID("filter", DrawFilter); - } - - private void DrawPairs() - { - var ySize = TransferPartHeight == 0 - ? 1 - : (ImGui.GetWindowContentRegionMax().Y - ImGui.GetWindowContentRegionMin().Y) - TransferPartHeight - ImGui.GetCursorPosY(); - - ImGui.BeginChild("list", new Vector2(WindowContentWidth, ySize), border: false); - - foreach (var item in _drawFolders) - { - item.Draw(); - } - - ImGui.EndChild(); - } - private void DrawServerStatus() { var buttonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Link); @@ -331,16 +268,6 @@ public class CompactUi : WindowMediatorSubscriberBase var color = UiSharedService.GetBoolColor(!_serverManager.CurrentServer!.FullPause); var connectedIcon = !_serverManager.CurrentServer.FullPause ? FontAwesomeIcon.Link : FontAwesomeIcon.Unlink; - if (_apiController.ServerState is ServerState.Connected) - { - ImGui.SetCursorPosX(0 + ImGui.GetStyle().ItemSpacing.X); - if (ImGuiComponents.IconButton(FontAwesomeIcon.UserCircle)) - { - Mediator.Publish(new UiToggleMessage(typeof(EditProfileUi))); - } - UiSharedService.AttachToolTip("Edit your Mare Profile"); - } - ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - buttonSize.X); if (printShard) { @@ -412,13 +339,6 @@ public class CompactUi : WindowMediatorSubscriberBase { ImGui.TextUnformatted("No downloads in progress"); } - - if (UiSharedService.IconTextButton(FontAwesomeIcon.PersonCircleQuestion, "Mare Character Data Analysis", WindowContentWidth)) - { - Mediator.Publish(new OpenDataAnalysisUiMessage()); - } - - ImGui.SameLine(); } private void DrawUIDHeader() @@ -495,7 +415,7 @@ public class CompactUi : WindowMediatorSubscriberBase } List groupFolders = new(); - foreach (var group in _pairManager.GroupPairs.Select(g => g.Key).OrderBy(g => g.GroupAliasOrGID, StringComparer.Ordinal)) + foreach (var group in _pairManager.GroupPairs.Select(g => g.Key).OrderBy(g => g.GroupAliasOrGID, StringComparer.OrdinalIgnoreCase)) { var groupUsers2 = users.Where(v => v.Value.Exists(g => string.Equals(g.GID, group.GID, StringComparison.Ordinal)) && (v.Key.IsOnline || (!v.Key.IsOnline && !_configService.Current.ShowOfflineUsersSeparately) @@ -514,7 +434,7 @@ public class CompactUi : WindowMediatorSubscriberBase .ThenBy( u => _configService.Current.ShowCharacterNameInsteadOfNotesForVisible && !string.IsNullOrEmpty(u.Key.PlayerName) ? (_configService.Current.PreferNotesOverNamesForVisible ? u.Key.GetNote() : u.Key.PlayerName) - : (u.Key.GetNote() ?? u.Key.UserData.AliasOrUID), StringComparer.Ordinal) + : (u.Key.GetNote() ?? u.Key.UserData.AliasOrUID), StringComparer.OrdinalIgnoreCase) .ToDictionary(k => k.Key, k => k.Value); groupFolders.Add(_drawEntityFactory.CreateDrawGroupFolder(group, groupUsers2, @@ -590,14 +510,14 @@ public class CompactUi : WindowMediatorSubscriberBase private Dictionary> GetFilteredGroupUsers() { - if (string.IsNullOrEmpty(_characterOrCommentFilter)) return _pairManager.PairsWithGroups; + if (string.IsNullOrEmpty(_tabMenu.Filter)) return _pairManager.PairsWithGroups; return _pairManager.PairsWithGroups.Where(p => { - if (_characterOrCommentFilter.IsNullOrEmpty()) return true; - return p.Key.UserData.AliasOrUID.Contains(_characterOrCommentFilter, StringComparison.OrdinalIgnoreCase) || - (p.Key.GetNote()?.Contains(_characterOrCommentFilter, StringComparison.OrdinalIgnoreCase) ?? false) || - (p.Key.PlayerName?.Contains(_characterOrCommentFilter, StringComparison.OrdinalIgnoreCase) ?? false); + if (_tabMenu.Filter.IsNullOrEmpty()) return true; + return p.Key.UserData.AliasOrUID.Contains(_tabMenu.Filter, StringComparison.OrdinalIgnoreCase) || + (p.Key.GetNote()?.Contains(_tabMenu.Filter, StringComparison.OrdinalIgnoreCase) ?? false) || + (p.Key.PlayerName?.Contains(_tabMenu.Filter, StringComparison.OrdinalIgnoreCase) ?? false); }).ToDictionary(k => k.Key, k => k.Value); } diff --git a/MareSynchronos/UI/Components/DrawFolderTag.cs b/MareSynchronos/UI/Components/DrawFolderTag.cs index 42b4cec..45db8f2 100644 --- a/MareSynchronos/UI/Components/DrawFolderTag.cs +++ b/MareSynchronos/UI/Components/DrawFolderTag.cs @@ -164,21 +164,25 @@ public class DrawFolderTag : DrawFolderBase private void PauseRemainingPairs(IEnumerable availablePairs) { - foreach (var pairToPause in availablePairs.Where(pair => !pair.UserPair!.OwnPermissions.IsPaused())) + _ = _apiController.SetBulkPermissions(new(availablePairs + .ToDictionary(g => g.UID, g => { - var perm = pairToPause.UserPair!.OwnPermissions; + var perm = g.UserPair.OwnPermissions; perm.SetPaused(paused: true); - _ = _apiController.UserSetPairPermissions(new(new(pairToPause.UID), perm)); - } + return perm; + }, StringComparer.Ordinal), new(StringComparer.Ordinal))) + .ConfigureAwait(false); } private void ResumeAllPairs(IEnumerable availablePairs) { - foreach (var pairToPause in availablePairs) - { - var perm = pairToPause.UserPair!.OwnPermissions; - perm.SetPaused(paused: false); - _ = _apiController.UserSetPairPermissions(new(new(pairToPause.UID), perm)); - } + _ = _apiController.SetBulkPermissions(new(availablePairs + .ToDictionary(g => g.UID, g => + { + var perm = g.UserPair.OwnPermissions; + perm.SetPaused(paused: false); + return perm; + }, StringComparer.Ordinal), new(StringComparer.Ordinal))) + .ConfigureAwait(false); } } \ No newline at end of file diff --git a/MareSynchronos/UI/DataAnalysisUi.cs b/MareSynchronos/UI/DataAnalysisUi.cs index 10cf97a..eb17d8a 100644 --- a/MareSynchronos/UI/DataAnalysisUi.cs +++ b/MareSynchronos/UI/DataAnalysisUi.cs @@ -39,7 +39,6 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase { _hasUpdate = true; }); - Mediator.Subscribe(this, (_) => Toggle()); SizeConstraints = new() { MinimumSize = new() diff --git a/MareSynchronos/UI/JoinSyncshellUI.cs b/MareSynchronos/UI/JoinSyncshellUI.cs index 339baa9..2a4c911 100644 --- a/MareSynchronos/UI/JoinSyncshellUI.cs +++ b/MareSynchronos/UI/JoinSyncshellUI.cs @@ -70,7 +70,7 @@ internal class JoinSyncshellUI : WindowMediatorSubscriberBase ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("Syncshell Password"); ImGui.SameLine(200); - ImGui.InputTextWithHint("##syncshellpw", "Password", ref _syncshellPassword, 20, ImGuiInputTextFlags.Password); + ImGui.InputTextWithHint("##syncshellpw", "Password", ref _syncshellPassword, 50, ImGuiInputTextFlags.Password); using (ImRaii.Disabled(string.IsNullOrEmpty(_desiredSyncshellToJoin) || string.IsNullOrEmpty(_syncshellPassword))) { if (UiSharedService.IconTextButton(Dalamud.Interface.FontAwesomeIcon.Plus, "Join Syncshell")) diff --git a/MareSynchronos/UI/TopTabMenu.cs b/MareSynchronos/UI/TopTabMenu.cs new file mode 100644 index 0000000..d3d7db0 --- /dev/null +++ b/MareSynchronos/UI/TopTabMenu.cs @@ -0,0 +1,557 @@ +using Dalamud.Interface; +using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; +using Dalamud.Utility; +using ImGuiNET; +using MareSynchronos.API.Data.Enum; +using MareSynchronos.API.Data.Extensions; +using MareSynchronos.PlayerData.Pairs; +using MareSynchronos.Services.Mediator; +using MareSynchronos.WebAPI; +using System.Numerics; + +namespace MareSynchronos.UI; + +public class TopTabMenu +{ + private readonly ApiController _apiController; + + private readonly MareMediator _mareMediator; + + private readonly PairManager _pairManager; + + private int _globalControlCountdown = 0; + + private string _pairToAdd = string.Empty; + + private SelectedTab _selectedTab = SelectedTab.None; + + public TopTabMenu(MareMediator mareMediator, ApiController apiController, PairManager pairManager) + { + _mareMediator = mareMediator; + _apiController = apiController; + _pairManager = pairManager; + } + + private enum SelectedTab + { + None, + Individual, + Syncshell, + Filter, + UserConfig + } + public string Filter { get; private set; } = string.Empty; + + public void Draw() + { + var availableWidth = ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X; + var spacing = ImGui.GetStyle().ItemSpacing; + var buttonX = (availableWidth - (spacing.X * 3)) / 4f; + var buttonY = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Pause).Y; + var buttonSize = new Vector2(buttonX, buttonY); + var drawList = ImGui.GetWindowDrawList(); + var underlineColor = ImGui.GetColorU32(ImGuiCol.Separator); + var btncolor = ImRaii.PushColor(ImGuiCol.Button, ImGui.ColorConvertFloat4ToU32(new(0, 0, 0, 0))); + + ImGuiHelpers.ScaledDummy(spacing.Y / 2f); + + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + var x = ImGui.GetCursorScreenPos(); + if (ImGui.Button(FontAwesomeIcon.User.ToIconString(), buttonSize)) + { + _selectedTab = _selectedTab == SelectedTab.Individual ? SelectedTab.None : SelectedTab.Individual; + } + ImGui.SameLine(); + var xAfter = ImGui.GetCursorScreenPos(); + if (_selectedTab == SelectedTab.Individual) + drawList.AddLine(x with { Y = x.Y + buttonSize.Y + spacing.Y }, + xAfter with { Y = xAfter.Y + buttonSize.Y + spacing.Y, X = xAfter.X - spacing.X }, + underlineColor, 2); + } + UiSharedService.AttachToolTip("Individual Pair Menu"); + + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + var x = ImGui.GetCursorScreenPos(); + if (ImGui.Button(FontAwesomeIcon.Users.ToIconString(), buttonSize)) + { + _selectedTab = _selectedTab == SelectedTab.Syncshell ? SelectedTab.None : SelectedTab.Syncshell; + } + ImGui.SameLine(); + var xAfter = ImGui.GetCursorScreenPos(); + if (_selectedTab == SelectedTab.Syncshell) + drawList.AddLine(x with { Y = x.Y + buttonSize.Y + spacing.Y }, + xAfter with { Y = xAfter.Y + buttonSize.Y + spacing.Y, X = xAfter.X - spacing.X }, + underlineColor, 2); + } + UiSharedService.AttachToolTip("Syncshell Menu"); + + ImGui.SameLine(); + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + var x = ImGui.GetCursorScreenPos(); + if (ImGui.Button(FontAwesomeIcon.Filter.ToIconString(), buttonSize)) + { + _selectedTab = _selectedTab == SelectedTab.Filter ? SelectedTab.None : SelectedTab.Filter; + } + + ImGui.SameLine(); + var xAfter = ImGui.GetCursorScreenPos(); + if (_selectedTab == SelectedTab.Filter) + drawList.AddLine(x with { Y = x.Y + buttonSize.Y + spacing.Y }, + xAfter with { Y = xAfter.Y + buttonSize.Y + spacing.Y, X = xAfter.X - spacing.X }, + underlineColor, 2); + } + UiSharedService.AttachToolTip("Filter"); + + ImGui.SameLine(); + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + var x = ImGui.GetCursorScreenPos(); + if (ImGui.Button(FontAwesomeIcon.UserCog.ToIconString(), buttonSize)) + { + _selectedTab = _selectedTab == SelectedTab.UserConfig ? SelectedTab.None : SelectedTab.UserConfig; + } + + ImGui.SameLine(); + var xAfter = ImGui.GetCursorScreenPos(); + if (_selectedTab == SelectedTab.UserConfig) + drawList.AddLine(x with { Y = x.Y + buttonSize.Y + spacing.Y }, + xAfter with { Y = xAfter.Y + buttonSize.Y + spacing.Y, X = xAfter.X - spacing.X }, + underlineColor, 2); + } + UiSharedService.AttachToolTip("Your User Menu"); + + ImGui.NewLine(); + btncolor.Dispose(); + + ImGuiHelpers.ScaledDummy(spacing); + + if (_selectedTab == SelectedTab.Individual) + { + DrawAddPair(availableWidth, spacing.X); + DrawGlobalIndividualButtons(availableWidth, spacing.X); + } + else if (_selectedTab == SelectedTab.Syncshell) + { + DrawSyncshellMenu(availableWidth, spacing.X); + DrawGlobalSyncshellButtons(availableWidth, spacing.X); + } + else if (_selectedTab == SelectedTab.Filter) + { + DrawFilter(availableWidth); + } + else if (_selectedTab == SelectedTab.UserConfig) + { + DrawUserConfig(availableWidth, spacing.X); + } + + if (_selectedTab != SelectedTab.None) ImGuiHelpers.ScaledDummy(3f); + ImGui.Separator(); + ImGuiHelpers.ScaledDummy(1f); + } + + private void DrawAddPair(float availableXWidth, float spacingX) + { + var buttonSize = UiSharedService.GetIconTextButtonSize(FontAwesomeIcon.UserPlus, "Add"); + ImGui.SetNextItemWidth(availableXWidth - buttonSize.X - spacingX); + ImGui.InputTextWithHint("##otheruid", "Other players UID/Alias", ref _pairToAdd, 20); + ImGui.SameLine(); + var alreadyExisting = _pairManager.DirectPairs.Exists(p => string.Equals(p.UserData.UID, _pairToAdd, StringComparison.Ordinal) || string.Equals(p.UserData.Alias, _pairToAdd, StringComparison.Ordinal)); + using (ImRaii.Disabled(alreadyExisting || string.IsNullOrEmpty(_pairToAdd))) + { + if (UiSharedService.IconTextButton(FontAwesomeIcon.UserPlus, "Add")) + { + _ = _apiController.UserAddPair(new(new(_pairToAdd))); + _pairToAdd = string.Empty; + } + } + UiSharedService.AttachToolTip("Pair with " + (_pairToAdd.IsNullOrEmpty() ? "other user" : _pairToAdd)); + } + + private void DrawFilter(float availableWidth) + { + ImGui.SetNextItemWidth(availableWidth); + string filter = Filter; + if (ImGui.InputTextWithHint("##filter", "Filter for UID/notes", ref filter, 255)) + { + Filter = filter; + _mareMediator.Publish(new RefreshUiMessage()); + } + } + + private void DrawGlobalIndividualButtons(float availableXWidth, float spacingX) + { + var buttonX = (availableXWidth - (spacingX * 3)) / 4f; + var buttonY = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Pause).Y; + var buttonSize = new Vector2(buttonX, buttonY); + + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + using var disabled = ImRaii.Disabled(_globalControlCountdown > 0); + + if (ImGui.Button(FontAwesomeIcon.Pause.ToIconString(), buttonSize)) + { + ImGui.OpenPopup("Individual Pause"); + } + } + UiSharedService.AttachToolTip("Globally resume or pause all individual pairs." + UiSharedService.TooltipSeparator + + (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty)); + + ImGui.SameLine(); + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + using var disabled = ImRaii.Disabled(_globalControlCountdown > 0); + + if (ImGui.Button(FontAwesomeIcon.VolumeUp.ToIconString(), buttonSize)) + { + ImGui.OpenPopup("Individual Sounds"); + } + } + UiSharedService.AttachToolTip("Globally enable or disable sound sync with all individual pairs." + + (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty)); + + ImGui.SameLine(); + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + using var disabled = ImRaii.Disabled(_globalControlCountdown > 0); + + if (ImGui.Button(FontAwesomeIcon.Running.ToIconString(), buttonSize)) + { + ImGui.OpenPopup("Individual Animations"); + } + } + UiSharedService.AttachToolTip("Globally enable or disable animation sync with all individual pairs." + UiSharedService.TooltipSeparator + + (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty)); + + ImGui.SameLine(); + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + using var disabled = ImRaii.Disabled(_globalControlCountdown > 0); + + if (ImGui.Button(FontAwesomeIcon.Sun.ToIconString(), buttonSize)) + { + ImGui.OpenPopup("Individual VFX"); + } + } + UiSharedService.AttachToolTip("Globally enable or disable VFX sync with all individual pairs." + UiSharedService.TooltipSeparator + + (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty)); + + + PopupIndividualSetting("Individual Pause", "Unpause all individuals", "Pause all individuals", + FontAwesomeIcon.Play, FontAwesomeIcon.Pause, + (perm) => + { + perm.SetPaused(false); + return perm; + }, + (perm) => + { + perm.SetPaused(true); + return perm; + }); + PopupIndividualSetting("Individual Sounds", "Enable sounds for all individuals", "Disable sounds for all individuals", + FontAwesomeIcon.VolumeUp, FontAwesomeIcon.VolumeMute, + (perm) => + { + perm.SetDisableSounds(false); + return perm; + }, + (perm) => + { + perm.SetDisableSounds(true); + return perm; + }); + PopupIndividualSetting("Individual Animations", "Enable sounds for all individuals", "Disable sounds for all individuals", + FontAwesomeIcon.Running, FontAwesomeIcon.Stop, + (perm) => + { + perm.SetDisableAnimations(false); + return perm; + }, + (perm) => + { + perm.SetDisableAnimations(true); + return perm; + }); + PopupIndividualSetting("Individual VFX", "Enable VFX for all individuals", "Disable VFX for all individuals", + FontAwesomeIcon.Sun, FontAwesomeIcon.Circle, + (perm) => + { + perm.SetDisableVFX(false); + return perm; + }, + (perm) => + { + perm.SetDisableVFX(true); + return perm; + }); + } + + private void DrawGlobalSyncshellButtons(float availableXWidth, float spacingX) + { + var buttonX = (availableXWidth - (spacingX * 4)) / 5f; + var buttonY = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Pause).Y; + var buttonSize = new Vector2(buttonX, buttonY); + + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + using var disabled = ImRaii.Disabled(_globalControlCountdown > 0); + + if (ImGui.Button(FontAwesomeIcon.Pause.ToIconString(), buttonSize)) + { + ImGui.OpenPopup("Syncshell Pause"); + } + } + UiSharedService.AttachToolTip("Globally resume or pause all syncshells." + UiSharedService.TooltipSeparator + + "Note: This will not affect users with preferred permissions in syncshells." + + (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty)); + + ImGui.SameLine(); + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + using var disabled = ImRaii.Disabled(_globalControlCountdown > 0); + + if (ImGui.Button(FontAwesomeIcon.VolumeUp.ToIconString(), buttonSize)) + { + ImGui.OpenPopup("Syncshell Sounds"); + } + } + UiSharedService.AttachToolTip("Globally enable or disable sound sync with all syncshells." + UiSharedService.TooltipSeparator + + "Note: This will not affect users with preferred permissions in syncshells." + + (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty)); + + ImGui.SameLine(); + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + using var disabled = ImRaii.Disabled(_globalControlCountdown > 0); + + if (ImGui.Button(FontAwesomeIcon.Running.ToIconString(), buttonSize)) + { + ImGui.OpenPopup("Syncshell Animations"); + } + } + UiSharedService.AttachToolTip("Globally enable or disable animation sync with all syncshells." + UiSharedService.TooltipSeparator + + "Note: This will not affect users with preferred permissions in syncshells." + + (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty)); + + ImGui.SameLine(); + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + using var disabled = ImRaii.Disabled(_globalControlCountdown > 0); + + if (ImGui.Button(FontAwesomeIcon.Sun.ToIconString(), buttonSize)) + { + ImGui.OpenPopup("Syncshell VFX"); + } + } + UiSharedService.AttachToolTip("Globally enable or disable VFX sync with all syncshells." + UiSharedService.TooltipSeparator + + "Note: This will not affect users with preferred permissions in syncshells." + + (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty)); + + + PopupSyncshellSetting("Syncshell Pause", "Unpause all syncshells", "Pause all syncshells", + FontAwesomeIcon.Play, FontAwesomeIcon.Pause, + (perm) => + { + perm.SetPaused(false); + return perm; + }, + (perm) => + { + perm.SetPaused(true); + return perm; + }); + PopupSyncshellSetting("Syncshell Sounds", "Enable sounds for all syncshells", "Disable sounds for all syncshells", + FontAwesomeIcon.VolumeUp, FontAwesomeIcon.VolumeMute, + (perm) => + { + perm.SetDisableSounds(false); + return perm; + }, + (perm) => + { + perm.SetDisableSounds(true); + return perm; + }); + PopupSyncshellSetting("Syncshell Animations", "Enable sounds for all syncshells", "Disable sounds for all syncshells", + FontAwesomeIcon.Running, FontAwesomeIcon.Stop, + (perm) => + { + perm.SetDisableAnimations(false); + return perm; + }, + (perm) => + { + perm.SetDisableAnimations(true); + return perm; + }); + PopupSyncshellSetting("Syncshell VFX", "Enable VFX for all syncshells", "Disable VFX for all syncshells", + FontAwesomeIcon.Sun, FontAwesomeIcon.Circle, + (perm) => + { + perm.SetDisableVFX(false); + return perm; + }, + (perm) => + { + perm.SetDisableVFX(true); + return perm; + }); + + ImGui.SameLine(); + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + using var disabled = ImRaii.Disabled(_globalControlCountdown > 0); + + if (ImGui.Button(FontAwesomeIcon.Check.ToIconString(), buttonSize)) + { + _ = GlobalControlCountdown(10); + var bulkSyncshells = _pairManager.GroupPairs.Keys.OrderBy(g => g.GroupAliasOrGID, StringComparer.OrdinalIgnoreCase) + .ToDictionary(g => g.Group.GID, g => + { + var perm = g.GroupUserPermissions; + perm.SetDisableSounds(g.GroupPermissions.IsPreferDisableSounds()); + perm.SetDisableAnimations(g.GroupPermissions.IsPreferDisableAnimations()); + perm.SetDisableVFX(g.GroupPermissions.IsPreferDisableVFX()); + return perm; + }, StringComparer.Ordinal); + + _ = _apiController.SetBulkPermissions(new(new(StringComparer.Ordinal), bulkSyncshells)).ConfigureAwait(false); + } + } + UiSharedService.AttachToolTip("Globally align syncshell permissions to suggested syncshell permissions." + UiSharedService.TooltipSeparator + + "Note: This will not affect users with preferred permissions in syncshells." + Environment.NewLine + + "Note: If multiple users share one syncshell the permissions to that user will be set to " + Environment.NewLine + + "the ones of the last applied syncshell in alphabetical order." + + (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty)); + } + + private void DrawSyncshellMenu(float availableWidth, float spacingX) + { + var buttonX = (availableWidth - (spacingX)) / 2f; + + using (ImRaii.Disabled(_pairManager.GroupPairs.Select(k => k.Key).Distinct() + .Count(g => string.Equals(g.OwnerUID, _apiController.UID, StringComparison.Ordinal)) >= _apiController.ServerInfo.MaxGroupsCreatedByUser)) + { + if (UiSharedService.IconTextButton(FontAwesomeIcon.Plus, "Create new Syncshell", buttonX)) + { + _mareMediator.Publish(new UiToggleMessage(typeof(CreateSyncshellUI))); + } + ImGui.SameLine(); + } + + using (ImRaii.Disabled(_pairManager.GroupPairs.Select(k => k.Key).Distinct().Count() >= _apiController.ServerInfo.MaxGroupsJoinedByUser)) + { + if (UiSharedService.IconTextButton(FontAwesomeIcon.Users, "Join existing Syncshell", buttonX)) + { + _mareMediator.Publish(new UiToggleMessage(typeof(JoinSyncshellUI))); + } + } + } + + private void DrawUserConfig(float availableWidth, float spacingX) + { + var buttonX = (availableWidth - spacingX) / 2f; + if (UiSharedService.IconTextButton(FontAwesomeIcon.UserCircle, "Edit Mare Profile", buttonX)) + { + _mareMediator.Publish(new UiToggleMessage(typeof(EditProfileUi))); + } + UiSharedService.AttachToolTip("Edit your Mare Profile"); + ImGui.SameLine(); + if (UiSharedService.IconTextButton(FontAwesomeIcon.PersonCircleQuestion, "Chara Data Analysis", buttonX)) + { + _mareMediator.Publish(new UiToggleMessage(typeof(DataAnalysisUi))); + } + UiSharedService.AttachToolTip("View and analyze your generated character data"); + } + + private async Task GlobalControlCountdown(int countdown) + { +#if DEBUG + return; +#endif + + _globalControlCountdown = countdown; + while (_globalControlCountdown > 0) + { + await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false); + _globalControlCountdown--; + } + } + + private void PopupIndividualSetting(string popupTitle, string enableText, string disableText, + FontAwesomeIcon enableIcon, FontAwesomeIcon disableIcon, + Func actEnable, Func actDisable) + { + if (ImGui.BeginPopup(popupTitle)) + { + if (UiSharedService.IconTextButton(enableIcon, enableText, 0, true)) + { + _ = GlobalControlCountdown(10); + var bulkIndividualPairs = _pairManager.PairsWithGroups.Keys + .Where(g => g.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.Bidirectional) + .ToDictionary(g => g.UserPair.User.UID, g => + { + return actEnable(g.UserPair.OwnPermissions); + }, StringComparer.Ordinal); + + _ = _apiController.SetBulkPermissions(new(bulkIndividualPairs, new(StringComparer.Ordinal))).ConfigureAwait(false); + ImGui.CloseCurrentPopup(); + } + + if (UiSharedService.IconTextButton(disableIcon, disableText, 0, true)) + { + _ = GlobalControlCountdown(10); + var bulkIndividualPairs = _pairManager.PairsWithGroups.Keys + .Where(g => g.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.Bidirectional) + .ToDictionary(g => g.UserPair.User.UID, g => + { + return actDisable(g.UserPair.OwnPermissions); + }, StringComparer.Ordinal); + + _ = _apiController.SetBulkPermissions(new(bulkIndividualPairs, new(StringComparer.Ordinal))).ConfigureAwait(false); + ImGui.CloseCurrentPopup(); + } + ImGui.EndPopup(); + } + } + private void PopupSyncshellSetting(string popupTitle, string enableText, string disableText, + FontAwesomeIcon enableIcon, FontAwesomeIcon disableIcon, + Func actEnable, + Func actDisable) + { + if (ImGui.BeginPopup(popupTitle)) + { + + if (UiSharedService.IconTextButton(enableIcon, enableText, 0, true)) + { + _ = GlobalControlCountdown(10); + var bulkSyncshells = _pairManager.GroupPairs.Keys + .ToDictionary(g => g.Group.GID, g => + { + return actEnable(g.GroupUserPermissions); + }, StringComparer.Ordinal); + + _ = _apiController.SetBulkPermissions(new(new(StringComparer.Ordinal), bulkSyncshells)).ConfigureAwait(false); + ImGui.CloseCurrentPopup(); + } + + if (UiSharedService.IconTextButton(disableIcon, disableText, 0, true)) + { + _ = GlobalControlCountdown(10); + var bulkSyncshells = _pairManager.GroupPairs.Keys + .ToDictionary(g => g.Group.GID, g => + { + return actDisable(g.GroupUserPermissions); + }, StringComparer.Ordinal); + + _ = _apiController.SetBulkPermissions(new(new(StringComparer.Ordinal), bulkSyncshells)).ConfigureAwait(false); + ImGui.CloseCurrentPopup(); + } + ImGui.EndPopup(); + } + } +} diff --git a/MareSynchronos/UI/UISharedService.cs b/MareSynchronos/UI/UISharedService.cs index 341e7b8..a75ef59 100644 --- a/MareSynchronos/UI/UISharedService.cs +++ b/MareSynchronos/UI/UISharedService.cs @@ -133,7 +133,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase public static void AttachToolTip(string text) { - if (ImGui.IsItemHovered()) + if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) { ImGui.BeginTooltip(); if (text.Contains(TooltipSeparator, StringComparison.Ordinal)) @@ -349,6 +349,31 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase return ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X; } + public static Vector2 GetIconTextButtonSize(FontAwesomeIcon icon, string text, float? width = null, bool isInPopup = false) + { + var iconSize = GetIconSize(icon); + var textSize = ImGui.CalcTextSize(text); + var padding = ImGui.GetStyle().FramePadding; + var spacing = ImGui.GetStyle().ItemSpacing; + + var buttonSizeY = textSize.Y + padding.Y * 2; + var iconExtraSpacing = isInPopup ? padding.X * 2 : 0; + + var iconXoffset = iconSize.X <= iconSize.Y ? (iconSize.Y - iconSize.X) / 2f : 0; + var iconScaling = iconSize.X > iconSize.Y ? 1 / (iconSize.X / iconSize.Y) : 1; + + if (width == null || width <= 0) + { + var buttonSizeX = (iconScaling == 1 ? iconSize.Y : (iconSize.X * iconScaling)) + + textSize.X + padding.X * 2 + spacing.X + (iconXoffset * 2); + return new Vector2(buttonSizeX + iconExtraSpacing, buttonSizeY); + } + else + { + return new Vector2(width.Value, buttonSizeY); + } + } + public static bool IconTextButton(FontAwesomeIcon icon, string text, float? width = null, bool isInPopup = false) { var wasClicked = false; diff --git a/MareSynchronos/WebAPI/SignalR/ApIController.Functions.Users.cs b/MareSynchronos/WebAPI/SignalR/ApIController.Functions.Users.cs index 1ae9602..a552834 100644 --- a/MareSynchronos/WebAPI/SignalR/ApIController.Functions.Users.cs +++ b/MareSynchronos/WebAPI/SignalR/ApIController.Functions.Users.cs @@ -69,6 +69,20 @@ public partial class ApiController } } + public async Task SetBulkPermissions(BulkPermissionsDto dto) + { + CheckConnection(); + + try + { + await _mareHub!.InvokeAsync(nameof(SetBulkPermissions), dto).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.LogWarning(ex, "Failed to set permissions"); + } + } + public async Task UserRemovePair(UserDto userDto) { if (!IsConnected) return; @@ -83,7 +97,10 @@ public partial class ApiController public async Task UserSetPairPermissions(UserPermissionsDto userPermissions) { - await _mareHub!.SendAsync(nameof(UserSetPairPermissions), userPermissions).ConfigureAwait(false); + await SetBulkPermissions(new(new(StringComparer.Ordinal) + { + { userPermissions.User.UID, userPermissions.Permissions } + }, new(StringComparer.Ordinal))).ConfigureAwait(false); } public async Task UserSetProfile(UserProfileDto userDescription) diff --git a/MareSynchronos/WebAPI/SignalR/ApiController.Functions.Groups.cs b/MareSynchronos/WebAPI/SignalR/ApiController.Functions.Groups.cs index 69ea973..5c3cb31 100644 --- a/MareSynchronos/WebAPI/SignalR/ApiController.Functions.Groups.cs +++ b/MareSynchronos/WebAPI/SignalR/ApiController.Functions.Groups.cs @@ -21,7 +21,10 @@ public partial class ApiController public async Task GroupChangeIndividualPermissionState(GroupPairUserPermissionDto dto) { CheckConnection(); - await _mareHub!.SendAsync(nameof(GroupChangeIndividualPermissionState), dto).ConfigureAwait(false); + await SetBulkPermissions(new(new(StringComparer.Ordinal), + new(StringComparer.Ordinal) { + { dto.Group.GID, dto.GroupPairPermissions } + })).ConfigureAwait(false); } public async Task GroupChangeOwnership(GroupPairDto groupPair) From c1fa46f44413c4c31beb7ae6c5e414fa518d7925 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Sat, 21 Oct 2023 18:29:33 +0200 Subject: [PATCH 09/56] adjust api to latest --- MareAPI | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MareAPI b/MareAPI index d04781f..edaa0dd 160000 --- a/MareAPI +++ b/MareAPI @@ -1 +1 @@ -Subproject commit d04781fddf11e3cece03d5bdd7acf249c453664c +Subproject commit edaa0ddb5123b2eb78d694b844bb7ad6cc13192d From a2584f75dc10964cc7e92c0951b29ce4b8f60b5d Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Sat, 21 Oct 2023 18:32:17 +0200 Subject: [PATCH 10/56] add ordering --- MareSynchronos/UI/TopTabMenu.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MareSynchronos/UI/TopTabMenu.cs b/MareSynchronos/UI/TopTabMenu.cs index d3d7db0..9e73b15 100644 --- a/MareSynchronos/UI/TopTabMenu.cs +++ b/MareSynchronos/UI/TopTabMenu.cs @@ -492,7 +492,7 @@ public class TopTabMenu { _ = GlobalControlCountdown(10); var bulkIndividualPairs = _pairManager.PairsWithGroups.Keys - .Where(g => g.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.Bidirectional) + .Where(g => g.IndividualPairStatus == IndividualPairStatus.Bidirectional) .ToDictionary(g => g.UserPair.User.UID, g => { return actEnable(g.UserPair.OwnPermissions); @@ -530,6 +530,7 @@ public class TopTabMenu { _ = GlobalControlCountdown(10); var bulkSyncshells = _pairManager.GroupPairs.Keys + .OrderBy(u => u.GroupAliasOrGID, StringComparer.OrdinalIgnoreCase) .ToDictionary(g => g.Group.GID, g => { return actEnable(g.GroupUserPermissions); @@ -543,6 +544,7 @@ public class TopTabMenu { _ = GlobalControlCountdown(10); var bulkSyncshells = _pairManager.GroupPairs.Keys + .OrderBy(u => u.GroupAliasOrGID, StringComparer.OrdinalIgnoreCase) .ToDictionary(g => g.Group.GID, g => { return actDisable(g.GroupUserPermissions); From a663c26df0bcf160ffa07a03651824c95fa99de1 Mon Sep 17 00:00:00 2001 From: Cara Date: Sun, 22 Oct 2023 03:03:08 +1030 Subject: [PATCH 11/56] Some display options for DTR tooltip (#66) --- .../Configurations/MareConfig.cs | 2 ++ MareSynchronos/UI/DtrEntry.cs | 17 ++++++++++++++--- MareSynchronos/UI/SettingsUi.cs | 18 ++++++++++++++++++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/MareSynchronos/MareConfiguration/Configurations/MareConfig.cs b/MareSynchronos/MareConfiguration/Configurations/MareConfig.cs index 168ad78..2c5ab9a 100644 --- a/MareSynchronos/MareConfiguration/Configurations/MareConfig.cs +++ b/MareSynchronos/MareConfiguration/Configurations/MareConfig.cs @@ -10,6 +10,8 @@ public class MareConfig : IMareConfiguration public string CacheFolder { get; set; } = string.Empty; public bool DisableOptionalPluginWarnings { get; set; } = false; public bool EnableDtrEntry { get; set; } = false; + public bool ShowUidInDtrTooltip { get; set; } = true; + public bool PreferNoteInDtrTooltip { get; set; } = false; public bool EnableRightClickMenus { get; set; } = true; public NotificationLocation ErrorNotification { get; set; } = NotificationLocation.Both; public string ExportFolder { get; set; } = string.Empty; diff --git a/MareSynchronos/UI/DtrEntry.cs b/MareSynchronos/UI/DtrEntry.cs index 4cb46bf..18ca0ba 100644 --- a/MareSynchronos/UI/DtrEntry.cs +++ b/MareSynchronos/UI/DtrEntry.cs @@ -122,9 +122,20 @@ public sealed class DtrEntry : IDisposable, IHostedService text = $"\uE044 {pairCount}"; if (pairCount > 0) { - var visiblePairs = _pairManager.GetOnlineUserPairs() - .Where(x => x.IsVisible) - .Select(x => string.Format("{0} ({1})", x.PlayerName, x.UserData.AliasOrUID)); + IEnumerable visiblePairs; + if (_configService.Current.ShowUidInDtrTooltip) + { + visiblePairs = _pairManager.GetOnlineUserPairs() + .Where(x => x.IsVisible) + .Select(x => string.Format("{0} ({1})", _configService.Current.PreferNoteInDtrTooltip ? x.GetNote() ?? x.PlayerName : x.PlayerName, x.UserData.AliasOrUID )); + } + else + { + visiblePairs = _pairManager.GetOnlineUserPairs() + .Where(x => x.IsVisible) + .Select(x => string.Format("{0}", _configService.Current.PreferNoteInDtrTooltip ? x.GetNote() ?? x.PlayerName : x.PlayerName)); + } + tooltip = $"Mare Synchronos: Connected{Environment.NewLine}----------{Environment.NewLine}{string.Join(Environment.NewLine, visiblePairs)}"; } else diff --git a/MareSynchronos/UI/SettingsUi.cs b/MareSynchronos/UI/SettingsUi.cs index 9453d50..d45fbcc 100644 --- a/MareSynchronos/UI/SettingsUi.cs +++ b/MareSynchronos/UI/SettingsUi.cs @@ -559,6 +559,8 @@ public class SettingsUi : WindowMediatorSubscriberBase var profileOnRight = _configService.Current.ProfilePopoutRight; var enableRightClickMenu = _configService.Current.EnableRightClickMenus; var enableDtrEntry = _configService.Current.EnableDtrEntry; + var showUidInDtrTooltip = _configService.Current.ShowUidInDtrTooltip; + var preferNoteInDtrTooltip = _configService.Current.PreferNoteInDtrTooltip; var preferNotesInsteadOfName = _configService.Current.PreferNotesOverNamesForVisible; var groupUpSyncshells = _configService.Current.GroupUpSyncshells; var groupInVisible = _configService.Current.ShowSyncshellUsersInVisible; @@ -578,6 +580,22 @@ public class SettingsUi : WindowMediatorSubscriberBase } UiSharedService.DrawHelpText("This will add Mare connection status and visible pair count in the Server Info Bar.\nYou can further configure this through your Dalamud Settings."); + using (ImRaii.Disabled(!enableDtrEntry)) + { + using var indent = ImRaii.PushIndent(); + if (ImGui.Checkbox("Show visible character's UID in tooltip", ref showUidInDtrTooltip)) + { + _configService.Current.ShowUidInDtrTooltip = showUidInDtrTooltip; + _configService.Save(); + } + + if (ImGui.Checkbox("Prefer notes over player names in tooltip", ref preferNoteInDtrTooltip)) + { + _configService.Current.PreferNoteInDtrTooltip = preferNoteInDtrTooltip; + _configService.Save(); + } + } + if (ImGui.Checkbox("Show separate Visible group", ref showVisibleSeparate)) { _configService.Current.ShowVisibleUsersSeparately = showVisibleSeparate; From fec63361b661dd672f63fbcf3150b8cb9ca25eb9 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Sat, 21 Oct 2023 18:33:45 +0200 Subject: [PATCH 12/56] blah --- MareSynchronos/UI/TopTabMenu.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MareSynchronos/UI/TopTabMenu.cs b/MareSynchronos/UI/TopTabMenu.cs index 9e73b15..97fac2d 100644 --- a/MareSynchronos/UI/TopTabMenu.cs +++ b/MareSynchronos/UI/TopTabMenu.cs @@ -506,7 +506,7 @@ public class TopTabMenu { _ = GlobalControlCountdown(10); var bulkIndividualPairs = _pairManager.PairsWithGroups.Keys - .Where(g => g.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.Bidirectional) + .Where(g => g.IndividualPairStatus == IndividualPairStatus.Bidirectional) .ToDictionary(g => g.UserPair.User.UID, g => { return actDisable(g.UserPair.OwnPermissions); From a51ce22d28aa8da557cf81205d67551534c394e1 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Sun, 22 Oct 2023 17:12:44 +0200 Subject: [PATCH 13/56] fix offset for transfer bar at the bottom, use async collections, clear filter on tab change + add button to clear, require ctrl for align syncshells --- MareSynchronos/Interop/IpcManager.cs | 25 +++++----- MareSynchronos/MareSynchronos.csproj | 2 +- MareSynchronos/UI/CompactUI.cs | 3 +- MareSynchronos/UI/SettingsUi.cs | 14 +++--- MareSynchronos/UI/TopTabMenu.cs | 73 ++++++++++++++++++++-------- 5 files changed, 76 insertions(+), 41 deletions(-) diff --git a/MareSynchronos/Interop/IpcManager.cs b/MareSynchronos/Interop/IpcManager.cs index 64dd28c..6ecd660 100644 --- a/MareSynchronos/Interop/IpcManager.cs +++ b/MareSynchronos/Interop/IpcManager.cs @@ -22,6 +22,7 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase private readonly ICallGateSubscriber _customizePlusOnScaleUpdate; private readonly ICallGateSubscriber _customizePlusRevertCharacter; private readonly ICallGateSubscriber _customizePlusSetBodyScaleToCharacter; + private readonly DalamudPluginInterface _pi; private readonly DalamudUtilService _dalamudUtil; private readonly ICallGateSubscriber<(int, int)> _glamourerApiVersions; private readonly ICallGateSubscriber? _glamourerApplyAll; @@ -47,7 +48,6 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase private readonly ICallGateSubscriber _palettePlusRemoveCharaPalette; private readonly ICallGateSubscriber _palettePlusSetCharaPalette; private readonly FuncSubscriber, string, int, PenumbraApiEc> _penumbraAddTemporaryMod; - private readonly FuncSubscriber<(int, int)> _penumbraApiVersion; private readonly FuncSubscriber _penumbraAssignTemporaryCollection; private readonly FuncSubscriber _penumbraConvertTextureFile; private readonly FuncSubscriber _penumbraCreateNamedTemporaryCollection; @@ -64,7 +64,7 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase private readonly FuncSubscriber _penumbraRemoveTemporaryCollection; private readonly FuncSubscriber _penumbraRemoveTemporaryMod; private readonly FuncSubscriber _penumbraResolveModDir; - private readonly FuncSubscriber _penumbraResolvePaths; + private readonly FuncSubscriber> _penumbraResolvePaths; private readonly ParamsFuncSubscriber?[]> _penumbraResourcePaths; private readonly SemaphoreSlim _redrawSemaphore = new(2); private readonly uint LockCode = 0x6D617265; @@ -80,6 +80,7 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase public IpcManager(ILogger logger, DalamudPluginInterface pi, DalamudUtilService dalamudUtil, MareMediator mediator) : base(logger, mediator) { + _pi = pi; _dalamudUtil = dalamudUtil; _penumbraInit = Penumbra.Api.Ipc.Initialized.Subscriber(pi, PenumbraInit); @@ -87,7 +88,6 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase _penumbraResolveModDir = Penumbra.Api.Ipc.GetModDirectory.Subscriber(pi); _penumbraRedraw = Penumbra.Api.Ipc.RedrawObjectByName.Subscriber(pi); _penumbraRedrawObject = Penumbra.Api.Ipc.RedrawObject.Subscriber(pi); - _penumbraApiVersion = Penumbra.Api.Ipc.ApiVersions.Subscriber(pi); _penumbraObjectIsRedrawn = Penumbra.Api.Ipc.GameObjectRedrawn.Subscriber(pi, RedrawEvent); _penumbraGetMetaManipulations = Penumbra.Api.Ipc.GetPlayerMetaManipulations.Subscriber(pi); _penumbraRemoveTemporaryMod = Penumbra.Api.Ipc.RemoveTemporaryMod.Subscriber(pi); @@ -95,7 +95,7 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase _penumbraCreateNamedTemporaryCollection = Penumbra.Api.Ipc.CreateNamedTemporaryCollection.Subscriber(pi); _penumbraRemoveTemporaryCollection = Penumbra.Api.Ipc.RemoveTemporaryCollectionByName.Subscriber(pi); _penumbraAssignTemporaryCollection = Penumbra.Api.Ipc.AssignTemporaryCollection.Subscriber(pi); - _penumbraResolvePaths = Penumbra.Api.Ipc.ResolvePlayerPaths.Subscriber(pi); + _penumbraResolvePaths = Penumbra.Api.Ipc.ResolvePlayerPathsAsync.Subscriber(pi); _penumbraEnabled = Penumbra.Api.Ipc.GetEnabledState.Subscriber(pi); _penumbraModSettingChanged = Penumbra.Api.Ipc.ModSettingChanged.Subscriber(pi, (change, arg1, arg, b) => { @@ -569,7 +569,7 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase public async Task<(string[] forward, string[][] reverse)> PenumbraResolvePathsAsync(string[] forward, string[] reverse) { - return await _dalamudUtil.RunOnFrameworkThread(() => _penumbraResolvePaths.Invoke(forward, reverse)).ConfigureAwait(false); + return await _penumbraResolvePaths.Invoke(forward, reverse).ConfigureAwait(false); } public async Task PenumbraSetManipulationDataAsync(ILogger logger, Guid applicationId, string collName, string manipulationData) @@ -700,20 +700,23 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase private bool CheckPenumbraApiInternal() { - bool apiAvailable = false; + bool penumbraAvailable = false; try { - apiAvailable = _penumbraApiVersion.Invoke() is { Item1: 4, Item2: >= 21 } && _penumbraEnabled.Invoke(); - _shownPenumbraUnavailable = _shownPenumbraUnavailable && !apiAvailable; - return apiAvailable; + penumbraAvailable = (_pi.InstalledPlugins + .FirstOrDefault(p => string.Equals(p.InternalName, "Penumbra", StringComparison.OrdinalIgnoreCase)) + ?.Version ?? new Version(0, 0, 0, 0)) >= new Version(0, 8, 1, 6); + penumbraAvailable &= _penumbraEnabled.Invoke(); + _shownPenumbraUnavailable = _shownPenumbraUnavailable && !penumbraAvailable; + return penumbraAvailable; } catch { - return apiAvailable; + return penumbraAvailable; } finally { - if (!apiAvailable && !_shownPenumbraUnavailable) + if (!penumbraAvailable && !_shownPenumbraUnavailable) { _shownPenumbraUnavailable = true; Mediator.Publish(new NotificationMessage("Penumbra inactive", "Your Penumbra installation is not active or out of date. Update Penumbra and/or the Enable Mods setting in Penumbra to continue to use Mare.", NotificationType.Error)); diff --git a/MareSynchronos/MareSynchronos.csproj b/MareSynchronos/MareSynchronos.csproj index a378a2d..6d2c8e5 100644 --- a/MareSynchronos/MareSynchronos.csproj +++ b/MareSynchronos/MareSynchronos.csproj @@ -39,7 +39,7 @@ - + diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs index e6715f1..4e8f0cb 100644 --- a/MareSynchronos/UI/CompactUI.cs +++ b/MareSynchronos/UI/CompactUI.cs @@ -183,7 +183,8 @@ public class CompactUi : WindowMediatorSubscriberBase { var ySize = TransferPartHeight == 0 ? 1 - : (ImGui.GetWindowContentRegionMax().Y - ImGui.GetWindowContentRegionMin().Y) - TransferPartHeight - ImGui.GetCursorPosY(); + : (ImGui.GetWindowContentRegionMax().Y - ImGui.GetWindowContentRegionMin().Y + + ImGui.GetTextLineHeight() - ImGui.GetStyle().WindowBorderSize) - TransferPartHeight - ImGui.GetCursorPosY(); ImGui.BeginChild("list", new Vector2(WindowContentWidth, ySize), border: false); diff --git a/MareSynchronos/UI/SettingsUi.cs b/MareSynchronos/UI/SettingsUi.cs index d45fbcc..3f68028 100644 --- a/MareSynchronos/UI/SettingsUi.cs +++ b/MareSynchronos/UI/SettingsUi.cs @@ -678,13 +678,6 @@ public class SettingsUi : WindowMediatorSubscriberBase Mediator.Publish(new CompactUiChange(Vector2.Zero, Vector2.Zero)); } UiSharedService.DrawHelpText("Will show profiles on the right side of the main UI"); - if (ImGui.Checkbox("Show profiles marked as NSFW", ref showNsfwProfiles)) - { - Mediator.Publish(new ClearProfileDataMessage()); - _configService.Current.ProfilesAllowNsfw = showNsfwProfiles; - _configService.Save(); - } - UiSharedService.DrawHelpText("Will show profiles that have the NSFW tag enabled"); if (ImGui.SliderFloat("Hover Delay", ref profileDelay, 1, 10)) { _configService.Current.ProfileDelay = profileDelay; @@ -693,6 +686,13 @@ public class SettingsUi : WindowMediatorSubscriberBase UiSharedService.DrawHelpText("Delay until the profile should be displayed"); if (!showProfiles) ImGui.EndDisabled(); ImGui.Unindent(); + if (ImGui.Checkbox("Show profiles marked as NSFW", ref showNsfwProfiles)) + { + Mediator.Publish(new ClearProfileDataMessage()); + _configService.Current.ProfilesAllowNsfw = showNsfwProfiles; + _configService.Save(); + } + UiSharedService.DrawHelpText("Will show profiles that have the NSFW tag enabled"); ImGui.Separator(); diff --git a/MareSynchronos/UI/TopTabMenu.cs b/MareSynchronos/UI/TopTabMenu.cs index 97fac2d..8042de6 100644 --- a/MareSynchronos/UI/TopTabMenu.cs +++ b/MareSynchronos/UI/TopTabMenu.cs @@ -20,12 +20,12 @@ public class TopTabMenu private readonly PairManager _pairManager; + private string _filter = string.Empty; private int _globalControlCountdown = 0; private string _pairToAdd = string.Empty; private SelectedTab _selectedTab = SelectedTab.None; - public TopTabMenu(MareMediator mareMediator, ApiController apiController, PairManager pairManager) { _mareMediator = mareMediator; @@ -41,8 +41,32 @@ public class TopTabMenu Filter, UserConfig } - public string Filter { get; private set; } = string.Empty; + public string Filter + { + get => _filter; + private set + { + if (!string.Equals(_filter, value, StringComparison.OrdinalIgnoreCase)) + { + _mareMediator.Publish(new RefreshUiMessage()); + } + + _filter = value; + } + } + private SelectedTab TabSelection + { + get => _selectedTab; set + { + if (_selectedTab == SelectedTab.Filter && value != SelectedTab.Filter) + { + Filter = string.Empty; + } + + _selectedTab = value; + } + } public void Draw() { var availableWidth = ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X; @@ -61,11 +85,11 @@ public class TopTabMenu var x = ImGui.GetCursorScreenPos(); if (ImGui.Button(FontAwesomeIcon.User.ToIconString(), buttonSize)) { - _selectedTab = _selectedTab == SelectedTab.Individual ? SelectedTab.None : SelectedTab.Individual; + TabSelection = TabSelection == SelectedTab.Individual ? SelectedTab.None : SelectedTab.Individual; } ImGui.SameLine(); var xAfter = ImGui.GetCursorScreenPos(); - if (_selectedTab == SelectedTab.Individual) + if (TabSelection == SelectedTab.Individual) drawList.AddLine(x with { Y = x.Y + buttonSize.Y + spacing.Y }, xAfter with { Y = xAfter.Y + buttonSize.Y + spacing.Y, X = xAfter.X - spacing.X }, underlineColor, 2); @@ -77,11 +101,11 @@ public class TopTabMenu var x = ImGui.GetCursorScreenPos(); if (ImGui.Button(FontAwesomeIcon.Users.ToIconString(), buttonSize)) { - _selectedTab = _selectedTab == SelectedTab.Syncshell ? SelectedTab.None : SelectedTab.Syncshell; + TabSelection = TabSelection == SelectedTab.Syncshell ? SelectedTab.None : SelectedTab.Syncshell; } ImGui.SameLine(); var xAfter = ImGui.GetCursorScreenPos(); - if (_selectedTab == SelectedTab.Syncshell) + if (TabSelection == SelectedTab.Syncshell) drawList.AddLine(x with { Y = x.Y + buttonSize.Y + spacing.Y }, xAfter with { Y = xAfter.Y + buttonSize.Y + spacing.Y, X = xAfter.X - spacing.X }, underlineColor, 2); @@ -94,12 +118,12 @@ public class TopTabMenu var x = ImGui.GetCursorScreenPos(); if (ImGui.Button(FontAwesomeIcon.Filter.ToIconString(), buttonSize)) { - _selectedTab = _selectedTab == SelectedTab.Filter ? SelectedTab.None : SelectedTab.Filter; + TabSelection = TabSelection == SelectedTab.Filter ? SelectedTab.None : SelectedTab.Filter; } ImGui.SameLine(); var xAfter = ImGui.GetCursorScreenPos(); - if (_selectedTab == SelectedTab.Filter) + if (TabSelection == SelectedTab.Filter) drawList.AddLine(x with { Y = x.Y + buttonSize.Y + spacing.Y }, xAfter with { Y = xAfter.Y + buttonSize.Y + spacing.Y, X = xAfter.X - spacing.X }, underlineColor, 2); @@ -112,12 +136,12 @@ public class TopTabMenu var x = ImGui.GetCursorScreenPos(); if (ImGui.Button(FontAwesomeIcon.UserCog.ToIconString(), buttonSize)) { - _selectedTab = _selectedTab == SelectedTab.UserConfig ? SelectedTab.None : SelectedTab.UserConfig; + TabSelection = TabSelection == SelectedTab.UserConfig ? SelectedTab.None : SelectedTab.UserConfig; } ImGui.SameLine(); var xAfter = ImGui.GetCursorScreenPos(); - if (_selectedTab == SelectedTab.UserConfig) + if (TabSelection == SelectedTab.UserConfig) drawList.AddLine(x with { Y = x.Y + buttonSize.Y + spacing.Y }, xAfter with { Y = xAfter.Y + buttonSize.Y + spacing.Y, X = xAfter.X - spacing.X }, underlineColor, 2); @@ -129,26 +153,26 @@ public class TopTabMenu ImGuiHelpers.ScaledDummy(spacing); - if (_selectedTab == SelectedTab.Individual) + if (TabSelection == SelectedTab.Individual) { DrawAddPair(availableWidth, spacing.X); DrawGlobalIndividualButtons(availableWidth, spacing.X); } - else if (_selectedTab == SelectedTab.Syncshell) + else if (TabSelection == SelectedTab.Syncshell) { DrawSyncshellMenu(availableWidth, spacing.X); DrawGlobalSyncshellButtons(availableWidth, spacing.X); } - else if (_selectedTab == SelectedTab.Filter) + else if (TabSelection == SelectedTab.Filter) { - DrawFilter(availableWidth); + DrawFilter(availableWidth, spacing.X); } - else if (_selectedTab == SelectedTab.UserConfig) + else if (TabSelection == SelectedTab.UserConfig) { DrawUserConfig(availableWidth, spacing.X); } - if (_selectedTab != SelectedTab.None) ImGuiHelpers.ScaledDummy(3f); + if (TabSelection != SelectedTab.None) ImGuiHelpers.ScaledDummy(3f); ImGui.Separator(); ImGuiHelpers.ScaledDummy(1f); } @@ -171,14 +195,20 @@ public class TopTabMenu UiSharedService.AttachToolTip("Pair with " + (_pairToAdd.IsNullOrEmpty() ? "other user" : _pairToAdd)); } - private void DrawFilter(float availableWidth) + private void DrawFilter(float availableWidth, float spacingX) { - ImGui.SetNextItemWidth(availableWidth); + var buttonSize = UiSharedService.GetIconTextButtonSize(FontAwesomeIcon.Ban, "Clear"); + ImGui.SetNextItemWidth(availableWidth - buttonSize.X - spacingX); string filter = Filter; if (ImGui.InputTextWithHint("##filter", "Filter for UID/notes", ref filter, 255)) { Filter = filter; - _mareMediator.Publish(new RefreshUiMessage()); + } + ImGui.SameLine(); + using var disabled = ImRaii.Disabled(string.IsNullOrEmpty(Filter)); + if (UiSharedService.IconTextButton(FontAwesomeIcon.Ban, "Clear")) + { + Filter = string.Empty; } } @@ -404,7 +434,7 @@ public class TopTabMenu ImGui.SameLine(); using (ImRaii.PushFont(UiBuilder.IconFont)) { - using var disabled = ImRaii.Disabled(_globalControlCountdown > 0); + using var disabled = ImRaii.Disabled(_globalControlCountdown > 0 || !UiSharedService.CtrlPressed()); if (ImGui.Button(FontAwesomeIcon.Check.ToIconString(), buttonSize)) { @@ -425,7 +455,8 @@ public class TopTabMenu UiSharedService.AttachToolTip("Globally align syncshell permissions to suggested syncshell permissions." + UiSharedService.TooltipSeparator + "Note: This will not affect users with preferred permissions in syncshells." + Environment.NewLine + "Note: If multiple users share one syncshell the permissions to that user will be set to " + Environment.NewLine - + "the ones of the last applied syncshell in alphabetical order." + + "the ones of the last applied syncshell in alphabetical order." + UiSharedService.TooltipSeparator + + "Hold CTRL to enable this button" + (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty)); } From 505d50a5472556f6c6361e5c20069c5200676ff2 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Mon, 23 Oct 2023 14:06:25 +0200 Subject: [PATCH 14/56] idk some fixes I guess --- MareSynchronos/FileCache/FileCacheManager.cs | 2 +- MareSynchronos/UI/TopTabMenu.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MareSynchronos/FileCache/FileCacheManager.cs b/MareSynchronos/FileCache/FileCacheManager.cs index 9f5aed2..6d56416 100644 --- a/MareSynchronos/FileCache/FileCacheManager.cs +++ b/MareSynchronos/FileCache/FileCacheManager.cs @@ -336,7 +336,7 @@ public sealed class FileCacheManager : IDisposable private void AddHashedFile(FileCacheEntity fileCache) { - if (!_fileCaches.TryGetValue(fileCache.Hash, out var entries)) + if (!_fileCaches.TryGetValue(fileCache.Hash, out var entries) || entries is null) { _fileCaches[fileCache.Hash] = entries = []; } diff --git a/MareSynchronos/UI/TopTabMenu.cs b/MareSynchronos/UI/TopTabMenu.cs index 8042de6..364bffe 100644 --- a/MareSynchronos/UI/TopTabMenu.cs +++ b/MareSynchronos/UI/TopTabMenu.cs @@ -406,7 +406,7 @@ public class TopTabMenu perm.SetDisableSounds(true); return perm; }); - PopupSyncshellSetting("Syncshell Animations", "Enable sounds for all syncshells", "Disable sounds for all syncshells", + PopupSyncshellSetting("Syncshell Animations", "Enable animations for all syncshells", "Disable animations for all syncshells", FontAwesomeIcon.Running, FontAwesomeIcon.Stop, (perm) => { From 6ed4e06eb1c521af74b535b178ab50e204e941ad Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Mon, 23 Oct 2023 19:13:23 +0200 Subject: [PATCH 15/56] fix some shit maybe --- MareSynchronos/Services/Mediator/Messages.cs | 6 +++--- MareSynchronos/WebAPI/SignalR/ApiController.cs | 13 ++++++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/MareSynchronos/Services/Mediator/Messages.cs b/MareSynchronos/Services/Mediator/Messages.cs index 05d0a4a..4d7b802 100644 --- a/MareSynchronos/Services/Mediator/Messages.cs +++ b/MareSynchronos/Services/Mediator/Messages.cs @@ -54,9 +54,9 @@ public record CharacterDataCreatedMessage(CharacterData CharacterData) : SameThr public record CharacterDataAnalyzedMessage : MessageBase; public record PenumbraStartRedrawMessage(IntPtr Address) : MessageBase; public record PenumbraEndRedrawMessage(IntPtr Address) : MessageBase; -public record HubReconnectingMessage(Exception? Exception) : MessageBase; -public record HubReconnectedMessage(string? Arg) : MessageBase; -public record HubClosedMessage(Exception? Exception) : MessageBase; +public record HubReconnectingMessage(Exception? Exception) : SameThreadMessage; +public record HubReconnectedMessage(string? Arg) : SameThreadMessage; +public record HubClosedMessage(Exception? Exception) : SameThreadMessage; public record DownloadReadyMessage(Guid RequestId) : MessageBase; public record DownloadStartedMessage(GameObjectHandler DownloadId, Dictionary DownloadStatus) : MessageBase; public record DownloadFinishedMessage(GameObjectHandler DownloadId) : MessageBase; diff --git a/MareSynchronos/WebAPI/SignalR/ApiController.cs b/MareSynchronos/WebAPI/SignalR/ApiController.cs index 2e08768..1b8e139 100644 --- a/MareSynchronos/WebAPI/SignalR/ApiController.cs +++ b/MareSynchronos/WebAPI/SignalR/ApiController.cs @@ -49,7 +49,7 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM Mediator.Subscribe(this, (_) => DalamudUtilOnLogIn()); Mediator.Subscribe(this, (_) => DalamudUtilOnLogOut()); Mediator.Subscribe(this, (msg) => MareHubOnClosed(msg.Exception)); - Mediator.Subscribe(this, (msg) => _ = Task.Run(MareHubOnReconnected)); + Mediator.Subscribe(this, async (msg) => await MareHubOnReconnected().ConfigureAwait(false)); Mediator.Subscribe(this, (msg) => MareHubOnReconnecting(msg.Exception)); Mediator.Subscribe(this, (msg) => _ = CyclePause(msg.UserData)); @@ -187,7 +187,7 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM $"Your client is outdated ({currentClientVer.Major}.{currentClientVer.Minor}.{currentClientVer.Build}), current is: " + $"{_connectionDto.CurrentClientVersion.Major}.{_connectionDto.CurrentClientVersion.Minor}.{_connectionDto.CurrentClientVersion.Build}. " + $"Please keep your Mare Synchronos client up-to-date.", - Dalamud.Interface.Internal.Notifications.NotificationType.Error)); + Dalamud.Interface.Internal.Notifications.NotificationType.Warning)); } await LoadIninitialPairs().ConfigureAwait(false); @@ -370,13 +370,13 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM try { InitializeApiHooks(); - await LoadIninitialPairs().ConfigureAwait(false); _connectionDto = await GetConnectionDto().ConfigureAwait(false); if (_connectionDto.ServerVersion != IMareHub.ApiVersion) { await StopConnection(ServerState.VersionMisMatch).ConfigureAwait(false); return; } + await LoadIninitialPairs().ConfigureAwait(false); await LoadOnlinePairs().ConfigureAwait(false); ServerState = ServerState.Connected; } @@ -418,6 +418,13 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM await StopConnection(ServerState.Unauthorized).ConfigureAwait(false); requireReconnect = true; } + catch (Exception ex) + { + Logger.LogWarning(ex, "Could not refresh token, forcing reconnect"); + _doNotNotifyOnNextInfo = true; + await CreateConnections().ConfigureAwait(false); + requireReconnect = true; + } return requireReconnect; } From 18d089d693f0e3a6495df54f6699e13be3754edb Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Mon, 23 Oct 2023 19:28:57 +0200 Subject: [PATCH 16/56] fix text alignment --- .../UI/Components/DrawFolderBase.cs | 24 ++++++------- .../UI/Components/DrawFolderGroup.cs | 23 ++++++------ MareSynchronos/UI/Components/DrawFolderTag.cs | 14 ++++---- MareSynchronos/UI/Components/DrawUserPair.cs | 36 ++++++++++--------- .../UI/Handlers/IdDisplayHandler.cs | 14 ++++---- 5 files changed, 58 insertions(+), 53 deletions(-) diff --git a/MareSynchronos/UI/Components/DrawFolderBase.cs b/MareSynchronos/UI/Components/DrawFolderBase.cs index 2acc636..2e31f5d 100644 --- a/MareSynchronos/UI/Components/DrawFolderBase.cs +++ b/MareSynchronos/UI/Components/DrawFolderBase.cs @@ -30,14 +30,12 @@ public abstract class DrawFolderBase : IDrawFolder if (!RenderIfEmpty && !_drawPairs.Any()) return; using var id = ImRaii.PushId("folder_" + _id); - var originalY = ImGui.GetCursorPosY(); - var pauseIconSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars); - var textSize = ImGui.CalcTextSize(_id); - var textPosY = originalY + pauseIconSize.Y / 2 - textSize.Y / 2; // draw opener var icon = _tagHandler.IsTagOpen(_id) ? FontAwesomeIcon.CaretDown : FontAwesomeIcon.CaretRight; - ImGui.SetCursorPosY(textPosY); + + ImGui.AlignTextToFramePadding(); + UiSharedService.FontText(icon.ToIconString(), UiBuilder.IconFont); if (ImGui.IsItemClicked()) { @@ -45,14 +43,14 @@ public abstract class DrawFolderBase : IDrawFolder } ImGui.SameLine(); - var leftSideEnd = DrawIcon(textPosY, originalY); + var leftSideEnd = DrawIcon(); ImGui.SameLine(); - var rightSideStart = DrawRightSide(originalY); + var rightSideStart = DrawRightSideInternal(); // draw name ImGui.SameLine(leftSideEnd); - DrawName(textPosY, rightSideStart - leftSideEnd); + DrawName(rightSideStart - leftSideEnd); ImGui.Separator(); // if opened draw content @@ -75,15 +73,15 @@ public abstract class DrawFolderBase : IDrawFolder } } - protected abstract float DrawIcon(float textPosY, float originalY); + protected abstract float DrawIcon(); protected abstract void DrawMenu(float menuWidth); - protected abstract void DrawName(float originalY, float width); + protected abstract void DrawName(float width); - protected abstract float DrawRightSide(float originalY, float currentRightSideX); + protected abstract float DrawRightSide(float currentRightSideX); - private float DrawRightSide(float originalY) + private float DrawRightSideInternal() { var barButtonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars); var spacingX = ImGui.GetStyle().ItemSpacing.X; @@ -114,6 +112,6 @@ public abstract class DrawFolderBase : IDrawFolder } } - return DrawRightSide(originalY, rightSideStart); + return DrawRightSide(rightSideStart); } } \ No newline at end of file diff --git a/MareSynchronos/UI/Components/DrawFolderGroup.cs b/MareSynchronos/UI/Components/DrawFolderGroup.cs index 1043cd5..68e9b7c 100644 --- a/MareSynchronos/UI/Components/DrawFolderGroup.cs +++ b/MareSynchronos/UI/Components/DrawFolderGroup.cs @@ -36,9 +36,9 @@ public class DrawFolderGroup : DrawFolderBase private bool IsOwner => string.Equals(_groupFullInfoDto.OwnerUID, _apiController.UID, StringComparison.Ordinal); private bool IsPinned => _groupFullInfoDto.GroupUserInfo.IsPinned(); - protected override float DrawIcon(float textPosY, float originalY) + protected override float DrawIcon() { - ImGui.SetCursorPosY(textPosY); + ImGui.AlignTextToFramePadding(); using (ImRaii.PushFont(UiBuilder.IconFont)) ImGui.TextUnformatted(_groupFullInfoDto.GroupPermissions.IsDisableInvites() ? FontAwesomeIcon.Lock.ToIconString() : FontAwesomeIcon.Users.ToIconString()); @@ -50,7 +50,8 @@ public class DrawFolderGroup : DrawFolderBase using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing with { X = ImGui.GetStyle().ItemSpacing.X / 2f })) { ImGui.SameLine(); - ImGui.SetCursorPosY(textPosY); + ImGui.AlignTextToFramePadding(); + ImGui.TextUnformatted("[" + OnlinePairs.ToString() + "]"); } UiSharedService.AttachToolTip(OnlinePairs + " online" + Environment.NewLine + TotalPairs + " total"); @@ -58,23 +59,21 @@ public class DrawFolderGroup : DrawFolderBase ImGui.SameLine(); if (IsOwner) { - ImGui.SameLine(); + ImGui.AlignTextToFramePadding(); using (ImRaii.PushFont(UiBuilder.IconFont)) ImGui.TextUnformatted(FontAwesomeIcon.Crown.ToIconString()); UiSharedService.AttachToolTip("You are the owner of " + _groupFullInfoDto.GroupAliasOrGID); } else if (IsModerator) { - ImGui.SameLine(); - ImGui.SetCursorPosY(textPosY); + ImGui.AlignTextToFramePadding(); using (ImRaii.PushFont(UiBuilder.IconFont)) ImGui.TextUnformatted(FontAwesomeIcon.UserShield.ToIconString()); UiSharedService.AttachToolTip("You are a moderator in " + _groupFullInfoDto.GroupAliasOrGID); } else if (IsPinned) { - ImGui.SameLine(); - ImGui.SetCursorPosY(textPosY); + ImGui.AlignTextToFramePadding(); using (ImRaii.PushFont(UiBuilder.IconFont)) ImGui.TextUnformatted(FontAwesomeIcon.Thumbtack.ToIconString()); UiSharedService.AttachToolTip("You are pinned in " + _groupFullInfoDto.GroupAliasOrGID); @@ -166,12 +165,12 @@ public class DrawFolderGroup : DrawFolderBase } } - protected override void DrawName(float originalY, float width) + protected override void DrawName(float width) { - _idDisplayHandler.DrawGroupText(_id, _groupFullInfoDto, ImGui.GetCursorPosX(), originalY, () => width); + _idDisplayHandler.DrawGroupText(_id, _groupFullInfoDto, ImGui.GetCursorPosX(), () => width); } - protected override float DrawRightSide(float originalY, float currentRightSideX) + protected override float DrawRightSide(float currentRightSideX) { var spacingX = ImGui.GetStyle().ItemSpacing.X; @@ -189,6 +188,8 @@ public class DrawFolderGroup : DrawFolderBase ImGui.SameLine(infoIconPosDist - userCogButtonSize.X); + ImGui.AlignTextToFramePadding(); + using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudYellow, _groupFullInfoDto.GroupPermissions.IsPreferDisableAnimations() != individualAnimDisabled || _groupFullInfoDto.GroupPermissions.IsPreferDisableSounds() != individualSoundsDisabled diff --git a/MareSynchronos/UI/Components/DrawFolderTag.cs b/MareSynchronos/UI/Components/DrawFolderTag.cs index 45db8f2..fb371e8 100644 --- a/MareSynchronos/UI/Components/DrawFolderTag.cs +++ b/MareSynchronos/UI/Components/DrawFolderTag.cs @@ -64,7 +64,7 @@ public class DrawFolderTag : DrawFolderBase _ => true }; - protected override float DrawIcon(float textPosY, float originalY) + protected override float DrawIcon() { using (ImRaii.PushFont(UiBuilder.IconFont)) { @@ -79,7 +79,7 @@ public class DrawFolderTag : DrawFolderBase _ => FontAwesomeIcon.Folder.ToIconString() }; - ImGui.SetCursorPosY(textPosY); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted(icon); } if (RenderCount) @@ -87,7 +87,8 @@ public class DrawFolderTag : DrawFolderBase using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing with { X = ImGui.GetStyle().ItemSpacing.X / 2f })) { ImGui.SameLine(); - ImGui.SetCursorPosY(textPosY); + ImGui.AlignTextToFramePadding(); + ImGui.TextUnformatted("[" + OnlinePairs.ToString() + "]"); } UiSharedService.AttachToolTip(OnlinePairs + " online" + Environment.NewLine + TotalPairs + " total"); @@ -112,9 +113,10 @@ public class DrawFolderTag : DrawFolderBase "Note: this will not unpair with users in this Group."); } - protected override void DrawName(float originalY, float width) + protected override void DrawName(float width) { - ImGui.SetCursorPosY(originalY); + ImGui.AlignTextToFramePadding(); + string name = _id switch { TagHandler.CustomUnpairedTag => "One-sided Individual Pairs", @@ -129,7 +131,7 @@ public class DrawFolderTag : DrawFolderBase ImGui.TextUnformatted(name); } - protected override float DrawRightSide(float originalY, float currentRightSideX) + protected override float DrawRightSide(float currentRightSideX) { if (!RenderPause) return currentRightSideX; diff --git a/MareSynchronos/UI/Components/DrawUserPair.cs b/MareSynchronos/UI/Components/DrawUserPair.cs index cb1e215..44c78a1 100644 --- a/MareSynchronos/UI/Components/DrawUserPair.cs +++ b/MareSynchronos/UI/Components/DrawUserPair.cs @@ -53,12 +53,11 @@ public class DrawUserPair var pauseIconSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars); var textSize = ImGui.CalcTextSize(_pair.UserData.AliasOrUID); - var textPosY = originalY + pauseIconSize.Y / 2 - textSize.Y / 2; - DrawLeftSide(textPosY); + DrawLeftSide(); ImGui.SameLine(); var posX = ImGui.GetCursorPosX(); - var rightSide = DrawRightSide(originalY); - DrawName(originalY, posX, rightSide); + var rightSide = DrawRightSide(); + DrawName(posX, rightSide); } private void DrawCommonClientMenu() @@ -180,15 +179,16 @@ public class DrawUserPair } } - private void DrawLeftSide(float textPosY) + private void DrawLeftSide() { string userPairText = string.Empty; - ImGui.SetCursorPosY(textPosY); + ImGui.AlignTextToFramePadding(); if (_pair.IsPaused) { - ImGui.SetCursorPosY(textPosY); + ImGui.AlignTextToFramePadding(); + using var _ = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); using var font = ImRaii.PushFont(UiBuilder.IconFont); ImGui.TextUnformatted(FontAwesomeIcon.PauseCircle.ToIconString()); @@ -196,7 +196,7 @@ public class DrawUserPair } else if (!_pair.IsOnline) { - ImGui.SetCursorPosY(textPosY); + ImGui.AlignTextToFramePadding(); using var _ = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed); using var font = ImRaii.PushFont(UiBuilder.IconFont); ImGui.TextUnformatted(_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.Bidirectional @@ -205,7 +205,8 @@ public class DrawUserPair } else { - using var _ = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.ParsedGreen); + ImGui.AlignTextToFramePadding(); + using var font = ImRaii.PushFont(UiBuilder.IconFont); ImGui.TextUnformatted(_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.Bidirectional ? FontAwesomeIcon.User.ToIconString() : FontAwesomeIcon.Users.ToIconString()); @@ -235,7 +236,7 @@ public class DrawUserPair if (_pair.UserPair.OwnPermissions.IsSticky()) { - ImGui.SetCursorPosY(textPosY); + ImGui.AlignTextToFramePadding(); using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing with { X = ImGui.GetStyle().ItemSpacing.X * 3 / 4f })) using (ImRaii.PushFont(UiBuilder.IconFont)) @@ -249,7 +250,8 @@ public class DrawUserPair if (_pair.IsVisible) { - ImGui.SetCursorPosY(textPosY); + ImGui.AlignTextToFramePadding(); + using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing with { X = ImGui.GetStyle().ItemSpacing.X * 3 / 4f })) using (ImRaii.PushFont(UiBuilder.IconFont)) { @@ -262,9 +264,9 @@ public class DrawUserPair } } - private void DrawName(float originalY, float leftSide, float rightSide) + private void DrawName(float leftSide, float rightSide) { - _displayHandler.DrawPairText(_id, _pair, leftSide, originalY, () => rightSide - leftSide); + _displayHandler.DrawPairText(_id, _pair, leftSide, () => rightSide - leftSide); } private void DrawPairedClientMenu() @@ -292,7 +294,7 @@ public class DrawUserPair } } - private float DrawRightSide(float originalY) + private float DrawRightSide() { var pauseIcon = _pair.UserPair!.OwnPermissions.IsPaused() ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause; var pauseIconSize = UiSharedService.GetIconButtonSize(pauseIcon); @@ -318,6 +320,8 @@ public class DrawUserPair infoIconDist = iconwidth.X; ImGui.SameLine(infoIconPosDist - iconwidth.X); + ImGui.AlignTextToFramePadding(); + UiSharedService.FontText(icon.ToIconString(), UiBuilder.IconFont); if (ImGui.IsItemHovered()) { @@ -378,7 +382,6 @@ public class DrawUserPair rightSideStart = windowEndX - barButtonSize.X - spacingX * 3 - pauseIconSize.X - infoIconDist; ImGui.SameLine(windowEndX - barButtonSize.X - spacingX - pauseIconSize.X); - ImGui.SetCursorPosY(originalY); if (ImGuiComponents.IconButton(pauseIcon)) { var perm = _pair.UserPair!.OwnPermissions; @@ -395,8 +398,7 @@ public class DrawUserPair rightSideStart = windowEndX - barButtonSize.X; } ImGui.SameLine(windowEndX - barButtonSize.X); - ImGui.SetCursorPosY(originalY); - + ImGui.AlignTextToFramePadding(); if (ImGuiComponents.IconButton(FontAwesomeIcon.Bars)) { ImGui.OpenPopup("User Flyout Menu"); diff --git a/MareSynchronos/UI/Handlers/IdDisplayHandler.cs b/MareSynchronos/UI/Handlers/IdDisplayHandler.cs index 0220c09..97484bf 100644 --- a/MareSynchronos/UI/Handlers/IdDisplayHandler.cs +++ b/MareSynchronos/UI/Handlers/IdDisplayHandler.cs @@ -31,13 +31,14 @@ public class IdDisplayHandler _mareConfigService = mareConfigService; } - public void DrawGroupText(string id, GroupFullInfoDto group, float textPosX, float originalY, Func editBoxWidth) + public void DrawGroupText(string id, GroupFullInfoDto group, float textPosX, Func editBoxWidth) { ImGui.SameLine(textPosX); (bool textIsUid, string playerText) = GetGroupText(group); if (!string.Equals(_editEntry, group.GID, StringComparison.Ordinal)) { - ImGui.SetCursorPosY(originalY); + ImGui.AlignTextToFramePadding(); + using (ImRaii.PushFont(UiBuilder.MonoFont, textIsUid)) ImGui.TextUnformatted(playerText); @@ -69,7 +70,7 @@ public class IdDisplayHandler } else { - ImGui.SetCursorPosY(originalY); + ImGui.AlignTextToFramePadding(); ImGui.SetNextItemWidth(editBoxWidth.Invoke()); if (ImGui.InputTextWithHint("", "Name/Notes", ref _editComment, 255, ImGuiInputTextFlags.EnterReturnsTrue)) @@ -86,13 +87,14 @@ public class IdDisplayHandler } } - public void DrawPairText(string id, Pair pair, float textPosX, float originalY, Func editBoxWidth) + public void DrawPairText(string id, Pair pair, float textPosX, Func editBoxWidth) { ImGui.SameLine(textPosX); (bool textIsUid, string playerText) = GetPlayerText(pair); if (!string.Equals(_editEntry, pair.UserData.UID, StringComparison.Ordinal)) { - ImGui.SetCursorPosY(originalY); + ImGui.AlignTextToFramePadding(); + if (textIsUid) ImGui.PushFont(UiBuilder.MonoFont); ImGui.TextUnformatted(playerText); if (textIsUid) ImGui.PopFont(); @@ -162,7 +164,7 @@ public class IdDisplayHandler } else { - ImGui.SetCursorPosY(originalY); + ImGui.AlignTextToFramePadding(); ImGui.SetNextItemWidth(editBoxWidth.Invoke()); if (ImGui.InputTextWithHint("", "Nick/Notes", ref _editComment, 255, ImGuiInputTextFlags.EnterReturnsTrue)) From 82cca2e035173010b4022b7a8845ec99b7a922c4 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Tue, 24 Oct 2023 01:57:31 +0200 Subject: [PATCH 17/56] fix total user count in syncshell (distinct by UIDs) --- MareSynchronos/UI/Components/DrawFolderBase.cs | 12 ++++++------ MareSynchronos/UI/Components/DrawFolderGroup.cs | 2 +- MareSynchronos/UI/Components/DrawFolderTag.cs | 8 ++++---- .../UI/Components/DrawGroupedGroupFolder.cs | 3 ++- MareSynchronos/UI/Components/IDrawFolder.cs | 5 ++++- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/MareSynchronos/UI/Components/DrawFolderBase.cs b/MareSynchronos/UI/Components/DrawFolderBase.cs index 2e31f5d..c3bfefe 100644 --- a/MareSynchronos/UI/Components/DrawFolderBase.cs +++ b/MareSynchronos/UI/Components/DrawFolderBase.cs @@ -8,16 +8,16 @@ namespace MareSynchronos.UI.Components; public abstract class DrawFolderBase : IDrawFolder { - protected readonly IEnumerable _drawPairs; + public IEnumerable DrawPairs { get; private set; } protected readonly string _id; protected readonly TagHandler _tagHandler; private float _menuWidth = -1; - public int OnlinePairs => _drawPairs.Count(u => u.Pair.IsOnline); + public int OnlinePairs => DrawPairs.Count(u => u.Pair.IsOnline); public int TotalPairs { get; } protected DrawFolderBase(string id, IEnumerable drawPairs, TagHandler tagHandler, int totalPairs) { _id = id; - _drawPairs = drawPairs; + DrawPairs = drawPairs; _tagHandler = tagHandler; TotalPairs = totalPairs; } @@ -27,7 +27,7 @@ public abstract class DrawFolderBase : IDrawFolder public void Draw() { - if (!RenderIfEmpty && !_drawPairs.Any()) return; + if (!RenderIfEmpty && !DrawPairs.Any()) return; using var id = ImRaii.PushId("folder_" + _id); @@ -57,9 +57,9 @@ public abstract class DrawFolderBase : IDrawFolder if (_tagHandler.IsTagOpen(_id)) { using var indent = ImRaii.PushIndent(20f); - if (_drawPairs.Any()) + if (DrawPairs.Any()) { - foreach (var item in _drawPairs) + foreach (var item in DrawPairs) { item.DrawPairedClient(); } diff --git a/MareSynchronos/UI/Components/DrawFolderGroup.cs b/MareSynchronos/UI/Components/DrawFolderGroup.cs index 68e9b7c..85e8c38 100644 --- a/MareSynchronos/UI/Components/DrawFolderGroup.cs +++ b/MareSynchronos/UI/Components/DrawFolderGroup.cs @@ -98,7 +98,7 @@ public class DrawFolderGroup : DrawFolderBase if (UiSharedService.IconTextButton(FontAwesomeIcon.StickyNote, "Copy Notes", menuWidth, true)) { ImGui.CloseCurrentPopup(); - ImGui.SetClipboardText(UiSharedService.GetNotes(_drawPairs.Select(k => k.Pair).ToList())); + ImGui.SetClipboardText(UiSharedService.GetNotes(DrawPairs.Select(k => k.Pair).ToList())); } UiSharedService.AttachToolTip("Copies all your notes for all users in this Syncshell to the clipboard." + Environment.NewLine + "They can be imported via Settings -> Privacy -> Import Notes from Clipboard"); diff --git a/MareSynchronos/UI/Components/DrawFolderTag.cs b/MareSynchronos/UI/Components/DrawFolderTag.cs index fb371e8..ba8f7d5 100644 --- a/MareSynchronos/UI/Components/DrawFolderTag.cs +++ b/MareSynchronos/UI/Components/DrawFolderTag.cs @@ -51,7 +51,7 @@ public class DrawFolderTag : DrawFolderBase TagHandler.CustomAllTag => false, TagHandler.CustomOfflineSyncshellTag => false, _ => true, - } && _drawPairs.Any(); + } && DrawPairs.Any(); private bool RenderCount => _id switch { @@ -135,7 +135,7 @@ public class DrawFolderTag : DrawFolderBase { if (!RenderPause) return currentRightSideX; - var allArePaused = _drawPairs.All(pair => pair.UserPair!.OwnPermissions.IsPaused()); + var allArePaused = DrawPairs.All(pair => pair.UserPair!.OwnPermissions.IsPaused()); var pauseButton = allArePaused ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause; var pauseButtonX = UiSharedService.GetIconButtonSize(pauseButton).X; @@ -145,11 +145,11 @@ public class DrawFolderTag : DrawFolderBase { if (allArePaused) { - ResumeAllPairs(_drawPairs); + ResumeAllPairs(DrawPairs); } else { - PauseRemainingPairs(_drawPairs); + PauseRemainingPairs(DrawPairs); } } if (allArePaused) diff --git a/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs b/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs index f8ee004..6968924 100644 --- a/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs +++ b/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs @@ -9,7 +9,8 @@ public class DrawGroupedGroupFolder : IDrawFolder { private readonly IEnumerable _groups; private readonly TagHandler _tagHandler; - public int OnlinePairs => _groups.Sum(g => g.OnlinePairs); + public IEnumerable DrawPairs => throw new NotSupportedException(); + public int OnlinePairs => _groups.SelectMany(g => g.DrawPairs).Where(g => g.Pair.IsOnline).DistinctBy(g => g.UID).Count(); public int TotalPairs => _groups.Sum(g => g.TotalPairs); public DrawGroupedGroupFolder(IEnumerable groups, TagHandler tagHandler) diff --git a/MareSynchronos/UI/Components/IDrawFolder.cs b/MareSynchronos/UI/Components/IDrawFolder.cs index 13563ca..82a2df5 100644 --- a/MareSynchronos/UI/Components/IDrawFolder.cs +++ b/MareSynchronos/UI/Components/IDrawFolder.cs @@ -1,8 +1,11 @@ -namespace MareSynchronos.UI.Components; + +namespace MareSynchronos.UI.Components; public interface IDrawFolder { int TotalPairs { get; } int OnlinePairs { get; } + IEnumerable DrawPairs { get; } + void Draw(); } From fc9953bb8da1015a0973ea3f4c53efdb20e6d9d4 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Tue, 24 Oct 2023 22:31:34 +0200 Subject: [PATCH 18/56] adjust token handling --- .../WebAPI/Files/FileTransferOrchestrator.cs | 2 +- MareSynchronos/WebAPI/SignalR/TokenProvider.cs | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/MareSynchronos/WebAPI/Files/FileTransferOrchestrator.cs b/MareSynchronos/WebAPI/Files/FileTransferOrchestrator.cs index 99aa36a..ab8c991 100644 --- a/MareSynchronos/WebAPI/Files/FileTransferOrchestrator.cs +++ b/MareSynchronos/WebAPI/Files/FileTransferOrchestrator.cs @@ -115,7 +115,7 @@ public class FileTransferOrchestrator : DisposableMediatorSubscriberBase private async Task SendRequestInternalAsync(HttpRequestMessage requestMessage, CancellationToken? ct = null, HttpCompletionOption httpCompletionOption = HttpCompletionOption.ResponseContentRead) { - requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await _tokenProvider.GetOrUpdateToken(ct!.Value).ConfigureAwait(false)); + requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _tokenProvider.GetToken()); if (requestMessage.Content != null && requestMessage.Content is not StreamContent && requestMessage.Content is not ByteArrayContent) { diff --git a/MareSynchronos/WebAPI/SignalR/TokenProvider.cs b/MareSynchronos/WebAPI/SignalR/TokenProvider.cs index 2d0f6e8..783e455 100644 --- a/MareSynchronos/WebAPI/SignalR/TokenProvider.cs +++ b/MareSynchronos/WebAPI/SignalR/TokenProvider.cs @@ -89,6 +89,9 @@ public sealed class TokenProvider : IDisposable, IMediatorSubscriber if (ex.StatusCode == System.Net.HttpStatusCode.Unauthorized) { + Mediator.Publish(new NotificationMessage("Error refreshing token", "Your authentication token could not be renewed. Try reconnecting to Mare manually.", + Dalamud.Interface.Internal.Notifications.NotificationType.Error)); + Mediator.Publish(new DisconnectedMessage()); throw new MareAuthFailureException(response); } @@ -103,6 +106,16 @@ public sealed class TokenProvider : IDisposable, IMediatorSubscriber return response; } + public string? GetToken() + { + if (_tokenCache.TryGetValue(CurrentIdentifier, out var token)) + { + return token; + } + + throw new InvalidOperationException("No token present"); + } + public async Task GetOrUpdateToken(CancellationToken ct, bool forceRenew = false) { bool renewal = forceRenew; From 9e6108a4d6b2547639b1c506be6b0115b2201079 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Wed, 25 Oct 2023 02:30:42 +0200 Subject: [PATCH 19/56] idk --- MareSynchronos/WebAPI/SignalR/ApiController.cs | 2 +- MareSynchronos/WebAPI/SignalR/TokenProvider.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/MareSynchronos/WebAPI/SignalR/ApiController.cs b/MareSynchronos/WebAPI/SignalR/ApiController.cs index 1b8e139..4526cb7 100644 --- a/MareSynchronos/WebAPI/SignalR/ApiController.cs +++ b/MareSynchronos/WebAPI/SignalR/ApiController.cs @@ -270,7 +270,7 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM bool requireReconnect = await RefreshToken(ct).ConfigureAwait(false); - if (requireReconnect) continue; + if (requireReconnect) break; _ = await CheckClientHealth().ConfigureAwait(false); } diff --git a/MareSynchronos/WebAPI/SignalR/TokenProvider.cs b/MareSynchronos/WebAPI/SignalR/TokenProvider.cs index 783e455..ff4dcee 100644 --- a/MareSynchronos/WebAPI/SignalR/TokenProvider.cs +++ b/MareSynchronos/WebAPI/SignalR/TokenProvider.cs @@ -116,10 +116,10 @@ public sealed class TokenProvider : IDisposable, IMediatorSubscriber throw new InvalidOperationException("No token present"); } - public async Task GetOrUpdateToken(CancellationToken ct, bool forceRenew = false) + public async Task GetOrUpdateToken(CancellationToken ct) { - bool renewal = forceRenew; - if (!forceRenew && _tokenCache.TryGetValue(CurrentIdentifier, out var token)) + bool renewal = false; + if (_tokenCache.TryGetValue(CurrentIdentifier, out var token)) { var handler = new JwtSecurityTokenHandler(); var jwtToken = handler.ReadJwtToken(token); From 24960fd1ba7f7b4b1cd72df7d5e3d67d03100692 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Wed, 25 Oct 2023 12:25:00 +0200 Subject: [PATCH 20/56] check for timezone --- .../WebAPI/SignalR/ApiController.cs | 6 ++++++ .../WebAPI/SignalR/TokenProvider.cs | 19 +++++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/MareSynchronos/WebAPI/SignalR/ApiController.cs b/MareSynchronos/WebAPI/SignalR/ApiController.cs index 4526cb7..6b8937b 100644 --- a/MareSynchronos/WebAPI/SignalR/ApiController.cs +++ b/MareSynchronos/WebAPI/SignalR/ApiController.cs @@ -212,6 +212,12 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM Logger.LogInformation("Failed to establish connection, retrying"); await Task.Delay(TimeSpan.FromSeconds(new Random().Next(5, 20)), token).ConfigureAwait(false); } + catch (InvalidOperationException ex) + { + Logger.LogWarning(ex, "InvalidOperationException on connection"); + ServerState = ServerState.Disconnected; + return; + } catch (Exception ex) { Logger.LogWarning(ex, "Exception on Connection"); diff --git a/MareSynchronos/WebAPI/SignalR/TokenProvider.cs b/MareSynchronos/WebAPI/SignalR/TokenProvider.cs index ff4dcee..5cd24e9 100644 --- a/MareSynchronos/WebAPI/SignalR/TokenProvider.cs +++ b/MareSynchronos/WebAPI/SignalR/TokenProvider.cs @@ -103,6 +103,16 @@ public sealed class TokenProvider : IDisposable, IMediatorSubscriber _logger.LogTrace("GetNewToken: JWT {token}", response); _logger.LogDebug("GetNewToken: Valid until {date}, ValidClaim until {date}", jwtToken.ValidTo, new DateTime(long.Parse(jwtToken.Claims.Single(c => string.Equals(c.Type, "expiration_date", StringComparison.Ordinal)).Value), DateTimeKind.Utc)); + if (jwtToken.ValidTo.Subtract(TimeSpan.FromHours(6).Add(TimeSpan.FromMinutes(1))) > DateTime.UtcNow) + { + _tokenCache.TryRemove(CurrentIdentifier, out _); + Mediator.Publish(new NotificationMessage("Invalid timezone", "The time zone of your computer is invalid. " + + "Mare will not function properly if the time zone is not set correctly. " + + "Please set your computers time zone correctly and keep it synchronized with the internet.", + Dalamud.Interface.Internal.Notifications.NotificationType.Error)); + Mediator.Publish(new DisconnectedMessage()); + throw new InvalidOperationException($"JwtToken is behind DateTime.UtcNow, DateTime.UtcNow is possibly wrong. DateTime.UtcNow is {DateTime.UtcNow}, JwtToken.ValidTo is {jwtToken.ValidTo}"); + } return response; } @@ -122,15 +132,20 @@ public sealed class TokenProvider : IDisposable, IMediatorSubscriber if (_tokenCache.TryGetValue(CurrentIdentifier, out var token)) { var handler = new JwtSecurityTokenHandler(); - var jwtToken = handler.ReadJwtToken(token); - if (jwtToken.ValidTo == DateTime.MinValue || jwtToken.ValidTo.Subtract(TimeSpan.FromMinutes(5)) > DateTime.UtcNow) + var jwt = handler.ReadJwtToken(token); + if (jwt.ValidTo == DateTime.MinValue || jwt.ValidTo.Subtract(TimeSpan.FromMinutes(5)) > DateTime.UtcNow) { _logger.LogTrace("GetOrUpdate: Returning token from cache"); return token; } + _logger.LogDebug("GetOrUpdate: Cached token requires renewal, token valid to: {valid}, UtcTime is {utcTime}", jwt.ValidTo, DateTime.UtcNow); renewal = true; } + else + { + _logger.LogDebug("GetOrUpdate: Did not find token in cache, requesting a new one"); + } _logger.LogTrace("GetOrUpdate: Getting new token"); return await GetNewToken(renewal, ct).ConfigureAwait(false); From 3383de7294ab226bca59515d20cbfe918c4b831a Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Wed, 25 Oct 2023 12:31:16 +0200 Subject: [PATCH 21/56] some minor fixes --- MareSynchronos/UI/Components/DrawUserPair.cs | 7 +++++-- MareSynchronos/WebAPI/SignalR/ApiController.cs | 2 +- MareSynchronos/WebAPI/SignalR/TokenProvider.cs | 5 ++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/MareSynchronos/UI/Components/DrawUserPair.cs b/MareSynchronos/UI/Components/DrawUserPair.cs index 44c78a1..2cf3be8 100644 --- a/MareSynchronos/UI/Components/DrawUserPair.cs +++ b/MareSynchronos/UI/Components/DrawUserPair.cs @@ -199,8 +199,10 @@ public class DrawUserPair ImGui.AlignTextToFramePadding(); using var _ = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed); using var font = ImRaii.PushFont(UiBuilder.IconFont); - ImGui.TextUnformatted(_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.Bidirectional - ? FontAwesomeIcon.User.ToIconString() : FontAwesomeIcon.Users.ToIconString()); + ImGui.TextUnformatted(_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.OneSided + ? FontAwesomeIcon.ArrowsLeftRight.ToIconString() + : (_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.Bidirectional + ? FontAwesomeIcon.User.ToIconString() : FontAwesomeIcon.Users.ToIconString())); userPairText = _pair.UserData.AliasOrUID + " is offline"; } else @@ -208,6 +210,7 @@ public class DrawUserPair ImGui.AlignTextToFramePadding(); using var font = ImRaii.PushFont(UiBuilder.IconFont); + using var _ = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.HealerGreen); ImGui.TextUnformatted(_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.Bidirectional ? FontAwesomeIcon.User.ToIconString() : FontAwesomeIcon.Users.ToIconString()); userPairText = _pair.UserData.AliasOrUID + " is online"; diff --git a/MareSynchronos/WebAPI/SignalR/ApiController.cs b/MareSynchronos/WebAPI/SignalR/ApiController.cs index 6b8937b..a0df76e 100644 --- a/MareSynchronos/WebAPI/SignalR/ApiController.cs +++ b/MareSynchronos/WebAPI/SignalR/ApiController.cs @@ -215,7 +215,7 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM catch (InvalidOperationException ex) { Logger.LogWarning(ex, "InvalidOperationException on connection"); - ServerState = ServerState.Disconnected; + await StopConnection(ServerState.Disconnected).ConfigureAwait(false); return; } catch (Exception ex) diff --git a/MareSynchronos/WebAPI/SignalR/TokenProvider.cs b/MareSynchronos/WebAPI/SignalR/TokenProvider.cs index 5cd24e9..825c111 100644 --- a/MareSynchronos/WebAPI/SignalR/TokenProvider.cs +++ b/MareSynchronos/WebAPI/SignalR/TokenProvider.cs @@ -106,11 +106,10 @@ public sealed class TokenProvider : IDisposable, IMediatorSubscriber if (jwtToken.ValidTo.Subtract(TimeSpan.FromHours(6).Add(TimeSpan.FromMinutes(1))) > DateTime.UtcNow) { _tokenCache.TryRemove(CurrentIdentifier, out _); - Mediator.Publish(new NotificationMessage("Invalid timezone", "The time zone of your computer is invalid. " + + Mediator.Publish(new NotificationMessage("Invalid system clock", "The clock of your computer is invalid. " + "Mare will not function properly if the time zone is not set correctly. " + - "Please set your computers time zone correctly and keep it synchronized with the internet.", + "Please set your computers time zone correctly and keep your clock synchronized with the internet.", Dalamud.Interface.Internal.Notifications.NotificationType.Error)); - Mediator.Publish(new DisconnectedMessage()); throw new InvalidOperationException($"JwtToken is behind DateTime.UtcNow, DateTime.UtcNow is possibly wrong. DateTime.UtcNow is {DateTime.UtcNow}, JwtToken.ValidTo is {jwtToken.ValidTo}"); } return response; From 2221f4d09e1e66f134547a0d1d760e9fb1b39dfb Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Wed, 25 Oct 2023 16:33:03 +0200 Subject: [PATCH 22/56] fix palette application and add experimental less redraws option --- .../FileCache/TransientResourceManager.cs | 8 +++- .../Configurations/MareConfig.cs | 1 + .../PlayerData/Data/PlayerChanges.cs | 15 +++---- .../Factories/PairHandlerFactory.cs | 7 +++- .../PlayerData/Handlers/GameObjectHandler.cs | 9 ++++ .../PlayerData/Handlers/PairHandler.cs | 33 ++++++++++++--- MareSynchronos/Services/DalamudUtilService.cs | 11 ----- MareSynchronos/Services/Mediator/Messages.cs | 2 +- MareSynchronos/UI/CompactUI.cs | 4 +- MareSynchronos/UI/SettingsUi.cs | 12 ++++++ MareSynchronos/Utils/VariousExtensions.cs | 42 ++++++++++++++++++- 11 files changed, 114 insertions(+), 30 deletions(-) diff --git a/MareSynchronos/FileCache/TransientResourceManager.cs b/MareSynchronos/FileCache/TransientResourceManager.cs index 644f612..8848abd 100644 --- a/MareSynchronos/FileCache/TransientResourceManager.cs +++ b/MareSynchronos/FileCache/TransientResourceManager.cs @@ -51,7 +51,13 @@ public sealed class TransientResourceManager : DisposableMediatorSubscriberBase Mediator.Subscribe(this, Manager_PenumbraResourceLoadEvent); Mediator.Subscribe(this, (_) => Manager_PenumbraModSettingChanged()); Mediator.Subscribe(this, (_) => DalamudUtil_FrameworkUpdate()); - Mediator.Subscribe(this, (_) => DalamudUtil_ClassJobChanged()); + Mediator.Subscribe(this, (msg) => + { + if (_playerRelatedPointers.Contains(msg.gameObjectHandler)) + { + DalamudUtil_ClassJobChanged(); + } + }); Mediator.Subscribe(this, (msg) => { _playerRelatedPointers.Add(msg.Handler); diff --git a/MareSynchronos/MareConfiguration/Configurations/MareConfig.cs b/MareSynchronos/MareConfiguration/Configurations/MareConfig.cs index 2c5ab9a..013c0eb 100644 --- a/MareSynchronos/MareConfiguration/Configurations/MareConfig.cs +++ b/MareSynchronos/MareConfiguration/Configurations/MareConfig.cs @@ -50,4 +50,5 @@ public class MareConfig : IMareConfiguration public bool UseCompactor { get; set; } = false; public int Version { get; set; } = 1; public NotificationLocation WarningNotification { get; set; } = NotificationLocation.Both; + public bool UseLessRedraws { get; set; } = false; } \ No newline at end of file diff --git a/MareSynchronos/PlayerData/Data/PlayerChanges.cs b/MareSynchronos/PlayerData/Data/PlayerChanges.cs index f33729c..bd1beb9 100644 --- a/MareSynchronos/PlayerData/Data/PlayerChanges.cs +++ b/MareSynchronos/PlayerData/Data/PlayerChanges.cs @@ -2,11 +2,12 @@ public enum PlayerChanges { - Heels = 1, - Customize = 2, - Palette = 3, - Honorific = 4, - ModFiles = 5, - ModManip = 6, - Glamourer = 7 + ModFiles = 1, + ModManip = 2, + Glamourer = 3, + Customize = 4, + Heels = 5, + Palette = 6, + Honorific = 7, + ForcedRedraw = 8, } \ No newline at end of file diff --git a/MareSynchronos/PlayerData/Factories/PairHandlerFactory.cs b/MareSynchronos/PlayerData/Factories/PairHandlerFactory.cs index f9ee5f6..73bad33 100644 --- a/MareSynchronos/PlayerData/Factories/PairHandlerFactory.cs +++ b/MareSynchronos/PlayerData/Factories/PairHandlerFactory.cs @@ -1,6 +1,7 @@ using MareSynchronos.API.Dto.User; using MareSynchronos.FileCache; using MareSynchronos.Interop; +using MareSynchronos.MareConfiguration; using MareSynchronos.PlayerData.Handlers; using MareSynchronos.PlayerData.Pairs; using MareSynchronos.Services; @@ -20,12 +21,13 @@ public class PairHandlerFactory private readonly IpcManager _ipcManager; private readonly ILoggerFactory _loggerFactory; private readonly MareMediator _mareMediator; + private readonly MareConfigService _mareConfigService; private readonly PluginWarningNotificationService _pluginWarningNotificationManager; public PairHandlerFactory(ILoggerFactory loggerFactory, GameObjectHandlerFactory gameObjectHandlerFactory, IpcManager ipcManager, FileDownloadManagerFactory fileDownloadManagerFactory, DalamudUtilService dalamudUtilService, PluginWarningNotificationService pluginWarningNotificationManager, IHostApplicationLifetime hostApplicationLifetime, - FileCacheManager fileCacheManager, MareMediator mareMediator) + FileCacheManager fileCacheManager, MareMediator mareMediator, MareConfigService mareConfigService) { _loggerFactory = loggerFactory; _gameObjectHandlerFactory = gameObjectHandlerFactory; @@ -36,12 +38,13 @@ public class PairHandlerFactory _hostApplicationLifetime = hostApplicationLifetime; _fileCacheManager = fileCacheManager; _mareMediator = mareMediator; + _mareConfigService = mareConfigService; } public PairHandler Create(OnlineUserIdentDto onlineUserIdentDto) { return new PairHandler(_loggerFactory.CreateLogger(), onlineUserIdentDto, _gameObjectHandlerFactory, _ipcManager, _fileDownloadManagerFactory.Create(), _pluginWarningNotificationManager, _dalamudUtilService, _hostApplicationLifetime, - _fileCacheManager, _mareMediator); + _fileCacheManager, _mareMediator, _mareConfigService); } } \ No newline at end of file diff --git a/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs b/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs index 84f979f..c30a915 100644 --- a/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs +++ b/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs @@ -22,6 +22,7 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase private bool _haltProcessing = false; private bool _ignoreSendAfterRedraw = false; private int _ptrNullCounter = 0; + private byte _classJob = 0; private CancellationTokenSource _zoningCts = new(); public GameObjectHandler(ILogger logger, PerformanceCollectorService performanceCollector, @@ -215,6 +216,14 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase if (((DrawObject*)DrawObjectAddress)->Object.GetObjectType() == ObjectType.CharacterBase && ((CharacterBase*)DrawObjectAddress)->GetModelType() == CharacterBase.ModelType.Human) { + var classJob = chara->CharacterData.ClassJob; + if (classJob != _classJob) + { + Logger.LogTrace("[{this}] classjob changed from {old} to {new}", this, _classJob, classJob); + _classJob = classJob; + Mediator.Publish(new ClassJobChangedMessage(this)); + } + equipDiff = CompareAndUpdateEquipByteData((byte*)&((Human*)DrawObjectAddress)->Head); ref var mh = ref chara->DrawData.Weapon(WeaponSlot.MainHand); diff --git a/MareSynchronos/PlayerData/Handlers/PairHandler.cs b/MareSynchronos/PlayerData/Handlers/PairHandler.cs index 8dffb29..25f61e0 100644 --- a/MareSynchronos/PlayerData/Handlers/PairHandler.cs +++ b/MareSynchronos/PlayerData/Handlers/PairHandler.cs @@ -2,6 +2,7 @@ using MareSynchronos.API.Dto.User; using MareSynchronos.FileCache; using MareSynchronos.Interop; +using MareSynchronos.MareConfiguration; using MareSynchronos.PlayerData.Factories; using MareSynchronos.PlayerData.Pairs; using MareSynchronos.Services; @@ -21,6 +22,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase private readonly DalamudUtilService _dalamudUtil; private readonly FileDownloadManager _downloadManager; private readonly FileCacheManager _fileDbManager; + private readonly MareConfigService _mareConfigService; private readonly GameObjectHandlerFactory _gameObjectHandlerFactory; private readonly IpcManager _ipcManager; private readonly IHostApplicationLifetime _lifetime; @@ -34,13 +36,15 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase private bool _forceApplyMods = false; private bool _isVisible; private string _penumbraCollection; + private bool _redrawOnNextApplication = false; public PairHandler(ILogger logger, OnlineUserIdentDto onlineUser, GameObjectHandlerFactory gameObjectHandlerFactory, IpcManager ipcManager, FileDownloadManager transferManager, PluginWarningNotificationService pluginWarningNotificationManager, DalamudUtilService dalamudUtil, IHostApplicationLifetime lifetime, - FileCacheManager fileDbManager, MareMediator mediator) : base(logger, mediator) + FileCacheManager fileDbManager, MareMediator mediator, + MareConfigService mareConfigService) : base(logger, mediator) { OnlineUser = onlineUser; _gameObjectHandlerFactory = gameObjectHandlerFactory; @@ -50,7 +54,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase _dalamudUtil = dalamudUtil; _lifetime = lifetime; _fileDbManager = fileDbManager; - + _mareConfigService = mareConfigService; _penumbraCollection = _ipcManager.PenumbraCreateTemporaryCollectionAsync(logger, OnlineUser.User.UID).ConfigureAwait(false).GetAwaiter().GetResult(); Mediator.Subscribe(this, (_) => FrameworkUpdate()); @@ -70,6 +74,13 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase _charaHandler = null; } }); + Mediator.Subscribe(this, (msg) => + { + if (_mareConfigService.Current.UseLessRedraws && msg.gameObjectHandler == _charaHandler) + { + _redrawOnNextApplication = true; + } + }); } public bool IsVisible @@ -128,6 +139,12 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase _forceApplyMods = false; } + if (_redrawOnNextApplication && charaDataToUpdate.TryGetValue(ObjectKind.Player, out var player)) + { + player.Add(PlayerChanges.ForcedRedraw); + _redrawOnNextApplication = false; + } + if (charaDataToUpdate.TryGetValue(ObjectKind.Player, out var playerChanges)) { _pluginWarningNotificationManager.NotifyForMissingPlugins(OnlineUser.User, PlayerName!, playerChanges); @@ -229,6 +246,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase Logger.LogDebug("[{applicationId}] Applying Customization Data for {handler}", applicationId, handler); await _dalamudUtil.WaitWhileCharacterIsDrawing(Logger, handler, applicationId, 30000, token).ConfigureAwait(false); token.ThrowIfCancellationRequested(); + if (!_mareConfigService.Current.UseLessRedraws) changes.Value.Remove(PlayerChanges.ForcedRedraw); foreach (var change in changes.Value.OrderBy(p => (int)p)) { Logger.LogDebug("[{applicationId}] Processing {change} for {handler}", applicationId, change, handler); @@ -264,15 +282,20 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase } break; - case PlayerChanges.ModFiles: - case PlayerChanges.ModManip: + case PlayerChanges.ForcedRedraw: + await _ipcManager.PenumbraRedrawAsync(Logger, handler, applicationId, token).ConfigureAwait(false); + break; + + default: break; } token.ThrowIfCancellationRequested(); } - if (changes.Value.Contains(PlayerChanges.ModFiles) || changes.Value.Contains(PlayerChanges.ModManip) || changes.Value.Contains(PlayerChanges.Glamourer)) + if (!_mareConfigService.Current.UseLessRedraws && (changes.Value.Contains(PlayerChanges.ModFiles) || changes.Value.Contains(PlayerChanges.ModManip) || changes.Value.Contains(PlayerChanges.Glamourer))) + { await _ipcManager.PenumbraRedrawAsync(Logger, handler, applicationId, token).ConfigureAwait(false); + } } finally { diff --git a/MareSynchronos/Services/DalamudUtilService.cs b/MareSynchronos/Services/DalamudUtilService.cs index 9a662ed..724cfad 100644 --- a/MareSynchronos/Services/DalamudUtilService.cs +++ b/MareSynchronos/Services/DalamudUtilService.cs @@ -494,17 +494,6 @@ public class DalamudUtilService : IHostedService _mediator.Publish(new DalamudLogoutMessage()); } - if (_clientState.LocalPlayer != null && _clientState.LocalPlayer.IsValid()) - { - var newclassJobId = _clientState.LocalPlayer.ClassJob.Id; - - if (_classJobId != newclassJobId) - { - _classJobId = newclassJobId; - _mediator.Publish(new ClassJobChangedMessage(_classJobId)); - } - } - _mediator.Publish(new DelayedFrameworkUpdateMessage()); _delayedFrameworkUpdateCheck = DateTime.Now; diff --git a/MareSynchronos/Services/Mediator/Messages.cs b/MareSynchronos/Services/Mediator/Messages.cs index 4d7b802..c814c26 100644 --- a/MareSynchronos/Services/Mediator/Messages.cs +++ b/MareSynchronos/Services/Mediator/Messages.cs @@ -18,7 +18,7 @@ public record OpenSettingsUiMessage : MessageBase; public record DalamudLoginMessage : MessageBase; public record DalamudLogoutMessage : MessageBase; public record FrameworkUpdateMessage : SameThreadMessage; -public record ClassJobChangedMessage(uint? ClassJob) : MessageBase; +public record ClassJobChangedMessage(GameObjectHandler gameObjectHandler) : MessageBase; public record DelayedFrameworkUpdateMessage : SameThreadMessage; public record ZoneSwitchStartMessage : MessageBase; public record ZoneSwitchEndMessage : MessageBase; diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs index 4e8f0cb..cd0a177 100644 --- a/MareSynchronos/UI/CompactUI.cs +++ b/MareSynchronos/UI/CompactUI.cs @@ -183,8 +183,8 @@ public class CompactUi : WindowMediatorSubscriberBase { var ySize = TransferPartHeight == 0 ? 1 - : (ImGui.GetWindowContentRegionMax().Y - ImGui.GetWindowContentRegionMin().Y - + ImGui.GetTextLineHeight() - ImGui.GetStyle().WindowBorderSize) - TransferPartHeight - ImGui.GetCursorPosY(); + : (ImGui.GetWindowContentRegionMax().Y - ImGui.GetWindowContentRegionMin().Y + + ImGui.GetTextLineHeight() - ImGui.GetStyle().WindowPadding.Y - ImGui.GetStyle().WindowBorderSize) - TransferPartHeight - ImGui.GetCursorPosY(); ImGui.BeginChild("list", new Vector2(WindowContentWidth, ySize), border: false); diff --git a/MareSynchronos/UI/SettingsUi.cs b/MareSynchronos/UI/SettingsUi.cs index 3f68028..6dc32b0 100644 --- a/MareSynchronos/UI/SettingsUi.cs +++ b/MareSynchronos/UI/SettingsUi.cs @@ -515,6 +515,18 @@ public class SettingsUi : WindowMediatorSubscriberBase } _lastTab = "General"; + UiSharedService.FontText("General Settings", _uiShared.UidFont); + bool lessRedraws = _configService.Current.UseLessRedraws; + if (ImGui.Checkbox("[Experimental] Use less redraws", ref lessRedraws)) + { + _configService.Current.UseLessRedraws = lessRedraws; + _configService.Save(); + } + UiSharedService.DrawHelpText("This will attempt to use less redraws. Changes that solely affect the players body appearance (i.e. clothes) should not cause a redraw anymore." + Environment.NewLine + + "Some changes, especially to hair, face, tail or any vfx, animation or skeleton changes, or class changes will still force a redraw." + Environment.NewLine + Environment.NewLine + + "WARNING: this is an experimental, little tested feature and can potentially lead to issues with animation state or crashes. Use at your own risk."); + ImGui.Separator(); + UiSharedService.FontText("Notes", _uiShared.UidFont); if (UiSharedService.IconTextButton(FontAwesomeIcon.StickyNote, "Export all your user notes to clipboard")) { diff --git a/MareSynchronos/Utils/VariousExtensions.cs b/MareSynchronos/Utils/VariousExtensions.cs index 4b66828..9e66477 100644 --- a/MareSynchronos/Utils/VariousExtensions.cs +++ b/MareSynchronos/Utils/VariousExtensions.cs @@ -59,6 +59,7 @@ public static class VariousExtensions cachedPlayer, objectKind, hasNewButNotOldFileReplacements, hasOldButNotNewFileReplacements, hasNewButNotOldGlamourerData, hasOldButNotNewGlamourerData, PlayerChanges.ModFiles, PlayerChanges.Glamourer); charaDataToUpdate[objectKind].Add(PlayerChanges.ModFiles); charaDataToUpdate[objectKind].Add(PlayerChanges.Glamourer); + charaDataToUpdate[objectKind].Add(PlayerChanges.ForcedRedraw); } else { @@ -69,6 +70,43 @@ public static class VariousExtensions { logger.LogDebug("[BASE-{appBase}] Updating {object}/{kind} (FileReplacements not equal) => {change}", applicationBase, cachedPlayer, objectKind, PlayerChanges.ModFiles); charaDataToUpdate[objectKind].Add(PlayerChanges.ModFiles); + if (forceApplyMods || objectKind != ObjectKind.Player) + { + charaDataToUpdate[objectKind].Add(PlayerChanges.ForcedRedraw); + } + else + { + var existingFace = existingFileReplacements.Where(g => g.GamePaths.Any(p => p.Contains("/face/", StringComparison.OrdinalIgnoreCase))) + .OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList(); + var existingHair = existingFileReplacements.Where(g => g.GamePaths.Any(p => p.Contains("/hair/", StringComparison.OrdinalIgnoreCase))) + .OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList(); + var existingTail = existingFileReplacements.Where(g => g.GamePaths.Any(p => p.Contains("/tail/", StringComparison.OrdinalIgnoreCase))) + .OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList(); + var newFace = newFileReplacements.Where(g => g.GamePaths.Any(p => p.Contains("/face/", StringComparison.OrdinalIgnoreCase))) + .OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList(); + var newHair = newFileReplacements.Where(g => g.GamePaths.Any(p => p.Contains("/hair/", StringComparison.OrdinalIgnoreCase))) + .OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList(); + var newTail = newFileReplacements.Where(g => g.GamePaths.Any(p => p.Contains("/tail/", StringComparison.OrdinalIgnoreCase))) + .OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList(); + var existingTransients = existingFileReplacements.Where(g => g.GamePaths.Any(g => !g.EndsWith("mdl") && !g.EndsWith("tex") && !g.EndsWith("mtrl"))) + .OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList(); + var newTransients = newFileReplacements.Where(g => g.GamePaths.Any(g => !g.EndsWith("mdl") && !g.EndsWith("tex") && !g.EndsWith("mtrl"))) + .OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList(); + + logger.LogTrace("[BASE-{appbase}] ExistingFace: {of}, NewFace: {fc}; ExistingHair: {eh}, NewHair: {nh}; ExistingTail: {et}, NewTail: {nt}; ExistingTransient: {etr}, NewTransient: {ntr}", applicationBase, + existingFace.Count, newFace.Count, existingHair.Count, newHair.Count, existingTail.Count, newTail.Count, existingTransients.Count, newTransients.Count); + + var differentFace = !existingFace.SequenceEqual(newFace, PlayerData.Data.FileReplacementDataComparer.Instance); + var differentHair = !existingHair.SequenceEqual(newHair, PlayerData.Data.FileReplacementDataComparer.Instance); + var differentTail = !existingTail.SequenceEqual(newTail, PlayerData.Data.FileReplacementDataComparer.Instance); + var differenTransients = !existingTransients.SequenceEqual(newTransients, PlayerData.Data.FileReplacementDataComparer.Instance); + if (differentFace || differentHair || differentTail || differenTransients) + { + logger.LogDebug("[BASE-{appbase}] Different Subparts: Face: {face}, Hair: {hair}, Tail: {tail}, Transients: {transients} => {change}", applicationBase, + differentFace, differentHair, differentTail, differenTransients, PlayerChanges.ForcedRedraw); + charaDataToUpdate[objectKind].Add(PlayerChanges.ForcedRedraw); + } + } } } @@ -100,6 +138,7 @@ public static class VariousExtensions { logger.LogDebug("[BASE-{appBase}] Updating {object}/{kind} (Diff manip data) => {change}", applicationBase, cachedPlayer, objectKind, PlayerChanges.ModManip); charaDataToUpdate[objectKind].Add(PlayerChanges.ModManip); + charaDataToUpdate[objectKind].Add(PlayerChanges.ForcedRedraw); } bool heelsOffsetDifferent = !string.Equals(oldData.HeelsData, newData.HeelsData, StringComparison.Ordinal); @@ -109,7 +148,8 @@ public static class VariousExtensions charaDataToUpdate[objectKind].Add(PlayerChanges.Heels); } - bool palettePlusDataDifferent = !string.Equals(oldData.PalettePlusData, newData.PalettePlusData, StringComparison.Ordinal); + bool palettePlusDataDifferent = !string.Equals(oldData.PalettePlusData, newData.PalettePlusData, StringComparison.Ordinal) + || (charaDataToUpdate.TryGetValue(objectKind, out var playerChanges) && playerChanges.Contains(PlayerChanges.Glamourer)); if (palettePlusDataDifferent || (forceApplyCustomization && !string.IsNullOrEmpty(newData.PalettePlusData))) { logger.LogDebug("[BASE-{appBase}] Updating {object}/{kind} (Diff palette data) => {change}", applicationBase, cachedPlayer, objectKind, PlayerChanges.Palette); From 5013f22e187b8b0c7c80b9202b38d5f87da95904 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Wed, 25 Oct 2023 16:37:49 +0200 Subject: [PATCH 23/56] wording --- MareSynchronos/UI/SettingsUi.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MareSynchronos/UI/SettingsUi.cs b/MareSynchronos/UI/SettingsUi.cs index 6dc32b0..eeda8ae 100644 --- a/MareSynchronos/UI/SettingsUi.cs +++ b/MareSynchronos/UI/SettingsUi.cs @@ -517,12 +517,12 @@ public class SettingsUi : WindowMediatorSubscriberBase _lastTab = "General"; UiSharedService.FontText("General Settings", _uiShared.UidFont); bool lessRedraws = _configService.Current.UseLessRedraws; - if (ImGui.Checkbox("[Experimental] Use less redraws", ref lessRedraws)) + if (ImGui.Checkbox("[Experimental] Use fewer redraws", ref lessRedraws)) { _configService.Current.UseLessRedraws = lessRedraws; _configService.Save(); } - UiSharedService.DrawHelpText("This will attempt to use less redraws. Changes that solely affect the players body appearance (i.e. clothes) should not cause a redraw anymore." + Environment.NewLine + + UiSharedService.DrawHelpText("This will attempt to use fewer redraws. Changes that solely affect the players body appearance (i.e. clothes) should not cause a redraw anymore." + Environment.NewLine + "Some changes, especially to hair, face, tail or any vfx, animation or skeleton changes, or class changes will still force a redraw." + Environment.NewLine + Environment.NewLine + "WARNING: this is an experimental, little tested feature and can potentially lead to issues with animation state or crashes. Use at your own risk."); ImGui.Separator(); From f7fca75e24cb157fdf74b647e9e3e0e63ae4cca9 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Wed, 25 Oct 2023 17:14:04 +0200 Subject: [PATCH 24/56] fix palette --- MareSynchronos/Utils/VariousExtensions.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MareSynchronos/Utils/VariousExtensions.cs b/MareSynchronos/Utils/VariousExtensions.cs index 9e66477..aeff6b4 100644 --- a/MareSynchronos/Utils/VariousExtensions.cs +++ b/MareSynchronos/Utils/VariousExtensions.cs @@ -149,7 +149,8 @@ public static class VariousExtensions } bool palettePlusDataDifferent = !string.Equals(oldData.PalettePlusData, newData.PalettePlusData, StringComparison.Ordinal) - || (charaDataToUpdate.TryGetValue(objectKind, out var playerChanges) && playerChanges.Contains(PlayerChanges.Glamourer)); + || (charaDataToUpdate.TryGetValue(objectKind, out var playerChanges) && playerChanges.Contains(PlayerChanges.Glamourer) + && (!string.IsNullOrEmpty(oldData.PalettePlusData) || !string.IsNullOrEmpty(newData.PalettePlusData))); if (palettePlusDataDifferent || (forceApplyCustomization && !string.IsNullOrEmpty(newData.PalettePlusData))) { logger.LogDebug("[BASE-{appBase}] Updating {object}/{kind} (Diff palette data) => {change}", applicationBase, cachedPlayer, objectKind, PlayerChanges.Palette); From 12c5448b4719aa0bd329b980adbb93a51c345a68 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Thu, 26 Oct 2023 12:04:53 +0200 Subject: [PATCH 25/56] wait for plugin disposal --- MareSynchronos/Plugin.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MareSynchronos/Plugin.cs b/MareSynchronos/Plugin.cs index cb9cb70..67846c8 100644 --- a/MareSynchronos/Plugin.cs +++ b/MareSynchronos/Plugin.cs @@ -29,12 +29,13 @@ namespace MareSynchronos; public sealed class Plugin : IDalamudPlugin { private readonly CancellationTokenSource _pluginCts = new(); + private Task _hostBuilderRunTask; public Plugin(DalamudPluginInterface pluginInterface, ICommandManager commandManager, IDataManager gameData, IFramework framework, IObjectTable objectTable, IClientState clientState, ICondition condition, IChatGui chatGui, IGameGui gameGui, IDtrBar dtrBar, IPluginLog pluginLog) { - _ = new HostBuilder() + _hostBuilderRunTask = new HostBuilder() .UseContentRoot(pluginInterface.ConfigDirectory.FullName) .ConfigureLogging(lb => { @@ -153,5 +154,6 @@ public sealed class Plugin : IDalamudPlugin { _pluginCts.Cancel(); _pluginCts.Dispose(); + Task.WaitAny(_hostBuilderRunTask); } } \ No newline at end of file From 6e33d5c5b48479a2dcdb64370ee647a4469d45e8 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Thu, 26 Oct 2023 12:09:20 +0200 Subject: [PATCH 26/56] readonly bla --- MareSynchronos/Plugin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MareSynchronos/Plugin.cs b/MareSynchronos/Plugin.cs index 67846c8..755e387 100644 --- a/MareSynchronos/Plugin.cs +++ b/MareSynchronos/Plugin.cs @@ -29,7 +29,7 @@ namespace MareSynchronos; public sealed class Plugin : IDalamudPlugin { private readonly CancellationTokenSource _pluginCts = new(); - private Task _hostBuilderRunTask; + private readonly Task _hostBuilderRunTask; public Plugin(DalamudPluginInterface pluginInterface, ICommandManager commandManager, IDataManager gameData, IFramework framework, IObjectTable objectTable, IClientState clientState, ICondition condition, IChatGui chatGui, From 58276e985d00a39f1a0a0ba9796d7a927c00d097 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Thu, 26 Oct 2023 17:25:10 +0200 Subject: [PATCH 27/56] do not allow to open main ui without finishing setup --- MareSynchronos/Services/CommandManagerService.cs | 10 ++++++++-- MareSynchronos/Services/UiService.cs | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/MareSynchronos/Services/CommandManagerService.cs b/MareSynchronos/Services/CommandManagerService.cs index 1c2688a..6e4034f 100644 --- a/MareSynchronos/Services/CommandManagerService.cs +++ b/MareSynchronos/Services/CommandManagerService.cs @@ -1,6 +1,7 @@ using Dalamud.Game.Command; using Dalamud.Plugin.Services; using MareSynchronos.FileCache; +using MareSynchronos.MareConfiguration; using MareSynchronos.Services.Mediator; using MareSynchronos.Services.ServerConfiguration; using MareSynchronos.UI; @@ -16,13 +17,14 @@ public sealed class CommandManagerService : IDisposable private readonly ApiController _apiController; private readonly ICommandManager _commandManager; private readonly MareMediator _mediator; + private readonly MareConfigService _mareConfigService; private readonly PerformanceCollectorService _performanceCollectorService; private readonly PeriodicFileScanner _periodicFileScanner; private readonly ServerConfigurationManager _serverConfigurationManager; public CommandManagerService(ICommandManager commandManager, PerformanceCollectorService performanceCollectorService, ServerConfigurationManager serverConfigurationManager, PeriodicFileScanner periodicFileScanner, - ApiController apiController, MareMediator mediator) + ApiController apiController, MareMediator mediator, MareConfigService mareConfigService) { _commandManager = commandManager; _performanceCollectorService = performanceCollectorService; @@ -30,6 +32,7 @@ public sealed class CommandManagerService : IDisposable _periodicFileScanner = periodicFileScanner; _apiController = apiController; _mediator = mediator; + _mareConfigService = mareConfigService; _commandManager.AddHandler(_commandName, new CommandInfo(OnCommand) { HelpMessage = "Opens the Mare Synchronos UI" @@ -48,7 +51,10 @@ public sealed class CommandManagerService : IDisposable if (splitArgs == null || splitArgs.Length == 0) { // Interpret this as toggling the UI - _mediator.Publish(new UiToggleMessage(typeof(CompactUi))); + if (_mareConfigService.Current.HasValidSetup()) + _mediator.Publish(new UiToggleMessage(typeof(CompactUi))); + else + _mediator.Publish(new UiToggleMessage(typeof(IntroUi))); return; } diff --git a/MareSynchronos/Services/UiService.cs b/MareSynchronos/Services/UiService.cs index 60565a7..7ae2efb 100644 --- a/MareSynchronos/Services/UiService.cs +++ b/MareSynchronos/Services/UiService.cs @@ -90,6 +90,7 @@ public sealed class UiService : DisposableMediatorSubscriberBase _dalamudPluginInterface.UiBuilder.Draw -= Draw; _dalamudPluginInterface.UiBuilder.OpenConfigUi -= ToggleUi; + _dalamudPluginInterface.UiBuilder.OpenMainUi -= ToggleMainUi; } private void Draw() From d4bc0c734d7b829c1c704c822e2725b805f839bb Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Fri, 27 Oct 2023 00:44:13 +0200 Subject: [PATCH 28/56] remove close button on intro ui --- MareSynchronos/UI/IntroUI.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MareSynchronos/UI/IntroUI.cs b/MareSynchronos/UI/IntroUI.cs index 96d5247..696e093 100644 --- a/MareSynchronos/UI/IntroUI.cs +++ b/MareSynchronos/UI/IntroUI.cs @@ -37,6 +37,8 @@ public class IntroUi : WindowMediatorSubscriberBase _serverConfigurationManager = serverConfigurationManager; IsOpen = false; + ShowCloseButton = false; + RespectCloseHotkey = false; SizeConstraints = new WindowSizeConstraints() { From a8bc5386ea73b7f690543e8284503fa84d50d6a8 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Fri, 27 Oct 2023 01:59:12 +0200 Subject: [PATCH 29/56] make syncshell admin ui to standalone window --- MareSynchronos/Plugin.cs | 16 +- MareSynchronos/Services/Mediator/Messages.cs | 2 +- MareSynchronos/Services/UiService.cs | 19 +- .../UI/Components/DrawFolderGroup.cs | 2 +- .../UI/Components/Popup/PopupHandler.cs | 12 +- .../Popup/SyncshellAdminPopupHandler.cs | 234 ---------------- MareSynchronos/UI/SyncshellAdminUI.cs | 252 ++++++++++++++++++ 7 files changed, 287 insertions(+), 250 deletions(-) delete mode 100644 MareSynchronos/UI/Components/Popup/SyncshellAdminPopupHandler.cs create mode 100644 MareSynchronos/UI/SyncshellAdminUI.cs diff --git a/MareSynchronos/Plugin.cs b/MareSynchronos/Plugin.cs index 755e387..138dacd 100644 --- a/MareSynchronos/Plugin.cs +++ b/MareSynchronos/Plugin.cs @@ -3,6 +3,7 @@ using Dalamud.Interface.ImGuiFileDialog; using Dalamud.Interface.Windowing; using Dalamud.Plugin; using Dalamud.Plugin.Services; +using MareSynchronos.API.Dto.Group; using MareSynchronos.FileCache; using MareSynchronos.Interop; using MareSynchronos.MareConfiguration; @@ -103,6 +104,14 @@ public sealed class Plugin : IDalamudPlugin s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), pair))); + collection.AddSingleton(s => + new Func((dto) => + new SyncshellAdminUI(s.GetRequiredService>(), + s.GetRequiredService(), + dto, + s.GetRequiredService(), + s.GetRequiredService(), + s.GetRequiredService()))); // add scoped services collection.AddScoped(); @@ -122,17 +131,18 @@ public sealed class Plugin : IDalamudPlugin collection.AddScoped(); collection.AddScoped(); collection.AddScoped(); - collection.AddScoped(); collection.AddScoped(); collection.AddScoped(); collection.AddScoped(); collection.AddScoped(); collection.AddScoped((s) => new UiService(s.GetRequiredService>(), pluginInterface, s.GetRequiredService(), - s.GetRequiredService(), s.GetServices(), s.GetRequiredService>(), + s.GetRequiredService(), s.GetServices(), + s.GetRequiredService>(), + s.GetRequiredService>(), s.GetRequiredService(), s.GetRequiredService())); collection.AddScoped((s) => new CommandManagerService(commandManager, s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), - s.GetRequiredService())); + s.GetRequiredService(), s.GetRequiredService())); collection.AddScoped((s) => new NotificationService(s.GetRequiredService>(), s.GetRequiredService(), pluginInterface.UiBuilder, chatGui, s.GetRequiredService())); collection.AddScoped((s) => new UiSharedService(s.GetRequiredService>(), s.GetRequiredService(), s.GetRequiredService(), diff --git a/MareSynchronos/Services/Mediator/Messages.cs b/MareSynchronos/Services/Mediator/Messages.cs index c814c26..943433c 100644 --- a/MareSynchronos/Services/Mediator/Messages.cs +++ b/MareSynchronos/Services/Mediator/Messages.cs @@ -72,7 +72,7 @@ public record PairHandlerVisibleMessage(PairHandler Player) : MessageBase; public record RefreshUiMessage : MessageBase; public record OpenReportPopupMessage(Pair PairToReport) : MessageBase; public record OpenBanUserPopupMessage(Pair PairToBan, GroupFullInfoDto GroupFullInfoDto) : MessageBase; -public record OpenSyncshellAdminPanelPopupMessage(GroupFullInfoDto GroupInfo) : MessageBase; +public record OpenSyncshellAdminPanel(GroupFullInfoDto GroupInfo) : MessageBase; #pragma warning restore S2094 #pragma warning restore MA0048 // File name must match type name \ No newline at end of file diff --git a/MareSynchronos/Services/UiService.cs b/MareSynchronos/Services/UiService.cs index 7ae2efb..eb72605 100644 --- a/MareSynchronos/Services/UiService.cs +++ b/MareSynchronos/Services/UiService.cs @@ -1,10 +1,12 @@ using Dalamud.Interface.ImGuiFileDialog; using Dalamud.Interface.Windowing; using Dalamud.Plugin; +using MareSynchronos.API.Dto.Group; using MareSynchronos.MareConfiguration; using MareSynchronos.PlayerData.Pairs; using MareSynchronos.Services.Mediator; using MareSynchronos.UI; +using MareSynchronos.UI.Components.Popup; using Microsoft.Extensions.Logging; namespace MareSynchronos.Services; @@ -17,10 +19,13 @@ public sealed class UiService : DisposableMediatorSubscriberBase private readonly ILogger _logger; private readonly MareConfigService _mareConfigService; private readonly WindowSystem _windowSystem; + private readonly Func _syncshellAdminUiFactory; public UiService(ILogger logger, DalamudPluginInterface dalamudPluginInterface, MareConfigService mareConfigService, WindowSystem windowSystem, - IEnumerable windows, Func standaloneProfileUiFactory, + IEnumerable windows, + Func standaloneProfileUiFactory, + Func syncshellAdminUiFactory, FileDialogManager fileDialogManager, MareMediator mareMediator) : base(logger, mareMediator) { _logger = logger; @@ -28,6 +33,7 @@ public sealed class UiService : DisposableMediatorSubscriberBase _dalamudPluginInterface = dalamudPluginInterface; _mareConfigService = mareConfigService; _windowSystem = windowSystem; + _syncshellAdminUiFactory = syncshellAdminUiFactory; _fileDialogManager = fileDialogManager; _dalamudPluginInterface.UiBuilder.DisableGposeUiHide = true; @@ -51,6 +57,17 @@ public sealed class UiService : DisposableMediatorSubscriberBase } }); + Mediator.Subscribe(this, (msg) => + { + if (!_createdWindows.Exists(p => p is SyncshellAdminUI ui + && string.Equals(ui.GroupFullInfo.GID, msg.GroupInfo.GID, StringComparison.Ordinal))) + { + var window = _syncshellAdminUiFactory(msg.GroupInfo); + _createdWindows.Add(window); + _windowSystem.AddWindow(window); + } + }); + Mediator.Subscribe(this, (msg) => { _windowSystem.RemoveWindow(msg.Window); diff --git a/MareSynchronos/UI/Components/DrawFolderGroup.cs b/MareSynchronos/UI/Components/DrawFolderGroup.cs index 85e8c38..1151b3b 100644 --- a/MareSynchronos/UI/Components/DrawFolderGroup.cs +++ b/MareSynchronos/UI/Components/DrawFolderGroup.cs @@ -160,7 +160,7 @@ public class DrawFolderGroup : DrawFolderBase if (UiSharedService.IconTextButton(FontAwesomeIcon.Cog, "Open Admin Panel", menuWidth, true)) { ImGui.CloseCurrentPopup(); - _mareMediator.Publish(new OpenSyncshellAdminPanelPopupMessage(_groupFullInfoDto)); + _mareMediator.Publish(new OpenSyncshellAdminPanel(_groupFullInfoDto)); } } } diff --git a/MareSynchronos/UI/Components/Popup/PopupHandler.cs b/MareSynchronos/UI/Components/Popup/PopupHandler.cs index 8359e97..8bd9b2f 100644 --- a/MareSynchronos/UI/Components/Popup/PopupHandler.cs +++ b/MareSynchronos/UI/Components/Popup/PopupHandler.cs @@ -1,4 +1,5 @@ using Dalamud.Interface; +using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using ImGuiNET; using MareSynchronos.Services.Mediator; @@ -43,15 +44,6 @@ public class PopupHandler : WindowMediatorSubscriberBase ((BanUserPopupHandler)_currentHandler).Open(msg); IsOpen = true; }); - - Mediator.Subscribe(this, (msg) => - { - IsOpen = true; - _openPopup = true; - _currentHandler = _handlers.OfType().Single(); - ((SyncshellAdminPopupHandler)_currentHandler).Open(msg.GroupInfo); - IsOpen = true; - }); } public override void Draw() @@ -65,7 +57,7 @@ public class PopupHandler : WindowMediatorSubscriberBase } var viewportSize = ImGui.GetWindowViewport().Size; - ImGui.SetNextWindowSize(_currentHandler!.PopupSize); + ImGui.SetNextWindowSize(_currentHandler!.PopupSize * ImGuiHelpers.GlobalScale); ImGui.SetNextWindowPos(viewportSize / 2, ImGuiCond.Always, new Vector2(0.5f)); using var popup = ImRaii.Popup(WindowName, ImGuiWindowFlags.Modal); if (!popup) return; diff --git a/MareSynchronos/UI/Components/Popup/SyncshellAdminPopupHandler.cs b/MareSynchronos/UI/Components/Popup/SyncshellAdminPopupHandler.cs deleted file mode 100644 index 7fbdbb6..0000000 --- a/MareSynchronos/UI/Components/Popup/SyncshellAdminPopupHandler.cs +++ /dev/null @@ -1,234 +0,0 @@ -using Dalamud.Interface; -using Dalamud.Interface.Colors; -using Dalamud.Interface.Utility.Raii; -using ImGuiNET; -using MareSynchronos.API.Data.Extensions; -using MareSynchronos.API.Dto.Group; -using MareSynchronos.PlayerData.Pairs; -using MareSynchronos.WebAPI; -using System.Globalization; -using System.Numerics; - -namespace MareSynchronos.UI.Components.Popup; - -internal class SyncshellAdminPopupHandler : IPopupHandler -{ - private readonly ApiController _apiController; - private readonly List _oneTimeInvites = []; - private readonly PairManager _pairManager; - private readonly UiSharedService _uiSharedService; - private List _bannedUsers = []; - private GroupFullInfoDto _groupFullInfo = null!; - private bool _isModerator = false; - private bool _isOwner = false; - private int _multiInvites = 30; - private string _newPassword = string.Empty; - private bool _pwChangeSuccess = true; - - public SyncshellAdminPopupHandler(ApiController apiController, UiSharedService uiSharedService, PairManager pairManager) - { - _apiController = apiController; - _uiSharedService = uiSharedService; - _pairManager = pairManager; - } - - public Vector2 PopupSize => new(700, 500); - - public void DrawContent() - { - if (!_isModerator && !_isOwner) return; - - _groupFullInfo = _pairManager.Groups[_groupFullInfo.Group]; - - using (ImRaii.PushFont(_uiSharedService.UidFont)) - ImGui.TextUnformatted(_groupFullInfo.GroupAliasOrGID + " Administrative Panel"); - - ImGui.Separator(); - var perm = _groupFullInfo.GroupPermissions; - - var inviteNode = ImRaii.TreeNode("Invites"); - if (inviteNode) - { - bool isInvitesDisabled = perm.IsDisableInvites(); - - if (UiSharedService.IconTextButton(isInvitesDisabled ? FontAwesomeIcon.Unlock : FontAwesomeIcon.Lock, - isInvitesDisabled ? "Unlock Syncshell" : "Lock Syncshell")) - { - perm.SetDisableInvites(!isInvitesDisabled); - _ = _apiController.GroupChangeGroupPermissionState(new(_groupFullInfo.Group, perm)); - } - - ImGui.Dummy(new(2f)); - - UiSharedService.TextWrapped("One-time invites work as single-use passwords. Use those if you do not want to distribute your Syncshell password."); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Envelope, "Single one-time invite")) - { - ImGui.SetClipboardText(_apiController.GroupCreateTempInvite(new(_groupFullInfo.Group), 1).Result.FirstOrDefault() ?? string.Empty); - } - UiSharedService.AttachToolTip("Creates a single-use password for joining the syncshell which is valid for 24h and copies it to the clipboard."); - ImGui.InputInt("##amountofinvites", ref _multiInvites); - ImGui.SameLine(); - using (ImRaii.Disabled(_multiInvites <= 1 || _multiInvites > 100)) - { - if (UiSharedService.IconTextButton(FontAwesomeIcon.Envelope, "Generate " + _multiInvites + " one-time invites")) - { - _oneTimeInvites.AddRange(_apiController.GroupCreateTempInvite(new(_groupFullInfo.Group), _multiInvites).Result); - } - } - - if (_oneTimeInvites.Any()) - { - var invites = string.Join(Environment.NewLine, _oneTimeInvites); - ImGui.InputTextMultiline("Generated Multi Invites", ref invites, 5000, new(0, 0), ImGuiInputTextFlags.ReadOnly); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Copy, "Copy Invites to clipboard")) - { - ImGui.SetClipboardText(invites); - } - } - } - inviteNode.Dispose(); - - var mgmtNode = ImRaii.TreeNode("User Management"); - if (mgmtNode) - { - if (UiSharedService.IconTextButton(FontAwesomeIcon.Broom, "Clear Syncshell")) - { - _ = _apiController.GroupClear(new(_groupFullInfo.Group)); - } - UiSharedService.AttachToolTip("This will remove all non-pinned, non-moderator users from the Syncshell"); - - ImGui.Dummy(new(2f)); - - if (UiSharedService.IconTextButton(FontAwesomeIcon.Retweet, "Refresh Banlist from Server")) - { - _bannedUsers = _apiController.GroupGetBannedUsers(new GroupDto(_groupFullInfo.Group)).Result; - } - - if (ImGui.BeginTable("bannedusertable" + _groupFullInfo.GID, 6, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.ScrollY)) - { - ImGui.TableSetupColumn("UID", ImGuiTableColumnFlags.None, 1); - ImGui.TableSetupColumn("Alias", ImGuiTableColumnFlags.None, 1); - ImGui.TableSetupColumn("By", ImGuiTableColumnFlags.None, 1); - ImGui.TableSetupColumn("Date", ImGuiTableColumnFlags.None, 2); - ImGui.TableSetupColumn("Reason", ImGuiTableColumnFlags.None, 3); - ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.None, 1); - - ImGui.TableHeadersRow(); - - foreach (var bannedUser in _bannedUsers.ToList()) - { - ImGui.TableNextColumn(); - ImGui.TextUnformatted(bannedUser.UID); - ImGui.TableNextColumn(); - ImGui.TextUnformatted(bannedUser.UserAlias ?? string.Empty); - ImGui.TableNextColumn(); - ImGui.TextUnformatted(bannedUser.BannedBy); - ImGui.TableNextColumn(); - ImGui.TextUnformatted(bannedUser.BannedOn.ToLocalTime().ToString(CultureInfo.CurrentCulture)); - ImGui.TableNextColumn(); - UiSharedService.TextWrapped(bannedUser.Reason); - ImGui.TableNextColumn(); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Check, "Unban#" + bannedUser.UID)) - { - _ = _apiController.GroupUnbanUser(bannedUser); - _bannedUsers.RemoveAll(b => string.Equals(b.UID, bannedUser.UID, StringComparison.Ordinal)); - } - } - - ImGui.EndTable(); - } - } - mgmtNode.Dispose(); - - var permNode = ImRaii.TreeNode("Permissions"); - if (permNode) - { - bool isDisableAnimations = perm.IsPreferDisableAnimations(); - bool isDisableSounds = perm.IsPreferDisableSounds(); - bool isDisableVfx = perm.IsPreferDisableVFX(); - - ImGui.AlignTextToFramePadding(); - ImGui.Text("Suggest Sound Sync"); - UiSharedService.BooleanToColoredIcon(!isDisableSounds); - ImGui.SameLine(230); - if (UiSharedService.IconTextButton(isDisableSounds ? FontAwesomeIcon.VolumeUp : FontAwesomeIcon.VolumeMute, - isDisableSounds ? "Suggest to enable sound sync" : "Suggest to disable sound sync")) - { - perm.SetPreferDisableSounds(!perm.IsPreferDisableSounds()); - _ = _apiController.GroupChangeGroupPermissionState(new(_groupFullInfo.Group, perm)); - } - - ImGui.AlignTextToFramePadding(); - ImGui.Text("Suggest Animation Sync"); - UiSharedService.BooleanToColoredIcon(!isDisableAnimations); - ImGui.SameLine(230); - if (UiSharedService.IconTextButton(isDisableAnimations ? FontAwesomeIcon.Running : FontAwesomeIcon.Stop, - isDisableAnimations ? "Suggest to enable animation sync" : "Suggest to disable animation sync")) - { - perm.SetPreferDisableAnimations(!perm.IsPreferDisableAnimations()); - _ = _apiController.GroupChangeGroupPermissionState(new(_groupFullInfo.Group, perm)); - } - - ImGui.AlignTextToFramePadding(); - ImGui.Text("Suggest VFX Sync"); - UiSharedService.BooleanToColoredIcon(!isDisableVfx); - ImGui.SameLine(230); - if (UiSharedService.IconTextButton(isDisableVfx ? FontAwesomeIcon.Sun : FontAwesomeIcon.Circle, - isDisableVfx ? "Suggest to enable vfx sync" : "Suggest to disable vfx sync")) - { - perm.SetPreferDisableVFX(!perm.IsPreferDisableVFX()); - _ = _apiController.GroupChangeGroupPermissionState(new(_groupFullInfo.Group, perm)); - } - - UiSharedService.TextWrapped("Note: those suggested permissions will be shown to users on joining the Syncshell."); - } - permNode.Dispose(); - - if (_isOwner) - { - var ownerNode = ImRaii.TreeNode("Owner Settings"); - if (ownerNode) - { - ImGui.AlignTextToFramePadding(); - ImGui.TextUnformatted("New Password"); - ImGui.SameLine(); - ImGui.InputTextWithHint("##changepw", "Min 10 characters", ref _newPassword, 50); - ImGui.SameLine(); - using (ImRaii.Disabled(_newPassword.Length < 10)) - { - if (UiSharedService.IconTextButton(FontAwesomeIcon.Passport, "Change Password")) - { - _pwChangeSuccess = _apiController.GroupChangePassword(new GroupPasswordDto(_groupFullInfo.Group, _newPassword)).Result; - _newPassword = string.Empty; - } - } - UiSharedService.AttachToolTip("Password requires to be at least 10 characters long. This action is irreversible."); - - if (!_pwChangeSuccess) - { - UiSharedService.ColorTextWrapped("Failed to change the password. Password requires to be at least 10 characters long.", ImGuiColors.DalamudYellow); - } - - if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete Syncshell") && UiSharedService.CtrlPressed() && UiSharedService.ShiftPressed()) - { - ImGui.CloseCurrentPopup(); - _ = _apiController.GroupDelete(new(_groupFullInfo.Group)); - } - UiSharedService.AttachToolTip("Hold CTRL and Shift and click to delete this Syncshell." + Environment.NewLine + "WARNING: this action is irreversible."); - } - ownerNode.Dispose(); - } - } - - public void Open(GroupFullInfoDto groupFullInfo) - { - _groupFullInfo = groupFullInfo; - _isOwner = string.Equals(_groupFullInfo.OwnerUID, _apiController.UID, StringComparison.Ordinal); - _isModerator = _groupFullInfo.GroupUserInfo.IsModerator(); - _newPassword = string.Empty; - _bannedUsers.Clear(); - _oneTimeInvites.Clear(); - _multiInvites = 30; - _pwChangeSuccess = true; - } -} \ No newline at end of file diff --git a/MareSynchronos/UI/SyncshellAdminUI.cs b/MareSynchronos/UI/SyncshellAdminUI.cs new file mode 100644 index 0000000..57f3a7a --- /dev/null +++ b/MareSynchronos/UI/SyncshellAdminUI.cs @@ -0,0 +1,252 @@ +using Dalamud.Interface; +using Dalamud.Interface.Colors; +using Dalamud.Interface.Utility.Raii; +using ImGuiNET; +using MareSynchronos.API.Data.Extensions; +using MareSynchronos.API.Dto.Group; +using MareSynchronos.PlayerData.Pairs; +using MareSynchronos.Services.Mediator; +using MareSynchronos.WebAPI; +using Microsoft.Extensions.Logging; +using System.Globalization; + +namespace MareSynchronos.UI.Components.Popup; + +public class SyncshellAdminUI : WindowMediatorSubscriberBase +{ + private readonly ApiController _apiController; + private readonly bool _isModerator = false; + private readonly bool _isOwner = false; + private readonly List _oneTimeInvites = []; + private readonly PairManager _pairManager; + private readonly UiSharedService _uiSharedService; + private List _bannedUsers = []; + private int _multiInvites; + private string _newPassword; + private bool _pwChangeSuccess; + public SyncshellAdminUI(ILogger logger, MareMediator mediator, GroupFullInfoDto groupFullInfo, ApiController apiController, UiSharedService uiSharedService, PairManager pairManager) + : base(logger, mediator, "Syncshell Admin Panel (" + groupFullInfo.GID + ")") + { + GroupFullInfo = groupFullInfo; + _apiController = apiController; + _uiSharedService = uiSharedService; + _pairManager = pairManager; + _isOwner = string.Equals(GroupFullInfo.OwnerUID, _apiController.UID, StringComparison.Ordinal); + _isModerator = GroupFullInfo.GroupUserInfo.IsModerator(); + _newPassword = string.Empty; + _multiInvites = 30; + _pwChangeSuccess = true; + IsOpen = true; + SizeConstraints = new WindowSizeConstraints() + { + MinimumSize = new(700, 500), + MaximumSize = new(700, 2000), + }; + } + + public GroupFullInfoDto GroupFullInfo { get; private set; } + + public override void Draw() + { + if (!_isModerator && !_isOwner) return; + + GroupFullInfo = _pairManager.Groups[GroupFullInfo.Group]; + + using var id = ImRaii.PushId("syncshell_admin_" + GroupFullInfo.GID); + + using (ImRaii.PushFont(_uiSharedService.UidFont)) + ImGui.TextUnformatted(GroupFullInfo.GroupAliasOrGID + " Administrative Panel"); + + ImGui.Separator(); + var perm = GroupFullInfo.GroupPermissions; + + using var tabbar = ImRaii.TabBar("syncshell_tab_" + GroupFullInfo.GID); + + if (tabbar) + { + var inviteTab = ImRaii.TabItem("Invites"); + if (inviteTab) + { + bool isInvitesDisabled = perm.IsDisableInvites(); + + if (UiSharedService.IconTextButton(isInvitesDisabled ? FontAwesomeIcon.Unlock : FontAwesomeIcon.Lock, + isInvitesDisabled ? "Unlock Syncshell" : "Lock Syncshell")) + { + perm.SetDisableInvites(!isInvitesDisabled); + _ = _apiController.GroupChangeGroupPermissionState(new(GroupFullInfo.Group, perm)); + } + + ImGui.Dummy(new(2f)); + + UiSharedService.TextWrapped("One-time invites work as single-use passwords. Use those if you do not want to distribute your Syncshell password."); + if (UiSharedService.IconTextButton(FontAwesomeIcon.Envelope, "Single one-time invite")) + { + ImGui.SetClipboardText(_apiController.GroupCreateTempInvite(new(GroupFullInfo.Group), 1).Result.FirstOrDefault() ?? string.Empty); + } + UiSharedService.AttachToolTip("Creates a single-use password for joining the syncshell which is valid for 24h and copies it to the clipboard."); + ImGui.InputInt("##amountofinvites", ref _multiInvites); + ImGui.SameLine(); + using (ImRaii.Disabled(_multiInvites <= 1 || _multiInvites > 100)) + { + if (UiSharedService.IconTextButton(FontAwesomeIcon.Envelope, "Generate " + _multiInvites + " one-time invites")) + { + _oneTimeInvites.AddRange(_apiController.GroupCreateTempInvite(new(GroupFullInfo.Group), _multiInvites).Result); + } + } + + if (_oneTimeInvites.Any()) + { + var invites = string.Join(Environment.NewLine, _oneTimeInvites); + ImGui.InputTextMultiline("Generated Multi Invites", ref invites, 5000, new(0, 0), ImGuiInputTextFlags.ReadOnly); + if (UiSharedService.IconTextButton(FontAwesomeIcon.Copy, "Copy Invites to clipboard")) + { + ImGui.SetClipboardText(invites); + } + } + } + inviteTab.Dispose(); + + var mgmtTab = ImRaii.TabItem("User Management"); + if (mgmtTab) + { + if (UiSharedService.IconTextButton(FontAwesomeIcon.Broom, "Clear Syncshell")) + { + _ = _apiController.GroupClear(new(GroupFullInfo.Group)); + } + UiSharedService.AttachToolTip("This will remove all non-pinned, non-moderator users from the Syncshell"); + + ImGui.Dummy(new(2f)); + + if (UiSharedService.IconTextButton(FontAwesomeIcon.Retweet, "Refresh Banlist from Server")) + { + _bannedUsers = _apiController.GroupGetBannedUsers(new GroupDto(GroupFullInfo.Group)).Result; + } + + if (ImGui.BeginTable("bannedusertable" + GroupFullInfo.GID, 6, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.ScrollY)) + { + ImGui.TableSetupColumn("UID", ImGuiTableColumnFlags.None, 1); + ImGui.TableSetupColumn("Alias", ImGuiTableColumnFlags.None, 1); + ImGui.TableSetupColumn("By", ImGuiTableColumnFlags.None, 1); + ImGui.TableSetupColumn("Date", ImGuiTableColumnFlags.None, 2); + ImGui.TableSetupColumn("Reason", ImGuiTableColumnFlags.None, 3); + ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.None, 1); + + ImGui.TableHeadersRow(); + + foreach (var bannedUser in _bannedUsers.ToList()) + { + ImGui.TableNextColumn(); + ImGui.TextUnformatted(bannedUser.UID); + ImGui.TableNextColumn(); + ImGui.TextUnformatted(bannedUser.UserAlias ?? string.Empty); + ImGui.TableNextColumn(); + ImGui.TextUnformatted(bannedUser.BannedBy); + ImGui.TableNextColumn(); + ImGui.TextUnformatted(bannedUser.BannedOn.ToLocalTime().ToString(CultureInfo.CurrentCulture)); + ImGui.TableNextColumn(); + UiSharedService.TextWrapped(bannedUser.Reason); + ImGui.TableNextColumn(); + if (UiSharedService.IconTextButton(FontAwesomeIcon.Check, "Unban#" + bannedUser.UID)) + { + _ = _apiController.GroupUnbanUser(bannedUser); + _bannedUsers.RemoveAll(b => string.Equals(b.UID, bannedUser.UID, StringComparison.Ordinal)); + } + } + + ImGui.EndTable(); + } + } + mgmtTab.Dispose(); + + var permissionTab = ImRaii.TabItem("Permissions"); + if (permissionTab) + { + bool isDisableAnimations = perm.IsPreferDisableAnimations(); + bool isDisableSounds = perm.IsPreferDisableSounds(); + bool isDisableVfx = perm.IsPreferDisableVFX(); + + ImGui.AlignTextToFramePadding(); + ImGui.Text("Suggest Sound Sync"); + UiSharedService.BooleanToColoredIcon(!isDisableSounds); + ImGui.SameLine(230); + if (UiSharedService.IconTextButton(isDisableSounds ? FontAwesomeIcon.VolumeUp : FontAwesomeIcon.VolumeMute, + isDisableSounds ? "Suggest to enable sound sync" : "Suggest to disable sound sync")) + { + perm.SetPreferDisableSounds(!perm.IsPreferDisableSounds()); + _ = _apiController.GroupChangeGroupPermissionState(new(GroupFullInfo.Group, perm)); + } + + ImGui.AlignTextToFramePadding(); + ImGui.Text("Suggest Animation Sync"); + UiSharedService.BooleanToColoredIcon(!isDisableAnimations); + ImGui.SameLine(230); + if (UiSharedService.IconTextButton(isDisableAnimations ? FontAwesomeIcon.Running : FontAwesomeIcon.Stop, + isDisableAnimations ? "Suggest to enable animation sync" : "Suggest to disable animation sync")) + { + perm.SetPreferDisableAnimations(!perm.IsPreferDisableAnimations()); + _ = _apiController.GroupChangeGroupPermissionState(new(GroupFullInfo.Group, perm)); + } + + ImGui.AlignTextToFramePadding(); + ImGui.Text("Suggest VFX Sync"); + UiSharedService.BooleanToColoredIcon(!isDisableVfx); + ImGui.SameLine(230); + if (UiSharedService.IconTextButton(isDisableVfx ? FontAwesomeIcon.Sun : FontAwesomeIcon.Circle, + isDisableVfx ? "Suggest to enable vfx sync" : "Suggest to disable vfx sync")) + { + perm.SetPreferDisableVFX(!perm.IsPreferDisableVFX()); + _ = _apiController.GroupChangeGroupPermissionState(new(GroupFullInfo.Group, perm)); + } + + UiSharedService.TextWrapped("Note: those suggested permissions will be shown to users on joining the Syncshell."); + } + permissionTab.Dispose(); + + if (_isOwner) + { + var ownerTab = ImRaii.TabItem("Owner Settings"); + if (ownerTab) + { + ImGui.AlignTextToFramePadding(); + ImGui.TextUnformatted("New Password"); + var availableWidth = ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X; + var buttonSize = UiSharedService.GetIconTextButtonSize(FontAwesomeIcon.Passport, "Change Password").X; + var textSize = ImGui.CalcTextSize("New Password").X; + var spacing = ImGui.GetStyle().ItemSpacing.X; + + ImGui.SameLine(); + ImGui.SetNextItemWidth(availableWidth - buttonSize - textSize - spacing * 2); + ImGui.InputTextWithHint("##changepw", "Min 10 characters", ref _newPassword, 50); + ImGui.SameLine(); + using (ImRaii.Disabled(_newPassword.Length < 10)) + { + if (UiSharedService.IconTextButton(FontAwesomeIcon.Passport, "Change Password")) + { + _pwChangeSuccess = _apiController.GroupChangePassword(new GroupPasswordDto(GroupFullInfo.Group, _newPassword)).Result; + _newPassword = string.Empty; + } + } + UiSharedService.AttachToolTip("Password requires to be at least 10 characters long. This action is irreversible."); + + if (!_pwChangeSuccess) + { + UiSharedService.ColorTextWrapped("Failed to change the password. Password requires to be at least 10 characters long.", ImGuiColors.DalamudYellow); + } + + if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete Syncshell") && UiSharedService.CtrlPressed() && UiSharedService.ShiftPressed()) + { + IsOpen = false; + _ = _apiController.GroupDelete(new(GroupFullInfo.Group)); + } + UiSharedService.AttachToolTip("Hold CTRL and Shift and click to delete this Syncshell." + Environment.NewLine + "WARNING: this action is irreversible."); + } + ownerTab.Dispose(); + } + } + } + + public override void OnClose() + { + Mediator.Publish(new RemoveWindowMessage(this)); + } +} \ No newline at end of file From 5c9415b6e909925c14cc2c0d84236ac2f652b714 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Fri, 27 Oct 2023 02:51:26 +0200 Subject: [PATCH 30/56] rework creation of popout windows into factory and some refactoring in general --- MareSynchronos/Plugin.cs | 30 +--- MareSynchronos/Services/UiFactory.cs | 46 +++++ MareSynchronos/Services/UiService.cs | 39 ++--- MareSynchronos/UI/CompactUI.cs | 43 ++--- .../UI/Components/DrawFolderBase.cs | 5 +- MareSynchronos/UI/Components/DrawUserPair.cs | 4 +- .../UI/Components/SelectTagForPairUi.cs | 3 +- .../UI/Handlers/IdDisplayHandler.cs | 5 +- MareSynchronos/UI/IntroUI.cs | 5 +- MareSynchronos/UI/PopoutProfileUi.cs | 7 +- MareSynchronos/UI/SettingsUi.cs | 162 ++++++++++-------- MareSynchronos/UI/StandaloneProfileUi.cs | 7 +- MareSynchronos/UI/SyncshellAdminUI.cs | 3 +- MareSynchronos/UI/UISharedService.cs | 128 +++++--------- MareSynchronos/WebAPI/SignalR/HubFactory.cs | 12 +- 15 files changed, 239 insertions(+), 260 deletions(-) create mode 100644 MareSynchronos/Services/UiFactory.cs diff --git a/MareSynchronos/Plugin.cs b/MareSynchronos/Plugin.cs index 138dacd..ac6e3fd 100644 --- a/MareSynchronos/Plugin.cs +++ b/MareSynchronos/Plugin.cs @@ -91,30 +91,11 @@ public sealed class Plugin : IDalamudPlugin collection.AddSingleton((s) => new ServerTagConfigService(pluginInterface.ConfigDirectory.FullName)); collection.AddSingleton((s) => new TransientConfigService(pluginInterface.ConfigDirectory.FullName)); collection.AddSingleton((s) => new ConfigurationMigrator(s.GetRequiredService>(), pluginInterface)); - collection.AddSingleton((s) => new HubFactory(s.GetRequiredService>(), s.GetRequiredService(), - s.GetRequiredService(), s.GetRequiredService(), - s.GetRequiredService(), pluginLog)); - - // func factory method singletons - collection.AddSingleton(s => - new Func((pair) => - new StandaloneProfileUi(s.GetRequiredService>(), - s.GetRequiredService(), - s.GetRequiredService(), - s.GetRequiredService(), - s.GetRequiredService(), - s.GetRequiredService(), pair))); - collection.AddSingleton(s => - new Func((dto) => - new SyncshellAdminUI(s.GetRequiredService>(), - s.GetRequiredService(), - dto, - s.GetRequiredService(), - s.GetRequiredService(), - s.GetRequiredService()))); + collection.AddSingleton(); // add scoped services collection.AddScoped(); + collection.AddScoped(); collection.AddScoped(); collection.AddScoped(); collection.AddScoped(); @@ -135,10 +116,9 @@ public sealed class Plugin : IDalamudPlugin collection.AddScoped(); collection.AddScoped(); collection.AddScoped(); - collection.AddScoped((s) => new UiService(s.GetRequiredService>(), pluginInterface, s.GetRequiredService(), - s.GetRequiredService(), s.GetServices(), - s.GetRequiredService>(), - s.GetRequiredService>(), + collection.AddScoped((s) => new UiService(s.GetRequiredService>(), pluginInterface.UiBuilder, s.GetRequiredService(), + s.GetRequiredService(), s.GetServices(), + s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService())); collection.AddScoped((s) => new CommandManagerService(commandManager, s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), diff --git a/MareSynchronos/Services/UiFactory.cs b/MareSynchronos/Services/UiFactory.cs new file mode 100644 index 0000000..8d611b2 --- /dev/null +++ b/MareSynchronos/Services/UiFactory.cs @@ -0,0 +1,46 @@ +using MareSynchronos.API.Dto.Group; +using MareSynchronos.PlayerData.Pairs; +using MareSynchronos.Services.Mediator; +using MareSynchronos.Services.ServerConfiguration; +using MareSynchronos.UI; +using MareSynchronos.UI.Components.Popup; +using MareSynchronos.WebAPI; +using Microsoft.Extensions.Logging; + +namespace MareSynchronos.Services; + +public class UiFactory +{ + private readonly ILoggerFactory _loggerFactory; + private readonly MareMediator _mareMediator; + private readonly ApiController _apiController; + private readonly UiSharedService _uiSharedService; + private readonly PairManager _pairManager; + private readonly ServerConfigurationManager _serverConfigManager; + private readonly MareProfileManager _mareProfileManager; + + public UiFactory(ILoggerFactory loggerFactory, MareMediator mareMediator, ApiController apiController, + UiSharedService uiSharedService, PairManager pairManager, ServerConfigurationManager serverConfigManager, + MareProfileManager mareProfileManager) + { + _loggerFactory = loggerFactory; + _mareMediator = mareMediator; + _apiController = apiController; + _uiSharedService = uiSharedService; + _pairManager = pairManager; + _serverConfigManager = serverConfigManager; + _mareProfileManager = mareProfileManager; + } + + public SyncshellAdminUI CreateSyncshellAdminUi(GroupFullInfoDto dto) + { + return new SyncshellAdminUI(_loggerFactory.CreateLogger(), _mareMediator, + _apiController, _uiSharedService, _pairManager, dto); + } + + public StandaloneProfileUi CreateStandaloneProfileUi(Pair pair) + { + return new StandaloneProfileUi(_loggerFactory.CreateLogger(), _mareMediator, + _uiSharedService, _serverConfigManager, _mareProfileManager, _pairManager, pair); + } +} diff --git a/MareSynchronos/Services/UiService.cs b/MareSynchronos/Services/UiService.cs index eb72605..a8151e5 100644 --- a/MareSynchronos/Services/UiService.cs +++ b/MareSynchronos/Services/UiService.cs @@ -1,9 +1,7 @@ -using Dalamud.Interface.ImGuiFileDialog; +using Dalamud.Interface; +using Dalamud.Interface.ImGuiFileDialog; using Dalamud.Interface.Windowing; -using Dalamud.Plugin; -using MareSynchronos.API.Dto.Group; using MareSynchronos.MareConfiguration; -using MareSynchronos.PlayerData.Pairs; using MareSynchronos.Services.Mediator; using MareSynchronos.UI; using MareSynchronos.UI.Components.Popup; @@ -14,32 +12,31 @@ namespace MareSynchronos.Services; public sealed class UiService : DisposableMediatorSubscriberBase { private readonly List _createdWindows = []; - private readonly DalamudPluginInterface _dalamudPluginInterface; + private readonly UiBuilder _uiBuilder; private readonly FileDialogManager _fileDialogManager; private readonly ILogger _logger; private readonly MareConfigService _mareConfigService; private readonly WindowSystem _windowSystem; - private readonly Func _syncshellAdminUiFactory; + private readonly UiFactory _uiFactory; - public UiService(ILogger logger, DalamudPluginInterface dalamudPluginInterface, + public UiService(ILogger logger, UiBuilder uiBuilder, MareConfigService mareConfigService, WindowSystem windowSystem, IEnumerable windows, - Func standaloneProfileUiFactory, - Func syncshellAdminUiFactory, - FileDialogManager fileDialogManager, MareMediator mareMediator) : base(logger, mareMediator) + UiFactory uiFactory, FileDialogManager fileDialogManager, + MareMediator mareMediator) : base(logger, mareMediator) { _logger = logger; _logger.LogTrace("Creating {type}", GetType().Name); - _dalamudPluginInterface = dalamudPluginInterface; + _uiBuilder = uiBuilder; _mareConfigService = mareConfigService; _windowSystem = windowSystem; - _syncshellAdminUiFactory = syncshellAdminUiFactory; + _uiFactory = uiFactory; _fileDialogManager = fileDialogManager; - _dalamudPluginInterface.UiBuilder.DisableGposeUiHide = true; - _dalamudPluginInterface.UiBuilder.Draw += Draw; - _dalamudPluginInterface.UiBuilder.OpenConfigUi += ToggleUi; - _dalamudPluginInterface.UiBuilder.OpenMainUi += ToggleMainUi; + _uiBuilder.DisableGposeUiHide = true; + _uiBuilder.Draw += Draw; + _uiBuilder.OpenConfigUi += ToggleUi; + _uiBuilder.OpenMainUi += ToggleMainUi; foreach (var window in windows) { @@ -51,7 +48,7 @@ public sealed class UiService : DisposableMediatorSubscriberBase if (!_createdWindows.Exists(p => p is StandaloneProfileUi ui && string.Equals(ui.Pair.UserData.AliasOrUID, msg.Pair.UserData.AliasOrUID, StringComparison.Ordinal))) { - var window = standaloneProfileUiFactory(msg.Pair); + var window = _uiFactory.CreateStandaloneProfileUi(msg.Pair); _createdWindows.Add(window); _windowSystem.AddWindow(window); } @@ -62,7 +59,7 @@ public sealed class UiService : DisposableMediatorSubscriberBase if (!_createdWindows.Exists(p => p is SyncshellAdminUI ui && string.Equals(ui.GroupFullInfo.GID, msg.GroupInfo.GID, StringComparison.Ordinal))) { - var window = _syncshellAdminUiFactory(msg.GroupInfo); + var window = _uiFactory.CreateSyncshellAdminUi(msg.GroupInfo); _createdWindows.Add(window); _windowSystem.AddWindow(window); } @@ -105,9 +102,9 @@ public sealed class UiService : DisposableMediatorSubscriberBase window.Dispose(); } - _dalamudPluginInterface.UiBuilder.Draw -= Draw; - _dalamudPluginInterface.UiBuilder.OpenConfigUi -= ToggleUi; - _dalamudPluginInterface.UiBuilder.OpenMainUi -= ToggleMainUi; + _uiBuilder.Draw -= Draw; + _uiBuilder.OpenConfigUi -= ToggleUi; + _uiBuilder.OpenMainUi -= ToggleMainUi; } private void Draw() diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs index cd0a177..cf73dac 100644 --- a/MareSynchronos/UI/CompactUI.cs +++ b/MareSynchronos/UI/CompactUI.cs @@ -115,21 +115,22 @@ public class CompactUi : WindowMediatorSubscriberBase $"It is highly recommended to keep Mare Synchronos up to date. Open /xlplugins and update the plugin.", ImGuiColors.DalamudRed); } - UiSharedService.DrawWithID("header", DrawUIDHeader); + using (ImRaii.PushId("header")) DrawUIDHeader(); ImGui.Separator(); - UiSharedService.DrawWithID("serverstatus", DrawServerStatus); + using (ImRaii.PushId("serverstatus")) DrawServerStatus(); ImGui.Separator(); if (_apiController.ServerState is ServerState.Connected) { - UiSharedService.DrawWithID("global-topmenu", () => _tabMenu.Draw()); - UiSharedService.DrawWithID("pairlist", DrawPairList); + using (ImRaii.PushId("global-topmenu")) _tabMenu.Draw(); + using (ImRaii.PushId("pairlist")) DrawPairs(); + TransferPartHeight = ImGui.GetCursorPosY(); ImGui.Separator(); - UiSharedService.DrawWithID("transfers", DrawTransfers); + using (ImRaii.PushId("transfers")) DrawTransfers(); TransferPartHeight = ImGui.GetCursorPosY() - TransferPartHeight - ImGui.GetTextLineHeight(); - UiSharedService.DrawWithID("group-user-popup", () => _selectPairsForGroupUi.Draw(_pairManager.DirectPairs)); - UiSharedService.DrawWithID("grouping-popup", () => _selectGroupForPairUi.Draw()); + using (ImRaii.PushId("group-user-popup")) _selectPairsForGroupUi.Draw(_pairManager.DirectPairs); + using (ImRaii.PushId("grouping-popup")) _selectGroupForPairUi.Draw(); } if (_configService.Current.OpenPopupOnAdd && _pairManager.LastAddedUser != null) @@ -173,12 +174,6 @@ public class CompactUi : WindowMediatorSubscriberBase } } - private void DrawPairList() - { - UiSharedService.DrawWithID("pairs", DrawPairs); - TransferPartHeight = ImGui.GetCursorPosY(); - } - private void DrawPairs() { var ySize = TransferPartHeight == 0 @@ -277,14 +272,16 @@ public class CompactUi : WindowMediatorSubscriberBase if (_apiController.ServerState is not (ServerState.Reconnecting or ServerState.Disconnecting)) { - ImGui.PushStyleColor(ImGuiCol.Text, color); - if (ImGuiComponents.IconButton(connectedIcon)) + using (ImRaii.PushColor(ImGuiCol.Text, color)) { - _serverManager.CurrentServer.FullPause = !_serverManager.CurrentServer.FullPause; - _serverManager.Save(); - _ = _apiController.CreateConnections(); + if (ImGuiComponents.IconButton(connectedIcon)) + { + _serverManager.CurrentServer.FullPause = !_serverManager.CurrentServer.FullPause; + _serverManager.Save(); + _ = _apiController.CreateConnections(); + } } - ImGui.PopStyleColor(); + UiSharedService.AttachToolTip(!_serverManager.CurrentServer.FullPause ? "Disconnect from " + _serverManager.CurrentServer.ServerName : "Connect to " + _serverManager.CurrentServer.ServerName); } } @@ -292,9 +289,7 @@ public class CompactUi : WindowMediatorSubscriberBase private void DrawTransfers() { var currentUploads = _fileTransferManager.CurrentUploads.ToList(); - ImGui.PushFont(UiBuilder.IconFont); - ImGui.TextUnformatted(FontAwesomeIcon.Upload.ToIconString()); - ImGui.PopFont(); + UiSharedService.FontText(FontAwesomeIcon.Upload.ToIconString(), UiBuilder.IconFont); ImGui.SameLine(35 * ImGuiHelpers.GlobalScale); if (currentUploads.Any()) @@ -317,9 +312,7 @@ public class CompactUi : WindowMediatorSubscriberBase } var currentDownloads = _currentDownloads.SelectMany(d => d.Value.Values).ToList(); - ImGui.PushFont(UiBuilder.IconFont); - ImGui.TextUnformatted(FontAwesomeIcon.Download.ToIconString()); - ImGui.PopFont(); + UiSharedService.FontText(FontAwesomeIcon.Download.ToIconString(), UiBuilder.IconFont); ImGui.SameLine(35 * ImGuiHelpers.GlobalScale); if (currentDownloads.Any()) diff --git a/MareSynchronos/UI/Components/DrawFolderBase.cs b/MareSynchronos/UI/Components/DrawFolderBase.cs index c3bfefe..c2a487f 100644 --- a/MareSynchronos/UI/Components/DrawFolderBase.cs +++ b/MareSynchronos/UI/Components/DrawFolderBase.cs @@ -99,10 +99,7 @@ public abstract class DrawFolderBase : IDrawFolder } if (ImGui.BeginPopup("User Flyout Menu")) { - UiSharedService.DrawWithID($"buttons-{_id}", () => - { - DrawMenu(_menuWidth); - }); + using (ImRaii.PushId($"buttons-{_id}")) DrawMenu(_menuWidth); _menuWidth = ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X; ImGui.EndPopup(); } diff --git a/MareSynchronos/UI/Components/DrawUserPair.cs b/MareSynchronos/UI/Components/DrawUserPair.cs index 2cf3be8..8752596 100644 --- a/MareSynchronos/UI/Components/DrawUserPair.cs +++ b/MareSynchronos/UI/Components/DrawUserPair.cs @@ -408,7 +408,7 @@ public class DrawUserPair } if (ImGui.BeginPopup("User Flyout Menu")) { - UiSharedService.DrawWithID($"buttons-{_pair.UserData.UID}", () => + using (ImRaii.PushId($"buttons-{_pair.UserData.UID}")) { ImGui.TextUnformatted("Common Pair Functions"); DrawCommonClientMenu(); @@ -418,7 +418,7 @@ public class DrawUserPair { _menuRenderWidth = ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X; } - }); + } ImGui.EndPopup(); } diff --git a/MareSynchronos/UI/Components/SelectTagForPairUi.cs b/MareSynchronos/UI/Components/SelectTagForPairUi.cs index 8157e7b..4e98da0 100644 --- a/MareSynchronos/UI/Components/SelectTagForPairUi.cs +++ b/MareSynchronos/UI/Components/SelectTagForPairUi.cs @@ -1,6 +1,7 @@ using Dalamud.Interface; using Dalamud.Interface.Components; using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; using Dalamud.Utility; using ImGuiNET; using MareSynchronos.PlayerData.Pairs; @@ -66,7 +67,7 @@ public class SelectTagForPairUi { foreach (var tag in tags) { - UiSharedService.DrawWithID($"groups-pair-{_pair.UserData.UID}-{tag}", () => DrawGroupName(_pair, tag)); + using (ImRaii.PushId($"groups-pair-{_pair.UserData.UID}-{tag}")) DrawGroupName(_pair, tag); } ImGui.EndChild(); } diff --git a/MareSynchronos/UI/Handlers/IdDisplayHandler.cs b/MareSynchronos/UI/Handlers/IdDisplayHandler.cs index 97484bf..8d95cad 100644 --- a/MareSynchronos/UI/Handlers/IdDisplayHandler.cs +++ b/MareSynchronos/UI/Handlers/IdDisplayHandler.cs @@ -95,9 +95,8 @@ public class IdDisplayHandler { ImGui.AlignTextToFramePadding(); - if (textIsUid) ImGui.PushFont(UiBuilder.MonoFont); - ImGui.TextUnformatted(playerText); - if (textIsUid) ImGui.PopFont(); + using (ImRaii.PushFont(UiBuilder.MonoFont, textIsUid)) ImGui.TextUnformatted(playerText); + if (ImGui.IsItemHovered()) { if (!string.Equals(_lastMouseOverUid, id)) diff --git a/MareSynchronos/UI/IntroUI.cs b/MareSynchronos/UI/IntroUI.cs index 696e093..8f64ada 100644 --- a/MareSynchronos/UI/IntroUI.cs +++ b/MareSynchronos/UI/IntroUI.cs @@ -1,5 +1,6 @@ using Dalamud.Interface.Colors; using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; using Dalamud.Utility; using ImGuiNET; using MareSynchronos.FileCache; @@ -62,9 +63,7 @@ public class IntroUi : WindowMediatorSubscriberBase if (!_configService.Current.AcceptedAgreement && !_readFirstPage) { - if (_uiShared.UidFontBuilt) ImGui.PushFont(_uiShared.UidFont); - ImGui.TextUnformatted("Welcome to Mare Synchronos"); - if (_uiShared.UidFontBuilt) ImGui.PopFont(); + _uiShared.BigText("Welcome to Mare Synchronos"); ImGui.Separator(); UiSharedService.TextWrapped("Mare Synchronos is a plugin that will replicate your full current character state including all Penumbra mods to other paired Mare Synchronos users. " + "Note that you will have to have Penumbra as well as Glamourer installed to use this plugin."); diff --git a/MareSynchronos/UI/PopoutProfileUi.cs b/MareSynchronos/UI/PopoutProfileUi.cs index a95ea87..4d9c85c 100644 --- a/MareSynchronos/UI/PopoutProfileUi.cs +++ b/MareSynchronos/UI/PopoutProfileUi.cs @@ -2,6 +2,7 @@ using Dalamud.Interface.Internal; using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; using ImGuiNET; using MareSynchronos.API.Data.Extensions; using MareSynchronos.MareConfiguration; @@ -110,9 +111,9 @@ public class PopoutProfileUi : WindowMediatorSubscriberBase var rectMin = drawList.GetClipRectMin(); var rectMax = drawList.GetClipRectMax(); - if (_uiSharedService.UidFontBuilt) ImGui.PushFont(_uiSharedService.UidFont); - UiSharedService.ColorText(_pair.UserData.AliasOrUID, ImGuiColors.HealerGreen); - if (_uiSharedService.UidFontBuilt) ImGui.PopFont(); + using (ImRaii.PushFont(_uiSharedService.UidFont, _uiSharedService.UidFontBuilt)) + UiSharedService.ColorText(_pair.UserData.AliasOrUID, ImGuiColors.HealerGreen); + ImGui.Dummy(new(spacing.Y, spacing.Y)); var textPos = ImGui.GetCursorPosY(); ImGui.Separator(); diff --git a/MareSynchronos/UI/SettingsUi.cs b/MareSynchronos/UI/SettingsUi.cs index eeda8ae..7da8a68 100644 --- a/MareSynchronos/UI/SettingsUi.cs +++ b/MareSynchronos/UI/SettingsUi.cs @@ -20,6 +20,7 @@ using MareSynchronos.WebAPI.Files.Models; using MareSynchronos.WebAPI.SignalR.Utils; using Microsoft.Extensions.Logging; using System.Collections.Concurrent; +using System.Globalization; using System.Numerics; using System.Text.Json; @@ -249,14 +250,14 @@ public class SettingsUi : WindowMediatorSubscriberBase foreach (var transfer in _fileTransferManager.CurrentUploads.ToArray()) { var color = UiSharedService.UploadColor((transfer.Transferred, transfer.Total)); - ImGui.PushStyleColor(ImGuiCol.Text, color); + var col = ImRaii.PushColor(ImGuiCol.Text, color); ImGui.TableNextColumn(); ImGui.TextUnformatted(transfer.Hash); ImGui.TableNextColumn(); ImGui.TextUnformatted(UiSharedService.ByteToString(transfer.Transferred)); ImGui.TableNextColumn(); ImGui.TextUnformatted(UiSharedService.ByteToString(transfer.Total)); - ImGui.PopStyleColor(); + col.Dispose(); ImGui.TableNextRow(); } @@ -282,13 +283,13 @@ public class SettingsUi : WindowMediatorSubscriberBase ImGui.TextUnformatted(userName); ImGui.TableNextColumn(); ImGui.TextUnformatted(entry.Key); - ImGui.PushStyleColor(ImGuiCol.Text, color); + var col = ImRaii.PushColor(ImGuiCol.Text, color); ImGui.TableNextColumn(); ImGui.TextUnformatted(entry.Value.TransferredFiles + "/" + entry.Value.TotalFiles); ImGui.TableNextColumn(); ImGui.TextUnformatted(UiSharedService.ByteToString(entry.Value.TransferredBytes) + "/" + UiSharedService.ByteToString(entry.Value.TotalBytes)); ImGui.TableNextColumn(); - ImGui.PopStyleColor(); + col.Dispose(); ImGui.TableNextRow(); } } @@ -352,7 +353,7 @@ public class SettingsUi : WindowMediatorSubscriberBase } UiSharedService.DrawHelpText("Enabling this can incur a (slight) performance impact. Enabling this for extended periods of time is not recommended."); - if (!logPerformance) ImGui.BeginDisabled(); + using var disabled = ImRaii.Disabled(!logPerformance); if (UiSharedService.IconTextButton(FontAwesomeIcon.StickyNote, "Print Performance Stats to /xllog")) { _performanceCollector.PrintPerformanceStats(); @@ -362,7 +363,6 @@ public class SettingsUi : WindowMediatorSubscriberBase { _performanceCollector.PrintPerformanceStats(60); } - if (!logPerformance) ImGui.EndDisabled(); } private void DrawFileStorageSettings() @@ -763,7 +763,7 @@ public class SettingsUi : WindowMediatorSubscriberBase } UiSharedService.DrawHelpText("Enabling this will show a small notification (type: Info) in the bottom right corner when pairs go online."); - if (!onlineNotifs) ImGui.BeginDisabled(); + using var disabled = ImRaii.Disabled(!onlineNotifs); if (ImGui.Checkbox("Notify only for individual pairs", ref onlineNotifsPairsOnly)) { _configService.Current.ShowOnlineNotificationsOnlyForIndividualPairs = onlineNotifsPairsOnly; @@ -776,7 +776,6 @@ public class SettingsUi : WindowMediatorSubscriberBase _configService.Save(); } UiSharedService.DrawHelpText("Enabling this will only show online notifications (type: Info) for pairs where you have set an individual note."); - if (!onlineNotifs) ImGui.EndDisabled(); } private void DrawServerConfiguration() @@ -885,59 +884,58 @@ public class SettingsUi : WindowMediatorSubscriberBase int i = 0; foreach (var item in selectedServer.Authentications.ToList()) { - UiSharedService.DrawWithID("selectedChara" + i, () => + using var charaId = ImRaii.PushId("selectedChara" + i); + + var worldIdx = (ushort)item.WorldId; + var data = _uiShared.WorldData.OrderBy(u => u.Value, StringComparer.Ordinal).ToDictionary(k => k.Key, k => k.Value); + if (!data.TryGetValue(worldIdx, out string? worldPreview)) { - var worldIdx = (ushort)item.WorldId; - var data = _uiShared.WorldData.OrderBy(u => u.Value, StringComparer.Ordinal).ToDictionary(k => k.Key, k => k.Value); - if (!data.TryGetValue(worldIdx, out string? worldPreview)) + worldPreview = data.First().Value; + } + + var secretKeyIdx = item.SecretKeyIdx; + var keys = selectedServer.SecretKeys; + if (!keys.TryGetValue(secretKeyIdx, out var secretKey)) + { + secretKey = new(); + } + var friendlyName = secretKey.FriendlyName; + + if (ImGui.TreeNode($"chara", $"Character: {item.CharacterName}, World: {worldPreview}, Secret Key: {friendlyName}")) + { + var charaName = item.CharacterName; + if (ImGui.InputText("Character Name", ref charaName, 64)) { - worldPreview = data.First().Value; + item.CharacterName = charaName; + _serverConfigurationManager.Save(); } - var secretKeyIdx = item.SecretKeyIdx; - var keys = selectedServer.SecretKeys; - if (!keys.TryGetValue(secretKeyIdx, out var secretKey)) - { - secretKey = new(); - } - var friendlyName = secretKey.FriendlyName; - - if (ImGui.TreeNode($"chara", $"Character: {item.CharacterName}, World: {worldPreview}, Secret Key: {friendlyName}")) - { - var charaName = item.CharacterName; - if (ImGui.InputText("Character Name", ref charaName, 64)) + _uiShared.DrawCombo("World##" + item.CharacterName + i, data, (w) => w.Value, + (w) => { - item.CharacterName = charaName; - _serverConfigurationManager.Save(); - } - - _uiShared.DrawCombo("World##" + item.CharacterName + i, data, (w) => w.Value, - (w) => + if (item.WorldId != w.Key) { - if (item.WorldId != w.Key) - { - item.WorldId = w.Key; - _serverConfigurationManager.Save(); - } - }, EqualityComparer>.Default.Equals(data.FirstOrDefault(f => f.Key == worldIdx), default) ? data.First() : data.First(f => f.Key == worldIdx)); + item.WorldId = w.Key; + _serverConfigurationManager.Save(); + } + }, EqualityComparer>.Default.Equals(data.FirstOrDefault(f => f.Key == worldIdx), default) ? data.First() : data.First(f => f.Key == worldIdx)); - _uiShared.DrawCombo("Secret Key##" + item.CharacterName + i, keys, (w) => w.Value.FriendlyName, - (w) => + _uiShared.DrawCombo("Secret Key##" + item.CharacterName + i, keys, (w) => w.Value.FriendlyName, + (w) => + { + if (w.Key != item.SecretKeyIdx) { - if (w.Key != item.SecretKeyIdx) - { - item.SecretKeyIdx = w.Key; - _serverConfigurationManager.Save(); - } - }, EqualityComparer>.Default.Equals(keys.FirstOrDefault(f => f.Key == item.SecretKeyIdx), default) ? keys.First() : keys.First(f => f.Key == item.SecretKeyIdx)); + item.SecretKeyIdx = w.Key; + _serverConfigurationManager.Save(); + } + }, EqualityComparer>.Default.Equals(keys.FirstOrDefault(f => f.Key == item.SecretKeyIdx), default) ? keys.First() : keys.First(f => f.Key == item.SecretKeyIdx)); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete Character") && UiSharedService.CtrlPressed()) - _serverConfigurationManager.RemoveCharacterFromServer(idx, item); - UiSharedService.AttachToolTip("Hold CTRL to delete this entry."); + if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete Character") && UiSharedService.CtrlPressed()) + _serverConfigurationManager.RemoveCharacterFromServer(idx, item); + UiSharedService.AttachToolTip("Hold CTRL to delete this entry."); - ImGui.TreePop(); - } - }); + ImGui.TreePop(); + } i++; } @@ -970,34 +968,32 @@ public class SettingsUi : WindowMediatorSubscriberBase { foreach (var item in selectedServer.SecretKeys.ToList()) { - UiSharedService.DrawWithID("key" + item.Key, () => + using var id = ImRaii.PushId("key" + item.Key); + var friendlyName = item.Value.FriendlyName; + if (ImGui.InputText("Secret Key Display Name", ref friendlyName, 255)) { - var friendlyName = item.Value.FriendlyName; - if (ImGui.InputText("Secret Key Display Name", ref friendlyName, 255)) + item.Value.FriendlyName = friendlyName; + _serverConfigurationManager.Save(); + } + var key = item.Value.Key; + if (ImGui.InputText("Secret Key", ref key, 64)) + { + item.Value.Key = key; + _serverConfigurationManager.Save(); + } + if (!selectedServer.Authentications.Exists(p => p.SecretKeyIdx == item.Key)) + { + if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete Secret Key") && UiSharedService.CtrlPressed()) { - item.Value.FriendlyName = friendlyName; + selectedServer.SecretKeys.Remove(item.Key); _serverConfigurationManager.Save(); } - var key = item.Value.Key; - if (ImGui.InputText("Secret Key", ref key, 64)) - { - item.Value.Key = key; - _serverConfigurationManager.Save(); - } - if (!selectedServer.Authentications.Exists(p => p.SecretKeyIdx == item.Key)) - { - if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete Secret Key") && UiSharedService.CtrlPressed()) - { - selectedServer.SecretKeys.Remove(item.Key); - _serverConfigurationManager.Save(); - } - UiSharedService.AttachToolTip("Hold CTRL to delete this secret key entry"); - } - else - { - UiSharedService.ColorTextWrapped("This key is in use and cannot be deleted", ImGuiColors.DalamudYellow); - } - }); + UiSharedService.AttachToolTip("Hold CTRL to delete this secret key entry"); + } + else + { + UiSharedService.ColorTextWrapped("This key is in use and cannot be deleted", ImGuiColors.DalamudYellow); + } if (item.Key != selectedServer.SecretKeys.Keys.LastOrDefault()) ImGui.Separator(); @@ -1136,7 +1132,21 @@ public class SettingsUi : WindowMediatorSubscriberBase private void DrawSettingsContent() { - _uiShared.PrintServerState(); + if (_apiController.ServerState is ServerState.Connected) + { + ImGui.TextUnformatted("Service " + _serverConfigurationManager.CurrentServer!.ServerName + ":"); + ImGui.SameLine(); + ImGui.TextColored(ImGuiColors.ParsedGreen, "Available"); + ImGui.SameLine(); + ImGui.TextUnformatted("("); + ImGui.SameLine(); + ImGui.TextColored(ImGuiColors.ParsedGreen, _apiController.OnlineUsers.ToString(CultureInfo.InvariantCulture)); + ImGui.SameLine(); + ImGui.TextUnformatted("Users Online"); + ImGui.SameLine(); + ImGui.TextUnformatted(")"); + } + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("Community and Support:"); ImGui.SameLine(); diff --git a/MareSynchronos/UI/StandaloneProfileUi.cs b/MareSynchronos/UI/StandaloneProfileUi.cs index 6991e3f..e84abb5 100644 --- a/MareSynchronos/UI/StandaloneProfileUi.cs +++ b/MareSynchronos/UI/StandaloneProfileUi.cs @@ -2,6 +2,7 @@ using Dalamud.Interface.Internal; using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; using ImGuiNET; using MareSynchronos.API.Data.Extensions; using MareSynchronos.PlayerData.Pairs; @@ -76,9 +77,9 @@ public class StandaloneProfileUi : WindowMediatorSubscriberBase var rectMax = drawList.GetClipRectMax(); var headerSize = ImGui.GetCursorPosY() - ImGui.GetStyle().WindowPadding.Y; - if (_uiSharedService.UidFontBuilt) ImGui.PushFont(_uiSharedService.UidFont); - UiSharedService.ColorText(Pair.UserData.AliasOrUID, ImGuiColors.HealerGreen); - if (_uiSharedService.UidFontBuilt) ImGui.PopFont(); + using (ImRaii.PushFont(_uiSharedService.UidFont, _uiSharedService.UidFontBuilt)) + UiSharedService.ColorText(Pair.UserData.AliasOrUID, ImGuiColors.HealerGreen); + ImGuiHelpers.ScaledDummy(new Vector2(spacing.Y, spacing.Y)); var textPos = ImGui.GetCursorPosY() - headerSize; ImGui.Separator(); diff --git a/MareSynchronos/UI/SyncshellAdminUI.cs b/MareSynchronos/UI/SyncshellAdminUI.cs index 57f3a7a..95650b2 100644 --- a/MareSynchronos/UI/SyncshellAdminUI.cs +++ b/MareSynchronos/UI/SyncshellAdminUI.cs @@ -24,7 +24,8 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase private int _multiInvites; private string _newPassword; private bool _pwChangeSuccess; - public SyncshellAdminUI(ILogger logger, MareMediator mediator, GroupFullInfoDto groupFullInfo, ApiController apiController, UiSharedService uiSharedService, PairManager pairManager) + public SyncshellAdminUI(ILogger logger, MareMediator mediator, ApiController apiController, + UiSharedService uiSharedService, PairManager pairManager, GroupFullInfoDto groupFullInfo) : base(logger, mediator, "Syncshell Admin Panel (" + groupFullInfo.GID + ")") { GroupFullInfo = groupFullInfo; diff --git a/MareSynchronos/UI/UISharedService.cs b/MareSynchronos/UI/UISharedService.cs index a75ef59..bd3f22d 100644 --- a/MareSynchronos/UI/UISharedService.cs +++ b/MareSynchronos/UI/UISharedService.cs @@ -207,16 +207,14 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase public static void ColorText(string text, Vector4 color) { - ImGui.PushStyleColor(ImGuiCol.Text, color); + using var raiicolor = ImRaii.PushColor(ImGuiCol.Text, color); ImGui.TextUnformatted(text); - ImGui.PopStyleColor(); } public static void ColorTextWrapped(string text, Vector4 color) { - ImGui.PushStyleColor(ImGuiCol.Text, color); + using var raiicolor = ImRaii.PushColor(ImGuiCol.Text, color); TextWrapped(text); - ImGui.PopStyleColor(); } public static bool CtrlPressed() => (GetKeyState(0xA2) & 0x8000) != 0 || (GetKeyState(0xA3) & 0x8000) != 0; @@ -224,18 +222,19 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase public static void DrawHelpText(string helpText) { ImGui.SameLine(); - ImGui.PushFont(UiBuilder.IconFont); - ImGui.SetWindowFontScale(0.8f); - ImGui.TextDisabled(FontAwesomeIcon.Question.ToIconString()); - ImGui.SetWindowFontScale(1.0f); - ImGui.PopFont(); + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + ImGui.SetWindowFontScale(0.8f); + ImGui.TextDisabled(FontAwesomeIcon.Question.ToIconString()); + ImGui.SetWindowFontScale(1.0f); + } + if (ImGui.IsItemHovered()) { - ImGui.BeginTooltip(); + using var tooltip = ImRaii.Tooltip(); ImGui.PushTextWrapPos(ImGui.GetFontSize() * 35.0f); ImGui.TextUnformatted(helpText); ImGui.PopTextWrapPos(); - ImGui.EndTooltip(); } } @@ -243,31 +242,34 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase { var original = ImGui.GetCursorPos(); - ImGui.PushStyleColor(ImGuiCol.Text, outlineColor); - ImGui.SetCursorPos(original with { Y = original.Y - thickness }); - ImGui.TextUnformatted(text); - ImGui.SetCursorPos(original with { X = original.X - thickness }); - ImGui.TextUnformatted(text); - ImGui.SetCursorPos(original with { Y = original.Y + thickness }); - ImGui.TextUnformatted(text); - ImGui.SetCursorPos(original with { X = original.X + thickness }); - ImGui.TextUnformatted(text); - ImGui.SetCursorPos(original with { X = original.X - thickness, Y = original.Y - thickness }); - ImGui.TextUnformatted(text); - ImGui.SetCursorPos(original with { X = original.X + thickness, Y = original.Y + thickness }); - ImGui.TextUnformatted(text); - ImGui.SetCursorPos(original with { X = original.X - thickness, Y = original.Y + thickness }); - ImGui.TextUnformatted(text); - ImGui.SetCursorPos(original with { X = original.X + thickness, Y = original.Y - thickness }); - ImGui.TextUnformatted(text); - ImGui.PopStyleColor(); + using (ImRaii.PushColor(ImGuiCol.Text, outlineColor)) + { + ImGui.PushStyleColor(ImGuiCol.Text, outlineColor); + ImGui.SetCursorPos(original with { Y = original.Y - thickness }); + ImGui.TextUnformatted(text); + ImGui.SetCursorPos(original with { X = original.X - thickness }); + ImGui.TextUnformatted(text); + ImGui.SetCursorPos(original with { Y = original.Y + thickness }); + ImGui.TextUnformatted(text); + ImGui.SetCursorPos(original with { X = original.X + thickness }); + ImGui.TextUnformatted(text); + ImGui.SetCursorPos(original with { X = original.X - thickness, Y = original.Y - thickness }); + ImGui.TextUnformatted(text); + ImGui.SetCursorPos(original with { X = original.X + thickness, Y = original.Y + thickness }); + ImGui.TextUnformatted(text); + ImGui.SetCursorPos(original with { X = original.X - thickness, Y = original.Y + thickness }); + ImGui.TextUnformatted(text); + ImGui.SetCursorPos(original with { X = original.X + thickness, Y = original.Y - thickness }); + ImGui.TextUnformatted(text); + } - ImGui.PushStyleColor(ImGuiCol.Text, fontColor); - ImGui.SetCursorPos(original); - ImGui.TextUnformatted(text); - ImGui.SetCursorPos(original); - ImGui.TextUnformatted(text); - ImGui.PopStyleColor(); + using (ImRaii.PushColor(ImGuiCol.Text, fontColor)) + { + ImGui.SetCursorPos(original); + ImGui.TextUnformatted(text); + ImGui.SetCursorPos(original); + ImGui.TextUnformatted(text); + } } public static void DrawOutlinedFont(ImDrawListPtr drawList, string text, Vector2 textPos, uint fontColor, uint outlineColor, int thickness) @@ -293,13 +295,6 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase drawList.AddText(textPos, fontColor, text); } - public static void DrawWithID(string id, Action drawSubSection) - { - ImGui.PushID(id); - drawSubSection.Invoke(); - ImGui.PopID(); - } - public static void FontText(string text, ImFontPtr font, Vector4? color = null) { using var pushedFont = ImRaii.PushFont(font); @@ -309,22 +304,17 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase public static Vector4 GetBoolColor(bool input) => input ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed; - public static Vector4 GetCpuLoadColor(double input) => input < 50 ? ImGuiColors.ParsedGreen : - input < 90 ? ImGuiColors.DalamudYellow : ImGuiColors.DalamudRed; - public static Vector2 GetIconButtonSize(FontAwesomeIcon icon) { - ImGui.PushFont(UiBuilder.IconFont); + using var font = ImRaii.PushFont(UiBuilder.IconFont); var buttonSize = ImGuiHelpers.GetButtonSize(icon.ToIconString()); - ImGui.PopFont(); return buttonSize; } public static Vector2 GetIconSize(FontAwesomeIcon icon) { - ImGui.PushFont(UiBuilder.IconFont); + using var font = ImRaii.PushFont(UiBuilder.IconFont); var iconSize = ImGui.CalcTextSize(icon.ToIconString()); - ImGui.PopFont(); return iconSize; } @@ -449,23 +439,6 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase } } - public static void OutlineTextWrapped(string text, Vector4 textcolor, Vector4 outlineColor, float dist = 3) - { - var cursorPos = ImGui.GetCursorPos(); - ColorTextWrapped(text, outlineColor); - ImGui.SetCursorPos(new(cursorPos.X, cursorPos.Y + dist)); - ColorTextWrapped(text, outlineColor); - ImGui.SetCursorPos(new(cursorPos.X + dist, cursorPos.Y)); - ColorTextWrapped(text, outlineColor); - ImGui.SetCursorPos(new(cursorPos.X + dist, cursorPos.Y + dist)); - ColorTextWrapped(text, outlineColor); - - ImGui.SetCursorPos(new(cursorPos.X + dist / 2, cursorPos.Y + dist / 2)); - ColorTextWrapped(text, textcolor); - ImGui.SetCursorPos(new(cursorPos.X + dist / 2, cursorPos.Y + dist / 2)); - ColorTextWrapped(text, textcolor); - } - public static void SetScaledWindowSize(float width, bool centerWindow = true) { var newLineHeight = ImGui.GetCursorPosY(); @@ -537,9 +510,8 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase public void BigText(string text) { - if (UidFontBuilt) ImGui.PushFont(UidFont); + using var font = ImRaii.PushFont(UidFont, UidFontBuilt); ImGui.TextUnformatted(text); - if (UidFontBuilt) ImGui.PopFont(); } public void DrawCacheDirectorySetting() @@ -854,24 +826,6 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase Strings.ToS = new Strings.ToSStrings(); } - public void PrintServerState() - { - if (_apiController.ServerState is ServerState.Connected) - { - ImGui.TextUnformatted("Service " + _serverConfigurationManager.CurrentServer!.ServerName + ":"); - ImGui.SameLine(); - ImGui.TextColored(ImGuiColors.ParsedGreen, "Available"); - ImGui.SameLine(); - ImGui.TextUnformatted("("); - ImGui.SameLine(); - ImGui.TextColored(ImGuiColors.ParsedGreen, _apiController.OnlineUsers.ToString(CultureInfo.InvariantCulture)); - ImGui.SameLine(); - ImGui.TextUnformatted("Users Online"); - ImGui.SameLine(); - ImGui.TextUnformatted(")"); - } - } - public void RecalculateFileCacheSize() { _cacheScanner.InvokeScan(forced: true); @@ -903,7 +857,9 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase ImGui.SetWindowPos(new Vector2(center.X - width / 2, center.Y - height / 2), cond); } +#pragma warning disable MA0009 // Add regex evaluation timeout [GeneratedRegex(@"^(?:[a-zA-Z]:\\[\w\s\-\\]+?|\/(?:[\w\s\-\/])+?)$", RegexOptions.ECMAScript)] +#pragma warning restore MA0009 // Add regex evaluation timeout private static partial Regex PathRegex(); private void BuildFont() diff --git a/MareSynchronos/WebAPI/SignalR/HubFactory.cs b/MareSynchronos/WebAPI/SignalR/HubFactory.cs index e9c6562..51512bf 100644 --- a/MareSynchronos/WebAPI/SignalR/HubFactory.cs +++ b/MareSynchronos/WebAPI/SignalR/HubFactory.cs @@ -16,21 +16,19 @@ namespace MareSynchronos.WebAPI.SignalR; public class HubFactory : MediatorSubscriberBase { - private readonly MareConfigService _configService; - private readonly IPluginLog _pluginLog; + private readonly ILoggerProvider _loggingProvider; private readonly ServerConfigurationManager _serverConfigurationManager; private readonly TokenProvider _tokenProvider; private HubConnection? _instance; private bool _isDisposed = false; public HubFactory(ILogger logger, MareMediator mediator, - ServerConfigurationManager serverConfigurationManager, MareConfigService configService, - TokenProvider tokenProvider, IPluginLog pluginLog) : base(logger, mediator) + ServerConfigurationManager serverConfigurationManager, + TokenProvider tokenProvider, ILoggerProvider pluginLog) : base(logger, mediator) { _serverConfigurationManager = serverConfigurationManager; - _configService = configService; _tokenProvider = tokenProvider; - _pluginLog = pluginLog; + _loggingProvider = pluginLog; } public async Task DisposeHubAsync() @@ -92,7 +90,7 @@ public class HubFactory : MediatorSubscriberBase .WithAutomaticReconnect(new ForeverRetryPolicy(Mediator)) .ConfigureLogging(a => { - a.ClearProviders().AddProvider(new DalamudLoggingProvider(_configService, _pluginLog)); + a.ClearProviders().AddProvider(_loggingProvider); a.SetMinimumLevel(LogLevel.Information); }) .Build(); From cdcfaa93694cee3cdc69cb7ee42349171ec87db6 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Fri, 27 Oct 2023 10:33:08 +0200 Subject: [PATCH 31/56] fix outlined font --- MareSynchronos/UI/UISharedService.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/MareSynchronos/UI/UISharedService.cs b/MareSynchronos/UI/UISharedService.cs index bd3f22d..7591de1 100644 --- a/MareSynchronos/UI/UISharedService.cs +++ b/MareSynchronos/UI/UISharedService.cs @@ -244,7 +244,6 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase using (ImRaii.PushColor(ImGuiCol.Text, outlineColor)) { - ImGui.PushStyleColor(ImGuiCol.Text, outlineColor); ImGui.SetCursorPos(original with { Y = original.Y - thickness }); ImGui.TextUnformatted(text); ImGui.SetCursorPos(original with { X = original.X - thickness }); From 1a64f08841f4adef9ed582e4c121a279d2c759cd Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Fri, 27 Oct 2023 17:25:15 +0200 Subject: [PATCH 32/56] make local groups more usable for pause/resume --- MareSynchronos/UI/CompactUI.cs | 175 ++++++++++-------- .../UI/Components/DrawFolderBase.cs | 13 +- .../UI/Components/DrawFolderGroup.cs | 8 +- MareSynchronos/UI/Components/DrawFolderTag.cs | 23 ++- .../UI/Components/DrawGroupedGroupFolder.cs | 5 +- MareSynchronos/UI/Components/DrawUserPair.cs | 4 - MareSynchronos/UI/Components/IDrawFolder.cs | 5 +- MareSynchronos/UI/DrawEntityFactory.cs | 15 +- 8 files changed, 144 insertions(+), 104 deletions(-) diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs index cf73dac..69d3caa 100644 --- a/MareSynchronos/UI/CompactUI.cs +++ b/MareSynchronos/UI/CompactUI.cs @@ -20,6 +20,7 @@ using MareSynchronos.WebAPI.Files.Models; using MareSynchronos.WebAPI.SignalR.Utils; using Microsoft.Extensions.Logging; using System.Collections.Concurrent; +using System.Collections.Immutable; using System.Globalization; using System.Numerics; using System.Reflection; @@ -392,47 +393,67 @@ public class CompactUi : WindowMediatorSubscriberBase { List drawFolders = []; - var users = GetFilteredGroupUsers() - .ToDictionary(g => g.Key, g => g.Value); + var allPairs = _pairManager.PairsWithGroups + .ToImmutableDictionary(k => k.Key, k => k.Value); + var filteredUsers = allPairs + .Where(p => + { + if (_tabMenu.Filter.IsNullOrEmpty()) return true; + return p.Key.UserData.AliasOrUID.Contains(_tabMenu.Filter, StringComparison.OrdinalIgnoreCase) || + (p.Key.GetNote()?.Contains(_tabMenu.Filter, StringComparison.OrdinalIgnoreCase) ?? false) || + (p.Key.PlayerName?.Contains(_tabMenu.Filter, StringComparison.OrdinalIgnoreCase) ?? false); + }) + .ToImmutableDictionary(k => k.Key, k => k.Value); + + string? alphabeticalSortFunc(KeyValuePair> u) + => (_configService.Current.ShowCharacterNameInsteadOfNotesForVisible && !string.IsNullOrEmpty(u.Key.PlayerName) + ? (_configService.Current.PreferNotesOverNamesForVisible ? u.Key.GetNote() : u.Key.PlayerName) + : (u.Key.GetNote() ?? u.Key.UserData.AliasOrUID)); if (_configService.Current.ShowVisibleUsersSeparately) { - var visibleUsers = users.Where(u => u.Key.IsVisible && - (_configService.Current.ShowSyncshellUsersInVisible || !(!_configService.Current.ShowSyncshellUsersInVisible && !u.Key.IsDirectlyPaired))) - .OrderBy( - u => _configService.Current.ShowCharacterNameInsteadOfNotesForVisible && !string.IsNullOrEmpty(u.Key.PlayerName) - ? (_configService.Current.PreferNotesOverNamesForVisible ? u.Key.GetNote() : u.Key.PlayerName) - : (u.Key.GetNote() ?? u.Key.UserData.AliasOrUID), StringComparer.OrdinalIgnoreCase) - .ToDictionary(k => k.Key, k => k.Value); + var allUsers = allPairs + .Where(u => u.Key.IsVisible + && (_configService.Current.ShowSyncshellUsersInVisible || !(!_configService.Current.ShowSyncshellUsersInVisible && !u.Key.IsDirectlyPaired))) + .Select(k => k.Key) + .ToImmutableList(); - drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomVisibleTag, visibleUsers)); + var visibleUsers = filteredUsers + .Where(u => u.Key.IsVisible + && (_configService.Current.ShowSyncshellUsersInVisible || !(!_configService.Current.ShowSyncshellUsersInVisible && !u.Key.IsDirectlyPaired))) + .OrderBy(alphabeticalSortFunc, StringComparer.OrdinalIgnoreCase) + .ToImmutableDictionary(k => k.Key, k => k.Value); + + drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomVisibleTag, visibleUsers, allUsers)); } List groupFolders = new(); foreach (var group in _pairManager.GroupPairs.Select(g => g.Key).OrderBy(g => g.GroupAliasOrGID, StringComparer.OrdinalIgnoreCase)) { - var groupUsers2 = users.Where(v => v.Value.Exists(g => string.Equals(g.GID, group.GID, StringComparison.Ordinal)) + var allUsers = allPairs + .Where(v => v.Value.Exists(g => string.Equals(g.GID, group.GID, StringComparison.Ordinal))) + .Select(k => k.Key) + .ToImmutableList(); + + var groupUsers = filteredUsers + .Where(v => v.Value.Exists(g => string.Equals(g.GID, group.GID, StringComparison.Ordinal)) && (v.Key.IsOnline || (!v.Key.IsOnline && !_configService.Current.ShowOfflineUsersSeparately) || v.Key.UserPair.OwnPermissions.IsPaused())) - .OrderByDescending(u => u.Key.IsOnline) - .ThenBy(u => + .OrderByDescending(u => u.Key.IsOnline) + .ThenBy(u => + { + if (string.Equals(u.Key.UserData.UID, group.OwnerUID, StringComparison.Ordinal)) return 0; + if (group.GroupPairUserInfos.TryGetValue(u.Key.UserData.UID, out var info)) { - if (string.Equals(u.Key.UserData.UID, group.OwnerUID, StringComparison.Ordinal)) return 0; - if (group.GroupPairUserInfos.TryGetValue(u.Key.UserData.UID, out var info)) - { - if (info.IsModerator()) return 1; - if (info.IsPinned()) return 2; - } - return u.Key.IsVisible ? 3 : 4; - }) - .ThenBy( - u => _configService.Current.ShowCharacterNameInsteadOfNotesForVisible && !string.IsNullOrEmpty(u.Key.PlayerName) - ? (_configService.Current.PreferNotesOverNamesForVisible ? u.Key.GetNote() : u.Key.PlayerName) - : (u.Key.GetNote() ?? u.Key.UserData.AliasOrUID), StringComparer.OrdinalIgnoreCase) - .ToDictionary(k => k.Key, k => k.Value); + if (info.IsModerator()) return 1; + if (info.IsPinned()) return 2; + } + return u.Key.IsVisible ? 3 : 4; + }) + .ThenBy(alphabeticalSortFunc, StringComparer.OrdinalIgnoreCase) + .ToImmutableDictionary(k => k.Key, k => k.Value); - groupFolders.Add(_drawEntityFactory.CreateDrawGroupFolder(group, groupUsers2, - users.Count(v => v.Value.Exists(g => string.Equals(g.GID, group.GID, StringComparison.Ordinal))))); + groupFolders.Add(_drawEntityFactory.CreateDrawGroupFolder(group, groupUsers, allUsers)); } if (_configService.Current.GroupUpSyncshells) @@ -444,77 +465,85 @@ public class CompactUi : WindowMediatorSubscriberBase HashSet alreadyInTags = []; foreach (var tag in tags) { - var tagUsers = users.Where(u => u.Key.IsDirectlyPaired && !u.Key.IsOneSidedPair && _tagHandler.HasTag(u.Key.UserData.UID, tag) - && (u.Key.IsOnline || (!u.Key.IsOnline && !_configService.Current.ShowOfflineUsersSeparately) - || u.Key.UserPair.OwnPermissions.IsPaused())) + var allUsers = allPairs + .Where(u => u.Key.IsDirectlyPaired && !u.Key.IsOneSidedPair && _tagHandler.HasTag(u.Key.UserData.UID, tag)) + .Select(k => k.Key) + .ToImmutableList(); + + var tagUsers = filteredUsers + .Where(u => u.Key.IsDirectlyPaired && !u.Key.IsOneSidedPair && _tagHandler.HasTag(u.Key.UserData.UID, tag) + && (u.Key.IsOnline || (!u.Key.IsOnline && !_configService.Current.ShowOfflineUsersSeparately) + || u.Key.UserPair.OwnPermissions.IsPaused())) .OrderByDescending(u => u.Key.IsVisible) .ThenByDescending(u => u.Key.IsOnline) - .ThenBy( - u => _configService.Current.ShowCharacterNameInsteadOfNotesForVisible && !string.IsNullOrEmpty(u.Key.PlayerName) - ? (_configService.Current.PreferNotesOverNamesForVisible ? u.Key.GetNote() : u.Key.PlayerName) - : (u.Key.GetNote() ?? u.Key.UserData.AliasOrUID), StringComparer.OrdinalIgnoreCase) - .ToDictionary(u => u.Key, u => u.Value); + .ThenBy(alphabeticalSortFunc, StringComparer.OrdinalIgnoreCase); drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(tag, tagUsers.Select(u => { alreadyInTags.Add(u.Key); return (u.Key, u.Value); - }).ToDictionary(u => u.Key, u => u.Value), users.Count(u => _tagHandler.HasTag(u.Key.UserData.UID, tag)))); + }).ToImmutableDictionary(u => u.Key, u => u.Value), allUsers)); } - var onlineDirectPairedUsersNotInTags = users.Where(u => u.Key.IsDirectlyPaired && !u.Key.IsOneSidedPair && !_tagHandler.HasAnyTag(u.Key.UserData.UID) - && (u.Key.IsOnline || (!u.Key.IsOnline && !_configService.Current.ShowOfflineUsersSeparately) - || u.Key.UserPair.OwnPermissions.IsPaused())) + var onlineAllPairedUsersNotInTags = allPairs + .Where(u => u.Key.IsDirectlyPaired && !u.Key.IsOneSidedPair && !_tagHandler.HasAnyTag(u.Key.UserData.UID)) + .Select(k => k.Key) + .ToImmutableList(); + + var onlineDirectPairedUsersNotInTags = filteredUsers + .Where(u => u.Key.IsDirectlyPaired && !u.Key.IsOneSidedPair && !_tagHandler.HasAnyTag(u.Key.UserData.UID) + && (u.Key.IsOnline || (!u.Key.IsOnline && !_configService.Current.ShowOfflineUsersSeparately) + || u.Key.UserPair.OwnPermissions.IsPaused())) .OrderByDescending(u => u.Key.IsVisible) .ThenByDescending(u => u.Key.IsOnline) - .ThenBy( - u => _configService.Current.ShowCharacterNameInsteadOfNotesForVisible && !string.IsNullOrEmpty(u.Key.PlayerName) - ? (_configService.Current.PreferNotesOverNamesForVisible ? u.Key.GetNote() : u.Key.PlayerName) - : (u.Key.GetNote() ?? u.Key.UserData.AliasOrUID), StringComparer.OrdinalIgnoreCase) - .ToDictionary(u => u.Key, u => u.Value); + .ThenBy(alphabeticalSortFunc, StringComparer.OrdinalIgnoreCase) + .ToImmutableDictionary(u => u.Key, u => u.Value); drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder((_configService.Current.ShowOfflineUsersSeparately ? TagHandler.CustomOnlineTag : TagHandler.CustomAllTag), - onlineDirectPairedUsersNotInTags)); + onlineDirectPairedUsersNotInTags, onlineAllPairedUsersNotInTags)); if (_configService.Current.ShowOfflineUsersSeparately) { - var offlineUsersEntries = users.Where(u => - ((u.Key.IsDirectlyPaired && _configService.Current.ShowSyncshellOfflineUsersSeparately) || !_configService.Current.ShowSyncshellOfflineUsersSeparately) - && (!u.Key.IsOneSidedPair || u.Value.Any()) && !u.Key.IsOnline && !u.Key.UserPair.OwnPermissions.IsPaused()).OrderBy( - u => _configService.Current.ShowCharacterNameInsteadOfNotesForVisible && !string.IsNullOrEmpty(u.Key.PlayerName) - ? (_configService.Current.PreferNotesOverNamesForVisible ? u.Key.GetNote() : u.Key.PlayerName) - : (u.Key.GetNote() ?? u.Key.UserData.AliasOrUID), StringComparer.OrdinalIgnoreCase) - .ToDictionary(u => u.Key, u => u.Value); + var allOfflineUsersEntries = allPairs + .Where(u => ((u.Key.IsDirectlyPaired && _configService.Current.ShowSyncshellOfflineUsersSeparately) + || !_configService.Current.ShowSyncshellOfflineUsersSeparately) + && (!u.Key.IsOneSidedPair || u.Value.Any()) && !u.Key.IsOnline && !u.Key.UserPair.OwnPermissions.IsPaused()) + .Select(k => k.Key) + .ToImmutableList(); - drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomOfflineTag, offlineUsersEntries)); + var offlineUsersEntries = filteredUsers + .Where(u => ((u.Key.IsDirectlyPaired && _configService.Current.ShowSyncshellOfflineUsersSeparately) + || !_configService.Current.ShowSyncshellOfflineUsersSeparately) + && (!u.Key.IsOneSidedPair || u.Value.Any()) && !u.Key.IsOnline && !u.Key.UserPair.OwnPermissions.IsPaused()) + .OrderBy(alphabeticalSortFunc, StringComparer.OrdinalIgnoreCase) + .ToImmutableDictionary(u => u.Key, u => u.Value); + + drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomOfflineTag, offlineUsersEntries, allOfflineUsersEntries)); if (_configService.Current.ShowSyncshellOfflineUsersSeparately) { - var offlineSyncshellUsers = users.Where(u => !u.Key.IsDirectlyPaired && !u.Key.IsOnline && !u.Key.UserPair.OwnPermissions.IsPaused()).OrderBy( - u => _configService.Current.ShowCharacterNameInsteadOfNotesForVisible && !string.IsNullOrEmpty(u.Key.PlayerName) - ? (_configService.Current.PreferNotesOverNamesForVisible ? u.Key.GetNote() : u.Key.PlayerName) - : (u.Key.GetNote() ?? u.Key.UserData.AliasOrUID), StringComparer.OrdinalIgnoreCase); - drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomOfflineSyncshellTag, offlineSyncshellUsers.ToDictionary(k => k.Key, k => k.Value))); + var allOfflineSyncshellUsers = allPairs + .Where(u => !u.Key.IsDirectlyPaired && !u.Key.IsOnline && !u.Key.UserPair.OwnPermissions.IsPaused()) + .Select(k => k.Key) + .ToImmutableList(); + + var offlineSyncshellUsers = allPairs + .Where(u => !u.Key.IsDirectlyPaired && !u.Key.IsOnline && !u.Key.UserPair.OwnPermissions.IsPaused()) + .OrderBy(alphabeticalSortFunc, StringComparer.OrdinalIgnoreCase) + .ToImmutableDictionary(k => k.Key, k => k.Value); + + drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomOfflineSyncshellTag, + offlineSyncshellUsers, + allOfflineSyncshellUsers)); } } - drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomUnpairedTag, users.Where(u => u.Key.IsOneSidedPair).ToDictionary(u => u.Key, u => u.Value))); + drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomUnpairedTag, + filteredUsers.Where(u => u.Key.IsOneSidedPair).ToImmutableDictionary(u => u.Key, u => u.Value), + allPairs.Where(u => u.Key.IsOneSidedPair).Select(k => k.Key).ToImmutableList())); return drawFolders; } - private Dictionary> GetFilteredGroupUsers() - { - if (string.IsNullOrEmpty(_tabMenu.Filter)) return _pairManager.PairsWithGroups; - - return _pairManager.PairsWithGroups.Where(p => - { - if (_tabMenu.Filter.IsNullOrEmpty()) return true; - return p.Key.UserData.AliasOrUID.Contains(_tabMenu.Filter, StringComparison.OrdinalIgnoreCase) || - (p.Key.GetNote()?.Contains(_tabMenu.Filter, StringComparison.OrdinalIgnoreCase) ?? false) || - (p.Key.PlayerName?.Contains(_tabMenu.Filter, StringComparison.OrdinalIgnoreCase) ?? false); - }).ToDictionary(k => k.Key, k => k.Value); - } - private string GetServerError() { return _apiController.ServerState switch diff --git a/MareSynchronos/UI/Components/DrawFolderBase.cs b/MareSynchronos/UI/Components/DrawFolderBase.cs index c2a487f..7d47425 100644 --- a/MareSynchronos/UI/Components/DrawFolderBase.cs +++ b/MareSynchronos/UI/Components/DrawFolderBase.cs @@ -2,24 +2,29 @@ using Dalamud.Interface.Components; using Dalamud.Interface.Utility.Raii; using ImGuiNET; +using MareSynchronos.PlayerData.Pairs; using MareSynchronos.UI.Handlers; +using System.Collections.Immutable; namespace MareSynchronos.UI.Components; public abstract class DrawFolderBase : IDrawFolder { - public IEnumerable DrawPairs { get; private set; } + public IImmutableList DrawPairs { get; init; } protected readonly string _id; + protected readonly IImmutableList _allPairs; protected readonly TagHandler _tagHandler; private float _menuWidth = -1; public int OnlinePairs => DrawPairs.Count(u => u.Pair.IsOnline); - public int TotalPairs { get; } - protected DrawFolderBase(string id, IEnumerable drawPairs, TagHandler tagHandler, int totalPairs) + public int TotalPairs => _allPairs.Count; + + protected DrawFolderBase(string id, IImmutableList drawPairs, + IImmutableList allPairs, TagHandler tagHandler) { _id = id; DrawPairs = drawPairs; + _allPairs = allPairs; _tagHandler = tagHandler; - TotalPairs = totalPairs; } protected abstract bool RenderIfEmpty { get; } diff --git a/MareSynchronos/UI/Components/DrawFolderGroup.cs b/MareSynchronos/UI/Components/DrawFolderGroup.cs index 1151b3b..b85d47a 100644 --- a/MareSynchronos/UI/Components/DrawFolderGroup.cs +++ b/MareSynchronos/UI/Components/DrawFolderGroup.cs @@ -6,9 +6,11 @@ using Dalamud.Interface.Utility.Raii; using ImGuiNET; using MareSynchronos.API.Data.Extensions; using MareSynchronos.API.Dto.Group; +using MareSynchronos.PlayerData.Pairs; using MareSynchronos.Services.Mediator; using MareSynchronos.UI.Handlers; using MareSynchronos.WebAPI; +using System.Collections.Immutable; namespace MareSynchronos.UI.Components; @@ -20,9 +22,9 @@ public class DrawFolderGroup : DrawFolderBase private readonly MareMediator _mareMediator; public DrawFolderGroup(string id, GroupFullInfoDto groupFullInfoDto, ApiController apiController, - IEnumerable drawPairs, TagHandler tagHandler, IdDisplayHandler idDisplayHandler, - MareMediator mareMediator, int totalPairs) : - base(id, drawPairs, tagHandler, totalPairs) + IImmutableList drawPairs, IImmutableList allPairs, TagHandler tagHandler, IdDisplayHandler idDisplayHandler, + MareMediator mareMediator) : + base(id, drawPairs, allPairs, tagHandler) { _groupFullInfoDto = groupFullInfoDto; _apiController = apiController; diff --git a/MareSynchronos/UI/Components/DrawFolderTag.cs b/MareSynchronos/UI/Components/DrawFolderTag.cs index ba8f7d5..e1acba5 100644 --- a/MareSynchronos/UI/Components/DrawFolderTag.cs +++ b/MareSynchronos/UI/Components/DrawFolderTag.cs @@ -3,8 +3,10 @@ using Dalamud.Interface.Components; using Dalamud.Interface.Utility.Raii; using ImGuiNET; using MareSynchronos.API.Data.Extensions; +using MareSynchronos.PlayerData.Pairs; using MareSynchronos.UI.Handlers; using MareSynchronos.WebAPI; +using System.Collections.Immutable; namespace MareSynchronos.UI.Components; @@ -13,8 +15,9 @@ public class DrawFolderTag : DrawFolderBase private readonly ApiController _apiController; private readonly SelectPairForTagUi _selectPairForTagUi; - public DrawFolderTag(string id, IEnumerable drawPairs, TagHandler tagHandler, ApiController apiController, SelectPairForTagUi selectPairForTagUi, int totalPairs) - : base(id, drawPairs, tagHandler, totalPairs) + public DrawFolderTag(string id, IImmutableList drawPairs, IImmutableList allPairs, + TagHandler tagHandler, ApiController apiController, SelectPairForTagUi selectPairForTagUi) + : base(id, drawPairs, allPairs, tagHandler) { _apiController = apiController; _selectPairForTagUi = selectPairForTagUi; @@ -51,7 +54,7 @@ public class DrawFolderTag : DrawFolderBase TagHandler.CustomAllTag => false, TagHandler.CustomOfflineSyncshellTag => false, _ => true, - } && DrawPairs.Any(); + } && _allPairs.Any(); private bool RenderCount => _id switch { @@ -135,7 +138,7 @@ public class DrawFolderTag : DrawFolderBase { if (!RenderPause) return currentRightSideX; - var allArePaused = DrawPairs.All(pair => pair.UserPair!.OwnPermissions.IsPaused()); + var allArePaused = _allPairs.All(pair => pair.UserPair!.OwnPermissions.IsPaused()); var pauseButton = allArePaused ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause; var pauseButtonX = UiSharedService.GetIconButtonSize(pauseButton).X; @@ -145,11 +148,11 @@ public class DrawFolderTag : DrawFolderBase { if (allArePaused) { - ResumeAllPairs(DrawPairs); + ResumeAllPairs(_allPairs); } else { - PauseRemainingPairs(DrawPairs); + PauseRemainingPairs(_allPairs); } } if (allArePaused) @@ -164,10 +167,10 @@ public class DrawFolderTag : DrawFolderBase return currentRightSideX; } - private void PauseRemainingPairs(IEnumerable availablePairs) + private void PauseRemainingPairs(IEnumerable availablePairs) { _ = _apiController.SetBulkPermissions(new(availablePairs - .ToDictionary(g => g.UID, g => + .ToDictionary(g => g.UserData.UID, g => { var perm = g.UserPair.OwnPermissions; perm.SetPaused(paused: true); @@ -176,10 +179,10 @@ public class DrawFolderTag : DrawFolderBase .ConfigureAwait(false); } - private void ResumeAllPairs(IEnumerable availablePairs) + private void ResumeAllPairs(IEnumerable availablePairs) { _ = _apiController.SetBulkPermissions(new(availablePairs - .ToDictionary(g => g.UID, g => + .ToDictionary(g => g.UserData.UID, g => { var perm = g.UserPair.OwnPermissions; perm.SetPaused(paused: false); diff --git a/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs b/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs index 6968924..d323b3e 100644 --- a/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs +++ b/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs @@ -2,6 +2,7 @@ using Dalamud.Interface.Utility.Raii; using ImGuiNET; using MareSynchronos.UI.Handlers; +using System.Collections.Immutable; namespace MareSynchronos.UI.Components; @@ -9,8 +10,8 @@ public class DrawGroupedGroupFolder : IDrawFolder { private readonly IEnumerable _groups; private readonly TagHandler _tagHandler; - public IEnumerable DrawPairs => throw new NotSupportedException(); - public int OnlinePairs => _groups.SelectMany(g => g.DrawPairs).Where(g => g.Pair.IsOnline).DistinctBy(g => g.UID).Count(); + public IImmutableList DrawPairs => throw new NotSupportedException(); + public int OnlinePairs => _groups.SelectMany(g => g.DrawPairs).Where(g => g.Pair.IsOnline).DistinctBy(g => g.Pair.UserData.UID).Count(); public int TotalPairs => _groups.Sum(g => g.TotalPairs); public DrawGroupedGroupFolder(IEnumerable groups, TagHandler tagHandler) diff --git a/MareSynchronos/UI/Components/DrawUserPair.cs b/MareSynchronos/UI/Components/DrawUserPair.cs index 8752596..6f05ad4 100644 --- a/MareSynchronos/UI/Components/DrawUserPair.cs +++ b/MareSynchronos/UI/Components/DrawUserPair.cs @@ -43,15 +43,11 @@ public class DrawUserPair } public Pair Pair => _pair; - public string UID => _pair.UserData.UID; public UserFullPairDto UserPair => _pair.UserPair!; public void DrawPairedClient() { using var id = ImRaii.PushId(GetType() + _id); - var originalY = ImGui.GetCursorPosY(); - var pauseIconSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars); - var textSize = ImGui.CalcTextSize(_pair.UserData.AliasOrUID); DrawLeftSide(); ImGui.SameLine(); diff --git a/MareSynchronos/UI/Components/IDrawFolder.cs b/MareSynchronos/UI/Components/IDrawFolder.cs index 82a2df5..cfac371 100644 --- a/MareSynchronos/UI/Components/IDrawFolder.cs +++ b/MareSynchronos/UI/Components/IDrawFolder.cs @@ -1,11 +1,12 @@  +using System.Collections.Immutable; + namespace MareSynchronos.UI.Components; public interface IDrawFolder { int TotalPairs { get; } int OnlinePairs { get; } - IEnumerable DrawPairs { get; } - + IImmutableList DrawPairs { get; } void Draw(); } diff --git a/MareSynchronos/UI/DrawEntityFactory.cs b/MareSynchronos/UI/DrawEntityFactory.cs index 320c9b2..6989d37 100644 --- a/MareSynchronos/UI/DrawEntityFactory.cs +++ b/MareSynchronos/UI/DrawEntityFactory.cs @@ -6,6 +6,7 @@ using MareSynchronos.UI.Components; using MareSynchronos.UI.Handlers; using MareSynchronos.WebAPI; using Microsoft.Extensions.Logging; +using System.Collections.Immutable; namespace MareSynchronos.UI; @@ -35,17 +36,19 @@ public class DrawEntityFactory _serverConfigurationManager = serverConfigurationManager; } - public DrawFolderGroup CreateDrawGroupFolder(GroupFullInfoDto groupFullInfoDto, Dictionary> pairs, int totalPairs = -1) + public DrawFolderGroup CreateDrawGroupFolder(GroupFullInfoDto groupFullInfoDto, IImmutableDictionary> pairs, + IImmutableList allPairs) { return new DrawFolderGroup(groupFullInfoDto.Group.GID, groupFullInfoDto, _apiController, - pairs.Select(p => CreateDrawPair(groupFullInfoDto.Group.GID + p.Key.UserData.UID, p.Key, p.Value)).ToList(), - _tagHandler, _uidDisplayHandler, _mediator, totalPairs); + pairs.Select(p => CreateDrawPair(groupFullInfoDto.Group.GID + p.Key.UserData.UID, p.Key, p.Value)).ToImmutableList(), + allPairs, _tagHandler, _uidDisplayHandler, _mediator); } - public DrawFolderTag CreateDrawTagFolder(string tag, Dictionary> pairs, int totalPairs = -1) + public DrawFolderTag CreateDrawTagFolder(string tag, IImmutableDictionary> pairs, + IImmutableList allPairs) { - return new(tag, pairs.Select(u => CreateDrawPair(tag, u.Key, u.Value)).ToList(), - _tagHandler, _apiController, _selectPairForTagUi, totalPairs); + return new(tag, pairs.Select(u => CreateDrawPair(tag, u.Key, u.Value)).ToImmutableList(), + allPairs, _tagHandler, _apiController, _selectPairForTagUi); } public DrawUserPair CreateDrawPair(string id, Pair user, List groups) From f8ad8f3d79226f7213ad0fbaf4f254df25612d46 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Fri, 27 Oct 2023 17:56:44 +0200 Subject: [PATCH 33/56] fix sorting and cleanup --- MareSynchronos/UI/CompactUI.cs | 177 +++++++++++++------------ MareSynchronos/UI/DrawEntityFactory.cs | 10 +- 2 files changed, 99 insertions(+), 88 deletions(-) diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs index 69d3caa..2eeb07b 100644 --- a/MareSynchronos/UI/CompactUI.cs +++ b/MareSynchronos/UI/CompactUI.cs @@ -29,8 +29,6 @@ namespace MareSynchronos.UI; public class CompactUi : WindowMediatorSubscriberBase { - public float TransferPartHeight; - public float WindowContentWidth; private readonly ApiController _apiController; private readonly MareConfigService _configService; private readonly ConcurrentDictionary> _currentDownloads = new(); @@ -39,8 +37,8 @@ public class CompactUi : WindowMediatorSubscriberBase private readonly PairManager _pairManager; private readonly SelectTagForPairUi _selectGroupForPairUi; private readonly SelectPairForTagUi _selectPairsForGroupUi; - private readonly TopTabMenu _tabMenu; private readonly ServerConfigurationManager _serverManager; + private readonly TopTabMenu _tabMenu; private readonly TagHandler _tagHandler; private readonly UiSharedService _uiShared; private List _drawFolders; @@ -50,8 +48,9 @@ public class CompactUi : WindowMediatorSubscriberBase private Vector2 _lastSize = Vector2.One; private int _secretKeyIdx = -1; private bool _showModalForUserAddition; + private float _transferPartHeight; private bool _wasOpen; - + private float _windowContentWidth; public CompactUi(ILogger logger, UiSharedService uiShared, MareConfigService configService, ApiController apiController, PairManager pairManager, ServerConfigurationManager serverManager, MareMediator mediator, FileUploadManager fileTransferManager, @@ -100,7 +99,7 @@ public class CompactUi : WindowMediatorSubscriberBase public override void Draw() { - WindowContentWidth = UiSharedService.GetWindowContentRegionWidth(); + _windowContentWidth = UiSharedService.GetWindowContentRegionWidth(); if (!_apiController.IsCurrentVersion) { var ver = _apiController.CurrentClientVersion; @@ -125,11 +124,10 @@ public class CompactUi : WindowMediatorSubscriberBase { using (ImRaii.PushId("global-topmenu")) _tabMenu.Draw(); using (ImRaii.PushId("pairlist")) DrawPairs(); - TransferPartHeight = ImGui.GetCursorPosY(); - ImGui.Separator(); + float pairlistEnd = ImGui.GetCursorPosY(); using (ImRaii.PushId("transfers")) DrawTransfers(); - TransferPartHeight = ImGui.GetCursorPosY() - TransferPartHeight - ImGui.GetTextLineHeight(); + _transferPartHeight = ImGui.GetCursorPosY() - pairlistEnd - ImGui.GetTextLineHeight(); using (ImRaii.PushId("group-user-popup")) _selectPairsForGroupUi.Draw(_pairManager.DirectPairs); using (ImRaii.PushId("grouping-popup")) _selectGroupForPairUi.Draw(); } @@ -175,23 +173,6 @@ public class CompactUi : WindowMediatorSubscriberBase } } - private void DrawPairs() - { - var ySize = TransferPartHeight == 0 - ? 1 - : (ImGui.GetWindowContentRegionMax().Y - ImGui.GetWindowContentRegionMin().Y - + ImGui.GetTextLineHeight() - ImGui.GetStyle().WindowPadding.Y - ImGui.GetStyle().WindowBorderSize) - TransferPartHeight - ImGui.GetCursorPosY(); - - ImGui.BeginChild("list", new Vector2(WindowContentWidth, ySize), border: false); - - foreach (var item in _drawFolders) - { - item.Draw(); - } - - ImGui.EndChild(); - } - private void DrawAddCharacter() { ImGui.Dummy(new(10)); @@ -221,6 +202,22 @@ public class CompactUi : WindowMediatorSubscriberBase } } + private void DrawPairs() + { + var ySize = _transferPartHeight == 0 + ? 1 + : (ImGui.GetWindowContentRegionMax().Y - ImGui.GetWindowContentRegionMin().Y + + ImGui.GetTextLineHeight() - ImGui.GetStyle().WindowPadding.Y - ImGui.GetStyle().WindowBorderSize) - _transferPartHeight - ImGui.GetCursorPosY(); + + ImGui.BeginChild("list", new Vector2(_windowContentWidth, ySize), border: false); + + foreach (var item in _drawFolders) + { + item.Draw(); + } + + ImGui.EndChild(); + } private void DrawServerStatus() { var buttonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Link); @@ -304,7 +301,7 @@ public class CompactUi : WindowMediatorSubscriberBase ImGui.TextUnformatted($"{doneUploads}/{totalUploads}"); var uploadText = $"({UiSharedService.ByteToString(totalUploaded)}/{UiSharedService.ByteToString(totalToUpload)})"; var textSize = ImGui.CalcTextSize(uploadText); - ImGui.SameLine(WindowContentWidth - textSize.X); + ImGui.SameLine(_windowContentWidth - textSize.X); ImGui.TextUnformatted(uploadText); } else @@ -327,7 +324,7 @@ public class CompactUi : WindowMediatorSubscriberBase var downloadText = $"({UiSharedService.ByteToString(totalDownloaded)}/{UiSharedService.ByteToString(totalToDownload)})"; var textSize = ImGui.CalcTextSize(downloadText); - ImGui.SameLine(WindowContentWidth - textSize.X); + ImGui.SameLine(_windowContentWidth - textSize.X); ImGui.TextUnformatted(downloadText); } else @@ -394,8 +391,8 @@ public class CompactUi : WindowMediatorSubscriberBase List drawFolders = []; var allPairs = _pairManager.PairsWithGroups - .ToImmutableDictionary(k => k.Key, k => k.Value); - var filteredUsers = allPairs + .ToDictionary(k => k.Key, k => k.Value); + var filteredPairs = allPairs .Where(p => { if (_tabMenu.Filter.IsNullOrEmpty()) return true; @@ -403,42 +400,48 @@ public class CompactUi : WindowMediatorSubscriberBase (p.Key.GetNote()?.Contains(_tabMenu.Filter, StringComparison.OrdinalIgnoreCase) ?? false) || (p.Key.PlayerName?.Contains(_tabMenu.Filter, StringComparison.OrdinalIgnoreCase) ?? false); }) - .ToImmutableDictionary(k => k.Key, k => k.Value); + .ToDictionary(k => k.Key, k => k.Value); - string? alphabeticalSortFunc(KeyValuePair> u) + string? AlphabeticalSort(KeyValuePair> u) => (_configService.Current.ShowCharacterNameInsteadOfNotesForVisible && !string.IsNullOrEmpty(u.Key.PlayerName) ? (_configService.Current.PreferNotesOverNamesForVisible ? u.Key.GetNote() : u.Key.PlayerName) : (u.Key.GetNote() ?? u.Key.UserData.AliasOrUID)); + bool FilterOnlineOrPausedSelf(KeyValuePair> u) + => (u.Key.IsOnline || (!u.Key.IsOnline && !_configService.Current.ShowOfflineUsersSeparately) + || u.Key.UserPair.OwnPermissions.IsPaused()); if (_configService.Current.ShowVisibleUsersSeparately) { - var allUsers = allPairs - .Where(u => u.Key.IsVisible - && (_configService.Current.ShowSyncshellUsersInVisible || !(!_configService.Current.ShowSyncshellUsersInVisible && !u.Key.IsDirectlyPaired))) + bool BaseFilterVisibleUsers(KeyValuePair> u) => + u.Key.IsVisible + && (_configService.Current.ShowSyncshellUsersInVisible || !(!_configService.Current.ShowSyncshellUsersInVisible && !u.Key.IsDirectlyPaired)); + + var allVisiblePairs = allPairs + .Where(BaseFilterVisibleUsers) .Select(k => k.Key) .ToImmutableList(); - var visibleUsers = filteredUsers - .Where(u => u.Key.IsVisible - && (_configService.Current.ShowSyncshellUsersInVisible || !(!_configService.Current.ShowSyncshellUsersInVisible && !u.Key.IsDirectlyPaired))) - .OrderBy(alphabeticalSortFunc, StringComparer.OrdinalIgnoreCase) - .ToImmutableDictionary(k => k.Key, k => k.Value); + var filteredVisiblePairs = filteredPairs + .Where(BaseFilterVisibleUsers) + .OrderBy(AlphabeticalSort, StringComparer.OrdinalIgnoreCase) + .ToDictionary(k => k.Key, k => k.Value); - drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomVisibleTag, visibleUsers, allUsers)); + drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomVisibleTag, filteredVisiblePairs, allVisiblePairs)); } List groupFolders = new(); foreach (var group in _pairManager.GroupPairs.Select(g => g.Key).OrderBy(g => g.GroupAliasOrGID, StringComparer.OrdinalIgnoreCase)) { - var allUsers = allPairs - .Where(v => v.Value.Exists(g => string.Equals(g.GID, group.GID, StringComparison.Ordinal))) + bool BaseFilterGroupUsers(KeyValuePair> u, GroupFullInfoDto group) => + u.Value.Exists(g => string.Equals(g.GID, group.GID, StringComparison.Ordinal)); + + var allGroupPairs = allPairs + .Where(u => BaseFilterGroupUsers(u, group)) .Select(k => k.Key) .ToImmutableList(); - var groupUsers = filteredUsers - .Where(v => v.Value.Exists(g => string.Equals(g.GID, group.GID, StringComparison.Ordinal)) - && (v.Key.IsOnline || (!v.Key.IsOnline && !_configService.Current.ShowOfflineUsersSeparately) - || v.Key.UserPair.OwnPermissions.IsPaused())) + var filteredGroupPairs = filteredPairs + .Where(u => BaseFilterGroupUsers(u, group) && FilterOnlineOrPausedSelf(u)) .OrderByDescending(u => u.Key.IsOnline) .ThenBy(u => { @@ -450,10 +453,10 @@ public class CompactUi : WindowMediatorSubscriberBase } return u.Key.IsVisible ? 3 : 4; }) - .ThenBy(alphabeticalSortFunc, StringComparer.OrdinalIgnoreCase) - .ToImmutableDictionary(k => k.Key, k => k.Value); + .ThenBy(AlphabeticalSort, StringComparer.OrdinalIgnoreCase) + .ToDictionary(k => k.Key, k => k.Value); - groupFolders.Add(_drawEntityFactory.CreateDrawGroupFolder(group, groupUsers, allUsers)); + groupFolders.Add(_drawEntityFactory.CreateDrawGroupFolder(group, filteredGroupPairs, allGroupPairs)); } if (_configService.Current.GroupUpSyncshells) @@ -465,80 +468,86 @@ public class CompactUi : WindowMediatorSubscriberBase HashSet alreadyInTags = []; foreach (var tag in tags) { - var allUsers = allPairs - .Where(u => u.Key.IsDirectlyPaired && !u.Key.IsOneSidedPair && _tagHandler.HasTag(u.Key.UserData.UID, tag)) + bool BaseFilterTagUsers(KeyValuePair> u, string tag) => + u.Key.IsDirectlyPaired && !u.Key.IsOneSidedPair && _tagHandler.HasTag(u.Key.UserData.UID, tag); + + var allTagPairs = allPairs + .Where(u => BaseFilterTagUsers(u, tag)) .Select(k => k.Key) .ToImmutableList(); - var tagUsers = filteredUsers - .Where(u => u.Key.IsDirectlyPaired && !u.Key.IsOneSidedPair && _tagHandler.HasTag(u.Key.UserData.UID, tag) - && (u.Key.IsOnline || (!u.Key.IsOnline && !_configService.Current.ShowOfflineUsersSeparately) - || u.Key.UserPair.OwnPermissions.IsPaused())) + var filteredTagPairs = filteredPairs + .Where(u => BaseFilterTagUsers(u, tag) && FilterOnlineOrPausedSelf(u)) .OrderByDescending(u => u.Key.IsVisible) .ThenByDescending(u => u.Key.IsOnline) - .ThenBy(alphabeticalSortFunc, StringComparer.OrdinalIgnoreCase); + .ThenBy(AlphabeticalSort, StringComparer.OrdinalIgnoreCase); - drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(tag, tagUsers.Select(u => + drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(tag, filteredTagPairs.Select(u => { alreadyInTags.Add(u.Key); return (u.Key, u.Value); - }).ToImmutableDictionary(u => u.Key, u => u.Value), allUsers)); + }).ToDictionary(u => u.Key, u => u.Value), allTagPairs)); } - var onlineAllPairedUsersNotInTags = allPairs - .Where(u => u.Key.IsDirectlyPaired && !u.Key.IsOneSidedPair && !_tagHandler.HasAnyTag(u.Key.UserData.UID)) + bool BaseFilterNotTaggedUsers(KeyValuePair> u) => + u.Key.IsDirectlyPaired && !u.Key.IsOneSidedPair && !_tagHandler.HasAnyTag(u.Key.UserData.UID); + + var allOnlineNotTaggedPairs = allPairs + .Where(BaseFilterNotTaggedUsers) .Select(k => k.Key) .ToImmutableList(); - var onlineDirectPairedUsersNotInTags = filteredUsers - .Where(u => u.Key.IsDirectlyPaired && !u.Key.IsOneSidedPair && !_tagHandler.HasAnyTag(u.Key.UserData.UID) - && (u.Key.IsOnline || (!u.Key.IsOnline && !_configService.Current.ShowOfflineUsersSeparately) - || u.Key.UserPair.OwnPermissions.IsPaused())) + var onlineNotTaggedPairs = filteredPairs + .Where(u => BaseFilterNotTaggedUsers(u) && FilterOnlineOrPausedSelf(u)) .OrderByDescending(u => u.Key.IsVisible) .ThenByDescending(u => u.Key.IsOnline) - .ThenBy(alphabeticalSortFunc, StringComparer.OrdinalIgnoreCase) - .ToImmutableDictionary(u => u.Key, u => u.Value); + .ThenBy(AlphabeticalSort, StringComparer.OrdinalIgnoreCase) + .ToDictionary(u => u.Key, u => u.Value); drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder((_configService.Current.ShowOfflineUsersSeparately ? TagHandler.CustomOnlineTag : TagHandler.CustomAllTag), - onlineDirectPairedUsersNotInTags, onlineAllPairedUsersNotInTags)); + onlineNotTaggedPairs, allOnlineNotTaggedPairs)); if (_configService.Current.ShowOfflineUsersSeparately) { - var allOfflineUsersEntries = allPairs - .Where(u => ((u.Key.IsDirectlyPaired && _configService.Current.ShowSyncshellOfflineUsersSeparately) + bool BaseFilterOfflineUsers(KeyValuePair> u) => + ((u.Key.IsDirectlyPaired && _configService.Current.ShowSyncshellOfflineUsersSeparately) || !_configService.Current.ShowSyncshellOfflineUsersSeparately) - && (!u.Key.IsOneSidedPair || u.Value.Any()) && !u.Key.IsOnline && !u.Key.UserPair.OwnPermissions.IsPaused()) + && (!u.Key.IsOneSidedPair || u.Value.Any()) && !u.Key.IsOnline && !u.Key.UserPair.OwnPermissions.IsPaused(); + + var allOfflinePairs = allPairs + .Where(BaseFilterOfflineUsers) .Select(k => k.Key) .ToImmutableList(); - var offlineUsersEntries = filteredUsers - .Where(u => ((u.Key.IsDirectlyPaired && _configService.Current.ShowSyncshellOfflineUsersSeparately) - || !_configService.Current.ShowSyncshellOfflineUsersSeparately) - && (!u.Key.IsOneSidedPair || u.Value.Any()) && !u.Key.IsOnline && !u.Key.UserPair.OwnPermissions.IsPaused()) - .OrderBy(alphabeticalSortFunc, StringComparer.OrdinalIgnoreCase) - .ToImmutableDictionary(u => u.Key, u => u.Value); + var filteredOfflinePairs = filteredPairs + .Where(BaseFilterOfflineUsers) + .OrderBy(AlphabeticalSort, StringComparer.OrdinalIgnoreCase) + .ToDictionary(u => u.Key, u => u.Value); - drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomOfflineTag, offlineUsersEntries, allOfflineUsersEntries)); + drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomOfflineTag, filteredOfflinePairs, allOfflinePairs)); if (_configService.Current.ShowSyncshellOfflineUsersSeparately) { + bool BaseFilterOfflineSyncshellUsers(KeyValuePair> u) => + (!u.Key.IsDirectlyPaired && !u.Key.IsOnline && !u.Key.UserPair.OwnPermissions.IsPaused()); + var allOfflineSyncshellUsers = allPairs - .Where(u => !u.Key.IsDirectlyPaired && !u.Key.IsOnline && !u.Key.UserPair.OwnPermissions.IsPaused()) + .Where(BaseFilterOfflineSyncshellUsers) .Select(k => k.Key) .ToImmutableList(); - var offlineSyncshellUsers = allPairs - .Where(u => !u.Key.IsDirectlyPaired && !u.Key.IsOnline && !u.Key.UserPair.OwnPermissions.IsPaused()) - .OrderBy(alphabeticalSortFunc, StringComparer.OrdinalIgnoreCase) - .ToImmutableDictionary(k => k.Key, k => k.Value); + var filteredOfflineSyncshellUsers = filteredPairs + .Where(BaseFilterOfflineSyncshellUsers) + .OrderBy(AlphabeticalSort, StringComparer.OrdinalIgnoreCase) + .ToDictionary(k => k.Key, k => k.Value); drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomOfflineSyncshellTag, - offlineSyncshellUsers, + filteredOfflineSyncshellUsers, allOfflineSyncshellUsers)); } } drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomUnpairedTag, - filteredUsers.Where(u => u.Key.IsOneSidedPair).ToImmutableDictionary(u => u.Key, u => u.Value), + filteredPairs.Where(u => u.Key.IsOneSidedPair).ToDictionary(u => u.Key, u => u.Value), allPairs.Where(u => u.Key.IsOneSidedPair).Select(k => k.Key).ToImmutableList())); return drawFolders; diff --git a/MareSynchronos/UI/DrawEntityFactory.cs b/MareSynchronos/UI/DrawEntityFactory.cs index 6989d37..3e7ecdd 100644 --- a/MareSynchronos/UI/DrawEntityFactory.cs +++ b/MareSynchronos/UI/DrawEntityFactory.cs @@ -36,18 +36,20 @@ public class DrawEntityFactory _serverConfigurationManager = serverConfigurationManager; } - public DrawFolderGroup CreateDrawGroupFolder(GroupFullInfoDto groupFullInfoDto, IImmutableDictionary> pairs, + public DrawFolderGroup CreateDrawGroupFolder(GroupFullInfoDto groupFullInfoDto, + Dictionary> filteredPairs, IImmutableList allPairs) { return new DrawFolderGroup(groupFullInfoDto.Group.GID, groupFullInfoDto, _apiController, - pairs.Select(p => CreateDrawPair(groupFullInfoDto.Group.GID + p.Key.UserData.UID, p.Key, p.Value)).ToImmutableList(), + filteredPairs.Select(p => CreateDrawPair(groupFullInfoDto.Group.GID + p.Key.UserData.UID, p.Key, p.Value)).ToImmutableList(), allPairs, _tagHandler, _uidDisplayHandler, _mediator); } - public DrawFolderTag CreateDrawTagFolder(string tag, IImmutableDictionary> pairs, + public DrawFolderTag CreateDrawTagFolder(string tag, + Dictionary> filteredPairs, IImmutableList allPairs) { - return new(tag, pairs.Select(u => CreateDrawPair(tag, u.Key, u.Value)).ToImmutableList(), + return new(tag, filteredPairs.Select(u => CreateDrawPair(tag, u.Key, u.Value)).ToImmutableList(), allPairs, _tagHandler, _apiController, _selectPairForTagUi); } From c1a3efe06626803fc4dbfd0bfe19deb5a2ac90c4 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Fri, 27 Oct 2023 18:15:44 +0200 Subject: [PATCH 34/56] more cleanup --- MareSynchronos/UI/CompactUI.cs | 131 ++++++++++++--------------------- 1 file changed, 49 insertions(+), 82 deletions(-) diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs index 2eeb07b..079d3dd 100644 --- a/MareSynchronos/UI/CompactUI.cs +++ b/MareSynchronos/UI/CompactUI.cs @@ -409,22 +409,36 @@ public class CompactUi : WindowMediatorSubscriberBase bool FilterOnlineOrPausedSelf(KeyValuePair> u) => (u.Key.IsOnline || (!u.Key.IsOnline && !_configService.Current.ShowOfflineUsersSeparately) || u.Key.UserPair.OwnPermissions.IsPaused()); + Dictionary> BasicSortedDictionary(IEnumerable>> u) + => u.OrderByDescending(u => u.Key.IsVisible) + .ThenByDescending(u => u.Key.IsOnline) + .ThenBy(AlphabeticalSort, StringComparer.OrdinalIgnoreCase) + .ToDictionary(u => u.Key, u => u.Value); + ImmutableList ImmutablePairList(IEnumerable>> u) + => u.Select(k => k.Key).ToImmutableList(); + bool FilterVisibleUsers(KeyValuePair> u) + => u.Key.IsVisible + && (_configService.Current.ShowSyncshellUsersInVisible || !(!_configService.Current.ShowSyncshellUsersInVisible && !u.Key.IsDirectlyPaired)); + bool FilterTagusers(KeyValuePair> u, string tag) + => u.Key.IsDirectlyPaired && !u.Key.IsOneSidedPair && _tagHandler.HasTag(u.Key.UserData.UID, tag); + bool FilterGroupUsers(KeyValuePair> u, GroupFullInfoDto group) + => u.Value.Exists(g => string.Equals(g.GID, group.GID, StringComparison.Ordinal)); + bool FilterNotTaggedUsers(KeyValuePair> u) + => u.Key.IsDirectlyPaired && !u.Key.IsOneSidedPair && !_tagHandler.HasAnyTag(u.Key.UserData.UID); + bool FilterOfflineUsers(KeyValuePair> u) + => ((u.Key.IsDirectlyPaired && _configService.Current.ShowSyncshellOfflineUsersSeparately) + || !_configService.Current.ShowSyncshellOfflineUsersSeparately) + && (!u.Key.IsOneSidedPair || u.Value.Any()) && !u.Key.IsOnline && !u.Key.UserPair.OwnPermissions.IsPaused(); + bool FilterOfflineSyncshellUsers(KeyValuePair> u) + => (!u.Key.IsDirectlyPaired && !u.Key.IsOnline && !u.Key.UserPair.OwnPermissions.IsPaused()); + if (_configService.Current.ShowVisibleUsersSeparately) { - bool BaseFilterVisibleUsers(KeyValuePair> u) => - u.Key.IsVisible - && (_configService.Current.ShowSyncshellUsersInVisible || !(!_configService.Current.ShowSyncshellUsersInVisible && !u.Key.IsDirectlyPaired)); - - var allVisiblePairs = allPairs - .Where(BaseFilterVisibleUsers) - .Select(k => k.Key) - .ToImmutableList(); - - var filteredVisiblePairs = filteredPairs - .Where(BaseFilterVisibleUsers) - .OrderBy(AlphabeticalSort, StringComparer.OrdinalIgnoreCase) - .ToDictionary(k => k.Key, k => k.Value); + var allVisiblePairs = ImmutablePairList(allPairs + .Where(FilterVisibleUsers)); + var filteredVisiblePairs = BasicSortedDictionary(filteredPairs + .Where(FilterVisibleUsers)); drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomVisibleTag, filteredVisiblePairs, allVisiblePairs)); } @@ -432,16 +446,11 @@ public class CompactUi : WindowMediatorSubscriberBase List groupFolders = new(); foreach (var group in _pairManager.GroupPairs.Select(g => g.Key).OrderBy(g => g.GroupAliasOrGID, StringComparer.OrdinalIgnoreCase)) { - bool BaseFilterGroupUsers(KeyValuePair> u, GroupFullInfoDto group) => - u.Value.Exists(g => string.Equals(g.GID, group.GID, StringComparison.Ordinal)); - - var allGroupPairs = allPairs - .Where(u => BaseFilterGroupUsers(u, group)) - .Select(k => k.Key) - .ToImmutableList(); + var allGroupPairs = ImmutablePairList(allPairs + .Where(u => FilterGroupUsers(u, group))); var filteredGroupPairs = filteredPairs - .Where(u => BaseFilterGroupUsers(u, group) && FilterOnlineOrPausedSelf(u)) + .Where(u => FilterGroupUsers(u, group) && FilterOnlineOrPausedSelf(u)) .OrderByDescending(u => u.Key.IsOnline) .ThenBy(u => { @@ -465,80 +474,38 @@ public class CompactUi : WindowMediatorSubscriberBase drawFolders.AddRange(groupFolders); var tags = _tagHandler.GetAllTagsSorted(); - HashSet alreadyInTags = []; foreach (var tag in tags) { - bool BaseFilterTagUsers(KeyValuePair> u, string tag) => - u.Key.IsDirectlyPaired && !u.Key.IsOneSidedPair && _tagHandler.HasTag(u.Key.UserData.UID, tag); + var allTagPairs = ImmutablePairList(allPairs + .Where(u => FilterTagusers(u, tag))); + var filteredTagPairs = BasicSortedDictionary(filteredPairs + .Where(u => FilterTagusers(u, tag) && FilterOnlineOrPausedSelf(u))); - var allTagPairs = allPairs - .Where(u => BaseFilterTagUsers(u, tag)) - .Select(k => k.Key) - .ToImmutableList(); - - var filteredTagPairs = filteredPairs - .Where(u => BaseFilterTagUsers(u, tag) && FilterOnlineOrPausedSelf(u)) - .OrderByDescending(u => u.Key.IsVisible) - .ThenByDescending(u => u.Key.IsOnline) - .ThenBy(AlphabeticalSort, StringComparer.OrdinalIgnoreCase); - - drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(tag, filteredTagPairs.Select(u => - { - alreadyInTags.Add(u.Key); - return (u.Key, u.Value); - }).ToDictionary(u => u.Key, u => u.Value), allTagPairs)); + drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(tag, filteredTagPairs, allTagPairs)); } - bool BaseFilterNotTaggedUsers(KeyValuePair> u) => - u.Key.IsDirectlyPaired && !u.Key.IsOneSidedPair && !_tagHandler.HasAnyTag(u.Key.UserData.UID); - - var allOnlineNotTaggedPairs = allPairs - .Where(BaseFilterNotTaggedUsers) - .Select(k => k.Key) - .ToImmutableList(); - - var onlineNotTaggedPairs = filteredPairs - .Where(u => BaseFilterNotTaggedUsers(u) && FilterOnlineOrPausedSelf(u)) - .OrderByDescending(u => u.Key.IsVisible) - .ThenByDescending(u => u.Key.IsOnline) - .ThenBy(AlphabeticalSort, StringComparer.OrdinalIgnoreCase) - .ToDictionary(u => u.Key, u => u.Value); + var allOnlineNotTaggedPairs = ImmutablePairList(allPairs + .Where(FilterNotTaggedUsers)); + var onlineNotTaggedPairs = BasicSortedDictionary(filteredPairs + .Where(u => FilterNotTaggedUsers(u) && FilterOnlineOrPausedSelf(u))); drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder((_configService.Current.ShowOfflineUsersSeparately ? TagHandler.CustomOnlineTag : TagHandler.CustomAllTag), onlineNotTaggedPairs, allOnlineNotTaggedPairs)); if (_configService.Current.ShowOfflineUsersSeparately) { - bool BaseFilterOfflineUsers(KeyValuePair> u) => - ((u.Key.IsDirectlyPaired && _configService.Current.ShowSyncshellOfflineUsersSeparately) - || !_configService.Current.ShowSyncshellOfflineUsersSeparately) - && (!u.Key.IsOneSidedPair || u.Value.Any()) && !u.Key.IsOnline && !u.Key.UserPair.OwnPermissions.IsPaused(); - - var allOfflinePairs = allPairs - .Where(BaseFilterOfflineUsers) - .Select(k => k.Key) - .ToImmutableList(); - - var filteredOfflinePairs = filteredPairs - .Where(BaseFilterOfflineUsers) - .OrderBy(AlphabeticalSort, StringComparer.OrdinalIgnoreCase) - .ToDictionary(u => u.Key, u => u.Value); + var allOfflinePairs = ImmutablePairList(allPairs + .Where(FilterOfflineUsers)); + var filteredOfflinePairs = BasicSortedDictionary(filteredPairs + .Where(FilterOfflineUsers)); drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomOfflineTag, filteredOfflinePairs, allOfflinePairs)); if (_configService.Current.ShowSyncshellOfflineUsersSeparately) { - bool BaseFilterOfflineSyncshellUsers(KeyValuePair> u) => - (!u.Key.IsDirectlyPaired && !u.Key.IsOnline && !u.Key.UserPair.OwnPermissions.IsPaused()); - - var allOfflineSyncshellUsers = allPairs - .Where(BaseFilterOfflineSyncshellUsers) - .Select(k => k.Key) - .ToImmutableList(); - - var filteredOfflineSyncshellUsers = filteredPairs - .Where(BaseFilterOfflineSyncshellUsers) - .OrderBy(AlphabeticalSort, StringComparer.OrdinalIgnoreCase) - .ToDictionary(k => k.Key, k => k.Value); + var allOfflineSyncshellUsers = ImmutablePairList(allPairs + .Where(FilterOfflineSyncshellUsers)); + var filteredOfflineSyncshellUsers = BasicSortedDictionary(filteredPairs + .Where(FilterOfflineSyncshellUsers)); drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomOfflineSyncshellTag, filteredOfflineSyncshellUsers, @@ -547,8 +514,8 @@ public class CompactUi : WindowMediatorSubscriberBase } drawFolders.Add(_drawEntityFactory.CreateDrawTagFolder(TagHandler.CustomUnpairedTag, - filteredPairs.Where(u => u.Key.IsOneSidedPair).ToDictionary(u => u.Key, u => u.Value), - allPairs.Where(u => u.Key.IsOneSidedPair).Select(k => k.Key).ToImmutableList())); + BasicSortedDictionary(filteredPairs.Where(u => u.Key.IsOneSidedPair)), + ImmutablePairList(allPairs.Where(u => u.Key.IsOneSidedPair)))); return drawFolders; } From 83b392907df8b505aa2afe097c73a8d3f3c8376f Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Fri, 27 Oct 2023 19:24:05 +0200 Subject: [PATCH 35/56] check tokentime more precisely in both directions --- MareSynchronos/WebAPI/SignalR/TokenProvider.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/MareSynchronos/WebAPI/SignalR/TokenProvider.cs b/MareSynchronos/WebAPI/SignalR/TokenProvider.cs index 825c111..e5bee2e 100644 --- a/MareSynchronos/WebAPI/SignalR/TokenProvider.cs +++ b/MareSynchronos/WebAPI/SignalR/TokenProvider.cs @@ -103,7 +103,10 @@ public sealed class TokenProvider : IDisposable, IMediatorSubscriber _logger.LogTrace("GetNewToken: JWT {token}", response); _logger.LogDebug("GetNewToken: Valid until {date}, ValidClaim until {date}", jwtToken.ValidTo, new DateTime(long.Parse(jwtToken.Claims.Single(c => string.Equals(c.Type, "expiration_date", StringComparison.Ordinal)).Value), DateTimeKind.Utc)); - if (jwtToken.ValidTo.Subtract(TimeSpan.FromHours(6).Add(TimeSpan.FromMinutes(1))) > DateTime.UtcNow) + var dateTimeMinus10 = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(10)); + var dateTimePlus10 = DateTime.UtcNow.Add(TimeSpan.FromMinutes(10)); + var tokenTime = jwtToken.ValidTo.Subtract(TimeSpan.FromHours(6)); + if (tokenTime <= dateTimeMinus10 || tokenTime >= dateTimePlus10) { _tokenCache.TryRemove(CurrentIdentifier, out _); Mediator.Publish(new NotificationMessage("Invalid system clock", "The clock of your computer is invalid. " + From 4d975372cc2676ee3c2156954e6a8031371908ec Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Fri, 27 Oct 2023 21:36:42 +0200 Subject: [PATCH 36/56] add owner/moderator/pinned user icons --- MareSynchronos/UI/Components/DrawUserPair.cs | 38 ++++++++++++++++++++ MareSynchronos/UI/DrawEntityFactory.cs | 8 ++--- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/MareSynchronos/UI/Components/DrawUserPair.cs b/MareSynchronos/UI/Components/DrawUserPair.cs index 6f05ad4..5869f4e 100644 --- a/MareSynchronos/UI/Components/DrawUserPair.cs +++ b/MareSynchronos/UI/Components/DrawUserPair.cs @@ -21,6 +21,7 @@ public class DrawUserPair protected readonly IdDisplayHandler _displayHandler; protected readonly MareMediator _mediator; protected readonly List _syncedGroups; + private readonly GroupFullInfoDto? _currentGroup; protected Pair _pair; private readonly string _id; private readonly SelectTagForPairUi _selectTagForPairUi; @@ -28,6 +29,7 @@ public class DrawUserPair private float _menuRenderWidth = -1; public DrawUserPair(string id, Pair entry, List syncedGroups, + GroupFullInfoDto? currentGroup, ApiController apiController, IdDisplayHandler uIDDisplayHandler, MareMediator mareMediator, SelectTagForPairUi selectTagForPairUi, ServerConfigurationManager serverConfigurationManager) @@ -35,6 +37,7 @@ public class DrawUserPair _id = id; _pair = entry; _syncedGroups = syncedGroups; + _currentGroup = currentGroup; _apiController = apiController; _displayHandler = uIDDisplayHandler; _mediator = mareMediator; @@ -233,6 +236,41 @@ public class DrawUserPair } UiSharedService.AttachToolTip(userPairText); + if (_currentGroup != null) + { + ImGui.AlignTextToFramePadding(); + if (string.Equals(_currentGroup.OwnerUID, _pair.UserData.UID, StringComparison.Ordinal)) + { + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + ImGui.SameLine(); + ImGui.TextUnformatted(FontAwesomeIcon.Crown.ToIconString()); + } + UiSharedService.AttachToolTip("User is owner of this syncshell"); + } + else if (_currentGroup.GroupPairUserInfos.TryGetValue(_pair.UserData.UID, out var userinfo)) + { + if (userinfo.IsModerator()) + { + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + ImGui.SameLine(); + ImGui.TextUnformatted(FontAwesomeIcon.UserShield.ToIconString()); + } + UiSharedService.AttachToolTip("User is moderator in this syncshell"); + } + else if (userinfo.IsPinned()) + { + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + ImGui.SameLine(); + ImGui.TextUnformatted(FontAwesomeIcon.Thumbtack.ToIconString()); + } + UiSharedService.AttachToolTip("User is pinned in this syncshell"); + } + } + } + if (_pair.UserPair.OwnPermissions.IsSticky()) { ImGui.AlignTextToFramePadding(); diff --git a/MareSynchronos/UI/DrawEntityFactory.cs b/MareSynchronos/UI/DrawEntityFactory.cs index 3e7ecdd..67f6793 100644 --- a/MareSynchronos/UI/DrawEntityFactory.cs +++ b/MareSynchronos/UI/DrawEntityFactory.cs @@ -41,7 +41,7 @@ public class DrawEntityFactory IImmutableList allPairs) { return new DrawFolderGroup(groupFullInfoDto.Group.GID, groupFullInfoDto, _apiController, - filteredPairs.Select(p => CreateDrawPair(groupFullInfoDto.Group.GID + p.Key.UserData.UID, p.Key, p.Value)).ToImmutableList(), + filteredPairs.Select(p => CreateDrawPair(groupFullInfoDto.Group.GID + p.Key.UserData.UID, p.Key, p.Value, groupFullInfoDto)).ToImmutableList(), allPairs, _tagHandler, _uidDisplayHandler, _mediator); } @@ -49,13 +49,13 @@ public class DrawEntityFactory Dictionary> filteredPairs, IImmutableList allPairs) { - return new(tag, filteredPairs.Select(u => CreateDrawPair(tag, u.Key, u.Value)).ToImmutableList(), + return new(tag, filteredPairs.Select(u => CreateDrawPair(tag, u.Key, u.Value, null)).ToImmutableList(), allPairs, _tagHandler, _apiController, _selectPairForTagUi); } - public DrawUserPair CreateDrawPair(string id, Pair user, List groups) + public DrawUserPair CreateDrawPair(string id, Pair user, List groups, GroupFullInfoDto? currentGroup) { - return new DrawUserPair(id + user.UserData.UID, user, groups, _apiController, _uidDisplayHandler, + return new DrawUserPair(id + user.UserData.UID, user, groups, currentGroup, _apiController, _uidDisplayHandler, _mediator, _selectTagForPairUi, _serverConfigurationManager); } } \ No newline at end of file From e65c363d0f08ba9582e383ea6e69f3ba3765ce32 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Sat, 28 Oct 2023 15:36:45 +0200 Subject: [PATCH 37/56] add normalized icons --- MareSynchronos/UI/CompactUI.cs | 2 +- .../UI/Components/DrawFolderBase.cs | 3 +- .../UI/Components/DrawFolderGroup.cs | 27 +++---- MareSynchronos/UI/Components/DrawFolderTag.cs | 26 +++---- .../UI/Components/DrawGroupedGroupFolder.cs | 12 ++- MareSynchronos/UI/Components/DrawUserPair.cs | 60 ++++++-------- MareSynchronos/UI/CreateSyncshellUI.cs | 5 +- MareSynchronos/UI/JoinSyncshellUI.cs | 7 +- MareSynchronos/UI/PopoutProfileUi.cs | 4 +- MareSynchronos/UI/SettingsUi.cs | 11 +-- MareSynchronos/UI/SyncshellAdminUI.cs | 5 +- MareSynchronos/UI/TopTabMenu.cs | 1 - MareSynchronos/UI/UISharedService.cs | 78 +++++++++++++++++-- 13 files changed, 146 insertions(+), 95 deletions(-) diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs index 079d3dd..0cc6399 100644 --- a/MareSynchronos/UI/CompactUI.cs +++ b/MareSynchronos/UI/CompactUI.cs @@ -175,7 +175,7 @@ public class CompactUi : WindowMediatorSubscriberBase private void DrawAddCharacter() { - ImGui.Dummy(new(10)); + ImGuiHelpers.ScaledDummy(10f); var keys = _serverManager.CurrentServer!.SecretKeys; if (keys.Any()) { diff --git a/MareSynchronos/UI/Components/DrawFolderBase.cs b/MareSynchronos/UI/Components/DrawFolderBase.cs index 7d47425..eeeb4cf 100644 --- a/MareSynchronos/UI/Components/DrawFolderBase.cs +++ b/MareSynchronos/UI/Components/DrawFolderBase.cs @@ -1,5 +1,6 @@ using Dalamud.Interface; using Dalamud.Interface.Components; +using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using ImGuiNET; using MareSynchronos.PlayerData.Pairs; @@ -41,7 +42,7 @@ public abstract class DrawFolderBase : IDrawFolder ImGui.AlignTextToFramePadding(); - UiSharedService.FontText(icon.ToIconString(), UiBuilder.IconFont); + UiSharedService.NormalizedIcon(icon); if (ImGui.IsItemClicked()) { _tagHandler.SetTagOpen(_id, !_tagHandler.IsTagOpen(_id)); diff --git a/MareSynchronos/UI/Components/DrawFolderGroup.cs b/MareSynchronos/UI/Components/DrawFolderGroup.cs index b85d47a..612c6ec 100644 --- a/MareSynchronos/UI/Components/DrawFolderGroup.cs +++ b/MareSynchronos/UI/Components/DrawFolderGroup.cs @@ -42,8 +42,7 @@ public class DrawFolderGroup : DrawFolderBase { ImGui.AlignTextToFramePadding(); - using (ImRaii.PushFont(UiBuilder.IconFont)) - ImGui.TextUnformatted(_groupFullInfoDto.GroupPermissions.IsDisableInvites() ? FontAwesomeIcon.Lock.ToIconString() : FontAwesomeIcon.Users.ToIconString()); + UiSharedService.NormalizedIcon(_groupFullInfoDto.GroupPermissions.IsDisableInvites() ? FontAwesomeIcon.Lock : FontAwesomeIcon.Users); if (_groupFullInfoDto.GroupPermissions.IsDisableInvites()) { UiSharedService.AttachToolTip("Syncshell " + _groupFullInfoDto.GroupAliasOrGID + " is closed for invites"); @@ -62,22 +61,19 @@ public class DrawFolderGroup : DrawFolderBase if (IsOwner) { ImGui.AlignTextToFramePadding(); - using (ImRaii.PushFont(UiBuilder.IconFont)) - ImGui.TextUnformatted(FontAwesomeIcon.Crown.ToIconString()); + UiSharedService.NormalizedIcon(FontAwesomeIcon.Crown); UiSharedService.AttachToolTip("You are the owner of " + _groupFullInfoDto.GroupAliasOrGID); } else if (IsModerator) { ImGui.AlignTextToFramePadding(); - using (ImRaii.PushFont(UiBuilder.IconFont)) - ImGui.TextUnformatted(FontAwesomeIcon.UserShield.ToIconString()); + UiSharedService.NormalizedIcon(FontAwesomeIcon.UserShield); UiSharedService.AttachToolTip("You are a moderator in " + _groupFullInfoDto.GroupAliasOrGID); } else if (IsPinned) { ImGui.AlignTextToFramePadding(); - using (ImRaii.PushFont(UiBuilder.IconFont)) - ImGui.TextUnformatted(FontAwesomeIcon.Thumbtack.ToIconString()); + UiSharedService.NormalizedIcon(FontAwesomeIcon.Thumbtack); UiSharedService.AttachToolTip("You are pinned in " + _groupFullInfoDto.GroupAliasOrGID); } ImGui.SameLine(); @@ -179,8 +175,7 @@ public class DrawFolderGroup : DrawFolderBase FontAwesomeIcon pauseIcon = _groupFullInfoDto.GroupUserPermissions.IsPaused() ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause; var pauseButtonSize = UiSharedService.GetIconButtonSize(pauseIcon); - var folderIcon = FontAwesomeIcon.UsersCog; - var userCogButtonSize = UiSharedService.GetIconSize(folderIcon); + var userCogButtonSize = UiSharedService.GetNormalizedIconSize(FontAwesomeIcon.UsersCog); var individualSoundsDisabled = _groupFullInfoDto.GroupUserPermissions.IsDisableSounds(); var individualAnimDisabled = _groupFullInfoDto.GroupUserPermissions.IsDisableAnimations(); @@ -192,17 +187,15 @@ public class DrawFolderGroup : DrawFolderBase ImGui.AlignTextToFramePadding(); - using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudYellow, - _groupFullInfoDto.GroupPermissions.IsPreferDisableAnimations() != individualAnimDisabled + UiSharedService.NormalizedIcon(FontAwesomeIcon.UsersCog, (_groupFullInfoDto.GroupPermissions.IsPreferDisableAnimations() != individualAnimDisabled || _groupFullInfoDto.GroupPermissions.IsPreferDisableSounds() != individualSoundsDisabled - || _groupFullInfoDto.GroupPermissions.IsPreferDisableVFX() != individualVFXDisabled)) - UiSharedService.FontText(folderIcon.ToIconString(), UiBuilder.IconFont); + || _groupFullInfoDto.GroupPermissions.IsPreferDisableVFX() != individualVFXDisabled) ? ImGuiColors.DalamudYellow : null); if (ImGui.IsItemHovered()) { ImGui.BeginTooltip(); ImGui.TextUnformatted("Syncshell Permissions"); - ImGui.Dummy(new(2f)); + ImGuiHelpers.ScaledDummy(2f); UiSharedService.BooleanToColoredIcon(!individualSoundsDisabled, inline: false); ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); @@ -218,9 +211,9 @@ public class DrawFolderGroup : DrawFolderBase ImGui.Separator(); - ImGui.Dummy(new(2f)); + ImGuiHelpers.ScaledDummy(2f); ImGui.TextUnformatted("Suggested Permissions"); - ImGui.Dummy(new(2f)); + ImGuiHelpers.ScaledDummy(2f); UiSharedService.BooleanToColoredIcon(!_groupFullInfoDto.GroupPermissions.IsPreferDisableSounds(), inline: false); ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); diff --git a/MareSynchronos/UI/Components/DrawFolderTag.cs b/MareSynchronos/UI/Components/DrawFolderTag.cs index e1acba5..7771be9 100644 --- a/MareSynchronos/UI/Components/DrawFolderTag.cs +++ b/MareSynchronos/UI/Components/DrawFolderTag.cs @@ -69,22 +69,20 @@ public class DrawFolderTag : DrawFolderBase protected override float DrawIcon() { - using (ImRaii.PushFont(UiBuilder.IconFont)) + var icon = _id switch { - var icon = _id switch - { - TagHandler.CustomUnpairedTag => FontAwesomeIcon.ArrowsLeftRight.ToIconString(), - TagHandler.CustomOnlineTag => FontAwesomeIcon.Link.ToIconString(), - TagHandler.CustomOfflineTag => FontAwesomeIcon.Unlink.ToIconString(), - TagHandler.CustomOfflineSyncshellTag => FontAwesomeIcon.Unlink.ToIconString(), - TagHandler.CustomVisibleTag => FontAwesomeIcon.Eye.ToIconString(), - TagHandler.CustomAllTag => FontAwesomeIcon.User.ToIconString(), - _ => FontAwesomeIcon.Folder.ToIconString() - }; + TagHandler.CustomUnpairedTag => FontAwesomeIcon.ArrowsLeftRight, + TagHandler.CustomOnlineTag => FontAwesomeIcon.Link, + TagHandler.CustomOfflineTag => FontAwesomeIcon.Unlink, + TagHandler.CustomOfflineSyncshellTag => FontAwesomeIcon.Unlink, + TagHandler.CustomVisibleTag => FontAwesomeIcon.Eye, + TagHandler.CustomAllTag => FontAwesomeIcon.User, + _ => FontAwesomeIcon.Folder + }; + + ImGui.AlignTextToFramePadding(); + UiSharedService.NormalizedIcon(icon); - ImGui.AlignTextToFramePadding(); - ImGui.TextUnformatted(icon); - } if (RenderCount) { using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing with { X = ImGui.GetStyle().ItemSpacing.X / 2f })) diff --git a/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs b/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs index d323b3e..35d2a56 100644 --- a/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs +++ b/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs @@ -1,8 +1,10 @@ using Dalamud.Interface; +using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using ImGuiNET; using MareSynchronos.UI.Handlers; using System.Collections.Immutable; +using System.Numerics; namespace MareSynchronos.UI.Components; @@ -27,24 +29,28 @@ public class DrawGroupedGroupFolder : IDrawFolder string _id = "__folder_syncshells"; using var id = ImRaii.PushId(_id); + ImGui.Dummy(new Vector2(0f, ImGui.GetFrameHeight())); + ImGui.SameLine(); + var icon = _tagHandler.IsTagOpen(_id) ? FontAwesomeIcon.CaretDown : FontAwesomeIcon.CaretRight; - UiSharedService.FontText(icon.ToIconString(), UiBuilder.IconFont); + UiSharedService.NormalizedIcon(icon); if (ImGui.IsItemClicked()) { _tagHandler.SetTagOpen(_id, !_tagHandler.IsTagOpen(_id)); } ImGui.SameLine(); - using (ImRaii.PushFont(UiBuilder.IconFont)) - ImGui.TextUnformatted(FontAwesomeIcon.UsersRectangle.ToIconString()); + UiSharedService.NormalizedIcon(FontAwesomeIcon.UsersRectangle); using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing with { X = ImGui.GetStyle().ItemSpacing.X / 2f })) { ImGui.SameLine(); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("[" + OnlinePairs.ToString() + "]"); } UiSharedService.AttachToolTip(OnlinePairs + " online in all of your joined syncshells" + Environment.NewLine + TotalPairs + " pairs combined in all of your joined syncshells"); ImGui.SameLine(); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("All Syncshells"); ImGui.Separator(); diff --git a/MareSynchronos/UI/Components/DrawUserPair.cs b/MareSynchronos/UI/Components/DrawUserPair.cs index 5869f4e..3264ee1 100644 --- a/MareSynchronos/UI/Components/DrawUserPair.cs +++ b/MareSynchronos/UI/Components/DrawUserPair.cs @@ -189,30 +189,31 @@ public class DrawUserPair ImGui.AlignTextToFramePadding(); using var _ = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); - using var font = ImRaii.PushFont(UiBuilder.IconFont); - ImGui.TextUnformatted(FontAwesomeIcon.PauseCircle.ToIconString()); + //using var font = ImRaii.PushFont(UiBuilder.IconFont); + UiSharedService.NormalizedIcon(FontAwesomeIcon.PauseCircle); userPairText = _pair.UserData.AliasOrUID + " is paused"; + ImGui.SameLine(); } else if (!_pair.IsOnline) { ImGui.AlignTextToFramePadding(); using var _ = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed); - using var font = ImRaii.PushFont(UiBuilder.IconFont); - ImGui.TextUnformatted(_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.OneSided - ? FontAwesomeIcon.ArrowsLeftRight.ToIconString() + UiSharedService.NormalizedIcon(_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.OneSided + ? FontAwesomeIcon.ArrowsLeftRight : (_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.Bidirectional - ? FontAwesomeIcon.User.ToIconString() : FontAwesomeIcon.Users.ToIconString())); + ? FontAwesomeIcon.User : FontAwesomeIcon.Users)); userPairText = _pair.UserData.AliasOrUID + " is offline"; + ImGui.SameLine(); } else { ImGui.AlignTextToFramePadding(); - using var font = ImRaii.PushFont(UiBuilder.IconFont); using var _ = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.HealerGreen); - ImGui.TextUnformatted(_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.Bidirectional - ? FontAwesomeIcon.User.ToIconString() : FontAwesomeIcon.Users.ToIconString()); + UiSharedService.NormalizedIcon(_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.Bidirectional + ? FontAwesomeIcon.User : FontAwesomeIcon.Users); userPairText = _pair.UserData.AliasOrUID + " is online"; + ImGui.SameLine(); } if (_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.OneSided) @@ -241,31 +242,22 @@ public class DrawUserPair ImGui.AlignTextToFramePadding(); if (string.Equals(_currentGroup.OwnerUID, _pair.UserData.UID, StringComparison.Ordinal)) { - using (ImRaii.PushFont(UiBuilder.IconFont)) - { - ImGui.SameLine(); - ImGui.TextUnformatted(FontAwesomeIcon.Crown.ToIconString()); - } + ImGui.SameLine(); + UiSharedService.NormalizedIcon(FontAwesomeIcon.Crown); UiSharedService.AttachToolTip("User is owner of this syncshell"); } else if (_currentGroup.GroupPairUserInfos.TryGetValue(_pair.UserData.UID, out var userinfo)) { if (userinfo.IsModerator()) { - using (ImRaii.PushFont(UiBuilder.IconFont)) - { - ImGui.SameLine(); - ImGui.TextUnformatted(FontAwesomeIcon.UserShield.ToIconString()); - } + ImGui.SameLine(); + UiSharedService.NormalizedIcon(FontAwesomeIcon.UserShield); UiSharedService.AttachToolTip("User is moderator in this syncshell"); } else if (userinfo.IsPinned()) { - using (ImRaii.PushFont(UiBuilder.IconFont)) - { - ImGui.SameLine(); - ImGui.TextUnformatted(FontAwesomeIcon.Thumbtack.ToIconString()); - } + ImGui.SameLine(); + UiSharedService.NormalizedIcon(FontAwesomeIcon.Thumbtack); UiSharedService.AttachToolTip("User is pinned in this syncshell"); } } @@ -273,32 +265,26 @@ public class DrawUserPair if (_pair.UserPair.OwnPermissions.IsSticky()) { + ImGui.SameLine(); ImGui.AlignTextToFramePadding(); - using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing with { X = ImGui.GetStyle().ItemSpacing.X * 3 / 4f })) - using (ImRaii.PushFont(UiBuilder.IconFont)) - { - ImGui.SameLine(); - ImGui.TextUnformatted(FontAwesomeIcon.ArrowCircleUp.ToIconString()); - } + UiSharedService.NormalizedIcon(FontAwesomeIcon.ArrowCircleUp); UiSharedService.AttachToolTip(_pair.UserData.AliasOrUID + " has preferred permissions enabled"); } if (_pair.IsVisible) { + ImGui.SameLine(); ImGui.AlignTextToFramePadding(); using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing with { X = ImGui.GetStyle().ItemSpacing.X * 3 / 4f })) - using (ImRaii.PushFont(UiBuilder.IconFont)) - { - ImGui.SameLine(); - using var _ = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.ParsedGreen); - ImGui.TextUnformatted(FontAwesomeIcon.Eye.ToIconString()); - } + UiSharedService.NormalizedIcon(FontAwesomeIcon.Eye, ImGuiColors.ParsedGreen); UiSharedService.AttachToolTip("User is visible: " + _pair.PlayerName); } + + ImGui.SameLine(); } private void DrawName(float leftSide, float rightSide) @@ -352,7 +338,7 @@ public class DrawUserPair { var infoIconPosDist = windowEndX - barButtonSize.X - spacingX - pauseIconSize.X - spacingX; var icon = FontAwesomeIcon.InfoCircle; - var iconwidth = UiSharedService.GetIconSize(icon); + var iconwidth = UiSharedService.GetNormalizedIconSize(icon); infoIconDist = iconwidth.X; ImGui.SameLine(infoIconPosDist - iconwidth.X); diff --git a/MareSynchronos/UI/CreateSyncshellUI.cs b/MareSynchronos/UI/CreateSyncshellUI.cs index 97c3d3f..8286afd 100644 --- a/MareSynchronos/UI/CreateSyncshellUI.cs +++ b/MareSynchronos/UI/CreateSyncshellUI.cs @@ -1,5 +1,6 @@ using Dalamud.Interface; using Dalamud.Interface.Components; +using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using ImGuiNET; using MareSynchronos.API.Data.Extensions; @@ -64,7 +65,7 @@ public class CreateSyncshellUI : WindowMediatorSubscriberBase "- You can own up to " + _apiController.ServerInfo.MaxGroupsCreatedByUser + " Syncshells on this server." + Environment.NewLine + "- You can join up to " + _apiController.ServerInfo.MaxGroupsJoinedByUser + " Syncshells on this server (including your own)" + Environment.NewLine + "- Syncshells on this server can have a maximum of " + _apiController.ServerInfo.MaxGroupUserCount + " users"); - ImGui.Dummy(new(2f)); + ImGuiHelpers.ScaledDummy(2f); ImGui.TextUnformatted("Your current Syncshell preferred permissions are:"); ImGui.TextUnformatted("- Animations"); UiSharedService.BooleanToColoredIcon(!_apiController.DefaultPermissions!.DisableGroupAnimations); @@ -88,7 +89,7 @@ public class CreateSyncshellUI : WindowMediatorSubscriberBase UiSharedService.TextWrapped("You can change the Syncshell password later at any time."); ImGui.Separator(); UiSharedService.TextWrapped("These settings were set based on your preferred syncshell permissions:"); - ImGui.Dummy(new(2f)); + ImGuiHelpers.ScaledDummy(2f); UiSharedService.TextWrapped("Suggest Animation sync:"); UiSharedService.BooleanToColoredIcon(!_lastCreatedGroup.GroupUserPreferredPermissions.IsDisableAnimations()); UiSharedService.TextWrapped("Suggest Sounds sync:"); diff --git a/MareSynchronos/UI/JoinSyncshellUI.cs b/MareSynchronos/UI/JoinSyncshellUI.cs index 2a4c911..654a49d 100644 --- a/MareSynchronos/UI/JoinSyncshellUI.cs +++ b/MareSynchronos/UI/JoinSyncshellUI.cs @@ -1,4 +1,5 @@ using Dalamud.Interface.Colors; +using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using ImGuiNET; using MareSynchronos.API.Data.Enum; @@ -91,7 +92,7 @@ internal class JoinSyncshellUI : WindowMediatorSubscriberBase else { ImGui.TextUnformatted("You are about to join the Syncshell " + _groupJoinInfo.GroupAliasOrGID + " by " + _groupJoinInfo.OwnerAliasOrUID); - ImGui.Dummy(new(2)); + ImGuiHelpers.ScaledDummy(2f); ImGui.TextUnformatted("This Syncshell staff has set the following suggested Syncshell permissions:"); ImGui.TextUnformatted("- Sounds "); UiSharedService.BooleanToColoredIcon(!_groupJoinInfo.GroupPermissions.IsPreferDisableSounds()); @@ -104,7 +105,7 @@ internal class JoinSyncshellUI : WindowMediatorSubscriberBase || _groupJoinInfo.GroupPermissions.IsPreferDisableVFX() != _ownPermissions.DisableGroupVFX || _groupJoinInfo.GroupPermissions.IsPreferDisableAnimations() != _ownPermissions.DisableGroupAnimations) { - ImGui.Dummy(new(2)); + ImGuiHelpers.ScaledDummy(2f); UiSharedService.ColorText("Your current preferred default Syncshell permissions deviate from the suggested permissions:", ImGuiColors.DalamudYellow); if (_groupJoinInfo.GroupPermissions.IsPreferDisableSounds() != _ownPermissions.DisableGroupSounds) { @@ -157,7 +158,7 @@ internal class JoinSyncshellUI : WindowMediatorSubscriberBase { UiSharedService.TextWrapped("Your default syncshell permissions on joining are in line with the suggested Syncshell permissions through the owner."); } - ImGui.Dummy(new(2)); + ImGuiHelpers.ScaledDummy(2f); if (UiSharedService.IconTextButton(Dalamud.Interface.FontAwesomeIcon.Plus, "Finalize and join " + _groupJoinInfo.GroupAliasOrGID)) { GroupUserPreferredPermissions joinPermissions = GroupUserPreferredPermissions.NoneSet; diff --git a/MareSynchronos/UI/PopoutProfileUi.cs b/MareSynchronos/UI/PopoutProfileUi.cs index 4d9c85c..48bcfd6 100644 --- a/MareSynchronos/UI/PopoutProfileUi.cs +++ b/MareSynchronos/UI/PopoutProfileUi.cs @@ -114,11 +114,11 @@ public class PopoutProfileUi : WindowMediatorSubscriberBase using (ImRaii.PushFont(_uiSharedService.UidFont, _uiSharedService.UidFontBuilt)) UiSharedService.ColorText(_pair.UserData.AliasOrUID, ImGuiColors.HealerGreen); - ImGui.Dummy(new(spacing.Y, spacing.Y)); + ImGuiHelpers.ScaledDummy(spacing.Y, spacing.Y); var textPos = ImGui.GetCursorPosY(); ImGui.Separator(); var imagePos = ImGui.GetCursorPos(); - ImGui.Dummy(new(256, 256 * ImGuiHelpers.GlobalScale + spacing.Y)); + ImGuiHelpers.ScaledDummy(256, 256 * ImGuiHelpers.GlobalScale + spacing.Y); var note = _serverManager.GetNoteForUid(_pair.UserData.UID); if (!string.IsNullOrEmpty(note)) { diff --git a/MareSynchronos/UI/SettingsUi.cs b/MareSynchronos/UI/SettingsUi.cs index 7da8a68..e8aac8b 100644 --- a/MareSynchronos/UI/SettingsUi.cs +++ b/MareSynchronos/UI/SettingsUi.cs @@ -1,5 +1,6 @@ using Dalamud.Interface; using Dalamud.Interface.Colors; +using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using Dalamud.Utility; using ImGuiNET; @@ -476,7 +477,7 @@ public class SettingsUi : WindowMediatorSubscriberBase ImGui.TextUnformatted("The file compactor is only available on Windows."); } - ImGui.Dummy(new Vector2(10, 10)); + ImGuiHelpers.ScaledDummy(new Vector2(10, 10)); ImGui.TextUnformatted("To clear the local storage accept the following disclaimer"); ImGui.Indent(); ImGui.Checkbox("##readClearCache", ref _readClearCache); @@ -865,7 +866,7 @@ public class SettingsUi : WindowMediatorSubscriberBase var idx = _uiShared.DrawServiceSelection(); - ImGui.Dummy(new Vector2(10, 10)); + ImGuiHelpers.ScaledDummy(new Vector2(10, 10)); var selectedServer = _serverConfigurationManager.GetServerByIndex(idx); if (selectedServer == _serverConfigurationManager.CurrentServer) @@ -1056,7 +1057,7 @@ public class SettingsUi : WindowMediatorSubscriberBase { UiSharedService.TextWrapped("Note: The default permissions settings here are not applied retroactively to existing pairs or joined Syncshells."); UiSharedService.TextWrapped("Note: The default permissions settings here are sent and stored on the connected service."); - ImGui.Dummy(new(5f)); + ImGuiHelpers.ScaledDummy(5f); var perms = _apiController.DefaultPermissions!; bool individualIsSticky = perms.IndividualIsSticky; bool disableIndividualSounds = perms.DisableIndividualSounds; @@ -1075,7 +1076,7 @@ public class SettingsUi : WindowMediatorSubscriberBase " - All individually set permissions for any pair will also automatically become preferred permissions. This includes pairs in Syncshells." + Environment.NewLine + Environment.NewLine + "It is possible to remove or set the preferred permission state for any pair at any time." + Environment.NewLine + Environment.NewLine + "If unsure, leave this setting off."); - ImGui.Dummy(new(3f)); + ImGuiHelpers.ScaledDummy(3f); if (ImGui.Checkbox("Disable individual pair sounds", ref disableIndividualSounds)) { @@ -1095,7 +1096,7 @@ public class SettingsUi : WindowMediatorSubscriberBase _ = _apiController.UserUpdateDefaultPermissions(perms); } UiSharedService.DrawHelpText("This setting will disable VFX sync for all new individual pairs."); - ImGui.Dummy(new(5f)); + ImGuiHelpers.ScaledDummy(5f); bool disableGroundSounds = perms.DisableGroupSounds; bool disableGroupAnimations = perms.DisableGroupAnimations; bool disableGroupVFX = perms.DisableGroupVFX; diff --git a/MareSynchronos/UI/SyncshellAdminUI.cs b/MareSynchronos/UI/SyncshellAdminUI.cs index 95650b2..5fd63f5 100644 --- a/MareSynchronos/UI/SyncshellAdminUI.cs +++ b/MareSynchronos/UI/SyncshellAdminUI.cs @@ -1,5 +1,6 @@ using Dalamud.Interface; using Dalamud.Interface.Colors; +using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using ImGuiNET; using MareSynchronos.API.Data.Extensions; @@ -77,7 +78,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase _ = _apiController.GroupChangeGroupPermissionState(new(GroupFullInfo.Group, perm)); } - ImGui.Dummy(new(2f)); + ImGuiHelpers.ScaledDummy(2f); UiSharedService.TextWrapped("One-time invites work as single-use passwords. Use those if you do not want to distribute your Syncshell password."); if (UiSharedService.IconTextButton(FontAwesomeIcon.Envelope, "Single one-time invite")) @@ -116,7 +117,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase } UiSharedService.AttachToolTip("This will remove all non-pinned, non-moderator users from the Syncshell"); - ImGui.Dummy(new(2f)); + ImGuiHelpers.ScaledDummy(2f); if (UiSharedService.IconTextButton(FontAwesomeIcon.Retweet, "Refresh Banlist from Server")) { diff --git a/MareSynchronos/UI/TopTabMenu.cs b/MareSynchronos/UI/TopTabMenu.cs index 364bffe..a9a9c36 100644 --- a/MareSynchronos/UI/TopTabMenu.cs +++ b/MareSynchronos/UI/TopTabMenu.cs @@ -174,7 +174,6 @@ public class TopTabMenu if (TabSelection != SelectedTab.None) ImGuiHelpers.ScaledDummy(3f); ImGui.Separator(); - ImGuiHelpers.ScaledDummy(1f); } private void DrawAddPair(float availableXWidth, float spacingX) diff --git a/MareSynchronos/UI/UISharedService.cs b/MareSynchronos/UI/UISharedService.cs index 7591de1..193efbb 100644 --- a/MareSynchronos/UI/UISharedService.cs +++ b/MareSynchronos/UI/UISharedService.cs @@ -310,13 +310,6 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase return buttonSize; } - public static Vector2 GetIconSize(FontAwesomeIcon icon) - { - using var font = ImRaii.PushFont(UiBuilder.IconFont); - var iconSize = ImGui.CalcTextSize(icon.ToIconString()); - return iconSize; - } - public static string GetNotes(List pairs) { StringBuilder sb = new(); @@ -416,6 +409,77 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase return wasClicked; } + public static Vector2 GetIconSize(FontAwesomeIcon icon) + { + if (_iconCacheDict.TryGetValue(ImGuiHelpers.GlobalScale, out var iconCache)) + { + if (iconCache.TryGetValue(icon, out var size)) return size; + iconCache[icon] = CalcIconSize(icon); + return iconCache[icon]; + } + + _iconCacheDict.Add(ImGuiHelpers.GlobalScale, new()); + return _iconCacheDict[ImGuiHelpers.GlobalScale][icon] = CalcIconSize(icon); + } + + private static Vector2 CalcIconSize(FontAwesomeIcon icon) + { + using var font = ImRaii.PushFont(UiBuilder.IconFont); + var iconSize = ImGui.CalcTextSize(icon.ToIconString()); + return iconSize; + } + + public static (float xOffset, float scaling) GetIconScaling(FontAwesomeIcon icon) + { + var iconSize = GetIconSize(icon); + return (iconSize.X < iconSize.Y ? (iconSize.Y - iconSize.X) / 2f : 0f, iconSize.X > iconSize.Y ? 1f / (iconSize.X / iconSize.Y) : 1f); + } + + private static Vector2 CalcIconScale(FontAwesomeIcon icon) + { + var iconSize = GetIconSize(icon); + var (iconXoffset, iconScaling) = GetIconScaling(icon); + return new((iconSize.X * iconScaling) + (iconXoffset * 2), + (iconSize.X * iconScaling) + (iconXoffset * 2)); + } + + public static Vector2 GetNormalizedIconSize(FontAwesomeIcon icon) + { + if (_iconCacheDict.TryGetValue(ImGuiHelpers.GlobalScale, out var iconCache)) + { + if (iconCache.TryGetValue(icon, out var size)) return size; + return iconCache[icon] = CalcIconScale(icon); + } + + _iconCacheDict.Add(ImGuiHelpers.GlobalScale, new()); + return _iconCacheDict[ImGuiHelpers.GlobalScale][icon] = CalcIconScale(icon); + } + + public static void NormalizedIcon(FontAwesomeIcon icon, Vector4? color = null) + { + var cursorPos = ImGui.GetCursorPos(); + var iconSize = GetIconSize(icon); + var normalizedIconSize = GetNormalizedIconSize(icon); + var drawList = ImGui.GetWindowDrawList(); + var windowPos = ImGui.GetWindowPos(); + var scrollPosX = ImGui.GetScrollX(); + var scrollPosY = ImGui.GetScrollY(); + var frameHeight = ImGui.GetFrameHeight(); + + var (iconXoffset, iconScaling) = GetIconScaling(icon); + var frameOffsetY = ((frameHeight - iconSize.Y * iconScaling) / 2f); + + drawList.AddText(UiBuilder.IconFont, ImGui.GetFontSize() * iconScaling, + new(windowPos.X - scrollPosX + cursorPos.X + iconXoffset, + windowPos.Y - scrollPosY + cursorPos.Y + frameOffsetY), + color != null ? ImGui.GetColorU32(color.Value) : ImGui.GetColorU32(ImGuiCol.Text), icon.ToIconString()); + + ImGui.Dummy(new(normalizedIconSize.X, ImGui.GetFrameHeight())); + } + + private static Dictionary> _normalizedIconScales = new(); + private static Dictionary> _iconCacheDict = new(); + public static bool IsDirectoryWritable(string dirPath, bool throwIfFails = false) { try From 11bb84bd6149afbdc2e594f084fb177815dbe9bd Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Sun, 29 Oct 2023 02:53:04 +0200 Subject: [PATCH 38/56] fix cache dict wtf --- MareSynchronos/UI/UISharedService.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/MareSynchronos/UI/UISharedService.cs b/MareSynchronos/UI/UISharedService.cs index 193efbb..ca1b98a 100644 --- a/MareSynchronos/UI/UISharedService.cs +++ b/MareSynchronos/UI/UISharedService.cs @@ -18,9 +18,7 @@ using MareSynchronos.Services; using MareSynchronos.Services.Mediator; using MareSynchronos.Services.ServerConfiguration; using MareSynchronos.WebAPI; -using MareSynchronos.WebAPI.SignalR.Utils; using Microsoft.Extensions.Logging; -using System.Globalization; using System.Numerics; using System.Runtime.InteropServices; using System.Text; @@ -445,14 +443,14 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase public static Vector2 GetNormalizedIconSize(FontAwesomeIcon icon) { - if (_iconCacheDict.TryGetValue(ImGuiHelpers.GlobalScale, out var iconCache)) + if (_normalizedIconScales.TryGetValue(ImGuiHelpers.GlobalScale, out var iconCache)) { if (iconCache.TryGetValue(icon, out var size)) return size; return iconCache[icon] = CalcIconScale(icon); } - _iconCacheDict.Add(ImGuiHelpers.GlobalScale, new()); - return _iconCacheDict[ImGuiHelpers.GlobalScale][icon] = CalcIconScale(icon); + _normalizedIconScales.Add(ImGuiHelpers.GlobalScale, new()); + return _normalizedIconScales[ImGuiHelpers.GlobalScale][icon] = CalcIconScale(icon); } public static void NormalizedIcon(FontAwesomeIcon icon, Vector4? color = null) From c23add802a0114473de945f861ceceed3f9e96d9 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Sun, 29 Oct 2023 14:56:44 +0100 Subject: [PATCH 39/56] ui icon boogaloo --- MareSynchronos/UI/CompactUI.cs | 16 +- .../UI/Components/DrawFolderBase.cs | 6 +- .../UI/Components/DrawFolderGroup.cs | 28 ++- MareSynchronos/UI/Components/DrawFolderTag.cs | 8 +- .../UI/Components/DrawGroupedGroupFolder.cs | 3 +- MareSynchronos/UI/Components/DrawUserPair.cs | 214 +++++++++--------- .../Components/Popup/BanUserPopupHandler.cs | 2 +- .../UI/Components/Popup/PopupHandler.cs | 2 +- .../UI/Components/Popup/ReportPopupHandler.cs | 2 +- .../UI/Components/SelectTagForPairUi.cs | 2 +- MareSynchronos/UI/CreateSyncshellUI.cs | 10 +- MareSynchronos/UI/DataAnalysisUi.cs | 10 +- MareSynchronos/UI/EditProfileUi.cs | 8 +- MareSynchronos/UI/GposeUi.cs | 4 +- MareSynchronos/UI/JoinSyncshellUI.cs | 16 +- MareSynchronos/UI/SettingsUi.cs | 30 +-- MareSynchronos/UI/SyncshellAdminUI.cs | 26 +-- MareSynchronos/UI/TopTabMenu.cs | 24 +- MareSynchronos/UI/UISharedService.cs | 187 +++++++-------- 19 files changed, 300 insertions(+), 298 deletions(-) diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs index 0cc6399..74f8c59 100644 --- a/MareSynchronos/UI/CompactUI.cs +++ b/MareSynchronos/UI/CompactUI.cs @@ -151,7 +151,7 @@ public class CompactUi : WindowMediatorSubscriberBase { UiSharedService.TextWrapped($"You have successfully added {_lastAddedUser.UserData.AliasOrUID}. Set a local note for the user in the field below:"); ImGui.InputTextWithHint("##noteforuser", $"Note for {_lastAddedUser.UserData.AliasOrUID}", ref _lastAddedUserComment, 100); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Save, "Save Note")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Save, "Save Note")) { _serverManager.SetNoteForUid(_lastAddedUser.UserData.UID, _lastAddedUserComment); _lastAddedUser = null; @@ -180,7 +180,7 @@ public class CompactUi : WindowMediatorSubscriberBase if (keys.Any()) { if (_secretKeyIdx == -1) _secretKeyIdx = keys.First().Key; - if (UiSharedService.IconTextButton(FontAwesomeIcon.Plus, "Add current character with secret key")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Plus, "Add current character with secret key")) { _serverManager.CurrentServer!.Authentications.Add(new MareConfiguration.Models.Authentication() { @@ -220,7 +220,7 @@ public class CompactUi : WindowMediatorSubscriberBase } private void DrawServerStatus() { - var buttonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Link); + var buttonSize = UiSharedService.NormalizedIconButtonSize(FontAwesomeIcon.Link); var userCount = _apiController.OnlineUsers.ToString(CultureInfo.InvariantCulture); var userSize = ImGui.CalcTextSize(userCount); var textSize = ImGui.CalcTextSize("Users Online"); @@ -272,7 +272,7 @@ public class CompactUi : WindowMediatorSubscriberBase { using (ImRaii.PushColor(ImGuiCol.Text, color)) { - if (ImGuiComponents.IconButton(connectedIcon)) + if (UiSharedService.NormalizedIconButton(connectedIcon)) { _serverManager.CurrentServer.FullPause = !_serverManager.CurrentServer.FullPause; _serverManager.Save(); @@ -287,7 +287,7 @@ public class CompactUi : WindowMediatorSubscriberBase private void DrawTransfers() { var currentUploads = _fileTransferManager.CurrentUploads.ToList(); - UiSharedService.FontText(FontAwesomeIcon.Upload.ToIconString(), UiBuilder.IconFont); + UiSharedService.NormalizedIcon(FontAwesomeIcon.Upload); ImGui.SameLine(35 * ImGuiHelpers.GlobalScale); if (currentUploads.Any()) @@ -302,15 +302,17 @@ public class CompactUi : WindowMediatorSubscriberBase var uploadText = $"({UiSharedService.ByteToString(totalUploaded)}/{UiSharedService.ByteToString(totalToUpload)})"; var textSize = ImGui.CalcTextSize(uploadText); ImGui.SameLine(_windowContentWidth - textSize.X); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted(uploadText); } else { + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("No uploads in progress"); } var currentDownloads = _currentDownloads.SelectMany(d => d.Value.Values).ToList(); - UiSharedService.FontText(FontAwesomeIcon.Download.ToIconString(), UiBuilder.IconFont); + UiSharedService.NormalizedIcon(FontAwesomeIcon.Download); ImGui.SameLine(35 * ImGuiHelpers.GlobalScale); if (currentDownloads.Any()) @@ -325,10 +327,12 @@ public class CompactUi : WindowMediatorSubscriberBase $"({UiSharedService.ByteToString(totalDownloaded)}/{UiSharedService.ByteToString(totalToDownload)})"; var textSize = ImGui.CalcTextSize(downloadText); ImGui.SameLine(_windowContentWidth - textSize.X); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted(downloadText); } else { + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("No downloads in progress"); } } diff --git a/MareSynchronos/UI/Components/DrawFolderBase.cs b/MareSynchronos/UI/Components/DrawFolderBase.cs index eeeb4cf..2c54671 100644 --- a/MareSynchronos/UI/Components/DrawFolderBase.cs +++ b/MareSynchronos/UI/Components/DrawFolderBase.cs @@ -62,7 +62,7 @@ public abstract class DrawFolderBase : IDrawFolder // if opened draw content if (_tagHandler.IsTagOpen(_id)) { - using var indent = ImRaii.PushIndent(20f); + using var indent = ImRaii.PushIndent(UiSharedService.GetIconData(FontAwesomeIcon.Bars).NormalizedIconScale.X + ImGui.GetStyle().ItemSpacing.X); if (DrawPairs.Any()) { foreach (var item in DrawPairs) @@ -89,7 +89,7 @@ public abstract class DrawFolderBase : IDrawFolder private float DrawRightSideInternal() { - var barButtonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars); + var barButtonSize = UiSharedService.NormalizedIconButtonSize(FontAwesomeIcon.Bars); var spacingX = ImGui.GetStyle().ItemSpacing.X; var windowEndX = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth(); @@ -99,7 +99,7 @@ public abstract class DrawFolderBase : IDrawFolder if (RenderMenu) { ImGui.SameLine(windowEndX - barButtonSize.X); - if (ImGuiComponents.IconButton(FontAwesomeIcon.Bars)) + if (UiSharedService.NormalizedIconButton(FontAwesomeIcon.Bars)) { ImGui.OpenPopup("User Flyout Menu"); } diff --git a/MareSynchronos/UI/Components/DrawFolderGroup.cs b/MareSynchronos/UI/Components/DrawFolderGroup.cs index 612c6ec..6506783 100644 --- a/MareSynchronos/UI/Components/DrawFolderGroup.cs +++ b/MareSynchronos/UI/Components/DrawFolderGroup.cs @@ -86,21 +86,21 @@ public class DrawFolderGroup : DrawFolderBase ImGui.Separator(); ImGui.TextUnformatted("General Syncshell Actions"); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Copy, "Copy ID", menuWidth, true)) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Copy, "Copy ID", menuWidth, true)) { ImGui.CloseCurrentPopup(); ImGui.SetClipboardText(_groupFullInfoDto.GroupAliasOrGID); } UiSharedService.AttachToolTip("Copy Syncshell ID to Clipboard"); - if (UiSharedService.IconTextButton(FontAwesomeIcon.StickyNote, "Copy Notes", menuWidth, true)) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.StickyNote, "Copy Notes", menuWidth, true)) { ImGui.CloseCurrentPopup(); ImGui.SetClipboardText(UiSharedService.GetNotes(DrawPairs.Select(k => k.Pair).ToList())); } UiSharedService.AttachToolTip("Copies all your notes for all users in this Syncshell to the clipboard." + Environment.NewLine + "They can be imported via Settings -> Privacy -> Import Notes from Clipboard"); - if (UiSharedService.IconTextButton(FontAwesomeIcon.ArrowCircleLeft, "Leave Syncshell", menuWidth, true) && UiSharedService.CtrlPressed()) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.ArrowCircleLeft, "Leave Syncshell", menuWidth, true) && UiSharedService.CtrlPressed()) { _ = _apiController.GroupLeave(_groupFullInfoDto); ImGui.CloseCurrentPopup(); @@ -118,7 +118,7 @@ public class DrawFolderGroup : DrawFolderBase if ((_groupFullInfoDto.GroupPermissions.IsPreferDisableAnimations() != disableAnims || _groupFullInfoDto.GroupPermissions.IsPreferDisableSounds() != disableSounds || _groupFullInfoDto.GroupPermissions.IsPreferDisableVFX() != disableVfx) - && UiSharedService.IconTextButton(FontAwesomeIcon.Check, "Align with suggested permissions", menuWidth, true)) + && UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Check, "Align with suggested permissions", menuWidth, true)) { perm.SetDisableVFX(_groupFullInfoDto.GroupPermissions.IsPreferDisableVFX()); perm.SetDisableSounds(_groupFullInfoDto.GroupPermissions.IsPreferDisableSounds()); @@ -127,7 +127,7 @@ public class DrawFolderGroup : DrawFolderBase ImGui.CloseCurrentPopup(); } - if (UiSharedService.IconTextButton(disableSounds ? FontAwesomeIcon.VolumeUp : FontAwesomeIcon.VolumeOff, disableSounds ? "Enable Sound Sync" : "Disable Sound Sync", + if (UiSharedService.NormalizedIconTextButton(disableSounds ? FontAwesomeIcon.VolumeUp : FontAwesomeIcon.VolumeOff, disableSounds ? "Enable Sound Sync" : "Disable Sound Sync", menuWidth, true)) { perm.SetDisableSounds(!disableSounds); @@ -135,7 +135,7 @@ public class DrawFolderGroup : DrawFolderBase ImGui.CloseCurrentPopup(); } - if (UiSharedService.IconTextButton(disableAnims ? FontAwesomeIcon.Running : FontAwesomeIcon.Stop, disableAnims ? "Enable Animation Sync" : "Disable Animation Sync", + if (UiSharedService.NormalizedIconTextButton(disableAnims ? FontAwesomeIcon.Running : FontAwesomeIcon.Stop, disableAnims ? "Enable Animation Sync" : "Disable Animation Sync", menuWidth, true)) { perm.SetDisableAnimations(!disableAnims); @@ -143,7 +143,7 @@ public class DrawFolderGroup : DrawFolderBase ImGui.CloseCurrentPopup(); } - if (UiSharedService.IconTextButton(disableVfx ? FontAwesomeIcon.Sun : FontAwesomeIcon.Circle, disableVfx ? "Enable VFX Sync" : "Disable VFX Sync", + if (UiSharedService.NormalizedIconTextButton(disableVfx ? FontAwesomeIcon.Sun : FontAwesomeIcon.Circle, disableVfx ? "Enable VFX Sync" : "Disable VFX Sync", menuWidth, true)) { perm.SetDisableVFX(!disableVfx); @@ -155,7 +155,7 @@ public class DrawFolderGroup : DrawFolderBase { ImGui.Separator(); ImGui.TextUnformatted("Syncshell Admin Functions"); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Cog, "Open Admin Panel", menuWidth, true)) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Cog, "Open Admin Panel", menuWidth, true)) { ImGui.CloseCurrentPopup(); _mareMediator.Publish(new OpenSyncshellAdminPanel(_groupFullInfoDto)); @@ -173,9 +173,9 @@ public class DrawFolderGroup : DrawFolderBase var spacingX = ImGui.GetStyle().ItemSpacing.X; FontAwesomeIcon pauseIcon = _groupFullInfoDto.GroupUserPermissions.IsPaused() ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause; - var pauseButtonSize = UiSharedService.GetIconButtonSize(pauseIcon); + var pauseButtonSize = UiSharedService.NormalizedIconButtonSize(pauseIcon); - var userCogButtonSize = UiSharedService.GetNormalizedIconSize(FontAwesomeIcon.UsersCog); + var userCogButtonSize = UiSharedService.GetIconData(FontAwesomeIcon.UsersCog).NormalizedIconScale; var individualSoundsDisabled = _groupFullInfoDto.GroupUserPermissions.IsDisableSounds(); var individualAnimDisabled = _groupFullInfoDto.GroupUserPermissions.IsDisableAnimations(); @@ -199,14 +199,17 @@ public class DrawFolderGroup : DrawFolderBase UiSharedService.BooleanToColoredIcon(!individualSoundsDisabled, inline: false); ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("Sound Sync"); UiSharedService.BooleanToColoredIcon(!individualAnimDisabled, inline: false); ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("Animation Sync"); UiSharedService.BooleanToColoredIcon(!individualVFXDisabled, inline: false); ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("VFX Sync"); ImGui.Separator(); @@ -217,21 +220,24 @@ public class DrawFolderGroup : DrawFolderBase UiSharedService.BooleanToColoredIcon(!_groupFullInfoDto.GroupPermissions.IsPreferDisableSounds(), inline: false); ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("Sound Sync"); UiSharedService.BooleanToColoredIcon(!_groupFullInfoDto.GroupPermissions.IsPreferDisableAnimations(), inline: false); ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("Animation Sync"); UiSharedService.BooleanToColoredIcon(!_groupFullInfoDto.GroupPermissions.IsPreferDisableVFX(), inline: false); ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("VFX Sync"); ImGui.EndTooltip(); } ImGui.SameLine(); - if (ImGuiComponents.IconButton(pauseIcon)) + if (UiSharedService.NormalizedIconButton(pauseIcon)) { var perm = _groupFullInfoDto.GroupUserPermissions; perm.SetPaused(!perm.IsPaused()); diff --git a/MareSynchronos/UI/Components/DrawFolderTag.cs b/MareSynchronos/UI/Components/DrawFolderTag.cs index 7771be9..d21cabb 100644 --- a/MareSynchronos/UI/Components/DrawFolderTag.cs +++ b/MareSynchronos/UI/Components/DrawFolderTag.cs @@ -101,12 +101,12 @@ public class DrawFolderTag : DrawFolderBase protected override void DrawMenu(float menuWidth) { ImGui.TextUnformatted("Group Menu"); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Users, "Select Pairs", menuWidth, true)) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Users, "Select Pairs", menuWidth, true)) { _selectPairForTagUi.Open(_id); } UiSharedService.AttachToolTip("Select Individual Pairs for this Pair Group"); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete Pair Group", menuWidth, true) && UiSharedService.CtrlPressed()) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Trash, "Delete Pair Group", menuWidth, true) && UiSharedService.CtrlPressed()) { _tagHandler.RemoveTag(_id); } @@ -138,11 +138,11 @@ public class DrawFolderTag : DrawFolderBase var allArePaused = _allPairs.All(pair => pair.UserPair!.OwnPermissions.IsPaused()); var pauseButton = allArePaused ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause; - var pauseButtonX = UiSharedService.GetIconButtonSize(pauseButton).X; + var pauseButtonX = UiSharedService.NormalizedIconButtonSize(pauseButton).X; var buttonPauseOffset = currentRightSideX - pauseButtonX; ImGui.SameLine(buttonPauseOffset); - if (ImGuiComponents.IconButton(pauseButton)) + if (UiSharedService.NormalizedIconButton(pauseButton)) { if (allArePaused) { diff --git a/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs b/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs index 35d2a56..efa57d0 100644 --- a/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs +++ b/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs @@ -30,7 +30,8 @@ public class DrawGroupedGroupFolder : IDrawFolder using var id = ImRaii.PushId(_id); ImGui.Dummy(new Vector2(0f, ImGui.GetFrameHeight())); - ImGui.SameLine(); + using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, new Vector2(0f, 0f))) + ImGui.SameLine(); var icon = _tagHandler.IsTagOpen(_id) ? FontAwesomeIcon.CaretDown : FontAwesomeIcon.CaretRight; UiSharedService.NormalizedIcon(icon); diff --git a/MareSynchronos/UI/Components/DrawUserPair.cs b/MareSynchronos/UI/Components/DrawUserPair.cs index 3264ee1..07f4623 100644 --- a/MareSynchronos/UI/Components/DrawUserPair.cs +++ b/MareSynchronos/UI/Components/DrawUserPair.cs @@ -4,6 +4,7 @@ using Dalamud.Interface.Components; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using ImGuiNET; +using Lumina.Excel.GeneratedSheets; using MareSynchronos.API.Data.Extensions; using MareSynchronos.API.Dto.Group; using MareSynchronos.API.Dto.User; @@ -63,7 +64,7 @@ public class DrawUserPair { if (!_pair.IsPaused) { - if (UiSharedService.IconTextButton(FontAwesomeIcon.User, "Open Profile", _menuRenderWidth, true)) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.User, "Open Profile", _menuRenderWidth, true)) { _displayHandler.OpenProfile(_pair); ImGui.CloseCurrentPopup(); @@ -72,7 +73,7 @@ public class DrawUserPair } if (_pair.IsVisible) { - if (UiSharedService.IconTextButton(FontAwesomeIcon.Sync, "Reload last data", _menuRenderWidth, true)) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Sync, "Reload last data", _menuRenderWidth, true)) { _pair.ApplyLastReceivedData(forced: true); ImGui.CloseCurrentPopup(); @@ -80,7 +81,7 @@ public class DrawUserPair UiSharedService.AttachToolTip("This reapplies the last received character data to this character"); } - if (UiSharedService.IconTextButton(FontAwesomeIcon.PlayCircle, "Cycle pause state", _menuRenderWidth, true)) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.PlayCircle, "Cycle pause state", _menuRenderWidth, true)) { _ = _apiController.CyclePause(_pair.UserData); ImGui.CloseCurrentPopup(); @@ -91,7 +92,7 @@ public class DrawUserPair var isSticky = _pair.UserPair!.OwnPermissions.IsSticky(); string stickyText = isSticky ? "Disable Preferred Permissions" : "Enable Preferred Permissions"; var stickyIcon = isSticky ? FontAwesomeIcon.ArrowCircleDown : FontAwesomeIcon.ArrowCircleUp; - if (UiSharedService.IconTextButton(stickyIcon, stickyText, _menuRenderWidth, true)) + if (UiSharedService.NormalizedIconTextButton(stickyIcon, stickyText, _menuRenderWidth, true)) { var permissions = _pair.UserPair.OwnPermissions; permissions.SetSticky(!isSticky); @@ -107,7 +108,7 @@ public class DrawUserPair var isDisableSounds = _pair.UserPair!.OwnPermissions.IsDisableSounds(); string disableSoundsText = isDisableSounds ? "Enable sound sync" : "Disable sound sync"; var disableSoundsIcon = isDisableSounds ? FontAwesomeIcon.VolumeUp : FontAwesomeIcon.VolumeMute; - if (UiSharedService.IconTextButton(disableSoundsIcon, disableSoundsText, _menuRenderWidth, true)) + if (UiSharedService.NormalizedIconTextButton(disableSoundsIcon, disableSoundsText, _menuRenderWidth, true)) { var permissions = _pair.UserPair.OwnPermissions; permissions.SetDisableSounds(!isDisableSounds); @@ -118,7 +119,7 @@ public class DrawUserPair var isDisableAnims = _pair.UserPair!.OwnPermissions.IsDisableAnimations(); string disableAnimsText = isDisableAnims ? "Enable animation sync" : "Disable animation sync"; var disableAnimsIcon = isDisableAnims ? FontAwesomeIcon.Running : FontAwesomeIcon.Stop; - if (UiSharedService.IconTextButton(disableAnimsIcon, disableAnimsText, _menuRenderWidth, true)) + if (UiSharedService.NormalizedIconTextButton(disableAnimsIcon, disableAnimsText, _menuRenderWidth, true)) { var permissions = _pair.UserPair.OwnPermissions; permissions.SetDisableAnimations(!isDisableAnims); @@ -129,7 +130,7 @@ public class DrawUserPair var isDisableVFX = _pair.UserPair!.OwnPermissions.IsDisableVFX(); string disableVFXText = isDisableVFX ? "Enable VFX sync" : "Disable VFX sync"; var disableVFXIcon = isDisableVFX ? FontAwesomeIcon.Sun : FontAwesomeIcon.Circle; - if (UiSharedService.IconTextButton(disableVFXIcon, disableVFXText, _menuRenderWidth, true)) + if (UiSharedService.NormalizedIconTextButton(disableVFXIcon, disableVFXText, _menuRenderWidth, true)) { var permissions = _pair.UserPair.OwnPermissions; permissions.SetDisableVFX(!isDisableVFX); @@ -141,7 +142,7 @@ public class DrawUserPair { ImGui.Separator(); ImGui.TextUnformatted("Pair reporting"); - if (UiSharedService.IconTextButton(FontAwesomeIcon.ExclamationTriangle, "Report Mare Profile", _menuRenderWidth, true)) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.ExclamationTriangle, "Report Mare Profile", _menuRenderWidth, true)) { ImGui.CloseCurrentPopup(); _mediator.Publish(new OpenReportPopupMessage(_pair)); @@ -157,12 +158,12 @@ public class DrawUserPair if (_pair.IndividualPairStatus != API.Data.Enum.IndividualPairStatus.None) { - if (UiSharedService.IconTextButton(FontAwesomeIcon.Folder, "Pair Groups", _menuRenderWidth, true)) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Folder, "Pair Groups", _menuRenderWidth, true)) { _selectTagForPairUi.Open(_pair); } UiSharedService.AttachToolTip("Choose pair groups for " + entryUID); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Unpair Permanently", _menuRenderWidth, true) && UiSharedService.CtrlPressed()) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Trash, "Unpair Permanently", _menuRenderWidth, true) && UiSharedService.CtrlPressed()) { _ = _apiController.UserRemovePair(new(_pair.UserData)); } @@ -170,7 +171,7 @@ public class DrawUserPair } else { - if (UiSharedService.IconTextButton(FontAwesomeIcon.Plus, "Pair individually", _menuRenderWidth, true)) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Plus, "Pair individually", _menuRenderWidth, true)) { _ = _apiController.UserAddPair(new(_pair.UserData)); } @@ -186,34 +187,30 @@ public class DrawUserPair if (_pair.IsPaused) { - ImGui.AlignTextToFramePadding(); - using var _ = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); - //using var font = ImRaii.PushFont(UiBuilder.IconFont); UiSharedService.NormalizedIcon(FontAwesomeIcon.PauseCircle); userPairText = _pair.UserData.AliasOrUID + " is paused"; - ImGui.SameLine(); } else if (!_pair.IsOnline) { - ImGui.AlignTextToFramePadding(); using var _ = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed); UiSharedService.NormalizedIcon(_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.OneSided ? FontAwesomeIcon.ArrowsLeftRight : (_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.Bidirectional ? FontAwesomeIcon.User : FontAwesomeIcon.Users)); userPairText = _pair.UserData.AliasOrUID + " is offline"; - ImGui.SameLine(); + } + else if (_pair.IsVisible) + { + UiSharedService.NormalizedIcon(FontAwesomeIcon.Eye, ImGuiColors.ParsedGreen); + userPairText = _pair.UserData.AliasOrUID + " is visible"; } else { - ImGui.AlignTextToFramePadding(); - using var _ = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.HealerGreen); UiSharedService.NormalizedIcon(_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.Bidirectional ? FontAwesomeIcon.User : FontAwesomeIcon.Users); userPairText = _pair.UserData.AliasOrUID + " is online"; - ImGui.SameLine(); } if (_pair.IndividualPairStatus == API.Data.Enum.IndividualPairStatus.OneSided) @@ -237,53 +234,6 @@ public class DrawUserPair } UiSharedService.AttachToolTip(userPairText); - if (_currentGroup != null) - { - ImGui.AlignTextToFramePadding(); - if (string.Equals(_currentGroup.OwnerUID, _pair.UserData.UID, StringComparison.Ordinal)) - { - ImGui.SameLine(); - UiSharedService.NormalizedIcon(FontAwesomeIcon.Crown); - UiSharedService.AttachToolTip("User is owner of this syncshell"); - } - else if (_currentGroup.GroupPairUserInfos.TryGetValue(_pair.UserData.UID, out var userinfo)) - { - if (userinfo.IsModerator()) - { - ImGui.SameLine(); - UiSharedService.NormalizedIcon(FontAwesomeIcon.UserShield); - UiSharedService.AttachToolTip("User is moderator in this syncshell"); - } - else if (userinfo.IsPinned()) - { - ImGui.SameLine(); - UiSharedService.NormalizedIcon(FontAwesomeIcon.Thumbtack); - UiSharedService.AttachToolTip("User is pinned in this syncshell"); - } - } - } - - if (_pair.UserPair.OwnPermissions.IsSticky()) - { - ImGui.SameLine(); - ImGui.AlignTextToFramePadding(); - using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing with { X = ImGui.GetStyle().ItemSpacing.X * 3 / 4f })) - UiSharedService.NormalizedIcon(FontAwesomeIcon.ArrowCircleUp); - - UiSharedService.AttachToolTip(_pair.UserData.AliasOrUID + " has preferred permissions enabled"); - } - - if (_pair.IsVisible) - { - ImGui.SameLine(); - ImGui.AlignTextToFramePadding(); - - using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing with { X = ImGui.GetStyle().ItemSpacing.X * 3 / 4f })) - UiSharedService.NormalizedIcon(FontAwesomeIcon.Eye, ImGuiColors.ParsedGreen); - - UiSharedService.AttachToolTip("User is visible: " + _pair.PlayerName); - } - ImGui.SameLine(); } @@ -320,32 +270,46 @@ public class DrawUserPair private float DrawRightSide() { var pauseIcon = _pair.UserPair!.OwnPermissions.IsPaused() ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause; - var pauseIconSize = UiSharedService.GetIconButtonSize(pauseIcon); - var barButtonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars); - var entryUID = _pair.UserData.AliasOrUID; + var pauseIconSize = UiSharedService.NormalizedIconButtonSize(pauseIcon); + var barButtonSize = UiSharedService.NormalizedIconButtonSize(FontAwesomeIcon.Bars); var spacingX = ImGui.GetStyle().ItemSpacing.X; var windowEndX = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth(); - var rightSideStart = 0f; - float infoIconDist = 0f; + float currentRightSide = windowEndX - barButtonSize.X; + + ImGui.SameLine(currentRightSide); + ImGui.AlignTextToFramePadding(); + if (UiSharedService.NormalizedIconButton(FontAwesomeIcon.Bars)) + { + ImGui.OpenPopup("User Flyout Menu"); + } + + currentRightSide -= (pauseIconSize.X + spacingX); + ImGui.SameLine(currentRightSide); + if (UiSharedService.NormalizedIconButton(pauseIcon)) + { + var perm = _pair.UserPair!.OwnPermissions; + perm.SetPaused(!perm.IsPaused()); + _ = _apiController.UserSetPairPermissions(new(_pair.UserData, perm)); + } + UiSharedService.AttachToolTip(!_pair.UserPair!.OwnPermissions.IsPaused() + ? "Pause pairing with " + _pair.UserData.AliasOrUID + : "Resume pairing with " + _pair.UserData.AliasOrUID); if (_pair.IsPaired) { var individualSoundsDisabled = (_pair.UserPair?.OwnPermissions.IsDisableSounds() ?? false) || (_pair.UserPair?.OtherPermissions.IsDisableSounds() ?? false); var individualAnimDisabled = (_pair.UserPair?.OwnPermissions.IsDisableAnimations() ?? false) || (_pair.UserPair?.OtherPermissions.IsDisableAnimations() ?? false); var individualVFXDisabled = (_pair.UserPair?.OwnPermissions.IsDisableVFX() ?? false) || (_pair.UserPair?.OtherPermissions.IsDisableVFX() ?? false); + var individualIsSticky = _pair.UserPair!.OwnPermissions.IsSticky(); + var individualIcon = individualIsSticky ? FontAwesomeIcon.ArrowCircleUp : FontAwesomeIcon.InfoCircle; - if (individualAnimDisabled || individualSoundsDisabled || individualVFXDisabled) + if (individualAnimDisabled || individualSoundsDisabled || individualVFXDisabled || individualIsSticky) { - var infoIconPosDist = windowEndX - barButtonSize.X - spacingX - pauseIconSize.X - spacingX; - var icon = FontAwesomeIcon.InfoCircle; - var iconwidth = UiSharedService.GetNormalizedIconSize(icon); + currentRightSide -= (UiSharedService.GetIconData(individualIcon).NormalizedIconScale.X + spacingX); - infoIconDist = iconwidth.X; - ImGui.SameLine(infoIconPosDist - iconwidth.X); - - ImGui.AlignTextToFramePadding(); - - UiSharedService.FontText(icon.ToIconString(), UiBuilder.IconFont); + ImGui.SameLine(currentRightSide); + using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.HealerGreen, individualIsSticky)) + UiSharedService.NormalizedIcon(individualIcon); if (ImGui.IsItemHovered()) { ImGui.BeginTooltip(); @@ -353,17 +317,30 @@ public class DrawUserPair ImGui.TextUnformatted("Individual User permissions"); ImGui.Separator(); + if (individualIsSticky) + { + UiSharedService.NormalizedIcon(individualIcon); + ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); + ImGui.AlignTextToFramePadding(); + ImGui.TextUnformatted("Preferred permissions enabled"); + if (individualAnimDisabled || individualSoundsDisabled || individualVFXDisabled) + ImGui.Separator(); + } + if (individualSoundsDisabled) { var userSoundsText = "Sound sync"; - UiSharedService.FontText(FontAwesomeIcon.VolumeOff.ToIconString(), UiBuilder.IconFont); + UiSharedService.NormalizedIcon(FontAwesomeIcon.VolumeOff); ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted(userSoundsText); ImGui.NewLine(); ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("You"); UiSharedService.BooleanToColoredIcon(!_pair.UserPair!.OwnPermissions.IsDisableSounds()); ImGui.SameLine(); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("They"); UiSharedService.BooleanToColoredIcon(!_pair.UserPair!.OtherPermissions.IsDisableSounds()); } @@ -371,14 +348,17 @@ public class DrawUserPair if (individualAnimDisabled) { var userAnimText = "Animation sync"; - UiSharedService.FontText(FontAwesomeIcon.Stop.ToIconString(), UiBuilder.IconFont); + UiSharedService.NormalizedIcon(FontAwesomeIcon.Stop); ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted(userAnimText); ImGui.NewLine(); ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("You"); UiSharedService.BooleanToColoredIcon(!_pair.UserPair!.OwnPermissions.IsDisableAnimations()); ImGui.SameLine(); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("They"); UiSharedService.BooleanToColoredIcon(!_pair.UserPair!.OtherPermissions.IsDisableAnimations()); } @@ -386,14 +366,17 @@ public class DrawUserPair if (individualVFXDisabled) { var userVFXText = "VFX sync"; - UiSharedService.FontText(FontAwesomeIcon.Circle.ToIconString(), UiBuilder.IconFont); + UiSharedService.NormalizedIcon(FontAwesomeIcon.Circle); ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted(userVFXText); ImGui.NewLine(); ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("You"); UiSharedService.BooleanToColoredIcon(!_pair.UserPair!.OwnPermissions.IsDisableVFX()); ImGui.SameLine(); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("They"); UiSharedService.BooleanToColoredIcon(!_pair.UserPair!.OtherPermissions.IsDisableVFX()); } @@ -403,29 +386,38 @@ public class DrawUserPair } } - rightSideStart = windowEndX - barButtonSize.X - spacingX * 3 - pauseIconSize.X - infoIconDist; - ImGui.SameLine(windowEndX - barButtonSize.X - spacingX - pauseIconSize.X); - if (ImGuiComponents.IconButton(pauseIcon)) + if (_currentGroup != null) { - var perm = _pair.UserPair!.OwnPermissions; - perm.SetPaused(!perm.IsPaused()); - _ = _apiController.UserSetPairPermissions(new(_pair.UserData, perm)); - } - UiSharedService.AttachToolTip(!_pair.UserPair!.OwnPermissions.IsPaused() - ? "Pause pairing with " + entryUID - : "Resume pairing with " + entryUID); + var icon = FontAwesomeIcon.None; + var text = string.Empty; + if (string.Equals(_currentGroup.OwnerUID, _pair.UserData.UID, StringComparison.Ordinal)) + { + icon = FontAwesomeIcon.Crown; + text = "User is owner of this syncshell"; + } + else if (_currentGroup.GroupPairUserInfos.TryGetValue(_pair.UserData.UID, out var userinfo)) + { + if (userinfo.IsModerator()) + { + icon = FontAwesomeIcon.UserShield; + text = "User is moderator in this syncshell"; + } + else if (userinfo.IsPinned()) + { + icon = FontAwesomeIcon.Thumbtack; + text = "User is pinned in this syncshell"; + } + } - // Flyout Menu - if (rightSideStart == 0f) - { - rightSideStart = windowEndX - barButtonSize.X; - } - ImGui.SameLine(windowEndX - barButtonSize.X); - ImGui.AlignTextToFramePadding(); - if (ImGuiComponents.IconButton(FontAwesomeIcon.Bars)) - { - ImGui.OpenPopup("User Flyout Menu"); + if (!string.IsNullOrEmpty(text)) + { + currentRightSide -= (UiSharedService.GetIconData(icon).NormalizedIconScale.X + spacingX); + ImGui.SameLine(currentRightSide); + UiSharedService.NormalizedIcon(icon); + UiSharedService.AttachToolTip(text); + } } + if (ImGui.BeginPopup("User Flyout Menu")) { using (ImRaii.PushId($"buttons-{_pair.UserData.UID}")) @@ -443,7 +435,7 @@ public class DrawUserPair ImGui.EndPopup(); } - return rightSideStart; + return currentRightSide - spacingX; } private void DrawSyncshellMenu(GroupFullInfoDto group, bool selfIsOwner, bool selfIsModerator, bool userIsPinned, bool userIsModerator) @@ -452,7 +444,7 @@ public class DrawUserPair { ImGui.TextUnformatted("Syncshell Moderator Functions"); var pinText = userIsPinned ? "Unpin user" : "Pin user"; - if (UiSharedService.IconTextButton(FontAwesomeIcon.Thumbtack, pinText, _menuRenderWidth, true)) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Thumbtack, pinText, _menuRenderWidth, true)) { ImGui.CloseCurrentPopup(); if (!group.GroupPairUserInfos.TryGetValue(_pair.UserData.UID, out var userinfo)) @@ -467,14 +459,14 @@ public class DrawUserPair } UiSharedService.AttachToolTip("Pin this user to the Syncshell. Pinned users will not be deleted in case of a manually initiated Syncshell clean"); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Remove user", _menuRenderWidth, true) && UiSharedService.CtrlPressed()) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Trash, "Remove user", _menuRenderWidth, true) && UiSharedService.CtrlPressed()) { ImGui.CloseCurrentPopup(); _ = _apiController.GroupRemoveUser(new(group.Group, _pair.UserData)); } UiSharedService.AttachToolTip("Hold CTRL and click to remove user " + (_pair.UserData.AliasOrUID) + " from Syncshell"); - if (UiSharedService.IconTextButton(FontAwesomeIcon.UserSlash, "Ban User", _menuRenderWidth, true)) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.UserSlash, "Ban User", _menuRenderWidth, true)) { _mediator.Publish(new OpenBanUserPopupMessage(_pair, group)); ImGui.CloseCurrentPopup(); @@ -488,7 +480,7 @@ public class DrawUserPair { ImGui.TextUnformatted("Syncshell Owner Functions"); string modText = userIsModerator ? "Demod user" : "Mod user"; - if (UiSharedService.IconTextButton(FontAwesomeIcon.UserShield, modText, _menuRenderWidth, true) && UiSharedService.CtrlPressed()) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.UserShield, modText, _menuRenderWidth, true) && UiSharedService.CtrlPressed()) { ImGui.CloseCurrentPopup(); if (!group.GroupPairUserInfos.TryGetValue(_pair.UserData.UID, out var userinfo)) @@ -505,7 +497,7 @@ public class DrawUserPair UiSharedService.AttachToolTip("Hold CTRL to change the moderator status for " + (_pair.UserData.AliasOrUID) + Environment.NewLine + "Moderators can kick, ban/unban, pin/unpin users and clear the Syncshell."); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Crown, "Transfer Ownership", _menuRenderWidth, true) && UiSharedService.CtrlPressed() && UiSharedService.ShiftPressed()) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Crown, "Transfer Ownership", _menuRenderWidth, true) && UiSharedService.CtrlPressed() && UiSharedService.ShiftPressed()) { ImGui.CloseCurrentPopup(); _ = _apiController.GroupChangeOwnership(new(group.Group, _pair.UserData)); diff --git a/MareSynchronos/UI/Components/Popup/BanUserPopupHandler.cs b/MareSynchronos/UI/Components/Popup/BanUserPopupHandler.cs index 97a633d..aac754b 100644 --- a/MareSynchronos/UI/Components/Popup/BanUserPopupHandler.cs +++ b/MareSynchronos/UI/Components/Popup/BanUserPopupHandler.cs @@ -27,7 +27,7 @@ public class BanUserPopupHandler : IPopupHandler UiSharedService.TextWrapped("User " + (_reportedPair.UserData.AliasOrUID) + " will be banned and removed from this Syncshell."); ImGui.InputTextWithHint("##banreason", "Ban Reason", ref _banReason, 255); - if (UiSharedService.IconTextButton(FontAwesomeIcon.UserSlash, "Ban User")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.UserSlash, "Ban User")) { ImGui.CloseCurrentPopup(); var reason = _banReason; diff --git a/MareSynchronos/UI/Components/Popup/PopupHandler.cs b/MareSynchronos/UI/Components/Popup/PopupHandler.cs index 8bd9b2f..4b5a014 100644 --- a/MareSynchronos/UI/Components/Popup/PopupHandler.cs +++ b/MareSynchronos/UI/Components/Popup/PopupHandler.cs @@ -63,7 +63,7 @@ public class PopupHandler : WindowMediatorSubscriberBase if (!popup) return; _currentHandler.DrawContent(); ImGui.Separator(); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Times, "Close")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Times, "Close")) { ImGui.CloseCurrentPopup(); } diff --git a/MareSynchronos/UI/Components/Popup/ReportPopupHandler.cs b/MareSynchronos/UI/Components/Popup/ReportPopupHandler.cs index c72146f..36352cc 100644 --- a/MareSynchronos/UI/Components/Popup/ReportPopupHandler.cs +++ b/MareSynchronos/UI/Components/Popup/ReportPopupHandler.cs @@ -40,7 +40,7 @@ internal class ReportPopupHandler : IPopupHandler using (ImRaii.Disabled(string.IsNullOrEmpty(_reportReason))) { - if (UiSharedService.IconTextButton(FontAwesomeIcon.ExclamationTriangle, "Send Report")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.ExclamationTriangle, "Send Report")) { ImGui.CloseCurrentPopup(); var reason = _reportReason; diff --git a/MareSynchronos/UI/Components/SelectTagForPairUi.cs b/MareSynchronos/UI/Components/SelectTagForPairUi.cs index 4e98da0..8b2541d 100644 --- a/MareSynchronos/UI/Components/SelectTagForPairUi.cs +++ b/MareSynchronos/UI/Components/SelectTagForPairUi.cs @@ -74,7 +74,7 @@ public class SelectTagForPairUi ImGui.Separator(); UiSharedService.FontText($"Create a new group for {name}.", UiBuilder.DefaultFont); - if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus)) + if (UiSharedService.NormalizedIconButton(FontAwesomeIcon.Plus)) { HandleAddTag(); } diff --git a/MareSynchronos/UI/CreateSyncshellUI.cs b/MareSynchronos/UI/CreateSyncshellUI.cs index 8286afd..1df80b0 100644 --- a/MareSynchronos/UI/CreateSyncshellUI.cs +++ b/MareSynchronos/UI/CreateSyncshellUI.cs @@ -42,7 +42,7 @@ public class CreateSyncshellUI : WindowMediatorSubscriberBase if (_lastCreatedGroup == null) { - if (UiSharedService.IconTextButton(FontAwesomeIcon.Plus, "Create Syncshell")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Plus, "Create Syncshell")) { try { @@ -67,10 +67,13 @@ public class CreateSyncshellUI : WindowMediatorSubscriberBase "- Syncshells on this server can have a maximum of " + _apiController.ServerInfo.MaxGroupUserCount + " users"); ImGuiHelpers.ScaledDummy(2f); ImGui.TextUnformatted("Your current Syncshell preferred permissions are:"); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("- Animations"); UiSharedService.BooleanToColoredIcon(!_apiController.DefaultPermissions!.DisableGroupAnimations); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("- Sounds"); UiSharedService.BooleanToColoredIcon(!_apiController.DefaultPermissions!.DisableGroupSounds); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("- VFX"); UiSharedService.BooleanToColoredIcon(!_apiController.DefaultPermissions!.DisableGroupVFX); UiSharedService.TextWrapped("(Those preferred permissions can be changed anytime after Syncshell creation, your defaults can be changed anytime in the Mare Settings)"); @@ -82,7 +85,7 @@ public class CreateSyncshellUI : WindowMediatorSubscriberBase ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("Syncshell Password: " + _lastCreatedGroup.Password); ImGui.SameLine(); - if (ImGuiComponents.IconButton(FontAwesomeIcon.Copy)) + if (UiSharedService.NormalizedIconButton(FontAwesomeIcon.Copy)) { ImGui.SetClipboardText(_lastCreatedGroup.Password); } @@ -90,10 +93,13 @@ public class CreateSyncshellUI : WindowMediatorSubscriberBase ImGui.Separator(); UiSharedService.TextWrapped("These settings were set based on your preferred syncshell permissions:"); ImGuiHelpers.ScaledDummy(2f); + ImGui.AlignTextToFramePadding(); UiSharedService.TextWrapped("Suggest Animation sync:"); UiSharedService.BooleanToColoredIcon(!_lastCreatedGroup.GroupUserPreferredPermissions.IsDisableAnimations()); + ImGui.AlignTextToFramePadding(); UiSharedService.TextWrapped("Suggest Sounds sync:"); UiSharedService.BooleanToColoredIcon(!_lastCreatedGroup.GroupUserPreferredPermissions.IsDisableSounds()); + ImGui.AlignTextToFramePadding(); UiSharedService.TextWrapped("Suggest VFX sync:"); UiSharedService.BooleanToColoredIcon(!_lastCreatedGroup.GroupUserPreferredPermissions.IsDisableVFX()); } diff --git a/MareSynchronos/UI/DataAnalysisUi.cs b/MareSynchronos/UI/DataAnalysisUi.cs index eb17d8a..28a527e 100644 --- a/MareSynchronos/UI/DataAnalysisUi.cs +++ b/MareSynchronos/UI/DataAnalysisUi.cs @@ -65,7 +65,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase { ImGui.TextUnformatted("BC7 Conversion in progress: " + _conversionCurrentFileProgress + "/" + _texturesToConvert.Count); UiSharedService.TextWrapped("Current file: " + _conversionCurrentFileName); - if (UiSharedService.IconTextButton(FontAwesomeIcon.StopCircle, "Cancel conversion")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.StopCircle, "Cancel conversion")) { _conversionCancellationTokenSource.Cancel(); } @@ -107,7 +107,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase { UiSharedService.ColorTextWrapped($"Analyzing {_characterAnalyzer.CurrentFile}/{_characterAnalyzer.TotalFiles}", ImGuiColors.DalamudYellow); - if (UiSharedService.IconTextButton(FontAwesomeIcon.StopCircle, "Cancel analysis")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.StopCircle, "Cancel analysis")) { _characterAnalyzer.CancelAnalyze(); } @@ -118,14 +118,14 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase { UiSharedService.ColorTextWrapped("Some entries in the analysis have file size not determined yet, press the button below to analyze your current data", ImGuiColors.DalamudYellow); - if (UiSharedService.IconTextButton(FontAwesomeIcon.PlayCircle, "Start analysis (missing entries)")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.PlayCircle, "Start analysis (missing entries)")) { _ = _characterAnalyzer.ComputeAnalysis(print: false); } } else { - if (UiSharedService.IconTextButton(FontAwesomeIcon.PlayCircle, "Start analysis (recalculate all entries)")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.PlayCircle, "Start analysis (recalculate all entries)")) { _ = _characterAnalyzer.ComputeAnalysis(print: false, recalculate: true); } @@ -259,7 +259,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase Environment.NewLine + "- Conversion will convert all found texture duplicates (entries with more than 1 file path) automatically." + Environment.NewLine + "- Converting textures to BC7 is a very expensive operation and, depending on the amount of textures to convert, will take a while to complete." , ImGuiColors.DalamudYellow); - if (_texturesToConvert.Count > 0 && UiSharedService.IconTextButton(FontAwesomeIcon.PlayCircle, "Start conversion of " + _texturesToConvert.Count + " texture(s)")) + if (_texturesToConvert.Count > 0 && UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.PlayCircle, "Start conversion of " + _texturesToConvert.Count + " texture(s)")) { _conversionCancellationTokenSource = _conversionCancellationTokenSource.CancelRecreate(); _conversionTask = _ipcManager.PenumbraConvertTextureFiles(_logger, _texturesToConvert, _conversionProgress, _conversionCancellationTokenSource.Token); diff --git a/MareSynchronos/UI/EditProfileUi.cs b/MareSynchronos/UI/EditProfileUi.cs index ae83cb9..d3d03dc 100644 --- a/MareSynchronos/UI/EditProfileUi.cs +++ b/MareSynchronos/UI/EditProfileUi.cs @@ -131,7 +131,7 @@ public class EditProfileUi : WindowMediatorSubscriberBase ImGui.Separator(); _uiSharedService.BigText("Profile Settings"); - if (UiSharedService.IconTextButton(FontAwesomeIcon.FileUpload, "Upload new profile picture")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.FileUpload, "Upload new profile picture")) { _fileDialogManager.OpenFileDialog("Select new Profile picture", ".png", (success, file) => { @@ -162,7 +162,7 @@ public class EditProfileUi : WindowMediatorSubscriberBase } UiSharedService.AttachToolTip("Select and upload a new profile picture"); ImGui.SameLine(); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Clear uploaded profile picture")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Trash, "Clear uploaded profile picture")) { _ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, "", Description: null)); } @@ -211,13 +211,13 @@ public class EditProfileUi : WindowMediatorSubscriberBase ImGui.EndChildFrame(); ImGui.PopFont(); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Save, "Save Description")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Save, "Save Description")) { _ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, ProfilePictureBase64: null, _descriptionText)); } UiSharedService.AttachToolTip("Sets your profile description text"); ImGui.SameLine(); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Clear Description")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Trash, "Clear Description")) { _ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, ProfilePictureBase64: null, "")); } diff --git a/MareSynchronos/UI/GposeUi.cs b/MareSynchronos/UI/GposeUi.cs index ac7b5ab..e90fedf 100644 --- a/MareSynchronos/UI/GposeUi.cs +++ b/MareSynchronos/UI/GposeUi.cs @@ -41,7 +41,7 @@ public class GposeUi : WindowMediatorSubscriberBase if (!_mareCharaFileManager.CurrentlyWorking) { - if (UiSharedService.IconTextButton(FontAwesomeIcon.FolderOpen, "Load MCDF")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.FolderOpen, "Load MCDF")) { _fileDialogManager.OpenFileDialog("Pick MCDF file", ".mcdf", (success, paths) => { @@ -59,7 +59,7 @@ public class GposeUi : WindowMediatorSubscriberBase { UiSharedService.TextWrapped("Loaded file: " + _mareCharaFileManager.LoadedCharaFile.FilePath); UiSharedService.TextWrapped("File Description: " + _mareCharaFileManager.LoadedCharaFile.CharaFileData.Description); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Check, "Apply loaded MCDF")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Check, "Apply loaded MCDF")) { _ = Task.Run(async () => await _mareCharaFileManager.ApplyMareCharaFile(_dalamudUtil.GposeTargetGameObject).ConfigureAwait(false)); } diff --git a/MareSynchronos/UI/JoinSyncshellUI.cs b/MareSynchronos/UI/JoinSyncshellUI.cs index 654a49d..2aeb9d2 100644 --- a/MareSynchronos/UI/JoinSyncshellUI.cs +++ b/MareSynchronos/UI/JoinSyncshellUI.cs @@ -74,7 +74,7 @@ internal class JoinSyncshellUI : WindowMediatorSubscriberBase ImGui.InputTextWithHint("##syncshellpw", "Password", ref _syncshellPassword, 50, ImGuiInputTextFlags.Password); using (ImRaii.Disabled(string.IsNullOrEmpty(_desiredSyncshellToJoin) || string.IsNullOrEmpty(_syncshellPassword))) { - if (UiSharedService.IconTextButton(Dalamud.Interface.FontAwesomeIcon.Plus, "Join Syncshell")) + if (UiSharedService.NormalizedIconTextButton(Dalamud.Interface.FontAwesomeIcon.Plus, "Join Syncshell")) { _groupJoinInfo = _apiController.GroupJoin(new GroupPasswordDto(new API.Data.GroupData(_desiredSyncshellToJoin), _syncshellPassword)).Result; _previousPassword = _syncshellPassword; @@ -94,10 +94,13 @@ internal class JoinSyncshellUI : WindowMediatorSubscriberBase ImGui.TextUnformatted("You are about to join the Syncshell " + _groupJoinInfo.GroupAliasOrGID + " by " + _groupJoinInfo.OwnerAliasOrUID); ImGuiHelpers.ScaledDummy(2f); ImGui.TextUnformatted("This Syncshell staff has set the following suggested Syncshell permissions:"); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("- Sounds "); UiSharedService.BooleanToColoredIcon(!_groupJoinInfo.GroupPermissions.IsPreferDisableSounds()); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("- Animations"); UiSharedService.BooleanToColoredIcon(!_groupJoinInfo.GroupPermissions.IsPreferDisableAnimations()); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("- VFX"); UiSharedService.BooleanToColoredIcon(!_groupJoinInfo.GroupPermissions.IsPreferDisableVFX()); @@ -113,11 +116,12 @@ internal class JoinSyncshellUI : WindowMediatorSubscriberBase ImGui.TextUnformatted("- Sounds"); UiSharedService.BooleanToColoredIcon(!_ownPermissions.DisableGroupSounds); ImGui.SameLine(200); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("Suggested"); UiSharedService.BooleanToColoredIcon(!_groupJoinInfo.GroupPermissions.IsPreferDisableSounds()); ImGui.SameLine(); using var id = ImRaii.PushId("suggestedSounds"); - if (UiSharedService.IconTextButton(Dalamud.Interface.FontAwesomeIcon.ArrowRight, "Apply suggested")) + if (UiSharedService.NormalizedIconTextButton(Dalamud.Interface.FontAwesomeIcon.ArrowRight, "Apply suggested")) { _ownPermissions.DisableGroupSounds = _groupJoinInfo.GroupPermissions.IsPreferDisableSounds(); } @@ -128,11 +132,12 @@ internal class JoinSyncshellUI : WindowMediatorSubscriberBase ImGui.TextUnformatted("- Animations"); UiSharedService.BooleanToColoredIcon(!_ownPermissions.DisableGroupAnimations); ImGui.SameLine(200); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("Suggested"); UiSharedService.BooleanToColoredIcon(!_groupJoinInfo.GroupPermissions.IsPreferDisableAnimations()); ImGui.SameLine(); using var id = ImRaii.PushId("suggestedAnims"); - if (UiSharedService.IconTextButton(Dalamud.Interface.FontAwesomeIcon.ArrowRight, "Apply suggested")) + if (UiSharedService.NormalizedIconTextButton(Dalamud.Interface.FontAwesomeIcon.ArrowRight, "Apply suggested")) { _ownPermissions.DisableGroupAnimations = _groupJoinInfo.GroupPermissions.IsPreferDisableAnimations(); } @@ -143,11 +148,12 @@ internal class JoinSyncshellUI : WindowMediatorSubscriberBase ImGui.TextUnformatted("- VFX"); UiSharedService.BooleanToColoredIcon(!_ownPermissions.DisableGroupVFX); ImGui.SameLine(200); + ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("Suggested"); UiSharedService.BooleanToColoredIcon(!_groupJoinInfo.GroupPermissions.IsPreferDisableVFX()); ImGui.SameLine(); using var id = ImRaii.PushId("suggestedVfx"); - if (UiSharedService.IconTextButton(Dalamud.Interface.FontAwesomeIcon.ArrowRight, "Apply suggested")) + if (UiSharedService.NormalizedIconTextButton(Dalamud.Interface.FontAwesomeIcon.ArrowRight, "Apply suggested")) { _ownPermissions.DisableGroupVFX = _groupJoinInfo.GroupPermissions.IsPreferDisableVFX(); } @@ -159,7 +165,7 @@ internal class JoinSyncshellUI : WindowMediatorSubscriberBase UiSharedService.TextWrapped("Your default syncshell permissions on joining are in line with the suggested Syncshell permissions through the owner."); } ImGuiHelpers.ScaledDummy(2f); - if (UiSharedService.IconTextButton(Dalamud.Interface.FontAwesomeIcon.Plus, "Finalize and join " + _groupJoinInfo.GroupAliasOrGID)) + if (UiSharedService.NormalizedIconTextButton(Dalamud.Interface.FontAwesomeIcon.Plus, "Finalize and join " + _groupJoinInfo.GroupAliasOrGID)) { GroupUserPreferredPermissions joinPermissions = GroupUserPreferredPermissions.NoneSet; joinPermissions.SetDisableSounds(_ownPermissions.DisableGroupSounds); diff --git a/MareSynchronos/UI/SettingsUi.cs b/MareSynchronos/UI/SettingsUi.cs index e8aac8b..a5bc263 100644 --- a/MareSynchronos/UI/SettingsUi.cs +++ b/MareSynchronos/UI/SettingsUi.cs @@ -327,7 +327,7 @@ public class SettingsUi : WindowMediatorSubscriberBase ImGui.TreePop(); } #endif - if (UiSharedService.IconTextButton(FontAwesomeIcon.Copy, "[DEBUG] Copy Last created Character Data to clipboard")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Copy, "[DEBUG] Copy Last created Character Data to clipboard")) { if (LastCreatedCharacterData != null) { @@ -355,12 +355,12 @@ public class SettingsUi : WindowMediatorSubscriberBase UiSharedService.DrawHelpText("Enabling this can incur a (slight) performance impact. Enabling this for extended periods of time is not recommended."); using var disabled = ImRaii.Disabled(!logPerformance); - if (UiSharedService.IconTextButton(FontAwesomeIcon.StickyNote, "Print Performance Stats to /xllog")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.StickyNote, "Print Performance Stats to /xllog")) { _performanceCollector.PrintPerformanceStats(); } ImGui.SameLine(); - if (UiSharedService.IconTextButton(FontAwesomeIcon.StickyNote, "Print Performance Stats (last 60s) to /xllog")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.StickyNote, "Print Performance Stats (last 60s) to /xllog")) { _performanceCollector.PrintPerformanceStats(60); } @@ -386,7 +386,7 @@ public class SettingsUi : WindowMediatorSubscriberBase if (!_mareCharaFileManager.CurrentlyWorking) { ImGui.InputTextWithHint("Export Descriptor", "This description will be shown on loading the data", ref _exportDescription, 255); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Save, "Export Character as MCDF")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Save, "Export Character as MCDF")) { string defaultFileName = string.IsNullOrEmpty(_exportDescription) ? "export.mcdf" @@ -454,14 +454,14 @@ public class SettingsUi : WindowMediatorSubscriberBase ImGui.SameLine(); if (!_fileCompactor.MassCompactRunning) { - if (UiSharedService.IconTextButton(FontAwesomeIcon.FileArchive, "Compact all files in storage")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.FileArchive, "Compact all files in storage")) { _ = Task.Run(() => _fileCompactor.CompactStorage(compress: true)); } UiSharedService.AttachToolTip("This will run compression on all files in your current Mare Storage." + Environment.NewLine + "You do not need to run this manually if you keep the file compactor enabled."); ImGui.SameLine(); - if (UiSharedService.IconTextButton(FontAwesomeIcon.File, "Decompact all files in storage")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.File, "Decompact all files in storage")) { _ = Task.Run(() => _fileCompactor.CompactStorage(compress: false)); } @@ -487,7 +487,7 @@ public class SettingsUi : WindowMediatorSubscriberBase + Environment.NewLine + "- This can make the situation of not getting other players data worse in situations of heavy file server load."); if (!_readClearCache) ImGui.BeginDisabled(); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Clear local storage") && UiSharedService.CtrlPressed() && _readClearCache) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Trash, "Clear local storage") && UiSharedService.CtrlPressed() && _readClearCache) { _ = Task.Run(() => { @@ -529,11 +529,11 @@ public class SettingsUi : WindowMediatorSubscriberBase ImGui.Separator(); UiSharedService.FontText("Notes", _uiShared.UidFont); - if (UiSharedService.IconTextButton(FontAwesomeIcon.StickyNote, "Export all your user notes to clipboard")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.StickyNote, "Export all your user notes to clipboard")) { ImGui.SetClipboardText(UiSharedService.GetNotes(_pairManager.DirectPairs.UnionBy(_pairManager.GroupPairs.SelectMany(p => p.Value), p => p.UserData, UserDataComparer.Instance).ToList())); } - if (UiSharedService.IconTextButton(FontAwesomeIcon.FileImport, "Import notes from clipboard")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.FileImport, "Import notes from clipboard")) { _notesSuccessfullyApplied = null; var notes = ImGui.GetClipboardText(); @@ -931,7 +931,7 @@ public class SettingsUi : WindowMediatorSubscriberBase } }, EqualityComparer>.Default.Equals(keys.FirstOrDefault(f => f.Key == item.SecretKeyIdx), default) ? keys.First() : keys.First(f => f.Key == item.SecretKeyIdx)); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete Character") && UiSharedService.CtrlPressed()) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Trash, "Delete Character") && UiSharedService.CtrlPressed()) _serverConfigurationManager.RemoveCharacterFromServer(idx, item); UiSharedService.AttachToolTip("Hold CTRL to delete this entry."); @@ -945,14 +945,14 @@ public class SettingsUi : WindowMediatorSubscriberBase if (!selectedServer.Authentications.Exists(c => string.Equals(c.CharacterName, _uiShared.PlayerName, StringComparison.Ordinal) && c.WorldId == _uiShared.WorldId)) { - if (UiSharedService.IconTextButton(FontAwesomeIcon.User, "Add current character")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.User, "Add current character")) { _serverConfigurationManager.AddCurrentCharacterToServer(idx); } ImGui.SameLine(); } - if (UiSharedService.IconTextButton(FontAwesomeIcon.Plus, "Add new character")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Plus, "Add new character")) { _serverConfigurationManager.AddEmptyCharacterToServer(idx); } @@ -984,7 +984,7 @@ public class SettingsUi : WindowMediatorSubscriberBase } if (!selectedServer.Authentications.Exists(p => p.SecretKeyIdx == item.Key)) { - if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete Secret Key") && UiSharedService.CtrlPressed()) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Trash, "Delete Secret Key") && UiSharedService.CtrlPressed()) { selectedServer.SecretKeys.Remove(item.Key); _serverConfigurationManager.Save(); @@ -1001,7 +1001,7 @@ public class SettingsUi : WindowMediatorSubscriberBase } ImGui.Separator(); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Plus, "Add new Secret Key")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Plus, "Add new Secret Key")) { selectedServer.SecretKeys.Add(selectedServer.SecretKeys.Any() ? selectedServer.SecretKeys.Max(p => p.Key) + 1 : 0, new SecretKey() { @@ -1041,7 +1041,7 @@ public class SettingsUi : WindowMediatorSubscriberBase if (!isMain && selectedServer != _serverConfigurationManager.CurrentServer) { - if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete Service") && UiSharedService.CtrlPressed()) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Trash, "Delete Service") && UiSharedService.CtrlPressed()) { _serverConfigurationManager.DeleteServer(selectedServer); } diff --git a/MareSynchronos/UI/SyncshellAdminUI.cs b/MareSynchronos/UI/SyncshellAdminUI.cs index 5fd63f5..b564b3a 100644 --- a/MareSynchronos/UI/SyncshellAdminUI.cs +++ b/MareSynchronos/UI/SyncshellAdminUI.cs @@ -71,7 +71,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase { bool isInvitesDisabled = perm.IsDisableInvites(); - if (UiSharedService.IconTextButton(isInvitesDisabled ? FontAwesomeIcon.Unlock : FontAwesomeIcon.Lock, + if (UiSharedService.NormalizedIconTextButton(isInvitesDisabled ? FontAwesomeIcon.Unlock : FontAwesomeIcon.Lock, isInvitesDisabled ? "Unlock Syncshell" : "Lock Syncshell")) { perm.SetDisableInvites(!isInvitesDisabled); @@ -81,7 +81,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase ImGuiHelpers.ScaledDummy(2f); UiSharedService.TextWrapped("One-time invites work as single-use passwords. Use those if you do not want to distribute your Syncshell password."); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Envelope, "Single one-time invite")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Envelope, "Single one-time invite")) { ImGui.SetClipboardText(_apiController.GroupCreateTempInvite(new(GroupFullInfo.Group), 1).Result.FirstOrDefault() ?? string.Empty); } @@ -90,7 +90,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase ImGui.SameLine(); using (ImRaii.Disabled(_multiInvites <= 1 || _multiInvites > 100)) { - if (UiSharedService.IconTextButton(FontAwesomeIcon.Envelope, "Generate " + _multiInvites + " one-time invites")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Envelope, "Generate " + _multiInvites + " one-time invites")) { _oneTimeInvites.AddRange(_apiController.GroupCreateTempInvite(new(GroupFullInfo.Group), _multiInvites).Result); } @@ -100,7 +100,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase { var invites = string.Join(Environment.NewLine, _oneTimeInvites); ImGui.InputTextMultiline("Generated Multi Invites", ref invites, 5000, new(0, 0), ImGuiInputTextFlags.ReadOnly); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Copy, "Copy Invites to clipboard")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Copy, "Copy Invites to clipboard")) { ImGui.SetClipboardText(invites); } @@ -111,7 +111,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase var mgmtTab = ImRaii.TabItem("User Management"); if (mgmtTab) { - if (UiSharedService.IconTextButton(FontAwesomeIcon.Broom, "Clear Syncshell")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Broom, "Clear Syncshell")) { _ = _apiController.GroupClear(new(GroupFullInfo.Group)); } @@ -119,7 +119,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase ImGuiHelpers.ScaledDummy(2f); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Retweet, "Refresh Banlist from Server")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Retweet, "Refresh Banlist from Server")) { _bannedUsers = _apiController.GroupGetBannedUsers(new GroupDto(GroupFullInfo.Group)).Result; } @@ -148,7 +148,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase ImGui.TableNextColumn(); UiSharedService.TextWrapped(bannedUser.Reason); ImGui.TableNextColumn(); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Check, "Unban#" + bannedUser.UID)) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Check, "Unban#" + bannedUser.UID)) { _ = _apiController.GroupUnbanUser(bannedUser); _bannedUsers.RemoveAll(b => string.Equals(b.UID, bannedUser.UID, StringComparison.Ordinal)); @@ -171,7 +171,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase ImGui.Text("Suggest Sound Sync"); UiSharedService.BooleanToColoredIcon(!isDisableSounds); ImGui.SameLine(230); - if (UiSharedService.IconTextButton(isDisableSounds ? FontAwesomeIcon.VolumeUp : FontAwesomeIcon.VolumeMute, + if (UiSharedService.NormalizedIconTextButton(isDisableSounds ? FontAwesomeIcon.VolumeUp : FontAwesomeIcon.VolumeMute, isDisableSounds ? "Suggest to enable sound sync" : "Suggest to disable sound sync")) { perm.SetPreferDisableSounds(!perm.IsPreferDisableSounds()); @@ -182,7 +182,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase ImGui.Text("Suggest Animation Sync"); UiSharedService.BooleanToColoredIcon(!isDisableAnimations); ImGui.SameLine(230); - if (UiSharedService.IconTextButton(isDisableAnimations ? FontAwesomeIcon.Running : FontAwesomeIcon.Stop, + if (UiSharedService.NormalizedIconTextButton(isDisableAnimations ? FontAwesomeIcon.Running : FontAwesomeIcon.Stop, isDisableAnimations ? "Suggest to enable animation sync" : "Suggest to disable animation sync")) { perm.SetPreferDisableAnimations(!perm.IsPreferDisableAnimations()); @@ -193,7 +193,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase ImGui.Text("Suggest VFX Sync"); UiSharedService.BooleanToColoredIcon(!isDisableVfx); ImGui.SameLine(230); - if (UiSharedService.IconTextButton(isDisableVfx ? FontAwesomeIcon.Sun : FontAwesomeIcon.Circle, + if (UiSharedService.NormalizedIconTextButton(isDisableVfx ? FontAwesomeIcon.Sun : FontAwesomeIcon.Circle, isDisableVfx ? "Suggest to enable vfx sync" : "Suggest to disable vfx sync")) { perm.SetPreferDisableVFX(!perm.IsPreferDisableVFX()); @@ -212,7 +212,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("New Password"); var availableWidth = ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X; - var buttonSize = UiSharedService.GetIconTextButtonSize(FontAwesomeIcon.Passport, "Change Password").X; + var buttonSize = UiSharedService.GetNormalizedIconTextButtonSize(FontAwesomeIcon.Passport, "Change Password").X; var textSize = ImGui.CalcTextSize("New Password").X; var spacing = ImGui.GetStyle().ItemSpacing.X; @@ -222,7 +222,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase ImGui.SameLine(); using (ImRaii.Disabled(_newPassword.Length < 10)) { - if (UiSharedService.IconTextButton(FontAwesomeIcon.Passport, "Change Password")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Passport, "Change Password")) { _pwChangeSuccess = _apiController.GroupChangePassword(new GroupPasswordDto(GroupFullInfo.Group, _newPassword)).Result; _newPassword = string.Empty; @@ -235,7 +235,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase UiSharedService.ColorTextWrapped("Failed to change the password. Password requires to be at least 10 characters long.", ImGuiColors.DalamudYellow); } - if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete Syncshell") && UiSharedService.CtrlPressed() && UiSharedService.ShiftPressed()) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Trash, "Delete Syncshell") && UiSharedService.CtrlPressed() && UiSharedService.ShiftPressed()) { IsOpen = false; _ = _apiController.GroupDelete(new(GroupFullInfo.Group)); diff --git a/MareSynchronos/UI/TopTabMenu.cs b/MareSynchronos/UI/TopTabMenu.cs index a9a9c36..0475562 100644 --- a/MareSynchronos/UI/TopTabMenu.cs +++ b/MareSynchronos/UI/TopTabMenu.cs @@ -178,14 +178,14 @@ public class TopTabMenu private void DrawAddPair(float availableXWidth, float spacingX) { - var buttonSize = UiSharedService.GetIconTextButtonSize(FontAwesomeIcon.UserPlus, "Add"); + var buttonSize = UiSharedService.GetNormalizedIconTextButtonSize(FontAwesomeIcon.UserPlus, "Add"); ImGui.SetNextItemWidth(availableXWidth - buttonSize.X - spacingX); ImGui.InputTextWithHint("##otheruid", "Other players UID/Alias", ref _pairToAdd, 20); ImGui.SameLine(); var alreadyExisting = _pairManager.DirectPairs.Exists(p => string.Equals(p.UserData.UID, _pairToAdd, StringComparison.Ordinal) || string.Equals(p.UserData.Alias, _pairToAdd, StringComparison.Ordinal)); using (ImRaii.Disabled(alreadyExisting || string.IsNullOrEmpty(_pairToAdd))) { - if (UiSharedService.IconTextButton(FontAwesomeIcon.UserPlus, "Add")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.UserPlus, "Add")) { _ = _apiController.UserAddPair(new(new(_pairToAdd))); _pairToAdd = string.Empty; @@ -196,7 +196,7 @@ public class TopTabMenu private void DrawFilter(float availableWidth, float spacingX) { - var buttonSize = UiSharedService.GetIconTextButtonSize(FontAwesomeIcon.Ban, "Clear"); + var buttonSize = UiSharedService.GetNormalizedIconTextButtonSize(FontAwesomeIcon.Ban, "Clear"); ImGui.SetNextItemWidth(availableWidth - buttonSize.X - spacingX); string filter = Filter; if (ImGui.InputTextWithHint("##filter", "Filter for UID/notes", ref filter, 255)) @@ -205,7 +205,7 @@ public class TopTabMenu } ImGui.SameLine(); using var disabled = ImRaii.Disabled(string.IsNullOrEmpty(Filter)); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Ban, "Clear")) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Ban, "Clear")) { Filter = string.Empty; } @@ -466,7 +466,7 @@ public class TopTabMenu using (ImRaii.Disabled(_pairManager.GroupPairs.Select(k => k.Key).Distinct() .Count(g => string.Equals(g.OwnerUID, _apiController.UID, StringComparison.Ordinal)) >= _apiController.ServerInfo.MaxGroupsCreatedByUser)) { - if (UiSharedService.IconTextButton(FontAwesomeIcon.Plus, "Create new Syncshell", buttonX)) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Plus, "Create new Syncshell", buttonX)) { _mareMediator.Publish(new UiToggleMessage(typeof(CreateSyncshellUI))); } @@ -475,7 +475,7 @@ public class TopTabMenu using (ImRaii.Disabled(_pairManager.GroupPairs.Select(k => k.Key).Distinct().Count() >= _apiController.ServerInfo.MaxGroupsJoinedByUser)) { - if (UiSharedService.IconTextButton(FontAwesomeIcon.Users, "Join existing Syncshell", buttonX)) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Users, "Join existing Syncshell", buttonX)) { _mareMediator.Publish(new UiToggleMessage(typeof(JoinSyncshellUI))); } @@ -485,13 +485,13 @@ public class TopTabMenu private void DrawUserConfig(float availableWidth, float spacingX) { var buttonX = (availableWidth - spacingX) / 2f; - if (UiSharedService.IconTextButton(FontAwesomeIcon.UserCircle, "Edit Mare Profile", buttonX)) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.UserCircle, "Edit Mare Profile", buttonX)) { _mareMediator.Publish(new UiToggleMessage(typeof(EditProfileUi))); } UiSharedService.AttachToolTip("Edit your Mare Profile"); ImGui.SameLine(); - if (UiSharedService.IconTextButton(FontAwesomeIcon.PersonCircleQuestion, "Chara Data Analysis", buttonX)) + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.PersonCircleQuestion, "Chara Data Analysis", buttonX)) { _mareMediator.Publish(new UiToggleMessage(typeof(DataAnalysisUi))); } @@ -518,7 +518,7 @@ public class TopTabMenu { if (ImGui.BeginPopup(popupTitle)) { - if (UiSharedService.IconTextButton(enableIcon, enableText, 0, true)) + if (UiSharedService.NormalizedIconTextButton(enableIcon, enableText, 0, true)) { _ = GlobalControlCountdown(10); var bulkIndividualPairs = _pairManager.PairsWithGroups.Keys @@ -532,7 +532,7 @@ public class TopTabMenu ImGui.CloseCurrentPopup(); } - if (UiSharedService.IconTextButton(disableIcon, disableText, 0, true)) + if (UiSharedService.NormalizedIconTextButton(disableIcon, disableText, 0, true)) { _ = GlobalControlCountdown(10); var bulkIndividualPairs = _pairManager.PairsWithGroups.Keys @@ -556,7 +556,7 @@ public class TopTabMenu if (ImGui.BeginPopup(popupTitle)) { - if (UiSharedService.IconTextButton(enableIcon, enableText, 0, true)) + if (UiSharedService.NormalizedIconTextButton(enableIcon, enableText, 0, true)) { _ = GlobalControlCountdown(10); var bulkSyncshells = _pairManager.GroupPairs.Keys @@ -570,7 +570,7 @@ public class TopTabMenu ImGui.CloseCurrentPopup(); } - if (UiSharedService.IconTextButton(disableIcon, disableText, 0, true)) + if (UiSharedService.NormalizedIconTextButton(disableIcon, disableText, 0, true)) { _ = GlobalControlCountdown(10); var bulkSyncshells = _pairManager.GroupPairs.Keys diff --git a/MareSynchronos/UI/UISharedService.cs b/MareSynchronos/UI/UISharedService.cs index ca1b98a..0eeb8e4 100644 --- a/MareSynchronos/UI/UISharedService.cs +++ b/MareSynchronos/UI/UISharedService.cs @@ -23,6 +23,7 @@ using System.Numerics; using System.Runtime.InteropServices; using System.Text; using System.Text.RegularExpressions; +using static System.Net.Mime.MediaTypeNames; namespace MareSynchronos.UI; @@ -153,7 +154,6 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase public static void BooleanToColoredIcon(bool value, bool inline = true) { - using var font = ImRaii.PushFont(UiBuilder.IconFont); using var colorgreen = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.HealerGreen, value); using var colorred = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed, !value); @@ -161,11 +161,11 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase if (value) { - ImGui.TextUnformatted(FontAwesomeIcon.Check.ToIconString()); + NormalizedIcon(FontAwesomeIcon.Check); } else { - ImGui.TextUnformatted(FontAwesomeIcon.Times.ToIconString()); + NormalizedIcon(FontAwesomeIcon.Times); } } @@ -329,24 +329,18 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase return ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X; } - public static Vector2 GetIconTextButtonSize(FontAwesomeIcon icon, string text, float? width = null, bool isInPopup = false) + public static Vector2 GetNormalizedIconTextButtonSize(FontAwesomeIcon icon, string text, float? width = null, bool isInPopup = false) { - var iconSize = GetIconSize(icon); + var iconData = GetIconData(icon); var textSize = ImGui.CalcTextSize(text); var padding = ImGui.GetStyle().FramePadding; - var spacing = ImGui.GetStyle().ItemSpacing; - - var buttonSizeY = textSize.Y + padding.Y * 2; + var buttonSizeY = ImGui.GetFrameHeight(); var iconExtraSpacing = isInPopup ? padding.X * 2 : 0; - var iconXoffset = iconSize.X <= iconSize.Y ? (iconSize.Y - iconSize.X) / 2f : 0; - var iconScaling = iconSize.X > iconSize.Y ? 1 / (iconSize.X / iconSize.Y) : 1; - if (width == null || width <= 0) { - var buttonSizeX = (iconScaling == 1 ? iconSize.Y : (iconSize.X * iconScaling)) - + textSize.X + padding.X * 2 + spacing.X + (iconXoffset * 2); - return new Vector2(buttonSizeX + iconExtraSpacing, buttonSizeY); + var buttonSizeX = iconData.NormalizedIconScale.X + (padding.X * 4) + iconExtraSpacing + textSize.X; + return new Vector2(buttonSizeX, buttonSizeY); } else { @@ -354,38 +348,56 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase } } - public static bool IconTextButton(FontAwesomeIcon icon, string text, float? width = null, bool isInPopup = false) + public static Vector2 NormalizedIconButtonSize(FontAwesomeIcon icon) { - var wasClicked = false; - - var iconSize = GetIconSize(icon); - var textSize = ImGui.CalcTextSize(text); + var iconData = GetIconData(icon); + var padding = ImGui.GetStyle().FramePadding; + + return iconData.NormalizedIconScale with { X = iconData.NormalizedIconScale.X + padding.X * 2, Y = iconData.NormalizedIconScale.Y + padding.Y * 2 }; + } + + public static bool NormalizedIconButton(FontAwesomeIcon icon) + { + bool wasClicked = false; + var iconData = GetIconData(icon); var padding = ImGui.GetStyle().FramePadding; - var spacing = ImGui.GetStyle().ItemSpacing; var cursor = ImGui.GetCursorPos(); var drawList = ImGui.GetWindowDrawList(); var pos = ImGui.GetWindowPos(); var scrollPosY = ImGui.GetScrollY(); var scrollPosX = ImGui.GetScrollX(); - Vector2 buttonSize; - var buttonSizeY = textSize.Y + padding.Y * 2; + var buttonSize = NormalizedIconButtonSize(icon); + + if (ImGui.Button("###" + icon.ToIconString(), buttonSize)) + { + wasClicked = true; + } + + drawList.AddText(UiBuilder.IconFont, ImGui.GetFontSize() * iconData.IconScaling, + new(pos.X - scrollPosX + cursor.X + iconData.OffsetX + padding.X, + pos.Y - scrollPosY + cursor.Y + (buttonSize.Y - (iconData.IconSize.Y * iconData.IconScaling)) / 2f), + ImGui.GetColorU32(ImGuiCol.Text), icon.ToIconString()); + + return wasClicked; + } + + public static bool NormalizedIconTextButton(FontAwesomeIcon icon, string text, float? width = null, bool isInPopup = false) + { + var wasClicked = false; + + var iconData = GetIconData(icon); + var textSize = ImGui.CalcTextSize(text); + var padding = ImGui.GetStyle().FramePadding; + var cursor = ImGui.GetCursorPos(); + var drawList = ImGui.GetWindowDrawList(); + var pos = ImGui.GetWindowPos(); + var scrollPosY = ImGui.GetScrollY(); + var scrollPosX = ImGui.GetScrollX(); + + Vector2 buttonSize = GetNormalizedIconTextButtonSize(icon, text, width, isInPopup); var iconExtraSpacing = isInPopup ? padding.X * 2 : 0; - var iconXoffset = iconSize.X <= iconSize.Y ? (iconSize.Y - iconSize.X) / 2f : 0; - var iconScaling = iconSize.X > iconSize.Y ? 1 / (iconSize.X / iconSize.Y) : 1; - - if (width == null || width <= 0) - { - var buttonSizeX = (iconScaling == 1 ? iconSize.Y : (iconSize.X * iconScaling)) - + textSize.X + padding.X * 2 + spacing.X + (iconXoffset * 2); - buttonSize = new Vector2(buttonSizeX + iconExtraSpacing, buttonSizeY); - } - else - { - buttonSize = new Vector2(width.Value, buttonSizeY); - } - using (ImRaii.PushColor(ImGuiCol.Button, ImGui.GetColorU32(ImGuiCol.PopupBg), isInPopup)) { if (ImGui.Button("###" + icon.ToIconString() + text, buttonSize)) @@ -394,89 +406,64 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase } } - drawList.AddText(UiBuilder.IconFont, ImGui.GetFontSize() * iconScaling, - new(pos.X - scrollPosX + cursor.X + iconXoffset + padding.X, - pos.Y - scrollPosY + cursor.Y + (buttonSizeY - (iconSize.Y * iconScaling)) / 2f), - ImGui.GetColorU32(ImGuiCol.Text), icon.ToIconString()); - drawList.AddText(UiBuilder.DefaultFont, ImGui.GetFontSize(), - new(pos.X - scrollPosX + cursor.X + (padding.X) + spacing.X + (iconSize.X * iconScaling) + (iconXoffset * 2) + iconExtraSpacing, - pos.Y - scrollPosY + cursor.Y + ((buttonSizeY - textSize.Y) / 2f)), + new(pos.X - scrollPosX + cursor.X + iconData.NormalizedIconScale.X + (padding.X * 2) + iconExtraSpacing, + pos.Y - scrollPosY + cursor.Y + ((buttonSize.Y - textSize.Y) / 2f)), ImGui.GetColorU32(ImGuiCol.Text), text); + drawList.AddText(UiBuilder.IconFont, ImGui.GetFontSize() * iconData.IconScaling, + new(pos.X - scrollPosX + cursor.X + iconData.OffsetX + padding.X, + pos.Y - scrollPosY + cursor.Y + (buttonSize.Y - (iconData.IconSize.Y * iconData.IconScaling)) / 2f), + ImGui.GetColorU32(ImGuiCol.Text), icon.ToIconString()); + return wasClicked; } - public static Vector2 GetIconSize(FontAwesomeIcon icon) - { - if (_iconCacheDict.TryGetValue(ImGuiHelpers.GlobalScale, out var iconCache)) - { - if (iconCache.TryGetValue(icon, out var size)) return size; - iconCache[icon] = CalcIconSize(icon); - return iconCache[icon]; - } - - _iconCacheDict.Add(ImGuiHelpers.GlobalScale, new()); - return _iconCacheDict[ImGuiHelpers.GlobalScale][icon] = CalcIconSize(icon); - } - - private static Vector2 CalcIconSize(FontAwesomeIcon icon) - { - using var font = ImRaii.PushFont(UiBuilder.IconFont); - var iconSize = ImGui.CalcTextSize(icon.ToIconString()); - return iconSize; - } - - public static (float xOffset, float scaling) GetIconScaling(FontAwesomeIcon icon) - { - var iconSize = GetIconSize(icon); - return (iconSize.X < iconSize.Y ? (iconSize.Y - iconSize.X) / 2f : 0f, iconSize.X > iconSize.Y ? 1f / (iconSize.X / iconSize.Y) : 1f); - } - - private static Vector2 CalcIconScale(FontAwesomeIcon icon) - { - var iconSize = GetIconSize(icon); - var (iconXoffset, iconScaling) = GetIconScaling(icon); - return new((iconSize.X * iconScaling) + (iconXoffset * 2), - (iconSize.X * iconScaling) + (iconXoffset * 2)); - } - - public static Vector2 GetNormalizedIconSize(FontAwesomeIcon icon) - { - if (_normalizedIconScales.TryGetValue(ImGuiHelpers.GlobalScale, out var iconCache)) - { - if (iconCache.TryGetValue(icon, out var size)) return size; - return iconCache[icon] = CalcIconScale(icon); - } - - _normalizedIconScales.Add(ImGuiHelpers.GlobalScale, new()); - return _normalizedIconScales[ImGuiHelpers.GlobalScale][icon] = CalcIconScale(icon); - } - public static void NormalizedIcon(FontAwesomeIcon icon, Vector4? color = null) { var cursorPos = ImGui.GetCursorPos(); - var iconSize = GetIconSize(icon); - var normalizedIconSize = GetNormalizedIconSize(icon); + var iconData = GetIconData(icon); var drawList = ImGui.GetWindowDrawList(); var windowPos = ImGui.GetWindowPos(); var scrollPosX = ImGui.GetScrollX(); var scrollPosY = ImGui.GetScrollY(); var frameHeight = ImGui.GetFrameHeight(); - var (iconXoffset, iconScaling) = GetIconScaling(icon); - var frameOffsetY = ((frameHeight - iconSize.Y * iconScaling) / 2f); + var frameOffsetY = ((frameHeight - iconData.IconSize.Y * iconData.IconScaling) / 2f); - drawList.AddText(UiBuilder.IconFont, ImGui.GetFontSize() * iconScaling, - new(windowPos.X - scrollPosX + cursorPos.X + iconXoffset, + drawList.AddText(UiBuilder.IconFont, ImGui.GetFontSize() * iconData.IconScaling, + new(windowPos.X - scrollPosX + cursorPos.X + iconData.OffsetX, windowPos.Y - scrollPosY + cursorPos.Y + frameOffsetY), color != null ? ImGui.GetColorU32(color.Value) : ImGui.GetColorU32(ImGuiCol.Text), icon.ToIconString()); - ImGui.Dummy(new(normalizedIconSize.X, ImGui.GetFrameHeight())); + ImGui.Dummy(new(iconData.NormalizedIconScale.X, ImGui.GetFrameHeight())); } - private static Dictionary> _normalizedIconScales = new(); - private static Dictionary> _iconCacheDict = new(); + private static IconScaleData CalcIconScaleData(FontAwesomeIcon icon) + { + using var font = ImRaii.PushFont(UiBuilder.IconFont); + var iconSize = ImGui.CalcTextSize(icon.ToIconString()); + var iconscaling = (iconSize.X < iconSize.Y ? (iconSize.Y - iconSize.X) / 2f : 0f, iconSize.X > iconSize.Y ? 1f / (iconSize.X / iconSize.Y) : 1f); + var normalized = iconscaling.Item2 == 1f ? + new Vector2(iconSize.Y, iconSize.Y) + : new((iconSize.X * iconscaling.Item2) + (iconscaling.Item1 * 2), (iconSize.X * iconscaling.Item2) + (iconscaling.Item1 * 2)); + return new(iconSize, normalized, iconscaling.Item1, iconscaling.Item2); + } + + public static IconScaleData GetIconData(FontAwesomeIcon icon) + { + if (_iconData.TryGetValue(ImGuiHelpers.GlobalScale, out var iconCache)) + { + if (iconCache.TryGetValue(icon, out var iconData)) return iconData; + return iconCache[icon] = CalcIconScaleData(icon); + } + + _iconData.Add(ImGuiHelpers.GlobalScale, new()); + return _iconData[ImGuiHelpers.GlobalScale][icon] = CalcIconScaleData(icon); + } + + public sealed record IconScaleData(Vector2 IconSize, Vector2 NormalizedIconScale, float OffsetX, float IconScaling); + private static Dictionary> _iconData = new(); public static bool IsDirectoryWritable(string dirPath, bool throwIfFails = false) { @@ -831,7 +818,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase ImGui.SameLine(); var text = "Connect"; if (_serverSelectionIndex == _serverConfigurationManager.CurrentServerIndex) text = "Reconnect"; - if (IconTextButton(FontAwesomeIcon.Link, text)) + if (NormalizedIconTextButton(FontAwesomeIcon.Link, text)) { _serverConfigurationManager.SelectServer(_serverSelectionIndex); _ = _apiController.CreateConnections(); @@ -844,7 +831,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase ImGui.InputText("Custom Service URI", ref _customServerUri, 255); ImGui.SetNextItemWidth(250); ImGui.InputText("Custom Service Name", ref _customServerName, 255); - if (UiSharedService.IconTextButton(FontAwesomeIcon.Plus, "Add Custom Service") + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Plus, "Add Custom Service") && !string.IsNullOrEmpty(_customServerUri) && !string.IsNullOrEmpty(_customServerName)) { From 6bf99d9d05ac41be1414b2e399717ca136d232ab Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Sun, 29 Oct 2023 15:10:51 +0100 Subject: [PATCH 40/56] remove unnecessary usings --- MareSynchronos/Plugin.cs | 1 - MareSynchronos/UI/Components/DrawFolderBase.cs | 2 -- MareSynchronos/UI/Components/DrawFolderGroup.cs | 1 - MareSynchronos/UI/Components/DrawFolderTag.cs | 1 - MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs | 1 - MareSynchronos/UI/Components/DrawUserPair.cs | 2 -- MareSynchronos/UI/Components/SelectTagForPairUi.cs | 1 - MareSynchronos/UI/CreateSyncshellUI.cs | 1 - MareSynchronos/UI/IntroUI.cs | 1 - MareSynchronos/UI/UISharedService.cs | 3 +-- MareSynchronos/WebAPI/SignalR/HubFactory.cs | 5 +---- 11 files changed, 2 insertions(+), 17 deletions(-) diff --git a/MareSynchronos/Plugin.cs b/MareSynchronos/Plugin.cs index ac6e3fd..477b5bd 100644 --- a/MareSynchronos/Plugin.cs +++ b/MareSynchronos/Plugin.cs @@ -3,7 +3,6 @@ using Dalamud.Interface.ImGuiFileDialog; using Dalamud.Interface.Windowing; using Dalamud.Plugin; using Dalamud.Plugin.Services; -using MareSynchronos.API.Dto.Group; using MareSynchronos.FileCache; using MareSynchronos.Interop; using MareSynchronos.MareConfiguration; diff --git a/MareSynchronos/UI/Components/DrawFolderBase.cs b/MareSynchronos/UI/Components/DrawFolderBase.cs index 2c54671..3b43d00 100644 --- a/MareSynchronos/UI/Components/DrawFolderBase.cs +++ b/MareSynchronos/UI/Components/DrawFolderBase.cs @@ -1,6 +1,4 @@ using Dalamud.Interface; -using Dalamud.Interface.Components; -using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using ImGuiNET; using MareSynchronos.PlayerData.Pairs; diff --git a/MareSynchronos/UI/Components/DrawFolderGroup.cs b/MareSynchronos/UI/Components/DrawFolderGroup.cs index 6506783..699f3bc 100644 --- a/MareSynchronos/UI/Components/DrawFolderGroup.cs +++ b/MareSynchronos/UI/Components/DrawFolderGroup.cs @@ -1,6 +1,5 @@ using Dalamud.Interface; using Dalamud.Interface.Colors; -using Dalamud.Interface.Components; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using ImGuiNET; diff --git a/MareSynchronos/UI/Components/DrawFolderTag.cs b/MareSynchronos/UI/Components/DrawFolderTag.cs index d21cabb..562de4d 100644 --- a/MareSynchronos/UI/Components/DrawFolderTag.cs +++ b/MareSynchronos/UI/Components/DrawFolderTag.cs @@ -1,5 +1,4 @@ using Dalamud.Interface; -using Dalamud.Interface.Components; using Dalamud.Interface.Utility.Raii; using ImGuiNET; using MareSynchronos.API.Data.Extensions; diff --git a/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs b/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs index efa57d0..ebbcdb7 100644 --- a/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs +++ b/MareSynchronos/UI/Components/DrawGroupedGroupFolder.cs @@ -1,5 +1,4 @@ using Dalamud.Interface; -using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using ImGuiNET; using MareSynchronos.UI.Handlers; diff --git a/MareSynchronos/UI/Components/DrawUserPair.cs b/MareSynchronos/UI/Components/DrawUserPair.cs index 07f4623..96c12ef 100644 --- a/MareSynchronos/UI/Components/DrawUserPair.cs +++ b/MareSynchronos/UI/Components/DrawUserPair.cs @@ -1,10 +1,8 @@ using Dalamud.Interface; using Dalamud.Interface.Colors; -using Dalamud.Interface.Components; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using ImGuiNET; -using Lumina.Excel.GeneratedSheets; using MareSynchronos.API.Data.Extensions; using MareSynchronos.API.Dto.Group; using MareSynchronos.API.Dto.User; diff --git a/MareSynchronos/UI/Components/SelectTagForPairUi.cs b/MareSynchronos/UI/Components/SelectTagForPairUi.cs index 8b2541d..7b8da47 100644 --- a/MareSynchronos/UI/Components/SelectTagForPairUi.cs +++ b/MareSynchronos/UI/Components/SelectTagForPairUi.cs @@ -1,5 +1,4 @@ using Dalamud.Interface; -using Dalamud.Interface.Components; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using Dalamud.Utility; diff --git a/MareSynchronos/UI/CreateSyncshellUI.cs b/MareSynchronos/UI/CreateSyncshellUI.cs index 1df80b0..f8a948b 100644 --- a/MareSynchronos/UI/CreateSyncshellUI.cs +++ b/MareSynchronos/UI/CreateSyncshellUI.cs @@ -1,5 +1,4 @@ using Dalamud.Interface; -using Dalamud.Interface.Components; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using ImGuiNET; diff --git a/MareSynchronos/UI/IntroUI.cs b/MareSynchronos/UI/IntroUI.cs index 8f64ada..3fceced 100644 --- a/MareSynchronos/UI/IntroUI.cs +++ b/MareSynchronos/UI/IntroUI.cs @@ -1,6 +1,5 @@ using Dalamud.Interface.Colors; using Dalamud.Interface.Utility; -using Dalamud.Interface.Utility.Raii; using Dalamud.Utility; using ImGuiNET; using MareSynchronos.FileCache; diff --git a/MareSynchronos/UI/UISharedService.cs b/MareSynchronos/UI/UISharedService.cs index 0eeb8e4..4183180 100644 --- a/MareSynchronos/UI/UISharedService.cs +++ b/MareSynchronos/UI/UISharedService.cs @@ -23,7 +23,6 @@ using System.Numerics; using System.Runtime.InteropServices; using System.Text; using System.Text.RegularExpressions; -using static System.Net.Mime.MediaTypeNames; namespace MareSynchronos.UI; @@ -339,7 +338,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase if (width == null || width <= 0) { - var buttonSizeX = iconData.NormalizedIconScale.X + (padding.X * 4) + iconExtraSpacing + textSize.X; + var buttonSizeX = iconData.NormalizedIconScale.X + (padding.X * 3) + iconExtraSpacing + textSize.X; return new Vector2(buttonSizeX, buttonSizeY); } else diff --git a/MareSynchronos/WebAPI/SignalR/HubFactory.cs b/MareSynchronos/WebAPI/SignalR/HubFactory.cs index 51512bf..52913cd 100644 --- a/MareSynchronos/WebAPI/SignalR/HubFactory.cs +++ b/MareSynchronos/WebAPI/SignalR/HubFactory.cs @@ -1,7 +1,4 @@ -using Dalamud.Plugin.Services; -using MareSynchronos.API.SignalR; -using MareSynchronos.Interop; -using MareSynchronos.MareConfiguration; +using MareSynchronos.API.SignalR; using MareSynchronos.Services.Mediator; using MareSynchronos.Services.ServerConfiguration; using MareSynchronos.WebAPI.SignalR.Utils; From 6bc9eaf96ea6b7b18ece4ff3405b6f69a664aef5 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Sun, 29 Oct 2023 15:31:08 +0100 Subject: [PATCH 41/56] indent nonscaled --- MareSynchronos/UI/Components/DrawFolderBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MareSynchronos/UI/Components/DrawFolderBase.cs b/MareSynchronos/UI/Components/DrawFolderBase.cs index 3b43d00..ae4db39 100644 --- a/MareSynchronos/UI/Components/DrawFolderBase.cs +++ b/MareSynchronos/UI/Components/DrawFolderBase.cs @@ -60,7 +60,7 @@ public abstract class DrawFolderBase : IDrawFolder // if opened draw content if (_tagHandler.IsTagOpen(_id)) { - using var indent = ImRaii.PushIndent(UiSharedService.GetIconData(FontAwesomeIcon.Bars).NormalizedIconScale.X + ImGui.GetStyle().ItemSpacing.X); + using var indent = ImRaii.PushIndent(UiSharedService.GetIconData(FontAwesomeIcon.Bars).NormalizedIconScale.Y + ImGui.GetStyle().ItemSpacing.X, false); if (DrawPairs.Any()) { foreach (var item in DrawPairs) From 2fbee855ae3fa42404d5f2d8fa61bc2d2ead57b0 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Mon, 30 Oct 2023 02:08:29 +0100 Subject: [PATCH 42/56] fix getting identifier during zoning --- .../WebAPI/SignalR/JwtIdentifier.cs | 8 ++- .../WebAPI/SignalR/TokenProvider.cs | 62 +++++++++++++++---- 2 files changed, 58 insertions(+), 12 deletions(-) diff --git a/MareSynchronos/WebAPI/SignalR/JwtIdentifier.cs b/MareSynchronos/WebAPI/SignalR/JwtIdentifier.cs index fa6c2f5..78ea0cb 100644 --- a/MareSynchronos/WebAPI/SignalR/JwtIdentifier.cs +++ b/MareSynchronos/WebAPI/SignalR/JwtIdentifier.cs @@ -1,3 +1,9 @@ namespace MareSynchronos.WebAPI.SignalR; -public record JwtIdentifier(string ApiUrl, string SecretKey); \ No newline at end of file +public record JwtIdentifier(string ApiUrl, string CharaHash, string SecretKey) +{ + public override string ToString() + { + return "{JwtIdentifier; Url: " + ApiUrl + ", Chara: " + CharaHash + ", HasSecretKey: " + !string.IsNullOrEmpty(SecretKey) + "}"; + } +} \ No newline at end of file diff --git a/MareSynchronos/WebAPI/SignalR/TokenProvider.cs b/MareSynchronos/WebAPI/SignalR/TokenProvider.cs index e5bee2e..d75673f 100644 --- a/MareSynchronos/WebAPI/SignalR/TokenProvider.cs +++ b/MareSynchronos/WebAPI/SignalR/TokenProvider.cs @@ -27,14 +27,22 @@ public sealed class TokenProvider : IDisposable, IMediatorSubscriber _httpClient = new(); var ver = Assembly.GetExecutingAssembly().GetName().Version; Mediator = mareMediator; - Mediator.Subscribe(this, (_) => _tokenCache.Clear()); - Mediator.Subscribe(this, (_) => _tokenCache.Clear()); + Mediator.Subscribe(this, (_) => + { + _lastJwtIdentifier = null; + _tokenCache.Clear(); + }); + Mediator.Subscribe(this, (_) => + { + _lastJwtIdentifier = null; + _tokenCache.Clear(); + }); _httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("MareSynchronos", ver!.Major + "." + ver!.Minor + "." + ver!.Build)); } public MareMediator Mediator { get; } - private JwtIdentifier CurrentIdentifier => new(_serverManager.CurrentApiUrl, _serverManager.GetSecretKey()!); + private JwtIdentifier? _lastJwtIdentifier; public void Dispose() { @@ -42,7 +50,7 @@ public sealed class TokenProvider : IDisposable, IMediatorSubscriber _httpClient.Dispose(); } - public async Task GetNewToken(bool isRenewal, CancellationToken token) + public async Task GetNewToken(bool isRenewal, JwtIdentifier identifier, CancellationToken token) { Uri tokenUri; string response = string.Empty; @@ -73,17 +81,17 @@ public sealed class TokenProvider : IDisposable, IMediatorSubscriber .Replace("wss://", "https://", StringComparison.OrdinalIgnoreCase) .Replace("ws://", "http://", StringComparison.OrdinalIgnoreCase))); HttpRequestMessage request = new(HttpMethod.Get, tokenUri.ToString()); - request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _tokenCache[CurrentIdentifier]); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _tokenCache[identifier]); result = await _httpClient.SendAsync(request, token).ConfigureAwait(false); } response = await result.Content.ReadAsStringAsync().ConfigureAwait(false); result.EnsureSuccessStatusCode(); - _tokenCache[CurrentIdentifier] = response; + _tokenCache[identifier] = response; } catch (HttpRequestException ex) { - _tokenCache.TryRemove(CurrentIdentifier, out _); + _tokenCache.TryRemove(identifier, out _); _logger.LogError(ex, "GetNewToken: Failure to get token"); @@ -108,7 +116,7 @@ public sealed class TokenProvider : IDisposable, IMediatorSubscriber var tokenTime = jwtToken.ValidTo.Subtract(TimeSpan.FromHours(6)); if (tokenTime <= dateTimeMinus10 || tokenTime >= dateTimePlus10) { - _tokenCache.TryRemove(CurrentIdentifier, out _); + _tokenCache.TryRemove(identifier, out _); Mediator.Publish(new NotificationMessage("Invalid system clock", "The clock of your computer is invalid. " + "Mare will not function properly if the time zone is not set correctly. " + "Please set your computers time zone correctly and keep your clock synchronized with the internet.", @@ -118,9 +126,38 @@ public sealed class TokenProvider : IDisposable, IMediatorSubscriber return response; } + private JwtIdentifier? GetIdentifier() + { + JwtIdentifier jwtIdentifier; + try + { + jwtIdentifier = new(_serverManager.CurrentApiUrl, + _dalamudUtil.GetPlayerNameHashedAsync().GetAwaiter().GetResult(), + _serverManager.GetSecretKey()!); + _lastJwtIdentifier = jwtIdentifier; + } + catch (Exception ex) + { + if (_lastJwtIdentifier == null) + { + _logger.LogError("GetOrUpdate: No last identifier found, aborting"); + return null; + } + + _logger.LogWarning(ex, "GetOrUpdate: Could not get JwtIdentifier for some reason or another, reusing last identifier {identifier}", _lastJwtIdentifier); + jwtIdentifier = _lastJwtIdentifier; + } + + _logger.LogDebug("GetOrUpdate: Using identifier {identifier}", jwtIdentifier); + return jwtIdentifier; + } + public string? GetToken() { - if (_tokenCache.TryGetValue(CurrentIdentifier, out var token)) + JwtIdentifier? jwtIdentifier = GetIdentifier(); + if (jwtIdentifier == null) return null; + + if (_tokenCache.TryGetValue(jwtIdentifier, out var token)) { return token; } @@ -130,8 +167,11 @@ public sealed class TokenProvider : IDisposable, IMediatorSubscriber public async Task GetOrUpdateToken(CancellationToken ct) { + JwtIdentifier? jwtIdentifier = GetIdentifier(); + if (jwtIdentifier == null) return null; + bool renewal = false; - if (_tokenCache.TryGetValue(CurrentIdentifier, out var token)) + if (_tokenCache.TryGetValue(jwtIdentifier, out var token)) { var handler = new JwtSecurityTokenHandler(); var jwt = handler.ReadJwtToken(token); @@ -150,6 +190,6 @@ public sealed class TokenProvider : IDisposable, IMediatorSubscriber } _logger.LogTrace("GetOrUpdate: Getting new token"); - return await GetNewToken(renewal, ct).ConfigureAwait(false); + return await GetNewToken(renewal, jwtIdentifier, ct).ConfigureAwait(false); } } \ No newline at end of file From 33535310aaa484e0e93da9dfe4a807b017d02fb4 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Mon, 30 Oct 2023 12:34:23 +0100 Subject: [PATCH 43/56] add permission popup ui --- MareSynchronos/PlayerData/Pairs/Pair.cs | 6 + MareSynchronos/Services/Mediator/Messages.cs | 1 + MareSynchronos/Services/UiFactory.cs | 6 + MareSynchronos/Services/UiService.cs | 11 ++ MareSynchronos/UI/Components/DrawUserPair.cs | 11 +- MareSynchronos/UI/PermissionWindowUI.cs | 186 +++++++++++++++++++ MareSynchronos/UI/UISharedService.cs | 27 ++- 7 files changed, 230 insertions(+), 18 deletions(-) create mode 100644 MareSynchronos/UI/PermissionWindowUI.cs diff --git a/MareSynchronos/PlayerData/Pairs/Pair.cs b/MareSynchronos/PlayerData/Pairs/Pair.cs index cbeb5e5..764be38 100644 --- a/MareSynchronos/PlayerData/Pairs/Pair.cs +++ b/MareSynchronos/PlayerData/Pairs/Pair.cs @@ -57,9 +57,11 @@ public class Pair SeStringBuilder seStringBuilder = new(); SeStringBuilder seStringBuilder2 = new(); SeStringBuilder seStringBuilder3 = new(); + SeStringBuilder seStringBuilder4 = new(); var openProfileSeString = seStringBuilder.AddUiForeground(526).AddText(" ").AddUiForegroundOff().AddText("Open Profile").Build(); var reapplyDataSeString = seStringBuilder2.AddUiForeground(526).AddText(" ").AddUiForegroundOff().AddText("Reapply last data").Build(); var cyclePauseState = seStringBuilder3.AddUiForeground(526).AddText(" ").AddUiForegroundOff().AddText("Cycle pause state").Build(); + var changePermissions = seStringBuilder4.AddUiForeground(526).AddText(" ").AddUiForegroundOff().AddText("Change Permissions").Build(); args.AddCustomItem(new GameObjectContextMenuItem(openProfileSeString, (a) => { _mediator.Publish(new ProfileOpenStandaloneMessage(this)); @@ -68,6 +70,10 @@ public class Pair { ApplyLastReceivedData(forced: true); }, useDalamudIndicator: false)); + args.AddCustomItem(new GameObjectContextMenuItem(changePermissions, (a) => + { + _mediator.Publish(new OpenPermissionWindow(this)); + }, useDalamudIndicator: false)); args.AddCustomItem(new GameObjectContextMenuItem(cyclePauseState, (a) => { _mediator.Publish(new CyclePauseMessage(UserData)); diff --git a/MareSynchronos/Services/Mediator/Messages.cs b/MareSynchronos/Services/Mediator/Messages.cs index 943433c..85a69d4 100644 --- a/MareSynchronos/Services/Mediator/Messages.cs +++ b/MareSynchronos/Services/Mediator/Messages.cs @@ -73,6 +73,7 @@ public record RefreshUiMessage : MessageBase; public record OpenReportPopupMessage(Pair PairToReport) : MessageBase; public record OpenBanUserPopupMessage(Pair PairToBan, GroupFullInfoDto GroupFullInfoDto) : MessageBase; public record OpenSyncshellAdminPanel(GroupFullInfoDto GroupInfo) : MessageBase; +public record OpenPermissionWindow(Pair Pair) : MessageBase; #pragma warning restore S2094 #pragma warning restore MA0048 // File name must match type name \ No newline at end of file diff --git a/MareSynchronos/Services/UiFactory.cs b/MareSynchronos/Services/UiFactory.cs index 8d611b2..7db5232 100644 --- a/MareSynchronos/Services/UiFactory.cs +++ b/MareSynchronos/Services/UiFactory.cs @@ -43,4 +43,10 @@ public class UiFactory return new StandaloneProfileUi(_loggerFactory.CreateLogger(), _mareMediator, _uiSharedService, _serverConfigManager, _mareProfileManager, _pairManager, pair); } + + public PermissionWindowUI CreatePermissionPopupUi(Pair pair) + { + return new PermissionWindowUI(_loggerFactory.CreateLogger(), pair, + _mareMediator, _uiSharedService, _apiController); + } } diff --git a/MareSynchronos/Services/UiService.cs b/MareSynchronos/Services/UiService.cs index a8151e5..54a8053 100644 --- a/MareSynchronos/Services/UiService.cs +++ b/MareSynchronos/Services/UiService.cs @@ -65,6 +65,17 @@ public sealed class UiService : DisposableMediatorSubscriberBase } }); + Mediator.Subscribe(this, (msg) => + { + if (!_createdWindows.Exists(p => p is PermissionWindowUI ui + && msg.Pair == ui.Pair)) + { + var window = _uiFactory.CreatePermissionPopupUi(msg.Pair); + _createdWindows.Add(window); + _windowSystem.AddWindow(window); + } + }); + Mediator.Subscribe(this, (msg) => { _windowSystem.RemoveWindow(msg.Window); diff --git a/MareSynchronos/UI/Components/DrawUserPair.cs b/MareSynchronos/UI/Components/DrawUserPair.cs index 96c12ef..572915c 100644 --- a/MareSynchronos/UI/Components/DrawUserPair.cs +++ b/MareSynchronos/UI/Components/DrawUserPair.cs @@ -87,6 +87,13 @@ public class DrawUserPair ImGui.Separator(); ImGui.TextUnformatted("Pair Permission Functions"); + if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.WindowMaximize, "Open Permissions Window", _menuRenderWidth, true)) + { + _mediator.Publish(new OpenPermissionWindow(_pair)); + ImGui.CloseCurrentPopup(); + } + UiSharedService.AttachToolTip("Opens the Permissions Window which allows you to manage multiple permissions at once."); + var isSticky = _pair.UserPair!.OwnPermissions.IsSticky(); string stickyText = isSticky ? "Disable Preferred Permissions" : "Enable Preferred Permissions"; var stickyIcon = isSticky ? FontAwesomeIcon.ArrowCircleDown : FontAwesomeIcon.ArrowCircleUp; @@ -201,7 +208,7 @@ public class DrawUserPair else if (_pair.IsVisible) { UiSharedService.NormalizedIcon(FontAwesomeIcon.Eye, ImGuiColors.ParsedGreen); - userPairText = _pair.UserData.AliasOrUID + " is visible"; + userPairText = _pair.UserData.AliasOrUID + " is visible: " + _pair.PlayerName; } else { @@ -306,7 +313,7 @@ public class DrawUserPair currentRightSide -= (UiSharedService.GetIconData(individualIcon).NormalizedIconScale.X + spacingX); ImGui.SameLine(currentRightSide); - using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.HealerGreen, individualIsSticky)) + using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudYellow, individualAnimDisabled || individualSoundsDisabled || individualVFXDisabled)) UiSharedService.NormalizedIcon(individualIcon); if (ImGui.IsItemHovered()) { diff --git a/MareSynchronos/UI/PermissionWindowUI.cs b/MareSynchronos/UI/PermissionWindowUI.cs new file mode 100644 index 0000000..a816178 --- /dev/null +++ b/MareSynchronos/UI/PermissionWindowUI.cs @@ -0,0 +1,186 @@ +using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; +using ImGuiNET; +using MareSynchronos.API.Data.Enum; +using MareSynchronos.API.Data.Extensions; +using MareSynchronos.PlayerData.Pairs; +using MareSynchronos.Services.Mediator; +using MareSynchronos.Utils; +using MareSynchronos.WebAPI; +using Microsoft.Extensions.Logging; + +namespace MareSynchronos.UI; + +public class PermissionWindowUI : WindowMediatorSubscriberBase +{ + public Pair Pair { get; init; } + + private readonly UiSharedService _uiSharedService; + private readonly ApiController _apiController; + private UserPermissions _ownPermissions; + + public PermissionWindowUI(ILogger logger, Pair pair, MareMediator mediator, UiSharedService uiSharedService, + ApiController apiController) : base(logger, mediator, "Permissions for " + pair.UserData.AliasOrUID + "###MareSynchronosPermissions" + pair.UserData.UID) + { + Pair = pair; + _uiSharedService = uiSharedService; + _apiController = apiController; + _ownPermissions = pair.UserPair.OwnPermissions.DeepClone(); + Flags = ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoResize; + SizeConstraints = new() + { + MinimumSize = new(450, 100), + MaximumSize = new(450, 500) + }; + IsOpen = true; + } + + public override void Draw() + { + var sticky = _ownPermissions.IsSticky(); + var paused = _ownPermissions.IsPaused(); + var disableSounds = _ownPermissions.IsDisableSounds(); + var disableAnimations = _ownPermissions.IsDisableAnimations(); + var disableVfx = _ownPermissions.IsDisableVFX(); + var style = ImGui.GetStyle(); + var indentSize = ImGui.GetFrameHeight() + style.ItemSpacing.X; + + _uiSharedService.BigText("Permissions for " + Pair.UserData.AliasOrUID); + ImGuiHelpers.ScaledDummy(1f); + + if (ImGui.Checkbox("Preferred Permissions", ref sticky)) + { + _ownPermissions.SetSticky(sticky); + } + UiSharedService.DrawHelpText("Preferred Permissions, when enabled, will exclude this user from any permission changes on any syncshells you share with this user."); + + ImGuiHelpers.ScaledDummy(1f); + + + if (ImGui.Checkbox("Pause Sync", ref paused)) + { + _ownPermissions.SetPaused(paused); + } + UiSharedService.DrawHelpText("Pausing will completely cease any sync with this user." + UiSharedService.TooltipSeparator + + "Note: this is bidirectional, either user pausing will cease sync completely."); + var otherPerms = Pair.UserPair.OtherPermissions; + + var otherIsPaused = otherPerms.IsPaused(); + var otherDisableSounds = otherPerms.IsDisableSounds(); + var otherDisableAnimations = otherPerms.IsDisableAnimations(); + var otherDisableVFX = otherPerms.IsDisableVFX(); + + using (ImRaii.PushIndent(indentSize, false)) + { + UiSharedService.BooleanToColoredIcon(!otherIsPaused, false); + ImGui.SameLine(); + ImGui.AlignTextToFramePadding(); + ImGui.Text(Pair.UserData.AliasOrUID + " has " + (!otherIsPaused ? "not " : string.Empty) + "paused you"); + } + + ImGuiHelpers.ScaledDummy(0.5f); + ImGui.Separator(); + ImGuiHelpers.ScaledDummy(0.5f); + + if (ImGui.Checkbox("Disable Sounds", ref disableSounds)) + { + _ownPermissions.SetDisableSounds(disableSounds); + } + UiSharedService.DrawHelpText("Disabling sounds will remove all sounds synced with this user on both sides." + UiSharedService.TooltipSeparator + + "Note: this is bidirectional, either user disabling sound sync will stop sound sync on both sides."); + using (ImRaii.PushIndent(indentSize, false)) + { + UiSharedService.BooleanToColoredIcon(!otherDisableSounds, false); + ImGui.SameLine(); + ImGui.AlignTextToFramePadding(); + ImGui.Text(Pair.UserData.AliasOrUID + " has " + (!otherDisableSounds ? "not " : string.Empty) + "disabled sound sync with you"); + } + + if (ImGui.Checkbox("Disable Animations", ref disableAnimations)) + { + _ownPermissions.SetDisableAnimations(disableAnimations); + } + UiSharedService.DrawHelpText("Disabling sounds will remove all animations synced with this user on both sides." + UiSharedService.TooltipSeparator + + "Note: this is bidirectional, either user disabling animation sync will stop animation sync on both sides."); + using (ImRaii.PushIndent(indentSize, false)) + { + UiSharedService.BooleanToColoredIcon(!otherDisableAnimations, false); + ImGui.SameLine(); + ImGui.AlignTextToFramePadding(); + ImGui.Text(Pair.UserData.AliasOrUID + " has " + (!otherDisableAnimations ? "not " : string.Empty) + "disabled animation sync with you"); + } + + if (ImGui.Checkbox("Disable VFX", ref disableVfx)) + { + _ownPermissions.SetDisableVFX(disableVfx); + } + UiSharedService.DrawHelpText("Disabling sounds will remove all VFX synced with this user on both sides." + UiSharedService.TooltipSeparator + + "Note: this is bidirectional, either user disabling VFX sync will stop VFX sync on both sides."); + using (ImRaii.PushIndent(indentSize, false)) + { + UiSharedService.BooleanToColoredIcon(!otherDisableVFX, false); + ImGui.SameLine(); + ImGui.AlignTextToFramePadding(); + ImGui.Text(Pair.UserData.AliasOrUID + " has " + (!otherDisableVFX ? "not " : string.Empty) + "disabled VFX sync with you"); + } + + ImGuiHelpers.ScaledDummy(0.5f); + ImGui.Separator(); + ImGuiHelpers.ScaledDummy(0.5f); + + bool hasChanges = _ownPermissions != Pair.UserPair.OwnPermissions; + + using (ImRaii.Disabled(!hasChanges)) + if (UiSharedService.NormalizedIconTextButton(Dalamud.Interface.FontAwesomeIcon.Save, "Save")) + { + _ = _apiController.SetBulkPermissions(new( + new(StringComparer.Ordinal) + { + { Pair.UserData.UID, _ownPermissions } + }, + new(StringComparer.Ordinal) + )); + } + UiSharedService.AttachToolTip("Save and apply all changes"); + + var rightSideButtons = UiSharedService.GetNormalizedIconTextButtonSize(Dalamud.Interface.FontAwesomeIcon.Undo, "Revert").X + + UiSharedService.GetNormalizedIconTextButtonSize(Dalamud.Interface.FontAwesomeIcon.ArrowsSpin, "Reset to Default Permissions").X; + var availableWidth = ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X; + + ImGui.SameLine(availableWidth - rightSideButtons); + + using (ImRaii.Disabled(!hasChanges)) + if (UiSharedService.NormalizedIconTextButton(Dalamud.Interface.FontAwesomeIcon.Undo, "Revert")) + { + _ownPermissions = Pair.UserPair.OwnPermissions.DeepClone(); + } + UiSharedService.AttachToolTip("Revert all changes"); + + ImGui.SameLine(); + if (UiSharedService.NormalizedIconTextButton(Dalamud.Interface.FontAwesomeIcon.ArrowsSpin, "Reset to Default Permissions")) + { + var defaultPermissions = _apiController.DefaultPermissions!; + _ownPermissions.SetSticky(Pair.IsDirectlyPaired || defaultPermissions.IndividualIsSticky); + _ownPermissions.SetPaused(false); + _ownPermissions.SetDisableVFX(Pair.IsDirectlyPaired ? defaultPermissions.DisableIndividualVFX : defaultPermissions.DisableGroupVFX); + _ownPermissions.SetDisableSounds(Pair.IsDirectlyPaired ? defaultPermissions.DisableIndividualSounds : defaultPermissions.DisableGroupSounds); + _ownPermissions.SetDisableAnimations(Pair.IsDirectlyPaired ? defaultPermissions.DisableIndividualAnimations : defaultPermissions.DisableGroupAnimations); + _ = _apiController.SetBulkPermissions(new( + new(StringComparer.Ordinal) + { + { Pair.UserData.UID, _ownPermissions } + }, + new(StringComparer.Ordinal) + )); + } + UiSharedService.AttachToolTip("This will set all permissions to your defined default permissions in the Mare Settings"); + + var ySize = ImGui.GetCursorPosY() + style.FramePadding.Y * ImGuiHelpers.GlobalScale + style.FrameBorderSize; + ImGui.SetWindowSize(new(400, ySize)); + } + + public override void OnClose() + { + Mediator.Publish(new RemoveWindowMessage(this)); + } +} diff --git a/MareSynchronos/UI/UISharedService.cs b/MareSynchronos/UI/UISharedService.cs index 4183180..ef497a3 100644 --- a/MareSynchronos/UI/UISharedService.cs +++ b/MareSynchronos/UI/UISharedService.cs @@ -134,6 +134,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) { ImGui.BeginTooltip(); + ImGui.PushTextWrapPos(ImGui.GetFontSize() * 35f); if (text.Contains(TooltipSeparator, StringComparison.Ordinal)) { var splitText = text.Split(TooltipSeparator, StringSplitOptions.RemoveEmptyEntries); @@ -147,6 +148,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase { ImGui.TextUnformatted(text); } + ImGui.PopTextWrapPos(); ImGui.EndTooltip(); } } @@ -219,20 +221,8 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase public static void DrawHelpText(string helpText) { ImGui.SameLine(); - using (ImRaii.PushFont(UiBuilder.IconFont)) - { - ImGui.SetWindowFontScale(0.8f); - ImGui.TextDisabled(FontAwesomeIcon.Question.ToIconString()); - ImGui.SetWindowFontScale(1.0f); - } - - if (ImGui.IsItemHovered()) - { - using var tooltip = ImRaii.Tooltip(); - ImGui.PushTextWrapPos(ImGui.GetFontSize() * 35.0f); - ImGui.TextUnformatted(helpText); - ImGui.PopTextWrapPos(); - } + NormalizedIcon(FontAwesomeIcon.QuestionCircle, ImGui.GetColorU32(ImGuiCol.TextDisabled)); + AttachToolTip(helpText); } public static void DrawOutlinedFont(string text, Vector4 fontColor, Vector4 outlineColor, int thickness) @@ -418,7 +408,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase return wasClicked; } - public static void NormalizedIcon(FontAwesomeIcon icon, Vector4? color = null) + public static void NormalizedIcon(FontAwesomeIcon icon, uint color) { var cursorPos = ImGui.GetCursorPos(); var iconData = GetIconData(icon); @@ -433,11 +423,16 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase drawList.AddText(UiBuilder.IconFont, ImGui.GetFontSize() * iconData.IconScaling, new(windowPos.X - scrollPosX + cursorPos.X + iconData.OffsetX, windowPos.Y - scrollPosY + cursorPos.Y + frameOffsetY), - color != null ? ImGui.GetColorU32(color.Value) : ImGui.GetColorU32(ImGuiCol.Text), icon.ToIconString()); + color, icon.ToIconString()); ImGui.Dummy(new(iconData.NormalizedIconScale.X, ImGui.GetFrameHeight())); } + public static void NormalizedIcon(FontAwesomeIcon icon, Vector4? color = null) + { + NormalizedIcon(icon, color == null ? ImGui.GetColorU32(ImGuiCol.Text) : ImGui.GetColorU32(color.Value)); + } + private static IconScaleData CalcIconScaleData(FontAwesomeIcon icon) { using var font = ImRaii.PushFont(UiBuilder.IconFont); From 2afb19f94d8872637bf8bf47adcb43689b0d054e Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Mon, 30 Oct 2023 12:34:59 +0100 Subject: [PATCH 44/56] wording --- MareSynchronos/UI/PermissionWindowUI.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MareSynchronos/UI/PermissionWindowUI.cs b/MareSynchronos/UI/PermissionWindowUI.cs index a816178..400135d 100644 --- a/MareSynchronos/UI/PermissionWindowUI.cs +++ b/MareSynchronos/UI/PermissionWindowUI.cs @@ -157,7 +157,7 @@ public class PermissionWindowUI : WindowMediatorSubscriberBase UiSharedService.AttachToolTip("Revert all changes"); ImGui.SameLine(); - if (UiSharedService.NormalizedIconTextButton(Dalamud.Interface.FontAwesomeIcon.ArrowsSpin, "Reset to Default Permissions")) + if (UiSharedService.NormalizedIconTextButton(Dalamud.Interface.FontAwesomeIcon.ArrowsSpin, "Reset to Default")) { var defaultPermissions = _apiController.DefaultPermissions!; _ownPermissions.SetSticky(Pair.IsDirectlyPaired || defaultPermissions.IndividualIsSticky); From 0f0d1e9a9d50285b911817dfa6f053cb029e1d46 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Mon, 30 Oct 2023 12:37:58 +0100 Subject: [PATCH 45/56] calc correct button size --- MareSynchronos/UI/PermissionWindowUI.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MareSynchronos/UI/PermissionWindowUI.cs b/MareSynchronos/UI/PermissionWindowUI.cs index 400135d..99a5b99 100644 --- a/MareSynchronos/UI/PermissionWindowUI.cs +++ b/MareSynchronos/UI/PermissionWindowUI.cs @@ -144,7 +144,7 @@ public class PermissionWindowUI : WindowMediatorSubscriberBase UiSharedService.AttachToolTip("Save and apply all changes"); var rightSideButtons = UiSharedService.GetNormalizedIconTextButtonSize(Dalamud.Interface.FontAwesomeIcon.Undo, "Revert").X + - UiSharedService.GetNormalizedIconTextButtonSize(Dalamud.Interface.FontAwesomeIcon.ArrowsSpin, "Reset to Default Permissions").X; + UiSharedService.GetNormalizedIconTextButtonSize(Dalamud.Interface.FontAwesomeIcon.ArrowsSpin, "Reset to Default").X; var availableWidth = ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X; ImGui.SameLine(availableWidth - rightSideButtons); From 4ee0a670c02a0751402f5a83d536cc7488b07e87 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Mon, 30 Oct 2023 15:31:07 +0100 Subject: [PATCH 46/56] add inner exception stacktraces --- MareSynchronos/Interop/DalamudLogger.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MareSynchronos/Interop/DalamudLogger.cs b/MareSynchronos/Interop/DalamudLogger.cs index 2f1d117..b244d7f 100644 --- a/MareSynchronos/Interop/DalamudLogger.cs +++ b/MareSynchronos/Interop/DalamudLogger.cs @@ -36,6 +36,13 @@ internal sealed class DalamudLogger : ILogger StringBuilder sb = new(); sb.AppendLine($"[{_name}]{{{(int)logLevel}}} {state}: {exception?.Message}"); sb.AppendLine(exception?.StackTrace); + var innerException = exception?.InnerException; + while (innerException != null) + { + sb.AppendLine($"InnerException {innerException}: {innerException.Message}"); + sb.AppendLine(innerException.StackTrace); + innerException = innerException.InnerException; + } if (logLevel == LogLevel.Warning) _pluginLog.Warning(sb.ToString()); else if (logLevel == LogLevel.Error) From 2e53c8e4fb02c8a9c1f952c6a09fe432304e7316 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Tue, 31 Oct 2023 23:36:26 +0100 Subject: [PATCH 47/56] fix startup breaking --- MareSynchronos/Interop/IpcManager.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/MareSynchronos/Interop/IpcManager.cs b/MareSynchronos/Interop/IpcManager.cs index 6ecd660..984e0c1 100644 --- a/MareSynchronos/Interop/IpcManager.cs +++ b/MareSynchronos/Interop/IpcManager.cs @@ -157,7 +157,14 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase Mediator.Subscribe(this, (_) => PeriodicApiStateCheck()); - PeriodicApiStateCheck(); + try + { + PeriodicApiStateCheck(); + } + catch (Exception ex) + { + logger.LogWarning(ex, "Failed to check for some IPC, plugin not installed?"); + } } public bool Initialized => CheckPenumbraApiInternal() && CheckGlamourerApiInternal(); From 75494c69fe3ad64bb62bbe7cee82b4795ad2b9fd Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Thu, 9 Nov 2023 19:48:46 +0100 Subject: [PATCH 48/56] use name for glamourer revert --- MareSynchronos/Interop/IpcManager.cs | 4 ++-- MareSynchronos/PlayerData/Export/MareCharaFileManager.cs | 2 +- MareSynchronos/PlayerData/Handlers/PairHandler.cs | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/MareSynchronos/Interop/IpcManager.cs b/MareSynchronos/Interop/IpcManager.cs index 984e0c1..6fe2327 100644 --- a/MareSynchronos/Interop/IpcManager.cs +++ b/MareSynchronos/Interop/IpcManager.cs @@ -293,7 +293,7 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase } } - public async Task GlamourerRevert(ILogger logger, GameObjectHandler handler, Guid applicationId, CancellationToken token) + public async Task GlamourerRevert(ILogger logger, string name, GameObjectHandler handler, Guid applicationId, CancellationToken token) { if ((!CheckGlamourerApi()) || _dalamudUtil.IsZoning) return; try @@ -304,7 +304,7 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase try { logger.LogDebug("[{appid}] Calling On IPC: GlamourerUnlockName", applicationId); - _glamourerUnlock.InvokeFunc(handler.Name, LockCode); + _glamourerUnlock.InvokeFunc(name, LockCode); logger.LogDebug("[{appid}] Calling On IPC: GlamourerRevert", applicationId); _glamourerRevert.InvokeAction(chara, LockCode); logger.LogDebug("[{appid}] Calling On IPC: PenumbraRedraw", applicationId); diff --git a/MareSynchronos/PlayerData/Export/MareCharaFileManager.cs b/MareSynchronos/PlayerData/Export/MareCharaFileManager.cs index 1025f72..b5bdd82 100644 --- a/MareSynchronos/PlayerData/Export/MareCharaFileManager.cs +++ b/MareSynchronos/PlayerData/Export/MareCharaFileManager.cs @@ -48,7 +48,7 @@ public class MareCharaFileManager : DisposableMediatorSubscriberBase { if ((await dalamudUtil.RunOnFrameworkThread(() => item.Value.CurrentAddress()).ConfigureAwait(false)) != nint.Zero) { - await _ipcManager.GlamourerRevert(logger, item.Value, Guid.NewGuid(), cts.Token).ConfigureAwait(false); + await _ipcManager.GlamourerRevert(logger, item.Value.Name, item.Value, Guid.NewGuid(), cts.Token).ConfigureAwait(false); } else { diff --git a/MareSynchronos/PlayerData/Handlers/PairHandler.cs b/MareSynchronos/PlayerData/Handlers/PairHandler.cs index 25f61e0..2f3693e 100644 --- a/MareSynchronos/PlayerData/Handlers/PairHandler.cs +++ b/MareSynchronos/PlayerData/Handlers/PairHandler.cs @@ -492,7 +492,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Player, () => address, isWatched: false).ConfigureAwait(false); tempHandler.CompareNameAndThrow(name); Logger.LogDebug("[{applicationId}] Restoring Customization and Equipment for {alias}/{name}", applicationId, OnlineUser.User.AliasOrUID, name); - await _ipcManager.GlamourerRevert(Logger, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false); + await _ipcManager.GlamourerRevert(Logger, name, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false); tempHandler.CompareNameAndThrow(name); Logger.LogDebug("[{applicationId}] Restoring Heels for {alias}/{name}", applicationId, OnlineUser.User.AliasOrUID, name); await _ipcManager.HeelsRestoreOffsetForPlayerAsync(address).ConfigureAwait(false); @@ -513,7 +513,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase { await _ipcManager.CustomizePlusRevertAsync(minionOrMount).ConfigureAwait(false); using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.MinionOrMount, () => minionOrMount, isWatched: false).ConfigureAwait(false); - await _ipcManager.GlamourerRevert(Logger, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false); + await _ipcManager.GlamourerRevert(Logger, tempHandler.Name, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false); await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false); } } @@ -524,7 +524,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase { await _ipcManager.CustomizePlusRevertAsync(pet).ConfigureAwait(false); using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Pet, () => pet, isWatched: false).ConfigureAwait(false); - await _ipcManager.GlamourerRevert(Logger, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false); + await _ipcManager.GlamourerRevert(Logger, tempHandler.Name, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false); await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false); } } @@ -535,7 +535,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase { await _ipcManager.CustomizePlusRevertAsync(companion).ConfigureAwait(false); using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Pet, () => companion, isWatched: false).ConfigureAwait(false); - await _ipcManager.GlamourerRevert(Logger, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false); + await _ipcManager.GlamourerRevert(Logger, tempHandler.Name, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false); await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false); } } From 4b3bd975d53a5ae8bb3e7293a170fe14132d0eae Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Fri, 10 Nov 2023 00:08:32 +0100 Subject: [PATCH 49/56] add download throttling, change header of mare, fix reverting players when going offline/paused when not visible --- MareSynchronos/Interop/IpcManager.cs | 20 ++ .../Configurations/MareConfig.cs | 2 + .../Models/DownloadSpeeds.cs | 8 + MareSynchronos/MareSynchronos.csproj | 1 + .../PlayerData/Handlers/PairHandler.cs | 54 +++-- MareSynchronos/PlayerData/Pairs/Pair.cs | 2 +- MareSynchronos/Services/Mediator/Messages.cs | 1 + MareSynchronos/UI/CompactUI.cs | 65 +++--- MareSynchronos/UI/SettingsUi.cs | 33 +++ .../WebAPI/Files/FileDownloadManager.cs | 35 ++- .../WebAPI/Files/FileTransferOrchestrator.cs | 18 ++ .../WebAPI/Files/ThrottledStream.cs | 217 ++++++++++++++++++ 12 files changed, 400 insertions(+), 56 deletions(-) create mode 100644 MareSynchronos/MareConfiguration/Models/DownloadSpeeds.cs create mode 100644 MareSynchronos/WebAPI/Files/ThrottledStream.cs diff --git a/MareSynchronos/Interop/IpcManager.cs b/MareSynchronos/Interop/IpcManager.cs index 6fe2327..9dbad06 100644 --- a/MareSynchronos/Interop/IpcManager.cs +++ b/MareSynchronos/Interop/IpcManager.cs @@ -322,6 +322,26 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase } } + public async Task GlamourerRevertByNameAsync(ILogger logger, string name, Guid applicationId) + { + if ((!CheckGlamourerApi()) || _dalamudUtil.IsZoning) return; + + await _dalamudUtil.RunOnFrameworkThread(() => + { + try + { + logger.LogDebug("[{appid}] Calling On IPC: GlamourerRevertByName", applicationId); + _glamourerRevertByName.InvokeAction(name, LockCode); + logger.LogDebug("[{appid}] Calling On IPC: GlamourerUnlockName", applicationId); + _glamourerUnlock.InvokeFunc(name, LockCode); + } + catch (Exception ex) + { + Logger.LogWarning(ex, "Error during Glamourer RevertByName"); + } + }).ConfigureAwait(false); + } + public void GlamourerRevertByName(ILogger logger, string name, Guid applicationId) { if ((!CheckGlamourerApi()) || _dalamudUtil.IsZoning) return; diff --git a/MareSynchronos/MareConfiguration/Configurations/MareConfig.cs b/MareSynchronos/MareConfiguration/Configurations/MareConfig.cs index 013c0eb..1099395 100644 --- a/MareSynchronos/MareConfiguration/Configurations/MareConfig.cs +++ b/MareSynchronos/MareConfiguration/Configurations/MareConfig.cs @@ -24,6 +24,8 @@ public class MareConfig : IMareConfiguration public bool OpenGposeImportOnGposeStart { get; set; } = false; public bool OpenPopupOnAdd { get; set; } = true; public int ParallelDownloads { get; set; } = 10; + public int DownloadSpeedLimitInBytes { get; set; } = 0; + public DownloadSpeeds DownloadSpeedType { get; set; } = DownloadSpeeds.MBps; public bool PreferNotesOverNamesForVisible { get; set; } = false; public float ProfileDelay { get; set; } = 1.5f; public bool ProfilePopoutRight { get; set; } = false; diff --git a/MareSynchronos/MareConfiguration/Models/DownloadSpeeds.cs b/MareSynchronos/MareConfiguration/Models/DownloadSpeeds.cs new file mode 100644 index 0000000..815da1f --- /dev/null +++ b/MareSynchronos/MareConfiguration/Models/DownloadSpeeds.cs @@ -0,0 +1,8 @@ +namespace MareSynchronos.MareConfiguration.Models; + +public enum DownloadSpeeds +{ + Bps, + KBps, + MBps +} \ No newline at end of file diff --git a/MareSynchronos/MareSynchronos.csproj b/MareSynchronos/MareSynchronos.csproj index 6d2c8e5..3a86763 100644 --- a/MareSynchronos/MareSynchronos.csproj +++ b/MareSynchronos/MareSynchronos.csproj @@ -31,6 +31,7 @@ + all diff --git a/MareSynchronos/PlayerData/Handlers/PairHandler.cs b/MareSynchronos/PlayerData/Handlers/PairHandler.cs index 2f3693e..6778809 100644 --- a/MareSynchronos/PlayerData/Handlers/PairHandler.cs +++ b/MareSynchronos/PlayerData/Handlers/PairHandler.cs @@ -191,22 +191,35 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase if (_lifetime.ApplicationStopping.IsCancellationRequested) return; - if (_dalamudUtil is { IsZoning: false, IsInCutscene: false }) + if (_dalamudUtil is { IsZoning: false, IsInCutscene: false } && !string.IsNullOrEmpty(name)) { Logger.LogTrace("[{applicationId}] Restoring state for {name} ({OnlineUser})", applicationId, name, OnlineUser); + Logger.LogDebug("[{applicationId}] Removing Temp Collection for {name} ({user})", applicationId, name, OnlineUser); _ipcManager.PenumbraRemoveTemporaryCollectionAsync(Logger, applicationId, _penumbraCollection).GetAwaiter().GetResult(); - - foreach (KeyValuePair> item in _cachedData?.FileReplacements ?? []) + if (!IsVisible) { - try + Logger.LogDebug("[{applicationId}] Restoring Glamourer for {name} ({user})", applicationId, name, OnlineUser); + _ipcManager.GlamourerRevertByNameAsync(Logger, name, applicationId).GetAwaiter().GetResult(); + } + else + { + var cts = new CancellationTokenSource(); + cts.CancelAfter(TimeSpan.FromSeconds(60)); + + foreach (KeyValuePair> item in _cachedData?.FileReplacements ?? []) { - RevertCustomizationDataAsync(item.Key, name, applicationId).GetAwaiter().GetResult(); - } - catch (InvalidOperationException ex) - { - Logger.LogWarning(ex, "Failed disposing player (not present anymore?)"); - break; + try + { + RevertCustomizationDataAsync(item.Key, name, applicationId, cts.Token).GetAwaiter().GetResult(); + } + catch (InvalidOperationException ex) + { + Logger.LogWarning(ex, "Failed disposing player (not present anymore?)"); + break; + } } + + cts.CancelDispose(); } } } @@ -477,14 +490,11 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase _ipcManager.PenumbraAssignTemporaryCollectionAsync(Logger, _penumbraCollection, _charaHandler.GetGameObject()!.ObjectIndex).GetAwaiter().GetResult(); } - private async Task RevertCustomizationDataAsync(ObjectKind objectKind, string name, Guid applicationId) + private async Task RevertCustomizationDataAsync(ObjectKind objectKind, string name, Guid applicationId, CancellationToken cancelToken) { nint address = _dalamudUtil.GetPlayerCharacterFromCachedTableByIdent(OnlineUser.Ident); if (address == nint.Zero) return; - var cancelToken = new CancellationTokenSource(); - cancelToken.CancelAfter(TimeSpan.FromSeconds(60)); - Logger.LogDebug("[{applicationId}] Reverting all Customization for {alias}/{name} {objectKind}", applicationId, OnlineUser.User.AliasOrUID, name, objectKind); if (objectKind == ObjectKind.Player) @@ -492,7 +502,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Player, () => address, isWatched: false).ConfigureAwait(false); tempHandler.CompareNameAndThrow(name); Logger.LogDebug("[{applicationId}] Restoring Customization and Equipment for {alias}/{name}", applicationId, OnlineUser.User.AliasOrUID, name); - await _ipcManager.GlamourerRevert(Logger, name, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false); + await _ipcManager.GlamourerRevert(Logger, name, tempHandler, applicationId, cancelToken).ConfigureAwait(false); tempHandler.CompareNameAndThrow(name); Logger.LogDebug("[{applicationId}] Restoring Heels for {alias}/{name}", applicationId, OnlineUser.User.AliasOrUID, name); await _ipcManager.HeelsRestoreOffsetForPlayerAsync(address).ConfigureAwait(false); @@ -513,8 +523,8 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase { await _ipcManager.CustomizePlusRevertAsync(minionOrMount).ConfigureAwait(false); using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.MinionOrMount, () => minionOrMount, isWatched: false).ConfigureAwait(false); - await _ipcManager.GlamourerRevert(Logger, tempHandler.Name, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false); - await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false); + await _ipcManager.GlamourerRevert(Logger, tempHandler.Name, tempHandler, applicationId, cancelToken).ConfigureAwait(false); + await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken).ConfigureAwait(false); } } else if (objectKind == ObjectKind.Pet) @@ -524,8 +534,8 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase { await _ipcManager.CustomizePlusRevertAsync(pet).ConfigureAwait(false); using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Pet, () => pet, isWatched: false).ConfigureAwait(false); - await _ipcManager.GlamourerRevert(Logger, tempHandler.Name, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false); - await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false); + await _ipcManager.GlamourerRevert(Logger, tempHandler.Name, tempHandler, applicationId, cancelToken).ConfigureAwait(false); + await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken).ConfigureAwait(false); } } else if (objectKind == ObjectKind.Companion) @@ -535,12 +545,10 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase { await _ipcManager.CustomizePlusRevertAsync(companion).ConfigureAwait(false); using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Pet, () => companion, isWatched: false).ConfigureAwait(false); - await _ipcManager.GlamourerRevert(Logger, tempHandler.Name, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false); - await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false); + await _ipcManager.GlamourerRevert(Logger, tempHandler.Name, tempHandler, applicationId, cancelToken).ConfigureAwait(false); + await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken).ConfigureAwait(false); } } - - cancelToken.CancelDispose(); } private List TryCalculateModdedDictionary(Guid applicationBase, CharacterData charaData, out Dictionary moddedDictionary, CancellationToken token) diff --git a/MareSynchronos/PlayerData/Pairs/Pair.cs b/MareSynchronos/PlayerData/Pairs/Pair.cs index 764be38..83a9a5a 100644 --- a/MareSynchronos/PlayerData/Pairs/Pair.cs +++ b/MareSynchronos/PlayerData/Pairs/Pair.cs @@ -40,7 +40,7 @@ public class Pair public bool IsOnline => CachedPlayer != null; public bool IsPaired => IndividualPairStatus == IndividualPairStatus.Bidirectional || UserPair.Groups.Any(); - public bool IsPaused => UserPair.OtherPermissions.IsPaused() || UserPair.OwnPermissions.IsPaused(); + public bool IsPaused => UserPair.OwnPermissions.IsPaused(); public bool IsVisible => CachedPlayer?.IsVisible ?? false; public CharacterData? LastReceivedCharacterData { get; set; } public string? PlayerName => CachedPlayer?.PlayerName ?? string.Empty; diff --git a/MareSynchronos/Services/Mediator/Messages.cs b/MareSynchronos/Services/Mediator/Messages.cs index 85a69d4..c839d29 100644 --- a/MareSynchronos/Services/Mediator/Messages.cs +++ b/MareSynchronos/Services/Mediator/Messages.cs @@ -74,6 +74,7 @@ public record OpenReportPopupMessage(Pair PairToReport) : MessageBase; public record OpenBanUserPopupMessage(Pair PairToBan, GroupFullInfoDto GroupFullInfoDto) : MessageBase; public record OpenSyncshellAdminPanel(GroupFullInfoDto GroupInfo) : MessageBase; public record OpenPermissionWindow(Pair Pair) : MessageBase; +public record DownloadLimitChangedMessage() : SameThreadMessage; #pragma warning restore S2094 #pragma warning restore MA0048 // File name must match type name \ No newline at end of file diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs index 74f8c59..15b5ea9 100644 --- a/MareSynchronos/UI/CompactUI.cs +++ b/MareSynchronos/UI/CompactUI.cs @@ -69,6 +69,21 @@ public class CompactUi : WindowMediatorSubscriberBase _selectPairsForGroupUi = selectPairForTagUi; _tabMenu = new TopTabMenu(Mediator, _apiController, _pairManager); + AllowPinning = false; + AllowClickthrough = false; + TitleBarButtons = new() + { + new TitleBarButton() + { + Icon = FontAwesomeIcon.Cog, + Click = (msg) => + { + Mediator.Publish(new UiToggleMessage(typeof(SettingsUi))); + }, + IconOffset = new(2,1) + } + }; + _drawFolders = GetDrawFolders().ToList(); #if DEBUG @@ -340,47 +355,35 @@ public class CompactUi : WindowMediatorSubscriberBase private void DrawUIDHeader() { var uidText = GetUidText(); - var buttonSizeX = 0f; - if (_uiShared.UidFontBuilt) ImGui.PushFont(_uiShared.UidFont); - var uidTextSize = ImGui.CalcTextSize(uidText); - if (_uiShared.UidFontBuilt) ImGui.PopFont(); - - var originalPos = ImGui.GetCursorPos(); - ImGui.SetWindowFontScale(1.5f); - var buttonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Cog); - buttonSizeX -= buttonSize.X - ImGui.GetStyle().ItemSpacing.X * 2; - ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - buttonSize.X); - ImGui.SetCursorPosY(originalPos.Y + uidTextSize.Y / 2 - buttonSize.Y / 2); - if (ImGuiComponents.IconButton(FontAwesomeIcon.Cog)) + using (ImRaii.PushFont(_uiShared.UidFont, _uiShared.UidFontBuilt)) { - Mediator.Publish(new OpenSettingsUiMessage()); + var uidTextSize = ImGui.CalcTextSize(uidText); + ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X) / 2 - (uidTextSize.X / 2)); + ImGui.TextColored(GetUidColor(), uidText); } - UiSharedService.AttachToolTip("Open the Mare Synchronos Settings"); - - ImGui.SameLine(); //Important to draw the uidText consistently - ImGui.SetCursorPos(originalPos); if (_apiController.ServerState is ServerState.Connected) { - buttonSizeX += UiSharedService.GetIconButtonSize(FontAwesomeIcon.Copy).X - ImGui.GetStyle().ItemSpacing.X * 2; - ImGui.SetCursorPosY(originalPos.Y + uidTextSize.Y / 2 - buttonSize.Y / 2); - if (ImGuiComponents.IconButton(FontAwesomeIcon.Copy)) + if (ImGui.IsItemClicked()) { ImGui.SetClipboardText(_apiController.DisplayName); } - UiSharedService.AttachToolTip("Copy your UID to clipboard"); - ImGui.SameLine(); + UiSharedService.AttachToolTip("Click to copy"); + + if (!string.Equals(_apiController.DisplayName, _apiController.UID, StringComparison.Ordinal)) + { + var origTextSize = ImGui.CalcTextSize(_apiController.UID); + ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X) / 2 - (origTextSize.X / 2)); + ImGui.TextColored(GetUidColor(), _apiController.UID); + if (ImGui.IsItemClicked()) + { + ImGui.SetClipboardText(_apiController.UID); + } + UiSharedService.AttachToolTip("Click to copy"); + } } - ImGui.SetWindowFontScale(1f); - - ImGui.SetCursorPosY(originalPos.Y + buttonSize.Y / 2 - uidTextSize.Y / 2 - ImGui.GetStyle().ItemSpacing.Y / 2); - ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMax().X + ImGui.GetWindowContentRegionMin().X) / 2 + buttonSizeX - uidTextSize.X / 2); - if (_uiShared.UidFontBuilt) ImGui.PushFont(_uiShared.UidFont); - ImGui.TextColored(GetUidColor(), uidText); - if (_uiShared.UidFontBuilt) ImGui.PopFont(); - - if (_apiController.ServerState is not ServerState.Connected) + else { UiSharedService.ColorTextWrapped(GetServerError(), GetUidColor()); if (_apiController.ServerState is ServerState.NoSecretKey) diff --git a/MareSynchronos/UI/SettingsUi.cs b/MareSynchronos/UI/SettingsUi.cs index a5bc263..1d7cc0b 100644 --- a/MareSynchronos/UI/SettingsUi.cs +++ b/MareSynchronos/UI/SettingsUi.cs @@ -69,6 +69,8 @@ public class SettingsUi : WindowMediatorSubscriberBase _apiController = apiController; _fileCompactor = fileCompactor; _uiShared = uiShared; + AllowClickthrough = false; + AllowPinning = false; SizeConstraints = new WindowSizeConstraints() { @@ -142,6 +144,37 @@ public class SettingsUi : WindowMediatorSubscriberBase int maxParallelDownloads = _configService.Current.ParallelDownloads; bool useAlternativeUpload = _configService.Current.UseAlternativeFileUpload; + int downloadSpeedLimit = _configService.Current.DownloadSpeedLimitInBytes; + + ImGui.AlignTextToFramePadding(); + ImGui.TextUnformatted("Global Download Speed Limit"); + ImGui.SameLine(); + ImGui.SetNextItemWidth(100); + if (ImGui.InputInt("###speedlimit", ref downloadSpeedLimit)) + { + _configService.Current.DownloadSpeedLimitInBytes = downloadSpeedLimit; + _configService.Save(); + Mediator.Publish(new DownloadLimitChangedMessage()); + } + ImGui.SameLine(); + ImGui.SetNextItemWidth(100); + _uiShared.DrawCombo("###speed", new[] { DownloadSpeeds.Bps, DownloadSpeeds.KBps, DownloadSpeeds.MBps }, + (s) => s switch + { + DownloadSpeeds.Bps => "Byte/s", + DownloadSpeeds.KBps => "KB/s", + DownloadSpeeds.MBps => "MB/s", + _ => throw new NotSupportedException() + }, (s) => + { + _configService.Current.DownloadSpeedType = s; + _configService.Save(); + Mediator.Publish(new DownloadLimitChangedMessage()); + }, _configService.Current.DownloadSpeedType); + ImGui.SameLine(); + ImGui.AlignTextToFramePadding(); + ImGui.TextUnformatted("0 = No limit/infinite"); + if (ImGui.SliderInt("Maximum Parallel Downloads", ref maxParallelDownloads, 1, 10)) { _configService.Current.ParallelDownloads = maxParallelDownloads; diff --git a/MareSynchronos/WebAPI/Files/FileDownloadManager.cs b/MareSynchronos/WebAPI/Files/FileDownloadManager.cs index 87290be..936a190 100644 --- a/MareSynchronos/WebAPI/Files/FileDownloadManager.cs +++ b/MareSynchronos/WebAPI/Files/FileDownloadManager.cs @@ -19,6 +19,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase private readonly FileCompactor _fileCompactor; private readonly FileCacheManager _fileDbManager; private readonly FileTransferOrchestrator _orchestrator; + private readonly List _activeDownloadStreams; public FileDownloadManager(ILogger logger, MareMediator mediator, FileTransferOrchestrator orchestrator, @@ -28,6 +29,18 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase _orchestrator = orchestrator; _fileDbManager = fileCacheManager; _fileCompactor = fileCompactor; + _activeDownloadStreams = []; + + Mediator.Subscribe(this, (msg) => + { + if (!_activeDownloadStreams.Any()) return; + var newLimit = _orchestrator.DownloadLimitPerSlot(); + Logger.LogTrace("Setting new Download Speed Limit to {newLimit}", newLimit); + foreach (var stream in _activeDownloadStreams) + { + stream.BandwidthLimit = newLimit; + } + }); } public List CurrentDownloads { get; private set; } = []; @@ -71,6 +84,14 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase protected override void Dispose(bool disposing) { CancelDownload(); + foreach (var stream in _activeDownloadStreams) + { + try + { + stream.Dispose(); + } + catch { } + } base.Dispose(disposing); } @@ -133,6 +154,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase } } + ThrottledStream? stream = null; try { var fileStream = File.Create(tempPath); @@ -142,7 +164,10 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase var buffer = new byte[bufferSize]; var bytesRead = 0; - var stream = await response.Content.ReadAsStreamAsync(ct).ConfigureAwait(false); + var limit = _orchestrator.DownloadLimitPerSlot(); + Logger.LogTrace("Starting Download of {id} with a speed limit of {limit}", requestId, limit); + stream = new ThrottledStream(await response.Content.ReadAsStreamAsync(ct).ConfigureAwait(false), limit); + _activeDownloadStreams.Add(stream); while ((bytesRead = await stream.ReadAsync(buffer, ct).ConfigureAwait(false)) > 0) { ct.ThrowIfCancellationRequested(); @@ -171,6 +196,14 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase } throw; } + finally + { + if (stream != null) + { + _activeDownloadStreams.Remove(stream); + await stream.DisposeAsync().ConfigureAwait(false); + } + } } private async Task DownloadFilesInternal(GameObjectHandler gameObjectHandler, List fileReplacement, CancellationToken ct) diff --git a/MareSynchronos/WebAPI/Files/FileTransferOrchestrator.cs b/MareSynchronos/WebAPI/Files/FileTransferOrchestrator.cs index ab8c991..f1764c6 100644 --- a/MareSynchronos/WebAPI/Files/FileTransferOrchestrator.cs +++ b/MareSynchronos/WebAPI/Files/FileTransferOrchestrator.cs @@ -19,6 +19,7 @@ public class FileTransferOrchestrator : DisposableMediatorSubscriberBase private readonly TokenProvider _tokenProvider; private int _availableDownloadSlots; private SemaphoreSlim _downloadSemaphore; + private int CurrentlyUsedDownloadSlots => _availableDownloadSlots - _downloadSemaphore.CurrentCount; public FileTransferOrchestrator(ILogger logger, MareConfigService mareConfig, MareMediator mediator, TokenProvider tokenProvider) : base(logger, mediator) @@ -72,6 +73,7 @@ public class FileTransferOrchestrator : DisposableMediatorSubscriberBase public void ReleaseDownloadSlot() { _downloadSemaphore.Release(); + Mediator.Publish(new DownloadLimitChangedMessage()); } public async Task SendRequestAsync(HttpMethod method, Uri uri, @@ -110,6 +112,22 @@ public class FileTransferOrchestrator : DisposableMediatorSubscriberBase } await _downloadSemaphore.WaitAsync(token).ConfigureAwait(false); + Mediator.Publish(new DownloadLimitChangedMessage()); + } + + public long DownloadLimitPerSlot() + { + var limit = _mareConfig.Current.DownloadSpeedLimitInBytes; + if (limit <= 0) return 0; + limit = _mareConfig.Current.DownloadSpeedType switch + { + MareConfiguration.Models.DownloadSpeeds.Bps => limit, + MareConfiguration.Models.DownloadSpeeds.KBps => limit * 1024, + MareConfiguration.Models.DownloadSpeeds.MBps => limit * 1024 * 1024, + _ => limit, + }; + var dividedLimit = limit / (CurrentlyUsedDownloadSlots == 0 ? 1 : CurrentlyUsedDownloadSlots); + return dividedLimit == 0 ? 1 : dividedLimit; } private async Task SendRequestInternalAsync(HttpRequestMessage requestMessage, diff --git a/MareSynchronos/WebAPI/Files/ThrottledStream.cs b/MareSynchronos/WebAPI/Files/ThrottledStream.cs new file mode 100644 index 0000000..0e50a68 --- /dev/null +++ b/MareSynchronos/WebAPI/Files/ThrottledStream.cs @@ -0,0 +1,217 @@ +using Microsoft.Extensions.Logging; + +namespace MareSynchronos.WebAPI.Files +{ + /// + /// Class for streaming data with throttling support. + /// Borrowed from https://github.com/bezzad/Downloader + /// + internal class ThrottledStream : Stream + { + public static long Infinite => long.MaxValue; + private readonly Stream _baseStream; + private long _bandwidthLimit; + private Bandwidth _bandwidth; + private CancellationTokenSource _bandwidthChangeTokenSource = new CancellationTokenSource(); + + /// + /// Initializes a new instance of the class. + /// + /// The base stream. + /// The maximum bytes per second that can be transferred through the base stream. + /// Thrown when is a null reference. + /// Thrown when is a negative value. + public ThrottledStream(Stream baseStream, long bandwidthLimit) + { + if (bandwidthLimit < 0) + { + throw new ArgumentOutOfRangeException(nameof(bandwidthLimit), + bandwidthLimit, "The maximum number of bytes per second can't be negative."); + } + + _baseStream = baseStream ?? throw new ArgumentNullException(nameof(baseStream)); + BandwidthLimit = bandwidthLimit; + } + + /// + /// Bandwidth Limit (in B/s) + /// + /// The maximum bytes per second. + public long BandwidthLimit + { + get => _bandwidthLimit; + set + { + if (_bandwidthLimit == value) return; + _bandwidthLimit = value <= 0 ? Infinite : value; + _bandwidth ??= new Bandwidth(); + _bandwidth.BandwidthLimit = _bandwidthLimit; + _bandwidthChangeTokenSource.Cancel(); + _bandwidthChangeTokenSource.Dispose(); + _bandwidthChangeTokenSource = new(); + } + } + + /// + public override bool CanRead => _baseStream.CanRead; + + /// + public override bool CanSeek => _baseStream.CanSeek; + + /// + public override bool CanWrite => _baseStream.CanWrite; + + /// + public override long Length => _baseStream.Length; + + /// + public override long Position + { + get => _baseStream.Position; + set => _baseStream.Position = value; + } + + /// + public override void Flush() + { + _baseStream.Flush(); + } + + /// + public override long Seek(long offset, SeekOrigin origin) + { + return _baseStream.Seek(offset, origin); + } + + /// + public override void SetLength(long value) + { + _baseStream.SetLength(value); + } + + /// + public override int Read(byte[] buffer, int offset, int count) + { + Throttle(count).Wait(); + return _baseStream.Read(buffer, offset, count); + } + + public override async Task ReadAsync(byte[] buffer, int offset, int count, + CancellationToken cancellationToken) + { + await Throttle(count, cancellationToken).ConfigureAwait(false); + return await _baseStream.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); + } + + /// + public override void Write(byte[] buffer, int offset, int count) + { + Throttle(count).Wait(); + _baseStream.Write(buffer, offset, count); + } + + /// + public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + await Throttle(count, cancellationToken).ConfigureAwait(false); + await _baseStream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); + } + + public override void Close() + { + _baseStream.Close(); + base.Close(); + } + + private async Task Throttle(int transmissionVolume, CancellationToken token = default) + { + // Make sure the buffer isn't empty. + if (BandwidthLimit > 0 && transmissionVolume > 0) + { + // Calculate the time to sleep. + _bandwidth.CalculateSpeed(transmissionVolume); + await Sleep(_bandwidth.PopSpeedRetrieveTime(), token).ConfigureAwait(false); + } + } + + private async Task Sleep(int time, CancellationToken token = default) + { + try + { + if (time > 0) + { + var bandWidthtoken = _bandwidthChangeTokenSource.Token; + var linked = CancellationTokenSource.CreateLinkedTokenSource(token, bandWidthtoken).Token; + await Task.Delay(time, linked).ConfigureAwait(false); + } + } + catch (TaskCanceledException) + { + // ignore + } + } + + /// + public override string ToString() + { + return _baseStream.ToString(); + } + + private class Bandwidth + { + private long _count; + private int _lastSecondCheckpoint; + private long _lastTransferredBytesCount; + private int _speedRetrieveTime; + public double Speed { get; private set; } + public double AverageSpeed { get; private set; } + public long BandwidthLimit { get; set; } + + public Bandwidth() + { + BandwidthLimit = long.MaxValue; + Reset(); + } + + public void CalculateSpeed(long receivedBytesCount) + { + int elapsedTime = Environment.TickCount - _lastSecondCheckpoint + 1; + receivedBytesCount = Interlocked.Add(ref _lastTransferredBytesCount, receivedBytesCount); + double momentSpeed = receivedBytesCount * 1000 / elapsedTime; // B/s + + if (1000 < elapsedTime) + { + Speed = momentSpeed; + AverageSpeed = ((AverageSpeed * _count) + Speed) / (_count + 1); + _count++; + SecondCheckpoint(); + } + + if (momentSpeed >= BandwidthLimit) + { + var expectedTime = receivedBytesCount * 1000 / BandwidthLimit; + Interlocked.Add(ref _speedRetrieveTime, (int)expectedTime - elapsedTime); + } + } + + public int PopSpeedRetrieveTime() + { + return Interlocked.Exchange(ref _speedRetrieveTime, 0); + } + + public void Reset() + { + SecondCheckpoint(); + _count = 0; + Speed = 0; + AverageSpeed = 0; + } + + private void SecondCheckpoint() + { + Interlocked.Exchange(ref _lastSecondCheckpoint, Environment.TickCount); + Interlocked.Exchange(ref _lastTransferredBytesCount, 0); + } + } + } +} From c196cdd344ac6c8fc8d081cbca22e4bc4b2410a7 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Fri, 10 Nov 2023 00:40:46 +0100 Subject: [PATCH 50/56] fix staging issues --- MareSynchronos/UI/CompactUI.cs | 17 ++++++++++++++--- MareSynchronos/UI/SettingsUi.cs | 5 +++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs index 15b5ea9..1be869c 100644 --- a/MareSynchronos/UI/CompactUI.cs +++ b/MareSynchronos/UI/CompactUI.cs @@ -1,6 +1,5 @@ using Dalamud.Interface; using Dalamud.Interface.Colors; -using Dalamud.Interface.Components; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using Dalamud.Utility; @@ -69,7 +68,8 @@ public class CompactUi : WindowMediatorSubscriberBase _selectPairsForGroupUi = selectPairForTagUi; _tabMenu = new TopTabMenu(Mediator, _apiController, _pairManager); - AllowPinning = false; + // todo: reenable when dalamud title bar buttons are out of staging + /*AllowPinning = false; AllowClickthrough = false; TitleBarButtons = new() { @@ -82,7 +82,7 @@ public class CompactUi : WindowMediatorSubscriberBase }, IconOffset = new(2,1) } - }; + };*/ _drawFolders = GetDrawFolders().ToList(); @@ -371,6 +371,17 @@ public class CompactUi : WindowMediatorSubscriberBase } UiSharedService.AttachToolTip("Click to copy"); + // todo: remove when dalamud title bar buttons are out of staging + ImGui.SetWindowFontScale(1.5f); + var buttonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Cog); + ImGui.SameLine(); + ImGui.SetCursorPosX(ImGui.GetWindowContentRegionMax().X - buttonSize.X); + if (UiSharedService.NormalizedIconButton(FontAwesomeIcon.Cog)) + { + Mediator.Publish(new UiToggleMessage(typeof(SettingsUi))); + } + ImGui.SetWindowFontScale(1.0f); + if (!string.Equals(_apiController.DisplayName, _apiController.UID, StringComparison.Ordinal)) { var origTextSize = ImGui.CalcTextSize(_apiController.UID); diff --git a/MareSynchronos/UI/SettingsUi.cs b/MareSynchronos/UI/SettingsUi.cs index 1d7cc0b..fea0a6c 100644 --- a/MareSynchronos/UI/SettingsUi.cs +++ b/MareSynchronos/UI/SettingsUi.cs @@ -69,8 +69,9 @@ public class SettingsUi : WindowMediatorSubscriberBase _apiController = apiController; _fileCompactor = fileCompactor; _uiShared = uiShared; - AllowClickthrough = false; - AllowPinning = false; + // todo: reenable when dalamud is out of staging + /*AllowClickthrough = false; + AllowPinning = false;*/ SizeConstraints = new WindowSizeConstraints() { From 96cc64b35aec534a5525515499c903c9a2f76b42 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Thu, 16 Nov 2023 15:57:23 +0100 Subject: [PATCH 51/56] add visibility for loaded mods size for pair, use menu bar for settings, remove settings button --- .../PlayerData/Handlers/PairHandler.cs | 9 +++++++++ MareSynchronos/PlayerData/Pairs/Pair.cs | 1 + MareSynchronos/UI/CompactUI.cs | 16 ++-------------- MareSynchronos/UI/Components/DrawUserPair.cs | 6 ++++++ MareSynchronos/UI/SettingsUi.cs | 5 ++--- MareSynchronos/UI/UISharedService.cs | 2 +- 6 files changed, 21 insertions(+), 18 deletions(-) diff --git a/MareSynchronos/PlayerData/Handlers/PairHandler.cs b/MareSynchronos/PlayerData/Handlers/PairHandler.cs index 6778809..88bf97a 100644 --- a/MareSynchronos/PlayerData/Handlers/PairHandler.cs +++ b/MareSynchronos/PlayerData/Handlers/PairHandler.cs @@ -37,6 +37,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase private bool _isVisible; private string _penumbraCollection; private bool _redrawOnNextApplication = false; + public long LastAppliedDataSize { get; private set; } public PairHandler(ILogger logger, OnlineUserIdentDto onlineUser, GameObjectHandlerFactory gameObjectHandlerFactory, @@ -81,6 +82,8 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase _redrawOnNextApplication = true; } }); + + LastAppliedDataSize = -1; } public bool IsVisible @@ -398,6 +401,12 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase if (updateModdedPaths) { await _ipcManager.PenumbraSetTemporaryModsAsync(Logger, _applicationId, _penumbraCollection, moddedPaths).ConfigureAwait(false); + LastAppliedDataSize = -1; + foreach (var path in moddedPaths.Select(v => new FileInfo(v.Value)).Where(p => p.Exists)) + { + if (path.Exists) + LastAppliedDataSize += path.Length; + } } if (updateManip) diff --git a/MareSynchronos/PlayerData/Pairs/Pair.cs b/MareSynchronos/PlayerData/Pairs/Pair.cs index 83a9a5a..5c1742c 100644 --- a/MareSynchronos/PlayerData/Pairs/Pair.cs +++ b/MareSynchronos/PlayerData/Pairs/Pair.cs @@ -44,6 +44,7 @@ public class Pair public bool IsVisible => CachedPlayer?.IsVisible ?? false; public CharacterData? LastReceivedCharacterData { get; set; } public string? PlayerName => CachedPlayer?.PlayerName ?? string.Empty; + public long LastAppliedDataSize => CachedPlayer?.LastAppliedDataSize ?? -1; public UserData UserData => UserPair.User; diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs index 1be869c..4207ebe 100644 --- a/MareSynchronos/UI/CompactUI.cs +++ b/MareSynchronos/UI/CompactUI.cs @@ -68,8 +68,7 @@ public class CompactUi : WindowMediatorSubscriberBase _selectPairsForGroupUi = selectPairForTagUi; _tabMenu = new TopTabMenu(Mediator, _apiController, _pairManager); - // todo: reenable when dalamud title bar buttons are out of staging - /*AllowPinning = false; + AllowPinning = false; AllowClickthrough = false; TitleBarButtons = new() { @@ -82,7 +81,7 @@ public class CompactUi : WindowMediatorSubscriberBase }, IconOffset = new(2,1) } - };*/ + }; _drawFolders = GetDrawFolders().ToList(); @@ -371,17 +370,6 @@ public class CompactUi : WindowMediatorSubscriberBase } UiSharedService.AttachToolTip("Click to copy"); - // todo: remove when dalamud title bar buttons are out of staging - ImGui.SetWindowFontScale(1.5f); - var buttonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Cog); - ImGui.SameLine(); - ImGui.SetCursorPosX(ImGui.GetWindowContentRegionMax().X - buttonSize.X); - if (UiSharedService.NormalizedIconButton(FontAwesomeIcon.Cog)) - { - Mediator.Publish(new UiToggleMessage(typeof(SettingsUi))); - } - ImGui.SetWindowFontScale(1.0f); - if (!string.Equals(_apiController.DisplayName, _apiController.UID, StringComparison.Ordinal)) { var origTextSize = ImGui.CalcTextSize(_apiController.UID); diff --git a/MareSynchronos/UI/Components/DrawUserPair.cs b/MareSynchronos/UI/Components/DrawUserPair.cs index 572915c..dbb8466 100644 --- a/MareSynchronos/UI/Components/DrawUserPair.cs +++ b/MareSynchronos/UI/Components/DrawUserPair.cs @@ -227,6 +227,12 @@ public class DrawUserPair userPairText += UiSharedService.TooltipSeparator + "You are directly Paired"; } + if (_pair.LastAppliedDataSize >= 0) + { + userPairText += UiSharedService.TooltipSeparator + (!_pair.IsVisible ? "(Last) " : string.Empty) + + "Loaded Mods Size: " + UiSharedService.ByteToString(_pair.LastAppliedDataSize, true); + } + if (_syncedGroups.Any()) { userPairText += UiSharedService.TooltipSeparator + string.Join(Environment.NewLine, diff --git a/MareSynchronos/UI/SettingsUi.cs b/MareSynchronos/UI/SettingsUi.cs index fea0a6c..1d7cc0b 100644 --- a/MareSynchronos/UI/SettingsUi.cs +++ b/MareSynchronos/UI/SettingsUi.cs @@ -69,9 +69,8 @@ public class SettingsUi : WindowMediatorSubscriberBase _apiController = apiController; _fileCompactor = fileCompactor; _uiShared = uiShared; - // todo: reenable when dalamud is out of staging - /*AllowClickthrough = false; - AllowPinning = false;*/ + AllowClickthrough = false; + AllowPinning = false; SizeConstraints = new WindowSizeConstraints() { diff --git a/MareSynchronos/UI/UISharedService.cs b/MareSynchronos/UI/UISharedService.cs index ef497a3..5fea4db 100644 --- a/MareSynchronos/UI/UISharedService.cs +++ b/MareSynchronos/UI/UISharedService.cs @@ -420,7 +420,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase var frameOffsetY = ((frameHeight - iconData.IconSize.Y * iconData.IconScaling) / 2f); - drawList.AddText(UiBuilder.IconFont, ImGui.GetFontSize() * iconData.IconScaling, + drawList.AddText(UiBuilder.IconFont, UiBuilder.IconFont.FontSize * iconData.IconScaling, new(windowPos.X - scrollPosX + cursorPos.X + iconData.OffsetX, windowPos.Y - scrollPosY + cursorPos.Y + frameOffsetY), color, icon.ToIconString()); From f89bbc45e690a01ac16efe9f817b1a9931e39c0a Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Thu, 16 Nov 2023 16:18:44 +0100 Subject: [PATCH 52/56] remove unnecessary exists check --- MareSynchronos/PlayerData/Handlers/PairHandler.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MareSynchronos/PlayerData/Handlers/PairHandler.cs b/MareSynchronos/PlayerData/Handlers/PairHandler.cs index 88bf97a..c55d640 100644 --- a/MareSynchronos/PlayerData/Handlers/PairHandler.cs +++ b/MareSynchronos/PlayerData/Handlers/PairHandler.cs @@ -404,8 +404,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase LastAppliedDataSize = -1; foreach (var path in moddedPaths.Select(v => new FileInfo(v.Value)).Where(p => p.Exists)) { - if (path.Exists) - LastAppliedDataSize += path.Length; + LastAppliedDataSize += path.Length; } } From 1522d8d7e72b2d31f71e2270bff5fdb7412214df Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Fri, 17 Nov 2023 02:05:49 +0100 Subject: [PATCH 53/56] heave fewer redraws as main method for data application, minor fixes --- .../FileCache/TransientResourceManager.cs | 61 +++++++++++-------- .../Configurations/MareConfig.cs | 1 - .../PlayerData/Handlers/PairHandler.cs | 8 +-- MareSynchronos/Services/DalamudUtilService.cs | 15 ++++- .../ServerConfigurationManager.cs | 4 +- MareSynchronos/UI/SettingsUi.cs | 12 ---- MareSynchronos/UI/UISharedService.cs | 2 +- .../WebAPI/Files/FileUploadManager.cs | 2 +- 8 files changed, 53 insertions(+), 52 deletions(-) diff --git a/MareSynchronos/FileCache/TransientResourceManager.cs b/MareSynchronos/FileCache/TransientResourceManager.cs index 8848abd..00329f6 100644 --- a/MareSynchronos/FileCache/TransientResourceManager.cs +++ b/MareSynchronos/FileCache/TransientResourceManager.cs @@ -24,30 +24,6 @@ public sealed class TransientResourceManager : DisposableMediatorSubscriberBase _configurationService = configurationService; _dalamudUtil = dalamudUtil; - PlayerPersistentDataKey = _dalamudUtil.GetPlayerNameAsync().GetAwaiter().GetResult() + "_" + _dalamudUtil.GetWorldIdAsync().GetAwaiter().GetResult(); - - SemiTransientResources.TryAdd(ObjectKind.Player, new HashSet(StringComparer.Ordinal)); - if (_configurationService.Current.PlayerPersistentTransientCache.TryGetValue(PlayerPersistentDataKey, out var gamePaths)) - { - int restored = 0; - foreach (var gamePath in gamePaths) - { - if (string.IsNullOrEmpty(gamePath)) continue; - - try - { - Logger.LogDebug("Loaded persistent transient resource {path}", gamePath); - SemiTransientResources[ObjectKind.Player].Add(gamePath); - restored++; - } - catch (Exception ex) - { - Logger.LogWarning(ex, "Error during loading persistent transient resource {path}", gamePath); - } - } - Logger.LogDebug("Restored {restored}/{total} semi persistent resources", restored, gamePaths.Count); - } - Mediator.Subscribe(this, Manager_PenumbraResourceLoadEvent); Mediator.Subscribe(this, (_) => Manager_PenumbraModSettingChanged()); Mediator.Subscribe(this, (_) => DalamudUtil_FrameworkUpdate()); @@ -68,8 +44,41 @@ public sealed class TransientResourceManager : DisposableMediatorSubscriberBase }); } - private string PlayerPersistentDataKey { get; } - private ConcurrentDictionary> SemiTransientResources { get; } = new(); + private string PlayerPersistentDataKey => _dalamudUtil.GetPlayerNameAsync().GetAwaiter().GetResult() + "_" + _dalamudUtil.GetHomeWorldIdAsync().GetAwaiter().GetResult(); + private ConcurrentDictionary>? _semiTransientResources = null; + private ConcurrentDictionary> SemiTransientResources + { + get + { + if (_semiTransientResources == null) + { + _semiTransientResources = new(); + _semiTransientResources.TryAdd(ObjectKind.Player, new HashSet(StringComparer.Ordinal)); + if (_configurationService.Current.PlayerPersistentTransientCache.TryGetValue(PlayerPersistentDataKey, out var gamePaths)) + { + int restored = 0; + foreach (var gamePath in gamePaths) + { + if (string.IsNullOrEmpty(gamePath)) continue; + + try + { + Logger.LogDebug("Loaded persistent transient resource {path}", gamePath); + SemiTransientResources[ObjectKind.Player].Add(gamePath); + restored++; + } + catch (Exception ex) + { + Logger.LogWarning(ex, "Error during loading persistent transient resource {path}", gamePath); + } + } + Logger.LogDebug("Restored {restored}/{total} semi persistent resources", restored, gamePaths.Count); + } + } + + return _semiTransientResources; + } + } private ConcurrentDictionary> TransientResources { get; } = new(); public void CleanUpSemiTransientResources(ObjectKind objectKind, List? fileReplacement = null) diff --git a/MareSynchronos/MareConfiguration/Configurations/MareConfig.cs b/MareSynchronos/MareConfiguration/Configurations/MareConfig.cs index 1099395..8392327 100644 --- a/MareSynchronos/MareConfiguration/Configurations/MareConfig.cs +++ b/MareSynchronos/MareConfiguration/Configurations/MareConfig.cs @@ -52,5 +52,4 @@ public class MareConfig : IMareConfiguration public bool UseCompactor { get; set; } = false; public int Version { get; set; } = 1; public NotificationLocation WarningNotification { get; set; } = NotificationLocation.Both; - public bool UseLessRedraws { get; set; } = false; } \ No newline at end of file diff --git a/MareSynchronos/PlayerData/Handlers/PairHandler.cs b/MareSynchronos/PlayerData/Handlers/PairHandler.cs index c55d640..21593d4 100644 --- a/MareSynchronos/PlayerData/Handlers/PairHandler.cs +++ b/MareSynchronos/PlayerData/Handlers/PairHandler.cs @@ -77,7 +77,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase }); Mediator.Subscribe(this, (msg) => { - if (_mareConfigService.Current.UseLessRedraws && msg.gameObjectHandler == _charaHandler) + if (msg.gameObjectHandler == _charaHandler) { _redrawOnNextApplication = true; } @@ -262,7 +262,6 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase Logger.LogDebug("[{applicationId}] Applying Customization Data for {handler}", applicationId, handler); await _dalamudUtil.WaitWhileCharacterIsDrawing(Logger, handler, applicationId, 30000, token).ConfigureAwait(false); token.ThrowIfCancellationRequested(); - if (!_mareConfigService.Current.UseLessRedraws) changes.Value.Remove(PlayerChanges.ForcedRedraw); foreach (var change in changes.Value.OrderBy(p => (int)p)) { Logger.LogDebug("[{applicationId}] Processing {change} for {handler}", applicationId, change, handler); @@ -307,11 +306,6 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase } token.ThrowIfCancellationRequested(); } - - if (!_mareConfigService.Current.UseLessRedraws && (changes.Value.Contains(PlayerChanges.ModFiles) || changes.Value.Contains(PlayerChanges.ModManip) || changes.Value.Contains(PlayerChanges.Glamourer))) - { - await _ipcManager.PenumbraRedrawAsync(Logger, handler, applicationId, token).ConfigureAwait(false); - } } finally { diff --git a/MareSynchronos/Services/DalamudUtilService.cs b/MareSynchronos/Services/DalamudUtilService.cs index 724cfad..0306e1c 100644 --- a/MareSynchronos/Services/DalamudUtilService.cs +++ b/MareSynchronos/Services/DalamudUtilService.cs @@ -173,7 +173,7 @@ public class DalamudUtilService : IHostedService public async Task GetPlayerNameHashedAsync() { - return await RunOnFrameworkThread(() => (GetPlayerName() + GetWorldId()).GetHash256()).ConfigureAwait(false); + return await RunOnFrameworkThread(() => (GetPlayerName() + GetHomeWorldId()).GetHash256()).ConfigureAwait(false); } public IntPtr GetPlayerPointer() @@ -187,17 +187,28 @@ public class DalamudUtilService : IHostedService return await RunOnFrameworkThread(GetPlayerPointer).ConfigureAwait(false); } - public uint GetWorldId() + public uint GetHomeWorldId() { EnsureIsOnFramework(); return _clientState.LocalPlayer!.HomeWorld.Id; } + public uint GetWorldId() + { + EnsureIsOnFramework(); + return _clientState.LocalPlayer!.CurrentWorld.Id; + } + public async Task GetWorldIdAsync() { return await RunOnFrameworkThread(GetWorldId).ConfigureAwait(false); } + public async Task GetHomeWorldIdAsync() + { + return await RunOnFrameworkThread(GetHomeWorldId).ConfigureAwait(false); + } + public unsafe bool IsGameObjectPresent(IntPtr key) { return _objectTable.Any(f => f.Address == key); diff --git a/MareSynchronos/Services/ServerConfiguration/ServerConfigurationManager.cs b/MareSynchronos/Services/ServerConfiguration/ServerConfigurationManager.cs index f89c6a8..b7c717d 100644 --- a/MareSynchronos/Services/ServerConfiguration/ServerConfigurationManager.cs +++ b/MareSynchronos/Services/ServerConfiguration/ServerConfigurationManager.cs @@ -62,7 +62,7 @@ public class ServerConfigurationManager } var charaName = _dalamudUtil.GetPlayerNameAsync().GetAwaiter().GetResult(); - var worldId = _dalamudUtil.GetWorldIdAsync().GetAwaiter().GetResult(); + var worldId = _dalamudUtil.GetHomeWorldIdAsync().GetAwaiter().GetResult(); if (!currentServer.Authentications.Any() && currentServer.SecretKeys.Any()) { currentServer.Authentications.Add(new Authentication() @@ -136,7 +136,7 @@ public class ServerConfigurationManager server.Authentications.Add(new Authentication() { CharacterName = _dalamudUtil.GetPlayerNameAsync().GetAwaiter().GetResult(), - WorldId = _dalamudUtil.GetWorldIdAsync().GetAwaiter().GetResult(), + WorldId = _dalamudUtil.GetHomeWorldIdAsync().GetAwaiter().GetResult(), SecretKeyIdx = addLastSecretKey ? server.SecretKeys.Last().Key : -1, }); Save(); diff --git a/MareSynchronos/UI/SettingsUi.cs b/MareSynchronos/UI/SettingsUi.cs index 1d7cc0b..b238ced 100644 --- a/MareSynchronos/UI/SettingsUi.cs +++ b/MareSynchronos/UI/SettingsUi.cs @@ -549,18 +549,6 @@ public class SettingsUi : WindowMediatorSubscriberBase } _lastTab = "General"; - UiSharedService.FontText("General Settings", _uiShared.UidFont); - bool lessRedraws = _configService.Current.UseLessRedraws; - if (ImGui.Checkbox("[Experimental] Use fewer redraws", ref lessRedraws)) - { - _configService.Current.UseLessRedraws = lessRedraws; - _configService.Save(); - } - UiSharedService.DrawHelpText("This will attempt to use fewer redraws. Changes that solely affect the players body appearance (i.e. clothes) should not cause a redraw anymore." + Environment.NewLine + - "Some changes, especially to hair, face, tail or any vfx, animation or skeleton changes, or class changes will still force a redraw." + Environment.NewLine + Environment.NewLine + - "WARNING: this is an experimental, little tested feature and can potentially lead to issues with animation state or crashes. Use at your own risk."); - ImGui.Separator(); - UiSharedService.FontText("Notes", _uiShared.UidFont); if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.StickyNote, "Export all your user notes to clipboard")) { diff --git a/MareSynchronos/UI/UISharedService.cs b/MareSynchronos/UI/UISharedService.cs index 5fea4db..ac32007 100644 --- a/MareSynchronos/UI/UISharedService.cs +++ b/MareSynchronos/UI/UISharedService.cs @@ -125,7 +125,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase public Dictionary WorldData => _dalamudUtil.WorldData.Value; - public uint WorldId => _dalamudUtil.GetWorldId(); + public uint WorldId => _dalamudUtil.GetHomeWorldId(); public const string TooltipSeparator = "--SEP--"; diff --git a/MareSynchronos/WebAPI/Files/FileUploadManager.cs b/MareSynchronos/WebAPI/Files/FileUploadManager.cs index 15e1935..171c11f 100644 --- a/MareSynchronos/WebAPI/Files/FileUploadManager.cs +++ b/MareSynchronos/WebAPI/Files/FileUploadManager.cs @@ -198,7 +198,7 @@ public sealed class FileUploadManager : DisposableMediatorSubscriberBase Logger.LogDebug("Verifying {count} files", unverifiedUploadHashes.Count); var filesToUpload = await FilesSend([.. unverifiedUploadHashes], visiblePlayers.Select(p => p.UID).ToList(), uploadToken).ConfigureAwait(false); - foreach (var file in filesToUpload.Where(f => !f.IsForbidden)) + foreach (var file in filesToUpload.Where(f => !f.IsForbidden).DistinctBy(f => f.Hash)) { try { From e9775a367758e01b7ff1add4420791f6d6ddb796 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Fri, 17 Nov 2023 02:06:17 +0100 Subject: [PATCH 54/56] census update --- .../Configurations/ServerConfig.cs | 2 ++ .../PlayerData/Handlers/GameObjectHandler.cs | 17 +++++++++++++++++ MareSynchronos/Services/Mediator/Messages.cs | 1 + .../ServerConfigurationManager.cs | 12 ++++++++++++ MareSynchronos/UI/SettingsUi.cs | 17 ++++++++++++++++- .../SignalR/ApIController.Functions.Users.cs | 15 ++++++++++++--- MareSynchronos/WebAPI/SignalR/ApiController.cs | 13 ++++++++++++- 7 files changed, 72 insertions(+), 5 deletions(-) diff --git a/MareSynchronos/MareConfiguration/Configurations/ServerConfig.cs b/MareSynchronos/MareConfiguration/Configurations/ServerConfig.cs index 103eb20..65e50a6 100644 --- a/MareSynchronos/MareConfiguration/Configurations/ServerConfig.cs +++ b/MareSynchronos/MareConfiguration/Configurations/ServerConfig.cs @@ -13,5 +13,7 @@ public class ServerConfig : IMareConfiguration { new ServerStorage() { ServerName = ApiController.MainServer, ServerUri = ApiController.MainServiceUri } }, }; + public bool SendCensusData { get; set; } = true; + public int Version { get; set; } = 1; } \ No newline at end of file diff --git a/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs b/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs index c30a915..ee2e71f 100644 --- a/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs +++ b/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs @@ -99,6 +99,10 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase ModelFilesInSlotLoaded } + public byte RaceId { get; private set; } + public byte Gender { get; private set; } + public byte TribeId { get; private set; } + public IntPtr Address { get; private set; } public string Name { get; private set; } public ObjectKind ObjectKind { get; } @@ -253,6 +257,19 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase if (((DrawObject*)DrawObjectAddress)->Object.GetObjectType() == ObjectType.CharacterBase && ((CharacterBase*)DrawObjectAddress)->GetModelType() == CharacterBase.ModelType.Human) { + var gender = ((Human*)DrawObjectAddress)->Customize.Sex; + var raceId = ((Human*)DrawObjectAddress)->Customize.Race; + var tribeId = ((Human*)DrawObjectAddress)->Customize.Clan; + + if (_isOwnedObject && ObjectKind == ObjectKind.Player + && (gender != Gender || raceId != RaceId || tribeId != TribeId)) + { + Mediator.Publish(new CensusUpdateMessage(gender, raceId, tribeId)); + Gender = gender; + RaceId = raceId; + TribeId = tribeId; + } + customizeDiff = CompareAndUpdateCustomizeData(((Human*)DrawObjectAddress)->Customize.Data); if (customizeDiff) Logger.LogTrace("Checking [{this}] customize data as human from draw obj, result: {diff}", this, customizeDiff); diff --git a/MareSynchronos/Services/Mediator/Messages.cs b/MareSynchronos/Services/Mediator/Messages.cs index c839d29..82c7966 100644 --- a/MareSynchronos/Services/Mediator/Messages.cs +++ b/MareSynchronos/Services/Mediator/Messages.cs @@ -75,6 +75,7 @@ public record OpenBanUserPopupMessage(Pair PairToBan, GroupFullInfoDto GroupFull public record OpenSyncshellAdminPanel(GroupFullInfoDto GroupInfo) : MessageBase; public record OpenPermissionWindow(Pair Pair) : MessageBase; public record DownloadLimitChangedMessage() : SameThreadMessage; +public record CensusUpdateMessage(byte Gender, byte RaceId, byte TribeId) : MessageBase; #pragma warning restore S2094 #pragma warning restore MA0048 // File name must match type name \ No newline at end of file diff --git a/MareSynchronos/Services/ServerConfiguration/ServerConfigurationManager.cs b/MareSynchronos/Services/ServerConfiguration/ServerConfigurationManager.cs index b7c717d..94c9983 100644 --- a/MareSynchronos/Services/ServerConfiguration/ServerConfigurationManager.cs +++ b/MareSynchronos/Services/ServerConfiguration/ServerConfigurationManager.cs @@ -31,6 +31,18 @@ public class ServerConfigurationManager public string CurrentApiUrl => CurrentServer.ServerUri; public ServerStorage CurrentServer => _configService.Current.ServerStorage[CurrentServerIndex]; + public bool SendCensusData + { + get + { + return _configService.Current.SendCensusData; + } + set + { + _configService.Current.SendCensusData = value; + _configService.Save(); + } + } public int CurrentServerIndex { diff --git a/MareSynchronos/UI/SettingsUi.cs b/MareSynchronos/UI/SettingsUi.cs index b238ced..ce0a22b 100644 --- a/MareSynchronos/UI/SettingsUi.cs +++ b/MareSynchronos/UI/SettingsUi.cs @@ -806,7 +806,7 @@ public class SettingsUi : WindowMediatorSubscriberBase if (ApiController.ServerAlive) { UiSharedService.FontText("Service Actions", _uiShared.UidFont); - + ImGuiHelpers.ScaledDummy(new Vector2(5, 5)); if (ImGui.Button("Delete all my files")) { _deleteFilesPopupModalShown = true; @@ -884,6 +884,21 @@ public class SettingsUi : WindowMediatorSubscriberBase } UiSharedService.FontText("Service & Character Settings", _uiShared.UidFont); + ImGuiHelpers.ScaledDummy(new Vector2(5, 5)); + var sendCensus = _serverConfigurationManager.SendCensusData; + if (ImGui.Checkbox("Send Statistical Census Data", ref sendCensus)) + { + _serverConfigurationManager.SendCensusData = sendCensus; + } + UiSharedService.DrawHelpText("This will allow sending census data to the currently connected service." + UiSharedService.TooltipSeparator + + "Census data contains:" + Environment.NewLine + + "- Current World" + Environment.NewLine + + "- Current Gender" + Environment.NewLine + + "- Current Race" + Environment.NewLine + + "- Current Clan (this is not your Free Company, this is e.g. Keeper or Seeker for Miqo'te)" + UiSharedService.TooltipSeparator + + "The census data is only saved temporarily and will be removed from the server on disconnect. It is stored temporarily associated with your UID while you are connected." + UiSharedService.TooltipSeparator + + "If you do not wish to participate in the statistical census, untick this box and reconnect to the server."); + ImGuiHelpers.ScaledDummy(new Vector2(10, 10)); var idx = _uiShared.DrawServiceSelection(); diff --git a/MareSynchronos/WebAPI/SignalR/ApIController.Functions.Users.cs b/MareSynchronos/WebAPI/SignalR/ApIController.Functions.Users.cs index a552834..819c69f 100644 --- a/MareSynchronos/WebAPI/SignalR/ApIController.Functions.Users.cs +++ b/MareSynchronos/WebAPI/SignalR/ApIController.Functions.Users.cs @@ -41,9 +41,9 @@ public partial class ApiController await CreateConnections().ConfigureAwait(false); } - public async Task> UserGetOnlinePairs() + public async Task> UserGetOnlinePairs(CensusDataDto? censusDataDto) { - return await _mareHub!.InvokeAsync>(nameof(UserGetOnlinePairs)).ConfigureAwait(false); + return await _mareHub!.InvokeAsync>(nameof(UserGetOnlinePairs), censusDataDto).ConfigureAwait(false); } public async Task> UserGetPairedClients() @@ -128,7 +128,16 @@ public partial class ApiController sb.AppendLine($"GlamourerData for {item.Key}: {!string.IsNullOrEmpty(item.Value)}"); } Logger.LogDebug("Chara data contained: {nl} {data}", Environment.NewLine, sb.ToString()); - await UserPushData(new(visibleCharacters, character)).ConfigureAwait(false); + + CensusDataDto? censusDto = null; + if (_serverManager.SendCensusData && _lastCensus != null) + { + var world = await _dalamudUtil.GetWorldIdAsync().ConfigureAwait(false); + censusDto = new((ushort)world, _lastCensus.RaceId, _lastCensus.TribeId, _lastCensus.Gender); + Logger.LogDebug("Attaching Census Data: {data}", censusDto); + } + + await UserPushData(new(visibleCharacters, character, censusDto)).ConfigureAwait(false); } } #pragma warning restore MA0040 \ No newline at end of file diff --git a/MareSynchronos/WebAPI/SignalR/ApiController.cs b/MareSynchronos/WebAPI/SignalR/ApiController.cs index a0df76e..3ff9f9f 100644 --- a/MareSynchronos/WebAPI/SignalR/ApiController.cs +++ b/MareSynchronos/WebAPI/SignalR/ApiController.cs @@ -2,6 +2,7 @@ using MareSynchronos.API.Data; using MareSynchronos.API.Data.Extensions; using MareSynchronos.API.Dto; +using MareSynchronos.API.Dto.User; using MareSynchronos.API.SignalR; using MareSynchronos.PlayerData.Pairs; using MareSynchronos.Services; @@ -34,6 +35,7 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM private string? _lastUsedToken; private HubConnection? _mareHub; private ServerState _serverState; + private CensusUpdateMessage? _lastCensus; public ApiController(ILogger logger, HubFactory hubFactory, DalamudUtilService dalamudUtil, PairManager pairManager, ServerConfigurationManager serverManager, MareMediator mediator, @@ -52,6 +54,7 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM Mediator.Subscribe(this, async (msg) => await MareHubOnReconnected().ConfigureAwait(false)); Mediator.Subscribe(this, (msg) => MareHubOnReconnecting(msg.Exception)); Mediator.Subscribe(this, (msg) => _ = CyclePause(msg.UserData)); + Mediator.Subscribe(this, (msg) => _lastCensus = msg); ServerState = ServerState.Offline; @@ -348,7 +351,15 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM private async Task LoadOnlinePairs() { - foreach (var entry in await UserGetOnlinePairs().ConfigureAwait(false)) + CensusDataDto? dto = null; + if (_serverManager.SendCensusData && _lastCensus != null) + { + var world = await _dalamudUtil.GetWorldIdAsync().ConfigureAwait(false); + dto = new((ushort)world, _lastCensus.RaceId, _lastCensus.TribeId, _lastCensus.Gender); + Logger.LogDebug("Attaching Census Data: {data}", dto); + } + + foreach (var entry in await UserGetOnlinePairs(dto).ConfigureAwait(false)) { Logger.LogDebug("Pair online: {pair}", entry); _pairManager.MarkPairOnline(entry, sendNotif: false); From 05f1c66a5d4df22d77b3cc4bfa0dc92184063893 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Fri, 17 Nov 2023 02:06:27 +0100 Subject: [PATCH 55/56] api update --- MareAPI | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MareAPI b/MareAPI index edaa0dd..47ff523 160000 --- a/MareAPI +++ b/MareAPI @@ -1 +1 @@ -Subproject commit edaa0ddb5123b2eb78d694b844bb7ad6cc13192d +Subproject commit 47ff5235d697eda92eac1171d29c7a8ba25e7841 From a32aef8ee89df3688f232019c1ee99c126df09d9 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Fri, 17 Nov 2023 11:14:33 +0100 Subject: [PATCH 56/56] add census popup on connection --- .../Configurations/ServerConfig.cs | 1 + MareSynchronos/Plugin.cs | 1 + MareSynchronos/Services/Mediator/Messages.cs | 1 + .../ServerConfigurationManager.cs | 13 +++++ .../Components/Popup/BanUserPopupHandler.cs | 4 ++ .../UI/Components/Popup/CensusPopupHandler.cs | 50 +++++++++++++++++++ .../UI/Components/Popup/IPopupHandler.cs | 2 + .../UI/Components/Popup/PopupHandler.cs | 14 ++++++ .../UI/Components/Popup/ReportPopupHandler.cs | 4 ++ .../WebAPI/SignalR/ApiController.cs | 9 ++++ 10 files changed, 99 insertions(+) create mode 100644 MareSynchronos/UI/Components/Popup/CensusPopupHandler.cs diff --git a/MareSynchronos/MareConfiguration/Configurations/ServerConfig.cs b/MareSynchronos/MareConfiguration/Configurations/ServerConfig.cs index 65e50a6..116c33b 100644 --- a/MareSynchronos/MareConfiguration/Configurations/ServerConfig.cs +++ b/MareSynchronos/MareConfiguration/Configurations/ServerConfig.cs @@ -14,6 +14,7 @@ public class ServerConfig : IMareConfiguration }; public bool SendCensusData { get; set; } = true; + public bool ShownCensusPopup { get; set; } = false; public int Version { get; set; } = 1; } \ No newline at end of file diff --git a/MareSynchronos/Plugin.cs b/MareSynchronos/Plugin.cs index 477b5bd..dd40218 100644 --- a/MareSynchronos/Plugin.cs +++ b/MareSynchronos/Plugin.cs @@ -111,6 +111,7 @@ public sealed class Plugin : IDalamudPlugin collection.AddScoped(); collection.AddScoped(); collection.AddScoped(); + collection.AddScoped(); collection.AddScoped(); collection.AddScoped(); collection.AddScoped(); diff --git a/MareSynchronos/Services/Mediator/Messages.cs b/MareSynchronos/Services/Mediator/Messages.cs index 82c7966..3d2f6f4 100644 --- a/MareSynchronos/Services/Mediator/Messages.cs +++ b/MareSynchronos/Services/Mediator/Messages.cs @@ -72,6 +72,7 @@ public record PairHandlerVisibleMessage(PairHandler Player) : MessageBase; public record RefreshUiMessage : MessageBase; public record OpenReportPopupMessage(Pair PairToReport) : MessageBase; public record OpenBanUserPopupMessage(Pair PairToBan, GroupFullInfoDto GroupFullInfoDto) : MessageBase; +public record OpenCensusPopupMessage() : MessageBase; public record OpenSyncshellAdminPanel(GroupFullInfoDto GroupInfo) : MessageBase; public record OpenPermissionWindow(Pair Pair) : MessageBase; public record DownloadLimitChangedMessage() : SameThreadMessage; diff --git a/MareSynchronos/Services/ServerConfiguration/ServerConfigurationManager.cs b/MareSynchronos/Services/ServerConfiguration/ServerConfigurationManager.cs index 94c9983..8bfd106 100644 --- a/MareSynchronos/Services/ServerConfiguration/ServerConfigurationManager.cs +++ b/MareSynchronos/Services/ServerConfiguration/ServerConfigurationManager.cs @@ -44,6 +44,19 @@ public class ServerConfigurationManager } } + public bool ShownCensusPopup + { + get + { + return _configService.Current.ShownCensusPopup; + } + set + { + _configService.Current.ShownCensusPopup = value; + _configService.Save(); + } + } + public int CurrentServerIndex { set diff --git a/MareSynchronos/UI/Components/Popup/BanUserPopupHandler.cs b/MareSynchronos/UI/Components/Popup/BanUserPopupHandler.cs index aac754b..8a2f69f 100644 --- a/MareSynchronos/UI/Components/Popup/BanUserPopupHandler.cs +++ b/MareSynchronos/UI/Components/Popup/BanUserPopupHandler.cs @@ -37,6 +37,10 @@ public class BanUserPopupHandler : IPopupHandler UiSharedService.TextWrapped("The reason will be displayed in the banlist. The current server-side alias if present (Vanity ID) will automatically be attached to the reason."); } + public void OnClose() + { + } + public void Open(OpenBanUserPopupMessage message) { _reportedPair = message.PairToBan; diff --git a/MareSynchronos/UI/Components/Popup/CensusPopupHandler.cs b/MareSynchronos/UI/Components/Popup/CensusPopupHandler.cs new file mode 100644 index 0000000..de95e33 --- /dev/null +++ b/MareSynchronos/UI/Components/Popup/CensusPopupHandler.cs @@ -0,0 +1,50 @@ +using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; +using ImGuiNET; +using MareSynchronos.Services.ServerConfiguration; +using System.Numerics; + +namespace MareSynchronos.UI.Components.Popup; + +public class CensusPopupHandler : IPopupHandler +{ + private readonly ServerConfigurationManager _serverConfigurationManager; + private readonly UiSharedService _uiSharedService; + + public CensusPopupHandler(ServerConfigurationManager serverConfigurationManager, UiSharedService uiSharedService) + { + _serverConfigurationManager = serverConfigurationManager; + _uiSharedService = uiSharedService; + } + + public Vector2 PopupSize => new(600, 350); + + public void DrawContent() + { + using (ImRaii.PushFont(_uiSharedService.UidFont)) + UiSharedService.TextWrapped("Mare Census Opt-Out"); + ImGuiHelpers.ScaledDummy(5, 5); + UiSharedService.TextWrapped("If you are seeing this popup you are updating from a Mare version that did not collect census data. Please read the following carefully."); + ImGui.Separator(); + UiSharedService.TextWrapped("Mare Census is a data collecting service that can be used for statistical purposes. " + + "All data collected through Mare Census is temporary and will be stored associated with your UID on the connected service as long as you are connected. " + + "The data cannot be used for long term tracking of individuals."); + UiSharedService.TextWrapped("If enabled, Mare Census will collect following data:" + Environment.NewLine + + "- Currently connected World" + Environment.NewLine + + "- Current Gender (reflecting Glamourer changes)" + Environment.NewLine + + "- Current Race (reflecting Glamourer changes)" + Environment.NewLine + + "- Current Clan (i.e. Seeker of the Sun, Keeper of the Moon, etc., reflecting Glamourer changes)"); + UiSharedService.TextWrapped("If you do not consent to the data mentioned above being sent, untick the checkbox below."); + UiSharedService.TextWrapped("This setting can be changed anytime in the Mare Settings."); + var sendCensus = _serverConfigurationManager.SendCensusData; + if (ImGui.Checkbox("Allow sending census data", ref sendCensus)) + { + _serverConfigurationManager.SendCensusData = sendCensus; + } + } + + public void OnClose() + { + _serverConfigurationManager.ShownCensusPopup = true; + } +} diff --git a/MareSynchronos/UI/Components/Popup/IPopupHandler.cs b/MareSynchronos/UI/Components/Popup/IPopupHandler.cs index c29d64e..aa649b4 100644 --- a/MareSynchronos/UI/Components/Popup/IPopupHandler.cs +++ b/MareSynchronos/UI/Components/Popup/IPopupHandler.cs @@ -7,4 +7,6 @@ public interface IPopupHandler Vector2 PopupSize { get; } void DrawContent(); + + void OnClose(); } \ No newline at end of file diff --git a/MareSynchronos/UI/Components/Popup/PopupHandler.cs b/MareSynchronos/UI/Components/Popup/PopupHandler.cs index 4b5a014..055ee91 100644 --- a/MareSynchronos/UI/Components/Popup/PopupHandler.cs +++ b/MareSynchronos/UI/Components/Popup/PopupHandler.cs @@ -44,6 +44,13 @@ public class PopupHandler : WindowMediatorSubscriberBase ((BanUserPopupHandler)_currentHandler).Open(msg); IsOpen = true; }); + + Mediator.Subscribe(this, (msg) => + { + _openPopup = true; + _currentHandler = _handlers.OfType().Single(); + IsOpen = true; + }); } public override void Draw() @@ -66,6 +73,13 @@ public class PopupHandler : WindowMediatorSubscriberBase if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Times, "Close")) { ImGui.CloseCurrentPopup(); + _currentHandler.OnClose(); } } + + public override void OnClose() + { + base.OnClose(); + _currentHandler?.OnClose(); + } } \ No newline at end of file diff --git a/MareSynchronos/UI/Components/Popup/ReportPopupHandler.cs b/MareSynchronos/UI/Components/Popup/ReportPopupHandler.cs index 36352cc..3f58dd2 100644 --- a/MareSynchronos/UI/Components/Popup/ReportPopupHandler.cs +++ b/MareSynchronos/UI/Components/Popup/ReportPopupHandler.cs @@ -49,6 +49,10 @@ internal class ReportPopupHandler : IPopupHandler } } + public void OnClose() + { + } + public void Open(OpenReportPopupMessage msg) { _reportedPair = msg.PairToReport; diff --git a/MareSynchronos/WebAPI/SignalR/ApiController.cs b/MareSynchronos/WebAPI/SignalR/ApiController.cs index 3ff9f9f..64f4187 100644 --- a/MareSynchronos/WebAPI/SignalR/ApiController.cs +++ b/MareSynchronos/WebAPI/SignalR/ApiController.cs @@ -102,6 +102,15 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM public async Task CreateConnections() { + if (!_serverManager.ShownCensusPopup) + { + Mediator.Publish(new OpenCensusPopupMessage()); + while (!_serverManager.ShownCensusPopup) + { + await Task.Delay(500).ConfigureAwait(false); + } + } + Logger.LogDebug("CreateConnections called"); if (_serverManager.CurrentServer?.FullPause ?? true)