From 022cc139528eb581029d7dcf0a7935ef34d0350a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 27 Oct 2019 22:55:46 +0100 Subject: [PATCH 01/26] Add beatmap carousel item sorting stability test Add visual test to ensure sorting stability when sorting criteria are applied in the beatmap carousel. --- .../SongSelect/TestSceneBeatmapCarousel.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index f87d6ebebb..8b82567a8d 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -245,6 +245,28 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert($"Check #{set_count} is at bottom", () => carousel.BeatmapSets.Last().Metadata.Title.EndsWith($"#{set_count}!")); } + [Test] + public void TestSortingStability() + { + var sets = new List(); + + for (int i = 0; i < 20; i++) + { + var set = createTestBeatmapSet(i); + set.Metadata.Artist = "same artist"; + set.Metadata.Title = "same title"; + sets.Add(set); + } + + loadBeatmaps(sets); + + AddStep("Sort by artist", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Artist }, false)); + AddAssert("Items remain in original order", () => carousel.BeatmapSets.Select((set, index) => set.ID == index).All(b => b)); + + AddStep("Sort by title", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Title }, false)); + AddAssert("Items remain in original order", () => carousel.BeatmapSets.Select((set, index) => set.ID == index).All(b => b)); + } + [Test] public void TestSortingWithFiltered() { From c8d3dd0e5a62e126eb9e2d7701aec7e731a5effb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 27 Oct 2019 23:14:14 +0100 Subject: [PATCH 02/26] Make carousel item sorting stable Migrate beatmap carousel item sorting from List.Sort() to IEnumerable.OrderBy(), as the second variant is documented to be a stable sorting algorithm. This allows for eliminating unnecessary movement of carousel items occurring whenever any set of items is tied when changing sorting criteria. --- .../Screens/Select/Carousel/CarouselGroup.cs | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroup.cs b/osu.Game/Screens/Select/Carousel/CarouselGroup.cs index 09b728abeb..b32561eb88 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroup.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroup.cs @@ -1,7 +1,9 @@ // 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; namespace osu.Game.Screens.Select.Carousel { @@ -81,12 +83,9 @@ namespace osu.Game.Screens.Select.Carousel { base.Filter(criteria); - var children = new List(InternalChildren); - - children.ForEach(c => c.Filter(criteria)); - children.Sort((x, y) => x.CompareTo(criteria, y)); - - InternalChildren = children; + InternalChildren.ForEach(c => c.Filter(criteria)); + // IEnumerable.OrderBy() is used instead of List.Sort() to ensure sorting stability + InternalChildren = InternalChildren.OrderBy(c => c, new CriteriaComparer(criteria)).ToList(); } protected virtual void ChildItemStateChanged(CarouselItem item, CarouselItemState value) @@ -104,5 +103,23 @@ namespace osu.Game.Screens.Select.Carousel State.Value = CarouselItemState.Selected; } } + + private class CriteriaComparer : IComparer + { + private readonly FilterCriteria criteria; + + public CriteriaComparer(FilterCriteria criteria) + { + this.criteria = criteria; + } + + public int Compare(CarouselItem x, CarouselItem y) + { + if (x != null && y != null) + return x.CompareTo(criteria, y); + + throw new ArgumentNullException(); + } + } } } From 66b00044483f1aa1429401f59c768ce28ab1839d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Oct 2019 12:02:58 +0900 Subject: [PATCH 03/26] Remove unused logger provider class --- osu.Game/Database/OsuDbContext.cs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index ea3318598f..2ae07b3cf8 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -166,19 +166,6 @@ namespace osu.Game.Database // no-op. called by tooling. } - private class OsuDbLoggerProvider : ILoggerProvider - { - #region Disposal - - public void Dispose() - { - } - - #endregion - - public ILogger CreateLogger(string categoryName) => new OsuDbLogger(); - } - private class OsuDbLogger : ILogger { public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) From 8f87957c7089fb731aa28063486b356ee031cb75 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Oct 2019 14:44:45 +0900 Subject: [PATCH 04/26] Make all control point attributes bindable Properties are left intact for compatibility reasons. --- .../ControlPoints/DifficultyControlPoint.cs | 19 +++++++---- .../ControlPoints/EffectControlPoint.cs | 26 +++++++++++++-- .../ControlPoints/SampleControlPoint.cs | 27 ++++++++++++++-- .../ControlPoints/TimingControlPoint.cs | 32 ++++++++++++++----- ...egacyDifficultyCalculatorBeatmapDecoder.cs | 6 +++- 5 files changed, 90 insertions(+), 20 deletions(-) diff --git a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs index 42651fd0ca..7726eb0245 100644 --- a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs @@ -1,24 +1,31 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osuTK; +using osu.Framework.Bindables; namespace osu.Game.Beatmaps.ControlPoints { public class DifficultyControlPoint : ControlPoint { + /// + /// The speed multiplier at this control point. + /// + public readonly BindableDouble SpeedMultiplierBindable = new BindableDouble(1) + { + MinValue = 0.1, + MaxValue = 10 + }; + /// /// The speed multiplier at this control point. /// public double SpeedMultiplier { - get => speedMultiplier; - set => speedMultiplier = MathHelper.Clamp(value, 0.1, 10); + get => SpeedMultiplierBindable.Value; + set => SpeedMultiplierBindable.Value = value; } - private double speedMultiplier = 1; - public override bool EquivalentTo(ControlPoint other) => - other is DifficultyControlPoint otherTyped && otherTyped.SpeedMultiplier.Equals(speedMultiplier); + other is DifficultyControlPoint otherTyped && otherTyped.SpeedMultiplier.Equals(SpeedMultiplier); } } diff --git a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs index 928f2a51ad..369b93ff3d 100644 --- a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs @@ -1,19 +1,39 @@ // 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.Bindables; + namespace osu.Game.Beatmaps.ControlPoints { public class EffectControlPoint : ControlPoint { /// - /// Whether this control point enables Kiai mode. + /// Whether the first bar line of this control point is ignored. /// - public bool KiaiMode; + public readonly BindableBool OmitFirstBarLineBindable = new BindableBool(); /// /// Whether the first bar line of this control point is ignored. /// - public bool OmitFirstBarLine; + public bool OmitFirstBarLine + { + get => OmitFirstBarLineBindable.Value; + set => OmitFirstBarLineBindable.Value = value; + } + + /// + /// Whether this control point enables Kiai mode. + /// + public readonly BindableBool KiaiModeBindable = new BindableBool(); + + /// + /// Whether this control point enables Kiai mode. + /// + public bool KiaiMode + { + get => KiaiModeBindable.Value; + set => KiaiModeBindable.Value = value; + } public override bool EquivalentTo(ControlPoint other) => other is EffectControlPoint otherTyped && diff --git a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs index 07f5aa6c90..37607c716e 100644 --- a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs @@ -1,6 +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 osu.Framework.Bindables; using osu.Game.Audio; namespace osu.Game.Beatmaps.ControlPoints @@ -12,12 +13,34 @@ namespace osu.Game.Beatmaps.ControlPoints /// /// The default sample bank at this control point. /// - public string SampleBank = DEFAULT_BANK; + public readonly Bindable SampleBankBindable = new Bindable(DEFAULT_BANK); + + /// + /// The speed multiplier at this control point. + /// + public string SampleBank + { + get => SampleBankBindable.Value; + set => SampleBankBindable.Value = value; + } + + /// + /// The default sample bank at this control point. + /// + public readonly BindableInt SampleVolumeBindable = new BindableInt(100) + { + MinValue = 0, + MaxValue = 100 + }; /// /// The default sample volume at this control point. /// - public int SampleVolume = 100; + public int SampleVolume + { + get => SampleVolumeBindable.Value; + set => SampleVolumeBindable.Value = value; + } /// /// Create a SampleInfo based on the sample settings in this control point. diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs index f8c84c79dd..d25d08c5bd 100644 --- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.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 osuTK; +using osu.Framework.Bindables; using osu.Game.Beatmaps.Timing; namespace osu.Game.Beatmaps.ControlPoints @@ -11,23 +11,39 @@ namespace osu.Game.Beatmaps.ControlPoints /// /// The time signature at this control point. /// - public TimeSignatures TimeSignature = TimeSignatures.SimpleQuadruple; + public readonly Bindable TimeSignatureBindable = new Bindable(TimeSignatures.SimpleQuadruple); + + /// + /// The time signature at this control point. + /// + public TimeSignatures TimeSignature + { + get => TimeSignatureBindable.Value; + set => TimeSignatureBindable.Value = value; + } public const double DEFAULT_BEAT_LENGTH = 1000; /// /// The beat length at this control point. /// - public virtual double BeatLength + public readonly BindableDouble BeatLengthBindable = new BindableDouble(DEFAULT_BEAT_LENGTH) { - get => beatLength; - set => beatLength = MathHelper.Clamp(value, 6, 60000); - } + MinValue = 6, + MaxValue = 60000 + }; - private double beatLength = DEFAULT_BEAT_LENGTH; + /// + /// The beat length at this control point. + /// + public double BeatLength + { + get => BeatLengthBindable.Value; + set => BeatLengthBindable.Value = value; + } public override bool EquivalentTo(ControlPoint other) => other is TimingControlPoint otherTyped - && TimeSignature == otherTyped.TimeSignature && beatLength.Equals(otherTyped.beatLength); + && TimeSignature == otherTyped.TimeSignature && BeatLength.Equals(otherTyped.BeatLength); } } diff --git a/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs index 238187bf8f..a16d391b99 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs @@ -32,7 +32,11 @@ namespace osu.Game.Beatmaps.Formats private class LegacyDifficultyCalculatorControlPoint : TimingControlPoint { - public override double BeatLength { get; set; } = DEFAULT_BEAT_LENGTH; + public LegacyDifficultyCalculatorControlPoint() + { + BeatLengthBindable.MinValue = double.MinValue; + BeatLengthBindable.MaxValue = double.MaxValue; + } } } } From 090881cf6f742637e7b2546fab04853bd3df048b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Oct 2019 16:21:14 +0900 Subject: [PATCH 05/26] Add default bindable values --- osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs | 2 ++ osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs | 5 +++-- osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs index 7726eb0245..8b21098a51 100644 --- a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs @@ -12,6 +12,8 @@ namespace osu.Game.Beatmaps.ControlPoints /// public readonly BindableDouble SpeedMultiplierBindable = new BindableDouble(1) { + Precision = 0.1, + Default = 1, MinValue = 0.1, MaxValue = 10 }; diff --git a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs index 37607c716e..42865c686c 100644 --- a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs @@ -13,7 +13,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// /// The default sample bank at this control point. /// - public readonly Bindable SampleBankBindable = new Bindable(DEFAULT_BANK); + public readonly Bindable SampleBankBindable = new Bindable(DEFAULT_BANK) { Default = DEFAULT_BANK }; /// /// The speed multiplier at this control point. @@ -30,7 +30,8 @@ namespace osu.Game.Beatmaps.ControlPoints public readonly BindableInt SampleVolumeBindable = new BindableInt(100) { MinValue = 0, - MaxValue = 100 + MaxValue = 100, + Default = 100 }; /// diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs index d25d08c5bd..c00b04b660 100644 --- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs @@ -11,7 +11,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// /// The time signature at this control point. /// - public readonly Bindable TimeSignatureBindable = new Bindable(TimeSignatures.SimpleQuadruple); + public readonly Bindable TimeSignatureBindable = new Bindable(TimeSignatures.SimpleQuadruple) { Default = TimeSignatures.SimpleQuadruple }; /// /// The time signature at this control point. @@ -29,6 +29,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// public readonly BindableDouble BeatLengthBindable = new BindableDouble(DEFAULT_BEAT_LENGTH) { + Default = DEFAULT_BEAT_LENGTH, MinValue = 6, MaxValue = 60000 }; From 6980f488dcab3baed2acf980471a512e6e07c448 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Oct 2019 16:20:33 +0900 Subject: [PATCH 06/26] Make OsuButton correctly block hover events --- osu.Game/Graphics/UserInterface/OsuButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs index c1810800a0..4124d2ad58 100644 --- a/osu.Game/Graphics/UserInterface/OsuButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuButton.cs @@ -59,7 +59,7 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnHover(HoverEvent e) { hover.FadeIn(200); - return base.OnHover(e); + return true; } protected override void OnHoverLost(HoverLostEvent e) From 08040adfad386e5e0f7a916f86f0d35ec971a0ae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Oct 2019 15:33:08 +0900 Subject: [PATCH 07/26] Expose Current bindable in LabelledComponents Adds a `LabelledDrawable` class for usages where bindables are not present. --- .../TestSceneLabelledComponent.cs | 14 +- .../UserInterface/TestSceneLabelledTextBox.cs | 3 +- osu.Game.Tournament/Screens/SetupScreen.cs | 2 +- .../UserInterfaceV2/LabelledComponent.cs | 124 ++-------------- .../UserInterfaceV2/LabelledDrawable.cs | 132 ++++++++++++++++++ .../UserInterfaceV2/LabelledSwitchButton.cs | 2 +- .../UserInterfaceV2/LabelledTextBox.cs | 2 +- 7 files changed, 151 insertions(+), 128 deletions(-) create mode 100644 osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledComponent.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledComponent.cs index 700adad9cb..8179f92ffc 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledComponent.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledComponent.cs @@ -11,7 +11,7 @@ using osuTK.Graphics; namespace osu.Game.Tests.Visual.UserInterface { - public class TestSceneLabelledComponent : OsuTestScene + public class TestSceneLabelledDrawable : OsuTestScene { [TestCase(false)] [TestCase(true)] @@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.UserInterface { AddStep("create component", () => { - LabelledComponent component; + LabelledDrawable component; Child = new Container { @@ -33,7 +33,7 @@ namespace osu.Game.Tests.Visual.UserInterface Origin = Anchor.Centre, Width = 500, AutoSizeAxes = Axes.Y, - Child = component = padded ? (LabelledComponent)new PaddedLabelledComponent() : new NonPaddedLabelledComponent(), + Child = component = padded ? (LabelledDrawable)new PaddedLabelledDrawable() : new NonPaddedLabelledDrawable(), }; component.Label = "a sample component"; @@ -41,9 +41,9 @@ namespace osu.Game.Tests.Visual.UserInterface }); } - private class PaddedLabelledComponent : LabelledComponent + private class PaddedLabelledDrawable : LabelledDrawable { - public PaddedLabelledComponent() + public PaddedLabelledDrawable() : base(true) { } @@ -57,9 +57,9 @@ namespace osu.Game.Tests.Visual.UserInterface }; } - private class NonPaddedLabelledComponent : LabelledComponent + private class NonPaddedLabelledDrawable : LabelledDrawable { - public NonPaddedLabelledComponent() + public NonPaddedLabelledDrawable() : base(false) { } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledTextBox.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledTextBox.cs index 53a2bfabbc..8208b55952 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledTextBox.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledTextBox.cs @@ -7,7 +7,6 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; namespace osu.Game.Tests.Visual.UserInterface @@ -28,7 +27,7 @@ namespace osu.Game.Tests.Visual.UserInterface { AddStep("create component", () => { - LabelledComponent component; + LabelledTextBox component; Child = new Container { diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/SetupScreen.cs index 091a837745..a67daa2756 100644 --- a/osu.Game.Tournament/Screens/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/SetupScreen.cs @@ -89,7 +89,7 @@ namespace osu.Game.Tournament.Screens }; } - private class ActionableInfo : LabelledComponent + private class ActionableInfo : LabelledDrawable { private OsuButton button; diff --git a/osu.Game/Graphics/UserInterfaceV2/LabelledComponent.cs b/osu.Game/Graphics/UserInterfaceV2/LabelledComponent.cs index 2e659825b7..1819b36667 100644 --- a/osu.Game/Graphics/UserInterfaceV2/LabelledComponent.cs +++ b/osu.Game/Graphics/UserInterfaceV2/LabelledComponent.cs @@ -1,132 +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.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics.Containers; -using osuTK; +using osu.Framework.Graphics.UserInterface; namespace osu.Game.Graphics.UserInterfaceV2 { - public abstract class LabelledComponent : CompositeDrawable - where T : Drawable + public abstract class LabelledComponent : LabelledDrawable, IHasCurrentValue + where T : Drawable, IHasCurrentValue { - protected const float CONTENT_PADDING_VERTICAL = 10; - protected const float CONTENT_PADDING_HORIZONTAL = 15; - protected const float CORNER_RADIUS = 15; - - /// - /// The component that is being displayed. - /// - protected readonly T Component; - - private readonly OsuTextFlowContainer labelText; - private readonly OsuTextFlowContainer descriptionText; - - /// - /// Creates a new . - /// - /// Whether the component should be padded or should be expanded to the bounds of this . protected LabelledComponent(bool padded) + : base(padded) { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - CornerRadius = CORNER_RADIUS; - Masking = true; - - InternalChildren = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.FromHex("1c2125"), - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Padding = padded - ? new MarginPadding { Horizontal = CONTENT_PADDING_HORIZONTAL, Vertical = CONTENT_PADDING_VERTICAL } - : new MarginPadding { Left = CONTENT_PADDING_HORIZONTAL }, - Spacing = new Vector2(0, 12), - Children = new Drawable[] - { - new GridContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Content = new[] - { - new Drawable[] - { - labelText = new OsuTextFlowContainer(s => s.Font = OsuFont.GetFont(weight: FontWeight.Bold)) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - Padding = new MarginPadding { Right = 20 } - }, - new Container - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Child = Component = CreateComponent().With(d => - { - d.Anchor = Anchor.CentreRight; - d.Origin = Anchor.CentreRight; - }) - } - }, - }, - RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }, - ColumnDimensions = new[] { new Dimension(GridSizeMode.AutoSize) } - }, - descriptionText = new OsuTextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold, italics: true)) - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Bottom = padded ? 0 : CONTENT_PADDING_VERTICAL }, - Alpha = 0, - } - } - } - }; } - [BackgroundDependencyLoader] - private void load(OsuColour osuColour) + public Bindable Current { - descriptionText.Colour = osuColour.Yellow; + get => Component.Current; + set => Component.Current = value; } - - public string Label - { - set => labelText.Text = value; - } - - public string Description - { - set - { - descriptionText.Text = value; - - if (!string.IsNullOrEmpty(value)) - descriptionText.Show(); - else - descriptionText.Hide(); - } - } - - /// - /// Creates the component that should be displayed. - /// - /// The component. - protected abstract T CreateComponent(); } } diff --git a/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs b/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs new file mode 100644 index 0000000000..f44bd72aee --- /dev/null +++ b/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs @@ -0,0 +1,132 @@ +// 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.Game.Graphics.Containers; +using osuTK; + +namespace osu.Game.Graphics.UserInterfaceV2 +{ + public abstract class LabelledDrawable : CompositeDrawable + where T : Drawable + { + protected const float CONTENT_PADDING_VERTICAL = 10; + protected const float CONTENT_PADDING_HORIZONTAL = 15; + protected const float CORNER_RADIUS = 15; + + /// + /// The component that is being displayed. + /// + protected readonly T Component; + + private readonly OsuTextFlowContainer labelText; + private readonly OsuTextFlowContainer descriptionText; + + /// + /// Creates a new . + /// + /// Whether the component should be padded or should be expanded to the bounds of this . + protected LabelledDrawable(bool padded) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + CornerRadius = CORNER_RADIUS; + Masking = true; + + InternalChildren = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.FromHex("1c2125"), + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Padding = padded + ? new MarginPadding { Horizontal = CONTENT_PADDING_HORIZONTAL, Vertical = CONTENT_PADDING_VERTICAL } + : new MarginPadding { Left = CONTENT_PADDING_HORIZONTAL }, + Spacing = new Vector2(0, 12), + Children = new Drawable[] + { + new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Content = new[] + { + new Drawable[] + { + labelText = new OsuTextFlowContainer(s => s.Font = OsuFont.GetFont(weight: FontWeight.Bold)) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + Padding = new MarginPadding { Right = 20 } + }, + new Container + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Child = Component = CreateComponent().With(d => + { + d.Anchor = Anchor.CentreRight; + d.Origin = Anchor.CentreRight; + }) + } + }, + }, + RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }, + ColumnDimensions = new[] { new Dimension(GridSizeMode.AutoSize) } + }, + descriptionText = new OsuTextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold, italics: true)) + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Bottom = padded ? 0 : CONTENT_PADDING_VERTICAL }, + Alpha = 0, + } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour osuColour) + { + descriptionText.Colour = osuColour.Yellow; + } + + public string Label + { + set => labelText.Text = value; + } + + public string Description + { + set + { + descriptionText.Text = value; + + if (!string.IsNullOrEmpty(value)) + descriptionText.Show(); + else + descriptionText.Hide(); + } + } + + /// + /// Creates the component that should be displayed. + /// + /// The component. + protected abstract T CreateComponent(); + } +} diff --git a/osu.Game/Graphics/UserInterfaceV2/LabelledSwitchButton.cs b/osu.Game/Graphics/UserInterfaceV2/LabelledSwitchButton.cs index c973f1d13e..c374d80830 100644 --- a/osu.Game/Graphics/UserInterfaceV2/LabelledSwitchButton.cs +++ b/osu.Game/Graphics/UserInterfaceV2/LabelledSwitchButton.cs @@ -3,7 +3,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 { - public class LabelledSwitchButton : LabelledComponent + public class LabelledSwitchButton : LabelledComponent { public LabelledSwitchButton() : base(true) diff --git a/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs b/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs index 50d2a14482..2cbe095d0b 100644 --- a/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs +++ b/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs @@ -8,7 +8,7 @@ using osu.Game.Graphics.UserInterface; namespace osu.Game.Graphics.UserInterfaceV2 { - public class LabelledTextBox : LabelledComponent + public class LabelledTextBox : LabelledComponent { public event TextBox.OnCommitHandler OnCommit; From cf3ed42bfc9e299c599e4215ab72ea2df69f7940 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Oct 2019 17:41:42 +0900 Subject: [PATCH 08/26] Fix download tracking components getting stuck on import failures --- osu.Game/Database/ArchiveModelManager.cs | 4 +++- osu.Game/Database/DownloadableArchiveModelManager.cs | 7 ++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index b567f0c0e3..9fed8e03ac 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -108,7 +108,7 @@ namespace osu.Game.Database return Import(notification, paths); } - protected async Task Import(ProgressNotification notification, params string[] paths) + protected async Task> Import(ProgressNotification notification, params string[] paths) { notification.Progress = 0; notification.Text = $"{HumanisedModelName.Humanize(LetterCasing.Title)} import is initialising..."; @@ -168,6 +168,8 @@ namespace osu.Game.Database notification.State = ProgressNotificationState.Completed; } + + return imported; } /// diff --git a/osu.Game/Database/DownloadableArchiveModelManager.cs b/osu.Game/Database/DownloadableArchiveModelManager.cs index 78c0837ce9..e3c6ad25e6 100644 --- a/osu.Game/Database/DownloadableArchiveModelManager.cs +++ b/osu.Game/Database/DownloadableArchiveModelManager.cs @@ -76,7 +76,12 @@ namespace osu.Game.Database Task.Factory.StartNew(async () => { // This gets scheduled back to the update thread, but we want the import to run in the background. - await Import(notification, filename); + var imported = await Import(notification, filename); + + // for now a failed import will be marked as a failed download for simplicity. + if (!imported.Any()) + DownloadFailed?.Invoke(request); + currentDownloads.Remove(request); }, TaskCreationOptions.LongRunning); }; From 16e33e8bc7d5661cf945bfc495780063ddd1b4ea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Oct 2019 18:34:58 +0900 Subject: [PATCH 09/26] Fix song progress not displaying correctly --- osu.Game/Screens/Play/SongProgress.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index 6642efdf8b..3df06ebfa8 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -106,6 +106,8 @@ namespace osu.Game.Screens.Play protected override void LoadComplete() { + base.LoadComplete(); + Show(); replayLoaded.ValueChanged += loaded => AllowSeeking = loaded.NewValue; From 46b44f4f99e08c0d5685ba3bddb239d4860bfb04 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Oct 2019 18:37:58 +0900 Subject: [PATCH 10/26] Fix PlayerSettingsOverlay being shown by default --- .../Visual/Gameplay/TestSceneReplaySettingsOverlay.cs | 2 ++ osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplaySettingsOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplaySettingsOverlay.cs index 944480243d..cdfb3beb19 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplaySettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplaySettingsOverlay.cs @@ -3,6 +3,7 @@ using NUnit.Framework; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.PlayerSettings; @@ -20,6 +21,7 @@ namespace osu.Game.Tests.Visual.Gameplay { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, + State = { Value = Visibility.Visible } }); Add(container = new ExampleContainer()); diff --git a/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs b/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs index b2c3952f38..d201b5d30e 100644 --- a/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs +++ b/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs @@ -45,8 +45,6 @@ namespace osu.Game.Screens.Play.HUD VisualSettings = new VisualSettings { Expanded = false } } }; - - Show(); } protected override void PopIn() => this.FadeIn(fade_duration); From 1a4817879e57800bf1ab7f829c5ca2c7d5b28623 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Oct 2019 19:10:39 +0900 Subject: [PATCH 11/26] Fix precision changes in legacy control point types --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 +- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 8 ++++++++ .../Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs | 6 +++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 786b7611b5..c5b9ddc23b 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -376,7 +376,7 @@ namespace osu.Game.Beatmaps.Formats handleTimingControlPoint(controlPoint); } - handleDifficultyControlPoint(new DifficultyControlPoint + handleDifficultyControlPoint(new LegacyDifficultyControlPoint { Time = time, SpeedMultiplier = speedMultiplier, diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index a5a4380d4a..49457833f2 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -189,6 +189,14 @@ namespace osu.Game.Beatmaps.Formats Foreground = 3 } + internal class LegacyDifficultyControlPoint : DifficultyControlPoint + { + public LegacyDifficultyControlPoint() + { + SpeedMultiplierBindable.Precision = Double.Epsilon; + } + } + internal class LegacySampleControlPoint : SampleControlPoint { public int CustomSampleBank; diff --git a/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs index a16d391b99..527f520172 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs @@ -28,11 +28,11 @@ namespace osu.Game.Beatmaps.Formats } protected override TimingControlPoint CreateTimingControlPoint() - => new LegacyDifficultyCalculatorControlPoint(); + => new LegacyDifficultyCalculatorTimingControlPoint(); - private class LegacyDifficultyCalculatorControlPoint : TimingControlPoint + private class LegacyDifficultyCalculatorTimingControlPoint : TimingControlPoint { - public LegacyDifficultyCalculatorControlPoint() + public LegacyDifficultyCalculatorTimingControlPoint() { BeatLengthBindable.MinValue = double.MinValue; BeatLengthBindable.MaxValue = double.MaxValue; From c181edaedfc59cd9f3cfc74f7f817059b8051dd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 28 Oct 2019 15:07:36 +0100 Subject: [PATCH 12/26] Replace manual comparer implementation Replace manually-implemented CriteriaComparer with a call to Comparer.Create() to decrease verbosity. --- .../Screens/Select/Carousel/CarouselGroup.cs | 22 ++----------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroup.cs b/osu.Game/Screens/Select/Carousel/CarouselGroup.cs index b32561eb88..aa48d1a04e 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroup.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroup.cs @@ -1,7 +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 System.Linq; @@ -85,7 +84,8 @@ namespace osu.Game.Screens.Select.Carousel InternalChildren.ForEach(c => c.Filter(criteria)); // IEnumerable.OrderBy() is used instead of List.Sort() to ensure sorting stability - InternalChildren = InternalChildren.OrderBy(c => c, new CriteriaComparer(criteria)).ToList(); + var criteriaComparer = Comparer.Create((x, y) => x.CompareTo(criteria, y)); + InternalChildren = InternalChildren.OrderBy(c => c, criteriaComparer).ToList(); } protected virtual void ChildItemStateChanged(CarouselItem item, CarouselItemState value) @@ -103,23 +103,5 @@ namespace osu.Game.Screens.Select.Carousel State.Value = CarouselItemState.Selected; } } - - private class CriteriaComparer : IComparer - { - private readonly FilterCriteria criteria; - - public CriteriaComparer(FilterCriteria criteria) - { - this.criteria = criteria; - } - - public int Compare(CarouselItem x, CarouselItem y) - { - if (x != null && y != null) - return x.CompareTo(criteria, y); - - throw new ArgumentNullException(); - } - } } } From a4a57eec544d7d66774364304f17cb629bfd4834 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Oct 2019 01:09:49 +0900 Subject: [PATCH 13/26] Fix game-wide performance drop when triangles intro is used --- osu.Game/Screens/Menu/IntroSequence.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Menu/IntroSequence.cs b/osu.Game/Screens/Menu/IntroSequence.cs index 093d01f12d..e2dd14b18c 100644 --- a/osu.Game/Screens/Menu/IntroSequence.cs +++ b/osu.Game/Screens/Menu/IntroSequence.cs @@ -42,6 +42,7 @@ namespace osu.Game.Screens.Menu public IntroSequence() { RelativeSizeAxes = Axes.Both; + Alpha = 0; } [BackgroundDependencyLoader] From ecf14bc7b9721850744884822232af96db104038 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Oct 2019 01:21:17 +0900 Subject: [PATCH 14/26] Rename class to match --- ...TestSceneLabelledComponent.cs => TestSceneLabelledDrawable.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename osu.Game.Tests/Visual/UserInterface/{TestSceneLabelledComponent.cs => TestSceneLabelledDrawable.cs} (100%) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledComponent.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledDrawable.cs similarity index 100% rename from osu.Game.Tests/Visual/UserInterface/TestSceneLabelledComponent.cs rename to osu.Game.Tests/Visual/UserInterface/TestSceneLabelledDrawable.cs From d1c6e3f62064305fbeb4d53bb627571bd8650ec6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Oct 2019 14:32:38 +0900 Subject: [PATCH 15/26] Add test for scroll to end when max history is exceeded --- .../Online/TestSceneStandAloneChatDisplay.cs | 37 +++++++++++++++++-- osu.Game/Online/Chat/Channel.cs | 8 ++-- osu.Game/Overlays/Chat/DrawableChannel.cs | 4 +- 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs index 39c2fbfcc9..d973799405 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs @@ -7,6 +7,9 @@ using osu.Game.Online.Chat; using osu.Game.Users; using osuTK; using System; +using System.Linq; +using osu.Framework.Graphics.Containers; +using osu.Game.Overlays.Chat; namespace osu.Game.Tests.Visual.Online { @@ -42,14 +45,14 @@ namespace osu.Game.Tests.Visual.Online [Cached] private ChannelManager channelManager = new ChannelManager(); - private readonly StandAloneChatDisplay chatDisplay; - private readonly StandAloneChatDisplay chatDisplay2; + private readonly TestStandAloneChatDisplay chatDisplay; + private readonly TestStandAloneChatDisplay chatDisplay2; public TestSceneStandAloneChatDisplay() { Add(channelManager); - Add(chatDisplay = new StandAloneChatDisplay + Add(chatDisplay = new TestStandAloneChatDisplay { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, @@ -57,7 +60,7 @@ namespace osu.Game.Tests.Visual.Online Size = new Vector2(400, 80) }); - Add(chatDisplay2 = new StandAloneChatDisplay(true) + Add(chatDisplay2 = new TestStandAloneChatDisplay(true) { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, @@ -119,6 +122,32 @@ namespace osu.Game.Tests.Visual.Online Content = "Message from the future!", Timestamp = DateTimeOffset.Now })); + + AddUntilStep("ensure still scrolled to bottom", () => chatDisplay.ScrolledToBottom); + + const int messages_per_call = 10; + AddRepeatStep("add many messages", () => + { + for (int i = 0; i < messages_per_call; i++) + testChannel.AddNewMessages(new Message(sequence++) + { + Sender = longUsernameUser, + Content = "Many messages! " + Guid.NewGuid(), + Timestamp = DateTimeOffset.Now + }); + }, Channel.MAX_HISTORY / messages_per_call + 5); + + AddUntilStep("ensure still scrolled to bottom", () => chatDisplay.ScrolledToBottom); + } + + private class TestStandAloneChatDisplay : StandAloneChatDisplay + { + public TestStandAloneChatDisplay(bool textbox = false) + : base(textbox) + { + } + + public bool ScrolledToBottom => ((ScrollContainer)((Container)InternalChildren.OfType().First().Child).Child).IsScrolledToEnd(1); } } } diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs index 9ec39c5cb1..451174a73c 100644 --- a/osu.Game/Online/Chat/Channel.cs +++ b/osu.Game/Online/Chat/Channel.cs @@ -14,7 +14,7 @@ namespace osu.Game.Online.Chat { public class Channel { - public readonly int MaxHistory = 300; + public const int MAX_HISTORY = 300; /// /// Contains every joined user except the current logged in user. Currently only returned for PM channels. @@ -80,8 +80,6 @@ namespace osu.Game.Online.Chat /// public Bindable Joined = new Bindable(); - public const int MAX_HISTORY = 300; - [JsonConstructor] public Channel() { @@ -162,8 +160,8 @@ namespace osu.Game.Online.Chat { // never purge local echos int messageCount = Messages.Count - pendingMessages.Count; - if (messageCount > MaxHistory) - Messages.RemoveRange(0, messageCount - MaxHistory); + if (messageCount > MAX_HISTORY) + Messages.RemoveRange(0, messageCount - MAX_HISTORY); } } } diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index 6cdbfabe0f..4ddaf4d5ae 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -1,4 +1,4 @@ -// 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; @@ -107,7 +107,7 @@ namespace osu.Game.Overlays.Chat scrollToEnd(); var staleMessages = chatLines.Where(c => c.LifetimeEnd == double.MaxValue).ToArray(); - int count = staleMessages.Length - Channel.MaxHistory; + int count = staleMessages.Length - Channel.MAX_HISTORY; for (int i = 0; i < count; i++) { From 09a6d1184a42ce86f5c61d1cf2464425b1c0a7ad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Oct 2019 14:33:05 +0900 Subject: [PATCH 16/26] Tidy up order of scroll changes --- osu.Game/Overlays/Chat/DrawableChannel.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index 4ddaf4d5ae..9301daa9ff 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -89,8 +89,10 @@ namespace osu.Game.Overlays.Chat private void newMessagesArrived(IEnumerable newMessages) { + bool shouldScrollToEnd = scroll.IsScrolledToEnd(10) || !chatLines.Any() || newMessages.Any(m => m is LocalMessage); + // Add up to last Channel.MAX_HISTORY messages - var displayMessages = newMessages.Skip(Math.Max(0, newMessages.Count() - Channel.MaxHistory)); + var displayMessages = newMessages.Skip(Math.Max(0, newMessages.Count() - Channel.MAX_HISTORY)); Message lastMessage = chatLines.LastOrDefault()?.Message; @@ -103,19 +105,18 @@ namespace osu.Game.Overlays.Chat lastMessage = message; } - if (scroll.IsScrolledToEnd(10) || !chatLines.Any() || newMessages.Any(m => m is LocalMessage)) - scrollToEnd(); - var staleMessages = chatLines.Where(c => c.LifetimeEnd == double.MaxValue).ToArray(); int count = staleMessages.Length - Channel.MAX_HISTORY; for (int i = 0; i < count; i++) { var d = staleMessages[i]; - if (!scroll.IsScrolledToEnd(10)) - scroll.OffsetScrollPosition(-d.DrawHeight); + scroll.OffsetScrollPosition(-d.DrawHeight); d.Expire(); } + + if (shouldScrollToEnd) + scrollToEnd(); } private void pendingMessageResolved(Message existing, Message updated) From b06e70e546ca0a2d82ecfbce85f977a2a200fb28 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Oct 2019 15:27:08 +0900 Subject: [PATCH 17/26] Add failing test showing issue with day separator logic --- .../Online/TestSceneStandAloneChatDisplay.cs | 20 ++++++++++++++++++- osu.Game/Overlays/Chat/DrawableChannel.cs | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs index d973799405..01400bf1d9 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs @@ -9,6 +9,7 @@ using osuTK; using System; using System.Linq; using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Containers; using osu.Game.Overlays.Chat; namespace osu.Game.Tests.Visual.Online @@ -137,6 +138,17 @@ namespace osu.Game.Tests.Visual.Online }); }, Channel.MAX_HISTORY / messages_per_call + 5); + AddAssert("Ensure no adjacent day separators", () => + { + var indices = chatDisplay.FillFlow.OfType().Select(ds => chatDisplay.FillFlow.IndexOf(ds)); + + foreach (var i in indices) + if (i < chatDisplay.FillFlow.Count && chatDisplay.FillFlow[i + 1] is DrawableChannel.DaySeparator) + return false; + + return true; + }); + AddUntilStep("ensure still scrolled to bottom", () => chatDisplay.ScrolledToBottom); } @@ -147,7 +159,13 @@ namespace osu.Game.Tests.Visual.Online { } - public bool ScrolledToBottom => ((ScrollContainer)((Container)InternalChildren.OfType().First().Child).Child).IsScrolledToEnd(1); + protected DrawableChannel DrawableChannel => InternalChildren.OfType().First(); + + protected OsuScrollContainer ScrollContainer => (OsuScrollContainer)((Container)DrawableChannel.Child).Child; + + public FillFlowContainer FillFlow => (FillFlowContainer)ScrollContainer.Child; + + public bool ScrolledToBottom => ScrollContainer.IsScrolledToEnd(1); } } } diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index 9301daa9ff..636fafb5f2 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -142,7 +142,7 @@ namespace osu.Game.Overlays.Chat private void scrollToEnd() => ScheduleAfterChildren(() => scroll.ScrollToEnd()); - protected class DaySeparator : Container + public class DaySeparator : Container { public float TextSize { From 54befb6f8fdbdfe36e9f299b3df54782ff08cf7f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Oct 2019 15:45:41 +0900 Subject: [PATCH 18/26] Remove adjacent day separators --- osu.Game/Overlays/Chat/DrawableChannel.cs | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index 636fafb5f2..427bd8dcde 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -108,11 +108,25 @@ namespace osu.Game.Overlays.Chat var staleMessages = chatLines.Where(c => c.LifetimeEnd == double.MaxValue).ToArray(); int count = staleMessages.Length - Channel.MAX_HISTORY; - for (int i = 0; i < count; i++) + if (count > 0) { - var d = staleMessages[i]; - scroll.OffsetScrollPosition(-d.DrawHeight); - d.Expire(); + void expireAndAdjustScroll(Drawable d) + { + scroll.OffsetScrollPosition(-d.DrawHeight); + d.Expire(); + } + + for (int i = 0; i < count; i++) + expireAndAdjustScroll(staleMessages[i]); + + // remove all adjacent day separators after stale message removal + for (int i = 0; i < ChatLineFlow.Count - 1; i++) + { + if (!(ChatLineFlow[i] is DaySeparator)) break; + if (!(ChatLineFlow[i + 1] is DaySeparator)) break; + + expireAndAdjustScroll(ChatLineFlow[i]); + } } if (shouldScrollToEnd) From 8e1faf6ff1810b828bad316475ff5a9d2bed88dc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Oct 2019 17:03:52 +0900 Subject: [PATCH 19/26] Fix loader animation tests failing occasionally --- .../Visual/Menus/TestSceneLoaderAnimation.cs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneLoaderAnimation.cs b/osu.Game.Tests/Visual/Menus/TestSceneLoaderAnimation.cs index 000832b784..61fed3013e 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneLoaderAnimation.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneLoaderAnimation.cs @@ -33,23 +33,15 @@ namespace osu.Game.Tests.Visual.Menus [Test] public void TestInstantLoad() { - bool logoVisible = false; + // visual only, very impossible to test this using asserts. - AddStep("begin loading", () => + AddStep("load immediately", () => { loader = new TestLoader(); loader.AllowLoad.Set(); LoadScreen(loader); }); - - AddUntilStep("loaded", () => - { - logoVisible = loader.Logo?.Alpha > 0; - return loader.Logo != null && loader.ScreenLoaded; - }); - - AddAssert("logo was not visible", () => !logoVisible); } [Test] @@ -58,7 +50,7 @@ namespace osu.Game.Tests.Visual.Menus AddStep("begin loading", () => LoadScreen(loader = new TestLoader())); AddUntilStep("wait for logo visible", () => loader.Logo?.Alpha > 0); AddStep("finish loading", () => loader.AllowLoad.Set()); - AddAssert("loaded", () => loader.Logo != null && loader.ScreenLoaded); + AddUntilStep("loaded", () => loader.Logo != null && loader.ScreenLoaded); AddUntilStep("logo gone", () => loader.Logo?.Alpha == 0); } From 7c6ccce3ba1b271309185d5587a76924f95d82b4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Oct 2019 18:02:30 +0900 Subject: [PATCH 20/26] Add tests covering precision case --- .../Formats/LegacyBeatmapDecoderTest.cs | 17 +++++++++++++++++ .../controlpoint-difficulty-multiplier.osu | 8 ++++++++ 2 files changed, 25 insertions(+) create mode 100644 osu.Game.Tests/Resources/controlpoint-difficulty-multiplier.osu diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index de516d3142..8545072199 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -362,6 +362,23 @@ namespace osu.Game.Tests.Beatmaps.Formats } } + [Test] + public void TestDecodeControlPointDifficultyChange() + { + var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + + using (var resStream = TestResources.OpenResource("controlpoint-difficulty-multiplier.osu")) + using (var stream = new LineBufferedReader(resStream)) + { + var controlPointInfo = decoder.Decode(stream).ControlPointInfo; + + Assert.That(controlPointInfo.DifficultyPointAt(5).SpeedMultiplier, Is.EqualTo(1)); + Assert.That(controlPointInfo.DifficultyPointAt(1000).SpeedMultiplier, Is.EqualTo(10)); + Assert.That(controlPointInfo.DifficultyPointAt(2000).SpeedMultiplier, Is.EqualTo(1.8518518518518519d)); + Assert.That(controlPointInfo.DifficultyPointAt(3000).SpeedMultiplier, Is.EqualTo(0.5)); + } + } + [Test] public void TestDecodeControlPointCustomSampleBank() { diff --git a/osu.Game.Tests/Resources/controlpoint-difficulty-multiplier.osu b/osu.Game.Tests/Resources/controlpoint-difficulty-multiplier.osu new file mode 100644 index 0000000000..5f06fc33c8 --- /dev/null +++ b/osu.Game.Tests/Resources/controlpoint-difficulty-multiplier.osu @@ -0,0 +1,8 @@ +osu file format v7 + +[TimingPoints] +0,100,4,2,0,100,1,0 +12,500,4,2,0,100,1,0 +1000,-10,4,2,0,100,0,0 +2000,-54,4,2,0,100,0,0 +3000,-200,4,2,0,100,0,0 From 97c1a6e86bfdfe65e18d0d52f53fb6e585f9edda Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Oct 2019 18:05:32 +0900 Subject: [PATCH 21/26] 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 43c1302e54..8b31be3f12 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -62,6 +62,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index e898a001de..0cb09d9b14 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -26,7 +26,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 656c60543e..719aced705 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -118,8 +118,8 @@ - - + + From e9cb3337b31e39372f3905405366d45d3372bded Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 29 Oct 2019 22:31:27 +0900 Subject: [PATCH 22/26] Fix 1x1 white pixel appearing in the centre of hitcircles on default skin --- .../Objects/Drawables/Pieces/NumberPiece.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs index 62c4ba5ee3..7c94568835 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Game.Graphics.Sprites; using osuTK.Graphics; -using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Skinning; @@ -30,17 +29,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Children = new Drawable[] { - new CircularContainer + new Container { Masking = true, - Origin = Anchor.Centre, EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow, Radius = 60, Colour = Color4.White.Opacity(0.5f), }, - Child = new Box() }, number = new SkinnableSpriteText(new OsuSkinComponent(OsuSkinComponents.HitCircleText), _ => new OsuSpriteText { From 5c2917d3030e872a656deda7a4f44e8a218ec6d7 Mon Sep 17 00:00:00 2001 From: Ganendra Afrasya Date: Wed, 30 Oct 2019 00:50:04 +0700 Subject: [PATCH 23/26] Place sign in button inside ShakeContainer --- .../Sections/General/LoginSettings.cs | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs index b02b1a5489..a8bbccb168 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs @@ -200,6 +200,7 @@ namespace osu.Game.Overlays.Settings.Sections.General { private TextBox username; private TextBox password; + private ShakeContainer shakeSignIn; private IAPIProvider api; public Action RequestHide; @@ -208,6 +209,8 @@ namespace osu.Game.Overlays.Settings.Sections.General { if (!string.IsNullOrEmpty(username.Text) && !string.IsNullOrEmpty(password.Text)) api.Login(username.Text, password.Text); + else + shakeSignIn.Shake(); } [BackgroundDependencyLoader(permitNulls: true)] @@ -244,10 +247,23 @@ namespace osu.Game.Overlays.Settings.Sections.General LabelText = "Stay signed in", Bindable = config.GetBindable(OsuSetting.SavePassword), }, - new SettingsButton + new Container { - Text = "Sign in", - Action = performLogin + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + shakeSignIn = new ShakeContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Child = new SettingsButton + { + Text = "Sign in", + Action = performLogin + }, + } + } }, new SettingsButton { From ced6042b3e977dcfcd18ca1e77872e061e0a8811 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 30 Oct 2019 15:51:09 +0900 Subject: [PATCH 24/26] Use internal type name --- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 49457833f2..2b914669cb 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -193,7 +193,7 @@ namespace osu.Game.Beatmaps.Formats { public LegacyDifficultyControlPoint() { - SpeedMultiplierBindable.Precision = Double.Epsilon; + SpeedMultiplierBindable.Precision = double.Epsilon; } } From 45af7969434d6216f71dc8da59759c3e33626986 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 30 Oct 2019 17:05:15 +0900 Subject: [PATCH 25/26] Remove usages of EF internals --- .../UserInterface/TestSceneBeatSyncedContainer.cs | 4 ++-- osu.Game/Screens/Edit/EditorClock.cs | 13 +++++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs index b6df559686..ed44d82bce 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; -using Microsoft.EntityFrameworkCore.Internal; +using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio.Track; @@ -153,7 +153,7 @@ namespace osu.Game.Tests.Visual.UserInterface }; } - private IReadOnlyList timingPoints => Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints; + private List timingPoints => Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.ToList(); private TimingControlPoint getNextTimingPoint(TimingControlPoint current) { diff --git a/osu.Game/Screens/Edit/EditorClock.cs b/osu.Game/Screens/Edit/EditorClock.cs index 1cfb123f25..f674a62a3a 100644 --- a/osu.Game/Screens/Edit/EditorClock.cs +++ b/osu.Game/Screens/Edit/EditorClock.cs @@ -3,7 +3,6 @@ using System; using System.Linq; -using Microsoft.EntityFrameworkCore.Internal; using osu.Framework.MathUtils; using osu.Framework.Timing; using osu.Game.Beatmaps; @@ -88,7 +87,17 @@ namespace osu.Game.Screens.Edit if (direction < 0 && timingPoint.Time == CurrentTime) { // When going backwards and we're at the boundary of two timing points, we compute the seek distance with the timing point which we are seeking into - int activeIndex = ControlPointInfo.TimingPoints.IndexOf(timingPoint); + int activeIndex = -1; + + for (int i = 0; i < ControlPointInfo.TimingPoints.Count; i++) + { + if (ControlPointInfo.TimingPoints[i] == timingPoint) + { + activeIndex = i; + break; + } + } + while (activeIndex > 0 && CurrentTime == timingPoint.Time) timingPoint = ControlPointInfo.TimingPoints[--activeIndex]; } From cf2d885099a0b92257adabf8bee9d130d8450d7d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 30 Oct 2019 18:02:18 +0900 Subject: [PATCH 26/26] Fix control points being flushed too late --- .../Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 15 +++++++++++++++ .../timingpoint-speedmultiplier-reset.osu | 5 +++++ osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 6 +++--- 3 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 osu.Game.Tests/Resources/timingpoint-speedmultiplier-reset.osu diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index a6b0dedf56..2ecc516919 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -262,6 +262,21 @@ namespace osu.Game.Tests.Beatmaps.Formats } } + [Test] + public void TestTimingPointResetsSpeedMultiplier() + { + var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + + using (var resStream = TestResources.OpenResource("timingpoint-speedmultiplier-reset.osu")) + using (var stream = new LineBufferedReader(resStream)) + { + var controlPoints = decoder.Decode(stream).ControlPointInfo; + + Assert.That(controlPoints.DifficultyPointAt(0).SpeedMultiplier, Is.EqualTo(0.5).Within(0.1)); + Assert.That(controlPoints.DifficultyPointAt(2000).SpeedMultiplier, Is.EqualTo(1).Within(0.1)); + } + } + [Test] public void TestDecodeBeatmapColours() { diff --git a/osu.Game.Tests/Resources/timingpoint-speedmultiplier-reset.osu b/osu.Game.Tests/Resources/timingpoint-speedmultiplier-reset.osu new file mode 100644 index 0000000000..4512903c68 --- /dev/null +++ b/osu.Game.Tests/Resources/timingpoint-speedmultiplier-reset.osu @@ -0,0 +1,5 @@ +osu file format v14 + +[TimingPoints] +0,-200,4,1,0,100,0,0 +2000,100,1,1,0,100,1,0 diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index a6b60bd7a0..aeb5df46f8 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -411,15 +411,15 @@ namespace osu.Game.Beatmaps.Formats private void addControlPoint(double time, ControlPoint point, bool timingChange) { + if (time != pendingControlPointsTime) + flushPendingPoints(); + if (timingChange) { beatmap.ControlPointInfo.Add(time, point); return; } - if (time != pendingControlPointsTime) - flushPendingPoints(); - pendingControlPoints.Add(point); pendingControlPointsTime = time; }