mirror of
https://github.com/Ellpeck/MLEM.git
synced 2024-11-22 12:58:33 +01:00
tabbing, part 1!
This commit is contained in:
parent
cc20682d47
commit
5c741a98e9
15 changed files with 173 additions and 71 deletions
|
@ -86,7 +86,8 @@ namespace Demos {
|
|||
// (like above), these custom values don't get undone
|
||||
HasCustomStyle = true,
|
||||
Texture = this.testPatch,
|
||||
HoveredColor = Color.LightGray
|
||||
HoveredColor = Color.LightGray,
|
||||
SelectionIndicator = style.SelectionIndicator
|
||||
});
|
||||
|
||||
root.AddChild(new VerticalSpace(3));
|
||||
|
@ -129,7 +130,7 @@ namespace Demos {
|
|||
root.AddChild(new RadioButton(Anchor.AutoLeft, new Vector2(1, 10), "Radio button 2!") {PositionOffset = new Vector2(0, 1)});
|
||||
|
||||
var tooltip = new Tooltip(50, "This is a test tooltip to see the window bounding") {IsHidden = true};
|
||||
this.UiSystem.Add("TestTooltip", tooltip);
|
||||
this.UiSystem.Add("TestTooltip", tooltip).CanSelectContent = false;
|
||||
root.AddChild(new VerticalSpace(3));
|
||||
root.AddChild(new Button(Anchor.AutoLeft, new Vector2(1, 10), "Toggle Test Tooltip") {
|
||||
OnPressed = element => tooltip.IsHidden = !tooltip.IsHidden
|
||||
|
|
|
@ -7,6 +7,7 @@ using Microsoft.Xna.Framework.Graphics;
|
|||
using Microsoft.Xna.Framework.Input;
|
||||
using MLEM.Extensions;
|
||||
using MLEM.Input;
|
||||
using MLEM.Textures;
|
||||
using MLEM.Ui.Style;
|
||||
|
||||
namespace MLEM.Ui.Elements {
|
||||
|
@ -98,11 +99,11 @@ namespace MLEM.Ui.Elements {
|
|||
this.InitStyle(this.system.Style);
|
||||
}
|
||||
}
|
||||
protected InputHandler Input => this.System.InputHandler;
|
||||
protected UiControls Controls => this.System.Controls;
|
||||
protected InputHandler Input => this.Controls.Input;
|
||||
public Point MousePos => this.Input.MousePosition;
|
||||
public RootElement Root { get; private set; }
|
||||
public float Scale => this.Root.ActualScale;
|
||||
public Point MousePos => this.Input.MousePosition;
|
||||
public Element Parent { get; private set; }
|
||||
public bool IsMouseOver { get; private set; }
|
||||
public bool IsSelected { get; private set; }
|
||||
|
@ -116,7 +117,8 @@ namespace MLEM.Ui.Elements {
|
|||
this.SetAreaDirty();
|
||||
}
|
||||
}
|
||||
public bool IgnoresMouse;
|
||||
public bool CanBeSelected = true;
|
||||
public bool CanBeMoused = true;
|
||||
public float DrawAlpha = 1;
|
||||
public bool HasCustomStyle;
|
||||
public bool SetHeightBasedOnChildren;
|
||||
|
@ -153,6 +155,7 @@ namespace MLEM.Ui.Elements {
|
|||
}
|
||||
private bool areaDirty;
|
||||
private bool sortedChildrenDirty;
|
||||
public NinePatch SelectionIndicator;
|
||||
|
||||
public Element(Anchor anchor, Vector2 size) {
|
||||
this.anchor = anchor;
|
||||
|
@ -422,6 +425,10 @@ namespace MLEM.Ui.Elements {
|
|||
if (!child.IsHidden)
|
||||
child.Draw(time, batch, alpha * child.DrawAlpha, offset);
|
||||
}
|
||||
|
||||
if (this.IsSelected && this.Controls.ShowSelectionIndicator && this.SelectionIndicator != null) {
|
||||
batch.Draw(this.SelectionIndicator, this.DisplayArea.OffsetCopy(offset), Color.White * alpha);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void DrawEarly(GameTime time, SpriteBatch batch, float alpha, BlendState blendState = null, SamplerState samplerState = null) {
|
||||
|
@ -432,17 +439,18 @@ namespace MLEM.Ui.Elements {
|
|||
}
|
||||
|
||||
public virtual Element GetMousedElement() {
|
||||
if (this.IsHidden || this.IgnoresMouse)
|
||||
if (this.IsHidden)
|
||||
return null;
|
||||
for (var i = this.SortedChildren.Count - 1; i >= 0; i--) {
|
||||
var element = this.SortedChildren[i].GetMousedElement();
|
||||
if (element != null)
|
||||
return element;
|
||||
}
|
||||
return this.Area.Contains(this.MousePos) ? this : null;
|
||||
return this.CanBeMoused && this.Area.Contains(this.MousePos) ? this : null;
|
||||
}
|
||||
|
||||
protected virtual void InitStyle(UiStyle style) {
|
||||
this.SelectionIndicator = style.SelectionIndicator;
|
||||
}
|
||||
|
||||
public delegate void TextInputCallback(Element element, Keys key, char character);
|
||||
|
|
|
@ -7,10 +7,7 @@ namespace MLEM.Ui.Elements {
|
|||
|
||||
public static Button ImageButton(Anchor anchor, Vector2 size, TextureRegion texture, string text = null, string tooltipText = null, float tooltipWidth = 50, int imagePadding = 2) {
|
||||
var button = new Button(anchor, size, text, tooltipText, tooltipWidth);
|
||||
var image = new Image(Anchor.CenterLeft, Vector2.One, texture) {
|
||||
Padding = new Point(imagePadding),
|
||||
IgnoresMouse = true
|
||||
};
|
||||
var image = new Image(Anchor.CenterLeft, Vector2.One, texture) {Padding = new Point(imagePadding)};
|
||||
button.OnAreaUpdated += e => image.Size = new Vector2(e.Area.Height, e.Area.Height) / e.Scale;
|
||||
button.AddChild(image, 0);
|
||||
return button;
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace MLEM.Ui.Elements {
|
|||
|
||||
public Group(Anchor anchor, Vector2 size, bool setHeightBasedOnChildren = true) : base(anchor, size) {
|
||||
this.SetHeightBasedOnChildren = setHeightBasedOnChildren;
|
||||
this.CanBeSelected = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ namespace MLEM.Ui.Elements {
|
|||
public Image(Anchor anchor, Vector2 size, TextureRegion texture, bool scaleToImage = false) : base(anchor, size) {
|
||||
this.Texture = texture;
|
||||
this.ScaleToImage = scaleToImage;
|
||||
this.CanBeSelected = false;
|
||||
}
|
||||
|
||||
protected override Point CalcActualSize(Rectangle parentArea) {
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace MLEM.Ui.Elements {
|
|||
this.SetHeightBasedOnChildren = setHeightBasedOnChildren;
|
||||
this.scrollOverflow = scrollOverflow;
|
||||
this.ChildPadding = new Point(5);
|
||||
this.CanBeSelected = false;
|
||||
|
||||
if (scrollOverflow) {
|
||||
var scrollSize = scrollerSize ?? Point.Zero;
|
||||
|
|
|
@ -45,7 +45,8 @@ namespace MLEM.Ui.Elements {
|
|||
public Paragraph(Anchor anchor, float width, string text, bool centerText = false) : base(anchor, new Vector2(width, 0)) {
|
||||
this.text = text;
|
||||
this.AutoAdjustWidth = centerText;
|
||||
this.IgnoresMouse = true;
|
||||
this.CanBeSelected = false;
|
||||
this.CanBeMoused = false;
|
||||
}
|
||||
|
||||
protected override Point CalcActualSize(Rectangle parentArea) {
|
||||
|
|
|
@ -43,14 +43,15 @@ namespace MLEM.Ui.Elements {
|
|||
this.maxValue = maxValue;
|
||||
this.Horizontal = horizontal;
|
||||
this.ScrollerSize = new Point(horizontal ? scrollerSize : size.X.Floor(), !horizontal ? scrollerSize : size.Y.Floor());
|
||||
this.CanBeSelected = false;
|
||||
}
|
||||
|
||||
public override void Update(GameTime time) {
|
||||
base.Update(time);
|
||||
var moused = this.System.MousedElement;
|
||||
if (moused == this && this.Controls.MainButton(this.Input)) {
|
||||
var moused = this.Controls.MousedElement;
|
||||
if (moused == this && this.Controls.Input.IsMouseButtonDown(MouseButton.Left)) {
|
||||
this.isMouseHeld = true;
|
||||
} else if (this.isMouseHeld && this.Controls.MainButton(this.Input)) {
|
||||
} else if (this.isMouseHeld && !this.Controls.Input.IsMouseButtonDown(MouseButton.Left)) {
|
||||
this.isMouseHeld = false;
|
||||
}
|
||||
|
||||
|
@ -65,7 +66,7 @@ namespace MLEM.Ui.Elements {
|
|||
}
|
||||
|
||||
if (!this.Horizontal && moused != null && (moused == this.Parent || moused.GetParentTree().Contains(this.Parent))) {
|
||||
var scroll = this.Controls.Scroll(this.Input);
|
||||
var scroll = this.Input.LastScrollWheel - this.Input.ScrollWheel;
|
||||
if (scroll != 0)
|
||||
this.CurrentValue += this.StepPerScroll * Math.Sign(scroll);
|
||||
}
|
||||
|
|
|
@ -13,9 +13,10 @@ namespace MLEM.Ui.Elements {
|
|||
base(Anchor.TopLeft, width, text) {
|
||||
this.AutoAdjustWidth = true;
|
||||
this.Padding = new Point(2);
|
||||
this.CanBeSelected = false;
|
||||
|
||||
if (elementToHover != null) {
|
||||
elementToHover.OnMouseEnter += element => element.System.Add(element.GetType().Name + "Tooltip", this);
|
||||
elementToHover.OnMouseEnter += element => element.System.Add(element.GetType().Name + "Tooltip", this).CanSelectContent = false;
|
||||
elementToHover.OnMouseExit += element => element.System.Remove(element.GetType().Name + "Tooltip");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace MLEM.Ui.Elements {
|
|||
public class VerticalSpace : Element {
|
||||
|
||||
public VerticalSpace(int height) : base(Anchor.AutoCenter, new Vector2(1, height)) {
|
||||
this.CanBeSelected = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ using MLEM.Textures;
|
|||
namespace MLEM.Ui.Style {
|
||||
public class UiStyle {
|
||||
|
||||
public NinePatch SelectionIndicator;
|
||||
public NinePatch ButtonTexture;
|
||||
public NinePatch ButtonHoveredTexture;
|
||||
public Color ButtonHoveredColor;
|
||||
|
@ -28,5 +29,6 @@ namespace MLEM.Ui.Style {
|
|||
public IGenericFont BoldFont;
|
||||
public IGenericFont ItalicFont;
|
||||
public float TextScale = 1;
|
||||
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ namespace MLEM.Ui.Style {
|
|||
public class UntexturedStyle : UiStyle {
|
||||
|
||||
public UntexturedStyle(SpriteBatch batch) {
|
||||
this.SelectionIndicator = GenerateTexture(batch, Color.Transparent, Color.Red);
|
||||
this.ButtonTexture = GenerateTexture(batch, Color.CadetBlue);
|
||||
this.ButtonHoveredColor = Color.LightGray;
|
||||
this.PanelTexture = GenerateTexture(batch, Color.Gray);
|
||||
|
@ -27,12 +28,13 @@ namespace MLEM.Ui.Style {
|
|||
this.Font = new EmptyFont();
|
||||
}
|
||||
|
||||
private static NinePatch GenerateTexture(SpriteBatch batch, Color color) {
|
||||
private static NinePatch GenerateTexture(SpriteBatch batch, Color color, Color? outlineColor = null) {
|
||||
var outli = outlineColor ?? Color.Black;
|
||||
var tex = new Texture2D(batch.GraphicsDevice, 3, 3);
|
||||
tex.SetData(new[] {
|
||||
Color.Black, Color.Black, Color.Black,
|
||||
Color.Black, color, Color.Black,
|
||||
Color.Black, Color.Black, Color.Black
|
||||
outli, outli, outli,
|
||||
outli, color, outli,
|
||||
outli, outli, outli
|
||||
});
|
||||
batch.Disposing += (sender, args) => {
|
||||
if (tex != null) {
|
||||
|
|
|
@ -1,15 +1,116 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using MLEM.Input;
|
||||
using MLEM.Ui.Elements;
|
||||
|
||||
namespace MLEM.Ui {
|
||||
public class UiControls {
|
||||
|
||||
public BoolQuery MainButton = h => h.IsMouseButtonPressed(MouseButton.Left);
|
||||
public BoolQuery SecondaryButton = h => h.IsMouseButtonPressed(MouseButton.Right);
|
||||
public FloatQuery Scroll = h => h.LastScrollWheel - h.ScrollWheel;
|
||||
public readonly InputHandler Input;
|
||||
private readonly bool isInputOurs;
|
||||
private readonly UiSystem system;
|
||||
|
||||
public delegate bool BoolQuery(InputHandler handler);
|
||||
public Element MousedElement { get; private set; }
|
||||
public Element SelectedElement { get; private set; }
|
||||
public bool ShowSelectionIndicator;
|
||||
|
||||
public delegate float FloatQuery(InputHandler handler);
|
||||
public UiControls(UiSystem system, InputHandler inputHandler = null) {
|
||||
this.system = system;
|
||||
this.Input = inputHandler ?? new InputHandler();
|
||||
this.isInputOurs = inputHandler == null;
|
||||
}
|
||||
|
||||
public void Update() {
|
||||
if (this.isInputOurs)
|
||||
this.Input.Update();
|
||||
|
||||
var mousedNow = this.GetMousedElement();
|
||||
// mouse new element
|
||||
if (mousedNow != this.MousedElement) {
|
||||
if (this.MousedElement != null)
|
||||
this.MousedElement.OnMouseExit?.Invoke(this.MousedElement);
|
||||
if (mousedNow != null)
|
||||
mousedNow.OnMouseEnter?.Invoke(mousedNow);
|
||||
this.MousedElement = mousedNow;
|
||||
}
|
||||
|
||||
if (this.Input.IsMouseButtonPressed(MouseButton.Left)) {
|
||||
// select element
|
||||
var selectedNow = mousedNow != null && mousedNow.CanBeSelected ? mousedNow : null;
|
||||
if (this.SelectedElement != selectedNow)
|
||||
this.SelectElement(selectedNow, false);
|
||||
|
||||
// first action on element
|
||||
if (mousedNow != null)
|
||||
mousedNow.OnPressed?.Invoke(mousedNow);
|
||||
} else if (this.Input.IsMouseButtonPressed(MouseButton.Right)) {
|
||||
// secondary action on element
|
||||
if (mousedNow != null)
|
||||
mousedNow.OnSecondaryPressed?.Invoke(mousedNow);
|
||||
} else if (this.Input.IsKeyPressed(Keys.Enter) || this.Input.IsKeyPressed(Keys.Space)) {
|
||||
if (this.SelectedElement != null) {
|
||||
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)) {
|
||||
// tab or shift-tab to next or previous element
|
||||
this.SelectElement(this.GetNextElement(this.Input.IsModifierKeyDown(ModifierKey.Shift)), true);
|
||||
}
|
||||
}
|
||||
|
||||
public void SelectElement(Element element, bool show) {
|
||||
if (this.SelectedElement != null)
|
||||
this.SelectedElement.OnDeselected?.Invoke(this.SelectedElement);
|
||||
if (element != null)
|
||||
element.OnSelected?.Invoke(element);
|
||||
this.SelectedElement = element;
|
||||
this.ShowSelectionIndicator = show;
|
||||
}
|
||||
|
||||
public Element GetMousedElement() {
|
||||
foreach (var root in this.system.GetRootElements()) {
|
||||
var moused = root.Element.GetMousedElement();
|
||||
if (moused != null)
|
||||
return moused;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Element GetNextElement(bool backward) {
|
||||
var currRoot = this.system.GetRootElements().FirstOrDefault(root => root.CanSelectContent);
|
||||
if (currRoot == null)
|
||||
return null;
|
||||
var children = currRoot.Element.GetChildren(regardChildrensChildren: true);
|
||||
if (this.SelectedElement == null || this.SelectedElement.Root != currRoot) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using MLEM.Extensions;
|
||||
|
@ -14,8 +15,6 @@ namespace MLEM.Ui {
|
|||
public readonly GraphicsDevice GraphicsDevice;
|
||||
public Rectangle Viewport { get; private set; }
|
||||
private readonly List<RootElement> rootElements = new List<RootElement>();
|
||||
public readonly InputHandler InputHandler;
|
||||
private readonly bool isInputOurs;
|
||||
|
||||
public bool AutoScaleWithScreen;
|
||||
public Point AutoScaleReferenceSize;
|
||||
|
@ -33,8 +32,6 @@ namespace MLEM.Ui {
|
|||
root.Element.ForceUpdateArea();
|
||||
}
|
||||
}
|
||||
public Element MousedElement { get; private set; }
|
||||
public Element SelectedElement { get; private set; }
|
||||
private UiStyle style;
|
||||
public UiStyle Style {
|
||||
get => this.style;
|
||||
|
@ -49,12 +46,11 @@ namespace MLEM.Ui {
|
|||
public float DrawAlpha = 1;
|
||||
public BlendState BlendState;
|
||||
public SamplerState SamplerState = SamplerState.PointClamp;
|
||||
public UiControls Controls = new UiControls();
|
||||
public UiControls Controls;
|
||||
|
||||
public UiSystem(GameWindow window, GraphicsDevice device, UiStyle style, InputHandler inputHandler = null) {
|
||||
this.Controls = new UiControls(this, inputHandler);
|
||||
this.GraphicsDevice = device;
|
||||
this.InputHandler = inputHandler ?? new InputHandler();
|
||||
this.isInputOurs = inputHandler == null;
|
||||
this.style = style;
|
||||
this.Viewport = device.Viewport.Bounds;
|
||||
this.AutoScaleReferenceSize = this.Viewport.Size;
|
||||
|
@ -71,37 +67,7 @@ namespace MLEM.Ui {
|
|||
}
|
||||
|
||||
public void Update(GameTime time) {
|
||||
if (this.isInputOurs)
|
||||
this.InputHandler.Update();
|
||||
|
||||
var mousedNow = this.GetMousedElement();
|
||||
// mouse new element
|
||||
if (mousedNow != this.MousedElement) {
|
||||
if (this.MousedElement != null)
|
||||
this.MousedElement.OnMouseExit?.Invoke(this.MousedElement);
|
||||
if (mousedNow != null)
|
||||
mousedNow.OnMouseEnter?.Invoke(mousedNow);
|
||||
this.MousedElement = mousedNow;
|
||||
}
|
||||
|
||||
if (this.Controls.MainButton(this.InputHandler)) {
|
||||
// select element
|
||||
if (this.SelectedElement != mousedNow) {
|
||||
if (this.SelectedElement != null)
|
||||
this.SelectedElement.OnDeselected?.Invoke(this.SelectedElement);
|
||||
if (mousedNow != null)
|
||||
mousedNow.OnSelected?.Invoke(mousedNow);
|
||||
this.SelectedElement = mousedNow;
|
||||
}
|
||||
|
||||
// first action on element
|
||||
if (mousedNow != null)
|
||||
mousedNow.OnPressed?.Invoke(mousedNow);
|
||||
} else if (this.Controls.SecondaryButton(this.InputHandler)) {
|
||||
// secondary action on element
|
||||
if (mousedNow != null)
|
||||
mousedNow.OnSecondaryPressed?.Invoke(mousedNow);
|
||||
}
|
||||
this.Controls.Update();
|
||||
|
||||
foreach (var root in this.rootElements)
|
||||
root.Element.Update(time);
|
||||
|
@ -158,13 +124,9 @@ namespace MLEM.Ui {
|
|||
return this.rootElements.FindIndex(element => element.Name == name);
|
||||
}
|
||||
|
||||
private Element GetMousedElement() {
|
||||
for (var i = this.rootElements.Count - 1; i >= 0; i--) {
|
||||
var moused = this.rootElements[i].Element.GetMousedElement();
|
||||
if (moused != null)
|
||||
return moused;
|
||||
}
|
||||
return null;
|
||||
public IEnumerable<RootElement> GetRootElements() {
|
||||
for (var i = this.rootElements.Count - 1; i >= 0; i--)
|
||||
yield return this.rootElements[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -185,6 +147,7 @@ namespace MLEM.Ui {
|
|||
}
|
||||
}
|
||||
public float ActualScale => this.System.GlobalScale * this.Scale;
|
||||
public bool CanSelectContent = true;
|
||||
|
||||
public RootElement(string name, Element element, UiSystem system) {
|
||||
this.Name = name;
|
||||
|
|
|
@ -77,6 +77,19 @@ namespace MLEM.Input {
|
|||
return this.WasKeyUp(key) && this.IsKeyDown(key);
|
||||
}
|
||||
|
||||
public bool IsModifierKeyDown(ModifierKey modifier) {
|
||||
switch (modifier) {
|
||||
case ModifierKey.Shift:
|
||||
return this.IsKeyDown(Keys.LeftShift) || this.IsKeyDown(Keys.RightShift);
|
||||
case ModifierKey.Control:
|
||||
return this.IsKeyDown(Keys.LeftControl) || this.IsKeyDown(Keys.RightControl);
|
||||
case ModifierKey.Alt:
|
||||
return this.IsKeyDown(Keys.LeftAlt) || this.IsKeyDown(Keys.RightAlt);
|
||||
default:
|
||||
throw new ArgumentException(nameof(modifier));
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsMouseButtonDown(MouseButton button) {
|
||||
return GetState(this.MouseState, button) == ButtonState.Pressed;
|
||||
}
|
||||
|
@ -145,4 +158,12 @@ namespace MLEM.Input {
|
|||
Extra2
|
||||
|
||||
}
|
||||
|
||||
public enum ModifierKey {
|
||||
|
||||
Shift,
|
||||
Control,
|
||||
Alt
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue