Aragas/Bannerlord.MBOptionScreen

View on GitHub
src/MCM.UI/Dropdown/MCMSelectorVM.cs

Summary

Maintainability
A
50 mins
Test Coverage
using HarmonyLib.BUTR.Extensions;

using System;
using System.Collections.Generic;

using TaleWorlds.Core;
using TaleWorlds.Library;

namespace MCM.UI.Dropdown
{
    internal class MCMSelectorVM<TSelectorItemVM> : ViewModel where TSelectorItemVM : ViewModel
    {
        private delegate bool CanBeSelectedDelegate(TSelectorItemVM instance);
        private static readonly CanBeSelectedDelegate _canBeSelectedDelegate =
            AccessTools2.GetPropertyGetterDelegate<CanBeSelectedDelegate>(typeof(TSelectorItemVM), "CanBeSelected") ?? (_ => false);

        private delegate void SetIsSelectedDelegate(object instance, bool value);
        private static readonly SetIsSelectedDelegate _setIsSelectedDelegate =
            AccessTools2.GetPropertySetterDelegate<SetIsSelectedDelegate>(typeof(TSelectorItemVM), "IsSelected") ?? ((_, _) => { });


        public static MCMSelectorVM<TSelectorItemVM> Empty => new();

        protected int _selectedIndex = -1;
        private TSelectorItemVM? _selectedItem;
        private bool _hasSingleItem;

        [DataSourceProperty]
        public MBBindingList<TSelectorItemVM> ItemList { get; } = [];

        [DataSourceProperty]
        public int SelectedIndex
        {
            get => _selectedIndex;
            set
            {
                if (SetField(ref _selectedIndex, value, nameof(SelectedIndex)))
                {
                    if (SelectedItem != null)
                        _setIsSelectedDelegate(SelectedItem, false);

                    SelectedItem = GetCurrentItem();
                    if (SelectedItem != null)
                        _setIsSelectedDelegate(SelectedItem, true);
                }
            }
        }

        [DataSourceProperty]
        public TSelectorItemVM? SelectedItem { get => _selectedItem; set => SetField(ref _selectedItem, value, nameof(SelectedItem)); }

        [DataSourceProperty]
        public bool HasSingleItem { get => _hasSingleItem; set => SetField(ref _hasSingleItem, value, nameof(HasSingleItem)); }

        public MCMSelectorVM()
        {
            HasSingleItem = true;
        }
        public MCMSelectorVM(IEnumerable<object> list, int selectedIndex)
        {
            Refresh(list, selectedIndex);
        }


        /// <inheritdoc/>
        public override void RefreshValues()
        {
            base.RefreshValues();

            ItemList.ApplyActionOnAllItems(x => x.RefreshValues());
        }

        public void Refresh(IEnumerable<object> list, int selectedIndex)
        {
            ItemList.Clear();

            _selectedIndex = -1;

            // Check that TSelectorItemVM has a constructor that accepts the object

            foreach (var obj in list)
            {
                if (Activator.CreateInstance(typeof(TSelectorItemVM), obj) is TSelectorItemVM val)
                    ItemList.Add(val);
            }

            HasSingleItem = ItemList.Count <= 1;

            SelectedIndex = selectedIndex;
        }

        public TSelectorItemVM? GetCurrentItem()
        {
            if (ItemList.Count > 0 && SelectedIndex >= 0 && SelectedIndex < ItemList.Count)
            {
                return ItemList[SelectedIndex];
            }
            return null;
        }

        public void AddItem(TSelectorItemVM item)
        {
            ItemList.Add(item);
            HasSingleItem = ItemList.Count <= 1;
        }

        public void ExecuteRandomize()
        {
            if (ItemList.GetRandomElementWithPredicate(i => _canBeSelectedDelegate(i)) is { } element)
            {
                SelectedIndex = ItemList.IndexOf(element);
            }
        }

        public void ExecuteSelectNextItem()
        {
            if (ItemList.Count > 0)
            {
                for (var num = (SelectedIndex + 1) % ItemList.Count; num != SelectedIndex; num = (num + 1) % ItemList.Count)
                {
                    if (_canBeSelectedDelegate(ItemList[num]))
                    {
                        SelectedIndex = num;
                        return;
                    }
                }
            }
        }

        public void ExecuteSelectPreviousItem()
        {
            if (ItemList.Count > 0)
            {
                for (var num = SelectedIndex - 1 >= 0 ? SelectedIndex - 1 : ItemList.Count - 1; num != SelectedIndex; num = num - 1 >= 0 ? num - 1 : ItemList.Count - 1)
                {
                    if (_canBeSelectedDelegate(ItemList[num]))
                    {
                        SelectedIndex = num;
                        return;
                    }
                }
            }
        }
    }
}