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(); return combination?.ToString(" + ", inputName) ?? unboundPlaceholder; } 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); } } }