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