Merge remote-tracking branch 'refs/remotes/ppy/master' into overlay-ruleset-selector

This commit is contained in:
Andrei Zavatski 2020-01-30 10:08:39 +03:00
commit 10e8361e7c
111 changed files with 2228 additions and 1042 deletions

View File

@ -135,7 +135,7 @@ csharp_preferred_modifier_order = public,private,protected,internal,new,abstract
csharp_style_expression_bodied_accessors = true:warning csharp_style_expression_bodied_accessors = true:warning
csharp_style_expression_bodied_constructors = false:none csharp_style_expression_bodied_constructors = false:none
csharp_style_expression_bodied_indexers = true:warning csharp_style_expression_bodied_indexers = true:warning
csharp_style_expression_bodied_methods = true:silent csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_operators = true:warning csharp_style_expression_bodied_operators = true:warning
csharp_style_expression_bodied_properties = true:warning csharp_style_expression_bodied_properties = true:warning
csharp_style_expression_bodied_local_functions = true:silent csharp_style_expression_bodied_local_functions = true:silent

View File

@ -29,7 +29,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.6" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.6" />
<PackageReference Include="Microsoft.Win32.Registry" Version="4.7.0" /> <PackageReference Include="Microsoft.Win32.Registry" Version="4.7.0" />
<PackageReference Include="DiscordRichPresence" Version="1.0.121" /> <PackageReference Include="DiscordRichPresence" Version="1.0.147" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Resources"> <ItemGroup Label="Resources">
<EmbeddedResource Include="lazer.ico" /> <EmbeddedResource Include="lazer.ico" />

View File

@ -20,7 +20,9 @@ namespace osu.Game.Rulesets.Catch.UI
internal readonly CatcherArea CatcherArea; internal readonly CatcherArea CatcherArea;
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || CatcherArea.ReceivePositionalInputAt(screenSpacePos); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) =>
// only check the X position; handle all vertical space.
base.ReceivePositionalInputAt(new Vector2(screenSpacePos.X, ScreenSpaceDrawQuad.Centre.Y));
public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> createDrawableRepresentation) public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> createDrawableRepresentation)
{ {

View File

@ -237,19 +237,19 @@ namespace osu.Game.Rulesets.Mania
{ {
LeftKeys = new[] LeftKeys = new[]
{ {
InputKey.Number1, InputKey.Q,
InputKey.Number2, InputKey.W,
InputKey.Number3, InputKey.E,
InputKey.Number4, InputKey.R,
}, },
RightKeys = new[] RightKeys = new[]
{ {
InputKey.Z,
InputKey.X, InputKey.X,
InputKey.C, InputKey.C,
InputKey.V InputKey.V,
InputKey.B
}, },
SpecialKey = InputKey.Tilde, SpecialKey = InputKey.S,
SpecialAction = ManiaAction.Special1, SpecialAction = ManiaAction.Special1,
NormalActionStart = ManiaAction.Key1 NormalActionStart = ManiaAction.Key1
}.GenerateKeyBindingsFor(keys, out var nextNormal); }.GenerateKeyBindingsFor(keys, out var nextNormal);
@ -265,12 +265,12 @@ namespace osu.Game.Rulesets.Mania
}, },
RightKeys = new[] RightKeys = new[]
{ {
InputKey.O, InputKey.K,
InputKey.P, InputKey.L,
InputKey.BracketLeft, InputKey.Semicolon,
InputKey.BracketRight InputKey.Quote
}, },
SpecialKey = InputKey.BackSlash, SpecialKey = InputKey.I,
SpecialAction = ManiaAction.Special2, SpecialAction = ManiaAction.Special2,
NormalActionStart = nextNormal NormalActionStart = nextNormal
}.GenerateKeyBindingsFor(keys, out _); }.GenerateKeyBindingsFor(keys, out _);

View File

@ -118,17 +118,19 @@ namespace osu.Game.Tests.Editor
[Test] [Test]
public void TestGetSnappedDurationFromDistance() public void TestGetSnappedDurationFromDistance()
{ {
assertSnappedDuration(50, 0); assertSnappedDuration(0, 0);
assertSnappedDuration(50, 1000);
assertSnappedDuration(100, 1000); assertSnappedDuration(100, 1000);
assertSnappedDuration(150, 1000); assertSnappedDuration(150, 2000);
assertSnappedDuration(200, 2000); assertSnappedDuration(200, 2000);
assertSnappedDuration(250, 2000); assertSnappedDuration(250, 3000);
AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2); AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2);
assertSnappedDuration(0, 0);
assertSnappedDuration(50, 0); assertSnappedDuration(50, 0);
assertSnappedDuration(100, 0); assertSnappedDuration(100, 1000);
assertSnappedDuration(150, 0); assertSnappedDuration(150, 1000);
assertSnappedDuration(200, 1000); assertSnappedDuration(200, 1000);
assertSnappedDuration(250, 1000); assertSnappedDuration(250, 1000);
@ -139,8 +141,8 @@ namespace osu.Game.Tests.Editor
}); });
assertSnappedDuration(50, 0); assertSnappedDuration(50, 0);
assertSnappedDuration(100, 0); assertSnappedDuration(100, 500);
assertSnappedDuration(150, 0); assertSnappedDuration(150, 500);
assertSnappedDuration(200, 500); assertSnappedDuration(200, 500);
assertSnappedDuration(250, 500); assertSnappedDuration(250, 500);
assertSnappedDuration(400, 1000); assertSnappedDuration(400, 1000);
@ -149,17 +151,17 @@ namespace osu.Game.Tests.Editor
[Test] [Test]
public void GetSnappedDistanceFromDistance() public void GetSnappedDistanceFromDistance()
{ {
assertSnappedDistance(50, 0); assertSnappedDistance(50, 100);
assertSnappedDistance(100, 100); assertSnappedDistance(100, 100);
assertSnappedDistance(150, 100); assertSnappedDistance(150, 200);
assertSnappedDistance(200, 200); assertSnappedDistance(200, 200);
assertSnappedDistance(250, 200); assertSnappedDistance(250, 300);
AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2); AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2);
assertSnappedDistance(50, 0); assertSnappedDistance(50, 0);
assertSnappedDistance(100, 0); assertSnappedDistance(100, 200);
assertSnappedDistance(150, 0); assertSnappedDistance(150, 200);
assertSnappedDistance(200, 200); assertSnappedDistance(200, 200);
assertSnappedDistance(250, 200); assertSnappedDistance(250, 200);
@ -170,8 +172,8 @@ namespace osu.Game.Tests.Editor
}); });
assertSnappedDistance(50, 0); assertSnappedDistance(50, 0);
assertSnappedDistance(100, 0); assertSnappedDistance(100, 200);
assertSnappedDistance(150, 0); assertSnappedDistance(150, 200);
assertSnappedDistance(200, 200); assertSnappedDistance(200, 200);
assertSnappedDistance(250, 200); assertSnappedDistance(250, 200);
assertSnappedDistance(400, 400); assertSnappedDistance(400, 400);

View File

@ -85,64 +85,64 @@ namespace osu.Game.Tests.Visual.Editor
{ {
} }
protected override void CreateContent(Vector2 startPosition) protected override void CreateContent()
{ {
AddInternal(new Circle AddInternal(new Circle
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Size = new Vector2(5), Size = new Vector2(5),
Position = startPosition Position = StartPosition
}); });
int beatIndex = 0; int indexFromPlacement = 0;
for (float s = startPosition.X + DistanceSpacing; s <= DrawWidth && beatIndex < MaxIntervals; s += DistanceSpacing, beatIndex++) for (float s = StartPosition.X + DistanceSpacing; s <= DrawWidth && indexFromPlacement < MaxIntervals; s += DistanceSpacing, indexFromPlacement++)
{ {
AddInternal(new Circle AddInternal(new Circle
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Size = new Vector2(5, 10), Size = new Vector2(5, 10),
Position = new Vector2(s, startPosition.Y), Position = new Vector2(s, StartPosition.Y),
Colour = GetColourForBeatIndex(beatIndex) Colour = GetColourForIndexFromPlacement(indexFromPlacement)
}); });
} }
beatIndex = 0; indexFromPlacement = 0;
for (float s = startPosition.X - DistanceSpacing; s >= 0 && beatIndex < MaxIntervals; s -= DistanceSpacing, beatIndex++) for (float s = StartPosition.X - DistanceSpacing; s >= 0 && indexFromPlacement < MaxIntervals; s -= DistanceSpacing, indexFromPlacement++)
{ {
AddInternal(new Circle AddInternal(new Circle
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Size = new Vector2(5, 10), Size = new Vector2(5, 10),
Position = new Vector2(s, startPosition.Y), Position = new Vector2(s, StartPosition.Y),
Colour = GetColourForBeatIndex(beatIndex) Colour = GetColourForIndexFromPlacement(indexFromPlacement)
}); });
} }
beatIndex = 0; indexFromPlacement = 0;
for (float s = startPosition.Y + DistanceSpacing; s <= DrawHeight && beatIndex < MaxIntervals; s += DistanceSpacing, beatIndex++) for (float s = StartPosition.Y + DistanceSpacing; s <= DrawHeight && indexFromPlacement < MaxIntervals; s += DistanceSpacing, indexFromPlacement++)
{ {
AddInternal(new Circle AddInternal(new Circle
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Size = new Vector2(10, 5), Size = new Vector2(10, 5),
Position = new Vector2(startPosition.X, s), Position = new Vector2(StartPosition.X, s),
Colour = GetColourForBeatIndex(beatIndex) Colour = GetColourForIndexFromPlacement(indexFromPlacement)
}); });
} }
beatIndex = 0; indexFromPlacement = 0;
for (float s = startPosition.Y - DistanceSpacing; s >= 0 && beatIndex < MaxIntervals; s -= DistanceSpacing, beatIndex++) for (float s = StartPosition.Y - DistanceSpacing; s >= 0 && indexFromPlacement < MaxIntervals; s -= DistanceSpacing, indexFromPlacement++)
{ {
AddInternal(new Circle AddInternal(new Circle
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Size = new Vector2(10, 5), Size = new Vector2(10, 5),
Position = new Vector2(startPosition.X, s), Position = new Vector2(StartPosition.X, s),
Colour = GetColourForBeatIndex(beatIndex) Colour = GetColourForIndexFromPlacement(indexFromPlacement)
}); });
} }
} }

View File

@ -1,146 +1,15 @@
// 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.Collections.Generic;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Compose.Components.Timeline; using osu.Game.Screens.Edit.Compose.Components.Timeline;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editor
{ {
[TestFixture] [TestFixture]
public class TestSceneTimelineBlueprintContainer : EditorClockTestScene public class TestSceneTimelineBlueprintContainer : TimelineTestScene
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] public override Drawable CreateTestComponent() => new TimelineBlueprintContainer();
{
typeof(TimelineArea),
typeof(Timeline),
typeof(TimelineButton),
typeof(CentreMarker)
};
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
Beatmap.Value = new WaveformTestBeatmap(audio);
var editorBeatmap = new EditorBeatmap((Beatmap<HitObject>)Beatmap.Value.Beatmap, BeatDivisor);
Dependencies.Cache(editorBeatmap);
Dependencies.CacheAs<IBeatSnapProvider>(editorBeatmap);
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
Children = new Drawable[]
{
new StartStopButton(),
new AudioVisualiser(),
}
},
new TimelineArea
{
Child = new TimelineBlueprintContainer(),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Size = new Vector2(0.8f, 100)
}
};
}
private class AudioVisualiser : CompositeDrawable
{
private readonly Drawable marker;
[Resolved]
private IBindable<WorkingBeatmap> beatmap { get; set; }
[Resolved]
private IAdjustableClock adjustableClock { get; set; }
public AudioVisualiser()
{
Size = new Vector2(250, 25);
InternalChildren = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.25f,
},
marker = new Box
{
RelativePositionAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
Width = 2,
}
};
}
protected override void Update()
{
base.Update();
if (beatmap.Value.Track.IsLoaded)
marker.X = (float)(adjustableClock.CurrentTime / beatmap.Value.Track.Length);
}
}
private class StartStopButton : OsuButton
{
private IAdjustableClock adjustableClock;
private bool started;
public StartStopButton()
{
BackgroundColour = Color4.SlateGray;
Size = new Vector2(100, 50);
Text = "Start";
Action = onClick;
}
[BackgroundDependencyLoader]
private void load(IAdjustableClock adjustableClock)
{
this.adjustableClock = adjustableClock;
}
private void onClick()
{
if (started)
{
adjustableClock.Stop();
Text = "Start";
}
else
{
adjustableClock.Start();
Text = "Stop";
}
started = !started;
}
}
} }
} }

View File

@ -0,0 +1,32 @@
// 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 NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Screens.Edit.Compose.Components;
using osu.Game.Screens.Edit.Compose.Components.Timeline;
using osuTK;
namespace osu.Game.Tests.Visual.Editor
{
[TestFixture]
public class TestSceneTimelineTickDisplay : TimelineTestScene
{
public override Drawable CreateTestComponent() => new TimelineTickDisplay();
[BackgroundDependencyLoader]
private void load()
{
BeatDivisor.Value = 4;
Add(new BeatDivisorControl(BeatDivisor)
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Margin = new MarginPadding(30),
Size = new Vector2(90)
});
}
}
}

View File

@ -0,0 +1,148 @@
// 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 System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Compose.Components.Timeline;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Editor
{
public abstract class TimelineTestScene : EditorClockTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(TimelineArea),
typeof(Timeline),
typeof(TimelineButton),
typeof(CentreMarker)
};
protected TimelineArea TimelineArea { get; private set; }
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
Beatmap.Value = new WaveformTestBeatmap(audio);
var editorBeatmap = new EditorBeatmap((Beatmap<HitObject>)Beatmap.Value.Beatmap, BeatDivisor);
Dependencies.Cache(editorBeatmap);
Dependencies.CacheAs<IBeatSnapProvider>(editorBeatmap);
AddRange(new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
Children = new Drawable[]
{
new StartStopButton(),
new AudioVisualiser(),
}
},
TimelineArea = new TimelineArea
{
Child = CreateTestComponent(),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Size = new Vector2(0.8f, 100),
}
});
}
public abstract Drawable CreateTestComponent();
private class AudioVisualiser : CompositeDrawable
{
private readonly Drawable marker;
[Resolved]
private IBindable<WorkingBeatmap> beatmap { get; set; }
[Resolved]
private IAdjustableClock adjustableClock { get; set; }
public AudioVisualiser()
{
Size = new Vector2(250, 25);
InternalChildren = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.25f,
},
marker = new Box
{
RelativePositionAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
Width = 2,
}
};
}
protected override void Update()
{
base.Update();
if (beatmap.Value.Track.IsLoaded)
marker.X = (float)(adjustableClock.CurrentTime / beatmap.Value.Track.Length);
}
}
private class StartStopButton : OsuButton
{
private IAdjustableClock adjustableClock;
private bool started;
public StartStopButton()
{
BackgroundColour = Color4.SlateGray;
Size = new Vector2(100, 50);
Text = "Start";
Action = onClick;
}
[BackgroundDependencyLoader]
private void load(IAdjustableClock adjustableClock)
{
this.adjustableClock = adjustableClock;
}
private void onClick()
{
if (started)
{
adjustableClock.Stop();
Text = "Start";
}
else
{
adjustableClock.Start();
Text = "Stop";
}
started = !started;
}
}
}
}

View File

@ -0,0 +1,122 @@
// 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.Bindables;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Overlays;
using osu.Game.Rulesets;
using osu.Game.Screens;
using osu.Game.Screens.Menu;
using osuTK.Graphics;
using IntroSequence = osu.Game.Configuration.IntroSequence;
namespace osu.Game.Tests.Visual.Navigation
{
/// <summary>
/// A scene which tests full game flow.
/// </summary>
public abstract class OsuGameTestScene : ManualInputManagerTestScene
{
private GameHost host;
protected TestOsuGame Game;
[BackgroundDependencyLoader]
private void load(GameHost host)
{
this.host = host;
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
};
}
[SetUpSteps]
public void SetUpSteps()
{
AddStep("Create new game instance", () =>
{
if (Game != null)
{
Remove(Game);
Game.Dispose();
}
RecycleLocalStorage();
// see MouseSettings
var frameworkConfig = host.Dependencies.Get<FrameworkConfigManager>();
frameworkConfig.GetBindable<double>(FrameworkSetting.CursorSensitivity).Disabled = false;
Game = new TestOsuGame(LocalStorage, API);
Game.SetHost(host);
// todo: this can be removed once we can run audio tracks without a device present
// see https://github.com/ppy/osu/issues/1302
Game.LocalConfig.Set(OsuSetting.IntroSequence, IntroSequence.Circles);
Add(Game);
});
AddUntilStep("Wait for load", () => Game.IsLoaded);
AddUntilStep("Wait for intro", () => Game.ScreenStack.CurrentScreen is IntroScreen);
ConfirmAtMainMenu();
}
protected void ConfirmAtMainMenu() => AddUntilStep("Wait for main menu", () => Game.ScreenStack.CurrentScreen is MainMenu menu && menu.IsLoaded);
public class TestOsuGame : OsuGame
{
public new ScreenStack ScreenStack => base.ScreenStack;
public new BackButton BackButton => base.BackButton;
public new BeatmapManager BeatmapManager => base.BeatmapManager;
public new SettingsPanel Settings => base.Settings;
public new OsuConfigManager LocalConfig => base.LocalConfig;
public new Bindable<WorkingBeatmap> Beatmap => base.Beatmap;
public new Bindable<RulesetInfo> Ruleset => base.Ruleset;
protected override Loader CreateLoader() => new TestLoader();
public TestOsuGame(Storage storage, IAPIProvider api)
{
Storage = storage;
API = api;
}
protected override void LoadComplete()
{
base.LoadComplete();
API.Login("Rhythm Champion", "osu!");
}
}
public class TestLoader : Loader
{
protected override ShaderPrecompiler CreateShaderPrecompiler() => new TestShaderPrecompiler();
private class TestShaderPrecompiler : ShaderPrecompiler
{
protected override bool AllLoaded => true;
}
}
}
}

View File

