ListView.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  1. //
  2. // Urho3D Engine
  3. // Copyright (c) 2008-2011 Lasse Öörni
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. //
  23. #include "Precompiled.h"
  24. #include "BorderImage.h"
  25. #include "InputEvents.h"
  26. #include "ListView.h"
  27. #include "Log.h"
  28. #include "StringUtils.h"
  29. #include "UIEvents.h"
  30. #include "DebugNew.h"
  31. static const ShortStringHash indentHash("Indent");
  32. static const std::string highlightModes[] =
  33. {
  34. "never",
  35. "focus"
  36. "always"
  37. };
  38. int getItemIndent(UIElement* item)
  39. {
  40. if (!item)
  41. return 0;
  42. return item->getUserData()[indentHash].getInt();
  43. }
  44. ListView::ListView(const std::string& name) :
  45. ScrollView(name),
  46. mHighlightMode(HM_FOCUS),
  47. mMultiselect(false),
  48. mHierarchyMode(false),
  49. mClearSelectionOnDefocus(false),
  50. mDoubleClickInterval(0.5f),
  51. mDoubleClickTimer(0.0f),
  52. mLastClickedItem(M_MAX_UNSIGNED)
  53. {
  54. UIElement* container = new UIElement();
  55. container->setEnabled(true);
  56. container->setLayout(LM_VERTICAL);
  57. setContentElement(container);
  58. subscribeToEvent(EVENT_UIMOUSECLICK, EVENT_HANDLER(ListView, handleUIMouseClick));
  59. }
  60. ListView::~ListView()
  61. {
  62. }
  63. void ListView::setStyle(const XMLElement& element, ResourceCache* cache)
  64. {
  65. ScrollView::setStyle(element, cache);
  66. UIElement* root = getRootElement();
  67. XMLElement itemElem = element.getChildElement("listitem");
  68. if (root)
  69. {
  70. while (itemElem)
  71. {
  72. if (itemElem.hasAttribute("name"))
  73. {
  74. UIElement* item = root->getChild(itemElem.getString("name"), true);
  75. addItem(item);
  76. if (itemElem.hasAttribute("indent"))
  77. item->getUserData()[indentHash] = itemElem.getInt("indent");
  78. itemElem = itemElem.getNextElement("listitem");
  79. }
  80. }
  81. }
  82. if (element.hasChildElement("highlightmode"))
  83. {
  84. std::string highlightMode = element.getChildElement("highlightmode").getStringLower("value");
  85. setHighlightMode((HighlightMode)getIndexFromStringList(highlightMode, highlightModes, 3, 1));
  86. }
  87. if (element.hasChildElement("multiselect"))
  88. setMultiselect(element.getChildElement("multiselect").getBool("enable"));
  89. if (element.hasChildElement("hierarchymode"))
  90. setHierarchyMode(element.getChildElement("hierarchymode").getBool("enable"));
  91. if (element.hasChildElement("clearselection"))
  92. setClearSelectionOnDefocus(element.getChildElement("clearselection").getBool("enable"));
  93. if (element.hasChildElement("doubleclickinterval"))
  94. setDoubleClickInterval(element.getChildElement("doubleclickinterval").getFloat("value"));
  95. XMLElement selectionElem = element.getChildElement("selection");
  96. while (selectionElem)
  97. {
  98. addSelection(selectionElem.getInt("value"));
  99. selectionElem = selectionElem.getNextElement("selection");
  100. }
  101. }
  102. void ListView::update(float timeStep)
  103. {
  104. if (mDoubleClickTimer > 0.0f)
  105. mDoubleClickTimer = max(mDoubleClickTimer - timeStep, 0.0f);
  106. }
  107. void ListView::onKey(int key, int buttons, int qualifiers)
  108. {
  109. // If no selection, can not move with keys
  110. unsigned numItems = getNumItems();
  111. unsigned selection = getSelection();
  112. if ((selection != M_MAX_UNSIGNED) && (numItems))
  113. {
  114. // If either shift or ctrl held down, add to selection if multiselect enabled
  115. bool additive = (mMultiselect) && (qualifiers != 0);
  116. switch (key)
  117. {
  118. case KEY_LEFT:
  119. if (mHierarchyMode)
  120. {
  121. setChildItemsVisible(selection, false);
  122. return;
  123. }
  124. break;
  125. case KEY_RIGHT:
  126. if (mHierarchyMode)
  127. {
  128. setChildItemsVisible(selection, true);
  129. return;
  130. }
  131. break;
  132. case KEY_RETURN:
  133. if (mHierarchyMode)
  134. {
  135. toggleChildItemsVisible(selection);
  136. return;
  137. }
  138. break;
  139. case KEY_UP:
  140. changeSelection(-1, additive);
  141. return;
  142. case KEY_DOWN:
  143. changeSelection(1, additive);
  144. return;
  145. case KEY_PAGEUP:
  146. {
  147. // Convert page step to pixels and see how many items we have to skip to reach that many pixels
  148. int stepPixels = ((int)(mPageStep * mScrollPanel->getHeight())) - getSelectedItem()->getHeight();
  149. unsigned newSelection = selection;
  150. unsigned okSelection = selection;
  151. while (newSelection < numItems)
  152. {
  153. UIElement* item = getItem(newSelection);
  154. int height = 0;
  155. if (item->isVisible())
  156. {
  157. height = item->getHeight();
  158. okSelection = newSelection;
  159. }
  160. if (stepPixels < height)
  161. break;
  162. stepPixels -= height;
  163. --newSelection;
  164. }
  165. if (!additive)
  166. setSelection(okSelection);
  167. else
  168. addSelection(okSelection);
  169. }
  170. return;
  171. case KEY_PAGEDOWN:
  172. {
  173. int stepPixels = ((int)(mPageStep * mScrollPanel->getHeight())) - getSelectedItem()->getHeight();
  174. unsigned newSelection = selection;
  175. unsigned okSelection = selection;
  176. while (newSelection < numItems)
  177. {
  178. UIElement* item = getItem(newSelection);
  179. int height = 0;
  180. if (item->isVisible())
  181. {
  182. height = item->getHeight();
  183. okSelection = newSelection;
  184. }
  185. if (stepPixels < height)
  186. break;
  187. stepPixels -= height;
  188. ++newSelection;
  189. }
  190. if (!additive)
  191. setSelection(okSelection);
  192. else
  193. addSelection(okSelection);
  194. }
  195. return;
  196. case KEY_HOME:
  197. changeSelection(-(int)getNumItems(), additive);
  198. return;
  199. case KEY_END:
  200. changeSelection(getNumItems(), additive);
  201. return;
  202. }
  203. }
  204. using namespace UnhandledKey;
  205. VariantMap eventData;
  206. eventData[P_ELEMENT] = (void*)this;
  207. eventData[P_KEY] = key;
  208. eventData[P_BUTTONS] = buttons;
  209. eventData[P_QUALIFIERS] = qualifiers;
  210. sendEvent(EVENT_UNHANDLEDKEY, eventData);
  211. }
  212. void ListView::onResize()
  213. {
  214. ScrollView::onResize();
  215. // Set the content element width to match the scrollpanel
  216. const IntRect& clipBorder = mScrollPanel->getClipBorder();
  217. mContentElement->setWidth(mScrollPanel->getWidth() - clipBorder.mLeft - clipBorder.mRight);
  218. }
  219. void ListView::onFocus()
  220. {
  221. updateSelectionEffect();
  222. }
  223. void ListView::onDefocus()
  224. {
  225. if (mClearSelectionOnDefocus)
  226. clearSelection();
  227. updateSelectionEffect();
  228. }
  229. void ListView::addItem(UIElement* item)
  230. {
  231. if ((!item) || (item->getParent() == mContentElement))
  232. return;
  233. // Enable input so that clicking the item can be detected
  234. item->setEnabled(true);
  235. item->setSelected(false);
  236. mContentElement->addChild(item);
  237. }
  238. void ListView::removeItem(UIElement* item)
  239. {
  240. unsigned numItems = getNumItems();
  241. for (unsigned i = 0; i < numItems; ++i)
  242. {
  243. if (getItem(i) == item)
  244. {
  245. item->setSelected(false);
  246. mSelections.erase(i);
  247. // Remove any child items in hierarchy mode
  248. unsigned removed = 1;
  249. if (mHierarchyMode)
  250. {
  251. int baseIndent = getItemIndent(item);
  252. for (unsigned j = i + 1; j < numItems; ++j)
  253. {
  254. UIElement* childItem = getItem(j);
  255. if (getItemIndent(childItem) > baseIndent)
  256. {
  257. childItem->setSelected(false);
  258. mSelections.erase(j);
  259. mContentElement->removeChild(childItem);
  260. ++removed;
  261. }
  262. else
  263. break;
  264. }
  265. }
  266. // If necessary, shift the following selections
  267. std::set<unsigned> prevSelections;
  268. mSelections.clear();
  269. for (std::set<unsigned>::iterator j = prevSelections.begin(); j != prevSelections.end(); ++j)
  270. {
  271. if (*j > i)
  272. mSelections.insert(*j - removed);
  273. else
  274. mSelections.insert(*j);
  275. }
  276. updateSelectionEffect();
  277. break;
  278. }
  279. }
  280. mContentElement->removeChild(item);
  281. }
  282. void ListView::removeItem(unsigned index)
  283. {
  284. removeItem(getItem(index));
  285. }
  286. void ListView::removeAllItems()
  287. {
  288. unsigned numItems = getNumItems();
  289. for (unsigned i = 0; i < numItems; ++i)
  290. mContentElement->getChild(i)->setSelected(false);
  291. mContentElement->removeAllChildren();
  292. clearSelection();
  293. }
  294. void ListView::setSelection(unsigned index)
  295. {
  296. std::set<unsigned> indices;
  297. indices.insert(index);
  298. setSelections(indices);
  299. ensureItemVisibility(index);
  300. }
  301. void ListView::setSelections(const std::set<unsigned>& indices)
  302. {
  303. unsigned numItems = getNumItems();
  304. // Remove first items that should no longer be selected
  305. for (std::set<unsigned>::iterator i = mSelections.begin(); i != mSelections.end();)
  306. {
  307. unsigned index = *i;
  308. if (indices.find(index) == indices.end())
  309. {
  310. std::set<unsigned>::iterator current = i++;
  311. mSelections.erase(current);
  312. using namespace ItemSelected;
  313. VariantMap eventData;
  314. eventData[P_ELEMENT] = (void*)this;
  315. eventData[P_SELECTION] = index;
  316. sendEvent(EVENT_ITEMDESELECTED, eventData);
  317. }
  318. else
  319. ++i;
  320. }
  321. // Then add missing items
  322. for (std::set<unsigned>::const_iterator i = indices.begin(); i != indices.end(); ++i)
  323. {
  324. unsigned index = *i;
  325. if (index < numItems)
  326. {
  327. // In singleselect mode, resend the event even for the same selection
  328. if ((mSelections.find(index) == mSelections.end()) || (!mMultiselect))
  329. {
  330. mSelections.insert(*i);
  331. using namespace ItemSelected;
  332. VariantMap eventData;
  333. eventData[P_ELEMENT] = (void*)this;
  334. eventData[P_SELECTION] = *i;
  335. sendEvent(EVENT_ITEMSELECTED, eventData);
  336. }
  337. }
  338. // If no multiselect enabled, allow setting only one item
  339. if (!mMultiselect)
  340. break;
  341. }
  342. updateSelectionEffect();
  343. }
  344. void ListView::addSelection(unsigned index)
  345. {
  346. if (!mMultiselect)
  347. setSelection(index);
  348. else
  349. {
  350. if (index >= getNumItems())
  351. return;
  352. std::set<unsigned> newSelections = mSelections;
  353. newSelections.insert(index);
  354. setSelections(newSelections);
  355. ensureItemVisibility(index);
  356. }
  357. }
  358. void ListView::removeSelection(unsigned index)
  359. {
  360. if (index >= getNumItems())
  361. return;
  362. std::set<unsigned> newSelections = mSelections;
  363. newSelections.erase(index);
  364. setSelections(newSelections);
  365. ensureItemVisibility(index);
  366. }
  367. void ListView::toggleSelection(unsigned index)
  368. {
  369. unsigned numItems = getNumItems();
  370. if (index >= numItems)
  371. return;
  372. if (mSelections.find(index) != mSelections.end())
  373. removeSelection(index);
  374. else
  375. addSelection(index);
  376. }
  377. void ListView::changeSelection(int delta, bool additive)
  378. {
  379. if (mSelections.empty())
  380. return;
  381. if (!mMultiselect)
  382. additive = false;
  383. // If going downwards, use the last selection as a base. Otherwise use first
  384. unsigned selection = delta > 0 ? *mSelections.rbegin() : *mSelections.begin();
  385. unsigned numItems = getNumItems();
  386. unsigned newSelection = selection;
  387. unsigned okSelection = selection;
  388. while (delta != 0)
  389. {
  390. if (delta > 0)
  391. {
  392. ++newSelection;
  393. if (newSelection >= numItems)
  394. break;
  395. }
  396. if (delta < 0)
  397. {
  398. --newSelection;
  399. if (newSelection >= numItems)
  400. break;
  401. }
  402. UIElement* item = getItem(newSelection);
  403. if (item->isVisible())
  404. {
  405. okSelection = newSelection;
  406. if (delta > 0)
  407. --delta;
  408. if (delta < 0)
  409. ++delta;
  410. }
  411. }
  412. if (!additive)
  413. setSelection(okSelection);
  414. else
  415. addSelection(okSelection);
  416. }
  417. void ListView::clearSelection()
  418. {
  419. setSelections(std::set<unsigned>());
  420. updateSelectionEffect();
  421. }
  422. void ListView::setHighlightMode(HighlightMode mode)
  423. {
  424. mHighlightMode = mode;
  425. updateSelectionEffect();
  426. }
  427. void ListView::setMultiselect(bool enable)
  428. {
  429. mMultiselect = enable;
  430. }
  431. void ListView::setHierarchyMode(bool enable)
  432. {
  433. mHierarchyMode = enable;
  434. }
  435. void ListView::setClearSelectionOnDefocus(bool enable)
  436. {
  437. mClearSelectionOnDefocus = enable;
  438. }
  439. void ListView::setDoubleClickInterval(float interval)
  440. {
  441. mDoubleClickInterval = interval;
  442. }
  443. void ListView::setChildItemsVisible(unsigned index, bool enable)
  444. {
  445. unsigned numItems = getNumItems();
  446. if ((!mHierarchyMode) || (index >= numItems))
  447. return;
  448. int baseIndent = getItemIndent(getItem(index));
  449. for (unsigned i = index + 1; i < numItems; ++i)
  450. {
  451. UIElement* item = getItem(i);
  452. if (getItemIndent(item) > baseIndent)
  453. item->setVisible(enable);
  454. else
  455. break;
  456. }
  457. }
  458. void ListView::toggleChildItemsVisible(unsigned index)
  459. {
  460. unsigned numItems = getNumItems();
  461. if ((!mHierarchyMode) || (index >= numItems))
  462. return;
  463. int baseIndent = getItemIndent(getItem(index));
  464. for (unsigned i = index + 1; i < numItems; ++i)
  465. {
  466. UIElement* item = getItem(i);
  467. if (getItemIndent(item) > baseIndent)
  468. item->setVisible(!item->isVisible());
  469. else
  470. break;
  471. }
  472. }
  473. unsigned ListView::getNumItems() const
  474. {
  475. return mContentElement->getNumChildren();
  476. }
  477. UIElement* ListView::getItem(unsigned index) const
  478. {
  479. return mContentElement->getChild(index);
  480. }
  481. std::vector<UIElement*> ListView::getItems() const
  482. {
  483. return mContentElement->getChildren();
  484. }
  485. unsigned ListView::getSelection() const
  486. {
  487. if (mSelections.empty())
  488. return M_MAX_UNSIGNED;
  489. else
  490. return *mSelections.begin();
  491. }
  492. UIElement* ListView::getSelectedItem() const
  493. {
  494. return mContentElement->getChild(getSelection());
  495. }
  496. std::vector<UIElement*> ListView::getSelectedItems() const
  497. {
  498. std::vector<UIElement*> ret;
  499. for (std::set<unsigned>::const_iterator i = mSelections.begin(); i != mSelections.end(); ++i)
  500. {
  501. UIElement* item = getItem(*i);
  502. if (item)
  503. ret.push_back(item);
  504. }
  505. return ret;
  506. }
  507. void ListView::updateSelectionEffect()
  508. {
  509. unsigned numItems = getNumItems();
  510. for (unsigned i = 0; i < numItems; ++i)
  511. {
  512. UIElement* item = getItem(i);
  513. if ((mHighlightMode != HM_NEVER) && (mSelections.find(i) != mSelections.end()))
  514. item->setSelected((mFocus) || (mHighlightMode == HM_ALWAYS));
  515. else
  516. item->setSelected(false);
  517. }
  518. }
  519. void ListView::ensureItemVisibility(unsigned index)
  520. {
  521. UIElement* item = getItem(index);
  522. if (!item)
  523. return;
  524. IntVector2 currentOffset = item->getScreenPosition() - mScrollPanel->getScreenPosition() - mContentElement->getPosition();
  525. IntVector2 newView = getViewPosition();
  526. const IntRect& clipBorder = mScrollPanel->getClipBorder();
  527. IntVector2 windowSize(mScrollPanel->getWidth() - clipBorder.mLeft - clipBorder.mRight, mScrollPanel->getHeight() -
  528. clipBorder.mTop - clipBorder.mBottom);
  529. if (currentOffset.mY < 0)
  530. newView.mY += currentOffset.mY;
  531. if (currentOffset.mY + item->getHeight() > windowSize.mY)
  532. newView.mY += currentOffset.mY + item->getHeight() - windowSize.mY;
  533. setViewPosition(newView);
  534. }
  535. void ListView::handleUIMouseClick(StringHash eventType, VariantMap& eventData)
  536. {
  537. if (eventData[UIMouseClick::P_BUTTON].getInt() != MOUSEB_LEFT)
  538. return;
  539. int qualifiers = eventData[UIMouseClick::P_QUALIFIERS].getInt();
  540. UIElement* element = static_cast<UIElement*>(eventData[UIMouseClick::P_ELEMENT].getPtr());
  541. unsigned numItems = getNumItems();
  542. for (unsigned i = 0; i < numItems; ++i)
  543. {
  544. if (element == getItem(i))
  545. {
  546. // Check doubleclick
  547. bool isDoubleClick = false;
  548. if ((!mMultiselect) || (!qualifiers))
  549. {
  550. if ((mDoubleClickTimer > 0.0f) && (mLastClickedItem == i))
  551. {
  552. isDoubleClick = true;
  553. mDoubleClickTimer = 0.0f;
  554. }
  555. else
  556. {
  557. mDoubleClickTimer = mDoubleClickInterval;
  558. mLastClickedItem = i;
  559. }
  560. setSelection(i);
  561. }
  562. // Check multiselect with shift & ctrl
  563. if (mMultiselect)
  564. {
  565. if (qualifiers & QUAL_SHIFT)
  566. {
  567. if (mSelections.empty())
  568. setSelection(i);
  569. else
  570. {
  571. unsigned first = *mSelections.begin();
  572. unsigned last = *mSelections.rbegin();
  573. std::set<unsigned> newSelections = mSelections;
  574. if ((i == first) || (i == last))
  575. {
  576. for (unsigned j = first; j <= last; ++j)
  577. newSelections.insert(j);
  578. }
  579. else if (i < first)
  580. {
  581. for (unsigned j = i; j <= first; ++j)
  582. newSelections.insert(j);
  583. }
  584. else if (i < last)
  585. {
  586. if ((abs((int)i - (int)first)) <= (abs((int)i - (int)last)))
  587. {
  588. for (unsigned j = first; j <= i; ++j)
  589. newSelections.insert(j);
  590. }
  591. else
  592. {
  593. for (unsigned j = i; j <= last; ++j)
  594. newSelections.insert(j);
  595. }
  596. }
  597. else if (i > last)
  598. {
  599. for (unsigned j = last; j <= i; ++j)
  600. newSelections.insert(j);
  601. }
  602. setSelections(newSelections);
  603. }
  604. }
  605. else if (qualifiers & QUAL_CTRL)
  606. toggleSelection(i);
  607. }
  608. if (isDoubleClick)
  609. {
  610. if (mHierarchyMode)
  611. toggleChildItemsVisible(i);
  612. VariantMap eventData;
  613. eventData[ItemDoubleClicked::P_ELEMENT] = (void*)this;
  614. eventData[ItemDoubleClicked::P_SELECTION] = i;
  615. sendEvent(EVENT_ITEMDOUBLECLICKED, eventData);
  616. }
  617. return;
  618. }
  619. }
  620. }