From a143aef67cbcc01f95be86741a728ecce4d9423f Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Sun, 30 Jan 2022 16:32:45 +0100 Subject: [PATCH] Revert scissor rectangle change since it doesn't support panels with complex transformations Revert "Use a scissor rectangle for panels in favor of a render target, and marked UiSystem.DrawEarly and Element.DrawEarly as obsolete" This reverts commit 3c4567e4a1fc7535837926e9f73f6dca1024edb7. Revert "cleaned up DrawEarly documentation references" This reverts commit dc6c472b84bee7c1e51abb1108efd9100b9de04c. --- CHANGELOG.md | 2 -- Docs/articles/ui.md | 5 ++- MLEM.Startup/MlemGame.cs | 1 + MLEM.Ui/Elements/Element.cs | 1 - MLEM.Ui/Elements/Panel.cs | 68 ++++++++++++++++++++++++++----------- MLEM.Ui/UiSystem.cs | 2 +- 6 files changed, 54 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bec1e8..cb6ffd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,14 +35,12 @@ Improvements - Avoid unnecessary panel updates by using an Epsilon comparison when scrolling children - Allow setting a default text alignment for paragraphs in UiStyle - Made custom values of Element.Style persist when a new ui style is set -- Use a scissor rectangle for panels instead of a render target Fixes - Fixed paragraph links having incorrect hover locations when using special text alignments Removals - Marked StyleProp equality members as obsolete -- Marked UiSystem.DrawEarly and Element.DrawEarly as obsolete ### MLEM.Extended Improvements diff --git a/Docs/articles/ui.md b/Docs/articles/ui.md index e39eded..296cffe 100644 --- a/Docs/articles/ui.md +++ b/Docs/articles/ui.md @@ -21,7 +21,10 @@ protected override void Update(GameTime gameTime) { this.UiSystem.Update(gameTime); } -protected override void Draw(GameTime gameTime) { +protected override void Draw(GameTime gameTime) { + // DrawEarly needs to be called before clearing your graphics context + this.UiSystem.DrawEarly(gameTime, this.SpriteBatch); + this.GraphicsDevice.Clear(Color.CornflowerBlue); // Do your regular game drawing here diff --git a/MLEM.Startup/MlemGame.cs b/MLEM.Startup/MlemGame.cs index 0989adb..ed83878 100644 --- a/MLEM.Startup/MlemGame.cs +++ b/MLEM.Startup/MlemGame.cs @@ -114,6 +114,7 @@ namespace MLEM.Startup { this.PreDraw?.Invoke(this, gameTime); CoroutineHandler.RaiseEvent(CoroutineEvents.PreDraw); + this.UiSystem.DrawEarly(gameTime, this.SpriteBatch); this.DoDraw(gameTime); this.UiSystem.Draw(gameTime, this.SpriteBatch); diff --git a/MLEM.Ui/Elements/Element.cs b/MLEM.Ui/Elements/Element.cs index 0699456..c5c30ab 100644 --- a/MLEM.Ui/Elements/Element.cs +++ b/MLEM.Ui/Elements/Element.cs @@ -985,7 +985,6 @@ namespace MLEM.Ui.Elements { /// The effect that is used for drawing /// The depth stencil state that is used for drawing /// The transformation matrix that is used for drawing - [Obsolete("DrawEarly has been deprecated. There is no replacement, and all drawing code should be placed in Draw.")] public virtual void DrawEarly(GameTime time, SpriteBatch batch, float alpha, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, Effect effect, Matrix matrix) { foreach (var child in this.GetRelevantChildren()) { if (!child.IsHidden) diff --git a/MLEM.Ui/Elements/Panel.cs b/MLEM.Ui/Elements/Panel.cs index 0554c54..c8914b4 100644 --- a/MLEM.Ui/Elements/Panel.cs +++ b/MLEM.Ui/Elements/Panel.cs @@ -13,15 +13,10 @@ namespace MLEM.Ui.Elements { /// A panel element to be used inside of a . /// The panel is a complex element that displays a box as a background to all of its child 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 { - private static readonly RasterizerState ScissorRasterizer = new RasterizerState { - // use the default cull mode from SpriteBatch, but with scissor test enabled - CullMode = CullMode.CullCounterClockwiseFace, - ScissorTestEnable = true - }; - /// /// The scroll bar that this panel contains. /// This is only nonnull if is true. @@ -56,6 +51,7 @@ namespace MLEM.Ui.Elements { private readonly List relevantChildren = new List(); private readonly bool scrollOverflow; + private RenderTarget2D renderTarget; private bool relevantChildrenDirty; private float scrollBarChildOffset; @@ -174,32 +170,44 @@ namespace MLEM.Ui.Elements { public override void Draw(GameTime time, SpriteBatch batch, float alpha, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, Effect effect, Matrix matrix) { if (this.Texture.HasValue()) batch.Draw(this.Texture, this.DisplayArea, this.DrawColor.OrDefault(Color.White) * alpha, this.Scale); - // if we handle overflow, draw using a scissor rectangle - if (this.scrollOverflow) { - batch.End(); - batch.GraphicsDevice.ScissorRectangle = (Rectangle) this.GetInnerArea(); - - // do the usual draw, but with the scissor rectangle applied - batch.Begin(SpriteSortMode.Deferred, blendState, samplerState, depthStencilState, ScissorRasterizer, effect, matrix); + // if we handle overflow, draw using the render target in DrawUnbound + if (!this.scrollOverflow || this.renderTarget == null) { base.Draw(time, batch, alpha, blendState, samplerState, depthStencilState, effect, matrix); - batch.End(); - - batch.Begin(SpriteSortMode.Deferred, blendState, samplerState, depthStencilState, null, effect, matrix); } else { - base.Draw(time, batch, alpha, blendState, samplerState, depthStencilState, effect, matrix); + // 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(), Color.White); } } + /// + public override void DrawEarly(GameTime time, SpriteBatch batch, float alpha, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, Effect effect, Matrix matrix) { + this.UpdateAreaIfDirty(); + if (this.scrollOverflow && this.renderTarget != null) { + // draw children onto the render target + using (batch.GraphicsDevice.WithRenderTarget(this.renderTarget)) { + batch.GraphicsDevice.Clear(Color.Transparent); + // offset children by the render target's location + var area = this.GetRenderTargetArea(); + var trans = Matrix.CreateTranslation(-area.X, -area.Y, 0); + // do the usual draw, but within the render target + batch.Begin(SpriteSortMode.Deferred, blendState, samplerState, depthStencilState, null, effect, trans); + base.Draw(time, batch, alpha, blendState, samplerState, depthStencilState, effect, trans); + batch.End(); + } + } + base.DrawEarly(time, batch, alpha, blendState, samplerState, depthStencilState, effect, matrix); + } + /// public override Element GetElementUnderPos(Vector2 position) { // if overflow is handled, don't propagate mouse checks to hidden children var transformed = this.TransformInverse(position); - if (this.scrollOverflow && !this.GetInnerArea().Contains(transformed)) + if (this.scrollOverflow && !this.GetRenderTargetArea().Contains(transformed)) return !this.IsHidden && this.CanBeMoused && this.DisplayArea.Contains(transformed) ? this : null; return base.GetElementUnderPos(position); } - private RectangleF GetInnerArea() { + private RectangleF GetRenderTargetArea() { var area = this.ChildPaddedArea; area.X = this.DisplayArea.X; area.Width = this.DisplayArea.Width; @@ -250,6 +258,26 @@ namespace MLEM.Ui.Elements { // the scroller height has the same relation to the scroll bar height as the visible area has to the total height of the panel's content var scrollerHeight = Math.Min(this.ChildPaddedArea.Height / childrenHeight / this.Scale, 1) * this.ScrollBar.Area.Height; this.ScrollBar.ScrollerSize = new Vector2(this.ScrollerSize.Value.X, Math.Max(this.ScrollerSize.Value.Y, scrollerHeight)); + + // update the render target + var targetArea = (Rectangle) this.GetRenderTargetArea(); + if (targetArea.Width <= 0 || targetArea.Height <= 0) + return; + if (this.renderTarget == null || targetArea.Width != this.renderTarget.Width || targetArea.Height != this.renderTarget.Height) { + if (this.renderTarget != null) + this.renderTarget.Dispose(); + this.renderTarget = targetArea.IsEmpty ? null : new RenderTarget2D(this.System.Game.GraphicsDevice, targetArea.Width, targetArea.Height); + this.relevantChildrenDirty = true; + } + } + + /// + public override void Dispose() { + if (this.renderTarget != null) { + this.renderTarget.Dispose(); + this.renderTarget = null; + } + base.Dispose(); } private void SetScrollBarStyle() { @@ -263,7 +291,7 @@ namespace MLEM.Ui.Elements { private void ForceUpdateRelevantChildren() { this.relevantChildrenDirty = false; this.relevantChildren.Clear(); - var visible = this.GetInnerArea(); + var visible = this.GetRenderTargetArea(); foreach (var child in this.SortedChildren) { if (child.Area.Intersects(visible)) { this.relevantChildren.Add(child); diff --git a/MLEM.Ui/UiSystem.cs b/MLEM.Ui/UiSystem.cs index 5063e82..5e4f72f 100644 --- a/MLEM.Ui/UiSystem.cs +++ b/MLEM.Ui/UiSystem.cs @@ -264,7 +264,6 @@ namespace MLEM.Ui { /// /// The game's time /// The sprite batch to use for drawing - [Obsolete("DrawEarly has been deprecated. There is no replacement, so only Draw has to be called.")] public void DrawEarly(GameTime time, SpriteBatch batch) { this.Metrics.ResetDraws(); this.Stopwatch.Restart(); @@ -281,6 +280,7 @@ namespace MLEM.Ui { /// /// Draws any s onto the screen. + /// Note that, when using s with a scroll bar, needs to be called as well. /// /// The game's time /// The sprite batch to use for drawing