Files
LightlessClient/LightlessSync/UI/Handlers/IdDisplayHandler.cs
2025-12-19 19:20:41 +01:00

531 lines
19 KiB
C#

using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using LightlessSync.API.Dto.Group;
using LightlessSync.LightlessConfiguration;
using LightlessSync.PlayerData.Pairs;
using LightlessSync.Services.Mediator;
using LightlessSync.Services.ServerConfiguration;
using LightlessSync.UI.Style;
using LightlessSync.Utils;
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Text;
namespace LightlessSync.UI.Handlers;
public class IdDisplayHandler
{
private readonly LightlessConfigService _lightlessConfigService;
private readonly PlayerPerformanceConfigService _playerPerformanceConfigService;
private readonly LightlessMediator _mediator;
private readonly ServerConfigurationManager _serverManager;
private readonly Dictionary<string, bool> _showIdForEntry = new(StringComparer.Ordinal);
private string _editComment = string.Empty;
private string _editEntry = string.Empty;
private bool _editIsUid = false;
private string _lastMouseOverUid = string.Empty;
private bool _popupShown = false;
private DateTime? _popupTime;
private Vector4 _currentBg = new(0.15f, 0.15f, 0.15f, 1f);
private float _highlightBoost;
public IdDisplayHandler(
LightlessMediator mediator,
ServerConfigurationManager serverManager,
LightlessConfigService lightlessConfigService,
PlayerPerformanceConfigService playerPerformanceConfigService)
{
_mediator = mediator;
_serverManager = serverManager;
_lightlessConfigService = lightlessConfigService;
_playerPerformanceConfigService = playerPerformanceConfigService;
}
public void DrawGroupText(string id, GroupFullInfoDto group, float textPosX, Func<float> editBoxWidth)
{
ImGui.SameLine(textPosX);
(bool textIsUid, string playerText) = GetGroupText(group);
if (!string.Equals(_editEntry, group.GID, StringComparison.Ordinal))
{
using (ImRaii.PushFont(UiBuilder.MonoFont, textIsUid))
{
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted(playerText);
}
if (ImGui.IsItemHovered())
{
ImGui.SetTooltip("Left click to switch between ID display and alias"
+ Environment.NewLine + "Right click to edit notes for this syncshell"
+ Environment.NewLine + "Middle Mouse Button to open syncshell profile in a separate window");
}
if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
{
var prevState = textIsUid;
if (_showIdForEntry.TryGetValue(group.GID, out bool value))
{
prevState = value;
}
_showIdForEntry[group.GID] = !prevState;
}
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
{
if (_editIsUid)
{
_serverManager.SetNoteForUid(_editEntry, _editComment, save: true);
}
else
{
_serverManager.SetNoteForGid(_editEntry, _editComment, save: true);
}
_editComment = _serverManager.GetNoteForGid(group.GID) ?? string.Empty;
_editEntry = group.GID;
_editIsUid = false;
}
if (ImGui.IsItemClicked(ImGuiMouseButton.Middle))
{
_mediator.Publish(new GroupProfileOpenStandaloneMessage(group.Group));
}
}
else
{
ImGui.AlignTextToFramePadding();
ImGui.SetNextItemWidth(editBoxWidth.Invoke());
if (ImGui.InputTextWithHint("", "Name/Notes", ref _editComment, 255, ImGuiInputTextFlags.EnterReturnsTrue))
{
_serverManager.SetNoteForGid(group.GID, _editComment, save: true);
_editEntry = string.Empty;
}
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
{
_editEntry = string.Empty;
}
UiSharedService.AttachToolTip("Hit ENTER to save\nRight click to cancel");
}
}
public void DrawPairText(string id, Pair pair, float textPosX, Func<float> editBoxWidth)
{
ImGui.SameLine(textPosX);
(bool textIsUid, string playerText) = GetPlayerText(pair);
var compactPerformanceText = BuildCompactPerformanceUsageText(pair);
if (!string.Equals(_editEntry, pair.UserData.UID, StringComparison.Ordinal))
{
var targetFontSize = ImGui.GetFontSize();
var font = textIsUid ? UiBuilder.MonoFont : ImGui.GetFont();
var rowWidth = MathF.Max(editBoxWidth.Invoke(), 0f);
float rowRightLimit = 0f;
Vector2 nameRectMin = Vector2.Zero;
Vector2 nameRectMax = Vector2.Zero;
float rowTopForStats = 0f;
float frameHeightForStats = 0f;
using (ImRaii.PushFont(font, textIsUid))
{
ImGui.AlignTextToFramePadding();
var rowStart = ImGui.GetCursorScreenPos();
rowRightLimit = rowStart.X + rowWidth;
Vector4? textColor = null;
Vector4? glowColor = null;
if (pair.UserData.HasVanity)
{
if (!string.IsNullOrWhiteSpace(pair.UserData.TextColorHex))
{
textColor = UIColors.HexToRgba(pair.UserData.TextColorHex);
}
if (!string.IsNullOrWhiteSpace(pair.UserData.TextGlowColorHex))
{
glowColor = UIColors.HexToRgba(pair.UserData.TextGlowColorHex);
}
}
var useVanityColors = _lightlessConfigService.Current.useColoredUIDs && (textColor != null || glowColor != null);
var seString = useVanityColors
? SeStringUtils.BuildFormattedPlayerName(playerText, textColor, glowColor)
: SeStringUtils.BuildPlain(playerText);
var drawList = ImGui.GetWindowDrawList();
bool useHighlight = false;
float highlightPadX = 0f;
float highlightPadY = 0f;
if (useVanityColors)
{
float boost = Luminance.ComputeHighlight(textColor, glowColor);
if (boost > 0f)
{
var style = ImGui.GetStyle();
useHighlight = true;
highlightPadX = MathF.Max(style.FramePadding.X * 0.6f, 2f * ImGuiHelpers.GlobalScale);
highlightPadY = MathF.Max(style.FramePadding.Y * 0.55f, 1.25f * ImGuiHelpers.GlobalScale);
drawList.ChannelsSplit(2);
drawList.ChannelsSetCurrent(1);
_highlightBoost = boost;
}
else
{
_highlightBoost = 0f;
}
}
SeStringUtils.RenderSeStringWithHitbox(seString, rowStart, targetFontSize, font, pair.UserData.UID);
nameRectMin = ImGui.GetItemRectMin();
nameRectMax = ImGui.GetItemRectMax();
if (useHighlight)
{
var style = ImGui.GetStyle();
var frameHeight = ImGui.GetFrameHeight();
var rowTop = rowStart.Y - style.FramePadding.Y;
var rowBottom = rowTop + frameHeight;
var highlightMin = new Vector2(nameRectMin.X - highlightPadX, rowTop - highlightPadY);
var highlightMax = new Vector2(nameRectMax.X + highlightPadX, rowBottom + highlightPadY);
var windowPos = ImGui.GetWindowPos();
var contentMin = windowPos + ImGui.GetWindowContentRegionMin();
var contentMax = windowPos + ImGui.GetWindowContentRegionMax();
highlightMin.X = MathF.Max(highlightMin.X, contentMin.X);
highlightMax.X = MathF.Min(highlightMax.X, contentMax.X);
highlightMin.Y = MathF.Max(highlightMin.Y, contentMin.Y);
highlightMax.Y = MathF.Min(highlightMax.Y, contentMax.Y);
var highlightColor = new Vector4(
0.25f + _highlightBoost,
0.25f + _highlightBoost,
0.25f + _highlightBoost,
1f
);
highlightColor = Luminance.BackgroundContrast(textColor, glowColor, highlightColor, ref _currentBg);
float rounding = style.FrameRounding > 0f ? style.FrameRounding : 5f * ImGuiHelpers.GlobalScale;
drawList.ChannelsSetCurrent(0);
drawList.AddRectFilled(highlightMin, highlightMax, ImGui.GetColorU32(highlightColor), rounding);
var borderColor = style.Colors[(int)ImGuiCol.Border];
borderColor.W *= 0.25f;
drawList.AddRect(highlightMin, highlightMax, ImGui.GetColorU32(borderColor), rounding);
drawList.ChannelsMerge();
}
}
{
var style = ImGui.GetStyle();
frameHeightForStats = ImGui.GetFrameHeight();
rowTopForStats = nameRectMin.Y - style.FramePadding.Y;
}
if (ImGui.IsItemHovered())
{
if (!string.Equals(_lastMouseOverUid, id, StringComparison.Ordinal))
{
_popupTime = DateTime.UtcNow.AddSeconds(_lightlessConfigService.Current.ProfileDelay);
}
_lastMouseOverUid = id;
if (_popupTime > DateTime.UtcNow || !_lightlessConfigService.Current.ProfilesShow)
{
ImGui.SetTooltip("Left click to switch between UID display and nick" + Environment.NewLine
+ "Right click to change nick for " + pair.UserData.AliasOrUID + Environment.NewLine
+ "Middle Mouse Button to open their profile in a separate window");
}
else if (_popupTime < DateTime.UtcNow && !_popupShown)
{
_popupShown = true;
_mediator.Publish(new ProfilePopoutToggle(pair));
}
}
else
{
if (string.Equals(_lastMouseOverUid, id, StringComparison.Ordinal))
{
_mediator.Publish(new ProfilePopoutToggle(Pair: null));
_lastMouseOverUid = string.Empty;
_popupShown = false;
}
}
if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
{
var prevState = textIsUid;
if (_showIdForEntry.ContainsKey(pair.UserData.UID))
{
prevState = _showIdForEntry[pair.UserData.UID];
}
_showIdForEntry[pair.UserData.UID] = !prevState;
}
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
{
if (_editIsUid)
{
_serverManager.SetNoteForUid(_editEntry, _editComment, save: true);
}
else
{
_serverManager.SetNoteForGid(_editEntry, _editComment, save: true);
}
_editComment = pair.GetNote() ?? string.Empty;
_editEntry = pair.UserData.UID;
_editIsUid = true;
}
if (ImGui.IsItemClicked(ImGuiMouseButton.Middle))
{
_mediator.Publish(new ProfileOpenStandaloneMessage(pair));
}
if (!string.IsNullOrEmpty(compactPerformanceText))
{
ImGui.SameLine();
const float compactFontScale = 0.85f;
ImGui.SetWindowFontScale(compactFontScale);
var compactHeight = ImGui.GetTextLineHeight();
var targetPos = ImGui.GetCursorScreenPos();
var availableWidth = MathF.Max(rowRightLimit - targetPos.X, 0f);
var centeredY = rowTopForStats + MathF.Max((frameHeightForStats - compactHeight) * 0.5f, 0f);
ImGui.SetCursorScreenPos(new Vector2(targetPos.X, centeredY));
var performanceText = string.Empty;
var wasTruncated = false;
if (availableWidth > 0f)
{
performanceText = TruncateTextToWidth(compactPerformanceText, availableWidth, out wasTruncated);
}
ImGui.TextDisabled(performanceText);
ImGui.SetWindowFontScale(1f);
if (wasTruncated && ImGui.IsItemHovered())
{
ImGui.SetTooltip(compactPerformanceText);
}
}
}
else
{
ImGui.AlignTextToFramePadding();
ImGui.SetNextItemWidth(MathF.Max(editBoxWidth.Invoke(), 0f));
if (ImGui.InputTextWithHint("##" + pair.UserData.UID, "Nick/Notes", ref _editComment, 255, ImGuiInputTextFlags.EnterReturnsTrue))
{
_serverManager.SetNoteForUid(pair.UserData.UID, _editComment);
_serverManager.SaveNotes();
_editEntry = string.Empty;
}
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
{
_editEntry = string.Empty;
}
UiSharedService.AttachToolTip("Hit ENTER to save\nRight click to cancel");
}
}
public (bool isGid, string text) GetGroupText(GroupFullInfoDto group)
{
var textIsGid = true;
bool showUidInsteadOfName = ShowGidInsteadOfName(group);
string? groupText = _serverManager.GetNoteForGid(group.GID);
if (!showUidInsteadOfName && groupText != null)
{
if (string.IsNullOrEmpty(groupText))
{
groupText = group.GroupAliasOrGID;
}
else
{
textIsGid = false;
}
}
else
{
groupText = group.GroupAliasOrGID;
}
return (textIsGid, groupText!);
}
public (bool isUid, string text) GetPlayerText(Pair pair)
{
var textIsUid = true;
bool showUidInsteadOfName = ShowUidInsteadOfName(pair);
string? playerText = _serverManager.GetNoteForUid(pair.UserData.UID);
if (!showUidInsteadOfName && playerText != null)
{
if (string.IsNullOrEmpty(playerText))
{
playerText = pair.UserData.AliasOrUID;
}
else
{
textIsUid = false;
}
}
else
{
playerText = pair.UserData.AliasOrUID;
}
if (_lightlessConfigService.Current.ShowCharacterNameInsteadOfNotesForVisible && pair.IsVisible && !showUidInsteadOfName)
{
playerText = pair.PlayerName;
textIsUid = false;
if (_lightlessConfigService.Current.PreferNotesOverNamesForVisible)
{
var note = pair.GetNote();
if (note != null)
{
playerText = note;
}
}
}
return (textIsUid, playerText!);
}
private string? BuildCompactPerformanceUsageText(Pair pair)
{
var config = _playerPerformanceConfigService.Current;
if (!config.ShowPerformanceIndicator || !config.ShowPerformanceUsageNextToName)
{
return null;
}
var vramBytes = pair.LastAppliedApproximateEffectiveVRAMBytes >= 0
? pair.LastAppliedApproximateEffectiveVRAMBytes
: pair.LastAppliedApproximateVRAMBytes;
var triangleCount = pair.LastAppliedDataTris;
if (vramBytes < 0 && triangleCount < 0)
{
return null;
}
var segments = new List<string>(2);
if (vramBytes >= 0)
{
segments.Add(UiSharedService.ByteToString(vramBytes));
}
if (triangleCount >= 0)
{
segments.Add(FormatTriangleCount(triangleCount));
}
return segments.Count == 0 ? null : string.Join(" / ", segments);
}
private static string FormatTriangleCount(long triangleCount)
{
if (triangleCount < 0)
{
return string.Empty;
}
if (triangleCount >= 1_000_000)
{
return FormattableString.Invariant($"{triangleCount / 1_000_000d:0.#}m tris");
}
if (triangleCount >= 1_000)
{
return FormattableString.Invariant($"{triangleCount / 1_000d:0.#}k tris");
}
return $"{triangleCount} tris";
}
internal void Clear()
{
_editEntry = string.Empty;
_editComment = string.Empty;
}
internal void OpenProfile(Pair entry)
{
_mediator.Publish(new ProfileOpenStandaloneMessage(entry));
}
private bool ShowGidInsteadOfName(GroupFullInfoDto group)
{
_showIdForEntry.TryGetValue(group.GID, out var showidInsteadOfName);
return showidInsteadOfName;
}
private bool ShowUidInsteadOfName(Pair pair)
{
_showIdForEntry.TryGetValue(pair.UserData.UID, out var showidInsteadOfName);
return showidInsteadOfName;
}
private static string TruncateTextToWidth(string text, float maxWidth, out bool wasTruncated)
{
wasTruncated = false;
if (string.IsNullOrEmpty(text) || maxWidth <= 0f)
{
return string.Empty;
}
var fullWidth = ImGui.CalcTextSize(text).X;
if (fullWidth <= maxWidth)
{
return text;
}
wasTruncated = true;
const string Ellipsis = "...";
var ellipsisWidth = ImGui.CalcTextSize(Ellipsis).X;
if (ellipsisWidth >= maxWidth)
{
return Ellipsis;
}
var builder = new StringBuilder(text.Length);
var remainingWidth = maxWidth - ellipsisWidth;
foreach (var rune in text.EnumerateRunes())
{
var runeText = rune.ToString();
var runeWidth = ImGui.CalcTextSize(runeText).X;
if (runeWidth > remainingWidth)
{
break;
}
builder.Append(runeText);
remainingWidth -= runeWidth;
}
if (builder.Length == 0)
{
return Ellipsis;
}
builder.Append(Ellipsis);
return builder.ToString();
}
}