1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-06 09:07:25 +08:00

Merge branch 'master' into profile-scores-update-new

This commit is contained in:
Dan Balasescu 2020-01-29 19:40:44 +09:00 committed by GitHub
commit 30da9981d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
61 changed files with 1418 additions and 667 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_constructors = false:none
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_properties = true:warning
csharp_style_expression_bodied_local_functions = true:silent

View File

@ -118,17 +118,19 @@ namespace osu.Game.Tests.Editor
[Test]
public void TestGetSnappedDurationFromDistance()
{
assertSnappedDuration(50, 0);
assertSnappedDuration(0, 0);
assertSnappedDuration(50, 1000);
assertSnappedDuration(100, 1000);
assertSnappedDuration(150, 1000);
assertSnappedDuration(150, 2000);
assertSnappedDuration(200, 2000);
assertSnappedDuration(250, 2000);
assertSnappedDuration(250, 3000);
AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2);
assertSnappedDuration(0, 0);
assertSnappedDuration(50, 0);
assertSnappedDuration(100, 0);
assertSnappedDuration(150, 0);
assertSnappedDuration(100, 1000);
assertSnappedDuration(150, 1000);
assertSnappedDuration(200, 1000);
assertSnappedDuration(250, 1000);
@ -139,8 +141,8 @@ namespace osu.Game.Tests.Editor
});
assertSnappedDuration(50, 0);
assertSnappedDuration(100, 0);
assertSnappedDuration(150, 0);
assertSnappedDuration(100, 500);
assertSnappedDuration(150, 500);
assertSnappedDuration(200, 500);
assertSnappedDuration(250, 500);
assertSnappedDuration(400, 1000);
@ -149,17 +151,17 @@ namespace osu.Game.Tests.Editor
[Test]
public void GetSnappedDistanceFromDistance()
{
assertSnappedDistance(50, 0);
assertSnappedDistance(50, 100);
assertSnappedDistance(100, 100);
assertSnappedDistance(150, 100);
assertSnappedDistance(150, 200);
assertSnappedDistance(200, 200);
assertSnappedDistance(250, 200);
assertSnappedDistance(250, 300);
AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2);
assertSnappedDistance(50, 0);
assertSnappedDistance(100, 0);
assertSnappedDistance(150, 0);
assertSnappedDistance(100, 200);
assertSnappedDistance(150, 200);
assertSnappedDistance(200, 200);
assertSnappedDistance(250, 200);
@ -170,8 +172,8 @@ namespace osu.Game.Tests.Editor
});
assertSnappedDistance(50, 0);
assertSnappedDistance(100, 0);
assertSnappedDistance(150, 0);
assertSnappedDistance(100, 200);
assertSnappedDistance(150, 200);
assertSnappedDistance(200, 200);
assertSnappedDistance(250, 200);
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
{
Origin = Anchor.Centre,
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
{
Origin = Anchor.Centre,
Size = new Vector2(5, 10),
Position = new Vector2(s, startPosition.Y),
Colour = GetColourForBeatIndex(beatIndex)
Position = new Vector2(s, StartPosition.Y),
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
{
Origin = Anchor.Centre,
Size = new Vector2(5, 10),
Position = new Vector2(s, startPosition.Y),
Colour = GetColourForBeatIndex(beatIndex)
Position = new Vector2(s, StartPosition.Y),
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
{
Origin = Anchor.Centre,
Size = new Vector2(10, 5),
Position = new Vector2(startPosition.X, s),
Colour = GetColourForBeatIndex(beatIndex)
Position = new Vector2(StartPosition.X, s),
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
{
Origin = Anchor.Centre,
Size = new Vector2(10, 5),
Position = new Vector2(startPosition.X, s),
Colour = GetColourForBeatIndex(beatIndex)
Position = new Vector2(StartPosition.X, s),
Colour = GetColourForIndexFromPlacement(indexFromPlacement)
});
}
}

View File

@ -1,146 +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.
using System;
using System.Collections.Generic;
using NUnit.Framework;
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
{
[TestFixture]
public class TestSceneTimelineBlueprintContainer : EditorClockTestScene
public class TestSceneTimelineBlueprintContainer : TimelineTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
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;
}
}
public override Drawable CreateTestComponent() => new TimelineBlueprintContainer();
}
}

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,111 @@
// 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]
[Ignore("will be fixed soon")]
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 osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
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.Overlays.Mods;
using osu.Game.Screens;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Play;
using osu.Game.Screens.Select;
using osu.Game.Tests.Beatmaps.IO;
using osuTK;
using osuTK.Graphics;
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 GameHost host;
private TestOsuGame game;
private Vector2 backButtonPosition => Game.ToScreenSpace(new Vector2(click_padding, Game.LayoutRectangle.Bottom - click_padding));
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));
[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();
}
private Vector2 optionsButtonPosition => Game.ToScreenSpace(new Vector2(click_padding, click_padding));
[Test]
public void TestExitSongSelectWithEscape()
@ -98,21 +46,21 @@ namespace osu.Game.Tests.Visual.Menus
{
Player player = null;
WorkingBeatmap beatmap() => game.Beatmap.Value;
WorkingBeatmap beatmap() => Game.Beatmap.Value;
Track track() => beatmap().Track;
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)
AddStep("pause", () => game.Dependencies.Get<MusicController>().Stop());
AddStep("pause", () => Game.Dependencies.Get<MusicController>().Stop());
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 track stop", () => !track().IsRunning);
@ -135,7 +83,7 @@ namespace osu.Game.Tests.Visual.Menus
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.
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));
AddUntilStep("Overlay was hidden", () => songSelect.ModSelectOverlay.State.Value == Visibility.Hidden);
@ -159,20 +107,20 @@ namespace osu.Game.Tests.Visual.Menus
[Test]
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("Move mouse to options overlay", () => InputManager.MoveMouseTo(optionsButtonPosition));
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));
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)
{
Screen screen = null;
AddStep("Push new screen", () => game.ScreenStack.Push(screen = newScreen()));
AddUntilStep("Wait for new screen", () => game.ScreenStack.CurrentScreen == screen && screen.IsLoaded);
AddStep("Push new screen", () => Game.ScreenStack.Push(screen = newScreen()));
AddUntilStep("Wait for new screen", () => Game.ScreenStack.CurrentScreen == screen && screen.IsLoaded);
}
private void pushEscape() =>
@ -181,64 +129,25 @@ namespace osu.Game.Tests.Visual.Menus
private void exitViaEscapeAndConfirm()
{
pushEscape();
confirmAtMainMenu();
ConfirmAtMainMenu();
}
private void exitViaBackButtonAndConfirm()
{
AddStep("Move mouse to backButton", () => InputManager.MoveMouseTo(backButtonPosition));
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)
{
InputManager.PressKey(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
{
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

@ -8,6 +8,8 @@ using osu.Game.Online.API.Requests;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Game.Overlays.Comments;
using osu.Game.Overlays;
using osu.Framework.Allocation;
namespace osu.Game.Tests.Visual.Online
{
@ -28,6 +30,9 @@ namespace osu.Game.Tests.Visual.Online
protected override bool UseOnlineAPI => true;
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
public TestSceneCommentsContainer()
{
BasicScrollContainer scroll;

View File

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

View File

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

View File

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

View File

@ -14,6 +14,7 @@ using osu.Framework.Extensions;
using osu.Framework.Utils;
using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Overlays;
@ -25,6 +26,7 @@ using osu.Game.Rulesets.Taiko;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Carousel;
using osu.Game.Screens.Select.Filter;
using osuTK.Input;
namespace osu.Game.Tests.Visual.SongSelect
{
@ -95,6 +97,127 @@ namespace osu.Game.Tests.Visual.SongSelect
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]
public void TestNoFilterOnSimpleResume()
{

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

@ -36,6 +36,11 @@ namespace osu.Game.Graphics.UserInterface
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;
public Color4 AccentColour
@ -169,11 +174,11 @@ namespace osu.Game.Graphics.UserInterface
else
{
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");
}
else
{
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>
{
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;
protected override Dropdown<T> CreateDropdown() => new OsuTabDropdown();
@ -62,21 +78,6 @@ namespace osu.Game.Graphics.UserInterface
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
{
get => strip.Colour;

View File

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

View File

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

View File

@ -4,12 +4,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests.Responses;
@ -27,8 +24,8 @@ namespace osu.Game.Overlays.Changelog
public ChangelogHeader()
{
BreadcrumbControl.AddItem(listing_string);
BreadcrumbControl.Current.ValueChanged += e =>
TabControl.AddItem(listing_string);
TabControl.Current.ValueChanged += e =>
{
if (e.NewValue == listing_string)
ListingSelected?.Invoke();
@ -48,12 +45,12 @@ namespace osu.Game.Overlays.Changelog
private void showBuild(ValueChangedEvent<APIChangelogBuild> e)
{
if (e.OldValue != null)
BreadcrumbControl.RemoveItem(e.OldValue.ToString());
TabControl.RemoveItem(e.OldValue.ToString());
if (e.NewValue != null)
{
BreadcrumbControl.AddItem(e.NewValue.ToString());
BreadcrumbControl.Current.Value = e.NewValue.ToString();
TabControl.AddItem(e.NewValue.ToString());
TabControl.Current.Value = e.NewValue.ToString();
updateCurrentStream();
@ -61,13 +58,13 @@ namespace osu.Game.Overlays.Changelog
}
else
{
BreadcrumbControl.Current.Value = listing_string;
TabControl.Current.Value = listing_string;
Streams.Current.Value = null;
title.Version = null;
}
}
protected override Drawable CreateBackground() => new HeaderBackground();
protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/changelog");
protected override Drawable CreateContent() => new Container
{
@ -95,21 +92,6 @@ namespace osu.Game.Overlays.Changelog
Streams.Current.Value = Streams.Items.FirstOrDefault(s => s.Name == Current.Value.UpdateStream.Name);
}
public class HeaderBackground : Sprite
{
public HeaderBackground()
{
RelativeSizeAxes = Axes.Both;
FillMode = FillMode.Fill;
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
Texture = textures.Get(@"Headers/changelog");
}
}
private class ChangelogHeaderTitle : ScreenTitle
{
public string Version

View File

@ -8,7 +8,6 @@ using osu.Game.Online.API.Requests;
using osu.Framework.Graphics;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Online.API.Requests.Responses;
using System.Threading;
using System.Linq;
@ -27,28 +26,26 @@ namespace osu.Game.Overlays.Comments
[Resolved]
private IAPIProvider api { get; set; }
[Resolved]
private OsuColour colours { get; set; }
private GetCommentsRequest request;
private CancellationTokenSource loadCancellation;
private int currentPage;
private readonly Box background;
private readonly FillFlowContainer content;
private readonly DeletedChildrenPlaceholder deletedChildrenPlaceholder;
private readonly CommentsShowMoreButton moreButton;
private readonly TotalCommentsCounter commentCounter;
private FillFlowContainer content;
private DeletedChildrenPlaceholder deletedChildrenPlaceholder;
private CommentsShowMoreButton moreButton;
private TotalCommentsCounter commentCounter;
public CommentsContainer()
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
AddRangeInternal(new Drawable[]
{
background = new Box
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colourProvider.Background5
},
new FillFlowContainer
{
@ -78,7 +75,7 @@ namespace osu.Game.Overlays.Comments
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.2f)
Colour = colourProvider.Background4
},
new FillFlowContainer
{
@ -113,12 +110,6 @@ namespace osu.Game.Overlays.Comments
});
}
[BackgroundDependencyLoader]
private void load()
{
background.Colour = colours.Gray2;
}
protected override void LoadComplete()
{
Sort.BindValueChanged(_ => refetchComments(), true);

View File

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

View File

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

View File

@ -5,7 +5,6 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Framework.Input.Events;
using osu.Game.Graphics.UserInterface;
@ -45,9 +44,9 @@ namespace osu.Game.Overlays.Comments
}
[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)

View File

@ -56,7 +56,7 @@ namespace osu.Game.Overlays.Comments
public readonly BindableBool Active = new BindableBool();
[Resolved]
private OsuColour colours { get; set; }
private OverlayColourProvider colourProvider { get; set; }
private readonly SpriteText text;
@ -78,7 +78,7 @@ namespace osu.Game.Overlays.Comments
updateBackgroundState();
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);
}

View File

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

View File

@ -1,11 +1,8 @@
// 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.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Graphics.UserInterface;
using System;
@ -23,9 +20,9 @@ namespace osu.Game.Overlays.News
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)
ShowFrontPage?.Invoke();
@ -37,41 +34,26 @@ namespace osu.Game.Overlays.News
private void showPost(ValueChangedEvent<string> e)
{
if (e.OldValue != null)
BreadcrumbControl.RemoveItem(e.OldValue);
TabControl.RemoveItem(e.OldValue);
if (e.NewValue != null)
{
BreadcrumbControl.AddItem(e.NewValue);
BreadcrumbControl.Current.Value = e.NewValue;
TabControl.AddItem(e.NewValue);
TabControl.Current.Value = e.NewValue;
title.IsReadingPost = true;
}
else
{
BreadcrumbControl.Current.Value = front_page_string;
TabControl.Current.Value = front_page_string;
title.IsReadingPost = false;
}
}
protected override Drawable CreateBackground() => new NewsHeaderBackground();
protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/news");
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 const string post_string = "post";

View File

@ -6,7 +6,6 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
using osuTK.Graphics;
@ -15,14 +14,9 @@ namespace osu.Game.Overlays
public abstract class OverlayHeader : Container
{
private readonly Box titleBackground;
private readonly Box controlBackground;
private readonly Container background;
private readonly ScreenTitle title;
protected float BackgroundHeight
{
set => background.Height = value;
}
protected readonly FillFlowContainer HeaderInfo;
protected OverlayHeader()
{
@ -36,46 +30,36 @@ namespace osu.Game.Overlays
Direction = FillDirection.Vertical,
Children = new[]
{
background = new Container
{
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
HeaderInfo = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Depth = -float.MaxValue,
Children = new Drawable[]
Children = new[]
{
controlBackground = new Box
CreateBackground(),
new Container
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Gray,
RelativeSizeAxes = Axes.X,
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()
@ -88,16 +72,14 @@ namespace osu.Game.Overlays
{
titleBackground.Colour = colourProvider.Dark5;
title.AccentColour = colourProvider.Highlight1;
controlBackground.Colour = colourProvider.Dark4;
}
protected abstract Drawable CreateBackground();
[NotNull]
protected virtual Drawable CreateContent() => Drawable.Empty();
[NotNull]
protected virtual Drawable CreateContent() => new Container();
protected virtual Drawable CreateBackground() => Drawable.Empty();
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
{
public abstract class OverlayTabControl<T> : TabControl<T>
public abstract class OverlayTabControl<T> : OsuTabControl<T>
{
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
{
set => bar.Height = value;
}
public override Color4 AccentColour
{
get => base.AccentColour;
set
{
base.AccentColour = value;
bar.Colour = value;
}
}
protected OverlayTabControl()
{
TabContainer.Masking = false;
@ -66,7 +50,7 @@ namespace osu.Game.Overlays
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 OsuSpriteText Text;

View File

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

View File

@ -17,10 +17,34 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
{
Children = new Drawable[]
{
new SettingsSlider<double> { LabelText = "Master", Bindable = audio.Volume, KeyboardStep = 0.01f },
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 },
new SettingsSlider<double> { LabelText = "Music", Bindable = audio.VolumeTrack, KeyboardStep = 0.01f },
new SettingsSlider<double>
{
LabelText = "Master",
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",
Bindable = config.GetBindable<double>(OsuSetting.DimLevel),
KeyboardStep = 0.01f
KeyboardStep = 0.01f,
DisplayAsPercentage = true
},
new SettingsSlider<double>
{
LabelText = "Background blur",
Bindable = config.GetBindable<double>(OsuSetting.BlurLevel),
KeyboardStep = 0.01f
KeyboardStep = 0.01f,
DisplayAsPercentage = true
},
new SettingsCheckbox
{

View File

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

View File

@ -3,6 +3,7 @@
using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Settings
@ -22,16 +23,32 @@ namespace osu.Game.Overlays.Settings
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
{
get => ((TSlider)Control).TransferValueOnCommit;
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
{
get => ((TSlider)Control).KeyboardStep;
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.
// See the LICENCE file in the repository root for full licence text.
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osuTK;
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]
private void load(OverlayColourProvider colourProvider)
{
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()
{
@ -34,10 +62,7 @@ namespace osu.Game.Overlays
Height = 35;
}
protected override TabItem<string> CreateTabItem(string value) => new OverlayHeaderTabItem(value)
{
AccentColour = AccentColour,
};
protected override TabItem<T> CreateTabItem(T value) => new OverlayHeaderTabItem(value);
protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
{
@ -49,10 +74,10 @@ namespace osu.Game.Overlays
private class OverlayHeaderTabItem : OverlayTabItem
{
public OverlayHeaderTabItem(string value)
public OverlayHeaderTabItem(T value)
: base(value)
{
Text.Text = value;
Text.Text = value.ToString().ToLower();
Text.Font = OsuFont.GetFont(size: 14);
Bar.ExpandedSize = 5;
}

View File

@ -9,6 +9,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Framework.Input.Events;
using osu.Framework.Logging;
using osu.Framework.Threading;
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.Components;
using osuTK;
using Key = osuTK.Input.Key;
namespace osu.Game.Rulesets.Edit
{
@ -58,6 +60,8 @@ namespace osu.Game.Rulesets.Edit
private InputManager inputManager;
private RadioButtonCollection toolboxCollection;
protected HitObjectComposer(Ruleset ruleset)
{
Ruleset = ruleset;
@ -100,7 +104,6 @@ namespace osu.Game.Rulesets.Edit
layerContainers.Add(layerBelowRuleset);
layerContainers.Add(layerAboveRuleset);
RadioButtonCollection toolboxCollection;
InternalChild = new GridContainer
{
RelativeSizeAxes = Axes.Both,
@ -137,16 +140,32 @@ namespace osu.Game.Rulesets.Edit
}
};
toolboxCollection.Items =
CompositionTools.Select(t => new RadioButton(t.Name, () => selectTool(t)))
.Prepend(new RadioButton("Select", () => selectTool(null)))
.ToList();
toolboxCollection.Items = CompositionTools
.Prepend(new SelectTool())
.Select(t => new RadioButton(t.Name, () => toolSelected(t)))
.ToList();
toolboxCollection.Items[0].Select();
setSelectTool();
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()
{
base.LoadComplete();
@ -181,20 +200,30 @@ namespace osu.Game.Rulesets.Edit
{
var hitObjects = selectedHitObjects.ToArray();
if (!hitObjects.Any())
distanceSnapGridContainer.Hide();
else
if (hitObjects.Any())
{
// ensure in selection mode if a selection is made.
setSelectTool();
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;
if (tool == null)
if (tool is SelectTool)
distanceSnapGridContainer.Hide();
else
{
EditorBeatmap.SelectedHitObjects.Clear();
showGridFor(Enumerable.Empty<HitObject>());
}
}
private void showGridFor(IEnumerable<HitObject> selectedHitObjects)
@ -275,10 +304,10 @@ namespace osu.Game.Rulesets.Edit
}
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)
=> DurationToDistance(referenceTime, beatSnapProvider.SnapTime(referenceTime, DistanceToDuration(referenceTime, distance)));
=> DurationToDistance(referenceTime, beatSnapProvider.SnapTime(DistanceToDuration(referenceTime, distance), referenceTime));
protected override void Dispose(bool isDisposing)
{

View File

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

@ -52,30 +52,52 @@ namespace osu.Game.Screens.Edit
{
switch (beatDivisor)
{
case 1:
return Color4.White;
case 2:
return colours.BlueLight;
return colours.Red;
case 4:
return colours.Blue;
case 8:
return colours.BlueDarker;
return colours.Yellow;
case 16:
return colours.PurpleDark;
case 3:
return colours.YellowLight;
return colours.Purple;
case 6:
return colours.Yellow;
return colours.YellowDark;
case 12:
return colours.YellowDarker;
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.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
@ -37,8 +36,8 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
{
this.button = button;
Text = button.Text;
Action = button.Action;
Text = button.Item.ToString();
Action = button.Select;
RelativeSizeAxes = Axes.X;
@ -100,19 +99,6 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
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
{
Depth = -1,

View File

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

View File

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

View File

@ -74,12 +74,16 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
foreach (var o in objects)
selectionBlueprints.FirstOrDefault(b => b.HitObject == o)?.Select();
SelectionChanged?.Invoke(selectedHitObjects);
};
selectedHitObjects.ItemsRemoved += objects =>
{
foreach (var o in objects)
selectionBlueprints.FirstOrDefault(b => b.HitObject == o)?.Deselect();
SelectionChanged?.Invoke(selectedHitObjects);
};
}
@ -332,8 +336,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
selectionHandler.HandleSelected(blueprint);
selectionBlueprints.ChangeChildDepth(blueprint, 1);
beatmap.SelectedHitObjects.Add(blueprint.HitObject);
SelectionChanged?.Invoke(selectionHandler.SelectedHitObjects);
}
private void onBlueprintDeselected(SelectionBlueprint blueprint)
@ -341,8 +343,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
selectionHandler.HandleDeselected(blueprint);
selectionBlueprints.ChangeChildDepth(blueprint, 0);
beatmap.SelectedHitObjects.Remove(blueprint.HitObject);
SelectionChanged?.Invoke(selectionHandler.SelectedHitObjects);
}
#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_max_size = 10;
@ -26,7 +26,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
new Box
{
Origin = Anchor.Centre,
Position = startPosition,
Position = StartPosition,
Width = crosshair_thickness,
EdgeSmoothness = new Vector2(1),
Height = Math.Min(crosshair_max_size, DistanceSpacing * 2),
@ -34,15 +34,15 @@ namespace osu.Game.Screens.Edit.Compose.Components
new Box
{
Origin = Anchor.Centre,
Position = startPosition,
Position = StartPosition,
EdgeSmoothness = new Vector2(1),
Width = Math.Min(crosshair_max_size, DistanceSpacing * 2),
Height = crosshair_thickness,
}
});
float dx = Math.Max(startPosition.X, DrawWidth - startPosition.X);
float dy = Math.Max(startPosition.Y, DrawHeight - startPosition.Y);
float dx = Math.Max(StartPosition.X, DrawWidth - StartPosition.X);
float dy = Math.Max(StartPosition.Y, DrawHeight - StartPosition.Y);
float maxDistance = new Vector2(dx, dy).Length;
int requiredCircles = Math.Min(MaxIntervals, (int)(maxDistance / DistanceSpacing));
@ -53,11 +53,11 @@ namespace osu.Game.Screens.Edit.Compose.Components
AddInternal(new CircularProgress
{
Origin = Anchor.Centre,
Position = startPosition,
Position = StartPosition,
Current = { Value = 1 },
Size = new Vector2(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.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Allocation;
using osu.Framework.Caching;
using osu.Framework.Graphics;
@ -106,7 +107,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
if (!gridCache.IsValid)
{
ClearInternal();
CreateContent(StartPosition);
CreateContent();
gridCache.Validate();
}
}
@ -114,7 +115,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
/// <summary>
/// Creates the content which visualises the grid ticks.
/// </summary>
protected abstract void CreateContent(Vector2 startPosition);
protected abstract void CreateContent();
/// <summary>
/// Snaps a position to this grid.
@ -126,25 +127,17 @@ namespace osu.Game.Screens.Edit.Compose.Components
/// <summary>
/// Retrieves the applicable colour for a beat index.
/// </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>
protected ColourInfo GetColourForBeatIndex(int index)
protected ColourInfo GetColourForIndexFromPlacement(int placementIndex)
{
int beat = (index + 1) % beatDivisor.Value;
ColourInfo colour = Colours.Gray5;
var timingPoint = beatmap.ControlPointInfo.TimingPointAt(StartTime);
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++)
{
int divisor = BindableBeatDivisor.VALID_DIVISORS[i];
var colour = BindableBeatDivisor.GetColourFor(BindableBeatDivisor.GetDivisorForBeatIndex(beatIndex + placementIndex + 1, beatDivisor.Value), Colours);
if ((beat * divisor) % beatDivisor.Value == 0)
{
colour = BindableBeatDivisor.GetColourFor(divisor, Colours);
break;
}
}
int repeatIndex = index / beatDivisor.Value;
int repeatIndex = placementIndex / beatDivisor.Value;
return colour.MultiplyAlpha(0.5f / (repeatIndex + 1));
}
}

View File

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

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 });
}
private int minZoom = 1;
private float minZoom = 1;
/// <summary>
/// The minimum zoom level allowed.
/// </summary>
public int MinZoom
public float MinZoom
{
get => minZoom;
set
@ -56,12 +56,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
}
}
private int maxZoom = 60;
private float maxZoom = 60;
/// <summary>
/// The maximum zoom level allowed.
/// </summary>
public int MaxZoom
public float MaxZoom
{
get => maxZoom;
set

View File

@ -348,7 +348,7 @@ namespace osu.Game.Screens.Edit
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);

View File

@ -128,12 +128,12 @@ namespace osu.Game.Screens.Edit
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 (int)((duration + 1) / beatLength) * beatLength;
return timingPoint.Time + (int)Math.Round((time - timingPoint.Time) / beatLength, MidpointRounding.AwayFromZero) * beatLength;
}
public double GetBeatLengthAtTime(double referenceTime) => ControlPointInfo.TimingPointAt(referenceTime).BeatLength / BeatDivisor;

View File

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

View File

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

View File

@ -56,6 +56,9 @@ namespace osu.Game.Screens.Select
public override bool HandleNonPositionalInput => AllowSelection;
public override bool HandlePositionalInput => AllowSelection;
public override bool PropagatePositionalInputSubTree => AllowSelection;
public override bool PropagateNonPositionalInputSubTree => AllowSelection;
/// <summary>
/// Whether carousel items have completed asynchronously loaded.
/// </summary>
@ -65,6 +68,7 @@ namespace osu.Game.Screens.Select
private IEnumerable<CarouselBeatmapSet> beatmapSets => root.Children.OfType<CarouselBeatmapSet>();
// todo: only used for testing, maybe remove.
public IEnumerable<BeatmapSetInfo> BeatmapSets
{
get => beatmapSets.Select(g => g.BeatmapSet);
@ -130,8 +134,11 @@ namespace osu.Game.Screens.Select
};
}
[Resolved]
private BeatmapManager beatmaps { get; set; }
[BackgroundDependencyLoader(permitNulls: true)]
private void load(OsuConfigManager config, BeatmapManager beatmaps)
private void load(OsuConfigManager config)
{
config.BindWith(OsuSetting.RandomSelectAlgorithm, RandomAlgorithm);
config.BindWith(OsuSetting.SongSelectRightMouseScroll, RightClickScrollingEnabled);
@ -139,6 +146,11 @@ namespace osu.Game.Screens.Select
RightClickScrollingEnabled.ValueChanged += enabled => scroll.RightMouseScrollbar = enabled.NewValue;
RightClickScrollingEnabled.TriggerChange();
beatmaps.ItemAdded += beatmapAdded;
beatmaps.ItemRemoved += beatmapRemoved;
beatmaps.BeatmapHidden += beatmapHidden;
beatmaps.BeatmapRestored += beatmapRestored;
loadBeatmapSets(beatmaps.GetAllUsableBeatmapSetsEnumerable());
}
@ -449,8 +461,6 @@ namespace osu.Game.Screens.Select
return true;
}
protected override bool ReceivePositionalInputAtSubTree(Vector2 screenSpacePos) => ReceivePositionalInputAt(screenSpacePos);
protected override void Update()
{
base.Update();
@ -534,11 +544,27 @@ namespace osu.Game.Screens.Select
{
base.Dispose(isDisposing);
if (beatmaps != null)
{
beatmaps.ItemAdded -= beatmapAdded;
beatmaps.ItemRemoved -= beatmapRemoved;
beatmaps.BeatmapHidden -= beatmapHidden;
beatmaps.BeatmapRestored -= beatmapRestored;
}
// aggressively dispose "off-screen" items to reduce GC pressure.
foreach (var i in Items)
i.Dispose();
}
private void beatmapRemoved(BeatmapSetInfo item) => RemoveBeatmapSet(item);
private void beatmapAdded(BeatmapSetInfo item) => UpdateBeatmapSet(item);
private void beatmapRestored(BeatmapInfo b) => UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID));
private void beatmapHidden(BeatmapInfo b) => UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID));
private CarouselBeatmapSet createCarouselSet(BeatmapSetInfo beatmapSet)
{
if (beatmapSet.Beatmaps.All(b => b.Hidden))

View File

@ -117,9 +117,20 @@ namespace osu.Game.Screens.Select.Details
mod.ApplyToDifficulty(adjustedDifficulty);
}
// Account for mania differences
firstValue.Title = (Beatmap?.Ruleset?.ID ?? 0) == 3 ? "Key Amount" : "Circle Size";
firstValue.Value = (baseDifficulty?.CircleSize ?? 0, adjustedDifficulty?.CircleSize);
switch (Beatmap?.Ruleset?.ID ?? 0)
{
case 3:
// Account for mania differences locally for now
// Eventually this should be handled in a more modular way, allowing rulesets to return arbitrary difficulty attributes
firstValue.Title = "Key Count";
firstValue.Value = (baseDifficulty?.CircleSize ?? 0, null);
break;
default:
firstValue.Title = "Circle Size";
firstValue.Value = (baseDifficulty?.CircleSize ?? 0, adjustedDifficulty?.CircleSize);
break;
}
starDifficulty.Value = ((float)(Beatmap?.StarDifficulty ?? 0), null);

View File

@ -72,7 +72,9 @@ namespace osu.Game.Screens.Select
private BeatmapInfoWedge beatmapInfoWedge;
private DialogOverlay dialogOverlay;
private BeatmapManager beatmaps;
[Resolved]
private BeatmapManager beatmaps { get; set; }
protected ModSelectOverlay ModSelect { get; private set; }
@ -89,7 +91,7 @@ namespace osu.Game.Screens.Select
private MusicController music { get; set; }
[BackgroundDependencyLoader(true)]
private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuColour colours, SkinManager skins, ScoreManager scores)
private void load(AudioManager audio, DialogOverlay dialog, OsuColour colours, SkinManager skins, ScoreManager scores)
{
// initial value transfer is required for FilterControl (it uses our re-cached bindables in its async load for the initial filter).
transferRulesetValue();
@ -247,14 +249,6 @@ namespace osu.Game.Screens.Select
BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.Solid.Trash, colours.Pink, () => delete(Beatmap.Value.BeatmapSetInfo), Key.Number3);
}
if (this.beatmaps == null)
this.beatmaps = beatmaps;
this.beatmaps.ItemAdded += onBeatmapSetAdded;
this.beatmaps.ItemRemoved += onBeatmapSetRemoved;
this.beatmaps.BeatmapHidden += onBeatmapHidden;
this.beatmaps.BeatmapRestored += onBeatmapRestored;
dialogOverlay = dialog;
sampleChangeDifficulty = audio.Samples.Get(@"SongSelect/select-difficulty");
@ -563,14 +557,6 @@ namespace osu.Game.Screens.Select
base.Dispose(isDisposing);
decoupledRuleset.UnbindAll();
if (beatmaps != null)
{
beatmaps.ItemAdded -= onBeatmapSetAdded;
beatmaps.ItemRemoved -= onBeatmapSetRemoved;
beatmaps.BeatmapHidden -= onBeatmapHidden;
beatmaps.BeatmapRestored -= onBeatmapRestored;
}
}
/// <summary>
@ -617,11 +603,6 @@ namespace osu.Game.Screens.Select
lastTrack.SetTarget(track);
}
private void onBeatmapSetAdded(BeatmapSetInfo s) => Carousel.UpdateBeatmapSet(s);
private void onBeatmapSetRemoved(BeatmapSetInfo s) => Carousel.RemoveBeatmapSet(s);
private void onBeatmapRestored(BeatmapInfo b) => Carousel.UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID));
private void onBeatmapHidden(BeatmapInfo b) => Carousel.UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID));
private void carouselBeatmapsLoaded()
{
bindBindables();

View File

@ -8,6 +8,7 @@ using osu.Framework.Testing.Input;
using osu.Game.Graphics.Cursor;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Input.Bindings;
using osuTK;
using osuTK.Graphics;
@ -30,7 +31,11 @@ namespace osu.Game.Tests.Visual
InputManager = new ManualInputManager
{
UseParentInput = true,
Child = content = new MenuCursorContainer { RelativeSizeAxes = Axes.Both },
Child = new GlobalActionContainer(null)
{
RelativeSizeAxes = Axes.Both,
Child = content = new MenuCursorContainer { RelativeSizeAxes = Axes.Both }
},
},
new Container
{

View File

@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual
protected new OsuScreenDependencies Dependencies { get; private set; }
private readonly Lazy<Storage> localStorage;
private Lazy<Storage> localStorage;
protected Storage LocalStorage => localStorage.Value;
private readonly Lazy<DatabaseContextFactory> contextFactory;
@ -91,7 +91,7 @@ namespace osu.Game.Tests.Visual
protected OsuTestScene()
{
localStorage = new Lazy<Storage>(() => new NativeStorage($"{GetType().Name}-{Guid.NewGuid()}"));
RecycleLocalStorage();
contextFactory = new Lazy<DatabaseContextFactory>(() =>
{
var factory = new DatabaseContextFactory(LocalStorage);
@ -104,6 +104,23 @@ namespace osu.Game.Tests.Visual
base.Content.Add(content = new DrawSizePreservingFillContainer());
}
public void RecycleLocalStorage()
{
if (localStorage?.IsValueCreated == true)
{
try
{
localStorage.Value.DeleteDirectory(".");
}
catch
{
// we don't really care if this fails; it will just leave folders lying around from test runs.
}
}
localStorage = new Lazy<Storage>(() => new NativeStorage($"{GetType().Name}-{Guid.NewGuid()}"));
}
[Resolved]
protected AudioManager Audio { get; private set; }
@ -131,17 +148,7 @@ namespace osu.Game.Tests.Visual
if (contextFactory.IsValueCreated)
contextFactory.Value.ResetDatabase();
if (localStorage.IsValueCreated)
{
try
{
localStorage.Value.DeleteDirectory(".");
}
catch
{
// we don't really care if this fails; it will just leave folders lying around from test runs.
}
}
RecycleLocalStorage();
}
protected override ITestSceneTestRunner CreateRunner() => new OsuTestSceneTestRunner();

View File

@ -1,4 +1,4 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Efnt/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Emp3/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Epng/@EntryIndexedValue">True</s:Boolean>
@ -245,7 +245,7 @@
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_WHILE/@EntryValue">RequiredForMultiline</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/DEFAULT_INTERNAL_MODIFIER/@EntryValue">Explicit</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/LOCAL_FUNCTION_BODY/@EntryValue">ExpressionBody</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/METHOD_OR_OPERATOR_BODY/@EntryValue">ExpressionBody</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/METHOD_OR_OPERATOR_BODY/@EntryValue">BlockBody</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/USE_HEURISTICS_FOR_BODY_STYLE/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ACCESSOR_DECLARATION_BRACES/@EntryValue">NEXT_LINE</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_LINQ_QUERY/@EntryValue">True</s:Boolean>