performance notifcation addition, with some regular bugfixes regarding the flexing of the notifications

This commit is contained in:
choco
2025-10-14 11:46:14 +02:00
parent 3f2e4d6640
commit f202818b55
9 changed files with 379 additions and 82 deletions

View File

@@ -22,6 +22,10 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase
private const float WindowPaddingOffset = 6f;
private const float SlideAnimationDistance = 100f;
private const float OutAnimationSpeedMultiplier = 0.7f;
private const float ContentPaddingX = 10f;
private const float ContentPaddingY = 6f;
private const float TitleMessageSpacing = 4f;
private const float ActionButtonSpacing = 8f;
private readonly List<LightlessNotification> _notifications = new();
private readonly object _notificationLock = new();
@@ -462,81 +466,112 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase
private void DrawNotificationText(LightlessNotification notification, float alpha)
{
var padding = new Vector2(10f, 6f);
var contentPos = new Vector2(padding.X, padding.Y);
var contentPos = new Vector2(ContentPaddingX, ContentPaddingY);
var windowSize = ImGui.GetWindowSize();
var contentSize = new Vector2(windowSize.X - padding.X, windowSize.Y - padding.Y * 2);
var contentWidth = CalculateContentWidth(windowSize.X);
ImGui.SetCursorPos(contentPos);
var titleHeight = DrawTitle(notification, contentSize.X, alpha);
DrawMessage(notification, contentPos, contentSize.X, titleHeight, alpha);
var titleHeight = DrawTitle(notification, contentWidth, alpha);
DrawMessage(notification, contentPos, contentWidth, titleHeight, alpha);
if (notification.Actions.Count > 0)
if (HasActions(notification))
{
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + ImGui.GetStyle().ItemSpacing.Y);
ImGui.SetCursorPosX(contentPos.X);
DrawNotificationActions(notification, contentSize.X, alpha);
PositionActionsAtBottom(windowSize.Y);
DrawNotificationActions(notification, contentWidth, alpha);
}
}
private float CalculateContentWidth(float windowWidth) =>
windowWidth - (ContentPaddingX * 2);
private bool HasActions(LightlessNotification notification) =>
notification.Actions.Count > 0;
private void PositionActionsAtBottom(float windowHeight)
{
var actionHeight = ImGui.GetFrameHeight();
var bottomY = windowHeight - ContentPaddingY - actionHeight;
ImGui.SetCursorPosY(bottomY);
ImGui.SetCursorPosX(ContentPaddingX);
}
private float DrawTitle(LightlessNotification notification, float contentWidth, float alpha)
{
using (ImRaii.PushColor(ImGuiCol.Text, new Vector4(1f, 1f, 1f, alpha)))
var titleColor = new Vector4(1f, 1f, 1f, alpha);
var titleText = FormatTitleText(notification);
using (ImRaii.PushColor(ImGuiCol.Text, titleColor))
{
ImGui.PushTextWrapPos(ImGui.GetCursorPosX() + contentWidth);
var titleStartY = ImGui.GetCursorPosY();
var titleText = _configService.Current.ShowNotificationTimestamp
? $"[{notification.CreatedAt.ToLocalTime():HH:mm:ss}] {notification.Title}"
: notification.Title;
ImGui.TextWrapped(titleText);
var titleHeight = ImGui.GetCursorPosY() - titleStartY;
ImGui.PopTextWrapPos();
return titleHeight;
return DrawWrappedText(titleText, contentWidth);
}
}
private string FormatTitleText(LightlessNotification notification)
{
if (!_configService.Current.ShowNotificationTimestamp)
return notification.Title;
var timestamp = notification.CreatedAt.ToLocalTime().ToString("HH:mm:ss");
return $"[{timestamp}] {notification.Title}";
}
private float DrawWrappedText(string text, float wrapWidth)
{
ImGui.PushTextWrapPos(ImGui.GetCursorPosX() + wrapWidth);
var startY = ImGui.GetCursorPosY();
ImGui.TextWrapped(text);
var height = ImGui.GetCursorPosY() - startY;
ImGui.PopTextWrapPos();
return height;
}
private void DrawMessage(LightlessNotification notification, Vector2 contentPos, float contentWidth, float titleHeight, float alpha)
{
if (string.IsNullOrEmpty(notification.Message)) return;
ImGui.SetCursorPos(contentPos + new Vector2(0f, titleHeight + 4f));
ImGui.PushTextWrapPos(ImGui.GetCursorPosX() + contentWidth);
using (ImRaii.PushColor(ImGuiCol.Text, new Vector4(0.9f, 0.9f, 0.9f, alpha)))
var messagePos = contentPos + new Vector2(0f, titleHeight + TitleMessageSpacing);
var messageColor = new Vector4(0.9f, 0.9f, 0.9f, alpha);
ImGui.SetCursorPos(messagePos);
using (ImRaii.PushColor(ImGuiCol.Text, messageColor))
{
ImGui.TextWrapped(notification.Message);
DrawWrappedText(notification.Message, contentWidth);
}
ImGui.PopTextWrapPos();
}
private void DrawNotificationActions(LightlessNotification notification, float availableWidth, float alpha)
{
var buttonSpacing = 8f;
var rightPadding = 10f;
var usableWidth = availableWidth - rightPadding;
var totalSpacing = (notification.Actions.Count - 1) * buttonSpacing;
var buttonWidth = (usableWidth - totalSpacing) / notification.Actions.Count;
var buttonWidth = CalculateActionButtonWidth(notification.Actions.Count, availableWidth);
_logger.LogDebug("Drawing {ActionCount} notification actions, buttonWidth: {ButtonWidth}, availableWidth: {AvailableWidth}",
notification.Actions.Count, buttonWidth, availableWidth);
var startCursorPos = ImGui.GetCursorPos();
var startX = ImGui.GetCursorPosX();
for (int i = 0; i < notification.Actions.Count; i++)
{
var action = notification.Actions[i];
if (i > 0)
{
ImGui.SameLine();
var currentX = startCursorPos.X + i * (buttonWidth + buttonSpacing);
ImGui.SetCursorPosX(currentX);
PositionActionButton(i, startX, buttonWidth);
}
DrawActionButton(action, notification, alpha, buttonWidth);
DrawActionButton(notification.Actions[i], notification, alpha, buttonWidth);
}
}
private float CalculateActionButtonWidth(int actionCount, float availableWidth)
{
var totalSpacing = (actionCount - 1) * ActionButtonSpacing;
return (availableWidth - totalSpacing) / actionCount;
}
private void PositionActionButton(int index, float startX, float buttonWidth)
{
var xPosition = startX + index * (buttonWidth + ActionButtonSpacing);
ImGui.SetCursorPosX(xPosition);
}
private void DrawActionButton(LightlessNotificationAction action, LightlessNotification notification, float alpha, float buttonWidth)
{
@@ -634,7 +669,7 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase
private float CalculateNotificationHeight(LightlessNotification notification)
{
var contentWidth = _configService.Current.NotificationWidth - 35f;
var contentWidth = CalculateContentWidth(_configService.Current.NotificationWidth);
var height = 12f;
height += CalculateTitleHeight(notification, contentWidth);
@@ -681,6 +716,8 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase
NotificationType.Error => UIColors.Get("DimRed"),
NotificationType.PairRequest => UIColors.Get("LightlessBlue"),
NotificationType.Download => UIColors.Get("LightlessGreen"),
NotificationType.Performance => UIColors.Get("LightlessOrange"),
_ => UIColors.Get("LightlessPurple")
};
}

