Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
using osu.Game.Overlays;
using osu.Game.Overlays.Settings;
using osu.Game.Overlays.Settings.Sections.Input;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Localisation;
using osuTK;

namespace osu.Game.Tests.Visual.Settings
Expand Down Expand Up @@ -117,12 +119,31 @@ public void TestRotationValidity()
}

[Test]
public void TestOffsetValidity()
public void TestOffsetClamping()
{
ensureValid();
AddStep("move right", () => tabletHandler.AreaOffset.Value = Vector2.Zero);
AddStep("move outside bounds", () => tabletHandler.AreaOffset.Value = Vector2.Zero);
AddAssert("offset is clamped", () => tabletHandler.AreaOffset.Value == tabletHandler.AreaSize.Value / 2);
ensureValid();

AddStep("disable lock to usable area", () => settings.ChildrenOfType<FormCheckBox>().First(c => c.Caption == TabletSettingsStrings.LockToUsableArea).Current.Value = false);
AddStep("move outside bounds", () => tabletHandler.AreaOffset.Value = Vector2.Zero);
ensureInvalid();
AddStep("move back", () => tabletHandler.AreaOffset.Value = tabletHandler.AreaSize.Value / 2);

AddStep("enable lock to usable area", () => settings.ChildrenOfType<FormCheckBox>().First(c => c.Caption == TabletSettingsStrings.LockToUsableArea).Current.Value = true);
AddAssert("offset is clamped again", () => tabletHandler.AreaOffset.Value == tabletHandler.AreaSize.Value / 2);
ensureValid();

AddStep("rotate 45", () => tabletHandler.Rotation.Value = 45);
AddAssert("lock is disabled automatically", () => !settings.ChildrenOfType<FormCheckBox>().First(c => c.Caption == TabletSettingsStrings.LockToUsableArea).Current.Value);
ensureInvalid();

AddStep("attempt to enable lock", () => settings.ChildrenOfType<FormCheckBox>().First(c => c.Caption == TabletSettingsStrings.LockToUsableArea).Current.Value = true);
AddAssert("lock remains disabled", () => !settings.ChildrenOfType<FormCheckBox>().First(c => c.Caption == TabletSettingsStrings.LockToUsableArea).Current.Value);

AddStep("rotate to 0", () => tabletHandler.Rotation.Value = 0);
AddStep("enable lock again", () => settings.ChildrenOfType<FormCheckBox>().First(c => c.Caption == TabletSettingsStrings.LockToUsableArea).Current.Value = true);
AddAssert("lock enabled successfully", () => settings.ChildrenOfType<FormCheckBox>().First(c => c.Caption == TabletSettingsStrings.LockToUsableArea).Current.Value);
ensureValid();
}

Expand Down
5 changes: 5 additions & 0 deletions osu.Game/Localisation/TabletSettingsStrings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ public static class TabletSettingsStrings
/// </summary>
public static LocalisableString ConformToCurrentGameAspectRatio => new TranslatableString(getKey(@"conform_to_current_game_aspect_ratio"), @"Conform to current game aspect ratio");

/// <summary>
/// "Lock to usable area"
/// </summary>
public static LocalisableString LockToUsableArea => new TranslatableString(getKey(@"lock_to_usable_area"), @"Lock to usable area");

