diff --git a/CHANGELOG.md b/CHANGELOG.md
index b737ea7..3141739 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -46,6 +46,7 @@ Improvements
- Allow setting a default color for clickable links in UiStyle
- Allow ElementHelper's KeybindButton to query a combination at a given index
- Automatically select the first element when a dropdown is opened in auto nav mode
+- Improved gamepad navigation by employing angles between elements
Fixes
- Fixed paragraph links having incorrect hover locations when using special text alignments
diff --git a/MLEM.Ui/UiControls.cs b/MLEM.Ui/UiControls.cs
index c0d8430..f7efd18 100644
--- a/MLEM.Ui/UiControls.cs
+++ b/MLEM.Ui/UiControls.cs
@@ -1,8 +1,10 @@
+using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
+using MLEM.Extensions;
using MLEM.Input;
using MLEM.Misc;
using MLEM.Ui.Elements;
@@ -355,11 +357,11 @@ namespace MLEM.Ui {
}
///
- /// Returns the next element that should be selected during gamepad navigation, based on the that we're looking for elements in.
+ /// Returns the next element that should be selected during gamepad navigation, based on the that we're looking for elements in.
///
- /// The area that we're looking for next elements in
+ /// The direction that we're looking for next elements in
/// The first element found in that area
- protected virtual Element GetGamepadNextElement(RectangleF searchArea) {
+ protected virtual Element GetGamepadNextElement(Direction2 direction) {
if (this.ActiveRoot == null)
return null;
var children = this.ActiveRoot.Element.GetChildren(c => !c.IsHidden, true, true).Append(this.ActiveRoot.Element);
@@ -367,14 +369,17 @@ namespace MLEM.Ui {
return children.FirstOrDefault(c => c.CanBeSelected);
} else {
Element closest = null;
- float closestDist = 0;
+ float closestDistSq = 0;
foreach (var child in children) {
- if (!child.CanBeSelected || child == this.SelectedElement || !searchArea.Intersects(child.Area))
+ if (!child.CanBeSelected || child == this.SelectedElement)
continue;
- var dist = Vector2.Distance(child.Area.Center, this.SelectedElement.Area.Center);
- if (closest == null || dist < closestDist) {
+ var distVec = child.Area.Center - this.SelectedElement.Area.Center;
+ if (Math.Abs(direction.Angle() - Math.Atan2(distVec.Y, distVec.X)) >= MathHelper.PiOver2 - Element.Epsilon)
+ continue;
+ var distSq = distVec.LengthSquared();
+ if (closest == null || distSq < closestDistSq) {
closest = child;
- closestDist = dist;
+ closestDistSq = distSq;
}
}
return closest;
@@ -383,28 +388,7 @@ namespace MLEM.Ui {
private void HandleGamepadNextElement(Direction2 dir) {
this.IsAutoNavMode = true;
- RectangleF searchArea = default;
- if (this.SelectedElement?.Root != null) {
- searchArea = this.SelectedElement.Area;
- var (_, _, width, height) = this.System.Viewport;
- switch (dir) {
- case Direction2.Down:
- searchArea.Height += height;
- break;
- case Direction2.Left:
- searchArea.X -= width;
- searchArea.Width += width;
- break;
- case Direction2.Right:
- searchArea.Width += width;
- break;
- case Direction2.Up:
- searchArea.Y -= height;
- searchArea.Height += height;
- break;
- }
- }
- var next = this.GetGamepadNextElement(searchArea);
+ var next = this.GetGamepadNextElement(dir);
if (this.SelectedElement != null)
next = this.SelectedElement.GetGamepadNextElement(dir, next);
if (next != null)