MyGUI
3.2.1
|
00001 /* 00002 * This source file is part of MyGUI. For the latest info, see http://mygui.info/ 00003 * Distributed under the MIT License 00004 * (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT) 00005 */ 00006 00007 #include "MyGUI_Precompiled.h" 00008 #include "MyGUI_ListBox.h" 00009 #include "MyGUI_Button.h" 00010 #include "MyGUI_ScrollBar.h" 00011 #include "MyGUI_ResourceSkin.h" 00012 #include "MyGUI_InputManager.h" 00013 #include "MyGUI_WidgetManager.h" 00014 00015 namespace MyGUI 00016 { 00017 00018 ListBox::ListBox() : 00019 mWidgetScroll(nullptr), 00020 mHeightLine(1), 00021 mTopIndex(0), 00022 mOffsetTop(0), 00023 mRangeIndex(-1), 00024 mLastRedrawLine(0), 00025 mIndexSelect(ITEM_NONE), 00026 mLineActive(ITEM_NONE), 00027 mNeedVisibleScroll(true), 00028 mClient(nullptr), 00029 mActivateOnClick(false) 00030 { 00031 } 00032 00033 void ListBox::initialiseOverride() 00034 { 00035 Base::initialiseOverride(); 00036 00037 // FIXME нам нужен фокус клавы 00038 setNeedKeyFocus(true); 00039 00040 // парсим свойства 00041 if (isUserString("SkinLine")) 00042 mSkinLine = getUserString("SkinLine"); 00043 00044 if (isUserString("HeightLine")) 00045 mHeightLine = utility::parseInt(getUserString("HeightLine")); 00046 00047 if (mHeightLine < 1) 00048 mHeightLine = 1; 00049 00051 assignWidget(mClient, "Client"); 00052 if (mClient != nullptr) 00053 { 00054 mClient->eventMouseButtonPressed += newDelegate(this, &ListBox::notifyMousePressed); 00055 mClient->eventMouseButtonReleased += newDelegate(this, &ListBox::notifyMouseButtonReleased); 00056 mClient->eventKeyButtonPressed += newDelegate(this, &ListBox::notifyKeyButtonPressed); 00057 mClient->eventKeyButtonReleased += newDelegate(this, &ListBox::notifyKeyButtonReleased); 00058 setWidgetClient(mClient); 00059 } 00060 00062 assignWidget(mWidgetScroll, "VScroll"); 00063 if (mWidgetScroll != nullptr) 00064 { 00065 mWidgetScroll->eventScrollChangePosition += newDelegate(this, &ListBox::notifyScrollChangePosition); 00066 mWidgetScroll->setScrollPage((size_t)mHeightLine); 00067 mWidgetScroll->setScrollViewPage((size_t)mHeightLine); 00068 } 00069 00070 updateScroll(); 00071 updateLine(); 00072 } 00073 00074 void ListBox::shutdownOverride() 00075 { 00076 mWidgetScroll = nullptr; 00077 mClient = nullptr; 00078 00079 Base::shutdownOverride(); 00080 } 00081 00082 void ListBox::onMouseWheel(int _rel) 00083 { 00084 notifyMouseWheel(nullptr, _rel); 00085 00086 Base::onMouseWheel(_rel); 00087 } 00088 00089 void ListBox::onKeyButtonPressed(KeyCode _key, Char _char) 00090 { 00091 if (getItemCount() == 0) 00092 { 00093 Base::onKeyButtonPressed(_key, _char); 00094 eventNotifyItem(this, IBNotifyItemData(ITEM_NONE, IBNotifyItemData::KeyPressed, _key, _char)); 00095 return; 00096 } 00097 00098 // очень секретный метод, запатентованный механизм движения курсора 00099 size_t sel = mIndexSelect; 00100 00101 if (_key == KeyCode::ArrowUp) 00102 { 00103 if (sel != 0) 00104 { 00105 if (sel == ITEM_NONE) 00106 sel = 0; 00107 else 00108 sel --; 00109 } 00110 } 00111 else if (_key == KeyCode::ArrowDown) 00112 { 00113 if (sel == ITEM_NONE) 00114 sel = 0; 00115 else 00116 sel ++; 00117 00118 if (sel >= getItemCount()) 00119 { 00120 // старое значение 00121 sel = mIndexSelect; 00122 } 00123 } 00124 else if (_key == KeyCode::Home) 00125 { 00126 if (sel != 0) 00127 sel = 0; 00128 } 00129 else if (_key == KeyCode::End) 00130 { 00131 if (sel != (getItemCount() - 1)) 00132 { 00133 sel = getItemCount() - 1; 00134 } 00135 } 00136 else if (_key == KeyCode::PageUp) 00137 { 00138 if (sel != 0) 00139 { 00140 if (sel == ITEM_NONE) 00141 { 00142 sel = 0; 00143 } 00144 else 00145 { 00146 size_t page = _getClientWidget()->getHeight() / mHeightLine; 00147 if (sel <= page) 00148 sel = 0; 00149 else 00150 sel -= page; 00151 } 00152 } 00153 } 00154 else if (_key == KeyCode::PageDown) 00155 { 00156 if (sel != (getItemCount() - 1)) 00157 { 00158 if (sel == ITEM_NONE) 00159 { 00160 sel = 0; 00161 } 00162 else 00163 { 00164 sel += _getClientWidget()->getHeight() / mHeightLine; 00165 if (sel >= getItemCount()) 00166 sel = getItemCount() - 1; 00167 } 00168 } 00169 } 00170 else if ((_key == KeyCode::Return) || (_key == KeyCode::NumpadEnter)) 00171 { 00172 if (sel != ITEM_NONE) 00173 { 00174 //FIXME нас могут удалить 00175 eventListSelectAccept(this, sel); 00176 00177 Base::onKeyButtonPressed(_key, _char); 00178 00179 eventNotifyItem(this, IBNotifyItemData(ITEM_NONE, IBNotifyItemData::KeyPressed, _key, _char)); 00180 // выходим, так как изменили колличество строк 00181 return; 00182 } 00183 } 00184 00185 if (sel != mIndexSelect) 00186 { 00187 _resetContainer(true); 00188 00189 if (!isItemVisibleAt(sel)) 00190 { 00191 beginToItemAt(sel); 00192 if (mWidgetScroll != nullptr) 00193 _sendEventChangeScroll(mWidgetScroll->getScrollPosition()); 00194 } 00195 setIndexSelected(sel); 00196 00197 // изменилась позиция 00198 // FIXME нас могут удалить 00199 eventListChangePosition(this, mIndexSelect); 00200 } 00201 00202 Base::onKeyButtonPressed(_key, _char); 00203 eventNotifyItem(this, IBNotifyItemData(ITEM_NONE, IBNotifyItemData::KeyPressed, _key, _char)); 00204 } 00205 00206 void ListBox::notifyMouseWheel(Widget* _sender, int _rel) 00207 { 00208 if (mRangeIndex <= 0) 00209 return; 00210 00211 if (mWidgetScroll == nullptr) 00212 return; 00213 00214 int offset = (int)mWidgetScroll->getScrollPosition(); 00215 if (_rel < 0) 00216 offset += mHeightLine; 00217 else 00218 offset -= mHeightLine; 00219 00220 if (offset >= mRangeIndex) 00221 offset = mRangeIndex; 00222 else if (offset < 0) 00223 offset = 0; 00224 00225 if ((int)mWidgetScroll->getScrollPosition() == offset) 00226 return; 00227 00228 mWidgetScroll->setScrollPosition(offset); 00229 _setScrollView(offset); 00230 _sendEventChangeScroll(offset); 00231 00232 _resetContainer(true); 00233 } 00234 00235 void ListBox::notifyScrollChangePosition(ScrollBar* _sender, size_t _position) 00236 { 00237 _setScrollView(_position); 00238 _sendEventChangeScroll(_position); 00239 } 00240 00241 void ListBox::notifyMousePressed(Widget* _sender, int _left, int _top, MouseButton _id) 00242 { 00243 if (MouseButton::Left == _id && !mActivateOnClick) 00244 _activateItem(_sender); 00245 00246 eventNotifyItem(this, IBNotifyItemData(getIndexByWidget(_sender), IBNotifyItemData::MousePressed, _left, _top, _id)); 00247 } 00248 00249 void ListBox::notifyMouseClick(MyGUI::Widget* _sender) 00250 { 00251 if (mActivateOnClick) 00252 _activateItem(_sender); 00253 } 00254 00255 void ListBox::notifyMouseDoubleClick(Widget* _sender) 00256 { 00257 if (mIndexSelect != ITEM_NONE) 00258 eventListSelectAccept(this, mIndexSelect); 00259 } 00260 00261 void ListBox::setPosition(const IntPoint& _point) 00262 { 00263 Base::setPosition(_point); 00264 } 00265 00266 void ListBox::setSize(const IntSize& _size) 00267 { 00268 Base::setSize(_size); 00269 00270 updateScroll(); 00271 updateLine(); 00272 } 00273 00274 void ListBox::setCoord(const IntCoord& _coord) 00275 { 00276 Base::setCoord(_coord); 00277 00278 updateScroll(); 00279 updateLine(); 00280 } 00281 00282 void ListBox::updateScroll() 00283 { 00284 mRangeIndex = (mHeightLine * (int)mItemsInfo.size()) - _getClientWidget()->getHeight(); 00285 00286 if (mWidgetScroll == nullptr) 00287 return; 00288 00289 if ((!mNeedVisibleScroll) || (mRangeIndex < 1) || (mWidgetScroll->getLeft() <= _getClientWidget()->getLeft())) 00290 { 00291 if (mWidgetScroll->getVisible()) 00292 { 00293 mWidgetScroll->setVisible(false); 00294 // увеличиваем клиентскую зону на ширину скрола 00295 if (mClient != nullptr) 00296 mClient->setSize(mClient->getWidth() + mWidgetScroll->getWidth(), mClient->getHeight()); 00297 } 00298 } 00299 else if (!mWidgetScroll->getVisible()) 00300 { 00301 if (mClient != nullptr) 00302 mClient->setSize(mClient->getWidth() - mWidgetScroll->getWidth(), mClient->getHeight()); 00303 mWidgetScroll->setVisible(true); 00304 } 00305 00306 mWidgetScroll->setScrollRange(mRangeIndex + 1); 00307 if (!mItemsInfo.empty()) 00308 mWidgetScroll->setTrackSize(mWidgetScroll->getLineSize() * _getClientWidget()->getHeight() / mHeightLine / (int)mItemsInfo.size()); 00309 } 00310 00311 void ListBox::updateLine(bool _reset) 00312 { 00313 // сбрасываем 00314 if (_reset) 00315 { 00316 mOldSize.clear(); 00317 mLastRedrawLine = 0; 00318 _resetContainer(false); 00319 } 00320 00321 // позиция скролла 00322 int position = mTopIndex * mHeightLine + mOffsetTop; 00323 00324 // если высота увеличивалась то добавляем виджеты 00325 if (mOldSize.height < mCoord.height) 00326 { 00327 int height = (int)mWidgetLines.size() * mHeightLine - mOffsetTop; 00328 00329 // до тех пор, пока не достигнем максимального колличества, и всегда на одну больше 00330 while ( (height <= (_getClientWidget()->getHeight() + mHeightLine)) && (mWidgetLines.size() < mItemsInfo.size()) ) 00331 { 00332 // создаем линию 00333 Widget* widget = _getClientWidget()->createWidgetT("Button", mSkinLine, 0, height, _getClientWidget()->getWidth(), mHeightLine, Align::Top | Align::HStretch); 00334 Button* line = widget->castType<Button>(); 00335 // подписываемся на всякие там события 00336 line->eventMouseButtonPressed += newDelegate(this, &ListBox::notifyMousePressed); 00337 line->eventMouseButtonReleased += newDelegate(this, &ListBox::notifyMouseButtonReleased); 00338 line->eventMouseButtonClick += newDelegate(this, &ListBox::notifyMouseClick); 00339 line->eventMouseButtonDoubleClick += newDelegate(this, &ListBox::notifyMouseDoubleClick); 00340 line->eventMouseWheel += newDelegate(this, &ListBox::notifyMouseWheel); 00341 line->eventKeyButtonPressed += newDelegate(this, &ListBox::notifyKeyButtonPressed); 00342 line->eventKeyButtonReleased += newDelegate(this, &ListBox::notifyKeyButtonReleased); 00343 line->eventMouseSetFocus += newDelegate(this, &ListBox::notifyMouseSetFocus); 00344 line->eventMouseLostFocus += newDelegate(this, &ListBox::notifyMouseLostFocus); 00345 line->_setContainer(this); 00346 // присваиваем порядковый номер, для простоты просчета 00347 line->_setInternalData((size_t)mWidgetLines.size()); 00348 // и сохраняем 00349 mWidgetLines.push_back(line); 00350 height += mHeightLine; 00351 } 00352 00353 // проверяем на возможность не менять положение списка 00354 if (position >= mRangeIndex) 00355 { 00356 // размер всех помещается в клиент 00357 if (mRangeIndex <= 0) 00358 { 00359 // обнуляем, если надо 00360 if (position || mOffsetTop || mTopIndex) 00361 { 00362 position = 0; 00363 mTopIndex = 0; 00364 mOffsetTop = 0; 00365 mLastRedrawLine = 0; // чтобы все перерисовалось 00366 00367 // выравниваем 00368 int offset = 0; 00369 for (size_t pos = 0; pos < mWidgetLines.size(); pos++) 00370 { 00371 mWidgetLines[pos]->setPosition(0, offset); 00372 offset += mHeightLine; 00373 } 00374 } 00375 } 00376 else 00377 { 00378 // прижимаем список к нижней границе 00379 int count = _getClientWidget()->getHeight() / mHeightLine; 00380 mOffsetTop = mHeightLine - (_getClientWidget()->getHeight() % mHeightLine); 00381 00382 if (mOffsetTop == mHeightLine) 00383 { 00384 mOffsetTop = 0; 00385 count --; 00386 } 00387 00388 int top = (int)mItemsInfo.size() - count - 1; 00389 00390 // выравниваем 00391 int offset = 0 - mOffsetTop; 00392 for (size_t pos = 0; pos < mWidgetLines.size(); pos++) 00393 { 00394 mWidgetLines[pos]->setPosition(0, offset); 00395 offset += mHeightLine; 00396 } 00397 00398 // высчитываем положение, должно быть максимальным 00399 position = top * mHeightLine + mOffsetTop; 00400 00401 // если индех изменился, то перерисовываем линии 00402 if (top != mTopIndex) 00403 { 00404 mTopIndex = top; 00405 _redrawItemRange(); 00406 } 00407 } 00408 } 00409 00410 // увеличился размер, но прокрутки вниз небыло, обновляем линии снизу 00411 _redrawItemRange(mLastRedrawLine); 00412 00413 } // if (old_cy < mCoord.height) 00414 00415 // просчитываем положение скролла 00416 if (mWidgetScroll != nullptr) 00417 mWidgetScroll->setScrollPosition(position); 00418 00419 mOldSize.width = mCoord.width; 00420 mOldSize.height = mCoord.height; 00421 00422 #if MYGUI_DEBUG_MODE == 1 00423 _checkMapping("ListBox::updateLine"); 00424 #endif 00425 } 00426 00427 void ListBox::_redrawItemRange(size_t _start) 00428 { 00429 // перерисовываем линии, только те, что видны 00430 size_t pos = _start; 00431 for (; pos < mWidgetLines.size(); pos++) 00432 { 00433 // индекс в нашем массиве 00434 size_t index = pos + (size_t)mTopIndex; 00435 00436 // не будем заходить слишком далеко 00437 if (index >= mItemsInfo.size()) 00438 { 00439 // запоминаем последнюю перерисованную линию 00440 mLastRedrawLine = pos; 00441 break; 00442 } 00443 if (mWidgetLines[pos]->getTop() > _getClientWidget()->getHeight()) 00444 { 00445 // запоминаем последнюю перерисованную линию 00446 mLastRedrawLine = pos; 00447 break; 00448 } 00449 00450 // если был скрыт, то покажем 00451 mWidgetLines[pos]->setVisible(true); 00452 // обновляем текст 00453 mWidgetLines[pos]->setCaption(mItemsInfo[index].first); 00454 00455 // если нужно выделить ,то выделим 00456 static_cast<Button*>(mWidgetLines[pos])->setStateSelected(index == mIndexSelect); 00457 } 00458 00459 // если цикл весь прошли, то ставим максимальную линию 00460 if (pos >= mWidgetLines.size()) 00461 { 00462 mLastRedrawLine = pos; 00463 } 00464 else 00465 { 00466 //Widget* focus = InputManager::getInstance().getMouseFocusWidget(); 00467 for (; pos < mWidgetLines.size(); pos++) 00468 { 00469 static_cast<Button*>(mWidgetLines[pos])->setStateSelected(false); 00470 static_cast<Button*>(mWidgetLines[pos])->setVisible(false); 00471 //if (focus == mWidgetLines[pos]) InputManager::getInstance()._unlinkWidget(focus); 00472 } 00473 } 00474 00475 #if MYGUI_DEBUG_MODE == 1 00476 _checkMapping("ListBox::_redrawItemRange"); 00477 #endif 00478 } 00479 00480 // перерисовывает индекс 00481 void ListBox::_redrawItem(size_t _index) 00482 { 00483 // невидно 00484 if (_index < (size_t)mTopIndex) 00485 return; 00486 _index -= (size_t)mTopIndex; 00487 // тоже невидно 00488 if (_index >= mLastRedrawLine) 00489 return; 00490 00491 MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "ListBox::_redrawItem"); 00492 // перерисовываем 00493 mWidgetLines[_index]->setCaption(mItemsInfo[_index + mTopIndex].first); 00494 00495 #if MYGUI_DEBUG_MODE == 1 00496 _checkMapping("ListBox::_redrawItem"); 00497 #endif 00498 } 00499 00500 void ListBox::insertItemAt(size_t _index, const UString& _name, Any _data) 00501 { 00502 MYGUI_ASSERT_RANGE_INSERT(_index, mItemsInfo.size(), "ListBox::insertItemAt"); 00503 if (_index == ITEM_NONE) 00504 _index = mItemsInfo.size(); 00505 00506 // вставляем физически 00507 mItemsInfo.insert(mItemsInfo.begin() + _index, PairItem(_name, _data)); 00508 00509 // если надо, то меняем выделенный элемент 00510 if ((mIndexSelect != ITEM_NONE) && (_index <= mIndexSelect)) 00511 mIndexSelect++; 00512 00513 // строка, до первого видимого элемента 00514 if ((_index <= (size_t)mTopIndex) && (mRangeIndex > 0)) 00515 { 00516 mTopIndex ++; 00517 // просчитываем положение скролла 00518 if (mWidgetScroll != nullptr) 00519 { 00520 mWidgetScroll->setScrollRange(mWidgetScroll->getScrollRange() + mHeightLine); 00521 if (!mItemsInfo.empty()) 00522 mWidgetScroll->setTrackSize( mWidgetScroll->getLineSize() * _getClientWidget()->getHeight() / mHeightLine / (int)mItemsInfo.size() ); 00523 mWidgetScroll->setScrollPosition(mTopIndex * mHeightLine + mOffsetTop); 00524 } 00525 mRangeIndex += mHeightLine; 00526 } 00527 else 00528 { 00529 // высчитывам положение строки 00530 int offset = ((int)_index - mTopIndex) * mHeightLine - mOffsetTop; 00531 00532 // строка, после последнего видимого элемента, плюс одна строка (потому что для прокрутки нужно на одну строчку больше) 00533 if (_getClientWidget()->getHeight() < (offset - mHeightLine)) 00534 { 00535 // просчитываем положение скролла 00536 if (mWidgetScroll != nullptr) 00537 { 00538 mWidgetScroll->setScrollRange(mWidgetScroll->getScrollRange() + mHeightLine); 00539 if (!mItemsInfo.empty()) 00540 mWidgetScroll->setTrackSize( mWidgetScroll->getLineSize() * _getClientWidget()->getHeight() / mHeightLine / (int)mItemsInfo.size() ); 00541 mWidgetScroll->setScrollPosition(mTopIndex * mHeightLine + mOffsetTop); 00542 } 00543 mRangeIndex += mHeightLine; 00544 00545 // строка в видимой области 00546 } 00547 else 00548 { 00549 // обновляем все 00550 updateScroll(); 00551 updateLine(true); 00552 00553 // позже сюда еще оптимизацию по колличеству перерисовок 00554 } 00555 } 00556 00557 #if MYGUI_DEBUG_MODE == 1 00558 _checkMapping("ListBox::insertItemAt"); 00559 #endif 00560 } 00561 00562 void ListBox::removeItemAt(size_t _index) 00563 { 00564 MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "ListBox::removeItemAt"); 00565 00566 // удяляем физически строку 00567 mItemsInfo.erase(mItemsInfo.begin() + _index); 00568 00569 // если надо, то меняем выделенный элемент 00570 if (mItemsInfo.empty()) mIndexSelect = ITEM_NONE; 00571 else if (mIndexSelect != ITEM_NONE) 00572 { 00573 if (_index < mIndexSelect) 00574 mIndexSelect--; 00575 else if ((_index == mIndexSelect) && (mIndexSelect == (mItemsInfo.size()))) 00576 mIndexSelect--; 00577 } 00578 00579 // если виджетов стало больше , то скрываем крайний 00580 if (mWidgetLines.size() > mItemsInfo.size()) 00581 { 00582 mWidgetLines[mItemsInfo.size()]->setVisible(false); 00583 } 00584 00585 // строка, до первого видимого элемента 00586 if (_index < (size_t)mTopIndex) 00587 { 00588 mTopIndex --; 00589 // просчитываем положение скролла 00590 if (mWidgetScroll != nullptr) 00591 { 00592 mWidgetScroll->setScrollRange(mWidgetScroll->getScrollRange() - mHeightLine); 00593 if (!mItemsInfo.empty()) 00594 mWidgetScroll->setTrackSize( mWidgetScroll->getLineSize() * _getClientWidget()->getHeight() / mHeightLine / (int)mItemsInfo.size() ); 00595 mWidgetScroll->setScrollPosition(mTopIndex * mHeightLine + mOffsetTop); 00596 } 00597 mRangeIndex -= mHeightLine; 00598 } 00599 else 00600 { 00601 // высчитывам положение удаляемой строки 00602 int offset = ((int)_index - mTopIndex) * mHeightLine - mOffsetTop; 00603 00604 // строка, после последнего видимого элемента 00605 if (_getClientWidget()->getHeight() < offset) 00606 { 00607 // просчитываем положение скролла 00608 if (mWidgetScroll != nullptr) 00609 { 00610 mWidgetScroll->setScrollRange(mWidgetScroll->getScrollRange() - mHeightLine); 00611 if (!mItemsInfo.empty()) 00612 mWidgetScroll->setTrackSize( mWidgetScroll->getLineSize() * _getClientWidget()->getHeight() / mHeightLine / (int)mItemsInfo.size() ); 00613 mWidgetScroll->setScrollPosition(mTopIndex * mHeightLine + mOffsetTop); 00614 } 00615 mRangeIndex -= mHeightLine; 00616 00617 // строка в видимой области 00618 } 00619 else 00620 { 00621 // обновляем все 00622 updateScroll(); 00623 updateLine(true); 00624 00625 // позже сюда еще оптимизацию по колличеству перерисовок 00626 } 00627 } 00628 00629 #if MYGUI_DEBUG_MODE == 1 00630 _checkMapping("ListBox::removeItemAt"); 00631 #endif 00632 } 00633 00634 void ListBox::setIndexSelected(size_t _index) 00635 { 00636 MYGUI_ASSERT_RANGE_AND_NONE(_index, mItemsInfo.size(), "ListBox::setIndexSelected"); 00637 if (mIndexSelect != _index) 00638 { 00639 _selectIndex(mIndexSelect, false); 00640 _selectIndex(_index, true); 00641 mIndexSelect = _index; 00642 } 00643 } 00644 00645 void ListBox::_selectIndex(size_t _index, bool _select) 00646 { 00647 if (_index == ITEM_NONE) 00648 return; 00649 // не видно строки 00650 if (_index < (size_t)mTopIndex) 00651 return; 00652 // высчитывам положение строки 00653 int offset = ((int)_index - mTopIndex) * mHeightLine - mOffsetTop; 00654 // строка, после последнего видимого элемента 00655 if (_getClientWidget()->getHeight() < offset) 00656 return; 00657 00658 size_t index = _index - mTopIndex; 00659 if (index < mWidgetLines.size()) 00660 static_cast<Button*>(mWidgetLines[index])->setStateSelected(_select); 00661 00662 #if MYGUI_DEBUG_MODE == 1 00663 _checkMapping("ListBox::_selectIndex"); 00664 #endif 00665 } 00666 00667 void ListBox::beginToItemAt(size_t _index) 00668 { 00669 MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "ListBox::beginToItemAt"); 00670 if (mRangeIndex <= 0) 00671 return; 00672 00673 int offset = (int)_index * mHeightLine; 00674 if (offset >= mRangeIndex) offset = mRangeIndex; 00675 00676 if (mWidgetScroll != nullptr) 00677 { 00678 if ((int)mWidgetScroll->getScrollPosition() == offset) 00679 return; 00680 mWidgetScroll->setScrollPosition(offset); 00681 } 00682 notifyScrollChangePosition(nullptr, offset); 00683 00684 #if MYGUI_DEBUG_MODE == 1 00685 _checkMapping("ListBox::beginToItemAt"); 00686 #endif 00687 } 00688 00689 // видим ли мы элемент, полностью или нет 00690 bool ListBox::isItemVisibleAt(size_t _index, bool _fill) 00691 { 00692 // если элемента нет, то мы его не видим (в том числе когда их вообще нет) 00693 if (_index >= mItemsInfo.size()) 00694 return false; 00695 // если скрола нет, то мы палюбак видим 00696 if (mRangeIndex <= 0) 00697 return true; 00698 00699 // строка, до первого видимого элемента 00700 if (_index < (size_t)mTopIndex) 00701 return false; 00702 00703 // строка это верхний выделенный 00704 if (_index == (size_t)mTopIndex) 00705 { 00706 if ((mOffsetTop != 0) && (_fill)) 00707 return false; // нам нужна полностью видимость 00708 return true; 00709 } 00710 00711 // высчитывам положение строки 00712 int offset = ((int)_index - mTopIndex) * mHeightLine - mOffsetTop; 00713 00714 // строка, после последнего видимого элемента 00715 if (_getClientWidget()->getHeight() < offset) 00716 return false; 00717 00718 // если мы внизу и нам нужен целый 00719 if ((_getClientWidget()->getHeight() < (offset + mHeightLine)) && (_fill)) 00720 return false; 00721 00722 return true; 00723 } 00724 00725 void ListBox::removeAllItems() 00726 { 00727 mTopIndex = 0; 00728 mIndexSelect = ITEM_NONE; 00729 mOffsetTop = 0; 00730 00731 mItemsInfo.clear(); 00732 00733 int offset = 0; 00734 for (size_t pos = 0; pos < mWidgetLines.size(); pos++) 00735 { 00736 mWidgetLines[pos]->setVisible(false); 00737 mWidgetLines[pos]->setPosition(0, offset); 00738 offset += mHeightLine; 00739 } 00740 00741 // обновляем все 00742 updateScroll(); 00743 updateLine(true); 00744 00745 #if MYGUI_DEBUG_MODE == 1 00746 _checkMapping("ListBox::removeAllItems"); 00747 #endif 00748 } 00749 00750 void ListBox::setItemNameAt(size_t _index, const UString& _name) 00751 { 00752 MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "ListBox::setItemNameAt"); 00753 mItemsInfo[_index].first = _name; 00754 _redrawItem(_index); 00755 } 00756 00757 void ListBox::setItemDataAt(size_t _index, Any _data) 00758 { 00759 MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "ListBox::setItemDataAt"); 00760 mItemsInfo[_index].second = _data; 00761 _redrawItem(_index); 00762 } 00763 00764 const UString& ListBox::getItemNameAt(size_t _index) 00765 { 00766 MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "ListBox::getItemNameAt"); 00767 return mItemsInfo[_index].first; 00768 } 00769 00770 void ListBox::notifyMouseSetFocus(Widget* _sender, Widget* _old) 00771 { 00772 00773 #if MYGUI_DEBUG_MODE == 1 00774 MYGUI_ASSERT_RANGE(*_sender->_getInternalData<size_t>(), mWidgetLines.size(), "ListBox::notifyMouseSetFocus"); 00775 #endif 00776 00777 mLineActive = *_sender->_getInternalData<size_t>(); 00778 eventListMouseItemFocus(this, mLineActive); 00779 } 00780 00781 void ListBox::notifyMouseLostFocus(Widget* _sender, Widget* _new) 00782 { 00783 if ((nullptr == _new) || (_new->getParent() != _getClientWidget())) 00784 { 00785 mLineActive = ITEM_NONE; 00786 eventListMouseItemFocus(this, ITEM_NONE); 00787 } 00788 } 00789 00790 void ListBox::_setItemFocus(size_t _index, bool _focus) 00791 { 00792 MYGUI_ASSERT_RANGE(_index, mWidgetLines.size(), "ListBox::_setItemFocus"); 00793 static_cast<Button*>(mWidgetLines[_index])->_setMouseFocus(_focus); 00794 } 00795 00796 void ListBox::setScrollVisible(bool _visible) 00797 { 00798 if (mNeedVisibleScroll == _visible) 00799 return; 00800 mNeedVisibleScroll = _visible; 00801 updateScroll(); 00802 } 00803 00804 void ListBox::setScrollPosition(size_t _position) 00805 { 00806 if (mWidgetScroll != nullptr) 00807 { 00808 if (mWidgetScroll->getScrollRange() > _position) 00809 { 00810 mWidgetScroll->setScrollPosition(_position); 00811 _setScrollView(_position); 00812 } 00813 } 00814 } 00815 00816 void ListBox::_setScrollView(size_t _position) 00817 { 00818 mOffsetTop = ((int)_position % mHeightLine); 00819 00820 // смещение с отрицательной стороны 00821 int offset = 0 - mOffsetTop; 00822 00823 for (size_t pos = 0; pos < mWidgetLines.size(); pos++) 00824 { 00825 mWidgetLines[pos]->setPosition(IntPoint(0, offset)); 00826 offset += mHeightLine; 00827 } 00828 00829 // если индех изменился, то перерисовываем линии 00830 int top = ((int)_position / mHeightLine); 00831 if (top != mTopIndex) 00832 { 00833 mTopIndex = top; 00834 _redrawItemRange(); 00835 } 00836 00837 // прорисовываем все нижние строки, если они появились 00838 _redrawItemRange(mLastRedrawLine); 00839 } 00840 00841 void ListBox::_sendEventChangeScroll(size_t _position) 00842 { 00843 eventListChangeScroll(this, _position); 00844 if (ITEM_NONE != mLineActive) 00845 eventListMouseItemFocus(this, mLineActive); 00846 } 00847 00848 void ListBox::swapItemsAt(size_t _index1, size_t _index2) 00849 { 00850 MYGUI_ASSERT_RANGE(_index1, mItemsInfo.size(), "ListBox::swapItemsAt"); 00851 MYGUI_ASSERT_RANGE(_index2, mItemsInfo.size(), "ListBox::swapItemsAt"); 00852 00853 if (_index1 == _index2) 00854 return; 00855 00856 std::swap(mItemsInfo[_index1], mItemsInfo[_index2]); 00857 00858 _redrawItem(_index1); 00859 _redrawItem(_index2); 00860 } 00861 00862 void ListBox::_checkMapping(const std::string& _owner) 00863 { 00864 size_t count_pressed = 0; 00865 size_t count_show = 0; 00866 00867 for (size_t pos = 0; pos < mWidgetLines.size(); pos++) 00868 { 00869 MYGUI_ASSERT(pos == *mWidgetLines[pos]->_getInternalData<size_t>(), _owner); 00870 if (static_cast<Button*>(mWidgetLines[pos])->getStateSelected()) 00871 count_pressed ++; 00872 if (static_cast<Button*>(mWidgetLines[pos])->getVisible()) 00873 count_show ++; 00874 } 00875 //MYGUI_ASSERT(count_pressed < 2, _owner); 00876 //MYGUI_ASSERT((count_show + mOffsetTop) <= mItemsInfo.size(), _owner); 00877 } 00878 00879 void ListBox::_checkAlign() 00880 { 00881 // максимальная высота всех строк 00882 int max_height = mItemsInfo.size() * mHeightLine; 00883 // видимая высота 00884 int visible_height = _getClientWidget()->getHeight(); 00885 00886 // все строки помещаются 00887 if (visible_height >= max_height) 00888 { 00889 MYGUI_ASSERT(mTopIndex == 0, "mTopIndex == 0"); 00890 MYGUI_ASSERT(mOffsetTop == 0, "mOffsetTop == 0"); 00891 int height = 0; 00892 for (size_t pos = 0; pos < mWidgetLines.size(); pos++) 00893 { 00894 if (pos >= mItemsInfo.size()) 00895 break; 00896 MYGUI_ASSERT(mWidgetLines[pos]->getTop() == height, "mWidgetLines[pos]->getTop() == height"); 00897 height += mWidgetLines[pos]->getHeight(); 00898 } 00899 } 00900 } 00901 00902 size_t ListBox::findItemIndexWith(const UString& _name) 00903 { 00904 for (size_t pos = 0; pos < mItemsInfo.size(); pos++) 00905 { 00906 if (mItemsInfo[pos].first == _name) 00907 return pos; 00908 } 00909 return ITEM_NONE; 00910 } 00911 00912 int ListBox::getOptimalHeight() 00913 { 00914 return (int)((mCoord.height - _getClientWidget()->getHeight()) + (mItemsInfo.size() * mHeightLine)); 00915 } 00916 00917 Widget* ListBox::_getClientWidget() 00918 { 00919 return mClient == nullptr ? this : mClient; 00920 } 00921 00922 size_t ListBox::getItemCount() const 00923 { 00924 return mItemsInfo.size(); 00925 } 00926 00927 void ListBox::addItem(const UString& _name, Any _data) 00928 { 00929 insertItemAt(ITEM_NONE, _name, _data); 00930 } 00931 00932 size_t ListBox::getIndexSelected() const 00933 { 00934 return mIndexSelect; 00935 } 00936 00937 void ListBox::clearIndexSelected() 00938 { 00939 setIndexSelected(ITEM_NONE); 00940 } 00941 00942 void ListBox::clearItemDataAt(size_t _index) 00943 { 00944 setItemDataAt(_index, Any::Null); 00945 } 00946 00947 void ListBox::beginToItemFirst() 00948 { 00949 if (getItemCount()) 00950 beginToItemAt(0); 00951 } 00952 00953 void ListBox::beginToItemLast() 00954 { 00955 if (getItemCount()) 00956 beginToItemAt(getItemCount() - 1); 00957 } 00958 00959 void ListBox::beginToItemSelected() 00960 { 00961 if (getIndexSelected() != ITEM_NONE) 00962 beginToItemAt(getIndexSelected()); 00963 } 00964 00965 bool ListBox::isItemSelectedVisible(bool _fill) 00966 { 00967 return isItemVisibleAt(mIndexSelect, _fill); 00968 } 00969 00970 void ListBox::setPosition(int _left, int _top) 00971 { 00972 setPosition(IntPoint(_left, _top)); 00973 } 00974 00975 void ListBox::setSize(int _width, int _height) 00976 { 00977 setSize(IntSize(_width, _height)); 00978 } 00979 00980 void ListBox::setCoord(int _left, int _top, int _width, int _height) 00981 { 00982 setCoord(IntCoord(_left, _top, _width, _height)); 00983 } 00984 00985 size_t ListBox::_getItemIndex(Widget* _item) 00986 { 00987 for (VectorButton::iterator iter = mWidgetLines.begin(); iter != mWidgetLines.end(); ++iter) 00988 { 00989 if ((*iter) == _item) 00990 return *(*iter)->_getInternalData<size_t>() + mTopIndex; 00991 } 00992 return ITEM_NONE; 00993 } 00994 00995 void ListBox::_resetContainer(bool _update) 00996 { 00997 // обязательно у базового 00998 Base::_resetContainer(_update); 00999 01000 if (!_update) 01001 { 01002 WidgetManager& instance = WidgetManager::getInstance(); 01003 for (VectorButton::iterator iter = mWidgetLines.begin(); iter != mWidgetLines.end(); ++iter) 01004 instance.unlinkFromUnlinkers(*iter); 01005 } 01006 } 01007 01008 void ListBox::setPropertyOverride(const std::string& _key, const std::string& _value) 01009 { 01010 // не коментировать 01011 if (_key == "AddItem") 01012 addItem(_value); 01013 else if (_key == "ActivateOnClick") 01014 mActivateOnClick = utility::parseBool(_value); 01015 else 01016 { 01017 Base::setPropertyOverride(_key, _value); 01018 return; 01019 } 01020 01021 eventChangeProperty(this, _key, _value); 01022 } 01023 01024 void ListBox::_activateItem(MyGUI::Widget* _sender) 01025 { 01026 // если выделен клиент, то сбрасываем 01027 if (_sender == _getClientWidget()) 01028 { 01029 if (mIndexSelect != ITEM_NONE) 01030 { 01031 _selectIndex(mIndexSelect, false); 01032 mIndexSelect = ITEM_NONE; 01033 eventListChangePosition(this, mIndexSelect); 01034 } 01035 eventListMouseItemActivate(this, mIndexSelect); 01036 01037 // если не клиент, то просчитывам 01038 } 01039 // ячейка может быть скрыта 01040 else if (_sender->getVisible()) 01041 { 01042 01043 #if MYGUI_DEBUG_MODE == 1 01044 _checkMapping("ListBox::notifyMousePressed"); 01045 MYGUI_ASSERT_RANGE(*_sender->_getInternalData<size_t>(), mWidgetLines.size(), "ListBox::notifyMousePressed"); 01046 MYGUI_ASSERT_RANGE(*_sender->_getInternalData<size_t>() + mTopIndex, mItemsInfo.size(), "ListBox::notifyMousePressed"); 01047 #endif 01048 01049 size_t index = *_sender->_getInternalData<size_t>() + mTopIndex; 01050 01051 if (mIndexSelect != index) 01052 { 01053 _selectIndex(mIndexSelect, false); 01054 _selectIndex(index, true); 01055 mIndexSelect = index; 01056 eventListChangePosition(this, mIndexSelect); 01057 } 01058 eventListMouseItemActivate(this, mIndexSelect); 01059 } 01060 01061 _resetContainer(true); 01062 } 01063 01064 size_t ListBox::_getItemCount() 01065 { 01066 return getItemCount(); 01067 } 01068 01069 void ListBox::_addItem(const MyGUI::UString& _name) 01070 { 01071 addItem(_name); 01072 } 01073 01074 void ListBox::_removeItemAt(size_t _index) 01075 { 01076 removeItemAt(_index); 01077 } 01078 01079 void ListBox::_setItemNameAt(size_t _index, const UString& _name) 01080 { 01081 setItemNameAt(_index, _name); 01082 } 01083 01084 const UString& ListBox::_getItemNameAt(size_t _index) 01085 { 01086 return getItemNameAt(_index); 01087 } 01088 01089 size_t ListBox::getIndexByWidget(Widget* _widget) 01090 { 01091 if (_widget == mClient) 01092 return ITEM_NONE; 01093 return *_widget->_getInternalData<size_t>() + mTopIndex; 01094 } 01095 01096 void ListBox::notifyKeyButtonPressed(Widget* _sender, KeyCode _key, Char _char) 01097 { 01098 eventNotifyItem(this, IBNotifyItemData(getIndexByWidget(_sender), IBNotifyItemData::KeyPressed, _key, _char)); 01099 } 01100 01101 void ListBox::notifyKeyButtonReleased(Widget* _sender, KeyCode _key) 01102 { 01103 eventNotifyItem(this, IBNotifyItemData(getIndexByWidget(_sender), IBNotifyItemData::KeyReleased, _key)); 01104 } 01105 01106 void ListBox::notifyMouseButtonReleased(Widget* _sender, int _left, int _top, MouseButton _id) 01107 { 01108 eventNotifyItem(this, IBNotifyItemData(getIndexByWidget(_sender), IBNotifyItemData::MouseReleased, _left, _top, _id)); 01109 } 01110 01111 void ListBox::onKeyButtonReleased(KeyCode _key) 01112 { 01113 Base::onKeyButtonReleased(_key); 01114 01115 eventNotifyItem(this, IBNotifyItemData(ITEM_NONE, IBNotifyItemData::KeyReleased, _key)); 01116 } 01117 01118 void ListBox::setActivateOnClick(bool activateOnClick) 01119 { 01120 mActivateOnClick = activateOnClick; 01121 } 01122 01123 Widget* ListBox::getWidgetByIndex(size_t _index) 01124 { 01125 if (_index == MyGUI::ITEM_NONE) 01126 return nullptr; 01127 01128 // индекс в нашем массиве 01129 size_t index = _index + (size_t)mTopIndex; 01130 01131 if (index < mWidgetLines.size()) 01132 return mWidgetLines[index]; 01133 return nullptr; 01134 } 01135 01136 } // namespace MyGUI