ListView.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  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 "UIEvents.h"
  28. #include "DebugNew.h"
  29. ListView::ListView(const std::string& name) :
  30. ScrollView(name),
  31. mSelection(M_MAX_UNSIGNED),
  32. mShowSelectionAlways(false)
  33. {
  34. UIElement* container = new UIElement();
  35. container->setEnabled(true);
  36. container->setLayout(LM_VERTICAL);
  37. setContentElement(container);
  38. subscribeToEvent(EVENT_TRYFOCUS, EVENT_HANDLER(ListView, handleTryFocus));
  39. }
  40. ListView::~ListView()
  41. {
  42. }
  43. void ListView::setStyle(const XMLElement& element, ResourceCache* cache)
  44. {
  45. ScrollView::setStyle(element, cache);
  46. UIElement* root = getRootElement();
  47. XMLElement itemElem = element.getChildElement("listitem");
  48. if (root)
  49. {
  50. while (itemElem)
  51. {
  52. if (itemElem.hasAttribute("name"))
  53. addItem(root->getChild(itemElem.getString("name"), true));
  54. itemElem = itemElem.getNextElement("listitem");
  55. }
  56. }
  57. if (element.hasChildElement("selection"))
  58. setSelection(element.getChildElement("selection").getInt("value"));
  59. if (element.hasChildElement("showselectionalways"))
  60. setShowSelectionAlways(element.getChildElement("showselectionalways").getBool("enable"));
  61. }
  62. void ListView::onWheel(int delta, int buttons, int qualifiers)
  63. {
  64. if (delta > 0)
  65. changeSelection(-1);
  66. if (delta < 0)
  67. changeSelection(1);
  68. }
  69. void ListView::onKey(int key, int buttons, int qualifiers)
  70. {
  71. // If no selection, can not move with keys
  72. if ((mSelection == M_MAX_UNSIGNED) || (!getNumItems()))
  73. return;
  74. switch (key)
  75. {
  76. case KEY_UP:
  77. if (qualifiers & QUAL_CTRL)
  78. setSelection(0);
  79. else
  80. changeSelection(-1);
  81. break;
  82. case KEY_DOWN:
  83. if (qualifiers & QUAL_CTRL)
  84. setSelection(getNumItems() - 1);
  85. else
  86. changeSelection(1);
  87. break;
  88. case KEY_PAGEUP:
  89. {
  90. // Convert page step to pixels and see how many items we have to skip to reach that many pixels
  91. int stepPixels = ((int)(mPageStep * mScrollPanel->getHeight())) - getSelectedItem()->getHeight();
  92. unsigned newSelection = mSelection;
  93. while (newSelection)
  94. {
  95. int height = getItem(newSelection)->getHeight();
  96. if (stepPixels < height)
  97. break;
  98. stepPixels -= height;
  99. --newSelection;
  100. }
  101. setSelection(newSelection);
  102. }
  103. break;
  104. case KEY_PAGEDOWN:
  105. {
  106. int stepPixels = ((int)(mPageStep * mScrollPanel->getHeight())) - getSelectedItem()->getHeight();
  107. unsigned newSelection = mSelection;
  108. while (newSelection < getNumItems() - 1)
  109. {
  110. int height = getItem(newSelection)->getHeight();
  111. if (stepPixels < height)
  112. break;
  113. stepPixels -= height;
  114. ++newSelection;
  115. }
  116. setSelection(newSelection);
  117. }
  118. break;
  119. case KEY_HOME:
  120. setSelection(0);
  121. break;
  122. case KEY_END:
  123. setSelection(getNumItems() - 1);
  124. break;
  125. }
  126. }
  127. void ListView::onResize()
  128. {
  129. ScrollView::onResize();
  130. // Set the content element width to match the scrollpanel
  131. const IntRect& clipBorder = mScrollPanel->getClipBorder();
  132. mContentElement->setWidth(mScrollPanel->getWidth() - clipBorder.mLeft - clipBorder.mRight);
  133. }
  134. void ListView::onFocus()
  135. {
  136. updateSelectionEffect();
  137. }
  138. void ListView::onDefocus()
  139. {
  140. updateSelectionEffect();
  141. }
  142. void ListView::addItem(UIElement* item)
  143. {
  144. if ((!item) || (item->getParent() == mContentElement))
  145. return;
  146. // Enable input so that clicking the item can be detected
  147. item->setEnabled(true);
  148. mContentElement->addChild(item);
  149. }
  150. void ListView::removeItem(UIElement* item)
  151. {
  152. std::vector<UIElement*> items = mContentElement->getChildren();
  153. for (unsigned i = 0; i < items.size(); ++i)
  154. {
  155. if (items[i] == item)
  156. {
  157. if (mSelection == i)
  158. clearSelection();
  159. else if (mSelection > i)
  160. changeSelection(-1);
  161. break;
  162. }
  163. }
  164. mContentElement->removeChild(item);
  165. }
  166. void ListView::removeItem(unsigned index)
  167. {
  168. if (index >= getNumItems())
  169. return;
  170. UIElement* item = mContentElement->getChild(index);
  171. if (mSelection == index)
  172. clearSelection();
  173. else if (mSelection > index)
  174. changeSelection(-1);
  175. mContentElement->removeChild(item);
  176. }
  177. void ListView::removeAllItems()
  178. {
  179. mContentElement->removeAllChildren();
  180. clearSelection();
  181. }
  182. void ListView::setSelection(unsigned index)
  183. {
  184. if (index >= getNumItems())
  185. index = M_MAX_UNSIGNED;
  186. bool changed = index != mSelection;
  187. mSelection = index;
  188. updateSelectionEffect();
  189. ensureItemVisibility();
  190. if (changed)
  191. {
  192. using namespace ItemSelected;
  193. VariantMap eventData;
  194. eventData[P_ELEMENT] = (void*)this;
  195. eventData[P_SELECTION] = mSelection;
  196. sendEvent(EVENT_ITEMSELECTED, eventData);
  197. }
  198. }
  199. void ListView::changeSelection(int delta)
  200. {
  201. if (mSelection == M_MAX_UNSIGNED)
  202. return;
  203. int newSelection = clamp((int)mSelection + delta, 0, (int)getNumItems() - 1);
  204. setSelection(newSelection);
  205. }
  206. void ListView::clearSelection()
  207. {
  208. setSelection(M_MAX_UNSIGNED);
  209. }
  210. void ListView::setShowSelectionAlways(bool enable)
  211. {
  212. mShowSelectionAlways = enable;
  213. }
  214. unsigned ListView::getNumItems() const
  215. {
  216. return mContentElement->getNumChildren();
  217. }
  218. UIElement* ListView::getItem(unsigned index) const
  219. {
  220. return mContentElement->getChild(index);
  221. }
  222. std::vector<UIElement*> ListView::getItems() const
  223. {
  224. return mContentElement->getChildren();
  225. }
  226. UIElement* ListView::getSelectedItem() const
  227. {
  228. return mContentElement->getChild(mSelection);
  229. }
  230. void ListView::updateSelectionEffect()
  231. {
  232. unsigned numItems = getNumItems();
  233. for (unsigned i = 0; i < numItems; ++i)
  234. getItem(i)->setSelected((i == mSelection) && ((mFocus) || (mShowSelectionAlways)));
  235. }
  236. void ListView::ensureItemVisibility()
  237. {
  238. UIElement* selected = getSelectedItem();
  239. if (!selected)
  240. return;
  241. IntVector2 currentOffset = selected->getScreenPosition() - mScrollPanel->getScreenPosition() - mContentElement->getPosition();
  242. IntVector2 newView = getViewPosition();
  243. const IntRect& clipBorder = mScrollPanel->getClipBorder();
  244. IntVector2 windowSize(mScrollPanel->getWidth() - clipBorder.mLeft - clipBorder.mRight, mScrollPanel->getHeight() -
  245. clipBorder.mTop - clipBorder.mBottom);
  246. if (currentOffset.mX < 0)
  247. newView.mX += currentOffset.mX;
  248. if (currentOffset.mX + selected->getWidth() > windowSize.mX)
  249. newView.mX += currentOffset.mX + selected->getWidth() - windowSize.mX;
  250. if (currentOffset.mY < 0)
  251. newView.mY += currentOffset.mY;
  252. if (currentOffset.mY + selected->getHeight() > windowSize.mY)
  253. newView.mY += currentOffset.mY + selected->getHeight() - windowSize.mY;
  254. setViewPosition(newView);
  255. }
  256. void ListView::handleTryFocus(StringHash eventType, VariantMap& eventData)
  257. {
  258. using namespace TryFocus;
  259. UIElement* focusElement = static_cast<UIElement*>(eventData[P_ELEMENT].getPtr());
  260. unsigned numItems = getNumItems();
  261. for (unsigned i = 0; i < numItems; ++i)
  262. {
  263. if (focusElement == getItem(i))
  264. {
  265. setSelection(i);
  266. return;
  267. }
  268. }
  269. }