diff --git a/CHANGELOG.md b/CHANGELOG.md
index cb6ffd1..390a94c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -35,12 +35,14 @@ 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 in favor 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/MLEM.Startup/MlemGame.cs b/MLEM.Startup/MlemGame.cs
index ed83878..0989adb 100644
--- a/MLEM.Startup/MlemGame.cs
+++ b/MLEM.Startup/MlemGame.cs
@@ -114,7 +114,6 @@ 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 221084c..0699456 100644
--- a/MLEM.Ui/Elements/Element.cs
+++ b/MLEM.Ui/Elements/Element.cs
@@ -924,6 +924,7 @@ namespace MLEM.Ui.Elements {
public void DrawTransformed(GameTime time, SpriteBatch batch, float alpha, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, Effect effect, Matrix matrix) {
var customDraw = this.BeginImpl != null || this.Transform != Matrix.Identity;
var mat = this.Transform * matrix;
+ // TODO ending and beginning again when the matrix changes isn't ideal (https://github.com/MonoGame/MonoGame/issues/3156)
if (customDraw) {
// end the usual draw so that we can begin our own
batch.End();
@@ -984,6 +985,7 @@ 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 c8914b4..7ab165b 100644
--- a/MLEM.Ui/Elements/Panel.cs
+++ b/MLEM.Ui/Elements/Panel.cs
@@ -13,10 +13,15 @@ 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.
@@ -51,7 +56,6 @@ namespace MLEM.Ui.Elements {
private readonly List relevantChildren = new List();
private readonly bool scrollOverflow;
- private RenderTarget2D renderTarget;
private bool relevantChildrenDirty;
private float scrollBarChildOffset;
@@ -170,44 +174,34 @@ 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 the render target in DrawUnbound
- if (!this.scrollOverflow || this.renderTarget == null) {
- base.Draw(time, batch, alpha, blendState, samplerState, depthStencilState, effect, matrix);
- } 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(), Color.White);
- }
- }
+ // if we handle overflow, draw using a scissor rectangle
+ if (this.scrollOverflow) {
+ this.UpdateAreaIfDirty();
- ///
- 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();
- }
+ 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);
+ 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);
}
- 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.GetRenderTargetArea().Contains(transformed))
+ if (this.scrollOverflow && !this.GetInnerArea().Contains(transformed))
return !this.IsHidden && this.CanBeMoused && this.DisplayArea.Contains(transformed) ? this : null;
return base.GetElementUnderPos(position);
}
- private RectangleF GetRenderTargetArea() {
+ private RectangleF GetInnerArea() {
var area = this.ChildPaddedArea;
area.X = this.DisplayArea.X;
area.Width = this.DisplayArea.Width;
@@ -258,26 +252,6 @@ 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() {
@@ -291,7 +265,7 @@ namespace MLEM.Ui.Elements {
private void ForceUpdateRelevantChildren() {
this.relevantChildrenDirty = false;
this.relevantChildren.Clear();
- var visible = this.GetRenderTargetArea();
+ var visible = this.GetInnerArea();
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 5e4f72f..77f9f46 100644
--- a/MLEM.Ui/UiSystem.cs
+++ b/MLEM.Ui/UiSystem.cs
@@ -264,6 +264,7 @@ 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();