using System; using System.Linq; using Microsoft.Xna.Framework; using MLEM.Input; using MLEM.Misc; 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 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 imagePadding = 2) { var button = new Button(anchor, size, text, tooltipText); var image = new Image(Anchor.CenterLeft, Vector2.One, texture); image.Padding = image.Padding.OrStyle(new Padding(imagePadding), 1); 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; } /// public static Button KeybindButton(Anchor anchor, Vector2 size, Keybind keybind, InputHandler inputHandler, string activePlaceholder, GenericInput unbindKey = default, string unboundPlaceholder = "", Func inputName = null, int index = 0) { return KeybindButton(anchor, size, keybind, inputHandler, activePlaceholder, new Keybind(unbindKey), unboundPlaceholder, inputName, index); } /// /// Creates a that acts as a way to input a custom value for a . /// Note that only the at index of the given keybind is displayed and edited, all others are ignored. /// Inputting custom keybinds using this element supports -based modifiers and any -based keys. /// The keybind button's current state can be retrieved using the "Active" key, which stores a bool that indicates whether the button is currently waiting for a new value to be assigned. /// /// 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 keybind to press that allows the keybind value to be unbound, clearing the combination /// 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. /// The index in the that the button should display. Note that, if the index is greater than the amount of combinations, combinations entered using this button will be stored at an earlier index. /// A keybind button with the given settings public static Button KeybindButton(Anchor anchor, Vector2 size, Keybind keybind, InputHandler inputHandler, string activePlaceholder, Keybind unbind = default, string unboundPlaceholder = "", Func inputName = null, int index = 0) { string GetCurrentName() => keybind.TryGetCombination(index, out var combination) ? combination.ToString(" + ", inputName) : unboundPlaceholder; var button = new Button(anchor, size, GetCurrentName()); var activeNext = false; button.OnPressed = e => { button.Text.Text = activePlaceholder; activeNext = true; }; button.OnUpdated = (e, time) => { if (activeNext) { button.SetData("Active", true); activeNext = false; } else if (button.GetData("Active")) { if (unbind != null && unbind.TryConsumePressed(inputHandler)) { keybind.Remove((c, i) => i == index); button.Text.Text = unboundPlaceholder; button.SetData("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 == index).Insert(index, key, mods.ToArray()); button.Text.Text = GetCurrentName(); button.SetData("Active", false); } } } else { button.Text.Text = GetCurrentName(); } }; 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 element to add the tooltip to /// The text to display on the tooltip /// The created tooltip instance public static Tooltip AddTooltip(this Element element, Paragraph.TextCallback textCallback) { return new Tooltip(textCallback, element); } /// /// Adds a new to the given element using the constructor /// /// The element to add the tooltip to /// The text to display on the tooltip /// The created tooltip instance public static Tooltip AddTooltip(this Element element, string text) { return new Tooltip(text, element); } } }