1
0
Fork 0
mirror of https://github.com/Ellpeck/MLEM.git synced 2024-11-26 06:28:35 +01:00

added panels and paragraphs

This commit is contained in:
Ellpeck 2019-08-09 19:28:48 +02:00
parent a67abd4661
commit b1d41d572c
11 changed files with 252 additions and 49 deletions

View file

@ -1,6 +1,8 @@
using System.Collections.Generic;
using System.Text; using System.Text;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using MLEM.Extended.Extensions;
using MLEM.Font; using MLEM.Font;
using MonoGame.Extended.BitmapFonts; using MonoGame.Extended.BitmapFonts;
@ -49,5 +51,13 @@ namespace MLEM.Extended.Font {
batch.DrawString(this.Font, text, position, color, rotation, origin, scale, effects, layerDepth); batch.DrawString(this.Font, text, position, color, rotation, origin, scale, effects, layerDepth);
} }
public void DrawCenteredString(SpriteBatch batch, string text, Vector2 position, float scale, Color color, bool horizontal = true, bool vertical = false, float addedScale = 0) {
batch.DrawCenteredString(this.Font, text, position, scale, color, horizontal, vertical, addedScale);
}
public IEnumerable<string> SplitString(string text, float width, float scale) {
return this.Font.SplitString(text, width, scale);
}
} }
} }

View file

