1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-22 17:12:54 +08:00

Merge pull request #16772 from peppy/mvp-safe-area-support

This commit is contained in:
Salman Ahmed 2022-02-04 17:28:32 +03:00 committed by GitHub
commit 0f15cf07ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 159 additions and 17 deletions

View File

@ -0,0 +1,115 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Overlays.Settings;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneSafeAreaHandling : OsuGameTestScene
{
private SafeAreaDefiningContainer safeAreaContainer;
private static BindableSafeArea safeArea;
private readonly Bindable<float> safeAreaPaddingTop = new BindableFloat { MinValue = 0, MaxValue = 200 };
private readonly Bindable<float> safeAreaPaddingBottom = new BindableFloat { MinValue = 0, MaxValue = 200 };
private readonly Bindable<float> safeAreaPaddingLeft = new BindableFloat { MinValue = 0, MaxValue = 200 };
private readonly Bindable<float> safeAreaPaddingRight = new BindableFloat { MinValue = 0, MaxValue = 200 };
protected override void LoadComplete()
{
base.LoadComplete();
// Usually this would be placed between the host and the game, but that's a bit of a pain to do with the test scene hierarchy.
// Add is required for the container to get a size (and give out correct metrics to the usages in SafeAreaContainer).
Add(safeAreaContainer = new SafeAreaDefiningContainer(safeArea = new BindableSafeArea())
{
RelativeSizeAxes = Axes.Both
});
// Cache is required for the test game to see the safe area.
Dependencies.CacheAs<ISafeArea>(safeAreaContainer);
}
public override void SetUpSteps()
{
AddStep("Add adjust controls", () =>
{
Add(new Container
{
Depth = float.MinValue,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
Colour = Color4.Black,
RelativeSizeAxes = Axes.Both,
Alpha = 0.8f,
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
Width = 400,
Children = new Drawable[]
{
new SettingsSlider<float>
{
Current = safeAreaPaddingTop,
LabelText = "Top"
},
new SettingsSlider<float>
{
Current = safeAreaPaddingBottom,
LabelText = "Bottom"
},
new SettingsSlider<float>
{
Current = safeAreaPaddingLeft,
LabelText = "Left"
},
new SettingsSlider<float>
{
Current = safeAreaPaddingRight,
LabelText = "Right"
},
}
}
}
});
safeAreaPaddingTop.BindValueChanged(_ => updateSafeArea());
safeAreaPaddingBottom.BindValueChanged(_ => updateSafeArea());
safeAreaPaddingLeft.BindValueChanged(_ => updateSafeArea());
safeAreaPaddingRight.BindValueChanged(_ => updateSafeArea());
});
base.SetUpSteps();
}
private void updateSafeArea()
{
safeArea.Value = new MarginPadding
{
Top = safeAreaPaddingTop.Value,
Bottom = safeAreaPaddingBottom.Value,
Left = safeAreaPaddingLeft.Value,
Right = safeAreaPaddingRight.Value,
};
}
[Test]
public void TestSafeArea()
{
}
}
}

View File

@ -23,6 +23,8 @@ namespace osu.Game.Graphics.Containers
private Bindable<float> posX; private Bindable<float> posX;
private Bindable<float> posY; private Bindable<float> posY;
private Bindable<MarginPadding> safeAreaPadding;
private readonly ScalingMode? targetMode; private readonly ScalingMode? targetMode;
private Bindable<ScalingMode> scalingMode; private Bindable<ScalingMode> scalingMode;
@ -50,7 +52,7 @@ namespace osu.Game.Graphics.Containers
return; return;
allowScaling = value; allowScaling = value;
if (IsLoaded) updateSize(); if (IsLoaded) Scheduler.AddOnce(updateSize);
} }
} }
@ -102,22 +104,25 @@ namespace osu.Game.Graphics.Containers
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuConfigManager config) private void load(OsuConfigManager config, ISafeArea safeArea)
{ {
scalingMode = config.GetBindable<ScalingMode>(OsuSetting.Scaling); scalingMode = config.GetBindable<ScalingMode>(OsuSetting.Scaling);
scalingMode.ValueChanged += _ => updateSize(); scalingMode.ValueChanged += _ => Scheduler.AddOnce(updateSize);
sizeX = config.GetBindable<float>(OsuSetting.ScalingSizeX); sizeX = config.GetBindable<float>(OsuSetting.ScalingSizeX);
sizeX.ValueChanged += _ => updateSize(); sizeX.ValueChanged += _ => Scheduler.AddOnce(updateSize);
sizeY = config.GetBindable<float>(OsuSetting.ScalingSizeY); sizeY = config.GetBindable<float>(OsuSetting.ScalingSizeY);
sizeY.ValueChanged += _ => updateSize(); sizeY.ValueChanged += _ => Scheduler.AddOnce(updateSize);
posX = config.GetBindable<float>(OsuSetting.ScalingPositionX); posX = config.GetBindable<float>(OsuSetting.ScalingPositionX);
posX.ValueChanged += _ => updateSize(); posX.ValueChanged += _ => Scheduler.AddOnce(updateSize);
posY = config.GetBindable<float>(OsuSetting.ScalingPositionY); posY = config.GetBindable<float>(OsuSetting.ScalingPositionY);
posY.ValueChanged += _ => updateSize(); posY.ValueChanged += _ => Scheduler.AddOnce(updateSize);
safeAreaPadding = safeArea.SafeAreaPadding.GetBoundCopy();
safeAreaPadding.BindValueChanged(_ => Scheduler.AddOnce(updateSize));
} }
protected override void LoadComplete() protected override void LoadComplete()
@ -161,7 +166,10 @@ namespace osu.Game.Graphics.Containers
var targetSize = scaling ? new Vector2(sizeX.Value, sizeY.Value) : Vector2.One; var targetSize = scaling ? new Vector2(sizeX.Value, sizeY.Value) : Vector2.One;
var targetPosition = scaling ? new Vector2(posX.Value, posY.Value) * (Vector2.One - targetSize) : Vector2.Zero; var targetPosition = scaling ? new Vector2(posX.Value, posY.Value) * (Vector2.One - targetSize) : Vector2.Zero;
bool requiresMasking = scaling && targetSize != Vector2.One; bool requiresMasking = (scaling && targetSize != Vector2.One)
// For the top level scaling container, for now we apply masking if safe areas are in use.
// In the future this can likely be removed as more of the actual UI supports overflowing into the safe areas.
|| (targetMode == ScalingMode.Everything && safeAreaPadding.Value.Total != Vector2.Zero);
if (requiresMasking) if (requiresMasking)
sizableContainer.Masking = true; sizableContainer.Masking = true;

