Merge 2.0.3 into branch
This commit is contained in:
@@ -34,8 +34,21 @@ namespace LightlessSync.UI;
|
||||
|
||||
public class CompactUi : WindowMediatorSubscriberBase
|
||||
{
|
||||
private readonly CharacterAnalyzer _characterAnalyzer;
|
||||
#region Constants
|
||||
|
||||
private const float ConnectButtonHighlightThickness = 14f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Services
|
||||
|
||||
private readonly ApiController _apiController;
|
||||
private readonly CharacterAnalyzer _characterAnalyzer;
|
||||
private readonly DalamudUtilService _dalamudUtilService;
|
||||
private readonly DrawEntityFactory _drawEntityFactory;
|
||||
private readonly FileUploadManager _fileTransferManager;
|
||||
private readonly IpcManager _ipcManager;
|
||||
private readonly LightFinderService _broadcastService;
|
||||
private readonly LightlessConfigService _configService;
|
||||
private readonly LightlessMediator _lightlessMediator;
|
||||
private readonly PairLedger _pairLedger;
|
||||
@@ -44,34 +57,46 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
private readonly FileUploadManager _fileTransferManager;
|
||||
private readonly PlayerPerformanceConfigService _playerPerformanceConfig;
|
||||
private readonly PairUiService _pairUiService;
|
||||
private readonly SelectTagForPairUi _selectTagForPairUi;
|
||||
private readonly SelectTagForSyncshellUi _selectTagForSyncshellUi;
|
||||
private readonly SelectSyncshellForTagUi _selectSyncshellForTagUi;
|
||||
private readonly RenameSyncshellTagUi _renameSyncshellTagUi;
|
||||
private readonly SelectPairForTagUi _selectPairsForGroupUi;
|
||||
private readonly RenamePairTagUi _renamePairTagUi;
|
||||
private readonly IpcManager _ipcManager;
|
||||
private readonly PlayerPerformanceConfigService _playerPerformanceConfig;
|
||||
private readonly ServerConfigurationManager _serverManager;
|
||||
private readonly TopTabMenu _tabMenu;
|
||||
private readonly TagHandler _tagHandler;
|
||||
private readonly UiSharedService _uiSharedService;
|
||||
private readonly LightFinderService _broadcastService;
|
||||
private readonly DalamudUtilService _dalamudUtilService;
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region UI Components
|
||||
|
||||
private readonly AnimatedHeader _animatedHeader = new();
|
||||
private readonly RenamePairTagUi _renamePairTagUi;
|
||||
private readonly RenameSyncshellTagUi _renameSyncshellTagUi;
|
||||
private readonly SelectPairForTagUi _selectPairsForGroupUi;
|
||||
private readonly SelectSyncshellForTagUi _selectSyncshellForTagUi;
|
||||
private readonly SelectTagForPairUi _selectTagForPairUi;
|
||||
private readonly SelectTagForSyncshellUi _selectTagForSyncshellUi;
|
||||
private readonly SeluneBrush _seluneBrush = new();
|
||||
private readonly TopTabMenu _tabMenu;
|
||||
|
||||
#endregion
|
||||
|
||||
#region State
|
||||
|
||||
private readonly ConcurrentDictionary<GameObjectHandler, Dictionary<string, FileDownloadStatus>> _currentDownloads = new();
|
||||
private List<IDrawFolder> _drawFolders;
|
||||
private Pair? _focusedPair;
|
||||
private Pair? _lastAddedUser;
|
||||
private string _lastAddedUserComment = string.Empty;
|
||||
private Vector2 _lastPosition = Vector2.One;
|
||||
private Vector2 _lastSize = Vector2.One;
|
||||
private int _pendingFocusFrame = -1;
|
||||
private Pair? _pendingFocusPair;
|
||||
private bool _showModalForUserAddition;
|
||||
private float _transferPartHeight;
|
||||
private bool _wasOpen;
|
||||
private float _windowContentWidth;
|
||||
private readonly SeluneBrush _seluneBrush = new();
|
||||
private const float _connectButtonHighlightThickness = 14f;
|
||||
private Pair? _focusedPair;
|
||||
private Pair? _pendingFocusPair;
|
||||
private int _pendingFocusFrame = -1;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
public CompactUi(
|
||||
ILogger<CompactUi> logger,
|
||||
@@ -127,6 +152,11 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
.Apply();
|
||||
|
||||
_drawFolders = [.. DrawFolders];
|
||||
|
||||
_animatedHeader.Height = 120f;
|
||||
_animatedHeader.EnableBottomGradient = true;
|
||||
_animatedHeader.GradientHeight = 250f;
|
||||
_animatedHeader.EnableParticles = _configService.Current.EnableParticleEffects;
|
||||
|
||||
#if DEBUG
|
||||
string dev = "Dev Build";
|
||||
@@ -150,9 +180,14 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
_lightlessMediator = mediator;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lifecycle
|
||||
|
||||
public override void OnClose()
|
||||
{
|
||||
ForceReleaseFocus();
|
||||
_animatedHeader.ClearParticles();
|
||||
base.OnClose();
|
||||
}
|
||||
|
||||
@@ -164,6 +199,13 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
using var selune = Selune.Begin(_seluneBrush, drawList, windowPos, windowSize);
|
||||
|
||||
_windowContentWidth = UiSharedService.GetWindowContentRegionWidth();
|
||||
|
||||
// Draw animated header background (just the gradient/particles, content drawn by existing methods)
|
||||
var startCursorY = ImGui.GetCursorPosY();
|
||||
_animatedHeader.Draw(_windowContentWidth, (_, _) => { });
|
||||
// Reset cursor to draw content on top of the header background
|
||||
ImGui.SetCursorPosY(startCursorY);
|
||||
|
||||
if (!_apiController.IsCurrentVersion)
|
||||
{
|
||||
var ver = _apiController.CurrentClientVersion;
|
||||
@@ -209,17 +251,11 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
}
|
||||
|
||||
using (ImRaii.PushId("header")) DrawUIDHeader();
|
||||
_uiSharedService.RoundedSeparator(UIColors.Get("LightlessPurple"), 2.5f, 1f, 12f);
|
||||
using (ImRaii.PushId("serverstatus"))
|
||||
{
|
||||
DrawServerStatus();
|
||||
}
|
||||
selune.DrawHighlightOnly(ImGui.GetIO().DeltaTime);
|
||||
var style = ImGui.GetStyle();
|
||||
var contentMinY = windowPos.Y + ImGui.GetWindowContentRegionMin().Y;
|
||||
var gradientInset = 4f * ImGuiHelpers.GlobalScale;
|
||||
var gradientTop = MathF.Max(contentMinY, ImGui.GetCursorScreenPos().Y - style.ItemSpacing.Y + gradientInset);
|
||||
ImGui.Separator();
|
||||
|
||||
if (_apiController.ServerState is ServerState.Connected)
|
||||
{
|
||||
@@ -227,7 +263,6 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
|
||||
using (ImRaii.PushId("global-topmenu")) _tabMenu.Draw(pairSnapshot);
|
||||
using (ImRaii.PushId("pairlist")) DrawPairs();
|
||||
ImGui.Separator();
|
||||
var transfersTop = ImGui.GetCursorScreenPos().Y;
|
||||
var gradientBottom = MathF.Max(gradientTop, transfersTop - style.ItemSpacing.Y - gradientInset);
|
||||
selune.DrawGradient(gradientTop, gradientBottom, ImGui.GetIO().DeltaTime);
|
||||
@@ -290,6 +325,10 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Content Drawing
|
||||
|
||||
private void DrawPairs()
|
||||
{
|
||||
float ySize = Math.Abs(_transferPartHeight) < 0.0001f
|
||||
@@ -308,95 +347,6 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
ImGui.EndChild();
|
||||
}
|
||||
|
||||
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");
|
||||
#if DEBUG
|
||||
string shardConnection = $"Shard: {_apiController.ServerInfo.ShardName}";
|
||||
#else
|
||||
string shardConnection = string.Equals(_apiController.ServerInfo.ShardName, "Main", StringComparison.OrdinalIgnoreCase) ? string.Empty : $"Shard: {_apiController.ServerInfo.ShardName}";
|
||||
#endif
|
||||
var shardTextSize = ImGui.CalcTextSize(shardConnection);
|
||||
var printShard = !string.IsNullOrEmpty(_apiController.ServerInfo.ShardName) && shardConnection != string.Empty;
|
||||
|
||||
if (_apiController.ServerState is ServerState.Connected)
|
||||
{
|
||||
ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth()) / 2 - (userSize.X + textSize.X) / 2 - ImGui.GetStyle().ItemSpacing.X / 2);
|
||||
if (!printShard) ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextColored(UIColors.Get("LightlessPurple"), userCount);
|
||||
ImGui.SameLine();
|
||||
if (!printShard) ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted("Users Online");
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextColored(UIColors.Get("DimRed"), "Not connected to any server");
|
||||
}
|
||||
|
||||
if (printShard)
|
||||
{
|
||||
ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ImGui.GetStyle().ItemSpacing.Y);
|
||||
ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth()) / 2 - shardTextSize.X / 2);
|
||||
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);
|
||||
}
|
||||
bool isConnectingOrConnected = _apiController.ServerState is ServerState.Connected or ServerState.Connecting or ServerState.Reconnecting;
|
||||
var color = UiSharedService.GetBoolColor(!isConnectingOrConnected);
|
||||
var connectedIcon = isConnectingOrConnected ? FontAwesomeIcon.Unlink : FontAwesomeIcon.Link;
|
||||
|
||||
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))
|
||||
{
|
||||
using (ImRaii.PushColor(ImGuiCol.Text, color))
|
||||
{
|
||||
if (_uiSharedService.IconButton(connectedIcon))
|
||||
{
|
||||
if (isConnectingOrConnected && !_serverManager.CurrentServer.FullPause)
|
||||
{
|
||||
_serverManager.CurrentServer.FullPause = true;
|
||||
_serverManager.Save();
|
||||
}
|
||||
else if (!isConnectingOrConnected && _serverManager.CurrentServer.FullPause)
|
||||
{
|
||||
_serverManager.CurrentServer.FullPause = false;
|
||||
_serverManager.Save();
|
||||
}
|
||||
|
||||
_ = _apiController.CreateConnectionsAsync();
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenBlockedByActiveItem) || ImGui.IsItemActive())
|
||||
{
|
||||
Selune.RegisterHighlight(
|
||||
ImGui.GetItemRectMin(),
|
||||
ImGui.GetItemRectMax(),
|
||||
SeluneHighlightMode.Both,
|
||||
borderOnly: true,
|
||||
borderThicknessOverride: _connectButtonHighlightThickness,
|
||||
exactSize: true,
|
||||
clipToElement: true,
|
||||
roundingOverride: ImGui.GetStyle().FrameRounding);
|
||||
}
|
||||
|
||||
UiSharedService.AttachToolTip(isConnectingOrConnected ? "Disconnect from " + _serverManager.CurrentServer.ServerName : "Connect to " + _serverManager.CurrentServer.ServerName);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawTransfers()
|
||||
{
|
||||
var currentUploads = _fileTransferManager.GetCurrentUploadsSnapshot();
|
||||
@@ -492,11 +442,9 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
return new DownloadSummary(totalFiles, transferredFiles, transferredBytes, totalBytes);
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
private readonly record struct DownloadSummary(int TotalFiles, int TransferredFiles, long TransferredBytes, long TotalBytes)
|
||||
{
|
||||
public bool HasDownloads => TotalFiles > 0 || TotalBytes > 0;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Header Drawing
|
||||
|
||||
private void DrawUIDHeader()
|
||||
{
|
||||
@@ -532,21 +480,52 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
using (_uiSharedService.IconFont.Push())
|
||||
iconSize = ImGui.CalcTextSize(FontAwesomeIcon.PersonCirclePlus.ToIconString());
|
||||
|
||||
float contentWidth = ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X;
|
||||
float uidStartX = (contentWidth - uidTextSize.X) / 2f;
|
||||
float uidStartX = 25f;
|
||||
float cursorY = ImGui.GetCursorPosY();
|
||||
|
||||
ImGui.SetCursorPosY(cursorY);
|
||||
ImGui.SetCursorPosX(uidStartX);
|
||||
|
||||
bool headerItemClicked;
|
||||
using (_uiSharedService.UidFont.Push())
|
||||
{
|
||||
if (useVanityColors)
|
||||
{
|
||||
var seString = SeStringUtils.BuildFormattedPlayerName(uidText, vanityTextColor, vanityGlowColor);
|
||||
var cursorPos = ImGui.GetCursorScreenPos();
|
||||
var targetFontSize = ImGui.GetFontSize();
|
||||
var font = ImGui.GetFont();
|
||||
SeStringUtils.RenderSeStringWithHitbox(seString, cursorPos, targetFontSize ,font , "uid-header");
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.TextColored(uidColor, uidText);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the actual rendered text rect for proper icon alignment
|
||||
var uidTextRect = ImGui.GetItemRectMax() - ImGui.GetItemRectMin();
|
||||
var uidTextRectMin = ImGui.GetItemRectMin();
|
||||
var uidTextHovered = ImGui.IsItemHovered();
|
||||
headerItemClicked = ImGui.IsItemClicked();
|
||||
|
||||
// Track position for icons next to UID text
|
||||
// Use uidTextSize.Y (actual font height) for vertical centering, not hitbox height
|
||||
float nextIconX = uidTextRectMin.X + uidTextRect.X + 10f;
|
||||
float iconYOffset = (uidTextSize.Y - iconSize.Y) * 0.5f;
|
||||
float textVerticalOffset = (uidTextRect.Y - uidTextSize.Y) * 0.5f;
|
||||
var buttonSize = new Vector2(iconSize.X, uidTextSize.Y);
|
||||
|
||||
if (_configService.Current.BroadcastEnabled && _apiController.IsConnected)
|
||||
{
|
||||
float iconYOffset = (uidTextSize.Y - iconSize.Y) * 0.5f;
|
||||
var buttonSize = new Vector2(iconSize.X, uidTextSize.Y);
|
||||
|
||||
ImGui.SetCursorPos(new Vector2(ImGui.GetStyle().ItemSpacing.X + 5f, cursorY));
|
||||
ImGui.SetCursorScreenPos(new Vector2(nextIconX, uidTextRectMin.Y + textVerticalOffset));
|
||||
|
||||
ImGui.InvisibleButton("BroadcastIcon", buttonSize);
|
||||
|
||||
var iconPos = ImGui.GetItemRectMin() + new Vector2(0f, iconYOffset);
|
||||
using (_uiSharedService.IconFont.Push())
|
||||
ImGui.GetWindowDrawList().AddText(iconPos, ImGui.GetColorU32(UIColors.Get("LightlessGreen")), FontAwesomeIcon.PersonCirclePlus.ToIconString());
|
||||
ImGui.GetWindowDrawList().AddText(iconPos, ImGui.GetColorU32(UIColors.Get("LightlessGreen")), FontAwesomeIcon.Wifi.ToIconString());
|
||||
|
||||
nextIconX = ImGui.GetItemRectMax().X + 6f;
|
||||
|
||||
|
||||
if (ImGui.IsItemHovered())
|
||||
@@ -618,50 +597,8 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
if (ImGui.IsItemClicked())
|
||||
_lightlessMediator.Publish(new UiToggleMessage(typeof(LightFinderUI)));
|
||||
}
|
||||
|
||||
ImGui.SetCursorPosY(cursorY);
|
||||
ImGui.SetCursorPosX(uidStartX);
|
||||
|
||||
bool headerItemClicked;
|
||||
using (_uiSharedService.UidFont.Push())
|
||||
{
|
||||
if (useVanityColors)
|
||||
{
|
||||
var seString = SeStringUtils.BuildFormattedPlayerName(uidText, vanityTextColor, vanityGlowColor);
|
||||
var cursorPos = ImGui.GetCursorScreenPos();
|
||||
var targetFontSize = ImGui.GetFontSize();
|
||||
var font = ImGui.GetFont();
|
||||
SeStringUtils.RenderSeStringWithHitbox(seString, cursorPos, targetFontSize ,font , "uid-header");
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.TextColored(uidColor, uidText);
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
var padding = new Vector2(35f * ImGuiHelpers.GlobalScale);
|
||||
Selune.RegisterHighlight(
|
||||
ImGui.GetItemRectMin() - padding,
|
||||
ImGui.GetItemRectMax() + padding,
|
||||
SeluneHighlightMode.Point,
|
||||
exactSize: true,
|
||||
clipToElement: true,
|
||||
clipPadding: padding,
|
||||
highlightColorOverride: vanityGlowColor,
|
||||
highlightAlphaOverride: 0.05f);
|
||||
}
|
||||
|
||||
headerItemClicked = ImGui.IsItemClicked();
|
||||
|
||||
if (headerItemClicked)
|
||||
{
|
||||
ImGui.SetClipboardText(uidText);
|
||||
}
|
||||
|
||||
UiSharedService.AttachToolTip("Click to copy");
|
||||
|
||||
|
||||
// Warning threshold icon (next to lightfinder or UID text)
|
||||
if (_apiController.ServerState is ServerState.Connected && analysisSummary.HasData)
|
||||
{
|
||||
var objectSummary = analysisSummary.Objects.Values.FirstOrDefault(summary => summary.HasEntries);
|
||||
@@ -675,24 +612,30 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
|
||||
if ((isOverTriHold || isOverVRAMUsage) && _playerPerformanceConfig.Current.WarnOnExceedingThresholds)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGui.SetCursorPosY(cursorY + 15f);
|
||||
_uiSharedService.IconText(FontAwesomeIcon.ExclamationTriangle, UIColors.Get("LightlessYellow"));
|
||||
ImGui.SetCursorScreenPos(new Vector2(nextIconX, uidTextRectMin.Y + textVerticalOffset));
|
||||
|
||||
ImGui.InvisibleButton("WarningThresholdIcon", buttonSize);
|
||||
var warningIconPos = ImGui.GetItemRectMin() + new Vector2(0f, iconYOffset);
|
||||
using (_uiSharedService.IconFont.Push())
|
||||
ImGui.GetWindowDrawList().AddText(warningIconPos, ImGui.GetColorU32(UIColors.Get("LightlessYellow")), FontAwesomeIcon.ExclamationTriangle.ToIconString());
|
||||
|
||||
string warningMessage = "";
|
||||
if (isOverTriHold)
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
warningMessage += $"You exceed your own triangles threshold by " +
|
||||
$"{actualTriCount - _playerPerformanceConfig.Current.TrisWarningThresholdThousands * 1000} triangles.";
|
||||
warningMessage += Environment.NewLine;
|
||||
|
||||
string warningMessage = "";
|
||||
if (isOverTriHold)
|
||||
{
|
||||
warningMessage += $"You exceed your own triangles threshold by " +
|
||||
$"{actualTriCount - _playerPerformanceConfig.Current.TrisWarningThresholdThousands * 1000} triangles.";
|
||||
warningMessage += Environment.NewLine;
|
||||
}
|
||||
if (isOverVRAMUsage)
|
||||
{
|
||||
warningMessage += $"You exceed your own VRAM threshold by " +
|
||||
$"{UiSharedService.ByteToString(actualVramUsage - (_playerPerformanceConfig.Current.VRAMSizeWarningThresholdMiB * 1024 * 1024))}.";
|
||||
}
|
||||
UiSharedService.AttachToolTip(warningMessage);
|
||||
}
|
||||
if (isOverVRAMUsage)
|
||||
{
|
||||
warningMessage += $"You exceed your own VRAM threshold by " +
|
||||
$"{UiSharedService.ByteToString(actualVramUsage - (_playerPerformanceConfig.Current.VRAMSizeWarningThresholdMiB * 1024 * 1024))}.";
|
||||
}
|
||||
UiSharedService.AttachToolTip(warningMessage);
|
||||
|
||||
if (ImGui.IsItemClicked())
|
||||
{
|
||||
_lightlessMediator.Publish(new UiToggleMessage(typeof(DataAnalysisUi)));
|
||||
@@ -701,6 +644,34 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (uidTextHovered)
|
||||
{
|
||||
var padding = new Vector2(35f * ImGuiHelpers.GlobalScale);
|
||||
Selune.RegisterHighlight(
|
||||
uidTextRectMin - padding,
|
||||
uidTextRectMin + uidTextRect + padding,
|
||||
SeluneHighlightMode.Point,
|
||||
exactSize: true,
|
||||
clipToElement: true,
|
||||
clipPadding: padding,
|
||||
highlightColorOverride: vanityGlowColor,
|
||||
highlightAlphaOverride: 0.05f);
|
||||
|
||||
ImGui.SetTooltip("Click to copy");
|
||||
}
|
||||
|
||||
if (headerItemClicked)
|
||||
{
|
||||
ImGui.SetClipboardText(uidText);
|
||||
}
|
||||
|
||||
// Connect/Disconnect button next to big UID (use screen pos to avoid affecting layout)
|
||||
DrawConnectButton(uidTextRectMin.Y + textVerticalOffset, uidTextSize.Y);
|
||||
|
||||
// Add spacing below the big UID
|
||||
ImGuiHelpers.ScaledDummy(5f);
|
||||
|
||||
if (_apiController.ServerState is ServerState.Connected)
|
||||
{
|
||||
if (headerItemClicked)
|
||||
@@ -708,10 +679,12 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
ImGui.SetClipboardText(_apiController.DisplayName);
|
||||
}
|
||||
|
||||
if (!string.Equals(_apiController.DisplayName, _apiController.UID, StringComparison.Ordinal))
|
||||
// Only show smaller UID line if DisplayName differs from UID (custom vanity name)
|
||||
bool hasCustomName = !string.Equals(_apiController.DisplayName, _apiController.UID, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (hasCustomName)
|
||||
{
|
||||
var origTextSize = ImGui.CalcTextSize(_apiController.UID);
|
||||
ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X) / 2 - (origTextSize.X / 2));
|
||||
ImGui.SetCursorPosX(uidStartX);
|
||||
|
||||
if (useVanityColors)
|
||||
{
|
||||
@@ -746,14 +719,88 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
{
|
||||
ImGui.SetClipboardText(_apiController.UID);
|
||||
}
|
||||
|
||||
// Users Online on same line as smaller UID (with separator)
|
||||
ImGui.SameLine();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted("|");
|
||||
ImGui.SameLine();
|
||||
ImGui.TextColored(UIColors.Get("LightlessGreen"), _apiController.OnlineUsers.ToString(CultureInfo.InvariantCulture));
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted("Users Online");
|
||||
}
|
||||
else
|
||||
{
|
||||
// No custom name - just show Users Online aligned to uidStartX
|
||||
ImGui.SetCursorPosX(uidStartX);
|
||||
ImGui.TextColored(UIColors.Get("LightlessGreen"), _apiController.OnlineUsers.ToString(CultureInfo.InvariantCulture));
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted("Users Online");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.SetCursorPosX(uidStartX);
|
||||
UiSharedService.ColorTextWrapped(_apiController.ServerState.GetServerError(_apiController.AuthFailureMessage), uidColor);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawConnectButton(float screenY, float textHeight)
|
||||
{
|
||||
var buttonSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Link);
|
||||
bool isConnectingOrConnected = _apiController.ServerState is ServerState.Connected or ServerState.Connecting or ServerState.Reconnecting;
|
||||
var color = UiSharedService.GetBoolColor(!isConnectingOrConnected);
|
||||
var connectedIcon = isConnectingOrConnected ? FontAwesomeIcon.Unlink : FontAwesomeIcon.Link;
|
||||
|
||||
// Position on right side, vertically centered with text
|
||||
if (_apiController.ServerState is not (ServerState.Reconnecting or ServerState.Disconnecting))
|
||||
{
|
||||
var windowPos = ImGui.GetWindowPos();
|
||||
var screenX = windowPos.X + UiSharedService.GetWindowContentRegionWidth() - buttonSize.X - 13f;
|
||||
var yOffset = (textHeight - buttonSize.Y) * 0.5f;
|
||||
ImGui.SetCursorScreenPos(new Vector2(screenX, screenY + yOffset));
|
||||
|
||||
using (ImRaii.PushColor(ImGuiCol.Text, color))
|
||||
using (ImRaii.PushColor(ImGuiCol.Button, ImGui.ColorConvertFloat4ToU32(new(0, 0, 0, 0))))
|
||||
{
|
||||
if (_uiSharedService.IconButton(connectedIcon, buttonSize.Y))
|
||||
{
|
||||
if (isConnectingOrConnected && !_serverManager.CurrentServer.FullPause)
|
||||
{
|
||||
_serverManager.CurrentServer.FullPause = true;
|
||||
_serverManager.Save();
|
||||
}
|
||||
else if (!isConnectingOrConnected && _serverManager.CurrentServer.FullPause)
|
||||
{
|
||||
_serverManager.CurrentServer.FullPause = false;
|
||||
_serverManager.Save();
|
||||
}
|
||||
|
||||
_ = _apiController.CreateConnectionsAsync();
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenBlockedByActiveItem) || ImGui.IsItemActive())
|
||||
{
|
||||
Selune.RegisterHighlight(
|
||||
ImGui.GetItemRectMin(),
|
||||
ImGui.GetItemRectMax(),
|
||||
SeluneHighlightMode.Both,
|
||||
borderOnly: true,
|
||||
borderThicknessOverride: ConnectButtonHighlightThickness,
|
||||
exactSize: true,
|
||||
clipToElement: true,
|
||||
roundingOverride: ImGui.GetStyle().FrameRounding);
|
||||
}
|
||||
|
||||
UiSharedService.AttachToolTip(isConnectingOrConnected ? "Disconnect from " + _serverManager.CurrentServer.ServerName : "Connect to " + _serverManager.CurrentServer.ServerName);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Folder Building
|
||||
|
||||
private IEnumerable<IDrawFolder> DrawFolders
|
||||
{
|
||||
get
|
||||
@@ -889,6 +936,10 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Filtering & Sorting
|
||||
|
||||
private static bool PassesFilter(PairUiEntry entry, string filter)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filter)) return true;
|
||||
@@ -1033,10 +1084,11 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
return SortGroupEntries(entries, group);
|
||||
}
|
||||
|
||||
private void UiSharedService_GposeEnd()
|
||||
{
|
||||
IsOpen = _wasOpen;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region GPose Handlers
|
||||
|
||||
private void UiSharedService_GposeEnd() => IsOpen = _wasOpen;
|
||||
|
||||
private void UiSharedService_GposeStart()
|
||||
{
|
||||
@@ -1044,6 +1096,10 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
IsOpen = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Focus Tracking
|
||||
|
||||
private void RegisterFocusCharacter(Pair pair)
|
||||
{
|
||||
_pendingFocusPair = pair;
|
||||
@@ -1089,4 +1145,16 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
_pendingFocusPair = null;
|
||||
_pendingFocusFrame = -1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper Types
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
private readonly record struct DownloadSummary(int TotalFiles, int TransferredFiles, long TransferredBytes, long TotalBytes)
|
||||
{
|
||||
public bool HasDownloads => TotalFiles > 0 || TotalBytes > 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user