00001
00032 #include "linden_common.h"
00033
00034 #include "llscrollbar.h"
00035
00036 #include "llmath.h"
00037 #include "lltimer.h"
00038 #include "v3color.h"
00039
00040 #include "llbutton.h"
00041 #include "llcriticaldamp.h"
00042 #include "llkeyboard.h"
00043 #include "llui.h"
00044
00045 #include "llfocusmgr.h"
00046 #include "llwindow.h"
00047 #include "llglheaders.h"
00048 #include "llcontrol.h"
00049 #include "llglimmediate.h"
00050
00051 LLScrollbar::LLScrollbar(
00052 const LLString& name, LLRect rect,
00053 LLScrollbar::ORIENTATION orientation,
00054 S32 doc_size, S32 doc_pos, S32 page_size,
00055 void (*change_callback)( S32 new_pos, LLScrollbar* self, void* userdata ),
00056 void* callback_user_data,
00057 S32 step_size)
00058 : LLUICtrl( name, rect, TRUE, NULL, NULL ),
00059
00060 mChangeCallback( change_callback ),
00061 mCallbackUserData( callback_user_data ),
00062 mOrientation( orientation ),
00063 mDocSize( doc_size ),
00064 mDocPos( doc_pos ),
00065 mPageSize( page_size ),
00066 mStepSize( step_size ),
00067 mDocChanged(FALSE),
00068 mDragStartX( 0 ),
00069 mDragStartY( 0 ),
00070 mHoverGlowStrength(0.15f),
00071 mCurGlowStrength(0.f),
00072 mTrackColor( LLUI::sColorsGroup->getColor("ScrollbarTrackColor") ),
00073 mThumbColor ( LLUI::sColorsGroup->getColor("ScrollbarThumbColor") ),
00074 mHighlightColor ( LLUI::sColorsGroup->getColor("DefaultHighlightLight") ),
00075 mShadowColor ( LLUI::sColorsGroup->getColor("DefaultShadowLight") ),
00076 mOnScrollEndCallback( NULL ),
00077 mOnScrollEndData( NULL )
00078 {
00079
00080
00081
00082 setTabStop(FALSE);
00083 updateThumbRect();
00084
00085
00086 LLRect line_up_rect;
00087 LLString line_up_img;
00088 LLString line_up_selected_img;
00089 LLString line_down_img;
00090 LLString line_down_selected_img;
00091
00092 LLRect line_down_rect;
00093
00094 if( LLScrollbar::VERTICAL == mOrientation )
00095 {
00096 line_up_rect.setLeftTopAndSize( 0, getRect().getHeight(), SCROLLBAR_SIZE, SCROLLBAR_SIZE );
00097 line_up_img="UIImgBtnScrollUpOutUUID";
00098 line_up_selected_img="UIImgBtnScrollUpInUUID";
00099
00100 line_down_rect.setOriginAndSize( 0, 0, SCROLLBAR_SIZE, SCROLLBAR_SIZE );
00101 line_down_img="UIImgBtnScrollDownOutUUID";
00102 line_down_selected_img="UIImgBtnScrollDownInUUID";
00103 }
00104 else
00105 {
00106
00107 line_up_rect.setOriginAndSize( 0, 0, SCROLLBAR_SIZE, SCROLLBAR_SIZE );
00108 line_up_img="UIImgBtnScrollLeftOutUUID";
00109 line_up_selected_img="UIImgBtnScrollLeftInUUID";
00110
00111 line_down_rect.setOriginAndSize( getRect().getWidth() - SCROLLBAR_SIZE, 0, SCROLLBAR_SIZE, SCROLLBAR_SIZE );
00112 line_down_img="UIImgBtnScrollRightOutUUID";
00113 line_down_selected_img="UIImgBtnScrollRightInUUID";
00114 }
00115
00116 LLButton* line_up_btn = new LLButton(
00117 "Line Up", line_up_rect,
00118 line_up_img, line_up_selected_img, "",
00119 &LLScrollbar::onLineUpBtnPressed, this, LLFontGL::sSansSerif );
00120 if( LLScrollbar::VERTICAL == mOrientation )
00121 {
00122 line_up_btn->setFollowsRight();
00123 line_up_btn->setFollowsTop();
00124 }
00125 else
00126 {
00127
00128 line_up_btn->setFollowsLeft();
00129 line_up_btn->setFollowsBottom();
00130 }
00131 line_up_btn->setHeldDownCallback( &LLScrollbar::onLineUpBtnPressed );
00132 line_up_btn->setTabStop(FALSE);
00133 addChild(line_up_btn);
00134
00135 LLButton* line_down_btn = new LLButton(
00136 "Line Down", line_down_rect,
00137 line_down_img, line_down_selected_img, "",
00138 &LLScrollbar::onLineDownBtnPressed, this, LLFontGL::sSansSerif );
00139 line_down_btn->setFollowsRight();
00140 line_down_btn->setFollowsBottom();
00141 line_down_btn->setHeldDownCallback( &LLScrollbar::onLineDownBtnPressed );
00142 line_down_btn->setTabStop(FALSE);
00143 addChild(line_down_btn);
00144 }
00145
00146
00147 LLScrollbar::~LLScrollbar()
00148 {
00149
00150 }
00151
00152 void LLScrollbar::setDocParams( S32 size, S32 pos )
00153 {
00154 mDocSize = size;
00155 mDocPos = llclamp( pos, 0, getDocPosMax() );
00156 mDocChanged = TRUE;
00157
00158 updateThumbRect();
00159 }
00160
00161 void LLScrollbar::setDocPos(S32 pos)
00162 {
00163 if (pos != mDocPos)
00164 {
00165 mDocPos = llclamp( pos, 0, getDocPosMax() );
00166 mDocChanged = TRUE;
00167
00168 updateThumbRect();
00169 }
00170 }
00171
00172 void LLScrollbar::setDocSize(S32 size)
00173 {
00174 if (size != mDocSize)
00175 {
00176 mDocSize = size;
00177 mDocPos = llclamp( mDocPos, 0, getDocPosMax() );
00178 mDocChanged = TRUE;
00179
00180 updateThumbRect();
00181 }
00182 }
00183
00184 void LLScrollbar::setPageSize( S32 page_size )
00185 {
00186 if (page_size != mPageSize)
00187 {
00188 mPageSize = page_size;
00189 mDocPos = llclamp( mDocPos, 0, getDocPosMax() );
00190 mDocChanged = TRUE;
00191
00192 updateThumbRect();
00193 }
00194 }
00195
00196 BOOL LLScrollbar::isAtBeginning()
00197 {
00198 return mDocPos == 0;
00199 }
00200
00201 BOOL LLScrollbar::isAtEnd()
00202 {
00203 return mDocPos == getDocPosMax();
00204 }
00205
00206
00207 void LLScrollbar::updateThumbRect()
00208 {
00209
00210
00211
00212 const S32 THUMB_MIN_LENGTH = 16;
00213
00214 S32 window_length = (mOrientation == LLScrollbar::HORIZONTAL) ? getRect().getWidth() : getRect().getHeight();
00215 S32 thumb_bg_length = window_length - 2 * SCROLLBAR_SIZE;
00216 S32 visible_lines = llmin( mDocSize, mPageSize );
00217 S32 thumb_length = mDocSize ? llmax( visible_lines * thumb_bg_length / mDocSize, THUMB_MIN_LENGTH ) : thumb_bg_length;
00218
00219 S32 variable_lines = mDocSize - visible_lines;
00220
00221 if( mOrientation == LLScrollbar::VERTICAL )
00222 {
00223 S32 thumb_start_max = thumb_bg_length + SCROLLBAR_SIZE;
00224 S32 thumb_start_min = SCROLLBAR_SIZE + THUMB_MIN_LENGTH;
00225 S32 thumb_start = variable_lines ? llclamp( thumb_start_max - (mDocPos * (thumb_bg_length - thumb_length)) / variable_lines, thumb_start_min, thumb_start_max ) : thumb_start_max;
00226
00227 mThumbRect.mLeft = 0;
00228 mThumbRect.mTop = thumb_start;
00229 mThumbRect.mRight = SCROLLBAR_SIZE;
00230 mThumbRect.mBottom = thumb_start - thumb_length;
00231 }
00232 else
00233 {
00234
00235 S32 thumb_start_max = thumb_bg_length + SCROLLBAR_SIZE - thumb_length;
00236 S32 thumb_start_min = SCROLLBAR_SIZE;
00237 S32 thumb_start = variable_lines ? llclamp( thumb_start_min + (mDocPos * (thumb_bg_length - thumb_length)) / variable_lines, thumb_start_min, thumb_start_max ) : thumb_start_min;
00238
00239 mThumbRect.mLeft = thumb_start;
00240 mThumbRect.mTop = SCROLLBAR_SIZE;
00241 mThumbRect.mRight = thumb_start + thumb_length;
00242 mThumbRect.mBottom = 0;
00243 }
00244
00245 if (mOnScrollEndCallback && mOnScrollEndData && (mDocPos == getDocPosMax()))
00246 {
00247 mOnScrollEndCallback(mOnScrollEndData);
00248 }
00249 }
00250
00251 BOOL LLScrollbar::handleMouseDown(S32 x, S32 y, MASK mask)
00252 {
00253
00254 BOOL handled_by_child = LLView::childrenHandleMouseDown(x, y, mask) != NULL;
00255 if( !handled_by_child )
00256 {
00257 if( mThumbRect.pointInRect(x,y) )
00258 {
00259
00260
00261 gFocusMgr.setMouseCapture( this );
00262 mDragStartX = x;
00263 mDragStartY = y;
00264 mOrigRect.mTop = mThumbRect.mTop;
00265 mOrigRect.mBottom = mThumbRect.mBottom;
00266 mOrigRect.mLeft = mThumbRect.mLeft;
00267 mOrigRect.mRight = mThumbRect.mRight;
00268 mLastDelta = 0;
00269 }
00270 else
00271 {
00272 if(
00273 ( (LLScrollbar::VERTICAL == mOrientation) && (mThumbRect.mTop < y) ) ||
00274 ( (LLScrollbar::HORIZONTAL == mOrientation) && (x < mThumbRect.mLeft) )
00275 )
00276 {
00277
00278 pageUp(0);
00279 }
00280 else
00281 if(
00282 ( (LLScrollbar::VERTICAL == mOrientation) && (y < mThumbRect.mBottom) ) ||
00283 ( (LLScrollbar::HORIZONTAL == mOrientation) && (mThumbRect.mRight < x) )
00284 )
00285 {
00286
00287 pageDown(0);
00288 }
00289 }
00290 }
00291
00292 return TRUE;
00293 }
00294
00295
00296 BOOL LLScrollbar::handleHover(S32 x, S32 y, MASK mask)
00297 {
00298
00299
00300
00301 BOOL handled = FALSE;
00302 if( hasMouseCapture() )
00303 {
00304 S32 height = getRect().getHeight();
00305 S32 width = getRect().getWidth();
00306
00307 if( VERTICAL == mOrientation )
00308 {
00309
00310
00311 S32 delta_pixels = y - mDragStartY;
00312 if( mOrigRect.mBottom + delta_pixels < SCROLLBAR_SIZE )
00313 {
00314 delta_pixels = SCROLLBAR_SIZE - mOrigRect.mBottom - 1;
00315 }
00316 else
00317 if( mOrigRect.mTop + delta_pixels > height - SCROLLBAR_SIZE )
00318 {
00319 delta_pixels = height - SCROLLBAR_SIZE - mOrigRect.mTop + 1;
00320 }
00321
00322 mThumbRect.mTop = mOrigRect.mTop + delta_pixels;
00323 mThumbRect.mBottom = mOrigRect.mBottom + delta_pixels;
00324
00325 S32 thumb_length = mThumbRect.getHeight();
00326 S32 thumb_track_length = height - 2 * SCROLLBAR_SIZE;
00327
00328
00329 if( delta_pixels != mLastDelta || mDocChanged)
00330 {
00331
00332 S32 usable_track_length = thumb_track_length - thumb_length;
00333 if( 0 < usable_track_length )
00334 {
00335 S32 variable_lines = getDocPosMax();
00336 S32 pos = mThumbRect.mTop;
00337 F32 ratio = F32(pos - SCROLLBAR_SIZE - thumb_length) / usable_track_length;
00338
00339 S32 new_pos = llclamp( S32(variable_lines - ratio * variable_lines + 0.5f), 0, variable_lines );
00340
00341
00342 changeLine( new_pos - mDocPos, FALSE );
00343 }
00344 }
00345
00346 mLastDelta = delta_pixels;
00347
00348 }
00349 else
00350 {
00351
00352
00353
00354 S32 delta_pixels = x - mDragStartX;
00355
00356 if( mOrigRect.mLeft + delta_pixels < SCROLLBAR_SIZE )
00357 {
00358 delta_pixels = SCROLLBAR_SIZE - mOrigRect.mLeft - 1;
00359 }
00360 else
00361 if( mOrigRect.mRight + delta_pixels > width - SCROLLBAR_SIZE )
00362 {
00363 delta_pixels = width - SCROLLBAR_SIZE - mOrigRect.mRight + 1;
00364 }
00365
00366 mThumbRect.mLeft = mOrigRect.mLeft + delta_pixels;
00367 mThumbRect.mRight = mOrigRect.mRight + delta_pixels;
00368
00369 S32 thumb_length = mThumbRect.getWidth();
00370 S32 thumb_track_length = width - 2 * SCROLLBAR_SIZE;
00371
00372 if( delta_pixels != mLastDelta || mDocChanged)
00373 {
00374
00375 S32 usable_track_length = thumb_track_length - thumb_length;
00376 if( 0 < usable_track_length )
00377 {
00378 S32 variable_lines = getDocPosMax();
00379 S32 pos = mThumbRect.mLeft;
00380 F32 ratio = F32(pos - SCROLLBAR_SIZE) / usable_track_length;
00381
00382 S32 new_pos = llclamp( S32(ratio * variable_lines + 0.5f), 0, variable_lines);
00383
00384
00385
00386 changeLine( new_pos - mDocPos, FALSE );
00387 }
00388 }
00389
00390 mLastDelta = delta_pixels;
00391 }
00392
00393 getWindow()->setCursor(UI_CURSOR_ARROW);
00394 lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;
00395 handled = TRUE;
00396 }
00397 else
00398 {
00399 handled = childrenHandleMouseUp( x, y, mask ) != NULL;
00400 }
00401
00402
00403 if( !handled )
00404 {
00405 getWindow()->setCursor(UI_CURSOR_ARROW);
00406 lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl;
00407 handled = TRUE;
00408 }
00409
00410 mDocChanged = FALSE;
00411 return handled;
00412 }
00413
00414
00415 BOOL LLScrollbar::handleScrollWheel(S32 x, S32 y, S32 clicks)
00416 {
00417 changeLine( clicks * mStepSize, TRUE );
00418 return TRUE;
00419 }
00420
00421 BOOL LLScrollbar::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
00422 EDragAndDropType cargo_type, void *cargo_data, EAcceptance *accept, LLString &tooltip_msg)
00423 {
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440 return FALSE;
00441 }
00442
00443 BOOL LLScrollbar::handleMouseUp(S32 x, S32 y, MASK mask)
00444 {
00445 BOOL handled = FALSE;
00446 if( hasMouseCapture() )
00447 {
00448 gFocusMgr.setMouseCapture( NULL );
00449 handled = TRUE;
00450 }
00451 else
00452 {
00453
00454 handled = LLView::handleMouseUp( x, y, mask );
00455 }
00456
00457 return handled;
00458 }
00459
00460 void LLScrollbar::reshape(S32 width, S32 height, BOOL called_from_parent)
00461 {
00462 LLView::reshape( width, height, called_from_parent );
00463 updateThumbRect();
00464 }
00465
00466
00467 void LLScrollbar::draw()
00468 {
00469 S32 local_mouse_x;
00470 S32 local_mouse_y;
00471 LLCoordWindow cursor_pos_window;
00472 getWindow()->getCursorPosition(&cursor_pos_window);
00473 LLCoordGL cursor_pos_gl;
00474 getWindow()->convertCoords(cursor_pos_window, &cursor_pos_gl);
00475
00476 screenPointToLocal(cursor_pos_gl.mX, cursor_pos_gl.mY, &local_mouse_x, &local_mouse_y);
00477 BOOL other_captor = gFocusMgr.getMouseCapture() && gFocusMgr.getMouseCapture() != this;
00478 BOOL hovered = getEnabled() && !other_captor && (hasMouseCapture() || mThumbRect.pointInRect(local_mouse_x, local_mouse_y));
00479 if (hovered)
00480 {
00481 mCurGlowStrength = lerp(mCurGlowStrength, mHoverGlowStrength, LLCriticalDamp::getInterpolant(0.05f));
00482 }
00483 else
00484 {
00485 mCurGlowStrength = lerp(mCurGlowStrength, 0.f, LLCriticalDamp::getInterpolant(0.05f));
00486 }
00487
00488
00489
00490 LLUIImage* rounded_rect_imagep = LLUI::sImageProvider->getUIImage("rounded_square.tga");
00491
00492 if (!rounded_rect_imagep)
00493 {
00494 gl_rect_2d(mOrientation == HORIZONTAL ? SCROLLBAR_SIZE : 0,
00495 mOrientation == VERTICAL ? getRect().getHeight() - 2 * SCROLLBAR_SIZE : getRect().getHeight(),
00496 mOrientation == HORIZONTAL ? getRect().getWidth() - 2 * SCROLLBAR_SIZE : getRect().getWidth(),
00497 mOrientation == VERTICAL ? SCROLLBAR_SIZE : 0, mTrackColor, TRUE);
00498
00499 gl_rect_2d(mThumbRect, mThumbColor, TRUE);
00500
00501 }
00502 else
00503 {
00504
00505 rounded_rect_imagep->drawSolid(mOrientation == HORIZONTAL ? SCROLLBAR_SIZE : 0,
00506 mOrientation == VERTICAL ? SCROLLBAR_SIZE : 0,
00507 mOrientation == HORIZONTAL ? getRect().getWidth() - 2 * SCROLLBAR_SIZE : getRect().getWidth(),
00508 mOrientation == VERTICAL ? getRect().getHeight() - 2 * SCROLLBAR_SIZE : getRect().getHeight(),
00509 mTrackColor);
00510
00511
00512 LLRect outline_rect = mThumbRect;
00513 outline_rect.stretch(2);
00514
00515 if (gFocusMgr.getKeyboardFocus() == this)
00516 {
00517 rounded_rect_imagep->draw(outline_rect, gFocusMgr.getFocusColor());
00518 }
00519
00520 rounded_rect_imagep->draw(mThumbRect, mThumbColor);
00521 if (mCurGlowStrength > 0.01f)
00522 {
00523 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
00524 rounded_rect_imagep->drawSolid(mThumbRect, LLColor4(1.f, 1.f, 1.f, mCurGlowStrength));
00525 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00526 }
00527
00528 }
00529
00530 BOOL was_scrolled_to_bottom = (getDocPos() == getDocPosMax());
00531 if (mOnScrollEndCallback && was_scrolled_to_bottom)
00532 {
00533 mOnScrollEndCallback(mOnScrollEndData);
00534 }
00535
00536
00537 LLView::draw();
00538 }
00539
00540
00541 void LLScrollbar::changeLine( S32 delta, BOOL update_thumb )
00542 {
00543 S32 new_pos = llclamp( mDocPos + delta, 0, getDocPosMax() );
00544 if( new_pos != mDocPos )
00545 {
00546 mDocPos = new_pos;
00547 }
00548
00549 if( mChangeCallback )
00550 {
00551 mChangeCallback( mDocPos, this, mCallbackUserData );
00552 }
00553
00554 if( update_thumb )
00555 {
00556 updateThumbRect();
00557 }
00558 }
00559
00560 void LLScrollbar::setValue(const LLSD& value)
00561 {
00562 setDocPos((S32) value.asInteger());
00563 }
00564
00565
00566 BOOL LLScrollbar::handleKeyHere(KEY key, MASK mask)
00567 {
00568 BOOL handled = FALSE;
00569
00570 switch( key )
00571 {
00572 case KEY_HOME:
00573 changeLine( -mDocPos, TRUE );
00574 handled = TRUE;
00575 break;
00576
00577 case KEY_END:
00578 changeLine( getDocPosMax() - mDocPos, TRUE );
00579 handled = TRUE;
00580 break;
00581
00582 case KEY_DOWN:
00583 changeLine( mStepSize, TRUE );
00584 handled = TRUE;
00585 break;
00586
00587 case KEY_UP:
00588 changeLine( - mStepSize, TRUE );
00589 handled = TRUE;
00590 break;
00591
00592 case KEY_PAGE_DOWN:
00593 pageDown(1);
00594 break;
00595
00596 case KEY_PAGE_UP:
00597 pageUp(1);
00598 break;
00599 }
00600
00601 return handled;
00602 }
00603
00604 void LLScrollbar::pageUp(S32 overlap)
00605 {
00606 if (mDocSize > mPageSize)
00607 {
00608 changeLine( -(mPageSize - overlap), TRUE );
00609 }
00610 }
00611
00612 void LLScrollbar::pageDown(S32 overlap)
00613 {
00614 if (mDocSize > mPageSize)
00615 {
00616 changeLine( mPageSize - overlap, TRUE );
00617 }
00618 }
00619
00620
00621 void LLScrollbar::onLineUpBtnPressed( void* userdata )
00622 {
00623 LLScrollbar* self = (LLScrollbar*) userdata;
00624
00625 self->changeLine( - self->mStepSize, TRUE );
00626 }
00627
00628
00629 void LLScrollbar::onLineDownBtnPressed( void* userdata )
00630 {
00631 LLScrollbar* self = (LLScrollbar*) userdata;
00632 self->changeLine( self->mStepSize, TRUE );
00633 }
00634
00635