// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; using FlaxEditor.GUI.ContextMenu; using FlaxEngine; using FlaxEngine.GUI; namespace FlaxEditor.GUI { /// /// Popup menu useful for renaming objects via UI. Displays text box for renaming. /// /// public class RenamePopup : ContextMenuBase { private string _startValue; private TextBox _inputField; /// /// Occurs when renaming is done. /// public event Action Renamed; /// /// Occurs when popup is closing (after renaming done or not). /// public event Action Closed; /// /// Input value validation delegate. /// /// The popup reference. /// The input text value. /// True if text is valid, otherwise false. public delegate bool ValidateDelegate(RenamePopup popup, string value); /// /// Occurs when input text validation should be performed. /// public ValidateDelegate Validate; /// /// Gets or sets the initial value. /// public string InitialValue { get => _startValue; set => _startValue = value; } /// /// Gets or sets the input field text. /// public string Text { get => _inputField.Text; set => _inputField.Text = value; } /// /// Gets the text input field control. /// public TextBox InputField => _inputField; /// /// Initializes a new instance of the class. /// /// The value. /// The size. /// Enable/disable multiline text input support public RenamePopup(string value, Float2 size, bool isMultiline) { if (!isMultiline) size.Y = TextBox.DefaultHeight; Size = size; _startValue = value; _inputField = new TextBox(isMultiline, 0, 0, size.Y); _inputField.TextChanged += OnTextChanged; _inputField.AnchorPreset = AnchorPresets.StretchAll; _inputField.Offsets = Margin.Zero; _inputField.Text = _startValue; _inputField.Parent = this; } private bool IsInputValid => !string.IsNullOrWhiteSpace(_inputField.Text) && (_inputField.Text == _startValue || Validate == null || Validate(this, _inputField.Text)); /// public override void Update(float deltaTime) { var mouseLocation = Root.MousePosition; if (!ContainsPoint(ref mouseLocation) && RootWindow.ContainsFocus && Text != _startValue) { // rename item before closing if left mouse button in clicked if (FlaxEngine.Input.GetMouseButtonDown(MouseButton.Left)) OnEnd(); } base.Update(deltaTime); } private void OnTextChanged() { if (Validate == null) return; var valid = IsInputValid; var style = Style.Current; if (valid) { _inputField.BorderColor = Color.Transparent; _inputField.BorderSelectedColor = style.BackgroundSelected; } else { var color = new Color(1.0f, 0.0f, 0.02745f, 1.0f); _inputField.BorderColor = Color.Lerp(color, style.TextBoxBackground, 0.6f); _inputField.BorderSelectedColor = color; } } /// /// Shows the rename popup. /// /// The target control. /// The target control area to cover. /// The initial value. /// Enable/disable multiline text input support /// Created popup. public static RenamePopup Show(Control control, Rectangle area, string value, bool isMultiline) { // Calculate the control size in the window space to handle scaled controls var upperLeft = control.PointToWindow(area.UpperLeft); var bottomRight = control.PointToWindow(area.BottomRight); var size = bottomRight - upperLeft; var rename = new RenamePopup(value, size, isMultiline); rename.Show(control, area.Location + new Float2(0, (size.Y - rename.Height) * 0.5f)); return rename; } private void OnEnd() { var text = Text; if (text != _startValue && IsInputValid) { Renamed?.Invoke(this); } Hide(); } /// protected override bool UseAutomaticDirectionFix => false; /// public override bool OnKeyDown(KeyboardKeys key) { // Enter if (key == KeyboardKeys.Return) { OnEnd(); return true; } // Esc if (key == KeyboardKeys.Escape) { Hide(); return true; } // Base return base.OnKeyDown(key); } /// protected override void OnShow() { _inputField.EndEditOnClick = false; // Ending edit is handled through popup _inputField.Focus(); _inputField.SelectAll(); base.OnShow(); } /// protected override void OnHide() { Closed?.Invoke(this); Closed = null; base.OnHide(); // Remove itself Dispose(); } /// public override void OnDestroy() { Renamed = null; Closed = null; Validate = null; _inputField = null; base.OnDestroy(); } } }