@ -0,0 +1,110 @@
// 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 System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Menu;
namespace osu.Game.Tests.Visual.Navigation
{
public class TestScenePresentBeatmap : OsuGameTestScene
{
[Test]
public void TestFromMainMenu()
{
var firstImport = importBeatmap(1);
presentAndConfirm(firstImport);
AddStep("return to menu", () => Game.ScreenStack.CurrentScreen.Exit());
AddUntilStep("wait for menu", () => Game.ScreenStack.CurrentScreen is MainMenu);
var secondimport = importBeatmap(2);
presentAndConfirm(secondimport);
}
[Test]
public void TestFromMainMenuDifferentRuleset()
{
var firstImport = importBeatmap(1);
presentAndConfirm(firstImport);
AddStep("return to menu", () => Game.ScreenStack.CurrentScreen.Exit());
AddUntilStep("wait for menu", () => Game.ScreenStack.CurrentScreen is MainMenu);
var secondimport = importBeatmap(2, new ManiaRuleset().RulesetInfo);
presentAndConfirm(secondimport);
}
[Test]
public void TestFromSongSelect()
{
var firstImport = importBeatmap(1);
presentAndConfirm(firstImport);
var secondimport = importBeatmap(2);
presentAndConfirm(secondimport);
}
[Test]
public void TestFromSongSelectDifferentRuleset()
{
var firstImport = importBeatmap(1);
presentAndConfirm(firstImport);
var secondimport = importBeatmap(2, new ManiaRuleset().RulesetInfo);
presentAndConfirm(secondimport);
}
private Func<BeatmapSetInfo> importBeatmap(int i, RulesetInfo ruleset = null)
{
BeatmapSetInfo imported = null;
AddStep($"import beatmap {i}", () =>
{
var difficulty = new BeatmapDifficulty();
var metadata = new BeatmapMetadata
{
Artist = "SomeArtist",
AuthorString = "SomeAuthor",
Title = $"import {i}"
};
imported = Game.BeatmapManager.Import(new BeatmapSetInfo
{
Hash = Guid.NewGuid().ToString(),
OnlineBeatmapSetID = i,
Metadata = metadata,
Beatmaps = new List<BeatmapInfo>
{
new BeatmapInfo
{
OnlineBeatmapID = i * 1024,
Metadata = metadata,
BaseDifficulty = difficulty,
Ruleset = ruleset ?? new OsuRuleset().RulesetInfo
},
}
}).Result;
});
AddAssert($"import {i} succeeded", () => imported != null);
return () => imported;
}
private void presentAndConfirm(Func<BeatmapSetInfo> getImport)
{
AddStep("present beatmap", () => Game.PresentBeatmap(getImport()));
AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is Screens.Select.SongSelect);
AddUntilStep("correct beatmap displayed", () => Game.Beatmap.Value.BeatmapSetInfo.ID == getImport().ID);
AddAssert("correct ruleset selected", () => Game.Ruleset.Value.ID == getImport().Beatmaps.First().Ruleset.ID);
}
}
}

View File

@ -6,78 +6,26 @@ using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio.Track; using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Platform;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.Mods; using osu.Game.Overlays.Mods;
using osu.Game.Screens;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using osu.Game.Screens.Select; using osu.Game.Screens.Select;
using osu.Game.Tests.Beatmaps.IO; using osu.Game.Tests.Beatmaps.IO;
using osuTK; using osuTK;
using osuTK.Graphics;
using osuTK.Input; using osuTK.Input;
using IntroSequence = osu.Game.Configuration.IntroSequence;
namespace osu.Game.Tests.Visual.Menus namespace osu.Game.Tests.Visual.Navigation
{ {
public class TestSceneScreenNavigation : ManualInputManagerTestScene public class TestSceneScreenNavigation : OsuGameTestScene
{ {
private const float click_padding = 25; private const float click_padding = 25;
private GameHost host; private Vector2 backButtonPosition => Game.ToScreenSpace(new Vector2(click_padding, Game.LayoutRectangle.Bottom - click_padding));
private TestOsuGame game;
private Vector2 backButtonPosition => game.ToScreenSpace(new Vector2(click_padding, game.LayoutRectangle.Bottom - click_padding)); private Vector2 optionsButtonPosition => Game.ToScreenSpace(new Vector2(click_padding, click_padding));
private Vector2 optionsButtonPosition => game.ToScreenSpace(new Vector2(click_padding, click_padding));
[BackgroundDependencyLoader]
private void load(GameHost host)
{
this.host = host;
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
};
}
[SetUpSteps]
public void SetUpSteps()
{
AddStep("Create new game instance", () =>
{
if (game != null)
{
Remove(game);
game.Dispose();
}
game = new TestOsuGame(LocalStorage, API);
game.SetHost(host);
// todo: this can be removed once we can run audio trakcs without a device present
// see https://github.com/ppy/osu/issues/1302
game.LocalConfig.Set(OsuSetting.IntroSequence, IntroSequence.Circles);
Add(game);
});
AddUntilStep("Wait for load", () => game.IsLoaded);
AddUntilStep("Wait for intro", () => game.ScreenStack.CurrentScreen is IntroScreen);
confirmAtMainMenu();
}
[Test] [Test]
public void TestExitSongSelectWithEscape() public void TestExitSongSelectWithEscape()
@ -98,21 +46,21 @@ namespace osu.Game.Tests.Visual.Menus
{ {
Player player = null; Player player = null;
WorkingBeatmap beatmap() => game.Beatmap.Value; WorkingBeatmap beatmap() => Game.Beatmap.Value;
Track track() => beatmap().Track; Track track() => beatmap().Track;
pushAndConfirm(() => new TestSongSelect()); pushAndConfirm(() => new TestSongSelect());
AddStep("import beatmap", () => ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).Wait()); AddStep("import beatmap", () => ImportBeatmapTest.LoadOszIntoOsu(Game, virtualTrack: true).Wait());
AddUntilStep("wait for selected", () => !game.Beatmap.IsDefault); AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
if (withUserPause) if (withUserPause)
AddStep("pause", () => game.Dependencies.Get<MusicController>().Stop()); AddStep("pause", () => Game.Dependencies.Get<MusicController>().Stop());
AddStep("press enter", () => pressAndRelease(Key.Enter)); AddStep("press enter", () => pressAndRelease(Key.Enter));
AddUntilStep("wait for player", () => (player = game.ScreenStack.CurrentScreen as Player) != null); AddUntilStep("wait for player", () => (player = Game.ScreenStack.CurrentScreen as Player) != null);
AddUntilStep("wait for fail", () => player.HasFailed); AddUntilStep("wait for fail", () => player.HasFailed);
AddUntilStep("wait for track stop", () => !track().IsRunning); AddUntilStep("wait for track stop", () => !track().IsRunning);
@ -135,7 +83,7 @@ namespace osu.Game.Tests.Visual.Menus
AddStep("Move mouse to backButton", () => InputManager.MoveMouseTo(backButtonPosition)); AddStep("Move mouse to backButton", () => InputManager.MoveMouseTo(backButtonPosition));
// BackButton handles hover using its child button, so this checks whether or not any of BackButton's children are hovered. // BackButton handles hover using its child button, so this checks whether or not any of BackButton's children are hovered.
AddUntilStep("Back button is hovered", () => InputManager.HoveredDrawables.Any(d => d.Parent == game.BackButton)); AddUntilStep("Back button is hovered", () => InputManager.HoveredDrawables.Any(d => d.Parent == Game.BackButton));
AddStep("Click back button", () => InputManager.Click(MouseButton.Left)); AddStep("Click back button", () => InputManager.Click(MouseButton.Left));
AddUntilStep("Overlay was hidden", () => songSelect.ModSelectOverlay.State.Value == Visibility.Hidden); AddUntilStep("Overlay was hidden", () => songSelect.ModSelectOverlay.State.Value == Visibility.Hidden);
@ -159,20 +107,20 @@ namespace osu.Game.Tests.Visual.Menus
[Test] [Test]
public void TestOpenOptionsAndExitWithEscape() public void TestOpenOptionsAndExitWithEscape()
{ {
AddUntilStep("Wait for options to load", () => game.Settings.IsLoaded); AddUntilStep("Wait for options to load", () => Game.Settings.IsLoaded);
AddStep("Enter menu", () => pressAndRelease(Key.Enter)); AddStep("Enter menu", () => pressAndRelease(Key.Enter));
AddStep("Move mouse to options overlay", () => InputManager.MoveMouseTo(optionsButtonPosition)); AddStep("Move mouse to options overlay", () => InputManager.MoveMouseTo(optionsButtonPosition));
AddStep("Click options overlay", () => InputManager.Click(MouseButton.Left)); AddStep("Click options overlay", () => InputManager.Click(MouseButton.Left));
AddAssert("Options overlay was opened", () => game.Settings.State.Value == Visibility.Visible); AddAssert("Options overlay was opened", () => Game.Settings.State.Value == Visibility.Visible);
AddStep("Hide options overlay using escape", () => pressAndRelease(Key.Escape)); AddStep("Hide options overlay using escape", () => pressAndRelease(Key.Escape));
AddAssert("Options overlay was closed", () => game.Settings.State.Value == Visibility.Hidden); AddAssert("Options overlay was closed", () => Game.Settings.State.Value == Visibility.Hidden);
} }
private void pushAndConfirm(Func<Screen> newScreen) private void pushAndConfirm(Func<Screen> newScreen)
{ {
Screen screen = null; Screen screen = null;
AddStep("Push new screen", () => game.ScreenStack.Push(screen = newScreen())); AddStep("Push new screen", () => Game.ScreenStack.Push(screen = newScreen()));
AddUntilStep("Wait for new screen", () => game.ScreenStack.CurrentScreen == screen && screen.IsLoaded); AddUntilStep("Wait for new screen", () => Game.ScreenStack.CurrentScreen == screen && screen.IsLoaded);
} }
private void pushEscape() => private void pushEscape() =>
@ -181,64 +129,25 @@ namespace osu.Game.Tests.Visual.Menus
private void exitViaEscapeAndConfirm() private void exitViaEscapeAndConfirm()
{ {
pushEscape(); pushEscape();
confirmAtMainMenu(); ConfirmAtMainMenu();
} }
private void exitViaBackButtonAndConfirm() private void exitViaBackButtonAndConfirm()
{ {
AddStep("Move mouse to backButton", () => InputManager.MoveMouseTo(backButtonPosition)); AddStep("Move mouse to backButton", () => InputManager.MoveMouseTo(backButtonPosition));
AddStep("Click back button", () => InputManager.Click(MouseButton.Left)); AddStep("Click back button", () => InputManager.Click(MouseButton.Left));
confirmAtMainMenu(); ConfirmAtMainMenu();
} }
private void confirmAtMainMenu() => AddUntilStep("Wait for main menu", () => game.ScreenStack.CurrentScreen is MainMenu menu && menu.IsLoaded);
private void pressAndRelease(Key key) private void pressAndRelease(Key key)
{ {
InputManager.PressKey(key); InputManager.PressKey(key);
InputManager.ReleaseKey(key); InputManager.ReleaseKey(key);
} }
private class TestOsuGame : OsuGame
{
public new ScreenStack ScreenStack => base.ScreenStack;
public new BackButton BackButton => base.BackButton;
public new SettingsPanel Settings => base.Settings;
public new OsuConfigManager LocalConfig => base.LocalConfig;
public new Bindable<WorkingBeatmap> Beatmap => base.Beatmap;
protected override Loader CreateLoader() => new TestLoader();
public TestOsuGame(Storage storage, IAPIProvider api)
{
Storage = storage;
API = api;
}
protected override void LoadComplete()
{
base.LoadComplete();
API.Login("Rhythm Champion", "osu!");
}
}
private class TestSongSelect : PlaySongSelect private class TestSongSelect : PlaySongSelect
{ {
public ModSelectOverlay ModSelectOverlay => ModSelect; public ModSelectOverlay ModSelectOverlay => ModSelect;
} }
private class TestLoader : Loader
{
protected override ShaderPrecompiler CreateShaderPrecompiler() => new TestShaderPrecompiler();
private class TestShaderPrecompiler : ShaderPrecompiler
{
protected override bool AllLoaded => true;
}
}
} }
} }

View File

@ -13,7 +13,7 @@ namespace osu.Game.Tests.Visual.Online
[TestFixture] [TestFixture]
public class TestSceneChangelogOverlay : OsuTestScene public class TestSceneChangelogOverlay : OsuTestScene
{ {
private ChangelogOverlay changelog; private TestChangelogOverlay changelog;
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {
@ -29,23 +29,40 @@ namespace osu.Game.Tests.Visual.Online
protected override bool UseOnlineAPI => true; protected override bool UseOnlineAPI => true;
protected override void LoadComplete() [SetUp]
public void SetUp() => Schedule(() =>
{ {
base.LoadComplete(); Child = changelog = new TestChangelogOverlay();
});
Add(changelog = new ChangelogOverlay()); [Test]
AddStep(@"Show", changelog.Show); public void ShowWithNoFetch()
AddStep(@"Hide", changelog.Hide); {
AddStep(@"Show", () => changelog.Show());
AddUntilStep(@"wait for streams", () => changelog.Streams?.Count > 0);
AddAssert(@"listing displayed", () => changelog.Current.Value == null);
AddAssert(@"no stream selected", () => changelog.Header.Streams.Current.Value == null);
}
AddWaitStep("wait for hide", 3); [Test]
public void ShowWithListing()
{
AddStep(@"Show with listing", () => changelog.ShowListing());
AddUntilStep(@"wait for streams", () => changelog.Streams?.Count > 0);
AddAssert(@"listing displayed", () => changelog.Current.Value == null);
AddAssert(@"no stream selected", () => changelog.Header.Streams.Current.Value == null);
}
[Test]
public void ShowWithBuild()
{
AddStep(@"Show with Lazer 2018.712.0", () => AddStep(@"Show with Lazer 2018.712.0", () =>
{ {
changelog.ShowBuild(new APIChangelogBuild changelog.ShowBuild(new APIChangelogBuild
{ {
Version = "2018.712.0", Version = "2018.712.0",
DisplayVersion = "2018.712.0", DisplayVersion = "2018.712.0",
UpdateStream = new APIUpdateStream { Name = OsuGameBase.CLIENT_STREAM_NAME }, UpdateStream = new APIUpdateStream { Id = 7, Name = OsuGameBase.CLIENT_STREAM_NAME },
ChangelogEntries = new List<APIChangelogEntry> ChangelogEntries = new List<APIChangelogEntry>
{ {
new APIChangelogEntry new APIChangelogEntry
@ -56,19 +73,16 @@ namespace osu.Game.Tests.Visual.Online
} }
} }
}); });
changelog.Show();
}); });
AddWaitStep("wait for show", 3); AddUntilStep(@"wait for streams", () => changelog.Streams?.Count > 0);
AddStep(@"Hide", changelog.Hide); AddAssert(@"correct build displayed", () => changelog.Current.Value.Version == "2018.712.0");
AddWaitStep("wait for hide", 3); AddAssert(@"correct stream selected", () => changelog.Header.Streams.Current.Value.Id == 7);
}
AddStep(@"Show with listing", () =>
{
changelog.ShowListing();
changelog.Show();
});
[Test]
public void TestHTMLUnescaping()
{
AddStep(@"Ensure HTML string unescaping", () => AddStep(@"Ensure HTML string unescaping", () =>
{ {
changelog.ShowBuild(new APIChangelogBuild changelog.ShowBuild(new APIChangelogBuild
@ -97,5 +111,12 @@ namespace osu.Game.Tests.Visual.Online
}); });
}); });
} }
private class TestChangelogOverlay : ChangelogOverlay
{
public new List<APIUpdateStream> Streams => base.Streams;
public new ChangelogHeader Header => base.Header;
}
} }
} }

View File

@ -8,6 +8,8 @@ using osu.Game.Online.API.Requests;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Overlays.Comments; using osu.Game.Overlays.Comments;
using osu.Game.Overlays;
using osu.Framework.Allocation;
namespace osu.Game.Tests.Visual.Online namespace osu.Game.Tests.Visual.Online
{ {
@ -22,12 +24,15 @@ namespace osu.Game.Tests.Visual.Online
typeof(HeaderButton), typeof(HeaderButton),
typeof(SortTabControl), typeof(SortTabControl),
typeof(ShowChildrenButton), typeof(ShowChildrenButton),
typeof(DeletedChildrenPlaceholder), typeof(DeletedCommentsCounter),
typeof(VotePill) typeof(VotePill)
}; };
protected override bool UseOnlineAPI => true; protected override bool UseOnlineAPI => true;
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
public TestSceneCommentsContainer() public TestSceneCommentsContainer()
{ {
BasicScrollContainer scroll; BasicScrollContainer scroll;

View File

@ -4,7 +4,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Game.Overlays;
using osu.Game.Overlays.Comments; using osu.Game.Overlays.Comments;
namespace osu.Game.Tests.Visual.Online namespace osu.Game.Tests.Visual.Online
@ -19,6 +21,9 @@ namespace osu.Game.Tests.Visual.Online
typeof(SortTabControl), typeof(SortTabControl),
}; };
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
private readonly Bindable<CommentsSortCriteria> sort = new Bindable<CommentsSortCriteria>(); private readonly Bindable<CommentsSortCriteria> sort = new Bindable<CommentsSortCriteria>();
private readonly BindableBool showDeleted = new BindableBool(); private readonly BindableBool showDeleted = new BindableBool();

View File

@ -4,10 +4,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Overlays;
using osu.Game.Overlays.Profile.Sections; using osu.Game.Overlays.Profile.Sections;
using osu.Game.Overlays.Profile.Sections.Historical; using osu.Game.Overlays.Profile.Sections.Historical;
using osu.Game.Users; using osu.Game.Users;
@ -27,6 +29,9 @@ namespace osu.Game.Tests.Visual.Online
typeof(DrawableProfileRow) typeof(DrawableProfileRow)
}; };
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink);
public TestSceneHistoricalSection() public TestSceneHistoricalSection()
{ {
HistoricalSection section; HistoricalSection section;

View File

@ -7,6 +7,8 @@ using osu.Framework.Graphics;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Game.Overlays.Comments; using osu.Game.Overlays.Comments;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Framework.Allocation;
using osu.Game.Overlays;
namespace osu.Game.Tests.Visual.Online namespace osu.Game.Tests.Visual.Online
{ {
@ -17,6 +19,9 @@ namespace osu.Game.Tests.Visual.Online
typeof(TotalCommentsCounter), typeof(TotalCommentsCounter),
}; };
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
public TestSceneTotalCommentsCounter() public TestSceneTotalCommentsCounter()
{ {
var count = new BindableInt(); var count = new BindableInt();

View File

@ -24,7 +24,7 @@ namespace osu.Game.Tests.Visual.Online
typeof(ProfileHeader), typeof(ProfileHeader),
typeof(RankGraph), typeof(RankGraph),
typeof(LineGraph), typeof(LineGraph),
typeof(TabControlOverlayHeader.OverlayHeaderTabControl), typeof(TabControlOverlayHeader<>.OverlayHeaderTabControl),
typeof(CentreHeaderContainer), typeof(CentreHeaderContainer),
typeof(BottomHeaderContainer), typeof(BottomHeaderContainer),
typeof(DetailHeaderContainer), typeof(DetailHeaderContainer),

View File

@ -0,0 +1,101 @@
// 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 System;
using System.Collections.Generic;
using osu.Game.Overlays.Profile.Sections;
using osu.Game.Overlays.Profile.Sections.Ranks;
using osu.Framework.Graphics;
using osu.Game.Scoring;
using osu.Framework.Graphics.Containers;
using osuTK;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Overlays;
using osu.Framework.Allocation;
namespace osu.Game.Tests.Visual.Online
{
public class TestSceneUserProfileScores : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(DrawableProfileScore),
typeof(DrawableProfileWeightedScore),
typeof(ProfileItemContainer),
};
public TestSceneUserProfileScores()
{
var score = new ScoreInfo
{
PP = 134.32,
Rank = ScoreRank.A,
Beatmap = new BeatmapInfo
{
Metadata = new BeatmapMetadata
{
Title = "Triumph & Regret",
Artist = "typeMARS"
},
Version = "[4K] Regret"
},
Date = DateTimeOffset.Now,
Mods = new Mod[]
{
new OsuModHardRock(),
new OsuModDoubleTime(),
},
Accuracy = 0.998546
};
var noPPScore = new ScoreInfo
{
Rank = ScoreRank.B,
Beatmap = new BeatmapInfo
{
Metadata = new BeatmapMetadata
{
Title = "C18H27NO3(extend)",
Artist = "Team Grimoire"
},
Version = "[4K] Cataclysmic Hypernova"
},
Date = DateTimeOffset.Now,
Accuracy = 0.55879
};
Add(new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 10),
Children = new[]
{
new ColourProvidedContainer(OverlayColourScheme.Green, new DrawableProfileScore(score)),
new ColourProvidedContainer(OverlayColourScheme.Pink, new DrawableProfileScore(noPPScore)),
new ColourProvidedContainer(OverlayColourScheme.Pink, new DrawableProfileWeightedScore(score, 0.85))
}
});
}
private class ColourProvidedContainer : Container
{
[Cached]
private readonly OverlayColourProvider colourProvider;
public ColourProvidedContainer(OverlayColourScheme colourScheme, DrawableProfileScore score)
{
colourProvider = new OverlayColourProvider(colourScheme);
AutoSizeAxes = Axes.Y;
RelativeSizeAxes = Axes.X;
Add(score);
}
}
}
}

