using System; using System.Linq; using System.Collections.Generic; using static Robust.Client.UserInterface.Controls.BaseButton; using static Robust.Client.UserInterface.Controls.BoxContainer; namespace Robust.Client.UserInterface.Controls { public enum RadioOptionsLayout { Horizontal, Vertical } [Virtual] public class RadioOptions : Control { private int internalIdCount = 0; private readonly List> _buttonDataList = new(); //private readonly Dictionary _idMap = new(); private ButtonGroup _buttonGroup = new(); private Container _container; public string ButtonStyle = string.Empty; public string FirstButtonStyle = string.Empty; public string LastButtonStyle = string.Empty; public int ItemCount => _buttonDataList.Count; /// /// Called when creating a Button Control for each radio item. /// The default creates a regular Button. /// public Func GenerateItem = (itemLabel, _) => new Button { Text = itemLabel, }; /// /// Called whenever you select a button. /// /// Note: You should add optionButtons.Select(args.Id); if you want to actually select the button. /// public event Action>? OnItemSelected; public RadioOptions(RadioOptionsLayout layout) { switch (layout) { case RadioOptionsLayout.Vertical: _container = new BoxContainer { Orientation = LayoutOrientation.Vertical }; break; case RadioOptionsLayout.Horizontal: default: _container = new BoxContainer { Orientation = LayoutOrientation.Horizontal }; break; } AddChild(_container); } public int AddItem(string label, T value, Action>? itemSelectedAction = null) { var button = GenerateItem.Invoke(label, value); button.Group = _buttonGroup; button.OnPressed += ButtonOnPressed; var data = new RadioOptionButtonData(label, value, button) { Id = internalIdCount++ }; if (itemSelectedAction != null) { data.OnItemSelected += itemSelectedAction; } _buttonDataList.Add(data); _container.AddChild(button); UpdateFirstAndLastButtonStyle(); if (_buttonDataList.Count == 1) { Select(data.Id); } return data.Id; } /// /// This is triggered when the button is pressed via the UI /// /// private void ButtonOnPressed(ButtonEventArgs obj) { var buttonData = _buttonDataList.FirstOrDefault(bd => bd.Button == obj.Button); if (buttonData != null) { InvokeItemSelected(new RadioOptionItemSelectedEventArgs(buttonData.Id, this)); return; } // Not reachable. throw new InvalidOperationException(); } public void Clear() { foreach (var buttonDatum in _buttonDataList) { buttonDatum.Button.OnPressed -= ButtonOnPressed; } _buttonDataList.Clear(); _container.Children.Clear(); SelectedId = 0; } public object? GetItemMetadata(int idx) { return _buttonDataList.FirstOrDefault(bd => bd.Id == idx)?.Metadata; } public int SelectedId { get; private set; } public RadioOptionButtonData SelectedButtonData => _buttonDataList.First(bd => bd.Id == SelectedId); public Button SelectedButton => SelectedButtonData.Button; public string SelectedText => SelectedButtonData.Text; public T SelectedValue => SelectedButtonData.Value; /// /// Always will return true if itemId is not found. /// /// /// public bool IsItemDisabled(int idx) { return _buttonDataList.FirstOrDefault(bd => bd.Id == idx)?.Disabled ?? true; } public void RemoveItem(int idx) { var data = _buttonDataList.FirstOrDefault(bd => bd.Id == idx); if (data!= null) { data.Button.OnPressed -= ButtonOnPressed; _container.RemoveChild(data.Button); var buttonData = _buttonDataList.FirstOrDefault(bd => bd.Id == idx); if (buttonData != null) _buttonDataList.Remove(buttonData); UpdateFirstAndLastButtonStyle(); } } public void Select(int idx) { var data = _buttonDataList.FirstOrDefault(bd => bd.Id == idx); if (data != null) { SelectedId = data.Id; data.Button.Pressed = true; return; } // Not found. } public void SelectByValue(T value) { var data = _buttonDataList.FirstOrDefault(bd => EqualityComparer.Default.Equals(bd.Value, value)); if (data != null) { Select(data.Id); } } public void InvokeItemSelected(RadioOptionItemSelectedEventArgs args) { var buttonData = _buttonDataList.FirstOrDefault(bd => bd.Id == args.Id); if (buttonData == null) return; if (buttonData.HasOnItemSelectedEvent) buttonData.InvokeItemSelected(args); else OnItemSelected?.Invoke(args); } public void UpdateFirstAndLastButtonStyle() { for (int i = 0; i < _buttonDataList.Count; i++) { var buttonData = _buttonDataList[i]; if (buttonData.Button == null) continue; buttonData.Button.StyleClasses.Remove(ButtonStyle); buttonData.Button.StyleClasses.Remove(LastButtonStyle); buttonData.Button.StyleClasses.Remove(FirstButtonStyle); if (i == 0) buttonData.Button.StyleClasses.Add(FirstButtonStyle); else if (i == _buttonDataList.Count - 1) buttonData.Button.StyleClasses.Add(LastButtonStyle); else buttonData.Button.StyleClasses.Add(ButtonStyle); } } public void SetItemDisabled(int idx, bool disabled) { var data = _buttonDataList.FirstOrDefault(bd => bd.Id == idx); if (data != null) { data.Disabled = disabled; data.Button.Disabled = disabled; } } public void SetItemMetadata(int idx, object metadata) { var buttonData = _buttonDataList.FirstOrDefault(bd => bd.Id == idx); if (buttonData != null) buttonData.Metadata = metadata; } public void SetItemText(int idx, string text) { var data = _buttonDataList.FirstOrDefault(bd => bd.Id == idx); if (data != null) { data.Text = text; data.Button.Text = text; } } } public sealed class RadioOptionItemSelectedEventArgs : EventArgs { public RadioOptions Button { get; } /// /// The ID of the item that has been selected. /// public int Id { get; } public RadioOptionItemSelectedEventArgs(int id, RadioOptions button) { Id = id; Button = button; } } public sealed class RadioOptionButtonData { public int Id; public string Text; public T Value; public bool Disabled; public object? Metadata; public Button Button; public RadioOptionButtonData(string text, T value, Button button) { Text = text; Button = button; Value = value; } public event Action>? OnItemSelected; public bool HasOnItemSelectedEvent => OnItemSelected != null; public void InvokeItemSelected(RadioOptionItemSelectedEventArgs args) { OnItemSelected?.Invoke(args); } } }