llscrollcontainer.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824
  1. /**
  2. * @file llscrollcontainer.cpp
  3. * @brief LLScrollableContainer base class
  4. *
  5. * $LicenseInfo:firstyear=2001&license=viewergpl$
  6. *
  7. * Copyright (c) 2001-2009, Linden Research, Inc.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file ("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. #include "linden_common.h"
  33. #include "llscrollcontainer.h"
  34. #include "llframetimer.h"
  35. #include "llkeyboard.h"
  36. #include "llrender.h"
  37. #include "lluictrlfactory.h"
  38. #include "llviewborder.h"
  39. static constexpr S32 HORIZONTAL_MULTIPLE = 8;
  40. static constexpr S32 VERTICAL_MULTIPLE = 16;
  41. static constexpr F32 MIN_AUTO_SCROLL_RATE = 120.f;
  42. static constexpr F32 MAX_AUTO_SCROLL_RATE = 500.f;
  43. static constexpr F32 AUTO_SCROLL_RATE_ACCEL = 120.f;
  44. static const std::string LL_SCROLLABLE_CONTAINER_VIEW_TAG = "scroll_container";
  45. static LLRegisterWidget<LLScrollableContainer>
  46. r18(LL_SCROLLABLE_CONTAINER_VIEW_TAG);
  47. // Default constructor
  48. LLScrollableContainer::LLScrollableContainer(const std::string& name,
  49. const LLRect& rect,
  50. LLView* scrolled_view,
  51. bool is_opaque,
  52. const LLColor4& bg_color)
  53. : LLUICtrl(name, rect, false, NULL, NULL),
  54. mScrolledView(scrolled_view),
  55. mIsOpaque(is_opaque),
  56. mBackgroundColor(bg_color),
  57. mReserveScrollCorner(false),
  58. mAutoScrolling(false),
  59. mAutoScrollRate(0.f)
  60. {
  61. if (mScrolledView)
  62. {
  63. addChild(mScrolledView);
  64. }
  65. init();
  66. }
  67. // LLUICtrl constructor
  68. LLScrollableContainer::LLScrollableContainer(const std::string& name,
  69. const LLRect& rect,
  70. LLUICtrl* scrolled_ctrl,
  71. bool is_opaque,
  72. const LLColor4& bg_color)
  73. : LLUICtrl(name, rect, false, NULL, NULL),
  74. mScrolledView(scrolled_ctrl),
  75. mIsOpaque(is_opaque),
  76. mBackgroundColor(bg_color),
  77. mReserveScrollCorner(false),
  78. mAutoScrolling(false),
  79. mAutoScrollRate(0.f)
  80. {
  81. if (scrolled_ctrl)
  82. {
  83. addChild(scrolled_ctrl);
  84. }
  85. init();
  86. }
  87. void LLScrollableContainer::init()
  88. {
  89. LLRect border_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
  90. mBorder = new LLViewBorder("scroll border", border_rect,
  91. LLViewBorder::BEVEL_IN);
  92. addChild(mBorder);
  93. mInnerRect.set(0, getRect().getHeight(), getRect().getWidth(), 0);
  94. mInnerRect.stretch(-getBorderWidth());
  95. LLRect vertical_scroll_rect = mInnerRect;
  96. vertical_scroll_rect.mLeft = vertical_scroll_rect.mRight - SCROLLBAR_SIZE;
  97. mScrollbar[VERTICAL] = new LLScrollbar("scrollable vertical",
  98. vertical_scroll_rect,
  99. LLScrollbar::VERTICAL,
  100. mInnerRect.getHeight(), 0,
  101. mInnerRect.getHeight(), NULL, this,
  102. VERTICAL_MULTIPLE);
  103. addChild(mScrollbar[VERTICAL]);
  104. mScrollbar[VERTICAL]->setVisible(false);
  105. mScrollbar[VERTICAL]->setFollowsRight();
  106. mScrollbar[VERTICAL]->setFollowsTop();
  107. mScrollbar[VERTICAL]->setFollowsBottom();
  108. LLRect horizontal_scroll_rect = mInnerRect;
  109. horizontal_scroll_rect.mTop =
  110. horizontal_scroll_rect.mBottom + SCROLLBAR_SIZE;
  111. mScrollbar[HORIZONTAL] = new LLScrollbar("scrollable horizontal",
  112. horizontal_scroll_rect,
  113. LLScrollbar::HORIZONTAL,
  114. mInnerRect.getWidth(), 0,
  115. mInnerRect.getWidth(), NULL,
  116. this, HORIZONTAL_MULTIPLE);
  117. addChild(mScrollbar[HORIZONTAL]);
  118. mScrollbar[HORIZONTAL]->setVisible(false);
  119. mScrollbar[HORIZONTAL]->setFollowsLeft();
  120. mScrollbar[HORIZONTAL]->setFollowsRight();
  121. setTabStop(false);
  122. }
  123. LLScrollableContainer::~LLScrollableContainer()
  124. {
  125. // mScrolledView and mScrollbar are child views, so the LLView destructor
  126. // takes care of memory deallocation.
  127. for (S32 i = 0; i < SCROLLBAR_COUNT; ++i)
  128. {
  129. mScrollbar[i] = NULL;
  130. }
  131. mScrolledView = NULL;
  132. }
  133. // Internal scrollbar handlers
  134. //virtual
  135. void LLScrollableContainer::scrollHorizontal(S32 new_pos)
  136. {
  137. if (mScrolledView)
  138. {
  139. LLRect doc_rect = mScrolledView->getRect();
  140. S32 old_pos = -(doc_rect.mLeft - mInnerRect.mLeft);
  141. mScrolledView->translate(-(new_pos - old_pos), 0);
  142. }
  143. }
  144. //virtual
  145. void LLScrollableContainer::scrollVertical(S32 new_pos)
  146. {
  147. if (mScrolledView)
  148. {
  149. LLRect doc_rect = mScrolledView->getRect();
  150. S32 old_pos = doc_rect.mTop - mInnerRect.mTop;
  151. mScrolledView->translate(0, new_pos - old_pos);
  152. }
  153. }
  154. // LLView functionality
  155. void LLScrollableContainer::reshape(S32 width, S32 height,
  156. bool called_from_parent)
  157. {
  158. LLUICtrl::reshape(width, height, called_from_parent);
  159. mInnerRect.set(0, getRect().getHeight(), getRect().getWidth(), 0);
  160. mInnerRect.stretch(-getBorderWidth());
  161. if (mScrolledView)
  162. {
  163. const LLRect& scrolled_rect = mScrolledView->getRect();
  164. S32 visible_width = 0;
  165. S32 visible_height = 0;
  166. bool show_v_scrollbar = false;
  167. bool show_h_scrollbar = false;
  168. calcVisibleSize(scrolled_rect, &visible_width, &visible_height,
  169. &show_h_scrollbar, &show_v_scrollbar);
  170. mScrollbar[VERTICAL]->setDocSize(scrolled_rect.getHeight());
  171. mScrollbar[VERTICAL]->setPageSize(visible_height);
  172. mScrollbar[HORIZONTAL]->setDocSize(scrolled_rect.getWidth());
  173. mScrollbar[HORIZONTAL]->setPageSize(visible_width);
  174. updateScroll();
  175. }
  176. }
  177. bool LLScrollableContainer::handleKeyHere(KEY key, MASK mask)
  178. {
  179. for (S32 i = 0; i < SCROLLBAR_COUNT; ++i)
  180. {
  181. if (mScrollbar[i] && mScrollbar[i]->handleKeyHere(key, mask))
  182. {
  183. return true;
  184. }
  185. }
  186. return false;
  187. }
  188. // Note: tries vertical and then horizontal
  189. bool LLScrollableContainer::handleScrollWheel(S32 x, S32 y, S32 clicks)
  190. {
  191. for (S32 i = 0; i < SCROLLBAR_COUNT; ++i)
  192. {
  193. // Pretend the mouse is over the scrollbar
  194. if (mScrollbar[i] && mScrollbar[i]->handleScrollWheel(0, 0, clicks))
  195. {
  196. return true;
  197. }
  198. }
  199. // Eat scroll wheel event (to avoid scrolling nested containers ?)
  200. return true;
  201. }
  202. bool LLScrollableContainer::needsToScroll(S32 x, S32 y,
  203. SCROLL_ORIENTATION axis) const
  204. {
  205. if (mScrollbar[axis] && mScrollbar[axis]->getVisible())
  206. {
  207. LLRect inner_rect_local(0, mInnerRect.getHeight(),
  208. mInnerRect.getWidth(), 0);
  209. constexpr S32 AUTOSCROLL_SIZE = 10;
  210. if (mScrollbar[axis]->getVisible())
  211. {
  212. inner_rect_local.mRight -= SCROLLBAR_SIZE;
  213. inner_rect_local.mTop += AUTOSCROLL_SIZE;
  214. inner_rect_local.mBottom = inner_rect_local.mTop - AUTOSCROLL_SIZE;
  215. }
  216. if (inner_rect_local.pointInRect(x, y) &&
  217. mScrollbar[axis]->getDocPos() > 0)
  218. {
  219. return true;
  220. }
  221. }
  222. return false;
  223. }
  224. bool LLScrollableContainer::handleDragAndDrop(S32 x, S32 y, MASK mask,
  225. bool drop,
  226. EDragAndDropType cargo_type,
  227. void* cargo_data,
  228. EAcceptance* accept,
  229. std::string& tooltip_msg)
  230. {
  231. // Scroll folder view if needed. Never accepts a drag or drop.
  232. *accept = ACCEPT_NO;
  233. bool handled = false;
  234. if (mScrollbar[HORIZONTAL] && mScrollbar[VERTICAL] &&
  235. (mScrollbar[HORIZONTAL]->getVisible() ||
  236. mScrollbar[VERTICAL]->getVisible()))
  237. {
  238. constexpr S32 AUTOSCROLL_SIZE = 10;
  239. S32 auto_scroll_speed =
  240. ll_roundp(mAutoScrollRate * LLFrameTimer::getFrameDeltaTimeF32());
  241. LLRect inner_rect_local(0, mInnerRect.getHeight(),
  242. mInnerRect.getWidth(), 0);
  243. if (mScrollbar[HORIZONTAL]->getVisible())
  244. {
  245. inner_rect_local.mBottom += SCROLLBAR_SIZE;
  246. }
  247. if (mScrollbar[VERTICAL]->getVisible())
  248. {
  249. inner_rect_local.mRight -= SCROLLBAR_SIZE;
  250. }
  251. if (mScrollbar[HORIZONTAL]->getVisible())
  252. {
  253. LLRect left_scroll_rect = inner_rect_local;
  254. left_scroll_rect.mRight = AUTOSCROLL_SIZE;
  255. if (left_scroll_rect.pointInRect(x, y) &&
  256. mScrollbar[HORIZONTAL]->getDocPos() > 0)
  257. {
  258. mScrollbar[HORIZONTAL]->setDocPos(mScrollbar[HORIZONTAL]->getDocPos() -
  259. auto_scroll_speed);
  260. mAutoScrolling = true;
  261. handled = true;
  262. }
  263. LLRect right_scroll_rect = inner_rect_local;
  264. right_scroll_rect.mLeft = inner_rect_local.mRight - AUTOSCROLL_SIZE;
  265. if (right_scroll_rect.pointInRect(x, y) &&
  266. mScrollbar[HORIZONTAL]->getDocPos() <
  267. mScrollbar[HORIZONTAL]->getDocPosMax())
  268. {
  269. mScrollbar[HORIZONTAL]->setDocPos(mScrollbar[HORIZONTAL]->getDocPos() +
  270. auto_scroll_speed);
  271. mAutoScrolling = true;
  272. handled = true;
  273. }
  274. }
  275. if (mScrollbar[VERTICAL]->getVisible())
  276. {
  277. LLRect bottom_scroll_rect = inner_rect_local;
  278. bottom_scroll_rect.mTop = AUTOSCROLL_SIZE + bottom_scroll_rect.mBottom;
  279. if (bottom_scroll_rect.pointInRect(x, y) &&
  280. mScrollbar[VERTICAL]->getDocPos() <
  281. mScrollbar[VERTICAL]->getDocPosMax())
  282. {
  283. mScrollbar[VERTICAL]->setDocPos(mScrollbar[VERTICAL]->getDocPos() +
  284. auto_scroll_speed);
  285. mAutoScrolling = true;
  286. handled = true;
  287. }
  288. LLRect top_scroll_rect = inner_rect_local;
  289. top_scroll_rect.mBottom = inner_rect_local.mTop - AUTOSCROLL_SIZE;
  290. if (top_scroll_rect.pointInRect(x, y) &&
  291. mScrollbar[VERTICAL]->getDocPos() > 0)
  292. {
  293. mScrollbar[VERTICAL]->setDocPos(mScrollbar[VERTICAL]->getDocPos() -
  294. auto_scroll_speed);
  295. mAutoScrolling = true;
  296. handled = true;
  297. }
  298. }
  299. }
  300. if (!handled)
  301. {
  302. handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type,
  303. cargo_data, accept, tooltip_msg) != NULL;
  304. }
  305. return true;
  306. }
  307. bool LLScrollableContainer::handleToolTip(S32 x, S32 y, std::string& msg,
  308. LLRect* sticky_rect)
  309. {
  310. S32 local_x, local_y;
  311. for (S32 i = 0; i < SCROLLBAR_COUNT; ++i)
  312. {
  313. local_x = x - mScrollbar[i]->getRect().mLeft;
  314. local_y = y - mScrollbar[i]->getRect().mBottom;
  315. if (mScrollbar[i] &&
  316. mScrollbar[i]->handleToolTip(local_x, local_y, msg, sticky_rect))
  317. {
  318. return true;
  319. }
  320. }
  321. // Handle 'child' view.
  322. if (mScrolledView)
  323. {
  324. local_x = x - mScrolledView->getRect().mLeft;
  325. local_y = y - mScrolledView->getRect().mBottom;
  326. if (mScrolledView->handleToolTip(local_x, local_y, msg, sticky_rect))
  327. {
  328. return true;
  329. }
  330. }
  331. // Opaque
  332. return true;
  333. }
  334. void LLScrollableContainer::calcVisibleSize(S32* visible_width,
  335. S32* visible_height,
  336. bool* show_h_scrollbar,
  337. bool* show_v_scrollbar) const
  338. {
  339. if (mScrolledView)
  340. {
  341. const LLRect& rect = mScrolledView->getRect();
  342. calcVisibleSize(rect, visible_width, visible_height, show_h_scrollbar,
  343. show_v_scrollbar);
  344. }
  345. }
  346. void LLScrollableContainer::calcVisibleSize(const LLRect& doc_rect,
  347. S32* visible_width,
  348. S32* visible_height,
  349. bool* show_h_scrollbar,
  350. bool* show_v_scrollbar) const
  351. {
  352. S32 doc_width = doc_rect.getWidth();
  353. S32 doc_height = doc_rect.getHeight();
  354. S32 width_delta = 2 * getBorderWidth();
  355. *visible_width = getRect().getWidth() - width_delta;
  356. *visible_height = getRect().getHeight() - width_delta;
  357. *show_v_scrollbar = false;
  358. if (*visible_height < doc_height)
  359. {
  360. *show_v_scrollbar = true;
  361. *visible_width -= SCROLLBAR_SIZE;
  362. }
  363. *show_h_scrollbar = false;
  364. if (*visible_width < doc_width)
  365. {
  366. *show_h_scrollbar = true;
  367. *visible_height -= SCROLLBAR_SIZE;
  368. // Must retest now that visible_height has changed
  369. if (!*show_v_scrollbar && (*visible_height < doc_height))
  370. {
  371. *show_v_scrollbar = true;
  372. *visible_width -= SCROLLBAR_SIZE;
  373. }
  374. }
  375. }
  376. void LLScrollableContainer::draw()
  377. {
  378. if (mAutoScrolling)
  379. {
  380. // Add acceleration to autoscroll
  381. mAutoScrollRate = llmin(mAutoScrollRate +
  382. LLFrameTimer::getFrameDeltaTimeF32() *
  383. AUTO_SCROLL_RATE_ACCEL,
  384. MAX_AUTO_SCROLL_RATE);
  385. }
  386. else
  387. {
  388. // Reset to minimum
  389. mAutoScrollRate = MIN_AUTO_SCROLL_RATE;
  390. }
  391. // Clear this flag to be set on next call to handleDragAndDrop
  392. mAutoScrolling = false;
  393. // Auto-focus when scrollbar active. This allows us to capture user intent
  394. // (i.e. stop automatically scrolling the view/etc).
  395. if (!gFocusMgr.childHasKeyboardFocus(this) &&
  396. (mScrollbar[VERTICAL]->hasMouseCapture() ||
  397. mScrollbar[HORIZONTAL]->hasMouseCapture()))
  398. {
  399. focusFirstItem();
  400. }
  401. // Draw background
  402. if (mIsOpaque)
  403. {
  404. gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
  405. gGL.color4fv(mBackgroundColor.mV);
  406. gl_rect_2d(mInnerRect);
  407. }
  408. // Draw mScrolledViews and update scroll bars. Get a scissor region ready,
  409. // and draw the scrolling view. The scissor region ensures that we don't
  410. // draw outside of the bounds of the rectangle.
  411. if (mScrolledView)
  412. {
  413. updateScroll();
  414. // Draw the scrolled area.
  415. S32 visible_width = 0;
  416. S32 visible_height = 0;
  417. bool show_v_scrollbar = false;
  418. bool show_h_scrollbar = false;
  419. calcVisibleSize(&visible_width, &visible_height, &show_h_scrollbar,
  420. &show_v_scrollbar);
  421. LLLocalClipRect clip(LLRect(mInnerRect.mLeft,
  422. mInnerRect.mBottom +
  423. (show_h_scrollbar ? SCROLLBAR_SIZE : 0) +
  424. visible_height,
  425. visible_width,
  426. mInnerRect.mBottom +
  427. (show_h_scrollbar ? SCROLLBAR_SIZE : 0)));
  428. drawChild(mScrolledView);
  429. }
  430. // Highlight border if a child of this container has keyboard focus
  431. if (mBorder && mBorder->getVisible())
  432. {
  433. mBorder->setKeyboardFocusHighlight(gFocusMgr.childHasKeyboardFocus(this));
  434. }
  435. // Draw all children except mScrolledView
  436. // Note: scrollbars have been adjusted by above drawing code
  437. for (child_list_const_reverse_iter_t child_iter = getChildList()->rbegin(),
  438. rend = getChildList()->rend();
  439. child_iter != rend; ++child_iter)
  440. {
  441. LLView* viewp = *child_iter;
  442. if (!viewp) continue;
  443. if (sDebugRects)
  444. {
  445. ++sDepth;
  446. }
  447. if (viewp != mScrolledView && viewp->getVisible())
  448. {
  449. drawChild(viewp);
  450. }
  451. if (sDebugRects)
  452. {
  453. --sDepth;
  454. }
  455. }
  456. if (sDebugRects)
  457. {
  458. drawDebugRect();
  459. }
  460. }
  461. void LLScrollableContainer::updateScroll()
  462. {
  463. if (!mScrolledView || !mScrollbar[VERTICAL] || !mScrollbar[HORIZONTAL])
  464. {
  465. return;
  466. }
  467. LLRect doc_rect = mScrolledView->getRect();
  468. S32 doc_width = doc_rect.getWidth();
  469. S32 doc_height = doc_rect.getHeight();
  470. S32 visible_width = 0;
  471. S32 visible_height = 0;
  472. bool show_v_scrollbar = false;
  473. bool show_h_scrollbar = false;
  474. calcVisibleSize(doc_rect, &visible_width, &visible_height,
  475. &show_h_scrollbar, &show_v_scrollbar);
  476. S32 border_width = getBorderWidth();
  477. if (show_v_scrollbar)
  478. {
  479. if (doc_rect.mTop < getRect().getHeight() - border_width)
  480. {
  481. mScrolledView->translate(0,
  482. getRect().getHeight() - border_width -
  483. doc_rect.mTop);
  484. }
  485. scrollVertical( mScrollbar[VERTICAL]->getDocPos());
  486. mScrollbar[VERTICAL]->setVisible(true);
  487. S32 v_scrollbar_height = visible_height;
  488. if (!show_h_scrollbar && mReserveScrollCorner)
  489. {
  490. v_scrollbar_height -= SCROLLBAR_SIZE;
  491. }
  492. mScrollbar[VERTICAL]->reshape(SCROLLBAR_SIZE, v_scrollbar_height);
  493. // Make room for the horizontal scrollbar (or not)
  494. S32 v_scrollbar_offset = 0;
  495. if (show_h_scrollbar || mReserveScrollCorner)
  496. {
  497. v_scrollbar_offset = SCROLLBAR_SIZE;
  498. }
  499. LLRect r = mScrollbar[VERTICAL]->getRect();
  500. r.translate(0, mInnerRect.mBottom - r.mBottom + v_scrollbar_offset);
  501. mScrollbar[VERTICAL]->setRect(r);
  502. }
  503. else
  504. {
  505. mScrolledView->translate(0,
  506. getRect().getHeight() - border_width -
  507. doc_rect.mTop);
  508. mScrollbar[VERTICAL]->setVisible(false);
  509. mScrollbar[VERTICAL]->setDocPos(0);
  510. }
  511. if (show_h_scrollbar)
  512. {
  513. if (doc_rect.mLeft > border_width)
  514. {
  515. mScrolledView->translate(border_width - doc_rect.mLeft, 0);
  516. mScrollbar[HORIZONTAL]->setDocPos(0);
  517. }
  518. else
  519. {
  520. scrollHorizontal(mScrollbar[HORIZONTAL]->getDocPos());
  521. }
  522. mScrollbar[HORIZONTAL]->setVisible(true);
  523. S32 h_scrollbar_width = visible_width;
  524. if (!show_v_scrollbar && mReserveScrollCorner)
  525. {
  526. h_scrollbar_width -= SCROLLBAR_SIZE;
  527. }
  528. mScrollbar[HORIZONTAL]->reshape(h_scrollbar_width, SCROLLBAR_SIZE);
  529. }
  530. else
  531. {
  532. mScrolledView->translate(border_width - doc_rect.mLeft, 0);
  533. mScrollbar[HORIZONTAL]->setVisible(false);
  534. mScrollbar[HORIZONTAL]->setDocPos(0);
  535. }
  536. mScrollbar[HORIZONTAL]->setDocSize(doc_width);
  537. mScrollbar[HORIZONTAL]->setPageSize(visible_width);
  538. mScrollbar[VERTICAL]->setDocSize(doc_height);
  539. mScrollbar[VERTICAL]->setPageSize(visible_height);
  540. }
  541. void LLScrollableContainer::setBorderVisible(bool b)
  542. {
  543. if (mBorder)
  544. {
  545. mBorder->setVisible(b);
  546. }
  547. }
  548. LLRect LLScrollableContainer::getContentWindowRect()
  549. {
  550. updateScroll();
  551. LLRect scroller_view_rect;
  552. S32 visible_width = 0;
  553. S32 visible_height = 0;
  554. bool show_h_scrollbar = false;
  555. bool show_v_scrollbar = false;
  556. calcVisibleSize(&visible_width, &visible_height, &show_h_scrollbar,
  557. &show_v_scrollbar);
  558. S32 left = getBorderWidth();
  559. S32 bottom = show_h_scrollbar ? mScrollbar[HORIZONTAL]->getRect().mTop
  560. : left; // = border width
  561. scroller_view_rect.setOriginAndSize(left, bottom, visible_width,
  562. visible_height);
  563. return scroller_view_rect;
  564. }
  565. // Scroll so that as much of rect as possible is showing (where rect is defined
  566. // in the space of scroller view, not scrolled)
  567. void LLScrollableContainer::scrollToShowRect(const LLRect& rect,
  568. const LLCoordGL& offset)
  569. {
  570. if (!mScrolledView || !mScrollbar[VERTICAL] || !mScrollbar[HORIZONTAL])
  571. {
  572. return;
  573. }
  574. S32 visible_width = 0;
  575. S32 visible_height = 0;
  576. bool show_v_scrollbar = false;
  577. bool show_h_scrollbar = false;
  578. const LLRect& scrolled_rect = mScrolledView->getRect();
  579. calcVisibleSize(scrolled_rect, &visible_width, &visible_height,
  580. &show_h_scrollbar, &show_v_scrollbar);
  581. // Cannot be so far left that right side of rect goes off screen, or so far
  582. // right that left side does
  583. S32 horiz_offset = llclamp(offset.mX,
  584. llmin(0, -visible_width + rect.getWidth()), 0);
  585. // Cannot be so high that bottom of rect goes off screen, or so low that
  586. // top does
  587. S32 vert_offset = llclamp(offset.mY, 0,
  588. llmax(0, visible_height - rect.getHeight()));
  589. // Vertical
  590. // 1. First make sure the top is visible
  591. // 2. Then, if possible without hiding the top, make the bottom visible.
  592. S32 vert_pos = mScrollbar[VERTICAL]->getDocPos();
  593. // Find scrollbar position to get top of rect on screen (scrolling up)
  594. S32 top_offset = scrolled_rect.mTop - rect.mTop - vert_offset;
  595. // Find scrollbar position to get bottom of rect on screen (scrolling down)
  596. S32 bottom_offset = vert_offset == 0 ? scrolled_rect.mTop - rect.mBottom -
  597. visible_height
  598. : top_offset;
  599. // Scroll up far enough to see top or scroll down just enough if item is
  600. // bigger than visual area
  601. if (vert_pos >= top_offset || visible_height < rect.getHeight())
  602. {
  603. vert_pos = top_offset;
  604. }
  605. // Else scroll down far enough to see bottom
  606. else if (vert_pos <= bottom_offset)
  607. {
  608. vert_pos = bottom_offset;
  609. }
  610. mScrollbar[VERTICAL]->setDocSize(scrolled_rect.getHeight());
  611. mScrollbar[VERTICAL]->setPageSize(visible_height);
  612. mScrollbar[VERTICAL]->setDocPos(vert_pos);
  613. // Horizontal
  614. // 1. First make sure left side is visible
  615. // 2. Then, if possible without hiding the left side, make the right side
  616. // visible.
  617. S32 horiz_pos = mScrollbar[HORIZONTAL]->getDocPos();
  618. S32 left_offset = rect.mLeft - scrolled_rect.mLeft + horiz_offset;
  619. S32 right_offset = horiz_offset == 0 ? rect.mRight - scrolled_rect.mLeft -
  620. visible_width
  621. : left_offset;
  622. if (horiz_pos >= left_offset || visible_width < rect.getWidth())
  623. {
  624. horiz_pos = left_offset;
  625. }
  626. else if (horiz_pos <= right_offset)
  627. {
  628. horiz_pos = right_offset;
  629. }
  630. mScrollbar[HORIZONTAL]->setDocSize(scrolled_rect.getWidth());
  631. mScrollbar[HORIZONTAL]->setPageSize(visible_width);
  632. mScrollbar[HORIZONTAL]->setDocPos(horiz_pos);
  633. // Propagate scroll to document
  634. updateScroll();
  635. }
  636. void LLScrollableContainer::pageUp(S32 overlap)
  637. {
  638. if (mScrollbar[VERTICAL])
  639. {
  640. mScrollbar[VERTICAL]->pageUp(overlap);
  641. }
  642. }
  643. void LLScrollableContainer::pageDown(S32 overlap)
  644. {
  645. if (mScrollbar[VERTICAL])
  646. {
  647. mScrollbar[VERTICAL]->pageDown(overlap);
  648. }
  649. }
  650. void LLScrollableContainer::goToTop()
  651. {
  652. if (mScrollbar[VERTICAL])
  653. {
  654. mScrollbar[VERTICAL]->setDocPos(0);
  655. }
  656. }
  657. void LLScrollableContainer::goToBottom()
  658. {
  659. if (mScrollbar[VERTICAL])
  660. {
  661. mScrollbar[VERTICAL]->setDocPos(mScrollbar[VERTICAL]->getDocSize());
  662. }
  663. }
  664. S32 LLScrollableContainer::getBorderWidth() const
  665. {
  666. return mBorder ? mBorder->getBorderWidth() : 0;
  667. }
  668. //virtual
  669. const std::string& LLScrollableContainer::getTag() const
  670. {
  671. return LL_SCROLLABLE_CONTAINER_VIEW_TAG;
  672. }
  673. //virtual
  674. LLXMLNodePtr LLScrollableContainer::getXML(bool save_children) const
  675. {
  676. LLXMLNodePtr nodep = LLUICtrl::getXML();
  677. nodep->setName(LL_SCROLLABLE_CONTAINER_VIEW_TAG);
  678. // Attributes
  679. nodep->createChild("opaque", true)->setBoolValue(mIsOpaque);
  680. if (mIsOpaque)
  681. {
  682. nodep->createChild("color", true)->setFloatValue(4,
  683. mBackgroundColor.mV);
  684. }
  685. // Contents
  686. LLXMLNodePtr child_nodep = mScrolledView->getXML();
  687. nodep->addChild(child_nodep);
  688. return nodep;
  689. }
  690. LLView* LLScrollableContainer::fromXML(LLXMLNodePtr nodep, LLView* parentp,
  691. LLUICtrlFactory* factoryp)
  692. {
  693. std::string name(LL_SCROLLABLE_CONTAINER_VIEW_TAG);
  694. nodep->getAttributeString("name", name);
  695. LLRect rect;
  696. createRect(nodep, rect, parentp, LLRect());
  697. bool opaque = false;
  698. nodep->getAttributeBool("opaque", opaque);
  699. LLColor4 color(0.f, 0.f, 0.f, 0.f);
  700. LLUICtrlFactory::getAttributeColor(nodep, "color", color);
  701. // Create the scroll view
  702. LLPanel* panelp = NULL;
  703. LLScrollableContainer* containerp = new LLScrollableContainer(name, rect,
  704. panelp,
  705. opaque,
  706. color);
  707. // Find a child panel to add
  708. for (LLXMLNodePtr childp = nodep->getFirstChild(); childp.notNull();
  709. childp = childp->getNextSibling())
  710. {
  711. LLView* viewp = factoryp->createCtrlWidget(panelp, childp);
  712. if (viewp && viewp->asPanel())
  713. {
  714. if (panelp)
  715. {
  716. llwarns << "Attempting to put multiple panels into a scrollable container view !"
  717. << llendl;
  718. delete viewp;
  719. }
  720. else
  721. {
  722. panelp = viewp->asPanel();
  723. containerp->addChild(panelp);
  724. }
  725. }
  726. }
  727. containerp->mScrolledView = panelp;
  728. return containerp;
  729. }