1
0
Fork 0
mirror of https://github.com/Ellpeck/MLEM.git synced 2024-11-22 12:58:33 +01:00

Added UiControls.NavType, which stores the most recently used type of ui navigation

This commit is contained in:
Ell 2023-11-08 10:31:36 +01:00
parent 476e1dd2a6
commit 0293ea435e
3 changed files with 70 additions and 6 deletions

View file

@ -29,6 +29,9 @@ Fixes
- Fixed TextInput not working correctly when using surrogate pairs - Fixed TextInput not working correctly when using surrogate pairs
### MLEM.Ui ### MLEM.Ui
Additions
- Added UiControls.NavType, which stores the most recently used type of ui navigation
Improvements Improvements
- Allow scrolling panels to contain other scrolling panels - Allow scrolling panels to contain other scrolling panels
- Allow dropdowns to have scrolling panels - Allow dropdowns to have scrolling panels

View file

@ -103,7 +103,7 @@ namespace MLEM.Ui {
/// </summary> /// </summary>
public bool HandleGamepad = true; public bool HandleGamepad = true;
/// <summary> /// <summary>
/// If this value is true, the ui controls are in automatic navigation mode. /// If this value is true, the ui controls are in automatic navigation mode. The state of automatic navigation is usually based on the current <see cref="NavType"/>.
/// This means that the <see cref="UiStyle.SelectionIndicator"/> will be drawn around the <see cref="SelectedElement"/>. /// This means that the <see cref="UiStyle.SelectionIndicator"/> will be drawn around the <see cref="SelectedElement"/>.
/// </summary> /// </summary>
public bool IsAutoNavMode { public bool IsAutoNavMode {
@ -115,12 +115,29 @@ namespace MLEM.Ui {
} }
} }
} }
/// <summary>
/// The current <see cref="NavigationType"/> of these ui controls, which represents the last type of interaction that was used to interact with the underlying <see cref="UiSystem"/>.
/// </summary>
public NavigationType NavType {
get => this.navType;
set {
if (this.navType != value) {
var last = this.navType;
this.navType = value;
this.NavTypeChanged?.Invoke(last, value);
}
}
}
/// <summary> /// <summary>
/// An event that is raised when <see cref="IsAutoNavMode"/> is changed. /// An event that is raised when <see cref="IsAutoNavMode"/> is changed.
/// This can be used for custom actions like hiding the mouse cursor when automatic navigation is enabled. /// This can be used for custom actions like hiding the mouse cursor when automatic navigation is enabled.
/// </summary> /// </summary>
public event Action<bool> AutoNavModeChanged; public event Action<bool> AutoNavModeChanged;
/// <summary>
/// An event that is raised when <see cref="NavType"/> is changed. It receives the previous navigation type, as well as the newly set navigation type.
/// </summary>
public event Action<NavigationType, NavigationType> NavTypeChanged;
/// <summary> /// <summary>
/// This value ist true if the <see cref="InputHandler"/> was created by this ui controls instance, or if it was passed in. /// This value ist true if the <see cref="InputHandler"/> was created by this ui controls instance, or if it was passed in.
@ -134,6 +151,7 @@ namespace MLEM.Ui {
private readonly Dictionary<string, Element> selectedElements = new Dictionary<string, Element>(); private readonly Dictionary<string, Element> selectedElements = new Dictionary<string, Element>();
private bool isAutoNavMode; private bool isAutoNavMode;
private NavigationType navType;
/// <summary> /// <summary>
/// Creates a new instance of the ui controls. /// Creates a new instance of the ui controls.
@ -167,6 +185,7 @@ namespace MLEM.Ui {
if (this.Input.IsPressedAvailable(MouseButton.Left)) { if (this.Input.IsPressedAvailable(MouseButton.Left)) {
this.IsAutoNavMode = false; this.IsAutoNavMode = false;
this.NavType = NavigationType.Mouse;
var selectedNow = mousedNow != null && mousedNow.CanBeSelected ? mousedNow : null; var selectedNow = mousedNow != null && mousedNow.CanBeSelected ? mousedNow : null;
this.SelectElement(this.ActiveRoot, selectedNow); this.SelectElement(this.ActiveRoot, selectedNow);
if (mousedNow != null && mousedNow.CanBePressed) { if (mousedNow != null && mousedNow.CanBePressed) {
@ -175,6 +194,7 @@ namespace MLEM.Ui {
} }
} else if (this.Input.IsPressedAvailable(MouseButton.Right)) { } else if (this.Input.IsPressedAvailable(MouseButton.Right)) {
this.IsAutoNavMode = false; this.IsAutoNavMode = false;
this.NavType = NavigationType.Mouse;
if (mousedNow != null && mousedNow.CanBePressed) { if (mousedNow != null && mousedNow.CanBePressed) {
this.PressElement(mousedNow, true); this.PressElement(mousedNow, true);
this.Input.TryConsumePressed(MouseButton.Right); this.Input.TryConsumePressed(MouseButton.Right);
@ -187,12 +207,14 @@ namespace MLEM.Ui {
if (this.HandleKeyboard) { if (this.HandleKeyboard) {
if (this.KeyboardButtons.IsPressedAvailable(this.Input, this.GamepadIndex)) { if (this.KeyboardButtons.IsPressedAvailable(this.Input, this.GamepadIndex)) {
if (this.SelectedElement?.Root != null && this.SelectedElement.CanBePressed) { if (this.SelectedElement?.Root != null && this.SelectedElement.CanBePressed) {
this.NavType = NavigationType.Keyboard;
// primary or secondary action on element using space or enter // primary or secondary action on element using space or enter
this.PressElement(this.SelectedElement, this.Input.IsModifierKeyDown(ModifierKey.Shift)); this.PressElement(this.SelectedElement, this.Input.IsModifierKeyDown(ModifierKey.Shift));
this.KeyboardButtons.TryConsumePressed(this.Input, this.GamepadIndex); this.KeyboardButtons.TryConsumePressed(this.Input, this.GamepadIndex);
} }
} else if (this.Input.IsPressedAvailable(Keys.Tab)) { } else if (this.Input.IsPressedAvailable(Keys.Tab)) {
this.IsAutoNavMode = true; this.IsAutoNavMode = true;
this.NavType = NavigationType.Keyboard;
// tab or shift-tab to next or previous element // tab or shift-tab to next or previous element
var backward = this.Input.IsModifierKeyDown(ModifierKey.Shift); var backward = this.Input.IsModifierKeyDown(ModifierKey.Shift);
var next = this.GetTabNextElement(backward); var next = this.GetTabNextElement(backward);
@ -208,12 +230,14 @@ namespace MLEM.Ui {
if (this.HandleTouch) { if (this.HandleTouch) {
if (this.Input.GetViewportGesture(GestureType.Tap, out var tap)) { if (this.Input.GetViewportGesture(GestureType.Tap, out var tap)) {
this.IsAutoNavMode = false; this.IsAutoNavMode = false;
this.NavType = NavigationType.Touch;
var tapped = this.GetElementUnderPos(tap.Position); var tapped = this.GetElementUnderPos(tap.Position);
this.SelectElement(this.ActiveRoot, tapped); this.SelectElement(this.ActiveRoot, tapped);
if (tapped != null && tapped.CanBePressed) if (tapped != null && tapped.CanBePressed)
this.PressElement(tapped); this.PressElement(tapped);
} else if (this.Input.GetViewportGesture(GestureType.Hold, out var hold)) { } else if (this.Input.GetViewportGesture(GestureType.Hold, out var hold)) {
this.IsAutoNavMode = false; this.IsAutoNavMode = false;
this.NavType = NavigationType.Touch;
var held = this.GetElementUnderPos(hold.Position); var held = this.GetElementUnderPos(hold.Position);
this.SelectElement(this.ActiveRoot, held); this.SelectElement(this.ActiveRoot, held);
if (held != null && held.CanBePressed) if (held != null && held.CanBePressed)
@ -224,6 +248,7 @@ namespace MLEM.Ui {
foreach (var location in this.Input.ViewportTouchState) { foreach (var location in this.Input.ViewportTouchState) {
var element = this.GetElementUnderPos(location.Position); var element = this.GetElementUnderPos(location.Position);
if (location.State == TouchLocationState.Pressed) { if (location.State == TouchLocationState.Pressed) {
this.NavType = NavigationType.Touch;
// start touching an element if we just touched down on it // start touching an element if we just touched down on it
this.SetTouchedElement(element); this.SetTouchedElement(element);
} else if (element != this.TouchedElement) { } else if (element != this.TouchedElement) {
@ -239,11 +264,13 @@ namespace MLEM.Ui {
if (this.HandleGamepad) { if (this.HandleGamepad) {
if (this.GamepadButtons.IsPressedAvailable(this.Input, this.GamepadIndex)) { if (this.GamepadButtons.IsPressedAvailable(this.Input, this.GamepadIndex)) {
if (this.SelectedElement?.Root != null && this.SelectedElement.CanBePressed) { if (this.SelectedElement?.Root != null && this.SelectedElement.CanBePressed) {
this.NavType = NavigationType.Gamepad;
this.PressElement(this.SelectedElement); this.PressElement(this.SelectedElement);
this.GamepadButtons.TryConsumePressed(this.Input, this.GamepadIndex); this.GamepadButtons.TryConsumePressed(this.Input, this.GamepadIndex);
} }
} else if (this.SecondaryGamepadButtons.IsPressedAvailable(this.Input, this.GamepadIndex)) { } else if (this.SecondaryGamepadButtons.IsPressedAvailable(this.Input, this.GamepadIndex)) {
if (this.SelectedElement?.Root != null && this.SelectedElement.CanBePressed) { if (this.SelectedElement?.Root != null && this.SelectedElement.CanBePressed) {
this.NavType = NavigationType.Gamepad;
this.PressElement(this.SelectedElement, true); this.PressElement(this.SelectedElement, true);
this.SecondaryGamepadButtons.TryConsumePressed(this.Input, this.GamepadIndex); this.SecondaryGamepadButtons.TryConsumePressed(this.Input, this.GamepadIndex);
} }
@ -286,8 +313,9 @@ namespace MLEM.Ui {
/// </summary> /// </summary>
/// <param name="root">The root element of the <see cref="Element"/></param> /// <param name="root">The root element of the <see cref="Element"/></param>
/// <param name="element">The element to select, or null to deselect the selected element.</param> /// <param name="element">The element to select, or null to deselect the selected element.</param>
/// <param name="autoNav">Whether automatic navigation should be forced on</param> /// <param name="autoNav">Whether automatic navigation should be forced on. If this is <see langword="null"/>, the automatic navigation state will stay the same.</param>
public void SelectElement(RootElement root, Element element, bool? autoNav = null) { /// <param name="navType">An optional <see cref="NavigationType"/> to set. If this is <see langword="null"/>, the navigation type will stay the same.</param>
public void SelectElement(RootElement root, Element element, bool? autoNav = null, NavigationType? navType = null) {
if (root == null) if (root == null)
return; return;
if (element != null && !element.CanBeSelected) if (element != null && !element.CanBeSelected)
@ -308,6 +336,8 @@ namespace MLEM.Ui {
if (autoNav != null) if (autoNav != null)
this.IsAutoNavMode = autoNav.Value; this.IsAutoNavMode = autoNav.Value;
if (navType != null)
this.NavType = navType.Value;
} }
/// <summary> /// <summary>
@ -444,6 +474,7 @@ namespace MLEM.Ui {
private bool HandleGamepadNextElement(Direction2 dir) { private bool HandleGamepadNextElement(Direction2 dir) {
this.IsAutoNavMode = true; this.IsAutoNavMode = true;
this.NavType = NavigationType.Gamepad;
var next = this.GetGamepadNextElement(dir); var next = this.GetGamepadNextElement(dir);
if (this.SelectedElement != null) if (this.SelectedElement != null)
next = this.SelectedElement.GetGamepadNextElement(dir, next); next = this.SelectedElement.GetGamepadNextElement(dir, next);
@ -454,5 +485,34 @@ namespace MLEM.Ui {
return false; return false;
} }
/// <summary>
/// An enumeration type that represents the possible types of navigation that a <see cref="UiControls"/> instance supports.
/// This is used by <see cref="UiControls.NavType"/>, which stores the most recently used navigation type for a <see cref="UiSystem"/>.
/// </summary>
public enum NavigationType {
/// <summary>
/// An unknown navigation type, which usually means there has not been any ui navigation of any type yet.
/// </summary>
Unknown = 0,
/// <summary>
/// Mouse cursor and mouse button navigation.
/// </summary>
Mouse,
/// <summary>
/// Keyboard navigation.
/// </summary>
Keyboard,
/// <summary>
/// Touch and gesture navigation.
/// </summary>
Touch,
/// <summary>
/// Gamepad-style navigation, which may also include arrow key-based navigation based on current <see cref="UiControls"/> settings.
/// </summary>
Gamepad
}
} }
} }

View file

@ -608,9 +608,10 @@ namespace MLEM.Ui {
/// Optionally, automatic navigation can be forced on, causing the <see cref="UiStyle.SelectionIndicator"/> to be drawn around the element. /// Optionally, automatic navigation can be forced on, causing the <see cref="UiStyle.SelectionIndicator"/> to be drawn around the element.
/// </summary> /// </summary>
/// <param name="element">The element to select, or null to deselect the selected element.</param> /// <param name="element">The element to select, or null to deselect the selected element.</param>
/// <param name="autoNav">Whether automatic navigation should be forced on</param> /// <param name="autoNav">Whether automatic navigation should be forced on. If this is <see langword="null"/>, the automatic navigation state will stay the same.</param>
public void SelectElement(Element element, bool? autoNav = null) { /// <param name="navType">An optional <see cref="UiControls.NavigationType"/> to set. If this is <see langword="null"/>, the navigation type will stay the same.</param>
this.System.Controls.SelectElement(this, element, autoNav); public void SelectElement(Element element, bool? autoNav = null, UiControls.NavigationType? navType = null) {
this.System.Controls.SelectElement(this, element, autoNav, navType);
} }
/// <summary> /// <summary>