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