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.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
{
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.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);
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs
index 39c2fbfcc9..01400bf1d9 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs
@@ -7,6 +7,10 @@ using osu.Game.Online.Chat;
using osu.Game.Users;
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
{
@@ -42,14 +46,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 +61,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 +123,49 @@ 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);
+
+ 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);
+ }
+
+ private class TestStandAloneChatDisplay : StandAloneChatDisplay
+ {
+ public TestStandAloneChatDisplay(bool textbox = false)
+ : base(textbox)
+ {
+ }
+
+ 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.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()
{
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledComponent.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledDrawable.cs
similarity index 82%
rename from osu.Game.Tests/Visual/UserInterface/TestSceneLabelledComponent.cs
rename to osu.Game.Tests/Visual/UserInterface/TestSceneLabelledDrawable.cs
index 700adad9cb..8179f92ffc 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledComponent.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledDrawable.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/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);
};
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)
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;
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..427bd8dcde 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;
@@ -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,32 @@ 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.MaxHistory;
+ int count = staleMessages.Length - Channel.MAX_HISTORY;
- for (int i = 0; i < count; i++)
+ if (count > 0)
{
- var d = staleMessages[i];
- if (!scroll.IsScrolledToEnd(10))
+ void expireAndAdjustScroll(Drawable d)
+ {
scroll.OffsetScrollPosition(-d.DrawHeight);
- d.Expire();
+ 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)
+ scrollToEnd();
}
private void pendingMessageResolved(Message existing, Message updated)
@@ -141,7 +156,7 @@ namespace osu.Game.Overlays.Chat
private void scrollToEnd() => ScheduleAfterChildren(() => scroll.ScrollToEnd());
- protected class DaySeparator : Container
+ public class DaySeparator : Container
{
public float TextSize
{
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
{
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]
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);
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;
diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroup.cs b/osu.Game/Screens/Select/Carousel/CarouselGroup.cs
index 09b728abeb..aa48d1a04e 100644
--- a/osu.Game/Screens/Select/Carousel/CarouselGroup.cs
+++ b/osu.Game/Screens/Select/Carousel/CarouselGroup.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
+using System.Linq;
namespace osu.Game.Screens.Select.Carousel
{
@@ -81,12 +82,10 @@ 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
+ 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)
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 @@
-
-
+
+