2019-08-09 18:26:28 +02:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using Microsoft.Xna.Framework;
|
|
|
|
using Microsoft.Xna.Framework.Graphics;
|
|
|
|
using MLEM.Extensions;
|
2019-08-09 19:28:48 +02:00
|
|
|
using MLEM.Font;
|
2019-08-09 22:04:26 +02:00
|
|
|
using MLEM.Input;
|
2019-08-09 18:26:28 +02:00
|
|
|
using MLEM.Ui.Elements;
|
2019-08-10 21:37:10 +02:00
|
|
|
using MLEM.Ui.Style;
|
2019-08-09 18:26:28 +02:00
|
|
|
|
|
|
|
namespace MLEM.Ui {
|
|
|
|
public class UiSystem {
|
|
|
|
|
2019-08-10 18:41:56 +02:00
|
|
|
public readonly GraphicsDevice GraphicsDevice;
|
2019-08-12 19:44:16 +02:00
|
|
|
public Rectangle Viewport { get; private set; }
|
2019-08-09 18:26:28 +02:00
|
|
|
private readonly List<RootElement> rootElements = new List<RootElement>();
|
2019-08-10 18:41:56 +02:00
|
|
|
public readonly InputHandler InputHandler;
|
2019-08-09 22:04:26 +02:00
|
|
|
private readonly bool isInputOurs;
|
2019-08-09 18:26:28 +02:00
|
|
|
|
2019-08-11 18:02:21 +02:00
|
|
|
private float globalScale = 1;
|
2019-08-09 23:43:50 +02:00
|
|
|
public float GlobalScale {
|
|
|
|
get => this.globalScale;
|
|
|
|
set {
|
|
|
|
this.globalScale = value;
|
|
|
|
foreach (var root in this.rootElements)
|
|
|
|
root.Element.ForceUpdateArea();
|
|
|
|
}
|
|
|
|
}
|
2019-08-09 22:04:26 +02:00
|
|
|
public Element MousedElement { get; private set; }
|
2019-08-10 18:41:56 +02:00
|
|
|
public Element SelectedElement { get; private set; }
|
2019-08-10 21:37:10 +02:00
|
|
|
private UiStyle style;
|
|
|
|
public UiStyle Style {
|
|
|
|
get => this.style;
|
|
|
|
set {
|
|
|
|
this.style = value;
|
|
|
|
foreach (var root in this.rootElements) {
|
|
|
|
root.Element.PropagateUiSystem(this);
|
2019-08-12 14:44:42 +02:00
|
|
|
root.Element.SetAreaDirty();
|
2019-08-10 21:37:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-08-10 13:42:18 +02:00
|
|
|
public float DrawAlpha = 1;
|
2019-08-09 23:43:50 +02:00
|
|
|
public BlendState BlendState;
|
|
|
|
public SamplerState SamplerState = SamplerState.PointClamp;
|
2019-08-09 18:26:28 +02:00
|
|
|
|
2019-08-10 21:37:10 +02:00
|
|
|
public UiSystem(GameWindow window, GraphicsDevice device, UiStyle style, InputHandler inputHandler = null) {
|
2019-08-10 18:41:56 +02:00
|
|
|
this.GraphicsDevice = device;
|
|
|
|
this.InputHandler = inputHandler ?? new InputHandler();
|
2019-08-09 22:04:26 +02:00
|
|
|
this.isInputOurs = inputHandler == null;
|
2019-08-10 21:37:10 +02:00
|
|
|
this.style = style;
|
2019-08-12 19:44:16 +02:00
|
|
|
this.Viewport = device.Viewport.Bounds;
|
2019-08-09 19:28:48 +02:00
|
|
|
|
2019-08-09 18:26:28 +02:00
|
|
|
window.ClientSizeChanged += (sender, args) => {
|
2019-08-12 19:44:16 +02:00
|
|
|
this.Viewport = device.Viewport.Bounds;
|
2019-08-09 18:26:28 +02:00
|
|
|
foreach (var root in this.rootElements)
|
|
|
|
root.Element.ForceUpdateArea();
|
|
|
|
};
|
2019-08-10 18:41:56 +02:00
|
|
|
window.TextInput += (sender, args) => {
|
|
|
|
foreach (var root in this.rootElements)
|
|
|
|
root.Element.PropagateInput(args.Key, args.Character);
|
|
|
|
};
|
2019-08-09 18:26:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void Update(GameTime time) {
|
2019-08-09 22:04:26 +02:00
|
|
|
if (this.isInputOurs)
|
2019-08-10 18:41:56 +02:00
|
|
|
this.InputHandler.Update();
|
2019-08-09 22:04:26 +02:00
|
|
|
|
|
|
|
var mousedNow = this.GetMousedElement();
|
|
|
|
if (mousedNow != this.MousedElement) {
|
|
|
|
if (this.MousedElement != null)
|
2019-08-11 18:02:21 +02:00
|
|
|
this.MousedElement.OnMouseExit?.Invoke(this.MousedElement);
|
2019-08-09 22:04:26 +02:00
|
|
|
if (mousedNow != null)
|
2019-08-11 18:02:21 +02:00
|
|
|
mousedNow.OnMouseEnter?.Invoke(mousedNow);
|
2019-08-09 22:04:26 +02:00
|
|
|
this.MousedElement = mousedNow;
|
|
|
|
}
|
|
|
|
|
2019-08-10 18:41:56 +02:00
|
|
|
if (this.SelectedElement != mousedNow && this.InputHandler.IsMouseButtonPressed(MouseButton.Left)) {
|
|
|
|
if (this.SelectedElement != null)
|
|
|
|
this.SelectedElement.OnDeselected?.Invoke(this.SelectedElement);
|
|
|
|
if (mousedNow != null)
|
|
|
|
mousedNow.OnSelected?.Invoke(mousedNow);
|
|
|
|
this.SelectedElement = mousedNow;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mousedNow?.OnClicked != null) {
|
2019-08-09 22:04:26 +02:00
|
|
|
foreach (var button in InputHandler.MouseButtons) {
|
2019-08-10 18:41:56 +02:00
|
|
|
if (this.InputHandler.IsMouseButtonPressed(button))
|
2019-08-11 18:02:21 +02:00
|
|
|
mousedNow.OnClicked(mousedNow, button);
|
2019-08-09 22:04:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-09 18:26:28 +02:00
|
|
|
foreach (var root in this.rootElements)
|
|
|
|
root.Element.Update(time);
|
|
|
|
}
|
|
|
|
|
2019-08-15 14:59:15 +02:00
|
|
|
public void DrawEarly(GameTime time, SpriteBatch batch) {
|
2019-08-12 19:44:16 +02:00
|
|
|
foreach (var root in this.rootElements) {
|
|
|
|
if (!root.Element.IsHidden)
|
2019-08-15 14:59:15 +02:00
|
|
|
root.Element.DrawEarly(time, batch, this.DrawAlpha * root.Element.DrawAlpha, this.BlendState, this.SamplerState);
|
2019-08-12 19:44:16 +02:00
|
|
|
}
|
2019-08-15 14:59:15 +02:00
|
|
|
}
|
2019-08-12 19:44:16 +02:00
|
|
|
|
2019-08-15 14:59:15 +02:00
|
|
|
public void Draw(GameTime time, SpriteBatch batch) {
|
2019-08-09 19:39:51 +02:00
|
|
|
foreach (var root in this.rootElements) {
|
2019-08-11 18:02:21 +02:00
|
|
|
if (root.Element.IsHidden)
|
|
|
|
continue;
|
2019-08-11 21:24:09 +02:00
|
|
|
batch.Begin(SpriteSortMode.Deferred, this.BlendState, this.SamplerState);
|
2019-08-12 19:44:16 +02:00
|
|
|
root.Element.Draw(time, batch, this.DrawAlpha * root.Element.DrawAlpha, Point.Zero);
|
2019-08-11 18:02:21 +02:00
|
|
|
batch.End();
|
2019-08-09 19:39:51 +02:00
|
|
|
}
|
2019-08-09 18:26:28 +02:00
|
|
|
}
|
|
|
|
|
2019-08-11 18:02:21 +02:00
|
|
|
public RootElement Add(string name, Element root) {
|
2019-08-09 18:26:28 +02:00
|
|
|
if (this.IndexOf(name) >= 0)
|
|
|
|
throw new ArgumentException($"There is already a root element with name {name}");
|
|
|
|
|
2019-08-11 18:02:21 +02:00
|
|
|
var rootInst = new RootElement(name, root, this);
|
|
|
|
this.rootElements.Add(rootInst);
|
|
|
|
root.PropagateRoot(rootInst);
|
2019-08-10 21:37:10 +02:00
|
|
|
root.PropagateUiSystem(this);
|
2019-08-11 18:02:21 +02:00
|
|
|
return rootInst;
|
2019-08-09 18:26:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void Remove(string name) {
|
|
|
|
var index = this.IndexOf(name);
|
|
|
|
if (index < 0)
|
|
|
|
return;
|
|
|
|
this.rootElements.RemoveAt(index);
|
|
|
|
}
|
|
|
|
|
2019-08-11 18:02:21 +02:00
|
|
|
public RootElement Get(string name) {
|
2019-08-09 18:26:28 +02:00
|
|
|
var index = this.IndexOf(name);
|
2019-08-11 18:02:21 +02:00
|
|
|
return index < 0 ? null : this.rootElements[index];
|
2019-08-09 18:26:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private int IndexOf(string name) {
|
|
|
|
return this.rootElements.FindIndex(element => element.Name == name);
|
|
|
|
}
|
|
|
|
|
2019-08-09 22:04:26 +02:00
|
|
|
private Element GetMousedElement() {
|
|
|
|
foreach (var root in this.rootElements) {
|
2019-08-11 18:02:21 +02:00
|
|
|
var moused = root.Element.GetMousedElement();
|
2019-08-09 22:04:26 +02:00
|
|
|
if (moused != null)
|
|
|
|
return moused;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2019-08-09 18:26:28 +02:00
|
|
|
}
|
|
|
|
|
2019-08-11 18:02:21 +02:00
|
|
|
public class RootElement {
|
2019-08-09 18:26:28 +02:00
|
|
|
|
|
|
|
public readonly string Name;
|
|
|
|
public readonly Element Element;
|
2019-08-11 18:02:21 +02:00
|
|
|
public readonly UiSystem System;
|
|
|
|
private float scale = 1;
|
|
|
|
public float Scale {
|
|
|
|
get => this.scale;
|
|
|
|
set {
|
|
|
|
if (this.scale == value)
|
|
|
|
return;
|
|
|
|
this.scale = value;
|
|
|
|
this.Element.ForceUpdateArea();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
public float ActualScale => this.System.GlobalScale * this.Scale;
|
2019-08-09 18:26:28 +02:00
|
|
|
|
2019-08-11 18:02:21 +02:00
|
|
|
public RootElement(string name, Element element, UiSystem system) {
|
2019-08-09 18:26:28 +02:00
|
|
|
this.Name = name;
|
|
|
|
this.Element = element;
|
2019-08-11 18:02:21 +02:00
|
|
|
this.System = system;
|
2019-08-09 18:26:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|