using System;
using System.Linq;
using Microsoft.Xna.Framework;
using MLEM.Input;
using MLEM.Textures;
namespace MLEM.Ui.Elements {
///
/// This class contains a set of helper methods that aid in creating special kinds of compound types for use inside of a .
///
public static class ElementHelper {
///
/// Creates a button with an image on the left side of its text.
///
/// The button's anchor
/// The button's size
/// The texture of the image to render on the button
/// The text to display on the button
/// The text of the button's tooltip
/// The width of the button's tooltip
/// The of the button's image
/// An image button
public static Button ImageButton(Anchor anchor, Vector2 size, TextureRegion texture, string text = null, string tooltipText = null, float tooltipWidth = 50, float imagePadding = 2) {
var button = new Button(anchor, size, text, tooltipText, tooltipWidth);
var image = new Image(Anchor.CenterLeft, Vector2.One, texture) {Padding = new Vector2(imagePadding)};
button.OnAreaUpdated += e => image.Size = new Vector2(e.Area.Height, e.Area.Height) / e.Scale;
button.AddChild(image, 0);
return button;
}
///
/// Creates a panel that contains a paragraph of text and a button to close the panel.
/// The panel is part of a group, which causes elements in the background (behind and around the panel) to not be clickable, leaving only the "close" button.
///
/// The ui system to add the panel to, optional.
/// The anchor of the panel
/// The width of the panel
/// The text to display on the panel
/// The height of the "close" button
/// The text on the "close" button
/// An info box panel
public static Panel ShowInfoBox(UiSystem system, Anchor anchor, float width, string text, float buttonHeight = 10, string okText = "Okay") {
var group = new Group(Anchor.TopLeft, Vector2.One, false);
var box = group.AddChild(new Panel(anchor, new Vector2(width, 1), Vector2.Zero, true));
box.AddChild(new Paragraph(Anchor.AutoLeft, 1, text));
var button = box.AddChild(new Button(Anchor.AutoCenter, new Vector2(0.5F, buttonHeight), okText) {
OnPressed = element => system.Remove("InfoBox"),
PositionOffset = new Vector2(0, 1)
});
var root = system.Add("InfoBox", group);
root.SelectElement(button);
return box;
}
///
/// Creates an array of groups with a fixed width that can be used to create a column structure
///
/// The element the groups should be added to, optional.
/// The total width of all of the groups combined
/// The amount of groups to split the total size into
/// Whether the groups should set their heights based on their children's heights
/// An array of columns
public static Group[] MakeColumns(Element parent, Vector2 totalSize, int amount, bool setHeightBasedOnChildren = true) {
var cols = new Group[amount];
for (var i = 0; i < amount; i++) {
var anchor = i == amount - 1 ? Anchor.AutoInlineIgnoreOverflow : Anchor.AutoInline;
cols[i] = new Group(anchor, new Vector2(totalSize.X / amount, totalSize.Y), setHeightBasedOnChildren);
if (parent != null)
parent.AddChild(cols[i]);
}
return cols;
}
///
/// Creates a with a + and a - button next to it, to allow for easy number input.
///
/// The text field's anchor
/// The size of the text field
/// The default content of the text field
/// The value that is added or removed to the text field's value when clicking the + or - buttons
/// The rule for text input. by default.
/// A callback that is invoked when the text field's text changes
/// A group that contains the number field
public static Group NumberField(Anchor anchor, Vector2 size, int defaultValue = 0, int stepPerClick = 1, TextField.Rule rule = null, TextField.TextChanged onTextChange = null) {
var group = new Group(anchor, size, false);
var field = new TextField(Anchor.TopLeft, Vector2.One, rule ?? TextField.OnlyNumbers);
field.OnTextChange = onTextChange;
field.SetText(defaultValue.ToString());
group.AddChild(field);
group.OnAreaUpdated += e => field.Size = new Vector2((e.Area.Width - e.Area.Height / 2) / e.Scale, 1);
var upButton = new Button(Anchor.TopRight, Vector2.One, "+") {
OnPressed = element => {
var text = field.Text;
if (int.TryParse(text, out var val))
field.SetText(val + stepPerClick);
}
};
group.AddChild(upButton);
group.OnAreaUpdated += e => upButton.Size = new Vector2(e.Area.Height / 2 / e.Scale);
var downButton = new Button(Anchor.BottomRight, Vector2.One, "-") {
OnPressed = element => {
var text = field.Text;
if (int.TryParse(text, out var val))
field.SetText(val - stepPerClick);
}
};
group.AddChild(downButton);
group.OnAreaUpdated += e => downButton.Size = new Vector2(e.Area.Height / 2 / e.Scale);
return group;
}
///
/// Creates a that acts as a way to input a custom value for a .
/// Note that only the first of the given keybind is displayed and edited, all others are ignored. The exception is that, if is set, unbinding the keybind clears all combinations.
/// Inputting custom keybinds using this element supports -based modifiers and any -based keys.
///
/// The button's anchor
/// The button's size
/// The keybind that this button should represent
/// The input handler to query inputs with
/// A placeholder text that is displayed while the keybind is being edited
/// An optional generic input that allows the keybind value to be unbound, clearing all combinations
/// A placeholder text that is displayed if the keybind is unbound
/// An optional function to give each input a display name that is easier to read. If this is null, is used.
/// A keybind button with the given settings
public static Button KeybindButton(Anchor anchor, Vector2 size, Keybind keybind, InputHandler inputHandler, string activePlaceholder, GenericInput unbindKey = default, string unboundPlaceholder = "", Func inputName = null) {
string GetCurrentName() {
var combination = keybind.GetCombinations().FirstOrDefault();
if (combination == null)
return unboundPlaceholder;
return string.Join(" + ", combination.Modifiers
.Append(combination.Key)
.Select(i => inputName?.Invoke(i) ?? i.ToString()));
}
var button = new Button(anchor, size, GetCurrentName());
var active = false;
var activeNext = false;
button.OnPressed = e => {
button.Text.Text = activePlaceholder;
activeNext = true;
};
button.OnUpdated = (e, time) => {
if (activeNext) {
active = true;
activeNext = false;
} else if (active) {
if (unbindKey != default && inputHandler.IsPressed(unbindKey)) {
keybind.Clear();
button.Text.Text = unboundPlaceholder;
active = false;
} else if (inputHandler.InputsPressed.Length > 0) {
var key = inputHandler.InputsPressed.FirstOrDefault(i => !i.IsModifier());
if (key != default) {
var mods = inputHandler.InputsDown.Where(i => i.IsModifier());
keybind.Remove((c, i) => i == 0).Add(key, mods.ToArray());
button.Text.Text = GetCurrentName();
active = false;
}
}
}
};
return button;
}
}
///
/// This class contains a set of extensions for dealing with objects
///
public static class ElementExtensions {
///
/// Adds a new to the given element using the constructor
///
///
/// The width of the tooltip
/// The text to display on the tooltip
/// The created tooltip instance
public static Tooltip AddTooltip(this Element element, float width, Paragraph.TextCallback textCallback) {
return new Tooltip(width, textCallback, element);
}
///
/// Adds a new to the given element using the constructor
///
///
/// The width of the tooltip
/// The text to display on the tooltip
/// The created tooltip instance
public static Tooltip AddTooltip(this Element element, float width, string text) {
return new Tooltip(width, text, element);
}
}
}