PageRenderTime 42ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/indra/llui/llresizehandle.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 338 lines | 258 code | 39 blank | 41 comment | 29 complexity | 77e12597282c12753758edf97de14f68 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llresizehandle.cpp
  3. * @brief LLResizeHandle base class
  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. #include "llresizehandle.h"
  28. #include "llfocusmgr.h"
  29. #include "llmath.h"
  30. #include "llui.h"
  31. #include "llmenugl.h"
  32. #include "llcontrol.h"
  33. #include "llfloater.h"
  34. #include "llwindow.h"
  35. const S32 RESIZE_BORDER_WIDTH = 3;
  36. LLResizeHandle::Params::Params()
  37. : corner("corner"),
  38. min_width("min_width"),
  39. min_height("min_height")
  40. {
  41. name = "resize_handle";
  42. }
  43. LLResizeHandle::LLResizeHandle(const LLResizeHandle::Params& p)
  44. : LLView(p),
  45. mDragLastScreenX( 0 ),
  46. mDragLastScreenY( 0 ),
  47. mLastMouseScreenX( 0 ),
  48. mLastMouseScreenY( 0 ),
  49. mImage( NULL ),
  50. mMinWidth( p.min_width ),
  51. mMinHeight( p.min_height ),
  52. mCorner( p.corner )
  53. {
  54. if( RIGHT_BOTTOM == mCorner)
  55. {
  56. mImage = LLUI::getUIImage("Resize_Corner");
  57. }
  58. switch( p.corner )
  59. {
  60. case LEFT_TOP: setFollows( FOLLOWS_LEFT | FOLLOWS_TOP ); break;
  61. case LEFT_BOTTOM: setFollows( FOLLOWS_LEFT | FOLLOWS_BOTTOM ); break;
  62. case RIGHT_TOP: setFollows( FOLLOWS_RIGHT | FOLLOWS_TOP ); break;
  63. case RIGHT_BOTTOM: setFollows( FOLLOWS_RIGHT | FOLLOWS_BOTTOM ); break;
  64. }
  65. }
  66. BOOL LLResizeHandle::handleMouseDown(S32 x, S32 y, MASK mask)
  67. {
  68. BOOL handled = FALSE;
  69. if( pointInHandle(x, y) )
  70. {
  71. handled = TRUE;
  72. // Route future Mouse messages here preemptively. (Release on mouse up.)
  73. // No handler needed for focus lost since this clas has no state that depends on it.
  74. gFocusMgr.setMouseCapture( this );
  75. localPointToScreen(x, y, &mDragLastScreenX, &mDragLastScreenY);
  76. mLastMouseScreenX = mDragLastScreenX;
  77. mLastMouseScreenY = mDragLastScreenY;
  78. }
  79. return handled;
  80. }
  81. BOOL LLResizeHandle::handleMouseUp(S32 x, S32 y, MASK mask)
  82. {
  83. BOOL handled = FALSE;
  84. if( hasMouseCapture() )
  85. {
  86. // Release the mouse
  87. gFocusMgr.setMouseCapture( NULL );
  88. handled = TRUE;
  89. }
  90. else if( pointInHandle(x, y) )
  91. {
  92. handled = TRUE;
  93. }
  94. return handled;
  95. }
  96. BOOL LLResizeHandle::handleHover(S32 x, S32 y, MASK mask)
  97. {
  98. BOOL handled = FALSE;
  99. // We only handle the click if the click both started and ended within us
  100. if( hasMouseCapture() )
  101. {
  102. // Make sure the mouse in still over the application. We don't want to make the parent
  103. // so big that we can't see the resize handle any more.
  104. S32 screen_x;
  105. S32 screen_y;
  106. localPointToScreen(x, y, &screen_x, &screen_y);
  107. const LLRect valid_rect = getRootView()->getRect();
  108. screen_x = llclamp( screen_x, valid_rect.mLeft, valid_rect.mRight );
  109. screen_y = llclamp( screen_y, valid_rect.mBottom, valid_rect.mTop );
  110. LLView* resizing_view = getParent();
  111. if( resizing_view )
  112. {
  113. // undock floater when user resize it
  114. LLFloater* floater_parent = dynamic_cast<LLFloater*>(getParent());
  115. if (floater_parent && floater_parent->isDocked())
  116. {
  117. floater_parent->setDocked(false, false);
  118. }
  119. // Resize the parent
  120. LLRect orig_rect = resizing_view->getRect();
  121. LLRect scaled_rect = orig_rect;
  122. S32 delta_x = screen_x - mDragLastScreenX;
  123. S32 delta_y = screen_y - mDragLastScreenY;
  124. LLCoordGL mouse_dir;
  125. // use hysteresis on mouse motion to preserve user intent when mouse stops moving
  126. mouse_dir.mX = (screen_x == mLastMouseScreenX) ? mLastMouseDir.mX : screen_x - mLastMouseScreenX;
  127. mouse_dir.mY = (screen_y == mLastMouseScreenY) ? mLastMouseDir.mY : screen_y - mLastMouseScreenY;
  128. mLastMouseScreenX = screen_x;
  129. mLastMouseScreenY = screen_y;
  130. mLastMouseDir = mouse_dir;
  131. S32 x_multiple = 1;
  132. S32 y_multiple = 1;
  133. switch( mCorner )
  134. {
  135. case LEFT_TOP:
  136. x_multiple = -1;
  137. y_multiple = 1;
  138. break;
  139. case LEFT_BOTTOM:
  140. x_multiple = -1;
  141. y_multiple = -1;
  142. break;
  143. case RIGHT_TOP:
  144. x_multiple = 1;
  145. y_multiple = 1;
  146. break;
  147. case RIGHT_BOTTOM:
  148. x_multiple = 1;
  149. y_multiple = -1;
  150. break;
  151. }
  152. S32 new_width = orig_rect.getWidth() + x_multiple * delta_x;
  153. if( new_width < mMinWidth )
  154. {
  155. new_width = mMinWidth;
  156. delta_x = x_multiple * (mMinWidth - orig_rect.getWidth());
  157. }
  158. S32 new_height = orig_rect.getHeight() + y_multiple * delta_y;
  159. if( new_height < mMinHeight )
  160. {
  161. new_height = mMinHeight;
  162. delta_y = y_multiple * (mMinHeight - orig_rect.getHeight());
  163. }
  164. switch( mCorner )
  165. {
  166. case LEFT_TOP:
  167. scaled_rect.translate(delta_x, 0);
  168. break;
  169. case LEFT_BOTTOM:
  170. scaled_rect.translate(delta_x, delta_y);
  171. break;
  172. case RIGHT_TOP:
  173. break;
  174. case RIGHT_BOTTOM:
  175. scaled_rect.translate(0, delta_y);
  176. break;
  177. }
  178. // temporarily set new parent rect
  179. scaled_rect.mRight = scaled_rect.mLeft + new_width;
  180. scaled_rect.mTop = scaled_rect.mBottom + new_height;
  181. resizing_view->setRect(scaled_rect);
  182. LLView* snap_view = NULL;
  183. LLView* test_view = NULL;
  184. static LLUICachedControl<S32> snap_margin ("SnapMargin", 0);
  185. // now do snapping
  186. switch(mCorner)
  187. {
  188. case LEFT_TOP:
  189. snap_view = resizing_view->findSnapEdge(scaled_rect.mLeft, mouse_dir, SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, snap_margin);
  190. test_view = resizing_view->findSnapEdge(scaled_rect.mTop, mouse_dir, SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, snap_margin);
  191. if (!snap_view)
  192. {
  193. snap_view = test_view;
  194. }
  195. break;
  196. case LEFT_BOTTOM:
  197. snap_view = resizing_view->findSnapEdge(scaled_rect.mLeft, mouse_dir, SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, snap_margin);
  198. test_view = resizing_view->findSnapEdge(scaled_rect.mBottom, mouse_dir, SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, snap_margin);
  199. if (!snap_view)
  200. {
  201. snap_view = test_view;
  202. }
  203. break;
  204. case RIGHT_TOP:
  205. snap_view = resizing_view->findSnapEdge(scaled_rect.mRight, mouse_dir, SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, snap_margin);
  206. test_view = resizing_view->findSnapEdge(scaled_rect.mTop, mouse_dir, SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, snap_margin);
  207. if (!snap_view)
  208. {
  209. snap_view = test_view;
  210. }
  211. break;
  212. case RIGHT_BOTTOM:
  213. snap_view = resizing_view->findSnapEdge(scaled_rect.mRight, mouse_dir, SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, snap_margin);
  214. test_view = resizing_view->findSnapEdge(scaled_rect.mBottom, mouse_dir, SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, snap_margin);
  215. if (!snap_view)
  216. {
  217. snap_view = test_view;
  218. }
  219. break;
  220. }
  221. // register "snap" behavior with snapped view
  222. resizing_view->setSnappedTo(snap_view);
  223. // reset parent rect
  224. resizing_view->setRect(orig_rect);
  225. // translate and scale to new shape
  226. resizing_view->setShape(scaled_rect, true);
  227. // update last valid mouse cursor position based on resized view's actual size
  228. LLRect new_rect = resizing_view->getRect();
  229. switch(mCorner)
  230. {
  231. case LEFT_TOP:
  232. mDragLastScreenX += new_rect.mLeft - orig_rect.mLeft;
  233. mDragLastScreenY += new_rect.mTop - orig_rect.mTop;
  234. break;
  235. case LEFT_BOTTOM:
  236. mDragLastScreenX += new_rect.mLeft - orig_rect.mLeft;
  237. mDragLastScreenY += new_rect.mBottom- orig_rect.mBottom;
  238. break;
  239. case RIGHT_TOP:
  240. mDragLastScreenX += new_rect.mRight - orig_rect.mRight;
  241. mDragLastScreenY += new_rect.mTop - orig_rect.mTop;
  242. break;
  243. case RIGHT_BOTTOM:
  244. mDragLastScreenX += new_rect.mRight - orig_rect.mRight;
  245. mDragLastScreenY += new_rect.mBottom- orig_rect.mBottom;
  246. break;
  247. default:
  248. break;
  249. }
  250. }
  251. handled = TRUE;
  252. }
  253. else // don't have mouse capture
  254. {
  255. if( pointInHandle( x, y ) )
  256. {
  257. handled = TRUE;
  258. }
  259. }
  260. if( handled )
  261. {
  262. switch( mCorner )
  263. {
  264. case RIGHT_BOTTOM:
  265. case LEFT_TOP:
  266. getWindow()->setCursor(UI_CURSOR_SIZENWSE);
  267. break;
  268. case LEFT_BOTTOM:
  269. case RIGHT_TOP:
  270. getWindow()->setCursor(UI_CURSOR_SIZENESW);
  271. break;
  272. }
  273. }
  274. return handled;
  275. } // end handleHover
  276. // assumes GL state is set for 2D
  277. void LLResizeHandle::draw()
  278. {
  279. if( mImage.notNull() && getVisible() && (RIGHT_BOTTOM == mCorner) )
  280. {
  281. mImage->draw(0, 0);
  282. }
  283. }
  284. BOOL LLResizeHandle::pointInHandle( S32 x, S32 y )
  285. {
  286. if( pointInView(x, y) )
  287. {
  288. const S32 TOP_BORDER = (getRect().getHeight() - RESIZE_BORDER_WIDTH);
  289. const S32 RIGHT_BORDER = (getRect().getWidth() - RESIZE_BORDER_WIDTH);
  290. switch( mCorner )
  291. {
  292. case LEFT_TOP: return (x <= RESIZE_BORDER_WIDTH) || (y >= TOP_BORDER);
  293. case LEFT_BOTTOM: return (x <= RESIZE_BORDER_WIDTH) || (y <= RESIZE_BORDER_WIDTH);
  294. case RIGHT_TOP: return (x >= RIGHT_BORDER) || (y >= TOP_BORDER);
  295. case RIGHT_BOTTOM: return TRUE;
  296. }
  297. }
  298. return FALSE;
  299. }