/* * Copyright 2022 Peter Han * Permission is hereby granted, free of charge, to any person obtaining a copy of this software * and associated documentation files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or * substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ using System; using TMPro; using UnityEngine; using UnityEngine.UI; namespace PeterHan.PLib.UI { /// /// A custom UI text area (multi-line text field) factory class. This class should /// probably be wrapped in a scroll pane. /// public sealed class PTextArea : IUIComponent { /// /// The text area's background color. /// public Color BackColor { get; set; } /// /// The flexible size bounds of this component. /// public Vector2 FlexSize { get; set; } /// /// The preferred number of text lines to be displayed. If the component is made /// bigger, the number of text lines (and size) can increase. /// public int LineCount { get; set; } /// /// The maximum number of characters in this text area. /// public int MaxLength { get; set; } public string Name { get; } /// /// The minimum width in units (not characters!) of this text area. /// public int MinWidth { get; set; } /// /// The text alignment in the text area. /// public TextAlignmentOptions TextAlignment { get; set; } /// /// The initial text in the text field. /// public string Text { get; set; } /// /// The text field's text color, font, word wrap settings, and font size. /// public TextStyleSetting TextStyle { get; set; } /// /// The tool tip text. /// public string ToolTip { get; set; } public event PUIDelegates.OnRealize OnRealize; /// /// The action to trigger on text change. It is passed the realized source object. /// public PUIDelegates.OnTextChanged OnTextChanged { get; set; } /// /// The callback to invoke when validating input. /// public TMP_InputField.OnValidateInput OnValidate { get; set; } public PTextArea() : this(null) { } public PTextArea(string name) { BackColor = PUITuning.Colors.BackgroundLight; FlexSize = Vector2.one; LineCount = 4; MaxLength = 1024; MinWidth = 64; Name = name ?? "TextArea"; Text = null; TextAlignment = TextAlignmentOptions.TopLeft; TextStyle = PUITuning.Fonts.TextDarkStyle; ToolTip = ""; } /// /// Adds a handler when this text area is realized. /// /// The handler to invoke on realization. /// This text area for call chaining. public PTextArea AddOnRealize(PUIDelegates.OnRealize onRealize) { OnRealize += onRealize; return this; } public GameObject Build() { var textField = PUIElements.CreateUI(null, Name); var style = TextStyle ?? PUITuning.Fonts.TextLightStyle; // Background var border = textField.AddComponent(); border.sprite = PUITuning.Images.BoxBorderWhite; border.type = Image.Type.Sliced; border.color = style.textColor; // Text box with rectangular clipping area; put pivot in upper left var textArea = PUIElements.CreateUI(textField, "Text Area", false); textArea.AddComponent().color = BackColor; var mask = textArea.AddComponent(); // Scrollable text var textBox = PUIElements.CreateUI(textArea, "Text"); // Text to display var textDisplay = PTextField.ConfigureField(textBox.AddComponent(), style, TextAlignment); textDisplay.enableWordWrapping = true; textDisplay.raycastTarget = true; // Text field itself textField.SetActive(false); var textEntry = textField.AddComponent(); textEntry.textComponent = textDisplay; textEntry.textViewport = textArea.rectTransform(); textEntry.text = Text ?? ""; textDisplay.text = Text ?? ""; // Events! ConfigureTextEntry(textEntry); var events = textField.AddComponent(); events.OnTextChanged = OnTextChanged; events.OnValidate = OnValidate; events.TextObject = textBox; // Add tooltip PUIElements.SetToolTip(textField, ToolTip); mask.enabled = true; PUIElements.SetAnchorOffsets(textBox, new RectOffset()); textField.SetActive(true); // Lay out var layout = PUIUtils.InsetChild(textField, textArea, Vector2.one, new Vector2( MinWidth, Math.Max(LineCount, 1) * PUIUtils.GetLineHeight(style))). AddOrGet(); layout.flexibleWidth = FlexSize.x; layout.flexibleHeight = FlexSize.y; OnRealize?.Invoke(textField); return textField; } /// /// Sets up the text entry field. /// /// The input field to configure. private void ConfigureTextEntry(TMP_InputField textEntry) { textEntry.characterLimit = Math.Max(1, MaxLength); textEntry.enabled = true; textEntry.inputType = TMP_InputField.InputType.Standard; textEntry.interactable = true; textEntry.isRichTextEditingAllowed = false; textEntry.keyboardType = TouchScreenKeyboardType.Default; textEntry.lineType = TMP_InputField.LineType.MultiLineNewline; textEntry.navigation = Navigation.defaultNavigation; textEntry.richText = false; textEntry.selectionColor = PUITuning.Colors.SelectionBackground; textEntry.transition = Selectable.Transition.None; textEntry.restoreOriginalTextOnEscape = true; } /// /// Sets the default Klei pink style as this text area's color and text style. /// /// This button for call chaining. public PTextArea SetKleiPinkStyle() { TextStyle = PUITuning.Fonts.UILightStyle; BackColor = PUITuning.Colors.ButtonPinkStyle.inactiveColor; return this; } /// /// Sets the default Klei blue style as this text area's color and text style. /// /// This button for call chaining. public PTextArea SetKleiBlueStyle() { TextStyle = PUITuning.Fonts.UILightStyle; BackColor = PUITuning.Colors.ButtonBlueStyle.inactiveColor; return this; } /// /// Sets the minimum (and preferred) width of this text area in characters. /// /// The width is computed using the currently selected text style. /// /// The number of characters to be displayed. /// This button for call chaining. public PTextArea SetMinWidthInCharacters(int chars) { int width = Mathf.RoundToInt(chars * PUIUtils.GetEmWidth(TextStyle)); if (width > 0) MinWidth = width; return this; } public override string ToString() { return string.Format("PTextArea[Name={0}]", Name); } } }