lldraghandle.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. /**
  2. * @file lldraghandle.cpp
  3. * @brief LLDragHandle 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. // A widget for dragging a view around the screen using the mouse.
  33. #include "linden_common.h"
  34. #include "lldraghandle.h"
  35. #include "llcontrol.h"
  36. #include "llmenugl.h"
  37. #include "lltextbox.h"
  38. #include "llwindow.h"
  39. constexpr S32 LEADING_PAD = 5;
  40. constexpr S32 TITLE_PAD = 8;
  41. constexpr S32 BORDER_PAD = 1;
  42. constexpr S32 LEFT_PAD = BORDER_PAD + TITLE_PAD + LEADING_PAD;
  43. constexpr S32 RIGHT_PAD = BORDER_PAD + 32; // 32=space for close & minimize btn
  44. LLDragHandle::LLDragHandle(const std::string& name, const LLRect& rect,
  45. const std::string& title)
  46. : LLView(name, rect, true),
  47. mTitleBox(NULL),
  48. mClickedCallback(NULL),
  49. mCallbackUserData(NULL),
  50. mDragLastScreenX(0),
  51. mDragLastScreenY(0),
  52. mLastMouseScreenX(0),
  53. mLastMouseScreenY(0),
  54. mMaxTitleWidth(0),
  55. mForeground(true)
  56. {
  57. setSaveToXML(false);
  58. }
  59. LLDragHandle::~LLDragHandle()
  60. {
  61. setTitleBox(NULL);
  62. }
  63. void LLDragHandle::setTitleVisible(bool visible)
  64. {
  65. if (mTitleBox)
  66. {
  67. mTitleBox->setVisible(visible);
  68. }
  69. }
  70. void LLDragHandle::setTitleBox(LLTextBox* titlebox)
  71. {
  72. if (mTitleBox)
  73. {
  74. removeChild(mTitleBox);
  75. delete mTitleBox;
  76. }
  77. mTitleBox = titlebox;
  78. if (mTitleBox)
  79. {
  80. addChild(mTitleBox);
  81. }
  82. }
  83. LLDragHandleTop::LLDragHandleTop(const std::string& name, const LLRect &rect,
  84. const std::string& title)
  85. : LLDragHandle(name, rect, title)
  86. {
  87. mFont = LLFontGL::getFontSansSerif();
  88. setFollowsAll();
  89. setTitle(title);
  90. }
  91. LLDragHandleLeft::LLDragHandleLeft(const std::string& name, const LLRect &rect,
  92. const std::string& title)
  93. : LLDragHandle(name, rect, title)
  94. {
  95. setFollowsAll();
  96. setTitle(title);
  97. }
  98. void LLDragHandleTop::setTitle(const std::string& title)
  99. {
  100. std::string trimmed_title = title;
  101. LLStringUtil::trim(trimmed_title);
  102. if (mTitleBox)
  103. {
  104. mTitleBox->setText(trimmed_title);
  105. }
  106. else
  107. {
  108. mTitleBox = new LLTextBox("Drag Handle Title", getRect(),
  109. trimmed_title, mFont);
  110. mTitleBox->setFollows(FOLLOWS_TOP | FOLLOWS_LEFT | FOLLOWS_RIGHT);
  111. mTitleBox->setFontStyle(LLFontGL::DROP_SHADOW_SOFT);
  112. addChild(mTitleBox);
  113. }
  114. reshapeTitleBox();
  115. }
  116. const std::string& LLDragHandleTop::getTitle() const
  117. {
  118. return mTitleBox ? mTitleBox->getText() : LLStringUtil::null;
  119. }
  120. void LLDragHandleLeft::setTitle(const std::string&)
  121. {
  122. setTitleBox(NULL);
  123. /* no title on left edge */
  124. }
  125. const std::string& LLDragHandleLeft::getTitle() const
  126. {
  127. return LLStringUtil::null;
  128. }
  129. void LLDragHandleTop::draw()
  130. {
  131. // Colorize the text to match the frontmost state
  132. if (mTitleBox)
  133. {
  134. mTitleBox->setEnabled(getForeground());
  135. }
  136. LLView::draw();
  137. }
  138. // Assumes GL state is set for 2D
  139. void LLDragHandleLeft::draw()
  140. {
  141. // Colorize the text to match the frontmost state
  142. if (mTitleBox)
  143. {
  144. mTitleBox->setEnabled(getForeground());
  145. }
  146. LLView::draw();
  147. }
  148. void LLDragHandleTop::reshapeTitleBox()
  149. {
  150. if (!mTitleBox)
  151. {
  152. return;
  153. }
  154. S32 title_width = mFont->getWidth(mTitleBox->getText()) + TITLE_PAD;
  155. if (getMaxTitleWidth() > 0)
  156. {
  157. title_width = llmin(title_width, getMaxTitleWidth());
  158. }
  159. S32 title_height = ll_roundp(mFont->getLineHeight());
  160. LLRect title_rect;
  161. title_rect.setLeftTopAndSize(LEFT_PAD, getRect().getHeight() - BORDER_PAD,
  162. getRect().getWidth() - LEFT_PAD - RIGHT_PAD,
  163. title_height);
  164. mTitleBox->setRect(title_rect);
  165. }
  166. void LLDragHandleTop::reshape(S32 width, S32 height, bool called_from_parent)
  167. {
  168. LLView::reshape(width, height, called_from_parent);
  169. reshapeTitleBox();
  170. }
  171. void LLDragHandleLeft::reshape(S32 width, S32 height, bool called_from_parent)
  172. {
  173. LLView::reshape(width, height, called_from_parent);
  174. }
  175. //-------------------------------------------------------------
  176. // UI event handling
  177. //-------------------------------------------------------------
  178. bool LLDragHandle::handleMouseDown(S32 x, S32 y, MASK mask)
  179. {
  180. // Route future Mouse messages here preemptively (release on mouse up).
  181. // No handler needed for focus lost since this clas has no state that
  182. // depends on it.
  183. gFocusMgr.setMouseCapture(this);
  184. localPointToScreen(x, y, &mDragLastScreenX, &mDragLastScreenY);
  185. mLastMouseScreenX = mDragLastScreenX;
  186. mLastMouseScreenY = mDragLastScreenY;
  187. if (mClickedCallback && getSoundFlags() & MOUSE_DOWN)
  188. {
  189. make_ui_sound("UISndClick");
  190. }
  191. // Note: do not pass on to children
  192. return true;
  193. }
  194. bool LLDragHandle::handleMouseUp(S32 x, S32 y, MASK mask)
  195. {
  196. if (hasMouseCapture())
  197. {
  198. // Release the mouse
  199. gFocusMgr.setMouseCapture(NULL);
  200. }
  201. if (mClickedCallback)
  202. {
  203. if (getSoundFlags() & MOUSE_UP)
  204. {
  205. make_ui_sound("UISndClickRelease");
  206. }
  207. // DO THIS AT THE VERY END to allow the handle to be destroyed as a
  208. // result of being clicked. If mouseup in the widget, it has been
  209. // clicked.
  210. (*mClickedCallback)(x, y, mCallbackUserData);
  211. }
  212. // Note: do not pass on to children
  213. return true;
  214. }
  215. bool LLDragHandle::handleHover(S32 x, S32 y, MASK mask)
  216. {
  217. bool handled = false;
  218. // We only handle the click if the click both started and ended within us
  219. if (hasMouseCapture())
  220. {
  221. S32 screen_x;
  222. S32 screen_y;
  223. localPointToScreen(x, y, &screen_x, &screen_y);
  224. // Resize the parent
  225. S32 delta_x = screen_x - mDragLastScreenX;
  226. S32 delta_y = screen_y - mDragLastScreenY;
  227. LLRect original_rect = getParent()->getRect();
  228. LLRect translated_rect = getParent()->getRect();
  229. translated_rect.translate(delta_x, delta_y);
  230. // Temporarily slam dragged window to new position
  231. getParent()->setRect(translated_rect);
  232. S32 pre_snap_x = getParent()->getRect().mLeft;
  233. S32 pre_snap_y = getParent()->getRect().mBottom;
  234. mDragLastScreenX = screen_x;
  235. mDragLastScreenY = screen_y;
  236. LLRect new_rect;
  237. LLCoordGL mouse_dir;
  238. // Use hysteresis on mouse motion to preserve user intent when mouse
  239. // stops moving
  240. mouse_dir.mX = screen_x == mLastMouseScreenX ? mLastMouseDir.mX
  241. : screen_x -
  242. mLastMouseScreenX;
  243. mouse_dir.mY = screen_y == mLastMouseScreenY ? mLastMouseDir.mY
  244. : screen_y -
  245. mLastMouseScreenY;
  246. mLastMouseDir = mouse_dir;
  247. mLastMouseScreenX = screen_x;
  248. mLastMouseScreenY = screen_y;
  249. LLView* snap_view = getParent()->findSnapRect(new_rect, mouse_dir,
  250. SNAP_PARENT_AND_SIBLINGS,
  251. LLUI::sSnapMargin);
  252. getParent()->snappedTo(snap_view);
  253. delta_x = new_rect.mLeft - pre_snap_x;
  254. delta_y = new_rect.mBottom - pre_snap_y;
  255. translated_rect.translate(delta_x, delta_y);
  256. // Restore original rect so delta are detected, then call user reshape
  257. // method to handle snapped floaters, etc
  258. getParent()->setRect(original_rect);
  259. getParent()->userSetShape(translated_rect);
  260. mDragLastScreenX += delta_x;
  261. mDragLastScreenY += delta_y;
  262. gWindowp->setCursor(UI_CURSOR_ARROW);
  263. LL_DEBUGS("UserInput") << "hover handled by " << getName()
  264. << " (active)" << LL_ENDL;
  265. handled = true;
  266. }
  267. else
  268. {
  269. gWindowp->setCursor(UI_CURSOR_ARROW);
  270. LL_DEBUGS("UserInput") << "hover handled by " << getName()
  271. << " (inactive)" << LL_ENDL;
  272. handled = true;
  273. }
  274. // Note: do not pass on to children
  275. return handled;
  276. }
  277. void LLDragHandle::setValue(const LLSD& value)
  278. {
  279. setTitle(value.asString());
  280. }