performance notifcation addition, with some regular bugfixes regarding the flexing of the notifications
This commit is contained in:
@@ -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")
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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)))
|
||||
{
|
||||
|
||||
@@ -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" },
|
||||
|
||||
Reference in New Issue
Block a user