View File

@ -4,11 +4,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Overlays;
using osu.Game.Overlays.Profile.Sections; using osu.Game.Overlays.Profile.Sections;
using osu.Game.Overlays.Profile.Sections.Ranks; using osu.Game.Overlays.Profile.Sections.Ranks;
using osu.Game.Users; using osu.Game.Users;
@ -20,7 +22,15 @@ namespace osu.Game.Tests.Visual.Online
{ {
protected override bool UseOnlineAPI => true; protected override bool UseOnlineAPI => true;
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(DrawableProfileScore), typeof(RanksSection) }; public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(DrawableProfileScore),
typeof(DrawableProfileWeightedScore),
typeof(RanksSection)
};
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green);
public TestSceneUserRanks() public TestSceneUserRanks()
{ {

View File

@ -14,6 +14,7 @@ using osu.Framework.Extensions;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Overlays; using osu.Game.Overlays;
@ -25,6 +26,7 @@ using osu.Game.Rulesets.Taiko;
using osu.Game.Screens.Select; using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Carousel; using osu.Game.Screens.Select.Carousel;
using osu.Game.Screens.Select.Filter; using osu.Game.Screens.Select.Filter;
using osuTK.Input;
namespace osu.Game.Tests.Visual.SongSelect namespace osu.Game.Tests.Visual.SongSelect
{ {
@ -95,6 +97,127 @@ namespace osu.Game.Tests.Visual.SongSelect
AddAssert("filter count is 1", () => songSelect.FilterCount == 1); AddAssert("filter count is 1", () => songSelect.FilterCount == 1);
} }
[Test]
public void TestChangeBeatmapBeforeEnter()
{
addRulesetImportStep(0);
createSongSelect();
AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault);
WorkingBeatmap selected = null;
AddStep("store selected beatmap", () => selected = Beatmap.Value);
AddStep("select next and enter", () =>
{
InputManager.PressKey(Key.Down);
InputManager.ReleaseKey(Key.Down);
InputManager.PressKey(Key.Enter);
InputManager.ReleaseKey(Key.Enter);
});
AddUntilStep("wait for not current", () => !songSelect.IsCurrentScreen());
AddAssert("ensure selection changed", () => selected != Beatmap.Value);
AddUntilStep("wait for return to song select", () => songSelect.IsCurrentScreen());
AddUntilStep("bindable lease returned", () => !Beatmap.Disabled);
}
[Test]
public void TestChangeBeatmapAfterEnter()
{
addRulesetImportStep(0);
createSongSelect();
AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault);
WorkingBeatmap selected = null;
AddStep("store selected beatmap", () => selected = Beatmap.Value);
AddStep("select next and enter", () =>
{
InputManager.PressKey(Key.Enter);
InputManager.ReleaseKey(Key.Enter);
InputManager.PressKey(Key.Down);
InputManager.ReleaseKey(Key.Down);
});
AddUntilStep("wait for not current", () => !songSelect.IsCurrentScreen());
AddAssert("ensure selection didn't change", () => selected == Beatmap.Value);
AddUntilStep("wait for return to song select", () => songSelect.IsCurrentScreen());
AddUntilStep("bindable lease returned", () => !Beatmap.Disabled);
}
[Test]
public void TestChangeBeatmapViaMouseBeforeEnter()
{
addRulesetImportStep(0);
createSongSelect();
AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault);
WorkingBeatmap selected = null;
AddStep("store selected beatmap", () => selected = Beatmap.Value);
AddStep("select next and enter", () =>
{
InputManager.MoveMouseTo(songSelect.Carousel.ChildrenOfType<DrawableCarouselBeatmap>()
.First(b => ((CarouselBeatmap)b.Item).Beatmap != songSelect.Carousel.SelectedBeatmap));
InputManager.PressButton(MouseButton.Left);
InputManager.ReleaseButton(MouseButton.Left);
InputManager.PressKey(Key.Enter);
InputManager.ReleaseKey(Key.Enter);
});
AddUntilStep("wait for not current", () => !songSelect.IsCurrentScreen());
AddAssert("ensure selection changed", () => selected != Beatmap.Value);
AddUntilStep("wait for return to song select", () => songSelect.IsCurrentScreen());
AddUntilStep("bindable lease returned", () => !Beatmap.Disabled);
}
[Test]
public void TestChangeBeatmapViaMouseAfterEnter()
{
addRulesetImportStep(0);
createSongSelect();
AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault);
WorkingBeatmap selected = null;
AddStep("store selected beatmap", () => selected = Beatmap.Value);
AddStep("select next and enter", () =>
{
InputManager.MoveMouseTo(songSelect.Carousel.ChildrenOfType<DrawableCarouselBeatmap>()
.First(b => ((CarouselBeatmap)b.Item).Beatmap != songSelect.Carousel.SelectedBeatmap));
InputManager.PressButton(MouseButton.Left);
InputManager.PressKey(Key.Enter);
InputManager.ReleaseKey(Key.Enter);
InputManager.ReleaseButton(MouseButton.Left);
});
AddUntilStep("wait for not current", () => !songSelect.IsCurrentScreen());
AddAssert("ensure selection didn't change", () => selected == Beatmap.Value);
AddUntilStep("wait for return to song select", () => songSelect.IsCurrentScreen());
AddUntilStep("bindable lease returned", () => !Beatmap.Disabled);
}
[Test] [Test]
public void TestNoFilterOnSimpleResume() public void TestNoFilterOnSimpleResume()
{ {

View File

@ -1,9 +1,11 @@
// 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.Linq;
using NUnit.Framework; 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.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
@ -12,11 +14,11 @@ namespace osu.Game.Tests.Visual.UserInterface
[TestFixture] [TestFixture]
public class TestSceneBreadcrumbControl : OsuTestScene public class TestSceneBreadcrumbControl : OsuTestScene
{ {
private readonly BreadcrumbControl<BreadcrumbTab> breadcrumbs; private readonly TestBreadcrumbControl breadcrumbs;
public TestSceneBreadcrumbControl() public TestSceneBreadcrumbControl()
{ {
Add(breadcrumbs = new BreadcrumbControl<BreadcrumbTab> Add(breadcrumbs = new TestBreadcrumbControl
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
@ -25,8 +27,13 @@ namespace osu.Game.Tests.Visual.UserInterface
}); });
AddStep(@"first", () => breadcrumbs.Current.Value = BreadcrumbTab.Click); AddStep(@"first", () => breadcrumbs.Current.Value = BreadcrumbTab.Click);
assertVisible(1);
AddStep(@"second", () => breadcrumbs.Current.Value = BreadcrumbTab.The); AddStep(@"second", () => breadcrumbs.Current.Value = BreadcrumbTab.The);
assertVisible(2);
AddStep(@"third", () => breadcrumbs.Current.Value = BreadcrumbTab.Circles); AddStep(@"third", () => breadcrumbs.Current.Value = BreadcrumbTab.Circles);
assertVisible(3);
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -35,11 +42,27 @@ namespace osu.Game.Tests.Visual.UserInterface
breadcrumbs.StripColour = colours.Blue; breadcrumbs.StripColour = colours.Blue;
} }
private void assertVisible(int count) => AddAssert($"first {count} item(s) visible", () =>
{
for (int i = 0; i < count; i++)
{
if (breadcrumbs.GetDrawable((BreadcrumbTab)i).State != Visibility.Visible)
return false;
}
return true;
});
private enum BreadcrumbTab private enum BreadcrumbTab
{ {
Click, Click,
The, The,
Circles, Circles,
} }
private class TestBreadcrumbControl : BreadcrumbControl<BreadcrumbTab>
{
public BreadcrumbTabItem GetDrawable(BreadcrumbTab tab) => (BreadcrumbTabItem)TabContainer.First(t => t.Value == tab);
}
} }
} }

View File

@ -16,7 +16,8 @@ namespace osu.Game.Tests.Visual.UserInterface
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {
typeof(FooterButtonMods) typeof(FooterButtonMods),
typeof(FooterButton)
}; };
private readonly TestFooterButtonMods footerButtonMods; private readonly TestFooterButtonMods footerButtonMods;

View File

@ -0,0 +1,163 @@
// 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.Graphics.Containers;
using osu.Game.Overlays;
using System;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Framework.Allocation;
using osu.Game.Graphics.UserInterface;
using osu.Framework.Graphics.Shapes;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneOverlayHeader : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(OverlayHeader),
typeof(TabControlOverlayHeader<>),
typeof(BreadcrumbControlOverlayHeader),
typeof(TestNoControlHeader),
typeof(TestStringTabControlHeader),
typeof(TestEnumTabControlHeader),
typeof(TestBreadcrumbControlHeader),
typeof(OverlayHeaderBackground)
};
private readonly FillFlowContainer flow;
public TestSceneOverlayHeader()
{
AddRange(new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
new BasicScrollContainer
{
RelativeSizeAxes = Axes.Both,
Child = flow = new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical
}
}
});
addHeader("Orange OverlayHeader (no background)", new TestNoBackgroundHeader(), OverlayColourScheme.Orange);
addHeader("Blue OverlayHeader", new TestNoControlHeader(), OverlayColourScheme.Blue);
addHeader("Green TabControlOverlayHeader (string)", new TestStringTabControlHeader(), OverlayColourScheme.Green);
addHeader("Pink TabControlOverlayHeader (enum)", new TestEnumTabControlHeader(), OverlayColourScheme.Pink);
addHeader("Red BreadcrumbControlOverlayHeader (no background)", new TestBreadcrumbControlHeader(), OverlayColourScheme.Red);
}
private void addHeader(string name, OverlayHeader header, OverlayColourScheme colourScheme)
{
flow.Add(new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Margin = new MarginPadding(20),
Text = name,
},
new ColourProvidedContainer(colourScheme, header)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
}
}
});
}
private class ColourProvidedContainer : Container
{
[Cached]
private readonly OverlayColourProvider colourProvider;
public ColourProvidedContainer(OverlayColourScheme colourScheme, OverlayHeader header)
{
colourProvider = new OverlayColourProvider(colourScheme);
AutoSizeAxes = Axes.Y;
RelativeSizeAxes = Axes.X;
Add(header);
}
}
private class TestNoBackgroundHeader : OverlayHeader
{
protected override ScreenTitle CreateTitle() => new TestTitle();
}
private class TestNoControlHeader : OverlayHeader
{
protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/changelog");
protected override ScreenTitle CreateTitle() => new TestTitle();
}
private class TestStringTabControlHeader : TabControlOverlayHeader<string>
{
protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/news");
protected override ScreenTitle CreateTitle() => new TestTitle();
public TestStringTabControlHeader()
{
TabControl.AddItem("tab1");
TabControl.AddItem("tab2");
}
}
private class TestEnumTabControlHeader : TabControlOverlayHeader<TestEnum>
{
protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/rankings");
protected override ScreenTitle CreateTitle() => new TestTitle();
}
private enum TestEnum
{
Some,
Cool,
Tabs
}
private class TestBreadcrumbControlHeader : BreadcrumbControlOverlayHeader
{
protected override ScreenTitle CreateTitle() => new TestTitle();
public TestBreadcrumbControlHeader()
{
TabControl.AddItem("tab1");
TabControl.AddItem("tab2");
TabControl.Current.Value = "tab2";
}
}
private class TestTitle : ScreenTitle
{
public TestTitle()
{
Title = "title";
Section = "section";
}
protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/changelog");
}
}
}

View File

@ -0,0 +1,42 @@
// 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.Graphics.Containers;
using osu.Game.Overlays;
using System;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osuTK;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneOverlayHeaderBackground : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(OverlayHeaderBackground)
};
public TestSceneOverlayHeaderBackground()
{
Add(new BasicScrollContainer
{
RelativeSizeAxes = Axes.Both,
Child = new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 20),
Children = new[]
{
new OverlayHeaderBackground(@"Headers/changelog"),
new OverlayHeaderBackground(@"Headers/news"),
new OverlayHeaderBackground(@"Headers/rankings"),
new OverlayHeaderBackground(@"Headers/search"),
}
}
});
}
}
}

View File

@ -29,9 +29,9 @@ namespace osu.Game.Graphics
} }
} }
public DrawableDate(DateTimeOffset date) public DrawableDate(DateTimeOffset date, float textSize = OsuFont.DEFAULT_FONT_SIZE)
{ {
Font = OsuFont.GetFont(weight: FontWeight.Regular, italics: true); Font = OsuFont.GetFont(weight: FontWeight.Regular, size: textSize, italics: true);
Date = date; Date = date;
} }

View File

