1 // Written in the D programming language.
2 
3 /**
4 DLANGUI library.
5 
6 This module contains list widgets implementation.
7 
8 Similar to lists implementation in Android UI API.
9 
10 Synopsis:
11 
12 ----
13 import dlangui.widgets.lists;
14 
15 ----
16 
17 Copyright: Vadim Lopatin, 2014
18 License:   $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
19 Authors:   $(WEB coolreader.org, Vadim Lopatin)
20 */
21 module dlangui.widgets.lists;
22 
23 import dlangui.widgets.widget;
24 import dlangui.widgets.controls;
25 
26 /// list widget adapter provides items for list widgets
27 interface ListAdapter {
28     /// returns number of widgets in list
29     @property int itemCount();
30     /// return list item widget by item index
31     Widget itemWidget(int index);
32 	/// return list item's state flags
33 	uint itemState(int index);
34 	/// set one or more list item's state flags, returns updated state
35 	uint setItemState(int index, uint flags);
36 	/// reset one or more list item's state flags, returns updated state
37 	uint resetItemState(int index, uint flags);
38 }
39 
40 /// List adapter for simple list of widget instances
41 class WidgetListAdapter : ListAdapter {
42     WidgetList _widgets;
43     /// list of widgets to display
44     @property ref WidgetList widgets() { return _widgets; }
45     /// returns number of widgets in list
46     @property override int itemCount() {
47         return _widgets.count;
48     }
49     /// return list item widget by item index
50     override Widget itemWidget(int index) {
51         return _widgets.get(index);
52     }
53 	/// return list item's state flags
54 	override uint itemState(int index) {
55 		return _widgets.get(index).state;
56 	}
57 	/// set one or more list item's state flags, returns updated state
58 	override uint setItemState(int index, uint flags) {
59 		return _widgets.get(index).setState(flags).state;
60 	}
61 	/// reset one or more list item's state flags, returns updated state
62 	override uint resetItemState(int index, uint flags) {
63 		return _widgets.get(index).resetState(flags).state;
64 	}
65 }
66 
67 /// List
68 class ListWidget : WidgetGroup, OnScrollHandler {
69     protected Orientation _orientation = Orientation.Vertical;
70     /// returns linear layout orientation (Vertical, Horizontal)
71     @property Orientation orientation() { return _orientation; }
72     /// sets linear layout orientation
73     @property ListWidget orientation(Orientation value) { 
74         _orientation = value;
75         _scrollbar.orientation = value;
76         requestLayout(); 
77         return this; 
78     }
79 
80     protected Rect[] _itemRects;
81     protected Point[] _itemSizes;
82     protected bool _needScrollbar;
83     protected Point _sbsz; // scrollbar size
84     protected ScrollBar _scrollbar;
85     protected int _lastMeasureWidth;
86     protected int _lastMeasureHeight;
87 
88     /// first visible item index
89     protected int _firstVisibleItem;
90     /// scroll position - offset of scroll area
91     protected int _scrollPosition;
92     /// maximum scroll position
93     protected int _maxScrollPosition;
94     /// client area rectangle (counting padding, margins, and scrollbar)
95     protected Rect _clientRc;
96     /// total height of all items for Vertical orientation, or width for Horizontal
97     protected int _totalSize;
98 	/// item with Hover state, -1 if no such item
99 	protected int _hoverItemIndex;
100 	/// item with Selected state, -1 if no such item
101 	protected int _selectedItemIndex;
102 
103 	/// when true, mouse hover selects underlying item
104 	protected bool _selectOnHover;
105 	/// when true, mouse hover selects underlying item
106 	@property bool selectOnHover() { return _selectOnHover; }
107 	/// when true, mouse hover selects underlying item
108 	@property ListWidget selectOnHover(bool select) { _selectOnHover = select; return this; }
109 
110 	/// if true, generate itemClicked on mouse down instead mouse up event
111 	protected bool _clickOnButtonDown;
112 
113     /// returns rectangle for item (not scrolled, first item starts at 0,0)
114     Rect itemRectNoScroll(int index) {
115         if (index < 0 || index >= _itemRects.length)
116             return Rect.init;
117         Rect res;
118         res = _itemRects[index];
119         return res;
120     }
121 
122     /// returns rectangle for item (scrolled)
123     Rect itemRect(int index) {
124         if (index < 0 || index >= _itemRects.length)
125             return Rect.init;
126         Rect res = itemRectNoScroll(index);
127         if (_orientation == Orientation.Horizontal) {
128             res.left -= _scrollPosition;
129             res.right -= _scrollPosition;
130         } else {
131             res.top -= _scrollPosition;
132             res.bottom -= _scrollPosition;
133         }
134         return res;
135     }
136 
137     /// returns item index by 0-based offset from top/left of list content
138     int itemByPosition(int pos) {
139         return 0;
140     }
141 
142     protected ListAdapter _adapter;
143     /// when true, need to destroy adapter on list destroy
144     protected bool _ownAdapter;
145 
146     /// get adapter
147     @property ListAdapter adapter() { return _adapter; }
148     /// set adapter
149     @property ListWidget adapter(ListAdapter adapter) {
150         if (_adapter !is null && _ownAdapter)
151             destroy(_adapter);
152         _adapter = adapter; 
153         _ownAdapter = false;
154         onAdapterChanged();
155         return this; 
156     }
157     /// set adapter, which will be owned by list (destroy will be called for adapter on widget destroy)
158     @property ListWidget ownAdapter(ListAdapter adapter) { 
159         if (_adapter !is null && _ownAdapter)
160             destroy(_adapter);
161         _adapter = adapter; 
162         _ownAdapter = true;
163         onAdapterChanged();
164         return this; 
165     }
166 
167     /// returns number of widgets in list
168     @property int itemCount() {
169         if (_adapter !is null)
170             return _adapter.itemCount;
171         return 0;
172     }
173 
174     /// return list item widget by item index
175     Widget itemWidget(int index) {
176         if (_adapter !is null)
177             return _adapter.itemWidget(index);
178         return null;
179     }
180 
181     /// returns true if item with corresponding index is enabled
182     bool itemEnabled(int index) {
183         if (_adapter !is null && index >= 0 && index < itemCount)
184             return (_adapter.itemState(index) & State.Enabled) != 0;
185         return false;
186     }
187 
188     void onAdapterChanged() {
189         requestLayout();
190     }
191 
192 	this(string ID = null, Orientation orientation = Orientation.Vertical) {
193 		super(ID);
194         _orientation = orientation;
195         focusable = true;
196 		_hoverItemIndex = -1;
197 		_selectedItemIndex = -1;
198         _scrollbar = new ScrollBar("listscroll", orientation);
199         _scrollbar.visibility = Visibility.Gone;
200         _scrollbar.onScrollEventListener = &onScrollEvent;
201         addChild(_scrollbar);
202 	}
203 
204 	protected void setHoverItem(int index) {
205 		if (_hoverItemIndex == index)
206 			return;
207 		if (_hoverItemIndex != -1) {
208 			_adapter.resetItemState(_hoverItemIndex, State.Hovered);
209 			invalidate();
210 		}
211 		_hoverItemIndex = index;
212 		if (_hoverItemIndex != -1) {
213 			_adapter.setItemState(_hoverItemIndex, State.Hovered);
214 			invalidate();
215 		}
216 	}
217 
218 	/// override to handle change of selection
219 	protected void selectionChanged(int index, int previouslySelectedItem = -1) {
220 	}
221 
222 	/// override to handle mouse up on item
223 	protected void itemClicked(int index) {
224 	}
225 
226     protected void updateSelectedItemFocus() {
227 		if (_selectedItemIndex != -1) {
228             if ((_adapter.itemState(_selectedItemIndex) & State.Focused) != (state & State.Focused)) {
229                 if (state & State.Focused)
230                     _adapter.setItemState(_selectedItemIndex, State.Focused);
231                 else
232                     _adapter.resetItemState(_selectedItemIndex, State.Focused);
233                 invalidate();
234             }
235         }
236     }
237 
238     /// override to handle focus changes
239     override protected void handleFocusChange(bool focused) {
240         updateSelectedItemFocus();
241     }
242 
243     /// ensure selected item is visible (scroll if necessary)
244     void makeSelectionVisible() {
245         if (_selectedItemIndex < 0)
246             return; // no selection
247         if (needLayout) {
248             _makeSelectionVisibleOnNextLayout = true;
249             return;
250         }
251         makeItemVisible(_selectedItemIndex);
252     }
253 
254     protected bool _makeSelectionVisibleOnNextLayout;
255     /// ensure item is visible
256     void makeItemVisible(int itemIndex) {
257         if (itemIndex < 0 || itemIndex >= itemCount)
258             return; // no selection
259 
260         Rect viewrc = Rect(0, 0, _clientRc.width, _clientRc.height);
261         Rect scrolledrc = itemRect(itemIndex);
262         if (scrolledrc.isInsideOf(viewrc)) // completely visible
263             return;
264         int delta = 0;
265         if (_orientation == Orientation.Vertical) {
266             if (scrolledrc.top < viewrc.top)
267                 delta = scrolledrc.top - viewrc.top;
268             else if (scrolledrc.bottom > viewrc.bottom)
269                 delta = scrolledrc.bottom - viewrc.bottom;
270         } else {
271             if (scrolledrc.left < viewrc.left)
272                 delta = scrolledrc.left - viewrc.left;
273             else if (scrolledrc.right > viewrc.right)
274                 delta = scrolledrc.right - viewrc.right;
275         }
276         int newPosition = _scrollPosition + delta;
277         _scrollbar.position = newPosition;
278         _scrollPosition = newPosition;
279         invalidate();
280     }
281 
282     /// move selection
283     bool moveSelection(int direction, bool wrapAround = true) {
284         if (itemCount <= 0)
285             return false;
286         int maxAttempts = itemCount - 1;
287         int index = _selectedItemIndex;
288         for (int i = 0; i < maxAttempts; i++) {
289             int newIndex = 0;
290             if (index < 0) {
291                 // no previous selection
292                 if (direction > 0)
293                     newIndex = wrapAround ? 0 : itemCount - 1;
294                 else
295                     newIndex = wrapAround ? itemCount - 1 : 0;
296             } else {
297                 // step
298                 newIndex = index + direction;
299             }
300             if (newIndex < 0)
301                 newIndex = wrapAround ? itemCount - 1 : 0;
302             else if (newIndex >= itemCount)
303                 newIndex = wrapAround ? 0 : itemCount - 1;
304             if (newIndex != index) {
305                 if (selectItem(newIndex))
306                     return true;
307                 index = newIndex;
308             }
309         }
310         return true;
311     }
312 
313 	bool selectItem(int index, int disabledItemsSkipDirection) {
314         if (index == -1 || disabledItemsSkipDirection == 0)
315             return selectItem(index);
316         int maxAttempts = itemCount;
317         for (int i = 0; i < maxAttempts; i++) {
318             if (selectItem(index))
319                 return true;
320             index += disabledItemsSkipDirection > 0 ? 1 : -1;
321             if (index < 0)
322                 index = itemCount - 1;
323             if (index >= itemCount)
324                 index = 0;
325         }
326         return false;
327     }
328 
329 	bool selectItem(int index) {
330 		if (_selectedItemIndex == index) {
331             updateSelectedItemFocus();
332             makeSelectionVisible();
333             return true;
334         }
335         if (index != -1 && !itemEnabled(index))
336             return false;
337 		if (_selectedItemIndex != -1) {
338 			_adapter.resetItemState(_selectedItemIndex, State.Selected | State.Focused);
339 			invalidate();
340 		}
341 		_selectedItemIndex = index;
342 		if (_selectedItemIndex != -1) {
343             makeSelectionVisible();
344 			_adapter.setItemState(_selectedItemIndex, State.Selected | (state & State.Focused));
345 			invalidate();
346 		}
347         return true;
348 	}
349 
350     ~this() {
351         if (_adapter !is null && _ownAdapter)
352             destroy(_adapter);
353         _adapter = null;
354     }
355 
356     /// handle scroll event
357     override bool onScrollEvent(AbstractSlider source, ScrollEvent event) {
358         int newPosition = _scrollPosition;
359         if (event.action == ScrollAction.SliderMoved) {
360             // scroll
361             newPosition = event.position;
362         } else {
363             // use default handler for page/line up/down events
364             newPosition = event.defaultUpdatePosition();
365         }
366         if (_scrollPosition != newPosition) {
367             _scrollPosition = newPosition;
368             if (_scrollPosition > _maxScrollPosition)
369                 _scrollPosition = _maxScrollPosition;
370             if (_scrollPosition < 0)
371                 _scrollPosition = 0;
372             invalidate();
373         }
374         return true;
375     }
376 
377     /// Measure widget according to desired width and height constraints. (Step 1 of two phase layout).
378     override void measure(int parentWidth, int parentHeight) {
379         if (visibility == Visibility.Gone) {
380             _measuredWidth = _measuredHeight = 0;
381             return;
382         }
383         if (_itemSizes.length < itemCount)
384             _itemSizes.length = itemCount;
385         Rect m = margins;
386         Rect p = padding;
387         // calc size constraints for children
388         int pwidth = parentWidth;
389         int pheight = parentHeight;
390         if (parentWidth != SIZE_UNSPECIFIED)
391             pwidth -= m.left + m.right + p.left + p.right;
392         if (parentHeight != SIZE_UNSPECIFIED)
393             pheight -= m.top + m.bottom + p.top + p.bottom;
394         _scrollbar.visibility = Visibility.Visible;
395         _scrollbar.measure(pwidth, pheight);
396 
397         _lastMeasureWidth = pwidth;
398         _lastMeasureHeight = pheight;
399 
400         int sbsize = _orientation == Orientation.Vertical ? _scrollbar.measuredWidth : _scrollbar.measuredHeight;
401         // measure children
402 		Point sz;
403         _sbsz.clear;
404         for (int i = 0; i < itemCount; i++) {
405             Widget w = itemWidget(i);
406             if (w is null || w.visibility == Visibility.Gone) {
407                 _itemSizes[i].x = _itemSizes[i].y = 0;
408                 continue;
409             }
410             w.measure(pwidth, pheight);
411             _itemSizes[i].x = w.measuredWidth;
412             _itemSizes[i].y = w.measuredHeight;
413             if (_orientation == Orientation.Vertical) {
414                 // Vertical
415                 if (sz.x < w.measuredWidth)
416                     sz.x = w.measuredWidth;
417                 sz.y += w.measuredHeight;
418             } else {
419                 // Horizontal
420                 w.measure(pwidth, pheight);
421                 if (sz.y < w.measuredHeight)
422                     sz.y = w.measuredHeight;
423                 sz.x += w.measuredWidth;
424             }
425         }
426         _needScrollbar = false;
427         if (_orientation == Orientation.Vertical) {
428             if (pheight != SIZE_UNSPECIFIED && sz.y > pheight) {
429                 // need scrollbar
430                 if (pwidth != SIZE_UNSPECIFIED) {
431                     pwidth -= sbsize;
432                     _sbsz.x = sbsize;
433                     _needScrollbar = true;
434                 }
435             }
436         } else {
437             if (pwidth != SIZE_UNSPECIFIED && sz.x > pwidth) {
438                 // need scrollbar
439                 if (pheight != SIZE_UNSPECIFIED) {
440                     pheight -= sbsize;
441                     _sbsz.y = sbsize;
442                     _needScrollbar = true;
443                 }
444             }
445         }
446         if (_needScrollbar) {
447             // recalculate with scrollbar
448             sz.x = sz.y = 0;
449             for (int i = 0; i < itemCount; i++) {
450                 Widget w = itemWidget(i);
451                 if (w is null || w.visibility == Visibility.Gone)
452                     continue;
453                 w.measure(pwidth, pheight);
454                 _itemSizes[i].x = w.measuredWidth;
455                 _itemSizes[i].y = w.measuredHeight;
456                 if (_orientation == Orientation.Vertical) {
457                     // Vertical
458                     if (sz.x < w.measuredWidth)
459                         sz.x = w.measuredWidth;
460                     sz.y += w.measuredHeight;
461                 } else {
462                     // Horizontal
463                     w.measure(pwidth, pheight);
464                     if (sz.y < w.measuredHeight)
465                         sz.y = w.measuredHeight;
466                     sz.x += w.measuredWidth;
467                 }
468             }
469         }
470         measuredContent(parentWidth, parentHeight, sz.x + _sbsz.x, sz.y + _sbsz.y);
471     }
472 
473 
474     protected void updateItemPositions() {
475         Rect r;
476         int p = 0;
477         for (int i = 0; i < itemCount; i++) {
478             if (_itemSizes[i].x == 0 && _itemSizes[i].y == 0)
479                 continue;
480             if (_orientation == Orientation.Vertical) {
481                 // Vertical
482                 int w = _clientRc.width;
483                 int h = _itemSizes[i].y;
484                 r.top = p;
485                 r.bottom = p + h;
486                 r.left = 0;
487                 r.right = w;
488                 _itemRects[i] = r;
489                 p += h;
490             } else {
491                 // Horizontal
492                 int h = _clientRc.height;
493                 int w = _itemSizes[i].x;
494                 r.top = 0;
495                 r.bottom = h;
496                 r.left = p;
497                 r.right = p + w;
498                 _itemRects[i] = r;
499                 p += w;
500             }
501         }
502         _totalSize = p;
503         if (_needScrollbar) {
504             if (_orientation == Orientation.Vertical) {
505                 _scrollbar.setRange(0, p);
506                 _scrollbar.pageSize = _clientRc.height;
507                 _scrollbar.position = _scrollPosition;
508             } else {
509                 _scrollbar.setRange(0, p);
510                 _scrollbar.pageSize = _clientRc.width;
511                 _scrollbar.position = _scrollPosition;
512             }
513         }
514         /// maximum scroll position
515         if (_orientation == Orientation.Vertical) {
516             _maxScrollPosition = _totalSize - _clientRc.height;
517             if (_maxScrollPosition < 0)
518                 _maxScrollPosition = 0;
519         } else {
520             _maxScrollPosition = _totalSize - _clientRc.width;
521             if (_maxScrollPosition < 0)
522                 _maxScrollPosition = 0;
523         }
524         if (_scrollPosition > _maxScrollPosition)
525             _scrollPosition = _maxScrollPosition;
526         if (_scrollPosition < 0)
527             _scrollPosition = 0;
528         if (_needScrollbar) {
529             if (_orientation == Orientation.Vertical) {
530                 _scrollbar.position = _scrollPosition;
531             } else {
532                 _scrollbar.position = _scrollPosition;
533             }
534         }
535     }
536 
537     /// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout).
538     override void layout(Rect rc) {
539         _needLayout = false;
540         if (visibility == Visibility.Gone) {
541             return;
542         }
543         _pos = rc;
544 
545         Rect parentrc = rc;
546         applyMargins(rc);
547         applyPadding(rc);
548 
549         if (_itemRects.length < itemCount)
550             _itemRects.length = itemCount;
551 
552         // measure again if client size has been changed
553         if (_lastMeasureWidth != rc.width || _lastMeasureHeight != rc.height)
554             measure(parentrc.width, parentrc.height);
555 
556         // layout scrollbar
557         if (_needScrollbar) {
558             _scrollbar.visibility = Visibility.Visible;
559             Rect sbrect = rc;
560             if (_orientation == Orientation.Vertical)
561                 sbrect.left = sbrect.right - _sbsz.x;
562             else
563                 sbrect.top = sbrect.bottom - _sbsz.y;
564             _scrollbar.layout(sbrect);
565             rc.right -= _sbsz.x;
566             rc.bottom -= _sbsz.y;
567         } else {
568             _scrollbar.visibility = Visibility.Gone;
569         }
570 
571         _clientRc = rc;
572 
573         // calc item rectangles
574         updateItemPositions();
575 
576         if (_makeSelectionVisibleOnNextLayout) {
577             makeSelectionVisible();
578             _makeSelectionVisibleOnNextLayout = false;
579         }
580     }
581 
582     /// Draw widget at its position to buffer
583     override void onDraw(DrawBuf buf) {
584         if (visibility != Visibility.Visible)
585             return;
586         super.onDraw(buf);
587         Rect rc = _pos;
588         applyMargins(rc);
589         applyPadding(rc);
590         auto saver = ClipRectSaver(buf, rc);
591         // draw scrollbar
592         if (_needScrollbar)
593             _scrollbar.onDraw(buf);
594 
595         Point scrollOffset;
596         if (_orientation == Orientation.Vertical) {
597             scrollOffset.y = _scrollPosition;
598         } else {
599             scrollOffset.x = _scrollPosition;
600         }
601         // draw items
602         for (int i = 0; i < itemCount; i++) {
603             Rect itemrc = _itemRects[i];
604             itemrc.left += rc.left - scrollOffset.x;
605             itemrc.right += rc.left - scrollOffset.x;
606             itemrc.top += rc.top - scrollOffset.y;
607             itemrc.bottom += rc.top - scrollOffset.y;
608             if (itemrc.intersects(rc)) {
609                 Widget w = itemWidget(i);
610                 if (w is null || w.visibility != Visibility.Visible)
611                     continue;
612                 w.measure(itemrc.width, itemrc.height);
613                 w.layout(itemrc);
614 			    w.onDraw(buf);
615             }
616 		}
617     }
618 
619     /// list navigation using keys
620     override bool onKeyEvent(KeyEvent event) {
621         if (itemCount == 0)
622             return false;
623         int navigationDelta = 0;
624         if (event.action == KeyAction.KeyDown) {
625             if (orientation == Orientation.Vertical) {
626                 if (event.keyCode == KeyCode.DOWN)
627                     navigationDelta = 1;
628                 else if (event.keyCode == KeyCode.UP)
629                     navigationDelta = -1;
630             } else {
631                 if (event.keyCode == KeyCode.RIGHT)
632                     navigationDelta = 1;
633                 else if (event.keyCode == KeyCode.LEFT)
634                     navigationDelta = -1;
635             }
636         }
637         if (navigationDelta != 0) {
638             moveSelection(navigationDelta);
639             return true;
640         }
641         if (event.action == KeyAction.KeyDown) {
642             if (event.keyCode == KeyCode.HOME) {
643                 // select first enabled item on HOME key
644                 selectItem(0, 1);
645                 return true;
646             } else if (event.keyCode == KeyCode.END) {
647                 // select last enabled item on END key
648                 selectItem(itemCount - 1, -1);
649                 return true;
650             } else if (event.keyCode == KeyCode.PAGEDOWN) {
651                 // TODO
652             } else if (event.keyCode == KeyCode.PAGEUP) {
653                 // TODO
654             }
655         }
656         return false;
657         //if (_selectedItemIndex != -1 && event.action == KeyAction.KeyUp && (event.keyCode == KeyCode.SPACE || event.keyCode == KeyCode.RETURN)) {
658         //    itemClicked(_selectedItemIndex);
659         //    return true;
660         //}
661         //if (navigationDelta != 0) {
662         //    int p = _selectedItemIndex;
663         //    if (p < 0) {
664         //        if (navigationDelta < 0)
665         //            p = itemCount - 1;
666         //        else
667         //            p = 0;
668         //    } else {
669         //        p += navigationDelta;
670         //        if (p < 0)
671         //            p = itemCount - 1;
672         //        else if (p >= itemCount)
673         //            p = 0;
674         //    }
675         //    setHoverItem(-1);
676         //    selectItem(p);
677         //    return true;
678         //}
679         //return false;
680     }
681 
682     /// process mouse event; return true if event is processed by widget.
683     override bool onMouseEvent(MouseEvent event) {
684         //Log.d("onMouseEvent ", id, " ", event.action, "  (", event.x, ",", event.y, ")");
685 		if (event.action == MouseAction.Leave || event.action == MouseAction.Cancel) {
686 			setHoverItem(-1);
687 			return true;
688 		}
689         // delegate processing of mouse wheel to scrollbar widget
690         if (event.action == MouseAction.Wheel && _needScrollbar) {
691             return _scrollbar.onMouseEvent(event);
692         }
693 		// support onClick
694         Rect rc = _pos;
695         applyMargins(rc);
696         applyPadding(rc);
697         Point scrollOffset;
698         if (_orientation == Orientation.Vertical) {
699             scrollOffset.y = _scrollPosition;
700         } else {
701             scrollOffset.x = _scrollPosition;
702         }
703         if (event.action == MouseAction.ButtonDown && (event.flags & (MouseFlag.LButton || MouseFlag.RButton)))
704             setFocus();
705         for (int i = 0; i < itemCount; i++) {
706             Rect itemrc = _itemRects[i];
707             itemrc.left += rc.left - scrollOffset.x;
708             itemrc.right += rc.left - scrollOffset.x;
709             itemrc.top += rc.top - scrollOffset.y;
710             itemrc.bottom += rc.top - scrollOffset.y;
711             if (itemrc.isPointInside(Point(event.x, event.y))) {
712 				if ((event.flags & (MouseFlag.LButton || MouseFlag.RButton)) || _selectOnHover) {
713 					if (_selectedItemIndex != i && itemEnabled(i)) {
714 						int prevSelection = _selectedItemIndex;
715 						selectItem(i);
716 						setHoverItem(-1);
717 						selectionChanged(_selectedItemIndex, prevSelection);
718 					}
719 				} else {
720                     if (itemEnabled(i))
721 					    setHoverItem(i);
722                 }
723 				if ((event.button == MouseFlag.LButton || event.button == MouseFlag.RButton)) {
724 					if ((_clickOnButtonDown && event.action == MouseAction.ButtonDown) || (!_clickOnButtonDown && event.action == MouseAction.ButtonUp)) {
725                         if (itemEnabled(i)) {
726 						    itemClicked(i);
727 						    if (_clickOnButtonDown)
728 							    event.doNotTrackButtonDown = true;
729                         }
730 					}
731 				}
732 				return true;
733             }
734 		}
735         return true;
736     }
737 
738 }
739