using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using MonoGame.Extended.Collections;
using MonoGame.Extended.Input;
namespace MonoGame.Extended.NuclexGui.Controls.Desktop
{
/// How the list lets the user select items
public enum ListSelectionMode
{
/// The user is not allowed to select an item
None,
/// The user can select only one item
Single,
/// The user can pick any number of items
Multi
}
/// List showing a sequence of items
public class GuiListControl : GuiControl, IFocusable
{
/// Items contained in the list
private readonly ObservableCollection _items;
///
/// Row locator through which the list can detect which row the mouse has
/// been pressed down on
///
private IListRowLocator _listRowLocator;
/// Last known Y coordinate of the mouse
private float _mouseY;
/// Items currently selected in the list
private readonly ObservableCollection _selectedItems;
/// How the list lets the user select from its items
private ListSelectionMode _selectionMode;
/// Slider the lists uses to scroll through its items
private readonly GuiVerticalSliderControl _slider;
public GuiListControl()
{
_items = new ObservableCollection();
_items.Cleared += ItemsCleared;
_items.ItemAdded += ItemAdded;
_items.ItemRemoved += ItemRemoved;
_selectedItems = new ObservableCollection();
_selectedItems.Cleared += SelectionCleared;
_selectedItems.ItemAdded += SelectionAdded;
_selectedItems.ItemRemoved += SelectionRemoved;
_slider = new GuiVerticalSliderControl
{
Bounds = new UniRectangle(
new UniScalar(1.0f, -20.0f), new UniScalar(0.0f, 0.0f),
new UniScalar(0.0f, 20.0f), new UniScalar(1.0f, 0.0f)
)
};
Children.Add(_slider);
}
/// How the user can select items in the list
public ListSelectionMode SelectionMode
{
get { return _selectionMode; }
set { _selectionMode = value; }
}
/// Slider the list uses to scroll through its items
public GuiVerticalSliderControl Slider => _slider;
/// Items being displayed in the list
public IList Items => _items;
/// Indices of the items current selected in the list
public IList SelectedItems => _selectedItems;
///
/// Can be set by renderers to enable selection of list items by mouse
///
public IListRowLocator ListRowLocator
{
get { return _listRowLocator; }
set
{
if (value != _listRowLocator)
{
_listRowLocator = value;
UpdateSlider();
}
}
}
/// Whether the control can currently obtain the input focus
bool IFocusable.CanGetFocus => true;
/// Triggered when the selected items in list have changed
public event EventHandler SelectionChanged;
/// Called when a mouse button has been pressed down
/// Index of the button that has been pressed
///
/// If this method states that a mouse press is processed by returning
/// true, that means the control did something with it and the mouse press
/// should not be acted upon by any other listener.
///
protected override void OnMousePressed(MouseButton button)
{
if (_listRowLocator != null)
{
var row = _listRowLocator.GetRow(GetAbsoluteBounds(), _slider.ThumbPosition, _items.Count, _mouseY);
if ((row >= 0) && (row < _items.Count))
OnRowClicked(row);
}
}
/// Called when the user has clicked on a row in the list
/// Row the user has clicked on
///
/// The default behavior of the list control in multi select mode is to
/// toggle items that are clicked between selected and unselected. If you
/// need different behavior (for example, dragging a selected region or
/// selecting sequences of items by holding the shift key), you can override
/// this method and handle the selection behavior yourself.
///
protected virtual void OnRowClicked(int row)
{
switch (_selectionMode)
{
// The user isn't allowed to select items in the list
case ListSelectionMode.None:
{
break;
}
// Only a single item can be selected at a time
case ListSelectionMode.Single:
{
if (_selectedItems.Count == 1)
{
if (_selectedItems[0] == row)
break; // do not fire the SelectionChanged event
_selectedItems[0] = row;
}
else
{
_selectedItems.Clear();
_selectedItems.Add(row);
}
OnSelectionChanged();
break;
}
// Any number of items can be selected
case ListSelectionMode.Multi:
{
if (!_selectedItems.Remove(row))
_selectedItems.Add(row);
OnSelectionChanged();
break;
}
}
}
/// Called when the mouse wheel has been rotated
/// Number of ticks that the mouse wheel has been rotated
protected override void OnMouseWheel(float ticks)
{
const float itemsPerTick = 1.0f;
if (_listRowLocator != null)
{
var bounds = GetAbsoluteBounds();
float totalitems = _items.Count;
var itemsInView = bounds.Height/_listRowLocator.GetRowHeight(bounds);
var scrollableItems = totalitems - itemsInView;
_slider.ThumbPosition -= itemsPerTick/scrollableItems*ticks;
_slider.ThumbPosition = MathHelper.Clamp(_slider.ThumbPosition, 0.0f, 1.0f);
}
}
/// Called when the mouse position is updated
/// X coordinate of the mouse cursor on the control
/// Y coordinate of the mouse cursor on the control
protected override void OnMouseMoved(float x, float y)
{
_mouseY = y;
}
/// Called when the selected items in the list have changed
protected virtual void OnSelectionChanged()
{
SelectionChanged?.Invoke(this, EventArgs.Empty);
}
/// Called when an item is removed from the items list
/// List the item has been removed from
/// Contains the item that has been removed
private void ItemRemoved(object sender, ItemEventArgs arguments)
{
UpdateSlider();
}
/// Called when an item is added to the items list
/// List the item has been added to
/// Contains the item that has been added
private void ItemAdded(object sender, ItemEventArgs arguments)
{
UpdateSlider();
}
/// Called when the items list is about to clear itself
/// Items list that is about to clear itself
/// Not used
private void ItemsCleared(object sender, EventArgs arguments)
{
UpdateSlider();
}
/// Called when an entry is added to the list of selected items
/// List to which an item was added to
/// Contains the added item
private void SelectionAdded(object sender, ItemEventArgs arguments)
{
OnSelectionChanged();
}
///
/// Called when an entry is removed from the list of selected items
///
/// List from which an item was removed
/// Contains the removed item
private void SelectionRemoved(object sender, ItemEventArgs arguments)
{
OnSelectionChanged();
}
/// Called when the selected items list is about to clear itself
/// List that is about to clear itself
/// Not Used
private void SelectionCleared(object sender, EventArgs arguments)
{
OnSelectionChanged();
}
/// Updates the size and position of the list's slider
private void UpdateSlider()
{
if ((Screen != null) && (_listRowLocator != null))
{
var bounds = GetAbsoluteBounds();
float totalitems = _items.Count;
var itemsInView = bounds.Height/_listRowLocator.GetRowHeight(bounds);
_slider.ThumbSize = Math.Min(1.0f, itemsInView/totalitems);
}
}
}
}