mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-09-06 17:58:05 +02:00
Improved ItemLists (#863)
* Improve ItemList * Address reviews * Items can only be at one ItemList at a time * Nested Item class
This commit is contained in:
committed by
Pieter-Jan Briers
parent
dc57178258
commit
7fc99ee528
@@ -1,20 +1,25 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Graphics.Drawing;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Timer = Robust.Shared.Timers.Timer;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
public class ItemList : Control
|
||||
public class ItemList : Control, IList<ItemList.Item>
|
||||
{
|
||||
private bool _isAtBottom = true;
|
||||
private int _totalContentHeight;
|
||||
|
||||
private VScrollBar _scrollBar = new VScrollBar();
|
||||
private VScrollBar _scrollBar;
|
||||
private readonly List<Item> _itemList = new List<Item>();
|
||||
public event Action<ItemListSelectedEventArgs> OnItemSelected;
|
||||
public event Action<ItemListDeselectedEventArgs> OnItemDeselected;
|
||||
@@ -25,9 +30,11 @@ namespace Robust.Client.UserInterface.Controls
|
||||
public const string StylePropertySelectedItemBackground = "selected-item-background";
|
||||
public const string StylePropertyDisabledItemBackground = "disabled-item-background";
|
||||
|
||||
public int ItemCount => _itemList.Count;
|
||||
public int Count => _itemList.Count;
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
public bool ScrollFollowing { get; set; } = true;
|
||||
public int ButtonDeselectDelay { get; set; } = 100;
|
||||
|
||||
public ItemListSelectMode SelectMode { get; set; } = ItemListSelectMode.Single;
|
||||
|
||||
@@ -62,28 +69,157 @@ namespace Robust.Client.UserInterface.Controls
|
||||
_updateScrollbarVisibility();
|
||||
}
|
||||
|
||||
public void AddItem(string text, Texture icon = null, bool selectable = true)
|
||||
public void Add(Item item)
|
||||
{
|
||||
var item = new Item {Text = text, Icon = icon, Selectable = selectable};
|
||||
if (item == null) return;
|
||||
if(item.Owner != this) throw new ArgumentException("Item is owned by another ItemList!");
|
||||
|
||||
_itemList.Add(item);
|
||||
|
||||
item.OnSelected += Select;
|
||||
item.OnDeselected += Deselect;
|
||||
|
||||
RecalculateContentHeight();
|
||||
if (_isAtBottom && ScrollFollowing)
|
||||
{
|
||||
_scrollBar.MoveToEnd();
|
||||
}
|
||||
}
|
||||
|
||||
public void AddIconItem(Texture icon, bool selectable = true)
|
||||
public Item AddItem(string text, Texture icon = null, bool selectable = true)
|
||||
{
|
||||
AddItem(null, icon, selectable);
|
||||
var item = new Item(this) {Text = text, Icon = icon, Selectable = selectable};
|
||||
Add(item);
|
||||
return item;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_itemList.Clear();
|
||||
foreach (var item in _itemList.ToArray())
|
||||
{
|
||||
Remove(item);
|
||||
}
|
||||
|
||||
_totalContentHeight = 0;
|
||||
}
|
||||
|
||||
public bool Contains(Item item)
|
||||
{
|
||||
return _itemList.Contains(item);
|
||||
}
|
||||
|
||||
public void CopyTo(Item[] array, int arrayIndex)
|
||||
{
|
||||
_itemList.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
public bool Remove(Item item)
|
||||
{
|
||||
if (item == null) return false;
|
||||
|
||||
var value = _itemList.Remove(item);
|
||||
|
||||
item.OnSelected -= Select;
|
||||
item.OnDeselected -= Deselect;
|
||||
|
||||
RecalculateContentHeight();
|
||||
if (_isAtBottom && ScrollFollowing)
|
||||
_scrollBar.MoveToEnd();
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
Remove(this[index]);
|
||||
}
|
||||
|
||||
public IEnumerator<Item> GetEnumerator()
|
||||
{
|
||||
return _itemList.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public int IndexOf(Item item)
|
||||
{
|
||||
return _itemList.IndexOf(item);
|
||||
}
|
||||
|
||||
public void Insert(int index, Item item)
|
||||
{
|
||||
if (item == null) return;
|
||||
if(item.Owner != this) throw new ArgumentException("Item is owned by another ItemList!");
|
||||
|
||||
_itemList.Insert(index, item);
|
||||
|
||||
item.OnSelected += Select;
|
||||
item.OnDeselected += Deselect;
|
||||
|
||||
RecalculateContentHeight();
|
||||
if (_isAtBottom && ScrollFollowing)
|
||||
_scrollBar.MoveToEnd();
|
||||
}
|
||||
|
||||
// Without this attribute, this would compile into a property called "Item", causing problems with the Item class.
|
||||
[System.Runtime.CompilerServices.IndexerName("IndexItem")]
|
||||
public Item this[int index]
|
||||
{
|
||||
get => _itemList[index];
|
||||
set => _itemList[index] = value;
|
||||
}
|
||||
|
||||
public IEnumerable<Item> GetSelected()
|
||||
{
|
||||
var list = new List<Item>();
|
||||
|
||||
for (var i = 0; i < _itemList.Count; i++)
|
||||
{
|
||||
var item = _itemList[i];
|
||||
if (item.Selected) list.Add(item);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private void Select(int idx)
|
||||
{
|
||||
OnItemSelected?.Invoke(new ItemListSelectedEventArgs(idx, this));
|
||||
}
|
||||
|
||||
private void Select(Item item)
|
||||
{
|
||||
var idx = IndexOf(item);
|
||||
if (idx != -1)
|
||||
Select(idx);
|
||||
}
|
||||
|
||||
private void Deselect(int idx)
|
||||
{
|
||||
OnItemDeselected?.Invoke(new ItemListDeselectedEventArgs(idx, this));
|
||||
}
|
||||
|
||||
private void Deselect(Item item)
|
||||
{
|
||||
var idx = IndexOf(item);
|
||||
if (idx == -1) return;
|
||||
Deselect(idx);
|
||||
}
|
||||
|
||||
public void ClearSelected()
|
||||
{
|
||||
foreach (var item in GetSelected())
|
||||
{
|
||||
item.Selected = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void SortItemsByText()
|
||||
{
|
||||
_itemList.Sort((p, q) => string.Compare(p.Text, q.Text, StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
public void EnsureCurrentIsVisible()
|
||||
{
|
||||
// TODO: Implement this.
|
||||
@@ -94,113 +230,6 @@ namespace Robust.Client.UserInterface.Controls
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool IsSelected(int idx)
|
||||
{
|
||||
return _itemList[idx].Selected;
|
||||
}
|
||||
|
||||
public void RemoveItem(int idx)
|
||||
{
|
||||
_itemList.RemoveAt(idx);
|
||||
RecalculateContentHeight();
|
||||
}
|
||||
|
||||
public void Select(int idx, bool single = true)
|
||||
{
|
||||
if (single)
|
||||
{
|
||||
for (var jdx = 0; jdx < _itemList.Count; jdx++)
|
||||
{
|
||||
Unselect(jdx);
|
||||
}
|
||||
}
|
||||
|
||||
var i = _itemList[idx];
|
||||
|
||||
if (i.Selectable)
|
||||
{
|
||||
i.Selected = true;
|
||||
OnItemSelected?.Invoke(new ItemListSelectedEventArgs(idx, this));
|
||||
}
|
||||
}
|
||||
|
||||
public void Select(Item item, bool single = true)
|
||||
{
|
||||
var idx = _itemList.IndexOf(item);
|
||||
if (idx != -1)
|
||||
Select(idx, single);
|
||||
}
|
||||
|
||||
public void SetItemDisabled(int idx, bool disabled)
|
||||
{
|
||||
Unselect(idx);
|
||||
var i = _itemList[idx];
|
||||
i.Disabled = disabled;
|
||||
}
|
||||
|
||||
public void SetItemIcon(int idx, Texture icon)
|
||||
{
|
||||
_itemList[idx].Icon = icon;
|
||||
}
|
||||
|
||||
public void SetItemIconRegion(int idx, UIBox2 region)
|
||||
{
|
||||
_itemList[idx].IconRegion = region;
|
||||
}
|
||||
|
||||
public void SetItemSelectable(int idx, bool selectable)
|
||||
{
|
||||
_itemList[idx].Selectable = selectable;
|
||||
}
|
||||
|
||||
public void SetItemText(int idx, string text)
|
||||
{
|
||||
_itemList[idx].Text = text;
|
||||
}
|
||||
|
||||
public void SetItemTooltip(int idx, string tooltip)
|
||||
{
|
||||
_itemList[idx].TooltipText = tooltip;
|
||||
}
|
||||
|
||||
public void SetItemTooltipEnabled(int idx, bool enabled)
|
||||
{
|
||||
_itemList[idx].TooltipEnabled = true;
|
||||
}
|
||||
|
||||
public void SortItemsByText()
|
||||
{
|
||||
_itemList.Sort((p, q) => string.Compare(p.Text, q.Text, StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
public void Unselect(int idx)
|
||||
{
|
||||
{
|
||||
var i = _itemList[idx];
|
||||
if (!i.Selected) return;
|
||||
i.Selected = false;
|
||||
OnItemDeselected?.Invoke(new ItemListDeselectedEventArgs(idx, this));
|
||||
}
|
||||
}
|
||||
|
||||
public void Unselect(Item item)
|
||||
{
|
||||
var idx = _itemList.IndexOf(item);
|
||||
if (idx == -1) return;
|
||||
Unselect(idx);
|
||||
}
|
||||
|
||||
public void ClearSelections()
|
||||
{
|
||||
foreach (var item in _itemList)
|
||||
{
|
||||
if (item.Selected)
|
||||
{
|
||||
Unselect(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Font ActualFont
|
||||
{
|
||||
get
|
||||
@@ -306,7 +335,10 @@ namespace Robust.Client.UserInterface.Controls
|
||||
bg = iconDisabledBg;
|
||||
|
||||
if (item.Selected)
|
||||
{
|
||||
Logger.Info("we draw");
|
||||
bg = iconSelectedBg;
|
||||
}
|
||||
|
||||
var itemHeight = 0f;
|
||||
if (item.Icon != null)
|
||||
@@ -393,13 +425,25 @@ namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
if (item.Region == null)
|
||||
continue;
|
||||
|
||||
if (!item.Region.Value.Contains(args.RelativePosition))
|
||||
continue;
|
||||
|
||||
if (item.Selectable && !item.Disabled)
|
||||
if (item.Selected)
|
||||
Unselect(item);
|
||||
else
|
||||
Select(item, SelectMode == ItemListSelectMode.Single);
|
||||
{
|
||||
if (item.Selected && SelectMode != ItemListSelectMode.Button)
|
||||
{
|
||||
ClearSelected();
|
||||
item.Selected = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if(SelectMode != ItemListSelectMode.Multiple)
|
||||
ClearSelected();
|
||||
item.Selected = true;
|
||||
if (SelectMode == ItemListSelectMode.Button)
|
||||
Timer.Spawn(ButtonDeselectDelay, () => { item.Selected = false; } );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -467,31 +511,6 @@ namespace Robust.Client.UserInterface.Controls
|
||||
_scrollBar.Visible = _totalContentHeight + ActualBackground.MinimumSize.Y > PixelHeight;
|
||||
}
|
||||
|
||||
public sealed class Item
|
||||
{
|
||||
public string Text;
|
||||
public string TooltipText;
|
||||
public Texture Icon;
|
||||
public UIBox2 IconRegion;
|
||||
public Color IconModulate = Color.White;
|
||||
public bool Selected;
|
||||
public bool Selectable = true;
|
||||
public bool TooltipEnabled = true;
|
||||
public bool Disabled;
|
||||
|
||||
public UIBox2? Region;
|
||||
|
||||
public Vector2 IconSize
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Icon == null)
|
||||
return Vector2.Zero;
|
||||
return IconRegion.Size != Vector2.Zero ? IconRegion.Size : Icon.Size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ItemListEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
@@ -548,7 +567,63 @@ namespace Robust.Client.UserInterface.Controls
|
||||
{
|
||||
None,
|
||||
Single,
|
||||
Multiple
|
||||
Multiple,
|
||||
Button,
|
||||
}
|
||||
|
||||
public sealed class Item
|
||||
{
|
||||
public event Action<Item> OnSelected;
|
||||
public event Action<Item> OnDeselected;
|
||||
|
||||
private bool _selected = false;
|
||||
private bool _disabled = false;
|
||||
|
||||
public ItemList Owner { get; }
|
||||
public string Text { get; set; }
|
||||
public string TooltipText { get; set; }
|
||||
public Texture Icon { get; set; }
|
||||
public UIBox2 IconRegion { get; set; }
|
||||
public Color IconModulate { get; set; } = Color.White;
|
||||
public bool Selectable { get; set; } = true;
|
||||
public bool TooltipEnabled { get; set; } = true;
|
||||
public UIBox2? Region { get; set; }
|
||||
|
||||
public bool Disabled
|
||||
{
|
||||
get => _disabled;
|
||||
set
|
||||
{
|
||||
_disabled = value;
|
||||
if (Selected) Selected = false;
|
||||
}
|
||||
}
|
||||
public bool Selected
|
||||
{
|
||||
get => _selected;
|
||||
set
|
||||
{
|
||||
if (!Selectable) return;
|
||||
_selected = value;
|
||||
if(_selected) OnSelected?.Invoke(this);
|
||||
else OnDeselected?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 IconSize
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Icon == null)
|
||||
return Vector2.Zero;
|
||||
return IconRegion.Size != Vector2.Zero ? IconRegion.Size : Icon.Size;
|
||||
}
|
||||
}
|
||||
|
||||
public Item(ItemList owner)
|
||||
{
|
||||
Owner = owner;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user