ScrollView.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  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 "ScrollBar.h"
  27. #include "ScrollView.h"
  28. #include "UIEvents.h"
  29. #include "DebugNew.h"
  30. ScrollView::ScrollView(const std::string& name) :
  31. UIElement(name),
  32. mViewPosition(IntVector2::sZero),
  33. mViewSize(IntVector2::sZero),
  34. mScrollStep(0.1f),
  35. mPageStep(1.0f)
  36. {
  37. mEnabled = true;
  38. mFocusMode = FM_FOCUSABLE_DEFOCUSABLE;
  39. mHorizontalScrollBar = new ScrollBar();
  40. mHorizontalScrollBar->setAlignment(HA_LEFT, VA_BOTTOM);
  41. mHorizontalScrollBar->setOrientation(O_HORIZONTAL);
  42. mVerticalScrollBar = new ScrollBar();
  43. mVerticalScrollBar->setAlignment(HA_RIGHT, VA_TOP);
  44. mVerticalScrollBar->setOrientation(O_VERTICAL);
  45. mScrollPanel = new BorderImage();
  46. mScrollPanel->setEnabled(true);
  47. mScrollPanel->setClipChildren(true);
  48. addChild(mHorizontalScrollBar);
  49. addChild(mVerticalScrollBar);
  50. addChild(mScrollPanel);
  51. subscribeToEvent(mHorizontalScrollBar, EVENT_SCROLLBARCHANGED, EVENT_HANDLER(ScrollView, handleScrollBarChanged));
  52. subscribeToEvent(mHorizontalScrollBar, EVENT_VISIBLECHANGED, EVENT_HANDLER(ScrollView, handleScrollBarVisibleChanged));
  53. subscribeToEvent(mVerticalScrollBar, EVENT_SCROLLBARCHANGED, EVENT_HANDLER(ScrollView, handleScrollBarChanged));
  54. subscribeToEvent(mVerticalScrollBar, EVENT_VISIBLECHANGED, EVENT_HANDLER(ScrollView, handleScrollBarVisibleChanged));
  55. subscribeToEvent(EVENT_TRYFOCUS, EVENT_HANDLER(ScrollView, handleTryFocus));
  56. }
  57. ScrollView::~ScrollView()
  58. {
  59. }
  60. void ScrollView::setStyle(const XMLElement& element, ResourceCache* cache)
  61. {
  62. UIElement::setStyle(element, cache);
  63. if (element.hasChildElement("viewposition"))
  64. setViewPosition(element.getChildElement("viewposition").getIntVector2("value"));
  65. if (element.hasChildElement("scrollstep"))
  66. setScrollStep(element.getChildElement("scrollstep").getFloat("value"));
  67. if (element.hasChildElement("pagestep"))
  68. setScrollStep(element.getChildElement("pagestep").getFloat("value"));
  69. XMLElement horizElem = element.getChildElement("horizontalscrollbar");
  70. if (horizElem)
  71. mHorizontalScrollBar->setStyle(horizElem, cache);
  72. XMLElement vertElem = element.getChildElement("verticalscrollbar");
  73. if (vertElem)
  74. mVerticalScrollBar->setStyle(vertElem, cache);
  75. XMLElement panelElem = element.getChildElement("scrollpanel");
  76. if (panelElem)
  77. mScrollPanel->setStyle(panelElem, cache);
  78. UIElement* root = getRootElement();
  79. if ((root) && (element.hasChildElement("contentelement")))
  80. setElement(root->getChild(element.getChildElement("contentelement").getString("name"), true));
  81. // Set the scrollbar orientations again and perform size update now that the style is known
  82. mHorizontalScrollBar->setOrientation(O_HORIZONTAL);
  83. mVerticalScrollBar->setOrientation(O_VERTICAL);
  84. onResize();
  85. }
  86. void ScrollView::onKey(int key, int buttons, int qualifiers)
  87. {
  88. switch (key)
  89. {
  90. case KEY_LEFT:
  91. if (mHorizontalScrollBar)
  92. {
  93. if (qualifiers & QUAL_CTRL)
  94. mHorizontalScrollBar->setValue(0.0f);
  95. else
  96. mHorizontalScrollBar->setValue(mHorizontalScrollBar->getValue() - mScrollStep);
  97. }
  98. break;
  99. case KEY_RIGHT:
  100. if (mHorizontalScrollBar)
  101. {
  102. if (qualifiers & QUAL_CTRL)
  103. mHorizontalScrollBar->setValue(mHorizontalScrollBar->getRange());
  104. else
  105. mHorizontalScrollBar->setValue(mHorizontalScrollBar->getValue() + mScrollStep);
  106. }
  107. break;
  108. case KEY_UP:
  109. if (mVerticalScrollBar)
  110. {
  111. if (qualifiers & QUAL_CTRL)
  112. mVerticalScrollBar->setValue(0.0f);
  113. else
  114. mVerticalScrollBar->setValue(mVerticalScrollBar->getValue() - mScrollStep);
  115. }
  116. break;
  117. case KEY_DOWN:
  118. if (mVerticalScrollBar)
  119. {
  120. if (qualifiers & QUAL_CTRL)
  121. mVerticalScrollBar->setValue(mVerticalScrollBar->getRange());
  122. else
  123. mVerticalScrollBar->setValue(mVerticalScrollBar->getValue() + mScrollStep);
  124. }
  125. break;
  126. case KEY_PAGEUP:
  127. if (mVerticalScrollBar)
  128. mVerticalScrollBar->setValue(mVerticalScrollBar->getValue() - mPageStep);
  129. break;
  130. case KEY_PAGEDOWN:
  131. if (mVerticalScrollBar)
  132. mVerticalScrollBar->setValue(mVerticalScrollBar->getValue() + mPageStep);
  133. break;
  134. case KEY_HOME:
  135. if (mVerticalScrollBar)
  136. mVerticalScrollBar->setValue(0.0f);
  137. break;
  138. case KEY_END:
  139. if (mVerticalScrollBar)
  140. mVerticalScrollBar->setValue(mVerticalScrollBar->getRange());
  141. break;
  142. }
  143. }
  144. void ScrollView::onResize()
  145. {
  146. IntVector2 panelSize = getSize();
  147. if (mVerticalScrollBar->isVisible())
  148. panelSize.mX -= mVerticalScrollBar->getWidth();
  149. if (mHorizontalScrollBar->isVisible())
  150. panelSize.mY -= mHorizontalScrollBar->getHeight();
  151. mScrollPanel->setSize(panelSize);
  152. mHorizontalScrollBar->setWidth(mScrollPanel->getWidth());
  153. mVerticalScrollBar->setHeight(mScrollPanel->getHeight());
  154. updateViewSize();
  155. }
  156. void ScrollView::setElement(UIElement* element)
  157. {
  158. if (element == mElement)
  159. return;
  160. if (mElement)
  161. {
  162. mScrollPanel->removeChild(mElement);
  163. unsubscribeFromEvent(mElement, EVENT_RESIZED);
  164. }
  165. mElement = element;
  166. if (mElement)
  167. {
  168. mScrollPanel->addChild(mElement);
  169. subscribeToEvent(mElement, EVENT_RESIZED, EVENT_HANDLER(ScrollView, handleElementResized));
  170. }
  171. updateViewSize();
  172. }
  173. void ScrollView::setViewPosition(const IntVector2& position)
  174. {
  175. updateView(position);
  176. updateScrollBars();
  177. }
  178. void ScrollView::setViewPosition(int x, int y)
  179. {
  180. setViewPosition(IntVector2(x, y));
  181. }
  182. void ScrollView::setScrollBarsVisible(bool horizontal, bool vertical)
  183. {
  184. mHorizontalScrollBar->setVisible(horizontal);
  185. mVerticalScrollBar->setVisible(vertical);
  186. }
  187. void ScrollView::setScrollStep(float step)
  188. {
  189. mScrollStep = max(step, 0.0f);
  190. }
  191. void ScrollView::setPageStep(float step)
  192. {
  193. mPageStep = max(step, 0.0f);
  194. }
  195. bool ScrollView::getHorizontalScrollBarVisible() const
  196. {
  197. return mHorizontalScrollBar->isVisible();
  198. }
  199. bool ScrollView::getVerticalScrollBarVisible() const
  200. {
  201. return mVerticalScrollBar->isVisible();
  202. }
  203. void ScrollView::updateViewSize()
  204. {
  205. IntVector2 size(IntVector2::sZero);
  206. if (mElement)
  207. size = mElement->getSize();
  208. mViewSize.mX = max(size.mX, mScrollPanel->getWidth());
  209. mViewSize.mY = max(size.mY, mScrollPanel->getHeight());
  210. updateView(mViewPosition);
  211. updateScrollBars();
  212. }
  213. void ScrollView::updateScrollBars()
  214. {
  215. mIgnoreEvents = true;
  216. const IntVector2& size = mScrollPanel->getSize();
  217. if ((mHorizontalScrollBar) && (size.mX > 0) && (mViewSize.mX > 0))
  218. {
  219. mHorizontalScrollBar->setRange((float)mViewSize.mX / (float)size.mX - 1.0f);
  220. mHorizontalScrollBar->setValue((float)mViewPosition.mX / (float)size.mX);
  221. }
  222. if ((mVerticalScrollBar) && (size.mY > 0) && (mViewSize.mY > 0))
  223. {
  224. mVerticalScrollBar->setRange((float)mViewSize.mY / (float)size.mY - 1.0f);
  225. mVerticalScrollBar->setValue((float)mViewPosition.mY / (float)size.mY);
  226. }
  227. mIgnoreEvents = false;
  228. }
  229. void ScrollView::updateView(const IntVector2& position)
  230. {
  231. IntVector2 oldPosition = mViewPosition;
  232. mViewPosition.mX = clamp(position.mX, 0, mViewSize.mX - mScrollPanel->getWidth());
  233. mViewPosition.mY = clamp(position.mY, 0, mViewSize.mY - mScrollPanel->getHeight());
  234. mScrollPanel->setChildOffset(-mViewPosition);
  235. if (mViewPosition != oldPosition)
  236. {
  237. using namespace ViewChanged;
  238. VariantMap eventData;
  239. eventData[P_ELEMENT] = (void*)this;
  240. eventData[P_X] = mViewPosition.mX;
  241. eventData[P_Y] = mViewPosition.mY;
  242. sendEvent(EVENT_VIEWCHANGED, eventData);
  243. }
  244. }
  245. void ScrollView::handleScrollBarChanged(StringHash eventType, VariantMap& eventData)
  246. {
  247. if (!mIgnoreEvents)
  248. {
  249. updateView(IntVector2(
  250. (int)(mHorizontalScrollBar->getValue() * (float)mScrollPanel->getWidth()),
  251. (int)(mVerticalScrollBar->getValue() * (float)mScrollPanel->getHeight())
  252. ));
  253. }
  254. }
  255. void ScrollView::handleScrollBarVisibleChanged(StringHash eventType, VariantMap& eventData)
  256. {
  257. // Need to recalculate panel size when scrollbar visibility changes
  258. onResize();
  259. }
  260. void ScrollView::handleElementResized(StringHash eventType, VariantMap& eventData)
  261. {
  262. updateViewSize();
  263. }
  264. void ScrollView::handleTryFocus(StringHash eventType, VariantMap& eventData)
  265. {
  266. using namespace TryFocus;
  267. UIElement* focusElement = static_cast<UIElement*>(eventData[P_ELEMENT].getPtr());
  268. if ((!focusElement) || (focusElement == this))
  269. return;
  270. // If the element is a non-focusable child of the ScrollView, divert focus to the ScrollView
  271. if (focusElement->getFocusMode() < FM_FOCUSABLE)
  272. {
  273. while (focusElement)
  274. {
  275. focusElement = focusElement->getParent();
  276. if (focusElement == this)
  277. {
  278. eventData[P_ELEMENT] = (void*)this;
  279. return;
  280. }
  281. }
  282. }
  283. }