@ -90,7 +90,7 @@ namespace osu.Game.Graphics
{ {
ScheduledDelegate waitDelegate = host.DrawThread.Scheduler.AddDelayed(() => ScheduledDelegate waitDelegate = host.DrawThread.Scheduler.AddDelayed(() =>
{ {
if (framesWaited++ < frames_to_wait) if (framesWaited++ >= frames_to_wait)
// ReSharper disable once AccessToDisposedClosure // ReSharper disable once AccessToDisposedClosure
framesWaitedEvent.Set(); framesWaitedEvent.Set();
}, 10, true); }, 10, true);

View File

@ -34,13 +34,13 @@ namespace osu.Game.Graphics.UserInterface
var tIndex = TabContainer.IndexOf(t); var tIndex = TabContainer.IndexOf(t);
var tabIndex = TabContainer.IndexOf(TabMap[index.NewValue]); var tabIndex = TabContainer.IndexOf(TabMap[index.NewValue]);
t.State = tIndex < tabIndex ? Visibility.Hidden : Visibility.Visible; t.State = tIndex > tabIndex ? Visibility.Hidden : Visibility.Visible;
t.Chevron.FadeTo(tIndex <= tabIndex ? 0f : 1f, 500, Easing.OutQuint); t.Chevron.FadeTo(tIndex >= tabIndex ? 0f : 1f, 500, Easing.OutQuint);
} }
}; };
} }
protected class BreadcrumbTabItem : OsuTabItem, IStateful<Visibility> public class BreadcrumbTabItem : OsuTabItem, IStateful<Visibility>
{ {
protected virtual float ChevronSize => 10; protected virtual float ChevronSize => 10;

View File

@ -36,6 +36,11 @@ namespace osu.Game.Graphics.UserInterface
public virtual string TooltipText { get; private set; } public virtual string TooltipText { get; private set; }
/// <summary>
/// Whether to format the tooltip as a percentage or the actual value.
/// </summary>
public bool DisplayAsPercentage { get; set; }
private Color4 accentColour; private Color4 accentColour;
public Color4 AccentColour public Color4 AccentColour
@ -169,11 +174,11 @@ namespace osu.Game.Graphics.UserInterface
else else
{ {
double floatValue = value.ToDouble(NumberFormatInfo.InvariantInfo); double floatValue = value.ToDouble(NumberFormatInfo.InvariantInfo);
double floatMinValue = CurrentNumber.MinValue.ToDouble(NumberFormatInfo.InvariantInfo);
double floatMaxValue = CurrentNumber.MaxValue.ToDouble(NumberFormatInfo.InvariantInfo);
if (floatMaxValue == 1 && floatMinValue >= -1) if (DisplayAsPercentage)
TooltipText = floatValue.ToString("P0"); {
TooltipText = floatValue.ToString("0%");
}
else else
{ {
var decimalPrecision = normalise(CurrentNumber.Precision.ToDecimal(NumberFormatInfo.InvariantInfo), max_decimal_digits); var decimalPrecision = normalise(CurrentNumber.Precision.ToDecimal(NumberFormatInfo.InvariantInfo), max_decimal_digits);

View File

@ -21,6 +21,22 @@ namespace osu.Game.Graphics.UserInterface
{ {
public class OsuTabControl<T> : TabControl<T> public class OsuTabControl<T> : TabControl<T>
{ {
private Color4 accentColour;
public virtual Color4 AccentColour
{
get => accentColour;
set
{
accentColour = value;
if (Dropdown is IHasAccentColour dropdown)
dropdown.AccentColour = value;
foreach (var i in TabContainer.Children.OfType<IHasAccentColour>())
i.AccentColour = value;
}
}
private readonly Box strip; private readonly Box strip;
protected override Dropdown<T> CreateDropdown() => new OsuTabDropdown(); protected override Dropdown<T> CreateDropdown() => new OsuTabDropdown();
@ -62,21 +78,6 @@ namespace osu.Game.Graphics.UserInterface
AccentColour = colours.Blue; AccentColour = colours.Blue;
} }
private Color4 accentColour;
public Color4 AccentColour
{
get => accentColour;
set
{
accentColour = value;
if (Dropdown is IHasAccentColour dropdown)
dropdown.AccentColour = value;
foreach (var i in TabContainer.Children.OfType<IHasAccentColour>())
i.AccentColour = value;
}
}
public Color4 StripColour public Color4 StripColour
{ {
get => strip.Colour; get => strip.Colour;

View File

@ -130,91 +130,91 @@ namespace osu.Game.IO.Legacy
} }
else else
{ {
switch (obj.GetType().Name) switch (obj)
{ {
case "Boolean": case bool boolObj:
Write((byte)ObjType.boolType); Write((byte)ObjType.boolType);
Write((bool)obj); Write(boolObj);
break; break;
case "Byte": case byte byteObj:
Write((byte)ObjType.byteType); Write((byte)ObjType.byteType);
Write((byte)obj); Write(byteObj);
break; break;
case "UInt16": case ushort ushortObj:
Write((byte)ObjType.uint16Type); Write((byte)ObjType.uint16Type);
Write((ushort)obj); Write(ushortObj);
break; break;
case "UInt32": case uint uintObj:
Write((byte)ObjType.uint32Type); Write((byte)ObjType.uint32Type);
Write((uint)obj); Write(uintObj);
break; break;
case "UInt64": case ulong ulongObj:
Write((byte)ObjType.uint64Type); Write((byte)ObjType.uint64Type);
Write((ulong)obj); Write(ulongObj);
break; break;
case "SByte": case sbyte sbyteObj:
Write((byte)ObjType.sbyteType); Write((byte)ObjType.sbyteType);
Write((sbyte)obj); Write(sbyteObj);
break; break;
case "Int16": case short shortObj:
Write((byte)ObjType.int16Type); Write((byte)ObjType.int16Type);
Write((short)obj); Write(shortObj);
break; break;
case "Int32": case int intObj:
Write((byte)ObjType.int32Type); Write((byte)ObjType.int32Type);
Write((int)obj); Write(intObj);
break; break;
case "Int64": case long longObj:
Write((byte)ObjType.int64Type); Write((byte)ObjType.int64Type);
Write((long)obj); Write(longObj);
break; break;
case "Char": case char charObj:
Write((byte)ObjType.charType); Write((byte)ObjType.charType);
base.Write((char)obj); base.Write(charObj);
break; break;
case "String": case string stringObj:
Write((byte)ObjType.stringType); Write((byte)ObjType.stringType);
base.Write((string)obj); base.Write(stringObj);
break; break;
case "Single": case float floatObj:
Write((byte)ObjType.singleType); Write((byte)ObjType.singleType);
Write((float)obj); Write(floatObj);
break; break;
case "Double": case double doubleObj:
Write((byte)ObjType.doubleType); Write((byte)ObjType.doubleType);
Write((double)obj); Write(doubleObj);
break; break;
case "Decimal": case decimal decimalObj:
Write((byte)ObjType.decimalType); Write((byte)ObjType.decimalType);
Write((decimal)obj); Write(decimalObj);
break; break;
case "DateTime": case DateTime dateTimeObj:
Write((byte)ObjType.dateTimeType); Write((byte)ObjType.dateTimeType);
Write((DateTime)obj); Write(dateTimeObj);
break; break;
case "Byte[]": case byte[] byteArray:
Write((byte)ObjType.byteArrayType); Write((byte)ObjType.byteArrayType);
base.Write((byte[])obj); base.Write(byteArray);
break; break;
case "Char[]": case char[] charArray:
Write((byte)ObjType.charArrayType); Write((byte)ObjType.charArrayType);
base.Write((char[])obj); base.Write(charArray);
break; break;
default: default:

View File

@ -277,7 +277,7 @@ namespace osu.Game.Online.Leaderboards
protected virtual IEnumerable<LeaderboardScoreStatistic> GetStatistics(ScoreInfo model) => new[] protected virtual IEnumerable<LeaderboardScoreStatistic> GetStatistics(ScoreInfo model) => new[]
{ {
new LeaderboardScoreStatistic(FontAwesome.Solid.Link, "Max Combo", model.MaxCombo.ToString()), new LeaderboardScoreStatistic(FontAwesome.Solid.Link, "Max Combo", model.MaxCombo.ToString()),
new LeaderboardScoreStatistic(FontAwesome.Solid.Crosshairs, "Accuracy", string.Format(model.Accuracy % 1 == 0 ? @"{0:P0}" : @"{0:P2}", model.Accuracy)) new LeaderboardScoreStatistic(FontAwesome.Solid.Crosshairs, "Accuracy", string.Format(model.Accuracy % 1 == 0 ? @"{0:0%}" : @"{0:0.00%}", model.Accuracy))
}; };
protected override bool OnHover(HoverEvent e) protected override bool OnHover(HoverEvent e)

View File

@ -42,7 +42,7 @@ namespace osu.Game.Overlays.BeatmapSet
int playCount = beatmap?.OnlineInfo?.PlayCount ?? 0; int playCount = beatmap?.OnlineInfo?.PlayCount ?? 0;
var rate = playCount != 0 ? (float)passCount / playCount : 0; var rate = playCount != 0 ? (float)passCount / playCount : 0;
successPercent.Text = rate.ToString("P0"); successPercent.Text = rate.ToString("0%");
successRate.Length = rate; successRate.Length = rate;
percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic); percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic);

View File

@ -1,24 +1,15 @@
// 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.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays namespace osu.Game.Overlays
{ {
public abstract class BreadcrumbControlOverlayHeader : OverlayHeader public abstract class BreadcrumbControlOverlayHeader : TabControlOverlayHeader<string>
{ {
protected OverlayHeaderBreadcrumbControl BreadcrumbControl; protected override OsuTabControl<string> CreateTabControl() => new OverlayHeaderBreadcrumbControl();
protected override TabControl<string> CreateTabControl() => BreadcrumbControl = new OverlayHeaderBreadcrumbControl();
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
BreadcrumbControl.AccentColour = colourProvider.Highlight1;
}
public class OverlayHeaderBreadcrumbControl : BreadcrumbControl<string> public class OverlayHeaderBreadcrumbControl : BreadcrumbControl<string>
{ {

View File

@ -2,13 +2,11 @@
// 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;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
@ -26,8 +24,8 @@ namespace osu.Game.Overlays.Changelog
public ChangelogHeader() public ChangelogHeader()
{ {
BreadcrumbControl.AddItem(listing_string); TabControl.AddItem(listing_string);
BreadcrumbControl.Current.ValueChanged += e => TabControl.Current.ValueChanged += e =>
{ {
if (e.NewValue == listing_string) if (e.NewValue == listing_string)
ListingSelected?.Invoke(); ListingSelected?.Invoke();
@ -37,7 +35,7 @@ namespace osu.Game.Overlays.Changelog
Streams.Current.ValueChanged += e => Streams.Current.ValueChanged += e =>
{ {
if (e.NewValue?.LatestBuild != null && e.NewValue != Current.Value?.UpdateStream) if (e.NewValue?.LatestBuild != null && !e.NewValue.Equals(Current.Value?.UpdateStream))
Current.Value = e.NewValue.LatestBuild; Current.Value = e.NewValue.LatestBuild;
}; };
} }
@ -47,26 +45,26 @@ namespace osu.Game.Overlays.Changelog
private void showBuild(ValueChangedEvent<APIChangelogBuild> e) private void showBuild(ValueChangedEvent<APIChangelogBuild> e)
{ {
if (e.OldValue != null) if (e.OldValue != null)
BreadcrumbControl.RemoveItem(e.OldValue.ToString()); TabControl.RemoveItem(e.OldValue.ToString());
if (e.NewValue != null) if (e.NewValue != null)
{ {
BreadcrumbControl.AddItem(e.NewValue.ToString()); TabControl.AddItem(e.NewValue.ToString());
BreadcrumbControl.Current.Value = e.NewValue.ToString(); TabControl.Current.Value = e.NewValue.ToString();
Streams.Current.Value = Streams.Items.FirstOrDefault(s => s.Name == e.NewValue.UpdateStream.Name); updateCurrentStream();
title.Version = e.NewValue.UpdateStream.DisplayName; title.Version = e.NewValue.UpdateStream.DisplayName;
} }
else else
{ {
BreadcrumbControl.Current.Value = listing_string; TabControl.Current.Value = listing_string;
Streams.Current.Value = null; Streams.Current.Value = null;
title.Version = null; title.Version = null;
} }
} }
protected override Drawable CreateBackground() => new HeaderBackground(); protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/changelog");
protected override Drawable CreateContent() => new Container protected override Drawable CreateContent() => new Container
{ {
@ -80,19 +78,18 @@ namespace osu.Game.Overlays.Changelog
protected override ScreenTitle CreateTitle() => title = new ChangelogHeaderTitle(); protected override ScreenTitle CreateTitle() => title = new ChangelogHeaderTitle();
public class HeaderBackground : Sprite public void Populate(List<APIUpdateStream> streams)
{ {
public HeaderBackground() Streams.Populate(streams);
{ updateCurrentStream();
RelativeSizeAxes = Axes.Both; }
FillMode = FillMode.Fill;
}
[BackgroundDependencyLoader] private void updateCurrentStream()
private void load(TextureStore textures) {
{ if (Current.Value == null)
Texture = textures.Get(@"Headers/changelog"); return;
}
Streams.Current.Value = Streams.Items.FirstOrDefault(s => s.Name == Current.Value.UpdateStream.Name);
} }
private class ChangelogHeaderTitle : ScreenTitle private class ChangelogHeaderTitle : ScreenTitle

View File

@ -29,8 +29,6 @@ namespace osu.Game.Overlays.Changelog
public void Populate(List<APIUpdateStream> streams) public void Populate(List<APIUpdateStream> streams)
{ {
Current.Value = null;
foreach (APIUpdateStream updateStream in streams) foreach (APIUpdateStream updateStream in streams)
AddItem(updateStream); AddItem(updateStream);
} }

View File

@ -26,7 +26,7 @@ namespace osu.Game.Overlays
{ {
public readonly Bindable<APIChangelogBuild> Current = new Bindable<APIChangelogBuild>(); public readonly Bindable<APIChangelogBuild> Current = new Bindable<APIChangelogBuild>();
private ChangelogHeader header; protected ChangelogHeader Header;
private Container<ChangelogContent> content; private Container<ChangelogContent> content;
@ -34,7 +34,7 @@ namespace osu.Game.Overlays
private List<APIChangelogBuild> builds; private List<APIChangelogBuild> builds;
private List<APIUpdateStream> streams; protected List<APIUpdateStream> Streams;
public ChangelogOverlay() public ChangelogOverlay()
: base(OverlayColourScheme.Purple) : base(OverlayColourScheme.Purple)
@ -62,7 +62,7 @@ namespace osu.Game.Overlays
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
Children = new Drawable[] Children = new Drawable[]
{ {
header = new ChangelogHeader Header = new ChangelogHeader
{ {
ListingSelected = ShowListing, ListingSelected = ShowListing,
}, },
@ -78,7 +78,7 @@ namespace osu.Game.Overlays
sampleBack = audio.Samples.Get(@"UI/generic-select-soft"); sampleBack = audio.Samples.Get(@"UI/generic-select-soft");
header.Current.BindTo(Current); Header.Current.BindTo(Current);
Current.BindValueChanged(e => Current.BindValueChanged(e =>
{ {
@ -117,7 +117,7 @@ namespace osu.Game.Overlays
performAfterFetch(() => performAfterFetch(() =>
{ {
var build = builds.Find(b => b.Version == version && b.UpdateStream.Name == updateStream) var build = builds.Find(b => b.Version == version && b.UpdateStream.Name == updateStream)
?? streams.Find(s => s.Name == updateStream)?.LatestBuild; ?? Streams.Find(s => s.Name == updateStream)?.LatestBuild;
if (build != null) if (build != null)
ShowBuild(build); ShowBuild(build);
@ -179,9 +179,9 @@ namespace osu.Game.Overlays
res.Streams.ForEach(s => s.LatestBuild.UpdateStream = res.Streams.Find(s2 => s2.Id == s.LatestBuild.UpdateStream.Id)); res.Streams.ForEach(s => s.LatestBuild.UpdateStream = res.Streams.Find(s2 => s2.Id == s.LatestBuild.UpdateStream.Id));
builds = res.Builds; builds = res.Builds;
streams = res.Streams; Streams = res.Streams;
header.Streams.Populate(res.Streams); Header.Populate(res.Streams);
tcs.SetResult(true); tcs.SetResult(true);
}); });

View File

@ -8,7 +8,6 @@ using osu.Game.Online.API.Requests;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
using System.Threading; using System.Threading;
using System.Linq; using System.Linq;
@ -27,28 +26,26 @@ namespace osu.Game.Overlays.Comments
[Resolved] [Resolved]
private IAPIProvider api { get; set; } private IAPIProvider api { get; set; }
[Resolved]
private OsuColour colours { get; set; }
private GetCommentsRequest request; private GetCommentsRequest request;
private CancellationTokenSource loadCancellation; private CancellationTokenSource loadCancellation;
private int currentPage; private int currentPage;
private readonly Box background; private FillFlowContainer content;
private readonly FillFlowContainer content; private DeletedCommentsCounter deletedCommentsCounter;
private readonly DeletedChildrenPlaceholder deletedChildrenPlaceholder; private CommentsShowMoreButton moreButton;
private readonly CommentsShowMoreButton moreButton; private TotalCommentsCounter commentCounter;
private readonly TotalCommentsCounter commentCounter;
public CommentsContainer() [BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{ {
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y; AutoSizeAxes = Axes.Y;
AddRangeInternal(new Drawable[] AddRangeInternal(new Drawable[]
{ {
background = new Box new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = colourProvider.Background5
}, },
new FillFlowContainer new FillFlowContainer
{ {
@ -78,7 +75,7 @@ namespace osu.Game.Overlays.Comments
new Box new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.2f) Colour = colourProvider.Background4
}, },
new FillFlowContainer new FillFlowContainer
{ {
@ -87,7 +84,7 @@ namespace osu.Game.Overlays.Comments
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
Children = new Drawable[] Children = new Drawable[]
{ {
deletedChildrenPlaceholder = new DeletedChildrenPlaceholder deletedCommentsCounter = new DeletedCommentsCounter
{ {
ShowDeleted = { BindTarget = ShowDeleted } ShowDeleted = { BindTarget = ShowDeleted }
}, },
@ -113,12 +110,6 @@ namespace osu.Game.Overlays.Comments
}); });
} }
[BackgroundDependencyLoader]
private void load()
{
background.Colour = colours.Gray2;
}
protected override void LoadComplete() protected override void LoadComplete()
{ {
Sort.BindValueChanged(_ => refetchComments(), true); Sort.BindValueChanged(_ => refetchComments(), true);
@ -162,7 +153,7 @@ namespace osu.Game.Overlays.Comments
private void clearComments() private void clearComments()
{ {
currentPage = 1; currentPage = 1;
deletedChildrenPlaceholder.DeletedCount.Value = 0; deletedCommentsCounter.Count.Value = 0;
moreButton.IsLoading = true; moreButton.IsLoading = true;
content.Clear(); content.Clear();
} }
@ -193,7 +184,7 @@ namespace osu.Game.Overlays.Comments
{ {
content.Add(loaded); content.Add(loaded);
deletedChildrenPlaceholder.DeletedCount.Value += response.Comments.Count(c => c.IsDeleted && c.IsTopLevel); deletedCommentsCounter.Count.Value += response.Comments.Count(c => c.IsDeleted && c.IsTopLevel);
if (response.HasMore) if (response.HasMore)
{ {

View File

@ -76,9 +76,9 @@ namespace osu.Game.Overlays.Comments
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OverlayColourProvider colourProvider)
{ {
background.Colour = colours.Gray3; background.Colour = colourProvider.Background4;
} }
private class ShowDeletedButton : HeaderButton private class ShowDeletedButton : HeaderButton

View File

@ -1,8 +1,8 @@
// 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.Bindables;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Comments namespace osu.Game.Overlays.Comments
@ -11,11 +11,12 @@ namespace osu.Game.Overlays.Comments
{ {
public readonly BindableInt Current = new BindableInt(); public readonly BindableInt Current = new BindableInt();
public CommentsShowMoreButton() [BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{ {
IdleColour = OsuColour.Gray(0.3f); IdleColour = colourProvider.Background2;
HoverColour = OsuColour.Gray(0.4f); HoverColour = colourProvider.Background1;
ChevronIconColour = OsuColour.Gray(0.5f); ChevronIconColour = colourProvider.Foreground1;
} }
protected override void LoadComplete() protected override void LoadComplete()

View File

@ -12,51 +12,56 @@ using osu.Game.Graphics.Sprites;
namespace osu.Game.Overlays.Comments namespace osu.Game.Overlays.Comments
{ {
public class DeletedChildrenPlaceholder : FillFlowContainer public class DeletedCommentsCounter : CompositeDrawable
{ {
public readonly BindableBool ShowDeleted = new BindableBool(); public readonly BindableBool ShowDeleted = new BindableBool();
public readonly BindableInt DeletedCount = new BindableInt();
public readonly BindableInt Count = new BindableInt();
private readonly SpriteText countText; private readonly SpriteText countText;
public DeletedChildrenPlaceholder() public DeletedCommentsCounter()
{ {
AutoSizeAxes = Axes.Both; AutoSizeAxes = Axes.Both;
Direction = FillDirection.Horizontal;
Spacing = new Vector2(3, 0);
Margin = new MarginPadding { Vertical = 10, Left = 80 }; Margin = new MarginPadding { Vertical = 10, Left = 80 };
Children = new Drawable[]
InternalChild = new FillFlowContainer
{ {
new SpriteIcon AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(3, 0),
Children = new Drawable[]
{ {
Icon = FontAwesome.Solid.Trash, new SpriteIcon
Size = new Vector2(14), {
}, Icon = FontAwesome.Solid.Trash,
countText = new OsuSpriteText Size = new Vector2(14),
{ },
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold, italics: true), countText = new OsuSpriteText
{
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold, italics: true),
}
} }
}; };
} }
protected override void LoadComplete() protected override void LoadComplete()
{ {
DeletedCount.BindValueChanged(_ => updateDisplay(), true);
ShowDeleted.BindValueChanged(_ => updateDisplay(), true);
base.LoadComplete(); base.LoadComplete();
Count.BindValueChanged(_ => updateDisplay(), true);
ShowDeleted.BindValueChanged(_ => updateDisplay(), true);
} }
private void updateDisplay() private void updateDisplay()
{ {
if (DeletedCount.Value != 0) if (!ShowDeleted.Value && Count.Value != 0)
{ {
countText.Text = @"deleted comment".ToQuantity(DeletedCount.Value); countText.Text = @"deleted comment".ToQuantity(Count.Value);
this.FadeTo(ShowDeleted.Value ? 0 : 1); Show();
} }
else else
{
Hide(); Hide();
}
} }
} }
} }

View File

@ -42,7 +42,7 @@ namespace osu.Game.Overlays.Comments
{ {
LinkFlowContainer username; LinkFlowContainer username;
FillFlowContainer childCommentsContainer; FillFlowContainer childCommentsContainer;
DeletedChildrenPlaceholder deletedChildrenPlaceholder; DeletedCommentsCounter deletedCommentsCounter;
FillFlowContainer info; FillFlowContainer info;
LinkFlowContainer message; LinkFlowContainer message;
GridContainer content; GridContainer content;
@ -184,7 +184,7 @@ namespace osu.Game.Overlays.Comments
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical Direction = FillDirection.Vertical
}, },
deletedChildrenPlaceholder = new DeletedChildrenPlaceholder deletedCommentsCounter = new DeletedCommentsCounter
{ {
ShowDeleted = { BindTarget = ShowDeleted } ShowDeleted = { BindTarget = ShowDeleted }
} }
@ -193,7 +193,7 @@ namespace osu.Game.Overlays.Comments
} }
}; };
deletedChildrenPlaceholder.DeletedCount.Value = comment.DeletedChildrenCount; deletedCommentsCounter.Count.Value = comment.DeletedChildrenCount;
if (comment.UserId.HasValue) if (comment.UserId.HasValue)
username.AddUserLink(comment.User); username.AddUserLink(comment.User);

View File

@ -5,7 +5,6 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
@ -45,9 +44,9 @@ namespace osu.Game.Overlays.Comments
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OverlayColourProvider colourProvider)
{ {
background.Colour = colours.Gray4; background.Colour = colourProvider.Background3;
} }
protected override bool OnHover(HoverEvent e) protected override bool OnHover(HoverEvent e)

View File

@ -56,7 +56,7 @@ namespace osu.Game.Overlays.Comments
public readonly BindableBool Active = new BindableBool(); public readonly BindableBool Active = new BindableBool();
[Resolved] [Resolved]
private OsuColour colours { get; set; } private OverlayColourProvider colourProvider { get; set; }
private readonly SpriteText text; private readonly SpriteText text;
@ -78,7 +78,7 @@ namespace osu.Game.Overlays.Comments
updateBackgroundState(); updateBackgroundState();
text.Font = text.Font.With(weight: active.NewValue ? FontWeight.Bold : FontWeight.Medium); text.Font = text.Font.With(weight: active.NewValue ? FontWeight.Bold : FontWeight.Medium);
text.Colour = active.NewValue ? colours.BlueLighter : Color4.White; text.Colour = active.NewValue ? colourProvider.Light1 : Color4.White;
}, true); }, true);
} }

View File

@ -5,7 +5,6 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Framework.Graphics.Sprites;
using osuTK; using osuTK;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
@ -17,9 +16,10 @@ namespace osu.Game.Overlays.Comments
{ {
public readonly BindableInt Current = new BindableInt(); public readonly BindableInt Current = new BindableInt();
private readonly SpriteText counter; private OsuSpriteText counter;
public TotalCommentsCounter() [BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{ {
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
Height = 50; Height = 50;
@ -38,6 +38,7 @@ namespace osu.Game.Overlays.Comments
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,
Font = OsuFont.GetFont(size: 20, italics: true), Font = OsuFont.GetFont(size: 20, italics: true),
Colour = colourProvider.Light1,
Text = @"Comments" Text = @"Comments"
}, },
new CircularContainer new CircularContainer
@ -51,14 +52,15 @@ namespace osu.Game.Overlays.Comments
new Box new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.05f) Colour = colourProvider.Background6
}, },
counter = new OsuSpriteText counter = new OsuSpriteText
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Margin = new MarginPadding { Horizontal = 10, Vertical = 5 }, Margin = new MarginPadding { Horizontal = 10, Vertical = 5 },
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold) Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold),
Colour = colourProvider.Foreground1
} }
}, },
} }
@ -66,12 +68,6 @@ namespace osu.Game.Overlays.Comments
}); });
} }
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
counter.Colour = colours.BlueLighter;
}
protected override void LoadComplete() protected override void LoadComplete()
{ {
Current.BindValueChanged(value => counter.Text = value.NewValue.ToString("N0"), true); Current.BindValueChanged(value => counter.Text = value.NewValue.ToString("N0"), true);

View File

@ -18,11 +18,11 @@ namespace osu.Game.Overlays
protected IAPIProvider API { get; private set; } protected IAPIProvider API { get; private set; }
[Cached] [Cached]
private readonly OverlayColourProvider colourProvider; protected readonly OverlayColourProvider ColourProvider;
protected FullscreenOverlay(OverlayColourScheme colourScheme) protected FullscreenOverlay(OverlayColourScheme colourScheme)
{ {
colourProvider = new OverlayColourProvider(colourScheme); ColourProvider = new OverlayColourProvider(colourScheme);
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
RelativePositionAxes = Axes.Both; RelativePositionAxes = Axes.Both;
@ -43,10 +43,10 @@ namespace osu.Game.Overlays
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
Waves.FirstWaveColour = colourProvider.Light4; Waves.FirstWaveColour = ColourProvider.Light4;
Waves.SecondWaveColour = colourProvider.Light3; Waves.SecondWaveColour = ColourProvider.Light3;
Waves.ThirdWaveColour = colourProvider.Dark4; Waves.ThirdWaveColour = ColourProvider.Dark4;
Waves.FourthWaveColour = colourProvider.Dark3; Waves.FourthWaveColour = ColourProvider.Dark3;
} }
public override void Show() public override void Show()

View File

@ -1,11 +1,8 @@
// 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.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using System; using System;
@ -23,9 +20,9 @@ namespace osu.Game.Overlays.News
public NewsHeader() public NewsHeader()
{ {
BreadcrumbControl.AddItem(front_page_string); TabControl.AddItem(front_page_string);
BreadcrumbControl.Current.ValueChanged += e => TabControl.Current.ValueChanged += e =>
{ {
if (e.NewValue == front_page_string) if (e.NewValue == front_page_string)
ShowFrontPage?.Invoke(); ShowFrontPage?.Invoke();
@ -37,41 +34,26 @@ namespace osu.Game.Overlays.News
private void showPost(ValueChangedEvent<string> e) private void showPost(ValueChangedEvent<string> e)
{ {
if (e.OldValue != null) if (e.OldValue != null)
BreadcrumbControl.RemoveItem(e.OldValue); TabControl.RemoveItem(e.OldValue);
if (e.NewValue != null) if (e.NewValue != null)
{ {
BreadcrumbControl.AddItem(e.NewValue); TabControl.AddItem(e.NewValue);
BreadcrumbControl.Current.Value = e.NewValue; TabControl.Current.Value = e.NewValue;
title.IsReadingPost = true; title.IsReadingPost = true;
} }
else else
{ {
BreadcrumbControl.Current.Value = front_page_string; TabControl.Current.Value = front_page_string;
title.IsReadingPost = false; title.IsReadingPost = false;
} }
} }
protected override Drawable CreateBackground() => new NewsHeaderBackground(); protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/news");
protected override ScreenTitle CreateTitle() => title = new NewsHeaderTitle(); protected override ScreenTitle CreateTitle() => title = new NewsHeaderTitle();
private class NewsHeaderBackground : Sprite
{
public NewsHeaderBackground()
{
RelativeSizeAxes = Axes.Both;
FillMode = FillMode.Fill;
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
Texture = textures.Get(@"Headers/news");
}
}
private class NewsHeaderTitle : ScreenTitle private class NewsHeaderTitle : ScreenTitle
{ {
private const string post_string = "post"; private const string post_string = "post";

View File

@ -6,7 +6,6 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osuTK.Graphics; using osuTK.Graphics;
@ -15,14 +14,9 @@ namespace osu.Game.Overlays
public abstract class OverlayHeader : Container public abstract class OverlayHeader : Container
{ {
private readonly Box titleBackground; private readonly Box titleBackground;
private readonly Box controlBackground;
private readonly Container background;
private readonly ScreenTitle title; private readonly ScreenTitle title;
protected float BackgroundHeight protected readonly FillFlowContainer HeaderInfo;
{
set => background.Height = value;
}
protected OverlayHeader() protected OverlayHeader()
{ {
@ -36,46 +30,36 @@ namespace osu.Game.Overlays
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
Children = new[] Children = new[]
{ {
background = new Container HeaderInfo = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
Height = 80,
Masking = true,
Child = CreateBackground()
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
titleBackground = new Box
{
RelativeSizeAxes = Axes.Both,
},
title = CreateTitle().With(title =>
{
title.Margin = new MarginPadding
{
Vertical = 10,
Left = UserProfileOverlay.CONTENT_X_MARGIN
};
})
}
},
new Container
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Depth = -float.MaxValue, Depth = -float.MaxValue,
Children = new Drawable[] Children = new[]
{ {
controlBackground = new Box CreateBackground(),
new Container
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.X,
Colour = Color4.Gray, AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
titleBackground = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Gray,
},
title = CreateTitle().With(title =>
{
title.Margin = new MarginPadding
{
Vertical = 10,
Left = UserProfileOverlay.CONTENT_X_MARGIN
};
})
}
}, },
CreateTabControl().With(control => control.Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN })
} }
}, },
CreateContent() CreateContent()
@ -88,16 +72,14 @@ namespace osu.Game.Overlays
{ {
titleBackground.Colour = colourProvider.Dark5; titleBackground.Colour = colourProvider.Dark5;
title.AccentColour = colourProvider.Highlight1; title.AccentColour = colourProvider.Highlight1;
controlBackground.Colour = colourProvider.Dark4;
} }
protected abstract Drawable CreateBackground(); [NotNull]
protected virtual Drawable CreateContent() => Drawable.Empty();
[NotNull] [NotNull]
protected virtual Drawable CreateContent() => new Container(); protected virtual Drawable CreateBackground() => Drawable.Empty();
protected abstract ScreenTitle CreateTitle(); protected abstract ScreenTitle CreateTitle();
protected abstract TabControl<string> CreateTabControl();
} }
} }

View File

@ -0,0 +1,43 @@
// 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.Sprites;
using osu.Framework.Graphics.Textures;
namespace osu.Game.Overlays
{
public class OverlayHeaderBackground : CompositeDrawable
{
public OverlayHeaderBackground(string textureName)
{
Height = 80;
RelativeSizeAxes = Axes.X;
Masking = true;
InternalChild = new Background(textureName);
}
private class Background : Sprite
{
private readonly string textureName;
public Background(string textureName)
{
this.textureName = textureName;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
RelativeSizeAxes = Axes.Both;
FillMode = FillMode.Fill;
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
Texture = textures.Get(textureName);
}
}
}
}

View File

@ -13,41 +13,25 @@ using osuTK.Graphics;
namespace osu.Game.Overlays namespace osu.Game.Overlays
{ {
public abstract class OverlayTabControl<T> : TabControl<T> public abstract class OverlayTabControl<T> : OsuTabControl<T>
{ {
private readonly Box bar; private readonly Box bar;
private Color4 accentColour = Color4.White;
public Color4 AccentColour
{
get => accentColour;
set
{
if (accentColour == value)
return;
accentColour = value;
bar.Colour = value;
foreach (TabItem<T> tabItem in TabContainer)
{
((OverlayTabItem)tabItem).AccentColour = value;
}
}
}
public new MarginPadding Padding
{
get => TabContainer.Padding;
set => TabContainer.Padding = value;
}
protected float BarHeight protected float BarHeight
{ {
set => bar.Height = value; set => bar.Height = value;
} }
public override Color4 AccentColour
{
get => base.AccentColour;
set
{
base.AccentColour = value;
bar.Colour = value;
}
}
protected OverlayTabControl() protected OverlayTabControl()
{ {
TabContainer.Masking = false; TabContainer.Masking = false;
@ -66,7 +50,7 @@ namespace osu.Game.Overlays
protected override TabItem<T> CreateTabItem(T value) => new OverlayTabItem(value); protected override TabItem<T> CreateTabItem(T value) => new OverlayTabItem(value);
protected class OverlayTabItem : TabItem<T> protected class OverlayTabItem : TabItem<T>, IHasAccentColour
{ {
protected readonly ExpandingBar Bar; protected readonly ExpandingBar Bar;
protected readonly OsuSpriteText Text; protected readonly OsuSpriteText Text;

View File

@ -33,16 +33,16 @@ namespace osu.Game.Overlays.Profile.Header
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OverlayColourProvider colourProvider)
{ {
iconColour = colours.GreySeafoamLighter; iconColour = colourProvider.Foreground1;
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
new Box new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = colours.GreySeafoamDark, Colour = colourProvider.Background4
}, },
new FillFlowContainer new FillFlowContainer
{ {

View File

@ -7,7 +7,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Game.Graphics;
using osu.Game.Overlays.Profile.Header.Components; using osu.Game.Overlays.Profile.Header.Components;
using osu.Game.Users; using osu.Game.Users;
using osuTK; using osuTK;
@ -28,7 +27,7 @@ namespace osu.Game.Overlays.Profile.Header
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours, TextureStore textures) private void load(OverlayColourProvider colourProvider, TextureStore textures)
{ {
Container<Drawable> hiddenDetailContainer; Container<Drawable> hiddenDetailContainer;
Container<Drawable> expandedDetailContainer; Container<Drawable> expandedDetailContainer;
@ -38,7 +37,7 @@ namespace osu.Game.Overlays.Profile.Header
new Box new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = colours.GreySeafoam Colour = colourProvider.Background4
}, },
new FillFlowContainer new FillFlowContainer
{ {
@ -119,12 +118,12 @@ namespace osu.Game.Overlays.Profile.Header
hiddenDetailGlobal = new OverlinedInfoContainer hiddenDetailGlobal = new OverlinedInfoContainer
{ {
Title = "Global Ranking", Title = "Global Ranking",
LineColour = colours.Yellow LineColour = colourProvider.Highlight1
}, },
hiddenDetailCountry = new OverlinedInfoContainer hiddenDetailCountry = new OverlinedInfoContainer
{ {
Title = "Country Ranking", Title = "Country Ranking",
LineColour = colours.Yellow LineColour = colourProvider.Highlight1
}, },
} }
} }

View File

@ -6,7 +6,6 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osuTK; using osuTK;
namespace osu.Game.Overlays.Profile.Header.Components namespace osu.Game.Overlays.Profile.Header.Components
@ -25,10 +24,10 @@ namespace osu.Game.Overlays.Profile.Header.Components
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OverlayColourProvider colourProvider)
{ {
IdleColour = colours.GreySeafoamLight; IdleColour = colourProvider.Background2;
HoverColour = colours.GreySeafoamLight.Darken(0.2f); HoverColour = colourProvider.Background2.Lighten(0.2f);
Child = icon = new SpriteIcon Child = icon = new SpriteIcon
{ {

View File

@ -43,7 +43,8 @@ namespace osu.Game.Overlays.Profile.Header.Components
line = new Circle line = new Circle
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Height = 4, Height = 2,
Margin = new MarginPadding { Bottom = 2 }
}, },
title = new OsuSpriteText title = new OsuSpriteText
{ {

View File

@ -6,7 +6,6 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Cursor;
using osu.Game.Graphics;
using osu.Game.Users; using osu.Game.Users;
namespace osu.Game.Overlays.Profile.Header.Components namespace osu.Game.Overlays.Profile.Header.Components
@ -27,12 +26,12 @@ namespace osu.Game.Overlays.Profile.Header.Components
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OverlayColourProvider colourProvider)
{ {
InternalChild = info = new OverlinedInfoContainer InternalChild = info = new OverlinedInfoContainer
{ {
Title = "Total Play Time", Title = "Total Play Time",
LineColour = colours.Yellow, LineColour = colourProvider.Highlight1,
}; };
User.BindValueChanged(updateTime, true); User.BindValueChanged(updateTime, true);

View File

@ -54,7 +54,7 @@ namespace osu.Game.Overlays.Profile.Header
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OverlayColourProvider colourProvider, OsuColour colours)
{ {
AutoSizeAxes = Axes.Y; AutoSizeAxes = Axes.Y;
@ -65,7 +65,7 @@ namespace osu.Game.Overlays.Profile.Header
new Box new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = colours.GreySeafoamDarker, Colour = colourProvider.Background5,
}, },
fillFlow = new FillFlowContainer fillFlow = new FillFlowContainer
{ {
@ -152,12 +152,12 @@ namespace osu.Game.Overlays.Profile.Header
detailGlobalRank = new OverlinedInfoContainer(true, 110) detailGlobalRank = new OverlinedInfoContainer(true, 110)
{ {
Title = "Global Ranking", Title = "Global Ranking",
LineColour = colours.Yellow, LineColour = colourProvider.Highlight1,
}, },
detailCountryRank = new OverlinedInfoContainer(false, 110) detailCountryRank = new OverlinedInfoContainer(false, 110)
{ {
Title = "Country Ranking", Title = "Country Ranking",
LineColour = colours.Yellow, LineColour = colourProvider.Highlight1,
}, },
} }
} }

View File

@ -8,7 +8,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Overlays.Profile.Header.Components; using osu.Game.Overlays.Profile.Header.Components;
using osu.Game.Users; using osu.Game.Users;
using osuTK; using osuTK;
@ -23,7 +22,7 @@ namespace osu.Game.Overlays.Profile.Header
public readonly Bindable<User> User = new Bindable<User>(); public readonly Bindable<User> User = new Bindable<User>();
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OverlayColourProvider colourProvider)
{ {
Alpha = 0; Alpha = 0;
AutoSizeAxes = Axes.Y; AutoSizeAxes = Axes.Y;
@ -34,7 +33,7 @@ namespace osu.Game.Overlays.Profile.Header
new Box new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = colours.GreySeafoamDarker, Colour = colourProvider.Background5,
}, },
new Container //artificial shadow new Container //artificial shadow
{ {

View File

@ -33,7 +33,7 @@ namespace osu.Game.Overlays.Profile.Header
private FillFlowContainer userStats; private FillFlowContainer userStats;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OverlayColourProvider colourProvider)
{ {
Height = 150; Height = 150;
@ -42,7 +42,7 @@ namespace osu.Game.Overlays.Profile.Header
new Box new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = colours.GreySeafoamDark, Colour = colourProvider.Background5,
}, },
new FillFlowContainer new FillFlowContainer
{ {
@ -117,7 +117,7 @@ namespace osu.Game.Overlays.Profile.Header
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Height = 1.5f, Height = 1.5f,
Margin = new MarginPadding { Top = 10 }, Margin = new MarginPadding { Top = 10 },
Colour = colours.GreySeafoamLighter, Colour = colourProvider.Light1,
}, },
new FillFlowContainer new FillFlowContainer
{ {
@ -137,7 +137,7 @@ namespace osu.Game.Overlays.Profile.Header
Margin = new MarginPadding { Left = 10 }, Margin = new MarginPadding { Left = 10 },
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Colour = colours.GreySeafoamLighter, Colour = colourProvider.Light1,
} }
} }
}, },

View File

@ -14,7 +14,7 @@ using osu.Game.Users;
namespace osu.Game.Overlays.Profile namespace osu.Game.Overlays.Profile
{ {
public class ProfileHeader : TabControlOverlayHeader public class ProfileHeader : TabControlOverlayHeader<string>
{ {
private UserCoverBackground coverContainer; private UserCoverBackground coverContainer;
@ -25,8 +25,6 @@ namespace osu.Game.Overlays.Profile
public ProfileHeader() public ProfileHeader()
{ {
BackgroundHeight = 150;
User.ValueChanged += e => updateDisplay(e.NewValue); User.ValueChanged += e => updateDisplay(e.NewValue);
TabControl.AddItem("info"); TabControl.AddItem("info");
@ -38,7 +36,9 @@ namespace osu.Game.Overlays.Profile
protected override Drawable CreateBackground() => protected override Drawable CreateBackground() =>
new Container new Container
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.X,
Height = 150,
Masking = true,
Children = new Drawable[] Children = new Drawable[]
{ {
coverContainer = new UserCoverBackground coverContainer = new UserCoverBackground

View File

@ -95,10 +95,10 @@ namespace osu.Game.Overlays.Profile
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OverlayColourProvider colourProvider)
{ {
background.Colour = colours.GreySeafoamDarker; background.Colour = colourProvider.Background5;
underscore.Colour = colours.Seafoam; underscore.Colour = colourProvider.Highlight1;
} }
private class SectionTriangles : Container private class SectionTriangles : Container
@ -128,11 +128,11 @@ namespace osu.Game.Overlays.Profile
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OverlayColourProvider colourProvider)
{ {
triangles.ColourLight = colours.GreySeafoamDark; triangles.ColourLight = colourProvider.Background4;
triangles.ColourDark = colours.GreySeafoamDarker.Darken(0.2f); triangles.ColourDark = colourProvider.Background5.Darken(0.2f);
foreground.Colour = ColourInfo.GradientVertical(colours.GreySeafoamDarker, colours.GreySeafoamDarker.Opacity(0)); foreground.Colour = ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Background5.Opacity(0));
} }
} }
} }

View File

@ -4,7 +4,6 @@
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.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation; using osu.Framework.Localisation;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
@ -13,32 +12,25 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osuTK; using osuTK;
using System.Collections.Generic;
using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Cursor;
namespace osu.Game.Overlays.Profile.Sections.Historical namespace osu.Game.Overlays.Profile.Sections.Historical
{ {
public class DrawableMostPlayedBeatmap : OsuHoverContainer public class DrawableMostPlayedBeatmap : CompositeDrawable
{ {
private const int cover_width = 100; private const int cover_width = 100;
private const int corner_radius = 6; private const int corner_radius = 6;
private const int height = 50;
private readonly BeatmapInfo beatmap; private readonly BeatmapInfo beatmap;
private readonly int playCount; private readonly int playCount;
private Box background;
protected override IEnumerable<Drawable> EffectTargets => new[] { background };
public DrawableMostPlayedBeatmap(BeatmapInfo beatmap, int playCount) public DrawableMostPlayedBeatmap(BeatmapInfo beatmap, int playCount)
{ {
this.beatmap = beatmap; this.beatmap = beatmap;
this.playCount = playCount; this.playCount = playCount;
Enabled.Value = true; //manually enabled, because we have no action
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
Height = height; Height = 50;
Masking = true; Masking = true;
CornerRadius = corner_radius; CornerRadius = corner_radius;
@ -47,10 +39,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
IdleColour = colours.GreySeafoam; AddRangeInternal(new Drawable[]
HoverColour = colours.GreySeafoamLight;
Children = new Drawable[]
{ {
new UpdateableBeatmapSetCover new UpdateableBeatmapSetCover
{ {
@ -72,46 +61,48 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
CornerRadius = corner_radius, CornerRadius = corner_radius,
Children = new Drawable[] Children = new Drawable[]
{ {
background = new Box { RelativeSizeAxes = Axes.Both }, new ProfileItemContainer
new Container
{ {
RelativeSizeAxes = Axes.Both, Child = new Container
Padding = new MarginPadding(10),
Children = new Drawable[]
{ {
new FillFlowContainer RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(10),
Children = new Drawable[]
{ {
Anchor = Anchor.CentreLeft, new FillFlowContainer
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{ {
new MostPlayedBeatmapMetadataContainer(beatmap), Anchor = Anchor.CentreLeft,
new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular)) Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{ {
AutoSizeAxes = Axes.Both, new MostPlayedBeatmapMetadataContainer(beatmap),
Direction = FillDirection.Horizontal, new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular))
Colour = colours.GreySeafoamLighter {
}.With(d => AutoSizeAxes = Axes.Both,
{ Direction = FillDirection.Horizontal,
d.AddText("mapped by "); Colour = colours.GreySeafoamLighter
d.AddUserLink(beatmap.Metadata.Author); }.With(d =>
}), {
} d.AddText("mapped by ");
}, d.AddUserLink(beatmap.Metadata.Author);
new PlayCountText(playCount) }),
{ }
Anchor = Anchor.CentreRight, },
Origin = Anchor.CentreRight new PlayCountText(playCount)
}, {
} Anchor = Anchor.CentreRight,
}, Origin = Anchor.CentreRight
},
}
},
}
} }
} }
} }
} }
}; });
} }
private class MostPlayedBeatmapMetadataContainer : BeatmapMetadataContainer private class MostPlayedBeatmapMetadataContainer : BeatmapMetadataContainer

View File

@ -101,7 +101,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu
{ {
Masking = true, Masking = true,
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Height = 5, Height = 2,
Child = lineBackground = new Box Child = lineBackground = new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
@ -128,10 +128,10 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OverlayColourProvider colourProvider)
{ {
lineBackground.Colour = colours.Yellow; lineBackground.Colour = colourProvider.Highlight1;
DescriptionText.Colour = colours.GreySeafoamLighter; DescriptionText.Colour = colourProvider.Foreground1;
} }
} }
} }

View File

@ -0,0 +1,63 @@
// 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.Framework.Input.Events;
using osuTK.Graphics;
namespace osu.Game.Overlays.Profile.Sections
{
public class ProfileItemContainer : Container
{
private const int hover_duration = 200;
protected override Container<Drawable> Content => content;
private Color4 idleColour;
private Color4 hoverColour;
private readonly Box background;
private readonly Container content;
public ProfileItemContainer()
{
RelativeSizeAxes = Axes.Both;
Masking = true;
CornerRadius = 6;
AddRangeInternal(new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
},
content = new Container
{
RelativeSizeAxes = Axes.Both,
}
});
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
background.Colour = idleColour = colourProvider.Background4;
hoverColour = colourProvider.Background3;
}
protected override bool OnHover(HoverEvent e)
{
background.FadeColour(hoverColour, hover_duration, Easing.OutQuint);
return base.OnHover(e);
}
protected override void OnHoverLost(HoverLostEvent e)
{
base.OnHoverLost(e);
background.FadeColour(idleColour, hover_duration, Easing.OutQuint);
}
}
}

View File

@ -2,7 +2,6 @@
// 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.Allocation;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Profile.Sections namespace osu.Game.Overlays.Profile.Sections
@ -10,11 +9,11 @@ namespace osu.Game.Overlays.Profile.Sections
public class ProfileShowMoreButton : ShowMoreButton public class ProfileShowMoreButton : ShowMoreButton
{ {
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colors) private void load(OverlayColourProvider colourProvider)
{ {
IdleColour = colors.GreySeafoamDark; IdleColour = colourProvider.Background2;
HoverColour = colors.GreySeafoam; HoverColour = colourProvider.Background1;
ChevronIconColour = colors.Yellow; ChevronIconColour = colourProvider.Foreground1;
} }
} }
} }

View File

@ -1,47 +0,0 @@
// 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.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Scoring;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
public class DrawablePerformanceScore : DrawableProfileScore
{
private readonly double? weight;
public DrawablePerformanceScore(ScoreInfo score, double? weight = null)
: base(score)
{
this.weight = weight;
}
[BackgroundDependencyLoader]
private void load(OsuColour colour)
{
double pp = Score.PP ?? 0;
RightFlowContainer.Add(new OsuSpriteText
{
Text = $"{pp:0}pp",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold, italics: true)
});
if (weight.HasValue)
{
RightFlowContainer.Add(new OsuSpriteText
{
Text = $"weighted: {pp * weight:0}pp ({weight:P0})",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Colour = colour.GrayA,
Font = OsuFont.GetFont(size: 11, weight: FontWeight.Regular, italics: true)
});
}
}
}
}

