mirror of
https://github.com/Ellpeck/MLEM.git
synced 2024-11-22 20:58:34 +01:00
added scroll bar and overflow handling panel
This commit is contained in:
parent
09fbdfd54e
commit
4624219b4e
13 changed files with 245 additions and 60 deletions
|
@ -26,7 +26,7 @@ namespace Examples {
|
||||||
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);
|
||||||
base.LoadContent();
|
base.LoadContent();
|
||||||
|
|
||||||
var style = new UiStyle {
|
var style = new UntexturedStyle(this.SpriteBatch) {
|
||||||
Font = new GenericSpriteFont(LoadContent<SpriteFont>("Fonts/TestFont")),
|
Font = new GenericSpriteFont(LoadContent<SpriteFont>("Fonts/TestFont")),
|
||||||
TextScale = 0.2F,
|
TextScale = 0.2F,
|
||||||
PanelTexture = this.testPatch,
|
PanelTexture = this.testPatch,
|
||||||
|
@ -39,7 +39,7 @@ namespace Examples {
|
||||||
this.UiSystem.Style = style;
|
this.UiSystem.Style = style;
|
||||||
this.UiSystem.GlobalScale = 5;
|
this.UiSystem.GlobalScale = 5;
|
||||||
|
|
||||||
var root = new Panel(Anchor.Center, new Vector2(100, 120), Point.Zero, true);
|
var root = new Panel(Anchor.Center, new Vector2(100, 80), Point.Zero, false, true, new Point(5, 10));
|
||||||
this.UiSystem.Add("Test", root);
|
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."));
|
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."));
|
||||||
|
@ -53,7 +53,7 @@ namespace Examples {
|
||||||
root.AddChild(new Button(Anchor.AutoCenter, new Vector2(1, 15), "Change Style") {
|
root.AddChild(new Button(Anchor.AutoCenter, new Vector2(1, 15), "Change Style") {
|
||||||
OnClicked = (element, button) => {
|
OnClicked = (element, button) => {
|
||||||
if (button == MouseButton.Left)
|
if (button == MouseButton.Left)
|
||||||
this.UiSystem.Style = this.UiSystem.Style is UntexturedStyle ? style : untexturedStyle;
|
this.UiSystem.Style = this.UiSystem.Style == untexturedStyle ? style : untexturedStyle;
|
||||||
},
|
},
|
||||||
HasCustomStyle = true,
|
HasCustomStyle = true,
|
||||||
Texture = this.testPatch,
|
Texture = this.testPatch,
|
||||||
|
@ -75,6 +75,7 @@ namespace Examples {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
root.AddChild(new Button(Anchor.AutoInline, new Vector2(30, 15), "Woop") {
|
root.AddChild(new Button(Anchor.AutoInline, new Vector2(30, 15), "Woop") {
|
||||||
|
PositionOffset = new Point(2, 0),
|
||||||
OnClicked = (element, button) => CoroutineHandler.Start(Woop(element))
|
OnClicked = (element, button) => CoroutineHandler.Start(Woop(element))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,10 +38,10 @@ namespace MLEM.Ui.Elements {
|
||||||
} while (measure.X <= this.DisplayArea.Size.X / this.Scale && measure.Y <= this.DisplayArea.Size.Y / this.Scale);
|
} while (measure.X <= this.DisplayArea.Size.X / this.Scale && measure.Y <= this.DisplayArea.Size.Y / this.Scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Draw(GameTime time, SpriteBatch batch, float alpha) {
|
public override void Draw(GameTime time, SpriteBatch batch, float alpha, Point offset) {
|
||||||
var pos = this.DisplayArea.Location.ToVector2() + this.DisplayArea.Size.ToVector2() / 2;
|
var pos = this.DisplayArea.Location.ToVector2() + this.DisplayArea.Size.ToVector2() / 2 + offset.ToVector2();
|
||||||
this.font.DrawCenteredString(batch, this.Text, pos, this.textScale * this.Scale, this.Color * alpha, true, true);
|
this.font.DrawCenteredString(batch, this.Text, pos, this.textScale * this.Scale, this.Color * alpha, true, true);
|
||||||
base.Draw(time, batch, alpha);
|
base.Draw(time, batch, alpha, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void InitStyle(UiStyle style) {
|
protected override void InitStyle(UiStyle style) {
|
||||||
|
|
|
@ -21,7 +21,7 @@ namespace MLEM.Ui.Elements {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Draw(GameTime time, SpriteBatch batch, float alpha) {
|
public override void Draw(GameTime time, SpriteBatch batch, float alpha, Point offset) {
|
||||||
var tex = this.Texture;
|
var tex = this.Texture;
|
||||||
var color = Color.White * alpha;
|
var color = Color.White * alpha;
|
||||||
if (this.IsMouseOver) {
|
if (this.IsMouseOver) {
|
||||||
|
@ -29,8 +29,8 @@ namespace MLEM.Ui.Elements {
|
||||||
tex = this.HoveredTexture;
|
tex = this.HoveredTexture;
|
||||||
color = this.HoveredColor * alpha;
|
color = this.HoveredColor * alpha;
|
||||||
}
|
}
|
||||||
batch.Draw(tex, this.DisplayArea, color, this.Scale);
|
batch.Draw(tex, this.DisplayArea.OffsetCopy(offset), color, this.Scale);
|
||||||
base.Draw(time, batch, alpha);
|
base.Draw(time, batch, alpha, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void InitStyle(UiStyle style) {
|
protected override void InitStyle(UiStyle style) {
|
||||||
|
|
|
@ -63,6 +63,15 @@ namespace MLEM.Ui.Elements {
|
||||||
this.SetAreaDirty();
|
this.SetAreaDirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public Rectangle ChildPaddedArea {
|
||||||
|
get {
|
||||||
|
var padded = this.Area;
|
||||||
|
padded.Location += this.ScaledChildPadding;
|
||||||
|
padded.Width -= this.ScaledChildPadding.X * 2;
|
||||||
|
padded.Height -= this.ScaledChildPadding.Y * 2;
|
||||||
|
return padded;
|
||||||
|
}
|
||||||
|
}
|
||||||
public Point ScaledChildPadding => this.childPadding.Multiply(this.Scale);
|
public Point ScaledChildPadding => this.childPadding.Multiply(this.Scale);
|
||||||
|
|
||||||
public MouseClickCallback OnClicked;
|
public MouseClickCallback OnClicked;
|
||||||
|
@ -143,38 +152,42 @@ namespace MLEM.Ui.Elements {
|
||||||
this.SetAreaDirty();
|
this.SetAreaDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public T AddChild<T>(T element, int index = -1) where T : Element {
|
public T AddChild<T>(T element, int index = -1, bool propagateInfo = true) where T : Element {
|
||||||
if (index < 0 || index > this.Children.Count)
|
if (index < 0 || index > this.Children.Count)
|
||||||
index = this.Children.Count;
|
index = this.Children.Count;
|
||||||
this.Children.Insert(index, element);
|
this.Children.Insert(index, element);
|
||||||
element.Parent = this;
|
if (propagateInfo) {
|
||||||
element.PropagateRoot(this.Root);
|
element.Parent = this;
|
||||||
element.PropagateUiSystem(this.System);
|
element.PropagateRoot(this.Root);
|
||||||
|
element.PropagateUiSystem(this.System);
|
||||||
|
}
|
||||||
this.SetSortedChildrenDirty();
|
this.SetSortedChildrenDirty();
|
||||||
this.SetAreaDirty();
|
this.SetAreaDirty();
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveChild(Element element) {
|
public void RemoveChild(Element element, bool propagateInfo = true) {
|
||||||
this.Children.Remove(element);
|
this.Children.Remove(element);
|
||||||
element.Parent = null;
|
if (propagateInfo) {
|
||||||
element.PropagateRoot(null);
|
element.Parent = null;
|
||||||
element.PropagateUiSystem(null);
|
element.PropagateRoot(null);
|
||||||
|
element.PropagateUiSystem(null);
|
||||||
|
}
|
||||||
this.SetSortedChildrenDirty();
|
this.SetSortedChildrenDirty();
|
||||||
this.SetAreaDirty();
|
this.SetAreaDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void MoveToFront() {
|
public void MoveToFront() {
|
||||||
if (this.Parent != null) {
|
if (this.Parent != null) {
|
||||||
this.Parent.RemoveChild(this);
|
this.Parent.RemoveChild(this, false);
|
||||||
this.Parent.AddChild(this);
|
this.Parent.AddChild(this, -1, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void MoveToBack() {
|
public void MoveToBack() {
|
||||||
if (this.Parent != null) {
|
if (this.Parent != null) {
|
||||||
this.Parent.RemoveChild(this);
|
this.Parent.RemoveChild(this, false);
|
||||||
this.Parent.AddChild(this, 0);
|
this.Parent.AddChild(this, 0, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,15 +219,7 @@ namespace MLEM.Ui.Elements {
|
||||||
public virtual void ForceUpdateArea() {
|
public virtual void ForceUpdateArea() {
|
||||||
this.areaDirty = false;
|
this.areaDirty = false;
|
||||||
|
|
||||||
Rectangle parentArea;
|
var parentArea = this.Parent != null ? this.Parent.ChildPaddedArea : this.system.Viewport;
|
||||||
if (this.Parent != null) {
|
|
||||||
parentArea = this.Parent.area;
|
|
||||||
parentArea.Location += this.Parent.ScaledChildPadding;
|
|
||||||
parentArea.Width -= this.Parent.ScaledChildPadding.X * 2;
|
|
||||||
parentArea.Height -= this.Parent.ScaledChildPadding.Y * 2;
|
|
||||||
} else {
|
|
||||||
parentArea = this.system.GraphicsDevice.Viewport.Bounds;
|
|
||||||
}
|
|
||||||
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;
|
||||||
|
|
||||||
|
@ -338,17 +343,17 @@ namespace MLEM.Ui.Elements {
|
||||||
child.Update(time);
|
child.Update(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Draw(GameTime time, SpriteBatch batch, float alpha) {
|
public virtual void Draw(GameTime time, SpriteBatch batch, float alpha, Point offset) {
|
||||||
foreach (var child in this.SortedChildren) {
|
foreach (var child in this.SortedChildren) {
|
||||||
if (!child.IsHidden)
|
if (!child.IsHidden)
|
||||||
child.Draw(time, batch, alpha * child.DrawAlpha);
|
child.Draw(time, batch, alpha * child.DrawAlpha, offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void DrawUnbound(GameTime time, SpriteBatch batch, float alpha, float scale, BlendState blendState = null, SamplerState samplerState = null) {
|
public virtual void DrawUnbound(GameTime time, SpriteBatch batch, float alpha, BlendState blendState = null, SamplerState samplerState = null) {
|
||||||
foreach (var child in this.SortedChildren) {
|
foreach (var child in this.SortedChildren) {
|
||||||
if (!child.IsHidden)
|
if (!child.IsHidden)
|
||||||
child.DrawUnbound(time, batch, alpha * child.DrawAlpha, scale, blendState, samplerState);
|
child.DrawUnbound(time, batch, alpha * child.DrawAlpha, blendState, samplerState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,9 +19,9 @@ namespace MLEM.Ui.Elements {
|
||||||
return this.scaleToImage ? this.texture.Size : base.CalcActualSize(parentArea);
|
return this.scaleToImage ? this.texture.Size : base.CalcActualSize(parentArea);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Draw(GameTime time, SpriteBatch batch, float alpha) {
|
public override void Draw(GameTime time, SpriteBatch batch, float alpha, Point offset) {
|
||||||
batch.Draw(this.texture, this.DisplayArea, this.Color * alpha);
|
batch.Draw(this.texture, this.DisplayArea.OffsetCopy(offset), this.Color * alpha);
|
||||||
base.Draw(time, batch, alpha);
|
base.Draw(time, batch, alpha, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using Microsoft.Xna.Framework.Graphics;
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
|
using MLEM.Extensions;
|
||||||
using MLEM.Textures;
|
using MLEM.Textures;
|
||||||
using MLEM.Ui.Style;
|
using MLEM.Ui.Style;
|
||||||
|
|
||||||
|
@ -7,17 +10,107 @@ namespace MLEM.Ui.Elements {
|
||||||
public class Panel : Element {
|
public class Panel : Element {
|
||||||
|
|
||||||
public NinePatch Texture;
|
public NinePatch Texture;
|
||||||
|
public readonly ScrollBar ScrollBar;
|
||||||
|
private readonly bool scrollOverflow;
|
||||||
|
private RenderTarget2D renderTarget;
|
||||||
|
|
||||||
public Panel(Anchor anchor, Vector2 size, Point positionOffset, bool setHeightBasedOnChildren = false, NinePatch texture = null) : base(anchor, size) {
|
public Panel(Anchor anchor, Vector2 size, Point positionOffset, bool setHeightBasedOnChildren = false, bool scrollOverflow = false, Point? scrollerSize = null) : base(anchor, size) {
|
||||||
this.Texture = texture;
|
|
||||||
this.PositionOffset = positionOffset;
|
this.PositionOffset = positionOffset;
|
||||||
this.SetHeightBasedOnChildren = setHeightBasedOnChildren;
|
this.SetHeightBasedOnChildren = setHeightBasedOnChildren;
|
||||||
|
this.scrollOverflow = scrollOverflow;
|
||||||
this.ChildPadding = new Point(5);
|
this.ChildPadding = new Point(5);
|
||||||
|
|
||||||
|
if (scrollOverflow) {
|
||||||
|
var scrollSize = scrollerSize ?? Point.Zero;
|
||||||
|
this.ScrollBar = new ScrollBar(Anchor.TopRight, new Vector2(scrollSize.X, 1), scrollSize.Y, 0) {
|
||||||
|
StepPerScroll = 10,
|
||||||
|
OnValueChanged = (element, value) => {
|
||||||
|
var firstChild = this.Children[0];
|
||||||
|
// if the first child is the scrollbar, there are no other children
|
||||||
|
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());
|
||||||
|
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.ChildPadding += new Point(scrollSize.X, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Draw(GameTime time, SpriteBatch batch, float alpha) {
|
public override void ForceUpdateArea() {
|
||||||
batch.Draw(this.Texture, this.DisplayArea, Color.White * alpha, this.Scale);
|
if (this.scrollOverflow) {
|
||||||
base.Draw(time, batch, alpha);
|
// sanity check
|
||||||
|
if (this.SetHeightBasedOnChildren)
|
||||||
|
throw new NotSupportedException("A panel can't both set height based on children and scroll overflow");
|
||||||
|
foreach (var child in this.Children) {
|
||||||
|
if (child != this.ScrollBar && child.Anchor < Anchor.AutoLeft)
|
||||||
|
throw new NotSupportedException($"A panel that handles overflow can't contain non-automatic anchors ({child})");
|
||||||
|
if (child is Panel panel && panel.scrollOverflow)
|
||||||
|
throw new NotSupportedException($"A panel that scrolls overflow cannot contain another panel that scrolls overflow ({child})");
|
||||||
|
}
|
||||||
|
|
||||||
|
// move the scrollbar to the front so it isn't used for auto-aligning
|
||||||
|
this.ScrollBar.MoveToFront();
|
||||||
|
}
|
||||||
|
|
||||||
|
base.ForceUpdateArea();
|
||||||
|
|
||||||
|
if (this.scrollOverflow) {
|
||||||
|
var firstChild = this.Children[0];
|
||||||
|
// if the first child is the scrollbar, then we know there's no other children
|
||||||
|
if (firstChild == this.ScrollBar)
|
||||||
|
return;
|
||||||
|
var lastChild = this.Children[this.Children.Count - 2];
|
||||||
|
// the max value of the scrollbar is the amount of non-scaled pixels taken up by overflowing components
|
||||||
|
var childrenHeight = lastChild.Area.Bottom - firstChild.Area.Top;
|
||||||
|
this.ScrollBar.MaxValue = (childrenHeight - this.Area.Height) / this.Scale + this.ChildPadding.Y * 2;
|
||||||
|
|
||||||
|
// update the render target
|
||||||
|
var targetArea = this.GetRenderTargetArea();
|
||||||
|
if (this.renderTarget == null || targetArea.Width != this.renderTarget.Width || targetArea.Height != this.renderTarget.Height) {
|
||||||
|
if (this.renderTarget != null)
|
||||||
|
this.renderTarget.Dispose();
|
||||||
|
this.renderTarget = new RenderTarget2D(this.System.GraphicsDevice, targetArea.Width, targetArea.Height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Draw(GameTime time, SpriteBatch batch, float alpha, Point offset) {
|
||||||
|
batch.Draw(this.Texture, this.DisplayArea.OffsetCopy(offset), Color.White * alpha, this.Scale);
|
||||||
|
// if we handle overflow, draw using the render target in DrawUnbound
|
||||||
|
if (!this.scrollOverflow) {
|
||||||
|
base.Draw(time, batch, alpha, offset);
|
||||||
|
} 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().OffsetCopy(offset), Color.White);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void DrawUnbound(GameTime time, SpriteBatch batch, float alpha, BlendState blendState = null, SamplerState samplerState = null) {
|
||||||
|
if (this.scrollOverflow) {
|
||||||
|
// draw children onto the render target
|
||||||
|
batch.GraphicsDevice.SetRenderTarget(this.renderTarget);
|
||||||
|
batch.GraphicsDevice.Clear(Color.Transparent);
|
||||||
|
batch.Begin(SpriteSortMode.Deferred, blendState, samplerState);
|
||||||
|
// offset children by the render target's location
|
||||||
|
var area = this.GetRenderTargetArea();
|
||||||
|
base.Draw(time, batch, alpha, new Point(-area.X, -area.Y));
|
||||||
|
batch.End();
|
||||||
|
batch.GraphicsDevice.SetRenderTarget(null);
|
||||||
|
}
|
||||||
|
base.DrawUnbound(time, batch, alpha, blendState, samplerState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Rectangle GetRenderTargetArea() {
|
||||||
|
var area = this.ChildPaddedArea;
|
||||||
|
area.X = this.DisplayArea.X;
|
||||||
|
area.Width = this.DisplayArea.Width;
|
||||||
|
return area;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void InitStyle(UiStyle style) {
|
protected override void InitStyle(UiStyle style) {
|
||||||
|
|
|
@ -46,19 +46,18 @@ namespace MLEM.Ui.Elements {
|
||||||
return new Point(size.X, height.Ceil());
|
return new Point(size.X, height.Ceil());
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Draw(GameTime time, SpriteBatch batch, float alpha) {
|
public override void Draw(GameTime time, SpriteBatch batch, float alpha, Point offset) {
|
||||||
base.Draw(time, batch, alpha);
|
|
||||||
|
|
||||||
var pos = this.DisplayArea.Location.ToVector2();
|
var pos = this.DisplayArea.Location.ToVector2();
|
||||||
var offset = new Vector2();
|
var off = offset.ToVector2();
|
||||||
foreach (var line in this.splitText) {
|
foreach (var line in this.splitText) {
|
||||||
if (this.centerText) {
|
if (this.centerText) {
|
||||||
this.font.DrawCenteredString(batch, line, pos + offset + 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, Color.White * alpha);
|
||||||
} else {
|
} else {
|
||||||
this.font.DrawString(batch, line, pos + offset, Color.White * alpha, 0, Vector2.Zero, this.TextScale * this.Scale, SpriteEffects.None, 0);
|
this.font.DrawString(batch, line, pos + off, Color.White * alpha, 0, Vector2.Zero, this.TextScale * this.Scale, SpriteEffects.None, 0);
|
||||||
}
|
}
|
||||||
offset.Y += this.lineHeight + 1;
|
off.Y += this.lineHeight + 1;
|
||||||
}
|
}
|
||||||
|
base.Draw(time, batch, alpha, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void InitStyle(UiStyle style) {
|
protected override void InitStyle(UiStyle style) {
|
||||||
|
|
71
MLEM.Ui/Elements/ScrollBar.cs
Normal file
71
MLEM.Ui/Elements/ScrollBar.cs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
using System;
|
||||||
|
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 ScrollBar : Element {
|
||||||
|
|
||||||
|
public NinePatch Background;
|
||||||
|
public NinePatch ScrollerTexture;
|
||||||
|
public Color HoveredColor;
|
||||||
|
public Point ScrollerOffset;
|
||||||
|
public Point ScrollerSize;
|
||||||
|
private float maxValue;
|
||||||
|
public float MaxValue {
|
||||||
|
get => this.maxValue;
|
||||||
|
set {
|
||||||
|
this.maxValue = Math.Max(0, value);
|
||||||
|
// force current value to be clamped
|
||||||
|
this.CurrentValue = this.currValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private float currValue;
|
||||||
|
public float CurrentValue {
|
||||||
|
get => this.currValue;
|
||||||
|
set {
|
||||||
|
var val = MathHelper.Clamp(value, 0, this.maxValue);
|
||||||
|
if (this.currValue != val) {
|
||||||
|
this.currValue = val;
|
||||||
|
this.OnValueChanged?.Invoke(this, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public float StepPerScroll = 1;
|
||||||
|
public ValueChanged OnValueChanged;
|
||||||
|
|
||||||
|
public ScrollBar(Anchor anchor, Vector2 size, int scrollerHeight, float maxValue) : base(anchor, size) {
|
||||||
|
this.maxValue = maxValue;
|
||||||
|
this.ScrollerSize = new Point(size.X.Floor(), scrollerHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(GameTime time) {
|
||||||
|
base.Update(time);
|
||||||
|
var scroll = this.Input.LastScrollWheel - this.Input.ScrollWheel;
|
||||||
|
if (scroll != 0)
|
||||||
|
this.CurrentValue += this.StepPerScroll * Math.Sign(scroll);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Draw(GameTime time, SpriteBatch batch, float alpha, Point offset) {
|
||||||
|
batch.Draw(this.Background, this.DisplayArea.OffsetCopy(offset), Color.White * alpha, this.Scale);
|
||||||
|
|
||||||
|
var scrollerPos = new Point(this.DisplayArea.X + offset.X + this.ScrollerOffset.X, this.DisplayArea.Y + offset.Y + this.ScrollerOffset.Y);
|
||||||
|
var scrollerYOffset = (this.currValue / this.maxValue * (this.DisplayArea.Height - this.ScrollerSize.Y * this.Scale)).Floor();
|
||||||
|
var scrollerRect = new Rectangle(scrollerPos + new Point(0, scrollerYOffset), this.ScrollerSize.Multiply(this.Scale));
|
||||||
|
batch.Draw(this.ScrollerTexture, scrollerRect, Color.White * alpha, this.Scale);
|
||||||
|
base.Draw(time, batch, alpha, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void InitStyle(UiStyle style) {
|
||||||
|
base.InitStyle(style);
|
||||||
|
this.Background = style.ScrollBarBackground;
|
||||||
|
this.ScrollerTexture = style.ScrollBarScrollerTexture;
|
||||||
|
this.HoveredColor = style.ScrollBarHoveredColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate void ValueChanged(Element element, float value);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ using System.Text;
|
||||||
using Microsoft.Xna.Framework;
|
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.Extensions;
|
||||||
using MLEM.Font;
|
using MLEM.Font;
|
||||||
using MLEM.Textures;
|
using MLEM.Textures;
|
||||||
using MLEM.Ui.Style;
|
using MLEM.Ui.Style;
|
||||||
|
@ -67,7 +68,7 @@ namespace MLEM.Ui.Elements {
|
||||||
this.caretBlinkTimer = 0;
|
this.caretBlinkTimer = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Draw(GameTime time, SpriteBatch batch, float alpha) {
|
public override void Draw(GameTime time, SpriteBatch batch, float alpha, Point offset) {
|
||||||
var tex = this.Texture;
|
var tex = this.Texture;
|
||||||
var color = Color.White * alpha;
|
var color = Color.White * alpha;
|
||||||
if (this.IsMouseOver) {
|
if (this.IsMouseOver) {
|
||||||
|
@ -75,11 +76,11 @@ namespace MLEM.Ui.Elements {
|
||||||
tex = this.HoveredTexture;
|
tex = this.HoveredTexture;
|
||||||
color = this.HoveredColor * alpha;
|
color = this.HoveredColor * alpha;
|
||||||
}
|
}
|
||||||
batch.Draw(tex, this.DisplayArea, color, this.Scale);
|
batch.Draw(tex, this.DisplayArea.OffsetCopy(offset), color, this.Scale);
|
||||||
var caret = this.IsSelected && this.caretBlinkTimer >= 0.5F ? "|" : "";
|
var caret = this.IsSelected && this.caretBlinkTimer >= 0.5F ? "|" : "";
|
||||||
var text = this.Text.ToString(this.textStartIndex, this.Text.Length - this.textStartIndex) + caret;
|
var text = this.Text.ToString(this.textStartIndex, this.Text.Length - this.textStartIndex) + caret;
|
||||||
this.font.DrawCenteredString(batch, text, this.DisplayArea.Location.ToVector2() + new Vector2(this.TextOffsetX * this.Scale, this.DisplayArea.Height / 2), this.TextScale * this.Scale, Color.White * alpha, false, true);
|
this.font.DrawCenteredString(batch, text, this.DisplayArea.Location.ToVector2() + new Vector2(offset.X + this.TextOffsetX * this.Scale, offset.Y + this.DisplayArea.Height / 2), this.TextScale * this.Scale, Color.White * alpha, false, true);
|
||||||
base.Draw(time, batch, alpha);
|
base.Draw(time, batch, alpha, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void InitStyle(UiStyle style) {
|
protected override void InitStyle(UiStyle style) {
|
||||||
|
|
|
@ -12,6 +12,9 @@ namespace MLEM.Ui.Style {
|
||||||
public NinePatch TextFieldTexture;
|
public NinePatch TextFieldTexture;
|
||||||
public NinePatch TextFieldHoveredTexture;
|
public NinePatch TextFieldHoveredTexture;
|
||||||
public Color TextFieldHoveredColor;
|
public Color TextFieldHoveredColor;
|
||||||
|
public NinePatch ScrollBarBackground;
|
||||||
|
public NinePatch ScrollBarScrollerTexture;
|
||||||
|
public Color ScrollBarHoveredColor;
|
||||||
public IGenericFont Font;
|
public IGenericFont Font;
|
||||||
public float TextScale = 1;
|
public float TextScale = 1;
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,9 @@ namespace MLEM.Ui.Style {
|
||||||
this.PanelTexture = GenerateTexture(batch, Color.Gray);
|
this.PanelTexture = GenerateTexture(batch, Color.Gray);
|
||||||
this.TextFieldTexture = GenerateTexture(batch, Color.MediumBlue);
|
this.TextFieldTexture = GenerateTexture(batch, Color.MediumBlue);
|
||||||
this.TextFieldHoveredColor = Color.LightGray;
|
this.TextFieldHoveredColor = Color.LightGray;
|
||||||
|
this.ScrollBarBackground = GenerateTexture(batch, Color.LightBlue);
|
||||||
|
this.ScrollBarScrollerTexture = GenerateTexture(batch, Color.Blue);
|
||||||
|
this.ScrollBarHoveredColor = Color.LightGray;
|
||||||
this.Font = new EmptyFont();
|
this.Font = new EmptyFont();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ namespace MLEM.Ui {
|
||||||
public class UiSystem {
|
public class UiSystem {
|
||||||
|
|
||||||
public readonly GraphicsDevice GraphicsDevice;
|
public readonly GraphicsDevice GraphicsDevice;
|
||||||
|
public Rectangle Viewport { get; private set; }
|
||||||
private readonly List<RootElement> rootElements = new List<RootElement>();
|
private readonly List<RootElement> rootElements = new List<RootElement>();
|
||||||
public readonly InputHandler InputHandler;
|
public readonly InputHandler InputHandler;
|
||||||
private readonly bool isInputOurs;
|
private readonly bool isInputOurs;
|
||||||
|
@ -47,8 +48,10 @@ namespace MLEM.Ui {
|
||||||
this.InputHandler = inputHandler ?? new InputHandler();
|
this.InputHandler = inputHandler ?? new InputHandler();
|
||||||
this.isInputOurs = inputHandler == null;
|
this.isInputOurs = inputHandler == null;
|
||||||
this.style = style;
|
this.style = style;
|
||||||
|
this.Viewport = device.Viewport.Bounds;
|
||||||
|
|
||||||
window.ClientSizeChanged += (sender, args) => {
|
window.ClientSizeChanged += (sender, args) => {
|
||||||
|
this.Viewport = device.Viewport.Bounds;
|
||||||
foreach (var root in this.rootElements)
|
foreach (var root in this.rootElements)
|
||||||
root.Element.ForceUpdateArea();
|
root.Element.ForceUpdateArea();
|
||||||
};
|
};
|
||||||
|
@ -92,16 +95,16 @@ namespace MLEM.Ui {
|
||||||
|
|
||||||
public void Draw(GameTime time, SpriteBatch batch) {
|
public void Draw(GameTime time, SpriteBatch batch) {
|
||||||
foreach (var root in this.rootElements) {
|
foreach (var root in this.rootElements) {
|
||||||
if (root.Element.IsHidden)
|
if (!root.Element.IsHidden)
|
||||||
continue;
|
root.Element.DrawUnbound(time, batch, this.DrawAlpha * root.Element.DrawAlpha, this.BlendState, this.SamplerState);
|
||||||
batch.Begin(SpriteSortMode.Deferred, this.BlendState, this.SamplerState);
|
|
||||||
root.Element.Draw(time, batch, this.DrawAlpha * root.Element.DrawAlpha);
|
|
||||||
batch.End();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var root in this.rootElements) {
|
foreach (var root in this.rootElements) {
|
||||||
if (!root.Element.IsHidden)
|
if (root.Element.IsHidden)
|
||||||
root.Element.DrawUnbound(time, batch, this.DrawAlpha * root.Element.DrawAlpha, root.ActualScale, this.BlendState, this.SamplerState);
|
continue;
|
||||||
|
batch.Begin(SpriteSortMode.Deferred, this.BlendState, this.SamplerState);
|
||||||
|
root.Element.Draw(time, batch, this.DrawAlpha * root.Element.DrawAlpha, Point.Zero);
|
||||||
|
batch.End();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,5 +28,11 @@ namespace MLEM.Extensions {
|
||||||
return new Point((point.X * f).Floor(), (point.Y * f).Floor());
|
return new Point((point.X * f).Floor(), (point.Y * f).Floor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Rectangle OffsetCopy(this Rectangle rect, Point offset) {
|
||||||
|
rect.X += offset.X;
|
||||||
|
rect.Y += offset.Y;
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue