From a8a01b303473cb6539ade7e3213c15fea02babda Mon Sep 17 00:00:00 2001 From: choco Date: Sun, 12 Oct 2025 18:00:49 +0200 Subject: [PATCH 1/2] added more customization to the notifs, settings improvemnts, left and right notifs, animations for sliding in and out --- .../Configurations/LightlessConfig.cs | 2 + .../Models/NotificationLocation.cs | 6 + LightlessSync/Services/Mediator/Messages.cs | 3 +- LightlessSync/UI/LightlessNotificationUI.cs | 153 ++++++-- LightlessSync/UI/SettingsUi.cs | 332 +++++++++++------- 5 files changed, 335 insertions(+), 161 deletions(-) diff --git a/LightlessSync/LightlessConfiguration/Configurations/LightlessConfig.cs b/LightlessSync/LightlessConfiguration/Configurations/LightlessConfig.cs index 4d82529..bee94fd 100644 --- a/LightlessSync/LightlessConfiguration/Configurations/LightlessConfig.cs +++ b/LightlessSync/LightlessConfiguration/Configurations/LightlessConfig.cs @@ -95,6 +95,7 @@ public class LightlessConfig : ILightlessConfiguration public bool ShowNotificationTimestamp { get; set; } = false; // Position & Layout + public NotificationCorner NotificationCorner { get; set; } = NotificationCorner.Right; public int NotificationOffsetY { get; set; } = 50; public int NotificationOffsetX { get; set; } = 0; public float NotificationWidth { get; set; } = 350f; @@ -102,6 +103,7 @@ public class LightlessConfig : ILightlessConfiguration // Animation & Effects public float NotificationAnimationSpeed { get; set; } = 10f; + public float NotificationSlideSpeed { get; set; } = 10f; public float NotificationAccentBarWidth { get; set; } = 3f; // Duration per Type diff --git a/LightlessSync/LightlessConfiguration/Models/NotificationLocation.cs b/LightlessSync/LightlessConfiguration/Models/NotificationLocation.cs index 2815986..73637ee 100644 --- a/LightlessSync/LightlessConfiguration/Models/NotificationLocation.cs +++ b/LightlessSync/LightlessConfiguration/Models/NotificationLocation.cs @@ -18,4 +18,10 @@ public enum NotificationType Error, PairRequest, Download +} + +public enum NotificationCorner +{ + Right, + Left } \ No newline at end of file diff --git a/LightlessSync/Services/Mediator/Messages.cs b/LightlessSync/Services/Mediator/Messages.cs index 1524dbd..3396027 100644 --- a/LightlessSync/Services/Mediator/Messages.cs +++ b/LightlessSync/Services/Mediator/Messages.cs @@ -48,7 +48,6 @@ public record PetNamesMessage(string PetNicknamesData) : MessageBase; public record HonorificReadyMessage : MessageBase; public record TransientResourceChangedMessage(IntPtr Address) : MessageBase; public record HaltScanMessage(string Source) : MessageBase; -public record ResumeScanMessage(string Source) : MessageBase; public record NotificationMessage (string Title, string Message, NotificationType Type, TimeSpan? TimeShownOnScreen = null) : MessageBase; public record CreateCacheForObjectMessage(GameObjectHandler ObjectToCreateFor) : SameThreadMessage; @@ -56,12 +55,14 @@ public record ClearCacheForObjectMessage(GameObjectHandler ObjectToCreateFor) : public record CharacterDataCreatedMessage(CharacterData CharacterData) : SameThreadMessage; public record LightlessNotificationMessage(LightlessSync.UI.Models.LightlessNotification Notification) : MessageBase; public record LightlessNotificationDismissMessage(string NotificationId) : MessageBase; +public record ClearAllNotificationsMessage : MessageBase; public record CharacterDataAnalyzedMessage : MessageBase; public record PenumbraStartRedrawMessage(IntPtr Address) : MessageBase; public record PenumbraEndRedrawMessage(IntPtr Address) : MessageBase; public record HubReconnectingMessage(Exception? Exception) : SameThreadMessage; public record HubReconnectedMessage(string? Arg) : SameThreadMessage; public record HubClosedMessage(Exception? Exception) : SameThreadMessage; +public record ResumeScanMessage(string Source) : MessageBase; public record DownloadReadyMessage(Guid RequestId) : MessageBase; public record DownloadStartedMessage(GameObjectHandler DownloadId, Dictionary DownloadStatus) : MessageBase; public record DownloadFinishedMessage(GameObjectHandler DownloadId) : MessageBase; diff --git a/LightlessSync/UI/LightlessNotificationUI.cs b/LightlessSync/UI/LightlessNotificationUI.cs index c0d5b01..36fa508 100644 --- a/LightlessSync/UI/LightlessNotificationUI.cs +++ b/LightlessSync/UI/LightlessNotificationUI.cs @@ -26,6 +26,8 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase private readonly List _notifications = new(); private readonly object _notificationLock = new(); private readonly LightlessConfigService _configService; + private readonly Dictionary _notificationYOffsets = new(); + private readonly Dictionary _notificationTargetYOffsets = new(); public LightlessNotificationUI(ILogger logger, LightlessMediator mediator, PerformanceCollectorService performanceCollector, LightlessConfigService configService) : base(logger, mediator, "Lightless Notifications##LightlessNotifications", performanceCollector) @@ -49,12 +51,11 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase Mediator.Subscribe(this, HandleNotificationMessage); Mediator.Subscribe(this, HandleNotificationDismissMessage); + Mediator.Subscribe(this, HandleClearAllNotifications); } - private void HandleNotificationMessage(LightlessNotificationMessage message) => - AddNotification(message.Notification); - - private void HandleNotificationDismissMessage(LightlessNotificationDismissMessage message) => - RemoveNotification(message.NotificationId); + private void HandleNotificationMessage(LightlessNotificationMessage message) => AddNotification(message.Notification); + private void HandleNotificationDismissMessage(LightlessNotificationDismissMessage message) => RemoveNotification(message.NotificationId); + private void HandleClearAllNotifications(ClearAllNotificationsMessage message) => ClearAllNotifications(); public void AddNotification(LightlessNotification notification) { @@ -96,20 +97,34 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase } } + public void ClearAllNotifications() + { + lock (_notificationLock) + { + foreach (var notification in _notifications) + { + StartOutAnimation(notification); + } + } + } + private void StartOutAnimation(LightlessNotification notification) { notification.IsAnimatingOut = true; notification.IsAnimatingIn = false; } + + private bool ShouldRemoveNotification(LightlessNotification notification) => + notification.IsAnimatingOut && notification.AnimationProgress <= 0.01f; protected override void DrawInternal() { ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, Vector2.Zero); - + lock (_notificationLock) { UpdateNotifications(); - + if (_notifications.Count == 0) { ImGui.PopStyleVar(); @@ -118,33 +133,49 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase } var viewport = ImGui.GetMainViewport(); + + // Set window to full viewport height + var width = _configService.Current.NotificationWidth; + Size = new Vector2(width, viewport.WorkSize.Y); + SizeCondition = ImGuiCond.Always; + Position = CalculateWindowPosition(viewport); + PositionCondition = ImGuiCond.Always; + DrawAllNotifications(); } - + ImGui.PopStyleVar(); } private Vector2 CalculateWindowPosition(ImGuiViewportPtr viewport) { - var x = viewport.WorkPos.X + viewport.WorkSize.X - - _configService.Current.NotificationWidth - - _configService.Current.NotificationOffsetX - - WindowPaddingOffset; - var y = viewport.WorkPos.Y + _configService.Current.NotificationOffsetY; - return new Vector2(x, y); + var corner = _configService.Current.NotificationCorner; + var offsetX = _configService.Current.NotificationOffsetX; + var width = _configService.Current.NotificationWidth; + + float posX = corner == NotificationCorner.Left + ? viewport.WorkPos.X + offsetX - WindowPaddingOffset + : viewport.WorkPos.X + viewport.WorkSize.X - width - offsetX - WindowPaddingOffset; + + return new Vector2(posX, viewport.WorkPos.Y); } private void DrawAllNotifications() { + var offsetY = _configService.Current.NotificationOffsetY; + var startY = ImGui.GetCursorPosY() + offsetY; + for (int i = 0; i < _notifications.Count; i++) { - DrawNotification(_notifications[i], i); + var notification = _notifications[i]; - if (i < _notifications.Count - 1) + if (_notificationYOffsets.TryGetValue(notification.Id, out var yOffset)) { - ImGui.Dummy(new Vector2(0, _configService.Current.NotificationSpacing)); + ImGui.SetCursorPosY(startY + yOffset); } + + DrawNotification(notification, i); } } @@ -174,18 +205,65 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase private void UpdateAnimationsAndRemoveExpired(float deltaTime) { + UpdateTargetYPositions(); + for (int i = _notifications.Count - 1; i >= 0; i--) { var notification = _notifications[i]; UpdateNotificationAnimation(notification, deltaTime); + UpdateNotificationYOffset(notification, deltaTime); if (ShouldRemoveNotification(notification)) { _notifications.RemoveAt(i); + _notificationYOffsets.Remove(notification.Id); + _notificationTargetYOffsets.Remove(notification.Id); } } } + private void UpdateTargetYPositions() + { + float currentY = 0f; + + for (int i = 0; i < _notifications.Count; i++) + { + var notification = _notifications[i]; + + if (!_notificationTargetYOffsets.ContainsKey(notification.Id)) + { + _notificationTargetYOffsets[notification.Id] = currentY; + _notificationYOffsets[notification.Id] = currentY; + } + else + { + _notificationTargetYOffsets[notification.Id] = currentY; + } + + currentY += CalculateNotificationHeight(notification) + _configService.Current.NotificationSpacing; + } + } + + private void UpdateNotificationYOffset(LightlessNotification notification, float deltaTime) + { + if (!_notificationYOffsets.ContainsKey(notification.Id) || !_notificationTargetYOffsets.ContainsKey(notification.Id)) + return; + + var current = _notificationYOffsets[notification.Id]; + var target = _notificationTargetYOffsets[notification.Id]; + var diff = target - current; + + if (Math.Abs(diff) < 0.5f) + { + _notificationYOffsets[notification.Id] = target; + } + else + { + var speed = _configService.Current.NotificationSlideSpeed; + _notificationYOffsets[notification.Id] = current + (diff * deltaTime * speed); + } + } + private void UpdateNotificationAnimation(LightlessNotification notification, float deltaTime) { if (notification.IsAnimatingIn && notification.AnimationProgress < 1f) @@ -209,20 +287,24 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase } } - private bool ShouldRemoveNotification(LightlessNotification notification) => - notification.IsAnimatingOut && notification.AnimationProgress <= 0.01f; + private Vector2 CalculateSlideOffset(float alpha) + { + var distance = (1f - alpha) * SlideAnimationDistance; + var corner = _configService.Current.NotificationCorner; + return corner == NotificationCorner.Left ? new Vector2(-distance, 0) : new Vector2(distance, 0); + } private void DrawNotification(LightlessNotification notification, int index) { var alpha = notification.AnimationProgress; if (alpha <= 0f) return; - var slideOffset = (1f - alpha) * SlideAnimationDistance; + var slideOffset = CalculateSlideOffset(alpha); var originalCursorPos = ImGui.GetCursorPos(); - ImGui.SetCursorPosX(originalCursorPos.X + slideOffset); + ImGui.SetCursorPos(originalCursorPos + slideOffset); var notificationHeight = CalculateNotificationHeight(notification); - var notificationWidth = _configService.Current.NotificationWidth - slideOffset; + var notificationWidth = _configService.Current.NotificationWidth - Math.Abs(slideOffset.X); ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, Vector2.Zero); @@ -308,15 +390,28 @@ public class LightlessNotificationUI : WindowMediatorSubscriberBase private void DrawAccentBar(ImDrawListPtr drawList, Vector2 windowPos, Vector2 windowSize, Vector4 accentColor) { var accentWidth = _configService.Current.NotificationAccentBarWidth; - if (accentWidth > 0f) + if (accentWidth <= 0f) return; + + var corner = _configService.Current.NotificationCorner; + Vector2 accentStart, accentEnd; + + if (corner == NotificationCorner.Left) { - drawList.AddRectFilled( - windowPos, - windowPos + new Vector2(accentWidth, windowSize.Y), - ImGui.ColorConvertFloat4ToU32(accentColor), - 3f - ); + accentStart = windowPos + new Vector2(windowSize.X - accentWidth, 0); + accentEnd = windowPos + windowSize; } + else + { + accentStart = windowPos; + accentEnd = windowPos + new Vector2(accentWidth, windowSize.Y); + } + + drawList.AddRectFilled( + accentStart, + accentEnd, + ImGui.ColorConvertFloat4ToU32(accentColor), + 3f + ); } private void DrawDurationProgressBar(LightlessNotification notification, float alpha, Vector2 windowPos, Vector2 windowSize, ImDrawListPtr drawList) diff --git a/LightlessSync/UI/SettingsUi.cs b/LightlessSync/UI/SettingsUi.cs index 6c2fd6e..4f57f2e 100644 --- a/LightlessSync/UI/SettingsUi.cs +++ b/LightlessSync/UI/SettingsUi.cs @@ -3493,69 +3493,162 @@ public class SettingsUi : WindowMediatorSubscriberBase if (useLightlessNotifications) { // Lightless notification locations - ImGui.Indent(); - var lightlessLocations = GetLightlessNotificationLocations(); - - ImGui.AlignTextToFramePadding(); - ImGui.TextUnformatted("Info Notifications:"); - ImGui.SameLine(); - ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale); - _uiShared.DrawCombo("###enhanced_info", lightlessLocations, GetNotificationLocationLabel, (location) => - { - _configService.Current.LightlessInfoNotification = location; - _configService.Save(); - }, _configService.Current.LightlessInfoNotification); - - ImGui.AlignTextToFramePadding(); - ImGui.TextUnformatted("Warning Notifications:"); - ImGui.SameLine(); - ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale); - _uiShared.DrawCombo("###enhanced_warning", lightlessLocations, GetNotificationLocationLabel, - (location) => - { - _configService.Current.LightlessWarningNotification = location; - _configService.Save(); - }, _configService.Current.LightlessWarningNotification); - - ImGui.AlignTextToFramePadding(); - ImGui.TextUnformatted("Error Notifications:"); - ImGui.SameLine(); - ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale); - _uiShared.DrawCombo("###enhanced_error", lightlessLocations, GetNotificationLocationLabel, (location) => - { - _configService.Current.LightlessErrorNotification = location; - _configService.Save(); - }, _configService.Current.LightlessErrorNotification); - - ImGuiHelpers.ScaledDummy(3); - _uiShared.DrawHelpText("Special notification types:"); - - ImGui.AlignTextToFramePadding(); - ImGui.TextUnformatted("Pair Request Notifications:"); - ImGui.SameLine(); - ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale); - _uiShared.DrawCombo("###enhanced_pairrequest", lightlessLocations, GetNotificationLocationLabel, - (location) => - { - _configService.Current.LightlessPairRequestNotification = location; - _configService.Save(); - }, _configService.Current.LightlessPairRequestNotification); - - ImGui.AlignTextToFramePadding(); - ImGui.TextUnformatted("Download Progress Notifications:"); - ImGui.SameLine(); - ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale); var downloadLocations = GetDownloadNotificationLocations(); - _uiShared.DrawCombo("###enhanced_download", downloadLocations, GetNotificationLocationLabel, - (location) => + + if (ImGui.BeginTable("##NotificationLocationTable", 3, ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit)) + { + ImGui.TableSetupColumn("Notification Type", ImGuiTableColumnFlags.WidthFixed, 200f * ImGuiHelpers.GlobalScale); + ImGui.TableSetupColumn("Location", ImGuiTableColumnFlags.WidthStretch); + ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.WidthFixed, 40f * ImGuiHelpers.GlobalScale); + ImGui.TableHeadersRow(); + + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.TextUnformatted("Info Notifications"); + ImGui.TableSetColumnIndex(1); + ImGui.SetNextItemWidth(-1); + _uiShared.DrawCombo("###enhanced_info", lightlessLocations, GetNotificationLocationLabel, (location) => { - _configService.Current.LightlessDownloadNotification = location; + _configService.Current.LightlessInfoNotification = location; _configService.Save(); - }, _configService.Current.LightlessDownloadNotification); + }, _configService.Current.LightlessInfoNotification); + ImGui.TableSetColumnIndex(2); + var availableWidth = ImGui.GetContentRegionAvail().X; + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + if (ImGui.Button($"{FontAwesomeIcon.Play.ToIconString()}##test_info", new Vector2(availableWidth, 0))) + { + Mediator.Publish(new NotificationMessage("Test Info", + "This is a test info notification to let you know Chocola is cute :3", NotificationType.Info)); + } + } + UiSharedService.AttachToolTip("Test info notification"); + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.TextUnformatted("Warning Notifications"); + ImGui.TableSetColumnIndex(1); + ImGui.SetNextItemWidth(-1); + _uiShared.DrawCombo("###enhanced_warning", lightlessLocations, GetNotificationLocationLabel, + (location) => + { + _configService.Current.LightlessWarningNotification = location; + _configService.Save(); + }, _configService.Current.LightlessWarningNotification); + ImGui.TableSetColumnIndex(2); + availableWidth = ImGui.GetContentRegionAvail().X; + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + if (ImGui.Button($"{FontAwesomeIcon.Play.ToIconString()}##test_warning", new Vector2(availableWidth, 0))) + { + Mediator.Publish(new NotificationMessage("Test Warning", "This is a test warning notification!", + NotificationType.Warning)); + } + } + UiSharedService.AttachToolTip("Test warning notification"); - ImGui.Unindent(); + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.TextUnformatted("Error Notifications"); + ImGui.TableSetColumnIndex(1); + ImGui.SetNextItemWidth(-1); + _uiShared.DrawCombo("###enhanced_error", lightlessLocations, GetNotificationLocationLabel, (location) => + { + _configService.Current.LightlessErrorNotification = location; + _configService.Save(); + }, _configService.Current.LightlessErrorNotification); + ImGui.TableSetColumnIndex(2); + availableWidth = ImGui.GetContentRegionAvail().X; + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + if (ImGui.Button($"{FontAwesomeIcon.Play.ToIconString()}##test_error", new Vector2(availableWidth, 0))) + { + Mediator.Publish(new NotificationMessage("Test Error", "This is a test error notification!", + NotificationType.Error)); + } + } + UiSharedService.AttachToolTip("Test error notification"); + + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.TextUnformatted("Pair Request Notifications"); + ImGui.TableSetColumnIndex(1); + ImGui.SetNextItemWidth(-1); + _uiShared.DrawCombo("###enhanced_pairrequest", lightlessLocations, GetNotificationLocationLabel, + (location) => + { + _configService.Current.LightlessPairRequestNotification = location; + _configService.Save(); + }, _configService.Current.LightlessPairRequestNotification); + ImGui.TableSetColumnIndex(2); + availableWidth = ImGui.GetContentRegionAvail().X; + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + if (ImGui.Button($"{FontAwesomeIcon.Play.ToIconString()}##test_pair", new Vector2(availableWidth, 0))) + { + _lightlessNotificationService.ShowPairRequestNotification( + "Test User", + "test-uid-123", + () => + { + Mediator.Publish(new NotificationMessage("Accepted", "You accepted the test pair request.", + NotificationType.Info)); + }, + () => + { + Mediator.Publish(new NotificationMessage("Declined", "You declined the test pair request.", + NotificationType.Info)); + } + ); + } + } + UiSharedService.AttachToolTip("Test pair request notification"); + + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.TextUnformatted("Download Progress Notifications"); + ImGui.TableSetColumnIndex(1); + ImGui.SetNextItemWidth(-1); + _uiShared.DrawCombo("###enhanced_download", downloadLocations, GetNotificationLocationLabel, + (location) => + { + _configService.Current.LightlessDownloadNotification = location; + _configService.Save(); + }, _configService.Current.LightlessDownloadNotification); + ImGui.TableSetColumnIndex(2); + availableWidth = ImGui.GetContentRegionAvail().X; + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + if (ImGui.Button($"{FontAwesomeIcon.Play.ToIconString()}##test_download", new Vector2(availableWidth, 0))) + { + _lightlessNotificationService.ShowPairDownloadNotification( + new List<(string playerName, float progress, string status)> + { + ("Player One", 0.35f, "downloading"), + ("Player Two", 0.75f, "downloading"), + ("Player Three", 1.0f, "downloading") + }, + queueWaiting: 2 + ); + } + } + UiSharedService.AttachToolTip("Test download progress notification"); + + ImGui.EndTable(); + } + + ImGuiHelpers.ScaledDummy(5); + if (_uiShared.IconTextButton(FontAwesomeIcon.Trash, "Clear All Notifications")) + { + Mediator.Publish(new ClearAllNotificationsMessage()); + } + _uiShared.DrawHelpText("Dismiss all active notifications immediately."); } else { @@ -3602,73 +3695,6 @@ public class SettingsUi : WindowMediatorSubscriberBase ImGui.Separator(); if (useLightlessNotifications) { - if (_uiShared.MediumTreeNode("Test Notifications", UIColors.Get("LightlessPurple"))) - { - ImGui.Indent(); - - // Test notification buttons - if (_uiShared.IconTextButton(FontAwesomeIcon.Bell, "Test Info")) - { - Mediator.Publish(new NotificationMessage("Test Info", - "This is a test info notification to let you know Chocola is cute :3", NotificationType.Info)); - } - - ImGui.SameLine(); - if (_uiShared.IconTextButton(FontAwesomeIcon.ExclamationTriangle, "Test Warning")) - { - Mediator.Publish(new NotificationMessage("Test Warning", "This is a test warning notification!", - NotificationType.Warning)); - } - - ImGui.SameLine(); - if (_uiShared.IconTextButton(FontAwesomeIcon.ExclamationCircle, "Test Error")) - { - Mediator.Publish(new NotificationMessage("Test Error", "This is a test error notification!", - NotificationType.Error)); - } - - ImGuiHelpers.ScaledDummy(3); - if (_uiShared.IconTextButton(FontAwesomeIcon.UserPlus, "Test Pair Request")) - { - _lightlessNotificationService.ShowPairRequestNotification( - "Test User", - "test-uid-123", - () => - { - Mediator.Publish(new NotificationMessage("Accepted", "You accepted the test pair request.", - NotificationType.Info)); - }, - () => - { - Mediator.Publish(new NotificationMessage("Declined", "You declined the test pair request.", - NotificationType.Info)); - } - ); - } - - ImGui.SameLine(); - if (_uiShared.IconTextButton(FontAwesomeIcon.Download, "Test Download Progress")) - { - _lightlessNotificationService.ShowPairDownloadNotification( - new List<(string playerName, float progress, string status)> - { - ("Player One", 0.35f, "downloading"), - ("Player Two", 0.75f, "downloading"), - ("Player Three", 1.0f, "downloading") - }, - queueWaiting: 2 - ); - } - - _uiShared.DrawHelpText("Preview how notifications will appear with your current settings."); - - ImGui.Unindent(); - - _uiShared.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.5f); - ImGui.TreePop(); - } - - ImGui.Separator(); if (_uiShared.MediumTreeNode("Basic Settings", UIColors.Get("LightlessPurple"))) { int maxNotifications = _configService.Current.MaxSimultaneousNotifications; @@ -3768,10 +3794,28 @@ public class SettingsUi : WindowMediatorSubscriberBase ImGui.Spacing(); ImGui.TextUnformatted("Position"); - int offsetY = _configService.Current.NotificationOffsetY; - if (ImGui.SliderInt("Vertical Offset", ref offsetY, 0, 500)) + var currentCorner = _configService.Current.NotificationCorner; + if (ImGui.BeginCombo("Notification Position", GetNotificationCornerLabel(currentCorner))) { - _configService.Current.NotificationOffsetY = Math.Clamp(offsetY, 0, 500); + foreach (NotificationCorner corner in Enum.GetValues(typeof(NotificationCorner))) + { + bool isSelected = currentCorner == corner; + if (ImGui.Selectable(GetNotificationCornerLabel(corner), isSelected)) + { + _configService.Current.NotificationCorner = corner; + _configService.Save(); + } + if (isSelected) + ImGui.SetItemDefaultFocus(); + } + ImGui.EndCombo(); + } + _uiShared.DrawHelpText("Choose which corner of the screen notifications appear in."); + + int offsetY = _configService.Current.NotificationOffsetY; + if (ImGui.SliderInt("Vertical Offset", ref offsetY, 0, 1000)) + { + _configService.Current.NotificationOffsetY = Math.Clamp(offsetY, 0, 1000); _configService.Save(); } if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) @@ -3781,7 +3825,7 @@ public class SettingsUi : WindowMediatorSubscriberBase } if (ImGui.IsItemHovered()) ImGui.SetTooltip("Right click to reset to default (50)."); - _uiShared.DrawHelpText("Move notifications down from the top-right corner."); + _uiShared.DrawHelpText("Distance from the top edge of the screen."); int offsetX = _configService.Current.NotificationOffsetX; if (ImGui.SliderInt("Horizontal Offset", ref offsetX, 0, 500)) @@ -3802,9 +3846,9 @@ public class SettingsUi : WindowMediatorSubscriberBase ImGui.TextUnformatted("Animation Settings"); float animSpeed = _configService.Current.NotificationAnimationSpeed; - if (ImGui.SliderFloat("Animation Speed", ref animSpeed, 1f, 30f, "%.1f")) + if (ImGui.SliderFloat("Animation Speed", ref animSpeed, 1f, 20f, "%.1f")) { - _configService.Current.NotificationAnimationSpeed = Math.Clamp(animSpeed, 1f, 30f); + _configService.Current.NotificationAnimationSpeed = Math.Clamp(animSpeed, 1f, 20f); _configService.Save(); } if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) @@ -3816,6 +3860,21 @@ public class SettingsUi : WindowMediatorSubscriberBase ImGui.SetTooltip("Right click to reset to default (10)."); _uiShared.DrawHelpText("How fast notifications slide in/out. Higher = faster."); + float slideSpeed = _configService.Current.NotificationSlideSpeed; + if (ImGui.SliderFloat("Slide Speed", ref slideSpeed, 1f, 20f, "%.1f")) + { + _configService.Current.NotificationSlideSpeed = Math.Clamp(slideSpeed, 1f, 20f); + _configService.Save(); + } + if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) + { + _configService.Current.NotificationSlideSpeed = 10f; + _configService.Save(); + } + if (ImGui.IsItemHovered()) + ImGui.SetTooltip("Right click to reset to default (10)."); + _uiShared.DrawHelpText("How fast notifications slide into position when others disappear. Higher = faster."); + ImGui.Spacing(); ImGui.TextUnformatted("Visual Effects"); @@ -3999,6 +4058,7 @@ public class SettingsUi : WindowMediatorSubscriberBase ImGui.Separator(); // Location descriptions removed - information is now inline with each setting + } } @@ -4043,6 +4103,16 @@ public class SettingsUi : WindowMediatorSubscriberBase }; } + private string GetNotificationCornerLabel(NotificationCorner corner) + { + return corner switch + { + NotificationCorner.Right => "Right", + NotificationCorner.Left => "Left", + _ => corner.ToString() + }; + } + private void DrawSoundTable() { var soundEffects = new[] @@ -4087,7 +4157,7 @@ public class SettingsUi : WindowMediatorSubscriberBase var currentIndex = Array.FindIndex(soundEffects, s => s.Item1 == currentSoundId); if (currentIndex == -1) currentIndex = 1; - ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale); + ImGui.SetNextItemWidth(-1); if (ImGui.Combo($"##sound_{typeIndex}", ref currentIndex, soundEffects.Select(s => s.Item2).ToArray(), soundEffects.Length)) { From 02c384603109203924ffb9913cdcdcbddcab74b9 Mon Sep 17 00:00:00 2001 From: choco Date: Sun, 12 Oct 2025 18:02:53 +0200 Subject: [PATCH 2/2] column rename for better UX c: --- LightlessSync/UI/SettingsUi.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LightlessSync/UI/SettingsUi.cs b/LightlessSync/UI/SettingsUi.cs index 4f57f2e..87df967 100644 --- a/LightlessSync/UI/SettingsUi.cs +++ b/LightlessSync/UI/SettingsUi.cs @@ -3500,7 +3500,7 @@ public class SettingsUi : WindowMediatorSubscriberBase { ImGui.TableSetupColumn("Notification Type", ImGuiTableColumnFlags.WidthFixed, 200f * ImGuiHelpers.GlobalScale); ImGui.TableSetupColumn("Location", ImGuiTableColumnFlags.WidthStretch); - ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.WidthFixed, 40f * ImGuiHelpers.GlobalScale); + ImGui.TableSetupColumn("Test", ImGuiTableColumnFlags.WidthFixed, 40f * ImGuiHelpers.GlobalScale); ImGui.TableHeadersRow(); ImGui.TableNextRow();