using System; using System.Collections.Generic; using System.Numerics; namespace Robust.Client.UserInterface.Controls { /// /// Number input LineEdit with increment buttons. /// [Virtual] public class SpinBox : BoxContainer { public const string LeftButtonStyle = "spinbox-left"; public const string RightButtonStyle = "spinbox-right"; public const string MiddleButtonStyle = "spinbox-middle"; public LineEdit LineEditControl { get; } private List _leftButtons = new(); private List _rightButtons = new(); private int _stepSize = 1; private bool _buttonsDisabled; /// /// Determines whether the SpinBox value gets changed by the input text. /// public Func? IsValid { get; set; } private int _value; public int Value { get => _value; set { OverrideValue(value); ValueChanged?.Invoke(new ValueChangedEventArgs(value)); } } /// /// Overrides the value of the spinbox without calling the event. /// Still applies validity-checks /// /// the new value public void OverrideValue(int value) { if (IsValid != null && !IsValid(value)) { return; } _value = value; UpdateButtonCanPress(); LineEditControl.Text = value.ToString(); } public event Action? ValueChanged; public SpinBox() { Orientation = LayoutOrientation.Horizontal; MouseFilter = MouseFilterMode.Pass; LineEditControl = new LineEdit { MinSize = new Vector2(40, 0), HorizontalExpand = true }; AddChild(LineEditControl); Value = 0; LineEditControl.IsValid = (str) => int.TryParse(str, out var i) && (IsValid == null || IsValid(i)); LineEditControl.OnTextChanged += (args) => { if (int.TryParse(args.Text, out int i)) Value = i; }; } /// /// Creates and sets buttons to - and +. /// public void InitDefaultButtons() { ClearButtons(); AddLeftButton(-1, "-"); AddRightButton(1, "+"); UpdateButtonCanPress(); } /// /// Adds a button to the right of the SpinBox LineEdit. /// public void AddRightButton(int num, string text) { var button = new SpinBoxButton(num) { Text = text }; button.OnPressed += _ => Value += num; AddChild(button); button.AddStyleClass(RightButtonStyle); if (_rightButtons.Count > 0) { _rightButtons[^1].RemoveStyleClass(RightButtonStyle); _rightButtons[^1].AddStyleClass(MiddleButtonStyle); } _rightButtons.Add(button); } /// /// Adds a button to the left of the SpinBox LineEdit. /// public void AddLeftButton(int num, string text) { var button = new SpinBoxButton(num) { Text = text }; button.OnPressed += _ => Value += num; AddChild(button); button.SetPositionInParent(_leftButtons.Count); button.AddStyleClass(_leftButtons.Count == 0 ? LeftButtonStyle : MiddleButtonStyle); if (_leftButtons.Count == 0) { } _leftButtons.Add(button); } /// /// Creates and sets buttons for each int in leftButtons and rightButtons. /// public void SetButtons(List leftButtons, List rightButtons) { ClearButtons(); foreach (var num in leftButtons) { AddLeftButton(num, num.ToString("+#;-#;0")); } foreach (var num in rightButtons) { AddRightButton(num, num.ToString("+#;-#;0")); } } /// /// Changes the editability of the lineedit-field /// public bool LineEditDisabled { get => !LineEditControl.Editable; set => LineEditControl.Editable = !value; } /// /// Changes the editability of the buttons /// /// public void SetButtonDisabled(bool disabled) { foreach (var leftButton in _leftButtons) { leftButton.Disabled = disabled; } foreach (var rightButton in _rightButtons) { rightButton.Disabled = disabled; } _buttonsDisabled = disabled; } private void UpdateButtonCanPress() { if (IsValid == null) return; foreach (var button in _leftButtons) { button.Disabled = !IsValid(_value + button.Value) || _buttonsDisabled; } foreach (var button in _rightButtons) { button.Disabled = !IsValid(_value + button.Value) || _buttonsDisabled; } } /// /// Removes all buttons inside the SpinBox. /// public void ClearButtons() { foreach (var button in _leftButtons) { button.Orphan(); } _leftButtons.Clear(); foreach (var button in _rightButtons) { button.Orphan(); } _rightButtons.Clear(); } protected internal override void MouseWheel(GUIMouseWheelEventArgs args) { base.MouseWheel(args); if (!LineEditControl.HasKeyboardFocus()) { return; } if (args.Delta.Y > 0) Value += _stepSize; else if (args.Delta.Y < 0) Value -= _stepSize; } private sealed class SpinBoxButton : Button { public readonly int Value; public SpinBoxButton(int value) { Value = value; } } } public sealed class ValueChangedEventArgs : EventArgs { public readonly int Value; public ValueChangedEventArgs(int value) { Value = value; } } }