1
0
Fork 0
mirror of https://github.com/Ellpeck/MLEM.git synced 2024-11-26 06:28:35 +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 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(new Paragraph(Anchor.AutoLeft, 1, paragraph => "Slider is at " + (slider.CurrentValue * 100).Floor() + "%") {PositionOffset = new Vector2(0, 1)});
root.AddChild(slider); root.AddChild(slider);

View file

@ -7,6 +7,7 @@ using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Input;
using MLEM.Extensions; using MLEM.Extensions;
using MLEM.Input; using MLEM.Input;
using MLEM.Misc;
using MLEM.Textures; using MLEM.Textures;
using MLEM.Ui.Style; using MLEM.Ui.Style;
@ -449,6 +450,14 @@ namespace MLEM.Ui.Elements {
return this.CanBeMoused && this.Area.Contains(position) ? this : null; 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) { public void AndChildren(Action<Element> action) {
action(this); action(this);
foreach (var child in this.Children) 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") { 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); var box = new Panel(anchor, new Vector2(width, 1), Vector2.Zero, true);
box.AddChild(new Paragraph(Anchor.AutoLeft, 1, text)); 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"), OnPressed = element => system.Remove("InfoBox"),
PositionOffset = new Vector2(0, 1) PositionOffset = new Vector2(0, 1)
}); });
system.Add("InfoBox", box); var root = system.Add("InfoBox", box);
root.SelectElement(button);
return box; 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 // handle automatic element selection, the scroller needs to scroll to the right location
this.OnSelectedElementChanged += (element, otherElement) => { this.OnSelectedElementChanged += (element, otherElement) => {
if (this.Controls.SelectedLastElementWithMouse) if (!this.Controls.IsAutoNavMode)
return; return;
if (otherElement == null || !otherElement.GetParentTree().Contains(this)) if (otherElement == null || !otherElement.GetParentTree().Contains(this))
return; return;

View file

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