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