diff --git a/Examples/GameImpl.cs b/Examples/GameImpl.cs index 63802db..cf72e48 100644 --- a/Examples/GameImpl.cs +++ b/Examples/GameImpl.cs @@ -32,8 +32,8 @@ namespace Examples { 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), - ScrollBarBackground = new NinePatch(new TextureRegion(this.testTexture, 8, 0, 4, 8), 1, 1, 2, 2), - ScrollBarScrollerTexture = new NinePatch(new TextureRegion(this.testTexture, 12, 0, 4, 8), 1, 1, 2, 2), + ScrollBarBackground = new NinePatch(new TextureRegion(this.testTexture, 12, 0, 4, 8), 1, 1, 2, 2), + ScrollBarScrollerTexture = new NinePatch(new TextureRegion(this.testTexture, 8, 0, 4, 8), 1, 1, 2, 2), CheckboxTexture = new NinePatch(new TextureRegion(this.testTexture, 24, 8, 16, 16), 4), CheckboxCheckmark = new TextureRegion(this.testTexture, 24, 0, 8, 8), RadioTexture = new NinePatch(new TextureRegion(this.testTexture, 16, 0, 8, 8), 3), @@ -43,12 +43,12 @@ namespace Examples { this.UiSystem.Style = style; this.UiSystem.GlobalScale = 5; - var root = new Panel(Anchor.Center, new Vector2(80, 100), Point.Zero, false, true, new Point(5, 10)); + var root = new Panel(Anchor.Center, new Vector2(80, 100), Vector2.Zero, false, true, new Point(5, 10)); this.UiSystem.Add("Test", root); root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "This is a small demo for MLEM.Ui, a user interface library that is part of (M)LEM (L)ibrary by (E)llpeck for (M)onoGame.")); var image = root.AddChild(new Image(Anchor.AutoCenter, new Vector2(50, 50), new TextureRegion(this.testTexture, 0, 0, 8, 8)) {IsHidden = true, Padding = new Point(3)}); - root.AddChild(new Button(Anchor.AutoCenter, new Vector2(1, 10), "Toggle Test Image") { + root.AddChild(new Button(Anchor.AutoCenter, new Vector2(1, 10), "Toggle Test Image", "This button shows a grass tile as a test image to show the automatic anchoring of objects.") { OnClicked = (element, button) => { if (button == MouseButton.Left) image.IsHidden = !image.IsHidden; @@ -61,7 +61,7 @@ namespace Examples { if (button == MouseButton.Left) this.UiSystem.Style = this.UiSystem.Style == untexturedStyle ? style : untexturedStyle; }, - PositionOffset = new Point(0, 1), + PositionOffset = new Vector2(0, 1), HasCustomStyle = true, Texture = this.testPatch, HoveredColor = Color.LightGray @@ -69,7 +69,7 @@ namespace Examples { root.AddChild(new VerticalSpace(3)); root.AddChild(new Paragraph(Anchor.AutoCenter, 1, "Text input:", true)); - root.AddChild(new TextField(Anchor.AutoLeft, new Vector2(1, 10)) {PositionOffset = new Point(0, 1)}); + root.AddChild(new TextField(Anchor.AutoLeft, new Vector2(1, 10)) {PositionOffset = new Vector2(0, 1)}); root.AddChild(new VerticalSpace(3)); root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "Zoom in or out:")); @@ -84,16 +84,16 @@ namespace Examples { if (element.Root.Scale > 0.5F) element.Root.Scale -= 0.1F; }, - PositionOffset = new Point(1, 0) + PositionOffset = new Vector2(1, 0) }); root.AddChild(new VerticalSpace(3)); root.AddChild(new Checkbox(Anchor.AutoLeft, new Vector2(1, 10), "Checkbox 1!")); - root.AddChild(new Checkbox(Anchor.AutoLeft, new Vector2(1, 10), "Checkbox 2!") {PositionOffset = new Point(0, 1)}); + root.AddChild(new Checkbox(Anchor.AutoLeft, new Vector2(1, 10), "Checkbox 2!") {PositionOffset = new Vector2(0, 1)}); root.AddChild(new VerticalSpace(3)); root.AddChild(new RadioButton(Anchor.AutoLeft, new Vector2(1, 10), "Radio button 1!")); - root.AddChild(new RadioButton(Anchor.AutoLeft, new Vector2(1, 10), "Radio button 2!") {PositionOffset = new Point(0, 1)}); + root.AddChild(new RadioButton(Anchor.AutoLeft, new Vector2(1, 10), "Radio button 2!") {PositionOffset = new Vector2(0, 1)}); } protected override void Draw(GameTime gameTime) { diff --git a/MLEM.Ui/Elements/Button.cs b/MLEM.Ui/Elements/Button.cs index 7e71af9..1669a93 100644 --- a/MLEM.Ui/Elements/Button.cs +++ b/MLEM.Ui/Elements/Button.cs @@ -11,12 +11,18 @@ namespace MLEM.Ui.Elements { public NinePatch HoveredTexture; public Color HoveredColor; public Paragraph Text; + public Tooltip Tooltip; - public Button(Anchor anchor, Vector2 size, string text = null) : base(anchor, size) { + public Button(Anchor anchor, Vector2 size, string text = null, string tooltipText = null, float tooltipWidth = 50) : base(anchor, size) { if (text != null) { this.Text = new Paragraph(Anchor.Center, 1, text, true); this.AddChild(this.Text); } + if (tooltipText != null) { + this.Tooltip = new Tooltip(tooltipWidth, tooltipText); + this.OnMouseEnter += element => this.System.Add("ButtonTooltip", this.Tooltip); + this.OnMouseExit += element => this.System.Remove("ButtonTooltip"); + } } public override void Draw(GameTime time, SpriteBatch batch, float alpha, Point offset) { diff --git a/MLEM.Ui/Elements/Element.cs b/MLEM.Ui/Elements/Element.cs index 7429490..d4515e3 100644 --- a/MLEM.Ui/Elements/Element.cs +++ b/MLEM.Ui/Elements/Element.cs @@ -21,7 +21,7 @@ namespace MLEM.Ui.Elements { } private Anchor anchor; private Vector2 size; - private Point offset; + private Vector2 offset; public Point Padding; public Point ScaledPadding => this.Padding.Multiply(this.Scale); private Point childPadding; @@ -44,7 +44,7 @@ namespace MLEM.Ui.Elements { } } public Vector2 ScaledSize => this.size * this.Scale; - public Point PositionOffset { + public Vector2 PositionOffset { get => this.offset; set { if (this.offset == value) @@ -53,7 +53,7 @@ namespace MLEM.Ui.Elements { this.SetAreaDirty(); } } - public Point ScaledOffset => this.offset.Multiply(this.Scale); + public Point ScaledOffset => (this.offset * this.Scale).ToPoint(); public Point ChildPadding { get => this.childPadding; set { diff --git a/MLEM.Ui/Elements/Panel.cs b/MLEM.Ui/Elements/Panel.cs index 25e0eb3..69ff37f 100644 --- a/MLEM.Ui/Elements/Panel.cs +++ b/MLEM.Ui/Elements/Panel.cs @@ -14,7 +14,7 @@ namespace MLEM.Ui.Elements { private readonly bool scrollOverflow; private RenderTarget2D renderTarget; - public Panel(Anchor anchor, Vector2 size, Point positionOffset, bool setHeightBasedOnChildren = false, bool scrollOverflow = false, Point? scrollerSize = null) : base(anchor, size) { + public Panel(Anchor anchor, Vector2 size, Vector2 positionOffset, bool setHeightBasedOnChildren = false, bool scrollOverflow = false, Point? scrollerSize = null) : base(anchor, size) { this.PositionOffset = positionOffset; this.SetHeightBasedOnChildren = setHeightBasedOnChildren; this.scrollOverflow = scrollOverflow; @@ -30,14 +30,14 @@ namespace MLEM.Ui.Elements { 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()); + firstChild.PositionOffset = new Vector2(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.ScrollBar.PositionOffset -= new Vector2(scrollSize.X + 1, 0); this.ChildPadding += new Point(scrollSize.X, 0); } } diff --git a/MLEM.Ui/Elements/Paragraph.cs b/MLEM.Ui/Elements/Paragraph.cs index 8fbf4f6..5a4ee21 100644 --- a/MLEM.Ui/Elements/Paragraph.cs +++ b/MLEM.Ui/Elements/Paragraph.cs @@ -5,6 +5,7 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using MLEM.Extensions; using MLEM.Font; +using MLEM.Textures; using MLEM.Ui.Style; namespace MLEM.Ui.Elements { @@ -12,10 +13,14 @@ namespace MLEM.Ui.Elements { private string text; private float lineHeight; + private float longestLineLength; private string[] splitText; private IGenericFont font; private readonly bool centerText; + public NinePatch Background; + public Color BackgroundColor; + public Color TextColor = Color.White; public float TextScale; public string Text { get => this.text; @@ -37,24 +42,32 @@ namespace MLEM.Ui.Elements { this.splitText = this.font.SplitString(this.text, size.X, this.TextScale * this.Scale).ToArray(); this.lineHeight = 0; + this.longestLineLength = 0; var height = 0F; foreach (var strg in this.splitText) { - var strgHeight = this.font.MeasureString(strg).Y * this.TextScale * this.Scale; - height += strgHeight + 1; - if (strgHeight > this.lineHeight) - this.lineHeight = strgHeight; + var strgScale = this.font.MeasureString(strg) * this.TextScale * this.Scale; + height += strgScale.Y + 1; + if (strgScale.Y > this.lineHeight) + this.lineHeight = strgScale.Y; + if (strgScale.X > this.longestLineLength) + this.longestLineLength = strgScale.X; } return new Point(size.X, height.Ceil()); } public override void Draw(GameTime time, SpriteBatch batch, float alpha, Point offset) { + if (this.Background != null) { + var backgroundArea = new Rectangle(this.Area.X + offset.X, this.Area.Y + offset.Y, this.longestLineLength.Ceil() + this.ScaledPadding.X * 2, this.Area.Height + this.ScaledPadding.Y * 2); + batch.Draw(this.Background, backgroundArea, this.BackgroundColor * alpha); + } + var pos = this.DisplayArea.Location.ToVector2(); var off = offset.ToVector2(); foreach (var line in this.splitText) { if (this.centerText) { - this.font.DrawCenteredString(batch, line, pos + off + 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, this.TextColor * alpha); } else { - this.font.DrawString(batch, line, pos + off, Color.White * alpha, 0, Vector2.Zero, this.TextScale * this.Scale, SpriteEffects.None, 0); + this.font.DrawString(batch, line, pos + off, this.TextColor * alpha, 0, Vector2.Zero, this.TextScale * this.Scale, SpriteEffects.None, 0); } off.Y += this.lineHeight + 1; } diff --git a/MLEM.Ui/Elements/Tooltip.cs b/MLEM.Ui/Elements/Tooltip.cs new file mode 100644 index 0000000..ff6a21c --- /dev/null +++ b/MLEM.Ui/Elements/Tooltip.cs @@ -0,0 +1,35 @@ +using System; +using Microsoft.Xna.Framework; +using MLEM.Extensions; +using MLEM.Font; +using MLEM.Ui.Style; + +namespace MLEM.Ui.Elements { + public class Tooltip : Paragraph { + + public Vector2 MouseOffset = new Vector2(2, 3); + + public Tooltip(float width, string text, IGenericFont font = null) : + base(Anchor.TopLeft, width, text, false, font) { + this.Padding = new Point(2); + } + + public override void Update(GameTime time) { + base.Update(time); + this.PositionOffset = this.MousePos.ToVector2() / this.Scale + this.MouseOffset; + } + + public override void ForceUpdateArea() { + if (this.Parent != null) + throw new NotSupportedException($"A tooltip shouldn't be the child of another element ({this.Parent})"); + base.ForceUpdateArea(); + } + + protected override void InitStyle(UiStyle style) { + base.InitStyle(style); + this.Background = style.TooltipBackground; + this.BackgroundColor = style.TooltipBackgroundColor; + } + + } +} \ No newline at end of file diff --git a/MLEM.Ui/Style/UiStyle.cs b/MLEM.Ui/Style/UiStyle.cs index e34af6b..0c0cf7f 100644 --- a/MLEM.Ui/Style/UiStyle.cs +++ b/MLEM.Ui/Style/UiStyle.cs @@ -22,6 +22,8 @@ namespace MLEM.Ui.Style { public NinePatch RadioHoveredTexture; public Color RadioHoveredColor; public TextureRegion RadioCheckmark; + public NinePatch TooltipBackground; + public Color TooltipBackgroundColor; public IGenericFont Font; public float TextScale = 1; diff --git a/MLEM.Ui/Style/UntexturedStyle.cs b/MLEM.Ui/Style/UntexturedStyle.cs index 2007570..ffac0bc 100644 --- a/MLEM.Ui/Style/UntexturedStyle.cs +++ b/MLEM.Ui/Style/UntexturedStyle.cs @@ -22,6 +22,8 @@ namespace MLEM.Ui.Style { this.RadioTexture = GenerateTexture(batch, Color.AliceBlue); this.RadioHoveredColor = Color.LightGray; this.RadioCheckmark = GenerateTexture(batch, Color.CornflowerBlue).Region; + this.TooltipBackground = GenerateTexture(batch, Color.DarkGray); + this.TooltipBackgroundColor = new Color(Color.Black, 0.65F); this.Font = new EmptyFont(); }