mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 11:37:28 +08:00
Add letterbox/screen scaling support
This commit is contained in:
parent
2fc10cb2bd
commit
3953f829c8
@ -96,6 +96,14 @@ namespace osu.Game.Configuration
|
||||
Set(OsuSetting.ScreenshotCaptureMenuCursor, false);
|
||||
|
||||
Set(OsuSetting.SongSelectRightMouseScroll, false);
|
||||
|
||||
Set(OsuSetting.Scaling, ScalingMode.Off);
|
||||
|
||||
Set(OsuSetting.ScalingSizeX, 0.8f, 0.2f, 1f);
|
||||
Set(OsuSetting.ScalingSizeY, 0.8f, 0.2f, 1f);
|
||||
|
||||
Set(OsuSetting.ScalingPositionX, 0.5f, 0f, 1f);
|
||||
Set(OsuSetting.ScalingPositionY, 0.5f, 0f, 1f);
|
||||
}
|
||||
|
||||
public OsuConfigManager(Storage storage) : base(storage)
|
||||
@ -151,6 +159,11 @@ namespace osu.Game.Configuration
|
||||
BeatmapHitsounds,
|
||||
IncreaseFirstObjectVisibility,
|
||||
ScoreDisplayMode,
|
||||
ExternalLinkWarning
|
||||
ExternalLinkWarning,
|
||||
Scaling,
|
||||
ScalingPositionX,
|
||||
ScalingPositionY,
|
||||
ScalingSizeX,
|
||||
ScalingSizeY
|
||||
}
|
||||
}
|
||||
|
12
osu.Game/Configuration/ScalingMode.cs
Normal file
12
osu.Game/Configuration/ScalingMode.cs
Normal file
@ -0,0 +1,12 @@
|
||||
// Copyright (c) 2007-2019 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
namespace osu.Game.Configuration
|
||||
{
|
||||
public enum ScalingMode
|
||||
{
|
||||
Off,
|
||||
Everything,
|
||||
ExcludeOverlays,
|
||||
Gameplay,
|
||||
}
|
||||
}
|
122
osu.Game/Graphics/Containers/ScalingContainer.cs
Normal file
122
osu.Game/Graphics/Containers/ScalingContainer.cs
Normal file
@ -0,0 +1,122 @@
|
||||
// Copyright (c) 2007-2019 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.Backgrounds;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Graphics.Containers
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles user-defined scaling, allowing application at multiple levels defined by <see cref="ScalingMode"/>.
|
||||
/// </summary>
|
||||
public class ScalingContainer : Container
|
||||
{
|
||||
private readonly bool isTopLevel;
|
||||
|
||||
private Bindable<float> sizeX;
|
||||
private Bindable<float> sizeY;
|
||||
private Bindable<float> posX;
|
||||
private Bindable<float> posY;
|
||||
|
||||
private readonly ScalingMode targetMode;
|
||||
|
||||
private Bindable<ScalingMode> scalingMode;
|
||||
|
||||
private readonly Container content;
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
private readonly Container sizableContainer;
|
||||
|
||||
private Drawable backgroundLayer;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance.
|
||||
/// </summary>
|
||||
/// <param name="targetMode">The mode which this container should be handling.</param>
|
||||
public ScalingContainer(ScalingMode targetMode)
|
||||
{
|
||||
this.targetMode = targetMode;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
InternalChild = sizableContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
RelativePositionAxes = Axes.Both,
|
||||
CornerRadius = 10,
|
||||
Child = content = new DrawSizePreservingFillContainer()
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
scalingMode = config.GetBindable<ScalingMode>(OsuSetting.Scaling);
|
||||
scalingMode.ValueChanged += _ => updateSize();
|
||||
|
||||
sizeX = config.GetBindable<float>(OsuSetting.ScalingSizeX);
|
||||
sizeX.ValueChanged += _ => updateSize();
|
||||
|
||||
sizeY = config.GetBindable<float>(OsuSetting.ScalingSizeY);
|
||||
sizeY.ValueChanged += _ => updateSize();
|
||||
|
||||
posX = config.GetBindable<float>(OsuSetting.ScalingPositionX);
|
||||
posX.ValueChanged += _ => updateSize();
|
||||
|
||||
posY = config.GetBindable<float>(OsuSetting.ScalingPositionY);
|
||||
posY.ValueChanged += _ => updateSize();
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
updateSize();
|
||||
content.FinishTransforms();
|
||||
}
|
||||
|
||||
private bool requiresBackgroundVisible => (scalingMode == ScalingMode.Everything || scalingMode == ScalingMode.ExcludeOverlays) && (sizeX.Value != 1 || sizeY.Value != 1);
|
||||
|
||||
private void updateSize()
|
||||
{
|
||||
if (targetMode == ScalingMode.Everything)
|
||||
{
|
||||
// the top level scaling container manages the background to be displayed while scaling.
|
||||
if (requiresBackgroundVisible)
|
||||
{
|
||||
if (backgroundLayer == null)
|
||||
LoadComponentAsync(backgroundLayer = new Background("Menu/menu-background-1")
|
||||
{
|
||||
Colour = OsuColour.Gray(0.1f),
|
||||
Alpha = 0,
|
||||
Depth = float.MaxValue
|
||||
}, d =>
|
||||
{
|
||||
AddInternal(d);
|
||||
d.FadeTo(requiresBackgroundVisible ? 1 : 0, 4000, Easing.OutQuint);
|
||||
});
|
||||
else
|
||||
backgroundLayer.FadeIn(500);
|
||||
}
|
||||
else
|
||||
backgroundLayer?.FadeOut(500);
|
||||
}
|
||||
|
||||
bool letterbox = scalingMode.Value == targetMode;
|
||||
|
||||
var targetSize = letterbox ? new Vector2(sizeX, sizeY) : Vector2.One;
|
||||
var targetPosition = letterbox ? new Vector2(posX, posY) * (Vector2.One - targetSize) : Vector2.Zero;
|
||||
bool requiresMasking = targetSize != Vector2.One;
|
||||
|
||||
if (requiresMasking)
|
||||
sizableContainer.Masking = true;
|
||||
|
||||
sizableContainer.MoveTo(targetPosition, 500, Easing.OutQuart);
|
||||
sizableContainer.ResizeTo(targetSize, 500, Easing.OutQuart).OnComplete(_ => { content.Masking = requiresMasking; });
|
||||
}
|
||||
}
|
||||
}
|
@ -47,7 +47,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
var floatMinValue = bindableDouble?.MinValue ?? bindableFloat.MinValue;
|
||||
var floatMaxValue = bindableDouble?.MaxValue ?? bindableFloat.MaxValue;
|
||||
|
||||
if (floatMaxValue == 1 && (floatMinValue == 0 || floatMinValue == -1))
|
||||
if (floatMaxValue == 1 && floatMinValue >= -1)
|
||||
return floatValue.Value.ToString("P0");
|
||||
|
||||
var decimalPrecision = normalise((decimal)floatPrecision, max_decimal_digits);
|
||||
|
@ -26,6 +26,7 @@ using osu.Framework.Platform;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Input;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Rulesets;
|
||||
@ -187,6 +188,7 @@ namespace osu.Game
|
||||
}
|
||||
|
||||
private ExternalLinkOpener externalLinkOpener;
|
||||
|
||||
public void OpenUrlExternally(string url)
|
||||
{
|
||||
if (url.StartsWith("/"))
|
||||
@ -353,7 +355,11 @@ namespace osu.Game
|
||||
ActionRequested = action => volume.Adjust(action),
|
||||
ScrollActionRequested = (action, amount, isPrecise) => volume.Adjust(action, amount, isPrecise),
|
||||
},
|
||||
mainContent = new Container { RelativeSizeAxes = Axes.Both },
|
||||
screenContainer = new ScalingContainer(ScalingMode.ExcludeOverlays)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
mainContent = new DrawSizePreservingFillContainer(),
|
||||
overlayContent = new Container { RelativeSizeAxes = Axes.Both, Depth = float.MinValue },
|
||||
idleTracker = new IdleTracker(6000)
|
||||
});
|
||||
@ -362,7 +368,7 @@ namespace osu.Game
|
||||
{
|
||||
screenStack.ModePushed += screenAdded;
|
||||
screenStack.Exited += screenRemoved;
|
||||
mainContent.Add(screenStack);
|
||||
screenContainer.Add(screenStack);
|
||||
});
|
||||
|
||||
loadComponentSingleFile(Toolbar = new Toolbar
|
||||
@ -497,7 +503,7 @@ namespace osu.Game
|
||||
if (notifications.State == Visibility.Visible)
|
||||
offset -= ToolbarButton.WIDTH / 2;
|
||||
|
||||
screenStack.MoveToX(offset, SettingsOverlay.TRANSITION_LENGTH, Easing.OutQuint);
|
||||
screenContainer.MoveToX(offset, SettingsOverlay.TRANSITION_LENGTH, Easing.OutQuint);
|
||||
}
|
||||
|
||||
settings.StateChanged += _ => updateScreenOffset();
|
||||
@ -555,7 +561,7 @@ namespace osu.Game
|
||||
focused.StateChanged += s =>
|
||||
{
|
||||
visibleOverlayCount += s == Visibility.Visible ? 1 : -1;
|
||||
screenStack.FadeColour(visibleOverlayCount > 0 ? OsuColour.Gray(0.5f) : Color4.White, 500, Easing.OutQuint);
|
||||
screenContainer.FadeColour(visibleOverlayCount > 0 ? OsuColour.Gray(0.5f) : Color4.White, 500, Easing.OutQuint);
|
||||
};
|
||||
}
|
||||
|
||||
@ -646,6 +652,7 @@ namespace osu.Game
|
||||
|
||||
private OsuScreen currentScreen;
|
||||
private FrameworkConfigManager frameworkConfig;
|
||||
private ScalingContainer screenContainer;
|
||||
|
||||
protected override bool OnExiting()
|
||||
{
|
||||
|
@ -24,6 +24,7 @@ using osu.Framework.Input;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Input;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.IO;
|
||||
@ -189,7 +190,7 @@ namespace osu.Game
|
||||
Child = content = new OsuTooltipContainer(MenuCursorContainer.Cursor) { RelativeSizeAxes = Axes.Both }
|
||||
};
|
||||
|
||||
base.Content.Add(new DrawSizePreservingFillContainer { Child = MenuCursorContainer });
|
||||
base.Content.Add(new ScalingContainer(ScalingMode.Everything) { Child = MenuCursorContainer });
|
||||
|
||||
KeyBindingStore.Register(globalBinding);
|
||||
dependencies.Cache(globalBinding);
|
||||
@ -247,7 +248,8 @@ namespace osu.Game
|
||||
var extension = Path.GetExtension(paths.First())?.ToLowerInvariant();
|
||||
|
||||
foreach (var importer in fileImporters)
|
||||
if (importer.HandledExtensions.Contains(extension)) importer.Import(paths);
|
||||
if (importer.HandledExtensions.Contains(extension))
|
||||
importer.Import(paths);
|
||||
}
|
||||
|
||||
public string[] HandledExtensions => fileImporters.SelectMany(i => i.HandledExtensions).ToArray();
|
||||
|
@ -8,6 +8,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
|
||||
namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||
@ -16,9 +17,9 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||
{
|
||||
protected override string Header => "Layout";
|
||||
|
||||
private FillFlowContainer letterboxSettings;
|
||||
private FillFlowContainer scalingSettings;
|
||||
|
||||
private Bindable<bool> letterboxing;
|
||||
private Bindable<ScalingMode> scalingMode;
|
||||
private Bindable<Size> sizeFullscreen;
|
||||
|
||||
private OsuGameBase game;
|
||||
@ -28,11 +29,11 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||
private const int transition_duration = 400;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(FrameworkConfigManager config, OsuGameBase game)
|
||||
private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, OsuGameBase game)
|
||||
{
|
||||
this.game = game;
|
||||
|
||||
letterboxing = config.GetBindable<bool>(FrameworkSetting.Letterboxing);
|
||||
scalingMode = osuConfig.GetBindable<ScalingMode>(OsuSetting.Scaling);
|
||||
sizeFullscreen = config.GetBindable<Size>(FrameworkSetting.SizeFullscreen);
|
||||
|
||||
Container resolutionSettingsContainer;
|
||||
@ -49,12 +50,12 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y
|
||||
},
|
||||
new SettingsCheckbox
|
||||
new SettingsEnumDropdown<ScalingMode>
|
||||
{
|
||||
LabelText = "Letterboxing",
|
||||
Bindable = letterboxing,
|
||||
LabelText = "Scaling",
|
||||
Bindable = osuConfig.GetBindable<ScalingMode>(OsuSetting.Scaling),
|
||||
},
|
||||
letterboxSettings = new FillFlowContainer
|
||||
scalingSettings = new FillFlowContainer
|
||||
{
|
||||
Direction = FillDirection.Vertical,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
@ -65,16 +66,28 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SettingsSlider<double>
|
||||
new SettingsSlider<float>
|
||||
{
|
||||
LabelText = "Horizontal position",
|
||||
Bindable = config.GetBindable<double>(FrameworkSetting.LetterboxPositionX),
|
||||
Bindable = osuConfig.GetBindable<float>(OsuSetting.ScalingPositionX),
|
||||
KeyboardStep = 0.01f
|
||||
},
|
||||
new SettingsSlider<double>
|
||||
new SettingsSlider<float>
|
||||
{
|
||||
LabelText = "Vertical position",
|
||||
Bindable = config.GetBindable<double>(FrameworkSetting.LetterboxPositionY),
|
||||
Bindable = osuConfig.GetBindable<float>(OsuSetting.ScalingPositionY),
|
||||
KeyboardStep = 0.01f
|
||||
},
|
||||
new SettingsSlider<float>
|
||||
{
|
||||
LabelText = "Horizontal size",
|
||||
Bindable = osuConfig.GetBindable<float>(OsuSetting.ScalingSizeX),
|
||||
KeyboardStep = 0.01f
|
||||
},
|
||||
new SettingsSlider<float>
|
||||
{
|
||||
LabelText = "Vertical size",
|
||||
Bindable = osuConfig.GetBindable<float>(OsuSetting.ScalingSizeY),
|
||||
KeyboardStep = 0.01f
|
||||
},
|
||||
}
|
||||
@ -105,13 +118,13 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||
}, true);
|
||||
}
|
||||
|
||||
letterboxing.BindValueChanged(isVisible =>
|
||||
scalingMode.BindValueChanged(mode =>
|
||||
{
|
||||
letterboxSettings.ClearTransforms();
|
||||
letterboxSettings.AutoSizeAxes = isVisible ? Axes.Y : Axes.None;
|
||||
scalingSettings.ClearTransforms();
|
||||
scalingSettings.AutoSizeAxes = mode != ScalingMode.Off ? Axes.Y : Axes.None;
|
||||
|
||||
if (!isVisible)
|
||||
letterboxSettings.ResizeHeightTo(0, transition_duration, Easing.OutQuint);
|
||||
if (mode == ScalingMode.Off)
|
||||
scalingSettings.ResizeHeightTo(0, transition_duration, Easing.OutQuint);
|
||||
}, true);
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,27 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Screens;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Backgrounds
|
||||
{
|
||||
public class BackgroundScreenEmpty : BackgroundScreen
|
||||
public class BackgroundScreenBlack : BackgroundScreen
|
||||
{
|
||||
public BackgroundScreenBlack()
|
||||
{
|
||||
Child = new Box
|
||||
{
|
||||
Colour = Color4.Black,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
};
|
||||
}
|
||||
|
||||
protected override void OnEntering(Screen last)
|
||||
{
|
||||
Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
public override bool CursorVisible => false;
|
||||
|
||||
protected override BackgroundScreen CreateBackground() => new BackgroundScreenEmpty();
|
||||
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBlack();
|
||||
|
||||
private Bindable<bool> menuVoice;
|
||||
private Bindable<bool> menuMusic;
|
||||
|
@ -58,6 +58,7 @@ namespace osu.Game.Screens.Menu
|
||||
Origin = Anchor.CentreLeft,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = box_width * 2,
|
||||
Height = 1.5f,
|
||||
// align off-screen to make sure our edges don't become visible during parallax.
|
||||
X = -box_width,
|
||||
Alpha = 0,
|
||||
@ -70,6 +71,7 @@ namespace osu.Game.Screens.Menu
|
||||
Origin = Anchor.CentreRight,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = box_width * 2,
|
||||
Height = 1.5f,
|
||||
X = box_width,
|
||||
Alpha = 0,
|
||||
Blending = BlendingMode.Additive,
|
||||
|
@ -20,6 +20,7 @@ using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Overlays;
|
||||
@ -179,10 +180,14 @@ namespace osu.Game.Screens.Play
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
},
|
||||
new LocalSkinOverrideContainer(working.Skin)
|
||||
new ScalingContainer(ScalingMode.Gameplay)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = RulesetContainer
|
||||
Child =
|
||||
new LocalSkinOverrideContainer(working.Skin)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = RulesetContainer
|
||||
}
|
||||
},
|
||||
new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor)
|
||||
{
|
||||
@ -191,7 +196,10 @@ namespace osu.Game.Screens.Play
|
||||
ProcessCustomClock = false,
|
||||
Breaks = beatmap.Breaks
|
||||
},
|
||||
RulesetContainer.Cursor?.CreateProxy() ?? new Container(),
|
||||
new ScalingContainer(ScalingMode.Gameplay)
|
||||
{
|
||||
Child = RulesetContainer.Cursor?.CreateProxy() ?? new Container(),
|
||||
},
|
||||
hudOverlay = new HUDOverlay(ScoreProcessor, RulesetContainer, working, offsetClock, adjustableClock)
|
||||
{
|
||||
Clock = Clock, // hud overlay doesn't want to use the audio clock directly
|
||||
|
Loading…
Reference in New Issue
Block a user