From 4a388dcfa97a37f610d7da575959716b7fcc24c3 Mon Sep 17 00:00:00 2001 From: SirConstance Date: Sat, 13 Sep 2025 22:42:08 +0200 Subject: [PATCH] Update 0.1.6 - UI change --- MareSynchronos/MareSynchronos.csproj | 2 +- MareSynchronos/Services/Mediator/Messages.cs | 2 +- MareSynchronos/UI/CompactUI.cs | 59 +++++++++++++++++-- MareSynchronos/UI/Components/DrawGroupPair.cs | 43 ++++++++++---- MareSynchronos/UI/Components/DrawUserPair.cs | 54 +++++++++-------- MareSynchronos/UI/Components/GroupPanel.cs | 4 +- MareSynchronos/UI/Components/PairGroupsUi.cs | 2 +- MareSynchronos/UI/SettingsUi.cs | 10 ++-- MareSynchronos/UI/UISharedService.cs | 6 +- 9 files changed, 129 insertions(+), 53 deletions(-) diff --git a/MareSynchronos/MareSynchronos.csproj b/MareSynchronos/MareSynchronos.csproj index 4617480..1ad3913 100644 --- a/MareSynchronos/MareSynchronos.csproj +++ b/MareSynchronos/MareSynchronos.csproj @@ -3,7 +3,7 @@ UmbraSync UmbraSync - 0.1.6.3 + 0.1.6.4 diff --git a/MareSynchronos/Services/Mediator/Messages.cs b/MareSynchronos/Services/Mediator/Messages.cs index b6302c1..1bbfd71 100644 --- a/MareSynchronos/Services/Mediator/Messages.cs +++ b/MareSynchronos/Services/Mediator/Messages.cs @@ -108,7 +108,7 @@ public record GPoseLobbyReceiveCharaData(CharaDataDownloadDto CharaDataDownloadD public record GPoseLobbyReceivePoseData(UserData UserData, PoseData PoseData) : MessageBase; public record GPoseLobbyReceiveWorldData(UserData UserData, WorldData WorldData) : MessageBase; -public record NearbyEntry(string Name, ushort WorldId, float Distance, bool IsMatch, string? Token, string? DisplayName, string? Uid); +public record NearbyEntry(string Name, ushort WorldId, float Distance, bool IsMatch, string? Token, string? DisplayName, string? Uid, bool AcceptPairRequests = true); public record DiscoveryListUpdated(List Entries) : MessageBase; public record NearbyDetectionToggled(bool Enabled) : MessageBase; public record AllowPairRequestsToggled(bool Enabled) : MessageBase; diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs index 84b7005..0e457d2 100644 --- a/MareSynchronos/UI/CompactUI.cs +++ b/MareSynchronos/UI/CompactUI.cs @@ -377,7 +377,6 @@ public class CompactUi : WindowMediatorSubscriberBase // Always show a Nearby group when detection is enabled, even if empty if (_configService.Current.EnableAutoDetectDiscovery) { - ImGui.Separator(); using (ImRaii.PushId("group-Nearby")) { var icon = _nearbyOpen ? FontAwesomeIcon.CaretSquareDown : FontAwesomeIcon.CaretSquareRight; @@ -401,14 +400,61 @@ public class CompactUi : WindowMediatorSubscriberBase if (_nearbyOpen) { ImGui.Indent(); - if (onUmbra == 0) + var nearby = _nearbyEntries == null + ? new List() + : _nearbyEntries.Where(e => e.IsMatch) + .OrderBy(e => e.Distance) + .ToList(); + if (nearby.Count == 0) { UiSharedService.ColorTextWrapped("No nearby players detected.", ImGuiColors.DalamudGrey3); } else { - UiSharedService.ColorTextWrapped("Open Nearby for details.", ImGuiColors.DalamudGrey3); + foreach (var e in nearby) + { + // name + var name = e.DisplayName ?? e.Name; + ImGui.AlignTextToFramePadding(); + ImGui.TextUnformatted(name); + + // right side status/action + var right = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth(); + ImGui.SameLine(); + + // detect if already paired (prefer UID if present) + bool isPaired = false; + try + { + isPaired = _pairManager.DirectPairs.Any(p => string.Equals(p.UserData.UID, e.Uid, StringComparison.Ordinal)); + } + catch + { + // fall back to display name/alias matching if UID is not available + var key = (e.DisplayName ?? e.Name) ?? string.Empty; + isPaired = _pairManager.DirectPairs.Any(p => string.Equals(p.UserData.AliasOrUID, key, StringComparison.OrdinalIgnoreCase)); + } + + var statusText = isPaired ? "✔ Paired" : (e.AcceptPairRequests ? "➕ Invite" : "⛔ Requests disabled"); + var statusSize = ImGui.CalcTextSize(statusText); + ImGui.SetCursorPosX(right - statusSize.X); + + if (isPaired || !e.AcceptPairRequests) + { + // just paint the status + ImGui.TextUnformatted(statusText); + } + else + { + // no direct send from Compact; open Nearby window to send the invite there + if (_uiSharedService.IconTextButton(FontAwesomeIcon.UserPlus, "Invite", statusSize.X)) + { + Mediator.Publish(new UiToggleMessage(typeof(AutoDetectUi))); + } + } + } } + // Pending Nearby requests (Accept / Dismiss) try { @@ -416,7 +462,7 @@ public class CompactUi : WindowMediatorSubscriberBase if (inbox != null && inbox.Pending.Count > 0) { ImGuiHelpers.ScaledDummy(6); - _uiSharedService.BigText("Requests"); + _uiSharedService.BigText("Incoming requests"); foreach (var kv in inbox.Pending) { ImGui.AlignTextToFramePadding(); @@ -483,8 +529,9 @@ public class CompactUi : WindowMediatorSubscriberBase { ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ((userSize.Y + textSize.Y) / 2 + shardTextSize.Y) / 2 - ImGui.GetStyle().ItemSpacing.Y + buttonSize.Y / 2); } - var color = UiSharedService.GetBoolColor(!_serverManager.CurrentServer!.FullPause); - var connectedIcon = !_serverManager.CurrentServer.FullPause ? FontAwesomeIcon.Link : FontAwesomeIcon.Unlink; + var isLinked = !_serverManager.CurrentServer!.FullPause; + var color = isLinked ? new Vector4(0.63f, 0.25f, 1f, 1f) : UiSharedService.GetBoolColor(isLinked); + var connectedIcon = isLinked ? FontAwesomeIcon.Link : FontAwesomeIcon.Unlink; if (_apiController.ServerState is ServerState.Connected) { diff --git a/MareSynchronos/UI/Components/DrawGroupPair.cs b/MareSynchronos/UI/Components/DrawGroupPair.cs index 64db21f..acdd871 100644 --- a/MareSynchronos/UI/Components/DrawGroupPair.cs +++ b/MareSynchronos/UI/Components/DrawGroupPair.cs @@ -1,4 +1,5 @@ -using Dalamud.Bindings.ImGui; +using System.Numerics; +using Dalamud.Bindings.ImGui; using Dalamud.Interface; using Dalamud.Interface.Colors; using Dalamud.Interface.Utility; @@ -38,15 +39,19 @@ public class DrawGroupPair : DrawPairBase var entryIsMod = _fullInfoDto.GroupPairStatusInfo.IsModerator(); var entryIsOwner = string.Equals(_pair.UserData.UID, _group.OwnerUID, StringComparison.Ordinal); var entryIsPinned = _fullInfoDto.GroupPairStatusInfo.IsPinned(); - var presenceIcon = _pair.IsVisible ? FontAwesomeIcon.Eye : (_pair.IsOnline ? FontAwesomeIcon.Link : FontAwesomeIcon.Unlink); - var presenceColor = (_pair.IsOnline || _pair.IsVisible) ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed; + var presenceIcon = _pair.IsVisible ? FontAwesomeIcon.Eye : FontAwesomeIcon.CloudMoon; + var presenceColor = (_pair.IsOnline || _pair.IsVisible) ? new Vector4(0.63f, 0.25f, 1f, 1f) : ImGuiColors.DalamudGrey; var presenceText = entryUID + " is offline"; ImGui.SetCursorPosY(textPosY); + + // We'll draw an optional prefix icon (pause or paired-moon). If we draw one, + // we add a SameLine() before drawing the presence icon. Otherwise, we draw + // the presence icon directly at the current cursor position to avoid gaps. + bool drewPrefixIcon = false; + if (_pair.IsPaused) { - presenceIcon = FontAwesomeIcon.Question; - presenceColor = ImGuiColors.DalamudGrey; presenceText = entryUID + " online status is unknown (paused)"; ImGui.PushFont(UiBuilder.IconFont); @@ -54,24 +59,37 @@ public class DrawGroupPair : DrawPairBase ImGui.PopFont(); UiSharedService.AttachToolTip("Pairing status with " + entryUID + " is paused"); + drewPrefixIcon = true; } else { - ImGui.PushFont(UiBuilder.IconFont); - UiSharedService.ColorText(FontAwesomeIcon.Check.ToIconString(), ImGuiColors.ParsedGreen); - ImGui.PopFont(); + bool individuallyPaired = _pair.UserPair != null; + var violet = new Vector4(0.63f, 0.25f, 1f, 1f); - UiSharedService.AttachToolTip("You are paired with " + entryUID); + // Show a violet moon only when individually paired AND online/visible. + // If offline or not individually paired, do not draw a moon at all. + if (individuallyPaired && (_pair.IsOnline || _pair.IsVisible)) + { + ImGui.PushFont(UiBuilder.IconFont); + UiSharedService.ColorText(FontAwesomeIcon.Moon.ToIconString(), violet); + ImGui.PopFont(); + UiSharedService.AttachToolTip("You are individually paired with " + entryUID); + drewPrefixIcon = true; + } } - if (_pair.IsOnline && !_pair.IsVisible) presenceText = entryUID + " is online"; - else if (_pair.IsOnline && _pair.IsVisible) presenceText = entryUID + " is visible: " + _pair.PlayerName + Environment.NewLine + "Click to target this player"; + // Presence status icon (eye when visible, cloud-moon otherwise) + if (drewPrefixIcon) + ImGui.SameLine(); - ImGui.SameLine(); ImGui.SetCursorPosY(textPosY); ImGui.PushFont(UiBuilder.IconFont); UiSharedService.ColorText(presenceIcon.ToIconString(), presenceColor); ImGui.PopFont(); + + if (_pair.IsOnline && !_pair.IsVisible) presenceText = entryUID + " is online"; + else if (_pair.IsOnline && _pair.IsVisible) presenceText = entryUID + " is visible: " + _pair.PlayerName + Environment.NewLine + "Click to target this player"; + if (_pair.IsVisible) { if (ImGui.IsItemClicked()) @@ -94,6 +112,7 @@ public class DrawGroupPair : DrawPairBase } } } + UiSharedService.AttachToolTip(presenceText); if (entryIsOwner) diff --git a/MareSynchronos/UI/Components/DrawUserPair.cs b/MareSynchronos/UI/Components/DrawUserPair.cs index 23989b7..5131307 100644 --- a/MareSynchronos/UI/Components/DrawUserPair.cs +++ b/MareSynchronos/UI/Components/DrawUserPair.cs @@ -38,33 +38,41 @@ public class DrawUserPair : DrawPairBase protected override void DrawLeftSide(float textPosY, float originalY) { - FontAwesomeIcon connectionIcon; - Vector4 connectionColor; - string connectionText; - if (!(_pair.UserPair!.OwnPermissions.IsPaired() && _pair.UserPair!.OtherPermissions.IsPaired())) - { - connectionIcon = FontAwesomeIcon.ArrowUp; - connectionText = _pair.UserData.AliasOrUID + " has not added you back"; - connectionColor = ImGuiColors.DalamudRed; - } - else if (_pair.UserPair!.OwnPermissions.IsPaused() || _pair.UserPair!.OtherPermissions.IsPaused()) - { - connectionIcon = FontAwesomeIcon.PauseCircle; - connectionText = "Pairing status with " + _pair.UserData.AliasOrUID + " is paused"; - connectionColor = ImGuiColors.DalamudYellow; - } - else - { - connectionIcon = FontAwesomeIcon.Check; - connectionText = "You are paired with " + _pair.UserData.AliasOrUID; - connectionColor = ImGuiColors.ParsedGreen; - } + // Primary presence indicator: moon (online = violet, offline = grey) + var online = _pair.IsOnline; + // Violet accent (#BA70EF) + var violet = new Vector4(0.69f, 0.27f, 0.93f, 1f); + var offlineGrey = ImGuiColors.DalamudGrey3; ImGui.SetCursorPosY(textPosY); ImGui.PushFont(UiBuilder.IconFont); - UiSharedService.ColorText(connectionIcon.ToIconString(), connectionColor); + UiSharedService.ColorText(FontAwesomeIcon.Moon.ToIconString(), online ? violet : offlineGrey); ImGui.PopFont(); - UiSharedService.AttachToolTip(connectionText); + UiSharedService.AttachToolTip(online + ? "User is online" + : "User is offline"); + + // Secondary pairing state badges (only if needed) + // Not mutually paired -> red ArrowUp + if (!(_pair.UserPair!.OwnPermissions.IsPaired() && _pair.UserPair!.OtherPermissions.IsPaired())) + { + ImGui.SameLine(); + ImGui.SetCursorPosY(textPosY); + ImGui.PushFont(UiBuilder.IconFont); + UiSharedService.ColorText(FontAwesomeIcon.ArrowUp.ToIconString(), ImGuiColors.DalamudRed); + ImGui.PopFont(); + UiSharedService.AttachToolTip(_pair.UserData.AliasOrUID + " has not added you back"); + } + // Paused (either side) -> yellow PauseCircle + else if (_pair.UserPair!.OwnPermissions.IsPaused() || _pair.UserPair!.OtherPermissions.IsPaused()) + { + ImGui.SameLine(); + ImGui.SetCursorPosY(textPosY); + ImGui.PushFont(UiBuilder.IconFont); + UiSharedService.ColorText(FontAwesomeIcon.PauseCircle.ToIconString(), ImGuiColors.DalamudYellow); + ImGui.PopFont(); + UiSharedService.AttachToolTip("Pairing with " + _pair.UserData.AliasOrUID + " is paused"); + } if (_pair is { IsOnline: true, IsVisible: true }) { ImGui.SameLine(); diff --git a/MareSynchronos/UI/Components/GroupPanel.cs b/MareSynchronos/UI/Components/GroupPanel.cs index 067d591..0f19e8a 100644 --- a/MareSynchronos/UI/Components/GroupPanel.cs +++ b/MareSynchronos/UI/Components/GroupPanel.cs @@ -1,4 +1,4 @@ -using Dalamud.Bindings.ImGui; +using Dalamud.Bindings.ImGui; using Dalamud.Interface; using Dalamud.Interface.Colors; using Dalamud.Interface.Utility; @@ -464,7 +464,9 @@ internal sealed class GroupPanel if (offlineUsers.Count > 0) { + ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudGrey); ImGui.TextUnformatted("Offline/Unknown"); + ImGui.PopStyleColor(); ImGui.Separator(); if (hideOfflineUsers) { diff --git a/MareSynchronos/UI/Components/PairGroupsUi.cs b/MareSynchronos/UI/Components/PairGroupsUi.cs index 0778a02..6c8dd42 100644 --- a/MareSynchronos/UI/Components/PairGroupsUi.cs +++ b/MareSynchronos/UI/Components/PairGroupsUi.cs @@ -157,7 +157,7 @@ public class PairGroupsUi { TagHandler.CustomUnpairedTag => "Unpaired", TagHandler.CustomOfflineTag => "Offline", - TagHandler.CustomOnlineTag => _mareConfig.Current.ShowOfflineUsersSeparately ? "Online/Paused" : "Contacts", + TagHandler.CustomOnlineTag => _mareConfig.Current.ShowOfflineUsersSeparately ? "Online" : "Contacts", TagHandler.CustomVisibleTag => "Visible", _ => tag }; diff --git a/MareSynchronos/UI/SettingsUi.cs b/MareSynchronos/UI/SettingsUi.cs index 77168c5..76e4759 100644 --- a/MareSynchronos/UI/SettingsUi.cs +++ b/MareSynchronos/UI/SettingsUi.cs @@ -652,7 +652,7 @@ public class SettingsUi : WindowMediatorSubscriberBase ImGui.SameLine(300.0f * ImGuiHelpers.GlobalScale); if (_ipcProvider.ImpersonationActive) { - UiSharedService.ColorTextWrapped("Umbra API active!", ImGuiColors.HealerGreen); + UiSharedService.ColorTextWrapped("Umbra API active!", UiSharedService.AccentColor); } else { @@ -991,7 +991,7 @@ public class SettingsUi : WindowMediatorSubscriberBase _uiShared.DrawHelpText("If this option is selected all already existing notes for UIDs will be overwritten by the imported notes."); if (_notesSuccessfullyApplied.HasValue && _notesSuccessfullyApplied.Value) { - UiSharedService.ColorTextWrapped("User Notes successfully imported", ImGuiColors.HealerGreen); + UiSharedService.ColorTextWrapped("User Notes successfully imported", UiSharedService.AccentColor); } else if (_notesSuccessfullyApplied.HasValue && !_notesSuccessfullyApplied.Value) { @@ -1310,7 +1310,7 @@ public class SettingsUi : WindowMediatorSubscriberBase ImGui.TextUnformatted("Current VRAM utilization by all nearby players:"); ImGui.SameLine(); - using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.HealerGreen, totalVramBytes < 2.0 * 1024.0 * 1024.0 * 1024.0)) + using (ImRaii.PushColor(ImGuiCol.Text, UiSharedService.AccentColor, totalVramBytes < 2.0 * 1024.0 * 1024.0 * 1024.0)) using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudYellow, totalVramBytes >= 4.0 * 1024.0 * 1024.0 * 1024.0)) using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed, totalVramBytes >= 6.0 * 1024.0 * 1024.0 * 1024.0)) ImGui.TextUnformatted($"{totalVramBytes / 1024.0 / 1024.0 / 1024.0:0.00} GiB"); @@ -1903,11 +1903,11 @@ public class SettingsUi : WindowMediatorSubscriberBase { ImGui.TextUnformatted("Service " + _serverConfigurationManager.CurrentServer!.ServerName + ":"); ImGui.SameLine(); - ImGui.TextColored(ImGuiColors.ParsedGreen, "Available"); + ImGui.TextColored(UiSharedService.AccentColor, "Available"); ImGui.SameLine(); ImGui.TextUnformatted("("); ImGui.SameLine(); - ImGui.TextColored(ImGuiColors.ParsedGreen, _apiController.OnlineUsers.ToString(CultureInfo.InvariantCulture)); + ImGui.TextColored(UiSharedService.AccentColor, _apiController.OnlineUsers.ToString(CultureInfo.InvariantCulture)); ImGui.SameLine(); ImGui.TextUnformatted("Users Online"); ImGui.SameLine(); diff --git a/MareSynchronos/UI/UISharedService.cs b/MareSynchronos/UI/UISharedService.cs index 649e3d9..f5a207a 100644 --- a/MareSynchronos/UI/UISharedService.cs +++ b/MareSynchronos/UI/UISharedService.cs @@ -36,7 +36,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse; - public static Vector4 AccentColor { get; set; } = ImGuiColors.DalamudYellow; + public static Vector4 AccentColor { get; set; } = ImGuiColors.DalamudViolet; public readonly FileDialogManager FileDialogManager; @@ -310,7 +310,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase } } - public static Vector4 GetBoolColor(bool input) => input ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed; + public static Vector4 GetBoolColor(bool input) => input ? AccentColor : ImGuiColors.DalamudRed; public float GetIconTextButtonSize(FontAwesomeIcon icon, string text) { @@ -517,7 +517,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase public void BooleanToColoredIcon(bool value, bool inline = true) { - using var colorgreen = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.HealerGreen, value); + using var colorgreen = ImRaii.PushColor(ImGuiCol.Text, AccentColor, value); using var colorred = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed, !value); if (inline) ImGui.SameLine();