turned some non-event events into events and added Disposing event to ui elements

This commit is contained in:
Ell 2021-06-09 00:27:50 +02:00
parent 2cc77f42cd
commit a02334a34c
5 changed files with 107 additions and 64 deletions

View File

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

View File

@ -15,7 +15,7 @@ namespace MLEM.Ui.Elements {
/// <summary>
/// This class represents a generic base class for ui elements of a <see cref="UiSystem"/>.
/// </summary>
public abstract class Element : GenericDataHolder {
public abstract class Element : GenericDataHolder, IDisposable {
/// <summary>
/// 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"/>
/// </summary>
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>
/// 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();
}
/// <inheritdoc />
~Element() {
this.Dispose();
}
/// <summary>
/// Adds a child to this element.
/// </summary>
@ -430,7 +440,7 @@ namespace MLEM.Ui.Elements {
element.AndChildren(e => {
e.Root = this.Root;
e.System = this.System;
this.Root?.OnElementAdded(e);
this.Root?.InvokeOnElementAdded(e);
this.OnChildAdded?.Invoke(this, e);
});
this.SetSortedChildrenDirty();
@ -451,7 +461,7 @@ namespace MLEM.Ui.Elements {
element.AndChildren(e => {
e.Root = null;
e.System = null;
this.Root?.OnElementRemoved(e);
this.Root?.InvokeOnElementRemoved(e);
this.OnChildRemoved?.Invoke(this, e);
});
this.SetSortedChildrenDirty();
@ -618,7 +628,7 @@ namespace MLEM.Ui.Elements {
}
this.area = new RectangleF(pos, newSize);
this.System.OnElementAreaUpdated?.Invoke(this);
this.System.InvokeOnElementAreaUpdated(this);
foreach (var child in this.Children)
child.ForceUpdateArea();
@ -626,7 +636,7 @@ namespace MLEM.Ui.Elements {
if (this.SetWidthBasedOnChildren || this.SetHeightBasedOnChildren) {
Element foundChild = null;
var autoSize = this.UnscrolledArea.Size;
if (this.SetHeightBasedOnChildren) {
var lowest = this.GetLowestChild(e => !e.IsHidden);
if (lowest != null) {
@ -638,7 +648,7 @@ namespace MLEM.Ui.Elements {
autoSize.Y = 0;
}
}
if (this.SetWidthBasedOnChildren) {
var rightmost = this.GetRightmostChild(e => !e.IsHidden);
if (rightmost != null) {
@ -650,7 +660,7 @@ namespace MLEM.Ui.Elements {
autoSize.X = 0;
}
}
if (this.TreatSizeAsMinimum)
autoSize = Vector2.Max(autoSize, actualSize);
if (!autoSize.Equals(this.UnscrolledArea.Size, 0.01F)) {
@ -837,7 +847,7 @@ namespace MLEM.Ui.Elements {
/// </summary>
/// <param name="time">The game's time</param>
public virtual void Update(GameTime time) {
this.System.OnElementUpdated?.Invoke(this, time);
this.System.InvokeOnElementUpdated(this, time);
foreach (var child in this.GetRelevantChildren())
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="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) {
this.System.OnElementDrawn?.Invoke(this, time, batch, alpha);
this.System.InvokeOnElementDrawn(this, time, batch, alpha);
if (this.IsSelected)
this.System.OnSelectedElementDrawn?.Invoke(this, time, batch, alpha);
this.System.InvokeOnSelectedElementDrawn(this, time, batch, alpha);
foreach (var child in this.GetRelevantChildren()) {
if (!child.IsHidden)
@ -935,6 +945,12 @@ namespace MLEM.Ui.Elements {
return this.CanBeMoused && this.DisplayArea.Contains(position) ? this : null;
}
/// <inheritdoc />
public virtual void Dispose() {
this.OnDisposed?.Invoke(this);
GC.SuppressFinalize(this);
}
/// <summary>
/// Performs the specified action on this element and all of its <see cref="Children"/>
/// </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"/>.
/// 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>
public class Panel : Element, IDisposable {
public class Panel : Element {
/// <summary>
/// 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 />
public override void ForceUpdateArea() {
if (this.scrollOverflow) {
@ -237,12 +232,12 @@ namespace MLEM.Ui.Elements {
}
/// <inheritdoc />
public void Dispose() {
public override void Dispose() {
if (this.renderTarget != null) {
this.renderTarget.Dispose();
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;
this.SelectElement(this.ActiveRoot, selectedNow);
if (mousedNow != null && mousedNow.CanBePressed)
this.System.OnElementPressed?.Invoke(mousedNow);
this.System.InvokeOnElementPressed(mousedNow);
} else if (this.Input.IsMouseButtonPressed(MouseButton.Right)) {
this.IsAutoNavMode = false;
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.Input.IsModifierKeyDown(ModifierKey.Shift)) {
// secondary action on element using space or enter
this.System.OnElementSecondaryPressed?.Invoke(this.SelectedElement);
this.System.InvokeOnElementSecondaryPressed(this.SelectedElement);
} else {
// 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)) {
@ -189,13 +189,13 @@ namespace MLEM.Ui {
var tapped = this.GetElementUnderPos(tap.Position);
this.SelectElement(this.ActiveRoot, tapped);
if (tapped != null && tapped.CanBePressed)
this.System.OnElementPressed?.Invoke(tapped);
this.System.InvokeOnElementPressed(tapped);
} else if (this.Input.GetGesture(GestureType.Hold, out var hold)) {
this.IsAutoNavMode = false;
var held = this.GetElementUnderPos(hold.Position);
this.SelectElement(this.ActiveRoot, held);
if (held != null && held.CanBePressed)
this.System.OnElementSecondaryPressed?.Invoke(held);
this.System.InvokeOnElementSecondaryPressed(held);
} else if (this.Input.TouchState.Count <= 0) {
this.SetTouchedElement(null);
} else {
@ -216,10 +216,10 @@ namespace MLEM.Ui {
if (this.HandleGamepad) {
if (this.GamepadButtons.IsPressed(this.Input, this.GamepadIndex)) {
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)) {
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)) {
this.HandleGamepadNextElement(Direction2.Down);
} else if (this.LeftButtons.IsPressed(this.Input, this.GamepadIndex)) {
@ -265,14 +265,14 @@ namespace MLEM.Ui {
return;
if (selected != null)
this.System.OnElementDeselected?.Invoke(selected);
this.System.InvokeOnElementDeselected(selected);
if (element != null) {
this.System.OnElementSelected?.Invoke(element);
this.System.InvokeOnElementSelected(element);
this.selectedElements[root.Name] = element;
} else {
this.selectedElements.Remove(root.Name);
}
this.System.OnSelectedElementChanged?.Invoke(element);
this.System.InvokeOnSelectedElementChanged(element);
if (autoNav != null)
this.IsAutoNavMode = autoNav.Value;
@ -285,11 +285,11 @@ namespace MLEM.Ui {
public void SetMousedElement(Element element) {
if (element != this.MousedElement) {
if (this.MousedElement != null)
this.System.OnElementMouseExit?.Invoke(this.MousedElement);
this.System.InvokeOnElementMouseExit(this.MousedElement);
if (element != null)
this.System.OnElementMouseEnter?.Invoke(element);
this.System.InvokeOnElementMouseEnter(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) {
if (element != this.TouchedElement) {
if (this.TouchedElement != null)
this.System.OnElementTouchExit?.Invoke(this.TouchedElement);
this.System.InvokeOnElementTouchExit(this.TouchedElement);
if (element != null)
this.System.OnElementTouchEnter?.Invoke(element);
this.System.InvokeOnElementTouchEnter(element);
this.TouchedElement = element;
this.System.OnTouchedElementChanged?.Invoke(element);
this.System.InvokeOnTouchedElementChanged(element);
}
}

View File

@ -96,71 +96,71 @@ namespace MLEM.Ui {
/// <summary>
/// Event that is invoked after an <see cref="Element"/> is drawn, but before its children are drawn.
/// </summary>
public Element.DrawCallback OnElementDrawn = (e, time, batch, alpha) => e.OnDrawn?.Invoke(e, time, batch, alpha);
public event Element.DrawCallback OnElementDrawn;
/// <summary>
/// Event that is invoked after the <see cref="RootElement.SelectedElement"/> for each root element is drawn, but before its children are drawn.
/// </summary>
public Element.DrawCallback OnSelectedElementDrawn;
public event Element.DrawCallback OnSelectedElementDrawn;
/// <summary>
/// Event that is invoked when an <see cref="Element"/> is updated
/// </summary>
public Element.TimeCallback OnElementUpdated = (e, time) => e.OnUpdated?.Invoke(e, time);
public event Element.TimeCallback OnElementUpdated;
/// <summary>
/// Event that is invoked when an <see cref="Element"/> is pressed with the primary action key
/// </summary>
public Element.GenericCallback OnElementPressed = e => e.OnPressed?.Invoke(e);
public event Element.GenericCallback OnElementPressed;
/// <summary>
/// Event that is invoked when an <see cref="Element"/> is pressed with the secondary action key
/// </summary>
public Element.GenericCallback OnElementSecondaryPressed = e => e.OnSecondaryPressed?.Invoke(e);
public event Element.GenericCallback OnElementSecondaryPressed;
/// <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.
/// </summary>
public Element.GenericCallback OnElementSelected = e => e.OnSelected?.Invoke(e);
public event Element.GenericCallback OnElementSelected;
/// <summary>
/// Event that is invoked when an <see cref="Element"/> is deselected during the selection of a new element.
/// </summary>
public Element.GenericCallback OnElementDeselected = e => e.OnDeselected?.Invoke(e);
public event Element.GenericCallback OnElementDeselected;
/// <summary>
/// Event that is invoked when the mouse enters an <see cref="Element"/>
/// </summary>
public Element.GenericCallback OnElementMouseEnter = e => e.OnMouseEnter?.Invoke(e);
public event Element.GenericCallback OnElementMouseEnter;
/// <summary>
/// Event that is invoked when the mouse exits an <see cref="Element"/>
/// </summary>
public Element.GenericCallback OnElementMouseExit = e => e.OnMouseExit?.Invoke(e);
public event Element.GenericCallback OnElementMouseExit;
/// <summary>
/// Event that is invoked when an <see cref="Element"/> starts being touched
/// </summary>
public Element.GenericCallback OnElementTouchEnter = e => e.OnTouchEnter?.Invoke(e);
public event Element.GenericCallback OnElementTouchEnter;
/// <summary>
/// Event that is invoked when an <see cref="Element"/> stops being touched
/// </summary>
public Element.GenericCallback OnElementTouchExit = e => e.OnTouchExit?.Invoke(e);
public event Element.GenericCallback OnElementTouchExit;
/// <summary>
/// Event that is invoked when an <see cref="Element"/>'s display area changes
/// </summary>
public Element.GenericCallback OnElementAreaUpdated = e => e.OnAreaUpdated?.Invoke(e);
public event Element.GenericCallback OnElementAreaUpdated;
/// <summary>
/// Event that is invoked when the <see cref="Element"/> that the mouse is currently over changes
/// </summary>
public Element.GenericCallback OnMousedElementChanged;
public event Element.GenericCallback OnMousedElementChanged;
/// <summary>
/// Event that is invoked when the <see cref="Element"/> that is being touched changes
/// </summary>
public Element.GenericCallback OnTouchedElementChanged;
public event Element.GenericCallback OnTouchedElementChanged;
/// <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
/// </summary>
public Element.GenericCallback OnSelectedElementChanged;
public event Element.GenericCallback OnSelectedElementChanged;
/// <summary>
/// Event that is invoked when a new <see cref="RootElement"/> is added to this ui system
/// </summary>
public RootCallback OnRootAdded;
public event RootCallback OnRootAdded;
/// <summary>
/// Event that is invoked when a <see cref="RootElement"/> is removed from this ui system
/// </summary>
public RootCallback OnRootRemoved;
public event RootCallback OnRootRemoved;
/// <summary>
/// Creates a new ui system with the given settings.
@ -172,6 +172,17 @@ namespace MLEM.Ui {
public UiSystem(Game game, UiStyle style, InputHandler inputHandler = null, bool automaticViewport = true) : base(game) {
this.Controls = new UiControls(this, inputHandler);
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);
if (automaticViewport) {
this.Viewport = new Rectangle(Point.Zero, game.Window.ClientBounds.Size);
@ -264,11 +275,11 @@ namespace MLEM.Ui {
root.Element.AndChildren(e => {
e.Root = root;
e.System = this;
root.OnElementAdded(e);
root.InvokeOnElementAdded(e);
e.SetAreaDirty();
});
this.OnRootAdded?.Invoke(root);
root.OnAddedToUi?.Invoke(this);
root.InvokeOnAddedToUi(this);
this.SortRoots();
return root;
}
@ -286,11 +297,11 @@ namespace MLEM.Ui {
root.Element.AndChildren(e => {
e.Root = null;
e.System = null;
root.OnElementRemoved(e);
root.InvokeOnElementRemoved(e);
e.SetAreaDirty();
});
this.OnRootRemoved?.Invoke(root);
root.OnRemovedFromUi?.Invoke(this);
root.InvokeOnRemovedFromUi(this);
}
/// <summary>
@ -333,6 +344,22 @@ namespace MLEM.Ui {
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>
/// A delegate used for callbacks that involve a <see cref="RootElement"/>
/// </summary>
@ -346,7 +373,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"/>
/// To create a new root element, use <see cref="UiSystem.Add"/>
/// </summary>
public class RootElement {
public class RootElement : GenericDataHolder {
/// <summary>
/// The name of this root element
@ -417,19 +444,19 @@ namespace MLEM.Ui {
/// <summary>
/// Event that is invoked when a <see cref="Element"/> is added to this root element or any of its children.
/// </summary>
public Element.GenericCallback OnElementAdded;
public event Element.GenericCallback OnElementAdded;
/// <summary>
/// Event that is invoked when a <see cref="Element"/> is removed rom this root element of any of its children.
/// </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 Action<UiSystem> OnAddedToUi;
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 Action<UiSystem> OnRemovedFromUi;
public event Action<UiSystem> OnRemovedFromUi;
internal RootElement(string name, Element element, UiSystem system) {
this.Name = name;
@ -465,5 +492,10 @@ namespace MLEM.Ui {
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);
}
}