diff --git a/MLEM.Startup/MlemGame.cs b/MLEM.Startup/MlemGame.cs index a930972..c2cac4c 100644 --- a/MLEM.Startup/MlemGame.cs +++ b/MLEM.Startup/MlemGame.cs @@ -4,6 +4,7 @@ using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using MLEM.Input; using MLEM.Ui; +using MLEM.Ui.Style; using MonoGame.Extended; namespace MLEM.Startup { @@ -40,7 +41,7 @@ namespace MLEM.Startup { protected override void LoadContent() { this.SpriteBatch = new SpriteBatch(this.GraphicsDevice); this.InputHandler = new InputHandler(); - this.UiSystem = new UiSystem(this.Window, this.GraphicsDevice, this.InputHandler); + this.UiSystem = new UiSystem(this.Window, this.GraphicsDevice, new UntexturedStyle(this.SpriteBatch), this.InputHandler); } protected override void Initialize() { diff --git a/MLEM.Ui/Elements/AutoScaledText.cs b/MLEM.Ui/Elements/AutoScaledText.cs index de247dd..f13dcef 100644 --- a/MLEM.Ui/Elements/AutoScaledText.cs +++ b/MLEM.Ui/Elements/AutoScaledText.cs @@ -3,11 +3,12 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using MLEM.Extensions; using MLEM.Font; +using MLEM.Ui.Style; namespace MLEM.Ui.Elements { public class AutoScaledText : Element { - private readonly IGenericFont font; + private IGenericFont font; private float scale; private string text; @@ -22,7 +23,7 @@ namespace MLEM.Ui.Elements { public AutoScaledText(Anchor anchor, Vector2 size, string text, IGenericFont font = null) : base(anchor, size) { this.Text = text; - this.font = font ?? Paragraph.DefaultFont; + this.font = font; this.IgnoresMouse = true; } @@ -43,5 +44,10 @@ namespace MLEM.Ui.Elements { base.Draw(time, batch, alpha); } + protected override void InitStyle(UiStyle style) { + base.InitStyle(style); + this.font = style.Font; + } + } } \ No newline at end of file diff --git a/MLEM.Ui/Elements/Button.cs b/MLEM.Ui/Elements/Button.cs index df2b988..c8901fd 100644 --- a/MLEM.Ui/Elements/Button.cs +++ b/MLEM.Ui/Elements/Button.cs @@ -2,17 +2,14 @@ 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 Button : Element { - public static NinePatch DefaultTexture; - public static NinePatch DefaultHoveredTexture; - public static Color DefaultHoveredColor = Color.LightGray; - - public NinePatch Texture = DefaultTexture; - public NinePatch HoveredTexture = DefaultHoveredTexture; - public Color HoveredColor = DefaultHoveredColor; + public NinePatch Texture; + public NinePatch HoveredTexture; + public Color HoveredColor; public AutoScaledText Text; public Button(Anchor anchor, Vector2 size, string text = null) : base(anchor, size) { @@ -36,5 +33,12 @@ namespace MLEM.Ui.Elements { base.Draw(time, batch, alpha); } + protected override void InitStyle(UiStyle style) { + base.InitStyle(style); + this.Texture = style.ButtonTexture; + this.HoveredTexture = style.ButtonHoveredTexture; + this.HoveredColor = style.ButtonHoveredColor; + } + } } \ No newline at end of file diff --git a/MLEM.Ui/Elements/Element.cs b/MLEM.Ui/Elements/Element.cs index 9cb6c2d..bddcb22 100644 --- a/MLEM.Ui/Elements/Element.cs +++ b/MLEM.Ui/Elements/Element.cs @@ -5,6 +5,7 @@ using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using MLEM.Extensions; using MLEM.Input; +using MLEM.Ui.Style; namespace MLEM.Ui.Elements { public abstract class Element { @@ -68,7 +69,15 @@ namespace MLEM.Ui.Elements { public MouseCallback OnMouseExit; public TextInputCallback OnTextInput; - public UiSystem System { get; private set; } + private UiSystem system; + public UiSystem System { + get => this.system; + set { + this.system = value; + if (this.system != null && !this.HasCustomStyle) + this.InitStyle(this.system.Style); + } + } protected InputHandler Input => this.System.InputHandler; public Element Parent { get; private set; } public bool IsMouseOver { get; private set; } @@ -85,6 +94,7 @@ namespace MLEM.Ui.Elements { } public bool IgnoresMouse; public float DrawAlpha = 1; + public bool HasCustomStyle; private Rectangle area; public Rectangle Area { @@ -122,7 +132,7 @@ namespace MLEM.Ui.Elements { index = this.children.Count; this.children.Insert(index, element); element.Parent = this; - element.System = this.System; + element.PropagateUiSystem(this.System); this.SetDirty(); return element; } @@ -130,7 +140,7 @@ namespace MLEM.Ui.Elements { public void RemoveChild(Element element) { this.children.Remove(element); element.Parent = null; - element.System = null; + element.PropagateUiSystem(this.System); this.SetDirty(); } @@ -307,6 +317,9 @@ namespace MLEM.Ui.Elements { return this; } + protected virtual void InitStyle(UiStyle style) { + } + public delegate void MouseClickCallback(Element element, Vector2 mousePos, MouseButton button); public delegate void MouseCallback(Element element, Vector2 mousePos); @@ -315,10 +328,10 @@ namespace MLEM.Ui.Elements { public delegate void GenericCallback(Element element); - internal void SetUiSystem(UiSystem system) { + internal void PropagateUiSystem(UiSystem system) { this.System = system; foreach (var child in this.children) - child.SetUiSystem(system); + child.PropagateUiSystem(system); } internal void PropagateInput(Keys key, char character) { diff --git a/MLEM.Ui/Elements/Panel.cs b/MLEM.Ui/Elements/Panel.cs index 6e2ddfe..3cd5dff 100644 --- a/MLEM.Ui/Elements/Panel.cs +++ b/MLEM.Ui/Elements/Panel.cs @@ -1,24 +1,28 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using MLEM.Textures; +using MLEM.Ui.Style; namespace MLEM.Ui.Elements { public class Panel : Element { - public static NinePatch DefaultTexture; - - private readonly NinePatch texture; + public NinePatch Texture; public Panel(Anchor anchor, Vector2 size, Point positionOffset, NinePatch texture = null) : base(anchor, size) { - this.texture = texture ?? DefaultTexture; + this.Texture = texture; this.PositionOffset = positionOffset; this.ChildPadding = new Point(5); } public override void Draw(GameTime time, SpriteBatch batch, float alpha) { - batch.Draw(this.texture, this.DisplayArea, Color.White * alpha); + batch.Draw(this.Texture, this.DisplayArea, Color.White * alpha); base.Draw(time, batch, alpha); } + protected override void InitStyle(UiStyle style) { + base.InitStyle(style); + this.Texture = style.PanelTexture; + } + } } \ No newline at end of file diff --git a/MLEM.Ui/Elements/Paragraph.cs b/MLEM.Ui/Elements/Paragraph.cs index b504faf..721e9a5 100644 --- a/MLEM.Ui/Elements/Paragraph.cs +++ b/MLEM.Ui/Elements/Paragraph.cs @@ -5,20 +5,18 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using MLEM.Extensions; using MLEM.Font; +using MLEM.Ui.Style; namespace MLEM.Ui.Elements { public class Paragraph : Element { - public static IGenericFont DefaultFont; - public static float DefaultTextScale = 1; - private string text; private float lineHeight; private string[] splitText; - private readonly IGenericFont font; + private IGenericFont font; private readonly bool centerText; - public float TextScale = DefaultTextScale; + public float TextScale; public string Text { get => this.text; set { @@ -29,7 +27,7 @@ namespace MLEM.Ui.Elements { public Paragraph(Anchor anchor, float width, string text, bool centerText = false, IGenericFont font = null) : base(anchor, new Vector2(width, 0)) { this.text = text; - this.font = font ?? DefaultFont; + this.font = font; this.centerText = centerText; } @@ -63,6 +61,12 @@ namespace MLEM.Ui.Elements { } } + protected override void InitStyle(UiStyle style) { + base.InitStyle(style); + this.TextScale = style.TextScale; + this.font = style.Font; + } + } } \ No newline at end of file diff --git a/MLEM.Ui/Elements/TextField.cs b/MLEM.Ui/Elements/TextField.cs index bd75a0b..b012b1b 100644 --- a/MLEM.Ui/Elements/TextField.cs +++ b/MLEM.Ui/Elements/TextField.cs @@ -5,28 +5,24 @@ using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using MLEM.Font; using MLEM.Textures; +using MLEM.Ui.Style; namespace MLEM.Ui.Elements { public class TextField : Element { - public static NinePatch DefaultTexture; - public static NinePatch DefaultHoveredTexture; - public static Color DefaultHoveredColor = Color.LightGray; - - public NinePatch Texture = DefaultTexture; - public NinePatch HoveredTexture = DefaultHoveredTexture; - public Color HoveredColor = DefaultHoveredColor; + public NinePatch Texture; + public NinePatch HoveredTexture; + public Color HoveredColor; public float TextScale; public readonly StringBuilder Text = new StringBuilder(); public TextChanged OnTextChange; public int MaxTextLength = int.MaxValue; public float TextOffsetX = 4; - private readonly IGenericFont font; + private IGenericFont font; private double caretBlinkTimer; public TextField(Anchor anchor, Vector2 size, IGenericFont font = null) : base(anchor, size) { - this.font = font ?? Paragraph.DefaultFont; - this.TextScale = Paragraph.DefaultTextScale; + this.font = font; this.OnTextInput += (element, key, character) => { if (!this.IsSelected) return; @@ -69,6 +65,15 @@ namespace MLEM.Ui.Elements { base.Draw(time, batch, alpha); } + protected override void InitStyle(UiStyle style) { + base.InitStyle(style); + this.TextScale = style.TextScale; + this.font = style.Font; + this.Texture = style.TextFieldTexture; + this.HoveredTexture = style.TextFieldHoveredTexture; + this.HoveredColor = style.TextFieldHoveredColor; + } + public delegate void TextChanged(TextField field, string text); } diff --git a/MLEM.Ui/Style/UiStyle.cs b/MLEM.Ui/Style/UiStyle.cs new file mode 100644 index 0000000..682180e --- /dev/null +++ b/MLEM.Ui/Style/UiStyle.cs @@ -0,0 +1,19 @@ +using Microsoft.Xna.Framework; +using MLEM.Font; +using MLEM.Textures; + +namespace MLEM.Ui.Style { + public class UiStyle { + + public NinePatch ButtonTexture; + public NinePatch ButtonHoveredTexture; + public Color ButtonHoveredColor; + public NinePatch PanelTexture; + public NinePatch TextFieldTexture; + public NinePatch TextFieldHoveredTexture; + public Color TextFieldHoveredColor; + public IGenericFont Font; + public float TextScale = 1; + + } +} \ No newline at end of file diff --git a/MLEM.Ui/Style/UntexturedStyle.cs b/MLEM.Ui/Style/UntexturedStyle.cs new file mode 100644 index 0000000..007c648 --- /dev/null +++ b/MLEM.Ui/Style/UntexturedStyle.cs @@ -0,0 +1,74 @@ +using System.Collections.Generic; +using System.Text; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using MLEM.Font; +using MLEM.Textures; + +namespace MLEM.Ui.Style { + public class UntexturedStyle : UiStyle { + + public UntexturedStyle(SpriteBatch batch) { + this.ButtonTexture = GenerateTexture(batch, Color.CadetBlue); + this.ButtonHoveredColor = Color.LightGray; + this.PanelTexture = GenerateTexture(batch, Color.Gray); + this.TextFieldTexture = GenerateTexture(batch, Color.MediumBlue); + this.TextFieldHoveredColor = Color.LightGray; + this.Font = new EmptyFont(); + } + + private static NinePatch GenerateTexture(SpriteBatch batch, Color color) { + var tex = new Texture2D(batch.GraphicsDevice, 3, 3); + tex.SetData(new[] { + Color.Black, Color.Black, Color.Black, + Color.Black, color, Color.Black, + Color.Black, Color.Black, Color.Black + }); + batch.Disposing += (sender, args) => { + if (tex != null) { + tex.Dispose(); + tex = null; + } + }; + return new NinePatch(tex, 1); + } + + private class EmptyFont : IGenericFont { + + public Vector2 MeasureString(string text) { + return Vector2.One; + } + + public Vector2 MeasureString(StringBuilder text) { + return Vector2.One; + } + + public void DrawString(SpriteBatch batch, string text, Vector2 position, Color color) { + } + + public void DrawString(SpriteBatch batch, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects effects, float layerDepth) { + } + + public void DrawString(SpriteBatch batch, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth) { + } + + public void DrawString(SpriteBatch batch, StringBuilder text, Vector2 position, Color color) { + } + + public void DrawString(SpriteBatch batch, StringBuilder text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects effects, float layerDepth) { + } + + public void DrawString(SpriteBatch batch, StringBuilder text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth) { + } + + public void DrawCenteredString(SpriteBatch batch, string text, Vector2 position, float scale, Color color, bool horizontal = true, bool vertical = false, float addedScale = 0) { + } + + public IEnumerable SplitString(string text, float width, float scale) { + yield break; + } + + } + + } +} \ No newline at end of file diff --git a/MLEM.Ui/UiSystem.cs b/MLEM.Ui/UiSystem.cs index 608f24f..ec5b371 100644 --- a/MLEM.Ui/UiSystem.cs +++ b/MLEM.Ui/UiSystem.cs @@ -6,6 +6,7 @@ using MLEM.Extensions; using MLEM.Font; using MLEM.Input; using MLEM.Ui.Elements; +using MLEM.Ui.Style; namespace MLEM.Ui { public class UiSystem { @@ -33,14 +34,26 @@ namespace MLEM.Ui { public Vector2 MousePos => this.InputHandler.MousePosition.ToVector2() / this.globalScale; public Element MousedElement { get; private set; } public Element SelectedElement { get; private set; } + private UiStyle style; + public UiStyle Style { + get => this.style; + set { + this.style = value; + foreach (var root in this.rootElements) { + root.Element.PropagateUiSystem(this); + root.Element.SetDirty(); + } + } + } public float DrawAlpha = 1; public BlendState BlendState; public SamplerState SamplerState = SamplerState.PointClamp; - public UiSystem(GameWindow window, GraphicsDevice device, InputHandler inputHandler = null) { + public UiSystem(GameWindow window, GraphicsDevice device, UiStyle style, InputHandler inputHandler = null) { this.GraphicsDevice = device; this.InputHandler = inputHandler ?? new InputHandler(); this.isInputOurs = inputHandler == null; + this.style = style; window.ClientSizeChanged += (sender, args) => { foreach (var root in this.rootElements) @@ -103,7 +116,7 @@ namespace MLEM.Ui { throw new ArgumentException($"There is already a root element with name {name}"); this.rootElements.Add(new RootElement(name, root)); - root.SetUiSystem(this); + root.PropagateUiSystem(this); } public void Remove(string name) { diff --git a/MLEM/Extensions/SpriteBatchExtensions.cs b/MLEM/Extensions/SpriteBatchExtensions.cs index 95bfb67..d79c94a 100644 --- a/MLEM/Extensions/SpriteBatchExtensions.cs +++ b/MLEM/Extensions/SpriteBatchExtensions.cs @@ -8,7 +8,7 @@ namespace MLEM.Extensions { public static Texture2D GetBlankTexture(this SpriteBatch batch) { if (blankTexture == null) { - blankTexture = new Texture2D(batch.GraphicsDevice, 1, 1, false, SurfaceFormat.Color); + blankTexture = new Texture2D(batch.GraphicsDevice, 1, 1); blankTexture.SetData(new[] {Color.White}); batch.Disposing += (sender, args) => { if (blankTexture != null) { diff --git a/MLEM/Textures/NinePatch.cs b/MLEM/Textures/NinePatch.cs index 1342509..782cbe8 100644 --- a/MLEM/Textures/NinePatch.cs +++ b/MLEM/Textures/NinePatch.cs @@ -27,6 +27,9 @@ namespace MLEM.Textures { this(new TextureRegion(texture), paddingLeft, paddingRight, paddingTop, paddingBottom) { } + public NinePatch(Texture2D texture, int padding) : this(new TextureRegion(texture), padding) { + } + public NinePatch(TextureRegion texture, int padding) : this(texture, padding, padding, padding, padding) { } diff --git a/Tests/GameImpl.cs b/Tests/GameImpl.cs index de44b4a..72a29b4 100644 --- a/Tests/GameImpl.cs +++ b/Tests/GameImpl.cs @@ -9,6 +9,7 @@ using MLEM.Startup; using MLEM.Textures; using MLEM.Ui; using MLEM.Ui.Elements; +using MLEM.Ui.Style; namespace Tests { public class GameImpl : MlemGame { @@ -21,17 +22,26 @@ namespace Tests { } protected override void LoadContent() { - base.LoadContent(); this.testTexture = LoadContent("Textures/Test"); this.testPatch = new NinePatch(new TextureRegion(this.testTexture, 0, 8, 24, 24), 8); + base.LoadContent(); - Paragraph.DefaultFont = new GenericSpriteFont(LoadContent("Fonts/TestFont")); - Paragraph.DefaultTextScale = 0.2F; - Button.DefaultTexture = new NinePatch(new TextureRegion(this.testTexture, 24, 8, 16, 16), 4); - TextField.DefaultTexture = Button.DefaultTexture; + var style = new UiStyle { + Font = new GenericSpriteFont(LoadContent("Fonts/TestFont")), + TextScale = 0.2F, + PanelTexture = this.testPatch, + ButtonTexture = new NinePatch(new TextureRegion(this.testTexture, 24, 8, 16, 16), 4), + TextFieldTexture = new NinePatch(new TextureRegion(this.testTexture, 24, 8, 16, 16), 4), + ButtonHoveredColor = Color.LightGray, + TextFieldHoveredColor = Color.LightGray + }; + var untexturedStyle = this.UiSystem.Style; + this.UiSystem.Style = style; this.UiSystem.GlobalScale = 5; - var root = new Panel(Anchor.BottomLeft, new Vector2(100, 100), new Point(5, 5), this.testPatch); + var root = new Panel(Anchor.BottomLeft, new Vector2(100, 120), new Point(5, 5)); + 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.")); var image = root.AddChild(new Image(Anchor.AutoCenter, new Vector2(20, 20), new TextureRegion(this.testTexture, 0, 0, 8, 8)) {IsHidden = true}); root.AddChild(new Button(Anchor.AutoCenter, new Vector2(1, 15), "Test Button") { @@ -40,8 +50,16 @@ namespace Tests { image.IsHidden = !image.IsHidden; } }); + root.AddChild(new Button(Anchor.AutoCenter, new Vector2(1, 15), "Change Style") { + OnClicked = (element, pos, button) => { + if (button == MouseButton.Left) + this.UiSystem.Style = this.UiSystem.Style is UntexturedStyle ? style : untexturedStyle; + }, + HasCustomStyle = true, + Texture = this.testPatch, + HoveredColor = Color.LightGray + }); root.AddChild(new TextField(Anchor.AutoLeft, new Vector2(1, 15))); - this.UiSystem.Add("Test", root); } protected override void Draw(GameTime gameTime) {