Newer
Older
DungeonShooting / DungeonShooting_Godot / src / framework / ui / grid / UiGrid.cs


using System;
using System.Collections.Generic;
using Godot;

/// <summary>
/// Ui网格组件
/// </summary>
/// <typeparam name="TUiCellNode">Ui节点类型</typeparam>
/// <typeparam name="TData">传给Cell的数据类型</typeparam>
public partial class UiGrid<TUiCellNode, TData> : IDestroy where TUiCellNode : IUiCellNode
{
    public bool IsDestroyed { get; private set; }
    
    private TUiCellNode _template;
    private Vector2 _size = Vector2.Zero;
    private Node _parent;
    private Type _cellType;
    private Stack<UiCell<TUiCellNode, TData>> _cellPool = new Stack<UiCell<TUiCellNode, TData>>();
    private List<UiCell<TUiCellNode, TData>> _cellList = new List<UiCell<TUiCellNode, TData>>();

    
    private GridContainer _gridContainer;
    private Vector2I _cellOffset;
    private int _columns;
    private bool _autoColumns;

    public UiGrid(TUiCellNode template, Type cellType)
    {
        _gridContainer = new GridContainer();
        _gridContainer.Ready += OnReady;
        _template = template;
        _cellType = cellType;
        var uiInstance = _template.GetUiInstance();
        _parent = uiInstance.GetParent();
        _parent.RemoveChild(uiInstance);
        _parent.AddChild(_gridContainer);
        if (uiInstance is Control control)
        {
            _size = control.Size;
        }
    }
    
    public void SetCellOffset(Vector2I offset)
    {
        if (_gridContainer != null)
        {
            _cellOffset = offset;
            _gridContainer.AddThemeConstantOverride("h_separation", offset.X);
            _gridContainer.AddThemeConstantOverride("v_separation", offset.Y);
        }
    }

    public Vector2I GetCellOffset()
    {
        return _cellOffset;
    }
    
    public void SetColumns(int columns)
    {
        _columns = columns;
        if (_gridContainer != null)
        {
            _gridContainer.Columns = columns;
        }
    }

    public int GetColumns()
    {
        if (_gridContainer != null)
        {
            return _gridContainer.Columns;
        }

        return _columns;
    }

    public void SetAutoColumns(bool flag)
    {
        if (flag != _autoColumns)
        {
            _autoColumns = flag;
            if (_gridContainer != null)
            {
                if (_autoColumns)
                {
                    _gridContainer.Resized += OnGridResized;
                    OnGridResized();
                }
                else
                {
                    _gridContainer.Columns = _columns;
                    _gridContainer.Resized -= OnGridResized;
                }
            }
        }
    }

    public bool GetAutoColumns()
    {
        return _autoColumns;
    }
    
    public void SetHorizontalExpand(bool flag)
    {
        if (_gridContainer != null)
        {
            if (flag)
            {
                _gridContainer.SizeFlagsHorizontal |= Control.SizeFlags.Expand;
            }
            else if ((_gridContainer.SizeFlagsHorizontal & Control.SizeFlags.Expand) != 0)
            {
                _gridContainer.SizeFlagsHorizontal ^= Control.SizeFlags.Expand;
            }
        }
    }

    public bool GetHorizontalExpand()
    {
        if (_gridContainer != null)
        {
            return (_gridContainer.SizeFlagsHorizontal & Control.SizeFlags.Expand) != 0;
        }

        return false;
    }
    
    /// <summary>
    /// 设置当前网格组件中的所有数据
    /// </summary>
    public void SetDataList(TData[] array)
    {
        if (array.Length > _cellList.Count)
        {
            do
            {
                var cell = GetCellInstance();
                _cellList.Add(cell);
                _gridContainer.AddChild(cell.CellNode.GetUiInstance());
            } while (array.Length > _cellList.Count);
        }
        else if(array.Length < _cellList.Count)
        {
            do
            {
                var cell = _cellList[_cellList.Count - 1];
                _cellList.RemoveAt(_cellList.Count - 1);
                ReclaimCellInstance(cell);
            } while (array.Length < _cellList.Count);
        }

        for (var i = 0; i < _cellList.Count; i++)
        {
            var data = array[i];
            _cellList[i].SetData(data);
        }
    }

    /// <summary>
    /// 添加单条数据
    /// </summary>
    /// <param name="data"></param>
    public void Add(TData data)
    {
        var cell = GetCellInstance();
        _cellList.Add(cell);
        _gridContainer.AddChild(cell.CellNode.GetUiInstance());
        cell.SetData(data);
    }
    
    public void Destroy()
    {
        if (IsDestroyed)
        {
            return;
        }

        IsDestroyed = true;
        
        for (var i = 0; i < _cellList.Count; i++)
        {
            _cellList[i].Destroy();
        }
        foreach (var uiCell in _cellPool)
        {
            uiCell.Destroy();
        }
        _cellList = null;
        _cellPool = null;
        if (_gridContainer != null)
        {
            _gridContainer.QueueFree();
        }
    }

    private void OnReady()
    {
        _gridContainer.Ready -= OnReady;
        if (_template.GetUiInstance() is Control control)
        {
            _gridContainer.Position = control.Position;
        }
    }
    
    private UiCell<TUiCellNode, TData> GetCellInstance()
    {
        if (_cellPool.Count > 0)
        {
            return _cellPool.Pop();
        }

        var uiCell = Activator.CreateInstance(_cellType) as UiCell<TUiCellNode, TData>;
        if (uiCell is null)
        {
            throw new Exception($"cellType 无法转为'{typeof(UiCell<TUiCellNode, TData>).FullName}'类型!");
        }
        uiCell.Init(this, (TUiCellNode)_template.CloneUiCell());
        return uiCell;
    }

    private void ReclaimCellInstance(UiCell<TUiCellNode, TData> cell)
    {
        _gridContainer.RemoveChild(cell.CellNode.GetUiInstance());
        _cellPool.Push(cell);
    }

    private void OnGridResized()
    {
        if (_autoColumns && _gridContainer != null)
        {
            var width = _gridContainer.Size.X;
            if (width <= _size.X + _cellOffset.X)
            {
                _gridContainer.Columns = 1;
            }
            else
            {
                _gridContainer.Columns = Mathf.FloorToInt(width / (_size.X + _cellOffset.X));
            }
        }
    }
}