From 508b26ac6973a07a7edb6870ec13e065b055a21c Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 7 Sep 2019 07:31:07 +0300 Subject: [PATCH 01/98] Implement basic layout --- .../UserInterface/TestScenePageSelector.cs | 29 +++++ .../Graphics/UserInterface/PageSelector.cs | 100 ++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs create mode 100644 osu.Game/Graphics/UserInterface/PageSelector.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs new file mode 100644 index 0000000000..9911a23bf1 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs @@ -0,0 +1,29 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Game.Graphics.UserInterface; +using osu.Framework.Graphics; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestScenePageSelector : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(PageSelector) + }; + + public TestScenePageSelector() + { + PageSelector pageSelector; + + Child = pageSelector = new PageSelector(10) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + } + } +} diff --git a/osu.Game/Graphics/UserInterface/PageSelector.cs b/osu.Game/Graphics/UserInterface/PageSelector.cs new file mode 100644 index 0000000000..53f112ed68 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/PageSelector.cs @@ -0,0 +1,100 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics; +using osu.Game.Graphics.Containers; +using osu.Framework.Bindables; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; + +namespace osu.Game.Graphics.UserInterface +{ + public class PageSelector : CompositeDrawable + { + private BindableInt currentPage = new BindableInt(1); + + private readonly int maxPages; + private readonly FillFlowContainer pillsFlow; + + public PageSelector(int maxPages) + { + this.maxPages = maxPages; + + AutoSizeAxes = Axes.Both; + InternalChild = pillsFlow = new FillFlowContainer + { + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + currentPage.BindValueChanged(page => redraw(page.NewValue), true); + } + + private void redraw(int newPage) + { + pillsFlow.Clear(); + + for (int i = 0; i < maxPages; i++) + { + addPagePill(i); + } + } + + private void addPagePill(int page) + { + var pill = new Pill(page); + + if (page != currentPage.Value) + pill.Action = () => currentPage.Value = page; + + pillsFlow.Add(pill); + } + + private class Pill : OsuClickableContainer + { + private const int height = 20; + + private readonly Box background; + + public Pill(int page) + { + AutoSizeAxes = Axes.X; + Height = height; + Child = new CircularContainer + { + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Masking = true, + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + }, + new SpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = page.ToString(), + Margin = new MarginPadding { Horizontal = 8 } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + background.Colour = colours.Seafoam; + } + } + } +} From f77cd6582d94811874d74315265d4e91b5f81321 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 7 Sep 2019 08:20:09 +0300 Subject: [PATCH 02/98] Implement CurrentPage class --- .../Graphics/UserInterface/PageSelector.cs | 150 ++++++++++++++---- 1 file changed, 119 insertions(+), 31 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/PageSelector.cs b/osu.Game/Graphics/UserInterface/PageSelector.cs index 53f112ed68..fef35ab229 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector.cs @@ -8,6 +8,9 @@ using osu.Framework.Bindables; using osu.Framework.Allocation; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Events; +using osu.Framework.Extensions.Color4Extensions; +using System; namespace osu.Game.Graphics.UserInterface { @@ -42,59 +45,144 @@ namespace osu.Game.Graphics.UserInterface { pillsFlow.Clear(); - for (int i = 0; i < maxPages; i++) + for (int i = 1; i <= maxPages; i++) { - addPagePill(i); + if (i == currentPage.Value) + addCurrentPagePill(); + else + addPagePill(i); } } private void addPagePill(int page) { - var pill = new Pill(page); - - if (page != currentPage.Value) - pill.Action = () => currentPage.Value = page; - - pillsFlow.Add(pill); + pillsFlow.Add(new Page(page.ToString(), () => currentPage.Value = page)); } - private class Pill : OsuClickableContainer + private void addCurrentPagePill() + { + pillsFlow.Add(new CurrentPage(currentPage.Value.ToString())); + } + + private abstract class DrawablePage : CompositeDrawable { private const int height = 20; + private const int margin = 8; - private readonly Box background; + protected readonly string Text; - public Pill(int page) + protected DrawablePage(string text) { + Text = text; + AutoSizeAxes = Axes.X; Height = height; - Child = new CircularContainer - { - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - Masking = true, - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both, - }, - new SpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Text = page.ToString(), - Margin = new MarginPadding { Horizontal = 8 } - } - } - }; + + var background = CreateBackground(); + + if (background != null) + AddInternal(background); + + var content = CreateContent(); + content.Margin = new MarginPadding { Horizontal = margin }; + + AddInternal(content); + } + + protected abstract Drawable CreateContent(); + + protected virtual Drawable CreateBackground() => null; + } + + private abstract class ActivatedDrawablePage : DrawablePage + { + private readonly Action action; + + public ActivatedDrawablePage(string text, Action action) + : base(text) + { + this.action = action; + } + + protected override bool OnClick(ClickEvent e) + { + action?.Invoke(); + return base.OnClick(e); + } + } + + private class Page : ActivatedDrawablePage + { + private SpriteText text; + + private OsuColour colours; + + public Page(string text, Action action) + : base(text, action) + { } [BackgroundDependencyLoader] private void load(OsuColour colours) { + this.colours = colours; + text.Colour = colours.Seafoam; + } + + protected override bool OnHover(HoverEvent e) + { + text.Colour = colours.Seafoam.Lighten(30f); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + text.Colour = colours.Seafoam; + base.OnHoverLost(e); + } + + protected override Drawable CreateContent() => text = new SpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = Text + }; + } + + private class CurrentPage : DrawablePage + { + private SpriteText text; + + private Box background; + + public CurrentPage(string text) + : base(text) + { + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + text.Colour = colours.GreySeafoam; background.Colour = colours.Seafoam; } + + protected override Drawable CreateContent() => text = new SpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = Text + }; + + protected override Drawable CreateBackground() => new CircularContainer + { + RelativeSizeAxes = Axes.Both, + Masking = true, + Child = background = new Box + { + RelativeSizeAxes = Axes.Both, + } + }; } } } From cea26baaefb58171ca76b1b3b39a70772d8eb7a5 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 7 Sep 2019 08:46:16 +0300 Subject: [PATCH 03/98] Implement placeholder and correct redraw algorithm --- .../UserInterface/TestScenePageSelector.cs | 4 +- .../Graphics/UserInterface/PageSelector.cs | 63 +++++++++++++++---- 2 files changed, 52 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs index 9911a23bf1..e5efa65c91 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs @@ -17,9 +17,7 @@ namespace osu.Game.Tests.Visual.UserInterface public TestScenePageSelector() { - PageSelector pageSelector; - - Child = pageSelector = new PageSelector(10) + Child = new PageSelector(200) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Graphics/UserInterface/PageSelector.cs b/osu.Game/Graphics/UserInterface/PageSelector.cs index fef35ab229..244e87f023 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector.cs @@ -3,7 +3,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics; -using osu.Game.Graphics.Containers; using osu.Framework.Bindables; using osu.Framework.Allocation; using osu.Framework.Graphics.Shapes; @@ -16,7 +15,7 @@ namespace osu.Game.Graphics.UserInterface { public class PageSelector : CompositeDrawable { - private BindableInt currentPage = new BindableInt(1); + public readonly BindableInt CurrentPage = new BindableInt(); private readonly int maxPages; private readonly FillFlowContainer pillsFlow; @@ -38,30 +37,47 @@ namespace osu.Game.Graphics.UserInterface { base.LoadComplete(); - currentPage.BindValueChanged(page => redraw(page.NewValue), true); + CurrentPage.BindValueChanged(_ => redraw(), true); } - private void redraw(int newPage) + private void redraw() { pillsFlow.Clear(); - for (int i = 1; i <= maxPages; i++) + if (CurrentPage.Value > 3) + addDrawablePage(1); + + if (CurrentPage.Value > 4) + addPlaceholder(); + + for (int i = Math.Max(CurrentPage.Value - 2, 1); i <= Math.Min(CurrentPage.Value + 2, maxPages); i++) { - if (i == currentPage.Value) + if (i == CurrentPage.Value) addCurrentPagePill(); else - addPagePill(i); + addDrawablePage(i); } + + if (CurrentPage.Value + 2 < maxPages - 1) + addPlaceholder(); + + if (CurrentPage.Value + 2 < maxPages) + addDrawablePage(maxPages); } - private void addPagePill(int page) + private void addDrawablePage(int page) { - pillsFlow.Add(new Page(page.ToString(), () => currentPage.Value = page)); + pillsFlow.Add(new Page(page.ToString(), () => CurrentPage.Value = page)); + } + + private void addPlaceholder() + { + pillsFlow.Add(new Placeholder()); } private void addCurrentPagePill() { - pillsFlow.Add(new CurrentPage(currentPage.Value.ToString())); + pillsFlow.Add(new SelectedPage(CurrentPage.Value.ToString())); } private abstract class DrawablePage : CompositeDrawable @@ -149,13 +165,13 @@ namespace osu.Game.Graphics.UserInterface }; } - private class CurrentPage : DrawablePage + private class SelectedPage : DrawablePage { private SpriteText text; private Box background; - public CurrentPage(string text) + public SelectedPage(string text) : base(text) { } @@ -184,5 +200,28 @@ namespace osu.Game.Graphics.UserInterface } }; } + + private class Placeholder : DrawablePage + { + private SpriteText text; + + public Placeholder() + : base("...") + { + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + text.Colour = colours.Seafoam; + } + + protected override Drawable CreateContent() => text = new SpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = Text + }; + } } } From 9bd4220e9f6817e3200926c7abce593b6f8e52aa Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 7 Sep 2019 09:20:11 +0300 Subject: [PATCH 04/98] Add prev/next buttons --- .../Graphics/UserInterface/PageSelector.cs | 206 ++++++++++++++++-- 1 file changed, 184 insertions(+), 22 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/PageSelector.cs b/osu.Game/Graphics/UserInterface/PageSelector.cs index 244e87f023..d93dcdace9 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector.cs @@ -10,12 +10,13 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Framework.Extensions.Color4Extensions; using System; +using osuTK; namespace osu.Game.Graphics.UserInterface { public class PageSelector : CompositeDrawable { - public readonly BindableInt CurrentPage = new BindableInt(); + public readonly BindableInt CurrentPage = new BindableInt(1); private readonly int maxPages; private readonly FillFlowContainer pillsFlow; @@ -44,6 +45,11 @@ namespace osu.Game.Graphics.UserInterface { pillsFlow.Clear(); + if (CurrentPage.Value == 1) + addPreviousPageButton(); + else + addPreviousPageButton(() => CurrentPage.Value -= 1); + if (CurrentPage.Value > 3) addDrawablePage(1); @@ -63,6 +69,11 @@ namespace osu.Game.Graphics.UserInterface if (CurrentPage.Value + 2 < maxPages) addDrawablePage(maxPages); + + if (CurrentPage.Value == maxPages) + addNextPageButton(); + else + addNextPageButton(() => CurrentPage.Value += 1); } private void addDrawablePage(int page) @@ -80,12 +91,23 @@ namespace osu.Game.Graphics.UserInterface pillsFlow.Add(new SelectedPage(CurrentPage.Value.ToString())); } + private void addPreviousPageButton(Action action = null) + { + pillsFlow.Add(new PreviousPageButton(action)); + } + + private void addNextPageButton(Action action = null) + { + pillsFlow.Add(new NextPageButton(action)); + } + private abstract class DrawablePage : CompositeDrawable { private const int height = 20; private const int margin = 8; protected readonly string Text; + protected readonly Drawable Content; protected DrawablePage(string text) { @@ -99,10 +121,10 @@ namespace osu.Game.Graphics.UserInterface if (background != null) AddInternal(background); - var content = CreateContent(); - content.Margin = new MarginPadding { Horizontal = margin }; + Content = CreateContent(); + Content.Margin = new MarginPadding { Horizontal = margin }; - AddInternal(content); + AddInternal(Content); } protected abstract Drawable CreateContent(); @@ -112,25 +134,23 @@ namespace osu.Game.Graphics.UserInterface private abstract class ActivatedDrawablePage : DrawablePage { - private readonly Action action; + protected readonly Action Action; - public ActivatedDrawablePage(string text, Action action) + public ActivatedDrawablePage(string text, Action action = null) : base(text) { - this.action = action; + Action = action; } protected override bool OnClick(ClickEvent e) { - action?.Invoke(); + Action?.Invoke(); return base.OnClick(e); } } private class Page : ActivatedDrawablePage { - private SpriteText text; - private OsuColour colours; public Page(string text, Action action) @@ -142,22 +162,22 @@ namespace osu.Game.Graphics.UserInterface private void load(OsuColour colours) { this.colours = colours; - text.Colour = colours.Seafoam; + Content.Colour = colours.Seafoam; } protected override bool OnHover(HoverEvent e) { - text.Colour = colours.Seafoam.Lighten(30f); + Content.Colour = colours.Seafoam.Lighten(30f); return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { - text.Colour = colours.Seafoam; + Content.Colour = colours.Seafoam; base.OnHoverLost(e); } - protected override Drawable CreateContent() => text = new SpriteText + protected override Drawable CreateContent() => new SpriteText { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -167,8 +187,6 @@ namespace osu.Game.Graphics.UserInterface private class SelectedPage : DrawablePage { - private SpriteText text; - private Box background; public SelectedPage(string text) @@ -179,11 +197,11 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load(OsuColour colours) { - text.Colour = colours.GreySeafoam; + Content.Colour = colours.GreySeafoam; background.Colour = colours.Seafoam; } - protected override Drawable CreateContent() => text = new SpriteText + protected override Drawable CreateContent() => new SpriteText { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -203,8 +221,6 @@ namespace osu.Game.Graphics.UserInterface private class Placeholder : DrawablePage { - private SpriteText text; - public Placeholder() : base("...") { @@ -213,15 +229,161 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load(OsuColour colours) { - text.Colour = colours.Seafoam; + Content.Colour = colours.Seafoam; } - protected override Drawable CreateContent() => text = new SpriteText + protected override Drawable CreateContent() => new SpriteText { Anchor = Anchor.Centre, Origin = Anchor.Centre, Text = Text }; } + + private class PreviousPageButton : ActivatedDrawablePage + { + private OsuColour colours; + private Box background; + + public PreviousPageButton(Action action) + : base("prev", action) + { + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + this.colours = colours; + Content.Colour = colours.Seafoam; + background.Colour = colours.GreySeafoam; + + if (Action == null) + { + Content.FadeColour(colours.GrayA); + background.FadeColour(colours.GrayA); + } + } + + protected override bool OnHover(HoverEvent e) + { + Content.Colour = colours.Seafoam.Lighten(30f); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + Content.Colour = colours.Seafoam; + base.OnHoverLost(e); + } + + protected override Drawable CreateContent() => new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(3), + Children = new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Icon = FontAwesome.Solid.CaretLeft, + Size = new Vector2(10), + }, + new SpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Text = Text.ToUpper(), + } + } + }; + + protected override Drawable CreateBackground() => new CircularContainer + { + RelativeSizeAxes = Axes.Both, + Masking = true, + Child = background = new Box + { + RelativeSizeAxes = Axes.Both, + } + }; + } + + private class NextPageButton : ActivatedDrawablePage + { + private OsuColour colours; + private Box background; + + public NextPageButton(Action action) + : base("next", action) + { + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + this.colours = colours; + Content.Colour = colours.Seafoam; + background.Colour = colours.GreySeafoam; + + if (Action == null) + { + Content.FadeColour(colours.GrayA); + background.FadeColour(colours.GrayA); + } + } + + protected override bool OnHover(HoverEvent e) + { + Content.Colour = colours.Seafoam.Lighten(30f); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + Content.Colour = colours.Seafoam; + base.OnHoverLost(e); + } + + protected override Drawable CreateContent() => new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(3), + Children = new Drawable[] + { + new SpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Text = Text.ToUpper(), + }, + new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Icon = FontAwesome.Solid.CaretRight, + Size = new Vector2(10), + }, + } + }; + + protected override Drawable CreateBackground() => new CircularContainer + { + RelativeSizeAxes = Axes.Both, + Masking = true, + Child = background = new Box + { + RelativeSizeAxes = Axes.Both, + } + }; + } } } From ba18f77b628496569cb5d5718081ea271b51e4f3 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 7 Sep 2019 22:31:08 +0300 Subject: [PATCH 05/98] Use OsuHoverContainer for prev/next buttons --- .../Graphics/UserInterface/PageSelector.cs | 231 ++++++------------ 1 file changed, 78 insertions(+), 153 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/PageSelector.cs b/osu.Game/Graphics/UserInterface/PageSelector.cs index d93dcdace9..3ca133d64b 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector.cs @@ -11,6 +11,8 @@ using osu.Framework.Input.Events; using osu.Framework.Extensions.Color4Extensions; using System; using osuTK; +using osu.Game.Graphics.Containers; +using System.Collections.Generic; namespace osu.Game.Graphics.UserInterface { @@ -21,16 +23,34 @@ namespace osu.Game.Graphics.UserInterface private readonly int maxPages; private readonly FillFlowContainer pillsFlow; + private readonly Button previousPageButton; + private readonly Button nextPageButton; + public PageSelector(int maxPages) { this.maxPages = maxPages; AutoSizeAxes = Axes.Both; - InternalChild = pillsFlow = new FillFlowContainer + InternalChild = new FillFlowContainer { - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + previousPageButton = new Button(false, "prev") + { + Action = () => CurrentPage.Value -= 1, + }, + pillsFlow = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + }, + nextPageButton = new Button(true, "next") + { + Action = () => CurrentPage.Value += 1 + } + } }; } @@ -43,12 +63,10 @@ namespace osu.Game.Graphics.UserInterface private void redraw() { - pillsFlow.Clear(); + previousPageButton.Enabled.Value = CurrentPage.Value != 1; + nextPageButton.Enabled.Value = CurrentPage.Value != maxPages; - if (CurrentPage.Value == 1) - addPreviousPageButton(); - else - addPreviousPageButton(() => CurrentPage.Value -= 1); + pillsFlow.Clear(); if (CurrentPage.Value > 3) addDrawablePage(1); @@ -69,11 +87,6 @@ namespace osu.Game.Graphics.UserInterface if (CurrentPage.Value + 2 < maxPages) addDrawablePage(maxPages); - - if (CurrentPage.Value == maxPages) - addNextPageButton(); - else - addNextPageButton(() => CurrentPage.Value += 1); } private void addDrawablePage(int page) @@ -91,16 +104,6 @@ namespace osu.Game.Graphics.UserInterface pillsFlow.Add(new SelectedPage(CurrentPage.Value.ToString())); } - private void addPreviousPageButton(Action action = null) - { - pillsFlow.Add(new PreviousPageButton(action)); - } - - private void addNextPageButton(Action action = null) - { - pillsFlow.Add(new NextPageButton(action)); - } - private abstract class DrawablePage : CompositeDrawable { private const int height = 20; @@ -240,150 +243,72 @@ namespace osu.Game.Graphics.UserInterface }; } - private class PreviousPageButton : ActivatedDrawablePage + private class Button : OsuHoverContainer { - private OsuColour colours; - private Box background; + private const int height = 20; + private const int margin = 8; - public PreviousPageButton(Action action) - : base("prev", action) + private readonly Anchor alignment; + private readonly Box background; + + protected override IEnumerable EffectTargets => new[] { background }; + + public Button(bool rightAligned, string text) { - } + alignment = rightAligned ? Anchor.x0 : Anchor.x2; - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - this.colours = colours; - Content.Colour = colours.Seafoam; - background.Colour = colours.GreySeafoam; + AutoSizeAxes = Axes.X; + Height = height; - if (Action == null) + Child = new CircularContainer { - Content.FadeColour(colours.GrayA); - background.FadeColour(colours.GrayA); - } - } - - protected override bool OnHover(HoverEvent e) - { - Content.Colour = colours.Seafoam.Lighten(30f); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - Content.Colour = colours.Seafoam; - base.OnHoverLost(e); - } - - protected override Drawable CreateContent() => new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(3), - Children = new Drawable[] - { - new SpriteIcon + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Masking = true, + Children = new Drawable[] { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Icon = FontAwesome.Solid.CaretLeft, - Size = new Vector2(10), - }, - new SpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Text = Text.ToUpper(), + background = new Box + { + RelativeSizeAxes = Axes.Both, + }, + new Container + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Horizontal = margin }, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new SpriteText + { + Anchor = Anchor.y1 | alignment, + Origin = Anchor.y1 | alignment, + Text = text.ToUpper(), + }, + new SpriteIcon + { + Anchor = Anchor.y1 | alignment, + Origin = Anchor.y1 | alignment, + Icon = alignment == Anchor.x2 ? FontAwesome.Solid.ChevronLeft : FontAwesome.Solid.ChevronRight, + Size = new Vector2(10), + }, + } + } + } } - } - }; - - protected override Drawable CreateBackground() => new CircularContainer - { - RelativeSizeAxes = Axes.Both, - Masking = true, - Child = background = new Box - { - RelativeSizeAxes = Axes.Both, - } - }; - } - - private class NextPageButton : ActivatedDrawablePage - { - private OsuColour colours; - private Box background; - - public NextPageButton(Action action) - : base("next", action) - { + }; } [BackgroundDependencyLoader] private void load(OsuColour colours) { - this.colours = colours; - Content.Colour = colours.Seafoam; - background.Colour = colours.GreySeafoam; - - if (Action == null) - { - Content.FadeColour(colours.GrayA); - background.FadeColour(colours.GrayA); - } + IdleColour = colours.GreySeafoamDark; + HoverColour = colours.GrayA; } - - protected override bool OnHover(HoverEvent e) - { - Content.Colour = colours.Seafoam.Lighten(30f); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - Content.Colour = colours.Seafoam; - base.OnHoverLost(e); - } - - protected override Drawable CreateContent() => new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(3), - Children = new Drawable[] - { - new SpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Text = Text.ToUpper(), - }, - new SpriteIcon - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Icon = FontAwesome.Solid.CaretRight, - Size = new Vector2(10), - }, - } - }; - - protected override Drawable CreateBackground() => new CircularContainer - { - RelativeSizeAxes = Axes.Both, - Masking = true, - Child = background = new Box - { - RelativeSizeAxes = Axes.Both, - } - }; } } } From ec8298ac53108d65a7901228bf524ebcae794356 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 7 Sep 2019 22:33:26 +0300 Subject: [PATCH 06/98] Simplify redraw function --- .../Graphics/UserInterface/PageSelector.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/PageSelector.cs b/osu.Game/Graphics/UserInterface/PageSelector.cs index 3ca133d64b..0b0c2eec83 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector.cs @@ -58,34 +58,34 @@ namespace osu.Game.Graphics.UserInterface { base.LoadComplete(); - CurrentPage.BindValueChanged(_ => redraw(), true); + CurrentPage.BindValueChanged(page => redraw(page.NewValue), true); } - private void redraw() + private void redraw(int newPage) { - previousPageButton.Enabled.Value = CurrentPage.Value != 1; - nextPageButton.Enabled.Value = CurrentPage.Value != maxPages; + previousPageButton.Enabled.Value = newPage != 1; + nextPageButton.Enabled.Value = newPage != maxPages; pillsFlow.Clear(); - if (CurrentPage.Value > 3) + if (newPage > 3) addDrawablePage(1); - if (CurrentPage.Value > 4) + if (newPage > 4) addPlaceholder(); - for (int i = Math.Max(CurrentPage.Value - 2, 1); i <= Math.Min(CurrentPage.Value + 2, maxPages); i++) + for (int i = Math.Max(newPage - 2, 1); i <= Math.Min(newPage + 2, maxPages); i++) { - if (i == CurrentPage.Value) + if (i == newPage) addCurrentPagePill(); else addDrawablePage(i); } - if (CurrentPage.Value + 2 < maxPages - 1) + if (newPage + 2 < maxPages - 1) addPlaceholder(); - if (CurrentPage.Value + 2 < maxPages) + if (newPage + 2 < maxPages) addDrawablePage(maxPages); } From df29465ba44c974290bdaa775d78b736a2340f8d Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 7 Sep 2019 23:42:30 +0300 Subject: [PATCH 07/98] Implement a single PageItem component --- .../Graphics/UserInterface/PageSelector.cs | 237 +++++++----------- 1 file changed, 86 insertions(+), 151 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/PageSelector.cs b/osu.Game/Graphics/UserInterface/PageSelector.cs index 0b0c2eec83..9dea9232ac 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector.cs @@ -7,12 +7,12 @@ using osu.Framework.Bindables; using osu.Framework.Allocation; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.Events; using osu.Framework.Extensions.Color4Extensions; using System; using osuTK; using osu.Game.Graphics.Containers; using System.Collections.Generic; +using osu.Framework.Extensions.IEnumerableExtensions; namespace osu.Game.Graphics.UserInterface { @@ -21,7 +21,7 @@ namespace osu.Game.Graphics.UserInterface public readonly BindableInt CurrentPage = new BindableInt(1); private readonly int maxPages; - private readonly FillFlowContainer pillsFlow; + private readonly FillFlowContainer itemsFlow; private readonly Button previousPageButton; private readonly Button nextPageButton; @@ -41,7 +41,7 @@ namespace osu.Game.Graphics.UserInterface { Action = () => CurrentPage.Value -= 1, }, - pillsFlow = new FillFlowContainer + itemsFlow = new FillFlowContainer { AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, @@ -66,7 +66,7 @@ namespace osu.Game.Graphics.UserInterface previousPageButton.Enabled.Value = newPage != 1; nextPageButton.Enabled.Value = newPage != maxPages; - pillsFlow.Clear(); + itemsFlow.Clear(); if (newPage > 3) addDrawablePage(1); @@ -77,7 +77,7 @@ namespace osu.Game.Graphics.UserInterface for (int i = Math.Max(newPage - 2, 1); i <= Math.Min(newPage + 2, maxPages); i++) { if (i == newPage) - addCurrentPagePill(); + addDrawableCurrentPage(); else addDrawablePage(i); } @@ -89,137 +89,101 @@ namespace osu.Game.Graphics.UserInterface addDrawablePage(maxPages); } - private void addDrawablePage(int page) + private void addDrawablePage(int page) => itemsFlow.Add(new DrawablePage(page.ToString()) { - pillsFlow.Add(new Page(page.ToString(), () => CurrentPage.Value = page)); - } + Action = () => CurrentPage.Value = page, + }); - private void addPlaceholder() - { - pillsFlow.Add(new Placeholder()); - } + private void addPlaceholder() => itemsFlow.Add(new Placeholder()); - private void addCurrentPagePill() - { - pillsFlow.Add(new SelectedPage(CurrentPage.Value.ToString())); - } + private void addDrawableCurrentPage() => itemsFlow.Add(new SelectedPage(CurrentPage.Value.ToString())); - private abstract class DrawablePage : CompositeDrawable + private abstract class PageItem : OsuHoverContainer { - private const int height = 20; private const int margin = 8; + private const int height = 20; - protected readonly string Text; - protected readonly Drawable Content; - - protected DrawablePage(string text) + protected PageItem(string text) { - Text = text; - AutoSizeAxes = Axes.X; Height = height; + var contentContainer = new CircularContainer + { + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Masking = true, + }; + var background = CreateBackground(); - if (background != null) - AddInternal(background); + contentContainer.Add(background); - Content = CreateContent(); - Content.Margin = new MarginPadding { Horizontal = margin }; + var drawableText = CreateText(text); + if (drawableText != null) + { + drawableText.Margin = new MarginPadding { Horizontal = margin }; + contentContainer.Add(drawableText); + } - AddInternal(Content); + Add(contentContainer); } - protected abstract Drawable CreateContent(); + protected abstract Drawable CreateText(string text); - protected virtual Drawable CreateBackground() => null; + protected abstract Drawable CreateBackground(); } - private abstract class ActivatedDrawablePage : DrawablePage + private class DrawablePage : PageItem { - protected readonly Action Action; + protected SpriteText SpriteText; - public ActivatedDrawablePage(string text, Action action = null) + protected override IEnumerable EffectTargets => new[] { SpriteText }; + + public DrawablePage(string text) : base(text) { - Action = action; } - protected override bool OnClick(ClickEvent e) + protected override Drawable CreateBackground() => null; + + protected override Drawable CreateText(string text) => SpriteText = new SpriteText { - Action?.Invoke(); - return base.OnClick(e); - } - } - - private class Page : ActivatedDrawablePage - { - private OsuColour colours; - - public Page(string text, Action action) - : base(text, action) - { - } + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = text, + }; [BackgroundDependencyLoader] private void load(OsuColour colours) { - this.colours = colours; - Content.Colour = colours.Seafoam; + IdleColour = colours.Seafoam; + HoverColour = colours.Seafoam.Lighten(30f); } - - protected override bool OnHover(HoverEvent e) - { - Content.Colour = colours.Seafoam.Lighten(30f); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - Content.Colour = colours.Seafoam; - base.OnHoverLost(e); - } - - protected override Drawable CreateContent() => new SpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Text = Text - }; } private class SelectedPage : DrawablePage { private Box background; + protected override IEnumerable EffectTargets => null; + public SelectedPage(string text) : base(text) { } + protected override Drawable CreateBackground() => background = new Box + { + RelativeSizeAxes = Axes.Both, + }; + [BackgroundDependencyLoader] private void load(OsuColour colours) { - Content.Colour = colours.GreySeafoam; background.Colour = colours.Seafoam; + SpriteText.Colour = colours.GreySeafoamDark; } - - protected override Drawable CreateContent() => new SpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Text = Text - }; - - protected override Drawable CreateBackground() => new CircularContainer - { - RelativeSizeAxes = Axes.Both, - Masking = true, - Child = background = new Box - { - RelativeSizeAxes = Axes.Both, - } - }; } private class Placeholder : DrawablePage @@ -228,79 +192,28 @@ namespace osu.Game.Graphics.UserInterface : base("...") { } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - Content.Colour = colours.Seafoam; - } - - protected override Drawable CreateContent() => new SpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Text = Text - }; } - private class Button : OsuHoverContainer + private class Button : PageItem { - private const int height = 20; - private const int margin = 8; - - private readonly Anchor alignment; - private readonly Box background; + private Box background; + private FillFlowContainer textContainer; + private SpriteIcon icon; protected override IEnumerable EffectTargets => new[] { background }; public Button(bool rightAligned, string text) + : base(text) { - alignment = rightAligned ? Anchor.x0 : Anchor.x2; + var alignment = rightAligned ? Anchor.x0 : Anchor.x2; - AutoSizeAxes = Axes.X; - Height = height; - - Child = new CircularContainer + textContainer.ForEach(drawable => { - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - Masking = true, - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both, - }, - new Container - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Child = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Horizontal = margin }, - Direction = FillDirection.Horizontal, - Children = new Drawable[] - { - new SpriteText - { - Anchor = Anchor.y1 | alignment, - Origin = Anchor.y1 | alignment, - Text = text.ToUpper(), - }, - new SpriteIcon - { - Anchor = Anchor.y1 | alignment, - Origin = Anchor.y1 | alignment, - Icon = alignment == Anchor.x2 ? FontAwesome.Solid.ChevronLeft : FontAwesome.Solid.ChevronRight, - Size = new Vector2(10), - }, - } - } - } - } - }; + drawable.Anchor = Anchor.y1 | alignment; + drawable.Origin = Anchor.y1 | alignment; + }); + + icon.Icon = alignment == Anchor.x2 ? FontAwesome.Solid.ChevronLeft : FontAwesome.Solid.ChevronRight; } [BackgroundDependencyLoader] @@ -309,6 +222,28 @@ namespace osu.Game.Graphics.UserInterface IdleColour = colours.GreySeafoamDark; HoverColour = colours.GrayA; } + + protected override Drawable CreateBackground() => background = new Box + { + RelativeSizeAxes = Axes.Both, + }; + + protected override Drawable CreateText(string text) => textContainer = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new SpriteText + { + Text = text.ToUpper(), + }, + icon = new SpriteIcon + { + Size = new Vector2(10), + }, + } + }; } } } From b0884d16fbcc4a540af86f40aa359a577d39d988 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 8 Sep 2019 00:13:23 +0300 Subject: [PATCH 08/98] Visual adjustments --- .../Graphics/UserInterface/PageSelector.cs | 67 +++++++++++++------ 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/PageSelector.cs b/osu.Game/Graphics/UserInterface/PageSelector.cs index 9dea9232ac..9a5ffad0d4 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector.cs @@ -13,6 +13,7 @@ using osuTK; using osu.Game.Graphics.Containers; using System.Collections.Generic; using osu.Framework.Extensions.IEnumerableExtensions; +using osuTK.Graphics; namespace osu.Game.Graphics.UserInterface { @@ -100,38 +101,47 @@ namespace osu.Game.Graphics.UserInterface private abstract class PageItem : OsuHoverContainer { - private const int margin = 8; + private const int margin = 10; private const int height = 20; + protected override Container Content => contentContainer; + + private readonly CircularContainer contentContainer; + protected PageItem(string text) { AutoSizeAxes = Axes.X; Height = height; - var contentContainer = new CircularContainer + base.Content.Add(contentContainer = new CircularContainer { AutoSizeAxes = Axes.X, RelativeSizeAxes = Axes.Y, Masking = true, - }; + }); var background = CreateBackground(); if (background != null) - contentContainer.Add(background); + Add(background); var drawableText = CreateText(text); if (drawableText != null) { drawableText.Margin = new MarginPadding { Horizontal = margin }; - contentContainer.Add(drawableText); + Add(drawableText); } + } - Add(contentContainer); + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + IdleColour = colours.Seafoam; + HoverColour = colours.Seafoam.Lighten(30f); } protected abstract Drawable CreateText(string text); - protected abstract Drawable CreateBackground(); + protected virtual Drawable CreateBackground() => null; } private class DrawablePage : PageItem @@ -145,28 +155,20 @@ namespace osu.Game.Graphics.UserInterface { } - protected override Drawable CreateBackground() => null; - protected override Drawable CreateText(string text) => SpriteText = new SpriteText { Anchor = Anchor.Centre, Origin = Anchor.Centre, Text = text, + Font = OsuFont.GetFont(size: 12), }; - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - IdleColour = colours.Seafoam; - HoverColour = colours.Seafoam.Lighten(30f); - } } private class SelectedPage : DrawablePage { private Box background; - protected override IEnumerable EffectTargets => null; + protected override IEnumerable EffectTargets => new[] { background }; public SelectedPage(string text) : base(text) @@ -181,7 +183,6 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load(OsuColour colours) { - background.Colour = colours.Seafoam; SpriteText.Colour = colours.GreySeafoamDark; } } @@ -196,11 +197,14 @@ namespace osu.Game.Graphics.UserInterface private class Button : PageItem { + private const int duration = 100; + private Box background; private FillFlowContainer textContainer; private SpriteIcon icon; + private readonly Box fadeBox; - protected override IEnumerable EffectTargets => new[] { background }; + protected override IEnumerable EffectTargets => new[] { textContainer }; public Button(bool rightAligned, string text) : base(text) @@ -214,13 +218,29 @@ namespace osu.Game.Graphics.UserInterface }); icon.Icon = alignment == Anchor.x2 ? FontAwesome.Solid.ChevronLeft : FontAwesome.Solid.ChevronRight; + + Add(fadeBox = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(100) + }); } [BackgroundDependencyLoader] private void load(OsuColour colours) { - IdleColour = colours.GreySeafoamDark; - HoverColour = colours.GrayA; + background.Colour = colours.GreySeafoamDark; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Enabled.BindValueChanged(onEnabledChanged, true); + } + + private void onEnabledChanged(ValueChangedEvent enabled) + { + fadeBox.FadeTo(enabled.NewValue ? 0 : 1, duration); } protected override Drawable CreateBackground() => background = new Box @@ -231,16 +251,19 @@ namespace osu.Game.Graphics.UserInterface protected override Drawable CreateText(string text) => textContainer = new FillFlowContainer { AutoSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Direction = FillDirection.Horizontal, Children = new Drawable[] { new SpriteText { Text = text.ToUpper(), + Font = OsuFont.GetFont(size: 12), }, icon = new SpriteIcon { - Size = new Vector2(10), + Size = new Vector2(8), }, } }; From b97f4a81db6a35ba5fac734bbdb0f8b42a948419 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 8 Sep 2019 00:27:40 +0300 Subject: [PATCH 09/98] Add more testing --- .../UserInterface/TestScenePageSelector.cs | 22 +++++++++++++++++++ .../Graphics/UserInterface/PageSelector.cs | 18 +++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs index e5efa65c91..cb83fbd028 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs @@ -22,6 +22,28 @@ namespace osu.Game.Tests.Visual.UserInterface Anchor = Anchor.Centre, Origin = Anchor.Centre, }; + + AddStep("1 max pages", () => redraw(1)); + AddStep("10 max pages", () => redraw(10)); + AddStep("200 max pages, current 199", () => redraw(200, 199)); + AddStep("200 max pages, current 201", () => redraw(200, 201)); + AddStep("200 max pages, current -10", () => redraw(200, -10)); + } + + private void redraw(int maxPages, int currentPage = 0) + { + Clear(); + + var selector = new PageSelector(maxPages) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + + if (currentPage != 0) + selector.CurrentPage.Value = currentPage; + + Add(selector); } } } diff --git a/osu.Game/Graphics/UserInterface/PageSelector.cs b/osu.Game/Graphics/UserInterface/PageSelector.cs index 9a5ffad0d4..79a1680e4b 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector.cs @@ -59,11 +59,25 @@ namespace osu.Game.Graphics.UserInterface { base.LoadComplete(); - CurrentPage.BindValueChanged(page => redraw(page.NewValue), true); + CurrentPage.BindValueChanged(_ => redraw(), true); } - private void redraw(int newPage) + private void redraw() { + if (CurrentPage.Value > maxPages) + { + CurrentPage.Value = maxPages; + return; + } + + if (CurrentPage.Value < 1) + { + CurrentPage.Value = 1; + return; + } + + int newPage = CurrentPage.Value; + previousPageButton.Enabled.Value = newPage != 1; nextPageButton.Enabled.Value = newPage != maxPages; From 0451b45fab4ed1a377f99e7f508e7b9c0b451ef6 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 8 Sep 2019 00:37:11 +0300 Subject: [PATCH 10/98] Add missing blank line --- osu.Game/Graphics/UserInterface/PageSelector.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Graphics/UserInterface/PageSelector.cs b/osu.Game/Graphics/UserInterface/PageSelector.cs index 79a1680e4b..9db6366ad6 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector.cs @@ -139,6 +139,7 @@ namespace osu.Game.Graphics.UserInterface Add(background); var drawableText = CreateText(text); + if (drawableText != null) { drawableText.Margin = new MarginPadding { Horizontal = margin }; From bee7760d29e884dff532828e3bbb1f1d71fd3d6d Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 10 Sep 2019 01:36:25 +0300 Subject: [PATCH 11/98] Make MaxPages value a bindable --- .../UserInterface/TestScenePageSelector.cs | 47 ++++++++++--------- .../Graphics/UserInterface/PageSelector.cs | 40 ++++++++++++---- 2 files changed, 57 insertions(+), 30 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs index cb83fbd028..14cb27c97e 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs @@ -15,35 +15,40 @@ namespace osu.Game.Tests.Visual.UserInterface typeof(PageSelector) }; + private readonly PageSelector pageSelector; + public TestScenePageSelector() { - Child = new PageSelector(200) + Child = pageSelector = new PageSelector { Anchor = Anchor.Centre, Origin = Anchor.Centre, }; - AddStep("1 max pages", () => redraw(1)); - AddStep("10 max pages", () => redraw(10)); - AddStep("200 max pages, current 199", () => redraw(200, 199)); - AddStep("200 max pages, current 201", () => redraw(200, 201)); - AddStep("200 max pages, current -10", () => redraw(200, -10)); - } - - private void redraw(int maxPages, int currentPage = 0) - { - Clear(); - - var selector = new PageSelector(maxPages) + AddStep("10 max pages", () => setMaxPages(10)); + AddStep("200 max pages, current 199", () => { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }; - - if (currentPage != 0) - selector.CurrentPage.Value = currentPage; - - Add(selector); + setMaxPages(200); + setCurrentPage(199); + }); + AddStep("200 max pages, current 201", () => + { + setMaxPages(200); + setCurrentPage(201); + }); + AddStep("200 max pages, current -10", () => + { + setMaxPages(200); + setCurrentPage(-10); + }); + AddStep("-10 max pages", () => + { + setMaxPages(-10); + }); } + + private void setMaxPages(int maxPages) => pageSelector.MaxPages.Value = maxPages; + + private void setCurrentPage(int currentPage) => pageSelector.CurrentPage.Value = currentPage; } } diff --git a/osu.Game/Graphics/UserInterface/PageSelector.cs b/osu.Game/Graphics/UserInterface/PageSelector.cs index 9db6366ad6..66f51a6dad 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector.cs @@ -20,17 +20,15 @@ namespace osu.Game.Graphics.UserInterface public class PageSelector : CompositeDrawable { public readonly BindableInt CurrentPage = new BindableInt(1); + public readonly BindableInt MaxPages = new BindableInt(1); - private readonly int maxPages; private readonly FillFlowContainer itemsFlow; private readonly Button previousPageButton; private readonly Button nextPageButton; - public PageSelector(int maxPages) + public PageSelector() { - this.maxPages = maxPages; - AutoSizeAxes = Axes.Both; InternalChild = new FillFlowContainer { @@ -59,24 +57,48 @@ namespace osu.Game.Graphics.UserInterface { base.LoadComplete(); - CurrentPage.BindValueChanged(_ => redraw(), true); + MaxPages.BindValueChanged(pagesAmount => onMaxPagesChanged(pagesAmount.NewValue), true); + CurrentPage.BindValueChanged(page => onCurrentPageChanged(page.NewValue), true); } - private void redraw() + private void onMaxPagesChanged(int pagesAmount) { - if (CurrentPage.Value > maxPages) + if (pagesAmount < 1) { - CurrentPage.Value = maxPages; + MaxPages.Value = 1; return; } - if (CurrentPage.Value < 1) + if (CurrentPage.Value > pagesAmount) + { + CurrentPage.Value = pagesAmount; + return; + } + + redraw(); + } + + private void onCurrentPageChanged(int newPage) + { + if (newPage > MaxPages.Value) + { + CurrentPage.Value = MaxPages.Value; + return; + } + + if (newPage < 1) { CurrentPage.Value = 1; return; } + redraw(); + } + + private void redraw() + { int newPage = CurrentPage.Value; + int maxPages = MaxPages.Value; previousPageButton.Enabled.Value = newPage != 1; nextPageButton.Enabled.Value = newPage != maxPages; From eb683b079b5c8dd20888a8dab9ce5ac8daf6077a Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 10 Sep 2019 05:12:50 +0300 Subject: [PATCH 12/98] Adjust colours --- osu.Game/Graphics/UserInterface/PageSelector.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/PageSelector.cs b/osu.Game/Graphics/UserInterface/PageSelector.cs index 66f51a6dad..25e6ed654c 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector.cs @@ -172,8 +172,8 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load(OsuColour colours) { - IdleColour = colours.Seafoam; - HoverColour = colours.Seafoam.Lighten(30f); + IdleColour = colours.Lime; + HoverColour = colours.Lime.Lighten(20f); } protected abstract Drawable CreateText(string text); From 41be9b3f8f08dd691a38ab1ff50872d126fe9f2e Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 19 Sep 2019 16:45:38 +0300 Subject: [PATCH 13/98] Add asserts to the test scene --- osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs index 14cb27c97e..3516e23f98 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs @@ -36,15 +36,18 @@ namespace osu.Game.Tests.Visual.UserInterface setMaxPages(200); setCurrentPage(201); }); + AddAssert("Current equals max", () => pageSelector.CurrentPage.Value == pageSelector.MaxPages.Value); AddStep("200 max pages, current -10", () => { setMaxPages(200); setCurrentPage(-10); }); + AddAssert("Current is 1", () => pageSelector.CurrentPage.Value == 1); AddStep("-10 max pages", () => { setMaxPages(-10); }); + AddAssert("Current is 1, max is 1", () => pageSelector.CurrentPage.Value == 1 && pageSelector.MaxPages.Value == 1); } private void setMaxPages(int maxPages) => pageSelector.MaxPages.Value = maxPages; From 2c0694257cd82e1dd539e0fb88424db7a6e1daba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Dec 2019 14:44:34 +0900 Subject: [PATCH 14/98] Fix incorrect spritetext usage --- osu.Game/Graphics/UserInterface/PageSelector.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/PageSelector.cs b/osu.Game/Graphics/UserInterface/PageSelector.cs index 25e6ed654c..c9f0f3c74d 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector.cs @@ -13,6 +13,7 @@ using osuTK; using osu.Game.Graphics.Containers; using System.Collections.Generic; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Graphics.Sprites; using osuTK.Graphics; namespace osu.Game.Graphics.UserInterface @@ -192,7 +193,7 @@ namespace osu.Game.Graphics.UserInterface { } - protected override Drawable CreateText(string text) => SpriteText = new SpriteText + protected override Drawable CreateText(string text) => SpriteText = new OsuSpriteText { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -293,7 +294,7 @@ namespace osu.Game.Graphics.UserInterface Direction = FillDirection.Horizontal, Children = new Drawable[] { - new SpriteText + new OsuSpriteText { Text = text.ToUpper(), Font = OsuFont.GetFont(size: 12), From 6fbbee3093a124021ee80bb053807dba8ec71832 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 4 Jan 2020 19:03:39 +0300 Subject: [PATCH 15/98] Move PageSelector to another namespace and organize TestScene --- .../UserInterface/TestScenePageSelector.cs | 18 +++++++++++++----- .../{ => PageSelector}/PageSelector.cs | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) rename osu.Game/Graphics/UserInterface/{ => PageSelector}/PageSelector.cs (99%) diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs index 3516e23f98..59491b7f90 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs @@ -3,8 +3,9 @@ using System; using System.Collections.Generic; -using osu.Game.Graphics.UserInterface; +using NUnit.Framework; using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface.PageSelector; namespace osu.Game.Tests.Visual.UserInterface { @@ -19,12 +20,19 @@ namespace osu.Game.Tests.Visual.UserInterface public TestScenePageSelector() { - Child = pageSelector = new PageSelector + AddRange(new Drawable[] { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }; + pageSelector = new PageSelector + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + }); + } + [Test] + public void TestPageSelectorValues() + { AddStep("10 max pages", () => setMaxPages(10)); AddStep("200 max pages, current 199", () => { diff --git a/osu.Game/Graphics/UserInterface/PageSelector.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs similarity index 99% rename from osu.Game/Graphics/UserInterface/PageSelector.cs rename to osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs index c9f0f3c74d..6767cb3a75 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs @@ -16,7 +16,7 @@ using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Graphics.Sprites; using osuTK.Graphics; -namespace osu.Game.Graphics.UserInterface +namespace osu.Game.Graphics.UserInterface.PageSelector { public class PageSelector : CompositeDrawable { From 70387c19f3f4be9413541b2588d1cf0d9de49d60 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 4 Jan 2020 19:36:05 +0300 Subject: [PATCH 16/98] Implement proper DrawablePage component --- .../UserInterface/TestScenePageSelector.cs | 17 ++- .../PageSelector/DrawablePage.cs | 108 ++++++++++++++++++ .../PageSelector/PageSelector.cs | 2 + 3 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Graphics/UserInterface/PageSelector/DrawablePage.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs index 59491b7f90..aad640ab58 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs @@ -13,10 +13,12 @@ namespace osu.Game.Tests.Visual.UserInterface { public override IReadOnlyList RequiredTypes => new[] { - typeof(PageSelector) + typeof(PageSelector), + typeof(DrawablePage) }; private readonly PageSelector pageSelector; + private readonly DrawablePage drawablePage; public TestScenePageSelector() { @@ -26,6 +28,12 @@ namespace osu.Game.Tests.Visual.UserInterface { Anchor = Anchor.Centre, Origin = Anchor.Centre, + }, + drawablePage = new DrawablePage(1234) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Margin = new MarginPadding { Top = 50 }, } }); } @@ -58,6 +66,13 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("Current is 1, max is 1", () => pageSelector.CurrentPage.Value == 1 && pageSelector.MaxPages.Value == 1); } + [Test] + public void TestDrawablePage() + { + AddStep("Select", () => drawablePage.Selected = true); + AddStep("Deselect", () => drawablePage.Selected = false); + } + private void setMaxPages(int maxPages) => pageSelector.MaxPages.Value = maxPages; private void setCurrentPage(int currentPage) => pageSelector.CurrentPage.Value = currentPage; diff --git a/osu.Game/Graphics/UserInterface/PageSelector/DrawablePage.cs b/osu.Game/Graphics/UserInterface/PageSelector/DrawablePage.cs new file mode 100644 index 0000000000..fe91874159 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/PageSelector/DrawablePage.cs @@ -0,0 +1,108 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics; +using osu.Framework.Bindables; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Extensions.Color4Extensions; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Framework.Input.Events; + +namespace osu.Game.Graphics.UserInterface.PageSelector +{ + public class DrawablePage : OsuClickableContainer + { + private const int duration = 200; + + private readonly BindableBool selected = new BindableBool(); + + public bool Selected + { + get => selected.Value; + set => selected.Value = value; + } + + [Resolved] + private OsuColour colours { get; set; } + + private readonly Box background; + private readonly OsuSpriteText text; + + public DrawablePage(int page) + { + AutoSizeAxes = Axes.X; + Height = PageSelector.HEIGHT; + Child = new CircularContainer + { + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Masking = true, + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + }, + text = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = page.ToString(), + Font = OsuFont.GetFont(size: 12), + Margin = new MarginPadding { Horizontal = 10 } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load() + { + background.Colour = colours.Lime; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + selected.BindValueChanged(onSelectedChanged, true); + } + + private void onSelectedChanged(ValueChangedEvent selected) + { + background.FadeTo(selected.NewValue ? 1 : 0, duration, Easing.OutQuint); + text.FadeColour(selected.NewValue ? colours.GreySeafoamDarker : colours.Lime, duration, Easing.OutQuint); + } + + protected override bool OnClick(ClickEvent e) + { + if (!selected.Value) + selected.Value = true; + + return base.OnClick(e); + } + + protected override bool OnHover(HoverEvent e) + { + updateHoverState(); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + base.OnHoverLost(e); + updateHoverState(); + } + + private void updateHoverState() + { + if (selected.Value) + return; + + text.FadeColour(IsHovered ? colours.Lime.Lighten(20f) : colours.Lime, duration, Easing.OutQuint); + } + } +} diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs index 6767cb3a75..54e3a035ec 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs @@ -20,6 +20,8 @@ namespace osu.Game.Graphics.UserInterface.PageSelector { public class PageSelector : CompositeDrawable { + public const int HEIGHT = 20; + public readonly BindableInt CurrentPage = new BindableInt(1); public readonly BindableInt MaxPages = new BindableInt(1); From 9af9da039da0c696a3277f4f19d5251a7561883f Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 4 Jan 2020 21:14:56 +0300 Subject: [PATCH 17/98] Implement proper PageSelectorItem --- .../UserInterface/TestScenePageSelector.cs | 5 +- .../PageSelector/DrawablePage.cs | 79 ++--- .../PageSelector/PageSelector.cs | 284 +++--------------- .../PageSelector/PageSelectorButton.cs | 77 +++++ .../PageSelector/PageSelectorItem.cs | 75 +++++ 5 files changed, 222 insertions(+), 298 deletions(-) create mode 100644 osu.Game/Graphics/UserInterface/PageSelector/PageSelectorButton.cs create mode 100644 osu.Game/Graphics/UserInterface/PageSelector/PageSelectorItem.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs index aad640ab58..33deff58dc 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs @@ -14,7 +14,9 @@ namespace osu.Game.Tests.Visual.UserInterface public override IReadOnlyList RequiredTypes => new[] { typeof(PageSelector), - typeof(DrawablePage) + typeof(DrawablePage), + typeof(PageSelectorButton), + typeof(PageSelectorItem) }; private readonly PageSelector pageSelector; @@ -42,6 +44,7 @@ namespace osu.Game.Tests.Visual.UserInterface public void TestPageSelectorValues() { AddStep("10 max pages", () => setMaxPages(10)); + AddStep("11 max pages", () => setMaxPages(11)); AddStep("200 max pages, current 199", () => { setMaxPages(200); diff --git a/osu.Game/Graphics/UserInterface/PageSelector/DrawablePage.cs b/osu.Game/Graphics/UserInterface/PageSelector/DrawablePage.cs index fe91874159..20f418085d 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/DrawablePage.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/DrawablePage.cs @@ -1,22 +1,16 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics; using osu.Framework.Bindables; using osu.Framework.Allocation; -using osu.Framework.Graphics.Shapes; using osu.Framework.Extensions.Color4Extensions; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osu.Framework.Input.Events; namespace osu.Game.Graphics.UserInterface.PageSelector { - public class DrawablePage : OsuClickableContainer + public class DrawablePage : PageSelectorItem { - private const int duration = 200; - private readonly BindableBool selected = new BindableBool(); public bool Selected @@ -25,44 +19,33 @@ namespace osu.Game.Graphics.UserInterface.PageSelector set => selected.Value = value; } - [Resolved] - private OsuColour colours { get; set; } + public int Page { get; private set; } - private readonly Box background; - private readonly OsuSpriteText text; + private OsuSpriteText text; public DrawablePage(int page) { - AutoSizeAxes = Axes.X; - Height = PageSelector.HEIGHT; - Child = new CircularContainer + Page = page; + text.Text = page.ToString(); + + Background.Alpha = 0; + + Action = () => { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Masking = true, - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - }, - text = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Text = page.ToString(), - Font = OsuFont.GetFont(size: 12), - Margin = new MarginPadding { Horizontal = 10 } - } - } + if (!selected.Value) + selected.Value = true; }; } + protected override Drawable CreateContent() => text = new OsuSpriteText + { + Font = OsuFont.GetFont(size: 12), + }; + [BackgroundDependencyLoader] private void load() { - background.Colour = colours.Lime; + Background.Colour = Colours.Lime; } protected override void LoadComplete() @@ -73,36 +56,16 @@ namespace osu.Game.Graphics.UserInterface.PageSelector private void onSelectedChanged(ValueChangedEvent selected) { - background.FadeTo(selected.NewValue ? 1 : 0, duration, Easing.OutQuint); - text.FadeColour(selected.NewValue ? colours.GreySeafoamDarker : colours.Lime, duration, Easing.OutQuint); + Background.FadeTo(selected.NewValue ? 1 : 0, DURATION, Easing.OutQuint); + text.FadeColour(selected.NewValue ? Colours.GreySeafoamDarker : Colours.Lime, DURATION, Easing.OutQuint); } - protected override bool OnClick(ClickEvent e) - { - if (!selected.Value) - selected.Value = true; - - return base.OnClick(e); - } - - protected override bool OnHover(HoverEvent e) - { - updateHoverState(); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - base.OnHoverLost(e); - updateHoverState(); - } - - private void updateHoverState() + protected override void UpdateHoverState() { if (selected.Value) return; - text.FadeColour(IsHovered ? colours.Lime.Lighten(20f) : colours.Lime, duration, Easing.OutQuint); + text.FadeColour(IsHovered ? Colours.Lime.Lighten(20f) : Colours.Lime, DURATION, Easing.OutQuint); } } } diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs index 54e3a035ec..ae6fc2b500 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs @@ -4,17 +4,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics; using osu.Framework.Bindables; -using osu.Framework.Allocation; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Extensions.Color4Extensions; -using System; -using osuTK; -using osu.Game.Graphics.Containers; -using System.Collections.Generic; using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Game.Graphics.Sprites; -using osuTK.Graphics; namespace osu.Game.Graphics.UserInterface.PageSelector { @@ -25,10 +15,10 @@ namespace osu.Game.Graphics.UserInterface.PageSelector public readonly BindableInt CurrentPage = new BindableInt(1); public readonly BindableInt MaxPages = new BindableInt(1); - private readonly FillFlowContainer itemsFlow; + private readonly FillFlowContainer itemsFlow; - private readonly Button previousPageButton; - private readonly Button nextPageButton; + private readonly PageSelectorButton previousPageButton; + private readonly PageSelectorButton nextPageButton; public PageSelector() { @@ -39,16 +29,16 @@ namespace osu.Game.Graphics.UserInterface.PageSelector Direction = FillDirection.Horizontal, Children = new Drawable[] { - previousPageButton = new Button(false, "prev") + previousPageButton = new PageSelectorButton(false, "prev") { Action = () => CurrentPage.Value -= 1, }, - itemsFlow = new FillFlowContainer + itemsFlow = new FillFlowContainer { AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, }, - nextPageButton = new Button(true, "next") + nextPageButton = new PageSelectorButton(true, "next") { Action = () => CurrentPage.Value += 1 } @@ -60,253 +50,69 @@ namespace osu.Game.Graphics.UserInterface.PageSelector { base.LoadComplete(); - MaxPages.BindValueChanged(pagesAmount => onMaxPagesChanged(pagesAmount.NewValue), true); - CurrentPage.BindValueChanged(page => onCurrentPageChanged(page.NewValue), true); - } - - private void onMaxPagesChanged(int pagesAmount) - { - if (pagesAmount < 1) - { - MaxPages.Value = 1; - return; - } - - if (CurrentPage.Value > pagesAmount) - { - CurrentPage.Value = pagesAmount; - return; - } - + MaxPages.BindValueChanged(_ => redraw()); + CurrentPage.BindValueChanged(page => onCurrentPageChanged(page.NewValue)); redraw(); } private void onCurrentPageChanged(int newPage) { - if (newPage > MaxPages.Value) - { - CurrentPage.Value = MaxPages.Value; - return; - } - if (newPage < 1) { CurrentPage.Value = 1; return; } - redraw(); + if (newPage > MaxPages.Value) + { + CurrentPage.Value = MaxPages.Value; + return; + } + + itemsFlow.ForEach(page => page.Selected = page.Page == newPage ? true : false); + updateButtonsState(); } private void redraw() + { + itemsFlow.Clear(); + + if (MaxPages.Value < 1) + { + MaxPages.Value = 1; + return; + } + + for (int i = 1; i <= MaxPages.Value; i++) + addDrawablePage(i); + + if (CurrentPage.Value > MaxPages.Value) + { + CurrentPage.Value = MaxPages.Value; + return; + } + + if (CurrentPage.Value < 1) + { + CurrentPage.Value = 1; + return; + } + + CurrentPage.TriggerChange(); + } + + private void updateButtonsState() { int newPage = CurrentPage.Value; int maxPages = MaxPages.Value; previousPageButton.Enabled.Value = newPage != 1; nextPageButton.Enabled.Value = newPage != maxPages; - - itemsFlow.Clear(); - - if (newPage > 3) - addDrawablePage(1); - - if (newPage > 4) - addPlaceholder(); - - for (int i = Math.Max(newPage - 2, 1); i <= Math.Min(newPage + 2, maxPages); i++) - { - if (i == newPage) - addDrawableCurrentPage(); - else - addDrawablePage(i); - } - - if (newPage + 2 < maxPages - 1) - addPlaceholder(); - - if (newPage + 2 < maxPages) - addDrawablePage(maxPages); } - private void addDrawablePage(int page) => itemsFlow.Add(new DrawablePage(page.ToString()) + private void addDrawablePage(int page) => itemsFlow.Add(new DrawablePage(page) { Action = () => CurrentPage.Value = page, }); - - private void addPlaceholder() => itemsFlow.Add(new Placeholder()); - - private void addDrawableCurrentPage() => itemsFlow.Add(new SelectedPage(CurrentPage.Value.ToString())); - - private abstract class PageItem : OsuHoverContainer - { - private const int margin = 10; - private const int height = 20; - - protected override Container Content => contentContainer; - - private readonly CircularContainer contentContainer; - - protected PageItem(string text) - { - AutoSizeAxes = Axes.X; - Height = height; - - base.Content.Add(contentContainer = new CircularContainer - { - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - Masking = true, - }); - - var background = CreateBackground(); - if (background != null) - Add(background); - - var drawableText = CreateText(text); - - if (drawableText != null) - { - drawableText.Margin = new MarginPadding { Horizontal = margin }; - Add(drawableText); - } - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - IdleColour = colours.Lime; - HoverColour = colours.Lime.Lighten(20f); - } - - protected abstract Drawable CreateText(string text); - - protected virtual Drawable CreateBackground() => null; - } - - private class DrawablePage : PageItem - { - protected SpriteText SpriteText; - - protected override IEnumerable EffectTargets => new[] { SpriteText }; - - public DrawablePage(string text) - : base(text) - { - } - - protected override Drawable CreateText(string text) => SpriteText = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Text = text, - Font = OsuFont.GetFont(size: 12), - }; - } - - private class SelectedPage : DrawablePage - { - private Box background; - - protected override IEnumerable EffectTargets => new[] { background }; - - public SelectedPage(string text) - : base(text) - { - } - - protected override Drawable CreateBackground() => background = new Box - { - RelativeSizeAxes = Axes.Both, - }; - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - SpriteText.Colour = colours.GreySeafoamDark; - } - } - - private class Placeholder : DrawablePage - { - public Placeholder() - : base("...") - { - } - } - - private class Button : PageItem - { - private const int duration = 100; - - private Box background; - private FillFlowContainer textContainer; - private SpriteIcon icon; - private readonly Box fadeBox; - - protected override IEnumerable EffectTargets => new[] { textContainer }; - - public Button(bool rightAligned, string text) - : base(text) - { - var alignment = rightAligned ? Anchor.x0 : Anchor.x2; - - textContainer.ForEach(drawable => - { - drawable.Anchor = Anchor.y1 | alignment; - drawable.Origin = Anchor.y1 | alignment; - }); - - icon.Icon = alignment == Anchor.x2 ? FontAwesome.Solid.ChevronLeft : FontAwesome.Solid.ChevronRight; - - Add(fadeBox = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(100) - }); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - background.Colour = colours.GreySeafoamDark; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - Enabled.BindValueChanged(onEnabledChanged, true); - } - - private void onEnabledChanged(ValueChangedEvent enabled) - { - fadeBox.FadeTo(enabled.NewValue ? 0 : 1, duration); - } - - protected override Drawable CreateBackground() => background = new Box - { - RelativeSizeAxes = Axes.Both, - }; - - protected override Drawable CreateText(string text) => textContainer = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Direction = FillDirection.Horizontal, - Children = new Drawable[] - { - new OsuSpriteText - { - Text = text.ToUpper(), - Font = OsuFont.GetFont(size: 12), - }, - icon = new SpriteIcon - { - Size = new Vector2(8), - }, - } - }; - } } } diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorButton.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorButton.cs new file mode 100644 index 0000000000..df007b32e0 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorButton.cs @@ -0,0 +1,77 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Extensions.Color4Extensions; +using osu.Game.Graphics.Sprites; +using osu.Framework.Graphics.Sprites; +using osuTK.Graphics; +using osuTK; +using osu.Framework.Extensions.IEnumerableExtensions; + +namespace osu.Game.Graphics.UserInterface.PageSelector +{ + public class PageSelectorButton : PageSelectorItem + { + private readonly Box fadeBox; + private SpriteIcon icon; + private OsuSpriteText name; + private FillFlowContainer buttonContent; + + public PageSelectorButton(bool rightAligned, string text) + { + Add(fadeBox = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(100) + }); + + var alignment = rightAligned ? Anchor.x0 : Anchor.x2; + + buttonContent.ForEach(drawable => + { + drawable.Anchor = Anchor.y1 | alignment; + drawable.Origin = Anchor.y1 | alignment; + }); + + icon.Icon = alignment == Anchor.x2 ? FontAwesome.Solid.ChevronLeft : FontAwesome.Solid.ChevronRight; + + name.Text = text.ToUpper(); + } + + protected override Drawable CreateContent() => buttonContent = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + name = new OsuSpriteText + { + Font = OsuFont.GetFont(size: 12), + }, + icon = new SpriteIcon + { + Size = new Vector2(8), + }, + } + }; + + [BackgroundDependencyLoader] + private void load() + { + Background.Colour = Colours.GreySeafoamDark; + name.Colour = icon.Colour = Colours.Lime; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Enabled.BindValueChanged(enabled => fadeBox.FadeTo(enabled.NewValue ? 0 : 1, DURATION), true); + } + + protected override void UpdateHoverState() => Background.FadeColour(IsHovered ? Colours.GreySeafoam : Colours.GreySeafoamDark, DURATION, Easing.OutQuint); + } +} diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorItem.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorItem.cs new file mode 100644 index 0000000000..d457b7ea0e --- /dev/null +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorItem.cs @@ -0,0 +1,75 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics; +using osu.Framework.Bindables; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Extensions.Color4Extensions; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Framework.Input.Events; +using osu.Framework.Graphics.Sprites; +using osuTK.Graphics; +using osuTK; +using osu.Framework.Extensions.IEnumerableExtensions; +using JetBrains.Annotations; + +namespace osu.Game.Graphics.UserInterface.PageSelector +{ + public abstract class PageSelectorItem : OsuClickableContainer + { + protected const int DURATION = 200; + + [Resolved] + protected OsuColour Colours { get; private set; } + + protected override Container Content => content; + + protected readonly Box Background; + private readonly CircularContainer content; + + protected PageSelectorItem() + { + AutoSizeAxes = Axes.X; + Height = PageSelector.HEIGHT; + base.Content.Add(content = new CircularContainer + { + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Masking = true, + Children = new Drawable[] + { + Background = new Box + { + RelativeSizeAxes = Axes.Both + }, + CreateContent().With(content => + { + content.Anchor = Anchor.Centre; + content.Origin = Anchor.Centre; + content.Margin = new MarginPadding { Horizontal = 10 }; + }) + } + }); + } + + [NotNull] + protected abstract Drawable CreateContent(); + + protected override bool OnHover(HoverEvent e) + { + UpdateHoverState(); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + base.OnHoverLost(e); + UpdateHoverState(); + } + + protected abstract void UpdateHoverState(); + } +} From b1c5e437ccf2ca260d5b8f177d78144651b1d24d Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 4 Jan 2020 21:22:45 +0300 Subject: [PATCH 18/98] Remove usings --- .../UserInterface/PageSelector/PageSelectorItem.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorItem.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorItem.cs index d457b7ea0e..63eb87a638 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorItem.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorItem.cs @@ -3,17 +3,10 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics; -using osu.Framework.Bindables; using osu.Framework.Allocation; using osu.Framework.Graphics.Shapes; -using osu.Framework.Extensions.Color4Extensions; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; using osu.Framework.Input.Events; -using osu.Framework.Graphics.Sprites; -using osuTK.Graphics; -using osuTK; -using osu.Framework.Extensions.IEnumerableExtensions; using JetBrains.Annotations; namespace osu.Game.Graphics.UserInterface.PageSelector From 37482b2ad474379ed941a5ab3a8c40f813427eaf Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 4 Jan 2020 22:05:34 +0300 Subject: [PATCH 19/98] CI fixes --- osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs | 2 +- .../Graphics/UserInterface/PageSelector/PageSelectorItem.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs index ae6fc2b500..eaa102bdd2 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs @@ -69,7 +69,7 @@ namespace osu.Game.Graphics.UserInterface.PageSelector return; } - itemsFlow.ForEach(page => page.Selected = page.Page == newPage ? true : false); + itemsFlow.ForEach(page => page.Selected = page.Page == newPage); updateButtonsState(); } diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorItem.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorItem.cs index 63eb87a638..5f0bfcdfdb 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorItem.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorItem.cs @@ -32,7 +32,7 @@ namespace osu.Game.Graphics.UserInterface.PageSelector RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, Masking = true, - Children = new Drawable[] + Children = new[] { Background = new Box { From d3c2dc43bd28812bafffb6d81a596268ed5ddca1 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 4 Jan 2020 22:25:08 +0300 Subject: [PATCH 20/98] TestScene improvements --- .../UserInterface/TestScenePageSelector.cs | 48 +++++++++---------- .../PageSelector/PageSelector.cs | 15 ++---- .../PageSelector/PageSelectorButton.cs | 1 + 3 files changed, 27 insertions(+), 37 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs index 33deff58dc..5e1105c834 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs @@ -41,32 +41,30 @@ namespace osu.Game.Tests.Visual.UserInterface } [Test] - public void TestPageSelectorValues() + public void TestCurrentPageReset() { - AddStep("10 max pages", () => setMaxPages(10)); - AddStep("11 max pages", () => setMaxPages(11)); - AddStep("200 max pages, current 199", () => - { - setMaxPages(200); - setCurrentPage(199); - }); - AddStep("200 max pages, current 201", () => - { - setMaxPages(200); - setCurrentPage(201); - }); - AddAssert("Current equals max", () => pageSelector.CurrentPage.Value == pageSelector.MaxPages.Value); - AddStep("200 max pages, current -10", () => - { - setMaxPages(200); - setCurrentPage(-10); - }); - AddAssert("Current is 1", () => pageSelector.CurrentPage.Value == 1); - AddStep("-10 max pages", () => - { - setMaxPages(-10); - }); - AddAssert("Current is 1, max is 1", () => pageSelector.CurrentPage.Value == 1 && pageSelector.MaxPages.Value == 1); + AddStep("Set 10 pages", () => setMaxPages(10)); + AddStep("Select 5 page", () => setCurrentPage(5)); + AddStep("Set 11 pages", () => setMaxPages(11)); + AddAssert("Check 1 page is current", () => pageSelector.CurrentPage.Value == 1); + } + + [Test] + public void TestUnexistingPageSelection() + { + AddStep("Set 10 pages", () => setMaxPages(10)); + AddStep("Select 11 page", () => setCurrentPage(11)); + AddAssert("Check current equals max", () => pageSelector.CurrentPage.Value == pageSelector.MaxPages.Value); + + AddStep("Select -1 page", () => setCurrentPage(-1)); + AddAssert("Check current is 1", () => pageSelector.CurrentPage.Value == 1); + } + + [Test] + public void TestNegativeMaxPages() + { + AddStep("Set -10 pages", () => setMaxPages(-10)); + AddAssert("Check current and max is 1", () => pageSelector.CurrentPage.Value == 1 && pageSelector.MaxPages.Value == 1); } [Test] diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs index eaa102bdd2..c2482d6330 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs @@ -86,19 +86,10 @@ namespace osu.Game.Graphics.UserInterface.PageSelector for (int i = 1; i <= MaxPages.Value; i++) addDrawablePage(i); - if (CurrentPage.Value > MaxPages.Value) - { - CurrentPage.Value = MaxPages.Value; - return; - } - - if (CurrentPage.Value < 1) - { + if (CurrentPage.Value == 1) + CurrentPage.TriggerChange(); + else CurrentPage.Value = 1; - return; - } - - CurrentPage.TriggerChange(); } private void updateButtonsState() diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorButton.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorButton.cs index df007b32e0..e81ce20d27 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorButton.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorButton.cs @@ -46,6 +46,7 @@ namespace osu.Game.Graphics.UserInterface.PageSelector { AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, + Spacing = new Vector2(3, 0), Children = new Drawable[] { name = new OsuSpriteText From 753db9599a225784c19e3459b6d830d5f62683c6 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 4 Jan 2020 22:29:11 +0300 Subject: [PATCH 21/98] Move items height out of PageSelector --- osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs | 2 -- .../Graphics/UserInterface/PageSelector/PageSelectorItem.cs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs index c2482d6330..8e055faea3 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs @@ -10,8 +10,6 @@ namespace osu.Game.Graphics.UserInterface.PageSelector { public class PageSelector : CompositeDrawable { - public const int HEIGHT = 20; - public readonly BindableInt CurrentPage = new BindableInt(1); public readonly BindableInt MaxPages = new BindableInt(1); diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorItem.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorItem.cs index 5f0bfcdfdb..cd61961dbe 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorItem.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorItem.cs @@ -26,7 +26,7 @@ namespace osu.Game.Graphics.UserInterface.PageSelector protected PageSelectorItem() { AutoSizeAxes = Axes.X; - Height = PageSelector.HEIGHT; + Height = 20; base.Content.Add(content = new CircularContainer { RelativeSizeAxes = Axes.Y, From 20ab415838fab4cca9fcf4675d908670032d1b56 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 6 Jan 2020 18:04:27 +0900 Subject: [PATCH 22/98] Reword tests --- .../UserInterface/TestScenePageSelector.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs index 5e1105c834..6494486d4e 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs @@ -41,30 +41,31 @@ namespace osu.Game.Tests.Visual.UserInterface } [Test] - public void TestCurrentPageReset() + public void TestResetCurrentPage() { AddStep("Set 10 pages", () => setMaxPages(10)); - AddStep("Select 5 page", () => setCurrentPage(5)); + AddStep("Select page 5", () => setCurrentPage(5)); AddStep("Set 11 pages", () => setMaxPages(11)); - AddAssert("Check 1 page is current", () => pageSelector.CurrentPage.Value == 1); + AddAssert("Page 1 is current", () => pageSelector.CurrentPage.Value == 1); } [Test] - public void TestUnexistingPageSelection() + public void TestOutOfBoundsSelection() { AddStep("Set 10 pages", () => setMaxPages(10)); - AddStep("Select 11 page", () => setCurrentPage(11)); - AddAssert("Check current equals max", () => pageSelector.CurrentPage.Value == pageSelector.MaxPages.Value); + AddStep("Select page 11", () => setCurrentPage(11)); + AddAssert("Page 10 is current", () => pageSelector.CurrentPage.Value == pageSelector.MaxPages.Value); - AddStep("Select -1 page", () => setCurrentPage(-1)); - AddAssert("Check current is 1", () => pageSelector.CurrentPage.Value == 1); + AddStep("Select page -1", () => setCurrentPage(-1)); + AddAssert("Page 1 is current", () => pageSelector.CurrentPage.Value == 1); } [Test] public void TestNegativeMaxPages() { AddStep("Set -10 pages", () => setMaxPages(-10)); - AddAssert("Check current and max is 1", () => pageSelector.CurrentPage.Value == 1 && pageSelector.MaxPages.Value == 1); + AddAssert("Page 1 is current", () => pageSelector.CurrentPage.Value == 1); + AddAssert("Max is 1", () => pageSelector.MaxPages.Value == 1); } [Test] From ef763e9bd7f28ab95b3f5b742c08e84cd223eed9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 27 Dec 2021 10:59:05 +0100 Subject: [PATCH 23/98] Add failing test for storyboard not tracking current track --- .../TestSceneBackgroundScreenDefault.cs | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs index 844fe7705a..884e74346b 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs @@ -6,18 +6,24 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Audio.Track; +using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Database; +using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; +using osu.Game.Graphics.Sprites; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Screens; using osu.Game.Screens.Backgrounds; using osu.Game.Skinning; +using osu.Game.Storyboards; +using osu.Game.Storyboards.Drawables; using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual.Background @@ -129,6 +135,46 @@ namespace osu.Game.Tests.Visual.Background AddAssert("top level background reused existing", () => screen.CheckLastLoadChange() == false); } + [Test] + public void TestBeatmapBackgroundWithStoryboardClockAlwaysUsesCurrentTrack() + { + BackgroundScreenBeatmap nestedScreen = null; + WorkingBeatmap originalWorking = null; + + setSupporter(true); + setSourceMode(BackgroundSource.BeatmapWithStoryboard); + + AddStep("change beatmap", () => originalWorking = Beatmap.Value = createTestWorkingBeatmapWithStoryboard()); + AddAssert("background changed", () => screen.CheckLastLoadChange() == true); + AddUntilStep("wait for beatmap background to be loaded", () => getCurrentBackground()?.GetType() == typeof(BeatmapBackgroundWithStoryboard)); + + AddStep("start music", () => MusicController.Play()); + AddUntilStep("storyboard clock running", () => screen.ChildrenOfType().SingleOrDefault()?.Clock.IsRunning == true); + + // of note, this needs to be a type that doesn't match BackgroundScreenDefault else it is silently not pushed by the background stack. + AddStep("push new background to stack", () => stack.Push(nestedScreen = new BackgroundScreenBeatmap(Beatmap.Value))); + AddUntilStep("wait for screen to load", () => nestedScreen.IsLoaded && nestedScreen.IsCurrentScreen()); + + // we're testing a case where scheduling may be used to avoid issues, so ensure the scheduler is no longer running. + AddUntilStep("wait for top level not alive", () => !screen.IsAlive); + + AddStep("stop music", () => MusicController.Stop()); + AddStep("change beatmap", () => Beatmap.Value = createTestWorkingBeatmapWithStoryboard()); + AddStep("change beatmap back", () => Beatmap.Value = originalWorking); + AddStep("restart music", () => MusicController.Play()); + + AddAssert("top level background hasn't changed yet", () => screen.CheckLastLoadChange() == null); + + AddStep("pop screen back to top level", () => screen.MakeCurrent()); + + AddStep("top level screen is current", () => screen.IsCurrentScreen()); + AddAssert("top level background reused existing", () => screen.CheckLastLoadChange() == false); + AddUntilStep("storyboard clock running", () => screen.ChildrenOfType().Single().Clock.IsRunning); + + AddStep("stop music", () => MusicController.Stop()); + AddStep("restore default beatmap", () => Beatmap.SetDefault()); + } + [Test] public void TestBackgroundTypeSwitch() { @@ -198,6 +244,7 @@ namespace osu.Game.Tests.Visual.Background }); private WorkingBeatmap createTestWorkingBeatmapWithUniqueBackground() => new UniqueBackgroundTestWorkingBeatmap(Audio); + private WorkingBeatmap createTestWorkingBeatmapWithStoryboard() => new TestWorkingBeatmapWithStoryboard(Audio); private class TestBackgroundScreenDefault : BackgroundScreenDefault { @@ -233,6 +280,51 @@ namespace osu.Game.Tests.Visual.Background protected override Texture GetBackground() => new Texture(1, 1); } + private class TestWorkingBeatmapWithStoryboard : TestWorkingBeatmap + { + public TestWorkingBeatmapWithStoryboard(AudioManager audioManager) + : base(new Beatmap(), createStoryboard(), audioManager) + { + } + + protected override Track GetBeatmapTrack() => new TrackVirtual(100000); + + private static Storyboard createStoryboard() + { + var storyboard = new Storyboard(); + storyboard.Layers.Last().Add(new TestStoryboardElement()); + return storyboard; + } + + private class TestStoryboardElement : IStoryboardElementWithDuration + { + public string Path => string.Empty; + public bool IsDrawable => true; + public double StartTime => double.MinValue; + public double EndTime => double.MaxValue; + + public Drawable CreateDrawable() => new DrawableTestStoryboardElement(); + } + + private class DrawableTestStoryboardElement : OsuSpriteText + { + public override bool RemoveWhenNotAlive => false; + + public DrawableTestStoryboardElement() + { + Anchor = Origin = Anchor.Centre; + Font = OsuFont.Default.With(size: 32); + Text = "(not started)"; + } + + protected override void Update() + { + base.Update(); + Text = Time.Current.ToString("N2"); + } + } + } + private void setCustomSkin() { // feign a skin switch. this doesn't do anything except force CurrentSkin to become a LegacySkin. From 9f8ad9f833f5057150297c455fee597dc6bc7db1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 27 Dec 2021 11:28:20 +0100 Subject: [PATCH 24/98] Fix menu background storyboard stopping after track reload --- .../BeatmapBackgroundWithStoryboard.cs | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs index 6a42e83305..71b1ef2c18 100644 --- a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs +++ b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs @@ -1,17 +1,25 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Timing; using osu.Game.Beatmaps; +using osu.Game.Overlays; using osu.Game.Storyboards.Drawables; namespace osu.Game.Graphics.Backgrounds { public class BeatmapBackgroundWithStoryboard : BeatmapBackground { + private InterpolatingFramedClock storyboardClock = null!; + + [Resolved(CanBeNull = true)] + private MusicController? musicController { get; set; } + public BeatmapBackgroundWithStoryboard(WorkingBeatmap beatmap, string fallbackTextureName = "Backgrounds/bg1") : base(beatmap, fallbackTextureName) { @@ -30,8 +38,33 @@ namespace osu.Game.Graphics.Backgrounds { RelativeSizeAxes = Axes.Both, Volume = { Value = 0 }, - Child = new DrawableStoryboard(Beatmap.Storyboard) { Clock = new InterpolatingFramedClock(Beatmap.Track) } + Child = new DrawableStoryboard(Beatmap.Storyboard) { Clock = storyboardClock = new InterpolatingFramedClock(Beatmap.Track) } }, AddInternal); } + + protected override void LoadComplete() + { + base.LoadComplete(); + if (musicController != null) + musicController.TrackChanged += onTrackChanged; + } + + private void onTrackChanged(WorkingBeatmap newBeatmap, TrackChangeDirection _) + { + if (newBeatmap != Beatmap) + return; + + // `MusicController` will sometimes reload the track, even when the working beatmap technically hasn't changed. + // ensure that the storyboard's clock is always using the latest track instance. + storyboardClock.ChangeSource(newBeatmap.Track); + storyboardClock.ProcessFrame(); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + if (musicController != null) + musicController.TrackChanged -= onTrackChanged; + } } } From 568cab68086792a3ed9cf863f63f25331ea13ae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 27 Dec 2021 21:13:04 +0100 Subject: [PATCH 25/98] Add clarification comment about explicit `ProcessFrame()` call --- .../Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs index 71b1ef2c18..c5c8a25f4a 100644 --- a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs +++ b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs @@ -57,6 +57,9 @@ namespace osu.Game.Graphics.Backgrounds // `MusicController` will sometimes reload the track, even when the working beatmap technically hasn't changed. // ensure that the storyboard's clock is always using the latest track instance. storyboardClock.ChangeSource(newBeatmap.Track); + // more often than not, the previous source track's time will be in the future relative to the new source track. + // explicitly process a single frame so that `InterpolatingFramedClock`'s interpolation logic is bypassed + // and the storyboard clock is correctly rewound to the source track's time exactly. storyboardClock.ProcessFrame(); } From 675bdd32138fb222889ab8a5d4590283f910c929 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 1 Jan 2022 12:08:49 +0900 Subject: [PATCH 26/98] Fix `MultiplayerMatchSubScreen` mutating mods outside of bindable lease As seen at https://github.com/peppy/osu/runs/4674501626?check_suite_focus=true. --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 2 +- .../Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index a560d85b7d..a0e7e8de87 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -381,7 +381,7 @@ namespace osu.Game.Screens.OnlinePlay.Match protected virtual void UpdateMods() { - if (SelectedItem.Value == null) + if (SelectedItem.Value == null || !this.IsCurrentScreen()) return; Mods.Value = UserMods.Value.Concat(SelectedItem.Value.RequiredMods).ToList(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index c4dd200614..4bd68f2034 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -241,7 +241,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override void UpdateMods() { - if (SelectedItem.Value == null || client.LocalUser == null) + if (SelectedItem.Value == null || client.LocalUser == null || !this.IsCurrentScreen()) return; // update local mods based on room's reported status for the local user (omitting the base call implementation). From 031a40af6af42db5f3876e2a369c054590c9a563 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Jan 2022 17:02:15 +0900 Subject: [PATCH 27/98] Replace usages of `Wait` with `WaitSafely` --- CodeAnalysis/BannedSymbols.txt | 1 + .../Collections/IO/ImportCollectionsTest.cs | 3 ++- osu.Game.Tests/Database/RealmLiveTests.cs | 19 ++++++++++--------- osu.Game.Tests/ImportTest.cs | 3 ++- ...eneOnlinePlayBeatmapAvailabilityTracker.cs | 8 ++++---- .../Background/TestSceneUserDimBackgrounds.cs | 3 ++- .../TestSceneManageCollectionsDialog.cs | 3 ++- .../Menus/TestSceneMusicActionHandling.cs | 3 ++- .../Visual/Multiplayer/QueueModeTestScene.cs | 3 ++- .../TestSceneDrawableRoomPlaylist.cs | 3 ++- .../Multiplayer/TestSceneMultiplayer.cs | 5 +++-- .../TestSceneMultiplayerMatchSongSelect.cs | 2 +- .../TestSceneMultiplayerMatchSubScreen.cs | 3 ++- .../TestSceneMultiplayerPlaylist.cs | 3 ++- .../TestSceneMultiplayerQueueList.cs | 3 ++- .../TestSceneMultiplayerReadyButton.cs | 3 ++- .../TestSceneMultiplayerSpectateButton.cs | 3 ++- .../TestScenePlaylistsSongSelect.cs | 3 ++- .../Visual/Multiplayer/TestSceneTeamVersus.cs | 3 ++- .../TestSceneMouseWheelVolumeAdjust.cs | 3 ++- .../Navigation/TestScenePerformFromScreen.cs | 3 ++- .../Navigation/TestSceneScreenNavigation.cs | 7 ++++--- .../TestScenePlaylistsRoomCreation.cs | 5 +++-- .../SongSelect/TestSceneBeatmapLeaderboard.cs | 5 +++-- .../SongSelect/TestSceneFilterControl.cs | 3 ++- .../SongSelect/TestScenePlaySongSelect.cs | 11 ++++++----- osu.Game/Screens/Play/SubmittingPlayer.cs | 3 ++- .../Multiplayer/TestMultiplayerClient.cs | 9 +++++---- 28 files changed, 76 insertions(+), 50 deletions(-) diff --git a/CodeAnalysis/BannedSymbols.txt b/CodeAnalysis/BannedSymbols.txt index c567adc0ae..2048714cb3 100644 --- a/CodeAnalysis/BannedSymbols.txt +++ b/CodeAnalysis/BannedSymbols.txt @@ -13,3 +13,4 @@ M:System.Enum.HasFlag(System.Enum);Use osu.Framework.Extensions.EnumExtensions.H M:Realms.IRealmCollection`1.SubscribeForNotifications`1(Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IRealmCollection,NotificationCallbackDelegate) instead. M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Linq.IQueryable{``0},Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IQueryable,NotificationCallbackDelegate) instead. M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Collections.Generic.IList{``0},Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IList,NotificationCallbackDelegate) instead. +M:System.Threading.Tasks.Task.Wait();Don't use Task.Wait. Use Task.WaitSafely() to ensure we avoid deadlocks. diff --git a/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs b/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs index d4ec5e897b..53e4ef07e7 100644 --- a/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs +++ b/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs @@ -6,6 +6,7 @@ using System.IO; using System.Text; using System.Threading.Tasks; using NUnit.Framework; +using osu.Framework.Extensions; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Tests.Resources; @@ -179,7 +180,7 @@ namespace osu.Game.Tests.Collections.IO { // intentionally spin this up on a separate task to avoid disposal deadlocks. // see https://github.com/EventStore/EventStore/issues/1179 - await Task.Run(() => osu.CollectionManager.Import(stream).Wait()); + await Task.Factory.StartNew(() => osu.CollectionManager.Import(stream).WaitSafely(), TaskCreationOptions.LongRunning); } } } diff --git a/osu.Game.Tests/Database/RealmLiveTests.cs b/osu.Game.Tests/Database/RealmLiveTests.cs index 06cb5a3607..9432a56741 100644 --- a/osu.Game.Tests/Database/RealmLiveTests.cs +++ b/osu.Game.Tests/Database/RealmLiveTests.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using NUnit.Framework; +using osu.Framework.Extensions; using osu.Framework.Testing; using osu.Game.Database; using osu.Game.Models; @@ -104,7 +105,7 @@ namespace osu.Game.Tests.Database liveBeatmap = beatmap.ToLive(realmFactory); } - }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait(); + }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).WaitSafely(); Debug.Assert(liveBeatmap != null); @@ -115,7 +116,7 @@ namespace osu.Game.Tests.Database Assert.IsTrue(beatmap.IsValid); Assert.IsFalse(beatmap.Hidden); }); - }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait(); + }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).WaitSafely(); }); } @@ -133,7 +134,7 @@ namespace osu.Game.Tests.Database liveBeatmap = beatmap.ToLive(realmFactory); } - }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait(); + }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).WaitSafely(); Debug.Assert(liveBeatmap != null); @@ -141,7 +142,7 @@ namespace osu.Game.Tests.Database { liveBeatmap.PerformWrite(beatmap => { beatmap.Hidden = true; }); liveBeatmap.PerformRead(beatmap => { Assert.IsTrue(beatmap.Hidden); }); - }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait(); + }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).WaitSafely(); }); } @@ -175,7 +176,7 @@ namespace osu.Game.Tests.Database liveBeatmap = beatmap.ToLive(realmFactory); } - }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait(); + }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).WaitSafely(); Debug.Assert(liveBeatmap != null); @@ -195,7 +196,7 @@ namespace osu.Game.Tests.Database var __ = liveBeatmap.Value; }); } - }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait(); + }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).WaitSafely(); }); } @@ -213,7 +214,7 @@ namespace osu.Game.Tests.Database liveBeatmap = beatmap.ToLive(realmFactory); } - }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait(); + }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).WaitSafely(); Debug.Assert(liveBeatmap != null); @@ -223,7 +224,7 @@ namespace osu.Game.Tests.Database { var unused = liveBeatmap.Value; }); - }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait(); + }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).WaitSafely(); }); } @@ -252,7 +253,7 @@ namespace osu.Game.Tests.Database liveBeatmap = beatmap.ToLive(realmFactory); } - }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait(); + }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).WaitSafely(); Debug.Assert(liveBeatmap != null); diff --git a/osu.Game.Tests/ImportTest.cs b/osu.Game.Tests/ImportTest.cs index dbeb453d4d..a658a0eaeb 100644 --- a/osu.Game.Tests/ImportTest.cs +++ b/osu.Game.Tests/ImportTest.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Platform; using osu.Game.Collections; using osu.Game.Tests.Resources; @@ -58,7 +59,7 @@ namespace osu.Game.Tests { // Beatmap must be imported before the collection manager is loaded. if (withBeatmap) - BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).Wait(); + BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).WaitSafely(); AddInternal(CollectionManager = new CollectionManager(Storage)); } diff --git a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs index 239c787349..a7b431fb6e 100644 --- a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs @@ -100,7 +100,7 @@ namespace osu.Game.Tests.Online public void TestTrackerRespectsSoftDeleting() { AddStep("allow importing", () => beatmaps.AllowImport.SetResult(true)); - AddStep("import beatmap", () => beatmaps.Import(testBeatmapFile).Wait()); + AddStep("import beatmap", () => beatmaps.Import(testBeatmapFile).WaitSafely()); addAvailabilityCheckStep("state locally available", BeatmapAvailability.LocallyAvailable); AddStep("delete beatmap", () => beatmaps.Delete(beatmaps.QueryBeatmapSet(b => b.OnlineID == testBeatmapSet.OnlineID))); @@ -114,12 +114,12 @@ namespace osu.Game.Tests.Online public void TestTrackerRespectsChecksum() { AddStep("allow importing", () => beatmaps.AllowImport.SetResult(true)); - AddStep("import beatmap", () => beatmaps.Import(testBeatmapFile).Wait()); + AddStep("import beatmap", () => beatmaps.Import(testBeatmapFile).WaitSafely()); addAvailabilityCheckStep("initially locally available", BeatmapAvailability.LocallyAvailable); AddStep("import altered beatmap", () => { - beatmaps.Import(TestResources.GetTestBeatmapForImport(true)).Wait(); + beatmaps.Import(TestResources.GetTestBeatmapForImport(true)).WaitSafely(); }); addAvailabilityCheckStep("state not downloaded", BeatmapAvailability.NotDownloaded); @@ -129,7 +129,7 @@ namespace osu.Game.Tests.Online }); addAvailabilityCheckStep("state not downloaded as well", BeatmapAvailability.NotDownloaded); - AddStep("reimport original beatmap", () => beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait()); + AddStep("reimport original beatmap", () => beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely()); addAvailabilityCheckStep("locally available after re-import", BeatmapAvailability.LocallyAvailable); } diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index 33b1d9a67d..54c5c252c0 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -7,6 +7,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; @@ -49,7 +50,7 @@ namespace osu.Game.Tests.Visual.Background Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); Dependencies.Cache(new OsuConfigManager(LocalStorage)); - manager.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); + manager.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); Beatmap.SetDefault(); } diff --git a/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs b/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs index 28218ea220..d2b0f7324b 100644 --- a/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs +++ b/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs @@ -4,6 +4,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; @@ -38,7 +39,7 @@ namespace osu.Game.Tests.Visual.Collections Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, Audio, Resources, host, Beatmap.Default)); - beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); + beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); base.Content.AddRange(new Drawable[] { diff --git a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs index 55e453c3d3..425e7718dc 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Extensions; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Database; @@ -33,7 +34,7 @@ namespace osu.Game.Tests.Visual.Menus Queue<(IWorkingBeatmap working, TrackChangeDirection changeDirection)> trackChangeQueue = null; // ensure we have at least two beatmaps available to identify the direction the music controller navigated to. - AddRepeatStep("import beatmap", () => Game.BeatmapManager.Import(TestResources.CreateTestBeatmapSetInfo()).Wait(), 5); + AddRepeatStep("import beatmap", () => Game.BeatmapManager.Import(TestResources.CreateTestBeatmapSetInfo()).WaitSafely(), 5); AddStep("import beatmap with track", () => { diff --git a/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs b/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs index 88c54eb2bb..c4d7bd7e6a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs +++ b/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs @@ -5,6 +5,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Extensions; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Framework.Testing; @@ -56,7 +57,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("import beatmap", () => { - beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); + beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); InitialBeatmap = importedSet.Beatmaps.First(b => b.RulesetID == 0); OtherBeatmap = importedSet.Beatmaps.Last(b => b.RulesetID == 0); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index f9784384fd..147bbf2626 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -8,6 +8,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Platform; @@ -153,7 +154,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { var beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo; - AddStep("import beatmap", () => manager.Import(beatmap.BeatmapSet).Wait()); + AddStep("import beatmap", () => manager.Import(beatmap.BeatmapSet).WaitSafely()); createPlaylistWithBeatmaps(beatmap); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 4eebda94e9..24fc3ac5b9 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -7,6 +7,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Extensions; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input; @@ -67,7 +68,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("import beatmap", () => { - beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); + beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); }); @@ -505,7 +506,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("restore beatmap", () => { - beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); + beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); }); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs index d671673d3c..bd4b38b9c0 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs @@ -83,7 +83,7 @@ namespace osu.Game.Tests.Visual.Multiplayer beatmapSetInfo.Beatmaps.Add(beatmap); } - manager.Import(beatmapSetInfo).Wait(); + manager.Import(beatmapSetInfo).WaitSafely(); } public override void SetUpSteps() diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index 55430fbb41..52e46ef5af 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -5,6 +5,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Extensions; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Framework.Testing; @@ -39,7 +40,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); - beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); + beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs index 7760661232..464c0ea5b6 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs @@ -6,6 +6,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Platform; using osu.Framework.Testing; @@ -53,7 +54,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("import beatmap", () => { - beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); + beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); importedBeatmap = importedSet.Beatmaps.First(b => b.RulesetID == 0); }); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs index 1a646d5e7e..29daff546d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs @@ -6,6 +6,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Platform; @@ -58,7 +59,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("import beatmap", () => { - beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); + beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); importedBeatmap = importedSet.Beatmaps.First(b => b.RulesetID == 0); }); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs index 93be28ad90..8f51b1e381 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs @@ -8,6 +8,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Platform; using osu.Framework.Testing; @@ -41,7 +42,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); - beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); + beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); } [SetUp] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs index eecf0dff9f..d4ff9f8c41 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs @@ -8,6 +8,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Platform; @@ -42,7 +43,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); - beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); + beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); } [SetUp] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs index 5aac228f4b..08fcac125d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs @@ -6,6 +6,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Extensions; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Framework.Utils; @@ -38,7 +39,7 @@ namespace osu.Game.Tests.Visual.Multiplayer var beatmapSet = TestResources.CreateTestBeatmapSetInfo(); - manager.Import(beatmapSet).Wait(); + manager.Import(beatmapSet).WaitSafely(); } public override void SetUpSteps() diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs index 81c59b90f5..d20fbd3539 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs @@ -6,6 +6,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Extensions; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Beatmaps; @@ -51,7 +52,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("import beatmap", () => { - beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); + beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); }); diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneMouseWheelVolumeAdjust.cs b/osu.Game.Tests/Visual/Navigation/TestSceneMouseWheelVolumeAdjust.cs index b3b80147ca..701ab480f6 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneMouseWheelVolumeAdjust.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneMouseWheelVolumeAdjust.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; +using osu.Framework.Extensions; using osu.Game.Configuration; using osu.Game.Screens.Play; using osu.Game.Tests.Beatmaps.IO; @@ -82,7 +83,7 @@ namespace osu.Game.Tests.Visual.Navigation PushAndConfirm(() => songSelect = new TestSceneScreenNavigation.TestPlaySongSelect()); AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded); - AddStep("import beatmap", () => ImportBeatmapTest.LoadOszIntoOsu(Game, virtualTrack: true).Wait()); + AddStep("import beatmap", () => ImportBeatmapTest.LoadOszIntoOsu(Game, virtualTrack: true).WaitSafely()); AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault); AddStep("press enter", () => InputManager.Key(Key.Enter)); diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs index 4e1b3bb9bf..24f5808961 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs @@ -4,6 +4,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Framework.Testing; @@ -172,7 +173,7 @@ namespace osu.Game.Tests.Visual.Navigation private void importAndWaitForSongSelect() { - AddStep("import beatmap", () => ImportBeatmapTest.LoadQuickOszIntoOsu(Game).Wait()); + AddStep("import beatmap", () => ImportBeatmapTest.LoadQuickOszIntoOsu(Game).WaitSafely()); PushAndConfirm(() => new TestPlaySongSelect()); AddUntilStep("beatmap updated", () => Game.Beatmap.Value.BeatmapSetInfo.OnlineID == 241526); } diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 75d8e62ca7..d094d8b688 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -4,6 +4,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Framework.Testing; @@ -70,7 +71,7 @@ namespace osu.Game.Tests.Visual.Navigation PushAndConfirm(() => songSelect = new TestPlaySongSelect()); AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded); - AddStep("import beatmap", () => ImportBeatmapTest.LoadQuickOszIntoOsu(Game).Wait()); + AddStep("import beatmap", () => ImportBeatmapTest.LoadQuickOszIntoOsu(Game).WaitSafely()); AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault); @@ -104,7 +105,7 @@ namespace osu.Game.Tests.Visual.Navigation PushAndConfirm(() => songSelect = new TestPlaySongSelect()); AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded); - AddStep("import beatmap", () => ImportBeatmapTest.LoadQuickOszIntoOsu(Game).Wait()); + AddStep("import beatmap", () => ImportBeatmapTest.LoadQuickOszIntoOsu(Game).WaitSafely()); AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault); @@ -138,7 +139,7 @@ namespace osu.Game.Tests.Visual.Navigation PushAndConfirm(() => songSelect = new TestPlaySongSelect()); AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded); - AddStep("import beatmap", () => ImportBeatmapTest.LoadOszIntoOsu(Game, virtualTrack: true).Wait()); + AddStep("import beatmap", () => ImportBeatmapTest.LoadOszIntoOsu(Game, virtualTrack: true).WaitSafely()); AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault); diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs index a426f075e1..f3d00dc076 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs @@ -7,6 +7,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Framework.Testing; @@ -142,7 +143,7 @@ namespace osu.Game.Tests.Visual.Playlists modifiedBeatmap.HitObjects.Clear(); modifiedBeatmap.HitObjects.Add(new HitCircle { StartTime = 5000 }); - manager.Import(modifiedBeatmap.BeatmapInfo.BeatmapSet).Wait(); + manager.Import(modifiedBeatmap.BeatmapInfo.BeatmapSet).WaitSafely(); }); // Create the room using the real beatmap values. @@ -184,7 +185,7 @@ namespace osu.Game.Tests.Visual.Playlists }, }; - manager.Import(originalBeatmap.BeatmapInfo.BeatmapSet).Wait(); + manager.Import(originalBeatmap.BeatmapInfo.BeatmapSet).WaitSafely(); }); AddUntilStep("match has correct beatmap", () => realHash == match.Beatmap.Value.BeatmapInfo.MD5Hash); diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index aa36bde030..605e03564d 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -6,6 +6,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Platform; using osu.Framework.Testing; @@ -74,7 +75,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep(@"Set beatmap", () => { - beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); + beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); beatmapInfo = beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps.First(); leaderboard.BeatmapInfo = beatmapInfo; @@ -175,7 +176,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep(@"Load new scores via manager", () => { foreach (var score in generateSampleScores(beatmapInfo())) - scoreManager.Import(score).Wait(); + scoreManager.Import(score).WaitSafely(); }); } diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs index 0ae4e0c5dc..1ee59eccc7 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -38,7 +39,7 @@ namespace osu.Game.Tests.Visual.SongSelect Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, Audio, Resources, host, Beatmap.Default)); - beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); + beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); base.Content.AddRange(new Drawable[] { diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 912d3f838c..bd5aeb7e08 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -8,6 +8,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Framework.Testing; @@ -256,7 +257,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("import multi-ruleset map", () => { var usableRulesets = rulesets.AvailableRulesets.Where(r => r.OnlineID != 2).ToArray(); - manager.Import(TestResources.CreateTestBeatmapSetInfo(rulesets: usableRulesets)).Wait(); + manager.Import(TestResources.CreateTestBeatmapSetInfo(rulesets: usableRulesets)).WaitSafely(); }); } else @@ -670,7 +671,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("import multi-ruleset map", () => { var usableRulesets = rulesets.AvailableRulesets.Where(r => r.OnlineID != 2).ToArray(); - manager.Import(TestResources.CreateTestBeatmapSetInfo(3, usableRulesets)).Wait(); + manager.Import(TestResources.CreateTestBeatmapSetInfo(3, usableRulesets)).WaitSafely(); }); int previousSetID = 0; @@ -710,7 +711,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("import multi-ruleset map", () => { var usableRulesets = rulesets.AvailableRulesets.Where(r => r.OnlineID != 2).ToArray(); - manager.Import(TestResources.CreateTestBeatmapSetInfo(3, usableRulesets)).Wait(); + manager.Import(TestResources.CreateTestBeatmapSetInfo(3, usableRulesets)).WaitSafely(); }); DrawableCarouselBeatmapSet set = null; @@ -868,7 +869,7 @@ namespace osu.Game.Tests.Visual.SongSelect private void addRulesetImportStep(int id) => AddStep($"import test map for ruleset {id}", () => importForRuleset(id)); - private void importForRuleset(int id) => manager.Import(TestResources.CreateTestBeatmapSetInfo(3, rulesets.AvailableRulesets.Where(r => r.OnlineID == id).ToArray())).Wait(); + private void importForRuleset(int id) => manager.Import(TestResources.CreateTestBeatmapSetInfo(3, rulesets.AvailableRulesets.Where(r => r.OnlineID == id).ToArray())).WaitSafely(); private void checkMusicPlaying(bool playing) => AddUntilStep($"music {(playing ? "" : "not ")}playing", () => music.IsPlaying == playing); @@ -898,7 +899,7 @@ namespace osu.Game.Tests.Visual.SongSelect var usableRulesets = rulesets.AvailableRulesets.Where(r => r.OnlineID != 2).ToArray(); for (int i = 0; i < 10; i++) - manager.Import(TestResources.CreateTestBeatmapSetInfo(difficultyCountPerSet, usableRulesets)).Wait(); + manager.Import(TestResources.CreateTestBeatmapSetInfo(difficultyCountPerSet, usableRulesets)).WaitSafely(); }); } diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index c613167908..2cf56be659 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Online.API; @@ -75,7 +76,7 @@ namespace osu.Game.Screens.Play api.Queue(req); - tcs.Task.Wait(); + tcs.Task.WaitSafely(); return true; void handleTokenFailure(Exception exception) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 571c3e759f..76acac3f8b 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -11,6 +11,7 @@ using System.Threading; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; @@ -77,7 +78,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void addUser(MultiplayerRoomUser user) { - ((IMultiplayerClient)this).UserJoined(user).Wait(); + ((IMultiplayerClient)this).UserJoined(user).WaitSafely(); // We want the user to be immediately available for testing, so force a scheduler update to run the update-bound continuation. Scheduler.Update(); @@ -93,7 +94,7 @@ namespace osu.Game.Tests.Visual.Multiplayer .Select(team => (teamID: team.ID, userCount: Room.Users.Count(u => (u.MatchState as TeamVersusUserState)?.TeamID == team.ID))) .OrderBy(pair => pair.userCount) .First().teamID; - ((IMultiplayerClient)this).MatchUserStateChanged(user.UserID, new TeamVersusUserState { TeamID = bestTeam }).Wait(); + ((IMultiplayerClient)this).MatchUserStateChanged(user.UserID, new TeamVersusUserState { TeamID = bestTeam }).WaitSafely(); break; } } @@ -156,7 +157,7 @@ namespace osu.Game.Tests.Visual.Multiplayer ChangeRoomState(MultiplayerRoomState.Open); ((IMultiplayerClient)this).ResultsReady(); - FinishCurrentItem().Wait(); + FinishCurrentItem().WaitSafely(); } break; @@ -216,7 +217,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Debug.Assert(Room != null); // emulate the server sending this after the join room. scheduler required to make sure the join room event is fired first (in Join). - changeMatchType(Room.Settings.MatchType).Wait(); + changeMatchType(Room.Settings.MatchType).WaitSafely(); RoomJoined = true; } From 73b40e68337dd791c2c9bffc8c60521dd3c32958 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Jan 2022 17:31:12 +0900 Subject: [PATCH 28/98] Replace usage of `.Result` with `.WaitSafelyForResult` --- CodeAnalysis/BannedSymbols.txt | 1 + .../Beatmaps/TestSceneBeatmapDifficultyCache.cs | 3 ++- osu.Game.Tests/NonVisual/TaskChainTest.cs | 11 ++++++----- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 5 +++-- .../Skins/TestSceneBeatmapSkinResources.cs | 3 ++- osu.Game.Tests/Skins/TestSceneSkinResources.cs | 3 ++- .../Visual/Editing/TestSceneDifficultySwitching.cs | 3 ++- .../Visual/Editing/TestSceneEditorTestGameplay.cs | 3 ++- .../Gameplay/TestSceneReplayDownloadButton.cs | 3 ++- .../Visual/Gameplay/TestSceneSpectator.cs | 3 ++- .../Visual/Menus/TestSceneMusicActionHandling.cs | 2 +- .../Multiplayer/TestSceneMultiSpectatorScreen.cs | 3 ++- .../TestSceneMultiplayerGameplayLeaderboard.cs | 5 +++-- ...TestSceneMultiplayerGameplayLeaderboardTeams.cs | 3 ++- .../Visual/Navigation/TestScenePresentBeatmap.cs | 3 ++- .../Visual/Navigation/TestScenePresentScore.cs | 5 +++-- .../Playlists/TestScenePlaylistsRoomCreation.cs | 2 +- .../SongSelect/TestSceneBeatmapRecommendations.cs | 3 ++- .../Visual/SongSelect/TestScenePlaySongSelect.cs | 2 +- .../UserInterface/TestSceneDeleteLocalScore.cs | 5 +++-- .../TestSceneUpdateableBeatmapBackgroundSprite.cs | 3 ++- osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 8 ++++++-- osu.Game/Beatmaps/BeatmapManager.cs | 3 ++- osu.Game/Beatmaps/WorkingBeatmap.cs | 3 ++- osu.Game/Database/OnlineLookupCache.cs | 3 ++- osu.Game/Online/Chat/ChannelManager.cs | 8 ++++---- .../Overlays/BeatmapSet/Scores/ScoresContainer.cs | 9 ++++++--- .../Overlays/Dashboard/CurrentlyPlayingDisplay.cs | 11 +++++++---- .../Settings/Sections/General/UpdateSettings.cs | 5 +++-- osu.Game/Scoring/ScoreManager.cs | 3 ++- osu.Game/Screens/Menu/IntroScreen.cs | 3 ++- .../Play/HUD/MultiplayerGameplayLeaderboard.cs | 7 +++++-- .../Screens/Play/HUD/PerformancePointsCounter.cs | 7 +++++-- osu.Game/Screens/Play/Player.cs | 3 ++- .../Ranking/Expanded/ExpandedPanelMiddleContent.cs | 3 ++- .../Expanded/Statistics/PerformanceStatistic.cs | 3 ++- osu.Game/Screens/Ranking/ScorePanelList.cs | 5 +++-- osu.Game/Screens/Select/Details/AdvancedStats.cs | 14 +++++++++----- .../Select/Leaderboards/BeatmapLeaderboard.cs | 7 ++++--- osu.Game/Screens/Spectate/SpectatorScreen.cs | 7 +++++-- osu.Game/Skinning/SkinManager.cs | 3 ++- 41 files changed, 121 insertions(+), 68 deletions(-) diff --git a/CodeAnalysis/BannedSymbols.txt b/CodeAnalysis/BannedSymbols.txt index 2048714cb3..4428656301 100644 --- a/CodeAnalysis/BannedSymbols.txt +++ b/CodeAnalysis/BannedSymbols.txt @@ -14,3 +14,4 @@ M:Realms.IRealmCollection`1.SubscribeForNotifications`1(Realms.NotificationCallb M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Linq.IQueryable{``0},Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IQueryable,NotificationCallbackDelegate) instead. M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Collections.Generic.IList{``0},Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IList,NotificationCallbackDelegate) instead. M:System.Threading.Tasks.Task.Wait();Don't use Task.Wait. Use Task.WaitSafely() to ensure we avoid deadlocks. +P:System.Threading.Tasks.Task`1.Result;Don't use Task.Result. Use Task.WaitSafelyForResult() to ensure we avoid deadlocks. diff --git a/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs b/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs index 3a82cbc785..a97b372db0 100644 --- a/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs +++ b/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Rulesets; @@ -32,7 +33,7 @@ namespace osu.Game.Tests.Beatmaps [BackgroundDependencyLoader] private void load(OsuGameBase osu) { - importedSet = ImportBeatmapTest.LoadQuickOszIntoOsu(osu).Result; + importedSet = ImportBeatmapTest.LoadQuickOszIntoOsu(osu).WaitSafelyForResult(); } [SetUpSteps] diff --git a/osu.Game.Tests/NonVisual/TaskChainTest.cs b/osu.Game.Tests/NonVisual/TaskChainTest.cs index d83eaafe20..db3cecf8ea 100644 --- a/osu.Game.Tests/NonVisual/TaskChainTest.cs +++ b/osu.Game.Tests/NonVisual/TaskChainTest.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using NUnit.Framework; +using osu.Framework.Extensions; using osu.Game.Utils; namespace osu.Game.Tests.NonVisual @@ -42,9 +43,9 @@ namespace osu.Game.Tests.NonVisual await Task.WhenAll(task1.task, task2.task, task3.task); - Assert.That(task1.task.Result, Is.EqualTo(1)); - Assert.That(task2.task.Result, Is.EqualTo(2)); - Assert.That(task3.task.Result, Is.EqualTo(3)); + Assert.That(task1.task.WaitSafelyForResult(), Is.EqualTo(1)); + Assert.That(task2.task.WaitSafelyForResult(), Is.EqualTo(2)); + Assert.That(task3.task.WaitSafelyForResult(), Is.EqualTo(3)); } [Test] @@ -68,9 +69,9 @@ namespace osu.Game.Tests.NonVisual // Wait on both tasks. await Task.WhenAll(task1.task, task3.task); - Assert.That(task1.task.Result, Is.EqualTo(1)); + Assert.That(task1.task.WaitSafelyForResult(), Is.EqualTo(1)); Assert.That(task2.task.IsCompleted, Is.False); - Assert.That(task3.task.Result, Is.EqualTo(2)); + Assert.That(task3.task.WaitSafelyForResult(), Is.EqualTo(2)); } [Test] diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs index 40d2455106..55a50fb825 100644 --- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -8,6 +8,7 @@ using System.Runtime.CompilerServices; using System.Threading.Tasks; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Platform; using osu.Game.Database; using osu.Game.IO; @@ -187,7 +188,7 @@ namespace osu.Game.Tests.Skins.IO var imported = skinManager.Import(new ImportTask(exportStream, "exported.osk")); - imported.Result.PerformRead(s => + imported.WaitSafelyForResult().PerformRead(s => { Assert.IsFalse(s.Protected); Assert.AreNotEqual(originalSkinId, s.ID); @@ -222,7 +223,7 @@ namespace osu.Game.Tests.Skins.IO var imported = skinManager.Import(new ImportTask(exportStream, "exported.osk")); - imported.Result.PerformRead(s => + imported.WaitSafelyForResult().PerformRead(s => { Assert.IsFalse(s.Protected); Assert.AreNotEqual(originalSkinId, s.ID); diff --git a/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs b/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs index 1d8b754837..a4c2ec3b34 100644 --- a/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs +++ b/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs @@ -4,6 +4,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio.Track; +using osu.Framework.Extensions; using osu.Framework.Testing; using osu.Game.Audio; using osu.Game.Beatmaps; @@ -24,7 +25,7 @@ namespace osu.Game.Tests.Skins [BackgroundDependencyLoader] private void load() { - var imported = beatmaps.Import(new ZipArchiveReader(TestResources.OpenResource("Archives/ogg-beatmap.osz"))).Result; + var imported = beatmaps.Import(new ZipArchiveReader(TestResources.OpenResource("Archives/ogg-beatmap.osz"))).WaitSafelyForResult(); beatmap = beatmaps.GetWorkingBeatmap(imported.Value.Beatmaps[0]); beatmap.LoadTrack(); } diff --git a/osu.Game.Tests/Skins/TestSceneSkinResources.cs b/osu.Game.Tests/Skins/TestSceneSkinResources.cs index 09535b76e3..a10e2436d6 100644 --- a/osu.Game.Tests/Skins/TestSceneSkinResources.cs +++ b/osu.Game.Tests/Skins/TestSceneSkinResources.cs @@ -3,6 +3,7 @@ using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Testing; using osu.Game.Audio; using osu.Game.IO.Archives; @@ -23,7 +24,7 @@ namespace osu.Game.Tests.Skins [BackgroundDependencyLoader] private void load() { - var imported = skins.Import(new ZipArchiveReader(TestResources.OpenResource("Archives/ogg-skin.osk"))).Result; + var imported = skins.Import(new ZipArchiveReader(TestResources.OpenResource("Archives/ogg-skin.osk"))).WaitSafelyForResult(); skin = imported.PerformRead(skinInfo => skins.GetSkin(skinInfo)); } diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs b/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs index c23db5e440..5dd5bad0d2 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs @@ -5,6 +5,7 @@ using System; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Overlays.Dialog; @@ -32,7 +33,7 @@ namespace osu.Game.Tests.Visual.Editing public override void SetUpSteps() { - AddStep("import test beatmap", () => importedBeatmapSet = ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).Result); + AddStep("import test beatmap", () => importedBeatmapSet = ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).WaitSafelyForResult()); base.SetUpSteps(); } diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorTestGameplay.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorTestGameplay.cs index 50794f15ed..fcbae44c76 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorTestGameplay.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorTestGameplay.cs @@ -5,6 +5,7 @@ using System; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; @@ -38,7 +39,7 @@ namespace osu.Game.Tests.Visual.Editing public override void SetUpSteps() { - AddStep("import test beatmap", () => importedBeatmapSet = ImportBeatmapTest.LoadOszIntoOsu(game).Result); + AddStep("import test beatmap", () => importedBeatmapSet = ImportBeatmapTest.LoadOszIntoOsu(game).WaitSafelyForResult()); base.SetUpSteps(); } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs index 42c4f89e9d..7f5ac5cf48 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs @@ -9,6 +9,7 @@ using osu.Game.Online.API.Requests.Responses; using osu.Game.Scoring; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Testing; using osu.Game.Database; using osu.Game.Graphics.UserInterface; @@ -135,7 +136,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("state is not downloaded", () => downloadButton.State.Value == DownloadState.NotDownloaded); - AddStep("import score", () => imported = scoreManager.Import(getScoreInfo(true)).Result); + AddStep("import score", () => imported = scoreManager.Import(getScoreInfo(true)).WaitSafelyForResult()); AddUntilStep("state is available", () => downloadButton.State.Value == DownloadState.LocallyAvailable); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index 9fadbe02bd..90065e3b51 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -4,6 +4,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Screens; using osu.Framework.Testing; @@ -60,7 +61,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("import beatmap", () => { - importedBeatmap = ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).Result; + importedBeatmap = ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).WaitSafelyForResult(); importedBeatmapId = importedBeatmap.Beatmaps.First(b => b.RulesetID == 0).OnlineID ?? -1; }); } diff --git a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs index 425e7718dc..c82bde3444 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs @@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Menus AddStep("import beatmap with track", () => { - var setWithTrack = Game.BeatmapManager.Import(new ImportTask(TestResources.GetTestBeatmapForImport())).Result; + var setWithTrack = Game.BeatmapManager.Import(new ImportTask(TestResources.GetTestBeatmapForImport())).WaitSafelyForResult(); Beatmap.Value = Game.BeatmapManager.GetWorkingBeatmap(setWithTrack.Value.Beatmaps.First()); }); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index b10856b704..3f24da2c9e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -46,7 +47,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [BackgroundDependencyLoader] private void load() { - importedSet = ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).Result; + importedSet = ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).WaitSafelyForResult(); importedBeatmap = importedSet.Beatmaps.First(b => b.RulesetID == 0); importedBeatmapId = importedBeatmap.OnlineID ?? -1; } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs index 25200560e4..6b40a4fc9a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Utils; @@ -43,7 +44,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { base.SetUpSteps(); - AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = LookupCache.GetUserAsync(1).Result); + AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = LookupCache.GetUserAsync(1).WaitSafelyForResult()); AddStep("create leaderboard", () => { @@ -90,7 +91,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public void TestUserQuit() { foreach (int user in users) - AddStep($"mark user {user} quit", () => Client.RemoveUser(LookupCache.GetUserAsync(user).Result.AsNonNull())); + AddStep($"mark user {user} quit", () => Client.RemoveUser(LookupCache.GetUserAsync(user).WaitSafelyForResult().AsNonNull())); } [Test] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs index 16a342df8c..f5c079573d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Utils; using osu.Game.Online.API; @@ -47,7 +48,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { base.SetUpSteps(); - AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = LookupCache.GetUserAsync(1).Result); + AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = LookupCache.GetUserAsync(1).WaitSafelyForResult()); AddStep("create leaderboard", () => { diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs b/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs index 3cb1ea85a9..9d495f7d05 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using NUnit.Framework; +using osu.Framework.Extensions; using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Extensions; @@ -126,7 +127,7 @@ namespace osu.Game.Tests.Visual.Navigation Ruleset = ruleset ?? new OsuRuleset().RulesetInfo }, } - }).Result.Value; + }).WaitSafelyForResult().Value; }); AddAssert($"import {i} succeeded", () => imported != null); diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs index 7ee593de00..16c416fedc 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using NUnit.Framework; +using osu.Framework.Extensions; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; @@ -57,7 +58,7 @@ namespace osu.Game.Tests.Visual.Navigation Ruleset = new OsuRuleset().RulesetInfo }, } - }).Result.Value; + }).WaitSafelyForResult().Value; }); } @@ -131,7 +132,7 @@ namespace osu.Game.Tests.Visual.Navigation OnlineID = i, BeatmapInfo = beatmap.Beatmaps.First(), Ruleset = ruleset ?? new OsuRuleset().RulesetInfo - }).Result.Value; + }).WaitSafelyForResult().Value; }); AddAssert($"import {i} succeeded", () => imported != null); diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs index f3d00dc076..0c5985ad67 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs @@ -202,7 +202,7 @@ namespace osu.Game.Tests.Visual.Playlists }); } - private void importBeatmap() => AddStep("import beatmap", () => importedBeatmap = manager.Import(CreateBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Result); + private void importBeatmap() => AddStep("import beatmap", () => importedBeatmap = manager.Import(CreateBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).WaitSafelyForResult()); private class TestPlaylistsRoomSubScreen : PlaylistsRoomSubScreen { diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs index ef11ad4153..071e3225f0 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Extensions; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Extensions; @@ -181,7 +182,7 @@ namespace osu.Game.Tests.Visual.SongSelect beatmap.DifficultyName = $"SR{i + 1}"; } - return Game.BeatmapManager.Import(beatmapSet).Result.Value; + return Game.BeatmapManager.Import(beatmapSet).WaitSafelyForResult().Value; } private bool ensureAllBeatmapSetsImported(IEnumerable beatmapSets) => beatmapSets.All(set => set != null); diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index bd5aeb7e08..ea667d02f3 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -760,7 +760,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("import huge difficulty count map", () => { var usableRulesets = rulesets.AvailableRulesets.Where(r => r.OnlineID != 2).ToArray(); - imported = manager.Import(TestResources.CreateTestBeatmapSetInfo(50, usableRulesets)).Result.Value; + imported = manager.Import(TestResources.CreateTestBeatmapSetInfo(50, usableRulesets)).WaitSafelyForResult().Value; }); AddStep("select the first beatmap of import", () => Beatmap.Value = manager.GetWorkingBeatmap(imported.Beatmaps.First())); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index 2363bbbfcf..bca396b46b 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -7,6 +7,7 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Extensions; using osu.Framework.Graphics.Cursor; using osu.Framework.Platform; using osu.Framework.Testing; @@ -85,7 +86,7 @@ namespace osu.Game.Tests.Visual.UserInterface dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, dependencies.Get(), Resources, dependencies.Get(), Beatmap.Default)); dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, ContextFactory, Scheduler)); - beatmapInfo = beatmapManager.Import(new ImportTask(TestResources.GetQuickTestBeatmapForImport())).Result.Value.Beatmaps[0]; + beatmapInfo = beatmapManager.Import(new ImportTask(TestResources.GetQuickTestBeatmapForImport())).WaitSafelyForResult().Value.Beatmaps[0]; for (int i = 0; i < 50; i++) { @@ -101,7 +102,7 @@ namespace osu.Game.Tests.Visual.UserInterface User = new APIUser { Username = "TestUser" }, }; - importedScores.Add(scoreManager.Import(score).Result.Value); + importedScores.Add(scoreManager.Import(score).WaitSafelyForResult().Value); } return dependencies; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs index 3fa9b8b877..f78cbdffc1 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; @@ -31,7 +32,7 @@ namespace osu.Game.Tests.Visual.UserInterface { this.api = api; - testBeatmap = ImportBeatmapTest.LoadOszIntoOsu(osu).Result; + testBeatmap = ImportBeatmapTest.LoadOszIntoOsu(osu).WaitSafelyForResult(); } [Test] diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index 119906cadc..0e4dab2b96 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Lists; using osu.Framework.Logging; using osu.Framework.Threading; @@ -266,8 +267,11 @@ namespace osu.Game.Beatmaps // We're on a threadpool thread, but we should exit back to the update thread so consumers can safely handle value-changed events. Schedule(() => { - if (!cancellationToken.IsCancellationRequested && t.Result != null) - bindable.Value = t.Result; + if (cancellationToken.IsCancellationRequested) + return; + + if (t.WaitSafelyForResult() is StarDifficulty sd) + bindable.Value = sd; }); }, cancellationToken); } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 8502b91096..c461135266 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Audio; using osu.Framework.Audio.Track; +using osu.Framework.Extensions; using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Framework.Testing; @@ -91,7 +92,7 @@ namespace osu.Game.Beatmaps } }; - var imported = beatmapModelManager.Import(set).Result.Value; + var imported = beatmapModelManager.Import(set).WaitSafelyForResult().Value; return GetWorkingBeatmap(imported.Beatmaps.First()); } diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 451b4ccac8..85e30b6e3d 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Audio; using osu.Framework.Audio.Track; +using osu.Framework.Extensions; using osu.Framework.Graphics.Textures; using osu.Framework.Logging; using osu.Framework.Testing; @@ -185,7 +186,7 @@ namespace osu.Game.Beatmaps { try { - return loadBeatmapAsync().Result; + return loadBeatmapAsync().WaitSafelyForResult(); } catch (AggregateException ae) { diff --git a/osu.Game/Database/OnlineLookupCache.cs b/osu.Game/Database/OnlineLookupCache.cs index 5eb9fa24fa..b392336bb4 100644 --- a/osu.Game/Database/OnlineLookupCache.cs +++ b/osu.Game/Database/OnlineLookupCache.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Game.Online.API; namespace osu.Game.Database @@ -58,7 +59,7 @@ namespace osu.Game.Database if (!task.IsCompletedSuccessfully) return null; - return task.Result; + return task.WaitSafelyForResult(); }, token)); } diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 2d7a0bc9fc..3b27dd5c5d 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Logging; using osu.Game.Database; using osu.Game.Input; @@ -533,11 +534,10 @@ namespace osu.Game.Online.Chat else if (lastClosedChannel.Type == ChannelType.PM) { // Try to get user in order to open PM chat - users.GetUserAsync((int)lastClosedChannel.Id).ContinueWith(u => + users.GetUserAsync((int)lastClosedChannel.Id).ContinueWith(task => { - if (u.Result == null) return; - - Schedule(() => CurrentChannel.Value = JoinChannel(new Channel(u.Result))); + if (task.WaitSafelyForResult() is APIUser u) + Schedule(() => CurrentChannel.Value = JoinChannel(new Channel(u))); }); } diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index 695661d5c9..adb1adeed4 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -79,14 +80,16 @@ namespace osu.Game.Overlays.BeatmapSet.Scores }; scoreManager.OrderByTotalScoreAsync(value.Scores.Select(s => s.CreateScoreInfo(rulesets, beatmapInfo)).ToArray(), loadCancellationSource.Token) - .ContinueWith(ordered => Schedule(() => + .ContinueWith(task => Schedule(() => { if (loadCancellationSource.IsCancellationRequested) return; - var topScore = ordered.Result.First(); + var scores = task.WaitSafelyForResult(); - scoreTable.DisplayScores(ordered.Result, apiBeatmap.Status.GrantsPerformancePoints()); + var topScore = scores.First(); + + scoreTable.DisplayScores(scores, apiBeatmap.Status.GrantsPerformancePoints()); scoreTable.Show(); var userScore = value.UserScore; diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs index 0844975906..b7d236c263 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -5,6 +5,7 @@ using System.Collections.Specialized; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; @@ -61,17 +62,19 @@ namespace osu.Game.Overlays.Dashboard case NotifyCollectionChangedAction.Add: foreach (int id in e.NewItems.OfType().ToArray()) { - users.GetUserAsync(id).ContinueWith(u => + users.GetUserAsync(id).ContinueWith(task => { - if (u.Result == null) return; + var user = task.WaitSafelyForResult(); + + if (user == null) return; Schedule(() => { // user may no longer be playing. - if (!playingUsers.Contains(u.Result.Id)) + if (!playingUsers.Contains(user.Id)) return; - userFlow.Add(createUserPanel(u.Result)); + userFlow.Add(createUserPanel(user)); }); }); } diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs index 6bcb5ef715..49eb80d993 100644 --- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using osu.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Framework.Platform; @@ -45,9 +46,9 @@ namespace osu.Game.Overlays.Settings.Sections.General Action = () => { checkForUpdatesButton.Enabled.Value = false; - Task.Run(updateManager.CheckForUpdateAsync).ContinueWith(t => Schedule(() => + Task.Run(updateManager.CheckForUpdateAsync).ContinueWith(task => Schedule(() => { - if (!t.Result) + if (!task.WaitSafelyForResult()) { notifications?.Post(new SimpleNotification { diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 6de6b57066..9715cd64a7 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Platform; using osu.Framework.Threading; using osu.Game.Beatmaps; @@ -112,7 +113,7 @@ namespace osu.Game.Scoring public void GetTotalScore([NotNull] ScoreInfo score, [NotNull] Action callback, ScoringMode mode = ScoringMode.Standardised, CancellationToken cancellationToken = default) { GetTotalScoreAsync(score, mode, cancellationToken) - .ContinueWith(s => scheduler.Add(() => callback(s.Result)), TaskContinuationOptions.OnlyOnRanToCompletion); + .ContinueWith(task => scheduler.Add(() => callback(task.WaitSafelyForResult())), TaskContinuationOptions.OnlyOnRanToCompletion); } /// diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index eb8d3dfea6..e7731631ff 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -9,6 +9,7 @@ using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Audio.Track; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Screens; using osu.Framework.Utils; @@ -110,7 +111,7 @@ namespace osu.Game.Screens.Menu { // if we detect that the theme track or beatmap is unavailable this is either first startup or things are in a bad state. // this could happen if a user has nuked their files store. for now, reimport to repair this. - var import = beatmaps.Import(new ZipArchiveReader(game.Resources.GetStream($"Tracks/{BeatmapFile}"), BeatmapFile)).Result; + var import = beatmaps.Import(new ZipArchiveReader(game.Resources.GetStream($"Tracks/{BeatmapFile}"), BeatmapFile)).WaitSafelyForResult(); import.PerformWrite(b => { diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index e019ee9a3d..6f2a76b3cb 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -7,6 +7,7 @@ using System.Collections.Specialized; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; using osu.Game.Configuration; using osu.Game.Database; @@ -76,9 +77,11 @@ namespace osu.Game.Screens.Play.HUD TeamScores.Add(team, new BindableInt()); } - userLookupCache.GetUsersAsync(playingUsers.Select(u => u.UserID).ToArray()).ContinueWith(users => Schedule(() => + userLookupCache.GetUsersAsync(playingUsers.Select(u => u.UserID).ToArray()).ContinueWith(task => Schedule(() => { - foreach (var user in users.Result) + var users = task.WaitSafelyForResult(); + + foreach (var user in users) { if (user == null) continue; diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index 908e6a27b8..f543cea15c 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio.Track; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -73,10 +74,12 @@ namespace osu.Game.Screens.Play.HUD var gameplayWorkingBeatmap = new GameplayWorkingBeatmap(gameplayState.Beatmap); difficultyCache.GetTimedDifficultyAttributesAsync(gameplayWorkingBeatmap, gameplayState.Ruleset, clonedMods, loadCancellationSource.Token) - .ContinueWith(r => Schedule(() => + .ContinueWith(task => Schedule(() => { - timedAttributes = r.Result; + timedAttributes = task.WaitSafelyForResult(); + IsValid = true; + if (lastJudgement != null) onJudgementChanged(lastJudgement); }), TaskContinuationOptions.OnlyOnRanToCompletion); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 77a6b27114..262424258a 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -9,6 +9,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; @@ -770,7 +771,7 @@ namespace osu.Game.Screens.Play // This player instance may already be in the process of exiting. return; - this.Push(CreateResults(prepareScoreForDisplayTask.Result)); + this.Push(CreateResults(prepareScoreForDisplayTask.WaitSafelyForResult())); }, Time.Current + delay, 50); Scheduler.Add(resultsDisplayDelegate); diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index 3ab2658f97..87940c61aa 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; @@ -79,7 +80,7 @@ namespace osu.Game.Screens.Ranking.Expanded statisticDisplays.AddRange(topStatistics); statisticDisplays.AddRange(bottomStatistics); - var starDifficulty = beatmapDifficultyCache.GetDifficultyAsync(beatmap, score.Ruleset, score.Mods).Result; + var starDifficulty = beatmapDifficultyCache.GetDifficultyAsync(beatmap, score.Ruleset, score.Mods).WaitSafelyForResult(); AddInternal(new FillFlowContainer { diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs index 68da4ec724..90ecd3642b 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs @@ -5,6 +5,7 @@ using System; using System.Threading; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Scoring; @@ -37,7 +38,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics else { performanceCache.CalculatePerformanceAsync(score, cancellationTokenSource.Token) - .ContinueWith(t => Schedule(() => setPerformanceValue(t.Result)), cancellationTokenSource.Token); + .ContinueWith(t => Schedule(() => setPerformanceValue(t.WaitSafelyForResult())), cancellationTokenSource.Token); } } diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index f3de48dcf0..b29f2302d5 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; @@ -151,9 +152,9 @@ namespace osu.Game.Screens.Ranking // Calculating score can take a while in extreme scenarios, so only display scores after the process completes. scoreManager.GetTotalScoreAsync(score) - .ContinueWith(totalScore => Schedule(() => + .ContinueWith(task => Schedule(() => { - flow.SetLayoutPosition(trackingContainer, totalScore.Result); + flow.SetLayoutPosition(trackingContainer, task.WaitSafelyForResult()); trackingContainer.Show(); diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index edbaba40bc..e7764b23be 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -16,6 +16,7 @@ using osu.Game.Rulesets.Mods; using System.Linq; using System.Threading; using System.Threading.Tasks; +using osu.Framework.Extensions; using osu.Framework.Localisation; using osu.Framework.Threading; using osu.Framework.Utils; @@ -147,15 +148,18 @@ namespace osu.Game.Screens.Select.Details starDifficultyCancellationSource = new CancellationTokenSource(); - var normalStarDifficulty = difficultyCache.GetDifficultyAsync(BeatmapInfo, ruleset.Value, null, starDifficultyCancellationSource.Token); - var moddedStarDifficulty = difficultyCache.GetDifficultyAsync(BeatmapInfo, ruleset.Value, mods.Value, starDifficultyCancellationSource.Token); + var normalStarDifficultyTask = difficultyCache.GetDifficultyAsync(BeatmapInfo, ruleset.Value, null, starDifficultyCancellationSource.Token); + var moddedStarDifficultyTask = difficultyCache.GetDifficultyAsync(BeatmapInfo, ruleset.Value, mods.Value, starDifficultyCancellationSource.Token); - Task.WhenAll(normalStarDifficulty, moddedStarDifficulty).ContinueWith(_ => Schedule(() => + Task.WhenAll(normalStarDifficultyTask, moddedStarDifficultyTask).ContinueWith(_ => Schedule(() => { - if (normalStarDifficulty.Result == null || moddedStarDifficulty.Result == null) + var normalDifficulty = normalStarDifficultyTask.WaitSafelyForResult(); + var moddeDifficulty = moddedStarDifficultyTask.WaitSafelyForResult(); + + if (normalDifficulty == null || moddeDifficulty == null) return; - starDifficulty.Value = ((float)normalStarDifficulty.Result.Value.Stars, (float)moddedStarDifficulty.Result.Value.Stars); + starDifficulty.Value = ((float)normalDifficulty.Value.Stars, (float)moddeDifficulty.Value.Stars); }), starDifficultyCancellationSource.Token, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Current); } diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 29c8907526..6943f2a3b6 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.API.Requests; @@ -142,7 +143,7 @@ namespace osu.Game.Screens.Select.Leaderboards } scoreManager.OrderByTotalScoreAsync(scores.ToArray(), cancellationToken) - .ContinueWith(ordered => scoresCallback?.Invoke(ordered.Result), TaskContinuationOptions.OnlyOnRanToCompletion); + .ContinueWith(ordered => scoresCallback?.Invoke(ordered.WaitSafelyForResult()), TaskContinuationOptions.OnlyOnRanToCompletion); return null; } @@ -178,12 +179,12 @@ namespace osu.Game.Screens.Select.Leaderboards req.Success += r => { scoreManager.OrderByTotalScoreAsync(r.Scores.Select(s => s.CreateScoreInfo(rulesets, fetchBeatmapInfo)).ToArray(), cancellationToken) - .ContinueWith(ordered => Schedule(() => + .ContinueWith(task => Schedule(() => { if (cancellationToken.IsCancellationRequested) return; - scoresCallback?.Invoke(ordered.Result); + scoresCallback?.Invoke(task.WaitSafelyForResult()); TopScore = r.UserScore?.CreateScoreInfo(rulesets, fetchBeatmapInfo); }), TaskContinuationOptions.OnlyOnRanToCompletion); }; diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs index ca56366927..f361b0cee4 100644 --- a/osu.Game/Screens/Spectate/SpectatorScreen.cs +++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs @@ -7,6 +7,7 @@ using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Extensions.ObjectExtensions; using osu.Game.Beatmaps; using osu.Game.Database; @@ -57,9 +58,11 @@ namespace osu.Game.Screens.Spectate { base.LoadComplete(); - userLookupCache.GetUsersAsync(users.ToArray()).ContinueWith(users => Schedule(() => + userLookupCache.GetUsersAsync(users.ToArray()).ContinueWith(task => Schedule(() => { - foreach (var u in users.Result) + var foundUsers = task.WaitSafelyForResult(); + + foreach (var u in foundUsers) { if (u == null) continue; diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index bb2f0a37b4..a55cc332c5 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -11,6 +11,7 @@ using JetBrains.Annotations; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; @@ -153,7 +154,7 @@ namespace osu.Game.Skinning Name = s.Name + @" (modified)", Creator = s.Creator, InstantiationInfo = s.InstantiationInfo, - }).Result; + }).WaitSafelyForResult(); if (result != null) { From f3e889d0f612ca15db77853bf283087f77b9389c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Jan 2022 17:51:56 +0900 Subject: [PATCH 29/98] Fix incorrect nested result retrieval in `ImportBeatmapTest` --- .../Beatmaps/IO/ImportBeatmapTest.cs | 54 ++++++++++--------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 9f3709f7a3..2a2cc2e086 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -93,7 +93,7 @@ namespace osu.Game.Tests.Beatmaps.IO using (var stream = File.OpenRead(tempPath)) { importedSet = await manager.Import(new ImportTask(stream, Path.GetFileName(tempPath))); - ensureLoaded(osu); + await ensureLoaded(osu); } Assert.IsTrue(File.Exists(tempPath), "Stream source file somehow went missing"); @@ -171,7 +171,7 @@ namespace osu.Game.Tests.Beatmaps.IO var importedSecondTime = await osu.Dependencies.Get().Import(new ImportTask(temp)); - ensureLoaded(osu); + await ensureLoaded(osu); // but contents doesn't, so existing should still be used. Assert.IsTrue(imported.ID == importedSecondTime.Value.ID); @@ -225,7 +225,7 @@ namespace osu.Game.Tests.Beatmaps.IO var importedSecondTime = await osu.Dependencies.Get().Import(new ImportTask(temp)); - ensureLoaded(osu); + await ensureLoaded(osu); // check the newly "imported" beatmap is not the original. Assert.IsTrue(imported.ID != importedSecondTime.Value.ID); @@ -277,7 +277,7 @@ namespace osu.Game.Tests.Beatmaps.IO var importedSecondTime = await osu.Dependencies.Get().Import(new ImportTask(temp)); - ensureLoaded(osu); + await ensureLoaded(osu); // check the newly "imported" beatmap is not the original. Assert.IsTrue(imported.ID != importedSecondTime.Value.ID); @@ -328,7 +328,7 @@ namespace osu.Game.Tests.Beatmaps.IO var importedSecondTime = await osu.Dependencies.Get().Import(new ImportTask(temp)); - ensureLoaded(osu); + await ensureLoaded(osu); // check the newly "imported" beatmap is not the original. Assert.IsTrue(imported.ID != importedSecondTime.Value.ID); @@ -637,7 +637,7 @@ namespace osu.Game.Tests.Beatmaps.IO if (!importer.ImportAsync(temp).Wait(10000)) Assert.Fail(@"IPC took too long to send"); - ensureLoaded(osu); + ensureLoaded(osu).WaitSafely(); waitForOrAssert(() => !File.Exists(temp), "Temporary still exists after IPC import", 5000); } @@ -659,7 +659,7 @@ namespace osu.Game.Tests.Beatmaps.IO string temp = TestResources.GetTestBeatmapForImport(); using (File.OpenRead(temp)) await osu.Dependencies.Get().Import(temp); - ensureLoaded(osu); + await ensureLoaded(osu); File.Delete(temp); Assert.IsFalse(File.Exists(temp), "We likely held a read lock on the file when we shouldn't"); } @@ -698,7 +698,7 @@ namespace osu.Game.Tests.Beatmaps.IO await osu.Dependencies.Get().Import(temp); - ensureLoaded(osu); + await ensureLoaded(osu); } finally { @@ -741,7 +741,7 @@ namespace osu.Game.Tests.Beatmaps.IO var imported = await osu.Dependencies.Get().Import(new ImportTask(temp)); - ensureLoaded(osu); + await ensureLoaded(osu); Assert.IsFalse(imported.Value.Files.Any(f => f.Filename.Contains("subfolder")), "Files contain common subfolder"); } @@ -794,7 +794,7 @@ namespace osu.Game.Tests.Beatmaps.IO var imported = await osu.Dependencies.Get().Import(new ImportTask(temp)); - ensureLoaded(osu); + await ensureLoaded(osu); Assert.IsFalse(imported.Value.Files.Any(f => f.Filename.Contains("__MACOSX")), "Files contain resource fork folder, which should be ignored"); Assert.IsFalse(imported.Value.Files.Any(f => f.Filename.Contains("actual_data")), "Files contain common subfolder"); @@ -812,7 +812,7 @@ namespace osu.Game.Tests.Beatmaps.IO } [Test] - public async Task TestUpdateBeatmapInfo() + public void TestUpdateBeatmapInfo() { using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) { @@ -822,7 +822,8 @@ namespace osu.Game.Tests.Beatmaps.IO var manager = osu.Dependencies.Get(); string temp = TestResources.GetTestBeatmapForImport(); - await osu.Dependencies.Get().Import(temp); + + osu.Dependencies.Get().Import(temp).WaitSafely(); // Update via the beatmap, not the beatmap info, to ensure correct linking BeatmapSetInfo setToUpdate = manager.GetAllUsableBeatmapSets()[0]; @@ -842,7 +843,7 @@ namespace osu.Game.Tests.Beatmaps.IO } [Test] - public async Task TestUpdateBeatmapFile() + public void TestUpdateBeatmapFile() { using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) { @@ -852,7 +853,8 @@ namespace osu.Game.Tests.Beatmaps.IO var manager = osu.Dependencies.Get(); string temp = TestResources.GetTestBeatmapForImport(); - await osu.Dependencies.Get().Import(temp); + + osu.Dependencies.Get().Import(temp).WaitSafely(); BeatmapSetInfo setToUpdate = manager.GetAllUsableBeatmapSets()[0]; @@ -976,35 +978,35 @@ namespace osu.Game.Tests.Beatmaps.IO } } - public static async Task LoadQuickOszIntoOsu(OsuGameBase osu) + public static Task LoadQuickOszIntoOsu(OsuGameBase osu) => Task.Factory.StartNew(() => { string temp = TestResources.GetQuickTestBeatmapForImport(); var manager = osu.Dependencies.Get(); - var importedSet = await manager.Import(new ImportTask(temp)).ConfigureAwait(false); + var importedSet = manager.Import(new ImportTask(temp)).WaitSafelyForResult(); - ensureLoaded(osu); + ensureLoaded(osu).WaitSafely(); waitForOrAssert(() => !File.Exists(temp), "Temporary file still exists after standard import", 5000); return manager.GetAllUsableBeatmapSets().Find(beatmapSet => beatmapSet.ID == importedSet.Value.ID); - } + }, TaskCreationOptions.LongRunning); - public static async Task LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false) + public static Task LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false) => Task.Factory.StartNew(() => { string temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack); var manager = osu.Dependencies.Get(); - var importedSet = await manager.Import(new ImportTask(temp)).ConfigureAwait(false); + var importedSet = manager.Import(new ImportTask(temp)).WaitSafelyForResult(); - ensureLoaded(osu); + ensureLoaded(osu).WaitSafely(); waitForOrAssert(() => !File.Exists(temp), "Temporary file still exists after standard import", 5000); return manager.GetAllUsableBeatmapSets().Find(beatmapSet => beatmapSet.ID == importedSet.Value.ID); - } + }, TaskCreationOptions.LongRunning); private void deleteBeatmapSet(BeatmapSetInfo imported, OsuGameBase osu) { @@ -1053,7 +1055,7 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.AreEqual(expected, osu.Dependencies.Get().Get().FileInfo.Count(f => f.ReferenceCount == 1)); } - private static void ensureLoaded(OsuGameBase osu, int timeout = 60000) + private static Task ensureLoaded(OsuGameBase osu, int timeout = 60000) => Task.Factory.StartNew(() => { IEnumerable resultSets = null; var store = osu.Dependencies.Get(); @@ -1089,14 +1091,14 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.IsTrue(beatmap?.HitObjects.Any() == true); beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 3))?.Beatmap; Assert.IsTrue(beatmap?.HitObjects.Any() == true); - } + }, TaskCreationOptions.LongRunning); private static void waitForOrAssert(Func result, string failureMessage, int timeout = 60000) { - Task task = Task.Run(() => + Task task = Task.Factory.StartNew(() => { while (!result()) Thread.Sleep(200); - }); + }, TaskCreationOptions.LongRunning); Assert.IsTrue(task.Wait(timeout), failureMessage); } From f9713b8895595c06b35f98a80859904ba7683739 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Jan 2022 18:00:28 +0900 Subject: [PATCH 30/98] Replace usage of `TimeoutAttribute` to fix beatmap conversion test failures --- .../CatchBeatmapConversionTest.cs | 1 - .../ManiaBeatmapConversionTest.cs | 1 - .../OsuBeatmapConversionTest.cs | 1 - .../TaikoBeatmapConversionTest.cs | 1 - .../Tests/Beatmaps/BeatmapConversionTest.cs | 58 +++++++++++-------- 5 files changed, 34 insertions(+), 28 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs index be1885cfa6..baca8166d1 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs @@ -14,7 +14,6 @@ using osu.Game.Tests.Beatmaps; namespace osu.Game.Rulesets.Catch.Tests { [TestFixture] - [Timeout(10000)] public class CatchBeatmapConversionTest : BeatmapConversionTest { protected override string ResourceAssembly => "osu.Game.Rulesets.Catch"; diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs index 948f088b4e..837474ad9e 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs @@ -14,7 +14,6 @@ using osu.Game.Tests.Beatmaps; namespace osu.Game.Rulesets.Mania.Tests { [TestFixture] - [Timeout(10000)] public class ManiaBeatmapConversionTest : BeatmapConversionTest { protected override string ResourceAssembly => "osu.Game.Rulesets.Mania"; diff --git a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs index 5f44e1b6b6..4c11efcc7c 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs @@ -12,7 +12,6 @@ using osu.Game.Tests.Beatmaps; namespace osu.Game.Rulesets.Osu.Tests { [TestFixture] - [Timeout(10000)] public class OsuBeatmapConversionTest : BeatmapConversionTest { protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs index b6db333dc9..b3f6a733d3 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs @@ -12,7 +12,6 @@ using osu.Game.Tests.Beatmaps; namespace osu.Game.Rulesets.Taiko.Tests { [TestFixture] - [Timeout(10000)] public class TaikoBeatmapConversionTest : BeatmapConversionTest { protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko"; diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index 651874e4de..1b3e49de37 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -6,9 +6,11 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; +using System.Threading.Tasks; using Newtonsoft.Json; using NUnit.Framework; using osu.Framework.Audio.Track; +using osu.Framework.Extensions; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; @@ -108,37 +110,45 @@ namespace osu.Game.Tests.Beatmaps private ConvertResult convert(string name, Mod[] mods) { - var beatmap = GetBeatmap(name); - - string beforeConversion = beatmap.Serialize(); - - var converterResult = new Dictionary>(); - - var working = new ConversionWorkingBeatmap(beatmap) + var conversionTask = Task.Factory.StartNew(() => { - ConversionGenerated = (o, r, c) => + var beatmap = GetBeatmap(name); + + string beforeConversion = beatmap.Serialize(); + + var converterResult = new Dictionary>(); + + var working = new ConversionWorkingBeatmap(beatmap) { - converterResult[o] = r; - OnConversionGenerated(o, r, c); - } - }; + ConversionGenerated = (o, r, c) => + { + converterResult[o] = r; + OnConversionGenerated(o, r, c); + } + }; - working.GetPlayableBeatmap(CreateRuleset().RulesetInfo, mods); + working.GetPlayableBeatmap(CreateRuleset().RulesetInfo, mods); - string afterConversion = beatmap.Serialize(); + string afterConversion = beatmap.Serialize(); - Assert.AreEqual(beforeConversion, afterConversion, "Conversion altered original beatmap"); + Assert.AreEqual(beforeConversion, afterConversion, "Conversion altered original beatmap"); - return new ConvertResult - { - Mappings = converterResult.Select(r => + return new ConvertResult { - var mapping = CreateConvertMapping(r.Key); - mapping.StartTime = r.Key.StartTime; - mapping.Objects.AddRange(r.Value.SelectMany(CreateConvertValue)); - return mapping; - }).ToList() - }; + Mappings = converterResult.Select(r => + { + var mapping = CreateConvertMapping(r.Key); + mapping.StartTime = r.Key.StartTime; + mapping.Objects.AddRange(r.Value.SelectMany(CreateConvertValue)); + return mapping; + }).ToList() + }; + }, TaskCreationOptions.LongRunning); + + if (!conversionTask.Wait(10000)) + Assert.Fail("Conversion timed out"); + + return conversionTask.WaitSafelyForResult(); } protected virtual void OnConversionGenerated(HitObject original, IEnumerable result, IBeatmapConverter beatmapConverter) From 1c899e4402bcc891f3e5a46126fae65151372928 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Jan 2022 16:46:42 +0900 Subject: [PATCH 31/98] Fix post-merge issues --- .../Visual/UserInterface/TestScenePageSelector.cs | 10 ---------- .../UserInterface/PageSelector/DrawablePage.cs | 2 +- .../UserInterface/PageSelector/PageSelector.cs | 1 + .../UserInterface/PageSelector/PageSelectorButton.cs | 4 ++-- 4 files changed, 4 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs index 6494486d4e..8a15dd9b46 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Graphics.UserInterface.PageSelector; @@ -11,14 +9,6 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestScenePageSelector : OsuTestScene { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(PageSelector), - typeof(DrawablePage), - typeof(PageSelectorButton), - typeof(PageSelectorItem) - }; - private readonly PageSelector pageSelector; private readonly DrawablePage drawablePage; diff --git a/osu.Game/Graphics/UserInterface/PageSelector/DrawablePage.cs b/osu.Game/Graphics/UserInterface/PageSelector/DrawablePage.cs index 20f418085d..4ea610bd89 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/DrawablePage.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/DrawablePage.cs @@ -57,7 +57,7 @@ namespace osu.Game.Graphics.UserInterface.PageSelector private void onSelectedChanged(ValueChangedEvent selected) { Background.FadeTo(selected.NewValue ? 1 : 0, DURATION, Easing.OutQuint); - text.FadeColour(selected.NewValue ? Colours.GreySeafoamDarker : Colours.Lime, DURATION, Easing.OutQuint); + text.FadeColour(selected.NewValue ? Colours.GreySeaFoamDarker : Colours.Lime, DURATION, Easing.OutQuint); } protected override void UpdateHoverState() diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs index 8e055faea3..612cf82b9f 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs @@ -21,6 +21,7 @@ namespace osu.Game.Graphics.UserInterface.PageSelector public PageSelector() { AutoSizeAxes = Axes.Both; + InternalChild = new FillFlowContainer { AutoSizeAxes = Axes.Both, diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorButton.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorButton.cs index e81ce20d27..4eba549dc3 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorButton.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorButton.cs @@ -63,7 +63,7 @@ namespace osu.Game.Graphics.UserInterface.PageSelector [BackgroundDependencyLoader] private void load() { - Background.Colour = Colours.GreySeafoamDark; + Background.Colour = Colours.GreySeaFoamDark; name.Colour = icon.Colour = Colours.Lime; } @@ -73,6 +73,6 @@ namespace osu.Game.Graphics.UserInterface.PageSelector Enabled.BindValueChanged(enabled => fadeBox.FadeTo(enabled.NewValue ? 0 : 1, DURATION), true); } - protected override void UpdateHoverState() => Background.FadeColour(IsHovered ? Colours.GreySeafoam : Colours.GreySeafoamDark, DURATION, Easing.OutQuint); + protected override void UpdateHoverState() => Background.FadeColour(IsHovered ? Colours.GreySeaFoam : Colours.GreySeaFoamDark, DURATION, Easing.OutQuint); } } From db58f5de8e6ea8c6dd14695b59a39aed33c82c9d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Jan 2022 16:58:32 +0900 Subject: [PATCH 32/98] Clean up unnecessary complexity --- .../PageSelector/DrawablePage.cs | 8 +- .../PageSelector/PageSelectorButton.cs | 80 +++++++++++-------- .../PageSelector/PageSelectorItem.cs | 12 ++- 3 files changed, 56 insertions(+), 44 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/PageSelector/DrawablePage.cs b/osu.Game/Graphics/UserInterface/PageSelector/DrawablePage.cs index 4ea610bd89..2610ae7571 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/DrawablePage.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/DrawablePage.cs @@ -15,20 +15,16 @@ namespace osu.Game.Graphics.UserInterface.PageSelector public bool Selected { - get => selected.Value; set => selected.Value = value; } - public int Page { get; private set; } + public int Page { get; } private OsuSpriteText text; public DrawablePage(int page) { Page = page; - text.Text = page.ToString(); - - Background.Alpha = 0; Action = () => { @@ -40,12 +36,14 @@ namespace osu.Game.Graphics.UserInterface.PageSelector protected override Drawable CreateContent() => text = new OsuSpriteText { Font = OsuFont.GetFont(size: 12), + Text = Page.ToString(), }; [BackgroundDependencyLoader] private void load() { Background.Colour = Colours.Lime; + Background.Alpha = 0; } protected override void LoadComplete() diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorButton.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorButton.cs index 4eba549dc3..1492319fc9 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorButton.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorButton.cs @@ -1,61 +1,64 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics; using osu.Framework.Allocation; -using osu.Framework.Graphics.Shapes; using osu.Framework.Extensions.Color4Extensions; -using osu.Game.Graphics.Sprites; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; -using osuTK.Graphics; +using osu.Game.Graphics.Sprites; using osuTK; -using osu.Framework.Extensions.IEnumerableExtensions; +using osuTK.Graphics; namespace osu.Game.Graphics.UserInterface.PageSelector { public class PageSelectorButton : PageSelectorItem { - private readonly Box fadeBox; + private readonly string text; + + private Box fadeBox; private SpriteIcon icon; private OsuSpriteText name; - private FillFlowContainer buttonContent; + + private readonly Anchor alignment; public PageSelectorButton(bool rightAligned, string text) { - Add(fadeBox = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(100) - }); - - var alignment = rightAligned ? Anchor.x0 : Anchor.x2; - - buttonContent.ForEach(drawable => - { - drawable.Anchor = Anchor.y1 | alignment; - drawable.Origin = Anchor.y1 | alignment; - }); - - icon.Icon = alignment == Anchor.x2 ? FontAwesome.Solid.ChevronLeft : FontAwesome.Solid.ChevronRight; - - name.Text = text.ToUpper(); + this.text = text; + alignment = rightAligned ? Anchor.x0 : Anchor.x2; } - protected override Drawable CreateContent() => buttonContent = new FillFlowContainer + protected override Drawable CreateContent() => new Container { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(3, 0), + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, Children = new Drawable[] { - name = new OsuSpriteText + new FillFlowContainer { - Font = OsuFont.GetFont(size: 12), - }, - icon = new SpriteIcon - { - Size = new Vector2(8), + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Spacing = new Vector2(3, 0), + Children = new Drawable[] + { + name = new OsuSpriteText + { + Font = OsuFont.GetFont(size: 12), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = text.ToUpper(), + }, + icon = new SpriteIcon + { + Icon = alignment == Anchor.x2 ? FontAwesome.Solid.ChevronLeft : FontAwesome.Solid.ChevronRight, + Size = new Vector2(8), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + } }, } }; @@ -70,6 +73,13 @@ namespace osu.Game.Graphics.UserInterface.PageSelector protected override void LoadComplete() { base.LoadComplete(); + + CircularContent.Add(fadeBox = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(100) + }); + Enabled.BindValueChanged(enabled => fadeBox.FadeTo(enabled.NewValue ? 0 : 1, DURATION), true); } diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorItem.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorItem.cs index cd61961dbe..92bf958ca9 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorItem.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorItem.cs @@ -18,16 +18,20 @@ namespace osu.Game.Graphics.UserInterface.PageSelector [Resolved] protected OsuColour Colours { get; private set; } - protected override Container Content => content; + protected Box Background; - protected readonly Box Background; - private readonly CircularContainer content; + public CircularContainer CircularContent { get; private set; } protected PageSelectorItem() { AutoSizeAxes = Axes.X; Height = 20; - base.Content.Add(content = new CircularContainer + } + + [BackgroundDependencyLoader] + private void load() + { + Add(CircularContent = new CircularContainer { RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, From ee4f5c0e79e11c2f8e9a9c574beddb16a2174652 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Jan 2022 17:52:40 +0900 Subject: [PATCH 33/98] Rename button classes to make more sense --- .../UserInterface/TestScenePageSelector.cs | 8 +- .../PageSelector/PageSelector.cs | 14 +-- .../PageSelector/PageSelectorButton.cs | 112 ++++++++---------- .../PageSelector/PageSelectorItem.cs | 72 ----------- ...wablePage.cs => PageSelectorPageButton.cs} | 4 +- .../PageSelectorPrevNextButton.cs | 88 ++++++++++++++ 6 files changed, 149 insertions(+), 149 deletions(-) delete mode 100644 osu.Game/Graphics/UserInterface/PageSelector/PageSelectorItem.cs rename osu.Game/Graphics/UserInterface/PageSelector/{DrawablePage.cs => PageSelectorPageButton.cs} (94%) create mode 100644 osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPrevNextButton.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs index 8a15dd9b46..000e75be31 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs @@ -10,7 +10,7 @@ namespace osu.Game.Tests.Visual.UserInterface public class TestScenePageSelector : OsuTestScene { private readonly PageSelector pageSelector; - private readonly DrawablePage drawablePage; + private readonly PageSelectorPageButton pageSelectorPageButton; public TestScenePageSelector() { @@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.UserInterface Anchor = Anchor.Centre, Origin = Anchor.Centre, }, - drawablePage = new DrawablePage(1234) + pageSelectorPageButton = new PageSelectorPageButton(1234) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, @@ -61,8 +61,8 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestDrawablePage() { - AddStep("Select", () => drawablePage.Selected = true); - AddStep("Deselect", () => drawablePage.Selected = false); + AddStep("Select", () => pageSelectorPageButton.Selected = true); + AddStep("Deselect", () => pageSelectorPageButton.Selected = false); } private void setMaxPages(int maxPages) => pageSelector.MaxPages.Value = maxPages; diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs index 612cf82b9f..4089fb5511 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs @@ -13,10 +13,10 @@ namespace osu.Game.Graphics.UserInterface.PageSelector public readonly BindableInt CurrentPage = new BindableInt(1); public readonly BindableInt MaxPages = new BindableInt(1); - private readonly FillFlowContainer itemsFlow; + private readonly FillFlowContainer itemsFlow; - private readonly PageSelectorButton previousPageButton; - private readonly PageSelectorButton nextPageButton; + private readonly PageSelectorPrevNextButton previousPageButton; + private readonly PageSelectorPrevNextButton nextPageButton; public PageSelector() { @@ -28,16 +28,16 @@ namespace osu.Game.Graphics.UserInterface.PageSelector Direction = FillDirection.Horizontal, Children = new Drawable[] { - previousPageButton = new PageSelectorButton(false, "prev") + previousPageButton = new PageSelectorPrevNextButton(false, "prev") { Action = () => CurrentPage.Value -= 1, }, - itemsFlow = new FillFlowContainer + itemsFlow = new FillFlowContainer { AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, }, - nextPageButton = new PageSelectorButton(true, "next") + nextPageButton = new PageSelectorPrevNextButton(true, "next") { Action = () => CurrentPage.Value += 1 } @@ -100,7 +100,7 @@ namespace osu.Game.Graphics.UserInterface.PageSelector nextPageButton.Enabled.Value = newPage != maxPages; } - private void addDrawablePage(int page) => itemsFlow.Add(new DrawablePage(page) + private void addDrawablePage(int page) => itemsFlow.Add(new PageSelectorPageButton(page) { Action = () => CurrentPage.Value = page, }); diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorButton.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorButton.cs index 1492319fc9..bdfe66f1d0 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorButton.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorButton.cs @@ -1,88 +1,72 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics; +using osu.Framework.Allocation; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics.Sprites; -using osuTK; -using osuTK.Graphics; +using osu.Game.Graphics.Containers; +using osu.Framework.Input.Events; +using JetBrains.Annotations; namespace osu.Game.Graphics.UserInterface.PageSelector { - public class PageSelectorButton : PageSelectorItem + public abstract class PageSelectorButton : OsuClickableContainer { - private readonly string text; + protected const int DURATION = 200; - private Box fadeBox; - private SpriteIcon icon; - private OsuSpriteText name; + [Resolved] + protected OsuColour Colours { get; private set; } - private readonly Anchor alignment; + protected Box Background; - public PageSelectorButton(bool rightAligned, string text) + public CircularContainer CircularContent { get; private set; } + + protected PageSelectorButton() { - this.text = text; - alignment = rightAligned ? Anchor.x0 : Anchor.x2; + AutoSizeAxes = Axes.X; + Height = 20; } - protected override Drawable CreateContent() => new Container - { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Children = new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Spacing = new Vector2(3, 0), - Children = new Drawable[] - { - name = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 12), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Text = text.ToUpper(), - }, - icon = new SpriteIcon - { - Icon = alignment == Anchor.x2 ? FontAwesome.Solid.ChevronLeft : FontAwesome.Solid.ChevronRight, - Size = new Vector2(8), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - } - }, - } - }; - [BackgroundDependencyLoader] private void load() { - Background.Colour = Colours.GreySeaFoamDark; - name.Colour = icon.Colour = Colours.Lime; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - CircularContent.Add(fadeBox = new Box + Add(CircularContent = new CircularContainer { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(100) + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Masking = true, + Children = new[] + { + Background = new Box + { + RelativeSizeAxes = Axes.Both + }, + CreateContent().With(content => + { + content.Anchor = Anchor.Centre; + content.Origin = Anchor.Centre; + content.Margin = new MarginPadding { Horizontal = 10 }; + }) + } }); - - Enabled.BindValueChanged(enabled => fadeBox.FadeTo(enabled.NewValue ? 0 : 1, DURATION), true); } - protected override void UpdateHoverState() => Background.FadeColour(IsHovered ? Colours.GreySeaFoam : Colours.GreySeaFoamDark, DURATION, Easing.OutQuint); + [NotNull] + protected abstract Drawable CreateContent(); + + protected override bool OnHover(HoverEvent e) + { + UpdateHoverState(); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + base.OnHoverLost(e); + UpdateHoverState(); + } + + protected abstract void UpdateHoverState(); } } diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorItem.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorItem.cs deleted file mode 100644 index 92bf958ca9..0000000000 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorItem.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics.Containers; -using osu.Framework.Input.Events; -using JetBrains.Annotations; - -namespace osu.Game.Graphics.UserInterface.PageSelector -{ - public abstract class PageSelectorItem : OsuClickableContainer - { - protected const int DURATION = 200; - - [Resolved] - protected OsuColour Colours { get; private set; } - - protected Box Background; - - public CircularContainer CircularContent { get; private set; } - - protected PageSelectorItem() - { - AutoSizeAxes = Axes.X; - Height = 20; - } - - [BackgroundDependencyLoader] - private void load() - { - Add(CircularContent = new CircularContainer - { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Masking = true, - Children = new[] - { - Background = new Box - { - RelativeSizeAxes = Axes.Both - }, - CreateContent().With(content => - { - content.Anchor = Anchor.Centre; - content.Origin = Anchor.Centre; - content.Margin = new MarginPadding { Horizontal = 10 }; - }) - } - }); - } - - [NotNull] - protected abstract Drawable CreateContent(); - - protected override bool OnHover(HoverEvent e) - { - UpdateHoverState(); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - base.OnHoverLost(e); - UpdateHoverState(); - } - - protected abstract void UpdateHoverState(); - } -} diff --git a/osu.Game/Graphics/UserInterface/PageSelector/DrawablePage.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPageButton.cs similarity index 94% rename from osu.Game/Graphics/UserInterface/PageSelector/DrawablePage.cs rename to osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPageButton.cs index 2610ae7571..81ab7c7155 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/DrawablePage.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPageButton.cs @@ -9,7 +9,7 @@ using osu.Game.Graphics.Sprites; namespace osu.Game.Graphics.UserInterface.PageSelector { - public class DrawablePage : PageSelectorItem + public class PageSelectorPageButton : PageSelectorButton { private readonly BindableBool selected = new BindableBool(); @@ -22,7 +22,7 @@ namespace osu.Game.Graphics.UserInterface.PageSelector private OsuSpriteText text; - public DrawablePage(int page) + public PageSelectorPageButton(int page) { Page = page; diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPrevNextButton.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPrevNextButton.cs new file mode 100644 index 0000000000..eaa3714fea --- /dev/null +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPrevNextButton.cs @@ -0,0 +1,88 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics.Sprites; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Graphics.UserInterface.PageSelector +{ + public class PageSelectorPrevNextButton : PageSelectorButton + { + private readonly string text; + + private Box fadeBox; + private SpriteIcon icon; + private OsuSpriteText name; + + private readonly Anchor alignment; + + public PageSelectorPrevNextButton(bool rightAligned, string text) + { + this.text = text; + alignment = rightAligned ? Anchor.x0 : Anchor.x2; + } + + protected override Drawable CreateContent() => new Container + { + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Spacing = new Vector2(3, 0), + Children = new Drawable[] + { + name = new OsuSpriteText + { + Font = OsuFont.GetFont(size: 12), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = text.ToUpper(), + }, + icon = new SpriteIcon + { + Icon = alignment == Anchor.x2 ? FontAwesome.Solid.ChevronLeft : FontAwesome.Solid.ChevronRight, + Size = new Vector2(8), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + } + }, + } + }; + + [BackgroundDependencyLoader] + private void load() + { + Background.Colour = Colours.GreySeaFoamDark; + name.Colour = icon.Colour = Colours.Lime; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + CircularContent.Add(fadeBox = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(100) + }); + + Enabled.BindValueChanged(enabled => fadeBox.FadeTo(enabled.NewValue ? 0 : 1, DURATION), true); + } + + protected override void UpdateHoverState() => Background.FadeColour(IsHovered ? Colours.GreySeaFoam : Colours.GreySeaFoamDark, DURATION, Easing.OutQuint); + } +} From d10b8c79b39682f144f7c117a7c8278822485bfb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Jan 2022 17:53:23 +0900 Subject: [PATCH 34/98] Remove pointless test coverage of `DrawablePage` --- .../Visual/UserInterface/TestScenePageSelector.cs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs index 000e75be31..b65af6b1f5 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs @@ -10,7 +10,6 @@ namespace osu.Game.Tests.Visual.UserInterface public class TestScenePageSelector : OsuTestScene { private readonly PageSelector pageSelector; - private readonly PageSelectorPageButton pageSelectorPageButton; public TestScenePageSelector() { @@ -21,12 +20,6 @@ namespace osu.Game.Tests.Visual.UserInterface Anchor = Anchor.Centre, Origin = Anchor.Centre, }, - pageSelectorPageButton = new PageSelectorPageButton(1234) - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Margin = new MarginPadding { Top = 50 }, - } }); } @@ -58,13 +51,6 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("Max is 1", () => pageSelector.MaxPages.Value == 1); } - [Test] - public void TestDrawablePage() - { - AddStep("Select", () => pageSelectorPageButton.Selected = true); - AddStep("Deselect", () => pageSelectorPageButton.Selected = false); - } - private void setMaxPages(int maxPages) => pageSelector.MaxPages.Value = maxPages; private void setCurrentPage(int currentPage) => pageSelector.CurrentPage.Value = currentPage; From 5a11ee7810b705461360c2b3daa2e27b1e3b05ff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Jan 2022 18:14:42 +0900 Subject: [PATCH 35/98] Use `OverlayColourProvider` and fix font weight --- .../UserInterface/TestScenePageSelector.cs | 5 +++ .../PageSelector/PageSelectorButton.cs | 7 ++-- .../PageSelector/PageSelectorPageButton.cs | 9 +++-- .../PageSelectorPrevNextButton.cs | 34 +++++++------------ 4 files changed, 24 insertions(+), 31 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs index b65af6b1f5..1595a7f22d 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs @@ -2,13 +2,18 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Graphics.UserInterface.PageSelector; +using osu.Game.Overlays; namespace osu.Game.Tests.Visual.UserInterface { public class TestScenePageSelector : OsuTestScene { + [Cached] + private OverlayColourProvider provider { get; } = new OverlayColourProvider(OverlayColourScheme.Green); + private readonly PageSelector pageSelector; public TestScenePageSelector() diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorButton.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorButton.cs index bdfe66f1d0..a2c6e8532b 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorButton.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorButton.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.Containers; using osu.Framework.Input.Events; using JetBrains.Annotations; +using osu.Game.Overlays; namespace osu.Game.Graphics.UserInterface.PageSelector { @@ -16,12 +17,10 @@ namespace osu.Game.Graphics.UserInterface.PageSelector protected const int DURATION = 200; [Resolved] - protected OsuColour Colours { get; private set; } + protected OverlayColourProvider ColourProvider { get; private set; } protected Box Background; - public CircularContainer CircularContent { get; private set; } - protected PageSelectorButton() { AutoSizeAxes = Axes.X; @@ -31,7 +30,7 @@ namespace osu.Game.Graphics.UserInterface.PageSelector [BackgroundDependencyLoader] private void load() { - Add(CircularContent = new CircularContainer + Add(new CircularContainer { RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPageButton.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPageButton.cs index 81ab7c7155..31aca0d2f2 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPageButton.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPageButton.cs @@ -4,7 +4,6 @@ using osu.Framework.Graphics; using osu.Framework.Bindables; using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; using osu.Game.Graphics.Sprites; namespace osu.Game.Graphics.UserInterface.PageSelector @@ -35,14 +34,14 @@ namespace osu.Game.Graphics.UserInterface.PageSelector protected override Drawable CreateContent() => text = new OsuSpriteText { - Font = OsuFont.GetFont(size: 12), + Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold), Text = Page.ToString(), }; [BackgroundDependencyLoader] private void load() { - Background.Colour = Colours.Lime; + Background.Colour = ColourProvider.Highlight1; Background.Alpha = 0; } @@ -55,7 +54,7 @@ namespace osu.Game.Graphics.UserInterface.PageSelector private void onSelectedChanged(ValueChangedEvent selected) { Background.FadeTo(selected.NewValue ? 1 : 0, DURATION, Easing.OutQuint); - text.FadeColour(selected.NewValue ? Colours.GreySeaFoamDarker : Colours.Lime, DURATION, Easing.OutQuint); + text.FadeColour(selected.NewValue ? ColourProvider.Dark4 : ColourProvider.Light3, DURATION, Easing.OutQuint); } protected override void UpdateHoverState() @@ -63,7 +62,7 @@ namespace osu.Game.Graphics.UserInterface.PageSelector if (selected.Value) return; - text.FadeColour(IsHovered ? Colours.Lime.Lighten(20f) : Colours.Lime, DURATION, Easing.OutQuint); + text.FadeColour(IsHovered ? ColourProvider.Light2 : ColourProvider.Light1, DURATION, Easing.OutQuint); } } } diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPrevNextButton.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPrevNextButton.cs index eaa3714fea..7503ab8135 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPrevNextButton.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPrevNextButton.cs @@ -2,31 +2,26 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics.Sprites; using osuTK; -using osuTK.Graphics; namespace osu.Game.Graphics.UserInterface.PageSelector { public class PageSelectorPrevNextButton : PageSelectorButton { + private readonly bool rightAligned; private readonly string text; - private Box fadeBox; private SpriteIcon icon; private OsuSpriteText name; - private readonly Anchor alignment; - public PageSelectorPrevNextButton(bool rightAligned, string text) { + this.rightAligned = rightAligned; this.text = text; - alignment = rightAligned ? Anchor.x0 : Anchor.x2; } protected override Drawable CreateContent() => new Container @@ -47,16 +42,16 @@ namespace osu.Game.Graphics.UserInterface.PageSelector name = new OsuSpriteText { Font = OsuFont.GetFont(size: 12), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, + Anchor = rightAligned ? Anchor.CentreLeft : Anchor.CentreRight, + Origin = rightAligned ? Anchor.CentreLeft : Anchor.CentreRight, Text = text.ToUpper(), }, icon = new SpriteIcon { - Icon = alignment == Anchor.x2 ? FontAwesome.Solid.ChevronLeft : FontAwesome.Solid.ChevronRight, + Icon = rightAligned ? FontAwesome.Solid.ChevronRight : FontAwesome.Solid.ChevronLeft, Size = new Vector2(8), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, + Anchor = rightAligned ? Anchor.CentreLeft : Anchor.CentreRight, + Origin = rightAligned ? Anchor.CentreLeft : Anchor.CentreRight, }, } }, @@ -66,23 +61,18 @@ namespace osu.Game.Graphics.UserInterface.PageSelector [BackgroundDependencyLoader] private void load() { - Background.Colour = Colours.GreySeaFoamDark; - name.Colour = icon.Colour = Colours.Lime; + Background.Colour = ColourProvider.Dark4; + name.Colour = icon.Colour = ColourProvider.Light1; } protected override void LoadComplete() { base.LoadComplete(); - CircularContent.Add(fadeBox = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(100) - }); - - Enabled.BindValueChanged(enabled => fadeBox.FadeTo(enabled.NewValue ? 0 : 1, DURATION), true); + Enabled.BindValueChanged(enabled => Background.FadeTo(enabled.NewValue ? 1 : 0.5f, DURATION), true); } - protected override void UpdateHoverState() => Background.FadeColour(IsHovered ? Colours.GreySeaFoam : Colours.GreySeaFoamDark, DURATION, Easing.OutQuint); + protected override void UpdateHoverState() => + Background.FadeColour(IsHovered ? ColourProvider.Dark3 : ColourProvider.Dark4, DURATION, Easing.OutQuint); } } From e75c9519f32f8e44e1beac1e57b83798a1f8d547 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Jan 2022 18:19:23 +0900 Subject: [PATCH 36/98] Adjust font weighting on selection --- .../UserInterface/PageSelector/PageSelectorPageButton.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPageButton.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPageButton.cs index 31aca0d2f2..6aac75565e 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPageButton.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPageButton.cs @@ -54,7 +54,9 @@ namespace osu.Game.Graphics.UserInterface.PageSelector private void onSelectedChanged(ValueChangedEvent selected) { Background.FadeTo(selected.NewValue ? 1 : 0, DURATION, Easing.OutQuint); + text.FadeColour(selected.NewValue ? ColourProvider.Dark4 : ColourProvider.Light3, DURATION, Easing.OutQuint); + text.Font = text.Font.With(weight: IsHovered ? FontWeight.SemiBold : FontWeight.Regular); } protected override void UpdateHoverState() From 86f72b71b1229c165d30d419047165b00cdaafc7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Jan 2022 18:46:44 +0900 Subject: [PATCH 37/98] Prepare tests and general structure to support omission of pages --- .../UserInterface/TestScenePageSelector.cs | 42 ++++++------ .../PageSelector/PageSelector.cs | 66 +++++++------------ .../PageSelector/PageSelectorPageButton.cs | 8 +-- 3 files changed, 52 insertions(+), 64 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs index 1595a7f22d..3b32a2a0dc 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs @@ -1,9 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Testing; using osu.Game.Graphics.UserInterface.PageSelector; using osu.Game.Overlays; @@ -28,36 +30,38 @@ namespace osu.Game.Tests.Visual.UserInterface }); } + [Test] + public void TestOmittedPages() + { + setAvailablePages(100); + + AddAssert("Correct page buttons", () => pageSelector.ChildrenOfType().Select(p => p.PageNumber).SequenceEqual(new[] { 1, 2, 3, 100 })); + } + [Test] public void TestResetCurrentPage() { - AddStep("Set 10 pages", () => setMaxPages(10)); - AddStep("Select page 5", () => setCurrentPage(5)); - AddStep("Set 11 pages", () => setMaxPages(11)); - AddAssert("Page 1 is current", () => pageSelector.CurrentPage.Value == 1); + setAvailablePages(10); + selectPage(6); + setAvailablePages(11); + AddAssert("Page 1 is current", () => pageSelector.CurrentPage.Value == 0); } [Test] public void TestOutOfBoundsSelection() { - AddStep("Set 10 pages", () => setMaxPages(10)); - AddStep("Select page 11", () => setCurrentPage(11)); - AddAssert("Page 10 is current", () => pageSelector.CurrentPage.Value == pageSelector.MaxPages.Value); + setAvailablePages(10); + selectPage(11); + AddAssert("Page 10 is current", () => pageSelector.CurrentPage.Value == pageSelector.AvailablePages.Value - 1); - AddStep("Select page -1", () => setCurrentPage(-1)); - AddAssert("Page 1 is current", () => pageSelector.CurrentPage.Value == 1); + selectPage(-1); + AddAssert("Page 1 is current", () => pageSelector.CurrentPage.Value == 0); } - [Test] - public void TestNegativeMaxPages() - { - AddStep("Set -10 pages", () => setMaxPages(-10)); - AddAssert("Page 1 is current", () => pageSelector.CurrentPage.Value == 1); - AddAssert("Max is 1", () => pageSelector.MaxPages.Value == 1); - } + private void selectPage(int pageIndex) => + AddStep($"Select page {pageIndex}", () => pageSelector.CurrentPage.Value = pageIndex); - private void setMaxPages(int maxPages) => pageSelector.MaxPages.Value = maxPages; - - private void setCurrentPage(int currentPage) => pageSelector.CurrentPage.Value = currentPage; + private void setAvailablePages(int availablePages) => + AddStep($"Set available pages to {availablePages}", () => pageSelector.AvailablePages.Value = availablePages); } } diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs index 4089fb5511..ae644bb852 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs @@ -1,19 +1,20 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics; using osu.Framework.Bindables; -using osu.Framework.Extensions.IEnumerableExtensions; namespace osu.Game.Graphics.UserInterface.PageSelector { public class PageSelector : CompositeDrawable { - public readonly BindableInt CurrentPage = new BindableInt(1); - public readonly BindableInt MaxPages = new BindableInt(1); + public readonly BindableInt CurrentPage = new BindableInt { MinValue = 0, }; - private readonly FillFlowContainer itemsFlow; + public readonly BindableInt AvailablePages = new BindableInt(1) { MinValue = 1, }; + + private readonly FillFlowContainer itemsFlow; private readonly PageSelectorPrevNextButton previousPageButton; private readonly PageSelectorPrevNextButton nextPageButton; @@ -32,7 +33,7 @@ namespace osu.Game.Graphics.UserInterface.PageSelector { Action = () => CurrentPage.Value -= 1, }, - itemsFlow = new FillFlowContainer + itemsFlow = new FillFlowContainer { AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, @@ -49,60 +50,43 @@ namespace osu.Game.Graphics.UserInterface.PageSelector { base.LoadComplete(); - MaxPages.BindValueChanged(_ => redraw()); - CurrentPage.BindValueChanged(page => onCurrentPageChanged(page.NewValue)); - redraw(); + CurrentPage.BindValueChanged(onCurrentPageChanged); + AvailablePages.BindValueChanged(_ => redraw(), true); } - private void onCurrentPageChanged(int newPage) + private void onCurrentPageChanged(ValueChangedEvent currentPage) { - if (newPage < 1) + if (currentPage.NewValue >= AvailablePages.Value) { - CurrentPage.Value = 1; + CurrentPage.Value = AvailablePages.Value - 1; return; } - if (newPage > MaxPages.Value) - { - CurrentPage.Value = MaxPages.Value; - return; - } + foreach (var page in itemsFlow.OfType()) + page.Selected = page.PageNumber == currentPage.NewValue + 1; - itemsFlow.ForEach(page => page.Selected = page.Page == newPage); - updateButtonsState(); + previousPageButton.Enabled.Value = currentPage.NewValue != 0; + nextPageButton.Enabled.Value = currentPage.NewValue < AvailablePages.Value - 1; } private void redraw() { itemsFlow.Clear(); - if (MaxPages.Value < 1) + for (int i = 0; i < AvailablePages.Value; i++) { - MaxPages.Value = 1; - return; + int pageIndex = i; + + itemsFlow.Add(new PageSelectorPageButton(pageIndex + 1) + { + Action = () => CurrentPage.Value = pageIndex, + }); } - for (int i = 1; i <= MaxPages.Value; i++) - addDrawablePage(i); - - if (CurrentPage.Value == 1) - CurrentPage.TriggerChange(); + if (CurrentPage.Value != 0) + CurrentPage.Value = 0; else - CurrentPage.Value = 1; + CurrentPage.TriggerChange(); } - - private void updateButtonsState() - { - int newPage = CurrentPage.Value; - int maxPages = MaxPages.Value; - - previousPageButton.Enabled.Value = newPage != 1; - nextPageButton.Enabled.Value = newPage != maxPages; - } - - private void addDrawablePage(int page) => itemsFlow.Add(new PageSelectorPageButton(page) - { - Action = () => CurrentPage.Value = page, - }); } } diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPageButton.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPageButton.cs index 6aac75565e..247a003492 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPageButton.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelectorPageButton.cs @@ -17,13 +17,13 @@ namespace osu.Game.Graphics.UserInterface.PageSelector set => selected.Value = value; } - public int Page { get; } + public int PageNumber { get; } private OsuSpriteText text; - public PageSelectorPageButton(int page) + public PageSelectorPageButton(int pageNumber) { - Page = page; + PageNumber = pageNumber; Action = () => { @@ -35,7 +35,7 @@ namespace osu.Game.Graphics.UserInterface.PageSelector protected override Drawable CreateContent() => text = new OsuSpriteText { Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold), - Text = Page.ToString(), + Text = PageNumber.ToString(), }; [BackgroundDependencyLoader] From 5ed69338a662165d7374e3de1940ad2997f35faa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Jan 2022 19:05:14 +0900 Subject: [PATCH 38/98] Add omission of pages when there are too many --- .../UserInterface/TestScenePageSelector.cs | 22 +++++++-- .../PageSelector/PageEllipsis.cs | 33 +++++++++++++ .../PageSelector/PageSelector.cs | 48 ++++++++++--------- 3 files changed, 76 insertions(+), 27 deletions(-) create mode 100644 osu.Game/Graphics/UserInterface/PageSelector/PageEllipsis.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs index 3b32a2a0dc..c99ac52cb1 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePageSelector.cs @@ -35,14 +35,24 @@ namespace osu.Game.Tests.Visual.UserInterface { setAvailablePages(100); - AddAssert("Correct page buttons", () => pageSelector.ChildrenOfType().Select(p => p.PageNumber).SequenceEqual(new[] { 1, 2, 3, 100 })); + selectPageIndex(0); + checkVisiblePageNumbers(new[] { 1, 2, 3, 100 }); + + selectPageIndex(6); + checkVisiblePageNumbers(new[] { 1, 5, 6, 7, 8, 9, 100 }); + + selectPageIndex(49); + checkVisiblePageNumbers(new[] { 1, 48, 49, 50, 51, 52, 100 }); + + selectPageIndex(99); + checkVisiblePageNumbers(new[] { 1, 98, 99, 100 }); } [Test] public void TestResetCurrentPage() { setAvailablePages(10); - selectPage(6); + selectPageIndex(6); setAvailablePages(11); AddAssert("Page 1 is current", () => pageSelector.CurrentPage.Value == 0); } @@ -51,14 +61,16 @@ namespace osu.Game.Tests.Visual.UserInterface public void TestOutOfBoundsSelection() { setAvailablePages(10); - selectPage(11); + selectPageIndex(11); AddAssert("Page 10 is current", () => pageSelector.CurrentPage.Value == pageSelector.AvailablePages.Value - 1); - selectPage(-1); + selectPageIndex(-1); AddAssert("Page 1 is current", () => pageSelector.CurrentPage.Value == 0); } - private void selectPage(int pageIndex) => + private void checkVisiblePageNumbers(int[] expected) => AddAssert($"Sequence is {string.Join(',', expected.Select(i => i.ToString()))}", () => pageSelector.ChildrenOfType().Select(p => p.PageNumber).SequenceEqual(expected)); + + private void selectPageIndex(int pageIndex) => AddStep($"Select page {pageIndex}", () => pageSelector.CurrentPage.Value = pageIndex); private void setAvailablePages(int availablePages) => diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageEllipsis.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageEllipsis.cs new file mode 100644 index 0000000000..d73d9f5824 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageEllipsis.cs @@ -0,0 +1,33 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; + +namespace osu.Game.Graphics.UserInterface.PageSelector +{ + internal class PageEllipsis : CompositeDrawable + { + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + RelativeSizeAxes = Axes.Y; + AutoSizeAxes = Axes.X; + + InternalChildren = new Drawable[] + { + new OsuSpriteText + { + Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold), + Text = "...", + Colour = colourProvider.Light3, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + }; + } + } +} diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs index ae644bb852..ce74259289 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs @@ -1,7 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; +using System; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics; using osu.Framework.Bindables; @@ -50,43 +50,47 @@ namespace osu.Game.Graphics.UserInterface.PageSelector { base.LoadComplete(); - CurrentPage.BindValueChanged(onCurrentPageChanged); + CurrentPage.BindValueChanged(_ => redraw()); AvailablePages.BindValueChanged(_ => redraw(), true); } - private void onCurrentPageChanged(ValueChangedEvent currentPage) + private void redraw() { - if (currentPage.NewValue >= AvailablePages.Value) + if (CurrentPage.Value >= AvailablePages.Value) { CurrentPage.Value = AvailablePages.Value - 1; return; } - foreach (var page in itemsFlow.OfType()) - page.Selected = page.PageNumber == currentPage.NewValue + 1; + previousPageButton.Enabled.Value = CurrentPage.Value != 0; + nextPageButton.Enabled.Value = CurrentPage.Value < AvailablePages.Value - 1; - previousPageButton.Enabled.Value = currentPage.NewValue != 0; - nextPageButton.Enabled.Value = currentPage.NewValue < AvailablePages.Value - 1; - } - - private void redraw() - { itemsFlow.Clear(); - for (int i = 0; i < AvailablePages.Value; i++) + int totalPages = AvailablePages.Value; + bool lastWasEllipsis = false; + + for (int i = 0; i < totalPages; i++) { int pageIndex = i; - itemsFlow.Add(new PageSelectorPageButton(pageIndex + 1) - { - Action = () => CurrentPage.Value = pageIndex, - }); - } + bool shouldShowPage = pageIndex == 0 || pageIndex == totalPages - 1 || Math.Abs(pageIndex - CurrentPage.Value) <= 2; - if (CurrentPage.Value != 0) - CurrentPage.Value = 0; - else - CurrentPage.TriggerChange(); + if (shouldShowPage) + { + lastWasEllipsis = false; + itemsFlow.Add(new PageSelectorPageButton(pageIndex + 1) + { + Action = () => CurrentPage.Value = pageIndex, + Selected = CurrentPage.Value == pageIndex, + }); + } + else if (!lastWasEllipsis) + { + lastWasEllipsis = true; + itemsFlow.Add(new PageEllipsis()); + } + } } } } From 2bf6b55b19a26add1ad4f3f6e19bc4b553025b95 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Jan 2022 14:53:32 +0900 Subject: [PATCH 39/98] Fix failing test due to changed reset page logic --- .../UserInterface/PageSelector/PageSelector.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs b/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs index ce74259289..005729580c 100644 --- a/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs +++ b/osu.Game/Graphics/UserInterface/PageSelector/PageSelector.cs @@ -50,8 +50,14 @@ namespace osu.Game.Graphics.UserInterface.PageSelector { base.LoadComplete(); - CurrentPage.BindValueChanged(_ => redraw()); - AvailablePages.BindValueChanged(_ => redraw(), true); + CurrentPage.BindValueChanged(_ => Scheduler.AddOnce(redraw)); + AvailablePages.BindValueChanged(_ => + { + CurrentPage.Value = 0; + + // AddOnce as the reset of CurrentPage may also trigger a redraw. + Scheduler.AddOnce(redraw); + }, true); } private void redraw() From ef2a4aed9a2600b3be2eb86215711c718030bde3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Jan 2022 16:05:15 +0900 Subject: [PATCH 40/98] Fix editor playfield not being centered correctly This has come up multiple times, with mappers citing that they have muscle memory for mapping based on the centre of the playfield being in the centre of the window. The original plan was to have a second toolbar on the right hand side of the screen to balance the padding, but we're not at that point yet. Easiest solution is to do what stable does and allow the left-hand toolbar items to overlap the playfield underneath it. In edge cases where the user is running at an aspect ratio that causes overlaps, they can choose to collapse the toolbars down. We can probably work on this UI/UX a bit more as we update designs to be more friendly to such cases. --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 4bbfe26d7b..cbc2415603 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -105,7 +105,6 @@ namespace osu.Game.Rulesets.Edit new Container { Name = "Content", - Padding = new MarginPadding { Left = toolbar_width }, RelativeSizeAxes = Axes.Both, Children = new Drawable[] { From 13cce50fa74726c3dc016fe43e2c67e1fe630ba6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Jan 2022 16:30:42 +0900 Subject: [PATCH 41/98] Remove existing handling of flip hotkeys --- .../Screens/Edit/Compose/Components/SelectionBox.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index be52a968bb..732c6a5dd4 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -174,12 +174,6 @@ namespace osu.Game.Screens.Edit.Compose.Components { case Key.G: return CanReverse && runOperationFromHotkey(OnReverse); - - case Key.H: - return CanFlipX && runOperationFromHotkey(() => OnFlip?.Invoke(Direction.Horizontal) ?? false); - - case Key.J: - return CanFlipY && runOperationFromHotkey(() => OnFlip?.Invoke(Direction.Vertical) ?? false); } return base.OnKeyDown(e); @@ -287,12 +281,12 @@ namespace osu.Game.Screens.Edit.Compose.Components private void addXFlipComponents() { - addButton(FontAwesome.Solid.ArrowsAltH, "Flip horizontally (Ctrl-H)", () => OnFlip?.Invoke(Direction.Horizontal)); + addButton(FontAwesome.Solid.ArrowsAltH, "Flip horizontally", () => OnFlip?.Invoke(Direction.Horizontal)); } private void addYFlipComponents() { - addButton(FontAwesome.Solid.ArrowsAltV, "Flip vertically (Ctrl-J)", () => OnFlip?.Invoke(Direction.Vertical)); + addButton(FontAwesome.Solid.ArrowsAltV, "Flip vertically", () => OnFlip?.Invoke(Direction.Vertical)); } private void addButton(IconUsage icon, string tooltip, Action action) From 866ae3472bb654af5bde50f79ecb0ae2cbd884e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Jan 2022 16:46:34 +0900 Subject: [PATCH 42/98] Add global flip hotkeys --- .../Edit/CatchSelectionHandler.cs | 12 ++++--- .../Edit/OsuSelectionHandler.cs | 7 +++-- .../Input/Bindings/GlobalActionContainer.cs | 10 +++++- .../GlobalActionKeyBindingStrings.cs | 10 ++++++ .../Edit/Compose/Components/SelectionBox.cs | 6 ++-- .../Compose/Components/SelectionHandler.cs | 31 +++++++++++++++++-- .../Timeline/TimelineSelectionHandler.cs | 12 +++---- .../Skinning/Editor/SkinSelectionHandler.cs | 4 +-- 8 files changed, 68 insertions(+), 24 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index 8cb0804ab7..41a2584acc 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Catch.Edit return true; } - public override bool HandleFlip(Direction direction) + public override bool HandleFlip(Direction direction, bool flipOverOrigin) { var selectionRange = CatchHitObjectUtils.GetPositionRange(SelectedItems); @@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Catch.Edit EditorBeatmap.PerformOnSelection(h => { if (h is CatchHitObject catchObject) - changed |= handleFlip(selectionRange, catchObject); + changed |= handleFlip(selectionRange, catchObject, flipOverOrigin); }); return changed; } @@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Catch.Edit return Math.Clamp(deltaX, lowerBound, upperBound); } - private bool handleFlip(PositionRange selectionRange, CatchHitObject hitObject) + private bool handleFlip(PositionRange selectionRange, CatchHitObject hitObject, bool flipOverOrigin) { switch (hitObject) { @@ -124,7 +124,7 @@ namespace osu.Game.Rulesets.Catch.Edit return false; case JuiceStream juiceStream: - juiceStream.OriginalX = selectionRange.GetFlippedPosition(juiceStream.OriginalX); + juiceStream.OriginalX = getFlippedPosition(juiceStream.OriginalX); foreach (var point in juiceStream.Path.ControlPoints) point.Position *= new Vector2(-1, 1); @@ -133,9 +133,11 @@ namespace osu.Game.Rulesets.Catch.Edit return true; default: - hitObject.OriginalX = selectionRange.GetFlippedPosition(hitObject.OriginalX); + hitObject.OriginalX = getFlippedPosition(hitObject.OriginalX); return true; } + + float getFlippedPosition(float original) => flipOverOrigin ? CatchPlayfield.WIDTH - original : selectionRange.GetFlippedPosition(original); } } } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 4a57d36eb4..d172fa5398 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -10,6 +10,7 @@ using osu.Game.Extensions; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.UI; using osu.Game.Screens.Edit.Compose.Components; using osuTK; @@ -84,15 +85,15 @@ namespace osu.Game.Rulesets.Osu.Edit return true; } - public override bool HandleFlip(Direction direction) + public override bool HandleFlip(Direction direction, bool flipOverOrigin) { var hitObjects = selectedMovableObjects; - var selectedObjectsQuad = getSurroundingQuad(hitObjects); + var flipQuad = flipOverOrigin ? new Quad(0, 0, OsuPlayfield.BASE_SIZE.X, OsuPlayfield.BASE_SIZE.Y) : getSurroundingQuad(hitObjects); foreach (var h in hitObjects) { - h.Position = GetFlippedPosition(direction, selectedObjectsQuad, h.Position); + h.Position = GetFlippedPosition(direction, flipQuad, h.Position); if (h is Slider slider) { diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index c71cb6a00a..47cb7be2cf 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -77,6 +77,8 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.K }, GlobalAction.EditorNudgeRight), new KeyBinding(new[] { InputKey.G }, GlobalAction.EditorCycleGridDisplayMode), new KeyBinding(new[] { InputKey.F5 }, GlobalAction.EditorTestGameplay), + new KeyBinding(new[] { InputKey.Control, InputKey.H }, GlobalAction.EditorFlipHorizontally), + new KeyBinding(new[] { InputKey.Control, InputKey.J }, GlobalAction.EditorFlipVertically), }; public IEnumerable InGameKeyBindings => new[] @@ -292,6 +294,12 @@ namespace osu.Game.Input.Bindings EditorCycleGridDisplayMode, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorTestGameplay))] - EditorTestGameplay + EditorTestGameplay, + + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorFlipHorizontally))] + EditorFlipHorizontally, + + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorFlipVertically))] + EditorFlipVertically, } } diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs index 35a0c2ae74..777e97d1e3 100644 --- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -229,6 +229,16 @@ namespace osu.Game.Localisation /// public static LocalisableString EditorNudgeRight => new TranslatableString(getKey(@"editor_nudge_right"), @"Nudge selection right"); + /// + /// "Flip selection horizontally" + /// + public static LocalisableString EditorFlipHorizontally => new TranslatableString(getKey(@"editor_flip_horizontally"), @"Flip selection horizontally"); + + /// + /// "Flip selection vertically" + /// + public static LocalisableString EditorFlipVertically => new TranslatableString(getKey(@"editor_flip_vertically"), @"Flip selection vertically"); + /// /// "Toggle skin editor" /// diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 732c6a5dd4..3a31f6ea8c 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -21,7 +21,7 @@ namespace osu.Game.Screens.Edit.Compose.Components public Func OnRotation; public Func OnScale; - public Func OnFlip; + public Func OnFlip; public Func OnReverse; public Action OperationStarted; @@ -281,12 +281,12 @@ namespace osu.Game.Screens.Edit.Compose.Components private void addXFlipComponents() { - addButton(FontAwesome.Solid.ArrowsAltH, "Flip horizontally", () => OnFlip?.Invoke(Direction.Horizontal)); + addButton(FontAwesome.Solid.ArrowsAltH, "Flip horizontally", () => OnFlip?.Invoke(Direction.Horizontal, false)); } private void addYFlipComponents() { - addButton(FontAwesome.Solid.ArrowsAltV, "Flip vertically", () => OnFlip?.Invoke(Direction.Vertical)); + addButton(FontAwesome.Solid.ArrowsAltV, "Flip vertically", () => OnFlip?.Invoke(Direction.Vertical, false)); } private void addButton(IconUsage icon, string tooltip, Action action) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index ee35b6a47c..d9d310c72c 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -17,6 +17,7 @@ using osu.Framework.Input.Events; using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; +using osu.Game.Input.Bindings; using osu.Game.Rulesets.Edit; using osuTK; using osuTK.Input; @@ -26,7 +27,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// A component which outlines items and handles movement of selections. /// - public abstract class SelectionHandler : CompositeDrawable, IKeyBindingHandler, IHasContextMenu + public abstract class SelectionHandler : CompositeDrawable, IKeyBindingHandler, IKeyBindingHandler, IHasContextMenu { /// /// The currently selected blueprints. @@ -127,9 +128,10 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// Handles the selected items being flipped. /// - /// The direction to flip + /// The direction to flip. + /// Whether the flip operation should be global to the playfield's origin or local to the selected pattern. /// Whether any items could be flipped. - public virtual bool HandleFlip(Direction direction) => false; + public virtual bool HandleFlip(Direction direction, bool flipOverOrigin) => false; /// /// Handles the selected items being reversed pattern-wise. @@ -137,6 +139,29 @@ namespace osu.Game.Screens.Edit.Compose.Components /// Whether any items could be reversed. public virtual bool HandleReverse() => false; + public virtual bool OnPressed(KeyBindingPressEvent e) + { + if (e.Repeat) + return false; + + switch (e.Action) + { + case GlobalAction.EditorFlipHorizontally: + HandleFlip(Direction.Horizontal, true); + return true; + + case GlobalAction.EditorFlipVertically: + HandleFlip(Direction.Vertical, true); + return true; + } + + return false; + } + + public void OnReleased(KeyBindingReleaseEvent e) + { + } + public bool OnPressed(KeyBindingPressEvent e) { switch (e.Action) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs index 845a671e2c..8b7ff45765 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs @@ -7,7 +7,6 @@ using System.Diagnostics; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; -using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Input.Bindings; using osu.Game.Rulesets.Edit; @@ -17,7 +16,7 @@ using osuTK.Input; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { - internal class TimelineSelectionHandler : EditorSelectionHandler, IKeyBindingHandler + internal class TimelineSelectionHandler : EditorSelectionHandler { [Resolved] private Timeline timeline { get; set; } @@ -27,8 +26,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline // for now we always allow movement. snapping is provided by the Timeline's "distance" snap implementation public override bool HandleMovement(MoveSelectionEvent moveEvent) => true; - public bool OnPressed(KeyBindingPressEvent e) + public override bool OnPressed(KeyBindingPressEvent e) { + // Importantly, we block the base call here. + // Other key operations will be handled by the composer view's SelectionHandler instead. + switch (e.Action) { case GlobalAction.EditorNudgeLeft: @@ -43,10 +45,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline return false; } - public void OnReleased(KeyBindingReleaseEvent e) - { - } - /// /// Nudge the current selection by the specified multiple of beat divisor lengths, /// based on the timing at the first object in the selection. diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index 01bd5e8196..a54590a0ea 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -126,9 +126,9 @@ namespace osu.Game.Skinning.Editor return true; } - public override bool HandleFlip(Direction direction) + public override bool HandleFlip(Direction direction, bool flipOverOrigin) { - var selectionQuad = getSelectionQuad(); + var selectionQuad = flipOverOrigin ? ScreenSpaceDrawQuad : getSelectionQuad(); Vector2 scaleFactor = direction == Direction.Horizontal ? new Vector2(-1, 1) : new Vector2(1, -1); foreach (var b in SelectedBlueprints) From 6779503e574e717c571d27ddef15939b6255945f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Jan 2022 16:56:54 +0900 Subject: [PATCH 43/98] Refactor logic to avoid `TimelineSelectionHandler` having to block base calls --- osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs | 5 +++++ osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs | 3 +++ .../Screens/Edit/Compose/Components/SelectionHandler.cs | 6 ++---- .../Compose/Components/Timeline/TimelineSelectionHandler.cs | 5 +---- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index 41a2584acc..d39f1d3c86 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -54,14 +54,19 @@ namespace osu.Game.Rulesets.Catch.Edit public override bool HandleFlip(Direction direction, bool flipOverOrigin) { + if (SelectedItems.Count == 0 && !flipOverOrigin) + return false; + var selectionRange = CatchHitObjectUtils.GetPositionRange(SelectedItems); bool changed = false; + EditorBeatmap.PerformOnSelection(h => { if (h is CatchHitObject catchObject) changed |= handleFlip(selectionRange, catchObject, flipOverOrigin); }); + return changed; } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index d172fa5398..d51f08ad68 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -89,6 +89,9 @@ namespace osu.Game.Rulesets.Osu.Edit { var hitObjects = selectedMovableObjects; + if (hitObjects.Length == 1 && !flipOverOrigin) + return false; + var flipQuad = flipOverOrigin ? new Quad(0, 0, OsuPlayfield.BASE_SIZE.X, OsuPlayfield.BASE_SIZE.Y) : getSurroundingQuad(hitObjects); foreach (var h in hitObjects) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index d9d310c72c..39de13899d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -147,12 +147,10 @@ namespace osu.Game.Screens.Edit.Compose.Components switch (e.Action) { case GlobalAction.EditorFlipHorizontally: - HandleFlip(Direction.Horizontal, true); - return true; + return HandleFlip(Direction.Horizontal, true); case GlobalAction.EditorFlipVertically: - HandleFlip(Direction.Vertical, true); - return true; + return HandleFlip(Direction.Vertical, true); } return false; diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs index 8b7ff45765..e98cf8332f 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs @@ -28,9 +28,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public override bool OnPressed(KeyBindingPressEvent e) { - // Importantly, we block the base call here. - // Other key operations will be handled by the composer view's SelectionHandler instead. - switch (e.Action) { case GlobalAction.EditorNudgeLeft: @@ -42,7 +39,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline return true; } - return false; + return base.OnPressed(e); } /// From ee24713002d91838e4531428517de38e5251ec66 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Jan 2022 14:37:13 +0900 Subject: [PATCH 44/98] Fix single sliders not being flippable due to incorrect precondition --- .../Edit/OsuSelectionHandler.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index d51f08ad68..071ecf6329 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -89,17 +89,24 @@ namespace osu.Game.Rulesets.Osu.Edit { var hitObjects = selectedMovableObjects; - if (hitObjects.Length == 1 && !flipOverOrigin) - return false; - var flipQuad = flipOverOrigin ? new Quad(0, 0, OsuPlayfield.BASE_SIZE.X, OsuPlayfield.BASE_SIZE.Y) : getSurroundingQuad(hitObjects); + bool didFlip = false; + foreach (var h in hitObjects) { - h.Position = GetFlippedPosition(direction, flipQuad, h.Position); + var flippedPosition = GetFlippedPosition(direction, flipQuad, h.Position); + + if (!Precision.AlmostEquals(flippedPosition, h.Position)) + { + h.Position = flippedPosition; + didFlip = true; + } if (h is Slider slider) { + didFlip = true; + foreach (var point in slider.Path.ControlPoints) { point.Position = new Vector2( @@ -110,7 +117,7 @@ namespace osu.Game.Rulesets.Osu.Edit } } - return true; + return didFlip; } public override bool HandleScale(Vector2 scale, Anchor reference) From 5c0494f3bae6468e0b7a2791f99c3e02b4a3216b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Jan 2022 14:39:00 +0900 Subject: [PATCH 45/98] Remove unnecessary precondition check and disallow vertical catch flips for now --- osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index d39f1d3c86..dd5835b4ed 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -54,7 +54,11 @@ namespace osu.Game.Rulesets.Catch.Edit public override bool HandleFlip(Direction direction, bool flipOverOrigin) { - if (SelectedItems.Count == 0 && !flipOverOrigin) + if (SelectedItems.Count == 0) + return false; + + // This could be implemented in the future if there's a requirement for it. + if (direction == Direction.Vertical) return false; var selectionRange = CatchHitObjectUtils.GetPositionRange(SelectedItems); From 243a1a3cf7ce26f11f6aea7305a04996be916dcc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Jan 2022 14:47:44 +0900 Subject: [PATCH 46/98] Fix incorrect origin specification for `SkinSelectionHandler` flips --- osu.Game/Skinning/Editor/SkinSelectionHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index a54590a0ea..bd6d097eb2 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -128,14 +128,14 @@ namespace osu.Game.Skinning.Editor public override bool HandleFlip(Direction direction, bool flipOverOrigin) { - var selectionQuad = flipOverOrigin ? ScreenSpaceDrawQuad : getSelectionQuad(); + var selectionQuad = getSelectionQuad(); Vector2 scaleFactor = direction == Direction.Horizontal ? new Vector2(-1, 1) : new Vector2(1, -1); foreach (var b in SelectedBlueprints) { var drawableItem = (Drawable)b.Item; - var flippedPosition = GetFlippedPosition(direction, selectionQuad, b.ScreenSpaceSelectionPoint); + var flippedPosition = GetFlippedPosition(direction, flipOverOrigin ? drawableItem.Parent.ScreenSpaceDrawQuad : selectionQuad, b.ScreenSpaceSelectionPoint); updateDrawablePosition(drawableItem, flippedPosition); From 84765b99b36bb66f7aded738867593ad9cb5936b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 6 Jan 2022 12:57:26 +0100 Subject: [PATCH 47/98] Handle score submission request in submission test scene Was previously not handled at all, therefore displaying request failures in the test log output. While that was mostly a red herring and shouldn't have caused any actual *test* failures, it is still better to handle this explicitly in a realistic manner. --- .../TestScenePlayerScoreSubmission.cs | 51 ++++++++++++++----- .../Online/Solo/SubmitSoloScoreRequest.cs | 8 +-- 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs index 25808d307d..5ee1f9dd8f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs @@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestNoSubmissionOnResultsWithNoToken() { - prepareTokenResponse(false); + prepareTestAPI(false); createPlayerTest(); @@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestSubmissionOnResults() { - prepareTokenResponse(true); + prepareTestAPI(true); createPlayerTest(); @@ -96,7 +96,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestSubmissionForDifferentRuleset() { - prepareTokenResponse(true); + prepareTestAPI(true); createPlayerTest(createRuleset: () => new TaikoRuleset()); @@ -116,7 +116,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestSubmissionForConvertedBeatmap() { - prepareTokenResponse(true); + prepareTestAPI(true); createPlayerTest(createRuleset: () => new ManiaRuleset(), createBeatmap: _ => createTestBeatmap(new OsuRuleset().RulesetInfo)); @@ -136,7 +136,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestNoSubmissionOnExitWithNoToken() { - prepareTokenResponse(false); + prepareTestAPI(false); createPlayerTest(); @@ -153,7 +153,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestNoSubmissionOnEmptyFail() { - prepareTokenResponse(true); + prepareTestAPI(true); createPlayerTest(true); @@ -168,7 +168,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestSubmissionOnFail() { - prepareTokenResponse(true); + prepareTestAPI(true); createPlayerTest(true); @@ -185,7 +185,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestNoSubmissionOnEmptyExit() { - prepareTokenResponse(true); + prepareTestAPI(true); createPlayerTest(); @@ -198,7 +198,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestSubmissionOnExit() { - prepareTokenResponse(true); + prepareTestAPI(true); createPlayerTest(); @@ -213,7 +213,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestSubmissionOnExitDuringImport() { - prepareTokenResponse(true); + prepareTestAPI(true); createPlayerTest(); AddStep("block imports", () => Player.AllowImportCompletion.Wait()); @@ -232,7 +232,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestNoSubmissionOnLocalBeatmap() { - prepareTokenResponse(true); + prepareTestAPI(true); createPlayerTest(false, r => { @@ -253,7 +253,7 @@ namespace osu.Game.Tests.Visual.Gameplay [TestCase(10)] public void TestNoSubmissionOnCustomRuleset(int? rulesetId) { - prepareTokenResponse(true); + prepareTestAPI(true); createPlayerTest(false, createRuleset: () => new OsuRuleset { RulesetInfo = { OnlineID = rulesetId ?? -1 } }); @@ -275,7 +275,7 @@ namespace osu.Game.Tests.Visual.Gameplay })); } - private void prepareTokenResponse(bool validToken) + private void prepareTestAPI(bool validToken) { AddStep("Prepare test API", () => { @@ -289,6 +289,31 @@ namespace osu.Game.Tests.Visual.Gameplay else tokenRequest.TriggerFailure(new APIException("something went wrong!", null)); return true; + + case SubmitSoloScoreRequest submissionRequest: + if (validToken) + { + var requestScore = submissionRequest.Score; + + submissionRequest.TriggerSuccess(new MultiplayerScore + { + ID = 1234, + User = dummyAPI.LocalUser.Value, + Rank = requestScore.Rank, + TotalScore = requestScore.TotalScore, + Accuracy = requestScore.Accuracy, + MaxCombo = requestScore.MaxCombo, + Mods = requestScore.Mods, + Statistics = requestScore.Statistics, + Passed = requestScore.Passed, + EndedAt = DateTimeOffset.Now, + Position = 1 + }); + + return true; + } + + break; } return false; diff --git a/osu.Game/Online/Solo/SubmitSoloScoreRequest.cs b/osu.Game/Online/Solo/SubmitSoloScoreRequest.cs index 99cf5ceff5..78ebddb2e6 100644 --- a/osu.Game/Online/Solo/SubmitSoloScoreRequest.cs +++ b/osu.Game/Online/Solo/SubmitSoloScoreRequest.cs @@ -12,17 +12,17 @@ namespace osu.Game.Online.Solo { public class SubmitSoloScoreRequest : APIRequest { + public readonly SubmittableScore Score; + private readonly long scoreId; private readonly int beatmapId; - private readonly SubmittableScore score; - public SubmitSoloScoreRequest(int beatmapId, long scoreId, ScoreInfo scoreInfo) { this.beatmapId = beatmapId; this.scoreId = scoreId; - score = new SubmittableScore(scoreInfo); + Score = new SubmittableScore(scoreInfo); } protected override WebRequest CreateWebRequest() @@ -33,7 +33,7 @@ namespace osu.Game.Online.Solo req.Method = HttpMethod.Put; req.Timeout = 30000; - req.AddRaw(JsonConvert.SerializeObject(score, new JsonSerializerSettings + req.AddRaw(JsonConvert.SerializeObject(Score, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore })); From 77980196c5e6473ae1c9ff70ac95f85050a107a0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Jan 2022 17:15:42 +0900 Subject: [PATCH 48/98] Split out expanding container logic from settings sidebar --- ...Sidebar.cs => ExpandingButtonContainer.cs} | 56 +++++++------------ osu.Game/Overlays/Settings/SettingsSidebar.cs | 37 ++++++++++++ .../Overlays/Settings/SidebarIconButton.cs | 4 +- osu.Game/Overlays/SettingsPanel.cs | 8 +-- osu.Game/Overlays/SettingsSubPanel.cs | 2 +- 5 files changed, 65 insertions(+), 42 deletions(-) rename osu.Game/Overlays/{Settings/Sidebar.cs => ExpandingButtonContainer.cs} (67%) create mode 100644 osu.Game/Overlays/Settings/SettingsSidebar.cs diff --git a/osu.Game/Overlays/Settings/Sidebar.cs b/osu.Game/Overlays/ExpandingButtonContainer.cs similarity index 67% rename from osu.Game/Overlays/Settings/Sidebar.cs rename to osu.Game/Overlays/ExpandingButtonContainer.cs index 93b1b19b17..a49f0493f6 100644 --- a/osu.Game/Overlays/Settings/Sidebar.cs +++ b/osu.Game/Overlays/ExpandingButtonContainer.cs @@ -1,47 +1,47 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using System.Linq; using osu.Framework; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; +using osu.Framework.Testing; using osu.Framework.Threading; -using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Settings; using osuTK; -namespace osu.Game.Overlays.Settings +namespace osu.Game.Overlays { - public class Sidebar : Container, IStateful + public abstract class ExpandingButtonContainer : Container, IStateful { - private readonly Box background; - private readonly FillFlowContainer content; - public const float DEFAULT_WIDTH = 70; - public const int EXPANDED_WIDTH = 200; + private readonly float contractedWidth; + private readonly float expandedWidth; public event Action StateChanged; - protected override Container Content => content; + protected override Container Content => FillFlow; - public Sidebar() + protected FillFlowContainer FillFlow { get; } + + protected ExpandingButtonContainer(float contractedWidth, float expandedWidth) { + this.contractedWidth = contractedWidth; + this.expandedWidth = expandedWidth; + RelativeSizeAxes = Axes.Y; + Width = contractedWidth; + InternalChildren = new Drawable[] { - background = new Box - { - Colour = OsuColour.Gray(0.02f), - RelativeSizeAxes = Axes.Both, - }, new SidebarScrollContainer { Children = new[] { - content = new FillFlowContainer + FillFlow = new FillFlowContainer { Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, @@ -54,12 +54,6 @@ namespace osu.Game.Overlays.Settings }; } - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) - { - background.Colour = colourProvider.Background5; - } - private ScheduledDelegate expandEvent; private ExpandedState state; @@ -107,11 +101,11 @@ namespace osu.Game.Overlays.Settings switch (state) { default: - this.ResizeTo(new Vector2(DEFAULT_WIDTH, Height), 500, Easing.OutQuint); + this.ResizeTo(new Vector2(contractedWidth, Height), 500, Easing.OutQuint); break; case ExpandedState.Expanded: - this.ResizeTo(new Vector2(EXPANDED_WIDTH, Height), 500, Easing.OutQuint); + this.ResizeTo(new Vector2(expandedWidth, Height), 500, Easing.OutQuint); break; } @@ -121,15 +115,13 @@ namespace osu.Game.Overlays.Settings private Drawable lastHoveredButton; - private Drawable hoveredButton => content.Children.FirstOrDefault(c => c.IsHovered); + private Drawable hoveredButton => FillFlow.ChildrenOfType().FirstOrDefault(c => c.IsHovered); private void queueExpandIfHovering() { // only expand when we hover a different button. if (lastHoveredButton == hoveredButton) return; - if (!IsHovered) return; - if (State != ExpandedState.Expanded) { expandEvent?.Cancel(); @@ -139,10 +131,4 @@ namespace osu.Game.Overlays.Settings lastHoveredButton = hoveredButton; } } - - public enum ExpandedState - { - Contracted, - Expanded, - } } diff --git a/osu.Game/Overlays/Settings/SettingsSidebar.cs b/osu.Game/Overlays/Settings/SettingsSidebar.cs new file mode 100644 index 0000000000..5aa2e61162 --- /dev/null +++ b/osu.Game/Overlays/Settings/SettingsSidebar.cs @@ -0,0 +1,37 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; + +namespace osu.Game.Overlays.Settings +{ + public class SettingsSidebar : ExpandingButtonContainer + { + public const float DEFAULT_WIDTH = 70; + public const int EXPANDED_WIDTH = 200; + + public SettingsSidebar() + : base(DEFAULT_WIDTH, EXPANDED_WIDTH) + { + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + AddInternal(new Box + { + Colour = colourProvider.Background5, + RelativeSizeAxes = Axes.Both, + Depth = float.MaxValue + }); + } + } + + public enum ExpandedState + { + Contracted, + Expanded, + } +} diff --git a/osu.Game/Overlays/Settings/SidebarIconButton.cs b/osu.Game/Overlays/Settings/SidebarIconButton.cs index fd57996b1b..6f3d3d5d52 100644 --- a/osu.Game/Overlays/Settings/SidebarIconButton.cs +++ b/osu.Game/Overlays/Settings/SidebarIconButton.cs @@ -62,14 +62,14 @@ namespace osu.Game.Overlays.Settings { textIconContent = new Container { - Width = Sidebar.DEFAULT_WIDTH, + Width = SettingsSidebar.DEFAULT_WIDTH, RelativeSizeAxes = Axes.Y, Colour = OsuColour.Gray(0.6f), Children = new Drawable[] { headerText = new OsuSpriteText { - Position = new Vector2(Sidebar.DEFAULT_WIDTH + 10, 0), + Position = new Vector2(SettingsSidebar.DEFAULT_WIDTH + 10, 0), Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, }, diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index 0ceb7fc50d..ba7118cffe 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -27,7 +27,7 @@ namespace osu.Game.Overlays public const float TRANSITION_LENGTH = 600; - private const float sidebar_width = Sidebar.DEFAULT_WIDTH; + private const float sidebar_width = SettingsSidebar.DEFAULT_WIDTH; /// /// The width of the settings panel content, excluding the sidebar. @@ -43,7 +43,7 @@ namespace osu.Game.Overlays protected override Container Content => ContentContainer; - protected Sidebar Sidebar; + protected SettingsSidebar Sidebar; private SidebarIconButton selectedSidebarButton; public SettingsSectionsContainer SectionsContainer { get; private set; } @@ -129,7 +129,7 @@ namespace osu.Game.Overlays if (showSidebar) { - AddInternal(Sidebar = new Sidebar { Width = sidebar_width }); + AddInternal(Sidebar = new SettingsSidebar { Width = sidebar_width }); } CreateSections()?.ForEach(AddSection); @@ -244,7 +244,7 @@ namespace osu.Game.Overlays if (selectedSidebarButton != null) selectedSidebarButton.Selected = false; - selectedSidebarButton = Sidebar.Children.FirstOrDefault(b => b.Section == section.NewValue); + selectedSidebarButton = Sidebar.Children.OfType().FirstOrDefault(b => b.Section == section.NewValue); if (selectedSidebarButton != null) selectedSidebarButton.Selected = true; diff --git a/osu.Game/Overlays/SettingsSubPanel.cs b/osu.Game/Overlays/SettingsSubPanel.cs index a65d792a9f..da806c09d3 100644 --- a/osu.Game/Overlays/SettingsSubPanel.cs +++ b/osu.Game/Overlays/SettingsSubPanel.cs @@ -39,7 +39,7 @@ namespace osu.Game.Overlays [BackgroundDependencyLoader] private void load() { - Size = new Vector2(Sidebar.DEFAULT_WIDTH); + Size = new Vector2(SettingsSidebar.DEFAULT_WIDTH); AddRange(new Drawable[] { From 5baaf356aac038145197dc8f5524a83ae5ad50d4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Jan 2022 21:05:00 +0900 Subject: [PATCH 49/98] Split out `SettingsToolboxGroup` from `PlayerSettingsGroup` --- osu.Game/Overlays/SettingsToolboxGroup.cs | 165 ++++++++++++++++++ .../PlayerSettings/PlayerSettingsGroup.cs | 155 +--------------- 2 files changed, 172 insertions(+), 148 deletions(-) create mode 100644 osu.Game/Overlays/SettingsToolboxGroup.cs diff --git a/osu.Game/Overlays/SettingsToolboxGroup.cs b/osu.Game/Overlays/SettingsToolboxGroup.cs new file mode 100644 index 0000000000..c154284fb9 --- /dev/null +++ b/osu.Game/Overlays/SettingsToolboxGroup.cs @@ -0,0 +1,165 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Events; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays +{ + public abstract class SettingsToolboxGroup : Container + { + private const float transition_duration = 250; + private const int container_width = 270; + private const int border_thickness = 2; + private const int header_height = 30; + private const int corner_radius = 5; + + private readonly FillFlowContainer content; + private readonly IconButton button; + + private bool expanded = true; + + public bool Expanded + { + get => expanded; + set + { + if (expanded == value) return; + + expanded = value; + + content.ClearTransforms(); + + if (expanded) + content.AutoSizeAxes = Axes.Y; + else + { + content.AutoSizeAxes = Axes.None; + content.ResizeHeightTo(0, transition_duration, Easing.OutQuint); + } + + updateExpanded(); + } + } + + private Color4 expandedColour; + + /// + /// Create a new instance. + /// + /// The title to be displayed in the header of this group. + protected SettingsToolboxGroup(string title) + { + AutoSizeAxes = Axes.Y; + Width = container_width; + Masking = true; + CornerRadius = corner_radius; + BorderColour = Color4.Black; + BorderThickness = border_thickness; + + InternalChildren = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.5f, + }, + new FillFlowContainer + { + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new Container + { + Name = @"Header", + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + Height = header_height, + Children = new Drawable[] + { + new OsuSpriteText + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Text = title.ToUpperInvariant(), + Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 17), + Margin = new MarginPadding { Left = 10 }, + }, + button = new IconButton + { + Origin = Anchor.Centre, + Anchor = Anchor.CentreRight, + Position = new Vector2(-15, 0), + Icon = FontAwesome.Solid.Bars, + Scale = new Vector2(0.75f), + Action = () => Expanded = !Expanded, + }, + } + }, + content = new FillFlowContainer + { + Name = @"Content", + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.X, + AutoSizeDuration = transition_duration, + AutoSizeEasing = Easing.OutQuint, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding(15), + Spacing = new Vector2(0, 15), + } + } + }, + }; + } + + private const float fade_duration = 800; + private const float inactive_alpha = 0.5f; + + protected override void LoadComplete() + { + base.LoadComplete(); + this.Delay(600).FadeTo(inactive_alpha, fade_duration, Easing.OutQuint); + } + + protected override bool OnHover(HoverEvent e) + { + this.FadeIn(fade_duration, Easing.OutQuint); + return false; + } + + protected override void OnHoverLost(HoverLostEvent e) + { + this.FadeTo(inactive_alpha, fade_duration, Easing.OutQuint); + base.OnHoverLost(e); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + expandedColour = colours.Yellow; + + updateExpanded(); + } + + private void updateExpanded() => button.FadeColour(expanded ? expandedColour : Color4.White, 200, Easing.InOutQuint); + + protected override Container Content => content; + + protected override bool OnMouseDown(MouseDownEvent e) => true; + } +} diff --git a/osu.Game/Screens/Play/PlayerSettings/PlayerSettingsGroup.cs b/osu.Game/Screens/Play/PlayerSettings/PlayerSettingsGroup.cs index 7928d41e3b..0bbe6902f4 100644 --- a/osu.Game/Screens/Play/PlayerSettings/PlayerSettingsGroup.cs +++ b/osu.Game/Screens/Play/PlayerSettings/PlayerSettingsGroup.cs @@ -1,165 +1,24 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osuTK; -using osuTK.Graphics; +using osu.Game.Overlays; namespace osu.Game.Screens.Play.PlayerSettings { - public abstract class PlayerSettingsGroup : Container + public class PlayerSettingsGroup : SettingsToolboxGroup { - private const float transition_duration = 250; - private const int container_width = 270; - private const int border_thickness = 2; - private const int header_height = 30; - private const int corner_radius = 5; - - private readonly FillFlowContainer content; - private readonly IconButton button; - - private bool expanded = true; - - public bool Expanded + public PlayerSettingsGroup(string title) + : base(title) { - get => expanded; - set - { - if (expanded == value) return; - - expanded = value; - - content.ClearTransforms(); - - if (expanded) - content.AutoSizeAxes = Axes.Y; - else - { - content.AutoSizeAxes = Axes.None; - content.ResizeHeightTo(0, transition_duration, Easing.OutQuint); - } - - updateExpanded(); - } - } - - private Color4 expandedColour; - - /// - /// Create a new instance. - /// - /// The title to be displayed in the header of this group. - protected PlayerSettingsGroup(string title) - { - AutoSizeAxes = Axes.Y; - Width = container_width; - Masking = true; - CornerRadius = corner_radius; - BorderColour = Color4.Black; - BorderThickness = border_thickness; - - InternalChildren = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.5f, - }, - new FillFlowContainer - { - Direction = FillDirection.Vertical, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - new Container - { - Name = @"Header", - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - Height = header_height, - Children = new Drawable[] - { - new OsuSpriteText - { - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Text = title.ToUpperInvariant(), - Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 17), - Margin = new MarginPadding { Left = 10 }, - }, - button = new IconButton - { - Origin = Anchor.Centre, - Anchor = Anchor.CentreRight, - Position = new Vector2(-15, 0), - Icon = FontAwesome.Solid.Bars, - Scale = new Vector2(0.75f), - Action = () => Expanded = !Expanded, - }, - } - }, - content = new FillFlowContainer - { - Name = @"Content", - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Direction = FillDirection.Vertical, - RelativeSizeAxes = Axes.X, - AutoSizeDuration = transition_duration, - AutoSizeEasing = Easing.OutQuint, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding(15), - Spacing = new Vector2(0, 15), - } - } - }, - }; - } - - private const float fade_duration = 800; - private const float inactive_alpha = 0.5f; - - protected override void LoadComplete() - { - base.LoadComplete(); - this.Delay(600).FadeTo(inactive_alpha, fade_duration, Easing.OutQuint); } protected override bool OnHover(HoverEvent e) { - this.FadeIn(fade_duration, Easing.OutQuint); + base.OnHover(e); + + // Importantly, return true to correctly take focus away from PlayerLoader. return true; } - - protected override void OnHoverLost(HoverLostEvent e) - { - this.FadeTo(inactive_alpha, fade_duration, Easing.OutQuint); - base.OnHoverLost(e); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - expandedColour = colours.Yellow; - - updateExpanded(); - } - - private void updateExpanded() => button.FadeColour(expanded ? expandedColour : Color4.White, 200, Easing.InOutQuint); - - protected override Container Content => content; - - protected override bool OnMouseDown(MouseDownEvent e) => true; } } From 66613cbaa4551c055a5e756787cb36ab0fe9072c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 6 Jan 2022 13:09:25 +0100 Subject: [PATCH 50/98] Wait for async continuation in score submission test The previous assert step was optimistically assuming that the async continuation that writes the value of `FakeImportingPlayer.ImportedScore` always completes before it, but that's not necessarily true even if the continuation is instant (it is still subject to things like task scheduling and TPL thread pool limits). To ensure no spurious failures, swap out the assert step for an until step instead. --- .../Visual/Gameplay/TestScenePlayerScoreSubmission.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs index 5ee1f9dd8f..cf5aadde6d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs @@ -226,7 +226,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("exit", () => Player.Exit()); AddStep("allow import to proceed", () => Player.AllowImportCompletion.Release(1)); - AddAssert("ensure submission", () => Player.SubmittedScore != null && Player.ImportedScore != null); + AddUntilStep("ensure submission", () => Player.SubmittedScore != null && Player.ImportedScore != null); } [Test] From cea9cab4dcca4da45b98cb868e89a65e391869c9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Jan 2022 21:10:45 +0900 Subject: [PATCH 51/98] Use `ExpandingButtonContainer` in editor composer --- osu.Game/Overlays/SettingsToolboxGroup.cs | 4 ++- ...{ToolboxGroup.cs => EditorToolboxGroup.cs} | 6 ++--- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 26 ++++++++++++------- .../Rulesets/Edit/ScrollingToolboxGroup.cs | 2 +- 4 files changed, 23 insertions(+), 15 deletions(-) rename osu.Game/Rulesets/Edit/{ToolboxGroup.cs => EditorToolboxGroup.cs} (71%) diff --git a/osu.Game/Overlays/SettingsToolboxGroup.cs b/osu.Game/Overlays/SettingsToolboxGroup.cs index c154284fb9..02b5bdc1e1 100644 --- a/osu.Game/Overlays/SettingsToolboxGroup.cs +++ b/osu.Game/Overlays/SettingsToolboxGroup.cs @@ -94,9 +94,11 @@ namespace osu.Game.Overlays { Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, + RelativeSizeAxes = Axes.X, + Truncate = true, Text = title.ToUpperInvariant(), Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 17), - Margin = new MarginPadding { Left = 10 }, + Padding = new MarginPadding { Left = 10, Right = 30 }, }, button = new IconButton { diff --git a/osu.Game/Rulesets/Edit/ToolboxGroup.cs b/osu.Game/Rulesets/Edit/EditorToolboxGroup.cs similarity index 71% rename from osu.Game/Rulesets/Edit/ToolboxGroup.cs rename to osu.Game/Rulesets/Edit/EditorToolboxGroup.cs index 22b2b05657..bde426f56a 100644 --- a/osu.Game/Rulesets/Edit/ToolboxGroup.cs +++ b/osu.Game/Rulesets/Edit/EditorToolboxGroup.cs @@ -2,13 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; -using osu.Game.Screens.Play.PlayerSettings; +using osu.Game.Overlays; namespace osu.Game.Rulesets.Edit { - public class ToolboxGroup : PlayerSettingsGroup + public class EditorToolboxGroup : SettingsToolboxGroup { - public ToolboxGroup(string title) + public EditorToolboxGroup(string title) : base(title) { RelativeSizeAxes = Axes.X; diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index cbc2415603..92ea2db338 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -13,6 +13,7 @@ using osu.Framework.Input; using osu.Framework.Input.Events; using osu.Framework.Logging; using osu.Game.Beatmaps; +using osu.Game.Overlays; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Mods; @@ -98,8 +99,6 @@ namespace osu.Game.Rulesets.Edit dependencies.CacheAs(Playfield); - const float toolbar_width = 200; - InternalChildren = new Drawable[] { new Container @@ -116,20 +115,15 @@ namespace osu.Game.Rulesets.Edit .WithChild(BlueprintContainer = CreateBlueprintContainer()) } }, - new FillFlowContainer + new LeftToolboxFlow { - Name = "Sidebar", - RelativeSizeAxes = Axes.Y, - Width = toolbar_width, - Padding = new MarginPadding { Right = 10 }, - Spacing = new Vector2(10), Children = new Drawable[] { - new ToolboxGroup("toolbox (1-9)") + new EditorToolboxGroup("toolbox (1-9)") { Child = toolboxCollection = new EditorRadioButtonCollection { RelativeSizeAxes = Axes.X } }, - new ToolboxGroup("toggles (Q~P)") + new EditorToolboxGroup("toggles (Q~P)") { Child = togglesCollection = new FillFlowContainer { @@ -426,6 +420,18 @@ namespace osu.Game.Rulesets.Edit } #endregion + + private class LeftToolboxFlow : ExpandingButtonContainer + { + public LeftToolboxFlow() + : base(80, 200) + { + RelativeSizeAxes = Axes.Y; + Padding = new MarginPadding { Right = 10 }; + + FillFlow.Spacing = new Vector2(10); + } + } } /// diff --git a/osu.Game/Rulesets/Edit/ScrollingToolboxGroup.cs b/osu.Game/Rulesets/Edit/ScrollingToolboxGroup.cs index a54f574bff..9998a997b3 100644 --- a/osu.Game/Rulesets/Edit/ScrollingToolboxGroup.cs +++ b/osu.Game/Rulesets/Edit/ScrollingToolboxGroup.cs @@ -7,7 +7,7 @@ using osu.Game.Graphics.Containers; namespace osu.Game.Rulesets.Edit { - public class ScrollingToolboxGroup : ToolboxGroup + public class ScrollingToolboxGroup : EditorToolboxGroup { protected readonly OsuScrollContainer Scroll; From 3ea7588a91261fce0969d56f4032ea217c34f925 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Jan 2022 15:54:10 +0900 Subject: [PATCH 52/98] Update continuation usages to use `GetCompletedResult` --- osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 8 +++++--- osu.Game/Database/OnlineLookupCache.cs | 2 +- osu.Game/Online/Chat/ChannelManager.cs | 6 ++++-- osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs | 2 +- osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs | 2 +- .../Overlays/Settings/Sections/General/UpdateSettings.cs | 2 +- osu.Game/Scoring/ScoreManager.cs | 2 +- .../Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs | 2 +- osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs | 2 +- .../Ranking/Expanded/Statistics/PerformanceStatistic.cs | 2 +- osu.Game/Screens/Ranking/ScorePanelList.cs | 2 +- osu.Game/Screens/Select/Details/AdvancedStats.cs | 4 ++-- .../Screens/Select/Leaderboards/BeatmapLeaderboard.cs | 4 ++-- osu.Game/Screens/Spectate/SpectatorScreen.cs | 2 +- osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs | 2 +- 15 files changed, 24 insertions(+), 20 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index 0e4dab2b96..45dadc4e3f 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -262,7 +262,7 @@ namespace osu.Game.Beatmaps // GetDifficultyAsync will fall back to existing data from IBeatmapInfo if not locally available // (contrary to GetAsync) GetDifficultyAsync(bindable.BeatmapInfo, rulesetInfo, mods, cancellationToken) - .ContinueWith(t => + .ContinueWith(task => { // We're on a threadpool thread, but we should exit back to the update thread so consumers can safely handle value-changed events. Schedule(() => @@ -270,8 +270,10 @@ namespace osu.Game.Beatmaps if (cancellationToken.IsCancellationRequested) return; - if (t.WaitSafelyForResult() is StarDifficulty sd) - bindable.Value = sd; + var starDifficulty = task.GetCompletedResult(); + + if (starDifficulty != null) + bindable.Value = starDifficulty.Value; }); }, cancellationToken); } diff --git a/osu.Game/Database/OnlineLookupCache.cs b/osu.Game/Database/OnlineLookupCache.cs index b392336bb4..f5847428ee 100644 --- a/osu.Game/Database/OnlineLookupCache.cs +++ b/osu.Game/Database/OnlineLookupCache.cs @@ -59,7 +59,7 @@ namespace osu.Game.Database if (!task.IsCompletedSuccessfully) return null; - return task.WaitSafelyForResult(); + return task.GetCompletedResult(); }, token)); } diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 3b27dd5c5d..52d5fa1642 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -536,8 +536,10 @@ namespace osu.Game.Online.Chat // Try to get user in order to open PM chat users.GetUserAsync((int)lastClosedChannel.Id).ContinueWith(task => { - if (task.WaitSafelyForResult() is APIUser u) - Schedule(() => CurrentChannel.Value = JoinChannel(new Channel(u))); + var user = task.GetCompletedResult(); + + if (user != null) + Schedule(() => CurrentChannel.Value = JoinChannel(new Channel(user))); }); } diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index adb1adeed4..0f25be2d3c 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -85,7 +85,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores if (loadCancellationSource.IsCancellationRequested) return; - var scores = task.WaitSafelyForResult(); + var scores = task.GetCompletedResult(); var topScore = scores.First(); diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs index b7d236c263..73b199f159 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -64,7 +64,7 @@ namespace osu.Game.Overlays.Dashboard { users.GetUserAsync(id).ContinueWith(task => { - var user = task.WaitSafelyForResult(); + var user = task.GetCompletedResult(); if (user == null) return; diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs index 49eb80d993..8dbae420ac 100644 --- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs @@ -48,7 +48,7 @@ namespace osu.Game.Overlays.Settings.Sections.General checkForUpdatesButton.Enabled.Value = false; Task.Run(updateManager.CheckForUpdateAsync).ContinueWith(task => Schedule(() => { - if (!task.WaitSafelyForResult()) + if (!task.GetCompletedResult()) { notifications?.Post(new SimpleNotification { diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 9715cd64a7..b4edb23689 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -113,7 +113,7 @@ namespace osu.Game.Scoring public void GetTotalScore([NotNull] ScoreInfo score, [NotNull] Action callback, ScoringMode mode = ScoringMode.Standardised, CancellationToken cancellationToken = default) { GetTotalScoreAsync(score, mode, cancellationToken) - .ContinueWith(task => scheduler.Add(() => callback(task.WaitSafelyForResult())), TaskContinuationOptions.OnlyOnRanToCompletion); + .ContinueWith(task => scheduler.Add(() => callback(task.GetCompletedResult())), TaskContinuationOptions.OnlyOnRanToCompletion); } /// diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 6f2a76b3cb..c384d97229 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -79,7 +79,7 @@ namespace osu.Game.Screens.Play.HUD userLookupCache.GetUsersAsync(playingUsers.Select(u => u.UserID).ToArray()).ContinueWith(task => Schedule(() => { - var users = task.WaitSafelyForResult(); + var users = task.GetCompletedResult(); foreach (var user in users) { diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index f543cea15c..897678640f 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -76,7 +76,7 @@ namespace osu.Game.Screens.Play.HUD difficultyCache.GetTimedDifficultyAttributesAsync(gameplayWorkingBeatmap, gameplayState.Ruleset, clonedMods, loadCancellationSource.Token) .ContinueWith(task => Schedule(() => { - timedAttributes = task.WaitSafelyForResult(); + timedAttributes = task.GetCompletedResult(); IsValid = true; diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs index 90ecd3642b..1d00043244 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs @@ -38,7 +38,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics else { performanceCache.CalculatePerformanceAsync(score, cancellationTokenSource.Token) - .ContinueWith(t => Schedule(() => setPerformanceValue(t.WaitSafelyForResult())), cancellationTokenSource.Token); + .ContinueWith(t => Schedule(() => setPerformanceValue(t.GetCompletedResult())), cancellationTokenSource.Token); } } diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index b29f2302d5..5ae6b9d417 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -154,7 +154,7 @@ namespace osu.Game.Screens.Ranking scoreManager.GetTotalScoreAsync(score) .ContinueWith(task => Schedule(() => { - flow.SetLayoutPosition(trackingContainer, task.WaitSafelyForResult()); + flow.SetLayoutPosition(trackingContainer, task.GetCompletedResult()); trackingContainer.Show(); diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index e7764b23be..dacc8c11e1 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -153,8 +153,8 @@ namespace osu.Game.Screens.Select.Details Task.WhenAll(normalStarDifficultyTask, moddedStarDifficultyTask).ContinueWith(_ => Schedule(() => { - var normalDifficulty = normalStarDifficultyTask.WaitSafelyForResult(); - var moddeDifficulty = moddedStarDifficultyTask.WaitSafelyForResult(); + var normalDifficulty = normalStarDifficultyTask.GetCompletedResult(); + var moddeDifficulty = moddedStarDifficultyTask.GetCompletedResult(); if (normalDifficulty == null || moddeDifficulty == null) return; diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 6943f2a3b6..fbfca130ba 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -143,7 +143,7 @@ namespace osu.Game.Screens.Select.Leaderboards } scoreManager.OrderByTotalScoreAsync(scores.ToArray(), cancellationToken) - .ContinueWith(ordered => scoresCallback?.Invoke(ordered.WaitSafelyForResult()), TaskContinuationOptions.OnlyOnRanToCompletion); + .ContinueWith(task => scoresCallback?.Invoke(task.GetCompletedResult()), TaskContinuationOptions.OnlyOnRanToCompletion); return null; } @@ -184,7 +184,7 @@ namespace osu.Game.Screens.Select.Leaderboards if (cancellationToken.IsCancellationRequested) return; - scoresCallback?.Invoke(task.WaitSafelyForResult()); + scoresCallback?.Invoke(task.GetCompletedResult()); TopScore = r.UserScore?.CreateScoreInfo(rulesets, fetchBeatmapInfo); }), TaskContinuationOptions.OnlyOnRanToCompletion); }; diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs index f361b0cee4..44a31d89ed 100644 --- a/osu.Game/Screens/Spectate/SpectatorScreen.cs +++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs @@ -60,7 +60,7 @@ namespace osu.Game.Screens.Spectate userLookupCache.GetUsersAsync(users.ToArray()).ContinueWith(task => Schedule(() => { - var foundUsers = task.WaitSafelyForResult(); + var foundUsers = task.GetCompletedResult(); foreach (var u in foundUsers) { diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index 1b3e49de37..589857b458 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -148,7 +148,7 @@ namespace osu.Game.Tests.Beatmaps if (!conversionTask.Wait(10000)) Assert.Fail("Conversion timed out"); - return conversionTask.WaitSafelyForResult(); + return conversionTask.GetCompletedResult(); } protected virtual void OnConversionGenerated(HitObject original, IEnumerable result, IBeatmapConverter beatmapConverter) From 00177a3ae195130dcabe9657c76c78a4b809f481 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Jan 2022 22:54:43 +0900 Subject: [PATCH 53/98] Update usages to new naming --- CodeAnalysis/BannedSymbols.txt | 2 +- osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs | 4 ++-- .../Beatmaps/TestSceneBeatmapDifficultyCache.cs | 2 +- osu.Game.Tests/NonVisual/TaskChainTest.cs | 10 +++++----- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 4 ++-- osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs | 2 +- osu.Game.Tests/Skins/TestSceneSkinResources.cs | 2 +- .../Visual/Editing/TestSceneDifficultySwitching.cs | 2 +- .../Visual/Editing/TestSceneEditorTestGameplay.cs | 2 +- .../Visual/Gameplay/TestSceneReplayDownloadButton.cs | 2 +- osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs | 2 +- .../Visual/Menus/TestSceneMusicActionHandling.cs | 2 +- .../Multiplayer/TestSceneMultiSpectatorScreen.cs | 2 +- .../TestSceneMultiplayerGameplayLeaderboard.cs | 4 ++-- .../TestSceneMultiplayerGameplayLeaderboardTeams.cs | 2 +- .../Visual/Navigation/TestScenePresentBeatmap.cs | 2 +- .../Visual/Navigation/TestScenePresentScore.cs | 4 ++-- .../Visual/Playlists/TestScenePlaylistsRoomCreation.cs | 2 +- .../SongSelect/TestSceneBeatmapRecommendations.cs | 2 +- .../Visual/SongSelect/TestScenePlaySongSelect.cs | 2 +- .../Visual/UserInterface/TestSceneDeleteLocalScore.cs | 4 ++-- .../TestSceneUpdateableBeatmapBackgroundSprite.cs | 2 +- osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 2 +- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- osu.Game/Database/OnlineLookupCache.cs | 2 +- osu.Game/Online/Chat/ChannelManager.cs | 2 +- osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs | 2 +- osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs | 2 +- .../Settings/Sections/General/UpdateSettings.cs | 2 +- osu.Game/Scoring/ScoreManager.cs | 2 +- osu.Game/Screens/Menu/IntroScreen.cs | 2 +- .../Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs | 2 +- osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs | 2 +- osu.Game/Screens/Play/Player.cs | 2 +- .../Ranking/Expanded/ExpandedPanelMiddleContent.cs | 2 +- .../Expanded/Statistics/PerformanceStatistic.cs | 2 +- osu.Game/Screens/Select/Details/AdvancedStats.cs | 4 ++-- .../Screens/Select/Leaderboards/BeatmapLeaderboard.cs | 4 ++-- osu.Game/Screens/Spectate/SpectatorScreen.cs | 2 +- osu.Game/Skinning/SkinManager.cs | 2 +- osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs | 2 +- 42 files changed, 53 insertions(+), 53 deletions(-) diff --git a/CodeAnalysis/BannedSymbols.txt b/CodeAnalysis/BannedSymbols.txt index 4428656301..e96ad48325 100644 --- a/CodeAnalysis/BannedSymbols.txt +++ b/CodeAnalysis/BannedSymbols.txt @@ -14,4 +14,4 @@ M:Realms.IRealmCollection`1.SubscribeForNotifications`1(Realms.NotificationCallb M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Linq.IQueryable{``0},Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IQueryable,NotificationCallbackDelegate) instead. M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Collections.Generic.IList{``0},Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IList,NotificationCallbackDelegate) instead. M:System.Threading.Tasks.Task.Wait();Don't use Task.Wait. Use Task.WaitSafely() to ensure we avoid deadlocks. -P:System.Threading.Tasks.Task`1.Result;Don't use Task.Result. Use Task.WaitSafelyForResult() to ensure we avoid deadlocks. +P:System.Threading.Tasks.Task`1.Result;Don't use Task.Result. Use Task.GetResultSafely() to ensure we avoid deadlocks. diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 2a2cc2e086..c02141bf9f 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -984,7 +984,7 @@ namespace osu.Game.Tests.Beatmaps.IO var manager = osu.Dependencies.Get(); - var importedSet = manager.Import(new ImportTask(temp)).WaitSafelyForResult(); + var importedSet = manager.Import(new ImportTask(temp)).GetResultSafely(); ensureLoaded(osu).WaitSafely(); @@ -999,7 +999,7 @@ namespace osu.Game.Tests.Beatmaps.IO var manager = osu.Dependencies.Get(); - var importedSet = manager.Import(new ImportTask(temp)).WaitSafelyForResult(); + var importedSet = manager.Import(new ImportTask(temp)).GetResultSafely(); ensureLoaded(osu).WaitSafely(); diff --git a/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs b/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs index a97b372db0..26ab8808b9 100644 --- a/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs +++ b/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs @@ -33,7 +33,7 @@ namespace osu.Game.Tests.Beatmaps [BackgroundDependencyLoader] private void load(OsuGameBase osu) { - importedSet = ImportBeatmapTest.LoadQuickOszIntoOsu(osu).WaitSafelyForResult(); + importedSet = ImportBeatmapTest.LoadQuickOszIntoOsu(osu).GetResultSafely(); } [SetUpSteps] diff --git a/osu.Game.Tests/NonVisual/TaskChainTest.cs b/osu.Game.Tests/NonVisual/TaskChainTest.cs index db3cecf8ea..3678279035 100644 --- a/osu.Game.Tests/NonVisual/TaskChainTest.cs +++ b/osu.Game.Tests/NonVisual/TaskChainTest.cs @@ -43,9 +43,9 @@ namespace osu.Game.Tests.NonVisual await Task.WhenAll(task1.task, task2.task, task3.task); - Assert.That(task1.task.WaitSafelyForResult(), Is.EqualTo(1)); - Assert.That(task2.task.WaitSafelyForResult(), Is.EqualTo(2)); - Assert.That(task3.task.WaitSafelyForResult(), Is.EqualTo(3)); + Assert.That(task1.task.GetResultSafely(), Is.EqualTo(1)); + Assert.That(task2.task.GetResultSafely(), Is.EqualTo(2)); + Assert.That(task3.task.GetResultSafely(), Is.EqualTo(3)); } [Test] @@ -69,9 +69,9 @@ namespace osu.Game.Tests.NonVisual // Wait on both tasks. await Task.WhenAll(task1.task, task3.task); - Assert.That(task1.task.WaitSafelyForResult(), Is.EqualTo(1)); + Assert.That(task1.task.GetResultSafely(), Is.EqualTo(1)); Assert.That(task2.task.IsCompleted, Is.False); - Assert.That(task3.task.WaitSafelyForResult(), Is.EqualTo(2)); + Assert.That(task3.task.GetResultSafely(), Is.EqualTo(2)); } [Test] diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs index 55a50fb825..3f063264e0 100644 --- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -188,7 +188,7 @@ namespace osu.Game.Tests.Skins.IO var imported = skinManager.Import(new ImportTask(exportStream, "exported.osk")); - imported.WaitSafelyForResult().PerformRead(s => + imported.GetResultSafely().PerformRead(s => { Assert.IsFalse(s.Protected); Assert.AreNotEqual(originalSkinId, s.ID); @@ -223,7 +223,7 @@ namespace osu.Game.Tests.Skins.IO var imported = skinManager.Import(new ImportTask(exportStream, "exported.osk")); - imported.WaitSafelyForResult().PerformRead(s => + imported.GetResultSafely().PerformRead(s => { Assert.IsFalse(s.Protected); Assert.AreNotEqual(originalSkinId, s.ID); diff --git a/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs b/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs index a4c2ec3b34..c20ab84a68 100644 --- a/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs +++ b/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tests.Skins [BackgroundDependencyLoader] private void load() { - var imported = beatmaps.Import(new ZipArchiveReader(TestResources.OpenResource("Archives/ogg-beatmap.osz"))).WaitSafelyForResult(); + var imported = beatmaps.Import(new ZipArchiveReader(TestResources.OpenResource("Archives/ogg-beatmap.osz"))).GetResultSafely(); beatmap = beatmaps.GetWorkingBeatmap(imported.Value.Beatmaps[0]); beatmap.LoadTrack(); } diff --git a/osu.Game.Tests/Skins/TestSceneSkinResources.cs b/osu.Game.Tests/Skins/TestSceneSkinResources.cs index a10e2436d6..0271198049 100644 --- a/osu.Game.Tests/Skins/TestSceneSkinResources.cs +++ b/osu.Game.Tests/Skins/TestSceneSkinResources.cs @@ -24,7 +24,7 @@ namespace osu.Game.Tests.Skins [BackgroundDependencyLoader] private void load() { - var imported = skins.Import(new ZipArchiveReader(TestResources.OpenResource("Archives/ogg-skin.osk"))).WaitSafelyForResult(); + var imported = skins.Import(new ZipArchiveReader(TestResources.OpenResource("Archives/ogg-skin.osk"))).GetResultSafely(); skin = imported.PerformRead(skinInfo => skins.GetSkin(skinInfo)); } diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs b/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs index 5dd5bad0d2..516305079b 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs @@ -33,7 +33,7 @@ namespace osu.Game.Tests.Visual.Editing public override void SetUpSteps() { - AddStep("import test beatmap", () => importedBeatmapSet = ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).WaitSafelyForResult()); + AddStep("import test beatmap", () => importedBeatmapSet = ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).GetResultSafely()); base.SetUpSteps(); } diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorTestGameplay.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorTestGameplay.cs index fcbae44c76..6d48ef3ba7 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorTestGameplay.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorTestGameplay.cs @@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.Editing public override void SetUpSteps() { - AddStep("import test beatmap", () => importedBeatmapSet = ImportBeatmapTest.LoadOszIntoOsu(game).WaitSafelyForResult()); + AddStep("import test beatmap", () => importedBeatmapSet = ImportBeatmapTest.LoadOszIntoOsu(game).GetResultSafely()); base.SetUpSteps(); } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs index 7f5ac5cf48..3168c4b94e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs @@ -136,7 +136,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("state is not downloaded", () => downloadButton.State.Value == DownloadState.NotDownloaded); - AddStep("import score", () => imported = scoreManager.Import(getScoreInfo(true)).WaitSafelyForResult()); + AddStep("import score", () => imported = scoreManager.Import(getScoreInfo(true)).GetResultSafely()); AddUntilStep("state is available", () => downloadButton.State.Value == DownloadState.LocallyAvailable); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index 90065e3b51..242eca0bbc 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -61,7 +61,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("import beatmap", () => { - importedBeatmap = ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).WaitSafelyForResult(); + importedBeatmap = ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).GetResultSafely(); importedBeatmapId = importedBeatmap.Beatmaps.First(b => b.RulesetID == 0).OnlineID ?? -1; }); } diff --git a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs index c82bde3444..ee9363fa12 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs @@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Menus AddStep("import beatmap with track", () => { - var setWithTrack = Game.BeatmapManager.Import(new ImportTask(TestResources.GetTestBeatmapForImport())).WaitSafelyForResult(); + var setWithTrack = Game.BeatmapManager.Import(new ImportTask(TestResources.GetTestBeatmapForImport())).GetResultSafely(); Beatmap.Value = Game.BeatmapManager.GetWorkingBeatmap(setWithTrack.Value.Beatmaps.First()); }); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index 3f24da2c9e..61058bc87a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -47,7 +47,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [BackgroundDependencyLoader] private void load() { - importedSet = ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).WaitSafelyForResult(); + importedSet = ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).GetResultSafely(); importedBeatmap = importedSet.Beatmaps.First(b => b.RulesetID == 0); importedBeatmapId = importedBeatmap.OnlineID ?? -1; } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs index 6b40a4fc9a..07a8ef66e1 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs @@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { base.SetUpSteps(); - AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = LookupCache.GetUserAsync(1).WaitSafelyForResult()); + AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = LookupCache.GetUserAsync(1).GetResultSafely()); AddStep("create leaderboard", () => { @@ -91,7 +91,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public void TestUserQuit() { foreach (int user in users) - AddStep($"mark user {user} quit", () => Client.RemoveUser(LookupCache.GetUserAsync(user).WaitSafelyForResult().AsNonNull())); + AddStep($"mark user {user} quit", () => Client.RemoveUser(LookupCache.GetUserAsync(user).GetResultSafely().AsNonNull())); } [Test] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs index f5c079573d..1237a21e94 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs @@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { base.SetUpSteps(); - AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = LookupCache.GetUserAsync(1).WaitSafelyForResult()); + AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = LookupCache.GetUserAsync(1).GetResultSafely()); AddStep("create leaderboard", () => { diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs b/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs index 9d495f7d05..6420e7b849 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs @@ -127,7 +127,7 @@ namespace osu.Game.Tests.Visual.Navigation Ruleset = ruleset ?? new OsuRuleset().RulesetInfo }, } - }).WaitSafelyForResult().Value; + }).GetResultSafely().Value; }); AddAssert($"import {i} succeeded", () => imported != null); diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs index 16c416fedc..5dc1808c12 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs @@ -58,7 +58,7 @@ namespace osu.Game.Tests.Visual.Navigation Ruleset = new OsuRuleset().RulesetInfo }, } - }).WaitSafelyForResult().Value; + }).GetResultSafely().Value; }); } @@ -132,7 +132,7 @@ namespace osu.Game.Tests.Visual.Navigation OnlineID = i, BeatmapInfo = beatmap.Beatmaps.First(), Ruleset = ruleset ?? new OsuRuleset().RulesetInfo - }).WaitSafelyForResult().Value; + }).GetResultSafely().Value; }); AddAssert($"import {i} succeeded", () => imported != null); diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs index 0c5985ad67..e59884f4f4 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs @@ -202,7 +202,7 @@ namespace osu.Game.Tests.Visual.Playlists }); } - private void importBeatmap() => AddStep("import beatmap", () => importedBeatmap = manager.Import(CreateBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).WaitSafelyForResult()); + private void importBeatmap() => AddStep("import beatmap", () => importedBeatmap = manager.Import(CreateBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).GetResultSafely()); private class TestPlaylistsRoomSubScreen : PlaylistsRoomSubScreen { diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs index 071e3225f0..08b5802713 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs @@ -182,7 +182,7 @@ namespace osu.Game.Tests.Visual.SongSelect beatmap.DifficultyName = $"SR{i + 1}"; } - return Game.BeatmapManager.Import(beatmapSet).WaitSafelyForResult().Value; + return Game.BeatmapManager.Import(beatmapSet).GetResultSafely().Value; } private bool ensureAllBeatmapSetsImported(IEnumerable beatmapSets) => beatmapSets.All(set => set != null); diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index ea667d02f3..37f110e727 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -760,7 +760,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("import huge difficulty count map", () => { var usableRulesets = rulesets.AvailableRulesets.Where(r => r.OnlineID != 2).ToArray(); - imported = manager.Import(TestResources.CreateTestBeatmapSetInfo(50, usableRulesets)).WaitSafelyForResult().Value; + imported = manager.Import(TestResources.CreateTestBeatmapSetInfo(50, usableRulesets)).GetResultSafely().Value; }); AddStep("select the first beatmap of import", () => Beatmap.Value = manager.GetWorkingBeatmap(imported.Beatmaps.First())); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index bca396b46b..a436fc0bfa 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -86,7 +86,7 @@ namespace osu.Game.Tests.Visual.UserInterface dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, dependencies.Get(), Resources, dependencies.Get(), Beatmap.Default)); dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, ContextFactory, Scheduler)); - beatmapInfo = beatmapManager.Import(new ImportTask(TestResources.GetQuickTestBeatmapForImport())).WaitSafelyForResult().Value.Beatmaps[0]; + beatmapInfo = beatmapManager.Import(new ImportTask(TestResources.GetQuickTestBeatmapForImport())).GetResultSafely().Value.Beatmaps[0]; for (int i = 0; i < 50; i++) { @@ -102,7 +102,7 @@ namespace osu.Game.Tests.Visual.UserInterface User = new APIUser { Username = "TestUser" }, }; - importedScores.Add(scoreManager.Import(score).WaitSafelyForResult().Value); + importedScores.Add(scoreManager.Import(score).GetResultSafely().Value); } return dependencies; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs index f78cbdffc1..6fe1ccc037 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs @@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.UserInterface { this.api = api; - testBeatmap = ImportBeatmapTest.LoadOszIntoOsu(osu).WaitSafelyForResult(); + testBeatmap = ImportBeatmapTest.LoadOszIntoOsu(osu).GetResultSafely(); } [Test] diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index 45dadc4e3f..f760c25170 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -270,7 +270,7 @@ namespace osu.Game.Beatmaps if (cancellationToken.IsCancellationRequested) return; - var starDifficulty = task.GetCompletedResult(); + var starDifficulty = task.GetResultSafely(); if (starDifficulty != null) bindable.Value = starDifficulty.Value; diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index c461135266..ed7fe0bc91 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -92,7 +92,7 @@ namespace osu.Game.Beatmaps } }; - var imported = beatmapModelManager.Import(set).WaitSafelyForResult().Value; + var imported = beatmapModelManager.Import(set).GetResultSafely().Value; return GetWorkingBeatmap(imported.Beatmaps.First()); } diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 85e30b6e3d..8289b32d31 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -186,7 +186,7 @@ namespace osu.Game.Beatmaps { try { - return loadBeatmapAsync().WaitSafelyForResult(); + return loadBeatmapAsync().GetResultSafely(); } catch (AggregateException ae) { diff --git a/osu.Game/Database/OnlineLookupCache.cs b/osu.Game/Database/OnlineLookupCache.cs index f5847428ee..2f98aef58a 100644 --- a/osu.Game/Database/OnlineLookupCache.cs +++ b/osu.Game/Database/OnlineLookupCache.cs @@ -59,7 +59,7 @@ namespace osu.Game.Database if (!task.IsCompletedSuccessfully) return null; - return task.GetCompletedResult(); + return task.GetResultSafely(); }, token)); } diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 52d5fa1642..82e042ae4e 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -536,7 +536,7 @@ namespace osu.Game.Online.Chat // Try to get user in order to open PM chat users.GetUserAsync((int)lastClosedChannel.Id).ContinueWith(task => { - var user = task.GetCompletedResult(); + var user = task.GetResultSafely(); if (user != null) Schedule(() => CurrentChannel.Value = JoinChannel(new Channel(user))); diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index 0f25be2d3c..a40f29abf2 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -85,7 +85,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores if (loadCancellationSource.IsCancellationRequested) return; - var scores = task.GetCompletedResult(); + var scores = task.GetResultSafely(); var topScore = scores.First(); diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs index 73b199f159..fde20575fc 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -64,7 +64,7 @@ namespace osu.Game.Overlays.Dashboard { users.GetUserAsync(id).ContinueWith(task => { - var user = task.GetCompletedResult(); + var user = task.GetResultSafely(); if (user == null) return; diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs index 8dbae420ac..158d8811b5 100644 --- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs @@ -48,7 +48,7 @@ namespace osu.Game.Overlays.Settings.Sections.General checkForUpdatesButton.Enabled.Value = false; Task.Run(updateManager.CheckForUpdateAsync).ContinueWith(task => Schedule(() => { - if (!task.GetCompletedResult()) + if (!task.GetResultSafely()) { notifications?.Post(new SimpleNotification { diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index b4edb23689..39cd28cad2 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -113,7 +113,7 @@ namespace osu.Game.Scoring public void GetTotalScore([NotNull] ScoreInfo score, [NotNull] Action callback, ScoringMode mode = ScoringMode.Standardised, CancellationToken cancellationToken = default) { GetTotalScoreAsync(score, mode, cancellationToken) - .ContinueWith(task => scheduler.Add(() => callback(task.GetCompletedResult())), TaskContinuationOptions.OnlyOnRanToCompletion); + .ContinueWith(task => scheduler.Add(() => callback(task.GetResultSafely())), TaskContinuationOptions.OnlyOnRanToCompletion); } /// diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index e7731631ff..948e3a7d88 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -111,7 +111,7 @@ namespace osu.Game.Screens.Menu { // if we detect that the theme track or beatmap is unavailable this is either first startup or things are in a bad state. // this could happen if a user has nuked their files store. for now, reimport to repair this. - var import = beatmaps.Import(new ZipArchiveReader(game.Resources.GetStream($"Tracks/{BeatmapFile}"), BeatmapFile)).WaitSafelyForResult(); + var import = beatmaps.Import(new ZipArchiveReader(game.Resources.GetStream($"Tracks/{BeatmapFile}"), BeatmapFile)).GetResultSafely(); import.PerformWrite(b => { diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index c384d97229..83c73e5a70 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -79,7 +79,7 @@ namespace osu.Game.Screens.Play.HUD userLookupCache.GetUsersAsync(playingUsers.Select(u => u.UserID).ToArray()).ContinueWith(task => Schedule(() => { - var users = task.GetCompletedResult(); + var users = task.GetResultSafely(); foreach (var user in users) { diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index 897678640f..21a7698248 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -76,7 +76,7 @@ namespace osu.Game.Screens.Play.HUD difficultyCache.GetTimedDifficultyAttributesAsync(gameplayWorkingBeatmap, gameplayState.Ruleset, clonedMods, loadCancellationSource.Token) .ContinueWith(task => Schedule(() => { - timedAttributes = task.GetCompletedResult(); + timedAttributes = task.GetResultSafely(); IsValid = true; diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 262424258a..2a6f5e2398 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -771,7 +771,7 @@ namespace osu.Game.Screens.Play // This player instance may already be in the process of exiting. return; - this.Push(CreateResults(prepareScoreForDisplayTask.WaitSafelyForResult())); + this.Push(CreateResults(prepareScoreForDisplayTask.GetResultSafely())); }, Time.Current + delay, 50); Scheduler.Add(resultsDisplayDelegate); diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index 87940c61aa..7e39708e65 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -80,7 +80,7 @@ namespace osu.Game.Screens.Ranking.Expanded statisticDisplays.AddRange(topStatistics); statisticDisplays.AddRange(bottomStatistics); - var starDifficulty = beatmapDifficultyCache.GetDifficultyAsync(beatmap, score.Ruleset, score.Mods).WaitSafelyForResult(); + var starDifficulty = beatmapDifficultyCache.GetDifficultyAsync(beatmap, score.Ruleset, score.Mods).GetResultSafely(); AddInternal(new FillFlowContainer { diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs index 1d00043244..d6e4cfbe51 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs @@ -38,7 +38,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics else { performanceCache.CalculatePerformanceAsync(score, cancellationTokenSource.Token) - .ContinueWith(t => Schedule(() => setPerformanceValue(t.GetCompletedResult())), cancellationTokenSource.Token); + .ContinueWith(t => Schedule(() => setPerformanceValue(t.GetResultSafely())), cancellationTokenSource.Token); } } diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index dacc8c11e1..354ee325d0 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -153,8 +153,8 @@ namespace osu.Game.Screens.Select.Details Task.WhenAll(normalStarDifficultyTask, moddedStarDifficultyTask).ContinueWith(_ => Schedule(() => { - var normalDifficulty = normalStarDifficultyTask.GetCompletedResult(); - var moddeDifficulty = moddedStarDifficultyTask.GetCompletedResult(); + var normalDifficulty = normalStarDifficultyTask.GetResultSafely(); + var moddeDifficulty = moddedStarDifficultyTask.GetResultSafely(); if (normalDifficulty == null || moddeDifficulty == null) return; diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index fbfca130ba..0102986070 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -143,7 +143,7 @@ namespace osu.Game.Screens.Select.Leaderboards } scoreManager.OrderByTotalScoreAsync(scores.ToArray(), cancellationToken) - .ContinueWith(task => scoresCallback?.Invoke(task.GetCompletedResult()), TaskContinuationOptions.OnlyOnRanToCompletion); + .ContinueWith(task => scoresCallback?.Invoke(task.GetResultSafely()), TaskContinuationOptions.OnlyOnRanToCompletion); return null; } @@ -184,7 +184,7 @@ namespace osu.Game.Screens.Select.Leaderboards if (cancellationToken.IsCancellationRequested) return; - scoresCallback?.Invoke(task.GetCompletedResult()); + scoresCallback?.Invoke(task.GetResultSafely()); TopScore = r.UserScore?.CreateScoreInfo(rulesets, fetchBeatmapInfo); }), TaskContinuationOptions.OnlyOnRanToCompletion); }; diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs index 44a31d89ed..c4e75cc413 100644 --- a/osu.Game/Screens/Spectate/SpectatorScreen.cs +++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs @@ -60,7 +60,7 @@ namespace osu.Game.Screens.Spectate userLookupCache.GetUsersAsync(users.ToArray()).ContinueWith(task => Schedule(() => { - var foundUsers = task.GetCompletedResult(); + var foundUsers = task.GetResultSafely(); foreach (var u in foundUsers) { diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index a55cc332c5..cde21b78c1 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -154,7 +154,7 @@ namespace osu.Game.Skinning Name = s.Name + @" (modified)", Creator = s.Creator, InstantiationInfo = s.InstantiationInfo, - }).WaitSafelyForResult(); + }).GetResultSafely(); if (result != null) { diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index 589857b458..897d4363f1 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -148,7 +148,7 @@ namespace osu.Game.Tests.Beatmaps if (!conversionTask.Wait(10000)) Assert.Fail("Conversion timed out"); - return conversionTask.GetCompletedResult(); + return conversionTask.GetResultSafely(); } protected virtual void OnConversionGenerated(HitObject original, IEnumerable result, IBeatmapConverter beatmapConverter) From 690b425380cfb0c7e6bcb74066ccbf164fbc09da Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Jan 2022 22:56:56 +0900 Subject: [PATCH 54/98] Move enum local to usage --- osu.Game/Overlays/ExpandingButtonContainer.cs | 7 ++++++- osu.Game/Overlays/Settings/SettingsSidebar.cs | 6 ------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/ExpandingButtonContainer.cs b/osu.Game/Overlays/ExpandingButtonContainer.cs index a49f0493f6..5964f7f263 100644 --- a/osu.Game/Overlays/ExpandingButtonContainer.cs +++ b/osu.Game/Overlays/ExpandingButtonContainer.cs @@ -11,7 +11,6 @@ using osu.Framework.Testing; using osu.Framework.Threading; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays.Settings; using osuTK; namespace osu.Game.Overlays @@ -131,4 +130,10 @@ namespace osu.Game.Overlays lastHoveredButton = hoveredButton; } } + + public enum ExpandedState + { + Contracted, + Expanded, + } } diff --git a/osu.Game/Overlays/Settings/SettingsSidebar.cs b/osu.Game/Overlays/Settings/SettingsSidebar.cs index 5aa2e61162..e6ce90c33e 100644 --- a/osu.Game/Overlays/Settings/SettingsSidebar.cs +++ b/osu.Game/Overlays/Settings/SettingsSidebar.cs @@ -28,10 +28,4 @@ namespace osu.Game.Overlays.Settings }); } } - - public enum ExpandedState - { - Contracted, - Expanded, - } } From 5aca2dd4ce17a4d39375e6b6577490b7f67abe46 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Jan 2022 23:08:50 +0900 Subject: [PATCH 55/98] Hide header text when it won't fit in the toolbox group --- osu.Game/Overlays/SettingsToolboxGroup.cs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/SettingsToolboxGroup.cs b/osu.Game/Overlays/SettingsToolboxGroup.cs index 02b5bdc1e1..fcc7ff2504 100644 --- a/osu.Game/Overlays/SettingsToolboxGroup.cs +++ b/osu.Game/Overlays/SettingsToolboxGroup.cs @@ -2,11 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; +using osu.Framework.Layout; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -53,6 +55,8 @@ namespace osu.Game.Overlays private Color4 expandedColour; + private readonly OsuSpriteText headerText; + /// /// Create a new instance. /// @@ -90,12 +94,10 @@ namespace osu.Game.Overlays Height = header_height, Children = new Drawable[] { - new OsuSpriteText + headerText = new OsuSpriteText { Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, - RelativeSizeAxes = Axes.X, - Truncate = true, Text = title.ToUpperInvariant(), Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 17), Padding = new MarginPadding { Left = 10, Right = 30 }, @@ -132,6 +134,16 @@ namespace osu.Game.Overlays private const float fade_duration = 800; private const float inactive_alpha = 0.5f; + protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source) + { + // These toolbox grouped may be contracted to only show icons. + // For now, let's hide the header to avoid text truncation weirdness in such cases. + if (invalidation.HasFlagFast(Invalidation.DrawSize)) + headerText.FadeTo(headerText.DrawWidth < DrawWidth ? 1 : 0, 150, Easing.OutQuint); + + return base.OnInvalidate(invalidation, source); + } + protected override void LoadComplete() { base.LoadComplete(); From f703c5f03879f68a8996e7e30959f4cf0fa4f97f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Jan 2022 23:38:54 +0900 Subject: [PATCH 56/98] Add comment and reduce how often `ChildrenOfType` is invoked in `ExpandingButtonContainer` --- osu.Game/Overlays/ExpandingButtonContainer.cs | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/osu.Game/Overlays/ExpandingButtonContainer.cs b/osu.Game/Overlays/ExpandingButtonContainer.cs index 5964f7f263..4eb8c47a1f 100644 --- a/osu.Game/Overlays/ExpandingButtonContainer.cs +++ b/osu.Game/Overlays/ExpandingButtonContainer.cs @@ -65,7 +65,7 @@ namespace osu.Game.Overlays protected override void OnHoverLost(HoverLostEvent e) { expandEvent?.Cancel(); - lastHoveredButton = null; + hoveredButton = null; State = ExpandedState.Contracted; base.OnHoverLost(e); @@ -112,22 +112,24 @@ namespace osu.Game.Overlays } } - private Drawable lastHoveredButton; - - private Drawable hoveredButton => FillFlow.ChildrenOfType().FirstOrDefault(c => c.IsHovered); + private Drawable hoveredButton; private void queueExpandIfHovering() { - // only expand when we hover a different button. - if (lastHoveredButton == hoveredButton) return; + // if the same button is hovered, let the scheduled expand play out.. + if (hoveredButton?.IsHovered == true) + return; - if (State != ExpandedState.Expanded) - { - expandEvent?.Cancel(); + // ..otherwise check whether a new button is hovered, and if so, queue a new hover operation. + + // usually we wouldn't use ChildrenOfType in implementations, but this is the simplest way + // to handle cases like the editor where the buttons may be nested within a child hierarchy. + hoveredButton = FillFlow.ChildrenOfType().FirstOrDefault(c => c.IsHovered); + + expandEvent?.Cancel(); + + if (hoveredButton?.IsHovered == true && State != ExpandedState.Expanded) expandEvent = Scheduler.AddDelayed(() => State = ExpandedState.Expanded, 750); - } - - lastHoveredButton = hoveredButton; } } From e02863f7806e4ddeb49564aef48ac68bb537569b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Jan 2022 01:24:30 +0900 Subject: [PATCH 57/98] Avoid accessing `DrawWidth` from invalidation --- osu.Game/Overlays/SettingsToolboxGroup.cs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/SettingsToolboxGroup.cs b/osu.Game/Overlays/SettingsToolboxGroup.cs index fcc7ff2504..ff8966d55f 100644 --- a/osu.Game/Overlays/SettingsToolboxGroup.cs +++ b/osu.Game/Overlays/SettingsToolboxGroup.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Caching; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -25,6 +26,11 @@ namespace osu.Game.Overlays private const int header_height = 30; private const int corner_radius = 5; + private const float fade_duration = 800; + private const float inactive_alpha = 0.5f; + + private readonly Cached headerTextVisibilityCache = new Cached(); + private readonly FillFlowContainer content; private readonly IconButton button; @@ -131,19 +137,24 @@ namespace osu.Game.Overlays }; } - private const float fade_duration = 800; - private const float inactive_alpha = 0.5f; - protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source) { - // These toolbox grouped may be contracted to only show icons. - // For now, let's hide the header to avoid text truncation weirdness in such cases. if (invalidation.HasFlagFast(Invalidation.DrawSize)) - headerText.FadeTo(headerText.DrawWidth < DrawWidth ? 1 : 0, 150, Easing.OutQuint); + headerTextVisibilityCache.Invalidate(); return base.OnInvalidate(invalidation, source); } + protected override void Update() + { + base.Update(); + + if (!headerTextVisibilityCache.IsValid) + // These toolbox grouped may be contracted to only show icons. + // For now, let's hide the header to avoid text truncation weirdness in such cases. + headerText.FadeTo(headerText.DrawWidth < DrawWidth ? 1 : 0, 150, Easing.OutQuint); + } + protected override void LoadComplete() { base.LoadComplete(); From 8f744c99ee0647b5786ef971f08bc513d5d0ed6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 6 Jan 2022 20:25:03 +0100 Subject: [PATCH 58/98] Fix settings toolbox toggle button starting in incorrect state While displaying replays, the colour of the toolbox toggle button would not match the actual state of the rest of the toolbox, i.e. both buttons would be white, even though the "playback settings" section was expanded and as such should have a yellow toggle button. In the case of the replay player, the failure scenario was as follows: 1. `SettingsToolboxGroup` calls `updateExpanded()` in its BDL to update the initial state of the toolbox, including the toggle button colour, by adding a colour fade transform. 2. An ancestor of both the toolbox groups - `PlayerSettingsOverlay`, which is a `VisibilityContainer` - calls `FinishTransforms(true)` in its `LoadCompleteAsync()`, therefore instantly applying the colour from point (1) to the toggle button instantly. 3. However, `IconButton` inherits from `OsuAnimatedButton`. And `OsuAnimatedButton` changes its colour in `LoadComplete()`, therefore undoing the instant application from point (2). This conjunction of circumstances is instrumental to reproducing the bug, because if the `FinishTransforms(true)` call wasn't there, point (3) wouldn't matter - the transform would get applied at some indeterminate point in the future, ignoring the write from `OsuAnimatedButton`. As for the fix, move the `updateExpanded()` call in `SettingsToolboxGroup` to `LoadComplete()` to avoid the above unfortunate order. Applying initial visual state in `LoadComplete()` is the idiomatic style of doing things these days anyhow. --- osu.Game/Overlays/SettingsToolboxGroup.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/SettingsToolboxGroup.cs b/osu.Game/Overlays/SettingsToolboxGroup.cs index ff8966d55f..ca0980a9c9 100644 --- a/osu.Game/Overlays/SettingsToolboxGroup.cs +++ b/osu.Game/Overlays/SettingsToolboxGroup.cs @@ -158,7 +158,9 @@ namespace osu.Game.Overlays protected override void LoadComplete() { base.LoadComplete(); + this.Delay(600).FadeTo(inactive_alpha, fade_duration, Easing.OutQuint); + updateExpanded(); } protected override bool OnHover(HoverEvent e) @@ -177,8 +179,6 @@ namespace osu.Game.Overlays private void load(OsuColour colours) { expandedColour = colours.Yellow; - - updateExpanded(); } private void updateExpanded() => button.FadeColour(expanded ? expandedColour : Color4.White, 200, Easing.InOutQuint); From 6a1e1d186f7294c869e76501b088f46cac4a3973 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Jan 2022 17:29:09 +0900 Subject: [PATCH 59/98] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 67a9cd41dd..3ec12089ad 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c7913cb71d..1b2b3318bd 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 63dc889e3c..833e0fbdb9 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -60,7 +60,7 @@ - + @@ -83,7 +83,7 @@ - + From 2ef791069c67911d548c0da48886f32bd95b7634 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Jan 2022 17:33:38 +0900 Subject: [PATCH 60/98] Fix typon on `AdvancedStats` --- osu.Game/Screens/Select/Details/AdvancedStats.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 354ee325d0..adaaa6425c 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -154,12 +154,12 @@ namespace osu.Game.Screens.Select.Details Task.WhenAll(normalStarDifficultyTask, moddedStarDifficultyTask).ContinueWith(_ => Schedule(() => { var normalDifficulty = normalStarDifficultyTask.GetResultSafely(); - var moddeDifficulty = moddedStarDifficultyTask.GetResultSafely(); + var moddedDifficulty = moddedStarDifficultyTask.GetResultSafely(); - if (normalDifficulty == null || moddeDifficulty == null) + if (normalDifficulty == null || moddedDifficulty == null) return; - starDifficulty.Value = ((float)normalDifficulty.Value.Stars, (float)moddeDifficulty.Value.Stars); + starDifficulty.Value = ((float)normalDifficulty.Value.Stars, (float)moddedDifficulty.Value.Stars); }), starDifficultyCancellationSource.Token, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Current); } From 0698ef633038df18077b24da424fd30ed44f795b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Jan 2022 17:36:29 +0900 Subject: [PATCH 61/98] Fix one missed rename --- osu.Game/Screens/Ranking/ScorePanelList.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index 5ae6b9d417..f8e9d08350 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -154,7 +154,7 @@ namespace osu.Game.Screens.Ranking scoreManager.GetTotalScoreAsync(score) .ContinueWith(task => Schedule(() => { - flow.SetLayoutPosition(trackingContainer, task.GetCompletedResult()); + flow.SetLayoutPosition(trackingContainer, task.GetResultSafely()); trackingContainer.Show(); From 12c3e56881d9dbc38ecec16c877397a8a7ea6a27 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Jan 2022 19:01:05 +0900 Subject: [PATCH 62/98] Fix `IPCLocationTest` not waiting for load of component As seen at https://github.com/ppy/osu/runs/4731480384?check_suite_focus=true. --- osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs b/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs index 952eb72bf4..03252e3be6 100644 --- a/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs +++ b/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs @@ -36,7 +36,7 @@ namespace osu.Game.Tournament.Tests.NonVisual TournamentStorage storage = (TournamentStorage)osu.Dependencies.Get(); FileBasedIPC ipc = null; - WaitForOrAssert(() => (ipc = osu.Dependencies.Get() as FileBasedIPC) != null, @"ipc could not be populated in a reasonable amount of time"); + WaitForOrAssert(() => (ipc = osu.Dependencies.Get() as FileBasedIPC)?.IsLoaded == true, @"ipc could not be populated in a reasonable amount of time"); Assert.True(ipc.SetIPCLocation(testStableInstallDirectory)); Assert.True(storage.AllTournaments.Exists("stable.json")); From 814b318a10fc584ae24bd3a4478724a3347502c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 7 Jan 2022 14:00:22 +0100 Subject: [PATCH 63/98] Add test coverage of slider end snapping behaviour --- .../Editor/TestSceneSliderSnapping.cs | 183 ++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs new file mode 100644 index 0000000000..4b4aedcff4 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs @@ -0,0 +1,183 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Input.Events; +using osu.Framework.Testing; +using osu.Framework.Utils; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Input.Bindings; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Edit; +using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Screens.Edit; +using osu.Game.Screens.Edit.Compose.Components; +using osu.Game.Tests.Beatmaps; +using osu.Game.Tests.Visual; +using osuTK; +using osuTK.Input; + +namespace osu.Game.Rulesets.Osu.Tests.Editor +{ + public class TestSceneSliderSnapping : EditorTestScene + { + private const double beat_length = 1000; + + protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); + + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) + { + var controlPointInfo = new ControlPointInfo(); + controlPointInfo.Add(0, new TimingControlPoint { BeatLength = beat_length }); + return new TestBeatmap(ruleset, false) + { + ControlPointInfo = controlPointInfo + }; + } + + private Slider slider; + + public override void SetUpSteps() + { + base.SetUpSteps(); + + AddStep("add unsnapped slider", () => EditorBeatmap.Add(slider = new Slider + { + StartTime = 0, + Position = OsuPlayfield.BASE_SIZE / 5, + Path = new SliderPath + { + ControlPoints = + { + new PathControlPoint(Vector2.Zero), + new PathControlPoint(OsuPlayfield.BASE_SIZE * 3 / 5) + } + } + })); + AddStep("set beat divisor to 1/1", () => + { + var beatDivisor = (BindableBeatDivisor)Editor.Dependencies.Get(typeof(BindableBeatDivisor)); + beatDivisor.Value = 1; + }); + } + + [Test] + public void TestMovingUnsnappedSliderNodesSnaps() + { + PathControlPointPiece sliderEnd = null; + + assertSliderSnapped(false); + + AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider)); + AddStep("select slider end", () => + { + sliderEnd = this.ChildrenOfType().Single(piece => piece.ControlPoint.Position != Vector2.Zero); + InputManager.MoveMouseTo(sliderEnd.ScreenSpaceDrawQuad.Centre); + }); + AddStep("move slider end", () => + { + InputManager.PressButton(MouseButton.Left); + InputManager.MoveMouseTo(sliderEnd.ScreenSpaceDrawQuad.Centre - new Vector2(0, 20)); + InputManager.ReleaseButton(MouseButton.Left); + }); + assertSliderSnapped(true); + } + + [Test] + public void TestResizingUnsnappedSliderSnaps() + { + SelectionBoxScaleHandle handle = null; + + assertSliderSnapped(false); + + AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider)); + AddStep("move mouse to scale handle", () => + { + handle = this.ChildrenOfType().First(); + InputManager.MoveMouseTo(handle.ScreenSpaceDrawQuad.Centre); + }); + AddStep("scale slider", () => + { + InputManager.PressButton(MouseButton.Left); + InputManager.MoveMouseTo(handle.ScreenSpaceDrawQuad.Centre + new Vector2(20, 20)); + InputManager.ReleaseButton(MouseButton.Left); + }); + assertSliderSnapped(true); + } + + [Test] + public void TestRotatingUnsnappedSliderDoesNotSnap() + { + SelectionBoxRotationHandle handle = null; + + assertSliderSnapped(false); + + AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider)); + AddStep("move mouse to rotate handle", () => + { + handle = this.ChildrenOfType().First(); + InputManager.MoveMouseTo(handle.ScreenSpaceDrawQuad.Centre); + }); + AddStep("scale slider", () => + { + InputManager.PressButton(MouseButton.Left); + InputManager.MoveMouseTo(handle.ScreenSpaceDrawQuad.Centre + new Vector2(0, 20)); + InputManager.ReleaseButton(MouseButton.Left); + }); + assertSliderSnapped(false); + } + + [Test] + public void TestFlippingSliderDoesNotSnap() + { + OsuSelectionHandler selectionHandler = null; + + assertSliderSnapped(false); + + AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider)); + AddStep("flip slider horizontally", () => + { + selectionHandler = this.ChildrenOfType().Single(); + selectionHandler.OnPressed(new KeyBindingPressEvent(InputManager.CurrentState, GlobalAction.EditorFlipHorizontally)); + }); + + assertSliderSnapped(false); + + AddStep("flip slider vertically", () => + { + selectionHandler = this.ChildrenOfType().Single(); + selectionHandler.OnPressed(new KeyBindingPressEvent(InputManager.CurrentState, GlobalAction.EditorFlipVertically)); + }); + + assertSliderSnapped(false); + } + + [Test] + public void TestReversingSliderDoesNotSnap() + { + assertSliderSnapped(false); + + AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider)); + AddStep("reverse slider", () => + { + InputManager.PressKey(Key.ControlLeft); + InputManager.Key(Key.G); + InputManager.ReleaseKey(Key.ControlLeft); + }); + + assertSliderSnapped(false); + } + + private void assertSliderSnapped(bool snapped) + => AddAssert($"slider is {(snapped ? "" : "not ")}snapped", () => + { + double durationInBeatLengths = slider.Duration / beat_length; + double fractionalPart = durationInBeatLengths - (int)durationInBeatLengths; + return Precision.AlmostEquals(fractionalPart, 0) == snapped; + }); + } +} From d2f44813dd9aed366931123b3fca31a25673decb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 7 Jan 2022 14:46:36 +0100 Subject: [PATCH 64/98] Add test coverage for slider snapping when adding/removing control points --- .../Editor/TestSceneSliderSnapping.cs | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs index 4b4aedcff4..b43b2b1461 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs @@ -54,6 +54,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor ControlPoints = { new PathControlPoint(Vector2.Zero), + new PathControlPoint(OsuPlayfield.BASE_SIZE * 2 / 5), new PathControlPoint(OsuPlayfield.BASE_SIZE * 3 / 5) } } @@ -75,7 +76,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider)); AddStep("select slider end", () => { - sliderEnd = this.ChildrenOfType().Single(piece => piece.ControlPoint.Position != Vector2.Zero); + sliderEnd = this.ChildrenOfType().Single(piece => piece.ControlPoint == slider.Path.ControlPoints.Last()); InputManager.MoveMouseTo(sliderEnd.ScreenSpaceDrawQuad.Centre); }); AddStep("move slider end", () => @@ -87,6 +88,47 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertSliderSnapped(true); } + [Test] + public void TestAddingControlPointToUnsnappedSliderNodesSnaps() + { + assertSliderSnapped(false); + + AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider)); + AddStep("move mouse to new point location", () => + { + var firstPiece = this.ChildrenOfType().Single(piece => piece.ControlPoint == slider.Path.ControlPoints[0]); + var secondPiece = this.ChildrenOfType().Single(piece => piece.ControlPoint == slider.Path.ControlPoints[1]); + InputManager.MoveMouseTo((firstPiece.ScreenSpaceDrawQuad.Centre + secondPiece.ScreenSpaceDrawQuad.Centre) / 2); + }); + AddStep("move slider end", () => + { + InputManager.PressKey(Key.ControlLeft); + InputManager.Click(MouseButton.Left); + InputManager.ReleaseKey(Key.ControlLeft); + }); + assertSliderSnapped(true); + } + + [Test] + public void TestRemovingControlPointFromUnsnappedSliderNodesSnaps() + { + assertSliderSnapped(false); + + AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider)); + AddStep("move mouse to second control point", () => + { + var secondPiece = this.ChildrenOfType().Single(piece => piece.ControlPoint == slider.Path.ControlPoints[1]); + InputManager.MoveMouseTo(secondPiece); + }); + AddStep("quick delete", () => + { + InputManager.PressKey(Key.ShiftLeft); + InputManager.PressButton(MouseButton.Right); + InputManager.ReleaseKey(Key.ShiftLeft); + }); + assertSliderSnapped(true); + } + [Test] public void TestResizingUnsnappedSliderSnaps() { From c09f6ee0522fa038d58810d010d02d8570e9f6bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 7 Jan 2022 15:11:38 +0100 Subject: [PATCH 65/98] Use slider snapping more liberally to match user expectations Previously the slider path length would be snapped using the current beat snap setting on *every* change of the slider path. As it turns out this is unexpected behaviour in some situations (e.g. when reversing a path, which is expected to preserve the previous duration, even though the slider may be technically "unsnapped" at that point in time due to a different beat snap setting being selected afterwards). --- .../Components/PathControlPointVisualiser.cs | 5 +++++ .../Sliders/SliderSelectionBlueprint.cs | 15 +++++++-------- .../Edit/OsuSelectionHandler.cs | 16 +++++++++++++++- .../Rulesets/Objects/SliderPathExtensions.cs | 10 ++++++++++ 4 files changed, 37 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 065d4737a5..ae4141073e 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -283,6 +283,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components } } + // Snap the path to the current beat divisor before checking length validity. + slider.SnapTo(snapProvider); + if (!slider.Path.HasValidLength) { for (int i = 0; i < slider.Path.ControlPoints.Count; i++) @@ -290,6 +293,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components slider.Position = oldPosition; slider.StartTime = oldStartTime; + // Snap the path length again to undo the invalid length. + slider.SnapTo(snapProvider); return; } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 2aebe05c2f..6cf2a493a9 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -80,7 +80,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders controlPoints.BindTo(HitObject.Path.ControlPoints); pathVersion.BindTo(HitObject.Path.Version); - pathVersion.BindValueChanged(_ => updatePath()); + pathVersion.BindValueChanged(_ => editorBeatmap?.Update(HitObject)); BodyPiece.UpdateFrom(HitObject); } @@ -208,6 +208,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders // Move the control points from the insertion index onwards to make room for the insertion controlPoints.Insert(insertionIndex, pathControlPoint); + HitObject.SnapTo(composer); + return pathControlPoint; } @@ -227,7 +229,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders controlPoints.Remove(c); } - // If there are 0 or 1 remaining control points, the slider is in a degenerate (single point) form and should be deleted + // Snap the slider to the current beat divisor before checking length validity. + HitObject.SnapTo(composer); + + // If there are 0 or 1 remaining control points, or the slider has an invalid length, it is in a degenerate form and should be deleted if (controlPoints.Count <= 1 || !HitObject.Path.HasValidLength) { placementHandler?.Delete(HitObject); @@ -242,12 +247,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders HitObject.Position += first; } - private void updatePath() - { - HitObject.Path.ExpectedDistance.Value = composer?.GetSnappedDistanceFromDistance(HitObject, (float)HitObject.Path.CalculatedDistance) ?? (float)HitObject.Path.CalculatedDistance; - editorBeatmap?.Update(HitObject); - } - private void convertToStream() { if (editorBeatmap == null || changeHandler == null || beatDivisor == null) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 071ecf6329..efbac5439c 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -1,12 +1,16 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System.Collections.Generic; using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Primitives; using osu.Framework.Utils; using osu.Game.Extensions; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; @@ -18,6 +22,9 @@ namespace osu.Game.Rulesets.Osu.Edit { public class OsuSelectionHandler : EditorSelectionHandler { + [Resolved(CanBeNull = true)] + private IPositionSnapProvider? positionSnapProvider { get; set; } + /// /// During a transform, the initial origin is stored so it can be used throughout the operation. /// @@ -27,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Edit /// During a transform, the initial path types of a single selected slider are stored so they /// can be maintained throughout the operation. /// - private List referencePathTypes; + private List? referencePathTypes; protected override void OnSelectionChanged() { @@ -197,6 +204,10 @@ namespace osu.Game.Rulesets.Osu.Edit for (int i = 0; i < slider.Path.ControlPoints.Count; ++i) slider.Path.ControlPoints[i].Type = referencePathTypes[i]; + // Snap the slider's length to the current beat divisor + // to calculate the final resulting duration / bounding box before the final checks. + slider.SnapTo(positionSnapProvider); + //if sliderhead or sliderend end up outside playfield, revert scaling. Quad scaledQuad = getSurroundingQuad(new OsuHitObject[] { slider }); (bool xInBounds, bool yInBounds) = isQuadInBounds(scaledQuad); @@ -206,6 +217,9 @@ namespace osu.Game.Rulesets.Osu.Edit foreach (var point in slider.Path.ControlPoints) point.Position = oldControlPoints.Dequeue(); + + // Snap the slider's length again to undo the potentially-invalid length applied by the previous snap. + slider.SnapTo(positionSnapProvider); } private void scaleHitObjects(OsuHitObject[] hitObjects, Anchor reference, Vector2 scale) diff --git a/osu.Game/Rulesets/Objects/SliderPathExtensions.cs b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs index 1308fff7ae..ba614900c0 100644 --- a/osu.Game/Rulesets/Objects/SliderPathExtensions.cs +++ b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects.Types; using osuTK; @@ -11,6 +12,15 @@ namespace osu.Game.Rulesets.Objects { public static class SliderPathExtensions { + /// + /// Snaps the provided 's duration using the . + /// + public static void SnapTo(this THitObject hitObject, IPositionSnapProvider? snapProvider) + where THitObject : HitObject, IHasPath + { + hitObject.Path.ExpectedDistance.Value = snapProvider?.GetSnappedDistanceFromDistance(hitObject, (float)hitObject.Path.CalculatedDistance) ?? hitObject.Path.CalculatedDistance; + } + /// /// Reverse the direction of this path. /// From 881fa2b86b17aa87119ce58d20fbc27011217c52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 6 Jan 2022 14:49:56 +0100 Subject: [PATCH 66/98] Add basic test scene for scoreboard time --- .../Visual/Online/TestSceneScoreboardTime.cs | 57 +++++++++++++++++++ .../BeatmapSet/Scores/ScoreboardTime.cs | 21 +++++++ 2 files changed, 78 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneScoreboardTime.cs create mode 100644 osu.Game/Overlays/BeatmapSet/Scores/ScoreboardTime.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneScoreboardTime.cs b/osu.Game.Tests/Visual/Online/TestSceneScoreboardTime.cs new file mode 100644 index 0000000000..7e33b5240c --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneScoreboardTime.cs @@ -0,0 +1,57 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Timing; +using osu.Game.Overlays.BeatmapSet.Scores; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneScoreboardTime : OsuTestScene + { + private StopwatchClock stopwatch; + + [Test] + public void TestVariousUnits() + { + AddStep("create various scoreboard times", () => Child = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Clock = new FramedClock(stopwatch = new StopwatchClock()), // prevent time from naturally elapsing. + Direction = FillDirection.Vertical, + ChildrenEnumerable = testCases.Select(dateTime => new ScoreboardTime(dateTime, 24).With(time => time.Anchor = time.Origin = Anchor.TopCentre)) + }); + + AddStep("start stopwatch", () => stopwatch.Start()); + } + + private static IEnumerable testCases => new[] + { + DateTimeOffset.Now, + DateTimeOffset.Now.AddSeconds(-1), + DateTimeOffset.Now.AddSeconds(-25), + DateTimeOffset.Now.AddSeconds(-59), + DateTimeOffset.Now.AddMinutes(-1), + DateTimeOffset.Now.AddMinutes(-25), + DateTimeOffset.Now.AddMinutes(-59), + DateTimeOffset.Now.AddHours(-1), + DateTimeOffset.Now.AddHours(-13), + DateTimeOffset.Now.AddHours(-23), + DateTimeOffset.Now.AddDays(-1), + DateTimeOffset.Now.AddDays(-6), + DateTimeOffset.Now.AddDays(-16), + DateTimeOffset.Now.AddMonths(-1), + DateTimeOffset.Now.AddMonths(-11), + DateTimeOffset.Now.AddYears(-1), + DateTimeOffset.Now.AddYears(-5) + }; + } +} diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreboardTime.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreboardTime.cs new file mode 100644 index 0000000000..db421ec6b7 --- /dev/null +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreboardTime.cs @@ -0,0 +1,21 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.BeatmapSet.Scores +{ + public class ScoreboardTime : DrawableDate + { + public ScoreboardTime(DateTimeOffset date, float textSize = OsuFont.DEFAULT_FONT_SIZE, bool italic = true) + : base(date, textSize, italic) + { + } + + protected override string Format() + { + return base.Format(); + } + } +} From 87f7c7e6915757752a6b978cbf5ca859bc03368f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 6 Jan 2022 16:00:45 +0100 Subject: [PATCH 67/98] Implement scoreboard-specific time formatting --- .../BeatmapSet/Scores/ScoreboardTime.cs | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreboardTime.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreboardTime.cs index db421ec6b7..ff1d3490b4 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreboardTime.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreboardTime.cs @@ -2,7 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System; +using Humanizer; using osu.Game.Graphics; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapSet.Scores { @@ -15,7 +17,40 @@ namespace osu.Game.Overlays.BeatmapSet.Scores protected override string Format() { - return base.Format(); + var now = DateTime.Now; + var difference = now - Date; + + // web uses momentjs's custom locales to format the date for the purposes of the scoreboard. + // this is intended to be a best-effort, more legible approximation of that. + // compare: + // * https://github.com/ppy/osu-web/blob/a8f5a68fb435cb19a4faa4c7c4bce08c4f096933/resources/assets/lib/scoreboard-time.tsx + // * https://momentjs.com/docs/#/customization/ (reference for the customisation format) + + // TODO: support localisation (probably via `CommonStrings.CountHours()` etc.) + // requires pluralisable string support framework-side + + if (difference.TotalHours < 1) + return CommonStrings.TimeNow.ToString(); + if (difference.TotalDays < 1) + return "hr".ToQuantity((int)difference.TotalHours); + + // this is where this gets more complicated because of how the calendar works. + // since there's no `TotalMonths` / `TotalYears`, we have to iteratively add months/years + // and test against cutoff dates to determine how many months/years to show. + + if (Date > now.AddMonths(-1)) + return difference.TotalDays < 2 ? "1dy" : $"{(int)difference.TotalDays}dys"; + + for (int months = 1; months <= 11; ++months) + { + if (Date > now.AddMonths(-(months + 1))) + return months == 1 ? "1mo" : $"{months}mos"; + } + + int years = 1; + while (Date <= now.AddYears(-(years + 1))) + years += 1; + return years == 1 ? "1yr" : $"{years}yrs"; } } } From 9e84e31eacd3008ae518fee266de58007eb46357 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 6 Jan 2022 16:14:26 +0100 Subject: [PATCH 68/98] Add score time to beatmap set overlay scoreboard table --- osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index 018faf2011..2c78fa264e 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -128,6 +128,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores if (showPerformancePoints) columns.Add(new TableColumn(BeatmapsetsStrings.ShowScoreboardHeaderspp, Anchor.CentreLeft, new Dimension(GridSizeMode.Absolute, 30))); + columns.Add(new TableColumn(BeatmapsetsStrings.ShowScoreboardHeadersTime, Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize))); columns.Add(new TableColumn(BeatmapsetsStrings.ShowScoreboardHeadersMods, Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize))); return columns.ToArray(); @@ -202,6 +203,11 @@ namespace osu.Game.Overlays.BeatmapSet.Scores }); } + content.Add(new ScoreboardTime(score.Date, text_size) + { + Margin = new MarginPadding { Right = 10 } + }); + content.Add(new FillFlowContainer { Direction = FillDirection.Horizontal, From f6f24220c2a0b9799d4eac60400ac3659ac400c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 8 Jan 2022 15:28:11 +0900 Subject: [PATCH 69/98] Fix `LegacyScoreDecoderTest` incorrectly comparing unset beatmap IDs This has been wrong from the outside, but hidden by the fact that the default values are equal. I've changed to MD5Hash which actually asserts that the correct beatmap has likely arrived. Found this in my realm changes, where it fails due to the beatmap ID being a differing Guid in each case. --- osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs index 81d89359e0..b886a61c0f 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs @@ -95,7 +95,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.That(decodedAfterEncode, Is.Not.Null); Assert.That(decodedAfterEncode.ScoreInfo.User.Username, Is.EqualTo(scoreInfo.User.Username)); - Assert.That(decodedAfterEncode.ScoreInfo.BeatmapInfoID, Is.EqualTo(scoreInfo.BeatmapInfoID)); + Assert.That(decodedAfterEncode.ScoreInfo.BeatmapInfo.MD5Hash, Is.EqualTo(scoreInfo.BeatmapInfo.MD5Hash)); Assert.That(decodedAfterEncode.ScoreInfo.Ruleset, Is.EqualTo(scoreInfo.Ruleset)); Assert.That(decodedAfterEncode.ScoreInfo.TotalScore, Is.EqualTo(scoreInfo.TotalScore)); Assert.That(decodedAfterEncode.ScoreInfo.MaxCombo, Is.EqualTo(scoreInfo.MaxCombo)); From f026973b19316c828696f0e34a4cd8230215b741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 8 Jan 2022 14:16:05 +0100 Subject: [PATCH 70/98] Add failing test for ruleset incorrectly applying from latest picked item --- .../TestSceneAllPlayersQueueMode.cs | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs index a5744f9986..a06bf44d88 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs @@ -1,13 +1,21 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; using System.Linq; using NUnit.Framework; +using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Online.Multiplayer; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Catch; +using osu.Game.Rulesets.Osu; using osu.Game.Screens.OnlinePlay.Multiplayer; +using osu.Game.Screens.OnlinePlay.Multiplayer.Match; +using osu.Game.Screens.Play; using osuTK.Input; namespace osu.Game.Tests.Visual.Multiplayer @@ -83,7 +91,24 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("selected beatmap is initial beatmap", () => Beatmap.Value.BeatmapInfo.OnlineID == InitialBeatmap.OnlineID); } - private void addItem(Func beatmap) + [Test] + public void TestCorrectRulesetSelectedAfterNewItemAdded() + { + addItem(() => OtherBeatmap, new CatchRuleset().RulesetInfo); + AddUntilStep("selected beatmap is initial beatmap", () => Beatmap.Value.BeatmapInfo.OnlineID == InitialBeatmap.OnlineID); + + AddUntilStep("wait for idle", () => Client.LocalUser?.State == MultiplayerUserState.Idle); + ClickButtonWhenEnabled(); + + AddUntilStep("wait for ready", () => Client.LocalUser?.State == MultiplayerUserState.Ready); + ClickButtonWhenEnabled(); + + AddUntilStep("wait for player", () => CurrentScreen is Player player && player.IsLoaded); + AddAssert("ruleset is correct", () => ((Player)CurrentScreen).Ruleset.Value.Equals(new OsuRuleset().RulesetInfo)); + AddStep("exit player", () => CurrentScreen.Exit()); + } + + private void addItem(Func beatmap, RulesetInfo? ruleset = null) { AddStep("click add button", () => { @@ -92,6 +117,12 @@ namespace osu.Game.Tests.Visual.Multiplayer }); AddUntilStep("wait for song select", () => CurrentSubScreen is Screens.Select.SongSelect select && select.BeatmapSetsLoaded); + + if (ruleset != null) + { + AddStep($"set {ruleset.Name} ruleset", () => ((Screens.Select.SongSelect)CurrentSubScreen).Ruleset.Value = ruleset); + } + AddStep("select other beatmap", () => ((Screens.Select.SongSelect)CurrentSubScreen).FinaliseSelection(beatmap())); AddUntilStep("wait for return to match", () => CurrentSubScreen is MultiplayerMatchSubScreen); } From 50077f05bdb07468a93a0ba844b1ea00ec1823d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 8 Jan 2022 14:39:22 +0100 Subject: [PATCH 71/98] Add test coverage for correct revert of ruleset when play starts at song select --- .../Multiplayer/TestSceneMultiplayer.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 4eebda94e9..1221a9f080 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -22,6 +22,7 @@ using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Overlays.Mods; using osu.Game.Rulesets; +using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens.OnlinePlay.Components; @@ -438,6 +439,45 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("Beatmap matches current item", () => Beatmap.Value.BeatmapInfo.OnlineID == client.Room?.Playlist.First().BeatmapID); } + [Test] + public void TestPlayStartsWithCorrectRulesetWhileAtSongSelect() + { + createRoom(() => new Room + { + Name = { Value = "Test Room" }, + Playlist = + { + new PlaylistItem + { + Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + } + } + }); + + pressReadyButton(); + + AddStep("Enter song select", () => + { + var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerComponents.CurrentScreen).CurrentSubScreen; + ((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(client.Room?.Settings.PlaylistItemId); + }); + + AddUntilStep("wait for song select", () => this.ChildrenOfType().FirstOrDefault()?.BeatmapSetsLoaded == true); + + AddAssert("Ruleset matches current item", () => Ruleset.Value.OnlineID == client.Room?.Playlist.First().RulesetID); + + AddStep("Switch ruleset", () => ((MultiplayerMatchSongSelect)multiplayerComponents.MultiplayerScreen.CurrentSubScreen).Ruleset.Value = new CatchRuleset().RulesetInfo); + + AddUntilStep("Ruleset doesn't match current item", () => Ruleset.Value.OnlineID != client.Room?.Playlist.First().RulesetID); + + AddStep("start match externally", () => client.StartMatch()); + + AddUntilStep("play started", () => multiplayerComponents.CurrentScreen is Player); + + AddAssert("Ruleset matches current item", () => Ruleset.Value.OnlineID == client.Room?.Playlist.First().RulesetID); + } + [Test] public void TestLocalPlayDoesNotStartWhileSpectatingWithNoBeatmap() { From 3dd5705a810d8f1f68e3bf8c9accb5cda865d6f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 8 Jan 2022 14:46:41 +0100 Subject: [PATCH 72/98] Add test coverage for correct revert of mods after new item is queued --- .../TestSceneAllPlayersQueueMode.cs | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs index a06bf44d88..ad60ac824d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs @@ -4,15 +4,19 @@ #nullable enable using System; +using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Online.Multiplayer; using osu.Game.Rulesets; using osu.Game.Rulesets.Catch; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; using osu.Game.Screens.Play; @@ -108,22 +112,43 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("exit player", () => CurrentScreen.Exit()); } - private void addItem(Func beatmap, RulesetInfo? ruleset = null) + [Test] + public void TestCorrectModsSelectedAfterNewItemAdded() { + addItem(() => OtherBeatmap, mods: new Mod[] { new OsuModDoubleTime() }); + AddUntilStep("selected beatmap is initial beatmap", () => Beatmap.Value.BeatmapInfo.OnlineID == InitialBeatmap.OnlineID); + + AddUntilStep("wait for idle", () => Client.LocalUser?.State == MultiplayerUserState.Idle); + ClickButtonWhenEnabled(); + + AddUntilStep("wait for ready", () => Client.LocalUser?.State == MultiplayerUserState.Ready); + ClickButtonWhenEnabled(); + + AddUntilStep("wait for player", () => CurrentScreen is Player player && player.IsLoaded); + AddAssert("mods are correct", () => !((Player)CurrentScreen).Mods.Value.Any()); + AddStep("exit player", () => CurrentScreen.Exit()); + } + + private void addItem(Func beatmap, RulesetInfo? ruleset = null, IReadOnlyList? mods = null) + { + Screens.Select.SongSelect? songSelect = null; + AddStep("click add button", () => { InputManager.MoveMouseTo(this.ChildrenOfType().Single()); InputManager.Click(MouseButton.Left); }); - AddUntilStep("wait for song select", () => CurrentSubScreen is Screens.Select.SongSelect select && select.BeatmapSetsLoaded); + AddUntilStep("wait for song select", () => (songSelect = CurrentSubScreen as Screens.Select.SongSelect) != null); + AddUntilStep("wait for loaded", () => songSelect.AsNonNull().BeatmapSetsLoaded); if (ruleset != null) - { - AddStep($"set {ruleset.Name} ruleset", () => ((Screens.Select.SongSelect)CurrentSubScreen).Ruleset.Value = ruleset); - } + AddStep($"set {ruleset.Name} ruleset", () => songSelect.AsNonNull().Ruleset.Value = ruleset); - AddStep("select other beatmap", () => ((Screens.Select.SongSelect)CurrentSubScreen).FinaliseSelection(beatmap())); + if (mods != null) + AddStep($"set mods to {string.Join(",", mods.Select(m => m.Acronym))}", () => songSelect.AsNonNull().Mods.Value = mods); + + AddStep("select other beatmap", () => songSelect.AsNonNull().FinaliseSelection(beatmap())); AddUntilStep("wait for return to match", () => CurrentSubScreen is MultiplayerMatchSubScreen); } } From 446962446ea3e8970a2da3712d1ceca493396d6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 8 Jan 2022 14:50:49 +0100 Subject: [PATCH 73/98] Add test coverage for correct revert of mods when play starts at song select --- .../Multiplayer/TestSceneMultiplayer.cs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 1221a9f080..43f9b6636a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -7,6 +7,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input; @@ -23,6 +24,7 @@ using osu.Game.Online.Rooms; using osu.Game.Overlays.Mods; using osu.Game.Rulesets; using osu.Game.Rulesets.Catch; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens.OnlinePlay.Components; @@ -478,6 +480,45 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("Ruleset matches current item", () => Ruleset.Value.OnlineID == client.Room?.Playlist.First().RulesetID); } + [Test] + public void TestPlayStartsWithCorrectModsWhileAtSongSelect() + { + createRoom(() => new Room + { + Name = { Value = "Test Room" }, + Playlist = + { + new PlaylistItem + { + Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + } + } + }); + + pressReadyButton(); + + AddStep("Enter song select", () => + { + var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerComponents.CurrentScreen).CurrentSubScreen; + ((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(client.Room?.Settings.PlaylistItemId); + }); + + AddUntilStep("wait for song select", () => this.ChildrenOfType().FirstOrDefault()?.BeatmapSetsLoaded == true); + + AddAssert("Mods match current item", () => SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(client.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym))); + + AddStep("Switch required mods", () => ((MultiplayerMatchSongSelect)multiplayerComponents.MultiplayerScreen.CurrentSubScreen).Mods.Value = new Mod[] { new OsuModDoubleTime() }); + + AddAssert("Mods don't match current item", () => !SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(client.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym))); + + AddStep("start match externally", () => client.StartMatch()); + + AddUntilStep("play started", () => multiplayerComponents.CurrentScreen is Player); + + AddAssert("Mods match current item", () => SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(client.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym))); + } + [Test] public void TestLocalPlayDoesNotStartWhileSpectatingWithNoBeatmap() { From c5ac996e3f73ee45ee07a21e1de04fe550c5f966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 8 Jan 2022 14:55:40 +0100 Subject: [PATCH 74/98] Restore ruleset using current playlist item on resuming room sub screen Ensures that the ruleset selected at the multiplayer song selection screen does not overwrite the current playlist item's ruleset. --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index a0e7e8de87..c31239616c 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -300,6 +300,7 @@ namespace osu.Game.Screens.OnlinePlay.Match updateWorkingBeatmap(); beginHandlingTrack(); Scheduler.AddOnce(UpdateMods); + Scheduler.AddOnce(updateRuleset); } public override bool OnExiting(IScreen next) @@ -353,8 +354,7 @@ namespace osu.Game.Screens.OnlinePlay.Match .ToList(); UpdateMods(); - - Ruleset.Value = rulesets.GetRuleset(selected.RulesetID); + updateRuleset(); if (!selected.AllowedMods.Any()) { @@ -387,6 +387,14 @@ namespace osu.Game.Screens.OnlinePlay.Match Mods.Value = UserMods.Value.Concat(SelectedItem.Value.RequiredMods).ToList(); } + private void updateRuleset() + { + if (SelectedItem.Value == null || !this.IsCurrentScreen()) + return; + + Ruleset.Value = rulesets.GetRuleset(SelectedItem.Value.RulesetID); + } + private void beginHandlingTrack() { Beatmap.BindValueChanged(applyLoopingToTrack, true); From 9370e8446047af81f1fa1ae3cec83456462d923d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 8 Jan 2022 16:12:52 +0100 Subject: [PATCH 75/98] Fix effect point multiplier text box displaying too much decimal digits --- osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs b/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs index 8738d36bf4..67f1dacec4 100644 --- a/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs +++ b/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Globalization; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -9,6 +10,7 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Overlays.Settings; +using osu.Game.Utils; namespace osu.Game.Screens.Edit.Timing { @@ -66,7 +68,8 @@ namespace osu.Game.Screens.Edit.Timing Current.BindValueChanged(val => { - textBox.Text = val.NewValue.ToString(); + decimal decimalValue = slider.Current.Value.ToDecimal(NumberFormatInfo.InvariantInfo); + textBox.Text = decimalValue.ToString($@"N{FormatUtils.FindPrecision(decimalValue)}"); }, true); } From 24d377fddb18f60bb72c3da0aca6ae449ffccb7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 8 Jan 2022 17:19:52 +0100 Subject: [PATCH 76/98] Move implementation of drag handle operations to concrete classes --- .../Edit/Compose/Components/SelectionBox.cs | 5 +++-- .../Components/SelectionBoxDragHandle.cs | 8 ------- .../Components/SelectionBoxRotationHandle.cs | 22 +++++++++++++++++++ .../Components/SelectionBoxScaleHandle.cs | 11 ++++++++++ 4 files changed, 36 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 3a31f6ea8c..cda986c7cd 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -15,6 +15,7 @@ using osuTK.Input; namespace osu.Game.Screens.Edit.Compose.Components { + [Cached] public class SelectionBox : CompositeDrawable { public const float BORDER_RADIUS = 3; @@ -306,7 +307,7 @@ namespace osu.Game.Screens.Edit.Compose.Components var handle = new SelectionBoxScaleHandle { Anchor = anchor, - HandleDrag = e => OnScale?.Invoke(e.Delta, anchor) + HandleScale = (delta, a) => OnScale?.Invoke(delta, a) }; handle.OperationStarted += operationStarted; @@ -319,7 +320,7 @@ namespace osu.Game.Screens.Edit.Compose.Components var handle = new SelectionBoxRotationHandle { Anchor = anchor, - HandleDrag = e => OnRotation?.Invoke(convertDragEventToAngleOfRotation(e)) + HandleRotate = angle => OnRotation?.Invoke(angle) }; handle.OperationStarted += operationStarted; diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandle.cs index cac907ca5e..c37fefeed4 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandle.cs @@ -8,20 +8,12 @@ namespace osu.Game.Screens.Edit.Compose.Components { public abstract class SelectionBoxDragHandle : SelectionBoxControl { - public Action HandleDrag { get; set; } - protected override bool OnDragStart(DragStartEvent e) { TriggerOperationStarted(); return true; } - protected override void OnDrag(DragEvent e) - { - HandleDrag?.Invoke(e); - base.OnDrag(e); - } - protected override void OnDragEnd(DragEndEvent e) { TriggerOperationEnded(); diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs index 65a54292ab..0db36d8902 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs @@ -1,10 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Events; using osuTK; using osuTK.Graphics; @@ -12,8 +14,13 @@ namespace osu.Game.Screens.Edit.Compose.Components { public class SelectionBoxRotationHandle : SelectionBoxDragHandle { + public Action HandleRotate { get; set; } + private SpriteIcon icon; + [Resolved] + private SelectionBox selectionBox { get; set; } + [BackgroundDependencyLoader] private void load() { @@ -38,5 +45,20 @@ namespace osu.Game.Screens.Edit.Compose.Components base.UpdateHoverState(); icon.FadeColour(!IsHeld && IsHovered ? Color4.White : Color4.Black, TRANSFORM_DURATION, Easing.OutQuint); } + + protected override void OnDrag(DragEvent e) + { + base.OnDrag(e); + HandleRotate?.Invoke(convertDragEventToAngleOfRotation(e)); + } + + private float convertDragEventToAngleOfRotation(DragEvent e) + { + // Adjust coordinate system to the center of SelectionBox + float startAngle = MathF.Atan2(e.LastMousePosition.Y - selectionBox.DrawHeight / 2, e.LastMousePosition.X - selectionBox.DrawWidth / 2); + float endAngle = MathF.Atan2(e.MousePosition.Y - selectionBox.DrawHeight / 2, e.MousePosition.X - selectionBox.DrawWidth / 2); + + return (endAngle - startAngle) * 180 / MathF.PI; + } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs index a87c661f45..1f82f28380 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs @@ -1,17 +1,28 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Input.Events; using osuTK; namespace osu.Game.Screens.Edit.Compose.Components { public class SelectionBoxScaleHandle : SelectionBoxDragHandle { + public Action HandleScale { get; set; } + [BackgroundDependencyLoader] private void load() { Size = new Vector2(10); } + + protected override void OnDrag(DragEvent e) + { + HandleScale?.Invoke(e.Delta, Anchor); + base.OnDrag(e); + } } } From d76c674abc569c383bc8adbc7c4d9986a2029de4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 8 Jan 2022 17:42:58 +0100 Subject: [PATCH 77/98] Add tooltip with relative rotation in degrees to rotation handles --- .../Components/SelectionBoxRotationHandle.cs | 46 ++++++++++++++++++- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs index 0db36d8902..22479bd9b3 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs @@ -3,21 +3,29 @@ using System; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Extensions.EnumExtensions; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osuTK; using osuTK.Graphics; namespace osu.Game.Screens.Edit.Compose.Components { - public class SelectionBoxRotationHandle : SelectionBoxDragHandle + public class SelectionBoxRotationHandle : SelectionBoxDragHandle, IHasTooltip { public Action HandleRotate { get; set; } + public LocalisableString TooltipText { get; private set; } + private SpriteIcon icon; + private readonly Bindable cumulativeRotation = new Bindable(); + [Resolved] private SelectionBox selectionBox { get; set; } @@ -40,16 +48,45 @@ namespace osu.Game.Screens.Edit.Compose.Components }); } + protected override void LoadComplete() + { + base.LoadComplete(); + cumulativeRotation.BindValueChanged(_ => updateTooltipText(), true); + } + protected override void UpdateHoverState() { base.UpdateHoverState(); icon.FadeColour(!IsHeld && IsHovered ? Color4.White : Color4.Black, TRANSFORM_DURATION, Easing.OutQuint); } + protected override bool OnDragStart(DragStartEvent e) + { + bool handle = base.OnDragStart(e); + if (handle) + cumulativeRotation.Value = 0; + return handle; + } + protected override void OnDrag(DragEvent e) { base.OnDrag(e); - HandleRotate?.Invoke(convertDragEventToAngleOfRotation(e)); + + float instantaneousAngle = convertDragEventToAngleOfRotation(e); + cumulativeRotation.Value += instantaneousAngle; + + if (cumulativeRotation.Value < -180) + cumulativeRotation.Value += 360; + else if (cumulativeRotation.Value > 180) + cumulativeRotation.Value -= 360; + + HandleRotate?.Invoke(instantaneousAngle); + } + + protected override void OnDragEnd(DragEndEvent e) + { + base.OnDragEnd(e); + cumulativeRotation.Value = null; } private float convertDragEventToAngleOfRotation(DragEvent e) @@ -60,5 +97,10 @@ namespace osu.Game.Screens.Edit.Compose.Components return (endAngle - startAngle) * 180 / MathF.PI; } + + private void updateTooltipText() + { + TooltipText = cumulativeRotation.Value?.ToLocalisableString("0.0°") ?? default(LocalisableString); + } } } From 82d6639a3b3e8c576e036f95b50367b7c97e8d2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 9 Jan 2022 14:31:37 +0100 Subject: [PATCH 78/98] Decouple rankings table test from online API --- .../Visual/Online/TestSceneRankingsTables.cs | 158 +++++++++++------- 1 file changed, 97 insertions(+), 61 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs index ee109189c7..35e219f839 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs @@ -1,38 +1,27 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using osu.Framework.Graphics.Containers; using osu.Game.Overlays.Rankings.Tables; using osu.Framework.Graphics; -using osu.Game.Online.API.Requests; -using osu.Game.Rulesets; using System.Threading; -using osu.Game.Online.API; -using osu.Game.Rulesets.Osu; -using osu.Game.Rulesets.Mania; -using osu.Game.Rulesets.Taiko; -using osu.Game.Rulesets.Catch; using osu.Framework.Allocation; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; -using osu.Game.Overlays.Rankings; +using osu.Game.Users; namespace osu.Game.Tests.Visual.Online { public class TestSceneRankingsTables : OsuTestScene { - protected override bool UseOnlineAPI => true; - - [Resolved] - private IAPIProvider api { get; set; } - [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); private readonly BasicScrollContainer scrollFlow; private readonly LoadingLayer loading; private CancellationTokenSource cancellationToken; - private APIRequest request; public TestSceneRankingsTables() { @@ -53,73 +42,120 @@ namespace osu.Game.Tests.Visual.Online { base.LoadComplete(); - AddStep("Osu performance", () => createPerformanceTable(new OsuRuleset().RulesetInfo, null)); - AddStep("Mania scores", () => createScoreTable(new ManiaRuleset().RulesetInfo)); - AddStep("Taiko country scores", () => createCountryTable(new TaikoRuleset().RulesetInfo)); - AddStep("Catch US performance page 10", () => createPerformanceTable(new CatchRuleset().RulesetInfo, "US", 10)); - AddStep("Osu spotlight table (chart 271)", () => createSpotlightTable(new OsuRuleset().RulesetInfo, 271)); + AddStep("User performance", createPerformanceTable); + AddStep("User scores", createScoreTable); + AddStep("Country scores", createCountryTable); } - private void createCountryTable(RulesetInfo ruleset, int page = 1) + private void createCountryTable() { onLoadStarted(); - request = new GetCountryRankingsRequest(ruleset, page); - ((GetCountryRankingsRequest)request).Success += rankings => Schedule(() => + var countries = new List { - var table = new CountriesTable(page, rankings.Countries); - loadTable(table); - }); + new CountryStatistics + { + Country = new Country { FlagName = "US", FullName = "United States" }, + FlagName = "US", + ActiveUsers = 2_972_623, + PlayCount = 3_086_515_743, + RankedScore = 449_407_643_332_546, + Performance = 371_974_024 + }, + new CountryStatistics + { + Country = new Country { FlagName = "RU", FullName = "Russian Federation" }, + FlagName = "RU", + ActiveUsers = 1_609_989, + PlayCount = 1_637_052_841, + RankedScore = 221_660_827_473_004, + Performance = 163_426_476 + } + }; - api.Queue(request); + var table = new CountriesTable(1, countries); + loadTable(table); } - private void createPerformanceTable(RulesetInfo ruleset, string country, int page = 1) + private static List createUserStatistics() => new List + { + new UserStatistics + { + User = new APIUser + { + Username = "first active user", + Country = new Country { FlagName = "JP" }, + Active = true, + }, + Accuracy = 0.9972, + PlayCount = 233_215, + TotalScore = 983_231_234_656, + RankedScore = 593_231_345_897, + PP = 23_934, + GradesCount = new UserStatistics.Grades + { + SS = 35_132, + S = 23_345, + A = 12_234 + } + }, + new UserStatistics + { + User = new APIUser + { + Username = "inactive user", + Country = new Country { FlagName = "AU" }, + Active = false, + }, + Accuracy = 0.9831, + PlayCount = 195_342, + TotalScore = 683_231_234_656, + RankedScore = 393_231_345_897, + PP = 20_934, + GradesCount = new UserStatistics.Grades + { + SS = 32_132, + S = 20_345, + A = 9_234 + } + }, + new UserStatistics + { + User = new APIUser + { + Username = "second active user", + Country = new Country { FlagName = "PL" }, + Active = true, + }, + Accuracy = 0.9584, + PlayCount = 100_903, + TotalScore = 97_242_983_434, + RankedScore = 3_156_345_897, + PP = 9_568, + GradesCount = new UserStatistics.Grades + { + SS = 13_152, + S = 24_375, + A = 9_960 + } + }, + }; + + private void createPerformanceTable() { onLoadStarted(); - - request = new GetUserRankingsRequest(ruleset, country: country, page: page); - ((GetUserRankingsRequest)request).Success += rankings => Schedule(() => - { - var table = new PerformanceTable(page, rankings.Users); - loadTable(table); - }); - - api.Queue(request); + loadTable(new PerformanceTable(1, createUserStatistics())); } - private void createScoreTable(RulesetInfo ruleset, int page = 1) + private void createScoreTable() { onLoadStarted(); - - request = new GetUserRankingsRequest(ruleset, UserRankingsType.Score, page); - ((GetUserRankingsRequest)request).Success += rankings => Schedule(() => - { - var table = new ScoresTable(page, rankings.Users); - loadTable(table); - }); - - api.Queue(request); - } - - private void createSpotlightTable(RulesetInfo ruleset, int spotlight) - { - onLoadStarted(); - - request = new GetSpotlightRankingsRequest(ruleset, spotlight, RankingsSortCriteria.All); - ((GetSpotlightRankingsRequest)request).Success += rankings => Schedule(() => - { - var table = new ScoresTable(1, rankings.Users); - loadTable(table); - }); - - api.Queue(request); + loadTable(new ScoresTable(1, createUserStatistics())); } private void onLoadStarted() { loading.Show(); - request?.Cancel(); cancellationToken?.Cancel(); cancellationToken = new CancellationTokenSource(); } From 12c8243a9b55b6ed2c1509852c191fb7b13a8898 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 9 Jan 2022 14:32:33 +0100 Subject: [PATCH 79/98] Fade out inactive player rows on user ranking table --- .../Overlays/Rankings/Tables/RankingsTable.cs | 8 ++++--- .../Rankings/Tables/UserBasedTable.cs | 23 +++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 6e6230f958..fd69b6c80a 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -54,13 +54,15 @@ namespace osu.Game.Overlays.Rankings.Tables Spacing = new Vector2(0, row_spacing), }); - rankings.ForEach(_ => backgroundFlow.Add(new TableRowBackground { Height = row_height })); + rankings.ForEach(s => backgroundFlow.Add(CreateRowBackground(s))); Columns = mainHeaders.Concat(CreateAdditionalHeaders()).Cast().ToArray(); - Content = rankings.Select((s, i) => createContent((page - 1) * items_per_page + i, s)).ToArray().ToRectangular(); + Content = rankings.Select((s, i) => CreateRowContent((page - 1) * items_per_page + i, s)).ToArray().ToRectangular(); } - private Drawable[] createContent(int index, TModel item) => new Drawable[] { createIndexDrawable(index), createMainContent(item) }.Concat(CreateAdditionalContent(item)).ToArray(); + protected virtual Drawable CreateRowBackground(TModel item) => new TableRowBackground { Height = row_height }; + + protected virtual Drawable[] CreateRowContent(int index, TModel item) => new Drawable[] { createIndexDrawable(index), createMainContent(item) }.Concat(CreateAdditionalContent(item)).ToArray(); private static RankingsTableColumn[] mainHeaders => new[] { diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs index cc2ef55a2b..c403d734d3 100644 --- a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs @@ -24,6 +24,29 @@ namespace osu.Game.Overlays.Rankings.Tables protected virtual IEnumerable GradeColumns => new List { RankingsStrings.Statss, RankingsStrings.Stats, RankingsStrings.Stata }; + protected override Drawable CreateRowBackground(UserStatistics item) + { + var background = base.CreateRowBackground(item); + + if (!item.User.Active) + background.Alpha = 0.5f; + + return background; + } + + protected override Drawable[] CreateRowContent(int index, UserStatistics item) + { + var content = base.CreateRowContent(index, item); + + if (!item.User.Active) + { + foreach (var d in content) + d.Alpha = 0.5f; + } + + return content; + } + protected override RankingsTableColumn[] CreateAdditionalHeaders() => new[] { new RankingsTableColumn(RankingsStrings.StatAccuracy, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), From 2e9ba40ae27fb3b177e8e86038a4328b8f2bb02c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 9 Jan 2022 14:46:15 +0100 Subject: [PATCH 80/98] Add references to web implementation wrt property used --- osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs index c403d734d3..5d150c9535 100644 --- a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs @@ -28,6 +28,7 @@ namespace osu.Game.Overlays.Rankings.Tables { var background = base.CreateRowBackground(item); + // see: https://github.com/ppy/osu-web/blob/9de00a0b874c56893d98261d558d78d76259d81b/resources/views/multiplayer/rooms/_rankings_table.blade.php#L23 if (!item.User.Active) background.Alpha = 0.5f; @@ -38,6 +39,7 @@ namespace osu.Game.Overlays.Rankings.Tables { var content = base.CreateRowContent(index, item); + // see: https://github.com/ppy/osu-web/blob/9de00a0b874c56893d98261d558d78d76259d81b/resources/views/multiplayer/rooms/_rankings_table.blade.php#L23 if (!item.User.Active) { foreach (var d in content) From ca162ed09a68693cbef2c0bc2c1ae3b1ae3e7c44 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jan 2022 11:36:26 +0900 Subject: [PATCH 81/98] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 67a9cd41dd..3b75014aad 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c7913cb71d..24e5858cb0 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 63dc889e3c..b0dafa1daa 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + From 3d14511286f8badd817e4543c4f07123d4deb5d6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jan 2022 12:17:32 +0900 Subject: [PATCH 82/98] Remove MD5 comparison also --- osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs index b886a61c0f..9ac7838821 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs @@ -95,7 +95,6 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.That(decodedAfterEncode, Is.Not.Null); Assert.That(decodedAfterEncode.ScoreInfo.User.Username, Is.EqualTo(scoreInfo.User.Username)); - Assert.That(decodedAfterEncode.ScoreInfo.BeatmapInfo.MD5Hash, Is.EqualTo(scoreInfo.BeatmapInfo.MD5Hash)); Assert.That(decodedAfterEncode.ScoreInfo.Ruleset, Is.EqualTo(scoreInfo.Ruleset)); Assert.That(decodedAfterEncode.ScoreInfo.TotalScore, Is.EqualTo(scoreInfo.TotalScore)); Assert.That(decodedAfterEncode.ScoreInfo.MaxCombo, Is.EqualTo(scoreInfo.MaxCombo)); From 02d8a6359a98a3c8ed393eda5c26376079d8cd79 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jan 2022 12:28:09 +0900 Subject: [PATCH 83/98] Update `FilterMatchingTest` and filter code to use ruleset's `OnlineID` The tests were relying on the `RulesetID` being set to 0 in the example beatmap, even though the ruleset *instance* was set to ID 5. This explicitly adds that 0 value to show intent, and also removes the incorrect specification of 5 (which would cause the convert filter tests to fail). Also updates the filter code to use `OnlineID`, which is required in realm changes. --- osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs | 3 ++- osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs b/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs index 55378043e6..8ba3d1a6c7 100644 --- a/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs +++ b/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs @@ -16,7 +16,8 @@ namespace osu.Game.Tests.NonVisual.Filtering { private BeatmapInfo getExampleBeatmap() => new BeatmapInfo { - Ruleset = new RulesetInfo { OnlineID = 5 }, + Ruleset = new RulesetInfo { OnlineID = 0 }, + RulesetID = 0, StarRating = 4.0d, BaseDifficulty = new BeatmapDifficulty { diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index cc2db6ed31..d2c7c75da8 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -28,8 +28,8 @@ namespace osu.Game.Screens.Select.Carousel bool match = criteria.Ruleset == null || - BeatmapInfo.RulesetID == criteria.Ruleset.ID || - (BeatmapInfo.RulesetID == 0 && criteria.Ruleset.ID > 0 && criteria.AllowConvertedBeatmaps); + BeatmapInfo.RulesetID == criteria.Ruleset.OnlineID || + (BeatmapInfo.RulesetID == 0 && criteria.Ruleset.OnlineID > 0 && criteria.AllowConvertedBeatmaps); if (BeatmapInfo.BeatmapSet?.Equals(criteria.SelectedBeatmapSet) == true) { From d072f1d08d2e045e7546edc20df60959a4193032 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Jan 2022 12:54:24 +0900 Subject: [PATCH 84/98] Add mention to detach methods of only running once --- osu.Game/Database/RealmObjectExtensions.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Database/RealmObjectExtensions.cs b/osu.Game/Database/RealmObjectExtensions.cs index e5177823ba..e09f046421 100644 --- a/osu.Game/Database/RealmObjectExtensions.cs +++ b/osu.Game/Database/RealmObjectExtensions.cs @@ -26,6 +26,9 @@ namespace osu.Game.Database /// /// Create a detached copy of the each item in the collection. /// + /// + /// Items which are already detached (ie. not managed by realm) will not be modified. + /// /// A list of managed s to detach. /// The type of object. /// A list containing non-managed copies of provided items. @@ -42,6 +45,9 @@ namespace osu.Game.Database /// /// Create a detached copy of the item. /// + /// + /// If the item if already detached (ie. not managed by realm) it will not be detached again and the original instance will be returned. This allows this method to be potentially called at multiple levels while only incurring the clone overhead once. + /// /// The managed to detach. /// The type of object. /// A non-managed copy of provided item. Will return the provided item if already detached. From b88a65166eb8cff24a1f4e9558de34721fd934fd Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 11 Jan 2022 10:22:16 +0900 Subject: [PATCH 85/98] Fix pp counter underflow with SpunOut mod --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index d7d294df47..fdf646ef85 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (mods.Any(m => m is OsuModNoFail)) multiplier *= Math.Max(0.90, 1.0 - 0.02 * effectiveMissCount); - if (mods.Any(m => m is OsuModSpunOut)) + if (mods.Any(m => m is OsuModSpunOut) && totalHits > 0) multiplier *= 1.0 - Math.Pow((double)Attributes.SpinnerCount / totalHits, 0.85); if (mods.Any(h => h is OsuModRelax)) From a09563a7d9ea559ad179d5997c0d0ae3c3265235 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 11 Jan 2022 11:00:30 +0900 Subject: [PATCH 86/98] Fix calculation of most common beat length for mania scroll speed --- .../UI/Scrolling/DrawableScrollingRuleset.cs | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs index 1f3a937311..926f2fd539 100644 --- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs +++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs @@ -116,25 +116,11 @@ namespace osu.Game.Rulesets.UI.Scrolling if (RelativeScaleBeatLengths) { - IReadOnlyList timingPoints = Beatmap.ControlPointInfo.TimingPoints; - double maxDuration = 0; + baseBeatLength = Beatmap.GetMostCommonBeatLength(); - for (int i = 0; i < timingPoints.Count; i++) - { - if (timingPoints[i].Time > lastObjectTime) - break; - - double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time : lastObjectTime; - double duration = endTime - timingPoints[i].Time; - - if (duration > maxDuration) - { - maxDuration = duration; - // The slider multiplier is post-multiplied to determine the final velocity, but for relative scale beat lengths - // the multiplier should not affect the effective timing point (the longest in the beatmap), so it is factored out here - baseBeatLength = timingPoints[i].BeatLength / Beatmap.Difficulty.SliderMultiplier; - } - } + // The slider multiplier is post-multiplied to determine the final velocity, but for relative scale beat lengths + // the multiplier should not affect the effective timing point (the longest in the beatmap), so it is factored out here + baseBeatLength /= Beatmap.Difficulty.SliderMultiplier; } // Merge sequences of timing and difficulty control points to create the aggregate "multiplier" control point From 81fed4c6bfed04d3a6ede5bbc685320a91ddcdd7 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 11 Jan 2022 12:55:12 +0900 Subject: [PATCH 87/98] Use time=0 as for the first control point --- osu.Game/Beatmaps/Beatmap.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index f69a10f000..133a22d8f7 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -93,8 +93,10 @@ namespace osu.Game.Beatmaps if (t.Time > lastTime) return (beatLength: t.BeatLength, 0); + double currentTime = i == 0 ? 0 : t.Time; double nextTime = i == ControlPointInfo.TimingPoints.Count - 1 ? lastTime : ControlPointInfo.TimingPoints[i + 1].Time; - return (beatLength: t.BeatLength, duration: nextTime - t.Time); + + return (beatLength: t.BeatLength, duration: nextTime - currentTime); }) // Aggregate durations into a set of (beatLength, duration) tuples for each beat length .GroupBy(t => Math.Round(t.beatLength * 1000) / 1000) From 38a51b9ce014fd210a02e8d499ec58295d16ec2b Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 11 Jan 2022 13:53:51 +0900 Subject: [PATCH 88/98] Add comment --- osu.Game/Beatmaps/Beatmap.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 133a22d8f7..435183fe92 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -93,6 +93,8 @@ namespace osu.Game.Beatmaps if (t.Time > lastTime) return (beatLength: t.BeatLength, 0); + // osu-stable forced the first control point to start at 0. + // This is reproduced here to maintain compatibility around osu!mania scroll speed and song select display. double currentTime = i == 0 ? 0 : t.Time; double nextTime = i == ControlPointInfo.TimingPoints.Count - 1 ? lastTime : ControlPointInfo.TimingPoints[i + 1].Time; From 797d3f5d65e9880c613375903532b70897fb6693 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 Jan 2022 15:09:51 +0900 Subject: [PATCH 89/98] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index f19a33ba75..92adbebf5a 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 43bb0cdb88..1430320c87 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 49301fc329..3cef888447 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -60,7 +60,7 @@ - + @@ -83,7 +83,7 @@ - + From 269df08fc9453b0ce2eaa9c24bd4a224e82fc9b1 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 11 Jan 2022 17:42:02 +0900 Subject: [PATCH 90/98] Make ChatLink tests into proper unit test methods --- .../Visual/Online/TestSceneChatLink.cs | 118 ++++++++++-------- 1 file changed, 65 insertions(+), 53 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs index a03c00eb58..ce5c38d33f 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs @@ -50,58 +50,19 @@ namespace osu.Game.Tests.Visual.Online Dependencies.Cache(new ChatOverlay()); Dependencies.Cache(dialogOverlay); - - testLinksGeneral(); - testEcho(); } - private void clear() => AddStep("clear messages", textContainer.Clear); - - private void addMessageWithChecks(string text, int linkAmount = 0, bool isAction = false, bool isImportant = false, params LinkAction[] expectedActions) + [SetUp] + public void Setup() => Schedule(() => { - int index = textContainer.Count + 1; - var newLine = new ChatLine(new DummyMessage(text, isAction, isImportant, index)); - textContainer.Add(newLine); + textContainer.Clear(); + }); - AddAssert($"msg #{index} has {linkAmount} link(s)", () => newLine.Message.Links.Count == linkAmount); - AddAssert($"msg #{index} has the right action", hasExpectedActions); - //AddAssert($"msg #{index} is " + (isAction ? "italic" : "not italic"), () => newLine.ContentFlow.Any() && isAction == isItalic()); - AddAssert($"msg #{index} shows {linkAmount} link(s)", isShowingLinks); - - bool hasExpectedActions() - { - var expectedActionsList = expectedActions.ToList(); - - if (expectedActionsList.Count != newLine.Message.Links.Count) - return false; - - for (int i = 0; i < newLine.Message.Links.Count; i++) - { - var action = newLine.Message.Links[i].Action; - if (action != expectedActions[i]) return false; - } - - return true; - } - - //bool isItalic() => newLine.ContentFlow.Where(d => d is OsuSpriteText).Cast().All(sprite => sprite.Font.Italics); - - bool isShowingLinks() - { - bool hasBackground = !string.IsNullOrEmpty(newLine.Message.Sender.Colour); - - Color4 textColour = isAction && hasBackground ? Color4Extensions.FromHex(newLine.Message.Sender.Colour) : Color4.White; - - var linkCompilers = newLine.ContentFlow.Where(d => d is DrawableLinkCompiler).ToList(); - var linkSprites = linkCompilers.SelectMany(comp => ((DrawableLinkCompiler)comp).Parts); - - return linkSprites.All(d => d.Colour == linkColour) - && newLine.ContentFlow.Except(linkSprites.Concat(linkCompilers)).All(d => d.Colour == textColour); - } - } - - private void testLinksGeneral() + [Test] + public void TestLinksGeneral() { + int messageIndex = 0; + addMessageWithChecks("test!"); addMessageWithChecks("dev.ppy.sh!"); addMessageWithChecks("https://dev.ppy.sh!", 1, expectedActions: LinkAction.External); @@ -117,7 +78,8 @@ namespace osu.Game.Tests.Visual.Online expectedActions: new[] { LinkAction.External, LinkAction.OpenBeatmap, LinkAction.External }); addMessageWithChecks("[https://dev.ppy.sh/home New link format with escaped [and \\[ paired] braces]", 1, expectedActions: LinkAction.External); addMessageWithChecks("[Markdown link format with escaped [and \\[ paired] braces](https://dev.ppy.sh/home)", 1, expectedActions: LinkAction.External); - addMessageWithChecks("(Old link format with escaped (and \\( paired) parentheses)[https://dev.ppy.sh/home] and [[also a rogue wiki link]]", 2, expectedActions: new[] { LinkAction.External, LinkAction.External }); + addMessageWithChecks("(Old link format with escaped (and \\( paired) parentheses)[https://dev.ppy.sh/home] and [[also a rogue wiki link]]", 2, + expectedActions: new[] { LinkAction.External, LinkAction.External }); // note that there's 0 links here (they get removed if a channel is not found) addMessageWithChecks("#lobby or #osu would be blue (and work) in the ChatDisplay test (when a proper ChatOverlay is present)."); addMessageWithChecks("I am important!", 0, false, true); @@ -129,11 +91,60 @@ namespace osu.Game.Tests.Visual.Online addMessageWithChecks("Join my osu://chan/#english.", 1, expectedActions: LinkAction.OpenChannel); addMessageWithChecks("Join my #english or #japanese channels.", 2, expectedActions: new[] { LinkAction.OpenChannel, LinkAction.OpenChannel }); addMessageWithChecks("Join my #english or #nonexistent #hashtag channels.", 1, expectedActions: LinkAction.OpenChannel); + + void addMessageWithChecks(string text, int linkAmount = 0, bool isAction = false, bool isImportant = false, params LinkAction[] expectedActions) + { + ChatLine newLine = null; + int index = messageIndex++; + + AddStep("add message", () => + { + newLine = new ChatLine(new DummyMessage(text, isAction, isImportant, index)); + textContainer.Add(newLine); + }); + + AddAssert($"msg #{index} has {linkAmount} link(s)", () => newLine.Message.Links.Count == linkAmount); + AddAssert($"msg #{index} has the right action", hasExpectedActions); + //AddAssert($"msg #{index} is " + (isAction ? "italic" : "not italic"), () => newLine.ContentFlow.Any() && isAction == isItalic()); + AddAssert($"msg #{index} shows {linkAmount} link(s)", isShowingLinks); + + bool hasExpectedActions() + { + var expectedActionsList = expectedActions.ToList(); + + if (expectedActionsList.Count != newLine.Message.Links.Count) + return false; + + for (int i = 0; i < newLine.Message.Links.Count; i++) + { + var action = newLine.Message.Links[i].Action; + if (action != expectedActions[i]) return false; + } + + return true; + } + + //bool isItalic() => newLine.ContentFlow.Where(d => d is OsuSpriteText).Cast().All(sprite => sprite.Font.Italics); + + bool isShowingLinks() + { + bool hasBackground = !string.IsNullOrEmpty(newLine.Message.Sender.Colour); + + Color4 textColour = isAction && hasBackground ? Color4Extensions.FromHex(newLine.Message.Sender.Colour) : Color4.White; + + var linkCompilers = newLine.ContentFlow.Where(d => d is DrawableLinkCompiler).ToList(); + var linkSprites = linkCompilers.SelectMany(comp => ((DrawableLinkCompiler)comp).Parts); + + return linkSprites.All(d => d.Colour == linkColour) + && newLine.ContentFlow.Except(linkSprites.Concat(linkCompilers)).All(d => d.Colour == textColour); + } + } } - private void testEcho() + [Test] + public void TestEcho() { - int echoCounter = 0; + int messageIndex = 0; addEchoWithWait("sent!", "received!"); addEchoWithWait("https://dev.ppy.sh/home", null, 500); @@ -142,15 +153,16 @@ namespace osu.Game.Tests.Visual.Online void addEchoWithWait(string text, string completeText = null, double delay = 250) { - var newLine = new ChatLine(new DummyEchoMessage(text)); + int index = messageIndex++; - AddStep($"send msg #{++echoCounter} after {delay}ms", () => + AddStep($"send msg #{index} after {delay}ms", () => { + ChatLine newLine = new ChatLine(new DummyEchoMessage(text)); textContainer.Add(newLine); Scheduler.AddDelayed(() => newLine.Message = new DummyMessage(completeText ?? text), delay); }); - AddUntilStep($"wait for msg #{echoCounter}", () => textContainer.All(line => line.Message is DummyMessage)); + AddUntilStep($"wait for msg #{index}", () => textContainer.All(line => line.Message is DummyMessage)); } } From 3cb5f43f775e4aa2e15c94b8d635aac7d7fca823 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 11 Jan 2022 17:45:31 +0900 Subject: [PATCH 91/98] Fix incorrect action returned for wiki links in DEBUG mode --- osu.Game.Tests/Visual/Online/TestSceneChatLink.cs | 4 ++-- osu.Game/Online/Chat/MessageFormatter.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs index ce5c38d33f..12b5f64559 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs @@ -67,7 +67,7 @@ namespace osu.Game.Tests.Visual.Online addMessageWithChecks("dev.ppy.sh!"); addMessageWithChecks("https://dev.ppy.sh!", 1, expectedActions: LinkAction.External); addMessageWithChecks("00:12:345 (1,2) - Test?", 1, expectedActions: LinkAction.OpenEditorTimestamp); - addMessageWithChecks("Wiki link for tasty [[Performance Points]]", 1, expectedActions: LinkAction.External); + addMessageWithChecks("Wiki link for tasty [[Performance Points]]", 1, expectedActions: LinkAction.OpenWiki); addMessageWithChecks("(osu forums)[https://dev.ppy.sh/forum] (old link format)", 1, expectedActions: LinkAction.External); addMessageWithChecks("[https://dev.ppy.sh/home New site] (new link format)", 1, expectedActions: LinkAction.External); addMessageWithChecks("[osu forums](https://dev.ppy.sh/forum) (new link format 2)", 1, expectedActions: LinkAction.External); @@ -79,7 +79,7 @@ namespace osu.Game.Tests.Visual.Online addMessageWithChecks("[https://dev.ppy.sh/home New link format with escaped [and \\[ paired] braces]", 1, expectedActions: LinkAction.External); addMessageWithChecks("[Markdown link format with escaped [and \\[ paired] braces](https://dev.ppy.sh/home)", 1, expectedActions: LinkAction.External); addMessageWithChecks("(Old link format with escaped (and \\( paired) parentheses)[https://dev.ppy.sh/home] and [[also a rogue wiki link]]", 2, - expectedActions: new[] { LinkAction.External, LinkAction.External }); + expectedActions: new[] { LinkAction.External, LinkAction.OpenWiki }); // note that there's 0 links here (they get removed if a channel is not found) addMessageWithChecks("#lobby or #osu would be blue (and work) in the ChatDisplay test (when a proper ChatOverlay is present)."); addMessageWithChecks("I am important!", 0, false, true); diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index 92911f0f51..0543c08308 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -262,7 +262,7 @@ namespace osu.Game.Online.Chat handleMatches(old_link_regex, "{1}", "{2}", result, startIndex, escapeChars: new[] { '(', ')' }); // handle wiki links - handleMatches(wiki_regex, "{1}", "https://osu.ppy.sh/wiki/{1}", result, startIndex); + handleMatches(wiki_regex, "{1}", $"https://{websiteRootUrl}/wiki/{{1}}", result, startIndex); // handle bare links handleAdvanced(advanced_link_regex, result, startIndex); From ef66ec462297f3680de43df9d7c2de3974ad3268 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 11 Jan 2022 17:53:15 +0900 Subject: [PATCH 92/98] Also fix MessageFormatter tests --- osu.Game.Tests/Chat/MessageFormatterTests.cs | 39 ++++++++++++++------ osu.Game/Online/Chat/MessageFormatter.cs | 5 ++- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Chat/MessageFormatterTests.cs b/osu.Game.Tests/Chat/MessageFormatterTests.cs index af87fc17ad..8def8005f1 100644 --- a/osu.Game.Tests/Chat/MessageFormatterTests.cs +++ b/osu.Game.Tests/Chat/MessageFormatterTests.cs @@ -9,6 +9,21 @@ namespace osu.Game.Tests.Chat [TestFixture] public class MessageFormatterTests { + private string originalWebsiteRootUrl; + + [OneTimeSetUp] + public void OneTimeSetUp() + { + originalWebsiteRootUrl = MessageFormatter.WebsiteRootUrl; + MessageFormatter.WebsiteRootUrl = "dev.ppy.sh"; + } + + [OneTimeTearDown] + public void OneTimeTearDown() + { + MessageFormatter.WebsiteRootUrl = originalWebsiteRootUrl; + } + [Test] public void TestBareLink() { @@ -32,8 +47,6 @@ namespace osu.Game.Tests.Chat [TestCase(LinkAction.External, "https://dev.ppy.sh/beatmapsets/discussions/123", "https://dev.ppy.sh/beatmapsets/discussions/123")] public void TestBeatmapLinks(LinkAction expectedAction, string expectedArg, string link) { - MessageFormatter.WebsiteRootUrl = "dev.ppy.sh"; - Message result = MessageFormatter.FormatMessage(new Message { Content = link }); Assert.AreEqual(result.Content, result.DisplayContent); @@ -47,7 +60,10 @@ namespace osu.Game.Tests.Chat [Test] public void TestMultipleComplexLinks() { - Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a http://test.io/link#fragment. (see https://twitter.com). Also, This string should not be altered. http://example.com/" }); + Message result = MessageFormatter.FormatMessage(new Message + { + Content = "This is a http://test.io/link#fragment. (see https://twitter.com). Also, This string should not be altered. http://example.com/" + }); Assert.AreEqual(result.Content, result.DisplayContent); Assert.AreEqual(3, result.Links.Count); @@ -104,7 +120,7 @@ namespace osu.Game.Tests.Chat Assert.AreEqual("This is a Wiki Link.", result.DisplayContent); Assert.AreEqual(1, result.Links.Count); - Assert.AreEqual("https://osu.ppy.sh/wiki/Wiki Link", result.Links[0].Url); + Assert.AreEqual("https://dev.ppy.sh/wiki/Wiki Link", result.Links[0].Url); Assert.AreEqual(10, result.Links[0].Index); Assert.AreEqual(9, result.Links[0].Length); } @@ -117,15 +133,15 @@ namespace osu.Game.Tests.Chat Assert.AreEqual("This is a Wiki Link Wiki:LinkWiki.Link.", result.DisplayContent); Assert.AreEqual(3, result.Links.Count); - Assert.AreEqual("https://osu.ppy.sh/wiki/Wiki Link", result.Links[0].Url); + Assert.AreEqual("https://dev.ppy.sh/wiki/Wiki Link", result.Links[0].Url); Assert.AreEqual(10, result.Links[0].Index); Assert.AreEqual(9, result.Links[0].Length); - Assert.AreEqual("https://osu.ppy.sh/wiki/Wiki:Link", result.Links[1].Url); + Assert.AreEqual("https://dev.ppy.sh/wiki/Wiki:Link", result.Links[1].Url); Assert.AreEqual(20, result.Links[1].Index); Assert.AreEqual(9, result.Links[1].Length); - Assert.AreEqual("https://osu.ppy.sh/wiki/Wiki.Link", result.Links[2].Url); + Assert.AreEqual("https://dev.ppy.sh/wiki/Wiki.Link", result.Links[2].Url); Assert.AreEqual(29, result.Links[2].Index); Assert.AreEqual(9, result.Links[2].Length); } @@ -445,12 +461,15 @@ namespace osu.Game.Tests.Chat [Test] public void TestLinkComplex() { - Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [http://www.simple-test.com simple test] with some [traps] and [[wiki links]]. Don't forget to visit https://osu.ppy.sh (now!)[http://google.com]\uD83D\uDE12" }); + Message result = MessageFormatter.FormatMessage(new Message + { + Content = "This is a [http://www.simple-test.com simple test] with some [traps] and [[wiki links]]. Don't forget to visit https://osu.ppy.sh (now!)[http://google.com]\uD83D\uDE12" + }); Assert.AreEqual("This is a simple test with some [traps] and wiki links. Don't forget to visit https://osu.ppy.sh now!\0\0\0", result.DisplayContent); Assert.AreEqual(5, result.Links.Count); - Link f = result.Links.Find(l => l.Url == "https://osu.ppy.sh/wiki/wiki links"); + Link f = result.Links.Find(l => l.Url == "https://dev.ppy.sh/wiki/wiki links"); Assert.That(f, Is.Not.Null); Assert.AreEqual(44, f.Index); Assert.AreEqual(10, f.Length); @@ -514,8 +533,6 @@ namespace osu.Game.Tests.Chat [TestCase("https://dev.ppy.sh/home/changelog/lazer/2021.1012", "lazer/2021.1012")] public void TestChangelogLinks(string link, string expectedArg) { - MessageFormatter.WebsiteRootUrl = "dev.ppy.sh"; - LinkDetails result = MessageFormatter.GetLinkDetails(link); Assert.AreEqual(LinkAction.OpenChangelog, result.Action); diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index 0543c08308..d7974004b1 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -57,6 +57,7 @@ namespace osu.Game.Online.Chat /// public static string WebsiteRootUrl { + get => websiteRootUrl; set => websiteRootUrl = value .Trim('/') // trim potential trailing slash/ .Split('/').Last(); // only keep domain name, ignoring protocol. @@ -134,7 +135,7 @@ namespace osu.Game.Online.Chat case "http": case "https": // length > 3 since all these links need another argument to work - if (args.Length > 3 && args[1].EndsWith(websiteRootUrl, StringComparison.OrdinalIgnoreCase)) + if (args.Length > 3 && args[1].EndsWith(WebsiteRootUrl, StringComparison.OrdinalIgnoreCase)) { string mainArg = args[3]; @@ -262,7 +263,7 @@ namespace osu.Game.Online.Chat handleMatches(old_link_regex, "{1}", "{2}", result, startIndex, escapeChars: new[] { '(', ')' }); // handle wiki links - handleMatches(wiki_regex, "{1}", $"https://{websiteRootUrl}/wiki/{{1}}", result, startIndex); + handleMatches(wiki_regex, "{1}", $"https://{WebsiteRootUrl}/wiki/{{1}}", result, startIndex); // handle bare links handleAdvanced(advanced_link_regex, result, startIndex); From 906e700b60900e41809d8e0334e12201b667074f Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 11 Jan 2022 21:22:16 +0900 Subject: [PATCH 93/98] Improve quality of beatmap background blurs --- osu.Game/Graphics/Backgrounds/Background.cs | 48 ++++++++++++++++++--- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/Background.cs b/osu.Game/Graphics/Backgrounds/Background.cs index 353054a1f1..b09ec1d9b9 100644 --- a/osu.Game/Graphics/Backgrounds/Background.cs +++ b/osu.Game/Graphics/Backgrounds/Background.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -17,8 +18,6 @@ namespace osu.Game.Graphics.Backgrounds /// public class Background : CompositeDrawable, IEquatable { - private const float blur_scale = 0.5f; - public readonly Sprite Sprite; private readonly string textureName; @@ -46,7 +45,7 @@ namespace osu.Game.Graphics.Backgrounds Sprite.Texture = textures.Get(textureName); } - public Vector2 BlurSigma => bufferedContainer?.BlurSigma / blur_scale ?? Vector2.Zero; + public Vector2 BlurSigma => Vector2.Divide(bufferedContainer?.BlurSigma ?? Vector2.Zero, blurScale); /// /// Smoothly adjusts over time. @@ -67,9 +66,48 @@ namespace osu.Game.Graphics.Backgrounds } if (bufferedContainer != null) - bufferedContainer.FrameBufferScale = newBlurSigma == Vector2.Zero ? Vector2.One : new Vector2(blur_scale); + transformBlurSigma(newBlurSigma, duration, easing); + } - bufferedContainer?.BlurTo(newBlurSigma * blur_scale, duration, easing); + private void transformBlurSigma(Vector2 newBlurSigma, double duration, Easing easing) + => this.TransformTo(nameof(blurSigma), newBlurSigma, duration, easing); + + private Vector2 blurSigmaBacking = Vector2.Zero; + private Vector2 blurScale = Vector2.One; + + private Vector2 blurSigma + { + get => blurSigmaBacking; + set + { + Debug.Assert(bufferedContainer != null); + + blurSigmaBacking = value; + blurScale = new Vector2(calculateBlurDownscale(value.X), calculateBlurDownscale(value.Y)); + + bufferedContainer.FrameBufferScale = blurScale; + bufferedContainer.BlurSigma = value * blurScale; // If the image is scaled down, the blur radius also needs to be reduced to cover the same pixel block. + } + } + + /// + /// Determines a factor to downscale the background based on a given blur sigma, in order to reduce the computational complexity of blurs. + /// + /// The blur sigma. + /// The scale-down factor. + private float calculateBlurDownscale(float sigma) + { + // If we're blurring within one pixel, scaling down will always result in an undesirable loss of quality. + // The algorithm below would also cause this value to go above 1, which is likewise undesirable. + if (sigma <= 1) + return 1; + + // A good value is one where the loss in quality as a result of downscaling the image is not easily perceivable. + // The constants here have been experimentally chosen to yield nice transitions by approximating a log curve through the points {{ 1, 1 }, { 4, 0.75 }, { 16, 0.5 }, { 32, 0.25 }}. + float scale = -0.18f * MathF.Log(0.004f * sigma); + + // To reduce shimmering, the scaling transitions are limited to happen only in increments of 0.2. + return MathF.Round(scale / 0.2f, MidpointRounding.AwayFromZero) * 0.2f; } public virtual bool Equals(Background other) From 60e42bf45eac844f656602b3074a8fdcc2f995b7 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 11 Jan 2022 23:01:17 +0900 Subject: [PATCH 94/98] Add lenience to fix test failures --- .../Visual/Background/TestSceneUserDimBackgrounds.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index 54c5c252c0..b1f642b909 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -14,6 +14,7 @@ using osu.Framework.Input.Events; using osu.Framework.Input.States; using osu.Framework.Platform; using osu.Framework.Screens; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Graphics; @@ -323,7 +324,7 @@ namespace osu.Game.Tests.Visual.Background public bool IsBackgroundUndimmed() => background.CurrentColour == Color4.White; - public bool IsUserBlurApplied() => background.CurrentBlur == new Vector2((float)BlurLevel.Value * BackgroundScreenBeatmap.USER_BLUR_FACTOR); + public bool IsUserBlurApplied() => Precision.AlmostEquals(background.CurrentBlur, new Vector2((float)BlurLevel.Value * BackgroundScreenBeatmap.USER_BLUR_FACTOR), 0.1f); public bool IsUserBlurDisabled() => background.CurrentBlur == new Vector2(0); @@ -331,9 +332,9 @@ namespace osu.Game.Tests.Visual.Background public bool IsBackgroundVisible() => background.CurrentAlpha == 1; - public bool IsBackgroundBlur() => background.CurrentBlur == new Vector2(BACKGROUND_BLUR); + public bool IsBackgroundBlur() => Precision.AlmostEquals(background.CurrentBlur, new Vector2(BACKGROUND_BLUR), 0.1f); - public bool CheckBackgroundBlur(Vector2 expected) => background.CurrentBlur == expected; + public bool CheckBackgroundBlur(Vector2 expected) => Precision.AlmostEquals(background.CurrentBlur, expected, 0.1f); /// /// Make sure every time a screen gets pushed, the background doesn't get replaced From a8c3fdd3835e53cf131f4e7667bb06d3d7930c50 Mon Sep 17 00:00:00 2001 From: r00ster Date: Tue, 11 Jan 2022 16:11:07 +0100 Subject: [PATCH 95/98] Update outdated OpenTabletDriver FAQ links --- osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index c94b418331..802d442ced 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -107,8 +107,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input t.NewLine(); t.AddText("If your tablet is not detected, please read "); t.AddLink("this FAQ", LinkAction.External, RuntimeInfo.OS == RuntimeInfo.Platform.Windows - ? @"https://github.com/OpenTabletDriver/OpenTabletDriver/wiki/Windows-FAQ" - : @"https://github.com/OpenTabletDriver/OpenTabletDriver/wiki/Linux-FAQ"); + ? @"https://opentabletdriver.net/Wiki/FAQ/Windows" + : @"https://opentabletdriver.net/Wiki/FAQ/Linux"); t.AddText(" for troubleshooting steps."); } }), From 76d5225bb9ee9bfcc599bf9ec6f769c3658a4b3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 11 Jan 2022 22:00:04 +0100 Subject: [PATCH 96/98] Rewrite storyboard clock management in slightly different way --- .../Backgrounds/BeatmapBackgroundWithStoryboard.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs index c5c8a25f4a..56ef87c1f4 100644 --- a/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs +++ b/osu.Game/Graphics/Backgrounds/BeatmapBackgroundWithStoryboard.cs @@ -15,7 +15,7 @@ namespace osu.Game.Graphics.Backgrounds { public class BeatmapBackgroundWithStoryboard : BeatmapBackground { - private InterpolatingFramedClock storyboardClock = null!; + private readonly InterpolatingFramedClock storyboardClock; [Resolved(CanBeNull = true)] private MusicController? musicController { get; set; } @@ -23,6 +23,7 @@ namespace osu.Game.Graphics.Backgrounds public BeatmapBackgroundWithStoryboard(WorkingBeatmap beatmap, string fallbackTextureName = "Backgrounds/bg1") : base(beatmap, fallbackTextureName) { + storyboardClock = new InterpolatingFramedClock(); } [BackgroundDependencyLoader] @@ -38,7 +39,7 @@ namespace osu.Game.Graphics.Backgrounds { RelativeSizeAxes = Axes.Both, Volume = { Value = 0 }, - Child = new DrawableStoryboard(Beatmap.Storyboard) { Clock = storyboardClock = new InterpolatingFramedClock(Beatmap.Track) } + Child = new DrawableStoryboard(Beatmap.Storyboard) { Clock = storyboardClock } }, AddInternal); } @@ -47,9 +48,13 @@ namespace osu.Game.Graphics.Backgrounds base.LoadComplete(); if (musicController != null) musicController.TrackChanged += onTrackChanged; + + updateStoryboardClockSource(Beatmap); } - private void onTrackChanged(WorkingBeatmap newBeatmap, TrackChangeDirection _) + private void onTrackChanged(WorkingBeatmap newBeatmap, TrackChangeDirection _) => updateStoryboardClockSource(newBeatmap); + + private void updateStoryboardClockSource(WorkingBeatmap newBeatmap) { if (newBeatmap != Beatmap) return; From 11e07c1137303f0e9c132b5ee684c356d8bac3f7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 Jan 2022 15:20:48 +0900 Subject: [PATCH 97/98] Add button to compact realm on demand In general we're doing things correctly so the realm file shouldn't expand (unless mass deletions are made from it), but this is a nice way to manually confirm the behaviour. Sometimes if using realm studio with osu! running, for instance, the realm file size can blow out of proportion. This will recover from such cases. Note that calling `RealmFactory.Compact` itself is not enough, as it will fail unless all instances of the realm have been closed. --- osu.Game/Localisation/DebugSettingsStrings.cs | 5 +++++ .../Sections/DebugSettings/MemorySettings.cs | 14 +++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/osu.Game/Localisation/DebugSettingsStrings.cs b/osu.Game/Localisation/DebugSettingsStrings.cs index dd21739096..74b2c8d892 100644 --- a/osu.Game/Localisation/DebugSettingsStrings.cs +++ b/osu.Game/Localisation/DebugSettingsStrings.cs @@ -44,6 +44,11 @@ namespace osu.Game.Localisation /// public static LocalisableString ClearAllCaches => new TranslatableString(getKey(@"clear_all_caches"), @"Clear all caches"); + /// + /// "Compact realm" + /// + public static LocalisableString CompactRealm => new TranslatableString(getKey(@"compact_realm"), @"Compact realm"); + private static string getKey(string key) => $"{prefix}:{key}"; } } diff --git a/osu.Game/Overlays/Settings/Sections/DebugSettings/MemorySettings.cs b/osu.Game/Overlays/Settings/Sections/DebugSettings/MemorySettings.cs index 6f48768dcd..eb6e48dfbf 100644 --- a/osu.Game/Overlays/Settings/Sections/DebugSettings/MemorySettings.cs +++ b/osu.Game/Overlays/Settings/Sections/DebugSettings/MemorySettings.cs @@ -6,6 +6,7 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Framework.Platform; +using osu.Game.Database; using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.DebugSettings @@ -15,7 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections.DebugSettings protected override LocalisableString Header => DebugSettingsStrings.MemoryHeader; [BackgroundDependencyLoader] - private void load(FrameworkDebugConfigManager config, GameHost host) + private void load(FrameworkDebugConfigManager config, GameHost host, RealmContextFactory realmFactory) { Children = new Drawable[] { @@ -24,6 +25,17 @@ namespace osu.Game.Overlays.Settings.Sections.DebugSettings Text = DebugSettingsStrings.ClearAllCaches, Action = host.Collect }, + new SettingsButton + { + Text = DebugSettingsStrings.CompactRealm, + Action = () => + { + // Blocking operations implicitly causes a Compact(). + using (realmFactory.BlockAllOperations()) + { + } + } + }, }; } } From c8641389afc681a65b2c1e93e0baf1b1538579b4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 12 Jan 2022 11:08:12 +0300 Subject: [PATCH 98/98] Update macOS minimum supported version in line with .NET 5 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f18c5e76f9..b1dfcab416 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ If you are looking to install or test osu! without setting up a development envi **Latest build:** -| [Windows 8.1+ (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | [macOS 10.12+](https://github.com/ppy/osu/releases/latest/download/osu.app.zip) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS 10+](https://osu.ppy.sh/home/testflight) | [Android 5+](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk) +| [Windows 8.1+ (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | [macOS 10.15+](https://github.com/ppy/osu/releases/latest/download/osu.app.zip) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS 10+](https://osu.ppy.sh/home/testflight) | [Android 5+](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk) | ------------- | ------------- | ------------- | ------------- | ------------- | - The iOS testflight link may fill up (Apple has a hard limit of 10,000 users). We reset it occasionally when this happens. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements of link resets.