PageRenderTime 30ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/indra/newview/llexpandabletextbox.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 449 lines | 306 code | 72 blank | 71 comment | 25 complexity | 790644e5adf14b20b7a67dd906dd573d MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llexpandabletextbox.cpp
  3. * @brief LLExpandableTextBox and related class implementations
  4. *
  5. * $LicenseInfo:firstyear=2004&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "llviewerprecompiledheaders.h"
  27. #include "llexpandabletextbox.h"
  28. #include "llscrollcontainer.h"
  29. #include "lltrans.h"
  30. #include "llwindow.h"
  31. #include "llviewerwindow.h"
  32. static LLDefaultChildRegistry::Register<LLExpandableTextBox> t1("expandable_text");
  33. class LLExpanderSegment : public LLTextSegment
  34. {
  35. public:
  36. LLExpanderSegment(const LLStyleSP& style, S32 start, S32 end, const std::string& more_text, LLTextBase& editor )
  37. : LLTextSegment(start, end),
  38. mEditor(editor),
  39. mStyle(style),
  40. mExpanderLabel(more_text)
  41. {}
  42. /*virtual*/ bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const
  43. {
  44. // more label always spans width of text box
  45. if (num_chars == 0)
  46. {
  47. width = 0;
  48. height = 0;
  49. }
  50. else
  51. {
  52. width = mEditor.getDocumentView()->getRect().getWidth() - mEditor.getHPad();
  53. height = llceil(mStyle->getFont()->getLineHeight());
  54. }
  55. return true;
  56. }
  57. /*virtual*/ S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const
  58. {
  59. return start_offset;
  60. }
  61. /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const
  62. {
  63. // require full line to ourselves
  64. if (line_offset == 0)
  65. {
  66. // print all our text
  67. return getEnd() - getStart();
  68. }
  69. else
  70. {
  71. // wait for next line
  72. return 0;
  73. }
  74. }
  75. /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect)
  76. {
  77. F32 right_x;
  78. mStyle->getFont()->renderUTF8(mExpanderLabel, start,
  79. draw_rect.mRight, draw_rect.mTop,
  80. mStyle->getColor(),
  81. LLFontGL::RIGHT, LLFontGL::TOP,
  82. 0,
  83. mStyle->getShadowType(),
  84. end - start, draw_rect.getWidth(),
  85. &right_x,
  86. mEditor.getUseEllipses());
  87. return right_x;
  88. }
  89. /*virtual*/ bool canEdit() const { return false; }
  90. // eat handleMouseDown event so we get the mouseup event
  91. /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask) { return TRUE; }
  92. /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask) { mEditor.onCommit(); return TRUE; }
  93. /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask)
  94. {
  95. LLUI::getWindow()->setCursor(UI_CURSOR_HAND);
  96. return TRUE;
  97. }
  98. private:
  99. LLTextBase& mEditor;
  100. LLStyleSP mStyle;
  101. std::string mExpanderLabel;
  102. };
  103. LLExpandableTextBox::LLTextBoxEx::Params::Params()
  104. {
  105. }
  106. LLExpandableTextBox::LLTextBoxEx::LLTextBoxEx(const Params& p)
  107. : LLTextEditor(p),
  108. mExpanderLabel(p.label.isProvided() ? p.label : LLTrans::getString("More")),
  109. mExpanderVisible(false)
  110. {
  111. setIsChrome(TRUE);
  112. }
  113. void LLExpandableTextBox::LLTextBoxEx::reshape(S32 width, S32 height, BOOL called_from_parent)
  114. {
  115. LLTextEditor::reshape(width, height, called_from_parent);
  116. hideOrShowExpandTextAsNeeded();
  117. }
  118. void LLExpandableTextBox::LLTextBoxEx::setText(const LLStringExplicit& text,const LLStyle::Params& input_params)
  119. {
  120. // LLTextBox::setText will obliterate the expander segment, so make sure
  121. // we generate it again by clearing mExpanderVisible
  122. mExpanderVisible = false;
  123. LLTextEditor::setText(text, input_params);
  124. hideOrShowExpandTextAsNeeded();
  125. }
  126. void LLExpandableTextBox::LLTextBoxEx::showExpandText()
  127. {
  128. if (!mExpanderVisible)
  129. {
  130. // make sure we're scrolled to top when collapsing
  131. if (mScroller)
  132. {
  133. mScroller->goToTop();
  134. }
  135. // get fully visible lines
  136. std::pair<S32, S32> visible_lines = getVisibleLines(true);
  137. S32 last_line = visible_lines.second - 1;
  138. LLStyle::Params expander_style(getDefaultStyleParams());
  139. expander_style.font.style = "UNDERLINE";
  140. expander_style.color = LLUIColorTable::instance().getColor("HTMLLinkColor");
  141. LLExpanderSegment* expanderp = new LLExpanderSegment(new LLStyle(expander_style), getLineStart(last_line), getLength() + 1, mExpanderLabel, *this);
  142. insertSegment(expanderp);
  143. mExpanderVisible = true;
  144. }
  145. }
  146. //NOTE: obliterates existing styles (including hyperlinks)
  147. void LLExpandableTextBox::LLTextBoxEx::hideExpandText()
  148. {
  149. if (mExpanderVisible)
  150. {
  151. // this will overwrite the expander segment and all text styling with a single style
  152. LLStyleConstSP sp(new LLStyle(getDefaultStyleParams()));
  153. LLNormalTextSegment* segmentp = new LLNormalTextSegment(sp, 0, getLength() + 1, *this);
  154. insertSegment(segmentp);
  155. mExpanderVisible = false;
  156. }
  157. }
  158. S32 LLExpandableTextBox::LLTextBoxEx::getVerticalTextDelta()
  159. {
  160. S32 text_height = getTextPixelHeight();
  161. S32 textbox_height = getRect().getHeight();
  162. return text_height - textbox_height;
  163. }
  164. S32 LLExpandableTextBox::LLTextBoxEx::getTextPixelHeight()
  165. {
  166. return getTextBoundingRect().getHeight();
  167. }
  168. void LLExpandableTextBox::LLTextBoxEx::hideOrShowExpandTextAsNeeded()
  169. {
  170. // Restore the text box contents to calculate the text height properly,
  171. // otherwise if a part of the text is hidden under "More" link
  172. // getTextPixelHeight() returns only the height of currently visible text
  173. // including the "More" link. See STORM-250.
  174. hideExpandText();
  175. // Show the expander a.k.a. "More" link if we need it, depending on text
  176. // contents height. If not, keep it hidden.
  177. if (getTextPixelHeight() > getRect().getHeight())
  178. {
  179. showExpandText();
  180. }
  181. }
  182. //////////////////////////////////////////////////////////////////////////
  183. //////////////////////////////////////////////////////////////////////////
  184. //////////////////////////////////////////////////////////////////////////
  185. LLExpandableTextBox::Params::Params()
  186. : textbox("textbox"),
  187. scroll("scroll"),
  188. max_height("max_height", 0),
  189. bg_visible("bg_visible", false),
  190. expanded_bg_visible("expanded_bg_visible", true),
  191. bg_color("bg_color", LLColor4::black),
  192. expanded_bg_color("expanded_bg_color", LLColor4::black)
  193. {
  194. }
  195. LLExpandableTextBox::LLExpandableTextBox(const Params& p)
  196. : LLUICtrl(p),
  197. mMaxHeight(p.max_height),
  198. mBGVisible(p.bg_visible),
  199. mExpandedBGVisible(p.expanded_bg_visible),
  200. mBGColor(p.bg_color),
  201. mExpandedBGColor(p.expanded_bg_color),
  202. mExpanded(false)
  203. {
  204. LLRect rc = getLocalRect();
  205. LLScrollContainer::Params scroll_params = p.scroll;
  206. scroll_params.rect(rc);
  207. mScroll = LLUICtrlFactory::create<LLScrollContainer>(scroll_params);
  208. addChild(mScroll);
  209. LLTextBoxEx::Params textbox_params = p.textbox;
  210. textbox_params.rect(rc);
  211. mTextBox = LLUICtrlFactory::create<LLTextBoxEx>(textbox_params);
  212. mScroll->addChild(mTextBox);
  213. updateTextBoxRect();
  214. mTextBox->setCommitCallback(boost::bind(&LLExpandableTextBox::onExpandClicked, this));
  215. }
  216. void LLExpandableTextBox::draw()
  217. {
  218. if(mBGVisible && !mExpanded)
  219. {
  220. gl_rect_2d(getLocalRect(), mBGColor.get(), TRUE);
  221. }
  222. if(mExpandedBGVisible && mExpanded)
  223. {
  224. gl_rect_2d(getLocalRect(), mExpandedBGColor.get(), TRUE);
  225. }
  226. collapseIfPosChanged();
  227. LLUICtrl::draw();
  228. }
  229. void LLExpandableTextBox::collapseIfPosChanged()
  230. {
  231. if(mExpanded)
  232. {
  233. LLView* parentp = getParent();
  234. LLRect parent_rect = parentp->getRect();
  235. parentp->localRectToOtherView(parent_rect, &parent_rect, getRootView());
  236. if(parent_rect.mLeft != mParentRect.mLeft
  237. || parent_rect.mTop != mParentRect.mTop)
  238. {
  239. collapseTextBox();
  240. }
  241. }
  242. }
  243. void LLExpandableTextBox::onExpandClicked()
  244. {
  245. expandTextBox();
  246. }
  247. void LLExpandableTextBox::updateTextBoxRect()
  248. {
  249. LLRect rc = getLocalRect();
  250. rc.mLeft += mScroll->getBorderWidth();
  251. rc.mRight -= mScroll->getBorderWidth();
  252. rc.mTop -= mScroll->getBorderWidth();
  253. rc.mBottom += mScroll->getBorderWidth();
  254. mTextBox->reshape(rc.getWidth(), rc.getHeight());
  255. mTextBox->setRect(rc);
  256. }
  257. S32 LLExpandableTextBox::recalculateTextDelta(S32 text_delta)
  258. {
  259. LLRect expanded_rect = getLocalRect();
  260. LLView* root_view = getRootView();
  261. LLRect window_rect = root_view->getRect();
  262. LLRect expanded_screen_rect;
  263. localRectToOtherView(expanded_rect, &expanded_screen_rect, root_view);
  264. // don't allow expanded text box bottom go off screen
  265. if(expanded_screen_rect.mBottom - text_delta < window_rect.mBottom)
  266. {
  267. text_delta = expanded_screen_rect.mBottom - window_rect.mBottom;
  268. }
  269. // show scroll bar if max_height is valid
  270. // and expanded size is greater that max_height
  271. else if(mMaxHeight > 0 && expanded_rect.getHeight() + text_delta > mMaxHeight)
  272. {
  273. text_delta = mMaxHeight - expanded_rect.getHeight();
  274. }
  275. return text_delta;
  276. }
  277. void LLExpandableTextBox::expandTextBox()
  278. {
  279. // hide "more" link, and show full text contents
  280. mTextBox->hideExpandText();
  281. // *HACK dz
  282. // hideExpandText brakes text styles (replaces hyper-links with plain text), see ticket EXT-3290
  283. // Set text again to make text box re-apply styles.
  284. // *TODO Find proper solution to fix this issue.
  285. // Maybe add removeSegment to LLTextBase
  286. mTextBox->setTextBase(mText);
  287. S32 text_delta = mTextBox->getVerticalTextDelta();
  288. text_delta += mTextBox->getVPad() * 2;
  289. text_delta += mScroll->getBorderWidth() * 2;
  290. // no need to expand
  291. if(text_delta <= 0)
  292. {
  293. return;
  294. }
  295. saveCollapsedState();
  296. LLRect expanded_rect = getLocalRect();
  297. LLRect expanded_screen_rect;
  298. S32 updated_text_delta = recalculateTextDelta(text_delta);
  299. // actual expand
  300. expanded_rect.mBottom -= updated_text_delta;
  301. LLRect text_box_rect = mTextBox->getRect();
  302. // check if we need to show scrollbar
  303. if(text_delta != updated_text_delta)
  304. {
  305. static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
  306. // disable horizontal scrollbar
  307. text_box_rect.mRight -= scrollbar_size;
  308. // text box size has changed - redo text wrap
  309. // Should be handled automatically in reshape() below. JC
  310. //mTextBox->setWrappedText(mText, text_box_rect.getWidth());
  311. // recalculate text delta since text wrap changed text height
  312. text_delta = mTextBox->getVerticalTextDelta() + mTextBox->getVPad() * 2;
  313. }
  314. // expand text
  315. text_box_rect.mBottom -= text_delta;
  316. mTextBox->reshape(text_box_rect.getWidth(), text_box_rect.getHeight());
  317. mTextBox->setRect(text_box_rect);
  318. // expand text box
  319. localRectToOtherView(expanded_rect, &expanded_screen_rect, getParent());
  320. reshape(expanded_screen_rect.getWidth(), expanded_screen_rect.getHeight(), FALSE);
  321. setRect(expanded_screen_rect);
  322. setFocus(TRUE);
  323. // this lets us receive top_lost event(needed to collapse text box)
  324. // it also draws text box above all other ui elements
  325. gViewerWindow->addPopup(this);
  326. mExpanded = true;
  327. }
  328. void LLExpandableTextBox::collapseTextBox()
  329. {
  330. if(!mExpanded)
  331. {
  332. return;
  333. }
  334. mExpanded = false;
  335. reshape(mCollapsedRect.getWidth(), mCollapsedRect.getHeight(), FALSE);
  336. setRect(mCollapsedRect);
  337. updateTextBoxRect();
  338. gViewerWindow->removePopup(this);
  339. }
  340. void LLExpandableTextBox::onFocusLost()
  341. {
  342. collapseTextBox();
  343. LLUICtrl::onFocusLost();
  344. }
  345. void LLExpandableTextBox::onTopLost()
  346. {
  347. collapseTextBox();
  348. LLUICtrl::onTopLost();
  349. }
  350. void LLExpandableTextBox::updateTextShape()
  351. {
  352. // I guess this should be done on every reshape(),
  353. // but adding this code to reshape() currently triggers bug VWR-26455,
  354. // which makes the text virtually unreadable.
  355. llassert(!mExpanded);
  356. updateTextBoxRect();
  357. }
  358. void LLExpandableTextBox::setValue(const LLSD& value)
  359. {
  360. collapseTextBox();
  361. mText = value.asString();
  362. mTextBox->setValue(value);
  363. }
  364. void LLExpandableTextBox::setText(const std::string& str)
  365. {
  366. collapseTextBox();
  367. mText = str;
  368. mTextBox->setText(str);
  369. }
  370. void LLExpandableTextBox::saveCollapsedState()
  371. {
  372. mCollapsedRect = getRect();
  373. mParentRect = getParent()->getRect();
  374. // convert parent rect to screen coordinates,
  375. // this will allow to track parent's position change
  376. getParent()->localRectToOtherView(mParentRect, &mParentRect, getRootView());
  377. }