View File

@@ -1990,7 +1990,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
("LightlessBlue", "Secondary Blue", "Secondary title colors, visable pairs"),
("LightlessGreen", "Success Green", "Join buttons and success messages"),
("LightlessYellow", "Warning Yellow", "Warning colors"),
("LightlessYellow2", "Warning Yellow (Alt)", "Warning colors"),
("LightlessOrange", "Performance Orange", "Performance notifications and warnings"),
("PairBlue", "Syncshell Blue", "Syncshell headers, toggle highlights, and moderator actions"),
("DimRed", "Error Red", "Error and offline colors")
};
@@ -3640,6 +3640,36 @@ public class SettingsUi : WindowMediatorSubscriberBase
}
UiSharedService.AttachToolTip("Test download progress notification");
ImGui.TableNextRow();
ImGui.TableSetColumnIndex(0);
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Performance Notifications");
ImGui.TableSetColumnIndex(1);
ImGui.SetNextItemWidth(-1);
_uiShared.DrawCombo("###enhanced_performance", lightlessLocations, GetNotificationLocationLabel,
(location) =>
{
_configService.Current.LightlessPerformanceNotification = location;
_configService.Save();
}, _configService.Current.LightlessPerformanceNotification);
ImGui.TableSetColumnIndex(2);
availableWidth = ImGui.GetContentRegionAvail().X;
using (ImRaii.PushFont(UiBuilder.IconFont))
{
if (ImGui.Button($"{FontAwesomeIcon.Play.ToIconString()}##test_performance", new Vector2(availableWidth, 0)))
{
var testUserData = new UserData("TEST123", "TestUser", false, false, false, null, null);
Mediator.Publish(new PerformanceNotificationMessage(
"Test Player (TestUser) exceeds performance threshold(s)",
"Player Test Player (TestUser) exceeds your configured VRAM warning threshold (500 MB/300 MB).",
testUserData,
false,
"Test Player"
));
}
}
UiSharedService.AttachToolTip("Test performance notification");
ImGui.EndTable();
}
@@ -3974,6 +4004,20 @@ public class SettingsUi : WindowMediatorSubscriberBase
if (ImGui.IsItemHovered())
ImGui.SetTooltip("Right click to reset to default (300).");
int performanceDuration = _configService.Current.PerformanceNotificationDurationSeconds;
if (ImGui.SliderInt("Performance Duration (seconds)", ref performanceDuration, 5, 60))
{
_configService.Current.PerformanceNotificationDurationSeconds = Math.Clamp(performanceDuration, 5, 60);
_configService.Save();
}
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
{
_configService.Current.PerformanceNotificationDurationSeconds = 20;
_configService.Save();
}
if (ImGui.IsItemHovered())
ImGui.SetTooltip("Right click to reset to default (20).");
_uiShared.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.5f);
ImGui.TreePop();
}
@@ -4041,6 +4085,22 @@ public class SettingsUi : WindowMediatorSubscriberBase
ImGui.TreePop();
}
if (_uiShared.MediumTreeNode("Performance Notifications", UIColors.Get("LightlessOrange")))
{
var showPerformanceActions = _configService.Current.ShowPerformanceNotificationActions;
if (ImGui.Checkbox("Show action buttons on performance warnings", ref showPerformanceActions))
{
_configService.Current.ShowPerformanceNotificationActions = showPerformanceActions;
_configService.Save();
}
_uiShared.DrawHelpText(
"When a player exceeds performance thresholds or is auto-paused, show Pause/Unpause buttons in the notification.");
_uiShared.ColoredSeparator(UIColors.Get("LightlessOrange"), 1.5f);
ImGui.TreePop();
}
if (_uiShared.MediumTreeNode("System Notifications", UIColors.Get("LightlessYellow")))
{
var disableOptionalPluginWarnings = _configService.Current.DisableOptionalPluginWarnings;
@@ -4074,8 +4134,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
{
return new[]
{
NotificationLocation.LightlessUi, NotificationLocation.ChatAndLightlessUi,
NotificationLocation.TextOverlay, NotificationLocation.Nowhere
NotificationLocation.LightlessUi, NotificationLocation.TextOverlay, NotificationLocation.Nowhere
};
}
@@ -4138,7 +4197,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
("Warning", 1, _configService.Current.CustomWarningSoundId, _configService.Current.DisableWarningSound, 16u),
("Error", 2, _configService.Current.CustomErrorSoundId, _configService.Current.DisableErrorSound, 16u),
("Pair Request", 3, _configService.Current.PairRequestSoundId, _configService.Current.DisablePairRequestSound, 5u),
("Download", 4, _configService.Current.DownloadSoundId, _configService.Current.DisableDownloadSound, 15u)
("Performance", 4, _configService.Current.PerformanceSoundId, _configService.Current.DisablePerformanceSound, 16u)
};
foreach (var (typeName, typeIndex, currentSoundId, isDisabled, defaultSoundId) in soundTypes)
@@ -4168,7 +4227,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
case 1: _configService.Current.CustomWarningSoundId = newSoundId; break;
case 2: _configService.Current.CustomErrorSoundId = newSoundId; break;
case 3: _configService.Current.PairRequestSoundId = newSoundId; break;
case 4: _configService.Current.DownloadSoundId = newSoundId; break;
case 4: _configService.Current.PerformanceSoundId = newSoundId; break;
}
_configService.Save();
@@ -4222,7 +4281,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
case 1: _configService.Current.DisableWarningSound = newDisabled; break;
case 2: _configService.Current.DisableErrorSound = newDisabled; break;
case 3: _configService.Current.DisablePairRequestSound = newDisabled; break;
case 4: _configService.Current.DisableDownloadSound = newDisabled; break;
case 4: _configService.Current.DisablePerformanceSound = newDisabled; break;
}
_configService.Save();
}
@@ -4248,7 +4307,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
case 1: _configService.Current.CustomWarningSoundId = defaultSoundId; break;
case 2: _configService.Current.CustomErrorSoundId = defaultSoundId; break;
case 3: _configService.Current.PairRequestSoundId = defaultSoundId; break;
case 4: _configService.Current.DownloadSoundId = defaultSoundId; break;
case 4: _configService.Current.PerformanceSoundId = defaultSoundId; break;
}
_configService.Save();
}