View File

@ -1,76 +1,225 @@
// 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 osuTK; using System.Linq;
using JetBrains.Annotations;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Online.Leaderboards; using osu.Game.Online.Leaderboards;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Beatmaps; using osuTK;
using osu.Framework.Localisation; using osuTK.Graphics;
using osu.Framework.Graphics.Containers;
namespace osu.Game.Overlays.Profile.Sections.Ranks namespace osu.Game.Overlays.Profile.Sections.Ranks
{ {
public abstract class DrawableProfileScore : DrawableProfileRow public class DrawableProfileScore : CompositeDrawable
{ {
private readonly FillFlowContainer modsContainer; private const int performance_width = 80;
private const int content_padding = 10;
protected readonly ScoreInfo Score; protected readonly ScoreInfo Score;
protected DrawableProfileScore(ScoreInfo score) [Resolved]
private OsuColour colours { get; set; }
[Resolved]
private OverlayColourProvider colourProvider { get; set; }
public DrawableProfileScore(ScoreInfo score)
{ {
Score = score; Score = score;
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
Height = 60; Height = 40;
Children = new Drawable[] }
[BackgroundDependencyLoader]
private void load()
{
AddInternal(new ProfileItemContainer
{ {
modsContainer = new FillFlowContainer Children = new Drawable[]
{ {
AutoSizeAxes = Axes.Both, new Container
Anchor = Anchor.CentreRight, {
Origin = Anchor.CentreRight, RelativeSizeAxes = Axes.Both,
Spacing = new Vector2(1), Padding = new MarginPadding { Left = content_padding, Right = performance_width + content_padding },
Margin = new MarginPadding { Right = 160 } Children = new Drawable[]
{
new FillFlowContainer
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(8, 0),
Children = new Drawable[]
{
new UpdateableRank(Score.Rank)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(50, 20),
},
new FillFlowContainer
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 2),
Children = new Drawable[]
{
new ScoreBeatmapMetadataContainer(Score.Beatmap),
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5, 0),
Children = new Drawable[]
{
new OsuSpriteText
{
Text = $"{Score.Beatmap.Version}",
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular),
Colour = colours.Yellow
},
new DrawableDate(Score.Date, 12)
{
Colour = colourProvider.Foreground1
}
}
}
}
}
}
},
new FillFlowContainer
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(15),
Children = new[]
{
CreateRightContent().With(c =>
{
c.Anchor = Anchor.CentreRight;
c.Origin = Anchor.CentreRight;
}),
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(2),
Children = Score.Mods.Select(mod => new ModIcon(mod)
{
Scale = new Vector2(0.35f)
}).ToList(),
}
}
}
}
},
new Container
{
RelativeSizeAxes = Axes.Y,
Width = performance_width,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Size = new Vector2(1, 0.5f),
Colour = Color4.Black.Opacity(0.5f),
Shear = new Vector2(-0.45f, 0),
EdgeSmoothness = new Vector2(2, 0),
},
new Box
{
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Y,
Size = new Vector2(1, -0.5f),
Position = new Vector2(0, 1),
Colour = Color4.Black.Opacity(0.5f),
Shear = new Vector2(0.45f, 0),
EdgeSmoothness = new Vector2(2, 0),
},
createDrawablePerformance().With(d =>
{
d.Anchor = Anchor.Centre;
d.Origin = Anchor.Centre;
})
}
}
} }
}; });
} }
[BackgroundDependencyLoader(true)] [NotNull]
private void load(OsuColour colour) protected virtual Drawable CreateRightContent() => CreateDrawableAccuracy();
protected OsuSpriteText CreateDrawableAccuracy() => new OsuSpriteText
{ {
var text = new OsuSpriteText Text = $"{Score.Accuracy:0.00%}",
{ Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold, italics: true),
Text = $"accuracy: {Score.Accuracy:P2}", Colour = colours.Yellow,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Colour = colour.GrayA,
Font = OsuFont.GetFont(size: 11, weight: FontWeight.Regular, italics: true)
};
RightFlowContainer.Insert(1, text);
LeftFlowContainer.Add(new ProfileScoreBeatmapMetadataContainer(Score.Beatmap));
LeftFlowContainer.Add(new DrawableDate(Score.Date));
foreach (Mod mod in Score.Mods)
modsContainer.Add(new ModIcon(mod) { Scale = new Vector2(0.5f) });
}
protected override Drawable CreateLeftVisual() => new UpdateableRank(Score.Rank)
{
RelativeSizeAxes = Axes.Y,
Width = 60,
FillMode = FillMode.Fit,
}; };
private class ProfileScoreBeatmapMetadataContainer : BeatmapMetadataContainer private Drawable createDrawablePerformance()
{ {
public ProfileScoreBeatmapMetadataContainer(BeatmapInfo beatmap) if (Score.PP.HasValue)
{
return new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Children = new[]
{
new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Font = OsuFont.GetFont(weight: FontWeight.Bold),
Text = $"{Score.PP:0}",
Colour = colourProvider.Highlight1
},
new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold),
Text = "pp",
Colour = colourProvider.Light3
}
}
};
}
return new OsuSpriteText
{
Font = OsuFont.GetFont(weight: FontWeight.Bold),
Text = "-",
Colour = colourProvider.Highlight1
};
}
private class ScoreBeatmapMetadataContainer : BeatmapMetadataContainer
{
public ScoreBeatmapMetadataContainer(BeatmapInfo beatmap)
: base(beatmap) : base(beatmap)
{ {
} }
@ -79,16 +228,19 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
{ {
new OsuSpriteText new OsuSpriteText
{ {
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Text = new LocalisedString(( Text = new LocalisedString((
$"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} [{beatmap.Version}] ", $"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} ",
$"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} [{beatmap.Version}] ")), $"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} ")),
Font = OsuFont.GetFont(size: 15, weight: FontWeight.SemiBold, italics: true) Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold, italics: true)
}, },
new OsuSpriteText new OsuSpriteText
{ {
Text = new LocalisedString((beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist)), Anchor = Anchor.BottomLeft,
Padding = new MarginPadding { Top = 3 }, Origin = Anchor.BottomLeft,
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular, italics: true) Text = "by " + new LocalisedString((beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist)),
Font = OsuFont.GetFont(size: 12, italics: true)
}, },
}; };
} }

