/indra/llui/lltooltip.cpp

https://bitbucket.org/lindenlab/viewer-beta/ · C++ · 590 lines · 410 code · 92 blank · 88 comment · 51 complexity · 98c6f6f41d3b319f8af43a9938be2d77 MD5 · raw file

  1. /**
  2. * @file lltooltip.cpp
  3. * @brief LLToolTipMgr class implementation and related classes
  4. *
  5. * $LicenseInfo:firstyear=2001&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 "linden_common.h"
  27. // self include
  28. #include "lltooltip.h"
  29. // Library includes
  30. #include "lltextbox.h"
  31. #include "lliconctrl.h"
  32. #include "llbutton.h"
  33. #include "llmenugl.h" // hideMenus()
  34. #include "llui.h" // positionViewNearMouse()
  35. #include "llwindow.h"
  36. #include "lltrans.h"
  37. //
  38. // Constants
  39. //
  40. //
  41. // Local globals
  42. //
  43. LLToolTipView *gToolTipView = NULL;
  44. //
  45. // Member functions
  46. //
  47. static LLDefaultChildRegistry::Register<LLToolTipView> register_tooltip_view("tooltip_view");
  48. LLToolTipView::Params::Params()
  49. {
  50. changeDefault(mouse_opaque, false);
  51. }
  52. LLToolTipView::LLToolTipView(const LLToolTipView::Params& p)
  53. : LLView(p)
  54. {
  55. }
  56. void LLToolTipView::draw()
  57. {
  58. LLToolTipMgr::instance().updateToolTipVisibility();
  59. // do the usual thing
  60. LLView::draw();
  61. }
  62. BOOL LLToolTipView::handleHover(S32 x, S32 y, MASK mask)
  63. {
  64. static S32 last_x = x;
  65. static S32 last_y = y;
  66. LLToolTipMgr& tooltip_mgr = LLToolTipMgr::instance();
  67. if (x != last_x && y != last_y && !tooltip_mgr.getMouseNearRect().pointInRect(x, y))
  68. {
  69. // allow new tooltips because mouse moved outside of mouse near rect
  70. tooltip_mgr.unblockToolTips();
  71. }
  72. last_x = x;
  73. last_y = y;
  74. return LLView::handleHover(x, y, mask);
  75. }
  76. BOOL LLToolTipView::handleMouseDown(S32 x, S32 y, MASK mask)
  77. {
  78. LLToolTipMgr::instance().blockToolTips();
  79. if (LLView::handleMouseDown(x, y, mask))
  80. {
  81. // If we are handling the mouse event menu holder
  82. // won't get a chance to close menus so do this here
  83. LLMenuGL::sMenuContainer->hideMenus();
  84. return TRUE;
  85. }
  86. return FALSE;
  87. }
  88. BOOL LLToolTipView::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
  89. {
  90. LLToolTipMgr::instance().blockToolTips();
  91. return LLView::handleMiddleMouseDown(x, y, mask);
  92. }
  93. BOOL LLToolTipView::handleRightMouseDown(S32 x, S32 y, MASK mask)
  94. {
  95. LLToolTipMgr::instance().blockToolTips();
  96. return LLView::handleRightMouseDown(x, y, mask);
  97. }
  98. BOOL LLToolTipView::handleScrollWheel( S32 x, S32 y, S32 clicks )
  99. {
  100. LLToolTipMgr::instance().blockToolTips();
  101. return FALSE;
  102. }
  103. void LLToolTipView::drawStickyRect()
  104. {
  105. gl_rect_2d(LLToolTipMgr::instance().getMouseNearRect(), LLColor4::white, false);
  106. }
  107. // defaults for floater param block pulled from widgets/floater.xml
  108. static LLWidgetNameRegistry::StaticRegistrar sRegisterInspectorParams(&typeid(LLInspector::Params), "inspector");
  109. //
  110. // LLToolTip
  111. //
  112. static LLDefaultChildRegistry::Register<LLToolTip> register_tooltip("tool_tip");
  113. LLToolTip::Params::Params()
  114. : max_width("max_width", 200),
  115. padding("padding", 4),
  116. wrap("wrap", true),
  117. pos("pos"),
  118. message("message"),
  119. delay_time("delay_time", LLUI::sSettingGroups["config"]->getF32( "ToolTipDelay" )),
  120. visible_time_over("visible_time_over", LLUI::sSettingGroups["config"]->getF32( "ToolTipVisibleTimeOver" )),
  121. visible_time_near("visible_time_near", LLUI::sSettingGroups["config"]->getF32( "ToolTipVisibleTimeNear" )),
  122. visible_time_far("visible_time_far", LLUI::sSettingGroups["config"]->getF32( "ToolTipVisibleTimeFar" )),
  123. sticky_rect("sticky_rect"),
  124. image("image"),
  125. text_color("text_color"),
  126. time_based_media("time_based_media", false),
  127. web_based_media("web_based_media", false),
  128. media_playing("media_playing", false)
  129. {
  130. changeDefault(chrome, true);
  131. }
  132. LLToolTip::LLToolTip(const LLToolTip::Params& p)
  133. : LLPanel(p),
  134. mHasClickCallback(p.click_callback.isProvided()),
  135. mPadding(p.padding),
  136. mTextBox(NULL),
  137. mInfoButton(NULL),
  138. mPlayMediaButton(NULL),
  139. mHomePageButton(NULL)
  140. {
  141. LLTextBox::Params params;
  142. params.name = params.initial_value().asString();
  143. // bake textbox padding into initial rect
  144. params.rect = LLRect (mPadding, mPadding + 1, mPadding + 1, mPadding);
  145. params.h_pad = 0;
  146. params.v_pad = 0;
  147. params.mouse_opaque = false;
  148. params.text_color = p.text_color;
  149. params.bg_visible = false;
  150. params.font = p.font;
  151. params.use_ellipses = true;
  152. params.wrap = p.wrap;
  153. params.parse_urls = false; // disallow hyperlinks in tooltips, as they want to spawn their own explanatory tooltips
  154. mTextBox = LLUICtrlFactory::create<LLTextBox> (params);
  155. addChild(mTextBox);
  156. S32 TOOLTIP_ICON_SIZE = 0;
  157. S32 TOOLTIP_PLAYBUTTON_SIZE = 0;
  158. if (p.image.isProvided())
  159. {
  160. LLButton::Params icon_params;
  161. icon_params.name = "tooltip_info";
  162. icon_params.label(""); // provid label but set to empty so name does not overwrite it -angela
  163. LLRect icon_rect;
  164. LLUIImage* imagep = p.image;
  165. TOOLTIP_ICON_SIZE = (imagep ? imagep->getWidth() : 16);
  166. icon_rect.setOriginAndSize(mPadding, mPadding, TOOLTIP_ICON_SIZE, TOOLTIP_ICON_SIZE);
  167. icon_params.rect = icon_rect;
  168. icon_params.image_unselected(imagep);
  169. icon_params.image_selected(imagep);
  170. icon_params.scale_image(true);
  171. icon_params.flash_color.control = "ButtonUnselectedFgColor";
  172. mInfoButton = LLUICtrlFactory::create<LLButton>(icon_params);
  173. if (p.click_callback.isProvided())
  174. {
  175. mInfoButton->setCommitCallback(boost::bind(p.click_callback()));
  176. }
  177. addChild(mInfoButton);
  178. // move text over to fit image in
  179. mTextBox->translate(TOOLTIP_ICON_SIZE + mPadding, 0);
  180. }
  181. if (p.time_based_media)
  182. {
  183. LLButton::Params p_button;
  184. p_button.name(std::string("play_media"));
  185. p_button.label(""); // provide label but set to empty so name does not overwrite it -angela
  186. TOOLTIP_PLAYBUTTON_SIZE = 16;
  187. LLRect button_rect;
  188. button_rect.setOriginAndSize((mPadding +TOOLTIP_ICON_SIZE+ mPadding ), mPadding, TOOLTIP_ICON_SIZE, TOOLTIP_ICON_SIZE);
  189. p_button.rect = button_rect;
  190. p_button.image_selected.name("button_anim_pause.tga");
  191. p_button.image_unselected.name("button_anim_play.tga");
  192. p_button.scale_image(true);
  193. mPlayMediaButton = LLUICtrlFactory::create<LLButton>(p_button);
  194. if(p.click_playmedia_callback.isProvided())
  195. {
  196. mPlayMediaButton->setCommitCallback(boost::bind(p.click_playmedia_callback()));
  197. }
  198. mPlayMediaButton->setToggleState(p.media_playing);
  199. addChild(mPlayMediaButton);
  200. // move text over to fit image in
  201. mTextBox->translate(TOOLTIP_PLAYBUTTON_SIZE + mPadding, 0);
  202. }
  203. if (p.web_based_media)
  204. {
  205. LLButton::Params p_w_button;
  206. p_w_button.name(std::string("home_page"));
  207. p_w_button.label(""); // provid label but set to empty so name does not overwrite it -angela
  208. TOOLTIP_PLAYBUTTON_SIZE = 16;
  209. LLRect button_rect;
  210. button_rect.setOriginAndSize((mPadding +TOOLTIP_ICON_SIZE+ mPadding ), mPadding, TOOLTIP_ICON_SIZE, TOOLTIP_ICON_SIZE);
  211. p_w_button.rect = button_rect;
  212. p_w_button.image_unselected.name("map_home.tga");
  213. p_w_button.scale_image(true);
  214. mHomePageButton = LLUICtrlFactory::create<LLButton>(p_w_button);
  215. if(p.click_homepage_callback.isProvided())
  216. {
  217. mHomePageButton->setCommitCallback(boost::bind(p.click_homepage_callback()));
  218. }
  219. addChild(mHomePageButton);
  220. // move text over to fit image in
  221. mTextBox->translate(TOOLTIP_PLAYBUTTON_SIZE + mPadding, 0);
  222. }
  223. if (p.click_callback.isProvided())
  224. {
  225. setMouseUpCallback(boost::bind(p.click_callback()));
  226. }
  227. }
  228. void LLToolTip::initFromParams(const LLToolTip::Params& p)
  229. {
  230. LLPanel::initFromParams(p);
  231. // do this *after* we've had our size set in LLPanel::initFromParams();
  232. const S32 REALLY_LARGE_HEIGHT = 10000;
  233. mTextBox->reshape(p.max_width, REALLY_LARGE_HEIGHT);
  234. if (p.styled_message.isProvided())
  235. {
  236. for (LLInitParam::ParamIterator<LLToolTip::StyledText>::const_iterator text_it = p.styled_message.begin();
  237. text_it != p.styled_message.end();
  238. ++text_it)
  239. {
  240. mTextBox->appendText(text_it->text(), false, text_it->style);
  241. }
  242. }
  243. else
  244. {
  245. mTextBox->setText(p.message());
  246. }
  247. S32 text_width = llmin(p.max_width(), mTextBox->getTextPixelWidth());
  248. S32 text_height = mTextBox->getTextPixelHeight();
  249. mTextBox->reshape(text_width, text_height);
  250. // reshape tooltip panel to fit text box
  251. LLRect tooltip_rect = calcBoundingRect();
  252. tooltip_rect.mTop += mPadding;
  253. tooltip_rect.mRight += mPadding;
  254. tooltip_rect.mBottom = 0;
  255. tooltip_rect.mLeft = 0;
  256. setShape(tooltip_rect);
  257. }
  258. void LLToolTip::setVisible(BOOL visible)
  259. {
  260. // fade out tooltip over time
  261. if (visible)
  262. {
  263. mVisibleTimer.start();
  264. mFadeTimer.stop();
  265. LLPanel::setVisible(TRUE);
  266. }
  267. else
  268. {
  269. mVisibleTimer.stop();
  270. // don't actually change mVisible state, start fade out transition instead
  271. if (!mFadeTimer.getStarted())
  272. {
  273. mFadeTimer.start();
  274. }
  275. }
  276. }
  277. BOOL LLToolTip::handleHover(S32 x, S32 y, MASK mask)
  278. {
  279. //mInfoButton->setFlashing(true);
  280. if(mInfoButton)
  281. mInfoButton->setHighlight(true);
  282. LLPanel::handleHover(x, y, mask);
  283. if (mHasClickCallback)
  284. {
  285. getWindow()->setCursor(UI_CURSOR_HAND);
  286. }
  287. return TRUE;
  288. }
  289. void LLToolTip::onMouseLeave(S32 x, S32 y, MASK mask)
  290. {
  291. //mInfoButton->setFlashing(true);
  292. if(mInfoButton)
  293. mInfoButton->setHighlight(false);
  294. LLUICtrl::onMouseLeave(x, y, mask);
  295. }
  296. void LLToolTip::draw()
  297. {
  298. F32 alpha = 1.f;
  299. if (mFadeTimer.getStarted())
  300. {
  301. F32 tool_tip_fade_time = LLUI::sSettingGroups["config"]->getF32("ToolTipFadeTime");
  302. alpha = clamp_rescale(mFadeTimer.getElapsedTimeF32(), 0.f, tool_tip_fade_time, 1.f, 0.f);
  303. if (alpha == 0.f)
  304. {
  305. // finished fading out, so hide ourselves
  306. mFadeTimer.stop();
  307. LLPanel::setVisible(false);
  308. }
  309. }
  310. // draw tooltip contents with appropriate alpha
  311. {
  312. LLViewDrawContext context(alpha);
  313. LLPanel::draw();
  314. }
  315. }
  316. bool LLToolTip::isFading()
  317. {
  318. return mFadeTimer.getStarted();
  319. }
  320. F32 LLToolTip::getVisibleTime()
  321. {
  322. return mVisibleTimer.getStarted() ? mVisibleTimer.getElapsedTimeF32() : 0.f;
  323. }
  324. bool LLToolTip::hasClickCallback()
  325. {
  326. return mHasClickCallback;
  327. }
  328. //
  329. // LLToolTipMgr
  330. //
  331. LLToolTipMgr::LLToolTipMgr()
  332. : mToolTipsBlocked(false),
  333. mToolTip(NULL),
  334. mNeedsToolTip(false)
  335. {}
  336. void LLToolTipMgr::createToolTip(const LLToolTip::Params& params)
  337. {
  338. // block all other tooltips until tooltips re-enabled (e.g. mouse moved)
  339. blockToolTips();
  340. delete mToolTip;
  341. LLToolTip::Params tooltip_params(params);
  342. // block mouse events if there is a click handler registered (specifically, hover)
  343. if (params.click_callback.isProvided() && !params.mouse_opaque.isProvided())
  344. {
  345. // set mouse_opaque to true if it wasn't already set to something else
  346. // this prevents mouse down from going "through" the tooltip and ultimately
  347. // causing the tooltip to disappear
  348. tooltip_params.mouse_opaque = true;
  349. }
  350. tooltip_params.rect = LLRect (0, 1, 1, 0);
  351. mToolTip = LLUICtrlFactory::create<LLToolTip> (tooltip_params);
  352. gToolTipView->addChild(mToolTip);
  353. if (params.pos.isProvided())
  354. {
  355. LLCoordGL pos = params.pos;
  356. // try to spawn at requested position
  357. LLUI::positionViewNearMouse(mToolTip, pos.mX, pos.mY);
  358. }
  359. else
  360. {
  361. // just spawn at mouse location
  362. LLUI::positionViewNearMouse(mToolTip);
  363. }
  364. //...update "sticky" rect and tooltip position
  365. if (params.sticky_rect.isProvided())
  366. {
  367. mMouseNearRect = params.sticky_rect;
  368. }
  369. else
  370. {
  371. S32 mouse_x;
  372. S32 mouse_y;
  373. LLUI::getMousePositionLocal(gToolTipView->getParent(), &mouse_x, &mouse_y);
  374. // allow mouse a little bit of slop before changing tooltips
  375. mMouseNearRect.setCenterAndSize(mouse_x, mouse_y, 3, 3);
  376. }
  377. // allow mouse to move all the way to the tooltip without changing tooltips
  378. // (tooltip can still time out)
  379. if (mToolTip->hasClickCallback())
  380. {
  381. // keep tooltip up when we mouse over it
  382. mMouseNearRect.unionWith(mToolTip->getRect());
  383. }
  384. }
  385. void LLToolTipMgr::show(const std::string& msg)
  386. {
  387. show(LLToolTip::Params().message(msg));
  388. }
  389. void LLToolTipMgr::show(const LLToolTip::Params& params)
  390. {
  391. // fill in default tooltip params from tool_tip.xml
  392. LLToolTip::Params params_with_defaults(params);
  393. params_with_defaults.fillFrom(LLUICtrlFactory::instance().getDefaultParams<LLToolTip>());
  394. if (!params_with_defaults.validateBlock())
  395. {
  396. llwarns << "Could not display tooltip!" << llendl;
  397. return;
  398. }
  399. S32 mouse_x;
  400. S32 mouse_y;
  401. LLUI::getMousePositionLocal(gToolTipView, &mouse_x, &mouse_y);
  402. // are we ready to show the tooltip?
  403. if (!mToolTipsBlocked // we haven't hit a key, moved the mouse, etc.
  404. && LLUI::getMouseIdleTime() > params_with_defaults.delay_time) // the mouse has been still long enough
  405. {
  406. bool tooltip_changed = mLastToolTipParams.message() != params_with_defaults.message()
  407. || mLastToolTipParams.pos() != params_with_defaults.pos()
  408. || mLastToolTipParams.time_based_media() != params_with_defaults.time_based_media()
  409. || mLastToolTipParams.web_based_media() != params_with_defaults.web_based_media();
  410. bool tooltip_shown = mToolTip
  411. && mToolTip->getVisible()
  412. && !mToolTip->isFading();
  413. mNeedsToolTip = tooltip_changed || !tooltip_shown;
  414. // store description of tooltip for later creation
  415. mNextToolTipParams = params_with_defaults;
  416. }
  417. }
  418. // allow new tooltips to be created, e.g. after mouse has moved
  419. void LLToolTipMgr::unblockToolTips()
  420. {
  421. mToolTipsBlocked = false;
  422. }
  423. // disallow new tooltips until unblockTooltips called
  424. void LLToolTipMgr::blockToolTips()
  425. {
  426. hideToolTips();
  427. mToolTipsBlocked = true;
  428. }
  429. void LLToolTipMgr::hideToolTips()
  430. {
  431. if (mToolTip)
  432. {
  433. mToolTip->setVisible(FALSE);
  434. }
  435. }
  436. bool LLToolTipMgr::toolTipVisible()
  437. {
  438. return mToolTip ? mToolTip->isInVisibleChain() : false;
  439. }
  440. LLRect LLToolTipMgr::getToolTipRect()
  441. {
  442. if (mToolTip && mToolTip->getVisible())
  443. {
  444. return mToolTip->getRect();
  445. }
  446. return LLRect();
  447. }
  448. LLRect LLToolTipMgr::getMouseNearRect()
  449. {
  450. return toolTipVisible() ? mMouseNearRect : LLRect();
  451. }
  452. // every frame, determine if current tooltip should be hidden
  453. void LLToolTipMgr::updateToolTipVisibility()
  454. {
  455. // create new tooltip if we have one ready to go
  456. if (mNeedsToolTip)
  457. {
  458. mNeedsToolTip = false;
  459. createToolTip(mNextToolTipParams);
  460. mLastToolTipParams = mNextToolTipParams;
  461. return;
  462. }
  463. // hide tooltips when mouse cursor is hidden
  464. if (LLUI::getWindow()->isCursorHidden())
  465. {
  466. blockToolTips();
  467. return;
  468. }
  469. // hide existing tooltips if they have timed out
  470. S32 mouse_x, mouse_y;
  471. LLUI::getMousePositionLocal(gToolTipView, &mouse_x, &mouse_y);
  472. F32 tooltip_timeout = 0.f;
  473. if (toolTipVisible())
  474. {
  475. // mouse far away from tooltip
  476. tooltip_timeout = mLastToolTipParams.visible_time_far;
  477. // mouse near rect will only include the tooltip if the
  478. // tooltip is clickable
  479. if (mMouseNearRect.pointInRect(mouse_x, mouse_y))
  480. {
  481. // mouse "close" to tooltip
  482. tooltip_timeout = mLastToolTipParams.visible_time_near;
  483. // if tooltip is clickable (has large mMouseNearRect)
  484. // than having cursor over tooltip keeps it up indefinitely
  485. if (mToolTip->parentPointInView(mouse_x, mouse_y))
  486. {
  487. // mouse over tooltip itself, don't time out
  488. tooltip_timeout = mLastToolTipParams.visible_time_over;
  489. }
  490. }
  491. if (mToolTip->getVisibleTime() > tooltip_timeout)
  492. {
  493. hideToolTips();
  494. unblockToolTips();
  495. }
  496. }
  497. }
  498. // EOF