ListView.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  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 ListViewKey;
  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_LISTVIEWKEY, 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. i = mSelections.erase(i);
  311. using namespace ItemSelected;
  312. VariantMap eventData;
  313. eventData[P_ELEMENT] = (void*)this;
  314. eventData[P_SELECTION] = index;
  315. sendEvent(EVENT_ITEMDESELECTED, eventData);
  316. }
  317. else
  318. ++i;
  319. }
  320. // Then add missing items
  321. for (std::set<unsigned>::const_iterator i = indices.begin(); i != indices.end(); ++i)
  322. {
  323. unsigned index = *i;
  324. if (index < numItems)
  325. {
  326. // In singleselect mode, resend the event even for the same selection
  327. if ((mSelections.find(index) == mSelections.end()) || (!mMultiselect))
  328. {
  329. mSelections.insert(*i);
  330. using namespace ItemSelected;
  331. VariantMap eventData;
  332. eventData[P_ELEMENT] = (void*)this;
  333. eventData[P_SELECTION] = *i;
  334. sendEvent(EVENT_ITEMSELECTED, eventData);
  335. }
  336. }
  337. // If no multiselect enabled, allow setting only one item
  338. if (!mMultiselect)
  339. break;
  340. }
  341. updateSelectionEffect();
  342. }
  343. void ListView::addSelection(unsigned index)
  344. {
  345. if (!mMultiselect)
  346. setSelection(index);
  347. else
  348. {
  349. if (index >= getNumItems())
  350. return;
  351. std::set<unsigned> newSelections = mSelections;
  352. newSelections.insert(index);
  353. setSelections(newSelections);
  354. ensureItemVisibility(index);
  355. }
  356. }
  357. void ListView::removeSelection(unsigned index)
  358. {
  359. if (index >= getNumItems())
  360. return;
  361. std::set<unsigned> newSelections = mSelections;
  362. newSelections.erase(index);
  363. setSelections(newSelections);
  364. ensureItemVisibility(index);
  365. }
  366. void ListView::toggleSelection(unsigned index)
  367. {
  368. unsigned numItems = getNumItems();
  369. if (index >= numItems)
  370. return;
  371. if (mSelections.find(index) != mSelections.end())
  372. removeSelection(index);
  373. else
  374. addSelection(index);
  375. }
  376. void ListView::changeSelection(int delta, bool additive)
  377. {
  378. if (mSelections.empty())
  379. return;
  380. if (!mMultiselect)
  381. additive = false;
  382. // If going downwards, use the last selection as a base. Otherwise use first
  383. unsigned selection = delta > 0 ? *mSelections.rbegin() : *mSelections.begin();
  384. unsigned numItems = getNumItems();
  385. unsigned newSelection = selection;
  386. unsigned okSelection = selection;
  387. while (delta != 0)
  388. {
  389. if (delta > 0)
  390. {
  391. ++newSelection;
  392. if (newSelection >= numItems)
  393. break;
  394. }
  395. if (delta < 0)
  396. {
  397. --newSelection;
  398. if (newSelection >= numItems)
  399. break;
  400. }
  401. UIElement* item = getItem(newSelection);
  402. if (item->isVisible())
  403. {
  404. okSelection = newSelection;
  405. if (delta > 0)
  406. --delta;
  407. if (delta < 0)
  408. ++delta;
  409. }
  410. }
  411. if (!additive)
  412. setSelection(okSelection);
  413. else
  414. addSelection(okSelection);
  415. }
  416. void ListView::clearSelection()
  417. {
  418. setSelections(std::set<unsigned>());
  419. updateSelectionEffect();
  420. }
  421. void ListView::setHighlightMode(HighlightMode mode)
  422. {
  423. mHighlightMode = mode;
  424. updateSelectionEffect();
  425. }
  426. void ListView::setMultiselect(bool enable)
  427. {
  428. mMultiselect = enable;
  429. }
  430. void ListView::setHierarchyMode(bool enable)
  431. {
  432. mHierarchyMode = enable;
  433. }
  434. void ListView::setClearSelectionOnDefocus(bool enable)
  435. {
  436. mClearSelectionOnDefocus = enable;
  437. }
  438. void ListView::setDoubleClickInterval(float interval)
  439. {
  440. mDoubleClickInterval = interval;
  441. }
  442. void ListView::setChildItemsVisible(unsigned index, bool enable)
  443. {
  444. unsigned numItems = getNumItems();
  445. if ((!mHierarchyMode) || (index >= numItems))
  446. return;
  447. int baseIndent = getItemIndent(getItem(index));
  448. for (unsigned i = index + 1; i < numItems; ++i)
  449. {
  450. UIElement* item = getItem(i);
  451. if (getItemIndent(item) > baseIndent)
  452. item->setVisible(enable);
  453. else
  454. break;
  455. }
  456. }
  457. void ListView::toggleChildItemsVisible(unsigned index)
  458. {
  459. unsigned numItems = getNumItems();
  460. if ((!mHierarchyMode) || (index >= numItems))
  461. return;
  462. int baseIndent = getItemIndent(getItem(index));
  463. for (unsigned i = index + 1; i < numItems; ++i)
  464. {
  465. UIElement* item = getItem(i);
  466. if (getItemIndent(item) > baseIndent)
  467. item->setVisible(!item->isVisible());
  468. else
  469. break;
  470. }
  471. }
  472. unsigned ListView::getNumItems() const
  473. {
  474. return mContentElement->getNumChildren();
  475. }
  476. UIElement* ListView::getItem(unsigned index) const
  477. {
  478. return mContentElement->getChild(index);
  479. }
  480. std::vector<UIElement*> ListView::getItems() const
  481. {
  482. return mContentElement->getChildren();
  483. }
  484. unsigned ListView::getSelection() const
  485. {
  486. if (mSelections.empty())
  487. return M_MAX_UNSIGNED;
  488. else
  489. return *mSelections.begin();
  490. }
  491. UIElement* ListView::getSelectedItem() const
  492. {
  493. return mContentElement->getChild(getSelection());
  494. }
  495. std::vector<UIElement*> ListView::getSelectedItems() const
  496. {
  497. std::vector<UIElement*> ret;
  498. for (std::set<unsigned>::const_iterator i = mSelections.begin(); i != mSelections.end(); ++i)
  499. {
  500. UIElement* item = getItem(*i);
  501. if (item)
  502. ret.push_back(item);
  503. }
  504. return ret;
  505. }
  506. void ListView::updateSelectionEffect()
  507. {
  508. unsigned numItems = getNumItems();
  509. for (unsigned i = 0; i < numItems; ++i)
  510. {
  511. UIElement* item = getItem(i);
  512. if ((mHighlightMode != HM_NEVER) && (mSelections.find(i) != mSelections.end()))
  513. item->setSelected((mFocus) || (mHighlightMode == HM_ALWAYS));
  514. else
  515. item->setSelected(false);
  516. }
  517. }
  518. void ListView::ensureItemVisibility(unsigned index)
  519. {
  520. UIElement* item = getItem(index);
  521. if (!item)
  522. return;
  523. IntVector2 currentOffset = item->getScreenPosition() - mScrollPanel->getScreenPosition() - mContentElement->getPosition();
  524. IntVector2 newView = getViewPosition();
  525. const IntRect& clipBorder = mScrollPanel->getClipBorder();
  526. IntVector2 windowSize(mScrollPanel->getWidth() - clipBorder.mLeft - clipBorder.mRight, mScrollPanel->getHeight() -
  527. clipBorder.mTop - clipBorder.mBottom);
  528. if (currentOffset.mY < 0)
  529. newView.mY += currentOffset.mY;
  530. if (currentOffset.mY + item->getHeight() > windowSize.mY)
  531. newView.mY += currentOffset.mY + item->getHeight() - windowSize.mY;
  532. setViewPosition(newView);
  533. }
  534. void ListView::handleUIMouseClick(StringHash eventType, VariantMap& eventData)
  535. {
  536. if (eventData[UIMouseClick::P_BUTTON].getInt() != MOUSEB_LEFT)
  537. return;
  538. int qualifiers = eventData[UIMouseClick::P_QUALIFIERS].getInt();
  539. UIElement* element = static_cast<UIElement*>(eventData[UIMouseClick::P_ELEMENT].getPtr());
  540. unsigned numItems = getNumItems();
  541. for (unsigned i = 0; i < numItems; ++i)
  542. {
  543. if (element == getItem(i))
  544. {
  545. // Check doubleclick
  546. bool isDoubleClick = false;
  547. if ((!mMultiselect) || (!qualifiers))
  548. {
  549. if ((mDoubleClickTimer > 0.0f) && (mLastClickedItem == i))
  550. {
  551. isDoubleClick = true;
  552. mDoubleClickTimer = 0.0f;
  553. }
  554. else
  555. {
  556. mDoubleClickTimer = mDoubleClickInterval;
  557. mLastClickedItem = i;
  558. }
  559. setSelection(i);
  560. }
  561. // Check multiselect with shift & ctrl
  562. if (mMultiselect)
  563. {
  564. if (qualifiers & QUAL_SHIFT)
  565. {
  566. if (mSelections.empty())
  567. setSelection(i);
  568. else
  569. {
  570. unsigned first = *mSelections.begin();
  571. unsigned last = *mSelections.rbegin();
  572. std::set<unsigned> newSelections = mSelections;
  573. if ((i == first) || (i == last))
  574. {
  575. for (unsigned j = first; j <= last; ++j)
  576. newSelections.insert(j);
  577. }
  578. else if (i < first)
  579. {
  580. for (unsigned j = i; j <= first; ++j)
  581. newSelections.insert(j);
  582. }
  583. else if (i < last)
  584. {
  585. if ((abs((int)i - (int)first)) <= (abs((int)i - (int)last)))
  586. {
  587. for (unsigned j = first; j <= i; ++j)
  588. newSelections.insert(j);
  589. }
  590. else
  591. {
  592. for (unsigned j = i; j <= last; ++j)
  593. newSelections.insert(j);
  594. }
  595. }
  596. else if (i > last)
  597. {
  598. for (unsigned j = last; j <= i; ++j)
  599. newSelections.insert(j);
  600. }
  601. setSelections(newSelections);
  602. }
  603. }
  604. else if (qualifiers & QUAL_CTRL)
  605. toggleSelection(i);
  606. }
  607. if (isDoubleClick)
  608. {
  609. if (mHierarchyMode)
  610. toggleChildItemsVisible(i);
  611. VariantMap eventData;
  612. eventData[ItemDoubleClicked::P_ELEMENT] = (void*)this;
  613. eventData[ItemDoubleClicked::P_SELECTION] = i;
  614. sendEvent(EVENT_ITEMDOUBLECLICKED, eventData);
  615. }
  616. return;
  617. }
  618. }
  619. }