#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