@ -5,12 +5,13 @@ using Microsoft.Xna.Framework.Graphics;
using MLEM.Extensions; using MLEM.Extensions;
namespace MLEM.Ui.Elements { namespace MLEM.Ui.Elements {
public class Element { public abstract class Element {
private Anchor anchor; private Anchor anchor;
private Point size; private Vector2 size;
private Point offset; private Point offset;
private Point padding; private Point padding;
private Point childPadding;
public Anchor Anchor { public Anchor Anchor {
get => this.anchor; get => this.anchor;
set { set {
@ -18,7 +19,7 @@ namespace MLEM.Ui.Elements {
this.SetDirty(); this.SetDirty();
} }
} }
public Point Size { public Vector2 Size {
get => this.size; get => this.size;
set { set {
this.size = value; this.size = value;
@ -39,8 +40,15 @@ namespace MLEM.Ui.Elements {
this.SetDirty(); this.SetDirty();
} }
} }
public Point ChildPadding {
get => this.childPadding;
set {
this.childPadding = value;
this.SetDirty();
}
}
public UiSystem System; public UiSystem System { get; private set; }
public Element Parent { get; private set; } public Element Parent { get; private set; }
private readonly List<Element> children = new List<Element>(); private readonly List<Element> children = new List<Element>();
@ -61,43 +69,54 @@ namespace MLEM.Ui.Elements {
} }
} }
protected bool AreaDirty; private bool areaDirty;
public Element(Anchor anchor, Point size, Point positionOffset) { public Element(Anchor anchor, Vector2 size, Point positionOffset) {
this.anchor = anchor; this.anchor = anchor;
this.size = size; this.size = size;
this.offset = positionOffset; this.offset = positionOffset;
this.SetDirty();
} }
public void AddChild(Element element) { public void AddChild(Element element) {
this.children.Add(element); this.children.Add(element);
element.Parent = this; element.Parent = this;
element.System = this.System;
this.SetDirty(); this.SetDirty();
} }
public void RemoveChild(Element element) { public void RemoveChild(Element element) {
this.children.Remove(element); this.children.Remove(element);
element.Parent = null; element.Parent = null;
element.System = null;
this.SetDirty(); this.SetDirty();
} }
public void SetDirty() { public void SetDirty() {
this.AreaDirty = true; this.areaDirty = true;
} }
public void UpdateAreaIfDirty() { public void UpdateAreaIfDirty() {
if (this.AreaDirty) if (this.areaDirty)
this.ForceUpdateArea(); this.ForceUpdateArea();
} }
public void ForceUpdateArea() { public virtual void ForceUpdateArea() {
this.AreaDirty = false; this.areaDirty = false;
var parentArea = this.Parent != null ? this.Parent.area : this.System.ScaledViewport; Rectangle parentArea;
if (this.Parent != null) {
parentArea = this.Parent.area;
parentArea.Location += this.Parent.ChildPadding;
parentArea.Width -= this.Parent.ChildPadding.X * 2;
parentArea.Height -= this.Parent.ChildPadding.Y * 2;
} else {
parentArea = this.System.ScaledViewport;
}
var parentCenterX = parentArea.X + parentArea.Width / 2; var parentCenterX = parentArea.X + parentArea.Width / 2;
var parentCenterY = parentArea.Y + parentArea.Height / 2; var parentCenterY = parentArea.Y + parentArea.Height / 2;
var actualSize = this.CalcActualSize(); var actualSize = this.CalcActualSize(parentArea);
var pos = new Point(); var pos = new Point();
switch (this.anchor) { switch (this.anchor) {
@ -177,11 +196,13 @@ namespace MLEM.Ui.Elements {
child.ForceUpdateArea(); child.ForceUpdateArea();
} }
private Point CalcActualSize() { protected virtual Point CalcActualSize(Rectangle parentArea) {
return this.size; return new Point(
(this.size.X > 1 ? this.size.X : parentArea.Width * this.size.X).Floor(),
(this.size.Y > 1 ? this.size.Y : parentArea.Height * this.size.Y).Floor());
} }
public Element GetPreviousChild() { protected Element GetPreviousChild() {
if (this.Parent == null) if (this.Parent == null)
return null; return null;
@ -194,16 +215,25 @@ namespace MLEM.Ui.Elements {
return lastChild; return lastChild;
} }
public void Update(GameTime time) { public virtual void Update(GameTime time) {
foreach (var child in this.children) foreach (var child in this.children)
child.Update(time); child.Update(time);
} }
public void Draw(GameTime time, SpriteBatch batch) { public virtual void Draw(GameTime time, SpriteBatch batch, Color color) {
batch.Draw(batch.GetBlankTexture(), this.DisplayArea, this.Parent == null ? Color.Blue : Color.Red);
foreach (var child in this.children) foreach (var child in this.children)
child.Draw(time, batch); child.Draw(time, batch, color);
}
public virtual void DrawUnbound(GameTime time, SpriteBatch batch, Color color, BlendState blendState = null, SamplerState samplerState = null) {
foreach (var child in this.children)
child.DrawUnbound(time, batch, color, blendState, samplerState);
}
public void SetUiSystem(UiSystem system) {
this.System = system;
foreach (var child in this.children)
child.System = system;
} }
} }

21
MLEM.Ui/Elements/Panel.cs Normal file
View file

@ -0,0 +1,21 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MLEM.Textures;
namespace MLEM.Ui.Elements {
public class Panel : Element {
private readonly NinePatch texture;
public Panel(Anchor anchor, Vector2 size, Point positionOffset, NinePatch texture) : base(anchor, size, positionOffset) {
this.texture = texture;
this.ChildPadding = new Point(5);
}
public override void Draw(GameTime time, SpriteBatch batch, Color color) {
batch.Draw(this.texture, this.DisplayArea, color);
base.Draw(time, batch, color);
}
}
}

View file

@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MLEM.Extensions;
using MLEM.Font;
namespace MLEM.Ui.Elements {
public class Paragraph : Element {
private string text;
private float lineHeight;
private string[] splitText;
private readonly IGenericFont font;
private readonly bool centerText;
public float TextScale;
public string Text {
get => this.text;
set {
this.text = value;
this.SetDirty();
}
}
public IGenericFont Font => this.font ?? this.System.DefaultFont;
public Paragraph(Anchor anchor, float width, Point positionOffset, string text, float textScale = 1, bool centerText = false, IGenericFont font = null) : base(anchor, new Vector2(width, 0), positionOffset) {
this.text = text;
this.font = font;
this.TextScale = textScale;
this.centerText = centerText;
}
protected override Point CalcActualSize(Rectangle parentArea) {
var size = base.CalcActualSize(parentArea);
this.splitText = this.Font.SplitString(this.text, size.X, this.TextScale).ToArray();
this.lineHeight = 0;
var height = 0F;
foreach (var strg in this.splitText) {
var strgHeight = this.Font.MeasureString(strg).Y * this.TextScale;
height += strgHeight + 1;
if (strgHeight > this.lineHeight)
this.lineHeight = strgHeight;
}
return new Point(size.X, height.Ceil());
}
public override void Draw(GameTime time, SpriteBatch batch, Color color) {
base.Draw(time, batch, color);
var pos = this.DisplayArea.Location.ToVector2();
var offset = new Vector2();
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, color);
} else {
this.Font.DrawString(batch, line, pos + offset, color, 0, Vector2.Zero, this.TextScale, SpriteEffects.None, 0);
}
offset.Y += this.lineHeight + 1;
}
}
}
}

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using MLEM.Extensions; using MLEM.Extensions;
using MLEM.Font;
using MLEM.Ui.Elements; using MLEM.Ui.Elements;
namespace MLEM.Ui { namespace MLEM.Ui {
@ -11,7 +12,8 @@ namespace MLEM.Ui {
private readonly GraphicsDevice graphicsDevice; private readonly GraphicsDevice graphicsDevice;
private readonly List<RootElement> rootElements = new List<RootElement>(); private readonly List<RootElement> rootElements = new List<RootElement>();
public float GlobalScale; public readonly float GlobalScale;
public readonly IGenericFont DefaultFont;
public Rectangle ScaledViewport { public Rectangle ScaledViewport {
get { get {
var bounds = this.graphicsDevice.Viewport.Bounds; var bounds = this.graphicsDevice.Viewport.Bounds;
@ -19,9 +21,11 @@ namespace MLEM.Ui {
} }
} }
public UiSystem(GameWindow window, GraphicsDevice device, float scale) { public UiSystem(GameWindow window, GraphicsDevice device, float scale, IGenericFont defaultFont) {
this.graphicsDevice = device; this.graphicsDevice = device;
this.GlobalScale = scale; this.GlobalScale = scale;
this.DefaultFont = defaultFont;
window.ClientSizeChanged += (sender, args) => { window.ClientSizeChanged += (sender, args) => {
foreach (var root in this.rootElements) foreach (var root in this.rootElements)
root.Element.ForceUpdateArea(); root.Element.ForceUpdateArea();
@ -33,11 +37,16 @@ namespace MLEM.Ui {
root.Element.Update(time); root.Element.Update(time);
} }
public void Draw(GameTime time, SpriteBatch batch, BlendState blendState = null, SamplerState samplerState = null) { public void Draw(GameTime time, SpriteBatch batch, Color? color = null, BlendState blendState = null, SamplerState samplerState = null) {
var col = color ?? Color.White;
batch.Begin(SpriteSortMode.Deferred, blendState, samplerState, transformMatrix: Matrix.CreateScale(this.GlobalScale)); batch.Begin(SpriteSortMode.Deferred, blendState, samplerState, transformMatrix: Matrix.CreateScale(this.GlobalScale));
foreach (var root in this.rootElements) foreach (var root in this.rootElements)
root.Element.Draw(time, batch); root.Element.Draw(time, batch, col);
batch.End(); batch.End();
foreach (var root in this.rootElements)
root.Element.DrawUnbound(time, batch, col, blendState, samplerState);
} }
public void Add(string name, Element root) { public void Add(string name, Element root) {
@ -45,7 +54,7 @@ namespace MLEM.Ui {
throw new ArgumentException($"There is already a root element with name {name}"); throw new ArgumentException($"There is already a root element with name {name}");
this.rootElements.Add(new RootElement(name, root)); this.rootElements.Add(new RootElement(name, root));
root.System = this; root.SetUiSystem(this);
} }
public void Remove(string name) { public void Remove(string name) {

View file

@ -1,6 +1,8 @@
using System.Collections.Generic;
using System.Text; using System.Text;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using MLEM.Extensions;
namespace MLEM.Font { namespace MLEM.Font {
public class GenericSpriteFont : IGenericFont { public class GenericSpriteFont : IGenericFont {
@ -47,5 +49,13 @@ namespace MLEM.Font {
batch.DrawString(this.Font, text, position, color, rotation, origin, scale, effects, layerDepth); batch.DrawString(this.Font, text, position, color, rotation, origin, scale, effects, layerDepth);
} }
public void DrawCenteredString(SpriteBatch batch, string text, Vector2 position, float scale, Color color, bool horizontal = true, bool vertical = false, float addedScale = 0) {
batch.DrawCenteredString(this.Font, text, position, scale, color, horizontal, vertical, addedScale);
}
public IEnumerable<string> SplitString(string text, float width, float scale) {
return this.Font.SplitString(text, width, scale);
}
} }
} }

View file

@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.Text; using System.Text;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
@ -21,5 +22,9 @@ namespace MLEM.Font {
void DrawString(SpriteBatch batch, StringBuilder text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth); void DrawString(SpriteBatch batch, StringBuilder text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth);
void DrawCenteredString(SpriteBatch batch, string text, Vector2 position, float scale, Color color, bool horizontal = true, bool vertical = false, float addedScale = 0);
IEnumerable<string> SplitString(string text, float width, float scale);
} }
} }

View file

@ -57,6 +57,7 @@ namespace MLEM.Textures {
var dest = texture.CreateRectangles(destinationRectangle); var dest = texture.CreateRectangles(destinationRectangle);
var count = 0; var count = 0;
foreach (var rect in dest) { foreach (var rect in dest) {
if (!rect.IsEmpty)
batch.Draw(texture.Region.Texture, rect, texture.SourceRectangles[count], color, rotation, origin, effects, layerDepth); batch.Draw(texture.Region.Texture, rect, texture.SourceRectangles[count], color, rotation, origin, effects, layerDepth);
count++; count++;
} }

View file

@ -13,6 +13,13 @@
#---------------------------------- Content ---------------------------------# #---------------------------------- Content ---------------------------------#
#begin Fonts/TestFont.spritefont
/importer:FontDescriptionImporter
/processor:FontDescriptionProcessor
/processorParam:PremultiplyAlpha=True
/processorParam:TextureFormat=Compressed
/build:Fonts/TestFont.spritefont
#begin Textures/Test.png #begin Textures/Test.png
/importer:TextureImporter /importer:TextureImporter
/processor:TextureProcessor /processor:TextureProcessor

View file

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This file contains an xml description of a font, and will be read by the XNA
Framework Content Pipeline. Follow the comments to customize the appearance
of the font in your game, and to change the characters which are available to draw
with.
-->
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
<Asset Type="Graphics:FontDescription">
<!--
Modify this string to change the font that will be imported.
-->
<FontName>BitPotion</FontName>
<!--
Size is a float value, measured in points. Modify this value to change
the size of the font.
-->
<Size>32</Size>
<!--
Spacing is a float value, measured in pixels. Modify this value to change
the amount of spacing in between characters.
-->
<Spacing>0</Spacing>
<!--
UseKerning controls the layout of the font. If this value is true, kerning information
will be used when placing characters.
-->
<UseKerning>true</UseKerning>
<!--
Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic",
and "Bold, Italic", and are case sensitive.
-->
<Style>Regular</Style>
<!--
If you uncomment this line, the default character will be substituted if you draw
or measure text that contains characters which were not included in the font.
-->
<!-- <DefaultCharacter>*</DefaultCharacter> -->
<!--
CharacterRegions control what letters are available in the font. Every
character from Start to End will be built and made available for drawing. The
default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin
character set. The characters are ordered according to the Unicode standard.
See the documentation for more information.
-->
<CharacterRegions>
<CharacterRegion>
<Start>&#32;</Start>
<End>&#126;</End>
</CharacterRegion>
</CharacterRegions>
</Asset>
</XnaContent>

View file

@ -3,6 +3,7 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Input;
using MLEM.Extended.Extensions; using MLEM.Extended.Extensions;
using MLEM.Font;
using MLEM.Input; using MLEM.Input;
using MLEM.Startup; using MLEM.Startup;
using MLEM.Textures; using MLEM.Textures;
@ -14,28 +15,19 @@ namespace Tests {
public class GameImpl : MlemGame { public class GameImpl : MlemGame {
private Texture2D testTexture; private Texture2D testTexture;
private TextureRegion testRegion;
private NinePatch testPatch; private NinePatch testPatch;
private NinePatchRegion2D extendedPatch;
private UiSystem uiSystem; private UiSystem uiSystem;
private Element testChild;
protected override void LoadContent() { protected override void LoadContent() {
base.LoadContent(); base.LoadContent();
this.testTexture = LoadContent<Texture2D>("Textures/Test"); this.testTexture = LoadContent<Texture2D>("Textures/Test");
this.testRegion = new TextureRegion(this.testTexture, 32, 0, 8, 8);
this.testPatch = new NinePatch(new TextureRegion(this.testTexture, 0, 8, 24, 24), 8); this.testPatch = new NinePatch(new TextureRegion(this.testTexture, 0, 8, 24, 24), 8);
this.extendedPatch = this.testPatch.ToExtended();
// Ui system tests // Ui system tests
this.uiSystem = new UiSystem(this.Window, this.GraphicsDevice, 5); this.uiSystem = new UiSystem(this.Window, this.GraphicsDevice, 5, new GenericSpriteFont(LoadContent<SpriteFont>("Fonts/TestFont")));
var root = new Element(Anchor.BottomLeft, new Point(100, 100), new Point(5, 5)); var root = new Panel(Anchor.BottomLeft, new Vector2(100, 100), new Point(5, 5), this.testPatch);
for (var i = 0; i < 3; i++) root.AddChild(new Paragraph(Anchor.AutoLeft, 1, Point.Zero, "This is a test text that is hopefully long enough to cover at least a few lines, otherwise it would be very sad.", 0.2F));
root.AddChild(new Element(Anchor.AutoInline, new Point(16, 16), Point.Zero) {
Padding = new Point(1, 1)
});
this.uiSystem.Add("Test", root); this.uiSystem.Add("Test", root);
} }
@ -57,17 +49,7 @@ namespace Tests {
base.Draw(gameTime); base.Draw(gameTime);
this.GraphicsDevice.Clear(Color.Black); this.GraphicsDevice.Clear(Color.Black);
// Texture region tests this.uiSystem.Draw(gameTime, this.SpriteBatch, samplerState: SamplerState.PointClamp);
this.SpriteBatch.Begin(samplerState: SamplerState.PointClamp);
this.SpriteBatch.Draw(this.testRegion, new Vector2(10, 10), Color.White);
this.SpriteBatch.Draw(this.testRegion, new Vector2(30, 10), Color.White, 0, Vector2.Zero, 10, SpriteEffects.None, 0);
this.SpriteBatch.End();
this.SpriteBatch.Begin(samplerState: SamplerState.PointClamp, transformMatrix: Matrix.CreateScale(10));
this.SpriteBatch.Draw(this.testPatch, new Rectangle(20, 20, 40, 20), Color.White);
this.SpriteBatch.Draw(this.extendedPatch, new Rectangle(80, 20, 40, 20), Color.White);
this.SpriteBatch.End();
this.uiSystem.Draw(gameTime, this.SpriteBatch);
} }
} }