From a02334a34c10c1b1678a9683b464fa7abef1a8f4 Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Wed, 9 Jun 2021 00:27:50 +0200 Subject: [PATCH] turned some non-event events into events and added Disposing event to ui elements --- MLEM.Startup/MlemGame.cs | 6 +-- MLEM.Ui/Elements/Element.cs | 36 +++++++++++----- MLEM.Ui/Elements/Panel.cs | 11 ++--- MLEM.Ui/UiControls.cs | 34 +++++++-------- MLEM.Ui/UiSystem.cs | 84 +++++++++++++++++++++++++------------ 5 files changed, 107 insertions(+), 64 deletions(-) diff --git a/MLEM.Startup/MlemGame.cs b/MLEM.Startup/MlemGame.cs index e2f0c7d..3a42e26 100644 --- a/MLEM.Startup/MlemGame.cs +++ b/MLEM.Startup/MlemGame.cs @@ -40,15 +40,15 @@ namespace MLEM.Startup { /// /// An event that is invoked in /// - public GenericCallback OnLoadContent; + public event GenericCallback OnLoadContent; /// /// An event that is invoked in /// - public TimeCallback OnUpdate; + public event TimeCallback OnUpdate; /// /// An event that is invoked in /// - public TimeCallback OnDraw; + public event TimeCallback OnDraw; /// /// Creates a new MlemGame instance with some default settings diff --git a/MLEM.Ui/Elements/Element.cs b/MLEM.Ui/Elements/Element.cs index c5ad4b5..a7b6925 100644 --- a/MLEM.Ui/Elements/Element.cs +++ b/MLEM.Ui/Elements/Element.cs @@ -15,7 +15,7 @@ namespace MLEM.Ui.Elements { /// /// This class represents a generic base class for ui elements of a . /// - public abstract class Element : GenericDataHolder { + public abstract class Element : GenericDataHolder, IDisposable { /// /// 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 /// public OtherElementCallback OnChildRemoved; + /// + /// Event that is called when this element's method is called, which also happens in . + /// This event is useful for unregistering global event handlers when this object should be destroyed. + /// + public GenericCallback OnDisposed; /// /// A style property that contains the selection indicator that is displayed on this element if it is the @@ -415,6 +420,11 @@ namespace MLEM.Ui.Elements { this.SetSortedChildrenDirty(); } + /// + ~Element() { + this.Dispose(); + } + /// /// Adds a child to this element. /// @@ -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 { /// /// The game's time 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 { /// The sampler state that is used for drawing /// The transformation matrix that is used for drawing 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; } + /// + public virtual void Dispose() { + this.OnDisposed?.Invoke(this); + GC.SuppressFinalize(this); + } + /// /// Performs the specified action on this element and all of its /// diff --git a/MLEM.Ui/Elements/Panel.cs b/MLEM.Ui/Elements/Panel.cs index 22ffa96..b6c23ca 100644 --- a/MLEM.Ui/Elements/Panel.cs +++ b/MLEM.Ui/Elements/Panel.cs @@ -15,7 +15,7 @@ namespace MLEM.Ui.Elements { /// Additionally, a panel can be set to on construction, which causes all elements that don't fit into the panel to be hidden until scrolled to using a . /// As this behavior is accomplished using a , scrolling panels need to have their methods called using . /// - public class Panel : Element, IDisposable { + public class Panel : Element { /// /// The texture that this panel should have, or null if it should be invisible. @@ -76,11 +76,6 @@ namespace MLEM.Ui.Elements { } } - /// - ~Panel() { - this.Dispose(); - } - /// public override void ForceUpdateArea() { if (this.scrollOverflow) { @@ -237,12 +232,12 @@ namespace MLEM.Ui.Elements { } /// - public void Dispose() { + public override void Dispose() { if (this.renderTarget != null) { this.renderTarget.Dispose(); this.renderTarget = null; } - GC.SuppressFinalize(this); + base.Dispose(); } } diff --git a/MLEM.Ui/UiControls.cs b/MLEM.Ui/UiControls.cs index 1b9adf7..9821353 100644 --- a/MLEM.Ui/UiControls.cs +++ b/MLEM.Ui/UiControls.cs @@ -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); } } diff --git a/MLEM.Ui/UiSystem.cs b/MLEM.Ui/UiSystem.cs index 6caceaa..764025d 100644 --- a/MLEM.Ui/UiSystem.cs +++ b/MLEM.Ui/UiSystem.cs @@ -96,71 +96,71 @@ namespace MLEM.Ui { /// /// Event that is invoked after an is drawn, but before its children are drawn. /// - public Element.DrawCallback OnElementDrawn = (e, time, batch, alpha) => e.OnDrawn?.Invoke(e, time, batch, alpha); + public event Element.DrawCallback OnElementDrawn; /// /// Event that is invoked after the for each root element is drawn, but before its children are drawn. /// - public Element.DrawCallback OnSelectedElementDrawn; + public event Element.DrawCallback OnSelectedElementDrawn; /// /// Event that is invoked when an is updated /// - public Element.TimeCallback OnElementUpdated = (e, time) => e.OnUpdated?.Invoke(e, time); + public event Element.TimeCallback OnElementUpdated; /// /// Event that is invoked when an is pressed with the primary action key /// - public Element.GenericCallback OnElementPressed = e => e.OnPressed?.Invoke(e); + public event Element.GenericCallback OnElementPressed; /// /// Event that is invoked when an is pressed with the secondary action key /// - public Element.GenericCallback OnElementSecondaryPressed = e => e.OnSecondaryPressed?.Invoke(e); + public event Element.GenericCallback OnElementSecondaryPressed; /// /// Event that is invoked when an is newly selected using automatic navigation, or after it has been pressed with the mouse. /// - public Element.GenericCallback OnElementSelected = e => e.OnSelected?.Invoke(e); + public event Element.GenericCallback OnElementSelected; /// /// Event that is invoked when an is deselected during the selection of a new element. /// - public Element.GenericCallback OnElementDeselected = e => e.OnDeselected?.Invoke(e); + public event Element.GenericCallback OnElementDeselected; /// /// Event that is invoked when the mouse enters an /// - public Element.GenericCallback OnElementMouseEnter = e => e.OnMouseEnter?.Invoke(e); + public event Element.GenericCallback OnElementMouseEnter; /// /// Event that is invoked when the mouse exits an /// - public Element.GenericCallback OnElementMouseExit = e => e.OnMouseExit?.Invoke(e); + public event Element.GenericCallback OnElementMouseExit; /// /// Event that is invoked when an starts being touched /// - public Element.GenericCallback OnElementTouchEnter = e => e.OnTouchEnter?.Invoke(e); + public event Element.GenericCallback OnElementTouchEnter; /// /// Event that is invoked when an stops being touched /// - public Element.GenericCallback OnElementTouchExit = e => e.OnTouchExit?.Invoke(e); + public event Element.GenericCallback OnElementTouchExit; /// /// Event that is invoked when an 's display area changes /// - public Element.GenericCallback OnElementAreaUpdated = e => e.OnAreaUpdated?.Invoke(e); + public event Element.GenericCallback OnElementAreaUpdated; /// /// Event that is invoked when the that the mouse is currently over changes /// - public Element.GenericCallback OnMousedElementChanged; + public event Element.GenericCallback OnMousedElementChanged; /// /// Event that is invoked when the that is being touched changes /// - public Element.GenericCallback OnTouchedElementChanged; + public event Element.GenericCallback OnTouchedElementChanged; /// /// Event that is invoked when the selected changes, either through automatic navigation, or by pressing on an element with the mouse /// - public Element.GenericCallback OnSelectedElementChanged; + public event Element.GenericCallback OnSelectedElementChanged; /// /// Event that is invoked when a new is added to this ui system /// - public RootCallback OnRootAdded; + public event RootCallback OnRootAdded; /// /// Event that is invoked when a is removed from this ui system /// - public RootCallback OnRootRemoved; + public event RootCallback OnRootRemoved; /// /// 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); } /// @@ -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); + /// /// A delegate used for callbacks that involve a /// @@ -346,7 +373,7 @@ namespace MLEM.Ui { /// Root elements are only used for the element in each element tree that doesn't have a /// To create a new root element, use /// - public class RootElement { + public class RootElement : GenericDataHolder { /// /// The name of this root element @@ -417,19 +444,19 @@ namespace MLEM.Ui { /// /// Event that is invoked when a is added to this root element or any of its children. /// - public Element.GenericCallback OnElementAdded; + public event Element.GenericCallback OnElementAdded; /// /// Event that is invoked when a is removed rom this root element of any of its children. /// - public Element.GenericCallback OnElementRemoved; + public event Element.GenericCallback OnElementRemoved; /// /// Event that is invoked when this gets added to a in /// - public Action OnAddedToUi; + public event Action OnAddedToUi; /// /// Event that is invoked when this gets removed from a in /// - public Action OnRemovedFromUi; + public event Action 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); + } } \ No newline at end of file