-
-
Notifications
You must be signed in to change notification settings - Fork 314
/
GuiListControl.cs
270 lines (230 loc) · 10.2 KB
/
GuiListControl.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using MonoGame.Extended.Collections;
using MonoGame.Extended.Input;
namespace MonoGame.Extended.NuclexGui.Controls.Desktop
{
/// <summary>How the list lets the user select items</summary>
public enum ListSelectionMode
{
/// <summary>The user is not allowed to select an item</summary>
None,
/// <summary>The user can select only one item</summary>
Single,
/// <summary>The user can pick any number of items</summary>
Multi
}
/// <summary>List showing a sequence of items</summary>
public class GuiListControl : GuiControl, IFocusable
{
/// <summary>Items contained in the list</summary>
private readonly ObservableCollection<string> _items;
/// <summary>
/// Row locator through which the list can detect which row the mouse has
/// been pressed down on
/// </summary>
private IListRowLocator _listRowLocator;
/// <summary>Last known Y coordinate of the mouse</summary>
private float _mouseY;
/// <summary>Items currently selected in the list</summary>
private readonly ObservableCollection<int> _selectedItems;
/// <summary>How the list lets the user select from its items</summary>
private ListSelectionMode _selectionMode;
/// <summary>Slider the lists uses to scroll through its items</summary>
private readonly GuiVerticalSliderControl _slider;
public GuiListControl()
{
_items = new ObservableCollection<string>();
_items.Cleared += ItemsCleared;
_items.ItemAdded += ItemAdded;
_items.ItemRemoved += ItemRemoved;
_selectedItems = new ObservableCollection<int>();
_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);
}
/// <summary>How the user can select items in the list</summary>
public ListSelectionMode SelectionMode
{
get { return _selectionMode; }
set { _selectionMode = value; }
}
/// <summary>Slider the list uses to scroll through its items</summary>
public GuiVerticalSliderControl Slider => _slider;
/// <summary>Items being displayed in the list</summary>
public IList<string> Items => _items;
/// <summary>Indices of the items current selected in the list</summary>
public IList<int> SelectedItems => _selectedItems;
/// <summary>
/// Can be set by renderers to enable selection of list items by mouse
/// </summary>
public IListRowLocator ListRowLocator
{
get { return _listRowLocator; }
set
{
if (value != _listRowLocator)
{
_listRowLocator = value;
UpdateSlider();
}
}
}
/// <summary>Whether the control can currently obtain the input focus</summary>
bool IFocusable.CanGetFocus => true;
/// <summary>Triggered when the selected items in list have changed</summary>
public event EventHandler SelectionChanged;
/// <summary>Called when a mouse button has been pressed down</summary>
/// <param name="button">Index of the button that has been pressed</param>
/// <remarks>
/// 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.
/// </remarks>
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);
}
}
/// <summary>Called when the user has clicked on a row in the list</summary>
/// <param name="row">Row the user has clicked on</param>
/// <remarks>
/// 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.
/// </remarks>
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;
}
}
}
/// <summary>Called when the mouse wheel has been rotated</summary>
/// <param name="ticks">Number of ticks that the mouse wheel has been rotated</param>
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);
}
}
/// <summary>Called when the mouse position is updated</summary>
/// <param name="x">X coordinate of the mouse cursor on the control</param>
/// <param name="y">Y coordinate of the mouse cursor on the control</param>
protected override void OnMouseMoved(float x, float y)
{
_mouseY = y;
}
/// <summary>Called when the selected items in the list have changed</summary>
protected virtual void OnSelectionChanged()
{
SelectionChanged?.Invoke(this, EventArgs.Empty);
}
/// <summary>Called when an item is removed from the items list</summary>
/// <param name="sender">List the item has been removed from</param>
/// <param name="arguments">Contains the item that has been removed</param>
private void ItemRemoved(object sender, ItemEventArgs<string> arguments)
{
UpdateSlider();
}
/// <summary>Called when an item is added to the items list</summary>
/// <param name="sender">List the item has been added to</param>
/// <param name="arguments">Contains the item that has been added</param>
private void ItemAdded(object sender, ItemEventArgs<string> arguments)
{
UpdateSlider();
}
/// <summary>Called when the items list is about to clear itself</summary>
/// <param name="sender">Items list that is about to clear itself</param>
/// <param name="arguments">Not used</param>
private void ItemsCleared(object sender, EventArgs arguments)
{
UpdateSlider();
}
/// <summary>Called when an entry is added to the list of selected items</summary>
/// <param name="sender">List to which an item was added to</param>
/// <param name="arguments">Contains the added item</param>
private void SelectionAdded(object sender, ItemEventArgs<int> arguments)
{
OnSelectionChanged();
}
/// <summary>
/// Called when an entry is removed from the list of selected items
/// </summary>
/// <param name="sender">List from which an item was removed</param>
/// <param name="arguments">Contains the removed item</param>
private void SelectionRemoved(object sender, ItemEventArgs<int> arguments)
{
OnSelectionChanged();
}
/// <summary>Called when the selected items list is about to clear itself</summary>
/// <param name="sender">List that is about to clear itself</param>
/// <param name="arguments">Not Used</param>
private void SelectionCleared(object sender, EventArgs arguments)
{
OnSelectionChanged();
}
/// <summary>Updates the size and position of the list's slider</summary>
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);
}
}
}
}