mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 15:07:44 +08:00
Merge branch 'master' into fix-mod-select-overflowing
This commit is contained in:
commit
3600b0daf0
@ -44,6 +44,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
private const double time_during_slide_2 = 3000;
|
||||
private const double time_during_slide_3 = 3500;
|
||||
private const double time_during_slide_4 = 3800;
|
||||
private const double time_slider_end = 4000;
|
||||
|
||||
private List<JudgementResult> judgementResults;
|
||||
private bool allJudgedFired;
|
||||
@ -284,6 +285,48 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
AddAssert("Tracking acquired", assertMidSliderJudgements);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scenario:
|
||||
/// - Press a key on the slider head
|
||||
/// - While holding the key, move cursor close to the edge of tracking area
|
||||
/// - Keep the cursor on the edge of tracking area until the slider ends
|
||||
/// Expected Result:
|
||||
/// A passing test case will have the slider track the cursor throughout the whole test.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestTrackingAreaEdge()
|
||||
{
|
||||
performTest(new List<ReplayFrame>
|
||||
{
|
||||
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_slider_start },
|
||||
new OsuReplayFrame { Position = new Vector2(0, OsuHitObject.OBJECT_RADIUS * 1.19f), Actions = { OsuAction.LeftButton }, Time = time_slider_start + 250 },
|
||||
new OsuReplayFrame { Position = new Vector2(slider_path_length, OsuHitObject.OBJECT_RADIUS * 1.199f), Actions = { OsuAction.LeftButton }, Time = time_slider_end },
|
||||
});
|
||||
|
||||
AddAssert("Tracking kept", assertGreatJudge);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scenario:
|
||||
/// - Press a key on the slider head
|
||||
/// - While holding the key, move cursor just outside the tracking area
|
||||
/// - Keep the cursor just outside the tracking area until the slider ends
|
||||
/// Expected Result:
|
||||
/// A passing test case will have the slider drop the tracking on frame 2.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestTrackingAreaOutsideEdge()
|
||||
{
|
||||
performTest(new List<ReplayFrame>
|
||||
{
|
||||
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_slider_start },
|
||||
new OsuReplayFrame { Position = new Vector2(0, OsuHitObject.OBJECT_RADIUS * 1.21f), Actions = { OsuAction.LeftButton }, Time = time_slider_start + 250 },
|
||||
new OsuReplayFrame { Position = new Vector2(slider_path_length, OsuHitObject.OBJECT_RADIUS * 1.201f), Actions = { OsuAction.LeftButton }, Time = time_slider_end },
|
||||
});
|
||||
|
||||
AddAssert("Tracking dropped", assertMidSliderJudgementFail);
|
||||
}
|
||||
|
||||
private bool assertGreatJudge() => judgementResults.Last().Type == HitResult.Great;
|
||||
|
||||
private bool assertHeadMissTailTracked() => judgementResults[^2].Type == HitResult.Great && judgementResults.First().Type == HitResult.Miss;
|
||||
@ -294,6 +337,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
private ScoreAccessibleReplayPlayer currentPlayer;
|
||||
|
||||
private const float slider_path_length = 25;
|
||||
|
||||
private void performTest(List<ReplayFrame> frames)
|
||||
{
|
||||
AddStep("load player", () =>
|
||||
@ -309,8 +354,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
Path = new SliderPath(PathType.PerfectCurve, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(25, 0),
|
||||
}, 25),
|
||||
new Vector2(slider_path_length, 0),
|
||||
}, slider_path_length),
|
||||
}
|
||||
},
|
||||
BeatmapInfo =
|
||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||
/// <summary>
|
||||
/// The start time of <see cref="Start"/>.
|
||||
/// </summary>
|
||||
public readonly Bindable<double> StartTime = new Bindable<double>();
|
||||
public readonly Bindable<double> StartTime = new BindableDouble();
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="DrawableOsuHitObject"/> which <see cref="FollowPoint"/>s will exit from.
|
||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
private readonly IBindable<Vector2> positionBindable = new Bindable<Vector2>();
|
||||
private readonly IBindable<int> stackHeightBindable = new Bindable<int>();
|
||||
private readonly IBindable<float> scaleBindable = new Bindable<float>();
|
||||
private readonly IBindable<float> scaleBindable = new BindableFloat();
|
||||
|
||||
public OsuAction? HitAction => HitArea.HitAction;
|
||||
|
||||
|
@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
InternalChild = scaleContainer = new ReverseArrowPiece();
|
||||
}
|
||||
|
||||
private readonly IBindable<float> scaleBindable = new Bindable<float>();
|
||||
private readonly IBindable<float> scaleBindable = new BindableFloat();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
|
@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
private readonly IBindable<Vector2> positionBindable = new Bindable<Vector2>();
|
||||
private readonly IBindable<int> stackHeightBindable = new Bindable<int>();
|
||||
private readonly IBindable<float> scaleBindable = new Bindable<float>();
|
||||
private readonly IBindable<float> scaleBindable = new BindableFloat();
|
||||
|
||||
public DrawableSlider(Slider s)
|
||||
: base(s)
|
||||
|
@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
};
|
||||
}
|
||||
|
||||
private readonly IBindable<float> scaleBindable = new Bindable<float>();
|
||||
private readonly IBindable<float> scaleBindable = new BindableFloat();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
public Func<OsuAction?> GetInitialHitAction;
|
||||
|
||||
private readonly Slider slider;
|
||||
public readonly Drawable FollowCircle;
|
||||
private readonly Drawable followCircle;
|
||||
private readonly DrawableSlider drawableSlider;
|
||||
|
||||
public SliderBall(Slider slider, DrawableSlider drawableSlider = null)
|
||||
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
|
||||
Children = new[]
|
||||
{
|
||||
FollowCircle = new FollowCircleContainer
|
||||
followCircle = new FollowCircleContainer
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
@ -95,8 +95,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
|
||||
tracking = value;
|
||||
|
||||
FollowCircle.ScaleTo(tracking ? 2f : 1, 300, Easing.OutQuint);
|
||||
FollowCircle.FadeTo(tracking ? 1f : 0, 300, Easing.OutQuint);
|
||||
followCircle.ScaleTo(tracking ? 2.4f : 1f, 300, Easing.OutQuint);
|
||||
followCircle.FadeTo(tracking ? 1f : 0, 300, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,7 +149,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
// in valid time range
|
||||
Time.Current >= slider.StartTime && Time.Current < slider.EndTime &&
|
||||
// in valid position range
|
||||
lastScreenSpaceMousePosition.HasValue && FollowCircle.ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) &&
|
||||
lastScreenSpaceMousePosition.HasValue && followCircle.ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) &&
|
||||
// valid action
|
||||
(actions?.Any(isValidTrackingAction) ?? false);
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
|
||||
public double Radius => OBJECT_RADIUS * Scale;
|
||||
|
||||
public readonly Bindable<float> ScaleBindable = new Bindable<float>(1);
|
||||
public readonly Bindable<float> ScaleBindable = new BindableFloat(1);
|
||||
|
||||
public float Scale
|
||||
{
|
||||
|
@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
autoCursorScale = config.GetBindable<bool>(OsuSetting.AutoCursorSize);
|
||||
autoCursorScale.ValueChanged += _ => calculateScale();
|
||||
|
||||
CursorScale = new Bindable<float>();
|
||||
CursorScale = new BindableFloat();
|
||||
CursorScale.ValueChanged += e => ActiveCursor.Scale = cursorTrail.Scale = new Vector2(e.NewValue);
|
||||
|
||||
calculateScale();
|
||||
|
@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
{
|
||||
Add(localCursorContainer = new OsuCursorContainer());
|
||||
|
||||
localCursorScale = new Bindable<float>();
|
||||
localCursorScale = new BindableFloat();
|
||||
localCursorScale.BindTo(localCursorContainer.CursorScale);
|
||||
localCursorScale.BindValueChanged(scale => cursorScaleContainer.Scale = new Vector2(scale.NewValue), true);
|
||||
}
|
||||
|
@ -68,10 +68,10 @@ namespace osu.Game.Tests.Visual.Background
|
||||
[SetUp]
|
||||
public virtual void SetUp() => Schedule(() =>
|
||||
{
|
||||
Child = new OsuScreenStack(songSelect = new DummySongSelect())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
};
|
||||
var stack = new OsuScreenStack { RelativeSizeAxes = Axes.Both };
|
||||
Child = stack;
|
||||
|
||||
stack.Push(songSelect = new DummySongSelect());
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
@ -277,7 +277,7 @@ namespace osu.Game.Tests.Visual.Background
|
||||
|
||||
private void setupUserSettings()
|
||||
{
|
||||
AddUntilStep("Song select has selection", () => songSelect.Carousel.SelectedBeatmap != null);
|
||||
AddUntilStep("Song select has selection", () => songSelect.Carousel?.SelectedBeatmap != null);
|
||||
AddStep("Set default user settings", () =>
|
||||
{
|
||||
SelectedMods.Value = SelectedMods.Value.Concat(new[] { new OsuModNoFail() }).ToArray();
|
||||
@ -302,8 +302,8 @@ namespace osu.Game.Tests.Visual.Background
|
||||
}
|
||||
|
||||
public readonly Bindable<bool> DimEnabled = new Bindable<bool>();
|
||||
public readonly Bindable<double> DimLevel = new Bindable<double>();
|
||||
public readonly Bindable<double> BlurLevel = new Bindable<double>();
|
||||
public readonly Bindable<double> DimLevel = new BindableDouble();
|
||||
public readonly Bindable<double> BlurLevel = new BindableDouble();
|
||||
|
||||
public new BeatmapCarousel Carousel => base.Carousel;
|
||||
|
||||
|
@ -207,9 +207,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
OsuScreenStack stack;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new OsuScreenStack(screen)
|
||||
stack = new OsuScreenStack
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
@ -224,6 +226,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
Origin = Anchor.TopLeft,
|
||||
}
|
||||
};
|
||||
|
||||
stack.Push(screen);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,10 +75,16 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
public void ResultsWithoutPlayer()
|
||||
{
|
||||
TestSoloResults screen = null;
|
||||
OsuScreenStack stack;
|
||||
|
||||
AddStep("load results", () => Child = new OsuScreenStack(screen = createResultsScreen())
|
||||
AddStep("load results", () =>
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
Child = stack = new OsuScreenStack
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
};
|
||||
|
||||
stack.Push(screen = createResultsScreen());
|
||||
});
|
||||
AddUntilStep("wait for loaded", () => screen.IsLoaded);
|
||||
AddAssert("retry overlay not present", () => screen.RetryOverlay == null);
|
||||
@ -102,11 +108,14 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
public TestResultsContainer(IScreen screen)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
OsuScreenStack stack;
|
||||
|
||||
InternalChild = new OsuScreenStack(screen)
|
||||
InternalChild = stack = new OsuScreenStack
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
};
|
||||
|
||||
stack.Push(screen);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.Menus
|
||||
|
||||
protected IntroTestScene()
|
||||
{
|
||||
Drawable introStack = null;
|
||||
OsuScreenStack introStack = null;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
@ -57,10 +57,12 @@ namespace osu.Game.Tests.Visual.Menus
|
||||
|
||||
introStack?.Expire();
|
||||
|
||||
Add(introStack = new OsuScreenStack(CreateScreen())
|
||||
Add(introStack = new OsuScreenStack
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
});
|
||||
|
||||
introStack.Push(CreateScreen());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
OsuScreenStack screenStack = new OsuScreenStack(new TestMultiplayerSubScreen(index)) { RelativeSizeAxes = Axes.Both };
|
||||
OsuScreenStack screenStack = new OsuScreenStack { RelativeSizeAxes = Axes.Both };
|
||||
|
||||
screenStack.Push(new TestMultiplayerSubScreen(index));
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
|
@ -1,6 +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 System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Configuration;
|
||||
@ -76,6 +78,13 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
ConfirmAtMainMenu();
|
||||
}
|
||||
|
||||
protected 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);
|
||||
}
|
||||
|
||||
protected void ConfirmAtMainMenu() => AddUntilStep("Wait for main menu", () => Game.ScreenStack.CurrentScreen is MainMenu menu && menu.IsLoaded);
|
||||
|
||||
public class TestOsuGame : OsuGame
|
||||
@ -96,6 +105,8 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
|
||||
protected override Loader CreateLoader() => new TestLoader();
|
||||
|
||||
public new void PerformFromScreen(Action<IScreen> action, IEnumerable<Type> validScreens = null) => base.PerformFromScreen(action, validScreens);
|
||||
|
||||
public TestOsuGame(Storage storage, IAPIProvider api)
|
||||
{
|
||||
Storage = storage;
|
||||
|
@ -0,0 +1,72 @@
|
||||
// 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.Game.Screens.Menu;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Select;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Navigation
|
||||
{
|
||||
public class TestScenePerformFromScreen : OsuGameTestScene
|
||||
{
|
||||
[Test]
|
||||
public void TestPerformAtMenu()
|
||||
{
|
||||
AddAssert("could perform immediately", () =>
|
||||
{
|
||||
bool actionPerformed = false;
|
||||
Game.PerformFromScreen(_ => actionPerformed = true);
|
||||
return actionPerformed;
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPerformAtSongSelect()
|
||||
{
|
||||
PushAndConfirm(() => new PlaySongSelect());
|
||||
|
||||
AddAssert("could perform immediately", () =>
|
||||
{
|
||||
bool actionPerformed = false;
|
||||
Game.PerformFromScreen(_ => actionPerformed = true, new[] { typeof(PlaySongSelect) });
|
||||
return actionPerformed;
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPerformAtMenuFromSongSelect()
|
||||
{
|
||||
PushAndConfirm(() => new PlaySongSelect());
|
||||
|
||||
bool actionPerformed = false;
|
||||
AddStep("try to perform", () => Game.PerformFromScreen(_ => actionPerformed = true));
|
||||
AddUntilStep("returned to menu", () => Game.ScreenStack.CurrentScreen is MainMenu);
|
||||
AddAssert("did perform", () => actionPerformed);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPerformAtSongSelectFromPlayerLoader()
|
||||
{
|
||||
PushAndConfirm(() => new PlaySongSelect());
|
||||
PushAndConfirm(() => new PlayerLoader(() => new Player()));
|
||||
|
||||
bool actionPerformed = false;
|
||||
AddStep("try to perform", () => Game.PerformFromScreen(_ => actionPerformed = true, new[] { typeof(PlaySongSelect) }));
|
||||
AddUntilStep("returned to song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect);
|
||||
AddAssert("did perform", () => actionPerformed);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPerformAtMenuFromPlayerLoader()
|
||||
{
|
||||
PushAndConfirm(() => new PlaySongSelect());
|
||||
PushAndConfirm(() => new PlayerLoader(() => new Player()));
|
||||
|
||||
bool actionPerformed = false;
|
||||
AddStep("try to perform", () => Game.PerformFromScreen(_ => actionPerformed = true));
|
||||
AddUntilStep("returned to song select", () => Game.ScreenStack.CurrentScreen is MainMenu);
|
||||
AddAssert("did perform", () => actionPerformed);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +1,11 @@
|
||||
// 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.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Mods;
|
||||
@ -32,7 +30,7 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
{
|
||||
TestSongSelect songSelect = null;
|
||||
|
||||
pushAndConfirm(() => songSelect = new TestSongSelect());
|
||||
PushAndConfirm(() => songSelect = new TestSongSelect());
|
||||
AddStep("Show mods overlay", () => songSelect.ModSelectOverlay.Show());
|
||||
AddAssert("Overlay was shown", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible);
|
||||
pushEscape();
|
||||
@ -49,7 +47,7 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
WorkingBeatmap beatmap() => Game.Beatmap.Value;
|
||||
Track track() => beatmap().Track;
|
||||
|
||||
pushAndConfirm(() => new TestSongSelect());
|
||||
PushAndConfirm(() => new TestSongSelect());
|
||||
|
||||
AddStep("import beatmap", () => ImportBeatmapTest.LoadOszIntoOsu(Game, virtualTrack: true).Wait());
|
||||
|
||||
@ -77,7 +75,7 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
{
|
||||
TestSongSelect songSelect = null;
|
||||
|
||||
pushAndConfirm(() => songSelect = new TestSongSelect());
|
||||
PushAndConfirm(() => songSelect = new TestSongSelect());
|
||||
AddStep("Show mods overlay", () => songSelect.ModSelectOverlay.Show());
|
||||
AddAssert("Overlay was shown", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible);
|
||||
AddStep("Move mouse to backButton", () => InputManager.MoveMouseTo(backButtonPosition));
|
||||
@ -93,14 +91,14 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
[Test]
|
||||
public void TestExitMultiWithEscape()
|
||||
{
|
||||
pushAndConfirm(() => new Screens.Multi.Multiplayer());
|
||||
PushAndConfirm(() => new Screens.Multi.Multiplayer());
|
||||
exitViaEscapeAndConfirm();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestExitMultiWithBackButton()
|
||||
{
|
||||
pushAndConfirm(() => new Screens.Multi.Multiplayer());
|
||||
PushAndConfirm(() => new Screens.Multi.Multiplayer());
|
||||
exitViaBackButtonAndConfirm();
|
||||
}
|
||||
|
||||
@ -116,13 +114,6 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
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);
|
||||
}
|
||||
|
||||
private void pushEscape() =>
|
||||
AddStep("Press escape", () => pressAndRelease(Key.Escape));
|
||||
|
||||
|
@ -25,7 +25,8 @@ namespace osu.Game.Tests.Visual.Online
|
||||
typeof(SortTabControl),
|
||||
typeof(ShowChildrenButton),
|
||||
typeof(DeletedCommentsCounter),
|
||||
typeof(VotePill)
|
||||
typeof(VotePill),
|
||||
typeof(CommentsPage),
|
||||
};
|
||||
|
||||
protected override bool UseOnlineAPI => true;
|
||||
|
162
osu.Game.Tests/Visual/Online/TestSceneCommentsPage.cs
Normal file
162
osu.Game.Tests/Visual/Online/TestSceneCommentsPage.cs
Normal file
@ -0,0 +1,162 @@
|
||||
// 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.Comments;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Users;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
public class TestSceneCommentsPage : OsuTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(DrawableComment),
|
||||
typeof(CommentsPage),
|
||||
};
|
||||
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
|
||||
|
||||
private readonly BindableBool showDeleted = new BindableBool();
|
||||
private readonly Container content;
|
||||
|
||||
public TestSceneCommentsPage()
|
||||
{
|
||||
Add(new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 10),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Width = 200,
|
||||
Child = new OsuCheckbox
|
||||
{
|
||||
Current = showDeleted,
|
||||
LabelText = @"Show Deleted"
|
||||
}
|
||||
},
|
||||
content = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
AddStep("load comments", () => createPage(comment_bundle));
|
||||
AddStep("load empty comments", () => createPage(empty_comment_bundle));
|
||||
}
|
||||
|
||||
private void createPage(CommentBundle commentBundle)
|
||||
{
|
||||
content.Clear();
|
||||
content.Add(new CommentsPage(commentBundle)
|
||||
{
|
||||
ShowDeleted = { BindTarget = showDeleted }
|
||||
});
|
||||
}
|
||||
|
||||
private static readonly CommentBundle empty_comment_bundle = new CommentBundle
|
||||
{
|
||||
Comments = new List<Comment>(),
|
||||
Total = 0,
|
||||
};
|
||||
|
||||
private static readonly CommentBundle comment_bundle = new CommentBundle
|
||||
{
|
||||
Comments = new List<Comment>
|
||||
{
|
||||
new Comment
|
||||
{
|
||||
Id = 1,
|
||||
Message = "Simple test comment",
|
||||
LegacyName = "TestUser1",
|
||||
CreatedAt = DateTimeOffset.Now,
|
||||
VotesCount = 5
|
||||
},
|
||||
new Comment
|
||||
{
|
||||
Id = 2,
|
||||
Message = "This comment has been deleted :( but visible for admins",
|
||||
LegacyName = "TestUser2",
|
||||
CreatedAt = DateTimeOffset.Now,
|
||||
DeletedAt = DateTimeOffset.Now,
|
||||
VotesCount = 5
|
||||
},
|
||||
new Comment
|
||||
{
|
||||
Id = 3,
|
||||
Message = "This comment is a top level",
|
||||
LegacyName = "TestUser3",
|
||||
CreatedAt = DateTimeOffset.Now,
|
||||
RepliesCount = 2,
|
||||
},
|
||||
new Comment
|
||||
{
|
||||
Id = 4,
|
||||
ParentId = 3,
|
||||
Message = "And this is a reply",
|
||||
RepliesCount = 1,
|
||||
LegacyName = "TestUser1",
|
||||
CreatedAt = DateTimeOffset.Now,
|
||||
},
|
||||
new Comment
|
||||
{
|
||||
Id = 15,
|
||||
ParentId = 4,
|
||||
Message = "Reply to reply",
|
||||
LegacyName = "TestUser1",
|
||||
CreatedAt = DateTimeOffset.Now,
|
||||
},
|
||||
new Comment
|
||||
{
|
||||
Id = 6,
|
||||
ParentId = 3,
|
||||
LegacyName = "TestUser11515",
|
||||
CreatedAt = DateTimeOffset.Now,
|
||||
DeletedAt = DateTimeOffset.Now,
|
||||
},
|
||||
new Comment
|
||||
{
|
||||
Id = 5,
|
||||
Message = "This comment is voted and edited",
|
||||
LegacyName = "BigBrainUser",
|
||||
CreatedAt = DateTimeOffset.Now,
|
||||
EditedAt = DateTimeOffset.Now,
|
||||
VotesCount = 1000,
|
||||
EditedById = 1,
|
||||
}
|
||||
},
|
||||
IncludedComments = new List<Comment>(),
|
||||
UserVotes = new List<long>
|
||||
{
|
||||
5
|
||||
},
|
||||
Users = new List<User>
|
||||
{
|
||||
new User
|
||||
{
|
||||
Id = 1,
|
||||
Username = "Good_Admin"
|
||||
}
|
||||
},
|
||||
TopLevelCount = 4,
|
||||
Total = 7
|
||||
};
|
||||
}
|
||||
}
|
@ -26,7 +26,6 @@ namespace osu.Game.Tests.Visual.Online
|
||||
typeof(HistoricalSection),
|
||||
typeof(PaginatedMostPlayedBeatmapContainer),
|
||||
typeof(DrawableMostPlayedBeatmap),
|
||||
typeof(DrawableProfileRow)
|
||||
};
|
||||
|
||||
[Cached]
|
||||
|
@ -11,6 +11,8 @@ using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Taiko;
|
||||
using osu.Game.Users;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Framework.Allocation;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
@ -22,10 +24,13 @@ namespace osu.Game.Tests.Visual.Online
|
||||
typeof(ProfileRulesetTabItem),
|
||||
};
|
||||
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green);
|
||||
|
||||
public TestSceneProfileRulesetSelector()
|
||||
{
|
||||
ProfileRulesetSelector selector;
|
||||
Bindable<User> user = new Bindable<User>();
|
||||
var user = new Bindable<User>();
|
||||
|
||||
Child = selector = new ProfileRulesetSelector
|
||||
{
|
||||
|
@ -4,11 +4,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Profile.Header.Components;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
@ -24,6 +26,9 @@ namespace osu.Game.Tests.Visual.Online
|
||||
typeof(LineGraph)
|
||||
};
|
||||
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink);
|
||||
|
||||
public TestSceneRankGraph()
|
||||
{
|
||||
RankGraph graph;
|
||||
|
@ -5,6 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
@ -12,6 +13,7 @@ using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Profile.Sections;
|
||||
using osu.Game.Overlays.Profile.Sections.Recent;
|
||||
|
||||
@ -28,6 +30,9 @@ namespace osu.Game.Tests.Visual.Online
|
||||
typeof(MedalIcon)
|
||||
};
|
||||
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green);
|
||||
|
||||
public TestSceneUserProfileRecentSection()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
@ -131,6 +136,22 @@ namespace osu.Game.Tests.Visual.Online
|
||||
Beatmap = dummyBeatmap,
|
||||
},
|
||||
new APIRecentActivity
|
||||
{
|
||||
User = dummyUser,
|
||||
Type = RecentActivityType.Rank,
|
||||
Rank = 1,
|
||||
Mode = "vitaru",
|
||||
Beatmap = dummyBeatmap,
|
||||
},
|
||||
new APIRecentActivity
|
||||
{
|
||||
User = dummyUser,
|
||||
Type = RecentActivityType.Rank,
|
||||
Rank = 1,
|
||||
Mode = "fruits",
|
||||
Beatmap = dummyBeatmap,
|
||||
},
|
||||
new APIRecentActivity
|
||||
{
|
||||
User = dummyUser,
|
||||
Type = RecentActivityType.RankLost,
|
||||
|
175
osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs
Normal file
175
osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs
Normal file
@ -0,0 +1,175 @@
|
||||
// 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.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Screens.Select.Details;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tests.Visual.SongSelect
|
||||
{
|
||||
[System.ComponentModel.Description("Advanced beatmap statistics display")]
|
||||
public class TestSceneAdvancedStats : OsuTestScene
|
||||
{
|
||||
private TestAdvancedStats advancedStats;
|
||||
|
||||
[Resolved]
|
||||
private RulesetStore rulesets { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
|
||||
[SetUp]
|
||||
public void Setup() => Schedule(() => Child = advancedStats = new TestAdvancedStats
|
||||
{
|
||||
Width = 500
|
||||
});
|
||||
|
||||
private BeatmapInfo exampleBeatmapInfo => new BeatmapInfo
|
||||
{
|
||||
RulesetID = 0,
|
||||
Ruleset = rulesets.AvailableRulesets.First(),
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 7.2f,
|
||||
DrainRate = 3,
|
||||
OverallDifficulty = 5.7f,
|
||||
ApproachRate = 3.5f
|
||||
},
|
||||
StarDifficulty = 4.5f
|
||||
};
|
||||
|
||||
[Test]
|
||||
public void TestNoMod()
|
||||
{
|
||||
AddStep("set beatmap", () => advancedStats.Beatmap = exampleBeatmapInfo);
|
||||
|
||||
AddStep("no mods selected", () => SelectedMods.Value = Array.Empty<Mod>());
|
||||
|
||||
AddAssert("first bar text is Circle Size", () => advancedStats.ChildrenOfType<SpriteText>().First().Text == "Circle Size");
|
||||
AddAssert("circle size bar is white", () => barIsWhite(advancedStats.FirstValue));
|
||||
AddAssert("HP drain bar is white", () => barIsWhite(advancedStats.HpDrain));
|
||||
AddAssert("accuracy bar is white", () => barIsWhite(advancedStats.Accuracy));
|
||||
AddAssert("approach rate bar is white", () => barIsWhite(advancedStats.ApproachRate));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestManiaFirstBarText()
|
||||
{
|
||||
AddStep("set beatmap", () => advancedStats.Beatmap = new BeatmapInfo
|
||||
{
|
||||
Ruleset = rulesets.GetRuleset(3),
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 5,
|
||||
DrainRate = 4.3f,
|
||||
OverallDifficulty = 4.5f,
|
||||
ApproachRate = 3.1f
|
||||
},
|
||||
StarDifficulty = 8
|
||||
});
|
||||
|
||||
AddAssert("first bar text is Key Count", () => advancedStats.ChildrenOfType<SpriteText>().First().Text == "Key Count");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEasyMod()
|
||||
{
|
||||
AddStep("set beatmap", () => advancedStats.Beatmap = exampleBeatmapInfo);
|
||||
|
||||
AddStep("select EZ mod", () =>
|
||||
{
|
||||
var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance();
|
||||
SelectedMods.Value = new[] { ruleset.GetAllMods().OfType<ModEasy>().Single() };
|
||||
});
|
||||
|
||||
AddAssert("circle size bar is blue", () => barIsBlue(advancedStats.FirstValue));
|
||||
AddAssert("HP drain bar is blue", () => barIsBlue(advancedStats.HpDrain));
|
||||
AddAssert("accuracy bar is blue", () => barIsBlue(advancedStats.Accuracy));
|
||||
AddAssert("approach rate bar is blue", () => barIsBlue(advancedStats.ApproachRate));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHardRockMod()
|
||||
{
|
||||
AddStep("set beatmap", () => advancedStats.Beatmap = exampleBeatmapInfo);
|
||||
|
||||
AddStep("select HR mod", () =>
|
||||
{
|
||||
var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance();
|
||||
SelectedMods.Value = new[] { ruleset.GetAllMods().OfType<ModHardRock>().Single() };
|
||||
});
|
||||
|
||||
AddAssert("circle size bar is red", () => barIsRed(advancedStats.FirstValue));
|
||||
AddAssert("HP drain bar is red", () => barIsRed(advancedStats.HpDrain));
|
||||
AddAssert("accuracy bar is red", () => barIsRed(advancedStats.Accuracy));
|
||||
AddAssert("approach rate bar is red", () => barIsRed(advancedStats.ApproachRate));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUnchangedDifficultyAdjustMod()
|
||||
{
|
||||
AddStep("set beatmap", () => advancedStats.Beatmap = exampleBeatmapInfo);
|
||||
|
||||
AddStep("select unchanged Difficulty Adjust mod", () =>
|
||||
{
|
||||
var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance();
|
||||
var difficultyAdjustMod = ruleset.GetAllMods().OfType<ModDifficultyAdjust>().Single();
|
||||
difficultyAdjustMod.ReadFromDifficulty(advancedStats.Beatmap.BaseDifficulty);
|
||||
SelectedMods.Value = new[] { difficultyAdjustMod };
|
||||
});
|
||||
|
||||
AddAssert("circle size bar is white", () => barIsWhite(advancedStats.FirstValue));
|
||||
AddAssert("HP drain bar is white", () => barIsWhite(advancedStats.HpDrain));
|
||||
AddAssert("accuracy bar is white", () => barIsWhite(advancedStats.Accuracy));
|
||||
AddAssert("approach rate bar is white", () => barIsWhite(advancedStats.ApproachRate));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestChangedDifficultyAdjustMod()
|
||||
{
|
||||
AddStep("set beatmap", () => advancedStats.Beatmap = exampleBeatmapInfo);
|
||||
|
||||
AddStep("select changed Difficulty Adjust mod", () =>
|
||||
{
|
||||
var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance();
|
||||
var difficultyAdjustMod = ruleset.GetAllMods().OfType<ModDifficultyAdjust>().Single();
|
||||
var originalDifficulty = advancedStats.Beatmap.BaseDifficulty;
|
||||
var adjustedDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = originalDifficulty.CircleSize,
|
||||
DrainRate = originalDifficulty.DrainRate - 0.5f,
|
||||
OverallDifficulty = originalDifficulty.OverallDifficulty,
|
||||
ApproachRate = originalDifficulty.ApproachRate + 2.2f,
|
||||
};
|
||||
difficultyAdjustMod.ReadFromDifficulty(adjustedDifficulty);
|
||||
SelectedMods.Value = new[] { difficultyAdjustMod };
|
||||
});
|
||||
|
||||
AddAssert("circle size bar is white", () => barIsWhite(advancedStats.FirstValue));
|
||||
AddAssert("drain rate bar is blue", () => barIsBlue(advancedStats.HpDrain));
|
||||
AddAssert("accuracy bar is white", () => barIsWhite(advancedStats.Accuracy));
|
||||
AddAssert("approach rate bar is red", () => barIsRed(advancedStats.ApproachRate));
|
||||
}
|
||||
|
||||
private bool barIsWhite(AdvancedStats.StatisticRow row) => row.ModBar.AccentColour == Color4.White;
|
||||
private bool barIsBlue(AdvancedStats.StatisticRow row) => row.ModBar.AccentColour == colours.BlueDark;
|
||||
private bool barIsRed(AdvancedStats.StatisticRow row) => row.ModBar.AccentColour == colours.Red;
|
||||
|
||||
private class TestAdvancedStats : AdvancedStats
|
||||
{
|
||||
public new StatisticRow FirstValue => base.FirstValue;
|
||||
public new StatisticRow HpDrain => base.HpDrain;
|
||||
public new StatisticRow Accuracy => base.Accuracy;
|
||||
public new StatisticRow ApproachRate => base.ApproachRate;
|
||||
}
|
||||
}
|
||||
}
|
@ -3,14 +3,8 @@
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Screens.Select;
|
||||
|
||||
namespace osu.Game.Tests.Visual.SongSelect
|
||||
@ -180,27 +174,5 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
OnlineBeatmapID = 162,
|
||||
});
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private RulesetStore rulesets { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
|
||||
[Test]
|
||||
public void TestModAdjustments()
|
||||
{
|
||||
TestAllMetrics();
|
||||
|
||||
Ruleset ruleset = rulesets.AvailableRulesets.First().CreateInstance();
|
||||
|
||||
AddStep("with EZ mod", () => SelectedMods.Value = new[] { ruleset.GetAllMods().First(m => m is ModEasy) });
|
||||
|
||||
AddAssert("first bar coloured blue", () => details.ChildrenOfType<Bar>().Skip(1).First().AccentColour == colours.BlueDark);
|
||||
|
||||
AddStep("with HR mod", () => SelectedMods.Value = new[] { ruleset.GetAllMods().First(m => m is ModHardRock) });
|
||||
|
||||
AddAssert("first bar coloured red", () => details.ChildrenOfType<Bar>().Skip(1).First().AccentColour == colours.Red);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.Taiko;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Screens.Select.Carousel;
|
||||
using osu.Game.Screens.Select.Filter;
|
||||
@ -77,7 +78,6 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
|
||||
private OsuConfigManager config;
|
||||
|
||||
[SetUpSteps]
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
base.SetUpSteps();
|
||||
@ -426,6 +426,31 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
AddAssert("start not requested", () => !startRequested);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAutoplayViaCtrlEnter()
|
||||
{
|
||||
addRulesetImportStep(0);
|
||||
|
||||
createSongSelect();
|
||||
|
||||
AddStep("press ctrl+enter", () =>
|
||||
{
|
||||
InputManager.PressKey(Key.ControlLeft);
|
||||
InputManager.PressKey(Key.Enter);
|
||||
|
||||
InputManager.ReleaseKey(Key.ControlLeft);
|
||||
InputManager.ReleaseKey(Key.Enter);
|
||||
});
|
||||
|
||||
AddUntilStep("wait for player", () => Stack.CurrentScreen is PlayerLoader);
|
||||
|
||||
AddAssert("autoplay enabled", () => songSelect.Mods.Value.FirstOrDefault() is ModAutoplay);
|
||||
|
||||
AddUntilStep("wait for return to ss", () => songSelect.IsCurrentScreen());
|
||||
|
||||
AddAssert("mod disabled", () => songSelect.Mods.Value.Count == 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHideSetSelectsCorrectBeatmap()
|
||||
{
|
||||
|
@ -62,6 +62,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
Origin = Anchor.BottomCentre,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
SelectedMods = { BindTarget = SelectedMods }
|
||||
},
|
||||
|
||||
modDisplay = new ModDisplay
|
||||
|
@ -27,6 +27,13 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
|
||||
private readonly Mod testCustomisableAutoOpenMod = new TestModCustomisable2();
|
||||
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() =>
|
||||
{
|
||||
SelectedMods.Value = Array.Empty<Mod>();
|
||||
Ruleset.Value = new TestRulesetInfo();
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestButtonShowsOnCustomisableMod()
|
||||
{
|
||||
@ -70,12 +77,11 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
AddStep("create mod select", () =>
|
||||
{
|
||||
Ruleset.Value = new TestRulesetInfo();
|
||||
|
||||
Child = modSelect = new TestModSelectOverlay
|
||||
{
|
||||
Origin = Anchor.BottomCentre,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
SelectedMods = { BindTarget = SelectedMods }
|
||||
};
|
||||
});
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
|
||||
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("Green TabControlOverlayHeader (string) with ruleset selector", new TestStringTabControlHeader(), OverlayColourScheme.Green);
|
||||
addHeader("Pink TabControlOverlayHeader (enum)", new TestEnumTabControlHeader(), OverlayColourScheme.Pink);
|
||||
addHeader("Red BreadcrumbControlOverlayHeader (no background)", new TestBreadcrumbControlHeader(), OverlayColourScheme.Red);
|
||||
}
|
||||
@ -116,6 +116,8 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
|
||||
protected override ScreenTitle CreateTitle() => new TestTitle();
|
||||
|
||||
protected override Drawable CreateTitleContent() => new OverlayRulesetSelector();
|
||||
|
||||
public TestStringTabControlHeader()
|
||||
{
|
||||
TabControl.AddItem("tab1");
|
||||
|
@ -0,0 +1,82 @@
|
||||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Catch;
|
||||
using osu.Game.Rulesets.Mania;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Taiko;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Rulesets;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osuTK;
|
||||
using osu.Framework.Allocation;
|
||||
|
||||
namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
public class TestSceneOverlayRulesetSelector : OsuTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(OverlayRulesetSelector),
|
||||
typeof(OverlayRulesetTabItem),
|
||||
};
|
||||
|
||||
private readonly OverlayRulesetSelector selector;
|
||||
private readonly Bindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
|
||||
|
||||
public TestSceneOverlayRulesetSelector()
|
||||
{
|
||||
Add(new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 5),
|
||||
Children = new[]
|
||||
{
|
||||
new ColourProvidedContainer(OverlayColourScheme.Green, selector = new OverlayRulesetSelector { Current = ruleset }),
|
||||
new ColourProvidedContainer(OverlayColourScheme.Blue, new OverlayRulesetSelector { Current = ruleset }),
|
||||
new ColourProvidedContainer(OverlayColourScheme.Orange, new OverlayRulesetSelector { Current = ruleset }),
|
||||
new ColourProvidedContainer(OverlayColourScheme.Pink, new OverlayRulesetSelector { Current = ruleset }),
|
||||
new ColourProvidedContainer(OverlayColourScheme.Purple, new OverlayRulesetSelector { Current = ruleset }),
|
||||
new ColourProvidedContainer(OverlayColourScheme.Red, new OverlayRulesetSelector { Current = ruleset }),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class ColourProvidedContainer : Container
|
||||
{
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider;
|
||||
|
||||
public ColourProvidedContainer(OverlayColourScheme colourScheme, OverlayRulesetSelector rulesetSelector)
|
||||
{
|
||||
colourProvider = new OverlayColourProvider(colourScheme);
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Add(rulesetSelector);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSelection()
|
||||
{
|
||||
AddStep("Select osu!", () => ruleset.Value = new OsuRuleset().RulesetInfo);
|
||||
AddAssert("Check osu! selected", () => selector.Current.Value.Equals(new OsuRuleset().RulesetInfo));
|
||||
|
||||
AddStep("Select mania", () => ruleset.Value = new ManiaRuleset().RulesetInfo);
|
||||
AddAssert("Check mania selected", () => selector.Current.Value.Equals(new ManiaRuleset().RulesetInfo));
|
||||
|
||||
AddStep("Select taiko", () => ruleset.Value = new TaikoRuleset().RulesetInfo);
|
||||
AddAssert("Check taiko selected", () => selector.Current.Value.Equals(new TaikoRuleset().RulesetInfo));
|
||||
|
||||
AddStep("Select catch", () => ruleset.Value = new CatchRuleset().RulesetInfo);
|
||||
AddAssert("Check catch selected", () => selector.Current.Value.Equals(new CatchRuleset().RulesetInfo));
|
||||
}
|
||||
}
|
||||
}
|
@ -25,7 +25,9 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
OsuSpriteText titleText;
|
||||
|
||||
IScreen startScreen = new TestScreenOne();
|
||||
screenStack = new OsuScreenStack(startScreen) { RelativeSizeAxes = Axes.Both };
|
||||
|
||||
screenStack = new OsuScreenStack { RelativeSizeAxes = Axes.Both };
|
||||
screenStack.Push(startScreen);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
|
@ -29,9 +29,9 @@ namespace osu.Game.Graphics
|
||||
}
|
||||
}
|
||||
|
||||
public DrawableDate(DateTimeOffset date, float textSize = OsuFont.DEFAULT_FONT_SIZE)
|
||||
public DrawableDate(DateTimeOffset date, float textSize = OsuFont.DEFAULT_FONT_SIZE, bool italic = true)
|
||||
{
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Regular, size: textSize, italics: true);
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Regular, size: textSize, italics: italic);
|
||||
Date = date;
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,8 @@ namespace osu.Game.Graphics.UserInterface
|
||||
stack.ScreenPushed += onPushed;
|
||||
stack.ScreenExited += onExited;
|
||||
|
||||
onPushed(null, stack.CurrentScreen);
|
||||
if (stack.CurrentScreen != null)
|
||||
onPushed(null, stack.CurrentScreen);
|
||||
|
||||
Current.ValueChanged += current => current.NewValue.MakeCurrent();
|
||||
}
|
||||
|
@ -40,13 +40,14 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
public ShowMoreButton()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Height = 30;
|
||||
Width = 140;
|
||||
}
|
||||
|
||||
protected override Drawable CreateContent() => new CircularContainer
|
||||
{
|
||||
Masking = true,
|
||||
Size = new Vector2(140, 30),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
background = new Box
|
||||
|
@ -6,8 +6,6 @@ using osu.Game.Users;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace osu.Game.Online.API.Requests.Responses
|
||||
{
|
||||
@ -70,12 +68,10 @@ namespace osu.Game.Online.API.Requests.Responses
|
||||
|
||||
public bool IsDeleted => DeletedAt.HasValue;
|
||||
|
||||
public bool HasMessage => !string.IsNullOrEmpty(MessageHtml);
|
||||
public bool HasMessage => !string.IsNullOrEmpty(Message);
|
||||
|
||||
public bool IsVoted { get; set; }
|
||||
|
||||
public string GetMessage => HasMessage ? WebUtility.HtmlDecode(Regex.Replace(MessageHtml, @"<(.|\n)*?>", string.Empty)) : string.Empty;
|
||||
|
||||
public int DeletedChildrenCount => ChildComments.Count(c => c.IsDeleted);
|
||||
}
|
||||
}
|
||||
|
@ -47,17 +47,18 @@ namespace osu.Game.Online.API.Requests.Responses
|
||||
[JsonProperty(@"included_comments")]
|
||||
public List<Comment> IncludedComments { get; set; }
|
||||
|
||||
private List<long> userVotes;
|
||||
|
||||
[JsonProperty(@"user_votes")]
|
||||
private List<long> userVotes
|
||||
public List<long> UserVotes
|
||||
{
|
||||
set => value.ForEach(v =>
|
||||
get => userVotes;
|
||||
set
|
||||
{
|
||||
Comments.ForEach(c =>
|
||||
{
|
||||
if (v == c.Id)
|
||||
c.IsVoted = true;
|
||||
});
|
||||
});
|
||||
userVotes = value;
|
||||
|
||||
Comments.ForEach(c => c.IsVoted = value.Contains(c.Id));
|
||||
}
|
||||
}
|
||||
|
||||
private List<User> users;
|
||||
|
@ -327,10 +327,10 @@ namespace osu.Game
|
||||
return;
|
||||
}
|
||||
|
||||
performFromMainMenu(() =>
|
||||
PerformFromScreen(screen =>
|
||||
{
|
||||
// we might already be at song select, so a check is required before performing the load to solo.
|
||||
if (menuScreen.IsCurrentScreen())
|
||||
if (screen is MainMenu)
|
||||
menuScreen.LoadToSolo();
|
||||
|
||||
// we might even already be at the song
|
||||
@ -344,7 +344,7 @@ namespace osu.Game
|
||||
|
||||
Ruleset.Value = first.Ruleset;
|
||||
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(first);
|
||||
}, $"load {beatmap}", bypassScreenAllowChecks: true, targetScreen: typeof(PlaySongSelect));
|
||||
}, validScreens: new[] { typeof(PlaySongSelect) });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -381,12 +381,12 @@ namespace osu.Game
|
||||
return;
|
||||
}
|
||||
|
||||
performFromMainMenu(() =>
|
||||
PerformFromScreen(screen =>
|
||||
{
|
||||
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap);
|
||||
|
||||
menuScreen.Push(new ReplayPlayerLoader(databasedScore));
|
||||
}, $"watch {databasedScoreInfo}", bypassScreenAllowChecks: true);
|
||||
screen.Push(new ReplayPlayerLoader(databasedScore));
|
||||
}, validScreens: new[] { typeof(PlaySongSelect) });
|
||||
}
|
||||
|
||||
protected virtual Loader CreateLoader() => new Loader();
|
||||
@ -441,53 +441,42 @@ namespace osu.Game
|
||||
private ScheduledDelegate performFromMainMenuTask;
|
||||
|
||||
/// <summary>
|
||||
/// Perform an action only after returning to the main menu.
|
||||
/// Perform an action only after returning to a specific screen as indicated by <paramref name="validScreens"/>.
|
||||
/// Eagerly tries to exit the current screen until it succeeds.
|
||||
/// </summary>
|
||||
/// <param name="action">The action to perform once we are in the correct state.</param>
|
||||
/// <param name="taskName">The task name to display in a notification (if we can't immediately reach the main menu state).</param>
|
||||
/// <param name="targetScreen">An optional target screen type. If this screen is already current we can immediately perform the action without returning to the menu.</param>
|
||||
/// <param name="bypassScreenAllowChecks">Whether checking <see cref="IOsuScreen.AllowExternalScreenChange"/> should be bypassed.</param>
|
||||
private void performFromMainMenu(Action action, string taskName, Type targetScreen = null, bool bypassScreenAllowChecks = false)
|
||||
/// <param name="validScreens">An optional collection of valid screen types. If any of these screens are already current we can perform the action immediately, else the first valid parent will be made current before performing the action. <see cref="MainMenu"/> is used if not specified.</param>
|
||||
protected void PerformFromScreen(Action<IScreen> action, IEnumerable<Type> validScreens = null)
|
||||
{
|
||||
performFromMainMenuTask?.Cancel();
|
||||
|
||||
// if the current screen does not allow screen changing, give the user an option to try again later.
|
||||
if (!bypassScreenAllowChecks && (ScreenStack.CurrentScreen as IOsuScreen)?.AllowExternalScreenChange == false)
|
||||
{
|
||||
notifications.Post(new SimpleNotification
|
||||
{
|
||||
Text = $"Click here to {taskName}",
|
||||
Activated = () =>
|
||||
{
|
||||
performFromMainMenu(action, taskName, targetScreen, true);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
validScreens ??= Enumerable.Empty<Type>();
|
||||
validScreens = validScreens.Append(typeof(MainMenu));
|
||||
|
||||
CloseAllOverlays(false);
|
||||
|
||||
// we may already be at the target screen type.
|
||||
if (targetScreen != null && ScreenStack.CurrentScreen?.GetType() == targetScreen)
|
||||
if (validScreens.Contains(ScreenStack.CurrentScreen?.GetType()) && !Beatmap.Disabled)
|
||||
{
|
||||
action();
|
||||
action(ScreenStack.CurrentScreen);
|
||||
return;
|
||||
}
|
||||
|
||||
// all conditions have been met to continue with the action.
|
||||
if (menuScreen?.IsCurrentScreen() == true && !Beatmap.Disabled)
|
||||
// find closest valid target
|
||||
IScreen screen = ScreenStack.CurrentScreen;
|
||||
|
||||
while (screen != null)
|
||||
{
|
||||
action();
|
||||
return;
|
||||
if (validScreens.Contains(screen.GetType()))
|
||||
{
|
||||
screen.MakeCurrent();
|
||||
break;
|
||||
}
|
||||
|
||||
screen = screen.GetParentScreen();
|
||||
}
|
||||
|
||||
// menuScreen may not be initialised yet (null check required).
|
||||
menuScreen?.MakeCurrent();
|
||||
|
||||
performFromMainMenuTask = Schedule(() => performFromMainMenu(action, taskName));
|
||||
performFromMainMenuTask = Schedule(() => PerformFromScreen(action, validScreens));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -68,6 +68,7 @@ namespace osu.Game.Overlays.Comments
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Name = @"Footer",
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new Drawable[]
|
||||
@ -154,6 +155,7 @@ namespace osu.Game.Overlays.Comments
|
||||
{
|
||||
currentPage = 1;
|
||||
deletedCommentsCounter.Count.Value = 0;
|
||||
moreButton.Show();
|
||||
moreButton.IsLoading = true;
|
||||
content.Clear();
|
||||
}
|
||||
@ -162,25 +164,10 @@ namespace osu.Game.Overlays.Comments
|
||||
{
|
||||
loadCancellation = new CancellationTokenSource();
|
||||
|
||||
var page = new FillFlowContainer
|
||||
LoadComponentAsync(new CommentsPage(response)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
};
|
||||
|
||||
foreach (var c in response.Comments)
|
||||
{
|
||||
if (c.IsTopLevel)
|
||||
{
|
||||
page.Add(new DrawableComment(c)
|
||||
{
|
||||
ShowDeleted = { BindTarget = ShowDeleted }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
LoadComponentAsync(page, loaded =>
|
||||
ShowDeleted = { BindTarget = ShowDeleted }
|
||||
}, loaded =>
|
||||
{
|
||||
content.Add(loaded);
|
||||
|
||||
@ -194,10 +181,12 @@ namespace osu.Game.Overlays.Comments
|
||||
moreButton.Current.Value = response.TopLevelCount - loadedTopLevelComments;
|
||||
moreButton.IsLoading = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
moreButton.Hide();
|
||||
}
|
||||
|
||||
commentCounter.Current.Value = response.Total;
|
||||
|
||||
moreButton.FadeTo(response.HasMore ? 1 : 0);
|
||||
}, loadCancellation.Token);
|
||||
}
|
||||
|
||||
|
92
osu.Game/Overlays/Comments/CommentsPage.cs
Normal file
92
osu.Game/Overlays/Comments/CommentsPage.cs
Normal file
@ -0,0 +1,92 @@
|
||||
// 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.Containers;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using System.Linq;
|
||||
|
||||
namespace osu.Game.Overlays.Comments
|
||||
{
|
||||
public class CommentsPage : CompositeDrawable
|
||||
{
|
||||
public readonly BindableBool ShowDeleted = new BindableBool();
|
||||
|
||||
private readonly CommentBundle commentBundle;
|
||||
|
||||
public CommentsPage(CommentBundle commentBundle)
|
||||
{
|
||||
this.commentBundle = commentBundle;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
FillFlowContainer flow;
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
AddRangeInternal(new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colourProvider.Background5
|
||||
},
|
||||
flow = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
}
|
||||
});
|
||||
|
||||
if (!commentBundle.Comments.Any())
|
||||
{
|
||||
flow.Add(new NoCommentsPlaceholder());
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var c in commentBundle.Comments)
|
||||
{
|
||||
if (c.IsTopLevel)
|
||||
{
|
||||
flow.Add(new DrawableComment(c)
|
||||
{
|
||||
ShowDeleted = { BindTarget = ShowDeleted }
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class NoCommentsPlaceholder : CompositeDrawable
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
Height = 80;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AddRangeInternal(new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colourProvider.Background4
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Margin = new MarginPadding { Left = 50 },
|
||||
Text = @"No comments yet."
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -14,6 +14,8 @@ namespace osu.Game.Overlays.Comments
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
Height = 20;
|
||||
|
||||
IdleColour = colourProvider.Background2;
|
||||
HoverColour = colourProvider.Background1;
|
||||
ChevronIconColour = colourProvider.Foreground1;
|
||||
|
@ -213,7 +213,7 @@ namespace osu.Game.Overlays.Comments
|
||||
|
||||
if (comment.HasMessage)
|
||||
{
|
||||
var formattedSource = MessageFormatter.FormatText(comment.GetMessage);
|
||||
var formattedSource = MessageFormatter.FormatText(comment.Message);
|
||||
message.AddLinks(formattedSource.Text, formattedSource.Links);
|
||||
}
|
||||
|
||||
@ -343,7 +343,7 @@ namespace osu.Game.Overlays.Comments
|
||||
if (parentComment == null)
|
||||
return string.Empty;
|
||||
|
||||
return parentComment.HasMessage ? parentComment.GetMessage : parentComment.IsDeleted ? @"deleted" : string.Empty;
|
||||
return parentComment.HasMessage ? parentComment.Message : parentComment.IsDeleted ? @"deleted" : string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
protected readonly Container ModSettingsContainer;
|
||||
|
||||
protected readonly Bindable<IReadOnlyList<Mod>> SelectedMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
||||
public readonly Bindable<IReadOnlyList<Mod>> SelectedMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
||||
|
||||
private Bindable<Dictionary<ModType, IReadOnlyList<Mod>>> availableMods;
|
||||
|
||||
@ -323,14 +323,13 @@ namespace osu.Game.Overlays.Mods
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(OsuColour colours, AudioManager audio, Bindable<IReadOnlyList<Mod>> selectedMods, OsuGameBase osu)
|
||||
private void load(OsuColour colours, AudioManager audio, OsuGameBase osu)
|
||||
{
|
||||
LowMultiplierColour = colours.Red;
|
||||
HighMultiplierColour = colours.Green;
|
||||
UnrankedLabel.Colour = colours.Blue;
|
||||
|
||||
availableMods = osu.AvailableMods.GetBoundCopy();
|
||||
SelectedMods.BindTo(selectedMods);
|
||||
|
||||
sampleOn = audio.Samples.Get(@"UI/check-on");
|
||||
sampleOff = audio.Samples.Get(@"UI/check-off");
|
||||
|
@ -50,14 +50,29 @@ namespace osu.Game.Overlays
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Gray,
|
||||
},
|
||||
title = CreateTitle().With(title =>
|
||||
new Container
|
||||
{
|
||||
title.Margin = new MarginPadding
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding
|
||||
{
|
||||
Horizontal = UserProfileOverlay.CONTENT_X_MARGIN,
|
||||
Vertical = 10,
|
||||
Left = UserProfileOverlay.CONTENT_X_MARGIN
|
||||
};
|
||||
})
|
||||
},
|
||||
Children = new[]
|
||||
{
|
||||
title = CreateTitle().With(title =>
|
||||
{
|
||||
title.Anchor = Anchor.CentreLeft;
|
||||
title.Origin = Anchor.CentreLeft;
|
||||
}),
|
||||
CreateTitleContent().With(content =>
|
||||
{
|
||||
content.Anchor = Anchor.CentreRight;
|
||||
content.Origin = Anchor.CentreRight;
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -75,10 +90,16 @@ namespace osu.Game.Overlays
|
||||
}
|
||||
|
||||
[NotNull]
|
||||
protected virtual Drawable CreateContent() => Drawable.Empty();
|
||||
protected virtual Drawable CreateContent() => Empty();
|
||||
|
||||
[NotNull]
|
||||
protected virtual Drawable CreateBackground() => Drawable.Empty();
|
||||
protected virtual Drawable CreateBackground() => Empty();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="Drawable"/> on the opposite side of the <see cref="ScreenTitle"/>. Used mostly to create <see cref="OverlayRulesetSelector"/>.
|
||||
/// </summary>
|
||||
[NotNull]
|
||||
protected virtual Drawable CreateTitleContent() => Empty();
|
||||
|
||||
protected abstract ScreenTitle CreateTitle();
|
||||
}
|
||||
|
28
osu.Game/Overlays/OverlayRulesetSelector.cs
Normal file
28
osu.Game/Overlays/OverlayRulesetSelector.cs
Normal file
@ -0,0 +1,28 @@
|
||||
// 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.Framework.Graphics.UserInterface;
|
||||
using osu.Game.Rulesets;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
public class OverlayRulesetSelector : RulesetSelector
|
||||
{
|
||||
public OverlayRulesetSelector()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
protected override TabItem<RulesetInfo> CreateTabItem(RulesetInfo value) => new OverlayRulesetTabItem(value);
|
||||
|
||||
protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(25, 0),
|
||||
};
|
||||
}
|
||||
}
|
93
osu.Game/Overlays/OverlayRulesetTabItem.cs
Normal file
93
osu.Game/Overlays/OverlayRulesetTabItem.cs
Normal file
@ -0,0 +1,93 @@
|
||||
// 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.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Rulesets;
|
||||
using osuTK.Graphics;
|
||||
using osuTK;
|
||||
using osu.Framework.Allocation;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
public class OverlayRulesetTabItem : TabItem<RulesetInfo>
|
||||
{
|
||||
private Color4 accentColour;
|
||||
|
||||
protected virtual Color4 AccentColour
|
||||
{
|
||||
get => accentColour;
|
||||
set
|
||||
{
|
||||
accentColour = value;
|
||||
text.FadeColour(value, 120, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
|
||||
protected override Container<Drawable> Content { get; }
|
||||
|
||||
[Resolved]
|
||||
private OverlayColourProvider colourProvider { get; set; }
|
||||
|
||||
private readonly OsuSpriteText text;
|
||||
|
||||
public OverlayRulesetTabItem(RulesetInfo value)
|
||||
: base(value)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
AddRangeInternal(new Drawable[]
|
||||
{
|
||||
Content = new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(3, 0),
|
||||
Child = text = new OsuSpriteText
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Text = value.Name,
|
||||
}
|
||||
},
|
||||
new HoverClickSounds()
|
||||
});
|
||||
|
||||
Enabled.Value = true;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
updateState();
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
base.OnHover(e);
|
||||
updateState();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
base.OnHoverLost(e);
|
||||
updateState();
|
||||
}
|
||||
|
||||
protected override void OnActivated() => updateState();
|
||||
|
||||
protected override void OnDeactivated() => updateState();
|
||||
|
||||
private void updateState()
|
||||
{
|
||||
text.Font = text.Font.With(weight: Active.Value ? FontWeight.Bold : FontWeight.Medium);
|
||||
AccentColour = IsHovered || Active.Value ? Color4.White : colourProvider.Highlight1;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
@ -82,7 +82,7 @@ namespace osu.Game.Overlays.Profile.Header
|
||||
else
|
||||
{
|
||||
topLinkContainer.AddText("Joined ");
|
||||
topLinkContainer.AddText(new DrawableDate(user.JoinDate), embolden);
|
||||
topLinkContainer.AddText(new DrawableDate(user.JoinDate, italic: false), embolden);
|
||||
}
|
||||
|
||||
addSpacer(topLinkContainer);
|
||||
@ -95,7 +95,7 @@ namespace osu.Game.Overlays.Profile.Header
|
||||
else if (user.LastVisit.HasValue)
|
||||
{
|
||||
topLinkContainer.AddText("Last seen ");
|
||||
topLinkContainer.AddText(new DrawableDate(user.LastVisit.Value), embolden);
|
||||
topLinkContainer.AddText(new DrawableDate(user.LastVisit.Value, italic: false), embolden);
|
||||
|
||||
addSpacer(topLinkContainer);
|
||||
}
|
||||
@ -111,34 +111,42 @@ namespace osu.Game.Overlays.Profile.Header
|
||||
topLinkContainer.AddText("Contributed ");
|
||||
topLinkContainer.AddLink($@"{user.PostCount:#,##0} forum posts", $"https://osu.ppy.sh/users/{user.Id}/posts", creationParameters: embolden);
|
||||
|
||||
string websiteWithoutProtcol = user.Website;
|
||||
string websiteWithoutProtocol = user.Website;
|
||||
|
||||
if (!string.IsNullOrEmpty(websiteWithoutProtcol))
|
||||
if (!string.IsNullOrEmpty(websiteWithoutProtocol))
|
||||
{
|
||||
if (Uri.TryCreate(websiteWithoutProtcol, UriKind.Absolute, out var uri))
|
||||
if (Uri.TryCreate(websiteWithoutProtocol, UriKind.Absolute, out var uri))
|
||||
{
|
||||
websiteWithoutProtcol = uri.Host + uri.PathAndQuery + uri.Fragment;
|
||||
websiteWithoutProtcol = websiteWithoutProtcol.TrimEnd('/');
|
||||
websiteWithoutProtocol = uri.Host + uri.PathAndQuery + uri.Fragment;
|
||||
websiteWithoutProtocol = websiteWithoutProtocol.TrimEnd('/');
|
||||
}
|
||||
}
|
||||
|
||||
tryAddInfo(FontAwesome.Solid.MapMarker, user.Location);
|
||||
tryAddInfo(OsuIcon.Heart, user.Interests);
|
||||
tryAddInfo(FontAwesome.Solid.Suitcase, user.Occupation);
|
||||
bottomLinkContainer.NewLine();
|
||||
bool anyInfoAdded = false;
|
||||
|
||||
anyInfoAdded |= tryAddInfo(FontAwesome.Solid.MapMarker, user.Location);
|
||||
anyInfoAdded |= tryAddInfo(OsuIcon.Heart, user.Interests);
|
||||
anyInfoAdded |= tryAddInfo(FontAwesome.Solid.Suitcase, user.Occupation);
|
||||
|
||||
if (anyInfoAdded)
|
||||
bottomLinkContainer.NewLine();
|
||||
|
||||
if (!string.IsNullOrEmpty(user.Twitter))
|
||||
tryAddInfo(FontAwesome.Brands.Twitter, "@" + user.Twitter, $@"https://twitter.com/{user.Twitter}");
|
||||
tryAddInfo(FontAwesome.Brands.Discord, user.Discord);
|
||||
tryAddInfo(FontAwesome.Brands.Skype, user.Skype, @"skype:" + user.Skype + @"?chat");
|
||||
tryAddInfo(FontAwesome.Brands.Lastfm, user.Lastfm, $@"https://last.fm/users/{user.Lastfm}");
|
||||
tryAddInfo(FontAwesome.Solid.Link, websiteWithoutProtcol, user.Website);
|
||||
anyInfoAdded |= tryAddInfo(FontAwesome.Brands.Twitter, "@" + user.Twitter, $@"https://twitter.com/{user.Twitter}");
|
||||
anyInfoAdded |= tryAddInfo(FontAwesome.Brands.Discord, user.Discord);
|
||||
anyInfoAdded |= tryAddInfo(FontAwesome.Brands.Skype, user.Skype, @"skype:" + user.Skype + @"?chat");
|
||||
anyInfoAdded |= tryAddInfo(FontAwesome.Brands.Lastfm, user.Lastfm, $@"https://last.fm/users/{user.Lastfm}");
|
||||
anyInfoAdded |= tryAddInfo(FontAwesome.Solid.Link, websiteWithoutProtocol, user.Website);
|
||||
|
||||
// If no information was added to the bottomLinkContainer, hide it to avoid unwanted padding
|
||||
bottomLinkContainer.Alpha = anyInfoAdded ? 1 : 0;
|
||||
}
|
||||
|
||||
private void addSpacer(OsuTextFlowContainer textFlow) => textFlow.AddArbitraryDrawable(new Container { Width = 15 });
|
||||
|
||||
private void tryAddInfo(IconUsage icon, string content, string link = null)
|
||||
private bool tryAddInfo(IconUsage icon, string content, string link = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(content)) return;
|
||||
if (string.IsNullOrEmpty(content)) return false;
|
||||
|
||||
// newlines could be contained in API returned user content.
|
||||
content = content.Replace("\n", " ");
|
||||
@ -155,6 +163,7 @@ namespace osu.Game.Overlays.Profile.Header
|
||||
bottomLinkContainer.AddText(" " + content, embolden);
|
||||
|
||||
addSpacer(bottomLinkContainer);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void embolden(SpriteText text) => text.Font = text.Font.With(weight: FontWeight.Bold);
|
||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
@ -42,7 +42,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
BackgroundColour = Color4.Black,
|
||||
Direction = BarDirection.LeftToRight,
|
||||
AccentColour = colours.Yellow
|
||||
AccentColour = colourProvider.Highlight1
|
||||
}
|
||||
},
|
||||
levelProgressText = new OsuSpriteText
|
||||
|
@ -2,12 +2,11 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Profile.Header.Components
|
||||
{
|
||||
@ -24,9 +23,6 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
||||
{
|
||||
AutoSizeAxes = Axes.X;
|
||||
|
||||
IdleColour = Color4.Black;
|
||||
HoverColour = OsuColour.Gray(0.1f);
|
||||
|
||||
base.Content.Add(new CircularContainer
|
||||
{
|
||||
Masking = true,
|
||||
@ -47,5 +43,12 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
IdleColour = colourProvider.Background6;
|
||||
HoverColour = colourProvider.Background5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,63 +1,29 @@
|
||||
// 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.Containers;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Profile.Header.Components
|
||||
{
|
||||
public class ProfileRulesetSelector : RulesetSelector
|
||||
public class ProfileRulesetSelector : OverlayRulesetSelector
|
||||
{
|
||||
private Color4 accentColour = Color4.White;
|
||||
|
||||
public readonly Bindable<User> User = new Bindable<User>();
|
||||
|
||||
public ProfileRulesetSelector()
|
||||
{
|
||||
TabContainer.Masking = false;
|
||||
TabContainer.Spacing = new Vector2(10, 0);
|
||||
AutoSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
accentColour = colours.Seafoam;
|
||||
|
||||
foreach (TabItem<RulesetInfo> tabItem in TabContainer)
|
||||
((ProfileRulesetTabItem)tabItem).AccentColour = accentColour;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
User.BindValueChanged(u => SetDefaultRuleset(Rulesets.GetRuleset(u.NewValue?.PlayMode ?? "osu")), true);
|
||||
}
|
||||
|
||||
public void SetDefaultRuleset(RulesetInfo ruleset)
|
||||
{
|
||||
foreach (TabItem<RulesetInfo> tabItem in TabContainer)
|
||||
foreach (var tabItem in TabContainer)
|
||||
((ProfileRulesetTabItem)tabItem).IsDefault = ((ProfileRulesetTabItem)tabItem).Value.ID == ruleset.ID;
|
||||
}
|
||||
|
||||
protected override TabItem<RulesetInfo> CreateTabItem(RulesetInfo value) => new ProfileRulesetTabItem(value)
|
||||
{
|
||||
AccentColour = accentColour
|
||||
};
|
||||
|
||||
protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
|
||||
{
|
||||
Direction = FillDirection.Horizontal,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
};
|
||||
protected override TabItem<RulesetInfo> CreateTabItem(RulesetInfo value) => new ProfileRulesetTabItem(value);
|
||||
}
|
||||
}
|
||||
|
@ -2,40 +2,15 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Rulesets;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Profile.Header.Components
|
||||
{
|
||||
public class ProfileRulesetTabItem : TabItem<RulesetInfo>, IHasAccentColour
|
||||
public class ProfileRulesetTabItem : OverlayRulesetTabItem
|
||||
{
|
||||
private readonly OsuSpriteText text;
|
||||
private readonly SpriteIcon icon;
|
||||
|
||||
private Color4 accentColour;
|
||||
|
||||
public Color4 AccentColour
|
||||
{
|
||||
get => accentColour;
|
||||
set
|
||||
{
|
||||
if (accentColour == value)
|
||||
return;
|
||||
|
||||
accentColour = value;
|
||||
|
||||
updateState();
|
||||
}
|
||||
}
|
||||
|
||||
private bool isDefault;
|
||||
|
||||
public bool IsDefault
|
||||
@ -52,74 +27,30 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
||||
}
|
||||
}
|
||||
|
||||
protected override Color4 AccentColour
|
||||
{
|
||||
get => base.AccentColour;
|
||||
set
|
||||
{
|
||||
base.AccentColour = value;
|
||||
icon.FadeColour(value, 120, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly SpriteIcon icon;
|
||||
|
||||
public ProfileRulesetTabItem(RulesetInfo value)
|
||||
: base(value)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
Children = new Drawable[]
|
||||
Add(icon = new SpriteIcon
|
||||
{
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(3, 0),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
text = new OsuSpriteText
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Text = value.Name,
|
||||
},
|
||||
icon = new SpriteIcon
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true,
|
||||
Icon = FontAwesome.Solid.Star,
|
||||
Size = new Vector2(12),
|
||||
},
|
||||
}
|
||||
},
|
||||
new HoverClickSounds()
|
||||
};
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
base.OnHover(e);
|
||||
updateState();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
base.OnHoverLost(e);
|
||||
updateState();
|
||||
}
|
||||
|
||||
protected override void OnActivated() => updateState();
|
||||
|
||||
protected override void OnDeactivated() => updateState();
|
||||
|
||||
private void updateState()
|
||||
{
|
||||
text.Font = text.Font.With(weight: Active.Value ? FontWeight.Bold : FontWeight.Medium);
|
||||
|
||||
if (IsHovered || Active.Value)
|
||||
{
|
||||
text.FadeColour(Color4.White, 120, Easing.InQuad);
|
||||
icon.FadeColour(Color4.White, 120, Easing.InQuad);
|
||||
}
|
||||
else
|
||||
{
|
||||
text.FadeColour(AccentColour, 120, Easing.InQuad);
|
||||
icon.FadeColour(AccentColour, 120, Easing.InQuad);
|
||||
}
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true,
|
||||
Icon = FontAwesome.Solid.Star,
|
||||
Size = new Vector2(12),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -167,9 +167,9 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
private void load(OverlayColourProvider colourProvider, OsuColour colours)
|
||||
{
|
||||
ballBg.Colour = colours.GreySeafoamDarker;
|
||||
ballBg.Colour = colourProvider.Background5;
|
||||
movingBall.BorderColour = line.Colour = colours.Yellow;
|
||||
}
|
||||
|
||||
@ -270,7 +270,9 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
background.Colour = colours.GreySeafoamDark;
|
||||
// Temporary colour since it's currently impossible to change it without bugs (see https://github.com/ppy/osu-framework/issues/3231)
|
||||
// If above is fixed, this should use OverlayColourProvider
|
||||
background.Colour = colours.Gray1;
|
||||
}
|
||||
|
||||
public bool SetContent(object content)
|
||||
|
@ -10,7 +10,7 @@ using osu.Game.Graphics.Containers;
|
||||
namespace osu.Game.Overlays.Profile.Sections
|
||||
{
|
||||
/// <summary>
|
||||
/// Display artist/title/mapper information, commonly used as the left portion of a profile or score display row (see <see cref="DrawableProfileRow"/>).
|
||||
/// Display artist/title/mapper information, commonly used as the left portion of a profile or score display row.
|
||||
/// </summary>
|
||||
public abstract class BeatmapMetadataContainer : OsuHoverContainer
|
||||
{
|
||||
|
@ -1,124 +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.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Profile.Sections
|
||||
{
|
||||
public abstract class DrawableProfileRow : Container
|
||||
{
|
||||
private const int fade_duration = 200;
|
||||
|
||||
private Box underscoreLine;
|
||||
private Box coloredBackground;
|
||||
private Container background;
|
||||
|
||||
/// <summary>
|
||||
/// A visual element displayed to the left of <see cref="LeftFlowContainer"/> content.
|
||||
/// </summary>
|
||||
protected abstract Drawable CreateLeftVisual();
|
||||
|
||||
protected FillFlowContainer LeftFlowContainer { get; private set; }
|
||||
protected FillFlowContainer RightFlowContainer { get; private set; }
|
||||
|
||||
protected override Container<Drawable> Content { get; }
|
||||
|
||||
protected DrawableProfileRow()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = 60;
|
||||
|
||||
Content = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Width = 0.97f,
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(OsuColour colour)
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
background = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
CornerRadius = 3,
|
||||
Alpha = 0,
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Offset = new Vector2(0f, 1f),
|
||||
Radius = 1f,
|
||||
Colour = Color4.Black.Opacity(0.2f),
|
||||
},
|
||||
Child = coloredBackground = new Box { RelativeSizeAxes = Axes.Both }
|
||||
},
|
||||
Content,
|
||||
underscoreLine = new Box
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 1,
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Children = new[]
|
||||
{
|
||||
CreateLeftVisual(),
|
||||
LeftFlowContainer = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Margin = new MarginPadding { Left = 10 },
|
||||
Direction = FillDirection.Vertical,
|
||||
},
|
||||
}
|
||||
},
|
||||
RightFlowContainer = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Direction = FillDirection.Vertical,
|
||||
},
|
||||
};
|
||||
|
||||
coloredBackground.Colour = underscoreLine.Colour = colour.Gray4;
|
||||
}
|
||||
|
||||
protected override bool OnClick(ClickEvent e) => true;
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
background.FadeIn(fade_duration, Easing.OutQuint);
|
||||
underscoreLine.FadeOut(fade_duration, Easing.OutQuint);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
background.FadeOut(fade_duration, Easing.OutQuint);
|
||||
underscoreLine.FadeIn(fade_duration, Easing.OutQuint);
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -44,8 +44,8 @@ namespace osu.Game.Overlays.Profile.Sections
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
background.Colour = idleColour = colourProvider.Background4;
|
||||
hoverColour = colourProvider.Background3;
|
||||
background.Colour = idleColour = colourProvider.Background3;
|
||||
hoverColour = colourProvider.Background2;
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
|
@ -1,10 +1,10 @@
|
||||
// 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.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
@ -16,14 +16,16 @@ using osu.Game.Online.Leaderboards;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Scoring;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Profile.Sections.Ranks
|
||||
{
|
||||
public class DrawableProfileScore : CompositeDrawable
|
||||
{
|
||||
private const int height = 40;
|
||||
private const int performance_width = 80;
|
||||
private const int content_padding = 10;
|
||||
|
||||
private const float performance_background_shear = 0.45f;
|
||||
private static readonly float performance_background_width = performance_width + (height / 4f * MathF.Tan(performance_background_shear));
|
||||
|
||||
protected readonly ScoreInfo Score;
|
||||
|
||||
@ -38,7 +40,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
|
||||
Score = score;
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = 40;
|
||||
Height = height;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -51,7 +53,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Left = content_padding, Right = performance_width + content_padding },
|
||||
Padding = new MarginPadding { Left = 10, Right = performance_width + 30 },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
@ -142,20 +144,26 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Size = new Vector2(1, 0.5f),
|
||||
Colour = Color4.Black.Opacity(0.5f),
|
||||
Shear = new Vector2(-0.45f, 0),
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = performance_background_width,
|
||||
Height = 0.5f,
|
||||
Colour = colourProvider.Background4,
|
||||
Shear = new Vector2(-performance_background_shear, 0),
|
||||
EdgeSmoothness = new Vector2(2, 0),
|
||||
},
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
RelativePositionAxes = Axes.Y,
|
||||
Size = new Vector2(1, -0.5f),
|
||||
Width = performance_background_width,
|
||||
Height = -0.5f,
|
||||
Position = new Vector2(0, 1),
|
||||
Colour = Color4.Black.Opacity(0.5f),
|
||||
Shear = new Vector2(0.45f, 0),
|
||||
Colour = colourProvider.Background4,
|
||||
Shear = new Vector2(performance_background_shear, 0),
|
||||
EdgeSmoothness = new Vector2(2, 0),
|
||||
},
|
||||
createDrawablePerformance().With(d =>
|
||||
|
@ -1,9 +1,11 @@
|
||||
// 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.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.API;
|
||||
@ -11,12 +13,19 @@ using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Chat;
|
||||
using osu.Game.Online.Leaderboards;
|
||||
using osu.Game.Rulesets;
|
||||
|
||||
namespace osu.Game.Overlays.Profile.Sections.Recent
|
||||
{
|
||||
public class DrawableRecentActivity : DrawableProfileRow
|
||||
public class DrawableRecentActivity : CompositeDrawable
|
||||
{
|
||||
private IAPIProvider api;
|
||||
private const int font_size = 14;
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private RulesetStore rulesets { get; set; }
|
||||
|
||||
private readonly APIRecentActivity activity;
|
||||
|
||||
@ -28,139 +37,191 @@ namespace osu.Game.Overlays.Profile.Sections.Recent
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IAPIProvider api)
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
this.api = api;
|
||||
|
||||
LeftFlowContainer.Padding = new MarginPadding { Left = 10, Right = 160 };
|
||||
|
||||
LeftFlowContainer.Add(content = new LinkFlowContainer
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
AddInternal(new GridContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.Absolute, size: 28),
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.AutoSize)
|
||||
},
|
||||
RowDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.AutoSize)
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Child = createIcon().With(icon =>
|
||||
{
|
||||
icon.Anchor = Anchor.Centre;
|
||||
icon.Origin = Anchor.Centre;
|
||||
})
|
||||
},
|
||||
content = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: font_size))
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
},
|
||||
new DrawableDate(activity.CreatedAt)
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Colour = colourProvider.Foreground1,
|
||||
Font = OsuFont.GetFont(size: font_size),
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
RightFlowContainer.Add(new DrawableDate(activity.CreatedAt)
|
||||
{
|
||||
Font = OsuFont.GetFont(size: 13),
|
||||
Colour = OsuColour.Gray(0xAA),
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
});
|
||||
|
||||
var formatted = createMessage();
|
||||
|
||||
content.AddLinks(formatted.Text, formatted.Links);
|
||||
createMessage();
|
||||
}
|
||||
|
||||
protected override Drawable CreateLeftVisual()
|
||||
private Drawable createIcon()
|
||||
{
|
||||
switch (activity.Type)
|
||||
{
|
||||
case RecentActivityType.Rank:
|
||||
return new UpdateableRank(activity.ScoreRank)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = 60,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 11,
|
||||
FillMode = FillMode.Fit,
|
||||
Margin = new MarginPadding { Top = 2 }
|
||||
};
|
||||
|
||||
case RecentActivityType.Achievement:
|
||||
return new DelayedLoadWrapper(new MedalIcon(activity.Achievement.Slug)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
FillMode = FillMode.Fit,
|
||||
})
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = 60,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.5f,
|
||||
Height = 18
|
||||
};
|
||||
|
||||
default:
|
||||
return new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = 60,
|
||||
FillMode = FillMode.Fit,
|
||||
};
|
||||
return Empty();
|
||||
}
|
||||
}
|
||||
|
||||
private string toAbsoluteUrl(string url) => $"{api.Endpoint}{url}";
|
||||
|
||||
private MessageFormatter.MessageFormatterResult createMessage()
|
||||
private void createMessage()
|
||||
{
|
||||
string userLinkTemplate() => $"[{toAbsoluteUrl(activity.User?.Url)} {activity.User?.Username}]";
|
||||
string beatmapLinkTemplate() => $"[{toAbsoluteUrl(activity.Beatmap?.Url)} {activity.Beatmap?.Title}]";
|
||||
string beatmapsetLinkTemplate() => $"[{toAbsoluteUrl(activity.Beatmapset?.Url)} {activity.Beatmapset?.Title}]";
|
||||
|
||||
string message;
|
||||
|
||||
switch (activity.Type)
|
||||
{
|
||||
case RecentActivityType.Achievement:
|
||||
message = $"{userLinkTemplate()} unlocked the {activity.Achievement.Name} medal!";
|
||||
addUserLink();
|
||||
addText($" unlocked the \"{activity.Achievement.Name}\" medal!");
|
||||
break;
|
||||
|
||||
case RecentActivityType.BeatmapPlaycount:
|
||||
message = $"{beatmapLinkTemplate()} has been played {activity.Count} times!";
|
||||
addBeatmapLink();
|
||||
addText($" has been played {activity.Count} times!");
|
||||
break;
|
||||
|
||||
case RecentActivityType.BeatmapsetApprove:
|
||||
message = $"{beatmapsetLinkTemplate()} has been {activity.Approval.ToString().ToLowerInvariant()}!";
|
||||
addBeatmapsetLink();
|
||||
addText($" has been {activity.Approval.ToString().ToLowerInvariant()}!");
|
||||
break;
|
||||
|
||||
case RecentActivityType.BeatmapsetDelete:
|
||||
message = $"{beatmapsetLinkTemplate()} has been deleted.";
|
||||
addBeatmapsetLink();
|
||||
addText(" has been deleted.");
|
||||
break;
|
||||
|
||||
case RecentActivityType.BeatmapsetRevive:
|
||||
message = $"{beatmapsetLinkTemplate()} has been revived from eternal slumber by {userLinkTemplate()}.";
|
||||
addBeatmapsetLink();
|
||||
addText(" has been revived from eternal slumber by ");
|
||||
addUserLink();
|
||||
break;
|
||||
|
||||
case RecentActivityType.BeatmapsetUpdate:
|
||||
message = $"{userLinkTemplate()} has updated the beatmap {beatmapsetLinkTemplate()}!";
|
||||
addUserLink();
|
||||
addText(" has updated the beatmap ");
|
||||
addBeatmapsetLink();
|
||||
break;
|
||||
|
||||
case RecentActivityType.BeatmapsetUpload:
|
||||
message = $"{userLinkTemplate()} has submitted a new beatmap {beatmapsetLinkTemplate()}!";
|
||||
addUserLink();
|
||||
addText(" has submitted a new beatmap ");
|
||||
addBeatmapsetLink();
|
||||
break;
|
||||
|
||||
case RecentActivityType.Medal:
|
||||
// apparently this shouldn't exist look at achievement instead (https://github.com/ppy/osu-web/blob/master/resources/assets/coffee/react/profile-page/recent-activity.coffee#L111)
|
||||
message = string.Empty;
|
||||
break;
|
||||
|
||||
case RecentActivityType.Rank:
|
||||
message = $"{userLinkTemplate()} achieved rank #{activity.Rank} on {beatmapLinkTemplate()} ({activity.Mode}!)";
|
||||
addUserLink();
|
||||
addText($" achieved rank #{activity.Rank} on ");
|
||||
addBeatmapLink();
|
||||
addText($" ({getRulesetName()})");
|
||||
break;
|
||||
|
||||
case RecentActivityType.RankLost:
|
||||
message = $"{userLinkTemplate()} has lost first place on {beatmapLinkTemplate()} ({activity.Mode}!)";
|
||||
addUserLink();
|
||||
addText(" has lost first place on ");
|
||||
addBeatmapLink();
|
||||
addText($" ({getRulesetName()})");
|
||||
break;
|
||||
|
||||
case RecentActivityType.UserSupportAgain:
|
||||
message = $"{userLinkTemplate()} has once again chosen to support osu! - thanks for your generosity!";
|
||||
addUserLink();
|
||||
addText(" has once again chosen to support osu! - thanks for your generosity!");
|
||||
break;
|
||||
|
||||
case RecentActivityType.UserSupportFirst:
|
||||
message = $"{userLinkTemplate()} has become an osu!supporter - thanks for your generosity!";
|
||||
addUserLink();
|
||||
addText(" has become an osu!supporter - thanks for your generosity!");
|
||||
break;
|
||||
|
||||
case RecentActivityType.UserSupportGift:
|
||||
message = $"{userLinkTemplate()} has received the gift of osu!supporter!";
|
||||
addUserLink();
|
||||
addText(" has received the gift of osu!supporter!");
|
||||
break;
|
||||
|
||||
case RecentActivityType.UsernameChange:
|
||||
message = $"{activity.User?.PreviousUsername} has changed their username to {userLinkTemplate()}!";
|
||||
break;
|
||||
|
||||
default:
|
||||
message = string.Empty;
|
||||
addText($"{activity.User?.PreviousUsername} has changed their username to ");
|
||||
addUserLink();
|
||||
break;
|
||||
}
|
||||
|
||||
return MessageFormatter.FormatText(message);
|
||||
}
|
||||
|
||||
private string getRulesetName() =>
|
||||
rulesets.AvailableRulesets.FirstOrDefault(r => r.ShortName == activity.Mode)?.Name ?? activity.Mode;
|
||||
|
||||
private void addUserLink()
|
||||
=> content.AddLink(activity.User?.Username, LinkAction.OpenUserProfile, getLinkArgument(activity.User?.Url), creationParameters: t => t.Font = getLinkFont(FontWeight.Bold));
|
||||
|
||||
private void addBeatmapLink()
|
||||
=> content.AddLink(activity.Beatmap?.Title, LinkAction.OpenBeatmap, getLinkArgument(activity.Beatmap?.Url), creationParameters: t => t.Font = getLinkFont());
|
||||
|
||||
private void addBeatmapsetLink()
|
||||
=> content.AddLink(activity.Beatmapset?.Title, LinkAction.OpenBeatmapSet, getLinkArgument(activity.Beatmapset?.Url), creationParameters: t => t.Font = getLinkFont());
|
||||
|
||||
private string getLinkArgument(string url) => MessageFormatter.GetLinkDetails($"{api.Endpoint}{url}").Argument;
|
||||
|
||||
private FontUsage getLinkFont(FontWeight fontWeight = FontWeight.Regular)
|
||||
=> OsuFont.GetFont(size: font_size, weight: fontWeight, italics: true);
|
||||
|
||||
private void addText(string text)
|
||||
=> content.AddText(text, t => t.Font = OsuFont.GetFont(size: font_size, weight: FontWeight.SemiBold));
|
||||
}
|
||||
}
|
||||
|
@ -23,8 +23,7 @@ namespace osu.Game.Overlays.Profile.Sections.Recent
|
||||
|
||||
Child = sprite = new Sprite
|
||||
{
|
||||
Height = 40,
|
||||
Width = 40,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
};
|
||||
|
@ -8,6 +8,7 @@ using osu.Framework.Bindables;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.API;
|
||||
using System.Collections.Generic;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Overlays.Profile.Sections.Recent
|
||||
{
|
||||
@ -16,7 +17,8 @@ namespace osu.Game.Overlays.Profile.Sections.Recent
|
||||
public PaginatedRecentActivityContainer(Bindable<User> user, string header, string missing)
|
||||
: base(user, header, missing)
|
||||
{
|
||||
ItemsPerPage = 5;
|
||||
ItemsPerPage = 10;
|
||||
ItemsContainer.Spacing = new Vector2(0, 8);
|
||||
}
|
||||
|
||||
protected override APIRequest<List<APIRecentActivity>> CreateRequest() =>
|
||||
|
@ -67,7 +67,7 @@ namespace osu.Game.Overlays
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Height = 30
|
||||
Height = 34
|
||||
};
|
||||
|
||||
Add(new Box
|
||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Objects
|
||||
/// </summary>
|
||||
public event Action DefaultsApplied;
|
||||
|
||||
public readonly Bindable<double> StartTimeBindable = new Bindable<double>();
|
||||
public readonly Bindable<double> StartTimeBindable = new BindableDouble();
|
||||
|
||||
/// <summary>
|
||||
/// The time at which the HitObject starts.
|
||||
|
@ -36,7 +36,7 @@ namespace osu.Game.Screens.Backgrounds
|
||||
/// <summary>
|
||||
/// The amount of blur to be applied in addition to user-specified blur.
|
||||
/// </summary>
|
||||
public readonly Bindable<float> BlurAmount = new Bindable<float>();
|
||||
public readonly Bindable<float> BlurAmount = new BindableFloat();
|
||||
|
||||
internal readonly IBindable<bool> IsBreakTime = new Bindable<bool>();
|
||||
|
||||
@ -119,7 +119,7 @@ namespace osu.Game.Screens.Backgrounds
|
||||
/// <remarks>
|
||||
/// Used in contexts where there can potentially be both user and screen-specified blurring occuring at the same time, such as in <see cref="PlayerLoader"/>
|
||||
/// </remarks>
|
||||
public readonly Bindable<float> BlurAmount = new Bindable<float>();
|
||||
public readonly Bindable<float> BlurAmount = new BindableFloat();
|
||||
|
||||
public Background Background
|
||||
{
|
||||
|
@ -44,7 +44,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
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;
|
||||
var until = i + 1 < beatmap.ControlPointInfo.TimingPoints.Count ? beatmap.ControlPointInfo.TimingPoints[i + 1].Time : working.Value.Track.Length;
|
||||
|
||||
int beat = 0;
|
||||
|
||||
|
@ -96,7 +96,7 @@ namespace osu.Game.Screens.Multi
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Top = Header.HEIGHT },
|
||||
Child = screenStack = new OsuScreenStack(loungeSubScreen = new LoungeSubScreen()) { RelativeSizeAxes = Axes.Both }
|
||||
Child = screenStack = new OsuScreenStack { RelativeSizeAxes = Axes.Both }
|
||||
},
|
||||
new Header(screenStack),
|
||||
createButton = new HeaderButton
|
||||
@ -120,6 +120,8 @@ namespace osu.Game.Screens.Multi
|
||||
}
|
||||
};
|
||||
|
||||
screenStack.Push(loungeSubScreen = new LoungeSubScreen());
|
||||
|
||||
screenStack.ScreenPushed += screenPushed;
|
||||
screenStack.ScreenExited += screenExited;
|
||||
}
|
||||
|
@ -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 System.Collections.Generic;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using osu.Framework.Allocation;
|
||||
@ -95,15 +96,30 @@ namespace osu.Game.Screens
|
||||
|
||||
public Bindable<IReadOnlyList<Mod>> Mods { get; private set; }
|
||||
|
||||
private OsuScreenDependencies screenDependencies;
|
||||
|
||||
internal void CreateLeasedDependencies(IReadOnlyDependencyContainer dependencies) => createDependencies(dependencies);
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
var screenDependencies = new OsuScreenDependencies(DisallowExternalBeatmapRulesetChanges, parent);
|
||||
if (screenDependencies == null)
|
||||
{
|
||||
if (DisallowExternalBeatmapRulesetChanges)
|
||||
throw new InvalidOperationException($"Screens that specify {nameof(DisallowExternalBeatmapRulesetChanges)} must be pushed immediately.");
|
||||
|
||||
createDependencies(parent);
|
||||
}
|
||||
|
||||
return base.CreateChildDependencies(screenDependencies);
|
||||
}
|
||||
|
||||
private void createDependencies(IReadOnlyDependencyContainer dependencies)
|
||||
{
|
||||
screenDependencies = new OsuScreenDependencies(DisallowExternalBeatmapRulesetChanges, dependencies);
|
||||
|
||||
Beatmap = screenDependencies.Beatmap;
|
||||
Ruleset = screenDependencies.Ruleset;
|
||||
Mods = screenDependencies.Mods;
|
||||
|
||||
return base.CreateChildDependencies(screenDependencies);
|
||||
}
|
||||
|
||||
protected BackgroundScreen Background => backgroundStack?.CurrentScreen as BackgroundScreen;
|
||||
|
@ -13,22 +13,11 @@ namespace osu.Game.Screens
|
||||
[Cached]
|
||||
private BackgroundScreenStack backgroundScreenStack;
|
||||
|
||||
private ParallaxContainer parallaxContainer;
|
||||
private readonly ParallaxContainer parallaxContainer;
|
||||
|
||||
protected float ParallaxAmount => parallaxContainer.ParallaxAmount;
|
||||
|
||||
public OsuScreenStack()
|
||||
{
|
||||
initializeStack();
|
||||
}
|
||||
|
||||
public OsuScreenStack(IScreen baseScreen)
|
||||
: base(baseScreen)
|
||||
{
|
||||
initializeStack();
|
||||
}
|
||||
|
||||
private void initializeStack()
|
||||
{
|
||||
InternalChild = parallaxContainer = new ParallaxContainer
|
||||
{
|
||||
@ -36,13 +25,32 @@ namespace osu.Game.Screens
|
||||
Child = backgroundScreenStack = new BackgroundScreenStack { RelativeSizeAxes = Axes.Both },
|
||||
};
|
||||
|
||||
ScreenPushed += onScreenChange;
|
||||
ScreenExited += onScreenChange;
|
||||
ScreenPushed += screenPushed;
|
||||
ScreenExited += screenExited;
|
||||
}
|
||||
|
||||
private void onScreenChange(IScreen prev, IScreen next)
|
||||
private void screenPushed(IScreen prev, IScreen next)
|
||||
{
|
||||
parallaxContainer.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT * ((IOsuScreen)next)?.BackgroundParallaxAmount ?? 1.0f;
|
||||
if (LoadState < LoadState.Ready)
|
||||
{
|
||||
// dependencies must be present to stay in a sane state.
|
||||
// this is generally only ever hit by test scenes.
|
||||
Schedule(() => screenPushed(prev, next));
|
||||
return;
|
||||
}
|
||||
|
||||
// create dependencies synchronously to ensure leases are in a sane state.
|
||||
((OsuScreen)next).CreateLeasedDependencies((prev as OsuScreen)?.Dependencies ?? Dependencies);
|
||||
|
||||
setParallax(next);
|
||||
}
|
||||
|
||||
private void screenExited(IScreen prev, IScreen next)
|
||||
{
|
||||
setParallax(next);
|
||||
}
|
||||
|
||||
private void setParallax(IScreen next) =>
|
||||
parallaxContainer.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT * ((IOsuScreen)next)?.BackgroundParallaxAmount ?? 1.0f;
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using System.Linq;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Overlays.Settings;
|
||||
|
||||
@ -26,7 +27,8 @@ namespace osu.Game.Screens.Select.Details
|
||||
[Resolved]
|
||||
private IBindable<IReadOnlyList<Mod>> mods { get; set; }
|
||||
|
||||
private readonly StatisticRow firstValue, hpDrain, accuracy, approachRate, starDifficulty;
|
||||
protected readonly StatisticRow FirstValue, HpDrain, Accuracy, ApproachRate;
|
||||
private readonly StatisticRow starDifficulty;
|
||||
|
||||
private BeatmapInfo beatmap;
|
||||
|
||||
@ -52,10 +54,10 @@ namespace osu.Game.Screens.Select.Details
|
||||
Spacing = new Vector2(4f),
|
||||
Children = new[]
|
||||
{
|
||||
firstValue = new StatisticRow(), //circle size/key amount
|
||||
hpDrain = new StatisticRow { Title = "HP Drain" },
|
||||
accuracy = new StatisticRow { Title = "Accuracy" },
|
||||
approachRate = new StatisticRow { Title = "Approach Rate" },
|
||||
FirstValue = new StatisticRow(), //circle size/key amount
|
||||
HpDrain = new StatisticRow { Title = "HP Drain" },
|
||||
Accuracy = new StatisticRow { Title = "Accuracy" },
|
||||
ApproachRate = new StatisticRow { Title = "Approach Rate" },
|
||||
starDifficulty = new StatisticRow(10, true) { Title = "Star Difficulty" },
|
||||
},
|
||||
};
|
||||
@ -122,24 +124,24 @@ namespace osu.Game.Screens.Select.Details
|
||||
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);
|
||||
FirstValue.Title = "Key Count";
|
||||
FirstValue.Value = (baseDifficulty?.CircleSize ?? 0, null);
|
||||
break;
|
||||
|
||||
default:
|
||||
firstValue.Title = "Circle Size";
|
||||
firstValue.Value = (baseDifficulty?.CircleSize ?? 0, adjustedDifficulty?.CircleSize);
|
||||
FirstValue.Title = "Circle Size";
|
||||
FirstValue.Value = (baseDifficulty?.CircleSize ?? 0, adjustedDifficulty?.CircleSize);
|
||||
break;
|
||||
}
|
||||
|
||||
starDifficulty.Value = ((float)(Beatmap?.StarDifficulty ?? 0), null);
|
||||
|
||||
hpDrain.Value = (baseDifficulty?.DrainRate ?? 0, adjustedDifficulty?.DrainRate);
|
||||
accuracy.Value = (baseDifficulty?.OverallDifficulty ?? 0, adjustedDifficulty?.OverallDifficulty);
|
||||
approachRate.Value = (baseDifficulty?.ApproachRate ?? 0, adjustedDifficulty?.ApproachRate);
|
||||
HpDrain.Value = (baseDifficulty?.DrainRate ?? 0, adjustedDifficulty?.DrainRate);
|
||||
Accuracy.Value = (baseDifficulty?.OverallDifficulty ?? 0, adjustedDifficulty?.OverallDifficulty);
|
||||
ApproachRate.Value = (baseDifficulty?.ApproachRate ?? 0, adjustedDifficulty?.ApproachRate);
|
||||
}
|
||||
|
||||
private class StatisticRow : Container, IHasAccentColour
|
||||
public class StatisticRow : Container, IHasAccentColour
|
||||
{
|
||||
private const float value_width = 25;
|
||||
private const float name_width = 70;
|
||||
@ -147,7 +149,8 @@ namespace osu.Game.Screens.Select.Details
|
||||
private readonly float maxValue;
|
||||
private readonly bool forceDecimalPlaces;
|
||||
private readonly OsuSpriteText name, valueText;
|
||||
private readonly Bar bar, modBar;
|
||||
private readonly Bar bar;
|
||||
public readonly Bar ModBar;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
@ -173,14 +176,14 @@ namespace osu.Game.Screens.Select.Details
|
||||
bar.Length = value.baseValue / maxValue;
|
||||
|
||||
valueText.Text = (value.adjustedValue ?? value.baseValue).ToString(forceDecimalPlaces ? "0.00" : "0.##");
|
||||
modBar.Length = (value.adjustedValue ?? 0) / maxValue;
|
||||
ModBar.Length = (value.adjustedValue ?? 0) / maxValue;
|
||||
|
||||
if (value.adjustedValue > value.baseValue)
|
||||
modBar.AccentColour = valueText.Colour = colours.Red;
|
||||
if (Precision.AlmostEquals(value.baseValue, value.adjustedValue ?? value.baseValue, 0.05f))
|
||||
ModBar.AccentColour = valueText.Colour = Color4.White;
|
||||
else if (value.adjustedValue > value.baseValue)
|
||||
ModBar.AccentColour = valueText.Colour = colours.Red;
|
||||
else if (value.adjustedValue < value.baseValue)
|
||||
modBar.AccentColour = valueText.Colour = colours.BlueDark;
|
||||
else
|
||||
modBar.AccentColour = valueText.Colour = Color4.White;
|
||||
ModBar.AccentColour = valueText.Colour = colours.BlueDark;
|
||||
}
|
||||
}
|
||||
|
||||
@ -217,7 +220,7 @@ namespace osu.Game.Screens.Select.Details
|
||||
BackgroundColour = Color4.White.Opacity(0.5f),
|
||||
Padding = new MarginPadding { Left = name_width + 10, Right = value_width + 10 },
|
||||
},
|
||||
modBar = new Bar
|
||||
ModBar = new Bar
|
||||
{
|
||||
Origin = Anchor.CentreLeft,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
|
@ -149,8 +149,8 @@ namespace osu.Game.Screens.Select
|
||||
private readonly IBindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
|
||||
|
||||
private readonly Bindable<bool> showConverted = new Bindable<bool>();
|
||||
private readonly Bindable<double> minimumStars = new Bindable<double>();
|
||||
private readonly Bindable<double> maximumStars = new Bindable<double>();
|
||||
private readonly Bindable<double> minimumStars = new BindableDouble();
|
||||
private readonly Bindable<double> maximumStars = new BindableDouble();
|
||||
|
||||
public readonly Box Background;
|
||||
|
||||
|
@ -33,6 +33,8 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
public override void OnResuming(IScreen last)
|
||||
{
|
||||
base.OnResuming(last);
|
||||
|
||||
player = null;
|
||||
|
||||
if (removeAutoModOnResume)
|
||||
@ -41,8 +43,6 @@ namespace osu.Game.Screens.Select
|
||||
ModSelect.DeselectTypes(new[] { autoType }, true);
|
||||
removeAutoModOnResume = false;
|
||||
}
|
||||
|
||||
base.OnResuming(last);
|
||||
}
|
||||
|
||||
protected override bool OnStart()
|
||||
@ -68,10 +68,7 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
SampleConfirm?.Play();
|
||||
|
||||
LoadComponentAsync(player = new PlayerLoader(() => new Player()), l =>
|
||||
{
|
||||
if (this.IsCurrentScreen()) this.Push(player);
|
||||
});
|
||||
this.Push(player = new PlayerLoader(() => new Player()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -75,6 +75,9 @@ namespace osu.Game.Screens.Select
|
||||
[Resolved(canBeNull: true)]
|
||||
private NotificationOverlay notificationOverlay { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private Bindable<IReadOnlyList<Mod>> selectedMods { get; set; }
|
||||
|
||||
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap.Value);
|
||||
|
||||
protected BeatmapCarousel Carousel { get; private set; }
|
||||
@ -474,6 +477,8 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
this.FadeInFromZero(250);
|
||||
FilterControl.Activate();
|
||||
|
||||
ModSelect.SelectedMods.BindTo(selectedMods);
|
||||
}
|
||||
|
||||
private const double logo_transition = 250;
|
||||
@ -514,6 +519,12 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
public override void OnResuming(IScreen last)
|
||||
{
|
||||
base.OnResuming(last);
|
||||
|
||||
// required due to https://github.com/ppy/osu-framework/issues/3218
|
||||
ModSelect.SelectedMods.Disabled = false;
|
||||
ModSelect.SelectedMods.BindTo(selectedMods);
|
||||
|
||||
Carousel.AllowSelection = true;
|
||||
|
||||
BeatmapDetails.Leaderboard.RefreshScores();
|
||||
@ -529,8 +540,6 @@ namespace osu.Game.Screens.Select
|
||||
music?.Play();
|
||||
}
|
||||
|
||||
base.OnResuming(last);
|
||||
|
||||
this.FadeIn(250);
|
||||
|
||||
this.ScaleTo(1, 250, Easing.OutSine);
|
||||
@ -540,6 +549,7 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
public override void OnSuspending(IScreen next)
|
||||
{
|
||||
ModSelect.SelectedMods.UnbindFrom(selectedMods);
|
||||
ModSelect.Hide();
|
||||
|
||||
BeatmapOptions.Hide();
|
||||
|
@ -47,7 +47,7 @@ namespace osu.Game.Tests.Visual
|
||||
public readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
|
||||
IBindable<ScrollingDirection> IScrollingInfo.Direction => Direction;
|
||||
|
||||
public readonly Bindable<double> TimeRange = new Bindable<double>(1000) { Value = 1000 };
|
||||
public readonly Bindable<double> TimeRange = new BindableDouble(1000) { Value = 1000 };
|
||||
IBindable<double> IScrollingInfo.TimeRange => TimeRange;
|
||||
|
||||
public readonly TestScrollAlgorithm Algorithm = new TestScrollAlgorithm();
|
||||
|
@ -24,7 +24,7 @@
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1230.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2020.131.0" />
|
||||
<PackageReference Include="Sentry" Version="1.2.0" />
|
||||
<PackageReference Include="Sentry" Version="2.0.1" />
|
||||
<PackageReference Include="SharpCompress" Version="0.24.0" />
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="4.7.0" />
|
||||
|
Loading…
Reference in New Issue
Block a user