1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 08:23:00 +08:00

Merge branch 'master' into bindable-control-point-properties

This commit is contained in:
Dan Balasescu 2019-10-30 16:02:39 +09:00 committed by GitHub
commit 3045ba4bf4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 300 additions and 181 deletions

View File

@ -62,6 +62,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.1023.0" /> <PackageReference Include="ppy.osu.Framework.Android" Version="2019.1029.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -7,7 +7,6 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Effects;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osuTK.Graphics; using osuTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -30,17 +29,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Children = new Drawable[] Children = new Drawable[]
{ {
new CircularContainer new Container
{ {
Masking = true, Masking = true,
Origin = Anchor.Centre,
EdgeEffect = new EdgeEffectParameters EdgeEffect = new EdgeEffectParameters
{ {
Type = EdgeEffectType.Glow, Type = EdgeEffectType.Glow,
Radius = 60, Radius = 60,
Colour = Color4.White.Opacity(0.5f), Colour = Color4.White.Opacity(0.5f),
}, },
Child = new Box()
}, },
number = new SkinnableSpriteText(new OsuSkinComponent(OsuSkinComponents.HitCircleText), _ => new OsuSpriteText number = new SkinnableSpriteText(new OsuSkinComponent(OsuSkinComponents.HitCircleText), _ => new OsuSpriteText
{ {

View File

@ -3,6 +3,7 @@
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.HUD;
using osu.Game.Screens.Play.PlayerSettings; using osu.Game.Screens.Play.PlayerSettings;
@ -20,6 +21,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
Anchor = Anchor.TopRight, Anchor = Anchor.TopRight,
Origin = Anchor.TopRight, Origin = Anchor.TopRight,
State = { Value = Visibility.Visible }
}); });
Add(container = new ExampleContainer()); Add(container = new ExampleContainer());

View File

@ -33,23 +33,15 @@ namespace osu.Game.Tests.Visual.Menus
[Test] [Test]
public void TestInstantLoad() 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 = new TestLoader();
loader.AllowLoad.Set(); loader.AllowLoad.Set();
LoadScreen(loader); LoadScreen(loader);
}); });
AddUntilStep("loaded", () =>
{
logoVisible = loader.Logo?.Alpha > 0;
return loader.Logo != null && loader.ScreenLoaded;
});
AddAssert("logo was not visible", () => !logoVisible);
} }
[Test] [Test]
@ -58,7 +50,7 @@ namespace osu.Game.Tests.Visual.Menus
AddStep("begin loading", () => LoadScreen(loader = new TestLoader())); AddStep("begin loading", () => LoadScreen(loader = new TestLoader()));
AddUntilStep("wait for logo visible", () => loader.Logo?.Alpha > 0); AddUntilStep("wait for logo visible", () => loader.Logo?.Alpha > 0);
AddStep("finish loading", () => loader.AllowLoad.Set()); 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); AddUntilStep("logo gone", () => loader.Logo?.Alpha == 0);
} }

View File