View File

@@ -88,7 +88,7 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase
ImGuiHelpers.ScaledDummy(0.5f);
ImGui.PushStyleVar(ImGuiStyleVar.FrameRounding, 10.0f);
ImGui.PushStyleColor(ImGuiCol.Button, UIColors.Get("LightlessYellow2"));
ImGui.PushStyleColor(ImGuiCol.Button, UIColors.Get("PairBlue"));
if (ImGui.Button("Open Lightfinder", new Vector2(200 * ImGuiHelpers.GlobalScale, 0)))
{

View File

@@ -11,21 +11,17 @@ namespace LightlessSync.UI
{ "LightlessPurple", "#ad8af5" },
{ "LightlessPurpleActive", "#be9eff" },
{ "LightlessPurpleDefault", "#9375d1" },
{ "ButtonDefault", "#323232" },
{ "FullBlack", "#000000" },
{ "LightlessBlue", "#a6c2ff" },
{ "LightlessYellow", "#ffe97a" },
{ "LightlessYellow2", "#cfbd63" },
{ "LightlessGreen", "#7cd68a" },
{ "LightlessOrange", "#ffb366" },
{ "PairBlue", "#88a2db" },
{ "DimRed", "#d44444" },
{ "LightlessAdminText", "#ffd663" },
{ "LightlessAdminGlow", "#b09343" },
{ "LightlessModeratorText", "#94ffda" },
{ "LightlessModeratorGlow", "#599c84" },
{ "Lightfinder", "#ad8af5" },
{ "LightfinderEdge", "#000000" },