View File

@ -0,0 +1,55 @@
// 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.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Scoring;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
public class DrawableProfileWeightedScore : DrawableProfileScore
{
private readonly double weight;
public DrawableProfileWeightedScore(ScoreInfo score, double weight)
: base(score)
{
this.weight = weight;
}
protected override Drawable CreateRightContent() => new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
new Container
{
AutoSizeAxes = Axes.Y,
Width = 60,
Child = CreateDrawableAccuracy()
},
new OsuSpriteText
{
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold, italics: true),
Text = $"{Score.PP * weight:0}pp",
},
}
},
new OsuSpriteText
{
Font = OsuFont.GetFont(size: 12),
Text = $@"weighted {weight:0%}"
}
}
};
}
}

View File

@ -1,31 +0,0 @@
// 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.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Scoring;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
public class DrawableTotalScore : DrawableProfileScore
{
public DrawableTotalScore(ScoreInfo score)
: base(score)
{
}
[BackgroundDependencyLoader]
private void load()
{
RightFlowContainer.Add(new OsuSpriteText
{
Text = Score.TotalScore.ToString("#,###"),
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold, italics: true)
});
}
}
}

View File

@ -15,14 +15,12 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
{ {
public class PaginatedScoreContainer : PaginatedContainer<APILegacyScoreInfo> public class PaginatedScoreContainer : PaginatedContainer<APILegacyScoreInfo>
{ {
private readonly bool includeWeight;
private readonly ScoreType type; private readonly ScoreType type;
public PaginatedScoreContainer(ScoreType type, Bindable<User> user, string header, string missing, bool includeWeight = false) public PaginatedScoreContainer(ScoreType type, Bindable<User> user, string header, string missing)
: base(user, header, missing) : base(user, header, missing)
{ {
this.type = type; this.type = type;
this.includeWeight = includeWeight;
ItemsPerPage = 5; ItemsPerPage = 5;
@ -43,10 +41,10 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
switch (type) switch (type)
{ {
default: default:
return new DrawablePerformanceScore(model.CreateScoreInfo(Rulesets), includeWeight ? Math.Pow(0.95, ItemsContainer.Count) : (double?)null); return new DrawableProfileScore(model.CreateScoreInfo(Rulesets));
case ScoreType.Recent: case ScoreType.Best:
return new DrawableTotalScore(model.CreateScoreInfo(Rulesets)); return new DrawableProfileWeightedScore(model.CreateScoreInfo(Rulesets), Math.Pow(0.95, ItemsContainer.Count));
} }
} }
} }

View File

@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Profile.Sections
{ {
Children = new[] Children = new[]
{ {
new PaginatedScoreContainer(ScoreType.Best, User, "Best Performance", "No performance records. :(", true), new PaginatedScoreContainer(ScoreType.Best, User, "Best Performance", "No performance records. :("),
new PaginatedScoreContainer(ScoreType.Firsts, User, "First Place Ranks", "No awesome performance records yet. :("), new PaginatedScoreContainer(ScoreType.Firsts, User, "First Place Ranks", "No awesome performance records yet. :("),
}; };
} }

View File

@ -17,10 +17,34 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
{ {
Children = new Drawable[] Children = new Drawable[]
{ {
new SettingsSlider<double> { LabelText = "Master", Bindable = audio.Volume, KeyboardStep = 0.01f }, new SettingsSlider<double>
new SettingsSlider<double> { LabelText = "Master (window inactive)", Bindable = config.GetBindable<double>(OsuSetting.VolumeInactive), KeyboardStep = 0.01f }, {
new SettingsSlider<double> { LabelText = "Effect", Bindable = audio.VolumeSample, KeyboardStep = 0.01f }, LabelText = "Master",
new SettingsSlider<double> { LabelText = "Music", Bindable = audio.VolumeTrack, KeyboardStep = 0.01f }, Bindable = audio.Volume,
KeyboardStep = 0.01f,
DisplayAsPercentage = true
},
new SettingsSlider<double>
{
LabelText = "Master (window inactive)",
Bindable = config.GetBindable<double>(OsuSetting.VolumeInactive),
KeyboardStep = 0.01f,
DisplayAsPercentage = true
},
new SettingsSlider<double>
{
LabelText = "Effect",
Bindable = audio.VolumeSample,
KeyboardStep = 0.01f,
DisplayAsPercentage = true
},
new SettingsSlider<double>
{
LabelText = "Music",
Bindable = audio.VolumeTrack,
KeyboardStep = 0.01f,
DisplayAsPercentage = true
},
}; };
} }
} }

View File