/// <summary>
/// "X Offset"
/// </summary>
Expand Down
13 changes: 10 additions & 3 deletions osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ public partial class TabletAreaSelection : CompositeDrawable
{
public bool IsWithinBounds { get; private set; }

public readonly BindableBool LockToUsableArea = new BindableBool(true);

private readonly ITabletHandler handler;

private Container tabletContainer;
Expand Down Expand Up @@ -84,7 +86,7 @@ private void load(OverlayColourProvider colourProvider)
RelativeSizeAxes = Axes.Both,
Colour = colourProvider.Background4,
},
usableAreaContainer = new UsableAreaContainer(handler)
usableAreaContainer = new UsableAreaContainer(handler, LockToUsableArea)
{
Origin = Anchor.Centre,
Children = new Drawable[]
Expand Down Expand Up @@ -246,10 +248,14 @@ protected override void Update()

public partial class UsableAreaContainer : Container
{
private readonly ITabletHandler tabletHandler;
private readonly Bindable<Vector2> areaOffset;
private readonly BindableBool lockToUsableArea;

public UsableAreaContainer(ITabletHandler tabletHandler)
public UsableAreaContainer(ITabletHandler tabletHandler, BindableBool lockToUsableArea)
{
this.tabletHandler = tabletHandler;
this.lockToUsableArea = lockToUsableArea;
areaOffset = tabletHandler.AreaOffset.GetBoundCopy();
}

Expand All @@ -258,7 +264,8 @@ public UsableAreaContainer(ITabletHandler tabletHandler)
protected override void OnDrag(DragEvent e)
{
var newPos = Position + e.Delta;
this.MoveTo(Vector2.Clamp(newPos, Vector2.Zero, Parent!.Size));
newPos = lockToUsableArea.Value ? tabletHandler.ClampOffset(newPos) : Vector2.Clamp(newPos, Vector2.Zero, Parent!.Size);
this.MoveTo(newPos);
}

protected override void OnDragEnd(DragEndEvent e)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;
using osu.Framework.Input.Handlers.Tablet;
using osuTK;

namespace osu.Game.Overlays.Settings.Sections.Input
{
public static class TabletHandlerExtensions
{
public static Vector2 ClampOffset(this ITabletHandler handler, Vector2 offset)
{
if (handler.Tablet.Value == null)
return offset;

var size = handler.AreaSize.Value;
var tabletSize = handler.Tablet.Value.Size;

float rad = float.DegreesToRadians(handler.Rotation.Value);
float cos = MathF.Abs(MathF.Cos(rad));
float sin = MathF.Abs(MathF.Sin(rad));

float maxX = (size.X / 2) * cos + (size.Y / 2) * sin;
float maxY = (size.X / 2) * sin + (size.Y / 2) * cos;

float minX = MathF.Min(maxX, tabletSize.X / 2);
float maxXRange = MathF.Max(tabletSize.X - maxX, tabletSize.X / 2);
float minY = MathF.Min(maxY, tabletSize.Y / 2);
float maxYRange = MathF.Max(tabletSize.Y - maxY, tabletSize.Y / 2);

return new Vector2(
Math.Clamp(offset.X, minX, maxXRange),
Math.Clamp(offset.Y, minY, maxYRange)
);
}

public static bool CanFit(this ITabletHandler handler)
{
if (handler.Tablet.Value == null)
return true;

var size = handler.AreaSize.Value;
var tabletSize = handler.Tablet.Value.Size;

float rad = float.DegreesToRadians(handler.Rotation.Value);
float cos = MathF.Abs(MathF.Cos(rad));
float sin = MathF.Abs(MathF.Sin(rad));

float maxX = (size.X / 2) * cos + (size.Y / 2) * sin;
float maxY = (size.X / 2) * sin + (size.Y / 2) * cos;

const float lenience = 0.5f;
return maxX <= tabletSize.X / 2 + lenience && maxY <= tabletSize.Y / 2 + lenience;
}
}
}
72 changes: 71 additions & 1 deletion osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

#nullable disable
Expand Down Expand Up @@ -72,6 +72,7 @@
};

private readonly BindableBool aspectLock = new BindableBool();
private readonly BindableBool lockToUsableArea = new BindableBool(true);

private ScheduledDelegate aspectRatioApplication;

Expand Down Expand Up @@ -161,6 +162,11 @@
{
Padding = SettingsPanel.CONTENT_PADDING,
},
new SettingsItemV2(new FormCheckBox
{
Caption = TabletSettingsStrings.LockToUsableArea,
Current = lockToUsableArea,
}),
new SettingsItemV2(new FormSliderBar<float>
{
TransferValueOnCommit = true,
Expand Down Expand Up @@ -200,14 +206,56 @@
{
base.LoadComplete();

AreaSelection.LockToUsableArea.BindTo(lockToUsableArea);

lockToUsableArea.BindValueChanged(val => Schedule(() =>
{
if (val.NewValue)
{
if (tabletHandler.CanFit())
{
var clampedOffset = clampOffset(areaOffset.Value);
if (clampedOffset != areaOffset.Value)
areaOffset.Value = clampedOffset;
}
else
{
lockToUsableArea.Value = false;
}
}
}));

enabled.BindTo(tabletHandler.Enabled);
enabled.BindValueChanged(_ => Scheduler.AddOnce(updateVisibility));

rotation.BindTo(tabletHandler.Rotation);
rotation.BindValueChanged(val => Schedule(() =>
{
if (lockToUsableArea.Value)
{
if (tabletHandler.CanFit())
{
var clampedOffset = clampOffset(areaOffset.Value);
if (clampedOffset != areaOffset.Value)
areaOffset.Value = clampedOffset;
}
else
{
lockToUsableArea.Value = false;
}
}
}), true);

areaOffset.BindTo(tabletHandler.AreaOffset);
areaOffset.BindValueChanged(val => Schedule(() =>
{
var clamped = clampOffset(val.NewValue);
if (clamped != val.NewValue)

Check warning

Code scanning / InspectCode

Incorrect blank lines: Blank lines are missing elsewhere Warning

Blank lines are missing, expected minimum 1 instead of 0
{
areaOffset.Value = clamped;
return;
}

offsetX.Value = val.NewValue.X;
offsetY.Value = val.NewValue.Y;
}), true);
Expand All @@ -218,6 +266,20 @@
areaSize.BindTo(tabletHandler.AreaSize);
areaSize.BindValueChanged(val => Schedule(() =>
{
if (lockToUsableArea.Value)
{
if (tabletHandler.CanFit())
{
var clampedOffset = clampOffset(areaOffset.Value);
if (clampedOffset != areaOffset.Value)
areaOffset.Value = clampedOffset;
}
else
{
lockToUsableArea.Value = false;
}
}

sizeX.Value = val.NewValue.X;
sizeY.Value = val.NewValue.Y;
}), true);
Expand Down Expand Up @@ -335,6 +397,14 @@
aspectLock.Value = true;
}

private Vector2 clampOffset(Vector2 offset)
{
if (!lockToUsableArea.Value)
return offset;

return tabletHandler.ClampOffset(offset);
}

private void updateAspectRatio() => aspectRatio.Value = currentAspectRatio;

private float currentAspectRatio => sizeX.Value / sizeY.Value;
Expand Down
Loading