diff options
author | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
---|---|---|
committer | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
commit | 38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch) | |
tree | adca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/llui/llscrollcontainer.cpp | |
parent | README.txt (diff) | |
download | meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2 meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz |
Second Life viewer sources 1.13.2.12
Diffstat (limited to 'linden/indra/llui/llscrollcontainer.cpp')
-rw-r--r-- | linden/indra/llui/llscrollcontainer.cpp | 791 |
1 files changed, 791 insertions, 0 deletions
diff --git a/linden/indra/llui/llscrollcontainer.cpp b/linden/indra/llui/llscrollcontainer.cpp new file mode 100644 index 0000000..73be607 --- /dev/null +++ b/linden/indra/llui/llscrollcontainer.cpp | |||
@@ -0,0 +1,791 @@ | |||
1 | /** | ||
2 | * @file llscrollcontainer.cpp | ||
3 | * @brief LLScrollableContainerView base class | ||
4 | * | ||
5 | * Copyright (c) 2001-2007, Linden Research, Inc. | ||
6 | * | ||
7 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
8 | * to you under the terms of the GNU General Public License, version 2.0 | ||
9 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
10 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
11 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
12 | * online at http://secondlife.com/developers/opensource/gplv2 | ||
13 | * | ||
14 | * There are special exceptions to the terms and conditions of the GPL as | ||
15 | * it is applied to this Source Code. View the full text of the exception | ||
16 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
17 | * online at http://secondlife.com/developers/opensource/flossexception | ||
18 | * | ||
19 | * By copying, modifying or distributing this software, you acknowledge | ||
20 | * that you have read and understood your obligations described above, | ||
21 | * and agree to abide by those obligations. | ||
22 | * | ||
23 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
24 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
25 | * COMPLETENESS OR PERFORMANCE. | ||
26 | */ | ||
27 | |||
28 | //***************************************************************************** | ||
29 | // | ||
30 | // A view meant to encapsulate a clipped region which is | ||
31 | // scrollable. It automatically takes care of pixel perfect scrolling | ||
32 | // and cliipping, as well as turning the scrollbars on or off based on | ||
33 | // the width and height of the view you're scrolling. | ||
34 | // | ||
35 | //***************************************************************************** | ||
36 | |||
37 | #include "linden_common.h" | ||
38 | |||
39 | #include "llgl.h" | ||
40 | |||
41 | #include "llscrollcontainer.h" | ||
42 | #include "llscrollbar.h" | ||
43 | #include "llui.h" | ||
44 | #include "llkeyboard.h" | ||
45 | #include "llviewborder.h" | ||
46 | #include "llfocusmgr.h" | ||
47 | #include "llframetimer.h" | ||
48 | #include "lluictrlfactory.h" | ||
49 | #include "llfontgl.h" | ||
50 | |||
51 | #include "llglheaders.h" | ||
52 | |||
53 | ///---------------------------------------------------------------------------- | ||
54 | /// Local function declarations, constants, enums, and typedefs | ||
55 | ///---------------------------------------------------------------------------- | ||
56 | |||
57 | static const S32 HORIZONTAL_MULTIPLE = 8; | ||
58 | static const S32 VERTICAL_MULTIPLE = 16; | ||
59 | static const F32 MIN_AUTO_SCROLL_RATE = 120.f; | ||
60 | static const F32 MAX_AUTO_SCROLL_RATE = 500.f; | ||
61 | static const F32 AUTO_SCROLL_RATE_ACCEL = 120.f; | ||
62 | |||
63 | ///---------------------------------------------------------------------------- | ||
64 | /// Class LLScrollableContainerView | ||
65 | ///---------------------------------------------------------------------------- | ||
66 | |||
67 | // Default constructor | ||
68 | LLScrollableContainerView::LLScrollableContainerView( const LLString& name, | ||
69 | const LLRect& rect, | ||
70 | LLView* scrolled_view, | ||
71 | BOOL is_opaque, | ||
72 | const LLColor4& bg_color ) : | ||
73 | LLUICtrl( name, rect, FALSE, NULL, NULL ), | ||
74 | mScrolledView( scrolled_view ), | ||
75 | mIsOpaque( is_opaque ), | ||
76 | mBackgroundColor( bg_color ), | ||
77 | mReserveScrollCorner( FALSE ), | ||
78 | mAutoScrolling( FALSE ), | ||
79 | mAutoScrollRate( 0.f ) | ||
80 | { | ||
81 | if( mScrolledView ) | ||
82 | { | ||
83 | addChild( mScrolledView ); | ||
84 | } | ||
85 | |||
86 | init(); | ||
87 | } | ||
88 | |||
89 | // LLUICtrl constructor | ||
90 | LLScrollableContainerView::LLScrollableContainerView( const LLString& name, const LLRect& rect, | ||
91 | LLUICtrl* scrolled_ctrl, BOOL is_opaque, | ||
92 | const LLColor4& bg_color) : | ||
93 | LLUICtrl( name, rect, FALSE, NULL, NULL ), | ||
94 | mScrolledView( scrolled_ctrl ), | ||
95 | mIsOpaque( is_opaque ), | ||
96 | mBackgroundColor( bg_color ), | ||
97 | mReserveScrollCorner( FALSE ), | ||
98 | mAutoScrolling( FALSE ), | ||
99 | mAutoScrollRate( 0.f ) | ||
100 | { | ||
101 | if( scrolled_ctrl ) | ||
102 | { | ||
103 | addChild( scrolled_ctrl ); | ||
104 | } | ||
105 | |||
106 | init(); | ||
107 | } | ||
108 | |||
109 | void LLScrollableContainerView::init() | ||
110 | { | ||
111 | LLRect border_rect( 0, mRect.getHeight(), mRect.getWidth(), 0 ); | ||
112 | mBorder = new LLViewBorder( "scroll border", border_rect, LLViewBorder::BEVEL_IN ); | ||
113 | addChild( mBorder ); | ||
114 | |||
115 | mInnerRect.set( 0, mRect.getHeight(), mRect.getWidth(), 0 ); | ||
116 | mInnerRect.stretch( -mBorder->getBorderWidth() ); | ||
117 | |||
118 | LLRect vertical_scroll_rect = mInnerRect; | ||
119 | vertical_scroll_rect.mLeft = vertical_scroll_rect.mRight - SCROLLBAR_SIZE; | ||
120 | mScrollbar[VERTICAL] = new LLScrollbar( "scrollable vertical", | ||
121 | vertical_scroll_rect, | ||
122 | LLScrollbar::VERTICAL, | ||
123 | mInnerRect.getHeight(), | ||
124 | 0, | ||
125 | mInnerRect.getHeight(), | ||
126 | NULL, this, | ||
127 | VERTICAL_MULTIPLE); | ||
128 | addChild( mScrollbar[VERTICAL] ); | ||
129 | mScrollbar[VERTICAL]->setVisible( FALSE ); | ||
130 | mScrollbar[VERTICAL]->setFollowsRight(); | ||
131 | mScrollbar[VERTICAL]->setFollowsTop(); | ||
132 | mScrollbar[VERTICAL]->setFollowsBottom(); | ||
133 | |||
134 | LLRect horizontal_scroll_rect = mInnerRect; | ||
135 | horizontal_scroll_rect.mTop = horizontal_scroll_rect.mBottom + SCROLLBAR_SIZE; | ||
136 | mScrollbar[HORIZONTAL] = new LLScrollbar( "scrollable horizontal", | ||
137 | horizontal_scroll_rect, | ||
138 | LLScrollbar::HORIZONTAL, | ||
139 | mInnerRect.getWidth(), | ||
140 | 0, | ||
141 | mInnerRect.getWidth(), | ||
142 | NULL, this, | ||
143 | HORIZONTAL_MULTIPLE); | ||
144 | addChild( mScrollbar[HORIZONTAL] ); | ||
145 | mScrollbar[HORIZONTAL]->setVisible( FALSE ); | ||
146 | mScrollbar[HORIZONTAL]->setFollowsLeft(); | ||
147 | mScrollbar[HORIZONTAL]->setFollowsRight(); | ||
148 | |||
149 | setTabStop(FALSE); | ||
150 | } | ||
151 | |||
152 | // Destroys the object | ||
153 | LLScrollableContainerView::~LLScrollableContainerView( void ) | ||
154 | { | ||
155 | // mScrolledView and mScrollbar are child views, so the LLView | ||
156 | // destructor takes care of memory deallocation. | ||
157 | for( S32 i = 0; i < SCROLLBAR_COUNT; i++ ) | ||
158 | { | ||
159 | mScrollbar[i] = NULL; | ||
160 | } | ||
161 | mScrolledView = NULL; | ||
162 | } | ||
163 | |||
164 | /* | ||
165 | // scrollbar handlers | ||
166 | void LLScrollableContainerView::horizontalChange( S32 new_pos, | ||
167 | LLScrollbar* sb, | ||
168 | void* user_data ) | ||
169 | { | ||
170 | LLScrollableContainerView* cont = reinterpret_cast<LLScrollableContainerView*>(user_data); | ||
171 | // cont->scrollHorizontal( new_pos ); | ||
172 | } | ||
173 | |||
174 | |||
175 | void LLScrollableContainerView::verticalChange( S32 new_pos, LLScrollbar* sb, | ||
176 | void* user_data ) | ||
177 | { | ||
178 | LLScrollableContainerView* cont = reinterpret_cast<LLScrollableContainerView*>(user_data); | ||
179 | // cont->scrollVertical( new_pos ); | ||
180 | } | ||
181 | */ | ||
182 | |||
183 | // internal scrollbar handlers | ||
184 | // virtual | ||
185 | void LLScrollableContainerView::scrollHorizontal( S32 new_pos ) | ||
186 | { | ||
187 | //llinfos << "LLScrollableContainerView::scrollHorizontal()" << llendl; | ||
188 | if( mScrolledView ) | ||
189 | { | ||
190 | LLRect doc_rect = mScrolledView->getRect(); | ||
191 | S32 old_pos = -(doc_rect.mLeft - mInnerRect.mLeft); | ||
192 | mScrolledView->translate( -(new_pos - old_pos), 0 ); | ||
193 | } | ||
194 | } | ||
195 | |||
196 | // virtual | ||
197 | void LLScrollableContainerView::scrollVertical( S32 new_pos ) | ||
198 | { | ||
199 | // llinfos << "LLScrollableContainerView::scrollVertical() " << new_pos << llendl; | ||
200 | if( mScrolledView ) | ||
201 | { | ||
202 | LLRect doc_rect = mScrolledView->getRect(); | ||
203 | S32 old_pos = doc_rect.mTop - mInnerRect.mTop; | ||
204 | mScrolledView->translate( 0, new_pos - old_pos ); | ||
205 | } | ||
206 | } | ||
207 | |||
208 | // LLView functionality | ||
209 | void LLScrollableContainerView::reshape(S32 width, S32 height, | ||
210 | BOOL called_from_parent) | ||
211 | { | ||
212 | LLUICtrl::reshape( width, height, called_from_parent ); | ||
213 | |||
214 | mInnerRect.set( 0, mRect.getHeight(), mRect.getWidth(), 0 ); | ||
215 | mInnerRect.stretch( -mBorder->getBorderWidth() ); | ||
216 | |||
217 | if (mScrolledView) | ||
218 | { | ||
219 | const LLRect& scrolled_rect = mScrolledView->getRect(); | ||
220 | |||
221 | S32 visible_width = 0; | ||
222 | S32 visible_height = 0; | ||
223 | BOOL show_v_scrollbar = FALSE; | ||
224 | BOOL show_h_scrollbar = FALSE; | ||
225 | calcVisibleSize( scrolled_rect, &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar ); | ||
226 | |||
227 | mScrollbar[VERTICAL]->setDocSize( scrolled_rect.getHeight() ); | ||
228 | mScrollbar[VERTICAL]->setPageSize( visible_height ); | ||
229 | |||
230 | mScrollbar[HORIZONTAL]->setDocSize( scrolled_rect.getWidth() ); | ||
231 | mScrollbar[HORIZONTAL]->setPageSize( visible_width ); | ||
232 | } | ||
233 | } | ||
234 | |||
235 | BOOL LLScrollableContainerView::handleKey( KEY key, MASK mask, BOOL called_from_parent ) | ||
236 | { | ||
237 | if( getVisible() && mEnabled ) | ||
238 | { | ||
239 | if( called_from_parent ) | ||
240 | { | ||
241 | // Downward traversal | ||
242 | |||
243 | // Don't pass keys to scrollbars on downward. | ||
244 | |||
245 | // Handle 'child' view. | ||
246 | if( mScrolledView && mScrolledView->handleKey(key, mask, TRUE) ) | ||
247 | { | ||
248 | return TRUE; | ||
249 | } | ||
250 | } | ||
251 | else | ||
252 | { | ||
253 | // Upward traversal | ||
254 | |||
255 | for( S32 i = 0; i < SCROLLBAR_COUNT; i++ ) | ||
256 | { | ||
257 | // Note: the scrollbar _is_ actually being called from it's parent. Here | ||
258 | // we're delgating LLScrollableContainerView's upward traversal to the scrollbars | ||
259 | if( mScrollbar[i]->handleKey(key, mask, TRUE) ) | ||
260 | { | ||
261 | return TRUE; | ||
262 | } | ||
263 | } | ||
264 | |||
265 | if (getParent()) | ||
266 | { | ||
267 | return getParent()->handleKey( key, mask, FALSE ); | ||
268 | } | ||
269 | } | ||
270 | } | ||
271 | |||
272 | return FALSE; | ||
273 | } | ||
274 | |||
275 | BOOL LLScrollableContainerView::handleScrollWheel( S32 x, S32 y, S32 clicks ) | ||
276 | { | ||
277 | if( mEnabled ) | ||
278 | { | ||
279 | for( S32 i = 0; i < SCROLLBAR_COUNT; i++ ) | ||
280 | { | ||
281 | // Note: tries vertical and then horizontal | ||
282 | |||
283 | // Pretend the mouse is over the scrollbar | ||
284 | if( mScrollbar[i]->handleScrollWheel( 0, 0, clicks ) ) | ||
285 | { | ||
286 | return TRUE; | ||
287 | } | ||
288 | } | ||
289 | } | ||
290 | |||
291 | // Opaque | ||
292 | return TRUE; | ||
293 | } | ||
294 | |||
295 | BOOL LLScrollableContainerView::handleDragAndDrop(S32 x, S32 y, MASK mask, | ||
296 | BOOL drop, | ||
297 | EDragAndDropType cargo_type, | ||
298 | void* cargo_data, | ||
299 | EAcceptance* accept, | ||
300 | LLString& tooltip_msg) | ||
301 | { | ||
302 | // Scroll folder view if needed. Never accepts a drag or drop. | ||
303 | *accept = ACCEPT_NO; | ||
304 | BOOL handled = FALSE; | ||
305 | if( mScrollbar[HORIZONTAL]->getVisible() || mScrollbar[VERTICAL]->getVisible() ) | ||
306 | { | ||
307 | const S32 AUTOSCROLL_SIZE = 10; | ||
308 | S32 auto_scroll_speed = llround(mAutoScrollRate * LLFrameTimer::getFrameDeltaTimeF32()); | ||
309 | |||
310 | LLRect inner_rect_local( 0, mInnerRect.getHeight(), mInnerRect.getWidth(), 0 ); | ||
311 | if( mScrollbar[HORIZONTAL]->getVisible() ) | ||
312 | { | ||
313 | inner_rect_local.mBottom += SCROLLBAR_SIZE; | ||
314 | } | ||
315 | if( mScrollbar[VERTICAL]->getVisible() ) | ||
316 | { | ||
317 | inner_rect_local.mRight -= SCROLLBAR_SIZE; | ||
318 | } | ||
319 | |||
320 | if( mScrollbar[HORIZONTAL]->getVisible() ) | ||
321 | { | ||
322 | LLRect left_scroll_rect = inner_rect_local; | ||
323 | left_scroll_rect.mRight = AUTOSCROLL_SIZE; | ||
324 | if( left_scroll_rect.pointInRect( x, y ) && (mScrollbar[HORIZONTAL]->getDocPos() > 0) ) | ||
325 | { | ||
326 | mScrollbar[HORIZONTAL]->setDocPos( mScrollbar[HORIZONTAL]->getDocPos() - auto_scroll_speed ); | ||
327 | mAutoScrolling = TRUE; | ||
328 | handled = TRUE; | ||
329 | } | ||
330 | |||
331 | LLRect right_scroll_rect = inner_rect_local; | ||
332 | right_scroll_rect.mLeft = inner_rect_local.mRight - AUTOSCROLL_SIZE; | ||
333 | if( right_scroll_rect.pointInRect( x, y ) && (mScrollbar[HORIZONTAL]->getDocPos() < mScrollbar[HORIZONTAL]->getDocPosMax()) ) | ||
334 | { | ||
335 | mScrollbar[HORIZONTAL]->setDocPos( mScrollbar[HORIZONTAL]->getDocPos() + auto_scroll_speed ); | ||
336 | mAutoScrolling = TRUE; | ||
337 | handled = TRUE; | ||
338 | } | ||
339 | } | ||
340 | if( mScrollbar[VERTICAL]->getVisible() ) | ||
341 | { | ||
342 | LLRect bottom_scroll_rect = inner_rect_local; | ||
343 | bottom_scroll_rect.mTop = AUTOSCROLL_SIZE + bottom_scroll_rect.mBottom; | ||
344 | if( bottom_scroll_rect.pointInRect( x, y ) && (mScrollbar[VERTICAL]->getDocPos() < mScrollbar[VERTICAL]->getDocPosMax()) ) | ||
345 | { | ||
346 | mScrollbar[VERTICAL]->setDocPos( mScrollbar[VERTICAL]->getDocPos() + auto_scroll_speed ); | ||
347 | mAutoScrolling = TRUE; | ||
348 | handled = TRUE; | ||
349 | } | ||
350 | |||
351 | LLRect top_scroll_rect = inner_rect_local; | ||
352 | top_scroll_rect.mBottom = inner_rect_local.mTop - AUTOSCROLL_SIZE; | ||
353 | if( top_scroll_rect.pointInRect( x, y ) && (mScrollbar[VERTICAL]->getDocPos() > 0) ) | ||
354 | { | ||
355 | mScrollbar[VERTICAL]->setDocPos( mScrollbar[VERTICAL]->getDocPos() - auto_scroll_speed ); | ||
356 | mAutoScrolling = TRUE; | ||
357 | handled = TRUE; | ||
358 | } | ||
359 | } | ||
360 | } | ||
361 | |||
362 | if( !handled ) | ||
363 | { | ||
364 | handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, | ||
365 | cargo_data, accept, tooltip_msg) != NULL; | ||
366 | } | ||
367 | |||
368 | return TRUE; | ||
369 | } | ||
370 | |||
371 | |||
372 | BOOL LLScrollableContainerView::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect) | ||
373 | { | ||
374 | if( getVisible() && pointInView(x,y) ) | ||
375 | { | ||
376 | S32 local_x, local_y; | ||
377 | for( S32 i = 0; i < SCROLLBAR_COUNT; i++ ) | ||
378 | { | ||
379 | local_x = x - mScrollbar[i]->getRect().mLeft; | ||
380 | local_y = y - mScrollbar[i]->getRect().mBottom; | ||
381 | if( mScrollbar[i]->handleToolTip(local_x, local_y, msg, sticky_rect) ) | ||
382 | { | ||
383 | return TRUE; | ||
384 | } | ||
385 | } | ||
386 | // Handle 'child' view. | ||
387 | if( mScrolledView ) | ||
388 | { | ||
389 | local_x = x - mScrolledView->getRect().mLeft; | ||
390 | local_y = y - mScrolledView->getRect().mBottom; | ||
391 | if( mScrolledView->handleToolTip(local_x, local_y, msg, sticky_rect) ) | ||
392 | { | ||
393 | return TRUE; | ||
394 | } | ||
395 | } | ||
396 | |||
397 | // Opaque | ||
398 | return TRUE; | ||
399 | } | ||
400 | return FALSE; | ||
401 | } | ||
402 | |||
403 | void LLScrollableContainerView::calcVisibleSize( S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) | ||
404 | { | ||
405 | const LLRect& rect = mScrolledView->getRect(); | ||
406 | calcVisibleSize(rect, visible_width, visible_height, show_h_scrollbar, show_v_scrollbar); | ||
407 | } | ||
408 | |||
409 | void LLScrollableContainerView::calcVisibleSize( const LLRect& doc_rect, S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) | ||
410 | { | ||
411 | S32 doc_width = doc_rect.getWidth(); | ||
412 | S32 doc_height = doc_rect.getHeight(); | ||
413 | |||
414 | *visible_width = mRect.getWidth() - 2 * mBorder->getBorderWidth(); | ||
415 | *visible_height = mRect.getHeight() - 2 * mBorder->getBorderWidth(); | ||
416 | |||
417 | *show_v_scrollbar = FALSE; | ||
418 | if( *visible_height < doc_height ) | ||
419 | { | ||
420 | *show_v_scrollbar = TRUE; | ||
421 | *visible_width -= SCROLLBAR_SIZE; | ||
422 | } | ||
423 | |||
424 | *show_h_scrollbar = FALSE; | ||
425 | if( *visible_width < doc_width ) | ||
426 | { | ||
427 | *show_h_scrollbar = TRUE; | ||
428 | *visible_height -= SCROLLBAR_SIZE; | ||
429 | |||
430 | // Must retest now that visible_height has changed | ||
431 | if( !*show_v_scrollbar && (*visible_height < doc_height) ) | ||
432 | { | ||
433 | *show_v_scrollbar = TRUE; | ||
434 | *visible_width -= SCROLLBAR_SIZE; | ||
435 | } | ||
436 | } | ||
437 | } | ||
438 | |||
439 | void LLScrollableContainerView::draw() | ||
440 | { | ||
441 | if (mAutoScrolling) | ||
442 | { | ||
443 | // add acceleration to autoscroll | ||
444 | mAutoScrollRate = llmin(mAutoScrollRate + (LLFrameTimer::getFrameDeltaTimeF32() * AUTO_SCROLL_RATE_ACCEL), MAX_AUTO_SCROLL_RATE); | ||
445 | } | ||
446 | else | ||
447 | { | ||
448 | // reset to minimum | ||
449 | mAutoScrollRate = MIN_AUTO_SCROLL_RATE; | ||
450 | } | ||
451 | // clear this flag to be set on next call to handleDragAndDrop | ||
452 | mAutoScrolling = FALSE; | ||
453 | |||
454 | if( getVisible() ) | ||
455 | { | ||
456 | // auto-focus when scrollbar active | ||
457 | // this allows us to capture user intent (i.e. stop automatically scrolling the view/etc) | ||
458 | if (!gFocusMgr.childHasKeyboardFocus(this) && | ||
459 | (gFocusMgr.getMouseCapture() == mScrollbar[VERTICAL] || gFocusMgr.getMouseCapture() == mScrollbar[HORIZONTAL])) | ||
460 | { | ||
461 | focusFirstItem(); | ||
462 | } | ||
463 | |||
464 | // Draw background | ||
465 | if( mIsOpaque ) | ||
466 | { | ||
467 | LLGLSNoTexture no_texture; | ||
468 | glColor4fv( mBackgroundColor.mV ); | ||
469 | gl_rect_2d( mInnerRect ); | ||
470 | } | ||
471 | |||
472 | // Draw mScrolledViews and update scroll bars. | ||
473 | // get a scissor region ready, and draw the scrolling view. The | ||
474 | // scissor region ensures that we don't draw outside of the bounds | ||
475 | // of the rectangle. | ||
476 | if( mScrolledView ) | ||
477 | { | ||
478 | updateScroll(); | ||
479 | |||
480 | // Draw the scrolled area. | ||
481 | { | ||
482 | S32 visible_width = 0; | ||
483 | S32 visible_height = 0; | ||
484 | BOOL show_v_scrollbar = FALSE; | ||
485 | BOOL show_h_scrollbar = FALSE; | ||
486 | calcVisibleSize( mScrolledView->getRect(), &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar ); | ||
487 | |||
488 | LLGLEnable scissor_test(GL_SCISSOR_TEST); | ||
489 | LLUI::setScissorRegionLocal(LLRect(mInnerRect.mLeft, | ||
490 | mInnerRect.mBottom + (show_h_scrollbar ? SCROLLBAR_SIZE : 0) + visible_height, | ||
491 | visible_width, | ||
492 | mInnerRect.mBottom + (show_h_scrollbar ? SCROLLBAR_SIZE : 0) | ||
493 | )); | ||
494 | drawChild(mScrolledView); | ||
495 | } | ||
496 | } | ||
497 | |||
498 | // Highlight border if a child of this container has keyboard focus | ||
499 | if( mBorder->getVisible() ) | ||
500 | { | ||
501 | mBorder->setKeyboardFocusHighlight( gFocusMgr.childHasKeyboardFocus(this) ); | ||
502 | } | ||
503 | |||
504 | // Draw all children except mScrolledView | ||
505 | // Note: scrollbars have been adjusted by above drawing code | ||
506 | for (child_list_const_reverse_iter_t child_iter = getChildList()->rbegin(); | ||
507 | child_iter != getChildList()->rend(); ++child_iter) | ||
508 | { | ||
509 | LLView *viewp = *child_iter; | ||
510 | if( sDebugRects ) | ||
511 | { | ||
512 | sDepth++; | ||
513 | } | ||
514 | if( (viewp != mScrolledView) && viewp->getVisible() ) | ||
515 | { | ||
516 | drawChild(viewp); | ||
517 | } | ||
518 | if( sDebugRects ) | ||
519 | { | ||
520 | sDepth--; | ||
521 | } | ||
522 | } | ||
523 | |||
524 | if (sDebugRects) | ||
525 | { | ||
526 | drawDebugRect(); | ||
527 | } | ||
528 | } | ||
529 | } | ||
530 | |||
531 | void LLScrollableContainerView::updateScroll() | ||
532 | { | ||
533 | LLRect doc_rect = mScrolledView->getRect(); | ||
534 | S32 doc_width = doc_rect.getWidth(); | ||
535 | S32 doc_height = doc_rect.getHeight(); | ||
536 | S32 visible_width = 0; | ||
537 | S32 visible_height = 0; | ||
538 | BOOL show_v_scrollbar = FALSE; | ||
539 | BOOL show_h_scrollbar = FALSE; | ||
540 | calcVisibleSize( doc_rect, &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar ); | ||
541 | |||
542 | S32 border_width = mBorder->getBorderWidth(); | ||
543 | if( show_v_scrollbar ) | ||
544 | { | ||
545 | if( doc_rect.mTop < mRect.getHeight() - border_width ) | ||
546 | { | ||
547 | mScrolledView->translate( 0, mRect.getHeight() - border_width - doc_rect.mTop ); | ||
548 | } | ||
549 | |||
550 | scrollVertical( mScrollbar[VERTICAL]->getDocPos() ); | ||
551 | mScrollbar[VERTICAL]->setVisible( TRUE ); | ||
552 | |||
553 | S32 v_scrollbar_height = visible_height; | ||
554 | if( !show_h_scrollbar && mReserveScrollCorner ) | ||
555 | { | ||
556 | v_scrollbar_height -= SCROLLBAR_SIZE; | ||
557 | } | ||
558 | mScrollbar[VERTICAL]->reshape( SCROLLBAR_SIZE, v_scrollbar_height, TRUE ); | ||
559 | |||
560 | // Make room for the horizontal scrollbar (or not) | ||
561 | S32 v_scrollbar_offset = 0; | ||
562 | if( show_h_scrollbar || mReserveScrollCorner ) | ||
563 | { | ||
564 | v_scrollbar_offset = SCROLLBAR_SIZE; | ||
565 | } | ||
566 | LLRect r = mScrollbar[VERTICAL]->getRect(); | ||
567 | r.translate( 0, mInnerRect.mBottom - r.mBottom + v_scrollbar_offset ); | ||
568 | mScrollbar[VERTICAL]->setRect( r ); | ||
569 | } | ||
570 | else | ||
571 | { | ||
572 | mScrolledView->translate( 0, mRect.getHeight() - border_width - doc_rect.mTop ); | ||
573 | |||
574 | mScrollbar[VERTICAL]->setVisible( FALSE ); | ||
575 | mScrollbar[VERTICAL]->setDocPos( 0 ); | ||
576 | } | ||
577 | |||
578 | if( show_h_scrollbar ) | ||
579 | { | ||
580 | if( doc_rect.mLeft > border_width ) | ||
581 | { | ||
582 | mScrolledView->translate( border_width - doc_rect.mLeft, 0 ); | ||
583 | mScrollbar[HORIZONTAL]->setDocPos( 0 ); | ||
584 | } | ||
585 | else | ||
586 | { | ||
587 | scrollHorizontal( mScrollbar[HORIZONTAL]->getDocPos() ); | ||
588 | } | ||
589 | |||
590 | mScrollbar[HORIZONTAL]->setVisible( TRUE ); | ||
591 | S32 h_scrollbar_width = visible_width; | ||
592 | if( !show_v_scrollbar && mReserveScrollCorner ) | ||
593 | { | ||
594 | h_scrollbar_width -= SCROLLBAR_SIZE; | ||
595 | } | ||
596 | mScrollbar[HORIZONTAL]->reshape( h_scrollbar_width, SCROLLBAR_SIZE, TRUE ); | ||
597 | } | ||
598 | else | ||
599 | { | ||
600 | mScrolledView->translate( border_width - doc_rect.mLeft, 0 ); | ||
601 | |||
602 | mScrollbar[HORIZONTAL]->setVisible( FALSE ); | ||
603 | mScrollbar[HORIZONTAL]->setDocPos( 0 ); | ||
604 | } | ||
605 | |||
606 | mScrollbar[HORIZONTAL]->setDocSize( doc_width ); | ||
607 | mScrollbar[HORIZONTAL]->setPageSize( visible_width ); | ||
608 | |||
609 | mScrollbar[VERTICAL]->setDocSize( doc_height ); | ||
610 | mScrollbar[VERTICAL]->setPageSize( visible_height ); | ||
611 | } | ||
612 | |||
613 | void LLScrollableContainerView::setBorderVisible(BOOL b) | ||
614 | { | ||
615 | mBorder->setVisible( b ); | ||
616 | } | ||
617 | |||
618 | // Scroll so that as much of rect as possible is showing (where rect is defined in the space of scroller view, not scrolled) | ||
619 | void LLScrollableContainerView::scrollToShowRect(const LLRect& rect, const LLCoordGL& desired_offset) | ||
620 | { | ||
621 | if (!mScrolledView) | ||
622 | { | ||
623 | llwarns << "LLScrollableContainerView::scrollToShowRect with no view!" << llendl; | ||
624 | return; | ||
625 | } | ||
626 | |||
627 | S32 visible_width = 0; | ||
628 | S32 visible_height = 0; | ||
629 | BOOL show_v_scrollbar = FALSE; | ||
630 | BOOL show_h_scrollbar = FALSE; | ||
631 | const LLRect& scrolled_rect = mScrolledView->getRect(); | ||
632 | calcVisibleSize( scrolled_rect, &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar ); | ||
633 | |||
634 | // can't be so far left that right side of rect goes off screen, or so far right that left side does | ||
635 | S32 horiz_offset = llclamp(desired_offset.mX, llmin(0, -visible_width + rect.getWidth()), 0); | ||
636 | // can't be so high that bottom of rect goes off screen, or so low that top does | ||
637 | S32 vert_offset = llclamp(desired_offset.mY, 0, llmax(0, visible_height - rect.getHeight())); | ||
638 | |||
639 | // Vertical | ||
640 | // 1. First make sure the top is visible | ||
641 | // 2. Then, if possible without hiding the top, make the bottom visible. | ||
642 | S32 vert_pos = mScrollbar[VERTICAL]->getDocPos(); | ||
643 | |||
644 | // find scrollbar position to get top of rect on screen (scrolling up) | ||
645 | S32 top_offset = scrolled_rect.mTop - rect.mTop - vert_offset; | ||
646 | // find scrollbar position to get bottom of rect on screen (scrolling down) | ||
647 | S32 bottom_offset = vert_offset == 0 ? scrolled_rect.mTop - rect.mBottom - visible_height : top_offset; | ||
648 | // scroll up far enough to see top or scroll down just enough if item is bigger than visual area | ||
649 | if( vert_pos >= top_offset || visible_height < rect.getHeight()) | ||
650 | { | ||
651 | vert_pos = top_offset; | ||
652 | } | ||
653 | // else scroll down far enough to see bottom | ||
654 | else | ||
655 | if( vert_pos <= bottom_offset ) | ||
656 | { | ||
657 | vert_pos = bottom_offset; | ||
658 | } | ||
659 | |||
660 | mScrollbar[VERTICAL]->setDocSize( scrolled_rect.getHeight() ); | ||
661 | mScrollbar[VERTICAL]->setPageSize( visible_height ); | ||
662 | mScrollbar[VERTICAL]->setDocPos( vert_pos ); | ||
663 | |||
664 | // Horizontal | ||
665 | // 1. First make sure left side is visible | ||
666 | // 2. Then, if possible without hiding the left side, make the right side visible. | ||
667 | S32 horiz_pos = mScrollbar[HORIZONTAL]->getDocPos(); | ||
668 | S32 left_offset = rect.mLeft - scrolled_rect.mLeft + horiz_offset; | ||
669 | S32 right_offset = horiz_offset == 0 ? rect.mRight - scrolled_rect.mLeft - visible_width : left_offset; | ||
670 | |||
671 | if( horiz_pos >= left_offset || visible_width < rect.getWidth() ) | ||
672 | { | ||
673 | horiz_pos = left_offset; | ||
674 | } | ||
675 | else if( horiz_pos <= right_offset ) | ||
676 | { | ||
677 | horiz_pos = right_offset; | ||
678 | } | ||
679 | |||
680 | mScrollbar[HORIZONTAL]->setDocSize( scrolled_rect.getWidth() ); | ||
681 | mScrollbar[HORIZONTAL]->setPageSize( visible_width ); | ||
682 | mScrollbar[HORIZONTAL]->setDocPos( horiz_pos ); | ||
683 | |||
684 | // propagate scroll to document | ||
685 | updateScroll(); | ||
686 | } | ||
687 | |||
688 | void LLScrollableContainerView::pageUp(S32 overlap) | ||
689 | { | ||
690 | mScrollbar[VERTICAL]->pageUp(overlap); | ||
691 | } | ||
692 | |||
693 | void LLScrollableContainerView::pageDown(S32 overlap) | ||
694 | { | ||
695 | mScrollbar[VERTICAL]->pageDown(overlap); | ||
696 | } | ||
697 | |||
698 | void LLScrollableContainerView::goToTop() | ||
699 | { | ||
700 | mScrollbar[VERTICAL]->setDocPos(0); | ||
701 | } | ||
702 | |||
703 | void LLScrollableContainerView::goToBottom() | ||
704 | { | ||
705 | mScrollbar[VERTICAL]->setDocPos(mScrollbar[VERTICAL]->getDocSize()); | ||
706 | } | ||
707 | |||
708 | S32 LLScrollableContainerView::getBorderWidth() | ||
709 | { | ||
710 | if (mBorder) | ||
711 | { | ||
712 | return mBorder->getBorderWidth(); | ||
713 | } | ||
714 | |||
715 | return 0; | ||
716 | } | ||
717 | |||
718 | // virtual | ||
719 | LLXMLNodePtr LLScrollableContainerView::getXML(bool save_children) const | ||
720 | { | ||
721 | LLXMLNodePtr node = LLView::getXML(); | ||
722 | |||
723 | // Attributes | ||
724 | |||
725 | node->createChild("opaque", TRUE)->setBoolValue(mIsOpaque); | ||
726 | |||
727 | if (mIsOpaque) | ||
728 | { | ||
729 | node->createChild("color", TRUE)->setFloatValue(4, mBackgroundColor.mV); | ||
730 | } | ||
731 | |||
732 | // Contents | ||
733 | |||
734 | LLXMLNodePtr child_node = mScrolledView->getXML(); | ||
735 | |||
736 | node->addChild(child_node); | ||
737 | |||
738 | return node; | ||
739 | } | ||
740 | |||
741 | LLView* LLScrollableContainerView::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) | ||
742 | { | ||
743 | LLString name("scroll_container"); | ||
744 | node->getAttributeString("name", name); | ||
745 | |||
746 | LLRect rect; | ||
747 | createRect(node, rect, parent, LLRect()); | ||
748 | |||
749 | BOOL opaque = FALSE; | ||
750 | node->getAttributeBOOL("opaque", opaque); | ||
751 | |||
752 | LLColor4 color(0,0,0,0); | ||
753 | LLUICtrlFactory::getAttributeColor(node,"color", color); | ||
754 | |||
755 | // Create the scroll view | ||
756 | LLScrollableContainerView *ret = new LLScrollableContainerView(name, rect, (LLPanel*)NULL, opaque, color); | ||
757 | |||
758 | LLPanel* panelp = NULL; | ||
759 | |||
760 | // Find a child panel to add | ||
761 | LLXMLNodePtr child; | ||
762 | for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) | ||
763 | { | ||
764 | LLView *control = factory->createCtrlWidget(panelp, child); | ||
765 | if (control && control->isPanel()) | ||
766 | { | ||
767 | if (panelp) | ||
768 | { | ||
769 | llinfos << "Warning! Attempting to put multiple panels into a scrollable container view!" << llendl; | ||
770 | delete control; | ||
771 | } | ||
772 | else | ||
773 | { | ||
774 | panelp = (LLPanel*)control; | ||
775 | } | ||
776 | } | ||
777 | } | ||
778 | |||
779 | if (panelp == NULL) | ||
780 | { | ||
781 | panelp = new LLPanel("dummy", LLRect::null, FALSE); | ||
782 | } | ||
783 | |||
784 | ret->mScrolledView = panelp; | ||
785 | |||
786 | return ret; | ||
787 | } | ||
788 | |||
789 | ///---------------------------------------------------------------------------- | ||
790 | /// Local function definitions | ||
791 | ///---------------------------------------------------------------------------- | ||