@ -21,13 +21,15 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
{ {
LabelText = "Background dim", LabelText = "Background dim",
Bindable = config.GetBindable<double>(OsuSetting.DimLevel), Bindable = config.GetBindable<double>(OsuSetting.DimLevel),
KeyboardStep = 0.01f KeyboardStep = 0.01f,
DisplayAsPercentage = true
}, },
new SettingsSlider<double> new SettingsSlider<double>
{ {
LabelText = "Background blur", LabelText = "Background blur",
Bindable = config.GetBindable<double>(OsuSetting.BlurLevel), Bindable = config.GetBindable<double>(OsuSetting.BlurLevel),
KeyboardStep = 0.01f KeyboardStep = 0.01f,
DisplayAsPercentage = true
}, },
new SettingsCheckbox new SettingsCheckbox
{ {

View File

@ -98,25 +98,29 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
{ {
LabelText = "Horizontal position", LabelText = "Horizontal position",
Bindable = scalingPositionX, Bindable = scalingPositionX,
KeyboardStep = 0.01f KeyboardStep = 0.01f,
DisplayAsPercentage = true
}, },
new SettingsSlider<float> new SettingsSlider<float>
{ {
LabelText = "Vertical position", LabelText = "Vertical position",
Bindable = scalingPositionY, Bindable = scalingPositionY,
KeyboardStep = 0.01f KeyboardStep = 0.01f,
DisplayAsPercentage = true
}, },
new SettingsSlider<float> new SettingsSlider<float>
{ {
LabelText = "Horizontal scale", LabelText = "Horizontal scale",
Bindable = scalingSizeX, Bindable = scalingSizeX,
KeyboardStep = 0.01f KeyboardStep = 0.01f,
DisplayAsPercentage = true
}, },
new SettingsSlider<float> new SettingsSlider<float>
{ {
LabelText = "Vertical scale", LabelText = "Vertical scale",
Bindable = scalingSizeY, Bindable = scalingSizeY,
KeyboardStep = 0.01f KeyboardStep = 0.01f,
DisplayAsPercentage = true
}, },
} }
}, },

View File

@ -3,6 +3,7 @@
using System; using System;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Settings namespace osu.Game.Overlays.Settings
@ -22,16 +23,32 @@ namespace osu.Game.Overlays.Settings
RelativeSizeAxes = Axes.X RelativeSizeAxes = Axes.X
}; };
/// <summary>
/// When set, value changes based on user input are only transferred to any bound control's Current on commit.
/// This is useful if the UI interaction could be adversely affected by the value changing, such as the position of the <see cref="SliderBar{T}"/> on the screen.
/// </summary>
public bool TransferValueOnCommit public bool TransferValueOnCommit
{ {
get => ((TSlider)Control).TransferValueOnCommit; get => ((TSlider)Control).TransferValueOnCommit;
set => ((TSlider)Control).TransferValueOnCommit = value; set => ((TSlider)Control).TransferValueOnCommit = value;
} }
/// <summary>
/// A custom step value for each key press which actuates a change on this control.
/// </summary>
public float KeyboardStep public float KeyboardStep
{ {
get => ((TSlider)Control).KeyboardStep; get => ((TSlider)Control).KeyboardStep;
set => ((TSlider)Control).KeyboardStep = value; set => ((TSlider)Control).KeyboardStep = value;
} }
/// <summary>
/// Whether to format the tooltip as a percentage or the actual value.
/// </summary>
public bool DisplayAsPercentage
{
get => ((TSlider)Control).DisplayAsPercentage;
set => ((TSlider)Control).DisplayAsPercentage = value;
}
} }
} }

View File

@ -1,28 +1,56 @@
// 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 JetBrains.Annotations;
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.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osuTK; using osuTK;
namespace osu.Game.Overlays namespace osu.Game.Overlays
{ {
public abstract class TabControlOverlayHeader : OverlayHeader /// <summary>
/// An overlay header which contains a <see cref="OsuTabControl{T}"/>.
/// </summary>
/// <typeparam name="T">The type of item to be represented by tabs.</typeparam>
public abstract class TabControlOverlayHeader<T> : OverlayHeader
{ {
protected OverlayHeaderTabControl TabControl; protected OsuTabControl<T> TabControl;
protected override TabControl<string> CreateTabControl() => TabControl = new OverlayHeaderTabControl(); private readonly Box controlBackground;
protected TabControlOverlayHeader()
{
HeaderInfo.Add(new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
controlBackground = new Box
{
RelativeSizeAxes = Axes.Both,
},
TabControl = CreateTabControl().With(control => control.Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN })
}
});
}
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider) private void load(OverlayColourProvider colourProvider)
{ {
TabControl.AccentColour = colourProvider.Highlight1; TabControl.AccentColour = colourProvider.Highlight1;
controlBackground.Colour = colourProvider.Dark4;
} }
public class OverlayHeaderTabControl : OverlayTabControl<string> [NotNull]
protected virtual OsuTabControl<T> CreateTabControl() => new OverlayHeaderTabControl();
public class OverlayHeaderTabControl : OverlayTabControl<T>
{ {
public OverlayHeaderTabControl() public OverlayHeaderTabControl()
{ {
@ -34,10 +62,7 @@ namespace osu.Game.Overlays
Height = 35; Height = 35;
} }
protected override TabItem<string> CreateTabItem(string value) => new OverlayHeaderTabItem(value) protected override TabItem<T> CreateTabItem(T value) => new OverlayHeaderTabItem(value);
{
AccentColour = AccentColour,
};
protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
{ {
@ -49,10 +74,10 @@ namespace osu.Game.Overlays
private class OverlayHeaderTabItem : OverlayTabItem private class OverlayHeaderTabItem : OverlayTabItem
{ {
public OverlayHeaderTabItem(string value) public OverlayHeaderTabItem(T value)
: base(value) : base(value)
{ {
Text.Text = value; Text.Text = value.ToString().ToLower();
Text.Font = OsuFont.GetFont(size: 14); Text.Font = OsuFont.GetFont(size: 14);
Bar.ExpandedSize = 5; Bar.ExpandedSize = 5;
} }

View File

@ -8,7 +8,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Profile; using osu.Game.Overlays.Profile;
@ -30,7 +29,7 @@ namespace osu.Game.Overlays
public const float CONTENT_X_MARGIN = 70; public const float CONTENT_X_MARGIN = 70;
public UserProfileOverlay() public UserProfileOverlay()
: base(OverlayColourScheme.Green) : base(OverlayColourScheme.Pink)
{ {
} }
@ -74,7 +73,7 @@ namespace osu.Game.Overlays
Add(new Box Add(new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.1f) Colour = ColourProvider.Background6
}); });
Add(sectionsContainer = new ProfileSectionsContainer Add(sectionsContainer = new ProfileSectionsContainer
@ -83,7 +82,8 @@ namespace osu.Game.Overlays
FixedHeader = tabs, FixedHeader = tabs,
HeaderBackground = new Box HeaderBackground = new Box
{ {
Colour = OsuColour.Gray(34), // this is only visible as the ProfileTabControl background
Colour = ColourProvider.Background5,
RelativeSizeAxes = Axes.Both RelativeSizeAxes = Axes.Both
}, },
}); });
@ -165,9 +165,9 @@ namespace osu.Game.Overlays
}; };
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OverlayColourProvider colourProvider)
{ {
AccentColour = colours.Seafoam; AccentColour = colourProvider.Highlight1;
} }
private class ProfileTabItem : OverlayTabItem private class ProfileTabItem : OverlayTabItem

View File

@ -9,6 +9,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.Input.Events;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Framework.Timing; using osu.Framework.Timing;
@ -25,6 +26,7 @@ using osu.Game.Screens.Edit.Components.RadioButtons;
using osu.Game.Screens.Edit.Compose; using osu.Game.Screens.Edit.Compose;
using osu.Game.Screens.Edit.Compose.Components; using osu.Game.Screens.Edit.Compose.Components;
using osuTK; using osuTK;
using Key = osuTK.Input.Key;
namespace osu.Game.Rulesets.Edit namespace osu.Game.Rulesets.Edit
{ {
@ -58,6 +60,8 @@ namespace osu.Game.Rulesets.Edit
private InputManager inputManager; private InputManager inputManager;
private RadioButtonCollection toolboxCollection;
protected HitObjectComposer(Ruleset ruleset) protected HitObjectComposer(Ruleset ruleset)
{ {
Ruleset = ruleset; Ruleset = ruleset;
@ -100,7 +104,6 @@ namespace osu.Game.Rulesets.Edit
layerContainers.Add(layerBelowRuleset); layerContainers.Add(layerBelowRuleset);
layerContainers.Add(layerAboveRuleset); layerContainers.Add(layerAboveRuleset);
RadioButtonCollection toolboxCollection;
InternalChild = new GridContainer InternalChild = new GridContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
@ -137,16 +140,32 @@ namespace osu.Game.Rulesets.Edit
} }
}; };
toolboxCollection.Items = toolboxCollection.Items = CompositionTools
CompositionTools.Select(t => new RadioButton(t.Name, () => selectTool(t))) .Prepend(new SelectTool())
.Prepend(new RadioButton("Select", () => selectTool(null))) .Select(t => new RadioButton(t.Name, () => toolSelected(t)))
.ToList(); .ToList();
toolboxCollection.Items[0].Select(); setSelectTool();
blueprintContainer.SelectionChanged += selectionChanged; blueprintContainer.SelectionChanged += selectionChanged;
} }
protected override bool OnKeyDown(KeyDownEvent e)
{
if (e.Key >= Key.Number1 && e.Key <= Key.Number9)
{
var item = toolboxCollection.Items.Skip(e.Key - Key.Number1).FirstOrDefault();
if (item != null)
{
item.Select();
return true;
}
}
return base.OnKeyDown(e);
}
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
@ -181,20 +200,30 @@ namespace osu.Game.Rulesets.Edit
{ {
var hitObjects = selectedHitObjects.ToArray(); var hitObjects = selectedHitObjects.ToArray();
if (!hitObjects.Any()) if (hitObjects.Any())
distanceSnapGridContainer.Hide(); {
else // ensure in selection mode if a selection is made.
setSelectTool();
showGridFor(hitObjects); showGridFor(hitObjects);
}
else
distanceSnapGridContainer.Hide();
} }
private void selectTool(HitObjectCompositionTool tool) private void setSelectTool() => toolboxCollection.Items.First().Select();
private void toolSelected(HitObjectCompositionTool tool)
{ {
blueprintContainer.CurrentTool = tool; blueprintContainer.CurrentTool = tool;
if (tool == null) if (tool is SelectTool)
distanceSnapGridContainer.Hide(); distanceSnapGridContainer.Hide();
else else
{
EditorBeatmap.SelectedHitObjects.Clear();
showGridFor(Enumerable.Empty<HitObject>()); showGridFor(Enumerable.Empty<HitObject>());
}
} }
private void showGridFor(IEnumerable<HitObject> selectedHitObjects) private void showGridFor(IEnumerable<HitObject> selectedHitObjects)
@ -275,10 +304,10 @@ namespace osu.Game.Rulesets.Edit
} }
public override double GetSnappedDurationFromDistance(double referenceTime, float distance) public override double GetSnappedDurationFromDistance(double referenceTime, float distance)
=> beatSnapProvider.SnapTime(referenceTime, DistanceToDuration(referenceTime, distance)); => beatSnapProvider.SnapTime(referenceTime + DistanceToDuration(referenceTime, distance), referenceTime) - referenceTime;
public override float GetSnappedDistanceFromDistance(double referenceTime, float distance) public override float GetSnappedDistanceFromDistance(double referenceTime, float distance)
=> DurationToDistance(referenceTime, beatSnapProvider.SnapTime(referenceTime, DistanceToDuration(referenceTime, distance))); => DurationToDistance(referenceTime, beatSnapProvider.SnapTime(DistanceToDuration(referenceTime, distance), referenceTime));
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)
{ {

View File

@ -8,10 +8,10 @@ namespace osu.Game.Rulesets.Edit
/// <summary> /// <summary>
/// Snaps a duration to the closest beat of a timing point applicable at the reference time. /// Snaps a duration to the closest beat of a timing point applicable at the reference time.
/// </summary> /// </summary>
/// <param name="referenceTime">The time of the timing point which <paramref name="duration"/> resides in.</param> /// <param name="time">The time to snap.</param>
/// <param name="duration">The duration to snap.</param> /// <param name="referenceTime">An optional reference point to use for timing point lookup.</param>
/// <returns>A value that represents <paramref name="duration"/> snapped to the closest beat of the timing point.</returns> /// <returns>A value that represents <paramref name="time"/> snapped to the closest beat of the timing point.</returns>
double SnapTime(double referenceTime, double duration); double SnapTime(double time, double? referenceTime = null);
/// <summary> /// <summary>
/// Get the most appropriate beat length at a given time. /// Get the most appropriate beat length at a given time.

View File

@ -13,5 +13,7 @@ namespace osu.Game.Rulesets.Edit.Tools
} }
public abstract PlacementBlueprint CreatePlacementBlueprint(); public abstract PlacementBlueprint CreatePlacementBlueprint();
public override string ToString() => Name;
} }
} }

View File

@ -0,0 +1,15 @@
// 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.
namespace osu.Game.Rulesets.Edit.Tools
{
public class SelectTool : HitObjectCompositionTool
{
public SelectTool()
: base("Select")
{
}
public override PlacementBlueprint CreatePlacementBlueprint() => null;
}
}

View File

@ -20,6 +20,9 @@ namespace osu.Game.Rulesets.Mods
/// </summary> /// </summary>
private const double final_rate_progress = 0.75f; private const double final_rate_progress = 0.75f;
[SettingSource("Initial rate", "The starting speed of the track")]
public abstract BindableNumber<double> InitialRate { get; }
[SettingSource("Final rate", "The final speed to ramp to")] [SettingSource("Final rate", "The final speed to ramp to")]
public abstract BindableNumber<double> FinalRate { get; } public abstract BindableNumber<double> FinalRate { get; }
@ -69,6 +72,6 @@ namespace osu.Game.Rulesets.Mods
/// </summary> /// </summary>
/// <param name="amount">The amount of adjustment to apply (from 0..1).</param> /// <param name="amount">The amount of adjustment to apply (from 0..1).</param>
private void applyAdjustment(double amount) => private void applyAdjustment(double amount) =>
SpeedChange.Value = 1 + (FinalRate.Value - 1) * Math.Clamp(amount, 0, 1); SpeedChange.Value = InitialRate.Value + (FinalRate.Value - InitialRate.Value) * Math.Clamp(amount, 0, 1);
} }
} }

View File

@ -17,6 +17,16 @@ namespace osu.Game.Rulesets.Mods
public override IconUsage? Icon => FontAwesome.Solid.ChevronCircleDown; public override IconUsage? Icon => FontAwesome.Solid.ChevronCircleDown;
public override double ScoreMultiplier => 1.0; public override double ScoreMultiplier => 1.0;
[SettingSource("Initial rate", "The starting speed of the track")]
public override BindableNumber<double> InitialRate { get; } = new BindableDouble
{
MinValue = 1,
MaxValue = 2,
Default = 1,
Value = 1,
Precision = 0.01,
};
[SettingSource("Final rate", "The speed increase to ramp towards")] [SettingSource("Final rate", "The speed increase to ramp towards")]
public override BindableNumber<double> FinalRate { get; } = new BindableDouble public override BindableNumber<double> FinalRate { get; } = new BindableDouble
{ {

View File

@ -17,6 +17,16 @@ namespace osu.Game.Rulesets.Mods
public override IconUsage? Icon => FontAwesome.Solid.ChevronCircleUp; public override IconUsage? Icon => FontAwesome.Solid.ChevronCircleUp;
public override double ScoreMultiplier => 1.0; public override double ScoreMultiplier => 1.0;
[SettingSource("Initial rate", "The starting speed of the track")]
public override BindableNumber<double> InitialRate { get; } = new BindableDouble
{
MinValue = 0.5,
MaxValue = 1,
Default = 1,
Value = 1,
Precision = 0.01,
};
[SettingSource("Final rate", "The speed increase to ramp towards")] [SettingSource("Final rate", "The speed increase to ramp towards")]
public override BindableNumber<double> FinalRate { get; } = new BindableDouble public override BindableNumber<double> FinalRate { get; } = new BindableDouble
{ {

View File

@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Scoring
public readonly Bindable<ScoreRank> Rank = new Bindable<ScoreRank>(ScoreRank.X); public readonly Bindable<ScoreRank> Rank = new Bindable<ScoreRank>(ScoreRank.X);
/// <summary> /// <summary>
/// THe highest combo achieved by this score. /// The highest combo achieved by this score.
/// </summary> /// </summary>
public readonly BindableInt HighestCombo = new BindableInt(); public readonly BindableInt HighestCombo = new BindableInt();

View File

@ -52,30 +52,52 @@ namespace osu.Game.Screens.Edit
{ {
switch (beatDivisor) switch (beatDivisor)
{ {
case 1:
return Color4.White;
case 2: case 2:
return colours.BlueLight; return colours.Red;
case 4: case 4:
return colours.Blue; return colours.Blue;
case 8: case 8:
return colours.BlueDarker; return colours.Yellow;
case 16: case 16:
return colours.PurpleDark; return colours.PurpleDark;
case 3: case 3:
return colours.YellowLight; return colours.Purple;
case 6: case 6:
return colours.Yellow; return colours.YellowDark;
case 12: case 12:
return colours.YellowDarker; return colours.YellowDarker;
default: default:
return Color4.White; return Color4.Red;
} }
} }
/// <summary>
/// Retrieves the applicable divisor for a specific beat index.
/// </summary>
/// <param name="index">The 0-based beat index.</param>
/// <param name="beatDivisor">The beat divisor.</param>
/// <returns>The applicable divisor.</returns>
public static int GetDivisorForBeatIndex(int index, int beatDivisor)
{
int beat = index % beatDivisor;
foreach (var divisor in BindableBeatDivisor.VALID_DIVISORS)
{
if ((beat * divisor) % beatDivisor == 0)
return divisor;
}
return 0;
}
} }
} }

View File

