1
0
Fork 0
mirror of https://github.com/Ellpeck/MLEM.git synced 2024-12-24 01:09:23 +01:00

clean up navigation a lot

This commit is contained in:
Ellpeck 2019-09-09 17:12:36 +02:00
parent c52c9825f0
commit 672e5eb548
7 changed files with 114 additions and 56 deletions

View file

@ -150,7 +150,9 @@ namespace Demos {
OnPressed = element => tooltip.IsHidden = !tooltip.IsHidden
});
var slider = new Slider(Anchor.AutoLeft, new Vector2(1, 10), 5, 1);
var slider = new Slider(Anchor.AutoLeft, new Vector2(1, 10), 5, 1) {
StepPerScroll = 0.01F
};
root.AddChild(new Paragraph(Anchor.AutoLeft, 1, paragraph => "Slider is at " + (slider.CurrentValue * 100).Floor() + "%") {PositionOffset = new Vector2(0, 1)});
root.AddChild(slider);

View file

@ -7,6 +7,7 @@ using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using MLEM.Extensions;
using MLEM.Input;
using MLEM.Misc;
using MLEM.Textures;
using MLEM.Ui.Style;
@ -75,7 +76,7 @@ namespace MLEM.Ui.Elements {
}
}
public Point ScaledChildPadding => this.childPadding.Multiply(this.Scale);
public GenericCallback OnPressed;
public GenericCallback OnSecondaryPressed;
public GenericCallback OnSelected;
@ -301,7 +302,7 @@ namespace MLEM.Ui.Elements {
}
}
}
this.area = new Rectangle(pos, actualSize);
this.OnAreaUpdated?.Invoke(this);
@ -449,6 +450,14 @@ namespace MLEM.Ui.Elements {
return this.CanBeMoused && this.Area.Contains(position) ? this : null;
}
public virtual Element GetTabNextElement(bool backward, Element usualNext) {
return usualNext;
}
public virtual Element GetGamepadNextElement(Direction2 dir, Element usualNext) {
return usualNext;
}
public void AndChildren(Action<Element> action) {
action(this);
foreach (var child in this.Children)

View file

@ -16,11 +16,12 @@ namespace MLEM.Ui.Elements {
public static Panel ShowInfoBox(UiSystem system, Anchor anchor, float width, string text, float buttonHeight = 10, string okText = "Okay") {
var box = new Panel(anchor, new Vector2(width, 1), Vector2.Zero, true);
box.AddChild(new Paragraph(Anchor.AutoLeft, 1, text));
box.AddChild(new Button(Anchor.AutoCenter, new Vector2(0.5F, buttonHeight), okText) {
var button = box.AddChild(new Button(Anchor.AutoCenter, new Vector2(0.5F, buttonHeight), okText) {
OnPressed = element => system.Remove("InfoBox"),
PositionOffset = new Vector2(0, 1)
});
system.Add("InfoBox", box);
var root = system.Add("InfoBox", box);
root.SelectElement(button);
return box;
}

View file

@ -38,7 +38,7 @@ namespace MLEM.Ui.Elements {
// handle automatic element selection, the scroller needs to scroll to the right location
this.OnSelectedElementChanged += (element, otherElement) => {
if (this.Controls.SelectedLastElementWithMouse)
if (!this.Controls.IsAutoNavMode)
return;
if (otherElement == null || !otherElement.GetParentTree().Contains(this))
return;

View file

@ -1,10 +1,31 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using MLEM.Misc;
namespace MLEM.Ui.Elements {
public class Slider : ScrollBar {
public Slider(Anchor anchor, Vector2 size, int scrollerSize, float maxValue) :
base(anchor, size, scrollerSize, maxValue, true) {
this.CanBeSelected = true;
}
public override void Update(GameTime time) {
base.Update(time);
if (this.IsSelected) {
if (this.Input.IsGamepadButtonDown(Buttons.DPadLeft) || this.Input.IsGamepadButtonDown(Buttons.LeftThumbstickLeft)) {
this.CurrentValue -= this.StepPerScroll;
} else if (this.Input.IsGamepadButtonDown(Buttons.DPadRight) || this.Input.IsGamepadButtonDown(Buttons.LeftThumbstickRight)) {
this.CurrentValue += this.StepPerScroll;
}
}
}
public override Element GetGamepadNextElement(Direction2 dir, Element usualNext) {
if (dir == Direction2.Left || dir == Direction2.Right)
return null;
return base.GetGamepadNextElement(dir, usualNext);
}
}

View file

@ -16,13 +16,14 @@ namespace MLEM.Ui {
private readonly UiSystem system;
public Element MousedElement { get; private set; }
public Element SelectedElement { get; private set; }
public bool SelectedLastElementWithMouse { get; private set; }
public Element SelectedElement => this.GetActiveRoot()?.SelectedElement;
public Buttons[] GamepadButtons = {Buttons.A};
public Buttons[] SecondaryGamepadButtons = {Buttons.X};
public int GamepadIndex = -1;
public bool IsAutoNavMode { get; private set; }
public UiControls(UiSystem system, InputHandler inputHandler = null) {
this.system = system;
this.Input = inputHandler ?? new InputHandler();
@ -48,17 +49,20 @@ namespace MLEM.Ui {
}
if (this.Input.IsMouseButtonPressed(MouseButton.Left)) {
this.IsAutoNavMode = false;
var selectedNow = mousedNow != null && mousedNow.CanBeSelected ? mousedNow : null;
this.SelectElement(selectedNow, true);
this.GetActiveRoot().SelectElement(selectedNow);
if (mousedNow != null)
mousedNow.OnPressed?.Invoke(mousedNow);
} else if (this.Input.IsMouseButtonPressed(MouseButton.Right)) {
this.IsAutoNavMode = false;
if (mousedNow != null)
mousedNow.OnSecondaryPressed?.Invoke(mousedNow);
}
// KEYBOARD INPUT
else if (this.Input.IsKeyPressed(Keys.Enter) || this.Input.IsKeyPressed(Keys.Space)) {
this.IsAutoNavMode = true;
if (this.SelectedElement?.Root != null) {
if (this.Input.IsModifierKeyDown(ModifierKey.Shift)) {
// secondary action on element using space or enter
@ -69,76 +73,50 @@ namespace MLEM.Ui {
}
}
} else if (this.Input.IsKeyPressed(Keys.Tab)) {
this.IsAutoNavMode = true;
// tab or shift-tab to next or previous element
this.SelectElement(this.GetTabNextElement(this.Input.IsModifierKeyDown(ModifierKey.Shift)), false);
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);
}
// TOUCH INPUT
else if (this.Input.GetGesture(GestureType.Tap, out var tap)) {
this.IsAutoNavMode = false;
var tapped = this.GetElementUnderPos(tap.Position.ToPoint());
this.SelectElement(tapped, true);
this.GetActiveRoot().SelectElement(tapped);
if (tapped != null)
tapped.OnPressed?.Invoke(tapped);
} else if (this.Input.GetGesture(GestureType.Hold, out var hold)) {
this.IsAutoNavMode = false;
var held = this.GetElementUnderPos(hold.Position.ToPoint());
this.SelectElement(held, true);
this.GetActiveRoot().SelectElement(held);
if (held != null)
held.OnSecondaryPressed?.Invoke(held);
}
// GAMEPAD INPUT
else if (this.GamepadButtons.Any(b => this.Input.IsGamepadButtonPressed(b, this.GamepadIndex))) {
this.IsAutoNavMode = true;
if (this.SelectedElement?.Root != null)
this.SelectedElement.OnPressed?.Invoke(this.SelectedElement);
} else if (this.SecondaryGamepadButtons.Any(b => this.Input.IsGamepadButtonPressed(b, this.GamepadIndex))) {
this.IsAutoNavMode = true;
if (this.SelectedElement?.Root != null)
this.SelectedElement.OnSecondaryPressed?.Invoke(this.SelectedElement);
} else if (this.Input.IsGamepadButtonPressed(Buttons.DPadDown) || this.Input.IsGamepadButtonPressed(Buttons.LeftThumbstickDown)) {
var next = this.GetGamepadNextElement(searchArea => {
searchArea.Height += this.system.Viewport.Height;
return searchArea;
});
if (next != null)
this.SelectElement(next, false);
this.HandleGamepadNextElement(Direction2.Down);
} else if (this.Input.IsGamepadButtonPressed(Buttons.DPadLeft) || this.Input.IsGamepadButtonPressed(Buttons.LeftThumbstickLeft)) {
var next = this.GetGamepadNextElement(searchArea => {
searchArea.X -= this.system.Viewport.Width;
searchArea.Width += this.system.Viewport.Width;
return searchArea;
});
if (next != null)
this.SelectElement(next, false);
this.HandleGamepadNextElement(Direction2.Left);
} else if (this.Input.IsGamepadButtonPressed(Buttons.DPadRight) || this.Input.IsGamepadButtonPressed(Buttons.LeftThumbstickRight)) {
var next = this.GetGamepadNextElement(searchArea => {
searchArea.Width += this.system.Viewport.Width;
return searchArea;
});
if (next != null)
this.SelectElement(next, false);
this.HandleGamepadNextElement(Direction2.Right);
} else if (this.Input.IsGamepadButtonPressed(Buttons.DPadUp) || this.Input.IsGamepadButtonPressed(Buttons.LeftThumbstickUp)) {
var next = this.GetGamepadNextElement(searchArea => {
searchArea.Y -= this.system.Viewport.Height;
searchArea.Height += this.system.Viewport.Height;
return searchArea;
});
if (next != null)
this.SelectElement(next, false);
this.HandleGamepadNextElement(Direction2.Up);
}
}
public void SelectElement(Element element, bool mouse) {
if (this.SelectedElement == element)
return;
if (this.SelectedElement != null)
this.SelectedElement.OnDeselected?.Invoke(this.SelectedElement);
if (element != null)
element.OnSelected?.Invoke(element);
this.SelectedElement = element;
this.SelectedLastElementWithMouse = mouse;
this.system.ApplyToAll(e => e.OnSelectedElementChanged?.Invoke(e, element));
}
public Element GetElementUnderPos(Point position, bool transform = true) {
foreach (var root in this.system.GetRootElements()) {
var pos = transform ? position.Transform(root.InvTransform) : position;
@ -150,7 +128,7 @@ namespace MLEM.Ui {
}
private Element GetTabNextElement(bool backward) {
var currRoot = this.system.GetRootElements().FirstOrDefault(root => root.CanSelectContent);
var currRoot = this.GetActiveRoot();
if (currRoot == null)
return null;
var children = currRoot.Element.GetChildren(regardChildrensChildren: true).Append(currRoot.Element);
@ -178,15 +156,44 @@ namespace MLEM.Ui {
}
}
private Element GetGamepadNextElement(Func<Rectangle, Rectangle> searchAreaFunc) {
var currRoot = this.system.GetRootElements().FirstOrDefault(root => root.CanSelectContent);
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();
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 {
var searchArea = searchAreaFunc(this.SelectedElement.Area);
Element closest = null;
float closestDist = 0;
foreach (var child in children) {
@ -202,5 +209,9 @@ namespace MLEM.Ui {
}
}
public RootElement GetActiveRoot() {
return this.system.GetRootElements().FirstOrDefault(root => root.CanSelectContent);
}
}
}

View file

@ -75,7 +75,7 @@ namespace MLEM.Ui {
});
this.OnSelectedElementDrawn = (element, time, batch, alpha) => {
if (!this.Controls.SelectedLastElementWithMouse && element.SelectionIndicator != null) {
if (this.Controls.IsAutoNavMode && element.SelectionIndicator != null) {
batch.Draw(element.SelectionIndicator, element.DisplayArea, Color.White * alpha, element.Scale / 2);
}
};
@ -176,12 +176,26 @@ namespace MLEM.Ui {
public Matrix Transform = Matrix.Identity;
public Matrix InvTransform => Matrix.Invert(this.Transform);
public Element SelectedElement { get; private set; }
public RootElement(string name, Element element, UiSystem system) {
this.Name = name;
this.Element = element;
this.System = system;
}
public void SelectElement(Element element) {
if (this.SelectedElement == element)
return;
if (this.SelectedElement != null)
this.SelectedElement.OnDeselected?.Invoke(this.SelectedElement);
if (element != null)
element.OnSelected?.Invoke(element);
this.SelectedElement = element;
this.System.ApplyToAll(e => e.OnSelectedElementChanged?.Invoke(e, element));
}
public void MoveToFront() {
this.System.Remove(this.Name);
this.System.Add(this);