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