1
0
Fork 0
mirror of https://github.com/Ellpeck/MLEM.git synced 2024-05-23 00:53:37 +02:00
MLEM/MLEM.Ui/UiControls.cs

217 lines
9.9 KiB
C#
Raw Normal View History

2019-08-28 18:27:17 +02:00
using System;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using MLEM.Extensions;
using MLEM.Input;
2019-09-09 16:25:07 +02:00
using MLEM.Misc;
2019-08-28 18:27:17 +02:00
using MLEM.Ui.Elements;
namespace MLEM.Ui {
public class UiControls {
2019-08-28 18:27:17 +02:00
public readonly InputHandler Input;
private readonly bool isInputOurs;
private readonly UiSystem system;
2019-08-28 18:27:17 +02:00
public Element MousedElement { get; private set; }
2019-09-09 17:12:36 +02:00
public Element SelectedElement => this.GetActiveRoot()?.SelectedElement;
2019-09-09 16:25:07 +02:00
public Buttons[] GamepadButtons = {Buttons.A};
public Buttons[] SecondaryGamepadButtons = {Buttons.X};
public int GamepadIndex = -1;
2019-09-09 17:12:36 +02:00
public bool IsAutoNavMode { get; private set; }
2019-08-28 18:27:17 +02:00
public UiControls(UiSystem system, InputHandler inputHandler = null) {
this.system = system;
this.Input = inputHandler ?? new InputHandler();
this.isInputOurs = inputHandler == null;
// enable all required gestures
InputHandler.EnableGestures(GestureType.Tap, GestureType.Hold);
2019-08-28 18:27:17 +02:00
}
public void Update() {
if (this.isInputOurs)
this.Input.Update();
// MOUSE INPUT
var mousedNow = this.GetElementUnderPos(this.Input.MousePosition);
2019-08-28 18:27:17 +02:00
if (mousedNow != this.MousedElement) {
if (this.MousedElement != null)
this.MousedElement.OnMouseExit?.Invoke(this.MousedElement);
if (mousedNow != null)
mousedNow.OnMouseEnter?.Invoke(mousedNow);
this.MousedElement = mousedNow;
2019-09-02 18:41:05 +02:00
this.system.ApplyToAll(e => e.OnMousedElementChanged?.Invoke(e, mousedNow));
2019-08-28 18:27:17 +02:00
}
2019-08-28 18:27:17 +02:00
if (this.Input.IsMouseButtonPressed(MouseButton.Left)) {
2019-09-09 17:12:36 +02:00
this.IsAutoNavMode = false;
2019-08-28 18:27:17 +02:00
var selectedNow = mousedNow != null && mousedNow.CanBeSelected ? mousedNow : null;
2019-09-09 17:12:36 +02:00
this.GetActiveRoot().SelectElement(selectedNow);
2019-08-28 18:27:17 +02:00
if (mousedNow != null)
mousedNow.OnPressed?.Invoke(mousedNow);
} else if (this.Input.IsMouseButtonPressed(MouseButton.Right)) {
2019-09-09 17:12:36 +02:00
this.IsAutoNavMode = false;
2019-08-28 18:27:17 +02:00
if (mousedNow != null)
mousedNow.OnSecondaryPressed?.Invoke(mousedNow);
}
// KEYBOARD INPUT
else if (this.Input.IsKeyPressed(Keys.Enter) || this.Input.IsKeyPressed(Keys.Space)) {
2019-09-09 17:12:36 +02:00
this.IsAutoNavMode = true;
if (this.SelectedElement?.Root != null) {
2019-08-28 18:27:17 +02:00
if (this.Input.IsModifierKeyDown(ModifierKey.Shift)) {
// secondary action on element using space or enter
this.SelectedElement.OnSecondaryPressed?.Invoke(this.SelectedElement);
} else {
// first action on element using space or enter
this.SelectedElement.OnPressed?.Invoke(this.SelectedElement);
}
}
} else if (this.Input.IsKeyPressed(Keys.Tab)) {
2019-09-09 17:12:36 +02:00
this.IsAutoNavMode = true;
2019-08-28 18:27:17 +02:00
// tab or shift-tab to next or previous element
2019-09-09 17:12:36 +02:00
var backward = this.Input.IsModifierKeyDown(ModifierKey.Shift);
var next = this.GetTabNextElement(backward);
if (this.SelectedElement?.Root != null)
next = this.SelectedElement.GetTabNextElement(backward, next);
this.GetActiveRoot().SelectElement(next);
2019-08-28 18:27:17 +02:00
}
// TOUCH INPUT
else if (this.Input.GetGesture(GestureType.Tap, out var tap)) {
2019-09-09 17:12:36 +02:00
this.IsAutoNavMode = false;
var tapped = this.GetElementUnderPos(tap.Position.ToPoint());
2019-09-09 17:12:36 +02:00
this.GetActiveRoot().SelectElement(tapped);
if (tapped != null)
tapped.OnPressed?.Invoke(tapped);
} else if (this.Input.GetGesture(GestureType.Hold, out var hold)) {
2019-09-09 17:12:36 +02:00
this.IsAutoNavMode = false;
var held = this.GetElementUnderPos(hold.Position.ToPoint());
2019-09-09 17:12:36 +02:00
this.GetActiveRoot().SelectElement(held);
if (held != null)
held.OnSecondaryPressed?.Invoke(held);
}
2019-09-09 16:25:07 +02:00
// GAMEPAD INPUT
else if (this.GamepadButtons.Any(b => this.Input.IsGamepadButtonPressed(b, this.GamepadIndex))) {
2019-09-09 17:12:36 +02:00
this.IsAutoNavMode = true;
2019-09-09 16:25:07 +02:00
if (this.SelectedElement?.Root != null)
this.SelectedElement.OnPressed?.Invoke(this.SelectedElement);
} else if (this.SecondaryGamepadButtons.Any(b => this.Input.IsGamepadButtonPressed(b, this.GamepadIndex))) {
2019-09-09 17:12:36 +02:00
this.IsAutoNavMode = true;
2019-09-09 16:25:07 +02:00
if (this.SelectedElement?.Root != null)
this.SelectedElement.OnSecondaryPressed?.Invoke(this.SelectedElement);
} else if (this.Input.IsGamepadButtonPressed(Buttons.DPadDown) || this.Input.IsGamepadButtonPressed(Buttons.LeftThumbstickDown)) {
2019-09-09 17:12:36 +02:00
this.HandleGamepadNextElement(Direction2.Down);
2019-09-09 16:25:07 +02:00
} else if (this.Input.IsGamepadButtonPressed(Buttons.DPadLeft) || this.Input.IsGamepadButtonPressed(Buttons.LeftThumbstickLeft)) {
2019-09-09 17:12:36 +02:00
this.HandleGamepadNextElement(Direction2.Left);
2019-09-09 16:25:07 +02:00
} else if (this.Input.IsGamepadButtonPressed(Buttons.DPadRight) || this.Input.IsGamepadButtonPressed(Buttons.LeftThumbstickRight)) {
2019-09-09 17:12:36 +02:00
this.HandleGamepadNextElement(Direction2.Right);
2019-09-09 16:25:07 +02:00
} else if (this.Input.IsGamepadButtonPressed(Buttons.DPadUp) || this.Input.IsGamepadButtonPressed(Buttons.LeftThumbstickUp)) {
2019-09-09 17:12:36 +02:00
this.HandleGamepadNextElement(Direction2.Up);
2019-09-09 16:25:07 +02:00
}
2019-08-28 18:27:17 +02:00
}
public Element GetElementUnderPos(Point position, bool transform = true) {
2019-08-28 18:27:17 +02:00
foreach (var root in this.system.GetRootElements()) {
var pos = transform ? position.Transform(root.InvTransform) : position;
var moused = root.Element.GetElementUnderPos(pos);
2019-08-28 18:27:17 +02:00
if (moused != null)
return moused;
}
return null;
}
2019-09-09 16:25:07 +02:00
private Element GetTabNextElement(bool backward) {
2019-09-09 17:12:36 +02:00
var currRoot = this.GetActiveRoot();
2019-08-28 18:27:17 +02:00
if (currRoot == null)
return null;
var children = currRoot.Element.GetChildren(regardChildrensChildren: true).Append(currRoot.Element);
if (this.SelectedElement?.Root != currRoot) {
2019-08-28 18:27:17 +02:00
return backward ? children.LastOrDefault(c => c.CanBeSelected) : children.FirstOrDefault(c => c.CanBeSelected);
} else {
var foundCurr = false;
Element lastFound = null;
foreach (var child in children) {
if (!child.CanBeSelected)
continue;
if (child == this.SelectedElement) {
// when going backwards, return the last element found before the current one
if (backward)
return lastFound;
foundCurr = true;
} else {
// when going forwards, return the element after the current one
if (!backward && foundCurr)
return child;
}
lastFound = child;
}
return null;
}
}
2019-09-09 17:12:36 +02:00
private void HandleGamepadNextElement(Direction2 dir) {
this.IsAutoNavMode = true;
Rectangle 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);
if (this.SelectedElement != null)
next = this.SelectedElement.GetGamepadNextElement(dir, next);
if (next != null)
this.GetActiveRoot().SelectElement(next);
}
private Element GetGamepadNextElement(Rectangle searchArea) {
var currRoot = this.GetActiveRoot();
2019-09-09 16:25:07 +02:00
if (currRoot == null)
return null;
var children = currRoot.Element.GetChildren(regardChildrensChildren: true).Append(currRoot.Element);
if (this.SelectedElement?.Root != currRoot) {
return children.FirstOrDefault(c => c.CanBeSelected);
} else {
Element closest = null;
float closestDist = 0;
foreach (var child in children) {
if (!child.CanBeSelected || child == this.SelectedElement || !searchArea.Intersects(child.Area))
continue;
var dist = Vector2.Distance(child.Area.Center.ToVector2(), this.SelectedElement.Area.Center.ToVector2());
if (closest == null || dist < closestDist) {
closest = child;
closestDist = dist;
}
}
return closest;
}
}
2019-09-09 17:12:36 +02:00
public RootElement GetActiveRoot() {
return this.system.GetRootElements().FirstOrDefault(root => root.CanSelectContent);
}
}
}