View File

@ -89,6 +89,12 @@ namespace osu.Game
} }
} }
/// <summary>
/// The <see cref="Edges"/> that the game should be drawn over at a top level.
/// Defaults to <see cref="Edges.None"/>.
/// </summary>
protected virtual Edges SafeAreaOverrideEdges => Edges.None;
protected OsuConfigManager LocalConfig { get; private set; } protected OsuConfigManager LocalConfig { get; private set; }
protected SessionStatics SessionStatics { get; private set; } protected SessionStatics SessionStatics { get; private set; }
@ -299,16 +305,23 @@ namespace osu.Game
GlobalActionContainer globalBindings; GlobalActionContainer globalBindings;
var mainContent = new Drawable[] base.Content.Add(new SafeAreaContainer
{ {
MenuCursorContainer = new MenuCursorContainer { RelativeSizeAxes = Axes.Both }, SafeAreaOverrideEdges = SafeAreaOverrideEdges,
// to avoid positional input being blocked by children, ensure the GlobalActionContainer is above everything. RelativeSizeAxes = Axes.Both,
globalBindings = new GlobalActionContainer(this) Child = CreateScalingContainer().WithChildren(new Drawable[]
}; {
(MenuCursorContainer = new MenuCursorContainer
MenuCursorContainer.Child = content = new OsuTooltipContainer(MenuCursorContainer.Cursor) { RelativeSizeAxes = Axes.Both }; {
RelativeSizeAxes = Axes.Both
base.Content.Add(CreateScalingContainer().WithChildren(mainContent)); }).WithChild(content = new OsuTooltipContainer(MenuCursorContainer.Cursor)
{
RelativeSizeAxes = Axes.Both
}),
// to avoid positional input being blocked by children, ensure the GlobalActionContainer is above everything.
globalBindings = new GlobalActionContainer(this)
})
});
KeyBindingStore = new RealmKeyBindingStore(realm, keyCombinationProvider); KeyBindingStore = new RealmKeyBindingStore(realm, keyCombinationProvider);
KeyBindingStore.Register(globalBindings, RulesetStore.AvailableRulesets); KeyBindingStore.Register(globalBindings, RulesetStore.AvailableRulesets);

View File

@ -3,6 +3,7 @@
using System; using System;
using Foundation; using Foundation;
using osu.Framework.Graphics;
using osu.Game; using osu.Game;
using osu.Game.Updater; using osu.Game.Updater;
using osu.Game.Utils; using osu.Game.Utils;
@ -18,6 +19,11 @@ namespace osu.iOS
protected override BatteryInfo CreateBatteryInfo() => new IOSBatteryInfo(); protected override BatteryInfo CreateBatteryInfo() => new IOSBatteryInfo();
protected override Edges SafeAreaOverrideEdges =>
// iOS shows a home indicator at the bottom, and adds a safe area to account for this.
// Because we have the home indicator (mostly) hidden we don't really care about drawing in this region.
Edges.Bottom;
private class IOSBatteryInfo : BatteryInfo private class IOSBatteryInfo : BatteryInfo
{ {
public override double ChargeLevel => Battery.ChargeLevel; public override double ChargeLevel => Battery.ChargeLevel;