diff --git a/MareSynchronos/MareSynchronos.csproj b/MareSynchronos/MareSynchronos.csproj
index d71724f..d403e3f 100644
--- a/MareSynchronos/MareSynchronos.csproj
+++ b/MareSynchronos/MareSynchronos.csproj
@@ -3,7 +3,7 @@
UmbraSync
UmbraSync
- 0.1.9.5
+ 0.1.9.6
diff --git a/MareSynchronos/Plugin.cs b/MareSynchronos/Plugin.cs
index fbfc915..2763ba6 100644
--- a/MareSynchronos/Plugin.cs
+++ b/MareSynchronos/Plugin.cs
@@ -182,17 +182,22 @@ public sealed class Plugin : IDalamudPlugin
// add scoped services
collection.AddScoped();
collection.AddScoped();
- collection.AddScoped();
- collection.AddScoped();
+ collection.AddScoped();
+ collection.AddScoped();
+ collection.AddScoped();
+ collection.AddScoped();
+ collection.AddScoped();
+ collection.AddScoped(sp => sp.GetRequiredService());
+ collection.AddScoped(sp => sp.GetRequiredService());
collection.AddScoped();
collection.AddScoped();
- collection.AddScoped();
+ collection.AddScoped(sp => sp.GetRequiredService());
collection.AddScoped();
collection.AddScoped();
- collection.AddScoped();
+ collection.AddScoped(sp => sp.GetRequiredService());
collection.AddScoped();
collection.AddScoped();
- collection.AddScoped();
+ collection.AddScoped(sp => sp.GetRequiredService());
collection.AddScoped();
collection.AddScoped();
collection.AddScoped();
diff --git a/MareSynchronos/UI/AutoDetectUi.cs b/MareSynchronos/UI/AutoDetectUi.cs
index 24c07e4..2883619 100644
--- a/MareSynchronos/UI/AutoDetectUi.cs
+++ b/MareSynchronos/UI/AutoDetectUi.cs
@@ -90,6 +90,11 @@ public class AutoDetectUi : WindowMediatorSubscriberBase
}
}
+ public void DrawInline()
+ {
+ DrawInternal();
+ }
+
private static void DrawStyledTab(string label, Vector4 accent, Vector4 inactive, Vector4 hover, Action draw, bool disabled = false)
{
var tabColor = disabled ? ImGuiColors.DalamudGrey3 : inactive;
diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs
index c2e59e2..8c1299c 100644
--- a/MareSynchronos/UI/CompactUI.cs
+++ b/MareSynchronos/UI/CompactUI.cs
@@ -21,7 +21,9 @@ using MareSynchronos.WebAPI.Files;
using MareSynchronos.WebAPI.Files.Models;
using MareSynchronos.WebAPI.SignalR.Utils;
using Microsoft.Extensions.Logging;
+using System;
using System.Collections.Concurrent;
+using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Numerics;
@@ -51,6 +53,10 @@ public class CompactUi : WindowMediatorSubscriberBase
private readonly CharacterAnalyzer _characterAnalyzer;
private readonly UidDisplayHandler _uidDisplayHandler;
private readonly UiSharedService _uiSharedService;
+ private readonly EditProfileUi _editProfileUi;
+ private readonly SettingsUi _settingsUi;
+ private readonly AutoDetectUi _autoDetectUi;
+ private readonly DataAnalysisUi _dataAnalysisUi;
private bool _buttonState;
private string _characterOrCommentFilter = string.Empty;
private Pair? _lastAddedUser;
@@ -60,19 +66,48 @@ public class CompactUi : WindowMediatorSubscriberBase
private string _pairToAdd = string.Empty;
private int _secretKeyIdx = -1;
private bool _showModalForUserAddition;
- private bool _showSyncShells;
private bool _wasOpen;
private bool _nearbyOpen = true;
+ private bool _selfAnalysisOpen = false;
private List _nearbyEntries = new();
private const long SelfAnalysisSizeWarningThreshold = 300L * 1024 * 1024;
private const long SelfAnalysisTriangleWarningThreshold = 150_000;
+ private CompactUiSection _activeSection = CompactUiSection.VisiblePairs;
+ private const float SidebarWidth = 42f;
+ private const float SidebarIconSize = 22f;
+ private const float ContentFontScale = 0.92f;
+ private static readonly Vector4 SidebarButtonColor = new(0.08f, 0.08f, 0.10f, 0.92f);
+ private static readonly Vector4 SidebarButtonHoverColor = new(0.12f, 0.12f, 0.16f, 0.95f);
+ private static readonly Vector4 SidebarButtonActiveColor = new(0.16f, 0.16f, 0.22f, 0.95f);
+
+ private enum CompactUiSection
+ {
+ VisiblePairs,
+ IndividualPairs,
+ Syncshells,
+ AutoDetect,
+ CharacterAnalysis,
+ CharacterDataHub,
+ EditProfile,
+ Settings
+ }
+
+ private enum PairContentMode
+ {
+ All,
+ VisibleOnly
+ }
public CompactUi(ILogger logger, UiSharedService uiShared, MareConfigService configService, ApiController apiController, PairManager pairManager, ChatService chatService,
ServerConfigurationManager serverManager, MareMediator mediator, FileUploadManager fileTransferManager, UidDisplayHandler uidDisplayHandler, CharaDataManager charaDataManager,
NearbyPendingService nearbyPendingService,
AutoDetectRequestService autoDetectRequestService,
CharacterAnalyzer characterAnalyzer,
- PerformanceCollectorService performanceCollectorService)
+ PerformanceCollectorService performanceCollectorService,
+ EditProfileUi editProfileUi,
+ SettingsUi settingsUi,
+ AutoDetectUi autoDetectUi,
+ DataAnalysisUi dataAnalysisUi)
: base(logger, mediator, "###UmbraSyncMainUI", performanceCollectorService)
{
_uiSharedService = uiShared;
@@ -86,6 +121,10 @@ public class CompactUi : WindowMediatorSubscriberBase
_nearbyPending = nearbyPendingService;
_autoDetectRequestService = autoDetectRequestService;
_characterAnalyzer = characterAnalyzer;
+ _editProfileUi = editProfileUi;
+ _settingsUi = settingsUi;
+ _autoDetectUi = autoDetectUi;
+ _dataAnalysisUi = dataAnalysisUi;
var tagHandler = new TagHandler(_serverManager);
_groupPanel = new(this, uiShared, _pairManager, chatService, uidDisplayHandler, _configService, _serverManager, _charaDataManager, _autoDetectRequestService);
@@ -127,8 +166,8 @@ public class CompactUi : WindowMediatorSubscriberBase
SizeConstraints = new WindowSizeConstraints()
{
- MinimumSize = new Vector2(350, 400),
- MaximumSize = new Vector2(350, 2000),
+ MinimumSize = new Vector2(420, 320),
+ MaximumSize = new Vector2(1400, 2000),
};
}
@@ -144,130 +183,43 @@ public class CompactUi : WindowMediatorSubscriberBase
using var buttonHover = ImRaii.PushColor(ImGuiCol.ButtonHovered, UiSharedService.AccentHoverColor);
using var buttonActive = ImRaii.PushColor(ImGuiCol.ButtonActive, UiSharedService.AccentActiveColor);
ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ImGui.GetStyle().WindowPadding.Y - 1f * ImGuiHelpers.GlobalScale + ImGui.GetStyle().ItemSpacing.Y);
+ var sidebarWidth = ImGuiHelpers.ScaledVector2(SidebarWidth, 0).X;
+
+ ImGui.SetWindowFontScale(ContentFontScale);
+ ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, ImGui.GetStyle().FramePadding * ContentFontScale);
+
+ ImGui.BeginChild("compact-sidebar", new Vector2(sidebarWidth, 0), false, ImGuiWindowFlags.NoScrollbar);
+ DrawSidebar();
+ ImGui.EndChild();
+
+ ImGui.SameLine();
+
+ float separatorHeight = ImGui.GetWindowHeight() - ImGui.GetStyle().WindowPadding.Y * 2f;
+ float separatorX = ImGui.GetCursorPosX();
+ float separatorY = ImGui.GetCursorPosY();
+ var drawList = ImGui.GetWindowDrawList();
+ var start = ImGui.GetCursorScreenPos();
+ var end = new Vector2(start.X, start.Y + separatorHeight);
+ drawList.AddLine(start, end, ImGui.GetColorU32(new Vector4(1f, 1f, 1f, 0.08f)), 1f * ImGuiHelpers.GlobalScale);
+ ImGui.SetCursorPos(new Vector2(separatorX + 6f * ImGuiHelpers.GlobalScale, separatorY));
+
+ ImGui.BeginChild("compact-content", Vector2.Zero, false);
WindowContentWidth = UiSharedService.GetWindowContentRegionWidth();
+
if (!_apiController.IsCurrentVersion)
{
- var ver = _apiController.CurrentClientVersion;
- var unsupported = "UNSUPPORTED VERSION";
- using (_uiSharedService.UidFont.Push())
- {
- var uidTextSize = ImGui.CalcTextSize(unsupported);
- ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMax().X + ImGui.GetWindowContentRegionMin().X) / 2 - uidTextSize.X / 2);
- ImGui.AlignTextToFramePadding();
- ImGui.TextColored(UiSharedService.AccentColor, unsupported);
- }
- UiSharedService.ColorTextWrapped($"Your UmbraSync installation is out of date, the current version is {ver.Major}.{ver.Minor}.{ver.Build}. " +
- $"It is highly recommended to keep UmbraSync up to date. Open /xlplugins and update the plugin.", UiSharedService.AccentColor);
+ DrawUnsupportedVersionBanner();
+ ImGui.Separator();
}
using (ImRaii.PushId("header")) DrawUIDHeader();
ImGui.Separator();
using (ImRaii.PushId("serverstatus")) DrawServerStatus();
+ ImGui.Separator();
- if (_apiController.ServerState is ServerState.Connected)
- {
- var hasShownSyncShells = _showSyncShells;
+ DrawMainContent();
- using (var hoverColor = ImRaii.PushColor(ImGuiCol.ButtonHovered, UiSharedService.AccentHoverColor))
- using (var activeColor = ImRaii.PushColor(ImGuiCol.ButtonActive, UiSharedService.AccentActiveColor))
- {
- if (!hasShownSyncShells)
- {
- using var selectedColor = ImRaii.PushColor(ImGuiCol.Button, accent);
- using (ImRaii.PushFont(UiBuilder.IconFont))
- {
- if (ImGui.Button(FontAwesomeIcon.User.ToIconString(), new Vector2((UiSharedService.GetWindowContentRegionWidth() - ImGui.GetWindowContentRegionMin().X) / 2, 30 * ImGuiHelpers.GlobalScale)))
- {
- _showSyncShells = false;
- }
- }
- }
- else
- {
- using (ImRaii.PushFont(UiBuilder.IconFont))
- {
- if (ImGui.Button(FontAwesomeIcon.User.ToIconString(), new Vector2((UiSharedService.GetWindowContentRegionWidth() - ImGui.GetWindowContentRegionMin().X) / 2, 30 * ImGuiHelpers.GlobalScale)))
- {
- _showSyncShells = false;
- }
- }
- }
-
- UiSharedService.AttachToolTip("Individual pairs");
-
- ImGui.SameLine();
-
- if (hasShownSyncShells)
- {
- using var selectedColor = ImRaii.PushColor(ImGuiCol.Button, accent);
- using (ImRaii.PushFont(UiBuilder.IconFont))
- {
- if (ImGui.Button(FontAwesomeIcon.UserFriends.ToIconString(), new Vector2((UiSharedService.GetWindowContentRegionWidth() - ImGui.GetWindowContentRegionMin().X) / 2, 30 * ImGuiHelpers.GlobalScale)))
- {
- _showSyncShells = true;
- }
- }
- }
- else
- {
- using (ImRaii.PushFont(UiBuilder.IconFont))
- {
- if (ImGui.Button(FontAwesomeIcon.UserFriends.ToIconString(), new Vector2((UiSharedService.GetWindowContentRegionWidth() - ImGui.GetWindowContentRegionMin().X) / 2, 30 * ImGuiHelpers.GlobalScale)))
- {
- _showSyncShells = true;
- }
- }
- }
-
- UiSharedService.AttachToolTip("Syncshells");
- }
-
- DrawDefaultSyncSettings();
- if (!hasShownSyncShells)
- {
- using (ImRaii.PushId("pairlist")) DrawPairList();
- }
- else
- {
- using (ImRaii.PushId("syncshells")) _groupPanel.DrawSyncshells();
- }
- ImGui.Separator();
- using (ImRaii.PushId("transfers")) DrawTransfers();
- TransferPartHeight = ImGui.GetCursorPosY() - TransferPartHeight;
- using (ImRaii.PushId("group-user-popup")) _selectPairsForGroupUi.Draw(_pairManager.DirectPairs);
- using (ImRaii.PushId("grouping-popup")) _selectGroupForPairUi.Draw();
- }
-
- if (_configService.Current.OpenPopupOnAdd && _pairManager.LastAddedUser != null)
- {
- _lastAddedUser = _pairManager.LastAddedUser;
- _pairManager.LastAddedUser = null;
- ImGui.OpenPopup("Set Notes for New User");
- _showModalForUserAddition = true;
- _lastAddedUserComment = string.Empty;
- }
-
- if (ImGui.BeginPopupModal("Set Notes for New User", ref _showModalForUserAddition, UiSharedService.PopupWindowFlags))
- {
- if (_lastAddedUser == null)
- {
- _showModalForUserAddition = false;
- }
- else
- {
- 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"))
- {
- _serverManager.SetNoteForUid(_lastAddedUser.UserData.UID, _lastAddedUserComment);
- _lastAddedUser = null;
- _lastAddedUserComment = string.Empty;
- _showModalForUserAddition = false;
- }
- }
- UiSharedService.SetScaledWindowSize(275);
- ImGui.EndPopup();
- }
+ ImGui.EndChild();
var pos = ImGui.GetWindowPos();
var size = ImGui.GetWindowSize();
@@ -277,6 +229,9 @@ public class CompactUi : WindowMediatorSubscriberBase
_lastPosition = pos;
Mediator.Publish(new CompactUiChange(_lastSize, _lastPosition));
}
+
+ ImGui.PopStyleVar();
+ ImGui.SetWindowFontScale(1f);
}
public override void OnClose()
@@ -287,7 +242,7 @@ public class CompactUi : WindowMediatorSubscriberBase
private void DrawDefaultSyncSettings()
{
- ImGuiHelpers.ScaledDummy(4f);
+ ImGuiHelpers.ScaledDummy(3f);
using (ImRaii.PushId("sync-defaults"))
{
const string soundLabel = "Audio";
@@ -303,9 +258,6 @@ public class CompactUi : WindowMediatorSubscriberBase
bool showNearby = _configService.Current.EnableAutoDetectDiscovery;
int pendingInvites = _nearbyPending.Pending.Count;
- const string nearbyLabel = "AutoDetect";
-
-
var soundIcon = soundsDisabled ? FontAwesomeIcon.VolumeMute : FontAwesomeIcon.VolumeUp;
var animIcon = animsDisabled ? FontAwesomeIcon.WindowClose : FontAwesomeIcon.Running;
var vfxIcon = vfxDisabled ? FontAwesomeIcon.TimesCircle : FontAwesomeIcon.Sun;
@@ -314,9 +266,7 @@ public class CompactUi : WindowMediatorSubscriberBase
float audioWidth = _uiSharedService.GetIconTextButtonSize(soundIcon, soundLabel);
float animWidth = _uiSharedService.GetIconTextButtonSize(animIcon, animLabel);
float vfxWidth = _uiSharedService.GetIconTextButtonSize(vfxIcon, vfxLabel);
- float nearbyWidth = showNearby ? _uiSharedService.GetIconTextButtonSize(FontAwesomeIcon.UserPlus, pendingInvites > 0 ? $"{nearbyLabel} ({pendingInvites})" : nearbyLabel) : 0f;
- int buttonCount = 3 + (showNearby ? 1 : 0);
- float totalWidth = audioWidth + animWidth + vfxWidth + nearbyWidth + spacing * (buttonCount - 1);
+ float totalWidth = audioWidth + animWidth + vfxWidth + spacing * 2f;
float available = ImGui.GetContentRegionAvail().X;
float startCursorX = ImGui.GetCursorPosX();
if (totalWidth < available)
@@ -351,18 +301,10 @@ public class CompactUi : WindowMediatorSubscriberBase
},
() => DisableStateTooltip(vfxSubject, _configService.Current.DefaultDisableVfx), spacing);
- if (showNearby)
+if (showNearby && pendingInvites > 0)
{
- ImGui.SameLine(0, spacing);
- var autodetectLabel = pendingInvites > 0 ? $"{nearbyLabel} ({pendingInvites})" : nearbyLabel;
- if (_uiSharedService.IconTextButton(FontAwesomeIcon.UserPlus, autodetectLabel, nearbyWidth))
- {
- Mediator.Publish(new UiToggleMessage(typeof(AutoDetectUi)));
- }
- string tooltip = pendingInvites > 0
- ? string.Format("Vous avez {0} invitation{1} reçue. Ouvrez l\'interface AutoDetect pour y répondre.", pendingInvites, pendingInvites > 1 ? "s" : string.Empty)
- : "Ouvrir les outils AutoDetect (invitations et proximité).\n\nLes demandes reçues sont listées dans l\'onglet 'Invitations'.";
- UiSharedService.AttachToolTip(tooltip);
+ ImGuiHelpers.ScaledDummy(3f);
+ UiSharedService.ColorTextWrapped($"AutoDetect : {pendingInvites} invitation(s) en attente. Utilisez l'icône AutoDetect dans la barre latérale pour y répondre.", ImGuiColors.DalamudYellow);
}
DrawSelfAnalysisPreview();
@@ -374,127 +316,147 @@ public class CompactUi : WindowMediatorSubscriberBase
{
using (ImRaii.PushId("self-analysis"))
{
- if (!ImGui.CollapsingHeader("Self Analysis"))
+ UiSharedService.DrawCard("self-analysis-card", () =>
{
- return;
- }
-
- var summary = _characterAnalyzer.CurrentSummary;
- bool isAnalyzing = _characterAnalyzer.IsAnalysisRunning;
-
- if (isAnalyzing)
- {
- UiSharedService.ColorTextWrapped(
- $"Analyse en cours ({_characterAnalyzer.CurrentFile}/{System.Math.Max(_characterAnalyzer.TotalFiles, 1)})...",
- ImGuiColors.DalamudYellow);
- if (_uiSharedService.IconTextButton(FontAwesomeIcon.StopCircle, "Annuler l'analyse"))
+ bool arrowState = _selfAnalysisOpen;
+ UiSharedService.DrawArrowToggle(ref arrowState, "##self-analysis-toggle");
+ if (arrowState != _selfAnalysisOpen)
{
- _characterAnalyzer.CancelAnalyze();
+ _selfAnalysisOpen = arrowState;
}
- UiSharedService.AttachToolTip("Stopper l'analyse en cours.");
- }
- else
- {
- bool recalculate = !summary.HasUncomputedEntries && !summary.IsEmpty;
- var label = recalculate ? "Recalculer l'analyse" : "Lancer l'analyse";
- var icon = recalculate ? FontAwesomeIcon.Sync : FontAwesomeIcon.PlayCircle;
- if (_uiSharedService.IconTextButton(icon, label))
+
+ ImGui.SameLine(0f, 6f * ImGuiHelpers.GlobalScale);
+ ImGui.AlignTextToFramePadding();
+ ImGui.TextUnformatted("Self Analysis");
+ if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
{
- _ = _characterAnalyzer.ComputeAnalysis(print: false, recalculate: recalculate);
+ _selfAnalysisOpen = !_selfAnalysisOpen;
}
- UiSharedService.AttachToolTip(recalculate
- ? "Recalcule toutes les entrées pour mettre à jour les tailles partagées."
- : "Analyse vos fichiers actuels pour estimer le poids partagé.");
- }
- if (summary.IsEmpty && !isAnalyzing)
- {
- UiSharedService.ColorTextWrapped("Aucune donnée analysée pour l'instant. Lancez une analyse pour générer cet aperçu.",
- ImGuiColors.DalamudGrey2);
- return;
- }
-
- if (summary.HasUncomputedEntries && !isAnalyzing)
- {
- UiSharedService.ColorTextWrapped("Certaines entrées n'ont pas encore de taille calculée. Lancez l'analyse pour compléter les données.",
- ImGuiColors.DalamudYellow);
- }
-
- ImGuiHelpers.ScaledDummy(4f);
-
- UiSharedService.DrawGrouped(() =>
- {
- if (ImGui.BeginTable("self-analysis-stats", 2, ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.NoSavedSettings))
+ if (!_selfAnalysisOpen)
{
- ImGui.TableSetupColumn("label", ImGuiTableColumnFlags.WidthStretch, 0.55f);
- ImGui.TableSetupColumn("value", ImGuiTableColumnFlags.WidthStretch, 0.45f);
-
- DrawSelfAnalysisStatRow("Fichiers moddés", summary.TotalFiles.ToString("N0", CultureInfo.CurrentCulture));
-
- var compressedValue = UiSharedService.ByteToString(summary.TotalCompressedSize);
- Vector4? compressedColor = null;
- FontAwesomeIcon? compressedIcon = null;
- Vector4? compressedIconColor = null;
- string? compressedTooltip = null;
- if (summary.HasUncomputedEntries)
- {
- compressedColor = ImGuiColors.DalamudYellow;
- compressedTooltip = "Lancez l'analyse pour calculer la taille de téléchargement exacte.";
- }
- else if (summary.TotalCompressedSize >= SelfAnalysisSizeWarningThreshold)
- {
- compressedColor = ImGuiColors.DalamudYellow;
- compressedTooltip = "Au-delà de 300 MiB, certains joueurs peuvent ne pas voir toutes vos modifications.";
- compressedIcon = FontAwesomeIcon.ExclamationTriangle;
- compressedIconColor = ImGuiColors.DalamudYellow;
- }
-
- DrawSelfAnalysisStatRow("Taille compressée", compressedValue, compressedColor, compressedTooltip, compressedIcon, compressedIconColor);
- DrawSelfAnalysisStatRow("Taille extraite", UiSharedService.ByteToString(summary.TotalOriginalSize));
-
- Vector4? trianglesColor = null;
- FontAwesomeIcon? trianglesIcon = null;
- Vector4? trianglesIconColor = null;
- string? trianglesTooltip = null;
- if (summary.TotalTriangles >= SelfAnalysisTriangleWarningThreshold)
- {
- trianglesColor = ImGuiColors.DalamudYellow;
- trianglesTooltip = "Plus de 150k triangles peuvent entraîner un auto-pause et impacter les performances.";
- trianglesIcon = FontAwesomeIcon.ExclamationTriangle;
- trianglesIconColor = ImGuiColors.DalamudYellow;
- }
- DrawSelfAnalysisStatRow("Triangles moddés", UiSharedService.TrisToString(summary.TotalTriangles), trianglesColor, trianglesTooltip, trianglesIcon, trianglesIconColor);
-
- ImGui.EndTable();
+ return;
}
- }, rounding: 4f, expectedWidth: ImGui.GetContentRegionAvail().X);
- string lastAnalysisText;
- Vector4 lastAnalysisColor = ImGuiColors.DalamudGrey2;
- if (isAnalyzing)
- {
- lastAnalysisText = "Dernière analyse : en cours...";
- lastAnalysisColor = ImGuiColors.DalamudYellow;
- }
- else if (_characterAnalyzer.LastCompletedAnalysis.HasValue)
- {
- var localTime = _characterAnalyzer.LastCompletedAnalysis.Value.ToLocalTime();
- lastAnalysisText = $"Dernière analyse : {localTime.ToString("g", CultureInfo.CurrentCulture)}";
- }
- else
- {
- lastAnalysisText = "Dernière analyse : jamais";
- }
+ ImGuiHelpers.ScaledDummy(4f);
- ImGuiHelpers.ScaledDummy(2f);
- UiSharedService.ColorTextWrapped(lastAnalysisText, lastAnalysisColor);
+ var summary = _characterAnalyzer.CurrentSummary;
+ bool isAnalyzing = _characterAnalyzer.IsAnalysisRunning;
- ImGuiHelpers.ScaledDummy(4f);
+ if (isAnalyzing)
+ {
+ UiSharedService.ColorTextWrapped(
+ $"Analyse en cours ({_characterAnalyzer.CurrentFile}/{System.Math.Max(_characterAnalyzer.TotalFiles, 1)})...",
+ ImGuiColors.DalamudYellow);
+ if (_uiSharedService.IconTextButton(FontAwesomeIcon.StopCircle, "Annuler l'analyse"))
+ {
+ _characterAnalyzer.CancelAnalyze();
+ }
+ UiSharedService.AttachToolTip("Stopper l'analyse en cours.");
+ }
+ else
+ {
+ bool recalculate = !summary.HasUncomputedEntries && !summary.IsEmpty;
+ var label = recalculate ? "Recalculer l'analyse" : "Lancer l'analyse";
+ var icon = recalculate ? FontAwesomeIcon.Sync : FontAwesomeIcon.PlayCircle;
+ if (_uiSharedService.IconTextButton(icon, label))
+ {
+ _ = _characterAnalyzer.ComputeAnalysis(print: false, recalculate: recalculate);
+ }
+ UiSharedService.AttachToolTip(recalculate
+ ? "Recalcule toutes les entrées pour mettre à jour les tailles partagées."
+ : "Analyse vos fichiers actuels pour estimer le poids partagé.");
+ }
- if (_uiSharedService.IconTextButton(FontAwesomeIcon.PersonCircleQuestion, "Ouvrir l'analyse détaillée"))
- {
- Mediator.Publish(new UiToggleMessage(typeof(DataAnalysisUi)));
- }
+ if (summary.IsEmpty && !isAnalyzing)
+ {
+ UiSharedService.ColorTextWrapped("Aucune donnée analysée pour l'instant. Lancez une analyse pour générer cet aperçu.",
+ ImGuiColors.DalamudGrey2);
+ return;
+ }
+
+ if (summary.HasUncomputedEntries && !isAnalyzing)
+ {
+ UiSharedService.ColorTextWrapped("Certaines entrées n'ont pas encore de taille calculée. Lancez l'analyse pour compléter les données.",
+ ImGuiColors.DalamudYellow);
+ }
+
+ ImGuiHelpers.ScaledDummy(3f);
+
+ UiSharedService.DrawGrouped(() =>
+ {
+ if (ImGui.BeginTable("self-analysis-stats", 2, ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.NoSavedSettings))
+ {
+ ImGui.TableSetupColumn("label", ImGuiTableColumnFlags.WidthStretch, 0.55f);
+ ImGui.TableSetupColumn("value", ImGuiTableColumnFlags.WidthStretch, 0.45f);
+
+ DrawSelfAnalysisStatRow("Fichiers moddés", summary.TotalFiles.ToString("N0", CultureInfo.CurrentCulture));
+
+ var compressedValue = UiSharedService.ByteToString(summary.TotalCompressedSize);
+ Vector4? compressedColor = null;
+ FontAwesomeIcon? compressedIcon = null;
+ Vector4? compressedIconColor = null;
+ string? compressedTooltip = null;
+ if (summary.HasUncomputedEntries)
+ {
+ compressedColor = ImGuiColors.DalamudYellow;
+ compressedTooltip = "Lancez l'analyse pour calculer la taille de téléchargement exacte.";
+ }
+ else if (summary.TotalCompressedSize >= SelfAnalysisSizeWarningThreshold)
+ {
+ compressedColor = ImGuiColors.DalamudYellow;
+ compressedTooltip = "Au-delà de 300 MiB, certains joueurs peuvent ne pas voir toutes vos modifications.";
+ compressedIcon = FontAwesomeIcon.ExclamationTriangle;
+ compressedIconColor = ImGuiColors.DalamudYellow;
+ }
+
+ DrawSelfAnalysisStatRow("Taille compressée", compressedValue, compressedColor, compressedTooltip, compressedIcon, compressedIconColor);
+ DrawSelfAnalysisStatRow("Taille extraite", UiSharedService.ByteToString(summary.TotalOriginalSize));
+
+ Vector4? trianglesColor = null;
+ FontAwesomeIcon? trianglesIcon = null;
+ Vector4? trianglesIconColor = null;
+ string? trianglesTooltip = null;
+ if (summary.TotalTriangles >= SelfAnalysisTriangleWarningThreshold)
+ {
+ trianglesColor = ImGuiColors.DalamudYellow;
+ trianglesTooltip = "Plus de 150k triangles peuvent entraîner un auto-pause et impacter les performances.";
+ trianglesIcon = FontAwesomeIcon.ExclamationTriangle;
+ trianglesIconColor = ImGuiColors.DalamudYellow;
+ }
+ DrawSelfAnalysisStatRow("Triangles moddés", UiSharedService.TrisToString(summary.TotalTriangles), trianglesColor, trianglesTooltip, trianglesIcon, trianglesIconColor);
+
+ ImGui.EndTable();
+ }
+ }, rounding: 4f, expectedWidth: ImGui.GetContentRegionAvail().X, drawBorder: false);
+
+ string lastAnalysisText;
+ Vector4 lastAnalysisColor = ImGuiColors.DalamudGrey2;
+ if (isAnalyzing)
+ {
+ lastAnalysisText = "Dernière analyse : en cours...";
+ lastAnalysisColor = ImGuiColors.DalamudYellow;
+ }
+ else if (_characterAnalyzer.LastCompletedAnalysis.HasValue)
+ {
+ var localTime = _characterAnalyzer.LastCompletedAnalysis.Value.ToLocalTime();
+ lastAnalysisText = $"Dernière analyse : {localTime.ToString("g", CultureInfo.CurrentCulture)}";
+ }
+ else
+ {
+ lastAnalysisText = "Dernière analyse : jamais";
+ }
+
+ ImGuiHelpers.ScaledDummy(2f);
+ UiSharedService.ColorTextWrapped(lastAnalysisText, lastAnalysisColor);
+
+ ImGuiHelpers.ScaledDummy(3f);
+
+ if (_uiSharedService.IconTextButton(FontAwesomeIcon.PersonCircleQuestion, "Ouvrir l'analyse détaillée"))
+ {
+ Mediator.Publish(new UiToggleMessage(typeof(DataAnalysisUi)));
+ }
+ }, stretchWidth: true);
}
}
@@ -688,106 +650,462 @@ public class CompactUi : WindowMediatorSubscriberBase
}
}
- private void DrawPairList()
+ private void DrawPairList(PairContentMode mode)
{
- using (ImRaii.PushId("addpair")) DrawAddPair();
- using (ImRaii.PushId("pairs")) DrawPairs();
+ if (mode == PairContentMode.All)
+ {
+ using (ImRaii.PushId("addpair")) DrawAddPair();
+ }
+
+ using (ImRaii.PushId("pairs")) DrawPairs(mode);
TransferPartHeight = ImGui.GetCursorPosY();
using (ImRaii.PushId("filter")) DrawFilter();
}
- private void DrawPairs()
+ private void DrawPairs(PairContentMode mode)
{
- var ySize = TransferPartHeight == 0
- ? 1
- : (ImGui.GetWindowContentRegionMax().Y - ImGui.GetWindowContentRegionMin().Y) - TransferPartHeight - ImGui.GetCursorPosY();
- var users = GetFilteredUsers().OrderBy(u => u.GetPairSortKey(), StringComparer.Ordinal);
+ float availableHeight = ImGui.GetContentRegionAvail().Y;
+ float ySize;
+ if (TransferPartHeight <= 0)
+ {
+ float reserve = ImGui.GetFrameHeightWithSpacing() * 2f;
+ ySize = availableHeight - reserve;
+ if (ySize <= 0)
+ {
+ ySize = System.Math.Max(availableHeight, 1f);
+ }
+ }
+ else
+ {
+ ySize = (ImGui.GetWindowContentRegionMax().Y - ImGui.GetWindowContentRegionMin().Y) - TransferPartHeight - ImGui.GetCursorPosY();
+ }
+ var allUsers = GetFilteredUsers().OrderBy(u => u.GetPairSortKey(), StringComparer.Ordinal).ToList();
+ var visibleUsersSource = allUsers.Where(u => u.IsVisible).ToList();
+ var nonVisibleUsers = allUsers.Where(u => !u.IsVisible).ToList();
ImGui.BeginChild("list", new Vector2(WindowContentWidth, ySize), border: false);
- var pendingCount = _nearbyPending?.Pending.Count ?? 0;
- if (pendingCount > 0)
+ if (mode == PairContentMode.All)
{
- UiSharedService.ColorTextWrapped("Invitation AutoDetect en attente. Ouvrez l\'interface AutoDetect pour gérer vos demandes.", ImGuiColors.DalamudYellow);
- ImGuiHelpers.ScaledDummy(4);
- }
-
- var onlineUsers = users.Where(u => u.UserPair!.OtherPermissions.IsPaired() && (u.IsOnline || u.UserPair!.OwnPermissions.IsPaused())).Select(c => new DrawUserPair("Online" + c.UserData.UID, c, _uidDisplayHandler, _apiController, Mediator, _selectGroupForPairUi, _uiSharedService, _charaDataManager, _serverManager)).ToList();
- var visibleUsers = users.Where(u => u.IsVisible).Select(c => new DrawUserPair("Visible" + c.UserData.UID, c, _uidDisplayHandler, _apiController, Mediator, _selectGroupForPairUi, _uiSharedService, _charaDataManager, _serverManager)).ToList();
- var offlineUsers = users.Where(u => !u.UserPair!.OtherPermissions.IsPaired() || (!u.IsOnline && !u.UserPair!.OwnPermissions.IsPaused())).Select(c => new DrawUserPair("Offline" + c.UserData.UID, c, _uidDisplayHandler, _apiController, Mediator, _selectGroupForPairUi, _uiSharedService, _charaDataManager, _serverManager)).ToList();
-
- _pairGroupsUi.Draw(visibleUsers, onlineUsers, offlineUsers);
-
- if (_configService.Current.EnableAutoDetectDiscovery)
- {
- using (ImRaii.PushId("group-Nearby"))
+ var pendingCount = _nearbyPending?.Pending.Count ?? 0;
+ if (pendingCount > 0)
{
- var icon = _nearbyOpen ? FontAwesomeIcon.CaretSquareDown : FontAwesomeIcon.CaretSquareRight;
- _uiSharedService.IconText(icon);
- if (ImGui.IsItemClicked(ImGuiMouseButton.Left)) _nearbyOpen = !_nearbyOpen;
- ImGui.SameLine();
- var onUmbra = _nearbyEntries?.Count(e => e.IsMatch && e.AcceptPairRequests && !string.IsNullOrEmpty(e.Token) && !IsAlreadyPairedQuickMenu(e)) ?? 0;
- ImGui.TextUnformatted($"Nearby ({onUmbra})");
- if (ImGui.IsItemClicked(ImGuiMouseButton.Left)) _nearbyOpen = !_nearbyOpen;
- if (_nearbyOpen)
- {
- ImGui.Indent();
- var nearby = _nearbyEntries == null
- ? new List()
- : _nearbyEntries.Where(e => e.IsMatch && e.AcceptPairRequests && !string.IsNullOrEmpty(e.Token) && !IsAlreadyPairedQuickMenu(e))
- .OrderBy(e => e.Distance)
- .ToList();
- if (nearby.Count == 0)
- {
- UiSharedService.ColorTextWrapped("Aucun nouveau joueur detecté.", ImGuiColors.DalamudGrey3);
- }
- else
- {
- foreach (var e in nearby)
- {
- if (!e.AcceptPairRequests || string.IsNullOrEmpty(e.Token))
- continue;
-
- var name = e.DisplayName ?? e.Name;
- ImGui.AlignTextToFramePadding();
- ImGui.TextUnformatted(name);
- var right = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth();
- ImGui.SameLine();
-
- var statusButtonSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.UserPlus);
- ImGui.SetCursorPosX(right - statusButtonSize.X);
- if (!e.AcceptPairRequests)
- {
- _uiSharedService.IconText(FontAwesomeIcon.Ban, ImGuiColors.DalamudGrey3);
- UiSharedService.AttachToolTip("Les demandes sont désactivées pour ce joueur");
- }
- else if (!string.IsNullOrEmpty(e.Token))
- {
- using (ImRaii.PushId(e.Token ?? e.Uid ?? e.Name ?? string.Empty))
- {
- if (_uiSharedService.IconButton(FontAwesomeIcon.UserPlus))
- {
- _ = _autoDetectRequestService.SendRequestAsync(e.Token!, e.Uid, e.DisplayName);
- }
- }
- UiSharedService.AttachToolTip("Envoyer une invitation d'apparaige");
- }
- else
- {
- _uiSharedService.IconText(FontAwesomeIcon.QuestionCircle, ImGuiColors.DalamudGrey3);
- UiSharedService.AttachToolTip("Impossible d'inviter ce joueur");
- }
- }
- }
- ImGui.Unindent();
- ImGui.Separator();
- }
+ UiSharedService.ColorTextWrapped("Invitation AutoDetect en attente. Ouvrez l\'interface AutoDetect pour gérer vos demandes.", ImGuiColors.DalamudYellow);
+ ImGuiHelpers.ScaledDummy(4);
}
}
+ if (mode == PairContentMode.VisibleOnly)
+ {
+ var visibleUsers = visibleUsersSource.Select(c => new DrawUserPair("Visible" + c.UserData.UID, c, _uidDisplayHandler, _apiController, Mediator, _selectGroupForPairUi, _uiSharedService, _charaDataManager, _serverManager)).ToList();
+ if (visibleUsers.Count == 0)
+ {
+ UiSharedService.ColorTextWrapped("Aucune paire visible pour l'instant.", ImGuiColors.DalamudGrey3);
+ }
+ else
+ {
+ foreach (var visibleUser in visibleUsers)
+ {
+ visibleUser.DrawPairedClient();
+ }
+ }
+
+ if (_configService.Current.EnableAutoDetectDiscovery)
+ {
+ DrawNearbyCard();
+ }
+ }
+ else
+ {
+ var visibleUsers = visibleUsersSource.Select(c => new DrawUserPair("Visible" + c.UserData.UID, c, _uidDisplayHandler, _apiController, Mediator, _selectGroupForPairUi, _uiSharedService, _charaDataManager, _serverManager)).ToList();
+ var onlineUsers = nonVisibleUsers.Where(u => u.UserPair!.OtherPermissions.IsPaired() && (u.IsOnline || u.UserPair!.OwnPermissions.IsPaused()))
+ .Select(c => new DrawUserPair("Online" + c.UserData.UID, c, _uidDisplayHandler, _apiController, Mediator, _selectGroupForPairUi, _uiSharedService, _charaDataManager, _serverManager))
+ .ToList();
+ var offlineUsers = nonVisibleUsers.Where(u => !u.UserPair!.OtherPermissions.IsPaired() || (!u.IsOnline && !u.UserPair!.OwnPermissions.IsPaused()))
+ .Select(c => new DrawUserPair("Offline" + c.UserData.UID, c, _uidDisplayHandler, _apiController, Mediator, _selectGroupForPairUi, _uiSharedService, _charaDataManager, _serverManager))
+ .ToList();
+
+ Action? drawVisibleExtras = null;
+ if (_configService.Current.EnableAutoDetectDiscovery)
+ {
+ drawVisibleExtras = () => DrawNearbyCard();
+ }
+
+ _pairGroupsUi.Draw(visibleUsers, onlineUsers, offlineUsers, drawVisibleExtras);
+ }
+
ImGui.EndChild();
}
+ private void DrawNearbyCard()
+ {
+ ImGuiHelpers.ScaledDummy(4f);
+ using (ImRaii.PushId("group-Nearby"))
+ {
+ UiSharedService.DrawCard("nearby-card", () =>
+ {
+ bool nearbyState = _nearbyOpen;
+ UiSharedService.DrawArrowToggle(ref nearbyState, "##nearby-toggle");
+ if (nearbyState != _nearbyOpen)
+ {
+ _nearbyOpen = nearbyState;
+ }
+
+ ImGui.SameLine(0f, 6f * ImGuiHelpers.GlobalScale);
+ var onUmbra = _nearbyEntries?.Count(e => e.IsMatch && e.AcceptPairRequests && !string.IsNullOrEmpty(e.Token) && !IsAlreadyPairedQuickMenu(e)) ?? 0;
+ ImGui.AlignTextToFramePadding();
+ ImGui.TextUnformatted($"Nearby ({onUmbra})");
+ if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
+ {
+ _nearbyOpen = !_nearbyOpen;
+ }
+
+ if (!_nearbyOpen)
+ {
+ return;
+ }
+
+ ImGuiHelpers.ScaledDummy(4f);
+ var indent = 18f * ImGuiHelpers.GlobalScale;
+ ImGui.Indent(indent);
+ var nearby = _nearbyEntries == null
+ ? new List()
+ : _nearbyEntries.Where(e => e.IsMatch && e.AcceptPairRequests && !string.IsNullOrEmpty(e.Token) && !IsAlreadyPairedQuickMenu(e))
+ .OrderBy(e => e.Distance)
+ .ToList();
+ if (nearby.Count == 0)
+ {
+ UiSharedService.ColorTextWrapped("Aucun nouveau joueur detecté.", ImGuiColors.DalamudGrey3);
+ }
+ else
+ {
+ foreach (var e in nearby)
+ {
+ if (!e.AcceptPairRequests || string.IsNullOrEmpty(e.Token))
+ continue;
+
+ var name = e.DisplayName ?? e.Name;
+ ImGui.AlignTextToFramePadding();
+ ImGui.TextUnformatted(name);
+ var right = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth();
+ ImGui.SameLine();
+
+ var statusButtonSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.UserPlus);
+ ImGui.SetCursorPosX(right - statusButtonSize.X);
+ if (!e.AcceptPairRequests)
+ {
+ _uiSharedService.IconText(FontAwesomeIcon.Ban, ImGuiColors.DalamudGrey3);
+ UiSharedService.AttachToolTip("Les demandes sont désactivées pour ce joueur");
+ }
+ else if (!string.IsNullOrEmpty(e.Token))
+ {
+ using (ImRaii.PushId(e.Token ?? e.Uid ?? e.Name ?? string.Empty))
+ {
+ if (_uiSharedService.IconButton(FontAwesomeIcon.UserPlus))
+ {
+ _ = _autoDetectRequestService.SendRequestAsync(e.Token!, e.Uid, e.DisplayName);
+ }
+ }
+ UiSharedService.AttachToolTip("Envoyer une invitation d'apparaige");
+ }
+ else
+ {
+ _uiSharedService.IconText(FontAwesomeIcon.QuestionCircle, ImGuiColors.DalamudGrey3);
+ UiSharedService.AttachToolTip("Impossible d'inviter ce joueur");
+ }
+ }
+ }
+ ImGui.Unindent(indent);
+ }, stretchWidth: true);
+ }
+ ImGuiHelpers.ScaledDummy(4f);
+ }
+
+ private void DrawSidebar()
+ {
+ bool isConnected = _apiController.ServerState is ServerState.Connected;
+
+ ImGuiHelpers.ScaledDummy(6f);
+ DrawConnectionIcon();
+ ImGuiHelpers.ScaledDummy(12f);
+
+ DrawSidebarButton(FontAwesomeIcon.Eye, "Visible pairs", CompactUiSection.VisiblePairs, isConnected);
+ ImGuiHelpers.ScaledDummy(3f);
+ DrawSidebarButton(FontAwesomeIcon.User, "Individual pairs", CompactUiSection.IndividualPairs, isConnected);
+ ImGuiHelpers.ScaledDummy(3f);
+ DrawSidebarButton(FontAwesomeIcon.UserFriends, "Syncshells", CompactUiSection.Syncshells, isConnected);
+ ImGuiHelpers.ScaledDummy(3f);
+ int pendingInvites = _nearbyPending?.Pending.Count ?? 0;
+ bool highlightAutoDetect = pendingInvites > 0;
+ string autoDetectTooltip = highlightAutoDetect
+ ? $"AutoDetect — {pendingInvites} invitation(s) en attente"
+ : "AutoDetect";
+ DrawSidebarButton(FontAwesomeIcon.BroadcastTower, autoDetectTooltip, CompactUiSection.AutoDetect, isConnected, highlightAutoDetect, pendingInvites);
+ ImGuiHelpers.ScaledDummy(3f);
+ DrawSidebarButton(FontAwesomeIcon.PersonCircleQuestion, "Character Analysis", CompactUiSection.CharacterAnalysis, isConnected, _dataAnalysisUi.IsOpen, 0, () =>
+ {
+ Mediator.Publish(new UiToggleMessage(typeof(DataAnalysisUi)));
+ });
+ ImGuiHelpers.ScaledDummy(3f);
+ DrawSidebarButton(FontAwesomeIcon.Running, "Character Data Hub", CompactUiSection.CharacterDataHub, isConnected, false, 0, () =>
+ {
+ Mediator.Publish(new UiToggleMessage(typeof(CharaDataHubUi)));
+ });
+ ImGuiHelpers.ScaledDummy(12f);
+ DrawSidebarButton(FontAwesomeIcon.UserCircle, "Edit Profile", CompactUiSection.EditProfile, isConnected);
+ ImGuiHelpers.ScaledDummy(3f);
+ DrawSidebarButton(FontAwesomeIcon.Cog, "Settings", CompactUiSection.Settings, true, _settingsUi.IsOpen, 0, () =>
+ {
+ Mediator.Publish(new UiToggleMessage(typeof(SettingsUi)));
+ });
+ }
+
+ private void DrawSidebarButton(FontAwesomeIcon icon, string tooltip, CompactUiSection section, bool enabled = true, bool highlight = false, int badgeCount = 0, Action? onClick = null)
+ {
+ using var id = ImRaii.PushId((int)section);
+ float regionWidth = ImGui.GetContentRegionAvail().X;
+ float buttonWidth = SidebarIconSize * ImGuiHelpers.GlobalScale;
+ float offset = System.Math.Max(0f, (regionWidth - buttonWidth) / 2f);
+ ImGui.SetCursorPosX(ImGui.GetCursorPosX() + offset);
+
+ bool isActive = _activeSection == section;
+
+ if (DrawSidebarSquareButton(icon, isActive, highlight, enabled, badgeCount))
+ {
+ if (onClick != null)
+ {
+ onClick.Invoke();
+ }
+ else
+ {
+ _activeSection = section;
+ }
+ }
+
+ UiSharedService.AttachToolTip(tooltip);
+ }
+
+ private void DrawConnectionIcon()
+ {
+ var state = _apiController.ServerState;
+ bool hasServer = _serverManager.CurrentServer != null;
+ bool isLinked = hasServer && !_serverManager.CurrentServer!.FullPause;
+ var icon = isLinked ? FontAwesomeIcon.Unlink : FontAwesomeIcon.Link;
+
+ using var id = ImRaii.PushId("connection-icon");
+ float regionWidth = ImGui.GetContentRegionAvail().X;
+ float buttonWidth = SidebarIconSize * ImGuiHelpers.GlobalScale;
+ float offset = System.Math.Max(0f, (regionWidth - buttonWidth) / 2f);
+ ImGui.SetCursorPosX(ImGui.GetCursorPosX() + offset);
+
+ bool isTogglingDisabled = !hasServer || state is ServerState.Reconnecting or ServerState.Disconnecting;
+
+ if (DrawSidebarSquareButton(icon, isLinked, false, !isTogglingDisabled, 0) && !isTogglingDisabled)
+ {
+ ToggleConnection();
+ }
+
+ if (hasServer)
+ {
+ var tooltip = isLinked
+ ? $"Disconnect from {_serverManager.CurrentServer!.ServerName}"
+ : $"Connect to {_serverManager.CurrentServer!.ServerName}";
+ UiSharedService.AttachToolTip(tooltip);
+ }
+ else
+ {
+ UiSharedService.AttachToolTip("No server configured");
+ }
+ }
+
+ private bool DrawSidebarSquareButton(FontAwesomeIcon icon, bool isActive, bool highlight, bool enabled, int badgeCount)
+ {
+ float size = SidebarIconSize * ImGuiHelpers.GlobalScale;
+
+ bool useAccent = (isActive || highlight) && enabled;
+ var buttonColor = useAccent ? UiSharedService.AccentColor : SidebarButtonColor;
+ var hoverColor = useAccent ? UiSharedService.AccentHoverColor : SidebarButtonHoverColor;
+ var activeColor = useAccent ? UiSharedService.AccentActiveColor : SidebarButtonActiveColor;
+
+ string iconText = icon.ToIconString();
+ Vector2 iconSize;
+ using (_uiSharedService.IconFont.Push())
+ {
+ iconSize = ImGui.CalcTextSize(iconText);
+ }
+
+ var start = ImGui.GetCursorScreenPos();
+ bool clicked;
+
+ using var disabled = ImRaii.Disabled(!enabled);
+ using var buttonColorPush = ImRaii.PushColor(ImGuiCol.Button, buttonColor);
+ using var hoverColorPush = ImRaii.PushColor(ImGuiCol.ButtonHovered, hoverColor);
+ using var activeColorPush = ImRaii.PushColor(ImGuiCol.ButtonActive, activeColor);
+
+ clicked = ImGui.Button("##sidebar-icon", new Vector2(size, size));
+
+ using (_uiSharedService.IconFont.Push())
+ {
+ var textPos = new Vector2(
+ start.X + (size - iconSize.X) / 2f,
+ start.Y + (size - iconSize.Y) / 2f);
+ uint iconColor = ImGui.ColorConvertFloat4ToU32(new Vector4(0.85f, 0.85f, 0.9f, 1f));
+ if (highlight)
+ iconColor = ImGui.ColorConvertFloat4ToU32(new Vector4(0.45f, 0.85f, 0.45f, 1f));
+ else if (isActive)
+ iconColor = ImGui.GetColorU32(ImGuiCol.Text);
+ ImGui.GetWindowDrawList().AddText(textPos, iconColor, iconText);
+ }
+
+ if (badgeCount > 0)
+ {
+ var min = ImGui.GetItemRectMin();
+ var max = ImGui.GetItemRectMax();
+ float radius = 6f * ImGuiHelpers.GlobalScale;
+ var center = new Vector2(max.X - radius * 0.8f, min.Y + radius * 0.8f);
+ var drawList = ImGui.GetWindowDrawList();
+ drawList.AddCircleFilled(center, radius, ImGui.ColorConvertFloat4ToU32(UiSharedService.AccentColor));
+ string badgeText = badgeCount > 9 ? "9+" : badgeCount.ToString();
+ var textSize = ImGui.CalcTextSize(badgeText);
+ drawList.AddText(center - textSize / 2f, ImGui.GetColorU32(ImGuiCol.Text), badgeText);
+ }
+
+ return clicked && enabled;
+ }
+
+
+ private void ToggleConnection()
+ {
+ if (_serverManager.CurrentServer == null) return;
+
+ _serverManager.CurrentServer.FullPause = !_serverManager.CurrentServer.FullPause;
+ _serverManager.Save();
+ _ = _apiController.CreateConnections();
+ }
+
+ private void DrawUnsupportedVersionBanner()
+ {
+ var ver = _apiController.CurrentClientVersion;
+ var unsupported = "UNSUPPORTED VERSION";
+ using (_uiSharedService.UidFont.Push())
+ {
+ var uidTextSize = ImGui.CalcTextSize(unsupported);
+ ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMax().X + ImGui.GetWindowContentRegionMin().X) / 2 - uidTextSize.X / 2);
+ ImGui.AlignTextToFramePadding();
+ ImGui.TextColored(UiSharedService.AccentColor, unsupported);
+ }
+
+ UiSharedService.ColorTextWrapped(
+ $"Your UmbraSync installation is out of date, the current version is {ver.Major}.{ver.Minor}.{ver.Build}. " +
+ "It is highly recommended to keep UmbraSync up to date. Open /xlplugins and update the plugin.",
+ UiSharedService.AccentColor);
+ }
+
+ private void DrawMainContent()
+ {
+ if (_activeSection is CompactUiSection.EditProfile)
+ {
+ _editProfileUi.DrawInline();
+ DrawNewUserNoteModal();
+ return;
+ }
+
+ bool requiresConnection = RequiresServerConnection(_activeSection);
+ if (requiresConnection && _apiController.ServerState is not ServerState.Connected)
+ {
+ UiSharedService.ColorTextWrapped("Connectez-vous au serveur pour accéder à cette section.", ImGuiColors.DalamudGrey3);
+ DrawNewUserNoteModal();
+ return;
+ }
+
+ switch (_activeSection)
+ {
+ case CompactUiSection.VisiblePairs:
+ DrawPairSection(PairContentMode.VisibleOnly);
+ break;
+ case CompactUiSection.IndividualPairs:
+ DrawPairSection(PairContentMode.All);
+ break;
+ case CompactUiSection.Syncshells:
+ DrawSyncshellSection();
+ break;
+ case CompactUiSection.AutoDetect:
+ DrawAutoDetectSection();
+ break;
+ }
+
+ DrawNewUserNoteModal();
+ }
+
+ private void DrawPairSection(PairContentMode mode)
+ {
+ DrawDefaultSyncSettings();
+ using (ImRaii.PushId("pairlist")) DrawPairList(mode);
+ ImGui.Separator();
+ using (ImRaii.PushId("transfers")) DrawTransfers();
+ TransferPartHeight = ImGui.GetCursorPosY() - TransferPartHeight;
+ using (ImRaii.PushId("group-user-popup")) _selectPairsForGroupUi.Draw(_pairManager.DirectPairs);
+ using (ImRaii.PushId("grouping-popup")) _selectGroupForPairUi.Draw();
+ }
+
+ private void DrawSyncshellSection()
+ {
+ using (ImRaii.PushId("syncshells")) _groupPanel.DrawSyncshells();
+ ImGui.Separator();
+ using (ImRaii.PushId("transfers")) DrawTransfers();
+ TransferPartHeight = ImGui.GetCursorPosY() - TransferPartHeight;
+ using (ImRaii.PushId("group-user-popup")) _selectPairsForGroupUi.Draw(_pairManager.DirectPairs);
+ using (ImRaii.PushId("grouping-popup")) _selectGroupForPairUi.Draw();
+ }
+
+ private void DrawAutoDetectSection()
+ {
+ using (ImRaii.PushId("autodetect-inline")) _autoDetectUi.DrawInline();
+ }
+
+ private void DrawNewUserNoteModal()
+ {
+ if (_configService.Current.OpenPopupOnAdd && _pairManager.LastAddedUser != null)
+ {
+ _lastAddedUser = _pairManager.LastAddedUser;
+ _pairManager.LastAddedUser = null;
+ ImGui.OpenPopup("Set Notes for New User");
+ _showModalForUserAddition = true;
+ _lastAddedUserComment = string.Empty;
+ }
+
+ if (ImGui.BeginPopupModal("Set Notes for New User", ref _showModalForUserAddition, UiSharedService.PopupWindowFlags))
+ {
+ if (_lastAddedUser == null)
+ {
+ _showModalForUserAddition = false;
+ }
+ else
+ {
+ 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"))
+ {
+ _serverManager.SetNoteForUid(_lastAddedUser.UserData.UID, _lastAddedUserComment);
+ _lastAddedUser = null;
+ _lastAddedUserComment = string.Empty;
+ _showModalForUserAddition = false;
+ }
+ }
+
+ UiSharedService.SetScaledWindowSize(275);
+ ImGui.EndPopup();
+ }
+ }
+
+ private static bool RequiresServerConnection(CompactUiSection section)
+ {
+ return section is CompactUiSection.VisiblePairs
+ or CompactUiSection.IndividualPairs
+ or CompactUiSection.Syncshells
+ or CompactUiSection.AutoDetect;
+ }
+
private bool IsAlreadyPairedQuickMenu(Services.Mediator.NearbyEntry entry)
{
try
@@ -811,7 +1129,6 @@ public class CompactUi : WindowMediatorSubscriberBase
private void DrawServerStatus()
{
- var buttonSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Link);
var userCount = _apiController.OnlineUsers.ToString(CultureInfo.InvariantCulture);
var userSize = ImGui.CalcTextSize(userCount);
var textSize = ImGui.CalcTextSize("Users Online");
@@ -841,43 +1158,6 @@ public class CompactUi : WindowMediatorSubscriberBase
ImGui.TextUnformatted(shardConnection);
}
- ImGui.SameLine();
- if (printShard)
- {
- ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ((userSize.Y + textSize.Y) / 2 + shardTextSize.Y) / 2 - ImGui.GetStyle().ItemSpacing.Y + buttonSize.Y / 2);
- }
- 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)
- {
- ImGui.SetCursorPosX(0 + ImGui.GetStyle().ItemSpacing.X);
- if (_uiSharedService.IconButton(FontAwesomeIcon.UserCircle))
- {
- Mediator.Publish(new UiToggleMessage(typeof(EditProfileUi)));
- }
- UiSharedService.AttachToolTip("Edit your Profile");
- }
-
- ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - buttonSize.X);
- if (printShard)
- {
- ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ((userSize.Y + textSize.Y) / 2 + shardTextSize.Y) / 2 - ImGui.GetStyle().ItemSpacing.Y + buttonSize.Y / 2);
- }
-
- if (_apiController.ServerState is not (ServerState.Reconnecting or ServerState.Disconnecting))
- {
- ImGui.PushStyleColor(ImGuiCol.Text, color);
- if (_uiSharedService.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);
- }
}
private void DrawTransfers()
@@ -923,25 +1203,12 @@ public class CompactUi : WindowMediatorSubscriberBase
ImGui.SameLine(WindowContentWidth - textSize.X);
ImGui.TextUnformatted(downloadText);
}
- var spacing = ImGui.GetStyle().ItemSpacing.X;
- var bottomButtonWidth = (WindowContentWidth - spacing) / 2f;
- if (_uiSharedService.IconTextButton(FontAwesomeIcon.PersonCircleQuestion, "Character Analysis", bottomButtonWidth))
- {
- Mediator.Publish(new UiToggleMessage(typeof(DataAnalysisUi)));
- }
-
- ImGui.SameLine();
- if (_uiSharedService.IconTextButton(FontAwesomeIcon.Running, "Character Data Hub", bottomButtonWidth))
- {
- Mediator.Publish(new UiToggleMessage(typeof(CharaDataHubUi)));
- }
ImGuiHelpers.ScaledDummy(2);
}
private void DrawUIDHeader()
{
var uidText = GetUidText();
- var buttonSizeX = 0f;
Vector2 uidTextSize;
using (_uiSharedService.UidFont.Push())
@@ -951,23 +1218,14 @@ public class CompactUi : WindowMediatorSubscriberBase
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 (_uiSharedService.IconButton(FontAwesomeIcon.Cog))
- {
- Mediator.Publish(new OpenSettingsUiMessage());
- }
- UiSharedService.AttachToolTip("Open the UmbraSync Settings");
-
- ImGui.SameLine();
- ImGui.SetCursorPos(originalPos);
+ Vector2 buttonSize = Vector2.Zero;
+ float spacingX = ImGui.GetStyle().ItemSpacing.X;
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);
+ buttonSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Copy);
+ ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - buttonSize.X);
+ ImGui.SetCursorPosY(originalPos.Y + uidTextSize.Y / 2f - buttonSize.Y / 2f);
if (_uiSharedService.IconButton(FontAwesomeIcon.Copy))
{
ImGui.SetClipboardText(_apiController.DisplayName);
@@ -975,10 +1233,18 @@ public class CompactUi : WindowMediatorSubscriberBase
UiSharedService.AttachToolTip("Copy your UID to clipboard");
ImGui.SameLine();
}
+
+ ImGui.SetCursorPos(originalPos);
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);
+ float referenceHeight = buttonSize.Y > 0f ? buttonSize.Y : ImGui.GetFrameHeight();
+ ImGui.SetCursorPosY(originalPos.Y + referenceHeight / 2f - uidTextSize.Y / 2f - spacingX / 2f);
+ float contentMin = ImGui.GetWindowContentRegionMin().X;
+ float contentMax = ImGui.GetWindowContentRegionMax().X;
+ float availableWidth = contentMax - contentMin;
+ float center = contentMin + availableWidth / 2f;
+ ImGui.SetCursorPosX(center - uidTextSize.X / 2f);
+
using (_uiSharedService.UidFont.Push())
ImGui.TextColored(GetUidColor(), uidText);
diff --git a/MareSynchronos/UI/Components/DrawGroupPair.cs b/MareSynchronos/UI/Components/DrawGroupPair.cs
index 3e42d59..e7565b1 100644
--- a/MareSynchronos/UI/Components/DrawGroupPair.cs
+++ b/MareSynchronos/UI/Components/DrawGroupPair.cs
@@ -45,6 +45,43 @@ public class DrawGroupPair : DrawPairBase
_serverConfigurationManager = serverConfigurationManager;
}
+ protected override float GetRightSideExtraWidth()
+ {
+ float width = 0f;
+ float spacing = ImGui.GetStyle().ItemSpacing.X;
+
+ var soundsDisabled = _fullInfoDto.GroupUserPermissions.IsDisableSounds();
+ var animDisabled = _fullInfoDto.GroupUserPermissions.IsDisableAnimations();
+ var vfxDisabled = _fullInfoDto.GroupUserPermissions.IsDisableVFX();
+ 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);
+
+ bool showInfo = individualAnimDisabled || individualSoundsDisabled || individualVFXDisabled || animDisabled || soundsDisabled || vfxDisabled;
+ bool showShared = _charaDataManager.SharedWithYouData.TryGetValue(_pair.UserData, out var sharedData);
+ bool showPlus = _pair.UserPair == null && _pair.IsOnline;
+
+ if (showShared)
+ {
+ width += _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Running).X + spacing;
+ }
+
+ if (showInfo)
+ {
+ var icon = (individualAnimDisabled || individualSoundsDisabled || individualVFXDisabled)
+ ? FontAwesomeIcon.ExclamationTriangle
+ : FontAwesomeIcon.InfoCircle;
+ width += UiSharedService.GetIconSize(icon).X + spacing;
+ }
+
+ if (showPlus)
+ {
+ width += _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Plus).X + spacing;
+ }
+
+ return width;
+ }
+
protected override void DrawLeftSide(float textPosY, float originalY)
{
var entryUID = _pair.UserData.AliasOrUID;
diff --git a/MareSynchronos/UI/Components/DrawPairBase.cs b/MareSynchronos/UI/Components/DrawPairBase.cs
index 54513b7..cc6c88d 100644
--- a/MareSynchronos/UI/Components/DrawPairBase.cs
+++ b/MareSynchronos/UI/Components/DrawPairBase.cs
@@ -1,5 +1,8 @@
-using Dalamud.Bindings.ImGui;
+using System;
+using System.Numerics;
+using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
+using Dalamud.Interface.Utility;
using MareSynchronos.PlayerData.Pairs;
using MareSynchronos.UI.Handlers;
using MareSynchronos.WebAPI;
@@ -28,38 +31,77 @@ public abstract class DrawPairBase
public void DrawPairedClient()
{
- var originalY = ImGui.GetCursorPosY();
- var pauseIconSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Play);
- var textSize = ImGui.CalcTextSize(_pair.UserData.AliasOrUID);
+ var style = ImGui.GetStyle();
+ var padding = style.FramePadding;
+ var spacing = style.ItemSpacing;
+ var rowStartCursor = ImGui.GetCursorPos();
+ var rowStartScreen = ImGui.GetCursorScreenPos();
- var startPos = ImGui.GetCursorStartPos();
+ var pauseButtonSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Pause);
+ var playButtonSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Play);
+ var menuButtonSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars);
- var framePadding = ImGui.GetStyle().FramePadding;
- var lineHeight = textSize.Y + framePadding.Y * 2;
+ float pauseClusterWidth = Math.Max(pauseButtonSize.X, playButtonSize.X);
+ float pauseClusterHeight = Math.Max(pauseButtonSize.Y, playButtonSize.Y);
+ float reservedSpacing = style.ItemSpacing.X * 2.4f;
+ float rightButtonWidth =
+ menuButtonSize.X +
+ pauseClusterWidth +
+ reservedSpacing +
+ GetRightSideExtraWidth();
- var off = startPos.Y;
- var height = UiSharedService.GetWindowContentRegionHeight();
+ float availableWidth = Math.Max(ImGui.GetContentRegionAvail().X - rightButtonWidth, 1f);
+ float textHeight = ImGui.GetFontSize();
+ var presenceIconSize = UiSharedService.GetIconSize(FontAwesomeIcon.Moon);
+ float iconHeight = presenceIconSize.Y;
+ float contentHeight = Math.Max(textHeight, Math.Max(iconHeight, pauseClusterHeight));
+ float rowHeight = contentHeight + padding.Y * 2f;
+ float totalHeight = rowHeight + spacing.Y;
- if ((originalY + off) < -lineHeight || (originalY + off) > height)
+ var origin = ImGui.GetCursorStartPos();
+ var top = origin.Y + rowStartCursor.Y;
+ var bottom = top + totalHeight;
+ var visibleHeight = UiSharedService.GetWindowContentRegionHeight();
+ if (bottom < 0 || top > visibleHeight)
{
- ImGui.Dummy(new System.Numerics.Vector2(0f, lineHeight));
+ ImGui.SetCursorPos(new Vector2(rowStartCursor.X, rowStartCursor.Y + totalHeight));
return;
}
- var textPosY = originalY + pauseIconSize.Y / 2 - textSize.Y / 2;
- DrawLeftSide(textPosY, originalY);
+ var drawList = ImGui.GetWindowDrawList();
+ var backgroundColor = new Vector4(0.10f, 0.10f, 0.14f, 0.95f);
+ var borderColor = new Vector4(0f, 0f, 0f, 0.9f);
+ float rounding = Math.Max(style.FrameRounding, 7f * ImGuiHelpers.GlobalScale);
+
+ var panelMin = rowStartScreen + new Vector2(0f, spacing.Y * 0.15f);
+ var panelMax = panelMin + new Vector2(availableWidth, rowHeight - spacing.Y * 0.3f);
+ drawList.AddRectFilled(panelMin, panelMax, ImGui.ColorConvertFloat4ToU32(backgroundColor), rounding);
+ drawList.AddRect(panelMin, panelMax, ImGui.ColorConvertFloat4ToU32(borderColor), rounding);
+
+ float iconTop = rowStartCursor.Y + (rowHeight - iconHeight) / 2f;
+ float textTop = rowStartCursor.Y + (rowHeight - textHeight) / 2f - padding.Y * 0.6f;
+ float buttonTop = rowStartCursor.Y + (rowHeight - pauseClusterHeight) / 2f;
+
+ ImGui.SetCursorPos(new Vector2(rowStartCursor.X + padding.X, iconTop));
+ DrawLeftSide(iconTop, iconTop);
ImGui.SameLine();
+ ImGui.SetCursorPosY(textTop);
var posX = ImGui.GetCursorPosX();
- var rightSide = DrawRightSide(textPosY, originalY);
- DrawName(originalY, posX, rightSide);
+ var rightSide = DrawRightSide(buttonTop, buttonTop);
+ DrawName(textTop + padding.Y * 0.15f, posX, rightSide);
+
+ ImGui.SetCursorPos(new Vector2(rowStartCursor.X, rowStartCursor.Y + totalHeight));
+ ImGui.SetCursorPosX(rowStartCursor.X);
}
protected abstract void DrawLeftSide(float textPosY, float originalY);
protected abstract float DrawRightSide(float textPosY, float originalY);
+ protected virtual float GetRightSideExtraWidth() => 0f;
+
private void DrawName(float originalY, float leftSide, float rightSide)
{
_displayHandler.DrawPairText(_id, _pair, leftSide, originalY, () => rightSide - leftSide);
}
-}
\ No newline at end of file
+}
diff --git a/MareSynchronos/UI/Components/DrawUserPair.cs b/MareSynchronos/UI/Components/DrawUserPair.cs
index 69fbbb8..45040c2 100644
--- a/MareSynchronos/UI/Components/DrawUserPair.cs
+++ b/MareSynchronos/UI/Components/DrawUserPair.cs
@@ -41,6 +41,28 @@ public class DrawUserPair : DrawPairBase
public bool IsVisible => _pair.IsVisible;
public UserPairDto UserPair => _pair.UserPair!;
+ protected override float GetRightSideExtraWidth()
+ {
+ float width = 0f;
+ var spacingX = ImGui.GetStyle().ItemSpacing.X;
+
+ 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);
+
+ if (individualSoundsDisabled || individualAnimDisabled || individualVFXDisabled)
+ {
+ width += _uiSharedService.GetIconButtonSize(FontAwesomeIcon.ExclamationTriangle).X + spacingX * 0.5f;
+ }
+
+ if (_charaDataManager.SharedWithYouData.TryGetValue(_pair.UserData, out var sharedData))
+ {
+ width += _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Running).X + spacingX * 0.5f;
+ }
+
+ return width;
+ }
+
protected override void DrawLeftSide(float textPosY, float originalY)
{
var online = _pair.IsOnline;
@@ -110,7 +132,8 @@ public class DrawUserPair : DrawPairBase
var barButtonSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars);
var entryUID = _pair.UserData.AliasOrUID;
var spacingX = ImGui.GetStyle().ItemSpacing.X;
- var windowEndX = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth();
+ var edgePadding = ImGui.GetStyle().WindowPadding.X + 4f * ImGuiHelpers.GlobalScale;
+ var windowEndX = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - edgePadding;
var rightSidePos = windowEndX - barButtonSize.X;
// Flyout Menu
diff --git a/MareSynchronos/UI/Components/GroupPanel.cs b/MareSynchronos/UI/Components/GroupPanel.cs
index 5a71c80..06a3a4a 100644
--- a/MareSynchronos/UI/Components/GroupPanel.cs
+++ b/MareSynchronos/UI/Components/GroupPanel.cs
@@ -313,19 +313,20 @@ internal sealed class GroupPanel
int shellNumber = _serverConfigurationManager.GetShellNumberForGid(groupDto.GID);
var name = groupDto.Group.Alias ?? groupDto.GID;
- if (!_expandedGroupState.TryGetValue(groupDto.GID, out bool isExpanded))
+ if (!_expandedGroupState.ContainsKey(groupDto.GID))
{
- isExpanded = false;
- _expandedGroupState.Add(groupDto.GID, isExpanded);
+ _expandedGroupState[groupDto.GID] = false;
}
- var icon = isExpanded ? FontAwesomeIcon.CaretSquareDown : FontAwesomeIcon.CaretSquareRight;
- _uiShared.IconText(icon);
- if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
+ UiSharedService.DrawCard($"syncshell-card-{groupDto.GID}", () =>
{
- _expandedGroupState[groupDto.GID] = !_expandedGroupState[groupDto.GID];
+ bool expandedState = _expandedGroupState[groupDto.GID];
+ UiSharedService.DrawArrowToggle(ref expandedState, $"##syncshell-toggle-{groupDto.GID}");
+ if (expandedState != _expandedGroupState[groupDto.GID])
+ {
+ _expandedGroupState[groupDto.GID] = expandedState;
}
- ImGui.SameLine();
+ ImGui.SameLine(0f, 6f * ImGuiHelpers.GlobalScale);
var textIsGid = true;
string groupName = groupDto.GroupAliasOrGID;
@@ -547,7 +548,7 @@ internal sealed class GroupPanel
bool hideOfflineUsers = pairsInGroup.Count > 1000;
ImGui.Indent(20);
- if (_expandedGroupState[groupDto.GID])
+ if (expandedState)
{
var sortedPairs = pairsInGroup
.OrderByDescending(u => string.Equals(u.UserData.UID, groupDto.OwnerUID, StringComparison.Ordinal))
@@ -614,6 +615,9 @@ internal sealed class GroupPanel
ImGui.Separator();
}
ImGui.Unindent(20);
+ }, background: new Vector4(0.15f, 0.15f, 0.20f, 0.94f), border: new Vector4(0f, 0f, 0f, 0.78f), stretchWidth: true);
+
+ ImGuiHelpers.ScaledDummy(4f);
}
private void DrawSyncShellButtons(GroupFullInfoDto groupDto, List groupPairs)
@@ -828,9 +832,22 @@ internal sealed class GroupPanel
private void DrawSyncshellList()
{
- var ySize = _mainUi.TransferPartHeight == 0
- ? 1
- : (ImGui.GetWindowContentRegionMax().Y - ImGui.GetWindowContentRegionMin().Y) - _mainUi.TransferPartHeight - ImGui.GetCursorPosY();
+ float availableHeight = ImGui.GetContentRegionAvail().Y;
+ float ySize;
+ if (_mainUi.TransferPartHeight <= 0)
+ {
+ float reserve = ImGui.GetFrameHeightWithSpacing() * 2f;
+ ySize = availableHeight - reserve;
+ if (ySize <= 0)
+ {
+ ySize = System.Math.Max(availableHeight, 1f);
+ }
+ }
+ else
+ {
+ ySize = (ImGui.GetWindowContentRegionMax().Y - ImGui.GetWindowContentRegionMin().Y) - _mainUi.TransferPartHeight - ImGui.GetCursorPosY();
+ }
+
ImGui.BeginChild("list", new Vector2(_mainUi.WindowContentWidth, ySize), border: false);
foreach (var entry in _pairManager.GroupPairs.OrderBy(g => g.Key.Group.AliasOrGID, StringComparer.OrdinalIgnoreCase).ToList())
{
diff --git a/MareSynchronos/UI/Components/PairGroupsUi.cs b/MareSynchronos/UI/Components/PairGroupsUi.cs
index e7a1c80..5e67b7b 100644
--- a/MareSynchronos/UI/Components/PairGroupsUi.cs
+++ b/MareSynchronos/UI/Components/PairGroupsUi.cs
@@ -1,10 +1,14 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
+using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using MareSynchronos.API.Data.Extensions;
using MareSynchronos.MareConfiguration;
using MareSynchronos.UI.Handlers;
using MareSynchronos.WebAPI;
+using System;
+using System.Collections.Generic;
+using System.Linq;
namespace MareSynchronos.UI.Components;
@@ -28,7 +32,7 @@ public class PairGroupsUi
_uiSharedService = uiSharedService;
}
- public void Draw(List visibleUsers, List onlineUsers, List offlineUsers) where T : DrawPairBase
+ public void Draw(List visibleUsers, List onlineUsers, List offlineUsers, Action? drawVisibleExtras = null) where T : DrawPairBase
{
// Only render those tags that actually have pairs in them, otherwise
// we can end up with a bunch of useless pair groups
@@ -36,7 +40,7 @@ public class PairGroupsUi
var allUsers = onlineUsers.Concat(offlineUsers).ToList();
if (typeof(T) == typeof(DrawUserPair))
{
- DrawUserPairs(tagsWithPairsInThem, allUsers.Cast().ToList(), visibleUsers.Cast(), onlineUsers.Cast(), offlineUsers.Cast());
+ DrawUserPairs(tagsWithPairsInThem, allUsers.Cast().ToList(), visibleUsers.Cast(), onlineUsers.Cast(), offlineUsers.Cast(), drawVisibleExtras);
}
}
@@ -44,14 +48,15 @@ public class PairGroupsUi
{
var allArePaused = availablePairsInThisTag.All(pair => pair.UserPair!.OwnPermissions.IsPaused());
var pauseButton = allArePaused ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause;
- var flyoutMenuX = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars).X;
- var pauseButtonX = _uiSharedService.GetIconButtonSize(pauseButton).X;
- var windowX = ImGui.GetWindowContentRegionMin().X;
- var windowWidth = UiSharedService.GetWindowContentRegionWidth();
+ var flyoutMenuSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars);
+ var pauseButtonSize = _uiSharedService.GetIconButtonSize(pauseButton);
var spacingX = ImGui.GetStyle().ItemSpacing.X;
+ var currentX = ImGui.GetCursorPosX();
+ var availableWidth = ImGui.GetContentRegionAvail().X;
+ var buttonsWidth = pauseButtonSize.X + flyoutMenuSize.X + spacingX;
+ var pauseStart = Math.Max(currentX, currentX + availableWidth - buttonsWidth);
- var buttonPauseOffset = windowX + windowWidth - flyoutMenuX - spacingX - pauseButtonX;
- ImGui.SameLine(buttonPauseOffset);
+ ImGui.SameLine(pauseStart);
if (_uiSharedService.IconButton(pauseButton))
{
if (allArePaused)
@@ -72,8 +77,8 @@ public class PairGroupsUi
UiSharedService.AttachToolTip($"Pause pairing with all pairs in {tag}");
}
- var buttonDeleteOffset = windowX + windowWidth - flyoutMenuX;
- ImGui.SameLine(buttonDeleteOffset);
+ var menuStart = Math.Max(pauseStart + pauseButtonSize.X + spacingX, currentX);
+ ImGui.SameLine(menuStart);
if (_uiSharedService.IconButton(FontAwesomeIcon.Bars))
{
ImGui.OpenPopup("Group Flyout Menu");
@@ -86,7 +91,7 @@ public class PairGroupsUi
}
}
- private void DrawCategory(string tag, IEnumerable onlineUsers, IEnumerable allUsers, IEnumerable? visibleUsers = null)
+ private void DrawCategory(string tag, IEnumerable onlineUsers, IEnumerable allUsers, IEnumerable? visibleUsers = null, Action? drawExtraContent = null)
{
IEnumerable usersInThisTag;
HashSet? otherUidsTaggedWithTag = null;
@@ -108,26 +113,25 @@ public class PairGroupsUi
if (isSpecialTag && !usersInThisTag.Any()) return;
- DrawName(tag, isSpecialTag, visibleInThisTag, usersInThisTag.Count(), otherUidsTaggedWithTag?.Count);
- if (!isSpecialTag)
+ UiSharedService.DrawCard($"pair-group-{tag}", () =>
{
- using (ImRaii.PushId($"group-{tag}-buttons")) DrawButtons(tag, allUsers.Cast().Where(p => otherUidsTaggedWithTag!.Contains(p.UID)).ToList());
- }
- else
- {
- if (!_tagHandler.IsTagOpen(tag))
+ DrawName(tag, isSpecialTag, visibleInThisTag, usersInThisTag.Count(), otherUidsTaggedWithTag?.Count);
+ if (!isSpecialTag)
{
- var size = ImGui.CalcTextSize("").Y + ImGui.GetStyle().FramePadding.Y * 2f;
- ImGui.SameLine();
- ImGui.Dummy(new(size, size));
+ using (ImRaii.PushId($"group-{tag}-buttons")) DrawButtons(tag, allUsers.Cast().Where(p => otherUidsTaggedWithTag!.Contains(p.UID)).ToList());
}
- }
- if (!_tagHandler.IsTagOpen(tag)) return;
+ if (!_tagHandler.IsTagOpen(tag)) return;
- ImGui.Indent(20);
- DrawPairs(tag, usersInThisTag);
- ImGui.Unindent(20);
+ ImGuiHelpers.ScaledDummy(4f);
+ var indent = 18f * ImGuiHelpers.GlobalScale;
+ ImGui.Indent(indent);
+ DrawPairs(tag, usersInThisTag);
+ drawExtraContent?.Invoke();
+ ImGui.Unindent(indent);
+ }, stretchWidth: true);
+
+ ImGuiHelpers.ScaledDummy(4f);
}
private void DrawGroupMenu(string tag)
@@ -157,17 +161,21 @@ public class PairGroupsUi
};
string resultFolderName = !isSpecialTag ? $"{displayedName} ({visible}/{online}/{total} Pairs)" : $"{displayedName} ({online} Pairs)";
- var icon = _tagHandler.IsTagOpen(tag) ? FontAwesomeIcon.CaretSquareDown : FontAwesomeIcon.CaretSquareRight;
- _uiSharedService.IconText(icon);
- if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
+ bool isOpen = _tagHandler.IsTagOpen(tag);
+ bool previousState = isOpen;
+ UiSharedService.DrawArrowToggle(ref isOpen, $"##group-toggle-{tag}");
+ if (isOpen != previousState)
{
- ToggleTagOpen(tag);
+ _tagHandler.SetTagOpen(tag, isOpen);
}
- ImGui.SameLine();
+ ImGui.SameLine(0f, 6f * ImGuiHelpers.GlobalScale);
+ ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted(resultFolderName);
if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
{
- ToggleTagOpen(tag);
+ bool newState = !_tagHandler.IsTagOpen(tag);
+ _tagHandler.SetTagOpen(tag, newState);
+ isOpen = newState;
}
if (!isSpecialTag && ImGui.IsItemHovered())
@@ -186,14 +194,13 @@ public class PairGroupsUi
{
// These are all the OtherUIDs that are tagged with this tag
_uidDisplayHandler.RenderPairList(availablePairsInThisCategory);
- ImGui.Separator();
}
- private void DrawUserPairs(List tagsWithPairsInThem, List allUsers, IEnumerable visibleUsers, IEnumerable onlineUsers, IEnumerable offlineUsers)
+ private void DrawUserPairs(List tagsWithPairsInThem, List allUsers, IEnumerable visibleUsers, IEnumerable onlineUsers, IEnumerable offlineUsers, Action? drawVisibleExtras)
{
if (_mareConfig.Current.ShowVisibleUsersSeparately)
{
- using (ImRaii.PushId("$group-VisibleCustomTag")) DrawCategory(TagHandler.CustomVisibleTag, visibleUsers, allUsers);
+ using (ImRaii.PushId("$group-VisibleCustomTag")) DrawCategory(TagHandler.CustomVisibleTag, visibleUsers, allUsers, drawExtraContent: drawVisibleExtras);
}
foreach (var tag in tagsWithPairsInThem)
{
@@ -247,4 +254,4 @@ public class PairGroupsUi
bool open = !_tagHandler.IsTagOpen(tag);
_tagHandler.SetTagOpen(tag, open);
}
-}
\ No newline at end of file
+}
diff --git a/MareSynchronos/UI/EditProfileUi.cs b/MareSynchronos/UI/EditProfileUi.cs
index cd1f81f..32221b8 100644
--- a/MareSynchronos/UI/EditProfileUi.cs
+++ b/MareSynchronos/UI/EditProfileUi.cs
@@ -64,6 +64,16 @@ public class EditProfileUi : WindowMediatorSubscriberBase
}
protected override void DrawInternal()
+ {
+ DrawProfileContent();
+ }
+
+ public void DrawInline()
+ {
+ DrawProfileContent();
+ }
+
+ private void DrawProfileContent()
{
_uiSharedService.BigText("Current Profile (as saved on server)");
ImGuiHelpers.ScaledDummy(new Vector2(0f, ImGui.GetStyle().ItemSpacing.Y / 2));
diff --git a/MareSynchronos/UI/SettingsUi.cs b/MareSynchronos/UI/SettingsUi.cs
index c0015cf..88f503a 100644
--- a/MareSynchronos/UI/SettingsUi.cs
+++ b/MareSynchronos/UI/SettingsUi.cs
@@ -131,6 +131,11 @@ public class SettingsUi : WindowMediatorSubscriberBase
DrawSettingsContent();
}
+ public void DrawInline()
+ {
+ DrawSettingsContent();
+ }
+
public override void OnClose()
{
_uiShared.EditTrackerPosition = false;
@@ -1002,7 +1007,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
_lastTab = "General";
- _uiShared.BigText("Notes");
+ _uiShared.BigText("Notes");
if (_uiShared.IconTextButton(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()));
diff --git a/MareSynchronos/UI/UISharedService.cs b/MareSynchronos/UI/UISharedService.cs
index 73868ce..174cf24 100644
--- a/MareSynchronos/UI/UISharedService.cs
+++ b/MareSynchronos/UI/UISharedService.cs
@@ -20,6 +20,7 @@ using MareSynchronos.Services.Mediator;
using MareSynchronos.Services.ServerConfiguration;
using MareSynchronos.WebAPI;
using Microsoft.Extensions.Logging;
+using System;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
@@ -216,7 +217,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
public static bool CtrlPressed() => (GetKeyState(0xA2) & 0x8000) != 0 || (GetKeyState(0xA3) & 0x8000) != 0;
- public static void DrawGrouped(Action imguiDrawAction, float rounding = 5f, float? expectedWidth = null)
+ public static void DrawGrouped(Action imguiDrawAction, float rounding = 5f, float? expectedWidth = null, bool drawBorder = true)
{
var cursorPos = ImGui.GetCursorPos();
using (ImRaii.Group())
@@ -230,10 +231,122 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
imguiDrawAction.Invoke();
}
- ImGui.GetWindowDrawList().AddRect(
- ImGui.GetItemRectMin() - ImGui.GetStyle().ItemInnerSpacing,
- ImGui.GetItemRectMax() + ImGui.GetStyle().ItemInnerSpacing,
- Color(ImGuiColors.DalamudGrey2), rounding);
+ if (drawBorder)
+ {
+ ImGui.GetWindowDrawList().AddRect(
+ ImGui.GetItemRectMin() - ImGui.GetStyle().ItemInnerSpacing,
+ ImGui.GetItemRectMax() + ImGui.GetStyle().ItemInnerSpacing,
+ Color(ImGuiColors.DalamudGrey2), rounding);
+ }
+ }
+
+ public static void DrawCard(string id, Action draw, Vector2? padding = null, Vector4? background = null,
+ Vector4? border = null, float? rounding = null, bool stretchWidth = false)
+ {
+ var style = ImGui.GetStyle();
+ var padBase = style.FramePadding;
+ var pad = padding ?? new Vector2(
+ padBase.X + 4f * ImGuiHelpers.GlobalScale,
+ padBase.Y + 3f * ImGuiHelpers.GlobalScale);
+ var cardBg = background ?? new Vector4(0.08f, 0.08f, 0.10f, 0.94f);
+ var cardBorder = border ?? new Vector4(0f, 0f, 0f, 0.85f);
+ float cardRounding = rounding ?? Math.Max(style.FrameRounding, 8f * ImGuiHelpers.GlobalScale);
+ float borderThickness = Math.Max(1f, Math.Max(style.FrameBorderSize, 1f) * ImGuiHelpers.GlobalScale);
+ float borderInset = borderThickness;
+
+ var originalCursor = ImGui.GetCursorPos();
+ if (stretchWidth)
+ {
+ ImGui.SetCursorPosX(ImGui.GetWindowContentRegionMin().X);
+ }
+
+ var startCursor = ImGui.GetCursorPos();
+ var drawList = ImGui.GetWindowDrawList();
+ drawList.ChannelsSplit(2);
+ drawList.ChannelsSetCurrent(1);
+
+ ImGui.PushID(id);
+ ImGui.SetCursorPos(new Vector2(startCursor.X + pad.X, startCursor.Y + pad.Y));
+ ImGui.BeginGroup();
+ draw();
+ ImGui.EndGroup();
+ ImGui.PopID();
+
+ var contentMin = ImGui.GetItemRectMin();
+ var contentMax = ImGui.GetItemRectMax();
+ var cardMin = contentMin - pad;
+ var cardMax = contentMax + pad;
+ var outerMin = cardMin;
+ var outerMax = cardMax;
+
+ if (stretchWidth)
+ {
+ var windowPos = ImGui.GetWindowPos();
+ var regionMin = ImGui.GetWindowContentRegionMin();
+ var regionMax = ImGui.GetWindowContentRegionMax();
+ var scrollX = ImGui.GetScrollX();
+ cardMin.X = windowPos.X + regionMin.X + scrollX;
+ cardMax.X = windowPos.X + regionMax.X + scrollX;
+ outerMin.X = cardMin.X;
+ outerMax.X = cardMax.X;
+ startCursor.X = ImGui.GetWindowContentRegionMin().X;
+ }
+
+ var drawMin = new Vector2(cardMin.X + borderInset, cardMin.Y + borderInset);
+ var drawMax = new Vector2(cardMax.X - borderInset, cardMax.Y - borderInset);
+ var clipMin = drawList.GetClipRectMin();
+ var clipMax = drawList.GetClipRectMax();
+ var clipInset = new Vector2(borderThickness * 0.5f + 0.5f, borderThickness * 0.5f + 0.5f);
+ drawMin = Vector2.Max(drawMin, clipMin + clipInset);
+ drawMax = Vector2.Min(drawMax, clipMax - clipInset);
+ if (drawMax.X <= drawMin.X)
+ {
+ drawMax.X = drawMin.X + borderThickness;
+ }
+ if (drawMax.Y <= drawMin.Y)
+ {
+ drawMax.Y = drawMin.Y + borderThickness;
+ }
+
+ drawList.ChannelsSetCurrent(0);
+ drawList.AddRectFilled(drawMin, drawMax, ImGui.ColorConvertFloat4ToU32(cardBg), cardRounding);
+ if (cardBorder.W > 0f && borderThickness > 0f)
+ {
+ drawList.AddRect(drawMin, drawMax, ImGui.ColorConvertFloat4ToU32(cardBorder), cardRounding, ImDrawFlags.None, borderThickness);
+ }
+ drawList.ChannelsMerge();
+
+ ImGui.SetCursorPos(startCursor);
+ var dummyWidth = outerMax.X - outerMin.X;
+ var dummyHeight = outerMax.Y - outerMin.Y;
+ ImGui.Dummy(new Vector2(dummyWidth, dummyHeight));
+ ImGui.SetCursorPos(new Vector2(startCursor.X, startCursor.Y + dummyHeight));
+
+ if (!stretchWidth)
+ {
+ ImGui.SetCursorPosX(originalCursor.X);
+ }
+ else
+ {
+ ImGui.SetCursorPosX(startCursor.X);
+ }
+ }
+
+ public static bool DrawArrowToggle(ref bool state, string id)
+ {
+ var framePadding = ImGui.GetStyle().FramePadding;
+ ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(framePadding.X, framePadding.Y * 0.85f));
+ ImGui.PushStyleColor(ImGuiCol.Button, Vector4.Zero);
+ ImGui.PushStyleColor(ImGuiCol.ButtonHovered, new Vector4(1f, 1f, 1f, 0.08f));
+ ImGui.PushStyleColor(ImGuiCol.ButtonActive, new Vector4(1f, 1f, 1f, 0.16f));
+ bool clicked = ImGui.ArrowButton(id, state ? ImGuiDir.Down : ImGuiDir.Right);
+ ImGui.PopStyleColor(3);
+ ImGui.PopStyleVar();
+ if (clicked)
+ {
+ state = !state;
+ }
+ return state;
}
public static void DrawGroupedCenteredColorText(string text, Vector4 color, float? maxWidth = null)