@ -9,7 +9,6 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
@ -37,8 +36,8 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
{ {
this.button = button; this.button = button;
Text = button.Text; Text = button.Item.ToString();
Action = button.Action; Action = button.Select;
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
@ -100,19 +99,6 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
bubble.Colour = button.Selected.Value ? selectedBubbleColour : defaultBubbleColour; bubble.Colour = button.Selected.Value ? selectedBubbleColour : defaultBubbleColour;
} }
protected override bool OnClick(ClickEvent e)
{
if (button.Selected.Value)
return true;
if (!Enabled.Value)
return true;
button.Selected.Value = true;
return base.OnClick(e);
}
protected override SpriteText CreateText() => new OsuSpriteText protected override SpriteText CreateText() => new OsuSpriteText
{ {
Depth = -1, Depth = -1,

View File

@ -15,33 +15,37 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
public readonly BindableBool Selected; public readonly BindableBool Selected;
/// <summary> /// <summary>
/// The text that should be displayed in this button. /// The item related to this button.
/// </summary> /// </summary>
public string Text; public object Item;
/// <summary> private readonly Action action;
/// The <see cref="Action"/> that should be invoked when this button is selected.
/// </summary>
public Action Action;
public RadioButton(string text, Action action) public RadioButton(object item, Action action)
{ {
Text = text; Item = item;
Action = action; this.action = action;
Selected = new BindableBool(); Selected = new BindableBool();
} }
public RadioButton(string text) public RadioButton(string item)
: this(text, null) : this(item, null)
{ {
Text = text; Item = item;
Action = null; action = null;
} }
/// <summary> /// <summary>
/// Selects this <see cref="RadioButton"/>. /// Selects this <see cref="RadioButton"/>.
/// </summary> /// </summary>
public void Select() => Selected.Value = true; public void Select()
{
if (!Selected.Value)
{
Selected.Value = true;
action?.Invoke();
}
}
/// <summary> /// <summary>
/// Deselects this <see cref="RadioButton"/>. /// Deselects this <see cref="RadioButton"/>.

View File

@ -12,7 +12,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations
/// </summary> /// </summary>
public class PointVisualisation : Box public class PointVisualisation : Box
{ {
protected PointVisualisation(double startTime) public PointVisualisation(double startTime)
{ {
Origin = Anchor.TopCentre; Origin = Anchor.TopCentre;

View File

@ -74,12 +74,16 @@ namespace osu.Game.Screens.Edit.Compose.Components
{ {
foreach (var o in objects) foreach (var o in objects)
selectionBlueprints.FirstOrDefault(b => b.HitObject == o)?.Select(); selectionBlueprints.FirstOrDefault(b => b.HitObject == o)?.Select();
SelectionChanged?.Invoke(selectedHitObjects);
}; };
selectedHitObjects.ItemsRemoved += objects => selectedHitObjects.ItemsRemoved += objects =>
{ {
foreach (var o in objects) foreach (var o in objects)
selectionBlueprints.FirstOrDefault(b => b.HitObject == o)?.Deselect(); selectionBlueprints.FirstOrDefault(b => b.HitObject == o)?.Deselect();
SelectionChanged?.Invoke(selectedHitObjects);
}; };
} }
@ -332,8 +336,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
selectionHandler.HandleSelected(blueprint); selectionHandler.HandleSelected(blueprint);
selectionBlueprints.ChangeChildDepth(blueprint, 1); selectionBlueprints.ChangeChildDepth(blueprint, 1);
beatmap.SelectedHitObjects.Add(blueprint.HitObject); beatmap.SelectedHitObjects.Add(blueprint.HitObject);
SelectionChanged?.Invoke(selectionHandler.SelectedHitObjects);
} }
private void onBlueprintDeselected(SelectionBlueprint blueprint) private void onBlueprintDeselected(SelectionBlueprint blueprint)
@ -341,8 +343,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
selectionHandler.HandleDeselected(blueprint); selectionHandler.HandleDeselected(blueprint);
selectionBlueprints.ChangeChildDepth(blueprint, 0); selectionBlueprints.ChangeChildDepth(blueprint, 0);
beatmap.SelectedHitObjects.Remove(blueprint.HitObject); beatmap.SelectedHitObjects.Remove(blueprint.HitObject);
SelectionChanged?.Invoke(selectionHandler.SelectedHitObjects);
} }
#endregion #endregion

View File

@ -16,7 +16,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
{ {
} }
protected override void CreateContent(Vector2 startPosition) protected override void CreateContent()
{ {
const float crosshair_thickness = 1; const float crosshair_thickness = 1;
const float crosshair_max_size = 10; const float crosshair_max_size = 10;
@ -26,7 +26,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
new Box new Box
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Position = startPosition, Position = StartPosition,
Width = crosshair_thickness, Width = crosshair_thickness,
EdgeSmoothness = new Vector2(1), EdgeSmoothness = new Vector2(1),
Height = Math.Min(crosshair_max_size, DistanceSpacing * 2), Height = Math.Min(crosshair_max_size, DistanceSpacing * 2),
@ -34,15 +34,15 @@ namespace osu.Game.Screens.Edit.Compose.Components
new Box new Box
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Position = startPosition, Position = StartPosition,
EdgeSmoothness = new Vector2(1), EdgeSmoothness = new Vector2(1),
Width = Math.Min(crosshair_max_size, DistanceSpacing * 2), Width = Math.Min(crosshair_max_size, DistanceSpacing * 2),
Height = crosshair_thickness, Height = crosshair_thickness,
} }
}); });
float dx = Math.Max(startPosition.X, DrawWidth - startPosition.X); float dx = Math.Max(StartPosition.X, DrawWidth - StartPosition.X);
float dy = Math.Max(startPosition.Y, DrawHeight - startPosition.Y); float dy = Math.Max(StartPosition.Y, DrawHeight - StartPosition.Y);
float maxDistance = new Vector2(dx, dy).Length; float maxDistance = new Vector2(dx, dy).Length;
int requiredCircles = Math.Min(MaxIntervals, (int)(maxDistance / DistanceSpacing)); int requiredCircles = Math.Min(MaxIntervals, (int)(maxDistance / DistanceSpacing));
@ -53,11 +53,11 @@ namespace osu.Game.Screens.Edit.Compose.Components
AddInternal(new CircularProgress AddInternal(new CircularProgress
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Position = startPosition, Position = StartPosition,
Current = { Value = 1 }, Current = { Value = 1 },
Size = new Vector2(radius), Size = new Vector2(radius),
InnerRadius = 4 * 1f / radius, InnerRadius = 4 * 1f / radius,
Colour = GetColourForBeatIndex(i) Colour = GetColourForIndexFromPlacement(i)
}); });
} }
} }

View File

@ -1,6 +1,7 @@
// 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 osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Caching; using osu.Framework.Caching;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -106,7 +107,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
if (!gridCache.IsValid) if (!gridCache.IsValid)
{ {
ClearInternal(); ClearInternal();
CreateContent(StartPosition); CreateContent();
gridCache.Validate(); gridCache.Validate();
} }
} }
@ -114,7 +115,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
/// <summary> /// <summary>
/// Creates the content which visualises the grid ticks. /// Creates the content which visualises the grid ticks.
/// </summary> /// </summary>
protected abstract void CreateContent(Vector2 startPosition); protected abstract void CreateContent();
/// <summary> /// <summary>
/// Snaps a position to this grid. /// Snaps a position to this grid.
@ -126,25 +127,17 @@ namespace osu.Game.Screens.Edit.Compose.Components
/// <summary> /// <summary>
/// Retrieves the applicable colour for a beat index. /// Retrieves the applicable colour for a beat index.
/// </summary> /// </summary>
/// <param name="index">The 0-based beat index.</param> /// <param name="placementIndex">The 0-based beat index from the point of placement.</param>
/// <returns>The applicable colour.</returns> /// <returns>The applicable colour.</returns>
protected ColourInfo GetColourForBeatIndex(int index) protected ColourInfo GetColourForIndexFromPlacement(int placementIndex)
{ {
int beat = (index + 1) % beatDivisor.Value; var timingPoint = beatmap.ControlPointInfo.TimingPointAt(StartTime);
ColourInfo colour = Colours.Gray5; var beatLength = timingPoint.BeatLength / beatDivisor.Value;
var beatIndex = (int)Math.Round((StartTime - timingPoint.Time) / beatLength);
for (int i = 0; i < BindableBeatDivisor.VALID_DIVISORS.Length; i++) var colour = BindableBeatDivisor.GetColourFor(BindableBeatDivisor.GetDivisorForBeatIndex(beatIndex + placementIndex + 1, beatDivisor.Value), Colours);
{
int divisor = BindableBeatDivisor.VALID_DIVISORS[i];
if ((beat * divisor) % beatDivisor.Value == 0) int repeatIndex = placementIndex / beatDivisor.Value;
{
colour = BindableBeatDivisor.GetColourFor(divisor, Colours);
break;
}
}
int repeatIndex = index / beatDivisor.Value;
return colour.MultiplyAlpha(0.5f / (repeatIndex + 1)); return colour.MultiplyAlpha(0.5f / (repeatIndex + 1));
} }
} }

View File

@ -30,7 +30,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
{ {
ZoomDuration = 200; ZoomDuration = 200;
ZoomEasing = Easing.OutQuint; ZoomEasing = Easing.OutQuint;
Zoom = 10;
ScrollbarVisible = false; ScrollbarVisible = false;
} }
@ -61,9 +60,15 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
{ {
waveform.Waveform = b.NewValue.Waveform; waveform.Waveform = b.NewValue.Waveform;
track = b.NewValue.Track; track = b.NewValue.Track;
MinZoom = getZoomLevelForVisibleMilliseconds(10000);
MaxZoom = getZoomLevelForVisibleMilliseconds(500);
Zoom = getZoomLevelForVisibleMilliseconds(2000);
}, true); }, true);
} }
private float getZoomLevelForVisibleMilliseconds(double milliseconds) => (float)(track.Length / milliseconds);
/// <summary> /// <summary>
/// The timeline's scroll position in the last frame. /// The timeline's scroll position in the last frame.
/// </summary> /// </summary>
@ -177,7 +182,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
public (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time) public (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time)
{ {
var targetTime = (position.X / Content.DrawWidth) * track.Length; var targetTime = (position.X / Content.DrawWidth) * track.Length;
return (position, beatSnapProvider.SnapTime(targetTime, targetTime)); return (position, beatSnapProvider.SnapTime(targetTime));
} }
public float GetBeatSnapDistanceAt(double referenceTime) => throw new NotImplementedException(); public float GetBeatSnapDistanceAt(double referenceTime) => throw new NotImplementedException();

View File

@ -0,0 +1,90 @@
// 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 System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations;
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
{
public class TimelineTickDisplay : TimelinePart
{
[Resolved]
private EditorBeatmap beatmap { get; set; }
[Resolved]
private Bindable<WorkingBeatmap> working { get; set; }
[Resolved]
private BindableBeatDivisor beatDivisor { get; set; }
[Resolved]
private OsuColour colours { get; set; }
public TimelineTickDisplay()
{
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load()
{
beatDivisor.BindValueChanged(_ => createLines(), true);
}
private void createLines()
{
Clear();
for (var i = 0; i < beatmap.ControlPointInfo.TimingPoints.Count; i++)
{
var point = beatmap.ControlPointInfo.TimingPoints[i];
var until = beatmap.ControlPointInfo.TimingPoints.Count < i + 1 ? beatmap.ControlPointInfo.TimingPoints[i + 1].Time : working.Value.Track.Length;
int beat = 0;
for (double t = point.Time; t < until; t += point.BeatLength / beatDivisor.Value)
{
var indexInBeat = beat % beatDivisor.Value;
if (indexInBeat == 0)
{
Add(new PointVisualisation(t)
{
Colour = BindableBeatDivisor.GetColourFor(1, colours),
Origin = Anchor.TopCentre,
});
}
else
{
var divisor = BindableBeatDivisor.GetDivisorForBeatIndex(beat, beatDivisor.Value);
var colour = BindableBeatDivisor.GetColourFor(divisor, colours);
var height = 0.1f - (float)divisor / BindableBeatDivisor.VALID_DIVISORS.Last() * 0.08f;
Add(new PointVisualisation(t)
{
Colour = colour,
Height = height,
Origin = Anchor.TopCentre,
});
Add(new PointVisualisation(t)
{
Colour = colour,
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomCentre,
Height = height,
});
}
beat++;
}
}
}
}
}

View File

@ -36,12 +36,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
base.Content.Add(zoomedContent = new Container { RelativeSizeAxes = Axes.Y }); base.Content.Add(zoomedContent = new Container { RelativeSizeAxes = Axes.Y });
} }
private int minZoom = 1; private float minZoom = 1;
/// <summary> /// <summary>
/// The minimum zoom level allowed. /// The minimum zoom level allowed.
/// </summary> /// </summary>
public int MinZoom public float MinZoom
{ {
get => minZoom; get => minZoom;
set set
@ -56,12 +56,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
} }
} }
private int maxZoom = 60; private float maxZoom = 60;
/// <summary> /// <summary>
/// The maximum zoom level allowed. /// The maximum zoom level allowed.
/// </summary> /// </summary>
public int MaxZoom public float MaxZoom
{ {
get => maxZoom; get => maxZoom;
set set

View File

@ -348,7 +348,7 @@ namespace osu.Game.Screens.Edit
beatmapManager.Export(Beatmap.Value.BeatmapSetInfo); beatmapManager.Export(Beatmap.Value.BeatmapSetInfo);
} }
public double SnapTime(double referenceTime, double duration) => editorBeatmap.SnapTime(referenceTime, duration); public double SnapTime(double time, double? referenceTime) => editorBeatmap.SnapTime(time, referenceTime);
public double GetBeatLengthAtTime(double referenceTime) => editorBeatmap.GetBeatLengthAtTime(referenceTime); public double GetBeatLengthAtTime(double referenceTime) => editorBeatmap.GetBeatLengthAtTime(referenceTime);

View File

@ -128,12 +128,12 @@ namespace osu.Game.Screens.Edit
return list.Count - 1; return list.Count - 1;
} }
public double SnapTime(double referenceTime, double duration) public double SnapTime(double time, double? referenceTime)
{ {
double beatLength = GetBeatLengthAtTime(referenceTime); var timingPoint = ControlPointInfo.TimingPointAt(referenceTime ?? time);
var beatLength = timingPoint.BeatLength / BeatDivisor;
// A 1ms offset prevents rounding errors due to minute variations in duration return timingPoint.Time + (int)Math.Round((time - timingPoint.Time) / beatLength, MidpointRounding.AwayFromZero) * beatLength;
return (int)((duration + 1) / beatLength) * beatLength;
} }
public double GetBeatLengthAtTime(double referenceTime) => ControlPointInfo.TimingPointAt(referenceTime).BeatLength / BeatDivisor; public double GetBeatLengthAtTime(double referenceTime) => ControlPointInfo.TimingPointAt(referenceTime).BeatLength / BeatDivisor;

View File

@ -102,7 +102,11 @@ namespace osu.Game.Screens.Edit
LoadComponentAsync(new TimelineArea LoadComponentAsync(new TimelineArea
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Child = CreateTimelineContent() Children = new[]
{
new TimelineTickDisplay(),
CreateTimelineContent(),
}
}, timelineContainer.Add); }, timelineContainer.Add);
}); });
} }

View File

@ -28,7 +28,7 @@ namespace osu.Game.Screens.Multi.Match.Components
protected override IEnumerable<LeaderboardScoreStatistic> GetStatistics(ScoreInfo model) => new[] protected override IEnumerable<LeaderboardScoreStatistic> GetStatistics(ScoreInfo model) => new[]
{ {
new LeaderboardScoreStatistic(FontAwesome.Solid.Crosshairs, "Accuracy", string.Format(model.Accuracy % 1 == 0 ? @"{0:P0}" : @"{0:P2}", model.Accuracy)), new LeaderboardScoreStatistic(FontAwesome.Solid.Crosshairs, "Accuracy", string.Format(model.Accuracy % 1 == 0 ? @"{0:0%}" : @"{0:0.00%}", model.Accuracy)),
new LeaderboardScoreStatistic(FontAwesome.Solid.Sync, "Total Attempts", score.TotalAttempts.ToString()), new LeaderboardScoreStatistic(FontAwesome.Solid.Sync, "Total Attempts", score.TotalAttempts.ToString()),
new LeaderboardScoreStatistic(FontAwesome.Solid.Check, "Completed Beatmaps", score.CompletedBeatmaps.ToString()), new LeaderboardScoreStatistic(FontAwesome.Solid.Check, "Completed Beatmaps", score.CompletedBeatmaps.ToString()),
}; };

View File

@ -41,7 +41,7 @@ namespace osu.Game.Screens.Play.HUD
} }
} }
private readonly FillFlowContainer<ModIcon> iconsContainer; protected readonly FillFlowContainer<ModIcon> IconsContainer;
private readonly OsuSpriteText unrankedText; private readonly OsuSpriteText unrankedText;
public ModDisplay() public ModDisplay()
@ -50,7 +50,7 @@ namespace osu.Game.Screens.Play.HUD
Children = new Drawable[] Children = new Drawable[]
{ {
iconsContainer = new ReverseChildIDFillFlowContainer<ModIcon> IconsContainer = new ReverseChildIDFillFlowContainer<ModIcon>
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
@ -69,11 +69,11 @@ namespace osu.Game.Screens.Play.HUD
Current.ValueChanged += mods => Current.ValueChanged += mods =>
{ {
iconsContainer.Clear(); IconsContainer.Clear();
foreach (Mod mod in mods.NewValue) foreach (Mod mod in mods.NewValue)
{ {
iconsContainer.Add(new ModIcon(mod) { Scale = new Vector2(0.6f) }); IconsContainer.Add(new ModIcon(mod) { Scale = new Vector2(0.6f) });
} }
if (IsLoaded) if (IsLoaded)
@ -92,7 +92,7 @@ namespace osu.Game.Screens.Play.HUD
base.LoadComplete(); base.LoadComplete();
appearTransform(); appearTransform();
iconsContainer.FadeInFromZero(fade_duration, Easing.OutQuint); IconsContainer.FadeInFromZero(fade_duration, Easing.OutQuint);
} }
private void appearTransform() private void appearTransform()
@ -104,17 +104,17 @@ namespace osu.Game.Screens.Play.HUD
expand(); expand();
using (iconsContainer.BeginDelayedSequence(1200)) using (IconsContainer.BeginDelayedSequence(1200))
contract(); contract();
} }
private void expand() private void expand()
{ {
if (AllowExpand) if (AllowExpand)
iconsContainer.TransformSpacingTo(new Vector2(5, 0), 500, Easing.OutQuint); IconsContainer.TransformSpacingTo(new Vector2(5, 0), 500, Easing.OutQuint);
} }
private void contract() => iconsContainer.TransformSpacingTo(new Vector2(-25, 0), 500, Easing.OutQuint); private void contract() => IconsContainer.TransformSpacingTo(new Vector2(-25, 0), 500, Easing.OutQuint);
protected override bool OnHover(HoverEvent e) protected override bool OnHover(HoverEvent e)
{ {

View File

@ -27,12 +27,18 @@ namespace osu.Game.Screens.Play.PlayerSettings
{ {
Text = "Background dim:" Text = "Background dim:"
}, },
dimSliderBar = new PlayerSliderBar<double>(), dimSliderBar = new PlayerSliderBar<double>
{
DisplayAsPercentage = true
},
new OsuSpriteText new OsuSpriteText
{ {
Text = "Background blur:" Text = "Background blur:"
}, },
blurSliderBar = new PlayerSliderBar<double>(), blurSliderBar = new PlayerSliderBar<double>
{
DisplayAsPercentage = true
},
new OsuSpriteText new OsuSpriteText
{ {
Text = "Toggles:" Text = "Toggles:"

Some files were not shown because too many files have changed in this diff Show More