#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2010 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#endregion
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Nuclex.Input;
using Nuclex.Support.Collections;
using Nuclex.UserInterface.Input;
namespace Nuclex.UserInterface.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 ListControl : Control, IFocusable {
/// Triggered when the selected items in list have changed
public event EventHandler SelectionChanged;
/// Initializes a new list box control
public ListControl() {
this.items = new ObservableList();
this.items.Cleared += new EventHandler(itemsCleared);
this.items.ItemAdded += new EventHandler>(itemAdded);
this.items.ItemRemoved += new EventHandler>(itemRemoved);
this.selectedItems = new ObservableList();
this.selectedItems.Cleared += new EventHandler(selectionCleared);
this.selectedItems.ItemAdded += new EventHandler>(
selectionAdded
);
this.selectedItems.ItemRemoved += new EventHandler>(
selectionRemoved
);
this.slider = new VerticalSliderControl();
this.slider.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(this.slider);
}
/// How the user can select items in the list
public ListSelectionMode SelectionMode {
get { return this.selectionMode; }
set { this.selectionMode = value; }
}
/// Slider the list uses to scroll through its items
public VerticalSliderControl Slider {
get { return this.slider; }
}
/// Items being displayed in the list
public IList Items {
get { return this.items; }
}
/// Indices of the items current selected in the list
public IList SelectedItems {
get { return this.selectedItems; }
}
/// 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(MouseButtons button) {
if(this.listRowLocator != null) {
int row = this.listRowLocator.GetRow(
GetAbsoluteBounds(), this.slider.ThumbPosition, this.items.Count, this.mouseY
);
if((row >= 0) && (row < this.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(this.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(this.selectedItems.Count == 1) {
if(this.selectedItems[0] == row) {
break; // do not fire the SelectionChanged event
}
this.selectedItems[0] = row;
} else {
this.selectedItems.Clear();
this.selectedItems.Add(row);
}
OnSelectionChanged();
break;
}
// Any number of items can be selected
case ListSelectionMode.Multi: {
if(!this.selectedItems.Remove(row)) {
this.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(this.listRowLocator != null) {
RectangleF bounds = GetAbsoluteBounds();
float totalitems = this.items.Count;
float itemsInView = bounds.Height / this.listRowLocator.GetRowHeight(bounds);
float scrollableItems = totalitems - itemsInView;
this.slider.ThumbPosition -= ItemsPerTick / scrollableItems * ticks;
this.slider.ThumbPosition = MathHelper.Clamp(
this.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) {
this.mouseY = y;
}
/// Called when the selected items in the list have changed
protected virtual void OnSelectionChanged() {
if(SelectionChanged != null) {
SelectionChanged(this, EventArgs.Empty);
}
}
/// Whether the control can currently obtain the input focus
bool IFocusable.CanGetFocus {
get { return true; }
}
/// 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) && (this.listRowLocator != null)) {
RectangleF bounds = GetAbsoluteBounds();
float totalitems = this.items.Count;
float itemsInView = bounds.Height / this.listRowLocator.GetRowHeight(bounds);
this.slider.ThumbSize = Math.Min(1.0f, itemsInView / totalitems);
}
}
///
/// Can be set by renderers to enable selection of list items by mouse
///
public IListRowLocator ListRowLocator {
get { return this.listRowLocator; }
set {
if(value != this.listRowLocator) {
this.listRowLocator = value;
updateSlider();
}
}
}
///
/// 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;
/// How the list lets the user select from its items
private ListSelectionMode selectionMode;
/// Items contained in the list
private ObservableList items;
/// Items currently selected in the list
private ObservableList selectedItems;
/// Slider the lists uses to scroll through its items
private VerticalSliderControl slider;
}
} // namespace Nuclex.UserInterface.Controls.Desktop