ListView.cpp 9.9 KB

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