mirror of
https://github.com/AlexMacocian/MonoGame.Extended.git
synced 2026-07-25 08:22:15 +00:00
Gui dialogs (#410)
* started refactoring for gui windows * a bit more work on gui windows * fixed some bugs and broken build stuff * fixed some bugs in gui controls * a little code cleanup * got gui dialogs positioning and experimenting with named styles by type * apply control styles directly in the constructor of controls * got everything compiling again * all gui controls have skinnable constructors * rework uniform grid desired size calculations * turns out gui layouts are complicated * include pdb files in nuget packages * fixed a bug in find nearest glyph * custom control styling * added a range constructor overload that takes one value * rethinking control desired size behaviour * made particle emitters more editable at runtime * moved the auto trigger code to the particle effect instead of the emitters * tweaks * changed the particle modifer interface to an abstract base with an optional name * changed the particle modifer interface to an abstract base with an optional name * an attempt at creating a gui combo box * rolled back the nested control experiment * pretty good (but not perfect) combo boxes * tweaks * name property bindings on lists * removed and cleaned up the hacks from the gui combo box * minor combo box styling tweaks * minor bug fix in gui combo box * experiments with data binding * fixed a mistake on the texture region service interface * working on the ability to serialize particles * finished particle serialization * more robust range deserialization * added editor browable attributes * fixed some broken demos * made the particle modifiers editable at runtime * fixed some issues with particle serialization * fixed some issues with interpolators
This commit is contained in:
@@ -1,26 +1,19 @@
|
||||
using System;
|
||||
using MonoGame.Extended.TextureAtlases;
|
||||
|
||||
namespace MonoGame.Extended.Gui.Controls
|
||||
{
|
||||
public class GuiButton : GuiControl
|
||||
{
|
||||
public GuiButton()
|
||||
: this(null)
|
||||
: base(null)
|
||||
{
|
||||
}
|
||||
|
||||
public GuiButton(TextureRegion2D backgroundRegion)
|
||||
: base(backgroundRegion)
|
||||
public GuiButton(GuiSkin skin)
|
||||
: base(skin)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Size2 CalculateDesiredSize(IGuiContext context, Size2 availableSize)
|
||||
{
|
||||
var size = base.CalculateDesiredSize(context, availableSize);
|
||||
return size;
|
||||
}
|
||||
|
||||
public event EventHandler Clicked;
|
||||
public event EventHandler PressedStateChanged;
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ namespace MonoGame.Extended.Gui.Controls
|
||||
{
|
||||
}
|
||||
|
||||
public GuiCanvas(TextureRegion2D backgroundRegion)
|
||||
: base(backgroundRegion)
|
||||
public GuiCanvas(GuiSkin skin)
|
||||
: base(skin)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using MonoGame.Extended.Shapes;
|
||||
using MonoGame.Extended.TextureAtlases;
|
||||
|
||||
namespace MonoGame.Extended.Gui.Controls
|
||||
{
|
||||
@@ -11,8 +9,8 @@ namespace MonoGame.Extended.Gui.Controls
|
||||
{
|
||||
}
|
||||
|
||||
public GuiCheckBox(TextureRegion2D backgroundRegion)
|
||||
: base(backgroundRegion)
|
||||
public GuiCheckBox(GuiSkin skin)
|
||||
: base(skin)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using MonoGame.Extended.Input.InputListeners;
|
||||
using MonoGame.Extended.TextureAtlases;
|
||||
|
||||
namespace MonoGame.Extended.Gui.Controls
|
||||
{
|
||||
public class GuiComboBox : GuiItemsControl
|
||||
{
|
||||
public GuiComboBox()
|
||||
: this(null)
|
||||
{
|
||||
}
|
||||
|
||||
public GuiComboBox(GuiSkin skin)
|
||||
: base(skin)
|
||||
{
|
||||
}
|
||||
|
||||
public bool IsOpen { get; set; }
|
||||
public TextureRegion2D DropDownRegion { get; set; }
|
||||
public Color DropDownColor { get; set; } = Color.White;
|
||||
|
||||
public override void OnKeyPressed(IGuiContext context, KeyboardEventArgs args)
|
||||
{
|
||||
base.OnKeyPressed(context, args);
|
||||
|
||||
if (args.Key == Keys.Enter)
|
||||
IsOpen = false;
|
||||
}
|
||||
|
||||
public override void OnPointerDown(IGuiContext context, GuiPointerEventArgs args)
|
||||
{
|
||||
base.OnPointerDown(context, args);
|
||||
|
||||
IsOpen = !IsOpen;
|
||||
}
|
||||
|
||||
protected override Rectangle GetContentRectangle(IGuiContext context)
|
||||
{
|
||||
return GetDropDownRectangle(context);
|
||||
}
|
||||
|
||||
public override bool Contains(IGuiContext context, Point point)
|
||||
{
|
||||
return base.Contains(context, point) || IsOpen && GetContentRectangle(context).Contains(point);
|
||||
}
|
||||
|
||||
protected override Size2 CalculateDesiredSize(IGuiContext context, Size2 availableSize)
|
||||
{
|
||||
var width = 0f;
|
||||
var height = 0f;
|
||||
|
||||
foreach (var item in Items)
|
||||
{
|
||||
var itemSize = GetItemSize(context, availableSize, item);
|
||||
|
||||
if (itemSize.Width > width)
|
||||
width = itemSize.Width;
|
||||
|
||||
if (itemSize.Height > height)
|
||||
height = itemSize.Height;
|
||||
}
|
||||
|
||||
return new Size2(width + ClipPadding.Size.Width, height + ClipPadding.Size.Height);
|
||||
}
|
||||
|
||||
protected override void DrawBackground(IGuiContext context, IGuiRenderer renderer, float deltaSeconds)
|
||||
{
|
||||
base.DrawBackground(context, renderer, deltaSeconds);
|
||||
|
||||
if (IsOpen)
|
||||
{
|
||||
var dropDownRectangle = GetContentRectangle(context);
|
||||
|
||||
if (DropDownRegion != null)
|
||||
renderer.DrawRegion(DropDownRegion, dropDownRectangle, DropDownColor);
|
||||
else
|
||||
renderer.FillRectangle(dropDownRectangle, DropDownColor);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void DrawForeground(IGuiContext context, IGuiRenderer renderer, float deltaSeconds, TextInfo textInfo)
|
||||
{
|
||||
var selectedTextInfo = GetItemTextInfo(context, ClippingRectangle, SelectedItem, ClippingRectangle);
|
||||
base.DrawForeground(context, renderer, deltaSeconds, selectedTextInfo);
|
||||
|
||||
if (IsOpen)
|
||||
DrawItemList(context, renderer);
|
||||
}
|
||||
|
||||
private Rectangle GetDropDownRectangle(IGuiContext context)
|
||||
{
|
||||
var dropDownRectangle = BoundingRectangle;
|
||||
dropDownRectangle.Y = dropDownRectangle.Y + dropDownRectangle.Height;
|
||||
var height = 0f;
|
||||
|
||||
foreach (var item in Items)
|
||||
{
|
||||
var itemSize = GetItemSize(context, new Size2(BoundingRectangle.Width, 100), item);
|
||||
height += itemSize.Height;
|
||||
}
|
||||
|
||||
dropDownRectangle.Height = (int)height;
|
||||
return dropDownRectangle;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +1,35 @@
|
||||
using System.ComponentModel;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using Microsoft.Xna.Framework;
|
||||
using MonoGame.Extended.BitmapFonts;
|
||||
using MonoGame.Extended.Input.InputListeners;
|
||||
using MonoGame.Extended.TextureAtlases;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace MonoGame.Extended.Gui.Controls
|
||||
{
|
||||
public abstract class GuiControl : IMovable, ISizable
|
||||
public abstract class GuiControl : GuiElement<GuiControl>, IMovable, ISizable, IRectangular
|
||||
{
|
||||
protected GuiControl()
|
||||
: this(skin: null)
|
||||
{
|
||||
}
|
||||
|
||||
protected GuiControl(GuiSkin skin)
|
||||
{
|
||||
Skin = skin;
|
||||
Color = Color.White;
|
||||
TextColor = Color.White;
|
||||
IsEnabled = true;
|
||||
IsVisible = true;
|
||||
Controls = new GuiControlCollection(this);
|
||||
Origin = Vector2.Zero;
|
||||
}
|
||||
|
||||
protected GuiControl(TextureRegion2D backgroundRegion)
|
||||
: this()
|
||||
{
|
||||
BackgroundRegion = backgroundRegion;
|
||||
var style = skin?.GetStyle(GetType());
|
||||
style?.Apply(this);
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[JsonIgnore]
|
||||
public GuiControl Parent { get; internal set; }
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[JsonIgnore]
|
||||
public Rectangle BoundingRectangle
|
||||
{
|
||||
get
|
||||
{
|
||||
var offset = Vector2.Zero;
|
||||
|
||||
if (Parent != null)
|
||||
offset = Parent.BoundingRectangle.Location.ToVector2();
|
||||
|
||||
return new Rectangle((offset + Position - Size * Origin).ToPoint(), (Point)Size);
|
||||
}
|
||||
}
|
||||
public GuiSkin Skin { get; }
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Thickness Margin { get; set; }
|
||||
@@ -63,69 +50,76 @@ namespace MonoGame.Extended.Gui.Controls
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public bool IsFocused { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
public Vector2 Position { get; set; }
|
||||
public Vector2 Offset { get; set; }
|
||||
public Vector2 Origin { get; set; }
|
||||
public Color Color { get; set; }
|
||||
public BitmapFont Font { get; set; }
|
||||
public string Text { get; set; }
|
||||
public Color TextColor { get; set; }
|
||||
public Vector2 TextOffset { get; set; }
|
||||
public GuiControlCollection Controls { get; }
|
||||
public HorizontalAlignment HorizontalAlignment { get; set; } = HorizontalAlignment.Stretch;
|
||||
public VerticalAlignment VerticalAlignment { get; set; } = VerticalAlignment.Stretch;
|
||||
public TextureRegion2D BackgroundRegion { get; set; }
|
||||
public HorizontalAlignment HorizontalAlignment { get; set; } = HorizontalAlignment.Centre;
|
||||
public VerticalAlignment VerticalAlignment { get; set; } = VerticalAlignment.Centre;
|
||||
public HorizontalAlignment HorizontalTextAlignment { get; set; } = HorizontalAlignment.Centre;
|
||||
public VerticalAlignment VerticalTextAlignment { get; set; } = VerticalAlignment.Centre;
|
||||
|
||||
public Size2 Size { get; set; }
|
||||
|
||||
public float Width
|
||||
private string _text;
|
||||
public string Text
|
||||
{
|
||||
get { return Size.Width; }
|
||||
set { Size = new Size2(value, Size.Height); }
|
||||
get { return _text; }
|
||||
set
|
||||
{
|
||||
if (_text != value)
|
||||
{
|
||||
_text = value;
|
||||
OnTextChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float Height
|
||||
protected virtual void OnTextChanged()
|
||||
{
|
||||
get { return Size.Height; }
|
||||
set { Size = new Size2(Size.Width, value); }
|
||||
TextChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public event EventHandler TextChanged;
|
||||
|
||||
public Size2 GetDesiredSize(IGuiContext context, Size2 availableSize)
|
||||
{
|
||||
return CalculateDesiredSize(context, availableSize);
|
||||
}
|
||||
|
||||
protected virtual Size2 CalculateDesiredSize(IGuiContext context, Size2 availableSize)
|
||||
{
|
||||
var minimumSize = Size2.Empty;
|
||||
var fixedSize = Size;
|
||||
var desiredSize = CalculateDesiredSize(context, availableSize);
|
||||
var ninePatch = BackgroundRegion as NinePatchRegion2D;
|
||||
|
||||
if (ninePatch != null)
|
||||
{
|
||||
minimumSize.Width += ninePatch.LeftPadding + ninePatch.RightPadding;
|
||||
minimumSize.Height += ninePatch.TopPadding + ninePatch.BottomPadding;
|
||||
desiredSize.Width = Math.Max(desiredSize.Width, ninePatch.Padding.Size.Width);
|
||||
desiredSize.Height = Math.Max(desiredSize.Height, ninePatch.Padding.Size.Height);
|
||||
}
|
||||
else if(BackgroundRegion != null)
|
||||
else if (BackgroundRegion != null)
|
||||
{
|
||||
minimumSize.Width += BackgroundRegion.Width;
|
||||
minimumSize.Height += BackgroundRegion.Height;
|
||||
desiredSize.Width = Math.Max(desiredSize.Width, BackgroundRegion.Width);
|
||||
desiredSize.Height = Math.Max(desiredSize.Height, BackgroundRegion.Height);
|
||||
}
|
||||
|
||||
var font = Font ?? context.DefaultFont;
|
||||
|
||||
if (font != null && !string.IsNullOrEmpty(Text))
|
||||
if (font != null && Text != null)
|
||||
{
|
||||
var textSize = font.MeasureString(Text);
|
||||
minimumSize.Width += textSize.Width;
|
||||
minimumSize.Height += textSize.Height;
|
||||
desiredSize.Width += textSize.Width;
|
||||
desiredSize.Height += textSize.Height;
|
||||
}
|
||||
|
||||
desiredSize.Width = Math.Min(desiredSize.Width, availableSize.Width);
|
||||
desiredSize.Height = Math.Min(desiredSize.Height, availableSize.Height);
|
||||
|
||||
// ReSharper disable CompareOfFloatsByEqualityOperator
|
||||
return new Size2(Size.Width == 0 ? minimumSize.Width : Size.Width, Size.Height == 0 ? minimumSize.Height : Size.Height);
|
||||
return new Size2(fixedSize.Width == 0 ? desiredSize.Width : fixedSize.Width, fixedSize.Height == 0 ? desiredSize.Height : fixedSize.Height);
|
||||
// ReSharper restore CompareOfFloatsByEqualityOperator
|
||||
}
|
||||
|
||||
protected virtual Size2 CalculateDesiredSize(IGuiContext context, Size2 availableSize)
|
||||
{
|
||||
return Size2.Empty;
|
||||
}
|
||||
|
||||
private bool _isEnabled;
|
||||
public bool IsEnabled
|
||||
{
|
||||
@@ -171,26 +165,31 @@ namespace MonoGame.Extended.Gui.Controls
|
||||
HoverStyle?.Revert(this);
|
||||
}
|
||||
|
||||
public void Draw(IGuiContext context, IGuiRenderer renderer, float deltaSeconds)
|
||||
public virtual bool Contains(IGuiContext context, Point point)
|
||||
{
|
||||
DrawBackground(context, renderer, deltaSeconds);
|
||||
DrawForeground(context, renderer, deltaSeconds, GetTextInfo(context, Text, BoundingRectangle, HorizontalAlignment.Centre, VerticalAlignment.Centre));
|
||||
return BoundingRectangle.Contains(point);
|
||||
}
|
||||
|
||||
protected TextInfo GetTextInfo(IGuiContext context, string text, Rectangle targetRectangle, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment)
|
||||
public override void Draw(IGuiContext context, IGuiRenderer renderer, float deltaSeconds)
|
||||
{
|
||||
DrawBackground(context, renderer, deltaSeconds);
|
||||
DrawForeground(context, renderer, deltaSeconds, GetTextInfo(context, Text, BoundingRectangle, HorizontalTextAlignment, VerticalTextAlignment));
|
||||
}
|
||||
|
||||
protected TextInfo GetTextInfo(IGuiContext context, string text, Rectangle targetRectangle, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment, Rectangle? clippingRectangle = null)
|
||||
{
|
||||
var font = Font ?? context.DefaultFont;
|
||||
var textSize = font.GetStringRectangle(text ?? string.Empty, Vector2.Zero).Size;
|
||||
var destinationRectangle = GuiLayoutHelper.AlignRectangle(horizontalAlignment, verticalAlignment, textSize, targetRectangle);
|
||||
var textPosition = destinationRectangle.Location.ToVector2();
|
||||
var textInfo = new TextInfo(text, font, textPosition, textSize, TextColor, ClippingRectangle);
|
||||
var textInfo = new TextInfo(text, font, textPosition, textSize, TextColor, clippingRectangle ?? ClippingRectangle);
|
||||
return textInfo;
|
||||
}
|
||||
|
||||
protected virtual void DrawBackground(IGuiContext context, IGuiRenderer renderer, float deltaSeconds)
|
||||
{
|
||||
renderer.DrawRegion(BackgroundRegion, BoundingRectangle, Color);
|
||||
//renderer.DrawRectangle(BoundingRectangle, Color.Red);
|
||||
if (BackgroundRegion != null)
|
||||
renderer.DrawRegion(BackgroundRegion, BoundingRectangle, Color);
|
||||
}
|
||||
|
||||
protected virtual void DrawForeground(IGuiContext context, IGuiRenderer renderer, float deltaSeconds, TextInfo textInfo)
|
||||
@@ -203,7 +202,7 @@ namespace MonoGame.Extended.Gui.Controls
|
||||
{
|
||||
public TextInfo(string text, BitmapFont font, Vector2 position, Vector2 size, Color color, Rectangle? clippingRectangle)
|
||||
{
|
||||
Text = text;
|
||||
Text = text ?? string.Empty;
|
||||
Font = font;
|
||||
Size = size;
|
||||
Color = color;
|
||||
|
||||
@@ -1,87 +1,15 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MonoGame.Extended.Gui.Controls
|
||||
namespace MonoGame.Extended.Gui.Controls
|
||||
{
|
||||
public class GuiControlCollection : IList<GuiControl>
|
||||
public class GuiControlCollection : GuiElementCollection<GuiControl, GuiControl>
|
||||
{
|
||||
public GuiControlCollection()
|
||||
: base(null)
|
||||
{
|
||||
}
|
||||
|
||||
public GuiControlCollection(GuiControl parent)
|
||||
: base(parent)
|
||||
{
|
||||
_parent = parent;
|
||||
}
|
||||
|
||||
private readonly GuiControl _parent;
|
||||
private readonly List<GuiControl> _list = new List<GuiControl>();
|
||||
|
||||
public IEnumerator<GuiControl> GetEnumerator()
|
||||
{
|
||||
return _list.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable) _list).GetEnumerator();
|
||||
}
|
||||
|
||||
public void Add(GuiControl item)
|
||||
{
|
||||
item.Parent = _parent;
|
||||
_list.Add(item);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
foreach (var control in _list)
|
||||
control.Parent = null;
|
||||
|
||||
_list.Clear();
|
||||
}
|
||||
|
||||
public bool Contains(GuiControl item)
|
||||
{
|
||||
return _list.Contains(item);
|
||||
}
|
||||
|
||||
public void CopyTo(GuiControl[] array, int arrayIndex)
|
||||
{
|
||||
_list.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
public bool Remove(GuiControl item)
|
||||
{
|
||||
item.Parent = null;
|
||||
return _list.Remove(item);
|
||||
}
|
||||
|
||||
public int Count => _list.Count;
|
||||
|
||||
public bool IsReadOnly => ((ICollection<GuiControl>) _list).IsReadOnly;
|
||||
|
||||
public int IndexOf(GuiControl item)
|
||||
{
|
||||
return _list.IndexOf(item);
|
||||
}
|
||||
|
||||
public void Insert(int index, GuiControl item)
|
||||
{
|
||||
item.Parent = _parent;
|
||||
_list.Insert(index, item);
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
_list[index].Parent = null;
|
||||
_list.RemoveAt(index);
|
||||
}
|
||||
|
||||
public GuiControl this[int index]
|
||||
{
|
||||
get { return _list[index]; }
|
||||
set { _list[index] = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,9 @@ namespace MonoGame.Extended.Gui.Controls
|
||||
: this(null)
|
||||
{
|
||||
}
|
||||
|
||||
public GuiDialog(TextureRegion2D backgroundRegion)
|
||||
: base(backgroundRegion)
|
||||
|
||||
public GuiDialog(GuiSkin skin)
|
||||
: base(skin)
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Centre;
|
||||
VerticalAlignment = VerticalAlignment.Centre;
|
||||
|
||||
@@ -7,10 +7,16 @@ namespace MonoGame.Extended.Gui.Controls
|
||||
public GuiImage()
|
||||
{
|
||||
}
|
||||
|
||||
public GuiImage(TextureRegion2D backgroundRegion)
|
||||
: base(backgroundRegion)
|
||||
|
||||
public GuiImage(GuiSkin skin)
|
||||
: base(skin)
|
||||
{
|
||||
}
|
||||
|
||||
public GuiImage(GuiSkin skin, TextureRegion2D image)
|
||||
: base(skin)
|
||||
{
|
||||
BackgroundRegion = image;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using MonoGame.Extended.Input.InputListeners;
|
||||
|
||||
namespace MonoGame.Extended.Gui.Controls
|
||||
{
|
||||
public abstract class GuiItemsControl : GuiControl
|
||||
{
|
||||
protected GuiItemsControl()
|
||||
: this(null)
|
||||
{
|
||||
}
|
||||
|
||||
protected GuiItemsControl(GuiSkin skin)
|
||||
: base(skin)
|
||||
{
|
||||
}
|
||||
|
||||
private int _selectedIndex = -1;
|
||||
public virtual int SelectedIndex
|
||||
{
|
||||
get { return _selectedIndex; }
|
||||
set
|
||||
{
|
||||
if (_selectedIndex != value)
|
||||
{
|
||||
_selectedIndex = value;
|
||||
SelectedIndexChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual List<object> Items { get; } = new List<object>();
|
||||
public virtual Color SelectedTextColor { get; set; } = Color.White;
|
||||
public virtual Color SelectedItemColor { get; set; } = Color.CornflowerBlue;
|
||||
public virtual Thickness ItemPadding { get; set; } = new Thickness(4, 2);
|
||||
public virtual string NameProperty { get; set; }
|
||||
|
||||
public event EventHandler SelectedIndexChanged;
|
||||
|
||||
protected int FirstIndex;
|
||||
|
||||
public object SelectedItem
|
||||
{
|
||||
get { return SelectedIndex >= 0 && SelectedIndex <= Items.Count - 1 ? Items[SelectedIndex] : null; }
|
||||
set { SelectedIndex = Items.IndexOf(value); }
|
||||
}
|
||||
|
||||
public override void OnKeyPressed(IGuiContext context, KeyboardEventArgs args)
|
||||
{
|
||||
base.OnKeyPressed(context, args);
|
||||
|
||||
if (args.Key == Keys.Down) ScrollDown();
|
||||
if (args.Key == Keys.Up) ScrollUp();
|
||||
}
|
||||
|
||||
public override void OnScrolled(int delta)
|
||||
{
|
||||
base.OnScrolled(delta);
|
||||
|
||||
if (delta < 0) ScrollDown();
|
||||
if (delta > 0) ScrollUp();
|
||||
}
|
||||
|
||||
private void ScrollDown()
|
||||
{
|
||||
if (SelectedIndex < Items.Count - 1)
|
||||
SelectedIndex++;
|
||||
}
|
||||
|
||||
private void ScrollUp()
|
||||
{
|
||||
if (SelectedIndex > 0)
|
||||
SelectedIndex--;
|
||||
}
|
||||
|
||||
public override void OnPointerDown(IGuiContext context, GuiPointerEventArgs args)
|
||||
{
|
||||
base.OnPointerDown(context, args);
|
||||
|
||||
var contentRectangle = GetContentRectangle(context);
|
||||
|
||||
for (var i = FirstIndex; i < Items.Count; i++)
|
||||
{
|
||||
var itemRectangle = GetItemRectangle(context, i - FirstIndex, contentRectangle);
|
||||
|
||||
if (itemRectangle.Contains(args.Position))
|
||||
{
|
||||
SelectedIndex = i;
|
||||
OnItemClicked(context, args);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnItemClicked(IGuiContext context, GuiPointerEventArgs args) { }
|
||||
|
||||
protected TextInfo GetItemTextInfo(IGuiContext context, Rectangle itemRectangle, object item, Rectangle? clippingRectangle)
|
||||
{
|
||||
var textRectangle = new Rectangle(itemRectangle.X + ItemPadding.Left, itemRectangle.Y + ItemPadding.Top,
|
||||
itemRectangle.Width - ItemPadding.Right, itemRectangle.Height - ItemPadding.Bottom);
|
||||
var itemTextInfo = GetTextInfo(context, GetItemName(item), textRectangle, HorizontalAlignment.Left, VerticalAlignment.Top, clippingRectangle);
|
||||
return itemTextInfo;
|
||||
}
|
||||
|
||||
private string GetItemName(object item)
|
||||
{
|
||||
if (item != null)
|
||||
{
|
||||
if (NameProperty != null)
|
||||
{
|
||||
return item.GetType()
|
||||
.GetRuntimeProperty(NameProperty)
|
||||
.GetValue(item)
|
||||
?.ToString() ?? string.Empty;
|
||||
}
|
||||
|
||||
return item.ToString();
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
protected Rectangle GetItemRectangle(IGuiContext context, int index, Rectangle contentRectangle)
|
||||
{
|
||||
var font = Font ?? context.DefaultFont;
|
||||
var itemHeight = font.LineHeight + ItemPadding.Top + ItemPadding.Bottom;
|
||||
return new Rectangle(contentRectangle.X, contentRectangle.Y + itemHeight * index, contentRectangle.Width, itemHeight);
|
||||
}
|
||||
|
||||
protected void ScrollIntoView(IGuiContext context)
|
||||
{
|
||||
var contentRectangle = GetContentRectangle(context);
|
||||
var selectedItemRectangle = GetItemRectangle(context, SelectedIndex - FirstIndex, contentRectangle);
|
||||
|
||||
if (selectedItemRectangle.Bottom > ClippingRectangle.Bottom)
|
||||
FirstIndex++;
|
||||
|
||||
if (selectedItemRectangle.Top < ClippingRectangle.Top && FirstIndex > 0)
|
||||
FirstIndex--;
|
||||
}
|
||||
|
||||
protected Size2 GetItemSize(IGuiContext context, Size2 availableSize, object item)
|
||||
{
|
||||
var text = GetItemName(item);
|
||||
var textInfo = GetTextInfo(context, text, new Rectangle(0, 0, (int)availableSize.Width, (int)availableSize.Height), HorizontalAlignment.Left, VerticalAlignment.Top);
|
||||
var itemWidth = textInfo.Size.X + ItemPadding.Size.Height;
|
||||
var itemHeight = textInfo.Size.Y + ItemPadding.Size.Width;
|
||||
|
||||
return new Size2(itemWidth, itemHeight);
|
||||
}
|
||||
|
||||
protected abstract Rectangle GetContentRectangle(IGuiContext context);
|
||||
|
||||
protected void DrawItemList(IGuiContext context, IGuiRenderer renderer)
|
||||
{
|
||||
var contentRectangle = GetContentRectangle(context);
|
||||
|
||||
for (var i = FirstIndex; i < Items.Count; i++)
|
||||
{
|
||||
var item = Items[i];
|
||||
var itemRectangle = GetItemRectangle(context, i - FirstIndex, contentRectangle);
|
||||
var itemTextInfo = GetItemTextInfo(context, itemRectangle, item, contentRectangle);
|
||||
var textColor = i == SelectedIndex ? SelectedTextColor : itemTextInfo.Color;
|
||||
|
||||
if (SelectedIndex == i)
|
||||
renderer.FillRectangle(itemRectangle, SelectedItemColor, contentRectangle);
|
||||
|
||||
renderer.DrawText(itemTextInfo.Font, itemTextInfo.Text, itemTextInfo.Position + TextOffset, textColor,
|
||||
itemTextInfo.ClippingRectangle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
using MonoGame.Extended.TextureAtlases;
|
||||
|
||||
namespace MonoGame.Extended.Gui.Controls
|
||||
namespace MonoGame.Extended.Gui.Controls
|
||||
{
|
||||
public class GuiLabel : GuiControl
|
||||
{
|
||||
@@ -8,15 +6,10 @@ namespace MonoGame.Extended.Gui.Controls
|
||||
{
|
||||
}
|
||||
|
||||
public GuiLabel(string text)
|
||||
public GuiLabel(GuiSkin skin, string text = null)
|
||||
: base(skin)
|
||||
{
|
||||
Text = text;
|
||||
}
|
||||
|
||||
public GuiLabel(string text, TextureRegion2D backgroundRegion)
|
||||
: base(backgroundRegion)
|
||||
{
|
||||
Text = text;
|
||||
Text = text ?? string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,4 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using MonoGame.Extended.TextureAtlases;
|
||||
|
||||
namespace MonoGame.Extended.Gui.Controls
|
||||
namespace MonoGame.Extended.Gui.Controls
|
||||
{
|
||||
public abstract class GuiLayoutControl : GuiControl
|
||||
{
|
||||
@@ -10,10 +7,11 @@ namespace MonoGame.Extended.Gui.Controls
|
||||
{
|
||||
}
|
||||
|
||||
protected GuiLayoutControl(TextureRegion2D backgroundRegion)
|
||||
: base(backgroundRegion)
|
||||
protected GuiLayoutControl(GuiSkin skin)
|
||||
: base(skin)
|
||||
{
|
||||
Origin = Vector2.Zero;
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch;
|
||||
VerticalAlignment = VerticalAlignment.Stretch;
|
||||
}
|
||||
|
||||
protected override Size2 CalculateDesiredSize(IGuiContext context, Size2 availableSize)
|
||||
@@ -27,5 +25,11 @@ namespace MonoGame.Extended.Gui.Controls
|
||||
{
|
||||
GuiLayoutHelper.PlaceControl(context, control, x, y, width, height);
|
||||
}
|
||||
|
||||
protected override void DrawBackground(IGuiContext context, IGuiRenderer renderer, float deltaSeconds)
|
||||
{
|
||||
if (BackgroundRegion != null)
|
||||
renderer.DrawRegion(BackgroundRegion, BoundingRectangle, Color);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,134 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using MonoGame.Extended.Input.InputListeners;
|
||||
using MonoGame.Extended.TextureAtlases;
|
||||
|
||||
namespace MonoGame.Extended.Gui.Controls
|
||||
{
|
||||
public class GuiListBox : GuiControl
|
||||
public class GuiListBox : GuiItemsControl
|
||||
{
|
||||
public GuiListBox()
|
||||
: this(null)
|
||||
{
|
||||
}
|
||||
|
||||
public GuiListBox(TextureRegion2D backgroundRegion)
|
||||
: base(backgroundRegion)
|
||||
public GuiListBox(GuiSkin skin)
|
||||
: base(skin)
|
||||
{
|
||||
}
|
||||
|
||||
private int _selectedIndex = -1;
|
||||
public int SelectedIndex
|
||||
protected override Size2 CalculateDesiredSize(IGuiContext context, Size2 availableSize)
|
||||
{
|
||||
get { return _selectedIndex; }
|
||||
set
|
||||
var width = 0f;
|
||||
var height = 0f;
|
||||
|
||||
foreach (var item in Items)
|
||||
{
|
||||
if (_selectedIndex != value)
|
||||
{
|
||||
_selectedIndex = value;
|
||||
SelectedIndexChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
var itemSize = GetItemSize(context, availableSize, item);
|
||||
|
||||
if (itemSize.Width > width)
|
||||
width = itemSize.Width;
|
||||
|
||||
height += itemSize.Height;
|
||||
}
|
||||
}
|
||||
|
||||
public List<object> Items { get; } = new List<object>();
|
||||
public Color SelectedTextColor { get; set; } = Color.White;
|
||||
public Thickness ItemPadding { get; set; } = new Thickness(4, 2);
|
||||
|
||||
public event EventHandler SelectedIndexChanged;
|
||||
|
||||
private int _firstIndex;
|
||||
|
||||
public object SelectedItem
|
||||
{
|
||||
get { return SelectedIndex >= 0 && SelectedIndex <= Items.Count - 1 ? Items[SelectedIndex] : null; }
|
||||
set { SelectedIndex = Items.IndexOf(value); }
|
||||
}
|
||||
|
||||
public override void OnKeyPressed(IGuiContext context, KeyboardEventArgs args)
|
||||
{
|
||||
base.OnKeyPressed(context, args);
|
||||
|
||||
if (args.Key == Keys.Down) ScrollDown();
|
||||
if (args.Key == Keys.Up) ScrollUp();
|
||||
}
|
||||
|
||||
public override void OnScrolled(int delta)
|
||||
{
|
||||
base.OnScrolled(delta);
|
||||
|
||||
if (delta < 0) ScrollDown();
|
||||
if (delta > 0) ScrollUp();
|
||||
}
|
||||
|
||||
private void ScrollDown()
|
||||
{
|
||||
if (SelectedIndex < Items.Count - 1)
|
||||
SelectedIndex++;
|
||||
}
|
||||
|
||||
private void ScrollUp()
|
||||
{
|
||||
if (SelectedIndex > 0)
|
||||
SelectedIndex--;
|
||||
}
|
||||
|
||||
public override void OnPointerDown(IGuiContext context, GuiPointerEventArgs args)
|
||||
{
|
||||
base.OnPointerDown(context, args);
|
||||
|
||||
for (var i = _firstIndex; i < Items.Count; i++)
|
||||
{
|
||||
var itemRectangle = GetItemRectangle(context, i - _firstIndex);
|
||||
|
||||
if (itemRectangle.Contains(args.Position))
|
||||
{
|
||||
SelectedIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new Size2(width + ClipPadding.Size.Width, height + ClipPadding.Size.Height);
|
||||
}
|
||||
|
||||
protected override void DrawForeground(IGuiContext context, IGuiRenderer renderer, float deltaSeconds, TextInfo textInfo)
|
||||
{
|
||||
ScrollIntoView(context);
|
||||
|
||||
for (var i = _firstIndex; i < Items.Count; i++)
|
||||
{
|
||||
var itemRectangle = GetItemRectangle(context, i - _firstIndex);
|
||||
|
||||
if (SelectedIndex == i)
|
||||
renderer.FillRectangle(itemRectangle, Color.CornflowerBlue, ClippingRectangle);
|
||||
|
||||
var item = Items[i];
|
||||
var textRectangle = new Rectangle(itemRectangle.X + ItemPadding.Left, itemRectangle.Y + ItemPadding.Top,
|
||||
itemRectangle.Width - ItemPadding.Right, itemRectangle.Height - ItemPadding.Bottom);
|
||||
var itemTextInfo = GetTextInfo(context, item?.ToString() ?? string.Empty, textRectangle, HorizontalAlignment.Left, VerticalAlignment.Top);
|
||||
var textColor = i == SelectedIndex ? SelectedTextColor : itemTextInfo.Color;
|
||||
|
||||
renderer.DrawText(itemTextInfo.Font, itemTextInfo.Text, itemTextInfo.Position + TextOffset, textColor, itemTextInfo.ClippingRectangle);
|
||||
}
|
||||
DrawItemList(context, renderer);
|
||||
}
|
||||
|
||||
private Rectangle GetItemRectangle(IGuiContext context, int index)
|
||||
protected override Rectangle GetContentRectangle(IGuiContext context)
|
||||
{
|
||||
var font = Font ?? context.DefaultFont;
|
||||
var contentRectangle = ClippingRectangle;
|
||||
var itemHeight = font.LineHeight + ItemPadding.Top + ItemPadding.Bottom;
|
||||
return new Rectangle(contentRectangle.X, contentRectangle.Y + itemHeight * index, contentRectangle.Width, itemHeight);
|
||||
}
|
||||
|
||||
private void ScrollIntoView(IGuiContext context)
|
||||
{
|
||||
var selectedItemRectangle = GetItemRectangle(context, SelectedIndex - _firstIndex);
|
||||
|
||||
if (selectedItemRectangle.Bottom > ClippingRectangle.Bottom)
|
||||
_firstIndex++;
|
||||
|
||||
if (selectedItemRectangle.Top < ClippingRectangle.Top && _firstIndex > 0)
|
||||
_firstIndex--;
|
||||
return ClippingRectangle;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,8 @@ namespace MonoGame.Extended.Gui.Controls
|
||||
{
|
||||
}
|
||||
|
||||
public GuiProgressBar(TextureRegion2D backgroundRegion)
|
||||
: base(backgroundRegion)
|
||||
public GuiProgressBar(GuiSkin skin)
|
||||
: base(skin)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using MonoGame.Extended.TextureAtlases;
|
||||
|
||||
namespace MonoGame.Extended.Gui.Controls
|
||||
{
|
||||
public class GuiStackPanel : GuiLayoutControl
|
||||
{
|
||||
public GuiStackPanel()
|
||||
: base(null)
|
||||
: this(null)
|
||||
{
|
||||
}
|
||||
|
||||
public GuiStackPanel(TextureRegion2D backgroundRegion)
|
||||
: base(backgroundRegion)
|
||||
public GuiStackPanel(GuiSkin skin)
|
||||
: base(skin)
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Centre;
|
||||
VerticalAlignment = VerticalAlignment.Centre;
|
||||
}
|
||||
|
||||
public GuiOrientation Orientation { get; set; } = GuiOrientation.Vertical;
|
||||
|
||||
@@ -2,31 +2,33 @@ using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using MonoGame.Extended.Input.InputListeners;
|
||||
using MonoGame.Extended.TextureAtlases;
|
||||
|
||||
namespace MonoGame.Extended.Gui.Controls
|
||||
{
|
||||
public class GuiTextBox : GuiControl
|
||||
{
|
||||
public GuiTextBox()
|
||||
: this(backgroundRegion: null, text: string.Empty)
|
||||
: this(null)
|
||||
{
|
||||
}
|
||||
|
||||
public GuiTextBox(TextureRegion2D backgroundRegion)
|
||||
: this(backgroundRegion: backgroundRegion, text: string.Empty)
|
||||
public GuiTextBox(GuiSkin skin, string text = null)
|
||||
: base(skin)
|
||||
{
|
||||
}
|
||||
|
||||
public GuiTextBox(TextureRegion2D backgroundRegion, string text)
|
||||
: base(backgroundRegion)
|
||||
{
|
||||
Text = text;
|
||||
Text = text ?? string.Empty;
|
||||
}
|
||||
|
||||
public int SelectionStart { get; set; }
|
||||
public char? PasswordCharacter { get; set; }
|
||||
|
||||
protected override void OnTextChanged()
|
||||
{
|
||||
if (SelectionStart > Text.Length)
|
||||
SelectionStart = Text.Length;
|
||||
|
||||
base.OnTextChanged();
|
||||
}
|
||||
|
||||
public override void OnPointerDown(IGuiContext context, GuiPointerEventArgs args)
|
||||
{
|
||||
base.OnPointerDown(context, args);
|
||||
@@ -39,20 +41,23 @@ namespace MonoGame.Extended.Gui.Controls
|
||||
{
|
||||
var font = Font ?? context.DefaultFont;
|
||||
var textInfo = GetTextInfo(context, Text, BoundingRectangle, HorizontalAlignment.Centre, VerticalAlignment.Centre);
|
||||
var glyphs = font.GetGlyphs(textInfo.Text, textInfo.Position).ToArray();
|
||||
var i = 0;
|
||||
|
||||
for (var i = 0; i < glyphs.Length; i++)
|
||||
foreach (var glyph in font.GetGlyphs(textInfo.Text, textInfo.Position))
|
||||
{
|
||||
var glyph = glyphs[i];
|
||||
var glyphMiddle = (int)(glyph.Position.X + glyph.FontRegion.Width * 0.5f);
|
||||
var fontRegionWidth = glyph.FontRegion?.Width ?? 0;
|
||||
var glyphMiddle = (int)(glyph.Position.X + fontRegionWidth * 0.5f);
|
||||
|
||||
if (position.X >= glyphMiddle)
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
return glyphs.Length;
|
||||
return i;
|
||||
}
|
||||
|
||||
public override void OnKeyPressed(IGuiContext context, KeyboardEventArgs args)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using MonoGame.Extended.TextureAtlases;
|
||||
|
||||
namespace MonoGame.Extended.Gui.Controls
|
||||
{
|
||||
@@ -11,8 +10,8 @@ namespace MonoGame.Extended.Gui.Controls
|
||||
{
|
||||
}
|
||||
|
||||
public GuiUniformGrid(TextureRegion2D backgroundRegion)
|
||||
: base(backgroundRegion)
|
||||
public GuiUniformGrid(GuiSkin skin)
|
||||
: base(skin)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -21,36 +20,27 @@ namespace MonoGame.Extended.Gui.Controls
|
||||
|
||||
protected override Size2 CalculateDesiredSize(IGuiContext context, Size2 availableSize)
|
||||
{
|
||||
var columns = CalculateColumns();
|
||||
var rows = CalculateRows(columns, Controls.Count);
|
||||
var maxCellWidth = availableSize.Width / columns;
|
||||
var maxCellHeight = availableSize.Height / rows;
|
||||
var sizes = Controls
|
||||
.Select(control => GuiLayoutHelper.GetSizeWithMargins(control, context, new Size2(maxCellWidth, maxCellHeight)))
|
||||
.ToArray();
|
||||
var cellWidth = Math.Min(sizes.Max(s => s.Width), maxCellWidth);
|
||||
var cellHeight = Math.Min(sizes.Max(s => s.Height), maxCellHeight);
|
||||
|
||||
return new Size2(cellWidth * columns, cellHeight * rows);
|
||||
var desiredSize = CalculateGridInfo(context, availableSize).MinCellSize;
|
||||
return desiredSize;
|
||||
}
|
||||
|
||||
public override void Layout(IGuiContext context, RectangleF rectangle)
|
||||
{
|
||||
var columns = CalculateColumns();
|
||||
var rows = CalculateRows(columns, Controls.Count);
|
||||
var cellWidth = (float)(rectangle.Width / columns);
|
||||
var cellHeight = (float)(rectangle.Height / rows);
|
||||
var gridInfo = CalculateGridInfo(context, rectangle.Size);
|
||||
var columnIndex = 0;
|
||||
var rowIndex = 0;
|
||||
var cellWidth = HorizontalAlignment == HorizontalAlignment.Stretch ? gridInfo.MaxCellWidth : gridInfo.MinCellWidth;
|
||||
var cellHeight = VerticalAlignment == VerticalAlignment.Stretch ? gridInfo.MaxCellHeight : gridInfo.MinCellHeight;
|
||||
|
||||
foreach (var control in Controls)
|
||||
{
|
||||
var x = columnIndex * cellWidth;
|
||||
var y = rowIndex * cellHeight;
|
||||
|
||||
PlaceControl(context, control, x, y, cellWidth, cellHeight);
|
||||
columnIndex++;
|
||||
|
||||
if (columnIndex > columns - 1)
|
||||
if (columnIndex > gridInfo.Columns - 1)
|
||||
{
|
||||
columnIndex = 0;
|
||||
rowIndex++;
|
||||
@@ -58,14 +48,38 @@ namespace MonoGame.Extended.Gui.Controls
|
||||
}
|
||||
}
|
||||
|
||||
private int CalculateRows(int columns, int controlCount)
|
||||
private struct GridInfo
|
||||
{
|
||||
return Rows == 0 ? (int)Math.Ceiling((float)controlCount / columns) : Rows;
|
||||
public float MinCellWidth;
|
||||
public float MinCellHeight;
|
||||
public float MaxCellWidth;
|
||||
public float MaxCellHeight;
|
||||
public float Columns;
|
||||
public float Rows;
|
||||
public Size2 MinCellSize => new Size2(MinCellWidth * Columns, MinCellHeight * Rows);
|
||||
}
|
||||
|
||||
private int CalculateColumns()
|
||||
|
||||
private GridInfo CalculateGridInfo(IGuiContext context, Size2 availableSize)
|
||||
{
|
||||
return Columns == 0 ? (int)Math.Ceiling(Math.Sqrt(Controls.Count)) : Columns;
|
||||
var columns = Columns == 0 ? (int)Math.Ceiling(Math.Sqrt(Controls.Count)) : Columns;
|
||||
var rows = Rows == 0 ? (int)Math.Ceiling((float)Controls.Count / columns) : Rows;
|
||||
var maxCellWidth = availableSize.Width / columns;
|
||||
var maxCellHeight = availableSize.Height / rows;
|
||||
var sizes = Controls
|
||||
.Select(control => GuiLayoutHelper.GetSizeWithMargins(control, context, new Size2(maxCellWidth, maxCellHeight)))
|
||||
.ToArray();
|
||||
var maxControlWidth = sizes.Length == 0 ? 0 : sizes.Max(s => s.Width);
|
||||
var maxControlHeight = sizes.Length == 0 ? 0 : sizes.Max(s => s.Height);
|
||||
|
||||
return new GridInfo
|
||||
{
|
||||
Columns = columns,
|
||||
Rows = rows,
|
||||
MinCellWidth = Math.Min(maxControlWidth, maxCellWidth),
|
||||
MinCellHeight = Math.Min(maxControlHeight, maxCellHeight),
|
||||
MaxCellWidth = maxCellWidth,
|
||||
MaxCellHeight = maxCellHeight
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user