1
0
Fork 0
mirror of https://github.com/Ellpeck/MLEM.git synced 2024-11-24 05:38:33 +01:00

Compare commits

...

3 commits

5 changed files with 130 additions and 80 deletions

View file

@ -40,15 +40,15 @@ namespace MLEM.Startup {
/// <summary> /// <summary>
/// An event that is invoked in <see cref="LoadContent"/> /// An event that is invoked in <see cref="LoadContent"/>
/// </summary> /// </summary>
public GenericCallback OnLoadContent; public event GenericCallback OnLoadContent;
/// <summary> /// <summary>
/// An event that is invoked in <see cref="Update"/> /// An event that is invoked in <see cref="Update"/>
/// </summary> /// </summary>
public TimeCallback OnUpdate; public event TimeCallback OnUpdate;
/// <summary> /// <summary>
/// An event that is invoked in <see cref="Draw"/> /// An event that is invoked in <see cref="Draw"/>
/// </summary> /// </summary>
public TimeCallback OnDraw; public event TimeCallback OnDraw;
/// <summary> /// <summary>
/// Creates a new MlemGame instance with some default settings /// Creates a new MlemGame instance with some default settings

View file

@ -15,7 +15,7 @@ namespace MLEM.Ui.Elements {
/// <summary> /// <summary>
/// This class represents a generic base class for ui elements of a <see cref="UiSystem"/>. /// This class represents a generic base class for ui elements of a <see cref="UiSystem"/>.
/// </summary> /// </summary>
public abstract class Element : GenericDataHolder { public abstract class Element : GenericDataHolder, IDisposable {
/// <summary> /// <summary>
/// A list of all of this element's direct children. /// A list of all of this element's direct children.
@ -377,6 +377,11 @@ namespace MLEM.Ui.Elements {
/// Event that is called when a child is removed from this element using <see cref="RemoveChild"/> /// Event that is called when a child is removed from this element using <see cref="RemoveChild"/>
/// </summary> /// </summary>
public OtherElementCallback OnChildRemoved; public OtherElementCallback OnChildRemoved;
/// <summary>
/// Event that is called when this element's <see cref="Dispose"/> method is called, which also happens in <see cref="Finalize"/>.
/// This event is useful for unregistering global event handlers when this object should be destroyed.
/// </summary>
public GenericCallback OnDisposed;
/// <summary> /// <summary>
/// A style property that contains the selection indicator that is displayed on this element if it is the <see cref="RootElement.SelectedElement"/> /// A style property that contains the selection indicator that is displayed on this element if it is the <see cref="RootElement.SelectedElement"/>
@ -415,6 +420,11 @@ namespace MLEM.Ui.Elements {
this.SetSortedChildrenDirty(); this.SetSortedChildrenDirty();
} }
/// <inheritdoc />
~Element() {
this.Dispose();
}
/// <summary> /// <summary>
/// Adds a child to this element. /// Adds a child to this element.
/// </summary> /// </summary>
@ -430,7 +440,7 @@ namespace MLEM.Ui.Elements {
element.AndChildren(e => { element.AndChildren(e => {
e.Root = this.Root; e.Root = this.Root;
e.System = this.System; e.System = this.System;
this.Root?.OnElementAdded(e); this.Root?.InvokeOnElementAdded(e);
this.OnChildAdded?.Invoke(this, e); this.OnChildAdded?.Invoke(this, e);
}); });
this.SetSortedChildrenDirty(); this.SetSortedChildrenDirty();
@ -451,7 +461,7 @@ namespace MLEM.Ui.Elements {
element.AndChildren(e => { element.AndChildren(e => {
e.Root = null; e.Root = null;
e.System = null; e.System = null;
this.Root?.OnElementRemoved(e); this.Root?.InvokeOnElementRemoved(e);
this.OnChildRemoved?.Invoke(this, e); this.OnChildRemoved?.Invoke(this, e);
}); });
this.SetSortedChildrenDirty(); this.SetSortedChildrenDirty();
@ -618,7 +628,7 @@ namespace MLEM.Ui.Elements {
} }
this.area = new RectangleF(pos, newSize); this.area = new RectangleF(pos, newSize);
this.System.OnElementAreaUpdated?.Invoke(this); this.System.InvokeOnElementAreaUpdated(this);
foreach (var child in this.Children) foreach (var child in this.Children)
child.ForceUpdateArea(); child.ForceUpdateArea();
@ -626,7 +636,7 @@ namespace MLEM.Ui.Elements {
if (this.SetWidthBasedOnChildren || this.SetHeightBasedOnChildren) { if (this.SetWidthBasedOnChildren || this.SetHeightBasedOnChildren) {
Element foundChild = null; Element foundChild = null;
var autoSize = this.UnscrolledArea.Size; var autoSize = this.UnscrolledArea.Size;
if (this.SetHeightBasedOnChildren) { if (this.SetHeightBasedOnChildren) {
var lowest = this.GetLowestChild(e => !e.IsHidden); var lowest = this.GetLowestChild(e => !e.IsHidden);
if (lowest != null) { if (lowest != null) {
@ -638,7 +648,7 @@ namespace MLEM.Ui.Elements {
autoSize.Y = 0; autoSize.Y = 0;
} }
} }
if (this.SetWidthBasedOnChildren) { if (this.SetWidthBasedOnChildren) {
var rightmost = this.GetRightmostChild(e => !e.IsHidden); var rightmost = this.GetRightmostChild(e => !e.IsHidden);
if (rightmost != null) { if (rightmost != null) {
@ -650,7 +660,7 @@ namespace MLEM.Ui.Elements {
autoSize.X = 0; autoSize.X = 0;
} }
} }
if (this.TreatSizeAsMinimum) if (this.TreatSizeAsMinimum)
autoSize = Vector2.Max(autoSize, actualSize); autoSize = Vector2.Max(autoSize, actualSize);
if (!autoSize.Equals(this.UnscrolledArea.Size, 0.01F)) { if (!autoSize.Equals(this.UnscrolledArea.Size, 0.01F)) {
@ -837,7 +847,7 @@ namespace MLEM.Ui.Elements {
/// </summary> /// </summary>
/// <param name="time">The game's time</param> /// <param name="time">The game's time</param>
public virtual void Update(GameTime time) { public virtual void Update(GameTime time) {
this.System.OnElementUpdated?.Invoke(this, time); this.System.InvokeOnElementUpdated(this, time);
foreach (var child in this.GetRelevantChildren()) foreach (var child in this.GetRelevantChildren())
if (child.System != null) if (child.System != null)
@ -888,9 +898,9 @@ namespace MLEM.Ui.Elements {
/// <param name="samplerState">The sampler state that is used for drawing</param> /// <param name="samplerState">The sampler state that is used for drawing</param>
/// <param name="matrix">The transformation matrix that is used for drawing</param> /// <param name="matrix">The transformation matrix that is used for drawing</param>
public virtual void Draw(GameTime time, SpriteBatch batch, float alpha, BlendState blendState, SamplerState samplerState, Matrix matrix) { public virtual void Draw(GameTime time, SpriteBatch batch, float alpha, BlendState blendState, SamplerState samplerState, Matrix matrix) {
this.System.OnElementDrawn?.Invoke(this, time, batch, alpha); this.System.InvokeOnElementDrawn(this, time, batch, alpha);
if (this.IsSelected) if (this.IsSelected)
this.System.OnSelectedElementDrawn?.Invoke(this, time, batch, alpha); this.System.InvokeOnSelectedElementDrawn(this, time, batch, alpha);
foreach (var child in this.GetRelevantChildren()) { foreach (var child in this.GetRelevantChildren()) {
if (!child.IsHidden) if (!child.IsHidden)
@ -935,6 +945,12 @@ namespace MLEM.Ui.Elements {
return this.CanBeMoused && this.DisplayArea.Contains(position) ? this : null; return this.CanBeMoused && this.DisplayArea.Contains(position) ? this : null;
} }
/// <inheritdoc />
public virtual void Dispose() {
this.OnDisposed?.Invoke(this);
GC.SuppressFinalize(this);
}
/// <summary> /// <summary>
/// Performs the specified action on this element and all of its <see cref="Children"/> /// Performs the specified action on this element and all of its <see cref="Children"/>
/// </summary> /// </summary>

View file

@ -15,7 +15,7 @@ namespace MLEM.Ui.Elements {
/// Additionally, a panel can be set to <see cref="scrollOverflow"/> on construction, which causes all elements that don't fit into the panel to be hidden until scrolled to using a <see cref="ScrollBar"/>. /// Additionally, a panel can be set to <see cref="scrollOverflow"/> on construction, which causes all elements that don't fit into the panel to be hidden until scrolled to using a <see cref="ScrollBar"/>.
/// As this behavior is accomplished using a <see cref="RenderTarget2D"/>, scrolling panels need to have their <see cref="DrawEarly"/> methods called using <see cref="UiSystem.DrawEarly"/>. /// As this behavior is accomplished using a <see cref="RenderTarget2D"/>, scrolling panels need to have their <see cref="DrawEarly"/> methods called using <see cref="UiSystem.DrawEarly"/>.
/// </summary> /// </summary>
public class Panel : Element, IDisposable { public class Panel : Element {
/// <summary> /// <summary>
/// The texture that this panel should have, or null if it should be invisible. /// The texture that this panel should have, or null if it should be invisible.
@ -76,11 +76,6 @@ namespace MLEM.Ui.Elements {
} }
} }
/// <inheritdoc />
~Panel() {
this.Dispose();
}
/// <inheritdoc /> /// <inheritdoc />
public override void ForceUpdateArea() { public override void ForceUpdateArea() {
if (this.scrollOverflow) { if (this.scrollOverflow) {
@ -237,12 +232,12 @@ namespace MLEM.Ui.Elements {
} }
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() { public override void Dispose() {
if (this.renderTarget != null) { if (this.renderTarget != null) {
this.renderTarget.Dispose(); this.renderTarget.Dispose();
this.renderTarget = null; this.renderTarget = null;
} }
GC.SuppressFinalize(this); base.Dispose();
} }
} }

View file

@ -151,11 +151,11 @@ namespace MLEM.Ui {
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)
this.System.OnElementPressed?.Invoke(mousedNow); this.System.InvokeOnElementPressed(mousedNow);
} else if (this.Input.IsMouseButtonPressed(MouseButton.Right)) { } else if (this.Input.IsMouseButtonPressed(MouseButton.Right)) {
this.IsAutoNavMode = false; this.IsAutoNavMode = false;
if (mousedNow != null && mousedNow.CanBePressed) if (mousedNow != null && mousedNow.CanBePressed)
this.System.OnElementSecondaryPressed?.Invoke(mousedNow); this.System.InvokeOnElementSecondaryPressed(mousedNow);
} }
} }
@ -165,10 +165,10 @@ namespace MLEM.Ui {
if (this.SelectedElement?.Root != null && this.SelectedElement.CanBePressed) { if (this.SelectedElement?.Root != null && this.SelectedElement.CanBePressed) {
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
this.System.OnElementSecondaryPressed?.Invoke(this.SelectedElement); this.System.InvokeOnElementSecondaryPressed(this.SelectedElement);
} else { } else {
// first action on element using space or enter // first action on element using space or enter
this.System.OnElementPressed?.Invoke(this.SelectedElement); this.System.InvokeOnElementPressed(this.SelectedElement);
} }
} }
} else if (this.Input.IsKeyPressed(Keys.Tab)) { } else if (this.Input.IsKeyPressed(Keys.Tab)) {
@ -189,13 +189,13 @@ namespace MLEM.Ui {
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.System.OnElementPressed?.Invoke(tapped); this.System.InvokeOnElementPressed(tapped);
} else if (this.Input.GetGesture(GestureType.Hold, out var hold)) { } else if (this.Input.GetGesture(GestureType.Hold, out var hold)) {
this.IsAutoNavMode = false; this.IsAutoNavMode = false;
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)
this.System.OnElementSecondaryPressed?.Invoke(held); this.System.InvokeOnElementSecondaryPressed(held);
} else if (this.Input.TouchState.Count <= 0) { } else if (this.Input.TouchState.Count <= 0) {
this.SetTouchedElement(null); this.SetTouchedElement(null);
} else { } else {
@ -216,10 +216,10 @@ namespace MLEM.Ui {
if (this.HandleGamepad) { if (this.HandleGamepad) {
if (this.GamepadButtons.IsPressed(this.Input, this.GamepadIndex)) { if (this.GamepadButtons.IsPressed(this.Input, this.GamepadIndex)) {
if (this.SelectedElement?.Root != null && this.SelectedElement.CanBePressed) if (this.SelectedElement?.Root != null && this.SelectedElement.CanBePressed)
this.System.OnElementPressed?.Invoke(this.SelectedElement); this.System.InvokeOnElementPressed(this.SelectedElement);
} else if (this.SecondaryGamepadButtons.IsPressed(this.Input, this.GamepadIndex)) { } else if (this.SecondaryGamepadButtons.IsPressed(this.Input, this.GamepadIndex)) {
if (this.SelectedElement?.Root != null && this.SelectedElement.CanBePressed) if (this.SelectedElement?.Root != null && this.SelectedElement.CanBePressed)
this.System.OnElementSecondaryPressed?.Invoke(this.SelectedElement); this.System.InvokeOnElementSecondaryPressed(this.SelectedElement);
} else if (this.DownButtons.IsPressed(this.Input, this.GamepadIndex)) { } else if (this.DownButtons.IsPressed(this.Input, this.GamepadIndex)) {
this.HandleGamepadNextElement(Direction2.Down); this.HandleGamepadNextElement(Direction2.Down);
} else if (this.LeftButtons.IsPressed(this.Input, this.GamepadIndex)) { } else if (this.LeftButtons.IsPressed(this.Input, this.GamepadIndex)) {
@ -265,14 +265,14 @@ namespace MLEM.Ui {
return; return;
if (selected != null) if (selected != null)
this.System.OnElementDeselected?.Invoke(selected); this.System.InvokeOnElementDeselected(selected);
if (element != null) { if (element != null) {
this.System.OnElementSelected?.Invoke(element); this.System.InvokeOnElementSelected(element);
this.selectedElements[root.Name] = element; this.selectedElements[root.Name] = element;
} else { } else {
this.selectedElements.Remove(root.Name); this.selectedElements.Remove(root.Name);
} }
this.System.OnSelectedElementChanged?.Invoke(element); this.System.InvokeOnSelectedElementChanged(element);
if (autoNav != null) if (autoNav != null)
this.IsAutoNavMode = autoNav.Value; this.IsAutoNavMode = autoNav.Value;
@ -285,11 +285,11 @@ namespace MLEM.Ui {
public void SetMousedElement(Element element) { public void SetMousedElement(Element element) {
if (element != this.MousedElement) { if (element != this.MousedElement) {
if (this.MousedElement != null) if (this.MousedElement != null)
this.System.OnElementMouseExit?.Invoke(this.MousedElement); this.System.InvokeOnElementMouseExit(this.MousedElement);
if (element != null) if (element != null)
this.System.OnElementMouseEnter?.Invoke(element); this.System.InvokeOnElementMouseEnter(element);
this.MousedElement = element; this.MousedElement = element;
this.System.OnMousedElementChanged?.Invoke(element); this.System.InvokeOnMousedElementChanged(element);
} }
} }
@ -300,11 +300,11 @@ namespace MLEM.Ui {
public void SetTouchedElement(Element element) { public void SetTouchedElement(Element element) {
if (element != this.TouchedElement) { if (element != this.TouchedElement) {
if (this.TouchedElement != null) if (this.TouchedElement != null)
this.System.OnElementTouchExit?.Invoke(this.TouchedElement); this.System.InvokeOnElementTouchExit(this.TouchedElement);
if (element != null) if (element != null)
this.System.OnElementTouchEnter?.Invoke(element); this.System.InvokeOnElementTouchEnter(element);
this.TouchedElement = element; this.TouchedElement = element;
this.System.OnTouchedElementChanged?.Invoke(element); this.System.InvokeOnTouchedElementChanged(element);
} }
} }

View file

@ -96,71 +96,71 @@ namespace MLEM.Ui {
/// <summary> /// <summary>
/// Event that is invoked after an <see cref="Element"/> is drawn, but before its children are drawn. /// Event that is invoked after an <see cref="Element"/> is drawn, but before its children are drawn.
/// </summary> /// </summary>
public Element.DrawCallback OnElementDrawn = (e, time, batch, alpha) => e.OnDrawn?.Invoke(e, time, batch, alpha); public event Element.DrawCallback OnElementDrawn;
/// <summary> /// <summary>
/// Event that is invoked after the <see cref="RootElement.SelectedElement"/> for each root element is drawn, but before its children are drawn. /// Event that is invoked after the <see cref="RootElement.SelectedElement"/> for each root element is drawn, but before its children are drawn.
/// </summary> /// </summary>
public Element.DrawCallback OnSelectedElementDrawn; public event Element.DrawCallback OnSelectedElementDrawn;
/// <summary> /// <summary>
/// Event that is invoked when an <see cref="Element"/> is updated /// Event that is invoked when an <see cref="Element"/> is updated
/// </summary> /// </summary>
public Element.TimeCallback OnElementUpdated = (e, time) => e.OnUpdated?.Invoke(e, time); public event Element.TimeCallback OnElementUpdated;
/// <summary> /// <summary>
/// Event that is invoked when an <see cref="Element"/> is pressed with the primary action key /// Event that is invoked when an <see cref="Element"/> is pressed with the primary action key
/// </summary> /// </summary>
public Element.GenericCallback OnElementPressed = e => e.OnPressed?.Invoke(e); public event Element.GenericCallback OnElementPressed;
/// <summary> /// <summary>
/// Event that is invoked when an <see cref="Element"/> is pressed with the secondary action key /// Event that is invoked when an <see cref="Element"/> is pressed with the secondary action key
/// </summary> /// </summary>
public Element.GenericCallback OnElementSecondaryPressed = e => e.OnSecondaryPressed?.Invoke(e); public event Element.GenericCallback OnElementSecondaryPressed;
/// <summary> /// <summary>
/// Event that is invoked when an <see cref="Element"/> is newly selected using automatic navigation, or after it has been pressed with the mouse. /// Event that is invoked when an <see cref="Element"/> is newly selected using automatic navigation, or after it has been pressed with the mouse.
/// </summary> /// </summary>
public Element.GenericCallback OnElementSelected = e => e.OnSelected?.Invoke(e); public event Element.GenericCallback OnElementSelected;
/// <summary> /// <summary>
/// Event that is invoked when an <see cref="Element"/> is deselected during the selection of a new element. /// Event that is invoked when an <see cref="Element"/> is deselected during the selection of a new element.
/// </summary> /// </summary>
public Element.GenericCallback OnElementDeselected = e => e.OnDeselected?.Invoke(e); public event Element.GenericCallback OnElementDeselected;
/// <summary> /// <summary>
/// Event that is invoked when the mouse enters an <see cref="Element"/> /// Event that is invoked when the mouse enters an <see cref="Element"/>
/// </summary> /// </summary>
public Element.GenericCallback OnElementMouseEnter = e => e.OnMouseEnter?.Invoke(e); public event Element.GenericCallback OnElementMouseEnter;
/// <summary> /// <summary>
/// Event that is invoked when the mouse exits an <see cref="Element"/> /// Event that is invoked when the mouse exits an <see cref="Element"/>
/// </summary> /// </summary>
public Element.GenericCallback OnElementMouseExit = e => e.OnMouseExit?.Invoke(e); public event Element.GenericCallback OnElementMouseExit;
/// <summary> /// <summary>
/// Event that is invoked when an <see cref="Element"/> starts being touched /// Event that is invoked when an <see cref="Element"/> starts being touched
/// </summary> /// </summary>
public Element.GenericCallback OnElementTouchEnter = e => e.OnTouchEnter?.Invoke(e); public event Element.GenericCallback OnElementTouchEnter;
/// <summary> /// <summary>
/// Event that is invoked when an <see cref="Element"/> stops being touched /// Event that is invoked when an <see cref="Element"/> stops being touched
/// </summary> /// </summary>
public Element.GenericCallback OnElementTouchExit = e => e.OnTouchExit?.Invoke(e); public event Element.GenericCallback OnElementTouchExit;
/// <summary> /// <summary>
/// Event that is invoked when an <see cref="Element"/>'s display area changes /// Event that is invoked when an <see cref="Element"/>'s display area changes
/// </summary> /// </summary>
public Element.GenericCallback OnElementAreaUpdated = e => e.OnAreaUpdated?.Invoke(e); public event Element.GenericCallback OnElementAreaUpdated;
/// <summary> /// <summary>
/// Event that is invoked when the <see cref="Element"/> that the mouse is currently over changes /// Event that is invoked when the <see cref="Element"/> that the mouse is currently over changes
/// </summary> /// </summary>
public Element.GenericCallback OnMousedElementChanged; public event Element.GenericCallback OnMousedElementChanged;
/// <summary> /// <summary>
/// Event that is invoked when the <see cref="Element"/> that is being touched changes /// Event that is invoked when the <see cref="Element"/> that is being touched changes
/// </summary> /// </summary>
public Element.GenericCallback OnTouchedElementChanged; public event Element.GenericCallback OnTouchedElementChanged;
/// <summary> /// <summary>
/// Event that is invoked when the selected <see cref="Element"/> changes, either through automatic navigation, or by pressing on an element with the mouse /// Event that is invoked when the selected <see cref="Element"/> changes, either through automatic navigation, or by pressing on an element with the mouse
/// </summary> /// </summary>
public Element.GenericCallback OnSelectedElementChanged; public event Element.GenericCallback OnSelectedElementChanged;
/// <summary> /// <summary>
/// Event that is invoked when a new <see cref="RootElement"/> is added to this ui system /// Event that is invoked when a new <see cref="RootElement"/> is added to this ui system
/// </summary> /// </summary>
public RootCallback OnRootAdded; public event RootCallback OnRootAdded;
/// <summary> /// <summary>
/// Event that is invoked when a <see cref="RootElement"/> is removed from this ui system /// Event that is invoked when a <see cref="RootElement"/> is removed from this ui system
/// </summary> /// </summary>
public RootCallback OnRootRemoved; public event RootCallback OnRootRemoved;
/// <summary> /// <summary>
/// Creates a new ui system with the given settings. /// Creates a new ui system with the given settings.
@ -172,6 +172,33 @@ namespace MLEM.Ui {
public UiSystem(Game game, UiStyle style, InputHandler inputHandler = null, bool automaticViewport = true) : base(game) { public UiSystem(Game game, UiStyle style, InputHandler inputHandler = null, bool automaticViewport = true) : base(game) {
this.Controls = new UiControls(this, inputHandler); this.Controls = new UiControls(this, inputHandler);
this.style = style; this.style = style;
this.OnElementDrawn += (e, time, batch, alpha) => e.OnDrawn?.Invoke(e, time, batch, alpha);
this.OnElementUpdated += (e, time) => e.OnUpdated?.Invoke(e, time);
this.OnElementPressed += e => e.OnPressed?.Invoke(e);
this.OnElementSecondaryPressed += e => e.OnSecondaryPressed?.Invoke(e);
this.OnElementSelected += e => e.OnSelected?.Invoke(e);
this.OnElementDeselected += e => e.OnDeselected?.Invoke(e);
this.OnElementMouseEnter += e => e.OnMouseEnter?.Invoke(e);
this.OnElementMouseExit += e => e.OnMouseExit?.Invoke(e);
this.OnElementTouchEnter += e => e.OnTouchEnter?.Invoke(e);
this.OnElementTouchExit += e => e.OnTouchExit?.Invoke(e);
this.OnElementAreaUpdated += e => e.OnAreaUpdated?.Invoke(e);
this.OnMousedElementChanged += e => this.ApplyToAll(t => t.OnMousedElementChanged?.Invoke(t, e));
this.OnTouchedElementChanged += e => this.ApplyToAll(t => t.OnTouchedElementChanged?.Invoke(t, e));
this.OnSelectedElementChanged += e => this.ApplyToAll(t => t.OnSelectedElementChanged?.Invoke(t, e));
this.OnSelectedElementDrawn += (element, time, batch, alpha) => {
if (this.Controls.IsAutoNavMode && element.SelectionIndicator.HasValue())
batch.Draw(element.SelectionIndicator, element.DisplayArea, Color.White * alpha, element.Scale / 2);
};
this.OnElementPressed += e => {
if (e.OnPressed != null)
e.ActionSound.Value?.Play();
};
this.OnElementSecondaryPressed += e => {
if (e.OnSecondaryPressed != null)
e.SecondActionSound.Value?.Play();
};
MlemPlatform.Current?.AddTextInputListener(game.Window, (sender, key, character) => this.ApplyToAll(e => e.OnTextInput?.Invoke(e, key, character)));
if (automaticViewport) { if (automaticViewport) {
this.Viewport = new Rectangle(Point.Zero, game.Window.ClientBounds.Size); this.Viewport = new Rectangle(Point.Zero, game.Window.ClientBounds.Size);
@ -183,25 +210,6 @@ namespace MLEM.Ui {
}; };
} }
if (MlemPlatform.Current != null)
MlemPlatform.Current.AddTextInputListener(game.Window, (sender, key, character) => this.ApplyToAll(e => e.OnTextInput?.Invoke(e, key, character)));
this.OnMousedElementChanged = e => this.ApplyToAll(t => t.OnMousedElementChanged?.Invoke(t, e));
this.OnTouchedElementChanged = e => this.ApplyToAll(t => t.OnTouchedElementChanged?.Invoke(t, e));
this.OnSelectedElementChanged = e => this.ApplyToAll(t => t.OnSelectedElementChanged?.Invoke(t, e));
this.OnSelectedElementDrawn = (element, time, batch, alpha) => {
if (this.Controls.IsAutoNavMode && element.SelectionIndicator.HasValue()) {
batch.Draw(element.SelectionIndicator, element.DisplayArea, Color.White * alpha, element.Scale / 2);
}
};
this.OnElementPressed += e => {
if (e.OnPressed != null)
e.ActionSound.Value?.Play();
};
this.OnElementSecondaryPressed += e => {
if (e.OnSecondaryPressed != null)
e.SecondActionSound.Value?.Play();
};
this.TextFormatter = new TextFormatter(); this.TextFormatter = new TextFormatter();
this.TextFormatter.Codes.Add(new Regex("<l(?: ([^>]+))?>"), (f, m, r) => new LinkCode(m, r, 1 / 16F, 0.85F, this.TextFormatter.Codes.Add(new Regex("<l(?: ([^>]+))?>"), (f, m, r) => new LinkCode(m, r, 1 / 16F, 0.85F,
t => this.Controls.MousedElement is Paragraph.Link l1 && l1.Token == t || this.Controls.TouchedElement is Paragraph.Link l2 && l2.Token == t)); t => this.Controls.MousedElement is Paragraph.Link l1 && l1.Token == t || this.Controls.TouchedElement is Paragraph.Link l2 && l2.Token == t));
@ -264,10 +272,11 @@ namespace MLEM.Ui {
root.Element.AndChildren(e => { root.Element.AndChildren(e => {
e.Root = root; e.Root = root;
e.System = this; e.System = this;
root.OnElementAdded(e); root.InvokeOnElementAdded(e);
e.SetAreaDirty(); e.SetAreaDirty();
}); });
this.OnRootAdded?.Invoke(root); this.OnRootAdded?.Invoke(root);
root.InvokeOnAddedToUi(this);
this.SortRoots(); this.SortRoots();
return root; return root;
} }
@ -285,10 +294,11 @@ namespace MLEM.Ui {
root.Element.AndChildren(e => { root.Element.AndChildren(e => {
e.Root = null; e.Root = null;
e.System = null; e.System = null;
root.OnElementRemoved(e); root.InvokeOnElementRemoved(e);
e.SetAreaDirty(); e.SetAreaDirty();
}); });
this.OnRootRemoved?.Invoke(root); this.OnRootRemoved?.Invoke(root);
root.InvokeOnRemovedFromUi(this);
} }
/// <summary> /// <summary>
@ -331,6 +341,22 @@ namespace MLEM.Ui {
root.Element.AndChildren(action); root.Element.AndChildren(action);
} }
internal void InvokeOnElementDrawn(Element element, GameTime time, SpriteBatch batch, float alpha) => this.OnElementDrawn?.Invoke(element, time, batch, alpha);
internal void InvokeOnSelectedElementDrawn(Element element, GameTime time, SpriteBatch batch, float alpha) => this.OnSelectedElementDrawn?.Invoke(element, time, batch, alpha);
internal void InvokeOnElementUpdated(Element element, GameTime time) => this.OnElementUpdated?.Invoke(element, time);
internal void InvokeOnElementAreaUpdated(Element element) => this.OnElementAreaUpdated?.Invoke(element);
internal void InvokeOnElementPressed(Element element) => this.OnElementPressed?.Invoke(element);
internal void InvokeOnElementSecondaryPressed(Element element) => this.OnElementSecondaryPressed?.Invoke(element);
internal void InvokeOnElementSelected(Element element) => this.OnElementSelected?.Invoke(element);
internal void InvokeOnElementDeselected(Element element) => this.OnElementDeselected?.Invoke(element);
internal void InvokeOnSelectedElementChanged(Element element) => this.OnSelectedElementChanged?.Invoke(element);
internal void InvokeOnElementMouseExit(Element element) => this.OnElementMouseExit?.Invoke(element);
internal void InvokeOnElementMouseEnter(Element element) => this.OnElementMouseEnter?.Invoke(element);
internal void InvokeOnMousedElementChanged(Element element) => this.OnMousedElementChanged?.Invoke(element);
internal void InvokeOnElementTouchExit(Element element) => this.OnElementTouchExit?.Invoke(element);
internal void InvokeOnElementTouchEnter(Element element) => this.OnElementTouchEnter?.Invoke(element);
internal void InvokeOnTouchedElementChanged(Element element) => this.OnTouchedElementChanged?.Invoke(element);
/// <summary> /// <summary>
/// A delegate used for callbacks that involve a <see cref="RootElement"/> /// A delegate used for callbacks that involve a <see cref="RootElement"/>
/// </summary> /// </summary>
@ -344,7 +370,7 @@ namespace MLEM.Ui {
/// Root elements are only used for the element in each element tree that doesn't have a <see cref="MLEM.Ui.Elements.Element.Parent"/> /// Root elements are only used for the element in each element tree that doesn't have a <see cref="MLEM.Ui.Elements.Element.Parent"/>
/// To create a new root element, use <see cref="UiSystem.Add"/> /// To create a new root element, use <see cref="UiSystem.Add"/>
/// </summary> /// </summary>
public class RootElement { public class RootElement : GenericDataHolder {
/// <summary> /// <summary>
/// The name of this root element /// The name of this root element
@ -415,11 +441,19 @@ namespace MLEM.Ui {
/// <summary> /// <summary>
/// Event that is invoked when a <see cref="Element"/> is added to this root element or any of its children. /// Event that is invoked when a <see cref="Element"/> is added to this root element or any of its children.
/// </summary> /// </summary>
public Element.GenericCallback OnElementAdded; public event Element.GenericCallback OnElementAdded;
/// <summary> /// <summary>
/// Even that is invoked when a <see cref="Element"/> is removed rom this root element of any of its children. /// Event that is invoked when a <see cref="Element"/> is removed rom this root element of any of its children.
/// </summary> /// </summary>
public Element.GenericCallback OnElementRemoved; public event Element.GenericCallback OnElementRemoved;
/// <summary>
/// Event that is invoked when this <see cref="RootElement"/> gets added to a <see cref="UiSystem"/> in <see cref="UiSystem.Add"/>
/// </summary>
public event Action<UiSystem> OnAddedToUi;
/// <summary>
/// Event that is invoked when this <see cref="RootElement"/> gets removed from a <see cref="UiSystem"/> in <see cref="UiSystem.Remove"/>
/// </summary>
public event Action<UiSystem> OnRemovedFromUi;
internal RootElement(string name, Element element, UiSystem system) { internal RootElement(string name, Element element, UiSystem system) {
this.Name = name; this.Name = name;
@ -455,5 +489,10 @@ namespace MLEM.Ui {
this.Transform = Matrix.CreateScale(scale, scale, 1) * Matrix.CreateTranslation(new Vector3((1 - scale) * (origin ?? this.Element.DisplayArea.Center), 0)); this.Transform = Matrix.CreateScale(scale, scale, 1) * Matrix.CreateTranslation(new Vector3((1 - scale) * (origin ?? this.Element.DisplayArea.Center), 0));
} }
internal void InvokeOnElementAdded(Element element) => this.OnElementAdded?.Invoke(element);
internal void InvokeOnElementRemoved(Element element) => this.OnElementRemoved?.Invoke(element);
internal void InvokeOnAddedToUi(UiSystem system) => this.OnAddedToUi?.Invoke(system);
internal void InvokeOnRemovedFromUi(UiSystem system) => this.OnRemovedFromUi?.Invoke(system);
} }
} }