From d6f90e3b9fd23496db6c57b8a3cb5339cf691cca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Sep 2022 21:32:22 +0900 Subject: [PATCH 01/14] Add basic fling-to-dismiss support --- .../Overlays/Notifications/Notification.cs | 132 +++++++++++++++++- 1 file changed, 128 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index 67739c2089..3e7bd0ab2f 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -2,16 +2,19 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Framework.Localisation; +using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osuTK; @@ -56,6 +59,8 @@ namespace osu.Game.Overlays.Notifications protected Container MainContent; + private readonly DragContainer dragContainer; + public virtual bool Read { get; set; } protected virtual IconUsage CloseButtonIcon => FontAwesome.Solid.Check; @@ -80,7 +85,11 @@ namespace osu.Game.Overlays.Notifications Anchor = Anchor.CentreLeft, Origin = Anchor.CentreRight, }, - MainContent = new Container + dragContainer = new DragContainer(this) + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }.WithChild(MainContent = new Container { CornerRadius = 6, Masking = true, @@ -144,7 +153,7 @@ namespace osu.Game.Overlays.Notifications Blending = BlendingParameters.Additive, }, } - } + }) }; } @@ -213,11 +222,126 @@ namespace osu.Game.Overlays.Notifications WasClosed = true; - Closed?.Invoke(); - this.FadeOut(100); + if (dragContainer.FlingLeft()) + { + Light.FadeOut(100); + this.FadeOut(600, Easing.In); + } + else + { + Closed?.Invoke(); + this.FadeOut(100); + } + Expire(); } + private class DragContainer : Container + { + private Vector2 velocity; + private Vector2 lastPosition; + + private readonly Notification notification; + + public DragContainer(Notification notification) + { + this.notification = notification; + notification.Closed += () => FlingLeft(); + } + + public override RectangleF BoundingBox + { + get + { + var childBounding = Children.First().BoundingBox; + + if (X < 0) childBounding *= new Vector2(1, Math.Max(0, 1 + (X / 800))); + if (Y > 0) childBounding *= new Vector2(1, Math.Max(0, 1 - (Y / 800))); + + return childBounding; + } + } + + protected override bool OnDragStart(DragStartEvent e) => true; + + protected override void OnDrag(DragEvent e) + { + Vector2 change = e.MousePosition - e.MouseDownPosition; + + // Diminish the drag distance as we go further to simulate "rubber band" feeling. + change *= change.Length <= 0 ? 0 : MathF.Pow(change.Length, 0.9f) / change.Length; + + // Only apply Y change if dragging to the left. + if (change.X > 0) + change.Y = 0; + + this.MoveTo(change); + } + + protected override void OnDragEnd(DragEndEvent e) + { + if (Rotation < -10 || velocity.X < -0.3f) + { + FlingLeft(); + } + else + { + this.MoveTo(Vector2.Zero, 800, Easing.OutElastic); + this.RotateTo(0, 800, Easing.OutElastic); + } + + base.OnDragEnd(e); + } + + private bool flinging; + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + Rotation = Math.Min(0, X * 0.1f); + + if (flinging) + { + velocity.Y += (float)Clock.ElapsedFrameTime * 0.005f; + Position += (float)Clock.ElapsedFrameTime * velocity; + } + else + { + Vector2 change = (Position - lastPosition) / (float)Clock.ElapsedFrameTime; + + if (velocity.X == 0) + velocity = change; + else + { + velocity = new Vector2( + (float)Interpolation.DampContinuously(velocity.X, change.X, 40, Clock.ElapsedFrameTime), + (float)Interpolation.DampContinuously(velocity.Y, change.Y, 40, Clock.ElapsedFrameTime) + ); + } + + lastPosition = Position; + } + } + + public bool FlingLeft() + { + if (this.FindClosestParent() == null) + return false; + + if (flinging) + return true; + + if (velocity.X > -0.3f) + velocity.X = -0.3f - 0.5f * RNG.NextSingle(); + + flinging = true; + ClearTransforms(); + notification.Close(); + return true; + } + } + internal class CloseButton : OsuClickableContainer { private SpriteIcon icon = null!; From b5a2f7003e0d0770c53c775a36a2a24a893be05a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Sep 2022 21:34:35 +0900 Subject: [PATCH 02/14] Disallow flinging when not in toast state --- osu.Game/Overlays/NotificationOverlay.cs | 5 +++++ osu.Game/Overlays/Notifications/Notification.cs | 9 +++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index b170ea5dfa..708bd4874d 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -158,7 +158,10 @@ namespace osu.Game.Overlays playDebouncedSample(notification.PopInSampleName); if (State.Value == Visibility.Hidden) + { + notification.IsInTray = true; toastTray.Post(notification); + } else addPermanently(notification); @@ -167,6 +170,8 @@ namespace osu.Game.Overlays private void addPermanently(Notification notification) { + notification.IsInTray = false; + var ourType = notification.GetType(); int depth = notification.DisplayOnTop ? -runningDepth : runningDepth; diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index 3e7bd0ab2f..595cff4171 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -68,6 +68,11 @@ namespace osu.Game.Overlays.Notifications [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; + /// + /// Whether this notification is in the . + /// + public bool IsInTray { get; set; } + private readonly Box initialFlash; private Box background = null!; @@ -262,7 +267,7 @@ namespace osu.Game.Overlays.Notifications } } - protected override bool OnDragStart(DragStartEvent e) => true; + protected override bool OnDragStart(DragStartEvent e) => notification.IsInTray; protected override void OnDrag(DragEvent e) { @@ -326,7 +331,7 @@ namespace osu.Game.Overlays.Notifications public bool FlingLeft() { - if (this.FindClosestParent() == null) + if (!notification.IsInTray) return false; if (flinging) From a56cadcf9057278f2aa3735a79385e0c2c097239 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Sep 2022 21:52:19 +0900 Subject: [PATCH 03/14] Ensure drag position is reset when transferred to tray --- osu.Game/Overlays/NotificationOverlay.cs | 4 +-- .../Overlays/Notifications/Notification.cs | 33 ++++++++++++++----- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 708bd4874d..15573f99af 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -159,7 +159,7 @@ namespace osu.Game.Overlays if (State.Value == Visibility.Hidden) { - notification.IsInTray = true; + notification.IsInToastTray = true; toastTray.Post(notification); } else @@ -170,7 +170,7 @@ namespace osu.Game.Overlays private void addPermanently(Notification notification) { - notification.IsInTray = false; + notification.IsInToastTray = false; var ourType = notification.GetType(); int depth = notification.DisplayOnTop ? -runningDepth : runningDepth; diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index 595cff4171..ded3c1606a 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -68,10 +68,21 @@ namespace osu.Game.Overlays.Notifications [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; + private bool isInToastTray; + /// /// Whether this notification is in the . /// - public bool IsInTray { get; set; } + public bool IsInToastTray + { + private get => isInToastTray; + set + { + isInToastTray = value; + if (!isInToastTray) + dragContainer.ResetPosition(); + } + } private readonly Box initialFlash; @@ -267,10 +278,13 @@ namespace osu.Game.Overlays.Notifications } } - protected override bool OnDragStart(DragStartEvent e) => notification.IsInTray; + protected override bool OnDragStart(DragStartEvent e) => notification.IsInToastTray; protected override void OnDrag(DragEvent e) { + if (!notification.IsInToastTray) + return; + Vector2 change = e.MousePosition - e.MouseDownPosition; // Diminish the drag distance as we go further to simulate "rubber band" feeling. @@ -286,14 +300,9 @@ namespace osu.Game.Overlays.Notifications protected override void OnDragEnd(DragEndEvent e) { if (Rotation < -10 || velocity.X < -0.3f) - { FlingLeft(); - } else - { - this.MoveTo(Vector2.Zero, 800, Easing.OutElastic); - this.RotateTo(0, 800, Easing.OutElastic); - } + ResetPosition(); base.OnDragEnd(e); } @@ -331,7 +340,7 @@ namespace osu.Game.Overlays.Notifications public bool FlingLeft() { - if (!notification.IsInTray) + if (!notification.IsInToastTray) return false; if (flinging) @@ -345,6 +354,12 @@ namespace osu.Game.Overlays.Notifications notification.Close(); return true; } + + public void ResetPosition() + { + this.MoveTo(Vector2.Zero, 800, Easing.OutElastic); + this.RotateTo(0, 800, Easing.OutElastic); + } } internal class CloseButton : OsuClickableContainer From 9ef23c79ced4e8ca705bec6ba20505a216791312 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Sep 2022 22:05:44 +0900 Subject: [PATCH 04/14] Disallow forwarding during a drag operation --- osu.Game/Overlays/NotificationOverlayToastTray.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/NotificationOverlayToastTray.cs b/osu.Game/Overlays/NotificationOverlayToastTray.cs index 40324963fc..e4d3864beb 100644 --- a/osu.Game/Overlays/NotificationOverlayToastTray.cs +++ b/osu.Game/Overlays/NotificationOverlayToastTray.cs @@ -118,7 +118,7 @@ namespace osu.Game.Overlays return; // Notification hovered; delay dismissal. - if (notification.IsHovered) + if (notification.IsHovered || notification.IsDragged) { scheduleDismissal(); return; From 92beb6cbe740336ab84e920818bb4fe5b372563f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 15:16:45 +0900 Subject: [PATCH 05/14] Hide notification read light when in a toast state Also adds test coverage of read state and light. --- .../TestSceneNotificationOverlay.cs | 20 +++++++++++++++++++ .../Overlays/Notifications/Notification.cs | 11 ++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs index 5f82a0024b..6b737b7f5f 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs @@ -219,6 +219,26 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("cancel notification", () => notification.State = ProgressNotificationState.Cancelled); } + [Test] + public void TestReadState() + { + SimpleNotification notification = null!; + AddStep(@"post", () => notificationOverlay.Post(notification = new BackgroundNotification { Text = @"Welcome to osu!. Enjoy your stay!" })); + AddUntilStep("check is toast", () => !notification.IsInToastTray); + AddAssert("light is not visible", () => notification.ChildrenOfType().Single().Alpha == 0); + + AddUntilStep("wait for forward to overlay", () => !notification.IsInToastTray); + + setState(Visibility.Visible); + AddAssert("state is not read", () => !notification.Read); + AddUntilStep("light is visible", () => notification.ChildrenOfType().Single().Alpha == 1); + + setState(Visibility.Hidden); + setState(Visibility.Visible); + AddAssert("state is read", () => notification.Read); + AddUntilStep("light is not visible", () => notification.ChildrenOfType().Single().Alpha == 0); + } + [Test] public void TestBasicFlow() { diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index ded3c1606a..91f5f3b19b 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -75,12 +75,17 @@ namespace osu.Game.Overlays.Notifications /// public bool IsInToastTray { - private get => isInToastTray; + get => isInToastTray; set { isInToastTray = value; + if (!isInToastTray) + { dragContainer.ResetPosition(); + if (!Read) + Light.FadeIn(100); + } } } @@ -97,6 +102,7 @@ namespace osu.Game.Overlays.Notifications { Light = new NotificationLight { + Alpha = 0, Margin = new MarginPadding { Right = 5 }, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreRight, @@ -239,10 +245,7 @@ namespace osu.Game.Overlays.Notifications WasClosed = true; if (dragContainer.FlingLeft()) - { - Light.FadeOut(100); this.FadeOut(600, Easing.In); - } else { Closed?.Invoke(); From e06a0f7300fc9cb71b7a9914a1f8143481a57790 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 15:21:01 +0900 Subject: [PATCH 06/14] Fix dragged state not being exposed correctly --- osu.Game/Overlays/Notifications/Notification.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index 91f5f3b19b..248d66ac16 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -63,6 +63,8 @@ namespace osu.Game.Overlays.Notifications public virtual bool Read { get; set; } + public new bool IsDragged => dragContainer.IsDragged; + protected virtual IconUsage CloseButtonIcon => FontAwesome.Solid.Check; [Resolved] From 2476cf8fb36e4e964b397d86ae082a0ab1de6149 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 15:37:48 +0900 Subject: [PATCH 07/14] Adjust movement to look less sudden when snapping back to Y=0 --- osu.Game/Overlays/Notifications/Notification.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index 248d66ac16..2e4b0c430a 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -293,11 +293,13 @@ namespace osu.Game.Overlays.Notifications Vector2 change = e.MousePosition - e.MouseDownPosition; // Diminish the drag distance as we go further to simulate "rubber band" feeling. - change *= change.Length <= 0 ? 0 : MathF.Pow(change.Length, 0.9f) / change.Length; + change *= change.Length <= 0 ? 0 : MathF.Pow(change.Length, 0.8f) / change.Length; // Only apply Y change if dragging to the left. - if (change.X > 0) + if (change.X >= 0) change.Y = 0; + else + change.Y *= (float)Interpolation.ApplyEasing(Easing.InOutQuart, Math.Min(1, -change.X / 200)); this.MoveTo(change); } From bd3673baa9fe9fa5c5b77659e270c1e784938191 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 16:25:32 +0900 Subject: [PATCH 08/14] Fix being able to drag after already closing a notification --- osu.Game/Overlays/Notifications/Notification.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index 2e4b0c430a..facf43128d 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -70,6 +70,8 @@ namespace osu.Game.Overlays.Notifications [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; + public override bool PropagatePositionalInputSubTree => base.PropagatePositionalInputSubTree && !WasClosed; + private bool isInToastTray; /// @@ -327,7 +329,7 @@ namespace osu.Game.Overlays.Notifications velocity.Y += (float)Clock.ElapsedFrameTime * 0.005f; Position += (float)Clock.ElapsedFrameTime * velocity; } - else + else if (Clock.ElapsedFrameTime > 0) { Vector2 change = (Position - lastPosition) / (float)Clock.ElapsedFrameTime; From a50617857176acbefb571e777429d9411cfdbff0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 16:39:46 +0900 Subject: [PATCH 09/14] Make bounding box shrink faster to allow for rapid flinging --- osu.Game/Overlays/Notifications/Notification.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index facf43128d..f83be0c446 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -278,8 +278,8 @@ namespace osu.Game.Overlays.Notifications { var childBounding = Children.First().BoundingBox; - if (X < 0) childBounding *= new Vector2(1, Math.Max(0, 1 + (X / 800))); - if (Y > 0) childBounding *= new Vector2(1, Math.Max(0, 1 - (Y / 800))); + if (X < 0) childBounding *= new Vector2(1, Math.Max(0, 1 + (X / 300))); + if (Y > 0) childBounding *= new Vector2(1, Math.Max(0, 1 - (Y / 200))); return childBounding; } From 5a02e1e7133e2be8f6eba967049b058a720957f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 16:46:45 +0900 Subject: [PATCH 10/14] Use padding instead of `FillFlow.Spacing` to avoid artifact during animation --- osu.Game/Overlays/NotificationOverlayToastTray.cs | 1 - osu.Game/Overlays/Notifications/Notification.cs | 3 +++ osu.Game/Overlays/Notifications/NotificationSection.cs | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/NotificationOverlayToastTray.cs b/osu.Game/Overlays/NotificationOverlayToastTray.cs index e4d3864beb..1da05dab75 100644 --- a/osu.Game/Overlays/NotificationOverlayToastTray.cs +++ b/osu.Game/Overlays/NotificationOverlayToastTray.cs @@ -78,7 +78,6 @@ namespace osu.Game.Overlays { LayoutDuration = 150, LayoutEasing = Easing.OutQuart, - Spacing = new Vector2(3), RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, }, diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index f83be0c446..fe2f67973a 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -113,6 +113,9 @@ namespace osu.Game.Overlays.Notifications }, dragContainer = new DragContainer(this) { + // Use margin instead of FillFlow spacing to fix extra padding appearing when notification shrinks + // in height. + Padding = new MarginPadding { Vertical = 3f }, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, }.WithChild(MainContent = new Container diff --git a/osu.Game/Overlays/Notifications/NotificationSection.cs b/osu.Game/Overlays/Notifications/NotificationSection.cs index d2e18a0cee..14bacca534 100644 --- a/osu.Game/Overlays/Notifications/NotificationSection.cs +++ b/osu.Game/Overlays/Notifications/NotificationSection.cs @@ -106,7 +106,6 @@ namespace osu.Game.Overlays.Notifications RelativeSizeAxes = Axes.X, LayoutDuration = 150, LayoutEasing = Easing.OutQuart, - Spacing = new Vector2(3), } }); } From d561fcb126689dade097acce3f4f425504680fff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 16:54:25 +0900 Subject: [PATCH 11/14] Don't trigger fling animation when `Close` is triggered by non-user action --- .../Online/TestSceneBeatmapDownloading.cs | 2 +- osu.Game/Database/ModelDownloader.cs | 2 +- osu.Game/Overlays/Notifications/Notification.cs | 13 ++++++------- .../Overlays/Notifications/NotificationSection.cs | 2 +- .../Overlays/Notifications/ProgressNotification.cs | 6 +++--- osu.Game/Updater/UpdateManager.cs | 6 +++--- 6 files changed, 15 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Online/TestSceneBeatmapDownloading.cs b/osu.Game.Tests/Online/TestSceneBeatmapDownloading.cs index e7a6e9a543..641c1ad523 100644 --- a/osu.Game.Tests/Online/TestSceneBeatmapDownloading.cs +++ b/osu.Game.Tests/Online/TestSceneBeatmapDownloading.cs @@ -91,7 +91,7 @@ namespace osu.Game.Tests.Online { AddStep("download beatmap", () => beatmaps.Download(test_db_model)); - AddStep("cancel download from notification", () => recentNotification.Close()); + AddStep("cancel download from notification", () => recentNotification.Close(true)); AddUntilStep("is removed from download list", () => beatmaps.GetExistingDownload(test_db_model) == null); AddAssert("is notification cancelled", () => recentNotification.State == ProgressNotificationState.Cancelled); diff --git a/osu.Game/Database/ModelDownloader.cs b/osu.Game/Database/ModelDownloader.cs index a3678602d1..877c90a534 100644 --- a/osu.Game/Database/ModelDownloader.cs +++ b/osu.Game/Database/ModelDownloader.cs @@ -111,7 +111,7 @@ namespace osu.Game.Database { if (error is WebException webException && webException.Message == @"TooManyRequests") { - notification.Close(); + notification.Close(false); PostNotification?.Invoke(new TooManyDownloadsNotification()); } else diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index fe2f67973a..acb277d4b4 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -168,7 +168,7 @@ namespace osu.Game.Overlays.Notifications }, new CloseButton(CloseButtonIcon) { - Action = Close, + Action = () => Close(true), Anchor = Anchor.TopRight, Origin = Anchor.TopRight, } @@ -214,7 +214,7 @@ namespace osu.Game.Overlays.Notifications // right click doesn't trigger OnClick so we need to handle here until that changes. if (e.Button != MouseButton.Left) { - Close(); + Close(true); return true; } @@ -227,7 +227,7 @@ namespace osu.Game.Overlays.Notifications if (e.Button == MouseButton.Left) Activated?.Invoke(); - Close(); + Close(true); return true; } @@ -245,13 +245,13 @@ namespace osu.Game.Overlays.Notifications public bool WasClosed; - public virtual void Close() + public virtual void Close(bool userTriggered) { if (WasClosed) return; WasClosed = true; - if (dragContainer.FlingLeft()) + if (userTriggered && dragContainer.FlingLeft()) this.FadeOut(600, Easing.In); else { @@ -272,7 +272,6 @@ namespace osu.Game.Overlays.Notifications public DragContainer(Notification notification) { this.notification = notification; - notification.Closed += () => FlingLeft(); } public override RectangleF BoundingBox @@ -363,7 +362,7 @@ namespace osu.Game.Overlays.Notifications flinging = true; ClearTransforms(); - notification.Close(); + notification.Close(true); return true; } diff --git a/osu.Game/Overlays/Notifications/NotificationSection.cs b/osu.Game/Overlays/Notifications/NotificationSection.cs index 14bacca534..16105f913f 100644 --- a/osu.Game/Overlays/Notifications/NotificationSection.cs +++ b/osu.Game/Overlays/Notifications/NotificationSection.cs @@ -112,7 +112,7 @@ namespace osu.Game.Overlays.Notifications private void clearAll() { - notifications.Children.ForEach(c => c.Close()); + notifications.Children.ForEach(c => c.Close(true)); } protected override void Update() diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index bdf6f704e5..55d6d5057a 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -142,7 +142,7 @@ namespace osu.Game.Overlays.Notifications case ProgressNotificationState.Completed: loadingSpinner.Hide(); attemptPostCompletion(); - base.Close(); + base.Close(false); break; } } @@ -235,12 +235,12 @@ namespace osu.Game.Overlays.Notifications }); } - public override void Close() + public override void Close(bool userTriggered) { switch (State) { case ProgressNotificationState.Cancelled: - base.Close(); + base.Close(userTriggered); break; case ProgressNotificationState.Active: diff --git a/osu.Game/Updater/UpdateManager.cs b/osu.Game/Updater/UpdateManager.cs index 49009e9124..2ef6741ffd 100644 --- a/osu.Game/Updater/UpdateManager.cs +++ b/osu.Game/Updater/UpdateManager.cs @@ -148,7 +148,7 @@ namespace osu.Game.Updater StartDownload(); } - public override void Close() + public override void Close(bool userTriggered) { // cancelling updates is not currently supported by the underlying updater. // only allow dismissing for now. @@ -156,7 +156,7 @@ namespace osu.Game.Updater switch (State) { case ProgressNotificationState.Cancelled: - base.Close(); + base.Close(userTriggered); break; } } @@ -177,7 +177,7 @@ namespace osu.Game.Updater public void FailDownload() { State = ProgressNotificationState.Cancelled; - Close(); + Close(false); } } } From 4ee3e8f087e14631622ffca7f446485f50d4982c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 18:57:18 +0900 Subject: [PATCH 12/14] Don't play fling animation when activating a notification --- osu.Game/Overlays/Notifications/Notification.cs | 6 +++--- osu.Game/Overlays/Notifications/ProgressNotification.cs | 4 ++-- osu.Game/Updater/UpdateManager.cs | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index acb277d4b4..887d729de7 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -227,7 +227,7 @@ namespace osu.Game.Overlays.Notifications if (e.Button == MouseButton.Left) Activated?.Invoke(); - Close(true); + Close(false); return true; } @@ -245,13 +245,13 @@ namespace osu.Game.Overlays.Notifications public bool WasClosed; - public virtual void Close(bool userTriggered) + public virtual void Close(bool runFlingAnimation) { if (WasClosed) return; WasClosed = true; - if (userTriggered && dragContainer.FlingLeft()) + if (runFlingAnimation && dragContainer.FlingLeft()) this.FadeOut(600, Easing.In); else { diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index 55d6d5057a..c4d402e5b9 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -235,12 +235,12 @@ namespace osu.Game.Overlays.Notifications }); } - public override void Close(bool userTriggered) + public override void Close(bool runFlingAnimation) { switch (State) { case ProgressNotificationState.Cancelled: - base.Close(userTriggered); + base.Close(runFlingAnimation); break; case ProgressNotificationState.Active: diff --git a/osu.Game/Updater/UpdateManager.cs b/osu.Game/Updater/UpdateManager.cs index 2ef6741ffd..100464029b 100644 --- a/osu.Game/Updater/UpdateManager.cs +++ b/osu.Game/Updater/UpdateManager.cs @@ -148,7 +148,7 @@ namespace osu.Game.Updater StartDownload(); } - public override void Close(bool userTriggered) + public override void Close(bool runFlingAnimation) { // cancelling updates is not currently supported by the underlying updater. // only allow dismissing for now. @@ -156,7 +156,7 @@ namespace osu.Game.Updater switch (State) { case ProgressNotificationState.Cancelled: - base.Close(userTriggered); + base.Close(runFlingAnimation); break; } } From d92e000fe6db82e258ec9a68e6d0fe9885c99d75 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 19:00:03 +0900 Subject: [PATCH 13/14] Fix flinging a notification not correctly running `Close` --- osu.Game/Overlays/Notifications/Notification.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index 887d729de7..6c1b43c3b5 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -254,11 +254,9 @@ namespace osu.Game.Overlays.Notifications if (runFlingAnimation && dragContainer.FlingLeft()) this.FadeOut(600, Easing.In); else - { - Closed?.Invoke(); this.FadeOut(100); - } + Closed?.Invoke(); Expire(); } @@ -311,7 +309,7 @@ namespace osu.Game.Overlays.Notifications protected override void OnDragEnd(DragEndEvent e) { if (Rotation < -10 || velocity.X < -0.3f) - FlingLeft(); + notification.Close(true); else ResetPosition(); @@ -362,7 +360,6 @@ namespace osu.Game.Overlays.Notifications flinging = true; ClearTransforms(); - notification.Close(true); return true; } From f56f6545c04cd3f4f56a7398a49998ab8fe13da7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 19:06:09 +0900 Subject: [PATCH 14/14] Add test coverage of flinging --- .../TestSceneNotificationOverlay.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs index 8504da69a7..3d13e98865 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs @@ -48,6 +48,40 @@ namespace osu.Game.Tests.Visual.UserInterface notificationOverlay.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count.NewValue}"; }; }); + [Test] + public void TestDismissWithoutActivationFling() + { + bool activated = false; + SimpleNotification notification = null!; + + AddStep("post", () => + { + activated = false; + notificationOverlay.Post(notification = new SimpleNotification + { + Text = @"Welcome to osu!. Enjoy your stay!", + Activated = () => activated = true, + }); + }); + + AddStep("start drag", () => + { + InputManager.MoveMouseTo(notification.ChildrenOfType().Single()); + InputManager.PressButton(MouseButton.Left); + InputManager.MoveMouseTo(notification.ChildrenOfType().Single().ScreenSpaceDrawQuad.Centre + new Vector2(-500, 0)); + }); + + AddStep("fling away", () => + { + InputManager.ReleaseButton(MouseButton.Left); + }); + + AddUntilStep("wait for closed", () => notification.WasClosed); + AddAssert("was not activated", () => !activated); + AddStep("reset mouse position", () => InputManager.MoveMouseTo(Vector2.Zero)); + AddAssert("unread count zero", () => notificationOverlay.UnreadCount.Value == 0); + } + [Test] public void TestDismissWithoutActivationCloseButton() { @@ -75,6 +109,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("wait for closed", () => notification.WasClosed); AddAssert("was not activated", () => !activated); AddStep("reset mouse position", () => InputManager.MoveMouseTo(Vector2.Zero)); + AddAssert("unread count zero", () => notificationOverlay.UnreadCount.Value == 0); } [Test]