@ -7,6 +7,10 @@ using osu.Game.Online.Chat;
using osu.Game.Users; using osu.Game.Users;
using osuTK; using osuTK;
using System; 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 namespace osu.Game.Tests.Visual.Online
{ {
@ -42,14 +46,14 @@ namespace osu.Game.Tests.Visual.Online
[Cached] [Cached]
private ChannelManager channelManager = new ChannelManager(); private ChannelManager channelManager = new ChannelManager();
private readonly StandAloneChatDisplay chatDisplay; private readonly TestStandAloneChatDisplay chatDisplay;
private readonly StandAloneChatDisplay chatDisplay2; private readonly TestStandAloneChatDisplay chatDisplay2;
public TestSceneStandAloneChatDisplay() public TestSceneStandAloneChatDisplay()
{ {
Add(channelManager); Add(channelManager);
Add(chatDisplay = new StandAloneChatDisplay Add(chatDisplay = new TestStandAloneChatDisplay
{ {
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,
@ -57,7 +61,7 @@ namespace osu.Game.Tests.Visual.Online
Size = new Vector2(400, 80) Size = new Vector2(400, 80)
}); });
Add(chatDisplay2 = new StandAloneChatDisplay(true) Add(chatDisplay2 = new TestStandAloneChatDisplay(true)
{ {
Anchor = Anchor.CentreRight, Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight, Origin = Anchor.CentreRight,
@ -119,6 +123,49 @@ namespace osu.Game.Tests.Visual.Online
Content = "Message from the future!", Content = "Message from the future!",
Timestamp = DateTimeOffset.Now 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<DrawableChannel.DaySeparator>().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<DrawableChannel>().First();
protected OsuScrollContainer ScrollContainer => (OsuScrollContainer)((Container)DrawableChannel.Child).Child;
public FillFlowContainer FillFlow => (FillFlowContainer)ScrollContainer.Child;
public bool ScrolledToBottom => ScrollContainer.IsScrolledToEnd(1);
} }
} }
} }

View File

@ -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}!")); AddAssert($"Check #{set_count} is at bottom", () => carousel.BeatmapSets.Last().Metadata.Title.EndsWith($"#{set_count}!"));
} }
[Test]
public void TestSortingStability()
{
var sets = new List<BeatmapSetInfo>();
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] [Test]
public void TestSortingWithFiltered() public void TestSortingWithFiltered()
{ {

View File

@ -11,7 +11,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface namespace osu.Game.Tests.Visual.UserInterface
{ {
public class TestSceneLabelledComponent : OsuTestScene public class TestSceneLabelledDrawable : OsuTestScene
{ {
[TestCase(false)] [TestCase(false)]
[TestCase(true)] [TestCase(true)]
@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.UserInterface
{ {
AddStep("create component", () => AddStep("create component", () =>
{ {
LabelledComponent<Drawable> component; LabelledDrawable<Drawable> component;
Child = new Container Child = new Container
{ {
@ -33,7 +33,7 @@ namespace osu.Game.Tests.Visual.UserInterface
Origin = Anchor.Centre, Origin = Anchor.Centre,
Width = 500, Width = 500,
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Child = component = padded ? (LabelledComponent<Drawable>)new PaddedLabelledComponent() : new NonPaddedLabelledComponent(), Child = component = padded ? (LabelledDrawable<Drawable>)new PaddedLabelledDrawable() : new NonPaddedLabelledDrawable(),
}; };
component.Label = "a sample component"; component.Label = "a sample component";
@ -41,9 +41,9 @@ namespace osu.Game.Tests.Visual.UserInterface
}); });
} }
private class PaddedLabelledComponent : LabelledComponent<Drawable> private class PaddedLabelledDrawable : LabelledDrawable<Drawable>
{ {
public PaddedLabelledComponent() public PaddedLabelledDrawable()
: base(true) : base(true)
{ {
} }
@ -57,9 +57,9 @@ namespace osu.Game.Tests.Visual.UserInterface
}; };
} }
private class NonPaddedLabelledComponent : LabelledComponent<Drawable> private class NonPaddedLabelledDrawable : LabelledDrawable<Drawable>
{ {
public NonPaddedLabelledComponent() public NonPaddedLabelledDrawable()
: base(false) : base(false)
{ {
} }

View File

@ -7,7 +7,6 @@ using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Graphics.UserInterfaceV2;
namespace osu.Game.Tests.Visual.UserInterface namespace osu.Game.Tests.Visual.UserInterface
@ -28,7 +27,7 @@ namespace osu.Game.Tests.Visual.UserInterface
{ {
AddStep("create component", () => AddStep("create component", () =>
{ {
LabelledComponent<OsuTextBox> component; LabelledTextBox component;
Child = new Container Child = new Container
{ {

View File

@ -89,7 +89,7 @@ namespace osu.Game.Tournament.Screens
}; };
} }
private class ActionableInfo : LabelledComponent<Drawable> private class ActionableInfo : LabelledDrawable<Drawable>
{ {
private OsuButton button; private OsuButton button;

View File

@ -108,7 +108,7 @@ namespace osu.Game.Database
return Import(notification, paths); return Import(notification, paths);
} }
protected async Task Import(ProgressNotification notification, params string[] paths) protected async Task<IEnumerable<TModel>> Import(ProgressNotification notification, params string[] paths)
{ {
notification.Progress = 0; notification.Progress = 0;
notification.Text = $"{HumanisedModelName.Humanize(LetterCasing.Title)} import is initialising..."; notification.Text = $"{HumanisedModelName.Humanize(LetterCasing.Title)} import is initialising...";
@ -168,6 +168,8 @@ namespace osu.Game.Database
notification.State = ProgressNotificationState.Completed; notification.State = ProgressNotificationState.Completed;
} }
return imported;
} }
/// <summary> /// <summary>

View File

@ -76,7 +76,12 @@ namespace osu.Game.Database
Task.Factory.StartNew(async () => Task.Factory.StartNew(async () =>
{ {
// This gets scheduled back to the update thread, but we want the import to run in the background. // 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); currentDownloads.Remove(request);
}, TaskCreationOptions.LongRunning); }, TaskCreationOptions.LongRunning);
}; };

View File

@ -59,7 +59,7 @@ namespace osu.Game.Graphics.UserInterface
protected override bool OnHover(HoverEvent e) protected override bool OnHover(HoverEvent e)
{ {
hover.FadeIn(200); hover.FadeIn(200);
return base.OnHover(e); return true;
} }
protected override void OnHoverLost(HoverLostEvent e) protected override void OnHoverLost(HoverLostEvent e)

View File

@ -1,132 +1,24 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // 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;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
using osuTK;
namespace osu.Game.Graphics.UserInterfaceV2 namespace osu.Game.Graphics.UserInterfaceV2
{ {
public abstract class LabelledComponent<T> : CompositeDrawable public abstract class LabelledComponent<T, U> : LabelledDrawable<T>, IHasCurrentValue<U>
where T : Drawable where T : Drawable, IHasCurrentValue<U>
{ {
protected const float CONTENT_PADDING_VERTICAL = 10;
protected const float CONTENT_PADDING_HORIZONTAL = 15;
protected const float CORNER_RADIUS = 15;
/// <summary>
/// The component that is being displayed.
/// </summary>
protected readonly T Component;
private readonly OsuTextFlowContainer labelText;
private readonly OsuTextFlowContainer descriptionText;
/// <summary>
/// Creates a new <see cref="LabelledComponent{T}"/>.
/// </summary>
/// <param name="padded">Whether the component should be padded or should be expanded to the bounds of this <see cref="LabelledComponent{T}"/>.</param>
protected LabelledComponent(bool padded) 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] public Bindable<U> Current
private void load(OsuColour osuColour)
{ {
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();
}
}
/// <summary>
/// Creates the component that should be displayed.
/// </summary>
/// <returns>The component.</returns>
protected abstract T CreateComponent();
} }
} }

View File

@ -0,0 +1,132 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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<T> : CompositeDrawable
where T : Drawable
{
protected const float CONTENT_PADDING_VERTICAL = 10;
protected const float CONTENT_PADDING_HORIZONTAL = 15;
protected const float CORNER_RADIUS = 15;
/// <summary>
/// The component that is being displayed.
/// </summary>
protected readonly T Component;
private readonly OsuTextFlowContainer labelText;
private readonly OsuTextFlowContainer descriptionText;
/// <summary>
/// Creates a new <see cref="LabelledComponent{T, U}"/>.
/// </summary>
/// <param name="padded">Whether the component should be padded or should be expanded to the bounds of this <see cref="LabelledComponent{T, U}"/>.</param>
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();
}
}
/// <summary>
/// Creates the component that should be displayed.
/// </summary>
/// <returns>The component.</returns>
protected abstract T CreateComponent();
}
}

View File

@ -3,7 +3,7 @@
namespace osu.Game.Graphics.UserInterfaceV2 namespace osu.Game.Graphics.UserInterfaceV2
{ {
public class LabelledSwitchButton : LabelledComponent<SwitchButton> public class LabelledSwitchButton : LabelledComponent<SwitchButton, bool>
{ {
public LabelledSwitchButton() public LabelledSwitchButton()
: base(true) : base(true)

View File

@ -8,7 +8,7 @@ using osu.Game.Graphics.UserInterface;
namespace osu.Game.Graphics.UserInterfaceV2 namespace osu.Game.Graphics.UserInterfaceV2
{ {
public class LabelledTextBox : LabelledComponent<OsuTextBox> public class LabelledTextBox : LabelledComponent<OsuTextBox, string>
{ {
public event TextBox.OnCommitHandler OnCommit; public event TextBox.OnCommitHandler OnCommit;

View File

@ -14,7 +14,7 @@ namespace osu.Game.Online.Chat
{ {
public class Channel public class Channel
{ {
public readonly int MaxHistory = 300; public const int MAX_HISTORY = 300;
/// <summary> /// <summary>
/// Contains every joined user except the current logged in user. Currently only returned for PM channels. /// 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
/// </summary> /// </summary>
public Bindable<bool> Joined = new Bindable<bool>(); public Bindable<bool> Joined = new Bindable<bool>();
public const int MAX_HISTORY = 300;
[JsonConstructor] [JsonConstructor]
public Channel() public Channel()
{ {
@ -162,8 +160,8 @@ namespace osu.Game.Online.Chat
{ {
// never purge local echos // never purge local echos
int messageCount = Messages.Count - pendingMessages.Count; int messageCount = Messages.Count - pendingMessages.Count;
if (messageCount > MaxHistory) if (messageCount > MAX_HISTORY)
Messages.RemoveRange(0, messageCount - MaxHistory); Messages.RemoveRange(0, messageCount - MAX_HISTORY);
} }
} }
} }

View File

@ -1,4 +1,4 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
@ -89,8 +89,10 @@ namespace osu.Game.Overlays.Chat
private void newMessagesArrived(IEnumerable<Message> newMessages) private void newMessagesArrived(IEnumerable<Message> newMessages)
{ {
bool shouldScrollToEnd = scroll.IsScrolledToEnd(10) || !chatLines.Any() || newMessages.Any(m => m is LocalMessage);
// Add up to last Channel.MAX_HISTORY messages // 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; Message lastMessage = chatLines.LastOrDefault()?.Message;
@ -103,19 +105,32 @@ namespace osu.Game.Overlays.Chat
lastMessage = message; 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(); 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]; void expireAndAdjustScroll(Drawable d)
if (!scroll.IsScrolledToEnd(10)) {
scroll.OffsetScrollPosition(-d.DrawHeight); 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) private void pendingMessageResolved(Message existing, Message updated)
@ -141,7 +156,7 @@ namespace osu.Game.Overlays.Chat
private void scrollToEnd() => ScheduleAfterChildren(() => scroll.ScrollToEnd()); private void scrollToEnd() => ScheduleAfterChildren(() => scroll.ScrollToEnd());
protected class DaySeparator : Container public class DaySeparator : Container
{ {
public float TextSize public float TextSize
{ {

View File

@ -200,6 +200,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
{ {
private TextBox username; private TextBox username;
private TextBox password; private TextBox password;
private ShakeContainer shakeSignIn;
private IAPIProvider api; private IAPIProvider api;
public Action RequestHide; public Action RequestHide;
@ -208,6 +209,8 @@ namespace osu.Game.Overlays.Settings.Sections.General
{ {
if (!string.IsNullOrEmpty(username.Text) && !string.IsNullOrEmpty(password.Text)) if (!string.IsNullOrEmpty(username.Text) && !string.IsNullOrEmpty(password.Text))
api.Login(username.Text, password.Text); api.Login(username.Text, password.Text);
else
shakeSignIn.Shake();
} }
[BackgroundDependencyLoader(permitNulls: true)] [BackgroundDependencyLoader(permitNulls: true)]
@ -244,10 +247,23 @@ namespace osu.Game.Overlays.Settings.Sections.General
LabelText = "Stay signed in", LabelText = "Stay signed in",
Bindable = config.GetBindable<bool>(OsuSetting.SavePassword), Bindable = config.GetBindable<bool>(OsuSetting.SavePassword),
}, },
new SettingsButton new Container
{ {
Text = "Sign in", RelativeSizeAxes = Axes.X,
Action = performLogin 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 new SettingsButton
{ {

View File

@ -42,6 +42,7 @@ namespace osu.Game.Screens.Menu
public IntroSequence() public IntroSequence()
{ {
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
Alpha = 0;
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]

View File

@ -45,8 +45,6 @@ namespace osu.Game.Screens.Play.HUD
VisualSettings = new VisualSettings { Expanded = false } VisualSettings = new VisualSettings { Expanded = false }
} }
}; };
Show();
} }
protected override void PopIn() => this.FadeIn(fade_duration); protected override void PopIn() => this.FadeIn(fade_duration);

View File

@ -106,6 +106,8 @@ namespace osu.Game.Screens.Play
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete();
Show(); Show();
replayLoaded.ValueChanged += loaded => AllowSeeking = loaded.NewValue; replayLoaded.ValueChanged += loaded => AllowSeeking = loaded.NewValue;

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace osu.Game.Screens.Select.Carousel namespace osu.Game.Screens.Select.Carousel
{ {
@ -81,12 +82,10 @@ namespace osu.Game.Screens.Select.Carousel
{ {
base.Filter(criteria); base.Filter(criteria);
var children = new List<CarouselItem>(InternalChildren); InternalChildren.ForEach(c => c.Filter(criteria));
// IEnumerable<T>.OrderBy() is used instead of List<T>.Sort() to ensure sorting stability
children.ForEach(c => c.Filter(criteria)); var criteriaComparer = Comparer<CarouselItem>.Create((x, y) => x.CompareTo(criteria, y));
children.Sort((x, y) => x.CompareTo(criteria, y)); InternalChildren = InternalChildren.OrderBy(c => c, criteriaComparer).ToList();
InternalChildren = children;
} }
protected virtual void ChildItemStateChanged(CarouselItem item, CarouselItemState value) protected virtual void ChildItemStateChanged(CarouselItem item, CarouselItemState value)

View File

@ -26,7 +26,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.1023.0" /> <PackageReference Include="ppy.osu.Framework" Version="2019.1029.0" />
<PackageReference Include="SharpCompress" Version="0.24.0" /> <PackageReference Include="SharpCompress" Version="0.24.0" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" /> <PackageReference Include="SharpRaven" Version="2.4.0" />

View File

@ -118,8 +118,8 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.1023.0" /> <PackageReference Include="ppy.osu.Framework" Version="2019.1029.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.1023.0" /> <PackageReference Include="ppy.osu.Framework.iOS" Version="2019.1029.0" />
<PackageReference Include="SharpCompress" Version="0.24.0" /> <PackageReference Include="SharpCompress" Version="0.24.0" />
<PackageReference Include="NUnit" Version="3.11.0" /> <PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" /> <PackageReference Include="SharpRaven" Version="2.4.0" />