mirror of
https://github.com/ppy/osu.git
synced 2025-02-08 11:42:55 +08:00
Merge branch 'master' into fix-limit-distance-snap-to-current
This commit is contained in:
commit
ee7d281e73
@ -1,34 +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 Android.Content.PM;
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Game.Screens.Play;
|
|
||||||
|
|
||||||
namespace osu.Android
|
|
||||||
{
|
|
||||||
public partial class GameplayScreenRotationLocker : Component
|
|
||||||
{
|
|
||||||
private IBindable<LocalUserPlayingState> localUserPlaying = null!;
|
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private OsuGameActivity gameActivity { get; set; } = null!;
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(ILocalUserPlayInfo localUserPlayInfo)
|
|
||||||
{
|
|
||||||
localUserPlaying = localUserPlayInfo.PlayingState.GetBoundCopy();
|
|
||||||
localUserPlaying.BindValueChanged(updateLock, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateLock(ValueChangedEvent<LocalUserPlayingState> userPlaying)
|
|
||||||
{
|
|
||||||
gameActivity.RunOnUiThread(() =>
|
|
||||||
{
|
|
||||||
gameActivity.RequestedOrientation = userPlaying.NewValue == LocalUserPlayingState.Playing ? ScreenOrientation.Locked : gameActivity.DefaultOrientation;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -49,6 +49,8 @@ namespace osu.Android
|
|||||||
/// <remarks>Adjusted on startup to match expected UX for the current device type (phone/tablet).</remarks>
|
/// <remarks>Adjusted on startup to match expected UX for the current device type (phone/tablet).</remarks>
|
||||||
public ScreenOrientation DefaultOrientation = ScreenOrientation.Unspecified;
|
public ScreenOrientation DefaultOrientation = ScreenOrientation.Unspecified;
|
||||||
|
|
||||||
|
public new bool IsTablet { get; private set; }
|
||||||
|
|
||||||
private readonly OsuGameAndroid game;
|
private readonly OsuGameAndroid game;
|
||||||
|
|
||||||
private bool gameCreated;
|
private bool gameCreated;
|
||||||
@ -89,9 +91,9 @@ namespace osu.Android
|
|||||||
WindowManager.DefaultDisplay.GetSize(displaySize);
|
WindowManager.DefaultDisplay.GetSize(displaySize);
|
||||||
#pragma warning restore CA1422
|
#pragma warning restore CA1422
|
||||||
float smallestWidthDp = Math.Min(displaySize.X, displaySize.Y) / Resources.DisplayMetrics.Density;
|
float smallestWidthDp = Math.Min(displaySize.X, displaySize.Y) / Resources.DisplayMetrics.Density;
|
||||||
bool isTablet = smallestWidthDp >= 600f;
|
IsTablet = smallestWidthDp >= 600f;
|
||||||
|
|
||||||
RequestedOrientation = DefaultOrientation = isTablet ? ScreenOrientation.FullUser : ScreenOrientation.SensorLandscape;
|
RequestedOrientation = DefaultOrientation = IsTablet ? ScreenOrientation.FullUser : ScreenOrientation.SensorLandscape;
|
||||||
|
|
||||||
// Currently (SDK 6.0.200), BundleAssemblies is not runnable for net6-android.
|
// Currently (SDK 6.0.200), BundleAssemblies is not runnable for net6-android.
|
||||||
// The assembly files are not available as files either after native AOT.
|
// The assembly files are not available as files either after native AOT.
|
||||||
|
@ -3,11 +3,13 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Android.App;
|
using Android.App;
|
||||||
|
using Android.Content.PM;
|
||||||
using Microsoft.Maui.Devices;
|
using Microsoft.Maui.Devices;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.ObjectExtensions;
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Game;
|
using osu.Game;
|
||||||
|
using osu.Game.Screens;
|
||||||
using osu.Game.Updater;
|
using osu.Game.Updater;
|
||||||
using osu.Game.Utils;
|
using osu.Game.Utils;
|
||||||
|
|
||||||
@ -71,7 +73,35 @@ namespace osu.Android
|
|||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
LoadComponentAsync(new GameplayScreenRotationLocker(), Add);
|
UserPlayingState.BindValueChanged(_ => updateOrientation());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ScreenChanged(IOsuScreen? current, IOsuScreen? newScreen)
|
||||||
|
{
|
||||||
|
base.ScreenChanged(current, newScreen);
|
||||||
|
|
||||||
|
if (newScreen != null)
|
||||||
|
updateOrientation();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateOrientation()
|
||||||
|
{
|
||||||
|
var orientation = MobileUtils.GetOrientation(this, (IOsuScreen)ScreenStack.CurrentScreen, gameActivity.IsTablet);
|
||||||
|
|
||||||
|
switch (orientation)
|
||||||
|
{
|
||||||
|
case MobileUtils.Orientation.Locked:
|
||||||
|
gameActivity.RequestedOrientation = ScreenOrientation.Locked;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MobileUtils.Orientation.Portrait:
|
||||||
|
gameActivity.RequestedOrientation = ScreenOrientation.Portrait;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MobileUtils.Orientation.Default:
|
||||||
|
gameActivity.RequestedOrientation = gameActivity.DefaultOrientation;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void SetHost(GameHost host)
|
public override void SetHost(GameHost host)
|
||||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods
|
|||||||
{
|
{
|
||||||
CreateModTest(new ModTestData
|
CreateModTest(new ModTestData
|
||||||
{
|
{
|
||||||
Mod = new ManiaModHidden(),
|
Mod = new ManiaModFadeIn(),
|
||||||
PassCondition = () => checkCoverage(ManiaModHidden.MIN_COVERAGE)
|
PassCondition = () => checkCoverage(ManiaModHidden.MIN_COVERAGE)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods
|
|||||||
{
|
{
|
||||||
CreateModTest(new ModTestData
|
CreateModTest(new ModTestData
|
||||||
{
|
{
|
||||||
Mod = new ManiaModHidden(),
|
Mod = new ManiaModFadeIn(),
|
||||||
PassCondition = () => checkCoverage(ManiaModHidden.MIN_COVERAGE)
|
PassCondition = () => checkCoverage(ManiaModHidden.MIN_COVERAGE)
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods
|
|||||||
{
|
{
|
||||||
CreateModTest(new ModTestData
|
CreateModTest(new ModTestData
|
||||||
{
|
{
|
||||||
Mod = new ManiaModHidden(),
|
Mod = new ManiaModFadeIn(),
|
||||||
PassCondition = () => checkCoverage(ManiaModHidden.MAX_COVERAGE)
|
PassCondition = () => checkCoverage(ManiaModHidden.MAX_COVERAGE)
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods
|
|||||||
{
|
{
|
||||||
CreateModTest(new ModTestData
|
CreateModTest(new ModTestData
|
||||||
{
|
{
|
||||||
Mod = new ManiaModHidden(),
|
Mod = new ManiaModFadeIn(),
|
||||||
PassCondition = () => checkCoverage(ManiaModHidden.MAX_COVERAGE)
|
PassCondition = () => checkCoverage(ManiaModHidden.MAX_COVERAGE)
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods
|
|||||||
{
|
{
|
||||||
CreateModTest(new ModTestData
|
CreateModTest(new ModTestData
|
||||||
{
|
{
|
||||||
Mod = new ManiaModHidden(),
|
Mod = new ManiaModFadeIn(),
|
||||||
CreateBeatmap = () => new Beatmap
|
CreateBeatmap = () => new Beatmap
|
||||||
{
|
{
|
||||||
HitObjects = Enumerable.Range(1, 100).Select(i => (HitObject)new Note { StartTime = 1000 + 200 * i }).ToList(),
|
HitObjects = Enumerable.Range(1, 100).Select(i => (HitObject)new Note { StartTime = 1000 + 200 * i }).ToList(),
|
||||||
|
@ -28,18 +28,20 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Width = 0.5f,
|
Width = 0.5f,
|
||||||
Child = new ColumnHitObjectArea(new HitObjectContainer())
|
Child = new ColumnHitObjectArea
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = new HitObjectContainer(),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new ColumnTestContainer(1, ManiaAction.Key2)
|
new ColumnTestContainer(1, ManiaAction.Key2)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Width = 0.5f,
|
Width = 0.5f,
|
||||||
Child = new ColumnHitObjectArea(new HitObjectContainer())
|
Child = new ColumnHitObjectArea
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = new HitObjectContainer(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
68
osu.Game.Rulesets.Mania.Tests/TestSceneManiaTouchInput.cs
Normal file
68
osu.Game.Rulesets.Mania.Tests/TestSceneManiaTouchInput.cs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
// 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 NUnit.Framework;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
|
{
|
||||||
|
public partial class TestSceneManiaTouchInput : PlayerTestScene
|
||||||
|
{
|
||||||
|
protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset();
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestTouchInput()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
int index = i;
|
||||||
|
|
||||||
|
AddStep($"touch column {index}", () => InputManager.BeginTouch(new Touch(TouchSource.Touch1, getColumn(index).ScreenSpaceDrawQuad.Centre)));
|
||||||
|
|
||||||
|
AddAssert("action sent",
|
||||||
|
() => this.ChildrenOfType<ManiaInputManager>().SelectMany(m => m.KeyBindingContainer.PressedActions),
|
||||||
|
() => Does.Contain(getColumn(index).Action.Value));
|
||||||
|
|
||||||
|
AddStep($"release column {index}", () => InputManager.EndTouch(new Touch(TouchSource.Touch1, getColumn(index).ScreenSpaceDrawQuad.Centre)));
|
||||||
|
|
||||||
|
AddAssert("action released",
|
||||||
|
() => this.ChildrenOfType<ManiaInputManager>().SelectMany(m => m.KeyBindingContainer.PressedActions),
|
||||||
|
() => Does.Not.Contain(getColumn(index).Action.Value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestOneColumnMultipleTouches()
|
||||||
|
{
|
||||||
|
AddStep("touch column 0", () => InputManager.BeginTouch(new Touch(TouchSource.Touch1, getColumn(0).ScreenSpaceDrawQuad.Centre)));
|
||||||
|
|
||||||
|
AddAssert("action sent",
|
||||||
|
() => this.ChildrenOfType<ManiaInputManager>().SelectMany(m => m.KeyBindingContainer.PressedActions),
|
||||||
|
() => Does.Contain(getColumn(0).Action.Value));
|
||||||
|
|
||||||
|
AddStep("touch another finger", () => InputManager.BeginTouch(new Touch(TouchSource.Touch2, getColumn(0).ScreenSpaceDrawQuad.Centre)));
|
||||||
|
|
||||||
|
AddAssert("action still pressed",
|
||||||
|
() => this.ChildrenOfType<ManiaInputManager>().SelectMany(m => m.KeyBindingContainer.PressedActions),
|
||||||
|
() => Does.Contain(getColumn(0).Action.Value));
|
||||||
|
|
||||||
|
AddStep("release first finger", () => InputManager.EndTouch(new Touch(TouchSource.Touch1, getColumn(0).ScreenSpaceDrawQuad.Centre)));
|
||||||
|
|
||||||
|
AddAssert("action still pressed",
|
||||||
|
() => this.ChildrenOfType<ManiaInputManager>().SelectMany(m => m.KeyBindingContainer.PressedActions),
|
||||||
|
() => Does.Contain(getColumn(0).Action.Value));
|
||||||
|
|
||||||
|
AddStep("release second finger", () => InputManager.EndTouch(new Touch(TouchSource.Touch2, getColumn(0).ScreenSpaceDrawQuad.Centre)));
|
||||||
|
|
||||||
|
AddAssert("action released",
|
||||||
|
() => this.ChildrenOfType<ManiaInputManager>().SelectMany(m => m.KeyBindingContainer.PressedActions),
|
||||||
|
() => Does.Not.Contain(getColumn(0).Action.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Column getColumn(int index) => this.ChildrenOfType<Column>().ElementAt(index);
|
||||||
|
}
|
||||||
|
}
|
@ -1,49 +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 System.Linq;
|
|
||||||
using NUnit.Framework;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Input;
|
|
||||||
using osu.Framework.Testing;
|
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
|
||||||
using osu.Game.Tests.Visual;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
|
||||||
{
|
|
||||||
public partial class TestSceneManiaTouchInputArea : PlayerTestScene
|
|
||||||
{
|
|
||||||
protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset();
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestTouchAreaNotInitiallyVisible()
|
|
||||||
{
|
|
||||||
AddAssert("touch area not visible", () => getTouchOverlay()?.State.Value == Visibility.Hidden);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestPressReceptors()
|
|
||||||
{
|
|
||||||
AddAssert("touch area not visible", () => getTouchOverlay()?.State.Value == Visibility.Hidden);
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
int index = i;
|
|
||||||
|
|
||||||
AddStep($"touch receptor {index}", () => InputManager.BeginTouch(new Touch(TouchSource.Touch1, getReceptor(index).ScreenSpaceDrawQuad.Centre)));
|
|
||||||
|
|
||||||
AddAssert("action sent",
|
|
||||||
() => this.ChildrenOfType<ManiaInputManager>().SelectMany(m => m.KeyBindingContainer.PressedActions),
|
|
||||||
() => Does.Contain(getReceptor(index).Action.Value));
|
|
||||||
|
|
||||||
AddStep($"release receptor {index}", () => InputManager.EndTouch(new Touch(TouchSource.Touch1, getReceptor(index).ScreenSpaceDrawQuad.Centre)));
|
|
||||||
|
|
||||||
AddAssert("touch area visible", () => getTouchOverlay()?.State.Value == Visibility.Visible);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ManiaTouchInputArea? getTouchOverlay() => this.ChildrenOfType<ManiaTouchInputArea>().SingleOrDefault();
|
|
||||||
|
|
||||||
private ManiaTouchInputArea.ColumnInputReceptor getReceptor(int index) => this.ChildrenOfType<ManiaTouchInputArea.ColumnInputReceptor>().ElementAt(index);
|
|
||||||
}
|
|
||||||
}
|
|
@ -8,7 +8,7 @@ using osu.Game.Rulesets.UI;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania
|
namespace osu.Game.Rulesets.Mania
|
||||||
{
|
{
|
||||||
[Cached] // Used for touch input, see ColumnTouchInputArea.
|
[Cached] // Used for touch input, see Column.OnTouchDown/OnTouchUp.
|
||||||
public partial class ManiaInputManager : RulesetInputManager<ManiaAction>
|
public partial class ManiaInputManager : RulesetInputManager<ManiaAction>
|
||||||
{
|
{
|
||||||
public ManiaInputManager(RulesetInfo ruleset, int variant)
|
public ManiaInputManager(RulesetInfo ruleset, int variant)
|
||||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
|
|
||||||
foreach (Column column in maniaPlayfield.Stages.SelectMany(stage => stage.Columns))
|
foreach (Column column in maniaPlayfield.Stages.SelectMany(stage => stage.Columns))
|
||||||
{
|
{
|
||||||
HitObjectContainer hoc = column.HitObjectArea.HitObjectContainer;
|
HitObjectContainer hoc = column.HitObjectContainer;
|
||||||
Container hocParent = (Container)hoc.Parent!;
|
Container hocParent = (Container)hoc.Parent!;
|
||||||
|
|
||||||
hocParent.Remove(hoc, false);
|
hocParent.Remove(hoc, false);
|
||||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
{
|
{
|
||||||
public partial class ArgonJudgementPiece : TextJudgementPiece, IAnimatableJudgement
|
public partial class ArgonJudgementPiece : TextJudgementPiece, IAnimatableJudgement
|
||||||
{
|
{
|
||||||
private const float judgement_y_position = 160;
|
private const float judgement_y_position = -180f;
|
||||||
|
|
||||||
private RingExplosion? ringExplosion;
|
private RingExplosion? ringExplosion;
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ using osu.Framework.Graphics.Animations;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
@ -23,7 +22,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
this.result = result;
|
this.result = result;
|
||||||
this.animation = animation;
|
this.animation = animation;
|
||||||
|
|
||||||
Anchor = Anchor.Centre;
|
Anchor = Anchor.BottomCentre;
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
@ -32,12 +31,11 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(ISkinSource skin)
|
private void load(ISkinSource skin)
|
||||||
{
|
{
|
||||||
float? scorePosition = skin.GetManiaSkinConfig<float>(LegacyManiaSkinConfigurationLookups.ScorePosition)?.Value;
|
float hitPosition = skin.GetManiaSkinConfig<float>(LegacyManiaSkinConfigurationLookups.HitPosition)?.Value ?? 0;
|
||||||
|
float scorePosition = skin.GetManiaSkinConfig<float>(LegacyManiaSkinConfigurationLookups.ScorePosition)?.Value ?? 0;
|
||||||
|
|
||||||
if (scorePosition != null)
|
float absoluteHitPosition = 480f * LegacyManiaSkinConfiguration.POSITION_SCALE_FACTOR - hitPosition;
|
||||||
scorePosition -= Stage.HIT_TARGET_POSITION + 150;
|
Y = scorePosition - absoluteHitPosition;
|
||||||
|
|
||||||
Y = scorePosition ?? 0;
|
|
||||||
|
|
||||||
InternalChild = animation.With(d =>
|
InternalChild = animation.With(d =>
|
||||||
{
|
{
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Pooling;
|
using osu.Framework.Graphics.Pooling;
|
||||||
@ -45,11 +44,11 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
internal readonly Container TopLevelContainer = new Container { RelativeSizeAxes = Axes.Both };
|
internal readonly Container TopLevelContainer = new Container { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
private DrawablePool<PoolableHitExplosion> hitExplosionPool;
|
private DrawablePool<PoolableHitExplosion> hitExplosionPool = null!;
|
||||||
private readonly OrderedHitPolicy hitPolicy;
|
private readonly OrderedHitPolicy hitPolicy;
|
||||||
public Container UnderlayElements => HitObjectArea.UnderlayElements;
|
public Container UnderlayElements => HitObjectArea.UnderlayElements;
|
||||||
|
|
||||||
private GameplaySampleTriggerSource sampleTriggerSource;
|
private GameplaySampleTriggerSource sampleTriggerSource = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether this is a special (ie. scratch) column.
|
/// Whether this is a special (ie. scratch) column.
|
||||||
@ -67,11 +66,15 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
Width = COLUMN_WIDTH;
|
Width = COLUMN_WIDTH;
|
||||||
|
|
||||||
hitPolicy = new OrderedHitPolicy(HitObjectContainer);
|
hitPolicy = new OrderedHitPolicy(HitObjectContainer);
|
||||||
HitObjectArea = new ColumnHitObjectArea(HitObjectContainer) { RelativeSizeAxes = Axes.Both };
|
HitObjectArea = new ColumnHitObjectArea
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = HitObjectContainer,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private ISkinSource skin { get; set; }
|
private ISkinSource skin { get; set; } = null!;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host)
|
private void load(GameHost host)
|
||||||
@ -132,7 +135,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
if (skin != null)
|
if (skin.IsNotNull())
|
||||||
skin.SourceChanged -= onSourceChanged;
|
skin.SourceChanged -= onSourceChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,5 +183,29 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos)
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos)
|
||||||
// This probably shouldn't exist as is, but the columns in the stage are separated by a 1px border
|
// This probably shouldn't exist as is, but the columns in the stage are separated by a 1px border
|
||||||
=> DrawRectangle.Inflate(new Vector2(Stage.COLUMN_SPACING / 2, 0)).Contains(ToLocalSpace(screenSpacePos));
|
=> DrawRectangle.Inflate(new Vector2(Stage.COLUMN_SPACING / 2, 0)).Contains(ToLocalSpace(screenSpacePos));
|
||||||
|
|
||||||
|
#region Touch Input
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private ManiaInputManager? maniaInputManager { get; set; }
|
||||||
|
|
||||||
|
private int touchActivationCount;
|
||||||
|
|
||||||
|
protected override bool OnTouchDown(TouchDownEvent e)
|
||||||
|
{
|
||||||
|
maniaInputManager?.KeyBindingContainer.TriggerPressed(Action.Value);
|
||||||
|
touchActivationCount++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnTouchUp(TouchUpEvent e)
|
||||||
|
{
|
||||||
|
touchActivationCount--;
|
||||||
|
|
||||||
|
if (touchActivationCount == 0)
|
||||||
|
maniaInputManager?.KeyBindingContainer.TriggerReleased(Action.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,12 @@
|
|||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.UI;
|
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.UI.Components
|
namespace osu.Game.Rulesets.Mania.UI.Components
|
||||||
{
|
{
|
||||||
public partial class ColumnHitObjectArea : HitObjectArea
|
public partial class ColumnHitObjectArea : HitPositionPaddedContainer
|
||||||
{
|
{
|
||||||
public readonly Container Explosions;
|
public readonly Container Explosions;
|
||||||
|
|
||||||
@ -17,25 +16,29 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
|||||||
|
|
||||||
private readonly Drawable hitTarget;
|
private readonly Drawable hitTarget;
|
||||||
|
|
||||||
public ColumnHitObjectArea(HitObjectContainer hitObjectContainer)
|
protected override Container<Drawable> Content => content;
|
||||||
: base(hitObjectContainer)
|
|
||||||
|
private readonly Container content;
|
||||||
|
|
||||||
|
public ColumnHitObjectArea()
|
||||||
{
|
{
|
||||||
AddRangeInternal(new[]
|
AddRangeInternal(new[]
|
||||||
{
|
{
|
||||||
UnderlayElements = new Container
|
UnderlayElements = new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Depth = 2,
|
|
||||||
},
|
},
|
||||||
hitTarget = new SkinnableDrawable(new ManiaSkinComponentLookup(ManiaSkinComponents.HitTarget), _ => new DefaultHitTarget())
|
hitTarget = new SkinnableDrawable(new ManiaSkinComponentLookup(ManiaSkinComponents.HitTarget), _ => new DefaultHitTarget())
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Depth = 1
|
},
|
||||||
|
content = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
},
|
},
|
||||||
Explosions = new Container
|
Explosions = new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Depth = -1,
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,52 +1,38 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.Mania.Skinning;
|
using osu.Game.Rulesets.Mania.Skinning;
|
||||||
using osu.Game.Rulesets.UI;
|
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.UI.Components
|
namespace osu.Game.Rulesets.Mania.UI.Components
|
||||||
{
|
{
|
||||||
public partial class HitObjectArea : SkinReloadableDrawable
|
public partial class HitPositionPaddedContainer : Container
|
||||||
{
|
{
|
||||||
protected readonly IBindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
|
protected readonly IBindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
|
||||||
public readonly HitObjectContainer HitObjectContainer;
|
|
||||||
|
|
||||||
public HitObjectArea(HitObjectContainer hitObjectContainer)
|
[Resolved]
|
||||||
{
|
private ISkinSource skin { get; set; } = null!;
|
||||||
InternalChild = new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Child = HitObjectContainer = hitObjectContainer
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(IScrollingInfo scrollingInfo)
|
private void load(IScrollingInfo scrollingInfo)
|
||||||
{
|
{
|
||||||
Direction.BindTo(scrollingInfo.Direction);
|
Direction.BindTo(scrollingInfo.Direction);
|
||||||
Direction.BindValueChanged(onDirectionChanged, true);
|
Direction.BindValueChanged(_ => UpdateHitPosition(), true);
|
||||||
|
|
||||||
|
skin.SourceChanged += onSkinChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SkinChanged(ISkinSource skin)
|
private void onSkinChanged() => UpdateHitPosition();
|
||||||
{
|
|
||||||
base.SkinChanged(skin);
|
|
||||||
UpdateHitPosition();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
|
||||||
{
|
|
||||||
UpdateHitPosition();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void UpdateHitPosition()
|
protected virtual void UpdateHitPosition()
|
||||||
{
|
{
|
||||||
float hitPosition = CurrentSkin.GetConfig<ManiaSkinConfigurationLookup, float>(
|
float hitPosition = skin.GetConfig<ManiaSkinConfigurationLookup, float>(
|
||||||
new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.HitPosition))?.Value
|
new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.HitPosition))?.Value
|
||||||
?? Stage.HIT_TARGET_POSITION;
|
?? Stage.HIT_TARGET_POSITION;
|
||||||
|
|
||||||
@ -54,5 +40,13 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
|||||||
? new MarginPadding { Top = hitPosition }
|
? new MarginPadding { Top = hitPosition }
|
||||||
: new MarginPadding { Bottom = hitPosition };
|
: new MarginPadding { Bottom = hitPosition };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
|
if (skin.IsNotNull())
|
||||||
|
skin.SourceChanged -= onSkinChanged;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,6 +6,7 @@
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.UI
|
namespace osu.Game.Rulesets.Mania.UI
|
||||||
{
|
{
|
||||||
@ -15,9 +16,12 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
private partial class DefaultManiaJudgementPiece : DefaultJudgementPiece
|
private partial class DefaultManiaJudgementPiece : DefaultJudgementPiece
|
||||||
{
|
{
|
||||||
|
private const float judgement_y_position = -180f;
|
||||||
|
|
||||||
public DefaultManiaJudgementPiece(HitResult result)
|
public DefaultManiaJudgementPiece(HitResult result)
|
||||||
: base(result)
|
: base(result)
|
||||||
{
|
{
|
||||||
|
Y = judgement_y_position;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -32,8 +36,20 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
switch (Result)
|
switch (Result)
|
||||||
{
|
{
|
||||||
case HitResult.None:
|
case HitResult.None:
|
||||||
|
this.FadeOutFromOne(800);
|
||||||
|
break;
|
||||||
|
|
||||||
case HitResult.Miss:
|
case HitResult.Miss:
|
||||||
base.PlayAnimation();
|
this.ScaleTo(1.6f);
|
||||||
|
this.ScaleTo(1, 100, Easing.In);
|
||||||
|
|
||||||
|
this.MoveToY(judgement_y_position);
|
||||||
|
this.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint);
|
||||||
|
|
||||||
|
this.RotateTo(0);
|
||||||
|
this.RotateTo(40, 800, Easing.InQuint);
|
||||||
|
|
||||||
|
this.FadeOutFromOne(800);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -43,8 +59,6 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
this.Delay(50)
|
this.Delay(50)
|
||||||
.ScaleTo(0.75f, 250)
|
.ScaleTo(0.75f, 250)
|
||||||
.FadeOut(200);
|
.FadeOut(200);
|
||||||
|
|
||||||
// osu!mania uses a custom fade length, so the base call is intentionally omitted.
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,6 @@ using osu.Game.Skinning;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.UI
|
namespace osu.Game.Rulesets.Mania.UI
|
||||||
{
|
{
|
||||||
[Cached]
|
|
||||||
public partial class DrawableManiaRuleset : DrawableScrollingRuleset<ManiaHitObject>
|
public partial class DrawableManiaRuleset : DrawableScrollingRuleset<ManiaHitObject>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -51,6 +50,8 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
public IEnumerable<BarLine> BarLines;
|
public IEnumerable<BarLine> BarLines;
|
||||||
|
|
||||||
|
public override bool RequiresPortraitOrientation => Beatmap.Stages.Count == 1;
|
||||||
|
|
||||||
protected override bool RelativeScaleBeatLengths => true;
|
protected override bool RelativeScaleBeatLengths => true;
|
||||||
|
|
||||||
protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config;
|
protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config;
|
||||||
@ -110,8 +111,6 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
configScrollSpeed.BindValueChanged(speed => TargetTimeRange = ComputeScrollTime(speed.NewValue));
|
configScrollSpeed.BindValueChanged(speed => TargetTimeRange = ComputeScrollTime(speed.NewValue));
|
||||||
|
|
||||||
TimeRange.Value = TargetTimeRange = currentTimeRange = ComputeScrollTime(configScrollSpeed.Value);
|
TimeRange.Value = TargetTimeRange = currentTimeRange = ComputeScrollTime(configScrollSpeed.Value);
|
||||||
|
|
||||||
KeyBindingInputManager.Add(new ManiaTouchInputArea());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void AdjustScrollSpeed(int amount) => configScrollSpeed.Value += amount;
|
protected override void AdjustScrollSpeed(int amount) => configScrollSpeed.Value += amount;
|
||||||
@ -162,7 +161,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
/// <returns>The scroll time.</returns>
|
/// <returns>The scroll time.</returns>
|
||||||
public static double ComputeScrollTime(double scrollSpeed) => MAX_TIME_RANGE / scrollSpeed;
|
public static double ComputeScrollTime(double scrollSpeed) => MAX_TIME_RANGE / scrollSpeed;
|
||||||
|
|
||||||
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new ManiaPlayfieldAdjustmentContainer();
|
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new ManiaPlayfieldAdjustmentContainer(this);
|
||||||
|
|
||||||
protected override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages);
|
protected override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages);
|
||||||
|
|
||||||
|
@ -1,17 +1,63 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.UI
|
namespace osu.Game.Rulesets.Mania.UI
|
||||||
{
|
{
|
||||||
public partial class ManiaPlayfieldAdjustmentContainer : PlayfieldAdjustmentContainer
|
public partial class ManiaPlayfieldAdjustmentContainer : PlayfieldAdjustmentContainer
|
||||||
{
|
{
|
||||||
public ManiaPlayfieldAdjustmentContainer()
|
protected override Container<Drawable> Content { get; }
|
||||||
|
|
||||||
|
private readonly DrawSizePreservingFillContainer scalingContainer;
|
||||||
|
|
||||||
|
private readonly DrawableManiaRuleset drawableManiaRuleset;
|
||||||
|
|
||||||
|
public ManiaPlayfieldAdjustmentContainer(DrawableManiaRuleset drawableManiaRuleset)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre;
|
this.drawableManiaRuleset = drawableManiaRuleset;
|
||||||
Origin = Anchor.Centre;
|
InternalChild = scalingContainer = new DrawSizePreservingFillContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = Content = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
float aspectRatio = DrawWidth / DrawHeight;
|
||||||
|
bool isPortrait = aspectRatio < 1f;
|
||||||
|
|
||||||
|
if (isPortrait && drawableManiaRuleset.Beatmap.Stages.Count == 1)
|
||||||
|
{
|
||||||
|
// Scale playfield up by 25% to become playable on mobile devices,
|
||||||
|
// and leave a 10% horizontal gap if the playfield is scaled down due to being too wide.
|
||||||
|
const float base_scale = 1.25f;
|
||||||
|
const float base_width = 768f / base_scale;
|
||||||
|
const float side_gap = 0.9f;
|
||||||
|
|
||||||
|
scalingContainer.Strategy = DrawSizePreservationStrategy.Maximum;
|
||||||
|
float stageWidth = drawableManiaRuleset.Playfield.Stages[0].DrawWidth;
|
||||||
|
scalingContainer.TargetDrawSize = new Vector2(1024, base_width * Math.Max(stageWidth / aspectRatio / (base_width * side_gap), 1f));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scalingContainer.Strategy = DrawSizePreservationStrategy.Minimum;
|
||||||
|
scalingContainer.Scale = new Vector2(1f);
|
||||||
|
scalingContainer.Size = new Vector2(1f);
|
||||||
|
scalingContainer.TargetDrawSize = new Vector2(1024, 768);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,199 +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 System.Collections.Generic;
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Framework.Input.Events;
|
|
||||||
using osu.Game.Configuration;
|
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.UI
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// An overlay that captures and displays osu!mania mouse and touch input.
|
|
||||||
/// </summary>
|
|
||||||
public partial class ManiaTouchInputArea : VisibilityContainer
|
|
||||||
{
|
|
||||||
// visibility state affects our child. we always want to handle input.
|
|
||||||
public override bool PropagatePositionalInputSubTree => true;
|
|
||||||
public override bool PropagateNonPositionalInputSubTree => true;
|
|
||||||
|
|
||||||
[SettingSource("Spacing", "The spacing between receptors.")]
|
|
||||||
public BindableFloat Spacing { get; } = new BindableFloat(10)
|
|
||||||
{
|
|
||||||
Precision = 1,
|
|
||||||
MinValue = 0,
|
|
||||||
MaxValue = 100,
|
|
||||||
};
|
|
||||||
|
|
||||||
[SettingSource("Opacity", "The receptor opacity.")]
|
|
||||||
public BindableFloat Opacity { get; } = new BindableFloat(1)
|
|
||||||
{
|
|
||||||
Precision = 0.1f,
|
|
||||||
MinValue = 0,
|
|
||||||
MaxValue = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private DrawableManiaRuleset drawableRuleset { get; set; } = null!;
|
|
||||||
|
|
||||||
private GridContainer gridContainer = null!;
|
|
||||||
|
|
||||||
public ManiaTouchInputArea()
|
|
||||||
{
|
|
||||||
Anchor = Anchor.BottomCentre;
|
|
||||||
Origin = Anchor.BottomCentre;
|
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
|
||||||
Height = 0.5f;
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load()
|
|
||||||
{
|
|
||||||
List<Drawable> receptorGridContent = new List<Drawable>();
|
|
||||||
List<Dimension> receptorGridDimensions = new List<Dimension>();
|
|
||||||
|
|
||||||
bool first = true;
|
|
||||||
|
|
||||||
foreach (var stage in drawableRuleset.Playfield.Stages)
|
|
||||||
{
|
|
||||||
foreach (var column in stage.Columns)
|
|
||||||
{
|
|
||||||
if (!first)
|
|
||||||
{
|
|
||||||
receptorGridContent.Add(new Gutter { Spacing = { BindTarget = Spacing } });
|
|
||||||
receptorGridDimensions.Add(new Dimension(GridSizeMode.AutoSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
receptorGridContent.Add(new ColumnInputReceptor { Action = { BindTarget = column.Action } });
|
|
||||||
receptorGridDimensions.Add(new Dimension());
|
|
||||||
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
InternalChild = gridContainer = new GridContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
AlwaysPresent = true,
|
|
||||||
Content = new[] { receptorGridContent.ToArray() },
|
|
||||||
ColumnDimensions = receptorGridDimensions.ToArray()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
|
||||||
{
|
|
||||||
base.LoadComplete();
|
|
||||||
Opacity.BindValueChanged(o => Alpha = o.NewValue, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool OnKeyDown(KeyDownEvent e)
|
|
||||||
{
|
|
||||||
// Hide whenever the keyboard is used.
|
|
||||||
Hide();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool OnTouchDown(TouchDownEvent e)
|
|
||||||
{
|
|
||||||
Show();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void PopIn()
|
|
||||||
{
|
|
||||||
gridContainer.FadeIn(500, Easing.OutQuint);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void PopOut()
|
|
||||||
{
|
|
||||||
gridContainer.FadeOut(300);
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial class ColumnInputReceptor : CompositeDrawable
|
|
||||||
{
|
|
||||||
public readonly IBindable<ManiaAction> Action = new Bindable<ManiaAction>();
|
|
||||||
|
|
||||||
private readonly Box highlightOverlay;
|
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private ManiaInputManager? inputManager { get; set; }
|
|
||||||
|
|
||||||
private bool isPressed;
|
|
||||||
|
|
||||||
public ColumnInputReceptor()
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
|
||||||
{
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Masking = true,
|
|
||||||
CornerRadius = 10,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Alpha = 0.15f,
|
|
||||||
},
|
|
||||||
highlightOverlay = new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Alpha = 0,
|
|
||||||
Blending = BlendingParameters.Additive,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool OnTouchDown(TouchDownEvent e)
|
|
||||||
{
|
|
||||||
updateButton(true);
|
|
||||||
return false; // handled by parent container to show overlay.
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnTouchUp(TouchUpEvent e)
|
|
||||||
{
|
|
||||||
updateButton(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateButton(bool press)
|
|
||||||
{
|
|
||||||
if (press == isPressed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
isPressed = press;
|
|
||||||
|
|
||||||
if (press)
|
|
||||||
{
|
|
||||||
inputManager?.KeyBindingContainer.TriggerPressed(Action.Value);
|
|
||||||
highlightOverlay.FadeTo(0.1f, 80, Easing.OutQuint);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
inputManager?.KeyBindingContainer.TriggerReleased(Action.Value);
|
|
||||||
highlightOverlay.FadeTo(0, 400, Easing.OutQuint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private partial class Gutter : Drawable
|
|
||||||
{
|
|
||||||
public readonly IBindable<float> Spacing = new Bindable<float>();
|
|
||||||
|
|
||||||
public Gutter()
|
|
||||||
{
|
|
||||||
Spacing.BindValueChanged(s => Size = new Vector2(s.NewValue));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -103,12 +103,13 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
Width = 1366, // Bar lines should only be masked on the vertical axis
|
Width = 1366, // Bar lines should only be masked on the vertical axis
|
||||||
BypassAutoSizeAxes = Axes.Both,
|
BypassAutoSizeAxes = Axes.Both,
|
||||||
Masking = true,
|
Masking = true,
|
||||||
Child = barLineContainer = new HitObjectArea(HitObjectContainer)
|
Child = barLineContainer = new HitPositionPaddedContainer
|
||||||
{
|
{
|
||||||
Name = "Bar lines",
|
Name = "Bar lines",
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Child = HitObjectContainer,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
columnFlow = new ColumnFlow<Column>(definition)
|
columnFlow = new ColumnFlow<Column>(definition)
|
||||||
@ -119,12 +120,13 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both
|
RelativeSizeAxes = Axes.Both
|
||||||
},
|
},
|
||||||
judgements = new JudgementContainer<DrawableManiaJudgement>
|
new HitPositionPaddedContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Y = HIT_TARGET_POSITION + 150
|
Child = judgements = new JudgementContainer<DrawableManiaJudgement>
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
topLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
|
topLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
|
||||||
}
|
}
|
||||||
@ -218,7 +220,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
{
|
{
|
||||||
j.Apply(result, judgedObject);
|
j.Apply(result, judgedObject);
|
||||||
|
|
||||||
j.Anchor = Anchor.Centre;
|
j.Anchor = Anchor.BottomCentre;
|
||||||
j.Origin = Anchor.Centre;
|
j.Origin = Anchor.Centre;
|
||||||
})!);
|
})!);
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.Tests.Resources;
|
using osu.Game.Tests.Resources;
|
||||||
|
|
||||||
@ -25,13 +26,16 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestLocallyModifyingOnlineBeatmap()
|
public void TestLocallyModifyingOnlineBeatmap()
|
||||||
{
|
{
|
||||||
|
string initialHash = string.Empty;
|
||||||
AddAssert("editor beatmap has online ID", () => EditorBeatmap.BeatmapInfo.OnlineID, () => Is.GreaterThan(0));
|
AddAssert("editor beatmap has online ID", () => EditorBeatmap.BeatmapInfo.OnlineID, () => Is.GreaterThan(0));
|
||||||
|
AddStep("store hash for later", () => initialHash = EditorBeatmap.BeatmapInfo.MD5Hash);
|
||||||
|
|
||||||
AddStep("delete first hitobject", () => EditorBeatmap.RemoveAt(0));
|
AddStep("delete first hitobject", () => EditorBeatmap.RemoveAt(0));
|
||||||
SaveEditor();
|
SaveEditor();
|
||||||
|
|
||||||
ReloadEditorToSameBeatmap();
|
ReloadEditorToSameBeatmap();
|
||||||
AddAssert("editor beatmap online ID reset", () => EditorBeatmap.BeatmapInfo.OnlineID, () => Is.EqualTo(-1));
|
AddAssert("beatmap marked as locally modified", () => EditorBeatmap.BeatmapInfo.Status, () => Is.EqualTo(BeatmapOnlineStatus.LocallyModified));
|
||||||
|
AddAssert("beatmap hash changed", () => EditorBeatmap.BeatmapInfo.MD5Hash, () => Is.Not.EqualTo(initialHash));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -475,11 +475,8 @@ namespace osu.Game.Beatmaps
|
|||||||
beatmapContent.BeatmapInfo = beatmapInfo;
|
beatmapContent.BeatmapInfo = beatmapInfo;
|
||||||
|
|
||||||
// Since now this is a locally-modified beatmap, we also set all relevant flags to indicate this.
|
// Since now this is a locally-modified beatmap, we also set all relevant flags to indicate this.
|
||||||
// Importantly, the `ResetOnlineInfo()` call must happen before encoding, as online ID is encoded into the `.osu` file,
|
|
||||||
// which influences the beatmap checksums.
|
|
||||||
beatmapInfo.LastLocalUpdate = DateTimeOffset.Now;
|
beatmapInfo.LastLocalUpdate = DateTimeOffset.Now;
|
||||||
beatmapInfo.Status = BeatmapOnlineStatus.LocallyModified;
|
beatmapInfo.Status = BeatmapOnlineStatus.LocallyModified;
|
||||||
beatmapInfo.ResetOnlineInfo();
|
|
||||||
|
|
||||||
Realm.Write(r =>
|
Realm.Write(r =>
|
||||||
{
|
{
|
||||||
|
@ -178,9 +178,9 @@ namespace osu.Game
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly IBindable<bool> backButtonVisibility = new Bindable<bool>();
|
private readonly IBindable<bool> backButtonVisibility = new Bindable<bool>();
|
||||||
|
|
||||||
IBindable<LocalUserPlayingState> ILocalUserPlayInfo.PlayingState => playingState;
|
IBindable<LocalUserPlayingState> ILocalUserPlayInfo.PlayingState => UserPlayingState;
|
||||||
|
|
||||||
private readonly Bindable<LocalUserPlayingState> playingState = new Bindable<LocalUserPlayingState>();
|
protected readonly Bindable<LocalUserPlayingState> UserPlayingState = new Bindable<LocalUserPlayingState>();
|
||||||
|
|
||||||
protected OsuScreenStack ScreenStack;
|
protected OsuScreenStack ScreenStack;
|
||||||
|
|
||||||
@ -306,7 +306,7 @@ namespace osu.Game
|
|||||||
protected override UserInputManager CreateUserInputManager()
|
protected override UserInputManager CreateUserInputManager()
|
||||||
{
|
{
|
||||||
var userInputManager = base.CreateUserInputManager();
|
var userInputManager = base.CreateUserInputManager();
|
||||||
(userInputManager as OsuUserInputManager)?.PlayingState.BindTo(playingState);
|
(userInputManager as OsuUserInputManager)?.PlayingState.BindTo(UserPlayingState);
|
||||||
return userInputManager;
|
return userInputManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -407,7 +407,7 @@ namespace osu.Game
|
|||||||
// Transfer any runtime changes back to configuration file.
|
// Transfer any runtime changes back to configuration file.
|
||||||
SkinManager.CurrentSkinInfo.ValueChanged += skin => configSkin.Value = skin.NewValue.ID.ToString();
|
SkinManager.CurrentSkinInfo.ValueChanged += skin => configSkin.Value = skin.NewValue.ID.ToString();
|
||||||
|
|
||||||
playingState.BindValueChanged(p =>
|
UserPlayingState.BindValueChanged(p =>
|
||||||
{
|
{
|
||||||
BeatmapManager.PauseImports = p.NewValue != LocalUserPlayingState.NotPlaying;
|
BeatmapManager.PauseImports = p.NewValue != LocalUserPlayingState.NotPlaying;
|
||||||
SkinManager.PauseImports = p.NewValue != LocalUserPlayingState.NotPlaying;
|
SkinManager.PauseImports = p.NewValue != LocalUserPlayingState.NotPlaying;
|
||||||
@ -1548,7 +1548,7 @@ namespace osu.Game
|
|||||||
GlobalCursorDisplay.ShowCursor = (ScreenStack.CurrentScreen as IOsuScreen)?.CursorVisible ?? false;
|
GlobalCursorDisplay.ShowCursor = (ScreenStack.CurrentScreen as IOsuScreen)?.CursorVisible ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void screenChanged(IScreen current, IScreen newScreen)
|
protected virtual void ScreenChanged([CanBeNull] IOsuScreen current, [CanBeNull] IOsuScreen newScreen)
|
||||||
{
|
{
|
||||||
SentrySdk.ConfigureScope(scope =>
|
SentrySdk.ConfigureScope(scope =>
|
||||||
{
|
{
|
||||||
@ -1564,10 +1564,10 @@ namespace osu.Game
|
|||||||
switch (current)
|
switch (current)
|
||||||
{
|
{
|
||||||
case Player player:
|
case Player player:
|
||||||
player.PlayingState.UnbindFrom(playingState);
|
player.PlayingState.UnbindFrom(UserPlayingState);
|
||||||
|
|
||||||
// reset for sanity.
|
// reset for sanity.
|
||||||
playingState.Value = LocalUserPlayingState.NotPlaying;
|
UserPlayingState.Value = LocalUserPlayingState.NotPlaying;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1584,7 +1584,7 @@ namespace osu.Game
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Player player:
|
case Player player:
|
||||||
player.PlayingState.BindTo(playingState);
|
player.PlayingState.BindTo(UserPlayingState);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -1592,30 +1592,32 @@ namespace osu.Game
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current is IOsuScreen currentOsuScreen)
|
if (current != null)
|
||||||
{
|
{
|
||||||
backButtonVisibility.UnbindFrom(currentOsuScreen.BackButtonVisibility);
|
backButtonVisibility.UnbindFrom(current.BackButtonVisibility);
|
||||||
OverlayActivationMode.UnbindFrom(currentOsuScreen.OverlayActivationMode);
|
OverlayActivationMode.UnbindFrom(current.OverlayActivationMode);
|
||||||
configUserActivity.UnbindFrom(currentOsuScreen.Activity);
|
configUserActivity.UnbindFrom(current.Activity);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newScreen is IOsuScreen newOsuScreen)
|
// Bind to new screen.
|
||||||
|
if (newScreen != null)
|
||||||
{
|
{
|
||||||
backButtonVisibility.BindTo(newOsuScreen.BackButtonVisibility);
|
backButtonVisibility.BindTo(newScreen.BackButtonVisibility);
|
||||||
OverlayActivationMode.BindTo(newOsuScreen.OverlayActivationMode);
|
OverlayActivationMode.BindTo(newScreen.OverlayActivationMode);
|
||||||
configUserActivity.BindTo(newOsuScreen.Activity);
|
configUserActivity.BindTo(newScreen.Activity);
|
||||||
|
|
||||||
GlobalCursorDisplay.MenuCursor.HideCursorOnNonMouseInput = newOsuScreen.HideMenuCursorOnNonMouseInput;
|
// Handle various configuration updates based on new screen settings.
|
||||||
|
GlobalCursorDisplay.MenuCursor.HideCursorOnNonMouseInput = newScreen.HideMenuCursorOnNonMouseInput;
|
||||||
|
|
||||||
if (newOsuScreen.HideOverlaysOnEnter)
|
if (newScreen.HideOverlaysOnEnter)
|
||||||
CloseAllOverlays();
|
CloseAllOverlays();
|
||||||
else
|
else
|
||||||
Toolbar.Show();
|
Toolbar.Show();
|
||||||
|
|
||||||
if (newOsuScreen.ShowFooter)
|
if (newScreen.ShowFooter)
|
||||||
{
|
{
|
||||||
BackButton.Hide();
|
BackButton.Hide();
|
||||||
ScreenFooter.SetButtons(newOsuScreen.CreateFooterButtons());
|
ScreenFooter.SetButtons(newScreen.CreateFooterButtons());
|
||||||
ScreenFooter.Show();
|
ScreenFooter.Show();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1623,16 +1625,16 @@ namespace osu.Game
|
|||||||
ScreenFooter.SetButtons(Array.Empty<ScreenFooterButton>());
|
ScreenFooter.SetButtons(Array.Empty<ScreenFooterButton>());
|
||||||
ScreenFooter.Hide();
|
ScreenFooter.Hide();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
skinEditor.SetTarget((OsuScreen)newScreen);
|
skinEditor.SetTarget((OsuScreen)newScreen);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void screenPushed(IScreen lastScreen, IScreen newScreen) => screenChanged(lastScreen, newScreen);
|
private void screenPushed(IScreen lastScreen, IScreen newScreen) => ScreenChanged((OsuScreen)lastScreen, (OsuScreen)newScreen);
|
||||||
|
|
||||||
private void screenExited(IScreen lastScreen, IScreen newScreen)
|
private void screenExited(IScreen lastScreen, IScreen newScreen)
|
||||||
{
|
{
|
||||||
screenChanged(lastScreen, newScreen);
|
ScreenChanged((OsuScreen)lastScreen, (OsuScreen)newScreen);
|
||||||
|
|
||||||
if (newScreen == null)
|
if (newScreen == null)
|
||||||
Exit();
|
Exit();
|
||||||
|
@ -21,7 +21,7 @@ using Realms;
|
|||||||
namespace osu.Game.Overlays.FirstRunSetup
|
namespace osu.Game.Overlays.FirstRunSetup
|
||||||
{
|
{
|
||||||
[LocalisableDescription(typeof(FirstRunSetupBeatmapScreenStrings), nameof(FirstRunSetupBeatmapScreenStrings.Header))]
|
[LocalisableDescription(typeof(FirstRunSetupBeatmapScreenStrings), nameof(FirstRunSetupBeatmapScreenStrings.Header))]
|
||||||
public partial class ScreenBeatmaps : FirstRunSetupScreen
|
public partial class ScreenBeatmaps : WizardScreen
|
||||||
{
|
{
|
||||||
private ProgressRoundedButton downloadBundledButton = null!;
|
private ProgressRoundedButton downloadBundledButton = null!;
|
||||||
private ProgressRoundedButton downloadTutorialButton = null!;
|
private ProgressRoundedButton downloadTutorialButton = null!;
|
||||||
|
@ -20,7 +20,7 @@ using osu.Game.Overlays.Settings.Sections;
|
|||||||
namespace osu.Game.Overlays.FirstRunSetup
|
namespace osu.Game.Overlays.FirstRunSetup
|
||||||
{
|
{
|
||||||
[LocalisableDescription(typeof(FirstRunSetupOverlayStrings), nameof(FirstRunSetupOverlayStrings.Behaviour))]
|
[LocalisableDescription(typeof(FirstRunSetupOverlayStrings), nameof(FirstRunSetupOverlayStrings.Behaviour))]
|
||||||
public partial class ScreenBehaviour : FirstRunSetupScreen
|
public partial class ScreenBehaviour : WizardScreen
|
||||||
{
|
{
|
||||||
private SearchContainer<SettingsSection> searchContainer;
|
private SearchContainer<SettingsSection> searchContainer;
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ using osuTK;
|
|||||||
namespace osu.Game.Overlays.FirstRunSetup
|
namespace osu.Game.Overlays.FirstRunSetup
|
||||||
{
|
{
|
||||||
[LocalisableDescription(typeof(FirstRunOverlayImportFromStableScreenStrings), nameof(FirstRunOverlayImportFromStableScreenStrings.Header))]
|
[LocalisableDescription(typeof(FirstRunOverlayImportFromStableScreenStrings), nameof(FirstRunOverlayImportFromStableScreenStrings.Header))]
|
||||||
public partial class ScreenImportFromStable : FirstRunSetupScreen
|
public partial class ScreenImportFromStable : WizardScreen
|
||||||
{
|
{
|
||||||
private static readonly Vector2 button_size = new Vector2(400, 50);
|
private static readonly Vector2 button_size = new Vector2(400, 50);
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ using osuTK;
|
|||||||
namespace osu.Game.Overlays.FirstRunSetup
|
namespace osu.Game.Overlays.FirstRunSetup
|
||||||
{
|
{
|
||||||
[LocalisableDescription(typeof(GraphicsSettingsStrings), nameof(GraphicsSettingsStrings.UIScaling))]
|
[LocalisableDescription(typeof(GraphicsSettingsStrings), nameof(GraphicsSettingsStrings.UIScaling))]
|
||||||
public partial class ScreenUIScale : FirstRunSetupScreen
|
public partial class ScreenUIScale : WizardScreen
|
||||||
{
|
{
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuConfigManager config)
|
private void load(OsuConfigManager config)
|
||||||
|
@ -23,7 +23,7 @@ using osuTK;
|
|||||||
namespace osu.Game.Overlays.FirstRunSetup
|
namespace osu.Game.Overlays.FirstRunSetup
|
||||||
{
|
{
|
||||||
[LocalisableDescription(typeof(FirstRunSetupOverlayStrings), nameof(FirstRunSetupOverlayStrings.WelcomeTitle))]
|
[LocalisableDescription(typeof(FirstRunSetupOverlayStrings), nameof(FirstRunSetupOverlayStrings.WelcomeTitle))]
|
||||||
public partial class ScreenWelcome : FirstRunSetupScreen
|
public partial class ScreenWelcome : WizardScreen
|
||||||
{
|
{
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(FrameworkConfigManager frameworkConfig)
|
private void load(FrameworkConfigManager frameworkConfig)
|
||||||
|
@ -1,38 +1,22 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Cursor;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Input.Events;
|
|
||||||
using osu.Framework.Localisation;
|
|
||||||
using osu.Framework.Screens;
|
|
||||||
using osu.Framework.Threading;
|
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
using osu.Game.Input.Bindings;
|
|
||||||
using osu.Game.Localisation;
|
using osu.Game.Localisation;
|
||||||
using osu.Game.Overlays.FirstRunSetup;
|
using osu.Game.Overlays.FirstRunSetup;
|
||||||
using osu.Game.Overlays.Mods;
|
|
||||||
using osu.Game.Overlays.Notifications;
|
using osu.Game.Overlays.Notifications;
|
||||||
using osu.Game.Screens;
|
using osu.Game.Screens;
|
||||||
using osu.Game.Screens.Footer;
|
|
||||||
using osu.Game.Screens.Menu;
|
using osu.Game.Screens.Menu;
|
||||||
|
|
||||||
namespace osu.Game.Overlays
|
namespace osu.Game.Overlays
|
||||||
{
|
{
|
||||||
[Cached]
|
[Cached]
|
||||||
public partial class FirstRunSetupOverlay : ShearedOverlayContainer
|
public partial class FirstRunSetupOverlay : WizardOverlay
|
||||||
{
|
{
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private IPerformFromScreenRunner performer { get; set; } = null!;
|
private IPerformFromScreenRunner performer { get; set; } = null!;
|
||||||
@ -43,28 +27,8 @@ namespace osu.Game.Overlays
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private OsuConfigManager config { get; set; } = null!;
|
private OsuConfigManager config { get; set; } = null!;
|
||||||
|
|
||||||
private ScreenStack? stack;
|
|
||||||
|
|
||||||
public ShearedButton? NextButton => DisplayedFooterContent?.NextButton;
|
|
||||||
|
|
||||||
private readonly Bindable<bool> showFirstRunSetup = new Bindable<bool>();
|
private readonly Bindable<bool> showFirstRunSetup = new Bindable<bool>();
|
||||||
|
|
||||||
private int? currentStepIndex;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The currently displayed screen, if any.
|
|
||||||
/// </summary>
|
|
||||||
public FirstRunSetupScreen? CurrentScreen => (FirstRunSetupScreen?)stack?.CurrentScreen;
|
|
||||||
|
|
||||||
private readonly List<Type> steps = new List<Type>();
|
|
||||||
|
|
||||||
private Container screenContent = null!;
|
|
||||||
|
|
||||||
private Container content = null!;
|
|
||||||
|
|
||||||
private LoadingSpinner loading = null!;
|
|
||||||
private ScheduledDelegate? loadingShowDelegate;
|
|
||||||
|
|
||||||
public FirstRunSetupOverlay()
|
public FirstRunSetupOverlay()
|
||||||
: base(OverlayColourScheme.Purple)
|
: base(OverlayColourScheme.Purple)
|
||||||
{
|
{
|
||||||
@ -73,67 +37,15 @@ namespace osu.Game.Overlays
|
|||||||
[BackgroundDependencyLoader(permitNulls: true)]
|
[BackgroundDependencyLoader(permitNulls: true)]
|
||||||
private void load(OsuColour colours, LegacyImportManager? legacyImportManager)
|
private void load(OsuColour colours, LegacyImportManager? legacyImportManager)
|
||||||
{
|
{
|
||||||
steps.Add(typeof(ScreenWelcome));
|
AddStep<ScreenWelcome>();
|
||||||
steps.Add(typeof(ScreenUIScale));
|
AddStep<ScreenUIScale>();
|
||||||
steps.Add(typeof(ScreenBeatmaps));
|
AddStep<ScreenBeatmaps>();
|
||||||
if (legacyImportManager?.SupportsImportFromStable == true)
|
if (legacyImportManager?.SupportsImportFromStable == true)
|
||||||
steps.Add(typeof(ScreenImportFromStable));
|
AddStep<ScreenImportFromStable>();
|
||||||
steps.Add(typeof(ScreenBehaviour));
|
AddStep<ScreenBehaviour>();
|
||||||
|
|
||||||
Header.Title = FirstRunSetupOverlayStrings.FirstRunSetupTitle;
|
Header.Title = FirstRunSetupOverlayStrings.FirstRunSetupTitle;
|
||||||
Header.Description = FirstRunSetupOverlayStrings.FirstRunSetupDescription;
|
Header.Description = FirstRunSetupOverlayStrings.FirstRunSetupDescription;
|
||||||
|
|
||||||
MainAreaContent.AddRange(new Drawable[]
|
|
||||||
{
|
|
||||||
content = new PopoverContainer
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Padding = new MarginPadding { Bottom = 20 },
|
|
||||||
Child = new GridContainer
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
ColumnDimensions = new[]
|
|
||||||
{
|
|
||||||
new Dimension(),
|
|
||||||
new Dimension(minSize: 640, maxSize: 800),
|
|
||||||
new Dimension(),
|
|
||||||
},
|
|
||||||
Content = new[]
|
|
||||||
{
|
|
||||||
new[]
|
|
||||||
{
|
|
||||||
Empty(),
|
|
||||||
new InputBlockingContainer
|
|
||||||
{
|
|
||||||
Masking = true,
|
|
||||||
CornerRadius = 14,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = ColourProvider.Background6,
|
|
||||||
},
|
|
||||||
loading = new LoadingSpinner(),
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Padding = new MarginPadding { Vertical = 20 },
|
|
||||||
Child = screenContent = new Container { RelativeSizeAxes = Axes.Both, },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Empty(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -145,55 +57,6 @@ namespace osu.Game.Overlays
|
|||||||
if (showFirstRunSetup.Value) Show();
|
if (showFirstRunSetup.Value) Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private ScreenFooter footer { get; set; } = null!;
|
|
||||||
|
|
||||||
public new FirstRunSetupFooterContent? DisplayedFooterContent => base.DisplayedFooterContent as FirstRunSetupFooterContent;
|
|
||||||
|
|
||||||
public override VisibilityContainer CreateFooterContent()
|
|
||||||
{
|
|
||||||
var footerContent = new FirstRunSetupFooterContent
|
|
||||||
{
|
|
||||||
ShowNextStep = showNextStep,
|
|
||||||
};
|
|
||||||
|
|
||||||
footerContent.OnLoadComplete += _ => updateButtons();
|
|
||||||
return footerContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool OnBackButton()
|
|
||||||
{
|
|
||||||
if (currentStepIndex == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Debug.Assert(stack != null);
|
|
||||||
|
|
||||||
stack.CurrentScreen.Exit();
|
|
||||||
currentStepIndex--;
|
|
||||||
|
|
||||||
updateButtons();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
|
||||||
{
|
|
||||||
if (!e.Repeat)
|
|
||||||
{
|
|
||||||
switch (e.Action)
|
|
||||||
{
|
|
||||||
case GlobalAction.Select:
|
|
||||||
DisplayedFooterContent?.NextButton.TriggerClick();
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case GlobalAction.Back:
|
|
||||||
footer.BackButton.TriggerClick();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.OnPressed(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Show()
|
public override void Show()
|
||||||
{
|
{
|
||||||
// if we are valid for display, only do so after reaching the main menu.
|
// if we are valid for display, only do so after reaching the main menu.
|
||||||
@ -207,24 +70,11 @@ namespace osu.Game.Overlays
|
|||||||
}, new[] { typeof(MainMenu) });
|
}, new[] { typeof(MainMenu) });
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PopIn()
|
|
||||||
{
|
|
||||||
base.PopIn();
|
|
||||||
|
|
||||||
content.ScaleTo(0.99f)
|
|
||||||
.ScaleTo(1, 400, Easing.OutQuint);
|
|
||||||
|
|
||||||
if (currentStepIndex == null)
|
|
||||||
showFirstStep();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void PopOut()
|
protected override void PopOut()
|
||||||
{
|
{
|
||||||
base.PopOut();
|
base.PopOut();
|
||||||
|
|
||||||
content.ScaleTo(0.99f, 400, Easing.OutQuint);
|
if (CurrentStepIndex != null)
|
||||||
|
|
||||||
if (currentStepIndex != null)
|
|
||||||
{
|
{
|
||||||
notificationOverlay.Post(new SimpleNotification
|
notificationOverlay.Post(new SimpleNotification
|
||||||
{
|
{
|
||||||
@ -237,112 +87,14 @@ namespace osu.Game.Overlays
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
stack?.FadeOut(100)
|
|
||||||
.Expire();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showFirstStep()
|
protected override void ShowNextStep()
|
||||||
{
|
{
|
||||||
Debug.Assert(currentStepIndex == null);
|
base.ShowNextStep();
|
||||||
|
|
||||||
screenContent.Child = stack = new ScreenStack
|
if (CurrentStepIndex == null)
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
};
|
|
||||||
|
|
||||||
currentStepIndex = -1;
|
|
||||||
showNextStep();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showNextStep()
|
|
||||||
{
|
|
||||||
Debug.Assert(currentStepIndex != null);
|
|
||||||
Debug.Assert(stack != null);
|
|
||||||
|
|
||||||
currentStepIndex++;
|
|
||||||
|
|
||||||
if (currentStepIndex < steps.Count)
|
|
||||||
{
|
|
||||||
var nextScreen = (Screen)Activator.CreateInstance(steps[currentStepIndex.Value])!;
|
|
||||||
|
|
||||||
loadingShowDelegate = Scheduler.AddDelayed(() => loading.Show(), 200);
|
|
||||||
nextScreen.OnLoadComplete += _ =>
|
|
||||||
{
|
|
||||||
loadingShowDelegate?.Cancel();
|
|
||||||
loading.Hide();
|
|
||||||
};
|
|
||||||
|
|
||||||
stack.Push(nextScreen);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
showFirstRunSetup.Value = false;
|
showFirstRunSetup.Value = false;
|
||||||
currentStepIndex = null;
|
|
||||||
Hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
updateButtons();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateButtons() => DisplayedFooterContent?.UpdateButtons(currentStepIndex, steps);
|
|
||||||
|
|
||||||
public partial class FirstRunSetupFooterContent : VisibilityContainer
|
|
||||||
{
|
|
||||||
public ShearedButton NextButton { get; private set; } = null!;
|
|
||||||
|
|
||||||
public Action? ShowNextStep;
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OverlayColourProvider colourProvider)
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
|
||||||
|
|
||||||
InternalChild = NextButton = new ShearedButton(0)
|
|
||||||
{
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
Margin = new MarginPadding { Right = 12f },
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Width = 1,
|
|
||||||
Text = FirstRunSetupOverlayStrings.GetStarted,
|
|
||||||
DarkerColour = colourProvider.Colour2,
|
|
||||||
LighterColour = colourProvider.Colour1,
|
|
||||||
Action = () => ShowNextStep?.Invoke(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateButtons(int? currentStep, IReadOnlyList<Type> steps)
|
|
||||||
{
|
|
||||||
NextButton.Enabled.Value = currentStep != null;
|
|
||||||
|
|
||||||
if (currentStep == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
bool isFirstStep = currentStep == 0;
|
|
||||||
bool isLastStep = currentStep == steps.Count - 1;
|
|
||||||
|
|
||||||
if (isFirstStep)
|
|
||||||
NextButton.Text = FirstRunSetupOverlayStrings.GetStarted;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
NextButton.Text = isLastStep
|
|
||||||
? CommonStrings.Finish
|
|
||||||
: LocalisableString.Interpolate($@"{CommonStrings.Next} ({steps[currentStep.Value + 1].GetLocalisableDescription()})");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void PopIn()
|
|
||||||
{
|
|
||||||
this.FadeIn();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void PopOut()
|
|
||||||
{
|
|
||||||
this.Delay(400).FadeOut();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
288
osu.Game/Overlays/WizardOverlay.cs
Normal file
288
osu.Game/Overlays/WizardOverlay.cs
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Framework.Screens;
|
||||||
|
using osu.Framework.Threading;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Input.Bindings;
|
||||||
|
using osu.Game.Localisation;
|
||||||
|
using osu.Game.Overlays.Mods;
|
||||||
|
using osu.Game.Screens.Footer;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays
|
||||||
|
{
|
||||||
|
public partial class WizardOverlay : ShearedOverlayContainer
|
||||||
|
{
|
||||||
|
private ScreenStack? stack;
|
||||||
|
|
||||||
|
public ShearedButton? NextButton => DisplayedFooterContent?.NextButton;
|
||||||
|
|
||||||
|
protected int? CurrentStepIndex { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The currently displayed screen, if any.
|
||||||
|
/// </summary>
|
||||||
|
public WizardScreen? CurrentScreen => (WizardScreen?)stack?.CurrentScreen;
|
||||||
|
|
||||||
|
private readonly List<Type> steps = new List<Type>();
|
||||||
|
|
||||||
|
private Container screenContent = null!;
|
||||||
|
|
||||||
|
private Container content = null!;
|
||||||
|
|
||||||
|
private LoadingSpinner loading = null!;
|
||||||
|
private ScheduledDelegate? loadingShowDelegate;
|
||||||
|
|
||||||
|
protected WizardOverlay(OverlayColourScheme scheme)
|
||||||
|
: base(scheme)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
MainAreaContent.AddRange(new Drawable[]
|
||||||
|
{
|
||||||
|
content = new PopoverContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding { Bottom = 20 },
|
||||||
|
Child = new GridContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
ColumnDimensions = new[]
|
||||||
|
{
|
||||||
|
new Dimension(),
|
||||||
|
new Dimension(minSize: 640, maxSize: 800),
|
||||||
|
new Dimension(),
|
||||||
|
},
|
||||||
|
Content = new[]
|
||||||
|
{
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
Empty(),
|
||||||
|
new InputBlockingContainer
|
||||||
|
{
|
||||||
|
Masking = true,
|
||||||
|
CornerRadius = 14,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = ColourProvider.Background6,
|
||||||
|
},
|
||||||
|
loading = new LoadingSpinner(),
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding { Vertical = 20 },
|
||||||
|
Child = screenContent = new Container { RelativeSizeAxes = Axes.Both, },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Empty(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private ScreenFooter footer { get; set; } = null!;
|
||||||
|
|
||||||
|
public new WizardFooterContent? DisplayedFooterContent => base.DisplayedFooterContent as WizardFooterContent;
|
||||||
|
|
||||||
|
public override VisibilityContainer CreateFooterContent()
|
||||||
|
{
|
||||||
|
var footerContent = new WizardFooterContent
|
||||||
|
{
|
||||||
|
ShowNextStep = ShowNextStep,
|
||||||
|
};
|
||||||
|
|
||||||
|
footerContent.OnLoadComplete += _ => updateButtons();
|
||||||
|
return footerContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool OnBackButton()
|
||||||
|
{
|
||||||
|
if (CurrentStepIndex == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Debug.Assert(stack != null);
|
||||||
|
|
||||||
|
stack.CurrentScreen.Exit();
|
||||||
|
CurrentStepIndex--;
|
||||||
|
|
||||||
|
updateButtons();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||||
|
{
|
||||||
|
if (!e.Repeat)
|
||||||
|
{
|
||||||
|
switch (e.Action)
|
||||||
|
{
|
||||||
|
case GlobalAction.Select:
|
||||||
|
DisplayedFooterContent?.NextButton.TriggerClick();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case GlobalAction.Back:
|
||||||
|
footer.BackButton.TriggerClick();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.OnPressed(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PopIn()
|
||||||
|
{
|
||||||
|
base.PopIn();
|
||||||
|
|
||||||
|
content.ScaleTo(0.99f)
|
||||||
|
.ScaleTo(1, 400, Easing.OutQuint);
|
||||||
|
|
||||||
|
if (CurrentStepIndex == null)
|
||||||
|
showFirstStep();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PopOut()
|
||||||
|
{
|
||||||
|
base.PopOut();
|
||||||
|
|
||||||
|
content.ScaleTo(0.99f, 400, Easing.OutQuint);
|
||||||
|
|
||||||
|
if (CurrentStepIndex == null)
|
||||||
|
{
|
||||||
|
stack?.FadeOut(100)
|
||||||
|
.Expire();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void AddStep<T>()
|
||||||
|
where T : WizardScreen
|
||||||
|
{
|
||||||
|
steps.Add(typeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showFirstStep()
|
||||||
|
{
|
||||||
|
Debug.Assert(CurrentStepIndex == null);
|
||||||
|
|
||||||
|
screenContent.Child = stack = new ScreenStack
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
};
|
||||||
|
|
||||||
|
CurrentStepIndex = -1;
|
||||||
|
ShowNextStep();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void ShowNextStep()
|
||||||
|
{
|
||||||
|
Debug.Assert(CurrentStepIndex != null);
|
||||||
|
Debug.Assert(stack != null);
|
||||||
|
|
||||||
|
CurrentStepIndex++;
|
||||||
|
|
||||||
|
if (CurrentStepIndex < steps.Count)
|
||||||
|
{
|
||||||
|
var nextScreen = (Screen)Activator.CreateInstance(steps[CurrentStepIndex.Value])!;
|
||||||
|
|
||||||
|
loadingShowDelegate = Scheduler.AddDelayed(() => loading.Show(), 200);
|
||||||
|
nextScreen.OnLoadComplete += _ =>
|
||||||
|
{
|
||||||
|
loadingShowDelegate?.Cancel();
|
||||||
|
loading.Hide();
|
||||||
|
};
|
||||||
|
|
||||||
|
stack.Push(nextScreen);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CurrentStepIndex = null;
|
||||||
|
Hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateButtons() => DisplayedFooterContent?.UpdateButtons(CurrentStepIndex, steps);
|
||||||
|
|
||||||
|
public partial class WizardFooterContent : VisibilityContainer
|
||||||
|
{
|
||||||
|
public ShearedButton NextButton { get; private set; } = null!;
|
||||||
|
|
||||||
|
public Action? ShowNextStep;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OverlayColourProvider colourProvider)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
InternalChild = NextButton = new ShearedButton(0)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Margin = new MarginPadding { Right = 12f },
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Width = 1,
|
||||||
|
Text = FirstRunSetupOverlayStrings.GetStarted,
|
||||||
|
DarkerColour = colourProvider.Colour2,
|
||||||
|
LighterColour = colourProvider.Colour1,
|
||||||
|
Action = () => ShowNextStep?.Invoke(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateButtons(int? currentStep, IReadOnlyList<Type> steps)
|
||||||
|
{
|
||||||
|
NextButton.Enabled.Value = currentStep != null;
|
||||||
|
|
||||||
|
if (currentStep == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool isFirstStep = currentStep == 0;
|
||||||
|
bool isLastStep = currentStep == steps.Count - 1;
|
||||||
|
|
||||||
|
if (isFirstStep)
|
||||||
|
NextButton.Text = FirstRunSetupOverlayStrings.GetStarted;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NextButton.Text = isLastStep
|
||||||
|
? CommonStrings.Finish
|
||||||
|
: LocalisableString.Interpolate($@"{CommonStrings.Next} ({steps[currentStep.Value + 1].GetLocalisableDescription()})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PopIn()
|
||||||
|
{
|
||||||
|
this.FadeIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PopOut()
|
||||||
|
{
|
||||||
|
this.Delay(400).FadeOut();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,9 +13,9 @@ using osu.Game.Graphics.Containers;
|
|||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.FirstRunSetup
|
namespace osu.Game.Overlays
|
||||||
{
|
{
|
||||||
public abstract partial class FirstRunSetupScreen : Screen
|
public abstract partial class WizardScreen : Screen
|
||||||
{
|
{
|
||||||
private const float offset = 100;
|
private const float offset = 100;
|
||||||
|
|
@ -577,6 +577,11 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual bool AllowGameplayOverlays => true;
|
public virtual bool AllowGameplayOverlays => true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// On mobile devices, this specifies whether this ruleset requires the device to be in portrait orientation.
|
||||||
|
/// </summary>
|
||||||
|
public virtual bool RequiresPortraitOrientation => false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets a replay to be used, overriding local input.
|
/// Sets a replay to be used, overriding local input.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -61,6 +61,17 @@ namespace osu.Game.Screens
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
bool HideMenuCursorOnNonMouseInput { get; }
|
bool HideMenuCursorOnNonMouseInput { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// On mobile phones, this specifies whether this <see cref="OsuScreen"/> requires the device to be in portrait orientation.
|
||||||
|
/// Tablet devices are unaffected by this property.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// By default, all screens in the game display in landscape orientation on phones.
|
||||||
|
/// Setting this to <c>true</c> will display this screen in portrait orientation instead,
|
||||||
|
/// and switch back to landscape when transitioning back to a regular non-portrait screen.
|
||||||
|
/// </remarks>
|
||||||
|
bool RequiresPortraitOrientation { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether overlays should be able to be opened when this screen is current.
|
/// Whether overlays should be able to be opened when this screen is current.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -47,6 +47,8 @@ namespace osu.Game.Screens
|
|||||||
|
|
||||||
public virtual bool HideMenuCursorOnNonMouseInput => false;
|
public virtual bool HideMenuCursorOnNonMouseInput => false;
|
||||||
|
|
||||||
|
public virtual bool RequiresPortraitOrientation => false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The initial overlay activation mode to use when this screen is entered for the first time.
|
/// The initial overlay activation mode to use when this screen is entered for the first time.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -68,6 +68,17 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
public override bool HideMenuCursorOnNonMouseInput => true;
|
public override bool HideMenuCursorOnNonMouseInput => true;
|
||||||
|
|
||||||
|
public override bool RequiresPortraitOrientation
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!LoadedBeatmapSuccessfully)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return DrawableRuleset!.RequiresPortraitOrientation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.UserTriggered;
|
protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.UserTriggered;
|
||||||
|
|
||||||
// We are managing our own adjustments (see OnEntering/OnExiting).
|
// We are managing our own adjustments (see OnEntering/OnExiting).
|
||||||
|
@ -53,6 +53,9 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
public override bool? AllowGlobalTrackControl => false;
|
public override bool? AllowGlobalTrackControl => false;
|
||||||
|
|
||||||
|
// this makes the game stay in portrait mode when restarting gameplay rather than switching back to landscape.
|
||||||
|
public override bool RequiresPortraitOrientation => CurrentPlayer?.RequiresPortraitOrientation == true;
|
||||||
|
|
||||||
public override float BackgroundParallaxAmount => quickRestart ? 0 : 1;
|
public override float BackgroundParallaxAmount => quickRestart ? 0 : 1;
|
||||||
|
|
||||||
// Here because IsHovered will not update unless we do so.
|
// Here because IsHovered will not update unless we do so.
|
||||||
|
@ -152,7 +152,7 @@ namespace osu.Game.Screens.Play
|
|||||||
Logger.Log($"Please ensure that you are using the latest version of the official game releases.\n\n{whatWillHappen}", level: LogLevel.Important);
|
Logger.Log($"Please ensure that you are using the latest version of the official game releases.\n\n{whatWillHappen}", level: LogLevel.Important);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case @"invalid beatmap_hash":
|
case @"invalid or missing beatmap_hash":
|
||||||
Logger.Log($"This beatmap does not match the online version. Please update or redownload it.\n\n{whatWillHappen}", level: LogLevel.Important);
|
Logger.Log($"This beatmap does not match the online version. Please update or redownload it.\n\n{whatWillHappen}", level: LogLevel.Important);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
@ -91,6 +90,9 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy
|
|||||||
|
|
||||||
private readonly ScoreInfo score;
|
private readonly ScoreInfo score;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private ResultsScreen? resultsScreen { get; set; }
|
||||||
|
|
||||||
private CircularProgress accuracyCircle = null!;
|
private CircularProgress accuracyCircle = null!;
|
||||||
private GradedCircles gradedCircles = null!;
|
private GradedCircles gradedCircles = null!;
|
||||||
private Container<RankBadge> badges = null!;
|
private Container<RankBadge> badges = null!;
|
||||||
@ -101,7 +103,6 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy
|
|||||||
private PoolableSkinnableSample? badgeMaxSound;
|
private PoolableSkinnableSample? badgeMaxSound;
|
||||||
private PoolableSkinnableSample? swooshUpSound;
|
private PoolableSkinnableSample? swooshUpSound;
|
||||||
private PoolableSkinnableSample? rankImpactSound;
|
private PoolableSkinnableSample? rankImpactSound;
|
||||||
private PoolableSkinnableSample? rankApplauseSound;
|
|
||||||
|
|
||||||
private readonly Bindable<double> tickPlaybackRate = new Bindable<double>();
|
private readonly Bindable<double> tickPlaybackRate = new Bindable<double>();
|
||||||
|
|
||||||
@ -197,15 +198,9 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy
|
|||||||
|
|
||||||
if (withFlair)
|
if (withFlair)
|
||||||
{
|
{
|
||||||
var applauseSamples = new List<string> { applauseSampleName };
|
|
||||||
if (score.Rank >= ScoreRank.B)
|
|
||||||
// when rank is B or higher, play legacy applause sample on legacy skins.
|
|
||||||
applauseSamples.Insert(0, @"applause");
|
|
||||||
|
|
||||||
AddRangeInternal(new Drawable[]
|
AddRangeInternal(new Drawable[]
|
||||||
{
|
{
|
||||||
rankImpactSound = new PoolableSkinnableSample(new SampleInfo(impactSampleName)),
|
rankImpactSound = new PoolableSkinnableSample(new SampleInfo(impactSampleName)),
|
||||||
rankApplauseSound = new PoolableSkinnableSample(new SampleInfo(applauseSamples.ToArray())),
|
|
||||||
scoreTickSound = new PoolableSkinnableSample(new SampleInfo(@"Results/score-tick")),
|
scoreTickSound = new PoolableSkinnableSample(new SampleInfo(@"Results/score-tick")),
|
||||||
badgeTickSound = new PoolableSkinnableSample(new SampleInfo(@"Results/badge-dink")),
|
badgeTickSound = new PoolableSkinnableSample(new SampleInfo(@"Results/badge-dink")),
|
||||||
badgeMaxSound = new PoolableSkinnableSample(new SampleInfo(@"Results/badge-dink-max")),
|
badgeMaxSound = new PoolableSkinnableSample(new SampleInfo(@"Results/badge-dink-max")),
|
||||||
@ -333,16 +328,9 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy
|
|||||||
});
|
});
|
||||||
|
|
||||||
const double applause_pre_delay = 545f;
|
const double applause_pre_delay = 545f;
|
||||||
const double applause_volume = 0.8f;
|
|
||||||
|
|
||||||
using (BeginDelayedSequence(applause_pre_delay))
|
using (BeginDelayedSequence(applause_pre_delay))
|
||||||
{
|
Schedule(() => resultsScreen?.PlayApplause(score.Rank));
|
||||||
Schedule(() =>
|
|
||||||
{
|
|
||||||
rankApplauseSound!.VolumeTo(applause_volume);
|
|
||||||
rankApplauseSound!.Play();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,34 +372,6 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string applauseSampleName
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
switch (score.Rank)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
case ScoreRank.D:
|
|
||||||
return @"Results/applause-d";
|
|
||||||
|
|
||||||
case ScoreRank.C:
|
|
||||||
return @"Results/applause-c";
|
|
||||||
|
|
||||||
case ScoreRank.B:
|
|
||||||
return @"Results/applause-b";
|
|
||||||
|
|
||||||
case ScoreRank.A:
|
|
||||||
return @"Results/applause-a";
|
|
||||||
|
|
||||||
case ScoreRank.S:
|
|
||||||
case ScoreRank.SH:
|
|
||||||
case ScoreRank.X:
|
|
||||||
case ScoreRank.XH:
|
|
||||||
return @"Results/applause-s";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string impactSampleName
|
private string impactSampleName
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -17,6 +17,7 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
|
using osu.Game.Audio;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
@ -29,10 +30,12 @@ using osu.Game.Scoring;
|
|||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Screens.Ranking.Expanded.Accuracy;
|
using osu.Game.Screens.Ranking.Expanded.Accuracy;
|
||||||
using osu.Game.Screens.Ranking.Statistics;
|
using osu.Game.Screens.Ranking.Statistics;
|
||||||
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Ranking
|
namespace osu.Game.Screens.Ranking
|
||||||
{
|
{
|
||||||
|
[Cached]
|
||||||
public abstract partial class ResultsScreen : ScreenWithBeatmapBackground, IKeyBindingHandler<GlobalAction>
|
public abstract partial class ResultsScreen : ScreenWithBeatmapBackground, IKeyBindingHandler<GlobalAction>
|
||||||
{
|
{
|
||||||
protected const float BACKGROUND_BLUR = 20;
|
protected const float BACKGROUND_BLUR = 20;
|
||||||
@ -263,6 +266,64 @@ namespace osu.Game.Screens.Ranking
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Applause
|
||||||
|
|
||||||
|
private PoolableSkinnableSample? rankApplauseSound;
|
||||||
|
|
||||||
|
public void PlayApplause(ScoreRank rank)
|
||||||
|
{
|
||||||
|
const double applause_volume = 0.8f;
|
||||||
|
|
||||||
|
if (!this.IsCurrentScreen())
|
||||||
|
return;
|
||||||
|
|
||||||
|
rankApplauseSound?.Dispose();
|
||||||
|
|
||||||
|
var applauseSamples = new List<string>();
|
||||||
|
|
||||||
|
if (rank >= ScoreRank.B)
|
||||||
|
// when rank is B or higher, play legacy applause sample on legacy skins.
|
||||||
|
applauseSamples.Insert(0, @"applause");
|
||||||
|
|
||||||
|
switch (rank)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case ScoreRank.D:
|
||||||
|
applauseSamples.Add(@"Results/applause-d");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ScoreRank.C:
|
||||||
|
applauseSamples.Add(@"Results/applause-c");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ScoreRank.B:
|
||||||
|
applauseSamples.Add(@"Results/applause-b");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ScoreRank.A:
|
||||||
|
applauseSamples.Add(@"Results/applause-a");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ScoreRank.S:
|
||||||
|
case ScoreRank.SH:
|
||||||
|
case ScoreRank.X:
|
||||||
|
case ScoreRank.XH:
|
||||||
|
applauseSamples.Add(@"Results/applause-s");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadComponentAsync(rankApplauseSound = new PoolableSkinnableSample(new SampleInfo(applauseSamples.ToArray())), s =>
|
||||||
|
{
|
||||||
|
if (!this.IsCurrentScreen() || s != rankApplauseSound)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rankApplauseSound.VolumeTo(applause_volume);
|
||||||
|
rankApplauseSound.Play();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs a fetch/refresh of scores to be displayed.
|
/// Performs a fetch/refresh of scores to be displayed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -330,6 +391,8 @@ namespace osu.Game.Screens.Ranking
|
|||||||
|
|
||||||
if (!skipExitTransition)
|
if (!skipExitTransition)
|
||||||
this.FadeOut(100);
|
this.FadeOut(100);
|
||||||
|
|
||||||
|
rankApplauseSound?.Stop();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
49
osu.Game/Utils/MobileUtils.cs
Normal file
49
osu.Game/Utils/MobileUtils.cs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// 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.Game.Screens;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
|
||||||
|
namespace osu.Game.Utils
|
||||||
|
{
|
||||||
|
public static class MobileUtils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Determines the correct <see cref="Orientation"/> state which a mobile device should be put into for the given information.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userPlayInfo">Information about whether the user is currently playing.</param>
|
||||||
|
/// <param name="currentScreen">The current screen which the user is at.</param>
|
||||||
|
/// <param name="isTablet">Whether the user is playing on a mobile tablet device instead of a phone.</param>
|
||||||
|
public static Orientation GetOrientation(ILocalUserPlayInfo userPlayInfo, IOsuScreen currentScreen, bool isTablet)
|
||||||
|
{
|
||||||
|
bool lockCurrentOrientation = userPlayInfo.PlayingState.Value == LocalUserPlayingState.Playing;
|
||||||
|
bool lockToPortraitOnPhone = currentScreen.RequiresPortraitOrientation;
|
||||||
|
|
||||||
|
if (lockToPortraitOnPhone && !isTablet)
|
||||||
|
return Orientation.Portrait;
|
||||||
|
|
||||||
|
if (lockCurrentOrientation)
|
||||||
|
return Orientation.Locked;
|
||||||
|
|
||||||
|
return Orientation.Default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Orientation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Lock the game orientation.
|
||||||
|
/// </summary>
|
||||||
|
Locked,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lock the game to portrait orientation (does not include upside-down portrait).
|
||||||
|
/// </summary>
|
||||||
|
Portrait,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Use the application's default settings.
|
||||||
|
/// </summary>
|
||||||
|
Default,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,61 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using Foundation;
|
using Foundation;
|
||||||
using osu.Framework.iOS;
|
using osu.Framework.iOS;
|
||||||
|
using UIKit;
|
||||||
|
|
||||||
namespace osu.iOS
|
namespace osu.iOS
|
||||||
{
|
{
|
||||||
[Register("AppDelegate")]
|
[Register("AppDelegate")]
|
||||||
public class AppDelegate : GameApplicationDelegate
|
public class AppDelegate : GameApplicationDelegate
|
||||||
{
|
{
|
||||||
protected override Framework.Game CreateGame() => new OsuGameIOS();
|
private UIInterfaceOrientationMask? defaultOrientationsMask;
|
||||||
|
private UIInterfaceOrientationMask? orientations;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current orientation the game is displayed in.
|
||||||
|
/// </summary>
|
||||||
|
public UIInterfaceOrientation CurrentOrientation => Host.Window.UIWindow.WindowScene!.InterfaceOrientation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Controls the orientations allowed for the device to rotate to, overriding the default allowed orientations.
|
||||||
|
/// </summary>
|
||||||
|
public UIInterfaceOrientationMask? Orientations
|
||||||
|
{
|
||||||
|
get => orientations;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (orientations == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
orientations = value;
|
||||||
|
|
||||||
|
if (OperatingSystem.IsIOSVersionAtLeast(16))
|
||||||
|
Host.Window.ViewController.SetNeedsUpdateOfSupportedInterfaceOrientations();
|
||||||
|
else
|
||||||
|
UIViewController.AttemptRotationToDeviceOrientation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Framework.Game CreateGame() => new OsuGameIOS(this);
|
||||||
|
|
||||||
|
public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations(UIApplication application, UIWindow forWindow)
|
||||||
|
{
|
||||||
|
if (orientations != null)
|
||||||
|
return orientations.Value;
|
||||||
|
|
||||||
|
if (defaultOrientationsMask == null)
|
||||||
|
{
|
||||||
|
defaultOrientationsMask = 0;
|
||||||
|
var defaultOrientations = (NSArray)NSBundle.MainBundle.ObjectForInfoDictionary("UISupportedInterfaceOrientations");
|
||||||
|
|
||||||
|
foreach (var value in defaultOrientations.ToArray<NSString>())
|
||||||
|
defaultOrientationsMask |= Enum.Parse<UIInterfaceOrientationMask>(value.ToString().Replace("UIInterfaceOrientation", string.Empty));
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultOrientationsMask.Value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,17 +8,60 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.iOS;
|
using osu.Framework.iOS;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Game;
|
using osu.Game;
|
||||||
|
using osu.Game.Screens;
|
||||||
using osu.Game.Updater;
|
using osu.Game.Updater;
|
||||||
using osu.Game.Utils;
|
using osu.Game.Utils;
|
||||||
|
using UIKit;
|
||||||
|
|
||||||
namespace osu.iOS
|
namespace osu.iOS
|
||||||
{
|
{
|
||||||
public partial class OsuGameIOS : OsuGame
|
public partial class OsuGameIOS : OsuGame
|
||||||
{
|
{
|
||||||
|
private readonly AppDelegate appDelegate;
|
||||||
public override Version AssemblyVersion => new Version(NSBundle.MainBundle.InfoDictionary["CFBundleVersion"].ToString());
|
public override Version AssemblyVersion => new Version(NSBundle.MainBundle.InfoDictionary["CFBundleVersion"].ToString());
|
||||||
|
|
||||||
public override bool HideUnlicensedContent => true;
|
public override bool HideUnlicensedContent => true;
|
||||||
|
|
||||||
|
public OsuGameIOS(AppDelegate appDelegate)
|
||||||
|
{
|
||||||
|
this.appDelegate = appDelegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
UserPlayingState.BindValueChanged(_ => updateOrientation());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ScreenChanged(IOsuScreen? current, IOsuScreen? newScreen)
|
||||||
|
{
|
||||||
|
base.ScreenChanged(current, newScreen);
|
||||||
|
|
||||||
|
if (newScreen != null)
|
||||||
|
updateOrientation();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateOrientation()
|
||||||
|
{
|
||||||
|
bool iPad = UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad;
|
||||||
|
var orientation = MobileUtils.GetOrientation(this, (IOsuScreen)ScreenStack.CurrentScreen, iPad);
|
||||||
|
|
||||||
|
switch (orientation)
|
||||||
|
{
|
||||||
|
case MobileUtils.Orientation.Locked:
|
||||||
|
appDelegate.Orientations = (UIInterfaceOrientationMask)(1 << (int)appDelegate.CurrentOrientation);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MobileUtils.Orientation.Portrait:
|
||||||
|
appDelegate.Orientations = UIInterfaceOrientationMask.Portrait;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MobileUtils.Orientation.Default:
|
||||||
|
appDelegate.Orientations = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override UpdateManager CreateUpdateManager() => new MobileUpdateNotifier();
|
protected override UpdateManager CreateUpdateManager() => new MobileUpdateNotifier();
|
||||||
|
|
||||||
protected override BatteryInfo CreateBatteryInfo() => new IOSBatteryInfo();
|
protected override BatteryInfo CreateBatteryInfo() => new IOSBatteryInfo();
|
||||||
|
Loading…
Reference in New Issue
Block a user