MyGUI  3.0.1
MyGUI_List.cpp
Go to the documentation of this file.
1 
7 /*
8  This file is part of MyGUI.
9 
10  MyGUI is free software: you can redistribute it and/or modify
11  it under the terms of the GNU Lesser General Public License as published by
12  the Free Software Foundation, either version 3 of the License, or
13  (at your option) any later version.
14 
15  MyGUI is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  GNU Lesser General Public License for more details.
19 
20  You should have received a copy of the GNU Lesser General Public License
21  along with MyGUI. If not, see <http://www.gnu.org/licenses/>.
22 */
23 #include "MyGUI_Precompiled.h"
24 #include "MyGUI_List.h"
25 #include "MyGUI_Button.h"
26 #include "MyGUI_VScroll.h"
27 #include "MyGUI_ResourceSkin.h"
28 #include "MyGUI_InputManager.h"
29 
30 namespace MyGUI
31 {
32 
34  mWidgetScroll(nullptr),
35  mHeightLine(1),
36  mTopIndex(0),
37  mOffsetTop(0),
38  mRangeIndex(-1),
39  mLastRedrawLine(0),
40  mIndexSelect(ITEM_NONE),
41  mLineActive(ITEM_NONE),
42  mIsFocus(false),
43  mNeedVisibleScroll(true)
44  {
45  }
46 
47  void List::_initialise(WidgetStyle _style, const IntCoord& _coord, Align _align, ResourceSkin* _info, Widget* _parent, ICroppedRectangle * _croppedParent, IWidgetCreator * _creator, const std::string& _name)
48  {
49  Base::_initialise(_style, _coord, _align, _info, _parent, _croppedParent, _creator, _name);
50 
51  initialiseWidgetSkin(_info);
52  }
53 
55  {
56  shutdownWidgetSkin();
57  }
58 
60  {
61  shutdownWidgetSkin();
63  initialiseWidgetSkin(_info);
64  }
65 
66  void List::initialiseWidgetSkin(ResourceSkin* _info)
67  {
68  // нам нужен фокус клавы
69  mNeedKeyFocus = true;
70 
71  for (VectorWidgetPtr::iterator iter=mWidgetChildSkin.begin(); iter!=mWidgetChildSkin.end(); ++iter)
72  {
73  if (*(*iter)->_getInternalData<std::string>() == "VScroll")
74  {
75  MYGUI_DEBUG_ASSERT( ! mWidgetScroll, "widget already assigned");
76  mWidgetScroll = (*iter)->castType<VScroll>();
79  }
80  else if (*(*iter)->_getInternalData<std::string>() == "Client")
81  {
82  MYGUI_DEBUG_ASSERT( ! mWidgetClient, "widget already assigned");
83  mWidgetClient = (*iter);
85  }
86  }
87  //MYGUI_ASSERT(nullptr != mWidgetScroll, "Child VScroll not found in skin (List must have VScroll)");
88  //MYGUI_ASSERT(nullptr != mWidgetClient, "Child Widget Client not found in skin (List must have Client)");
89 
90  // парсим свойства
91  const MapString& properties = _info->getProperties();
92  MapString::const_iterator iterS = properties.find("SkinLine");
93  if (iterS != properties.end()) mSkinLine = iterS->second;
94  //MYGUI_ASSERT(!mSkinLine.empty(), "SkinLine property not found (List must have SkinLine property)");
95 
96  iterS = properties.find("HeightLine");
97  if (iterS != properties.end()) mHeightLine = utility::parseInt(iterS->second);
98  if (mHeightLine < 1) mHeightLine = 1;
99 
100 
101  if (mWidgetScroll != nullptr)
102  {
103  mWidgetScroll->setScrollPage((size_t)mHeightLine);
104  mWidgetScroll->setScrollViewPage((size_t)mHeightLine);
105  }
106 
107  updateScroll();
108  updateLine();
109  }
110 
111  void List::shutdownWidgetSkin()
112  {
113  mWidgetScroll = nullptr;
114  mWidgetClient = nullptr;
115  }
116 
117  void List::onMouseWheel(int _rel)
118  {
119  notifyMouseWheel(nullptr, _rel);
120 
121  Base::onMouseWheel(_rel);
122  }
123 
125  {
126  mIsFocus = true;
127  _updateState();
128 
129  Base::onKeySetFocus(_old);
130  }
131 
133  {
134  mIsFocus = false;
135  _updateState();
136 
137  Base::onKeyLostFocus(_new);
138  }
139 
141  {
142  if (getItemCount() == 0)
143  {
144  Base::onKeyButtonPressed(_key, _char);
145  return;
146  }
147 
148  // очень секретный метод, запатентованный механизм движения курсора
149  size_t sel = mIndexSelect;
150 
151  if (_key == KeyCode::ArrowUp)
152  {
153  if (sel != 0)
154  {
155  if (sel == ITEM_NONE) sel = 0;
156  else sel --;
157  }
158 
159  }
160  else if (_key == KeyCode::ArrowDown)
161  {
162  if (sel == ITEM_NONE) sel = 0;
163  else sel ++;
164 
165  if (sel >= getItemCount())
166  {
167  // старое значение
168  sel = mIndexSelect;
169  }
170 
171  }
172  else if (_key == KeyCode::Home)
173  {
174  if (sel != 0) sel = 0;
175 
176  }
177  else if (_key == KeyCode::End)
178  {
179  if (sel != (getItemCount() - 1))
180  {
181  sel = getItemCount() - 1;
182  }
183 
184  }
185  else if (_key == KeyCode::PageUp)
186  {
187  if (sel != 0)
188  {
189  if (sel == ITEM_NONE) sel = 0;
190  else
191  {
192  size_t page = _getClientWidget()->getHeight() / mHeightLine;
193  if (sel <= page) sel = 0;
194  else sel -= page;
195  }
196  }
197 
198  }
199  else if (_key == KeyCode::PageDown)
200  {
201  if (sel != (getItemCount() - 1))
202  {
203  if (sel == ITEM_NONE) sel = 0;
204  else
205  {
206  sel += _getClientWidget()->getHeight() / mHeightLine;
207  if (sel >= getItemCount()) sel = getItemCount() - 1;
208  }
209  }
210 
211  }
212  else if ((_key == KeyCode::Return) || (_key == KeyCode::NumpadEnter))
213  {
214  if (sel != ITEM_NONE)
215  {
216  //FIXME нас могут удалить
217  eventListSelectAccept(this, sel);
218 
219  Base::onKeyButtonPressed(_key, _char);
220  // выходим, так как изменили колличество строк
221  return;
222  }
223 
224  }
225 
226  if (sel != mIndexSelect)
227  {
228  if ( !isItemVisibleAt(sel))
229  {
230  beginToItemAt(sel);
231  if (mWidgetScroll != nullptr)
232  _sendEventChangeScroll(mWidgetScroll->getScrollPosition());
233  }
234  setIndexSelected(sel);
235 
236  // изменилась позиция
237  // FIXME нас могут удалить
238  eventListChangePosition(this, mIndexSelect);
239  }
240 
241  Base::onKeyButtonPressed(_key, _char);
242  }
243 
244  void List::notifyMouseWheel(Widget* _sender, int _rel)
245  {
246  if (mRangeIndex <= 0)
247  return;
248 
249  if (mWidgetScroll == nullptr)
250  return;
251 
252  int offset = (int)mWidgetScroll->getScrollPosition();
253  if (_rel < 0) offset += mHeightLine;
254  else offset -= mHeightLine;
255 
256  if (offset >= mRangeIndex) offset = mRangeIndex;
257  else if (offset < 0) offset = 0;
258 
259  if ((int)mWidgetScroll->getScrollPosition() == offset) return;
260 
261  mWidgetScroll->setScrollPosition(offset);
262  _setScrollView(offset);
263  _sendEventChangeScroll(offset);
264  }
265 
266  void List::notifyScrollChangePosition(VScroll* _sender, size_t _position)
267  {
268  _setScrollView(_position);
269  _sendEventChangeScroll(_position);
270  }
271 
272  void List::notifyMousePressed(Widget* _sender, int _left, int _top, MouseButton _id)
273  {
274  if (MouseButton::Left != _id)
275  return;
276 
277  if (_sender == mWidgetScroll)
278  return;
279 
280  // если выделен клиент, то сбрасываем
281  if (_sender == _getClientWidget())
282  {
283  if (mIndexSelect != ITEM_NONE)
284  {
285  _selectIndex(mIndexSelect, false);
286  mIndexSelect = ITEM_NONE;
287  eventListChangePosition(this, mIndexSelect);
288  }
289  eventListMouseItemActivate(this, mIndexSelect);
290 
291  // если не клиент, то просчитывам
292  }
293  // ячейка может быть скрыта
294  else if (_sender->isVisible())
295  {
296 
297 #if MYGUI_DEBUG_MODE == 1
298  _checkMapping("List::notifyMousePressed");
299  MYGUI_ASSERT_RANGE(*_sender->_getInternalData<size_t>(), mWidgetLines.size(), "List::notifyMousePressed");
300  MYGUI_ASSERT_RANGE(*_sender->_getInternalData<size_t>() + mTopIndex, mItemsInfo.size(), "List::notifyMousePressed");
301 #endif
302 
303  size_t index = *_sender->_getInternalData<size_t>() + mTopIndex;
304 
305  if (mIndexSelect != index)
306  {
307  _selectIndex(mIndexSelect, false);
308  _selectIndex(index, true);
309  mIndexSelect = index;
310  eventListChangePosition(this, mIndexSelect);
311  }
312  eventListMouseItemActivate(this, mIndexSelect);
313 
314  }
315  }
316 
318  {
319  if (mIndexSelect != ITEM_NONE)
320  eventListSelectAccept(this, mIndexSelect);
321  }
322 
323  void List::setPosition(const IntPoint& _point)
324  {
325  Base::setPosition(_point);
326  }
327 
328  void List::setSize(const IntSize& _size)
329  {
330  Base::setSize(_size);
331 
332  updateScroll();
333  updateLine();
334  }
335 
336  void List::setCoord(const IntCoord& _coord)
337  {
338  Base::setCoord(_coord);
339 
340  updateScroll();
341  updateLine();
342  }
343 
345  {
346  mRangeIndex = (mHeightLine * (int)mItemsInfo.size()) - _getClientWidget()->getHeight();
347 
348  if (mWidgetScroll == nullptr)
349  return;
350 
351  if ( (!mNeedVisibleScroll) || (mRangeIndex < 1) || (mWidgetScroll->getLeft() <= _getClientWidget()->getLeft()) )
352  {
353  if (mWidgetScroll->isVisible())
354  {
355  mWidgetScroll->setVisible(false);
356  // увеличиваем клиентскую зону на ширину скрола
357  if (mWidgetClient != nullptr)
359  }
360  }
361  else if (!mWidgetScroll->isVisible())
362  {
363  if (mWidgetClient != nullptr)
365  mWidgetScroll->setVisible(true);
366  }
367 
368  mWidgetScroll->setScrollRange(mRangeIndex + 1);
369  if ((int)mItemsInfo.size()) mWidgetScroll->setTrackSize( mWidgetScroll->getLineSize() * _getClientWidget()->getHeight() / mHeightLine / (int)mItemsInfo.size() );
370  }
371 
372  void List::updateLine(bool _reset)
373  {
374  // сбрасываем
375  if (_reset)
376  {
377  mOldSize.clear();
378  mLastRedrawLine = 0;
379  }
380 
381  // позиция скролла
382  int position = mTopIndex * mHeightLine + mOffsetTop;
383 
384  // если высота увеличивалась то добавляем виджеты
385  if (mOldSize.height < mCoord.height)
386  {
387  int height = (int)mWidgetLines.size() * mHeightLine - mOffsetTop;
388 
389  // до тех пор, пока не достигнем максимального колличества, и всегда на одну больше
390  while ( (height <= (_getClientWidget()->getHeight() + mHeightLine)) && (mWidgetLines.size() < mItemsInfo.size()) )
391  {
392  // создаем линию
393  Widget* line = _getClientWidget()->createWidgetT("Button", mSkinLine, 0, height, _getClientWidget()->getWidth(), mHeightLine, Align::Top | Align::HStretch);
394  // подписываемся на всякие там события
400  // присваиваем порядковый номер, для простоты просчета
401  line->_setInternalData((size_t)mWidgetLines.size());
402  // и сохраняем
403  mWidgetLines.push_back(line);
404  height += mHeightLine;
405  }
406 
407  // проверяем на возможность не менять положение списка
408  if (position >= mRangeIndex)
409  {
410  // размер всех помещается в клиент
411  if (mRangeIndex <= 0)
412  {
413  // обнуляем, если надо
414  if (position || mOffsetTop || mTopIndex)
415  {
416  position = 0;
417  mTopIndex = 0;
418  mOffsetTop = 0;
419  mLastRedrawLine = 0; // чтобы все перерисовалось
420 
421  // выравниваем
422  int offset = 0;
423  for (size_t pos=0; pos<mWidgetLines.size(); pos++)
424  {
425  mWidgetLines[pos]->setPosition(0, offset);
426  offset += mHeightLine;
427  }
428  }
429 
430  }
431  else
432  {
433  // прижимаем список к нижней границе
434  int count = _getClientWidget()->getHeight() / mHeightLine;
435  mOffsetTop = mHeightLine - (_getClientWidget()->getHeight() % mHeightLine);
436 
437  if (mOffsetTop == mHeightLine)
438  {
439  mOffsetTop = 0;
440  count --;
441  }
442 
443  int top = (int)mItemsInfo.size() - count - 1;
444 
445  // выравниваем
446  int offset = 0 - mOffsetTop;
447  for (size_t pos=0; pos<mWidgetLines.size(); pos++)
448  {
449  mWidgetLines[pos]->setPosition(0, offset);
450  offset += mHeightLine;
451  }
452 
453  // высчитываем положение, должно быть максимальным
454  position = top * mHeightLine + mOffsetTop;
455 
456  // если индех изменился, то перерисовываем линии
457  if (top != mTopIndex)
458  {
459  mTopIndex = top;
461  }
462 
463  }
464  }
465 
466  // увеличился размер, но прокрутки вниз небыло, обновляем линии снизу
467  _redrawItemRange(mLastRedrawLine);
468 
469  } // if (old_cy < mCoord.height)
470 
471  // просчитываем положение скролла
472  if (mWidgetScroll != nullptr)
473  mWidgetScroll->setScrollPosition(position);
474 
475  mOldSize.width = mCoord.width;
476  mOldSize.height = mCoord.height;
477 
478 #if MYGUI_DEBUG_MODE == 1
479  _checkMapping("List::updateLine");
480 #endif
481 
482  }
483 
484  void List::_redrawItemRange(size_t _start)
485  {
486  // перерисовываем линии, только те, что видны
487  size_t pos = _start;
488  for (; pos<mWidgetLines.size(); pos++)
489  {
490  // индекс в нашем массиве
491  size_t index = pos + (size_t)mTopIndex;
492 
493  // не будем заходить слишком далеко
494  if (index >= mItemsInfo.size())
495  {
496  // запоминаем последнюю перерисованную линию
497  mLastRedrawLine = pos;
498  break;
499  }
500  if (mWidgetLines[pos]->getTop() > _getClientWidget()->getHeight())
501  {
502  // запоминаем последнюю перерисованную линию
503  mLastRedrawLine = pos;
504  break;
505  }
506 
507  // если был скрыт, то покажем
508  mWidgetLines[pos]->setVisible(true);
509  // обновляем текст
510  mWidgetLines[pos]->setCaption(mItemsInfo[index].first);
511 
512  // если нужно выделить ,то выделим
513  static_cast<Button*>(mWidgetLines[pos])->setButtonPressed(index == mIndexSelect);
514  }
515 
516  // если цикл весь прошли, то ставим максимальную линию
517  if (pos >= mWidgetLines.size()) mLastRedrawLine = pos;
518  else
519  {
520  //Widget* focus = InputManager::getInstance().getMouseFocusWidget();
521  for (; pos<mWidgetLines.size(); pos++)
522  {
523  static_cast<Button*>(mWidgetLines[pos])->setButtonPressed(false);
524  static_cast<Button*>(mWidgetLines[pos])->setVisible(false);
525  //if (focus == mWidgetLines[pos]) InputManager::getInstance()._unlinkWidget(focus);
526  }
527  }
528 
529 #if MYGUI_DEBUG_MODE == 1
530  _checkMapping("List::_redrawItemRange");
531 #endif
532 
533  }
534 
535  // перерисовывает индекс
536  void List::_redrawItem(size_t _index)
537  {
538  // невидно
539  if (_index < (size_t)mTopIndex) return;
540  _index -= (size_t)mTopIndex;
541  // тоже невидно
542  if (_index >= mLastRedrawLine) return;
543 
544  MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "List::_redrawItem");
545  // перерисовываем
546  mWidgetLines[_index]->setCaption(mItemsInfo[_index + mTopIndex].first);
547 
548 #if MYGUI_DEBUG_MODE == 1
549  _checkMapping("List::_redrawItem");
550 #endif
551 
552  }
553 
554  void List::insertItemAt(size_t _index, const UString& _name, Any _data)
555  {
556  MYGUI_ASSERT_RANGE_INSERT(_index, mItemsInfo.size(), "List::insertItemAt");
557  if (_index == ITEM_NONE) _index = mItemsInfo.size();
558 
559  // вставляем физически
560  mItemsInfo.insert(mItemsInfo.begin() + _index, PairItem(_name, _data));
561 
562  // если надо, то меняем выделенный элемент
563  if ( (mIndexSelect != ITEM_NONE) && (_index <= mIndexSelect) ) mIndexSelect++;
564 
565  // строка, до первого видимого элемента
566  if ((_index <= (size_t)mTopIndex) && (mRangeIndex > 0))
567  {
568  mTopIndex ++;
569  // просчитываем положение скролла
570  if (mWidgetScroll != nullptr)
571  {
572  mWidgetScroll->setScrollRange(mWidgetScroll->getScrollRange() + mHeightLine);
573  if ((int)mItemsInfo.size()) mWidgetScroll->setTrackSize( mWidgetScroll->getLineSize() * _getClientWidget()->getHeight() / mHeightLine / (int)mItemsInfo.size() );
574  mWidgetScroll->setScrollPosition(mTopIndex * mHeightLine + mOffsetTop);
575  }
576  mRangeIndex += mHeightLine;
577  }
578  else
579  {
580  // высчитывам положение строки
581  int offset = ((int)_index - mTopIndex) * mHeightLine - mOffsetTop;
582 
583  // строка, после последнего видимого элемента, плюс одна строка (потому что для прокрутки нужно на одну строчку больше)
584  if (_getClientWidget()->getHeight() < (offset - mHeightLine))
585  {
586  // просчитываем положение скролла
587  if (mWidgetScroll != nullptr)
588  {
589  mWidgetScroll->setScrollRange(mWidgetScroll->getScrollRange() + mHeightLine);
590  if ((int)mItemsInfo.size()) mWidgetScroll->setTrackSize( mWidgetScroll->getLineSize() * _getClientWidget()->getHeight() / mHeightLine / (int)mItemsInfo.size() );
591  mWidgetScroll->setScrollPosition(mTopIndex * mHeightLine + mOffsetTop);
592  }
593  mRangeIndex += mHeightLine;
594 
595  // строка в видимой области
596  }
597  else
598  {
599  // обновляем все
600  updateScroll();
601  updateLine(true);
602 
603  // позже сюда еще оптимизацию по колличеству перерисовок
604  }
605  }
606 
607 #if MYGUI_DEBUG_MODE == 1
608  _checkMapping("List::insertItemAt");
609 #endif
610 
611  }
612 
613  void List::removeItemAt(size_t _index)
614  {
615  MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "List::removeItemAt");
616 
617  // удяляем физически строку
618  mItemsInfo.erase(mItemsInfo.begin() + _index);
619 
620  // если надо, то меняем выделенный элемент
621  if (mItemsInfo.empty()) mIndexSelect = ITEM_NONE;
622  else if (mIndexSelect != ITEM_NONE)
623  {
624  if (_index < mIndexSelect) mIndexSelect--;
625  else if ( (_index == mIndexSelect) && (mIndexSelect == (mItemsInfo.size())) ) mIndexSelect--;
626  }
627 
628  // если виджетов стало больше , то скрываем крайний
629  if (mWidgetLines.size() > mItemsInfo.size())
630  {
631  mWidgetLines[mItemsInfo.size()]->setVisible(false);
632  }
633 
634  // строка, до первого видимого элемента
635  if (_index < (size_t)mTopIndex)
636  {
637  mTopIndex --;
638  // просчитываем положение скролла
639  if (mWidgetScroll != nullptr)
640  {
641  mWidgetScroll->setScrollRange(mWidgetScroll->getScrollRange() - mHeightLine);
642  if ((int)mItemsInfo.size()) mWidgetScroll->setTrackSize( mWidgetScroll->getLineSize() * _getClientWidget()->getHeight() / mHeightLine / (int)mItemsInfo.size() );
643  mWidgetScroll->setScrollPosition(mTopIndex * mHeightLine + mOffsetTop);
644  }
645  mRangeIndex -= mHeightLine;
646  }
647  else
648  {
649  // высчитывам положение удаляемой строки
650  int offset = ((int)_index - mTopIndex) * mHeightLine - mOffsetTop;
651 
652  // строка, после последнего видимого элемента
653  if (_getClientWidget()->getHeight() < offset)
654  {
655  // просчитываем положение скролла
656  if (mWidgetScroll != nullptr)
657  {
658  mWidgetScroll->setScrollRange(mWidgetScroll->getScrollRange() - mHeightLine);
659  if ((int)mItemsInfo.size()) mWidgetScroll->setTrackSize( mWidgetScroll->getLineSize() * _getClientWidget()->getHeight() / mHeightLine / (int)mItemsInfo.size() );
660  mWidgetScroll->setScrollPosition(mTopIndex * mHeightLine + mOffsetTop);
661  }
662  mRangeIndex -= mHeightLine;
663 
664  // строка в видимой области
665  }
666  else
667  {
668  // обновляем все
669  updateScroll();
670  updateLine(true);
671 
672  // позже сюда еще оптимизацию по колличеству перерисовок
673  }
674  }
675 
676 #if MYGUI_DEBUG_MODE == 1
677  _checkMapping("List::removeItemAt");
678 #endif
679 
680  }
681 
682  void List::setIndexSelected(size_t _index)
683  {
684  MYGUI_ASSERT_RANGE_AND_NONE(_index, mItemsInfo.size(), "List::setIndexSelected");
685  if (mIndexSelect != _index)
686  {
687  _selectIndex(mIndexSelect, false);
688  _selectIndex(_index, true);
689  mIndexSelect = _index;
690  }
691  }
692 
693  void List::_selectIndex(size_t _index, bool _select)
694  {
695  if (_index == ITEM_NONE) return;
696  // не видно строки
697  if (_index < (size_t)mTopIndex) return;
698  // высчитывам положение строки
699  int offset = ((int)_index - mTopIndex) * mHeightLine - mOffsetTop;
700  // строка, после последнего видимого элемента
701  if (_getClientWidget()->getHeight() < offset) return;
702 
703  size_t index = _index - mTopIndex;
704  if (index < mWidgetLines.size())
705  static_cast<Button*>(mWidgetLines[index])->setButtonPressed(_select);
706 
707 #if MYGUI_DEBUG_MODE == 1
708  _checkMapping("List::_selectIndex");
709 #endif
710 
711  }
712 
713  void List::beginToItemAt(size_t _index)
714  {
715  MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "List::beginToItemAt");
716  if (mRangeIndex <= 0) return;
717 
718  int offset = (int)_index * mHeightLine;
719  if (offset >= mRangeIndex) offset = mRangeIndex;
720 
721  if (mWidgetScroll != nullptr)
722  {
723  if ((int)mWidgetScroll->getScrollPosition() == offset) return;
724  mWidgetScroll->setScrollPosition(offset);
725  }
726  notifyScrollChangePosition(nullptr, offset);
727 
728 #if MYGUI_DEBUG_MODE == 1
729  _checkMapping("List::beginToItemAt");
730 #endif
731 
732  }
733 
734  // видим ли мы элемент, полностью или нет
735  bool List::isItemVisibleAt(size_t _index, bool _fill)
736  {
737  // если элемента нет, то мы его не видим (в том числе когда их вообще нет)
738  if (_index >= mItemsInfo.size()) return false;
739  // если скрола нет, то мы палюбак видим
740  if (mRangeIndex <= 0) return true;
741 
742  // строка, до первого видимого элемента
743  if (_index < (size_t)mTopIndex) return false;
744 
745  // строка это верхний выделенный
746  if (_index == (size_t)mTopIndex)
747  {
748  if ( (mOffsetTop != 0) && (_fill) ) return false; // нам нужна полностью видимость
749  return true;
750  }
751 
752  // высчитывам положение строки
753  int offset = ((int)_index - mTopIndex) * mHeightLine - mOffsetTop;
754 
755  // строка, после последнего видимого элемента
756  if (_getClientWidget()->getHeight() < offset) return false;
757 
758  // если мы внизу и нам нужен целый
759  if ((_getClientWidget()->getHeight() < (offset + mHeightLine)) && (_fill) ) return false;
760 
761  return true;
762  }
763 
765  {
766  mTopIndex = 0;
767  mIndexSelect = ITEM_NONE;
768  mOffsetTop = 0;
769 
770  mItemsInfo.clear();
771 
772  int offset = 0;
773  for (size_t pos=0; pos<mWidgetLines.size(); pos++)
774  {
775  mWidgetLines[pos]->setVisible(false);
776  mWidgetLines[pos]->setPosition(0, offset);
777  offset += mHeightLine;
778  }
779 
780  // обновляем все
781  updateScroll();
782  updateLine(true);
783 
784 #if MYGUI_DEBUG_MODE == 1
785  _checkMapping("List::removeAllItems");
786 #endif
787 
788  }
789 
790  void List::setItemNameAt(size_t _index, const UString& _name)
791  {
792  MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "List::setItemNameAt");
793  mItemsInfo[_index].first =_name;
794  _redrawItem(_index);
795  }
796 
797  void List::setItemDataAt(size_t _index, Any _data)
798  {
799  MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "List::setItemDataAt");
800  mItemsInfo[_index].second = _data;
801  _redrawItem(_index);
802  }
803 
804  const UString& List::getItemNameAt(size_t _index)
805  {
806  MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "List::getItemNameAt");
807  return mItemsInfo[_index].first;
808  }
809 
811  {
812 
813 #if MYGUI_DEBUG_MODE == 1
814  MYGUI_ASSERT_RANGE(*_sender->_getInternalData<size_t>(), mWidgetLines.size(), "List::notifyMouseSetFocus");
815 #endif
816 
817  mLineActive = *_sender->_getInternalData<size_t>();
818  eventListMouseItemFocus(this, mLineActive);
819  }
820 
822  {
823  if ((nullptr == _new) || (_new->getParent() != _getClientWidget()))
824  {
825  mLineActive = ITEM_NONE;
827  }
828  }
829 
830  void List::_setItemFocus(size_t _index, bool _focus)
831  {
832  MYGUI_ASSERT_RANGE(_index, mWidgetLines.size(), "List::_setItemFocus");
833  static_cast<Button*>(mWidgetLines[_index])->_setMouseFocus(_focus);
834  }
835 
836  void List::setScrollVisible(bool _visible)
837  {
838  if (mNeedVisibleScroll == _visible) return;
839  mNeedVisibleScroll = _visible;
840  updateScroll();
841  }
842 
843  void List::setScrollPosition(size_t _position)
844  {
845  if (mWidgetScroll != nullptr)
846  {
847  if (mWidgetScroll->getScrollRange() > _position)
848  {
849  mWidgetScroll->setScrollPosition(_position);
850  _setScrollView(_position);
851  }
852  }
853  }
854 
855  void List::_setScrollView(size_t _position)
856  {
857  mOffsetTop = ((int)_position % mHeightLine);
858 
859  // смещение с отрицательной стороны
860  int offset = 0 - mOffsetTop;
861 
862  for (size_t pos=0; pos<mWidgetLines.size(); pos++)
863  {
864  mWidgetLines[pos]->setPosition(IntPoint(0, offset));
865  offset += mHeightLine;
866  }
867 
868  // если индех изменился, то перерисовываем линии
869  int top = ((int)_position / mHeightLine);
870  if (top != mTopIndex)
871  {
872  mTopIndex = top;
874  }
875 
876  // прорисовываем все нижние строки, если они появились
877  _redrawItemRange(mLastRedrawLine);
878  }
879 
880  void List::_sendEventChangeScroll(size_t _position)
881  {
882  eventListChangeScroll(this, _position);
883  if (ITEM_NONE != mLineActive) eventListMouseItemFocus(this, mLineActive);
884  }
885 
886  void List::swapItemsAt(size_t _index1, size_t _index2)
887  {
888  MYGUI_ASSERT_RANGE(_index1, mItemsInfo.size(), "List::swapItemsAt");
889  MYGUI_ASSERT_RANGE(_index2, mItemsInfo.size(), "List::swapItemsAt");
890 
891  if (_index1 == _index2) return;
892 
893  std::swap(mItemsInfo[_index1], mItemsInfo[_index2]);
894 
895  _redrawItem(_index1);
896  _redrawItem(_index2);
897  }
898 
899  void List::_checkMapping(const std::string& _owner)
900  {
901  size_t count_pressed = 0;
902  size_t count_show = 0;
903 
904  for (size_t pos=0; pos<mWidgetLines.size(); pos++)
905  {
906  MYGUI_ASSERT(pos == *mWidgetLines[pos]->_getInternalData<size_t>(), _owner);
907  static_cast<Button*>(mWidgetLines[pos])->getButtonPressed() ? count_pressed ++ : 0;
908  static_cast<Button*>(mWidgetLines[pos])->isVisible() ? count_show ++ : 0;
909  }
910  MYGUI_ASSERT(count_pressed < 2, _owner);
911  //MYGUI_ASSERT((count_show + mOffsetTop) <= mItemsInfo.size(), _owner);
912  }
913 
915  {
916  // максимальная высота всех строк
917  int max_height = mItemsInfo.size() * mHeightLine;
918  // видимая высота
919  int visible_height = _getClientWidget()->getHeight();
920 
921  // все строки помещаются
922  if (visible_height >= max_height)
923  {
924  MYGUI_ASSERT(mTopIndex == 0, "mTopIndex == 0");
925  MYGUI_ASSERT(mOffsetTop == 0, "mOffsetTop == 0");
926  int height = 0;
927  for (size_t pos=0; pos<mWidgetLines.size(); pos++)
928  {
929  if (pos >= mItemsInfo.size()) break;
930  MYGUI_ASSERT(mWidgetLines[pos]->getTop() == height, "mWidgetLines[pos]->getTop() == height");
931  height += mWidgetLines[pos]->getHeight();
932  }
933  }
934  }
935 
936  size_t List::findItemIndexWith(const UString& _name)
937  {
938  for (size_t pos=0; pos<mItemsInfo.size(); pos++)
939  {
940  if (mItemsInfo[pos].first == _name) return pos;
941  }
942  return ITEM_NONE;
943  }
944 
946  {
947  return (mCoord.height - _getClientWidget()->getHeight()) + (mItemsInfo.size() * mHeightLine);
948  }
949 
950  void List::setProperty(const std::string& _key, const std::string& _value)
951  {
952  if (_key == "List_AddItem") addItem(_value);
953  else
954  {
955  Base::setProperty(_key, _value);
956  return;
957  }
958  eventChangeProperty(this, _key, _value);
959  }
960 
961  Widget* List::_getClientWidget()
962  {
963  return mWidgetClient == nullptr ? this : mWidgetClient;
964  }
965 
966  const Widget* List::_getClientWidget() const
967  {
968  return mWidgetClient == nullptr ? this : mWidgetClient;
969  }
970 
971 } // namespace MyGUI