From 4624219b4ea7292102c1ad4da93693c44b217b65 Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Mon, 12 Aug 2019 19:44:16 +0200 Subject: [PATCH] added scroll bar and overflow handling panel --- Examples/GameImpl.cs | 7 +- MLEM.Ui/Elements/AutoScaledText.cs | 6 +- MLEM.Ui/Elements/Button.cs | 6 +- MLEM.Ui/Elements/Element.cs | 55 ++++++++------- MLEM.Ui/Elements/Image.cs | 6 +- MLEM.Ui/Elements/Panel.cs | 103 ++++++++++++++++++++++++++-- MLEM.Ui/Elements/Paragraph.cs | 13 ++-- MLEM.Ui/Elements/ScrollBar.cs | 71 +++++++++++++++++++ MLEM.Ui/Elements/TextField.cs | 9 +-- MLEM.Ui/Style/UiStyle.cs | 3 + MLEM.Ui/Style/UntexturedStyle.cs | 3 + MLEM.Ui/UiSystem.cs | 17 +++-- MLEM/Extensions/NumberExtensions.cs | 6 ++ 13 files changed, 245 insertions(+), 60 deletions(-) create mode 100644 MLEM.Ui/Elements/ScrollBar.cs diff --git a/Examples/GameImpl.cs b/Examples/GameImpl.cs index 2fd71d7..a7130eb 100644 --- a/Examples/GameImpl.cs +++ b/Examples/GameImpl.cs @@ -26,7 +26,7 @@ namespace Examples { this.testPatch = new NinePatch(new TextureRegion(this.testTexture, 0, 8, 24, 24), 8); base.LoadContent(); - var style = new UiStyle { + var style = new UntexturedStyle(this.SpriteBatch) { Font = new GenericSpriteFont(LoadContent("Fonts/TestFont")), TextScale = 0.2F, PanelTexture = this.testPatch, @@ -39,7 +39,7 @@ namespace Examples { this.UiSystem.Style = style; this.UiSystem.GlobalScale = 5; - var root = new Panel(Anchor.Center, new Vector2(100, 120), Point.Zero, true); + var root = new Panel(Anchor.Center, new Vector2(100, 80), Point.Zero, false, true, new Point(5, 10)); this.UiSystem.Add("Test", root); root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "This is a test text that is hopefully long enough to cover at least a few lines, otherwise it would be very sad.")); @@ -53,7 +53,7 @@ namespace Examples { root.AddChild(new Button(Anchor.AutoCenter, new Vector2(1, 15), "Change Style") { OnClicked = (element, button) => { if (button == MouseButton.Left) - this.UiSystem.Style = this.UiSystem.Style is UntexturedStyle ? style : untexturedStyle; + this.UiSystem.Style = this.UiSystem.Style == untexturedStyle ? style : untexturedStyle; }, HasCustomStyle = true, Texture = this.testPatch, @@ -75,6 +75,7 @@ namespace Examples { } }); root.AddChild(new Button(Anchor.AutoInline, new Vector2(30, 15), "Woop") { + PositionOffset = new Point(2, 0), OnClicked = (element, button) => CoroutineHandler.Start(Woop(element)) }); } diff --git a/MLEM.Ui/Elements/AutoScaledText.cs b/MLEM.Ui/Elements/AutoScaledText.cs index 0c08f5a..95aa599 100644 --- a/MLEM.Ui/Elements/AutoScaledText.cs +++ b/MLEM.Ui/Elements/AutoScaledText.cs @@ -38,10 +38,10 @@ namespace MLEM.Ui.Elements { } while (measure.X <= this.DisplayArea.Size.X / this.Scale && measure.Y <= this.DisplayArea.Size.Y / this.Scale); } - public override void Draw(GameTime time, SpriteBatch batch, float alpha) { - var pos = this.DisplayArea.Location.ToVector2() + this.DisplayArea.Size.ToVector2() / 2; + public override void Draw(GameTime time, SpriteBatch batch, float alpha, Point offset) { + var pos = this.DisplayArea.Location.ToVector2() + this.DisplayArea.Size.ToVector2() / 2 + offset.ToVector2(); this.font.DrawCenteredString(batch, this.Text, pos, this.textScale * this.Scale, this.Color * alpha, true, true); - base.Draw(time, batch, alpha); + base.Draw(time, batch, alpha, offset); } protected override void InitStyle(UiStyle style) { diff --git a/MLEM.Ui/Elements/Button.cs b/MLEM.Ui/Elements/Button.cs index 2e0dd55..b36e8b7 100644 --- a/MLEM.Ui/Elements/Button.cs +++ b/MLEM.Ui/Elements/Button.cs @@ -21,7 +21,7 @@ namespace MLEM.Ui.Elements { } } - public override void Draw(GameTime time, SpriteBatch batch, float alpha) { + public override void Draw(GameTime time, SpriteBatch batch, float alpha, Point offset) { var tex = this.Texture; var color = Color.White * alpha; if (this.IsMouseOver) { @@ -29,8 +29,8 @@ namespace MLEM.Ui.Elements { tex = this.HoveredTexture; color = this.HoveredColor * alpha; } - batch.Draw(tex, this.DisplayArea, color, this.Scale); - base.Draw(time, batch, alpha); + batch.Draw(tex, this.DisplayArea.OffsetCopy(offset), color, this.Scale); + base.Draw(time, batch, alpha, offset); } protected override void InitStyle(UiStyle style) { diff --git a/MLEM.Ui/Elements/Element.cs b/MLEM.Ui/Elements/Element.cs index bdb96fc..2b83e55 100644 --- a/MLEM.Ui/Elements/Element.cs +++ b/MLEM.Ui/Elements/Element.cs @@ -63,6 +63,15 @@ namespace MLEM.Ui.Elements { this.SetAreaDirty(); } } + public Rectangle ChildPaddedArea { + get { + var padded = this.Area; + padded.Location += this.ScaledChildPadding; + padded.Width -= this.ScaledChildPadding.X * 2; + padded.Height -= this.ScaledChildPadding.Y * 2; + return padded; + } + } public Point ScaledChildPadding => this.childPadding.Multiply(this.Scale); public MouseClickCallback OnClicked; @@ -143,38 +152,42 @@ namespace MLEM.Ui.Elements { this.SetAreaDirty(); } - public T AddChild(T element, int index = -1) where T : Element { + public T AddChild(T element, int index = -1, bool propagateInfo = true) where T : Element { if (index < 0 || index > this.Children.Count) index = this.Children.Count; this.Children.Insert(index, element); - element.Parent = this; - element.PropagateRoot(this.Root); - element.PropagateUiSystem(this.System); + if (propagateInfo) { + element.Parent = this; + element.PropagateRoot(this.Root); + element.PropagateUiSystem(this.System); + } this.SetSortedChildrenDirty(); this.SetAreaDirty(); return element; } - public void RemoveChild(Element element) { + public void RemoveChild(Element element, bool propagateInfo = true) { this.Children.Remove(element); - element.Parent = null; - element.PropagateRoot(null); - element.PropagateUiSystem(null); + if (propagateInfo) { + element.Parent = null; + element.PropagateRoot(null); + element.PropagateUiSystem(null); + } this.SetSortedChildrenDirty(); this.SetAreaDirty(); } public void MoveToFront() { if (this.Parent != null) { - this.Parent.RemoveChild(this); - this.Parent.AddChild(this); + this.Parent.RemoveChild(this, false); + this.Parent.AddChild(this, -1, false); } } public void MoveToBack() { if (this.Parent != null) { - this.Parent.RemoveChild(this); - this.Parent.AddChild(this, 0); + this.Parent.RemoveChild(this, false); + this.Parent.AddChild(this, 0, false); } } @@ -206,15 +219,7 @@ namespace MLEM.Ui.Elements { public virtual void ForceUpdateArea() { this.areaDirty = false; - Rectangle parentArea; - if (this.Parent != null) { - parentArea = this.Parent.area; - parentArea.Location += this.Parent.ScaledChildPadding; - parentArea.Width -= this.Parent.ScaledChildPadding.X * 2; - parentArea.Height -= this.Parent.ScaledChildPadding.Y * 2; - } else { - parentArea = this.system.GraphicsDevice.Viewport.Bounds; - } + var parentArea = this.Parent != null ? this.Parent.ChildPaddedArea : this.system.Viewport; var parentCenterX = parentArea.X + parentArea.Width / 2; var parentCenterY = parentArea.Y + parentArea.Height / 2; @@ -338,17 +343,17 @@ namespace MLEM.Ui.Elements { child.Update(time); } - public virtual void Draw(GameTime time, SpriteBatch batch, float alpha) { + public virtual void Draw(GameTime time, SpriteBatch batch, float alpha, Point offset) { foreach (var child in this.SortedChildren) { if (!child.IsHidden) - child.Draw(time, batch, alpha * child.DrawAlpha); + child.Draw(time, batch, alpha * child.DrawAlpha, offset); } } - public virtual void DrawUnbound(GameTime time, SpriteBatch batch, float alpha, float scale, BlendState blendState = null, SamplerState samplerState = null) { + public virtual void DrawUnbound(GameTime time, SpriteBatch batch, float alpha, BlendState blendState = null, SamplerState samplerState = null) { foreach (var child in this.SortedChildren) { if (!child.IsHidden) - child.DrawUnbound(time, batch, alpha * child.DrawAlpha, scale, blendState, samplerState); + child.DrawUnbound(time, batch, alpha * child.DrawAlpha, blendState, samplerState); } } diff --git a/MLEM.Ui/Elements/Image.cs b/MLEM.Ui/Elements/Image.cs index 9c0ef5e..55df7f7 100644 --- a/MLEM.Ui/Elements/Image.cs +++ b/MLEM.Ui/Elements/Image.cs @@ -19,9 +19,9 @@ namespace MLEM.Ui.Elements { return this.scaleToImage ? this.texture.Size : base.CalcActualSize(parentArea); } - public override void Draw(GameTime time, SpriteBatch batch, float alpha) { - batch.Draw(this.texture, this.DisplayArea, this.Color * alpha); - base.Draw(time, batch, alpha); + public override void Draw(GameTime time, SpriteBatch batch, float alpha, Point offset) { + batch.Draw(this.texture, this.DisplayArea.OffsetCopy(offset), this.Color * alpha); + base.Draw(time, batch, alpha, offset); } } diff --git a/MLEM.Ui/Elements/Panel.cs b/MLEM.Ui/Elements/Panel.cs index fbc7e98..25e0eb3 100644 --- a/MLEM.Ui/Elements/Panel.cs +++ b/MLEM.Ui/Elements/Panel.cs @@ -1,5 +1,8 @@ +using System; +using System.Diagnostics; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; +using MLEM.Extensions; using MLEM.Textures; using MLEM.Ui.Style; @@ -7,17 +10,107 @@ namespace MLEM.Ui.Elements { public class Panel : Element { public NinePatch Texture; + public readonly ScrollBar ScrollBar; + private readonly bool scrollOverflow; + private RenderTarget2D renderTarget; - public Panel(Anchor anchor, Vector2 size, Point positionOffset, bool setHeightBasedOnChildren = false, NinePatch texture = null) : base(anchor, size) { - this.Texture = texture; + public Panel(Anchor anchor, Vector2 size, Point positionOffset, bool setHeightBasedOnChildren = false, bool scrollOverflow = false, Point? scrollerSize = null) : base(anchor, size) { this.PositionOffset = positionOffset; this.SetHeightBasedOnChildren = setHeightBasedOnChildren; + this.scrollOverflow = scrollOverflow; this.ChildPadding = new Point(5); + + if (scrollOverflow) { + var scrollSize = scrollerSize ?? Point.Zero; + this.ScrollBar = new ScrollBar(Anchor.TopRight, new Vector2(scrollSize.X, 1), scrollSize.Y, 0) { + StepPerScroll = 10, + OnValueChanged = (element, value) => { + var firstChild = this.Children[0]; + // if the first child is the scrollbar, there are no other children + if (firstChild == element) + return; + // as all children have to be auto-aligned, moving the first one up will move all others + firstChild.PositionOffset = new Point(firstChild.PositionOffset.X, -value.Ceil()); + this.ForceUpdateArea(); + } + }; + this.AddChild(this.ScrollBar); + + // modify the padding so that the scroll bar isn't over top of something else + this.ScrollBar.PositionOffset -= new Point(scrollSize.X + 1, 0); + this.ChildPadding += new Point(scrollSize.X, 0); + } } - public override void Draw(GameTime time, SpriteBatch batch, float alpha) { - batch.Draw(this.Texture, this.DisplayArea, Color.White * alpha, this.Scale); - base.Draw(time, batch, alpha); + public override void ForceUpdateArea() { + if (this.scrollOverflow) { + // sanity check + if (this.SetHeightBasedOnChildren) + throw new NotSupportedException("A panel can't both set height based on children and scroll overflow"); + foreach (var child in this.Children) { + if (child != this.ScrollBar && child.Anchor < Anchor.AutoLeft) + throw new NotSupportedException($"A panel that handles overflow can't contain non-automatic anchors ({child})"); + if (child is Panel panel && panel.scrollOverflow) + throw new NotSupportedException($"A panel that scrolls overflow cannot contain another panel that scrolls overflow ({child})"); + } + + // move the scrollbar to the front so it isn't used for auto-aligning + this.ScrollBar.MoveToFront(); + } + + base.ForceUpdateArea(); + + if (this.scrollOverflow) { + var firstChild = this.Children[0]; + // if the first child is the scrollbar, then we know there's no other children + if (firstChild == this.ScrollBar) + return; + var lastChild = this.Children[this.Children.Count - 2]; + // the max value of the scrollbar is the amount of non-scaled pixels taken up by overflowing components + var childrenHeight = lastChild.Area.Bottom - firstChild.Area.Top; + this.ScrollBar.MaxValue = (childrenHeight - this.Area.Height) / this.Scale + this.ChildPadding.Y * 2; + + // update the render target + var targetArea = this.GetRenderTargetArea(); + if (this.renderTarget == null || targetArea.Width != this.renderTarget.Width || targetArea.Height != this.renderTarget.Height) { + if (this.renderTarget != null) + this.renderTarget.Dispose(); + this.renderTarget = new RenderTarget2D(this.System.GraphicsDevice, targetArea.Width, targetArea.Height); + } + } + } + + public override void Draw(GameTime time, SpriteBatch batch, float alpha, Point offset) { + batch.Draw(this.Texture, this.DisplayArea.OffsetCopy(offset), Color.White * alpha, this.Scale); + // if we handle overflow, draw using the render target in DrawUnbound + if (!this.scrollOverflow) { + base.Draw(time, batch, alpha, offset); + } else { + // draw the actual render target (don't apply the alpha here because it's already drawn onto with alpha) + batch.Draw(this.renderTarget, this.GetRenderTargetArea().OffsetCopy(offset), Color.White); + } + } + + public override void DrawUnbound(GameTime time, SpriteBatch batch, float alpha, BlendState blendState = null, SamplerState samplerState = null) { + if (this.scrollOverflow) { + // draw children onto the render target + batch.GraphicsDevice.SetRenderTarget(this.renderTarget); + batch.GraphicsDevice.Clear(Color.Transparent); + batch.Begin(SpriteSortMode.Deferred, blendState, samplerState); + // offset children by the render target's location + var area = this.GetRenderTargetArea(); + base.Draw(time, batch, alpha, new Point(-area.X, -area.Y)); + batch.End(); + batch.GraphicsDevice.SetRenderTarget(null); + } + base.DrawUnbound(time, batch, alpha, blendState, samplerState); + } + + private Rectangle GetRenderTargetArea() { + var area = this.ChildPaddedArea; + area.X = this.DisplayArea.X; + area.Width = this.DisplayArea.Width; + return area; } protected override void InitStyle(UiStyle style) { diff --git a/MLEM.Ui/Elements/Paragraph.cs b/MLEM.Ui/Elements/Paragraph.cs index d4542e9..8f25202 100644 --- a/MLEM.Ui/Elements/Paragraph.cs +++ b/MLEM.Ui/Elements/Paragraph.cs @@ -46,19 +46,18 @@ namespace MLEM.Ui.Elements { return new Point(size.X, height.Ceil()); } - public override void Draw(GameTime time, SpriteBatch batch, float alpha) { - base.Draw(time, batch, alpha); - + public override void Draw(GameTime time, SpriteBatch batch, float alpha, Point offset) { var pos = this.DisplayArea.Location.ToVector2(); - var offset = new Vector2(); + var off = offset.ToVector2(); foreach (var line in this.splitText) { if (this.centerText) { - this.font.DrawCenteredString(batch, line, pos + offset + new Vector2(this.DisplayArea.Width / 2, 0), this.TextScale * this.Scale, Color.White * alpha); + this.font.DrawCenteredString(batch, line, pos + off + new Vector2(this.DisplayArea.Width / 2, 0), this.TextScale * this.Scale, Color.White * alpha); } else { - this.font.DrawString(batch, line, pos + offset, Color.White * alpha, 0, Vector2.Zero, this.TextScale * this.Scale, SpriteEffects.None, 0); + this.font.DrawString(batch, line, pos + off, Color.White * alpha, 0, Vector2.Zero, this.TextScale * this.Scale, SpriteEffects.None, 0); } - offset.Y += this.lineHeight + 1; + off.Y += this.lineHeight + 1; } + base.Draw(time, batch, alpha, offset); } protected override void InitStyle(UiStyle style) { diff --git a/MLEM.Ui/Elements/ScrollBar.cs b/MLEM.Ui/Elements/ScrollBar.cs new file mode 100644 index 0000000..8121f23 --- /dev/null +++ b/MLEM.Ui/Elements/ScrollBar.cs @@ -0,0 +1,71 @@ +using System; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using MLEM.Extensions; +using MLEM.Textures; +using MLEM.Ui.Style; + +namespace MLEM.Ui.Elements { + public class ScrollBar : Element { + + public NinePatch Background; + public NinePatch ScrollerTexture; + public Color HoveredColor; + public Point ScrollerOffset; + public Point ScrollerSize; + private float maxValue; + public float MaxValue { + get => this.maxValue; + set { + this.maxValue = Math.Max(0, value); + // force current value to be clamped + this.CurrentValue = this.currValue; + } + } + private float currValue; + public float CurrentValue { + get => this.currValue; + set { + var val = MathHelper.Clamp(value, 0, this.maxValue); + if (this.currValue != val) { + this.currValue = val; + this.OnValueChanged?.Invoke(this, val); + } + } + } + public float StepPerScroll = 1; + public ValueChanged OnValueChanged; + + public ScrollBar(Anchor anchor, Vector2 size, int scrollerHeight, float maxValue) : base(anchor, size) { + this.maxValue = maxValue; + this.ScrollerSize = new Point(size.X.Floor(), scrollerHeight); + } + + public override void Update(GameTime time) { + base.Update(time); + var scroll = this.Input.LastScrollWheel - this.Input.ScrollWheel; + if (scroll != 0) + this.CurrentValue += this.StepPerScroll * Math.Sign(scroll); + } + + public override void Draw(GameTime time, SpriteBatch batch, float alpha, Point offset) { + batch.Draw(this.Background, this.DisplayArea.OffsetCopy(offset), Color.White * alpha, this.Scale); + + var scrollerPos = new Point(this.DisplayArea.X + offset.X + this.ScrollerOffset.X, this.DisplayArea.Y + offset.Y + this.ScrollerOffset.Y); + var scrollerYOffset = (this.currValue / this.maxValue * (this.DisplayArea.Height - this.ScrollerSize.Y * this.Scale)).Floor(); + var scrollerRect = new Rectangle(scrollerPos + new Point(0, scrollerYOffset), this.ScrollerSize.Multiply(this.Scale)); + batch.Draw(this.ScrollerTexture, scrollerRect, Color.White * alpha, this.Scale); + base.Draw(time, batch, alpha, offset); + } + + protected override void InitStyle(UiStyle style) { + base.InitStyle(style); + this.Background = style.ScrollBarBackground; + this.ScrollerTexture = style.ScrollBarScrollerTexture; + this.HoveredColor = style.ScrollBarHoveredColor; + } + + public delegate void ValueChanged(Element element, float value); + + } +} \ No newline at end of file diff --git a/MLEM.Ui/Elements/TextField.cs b/MLEM.Ui/Elements/TextField.cs index 52e8eda..a0b3a67 100644 --- a/MLEM.Ui/Elements/TextField.cs +++ b/MLEM.Ui/Elements/TextField.cs @@ -3,6 +3,7 @@ using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; +using MLEM.Extensions; using MLEM.Font; using MLEM.Textures; using MLEM.Ui.Style; @@ -67,7 +68,7 @@ namespace MLEM.Ui.Elements { this.caretBlinkTimer = 0; } - public override void Draw(GameTime time, SpriteBatch batch, float alpha) { + public override void Draw(GameTime time, SpriteBatch batch, float alpha, Point offset) { var tex = this.Texture; var color = Color.White * alpha; if (this.IsMouseOver) { @@ -75,11 +76,11 @@ namespace MLEM.Ui.Elements { tex = this.HoveredTexture; color = this.HoveredColor * alpha; } - batch.Draw(tex, this.DisplayArea, color, this.Scale); + batch.Draw(tex, this.DisplayArea.OffsetCopy(offset), color, this.Scale); var caret = this.IsSelected && this.caretBlinkTimer >= 0.5F ? "|" : ""; var text = this.Text.ToString(this.textStartIndex, this.Text.Length - this.textStartIndex) + caret; - this.font.DrawCenteredString(batch, text, this.DisplayArea.Location.ToVector2() + new Vector2(this.TextOffsetX * this.Scale, this.DisplayArea.Height / 2), this.TextScale * this.Scale, Color.White * alpha, false, true); - base.Draw(time, batch, alpha); + this.font.DrawCenteredString(batch, text, this.DisplayArea.Location.ToVector2() + new Vector2(offset.X + this.TextOffsetX * this.Scale, offset.Y + this.DisplayArea.Height / 2), this.TextScale * this.Scale, Color.White * alpha, false, true); + base.Draw(time, batch, alpha, offset); } protected override void InitStyle(UiStyle style) { diff --git a/MLEM.Ui/Style/UiStyle.cs b/MLEM.Ui/Style/UiStyle.cs index 682180e..7abd92b 100644 --- a/MLEM.Ui/Style/UiStyle.cs +++ b/MLEM.Ui/Style/UiStyle.cs @@ -12,6 +12,9 @@ namespace MLEM.Ui.Style { public NinePatch TextFieldTexture; public NinePatch TextFieldHoveredTexture; public Color TextFieldHoveredColor; + public NinePatch ScrollBarBackground; + public NinePatch ScrollBarScrollerTexture; + public Color ScrollBarHoveredColor; public IGenericFont Font; public float TextScale = 1; diff --git a/MLEM.Ui/Style/UntexturedStyle.cs b/MLEM.Ui/Style/UntexturedStyle.cs index 007c648..c77f081 100644 --- a/MLEM.Ui/Style/UntexturedStyle.cs +++ b/MLEM.Ui/Style/UntexturedStyle.cs @@ -14,6 +14,9 @@ namespace MLEM.Ui.Style { this.PanelTexture = GenerateTexture(batch, Color.Gray); this.TextFieldTexture = GenerateTexture(batch, Color.MediumBlue); this.TextFieldHoveredColor = Color.LightGray; + this.ScrollBarBackground = GenerateTexture(batch, Color.LightBlue); + this.ScrollBarScrollerTexture = GenerateTexture(batch, Color.Blue); + this.ScrollBarHoveredColor = Color.LightGray; this.Font = new EmptyFont(); } diff --git a/MLEM.Ui/UiSystem.cs b/MLEM.Ui/UiSystem.cs index beaf54f..35fbd2d 100644 --- a/MLEM.Ui/UiSystem.cs +++ b/MLEM.Ui/UiSystem.cs @@ -12,6 +12,7 @@ namespace MLEM.Ui { public class UiSystem { public readonly GraphicsDevice GraphicsDevice; + public Rectangle Viewport { get; private set; } private readonly List rootElements = new List(); public readonly InputHandler InputHandler; private readonly bool isInputOurs; @@ -47,8 +48,10 @@ namespace MLEM.Ui { this.InputHandler = inputHandler ?? new InputHandler(); this.isInputOurs = inputHandler == null; this.style = style; + this.Viewport = device.Viewport.Bounds; window.ClientSizeChanged += (sender, args) => { + this.Viewport = device.Viewport.Bounds; foreach (var root in this.rootElements) root.Element.ForceUpdateArea(); }; @@ -92,16 +95,16 @@ namespace MLEM.Ui { public void Draw(GameTime time, SpriteBatch batch) { foreach (var root in this.rootElements) { - if (root.Element.IsHidden) - continue; - batch.Begin(SpriteSortMode.Deferred, this.BlendState, this.SamplerState); - root.Element.Draw(time, batch, this.DrawAlpha * root.Element.DrawAlpha); - batch.End(); + if (!root.Element.IsHidden) + root.Element.DrawUnbound(time, batch, this.DrawAlpha * root.Element.DrawAlpha, this.BlendState, this.SamplerState); } foreach (var root in this.rootElements) { - if (!root.Element.IsHidden) - root.Element.DrawUnbound(time, batch, this.DrawAlpha * root.Element.DrawAlpha, root.ActualScale, this.BlendState, this.SamplerState); + if (root.Element.IsHidden) + continue; + batch.Begin(SpriteSortMode.Deferred, this.BlendState, this.SamplerState); + root.Element.Draw(time, batch, this.DrawAlpha * root.Element.DrawAlpha, Point.Zero); + batch.End(); } } diff --git a/MLEM/Extensions/NumberExtensions.cs b/MLEM/Extensions/NumberExtensions.cs index 73a842f..b627d2b 100644 --- a/MLEM/Extensions/NumberExtensions.cs +++ b/MLEM/Extensions/NumberExtensions.cs @@ -28,5 +28,11 @@ namespace MLEM.Extensions { return new Point((point.X * f).Floor(), (point.Y * f).Floor()); } + public static Rectangle OffsetCopy(this Rectangle rect, Point offset) { + rect.X += offset.X; + rect.Y += offset.Y; + return rect; + } + } } \ No newline at end of file