diff options
Diffstat (limited to 'indra/llui')
46 files changed, 2369 insertions, 1506 deletions
diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index 26ce473e08..ce3a4b64c7 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -59,9 +59,6 @@ S32 BTN_HEIGHT = 0; S32 BTN_GRID = 12; S32 BORDER_SIZE = 1; -// static -LLFrameTimer LLButton::sFlashingTimer; - LLButton::LLButton( const LLString& name, const LLRect& rect, const LLString& control_name, void (*click_callback)(void*), void *callback_data) : LLUICtrl(name, rect, TRUE, NULL, NULL), mClickedCallback( click_callback ), @@ -79,6 +76,7 @@ LLButton::LLButton( const LLString& name, const LLRect& rect, const LLString& co mImageDisabled( NULL ), mImageDisabledSelected( NULL ), mToggleState( FALSE ), + mIsToggle( FALSE ), mScaleImage( TRUE ), mDropShadowedText( TRUE ), mBorderEnabled( FALSE ), @@ -86,8 +84,6 @@ LLButton::LLButton( const LLString& name, const LLRect& rect, const LLString& co mHAlign( LLFontGL::HCENTER ), mLeftHPad( LLBUTTON_H_PAD ), mRightHPad( LLBUTTON_H_PAD ), - mFixedWidth( 16 ), - mFixedHeight( 16 ), mHoverGlowStrength(0.15f), mCurGlowStrength(0.f), mNeedsHighlight(FALSE), @@ -134,6 +130,7 @@ LLButton::LLButton(const LLString& name, const LLRect& rect, mImageDisabled( NULL ), mImageDisabledSelected( NULL ), mToggleState( FALSE ), + mIsToggle( FALSE ), mScaleImage( TRUE ), mDropShadowedText( TRUE ), mBorderEnabled( FALSE ), @@ -141,8 +138,6 @@ LLButton::LLButton(const LLString& name, const LLRect& rect, mHAlign( LLFontGL::HCENTER ), mLeftHPad( LLBUTTON_H_PAD ), mRightHPad( LLBUTTON_H_PAD ), - mFixedWidth( 16 ), - mFixedHeight( 16 ), mHoverGlowStrength(0.25f), mCurGlowStrength(0.f), mNeedsHighlight(FALSE), @@ -158,15 +153,11 @@ LLButton::LLButton(const LLString& name, const LLRect& rect, if( unselected_image_name != "" ) { + // user-specified image - don't use fixed borders unless requested setImageUnselected(unselected_image_name); setImageDisabled(unselected_image_name); mDisabledImageColor.mV[VALPHA] = 0.5f; - mImageDisabled = mImageUnselected; - mDisabledImageColor.mV[VALPHA] = 0.5f; - // user-specified image - don't use fixed borders unless requested - mFixedWidth = 0; - mFixedHeight = 0; mScaleImage = FALSE; } else @@ -177,13 +168,11 @@ LLButton::LLButton(const LLString& name, const LLRect& rect, if( selected_image_name != "" ) { + // user-specified image - don't use fixed borders unless requested setImageSelected(selected_image_name); setImageDisabledSelected(selected_image_name); mDisabledImageColor.mV[VALPHA] = 0.5f; - // user-specified image - don't use fixed borders unless requested - mFixedWidth = 0; - mFixedHeight = 0; mScaleImage = FALSE; } else @@ -273,6 +262,12 @@ void LLButton::onCommit() make_ui_sound("UISndClickRelease"); } + if (mIsToggle) + { + toggleState(); + } + + // do this last, as it can result in destroying this button if (mClickedCallback) { (*mClickedCallback)( mCallbackUserData ); @@ -286,6 +281,11 @@ BOOL LLButton::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent) BOOL handled = FALSE; if( getVisible() && mEnabled && !called_from_parent && ' ' == uni_char && !gKeyboard->getKeyRepeated(' ')) { + if (mIsToggle) + { + toggleState(); + } + if (mClickedCallback) { (*mClickedCallback)( mCallbackUserData ); @@ -302,11 +302,17 @@ BOOL LLButton::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent ) { if( mCommitOnReturn && KEY_RETURN == key && mask == MASK_NONE && !gKeyboard->getKeyRepeated(key)) { + if (mIsToggle) + { + toggleState(); + } + + handled = TRUE; + if (mClickedCallback) { (*mClickedCallback)( mCallbackUserData ); } - handled = TRUE; } } return handled; @@ -354,6 +360,9 @@ BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask) (*mMouseUpCallback)(mCallbackUserData); } + mMouseDownTimer.stop(); + mMouseDownTimer.reset(); + // DO THIS AT THE VERY END to allow the button to be destroyed as a result of being clicked. // If mouseup in the widget, it's been clicked if (pointInView(x, y)) @@ -363,6 +372,11 @@ BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask) make_ui_sound("UISndClickRelease"); } + if (mIsToggle) + { + toggleState(); + } + if (mClickedCallback) { (*mClickedCallback)( mCallbackUserData ); @@ -422,8 +436,10 @@ void LLButton::draw() BOOL flash = FALSE; if( mFlashing ) { - F32 elapsed = LLButton::sFlashingTimer.getElapsedTimeF32(); - flash = S32(elapsed * 2) & 1; + F32 elapsed = mFlashingTimer.getElapsedTimeF32(); + S32 flash_count = S32(elapsed * LLUI::sConfigGroup->getF32("ButtonFlashRate") * 2.f); + // flash on or off? + flash = (flash_count % 2 == 0) || flash_count > (F32)LLUI::sConfigGroup->getS32("ButtonFlashCount"); } BOOL pressed_by_keyboard = FALSE; @@ -443,24 +459,14 @@ void LLButton::draw() cursor_pos_gl.mY = llround((F32)cursor_pos_gl.mY / LLUI::sGLScaleFactor.mV[VY]); screenPointToLocal(cursor_pos_gl.mX, cursor_pos_gl.mY, &local_mouse_x, &local_mouse_y); - BOOL pressed = pressed_by_keyboard || (hasMouseCapture() && pointInView(local_mouse_x, local_mouse_y)); - - BOOL display_state = FALSE; - if( pressed ) - { - mImagep = mImageSelected; - // show the resulting state after releasing the mouse button while it is down - display_state = mToggleState ? FALSE : TRUE; - } - else - { - display_state = mToggleState || flash; - } + BOOL pressed = pressed_by_keyboard + || (hasMouseCapture() && pointInView(local_mouse_x, local_mouse_y)) + || mToggleState; BOOL use_glow_effect = FALSE; - if ( mNeedsHighlight ) + if ( mNeedsHighlight || flash ) { - if (display_state) + if (pressed) { if (mImageHoverSelected) { @@ -485,7 +491,7 @@ void LLButton::draw() } } } - else if ( display_state ) + else if ( pressed ) { mImagep = mImageSelected; } @@ -499,11 +505,11 @@ void LLButton::draw() // enabled and tentative // or // disabled but checked - if (!mImageDisabledSelected.isNull() && ( (mEnabled && mTentative) || (!mEnabled && display_state ) ) ) + if (!mImageDisabledSelected.isNull() && ( (mEnabled && mTentative) || (!mEnabled && pressed ) ) ) { mImagep = mImageDisabledSelected; } - else if (!mImageDisabled.isNull() && !mEnabled && !display_state) + else if (!mImageDisabled.isNull() && !mEnabled && !pressed) { mImagep = mImageDisabled; } @@ -516,33 +522,34 @@ void LLButton::draw() // Figure out appropriate color for the text LLColor4 label_color; + // label changes when button state changes, not when pressed if ( mEnabled ) { - if ( !display_state ) + if ( mToggleState ) { - label_color = mUnselectedLabelColor; + label_color = mSelectedLabelColor; } else { - label_color = mSelectedLabelColor; + label_color = mUnselectedLabelColor; } } else { - if ( !display_state ) + if ( mToggleState ) { - label_color = mDisabledLabelColor; + label_color = mDisabledSelectedLabelColor; } else { - label_color = mDisabledSelectedLabelColor; + label_color = mDisabledLabelColor; } } // Unselected label assignments LLWString label; - if( display_state ) + if( mToggleState ) { if( mEnabled || mDisabledSelectedLabel.empty() ) { @@ -591,24 +598,22 @@ void LLButton::draw() // Otherwise draw basic rectangular button. if( mImagep.notNull() && !mScaleImage) { - gl_draw_image( 0, 0, mImagep, mEnabled ? mImageColor : mDisabledImageColor ); + mImagep->draw(0, 0, mEnabled ? mImageColor : mDisabledImageColor ); if (mCurGlowStrength > 0.01f) { glBlendFunc(GL_SRC_ALPHA, GL_ONE); - gl_draw_scaled_image_with_border(0, 0, 0, 0, mImagep->getWidth(), mImagep->getHeight(), mImagep, LLColor4(1.f, 1.f, 1.f, mCurGlowStrength), TRUE); + mImagep->drawSolid(0, 0, LLColor4(1.f, 1.f, 1.f, mCurGlowStrength)); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } } else if ( mImagep.notNull() && mScaleImage) { - gl_draw_scaled_image_with_border(0, 0, mFixedWidth, mFixedHeight, mRect.getWidth(), mRect.getHeight(), - mImagep, mEnabled ? mImageColor : mDisabledImageColor ); + mImagep->draw(0, 0, mRect.getWidth(), mRect.getHeight(), mEnabled ? mImageColor : mDisabledImageColor ); if (mCurGlowStrength > 0.01f) { glBlendFunc(GL_SRC_ALPHA, GL_ONE); - gl_draw_scaled_image_with_border(0, 0, mFixedWidth, mFixedHeight, mRect.getWidth(), mRect.getHeight(), - mImagep, LLColor4(1.f, 1.f, 1.f, mCurGlowStrength), TRUE); + mImagep->drawSolid(0, 0, mRect.getWidth(), mRect.getHeight(), LLColor4(1.f, 1.f, 1.f, mCurGlowStrength)); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } } @@ -620,13 +625,17 @@ void LLButton::draw() gl_rect_2d(0, mRect.getHeight(), mRect.getWidth(), 0, LLColor4::pink1, FALSE); } + // let overlay image and text play well together + S32 text_left = mLeftHPad; + S32 text_right = mRect.getWidth() - mRightHPad; + S32 text_width = mRect.getWidth() - mLeftHPad - mRightHPad; + // draw overlay image if (mImageOverlay.notNull()) { - const S32 IMG_PAD = 5; // get max width and height (discard level 0) - S32 overlay_width = mImageOverlay->getWidth(0); - S32 overlay_height = mImageOverlay->getHeight(0); + S32 overlay_width = mImageOverlay->getWidth(); + S32 overlay_height = mImageOverlay->getHeight(); F32 scale_factor = llmin((F32)mRect.getWidth() / (F32)overlay_width, (F32)mRect.getHeight() / (F32)overlay_height, 1.f); overlay_width = llround((F32)overlay_width * scale_factor); @@ -635,34 +644,49 @@ void LLButton::draw() S32 center_x = getLocalRect().getCenterX(); S32 center_y = getLocalRect().getCenterY(); + //FUGLY HACK FOR "DEPRESSED" BUTTONS + if (pressed) + { + center_y--; + center_x++; + } + + // fade out overlay images on disabled buttons + LLColor4 overlay_color = mImageOverlayColor; + if (!getEnabled()) + { + overlay_color.mV[VALPHA] = 0.5f; + } + switch(mImageOverlayAlignment) { case LLFontGL::LEFT: - gl_draw_scaled_image( - IMG_PAD, + text_left += overlay_width + 1; + text_width -= overlay_width + 1; + mImageOverlay->draw( + mLeftHPad, center_y - (overlay_height / 2), overlay_width, overlay_height, - mImageOverlay, - mImageOverlayColor); + overlay_color); break; case LLFontGL::HCENTER: - gl_draw_scaled_image( + mImageOverlay->draw( center_x - (overlay_width / 2), center_y - (overlay_height / 2), overlay_width, overlay_height, - mImageOverlay, - mImageOverlayColor); + overlay_color); break; case LLFontGL::RIGHT: - gl_draw_scaled_image( - mRect.getWidth() - IMG_PAD - overlay_width, + text_right -= overlay_width + 1; + text_width -= overlay_width + 1; + mImageOverlay->draw( + mRect.getWidth() - mRightHPad - overlay_width, center_y - (overlay_height / 2), overlay_width, overlay_height, - mImageOverlay, - mImageOverlayColor); + overlay_color); break; default: // draw nothing @@ -673,28 +697,26 @@ void LLButton::draw() // Draw label if( !label.empty() ) { - S32 drawable_width = mRect.getWidth() - mLeftHPad - mRightHPad; - LLWString::trim(label); S32 x; switch( mHAlign ) { case LLFontGL::RIGHT: - x = mRect.getWidth() - mRightHPad; + x = text_right; break; case LLFontGL::HCENTER: x = mRect.getWidth() / 2; break; case LLFontGL::LEFT: default: - x = mLeftHPad; + x = text_left; break; } S32 y_offset = 2 + (mRect.getHeight() - 20)/2; - if (pressed || display_state) + if (pressed) { y_offset--; x++; @@ -704,7 +726,7 @@ void LLButton::draw() label_color, mHAlign, LLFontGL::BOTTOM, mDropShadowedText ? LLFontGL::DROP_SHADOW_SOFT : LLFontGL::NORMAL, - U32_MAX, drawable_width, + U32_MAX, text_width, NULL, FALSE, FALSE); } @@ -733,13 +755,11 @@ void LLButton::drawBorder(const LLColor4& color, S32 size) if (mScaleImage) { - gl_draw_scaled_image_with_border(left, bottom, mFixedWidth, mFixedHeight, right-left, top-bottom, - mImagep, color, TRUE ); + mImagep->drawSolid(left, bottom, right-left, top-bottom, color); } else { - gl_draw_scaled_image_with_border(left, bottom, 0, 0, mImagep->getWidth() + size * 2, - mImagep->getHeight() + size * 2, mImagep, color, TRUE ); + mImagep->drawSolid(left, bottom, mImagep->getWidth() + size * 2, mImagep->getHeight() + size * 2, color); } } @@ -763,6 +783,22 @@ void LLButton::setToggleState(BOOL b) } } +void LLButton::setFlashing( BOOL b ) +{ + if (b != mFlashing) + { + mFlashing = b; + mFlashingTimer.reset(); + } +} + + +BOOL LLButton::toggleState() +{ + setToggleState( !mToggleState ); + return mToggleState; +} + void LLButton::setValue(const LLSD& value ) { mToggleState = value.asBoolean(); @@ -770,7 +806,7 @@ void LLButton::setValue(const LLSD& value ) LLSD LLButton::getValue() const { - return mToggleState; + return mToggleState == TRUE; } void LLButton::setLabel( const LLStringExplicit& label ) @@ -807,10 +843,9 @@ void LLButton::setDisabledSelectedLabel( const LLStringExplicit& label ) mDisabledSelectedLabel = label; } -void LLButton::setImageUnselectedID( const LLUUID &image_id ) -{ - mImageUnselectedName = ""; - mImageUnselected = LLUI::sImageProvider->getUIImageByID(image_id); +void LLButton::setImageUnselected(LLPointer<LLUIImage> image) +{ + mImageUnselected = image; } void LLButton::setImages( const LLString &image_name, const LLString &selected_name ) @@ -820,10 +855,9 @@ void LLButton::setImages( const LLString &image_name, const LLString &selected_n } -void LLButton::setImageSelectedID( const LLUUID &image_id ) +void LLButton::setImageSelected(LLPointer<LLUIImage> image) { - mImageSelectedName = ""; - mImageSelected = LLUI::sImageProvider->getUIImageByID(image_id); + mImageSelected = image; } void LLButton::setImageColor(const LLColor4& c) @@ -831,19 +865,22 @@ void LLButton::setImageColor(const LLColor4& c) mImageColor = c; } +void LLButton::setColor(const LLColor4& color) +{ + setImageColor(color); +} + -void LLButton::setImageDisabledID( const LLUUID &image_id ) +void LLButton::setImageDisabled(LLPointer<LLUIImage> image) { - mImageDisabledName = ""; - mImageDisabled = LLUI::sImageProvider->getUIImageByID(image_id); + mImageDisabled = image; mDisabledImageColor = mImageColor; mDisabledImageColor.mV[VALPHA] *= 0.5f; } -void LLButton::setImageDisabledSelectedID( const LLUUID &image_id ) -{ - mImageDisabledSelectedName = ""; - mImageDisabledSelected = LLUI::sImageProvider->getUIImageByID(image_id); +void LLButton::setImageDisabledSelected(LLPointer<LLUIImage> image) +{ + mImageDisabledSelected = image; mDisabledImageColor = mImageColor; mDisabledImageColor.mV[VALPHA] *= 0.5f; } @@ -855,11 +892,9 @@ void LLButton::setDisabledImages( const LLString &image_name, const LLString &se mDisabledImageColor = c; } - -void LLButton::setImageHoverSelectedID( const LLUUID& image_id ) +void LLButton::setImageHoverSelected(LLPointer<LLUIImage> image) { - mImageHoverSelectedName = ""; - mImageHoverSelected = LLUI::sImageProvider->getUIImageByID(image_id); + mImageHoverSelected = image; } void LLButton::setDisabledImages( const LLString &image_name, const LLString &selected_name) @@ -869,10 +904,9 @@ void LLButton::setDisabledImages( const LLString &image_name, const LLString &se setDisabledImages( image_name, selected_name, clr ); } -void LLButton::setImageHoverUnselectedID( const LLUUID& image_id ) +void LLButton::setImageHoverUnselected(LLPointer<LLUIImage> image) { - mImageHoverUnselectedName = ""; - mImageHoverUnselected = LLUI::sImageProvider->getUIImageByID(image_id); + mImageHoverUnselected = image; } void LLButton::setHoverImages( const LLString& image_name, const LLString& selected_name ) @@ -889,8 +923,7 @@ void LLButton::setImageOverlay(const LLString &image_name, LLFontGL::HAlign alig } else { - LLUUID overlay_image_id = LLUI::findAssetUUIDByName(image_name); - mImageOverlay = LLUI::sImageProvider->getUIImageByID(overlay_image_id); + mImageOverlay = LLUI::getUIImageByName(image_name); mImageOverlayAlignment = alignment; mImageOverlayColor = color; } @@ -904,34 +937,6 @@ void LLButton::onMouseCaptureLost() } //------------------------------------------------------------------------- -// LLSquareButton -//------------------------------------------------------------------------- -LLSquareButton::LLSquareButton(const LLString& name, const LLRect& rect, - const LLString& label, - const LLFontGL *font, - const LLString& control_name, - void (*click_callback)(void*), - void *callback_data, - const LLString& selected_label ) -: LLButton(name, rect, "","", - control_name, - click_callback, callback_data, - font, - label, - (selected_label.empty() ? label : selected_label) ) -{ - setImageUnselected("square_btn_32x128.tga"); - // mImageUnselected = LLUI::sImageProvider->getUIImageByID(LLUUID(LLUI::sAssetsGroup->getString("square_btn_32x128.tga"))); - setImageSelected("square_btn_selected_32x128.tga"); - // mImageSelectedImage = LLUI::sImageProvider->getUIImageByID(LLUUID(LLUI::sAssetsGroup->getString("square_btn_selected_32x128.tga"))); - setImageDisabled("square_btn_32x128.tga"); - //mDisabledImage = LLUI::sImageProvider->getUIImageByID(LLUUID(LLUI::sAssetsGroup->getString("square_btn_32x128.tga"))); - setImageDisabledSelected("square_btn_selected_32x128.tga"); - //mDisabledSelectedImage = LLUI::sImageProvider->getUIImageByID(LLUUID(LLUI::sAssetsGroup->getString("square_btn_selected_32x128.tga"))); - mImageColor = LLUI::sColorsGroup->getColor("ButtonColor"); -} - -//------------------------------------------------------------------------- // Utilities //------------------------------------------------------------------------- S32 round_up(S32 grid, S32 value) @@ -951,37 +956,37 @@ S32 round_up(S32 grid, S32 value) void LLButton::setImageUnselected(const LLString &image_name) { - setImageUnselectedID(LLUI::findAssetUUIDByName(image_name)); + setImageUnselected(LLUI::getUIImageByName(image_name)); mImageUnselectedName = image_name; } void LLButton::setImageSelected(const LLString &image_name) { - setImageSelectedID(LLUI::findAssetUUIDByName(image_name)); + setImageSelected(LLUI::getUIImageByName(image_name)); mImageSelectedName = image_name; } void LLButton::setImageHoverSelected(const LLString &image_name) { - setImageHoverSelectedID(LLUI::findAssetUUIDByName(image_name)); + setImageHoverSelected(LLUI::getUIImageByName(image_name)); mImageHoverSelectedName = image_name; } void LLButton::setImageHoverUnselected(const LLString &image_name) { - setImageHoverUnselectedID(LLUI::findAssetUUIDByName(image_name)); + setImageHoverUnselected(LLUI::getUIImageByName(image_name)); mImageHoverUnselectedName = image_name; } void LLButton::setImageDisabled(const LLString &image_name) { - setImageDisabledID(LLUI::findAssetUUIDByName(image_name)); + setImageDisabled(LLUI::getUIImageByName(image_name)); mImageDisabledName = image_name; } void LLButton::setImageDisabledSelected(const LLString &image_name) { - setImageDisabledSelectedID(LLUI::findAssetUUIDByName(image_name)); + setImageDisabledSelected(LLUI::getUIImageByName(image_name)); mImageDisabledSelectedName = image_name; } @@ -1009,8 +1014,6 @@ LLXMLNodePtr LLButton::getXML(bool save_children) const node->createChild("label_selected", TRUE)->setStringValue(getLabelSelected()); node->createChild("font", TRUE)->setStringValue(LLFontGL::nameFromFont(mGLFont)); node->createChild("halign", TRUE)->setStringValue(LLFontGL::nameFromHAlign(mHAlign)); - node->createChild("border_width", TRUE)->setIntValue(mFixedWidth); - node->createChild("border_height", TRUE)->setIntValue(mFixedHeight); addImageAttributeToXML(node,mImageUnselectedName,mImageUnselectedID,"image_unselected"); addImageAttributeToXML(node,mImageSelectedName,mImageSelectedID,"image_selected"); @@ -1092,8 +1095,12 @@ LLView* LLButton::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *fa label, label_selected); - node->getAttributeS32("border_width", button->mFixedWidth); - node->getAttributeS32("border_height", button->mFixedHeight); + node->getAttributeS32("pad_right", button->mRightHPad); + node->getAttributeS32("pad_left", button->mLeftHPad); + + BOOL is_toggle = button->getIsToggle(); + node->getAttributeBOOL("toggle", is_toggle); + button->setIsToggle(is_toggle); if(image_hover_selected != LLString::null) button->setImageHoverSelected(image_hover_selected); diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h index 5f7d917b4e..0e140a45a6 100644 --- a/indra/llui/llbutton.h +++ b/indra/llui/llbutton.h @@ -114,11 +114,13 @@ public: F32 getHeldDownTime() const { return mMouseDownTimer.getElapsedTimeF32(); } - BOOL toggleState() { setToggleState( !mToggleState ); return mToggleState; } + BOOL getIsToggle() const { return mIsToggle; } + void setIsToggle(BOOL is_toggle) { mIsToggle = is_toggle; } + BOOL toggleState(); BOOL getToggleState() const { return mToggleState; } void setToggleState(BOOL b); - void setFlashing( BOOL b ) { mFlashing = b; } + void setFlashing( BOOL b ); BOOL getFlashing() const { return mFlashing; } void setHAlign( LLFontGL::HAlign align ) { mHAlign = align; } @@ -128,14 +130,11 @@ public: const LLString getLabelUnselected() const { return wstring_to_utf8str(mUnselectedLabel); } const LLString getLabelSelected() const { return wstring_to_utf8str(mSelectedLabel); } - - // HACK to allow images to be freed when the caller knows he's done with it. - LLImageGL* getImageUnselected() const { return mImageUnselected; } - void setImageColor(const LLString& color_control); - void setImages(const LLString &image_name, const LLString &selected_name); void setImageColor(const LLColor4& c); - + virtual void setColor(const LLColor4& c); + + void setImages(const LLString &image_name, const LLString &selected_name); void setDisabledImages(const LLString &image_name, const LLString &selected_name); void setDisabledImages(const LLString &image_name, const LLString &selected_name, const LLColor4& c); @@ -146,7 +145,7 @@ public: void setDisabledSelectedLabelColor( const LLColor4& c ) { mDisabledSelectedLabelColor = c; } void setImageOverlay(const LLString &image_name, LLFontGL::HAlign alignment = LLFontGL::HCENTER, const LLColor4& color = LLColor4::white); - LLPointer<LLImageGL> getImageOverlay() { return mImageOverlay; } + LLPointer<LLUIImage> getImageOverlay() { return mImageOverlay; } virtual void setValue(const LLSD& value ); @@ -170,16 +169,8 @@ public: static void onHeldDown(void *userdata); // to be called by gIdleCallbacks - void setFixedBorder(S32 width, S32 height) { mFixedWidth = width; mFixedHeight = height; } void setHoverGlowStrength(F32 strength) { mHoverGlowStrength = strength; } -private: - void setImageUnselectedID(const LLUUID &image_id); - void setImageSelectedID(const LLUUID &image_id); - void setImageHoverSelectedID(const LLUUID &image_id); - void setImageHoverUnselectedID(const LLUUID &image_id); - void setImageDisabledID(const LLUUID &image_id); - void setImageDisabledSelectedID(const LLUUID &image_id); public: void setImageUnselected(const LLString &image_name); void setImageSelected(const LLString &image_name); @@ -187,6 +178,14 @@ public: void setImageHoverUnselected(const LLString &image_name); void setImageDisabled(const LLString &image_name); void setImageDisabledSelected(const LLString &image_name); + + void setImageUnselected(LLPointer<LLUIImage> image); + void setImageSelected(LLPointer<LLUIImage> image); + void setImageHoverSelected(LLPointer<LLUIImage> image); + void setImageHoverUnselected(LLPointer<LLUIImage> image); + void setImageDisabled(LLPointer<LLUIImage> image); + void setImageDisabledSelected(LLPointer<LLUIImage> image); + void setCommitOnReturn(BOOL commit) { mCommitOnReturn = commit; } BOOL getCommitOnReturn() { return mCommitOnReturn; } @@ -209,27 +208,27 @@ protected: F32 mHeldDownDelay; // seconds, after which held-down callbacks get called S32 mHeldDownFrameDelay; // frames, after which held-down callbacks get called - LLPointer<LLImageGL> mImageOverlay; + LLPointer<LLUIImage> mImageOverlay; LLFontGL::HAlign mImageOverlayAlignment; LLColor4 mImageOverlayColor; - LLPointer<LLImageGL> mImageUnselected; + LLPointer<LLUIImage> mImageUnselected; LLUIString mUnselectedLabel; LLColor4 mUnselectedLabelColor; - LLPointer<LLImageGL> mImageSelected; + LLPointer<LLUIImage> mImageSelected; LLUIString mSelectedLabel; LLColor4 mSelectedLabelColor; - LLPointer<LLImageGL> mImageHoverSelected; + LLPointer<LLUIImage> mImageHoverSelected; - LLPointer<LLImageGL> mImageHoverUnselected; + LLPointer<LLUIImage> mImageHoverUnselected; - LLPointer<LLImageGL> mImageDisabled; + LLPointer<LLUIImage> mImageDisabled; LLUIString mDisabledLabel; LLColor4 mDisabledLabelColor; - LLPointer<LLImageGL> mImageDisabledSelected; + LLPointer<LLUIImage> mImageDisabledSelected; LLUIString mDisabledSelectedLabel; LLColor4 mDisabledSelectedLabelColor; @@ -254,6 +253,7 @@ protected: LLColor4 mImageColor; LLColor4 mDisabledImageColor; + BOOL mIsToggle; BOOL mToggleState; BOOL mScaleImage; @@ -267,9 +267,6 @@ protected: S32 mLeftHPad; S32 mRightHPad; - S32 mFixedWidth; - S32 mFixedHeight; - F32 mHoverGlowStrength; F32 mCurGlowStrength; @@ -278,22 +275,9 @@ protected: LLString mHelpURL; - LLPointer<LLImageGL> mImagep; - - static LLFrameTimer sFlashingTimer; -}; + LLPointer<LLUIImage> mImagep; -class LLSquareButton -: public LLButton -{ -public: - LLSquareButton(const LLString& name, const LLRect& rect, - const LLString& label, - const LLFontGL *font = NULL, - const LLString& control_name = LLString(), - void (*click_callback)(void*) = NULL, - void *callback_data = NULL, - const LLString& selected_label = LLString::null ); + LLFrameTimer mFlashingTimer; }; // Helpful functions diff --git a/indra/llui/llcheckboxctrl.cpp b/indra/llui/llcheckboxctrl.cpp index 504b342003..b0a7e9d27f 100644 --- a/indra/llui/llcheckboxctrl.cpp +++ b/indra/llui/llcheckboxctrl.cpp @@ -75,7 +75,7 @@ LLCheckBoxCtrl::LLCheckBoxCtrl(const LLString& name, const LLRect& rect, } // must be big enough to hold all children - setSpanChildren(TRUE); + setUseBoundingRect(TRUE); mKeyboardFocusOnClick = TRUE; @@ -130,6 +130,7 @@ LLCheckBoxCtrl::LLCheckBoxCtrl(const LLString& name, const LLRect& rect, mButton->setDisabledImages( inactive_false_id, inactive_true_id ); mButton->setHoverGlowStrength(0.35f); } + mButton->setIsToggle(TRUE); mButton->setToggleState( initial_value ); mButton->setFollowsLeft(); mButton->setFollowsBottom(); @@ -150,16 +151,11 @@ void LLCheckBoxCtrl::onButtonPress( void *userdata ) if (self->mRadioStyle) { - if (!self->getValue()) - { - self->setValue(TRUE); - } - } - else - { - self->toggle(); + self->setValue(TRUE); } + self->setControlValue(self->getValue()); + // HACK: because buttons don't normally commit self->onCommit(); if (self->mKeyboardFocusOnClick) @@ -232,14 +228,13 @@ void LLCheckBoxCtrl::draw() //virtual void LLCheckBoxCtrl::setValue(const LLSD& value ) { - mSetValue = value.asBoolean(); - mButton->setToggleState( mSetValue ); + mButton->setValue( value ); } //virtual LLSD LLCheckBoxCtrl::getValue() const { - return mButton->getToggleState(); + return mButton->getValue(); } void LLCheckBoxCtrl::setLabel( const LLStringExplicit& label ) diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp index 5f76cfc94b..6063fc155a 100644 --- a/indra/llui/llcombobox.cpp +++ b/indra/llui/llcombobox.cpp @@ -55,17 +55,16 @@ // Globals S32 LLCOMBOBOX_HEIGHT = 0; S32 LLCOMBOBOX_WIDTH = 0; - +S32 MAX_COMBO_WIDTH = 500; + LLComboBox::LLComboBox( const LLString& name, const LLRect &rect, const LLString& label, void (*commit_callback)(LLUICtrl*,void*), void *callback_userdata ) : LLUICtrl(name, rect, TRUE, commit_callback, callback_userdata, FOLLOWS_LEFT | FOLLOWS_TOP), - mDrawArrow(TRUE), mTextEntry(NULL), mArrowImage(NULL), - mArrowImageWidth(8), mAllowTextEntry(FALSE), mMaxChars(20), mTextEntryTentative(TRUE), @@ -73,55 +72,43 @@ LLComboBox::LLComboBox( const LLString& name, const LLRect &rect, const LLString mPrearrangeCallback( NULL ), mTextEntryCallback( NULL ) { - // For now, all comboboxes don't take keyboard focus when clicked. - // This might change if it is part of a modal dialog. - // mKeyboardFocusOnClick = FALSE; - - // Revert to standard behavior. When this control's parent is hidden, it needs to - // hide this ctrl--which won't just happen automatically since when LLComboBox is - // showing its list, it's also set to TopCtrl. When keyboard focus is cleared all - // controls (including this one) know that they are no longer editing. - mKeyboardFocusOnClick = TRUE; - - LLRect r; - r.setOriginAndSize(0, 0, rect.getWidth(), rect.getHeight()); - // Always use text box // Text label button - mButton = new LLSquareButton("comboxbox button", - r, label, NULL, LLString::null, + mButton = new LLButton("comboxbox button", + LLRect(), label, NULL, LLString::null, NULL, this); + mButton->setImageUnselected("square_btn_32x128.tga"); + mButton->setImageSelected("square_btn_selected_32x128.tga"); + mButton->setImageDisabled("square_btn_32x128.tga"); + mButton->setImageDisabledSelected("square_btn_selected_32x128.tga"); + mButton->setScaleImage(TRUE); + mButton->setMouseDownCallback(onButtonDown); mButton->setFont(LLFontGL::sSansSerifSmall); mButton->setFollows(FOLLOWS_LEFT | FOLLOWS_BOTTOM | FOLLOWS_RIGHT); mButton->setHAlign( LLFontGL::LEFT ); - - const S32 ARROW_WIDTH = 16; - mButton->setRightHPad( ARROW_WIDTH ); + mButton->setRightHPad(2); addChild(mButton); - // Default size, will be set by arrange() call in button callback. - S32 list_width = mRect.getWidth() + SCROLLBAR_SIZE; - r.setOriginAndSize(0, 16, list_width, 220); - // disallow multiple selection mList = new LLScrollListCtrl( - "ComboBox", r, + "ComboBox", LLRect(), &LLComboBox::onItemSelected, this, FALSE); mList->setVisible(FALSE); mList->setBgWriteableColor( LLColor4(1,1,1,1) ); mList->setCommitOnKeyboardMovement(FALSE); - mList->setFocusChangedCallback(onListFocusChanged); addChild(mList); LLRect border_rect(0, mRect.getHeight(), mRect.getWidth(), 0); mBorder = new LLViewBorder( "combo border", border_rect ); addChild( mBorder ); - mBorder->setFollows(FOLLOWS_LEFT|FOLLOWS_RIGHT|FOLLOWS_TOP|FOLLOWS_BOTTOM); + mBorder->setFollowsAll(); LLUUID arrow_image_id( LLUI::sAssetsGroup->getString("combobox_arrow.tga") ); - mArrowImage = LLUI::sImageProvider->getUIImageByID(arrow_image_id); - mArrowImageWidth = llmax(8,mArrowImage->getWidth(0)); // In case image hasn't loaded yet + mArrowImage = LLUI::sImageProvider->getImageByID(arrow_image_id); + mButton->setImageOverlay("combobox_arrow.tga", LLFontGL::RIGHT); + + updateLayout(); } @@ -155,7 +142,7 @@ LLXMLNodePtr LLComboBox::getXML(bool save_children) const LLSD value = item->getValue(); item_node->createChild("value", TRUE)->setStringValue(value.asString()); item_node->createChild("enabled", TRUE)->setBoolValue(item->getEnabled()); - item_node->setStringValue(cell->getText()); + item_node->setStringValue(cell->getValue().asString()); } } @@ -272,34 +259,42 @@ void LLComboBox::resetDirty() // add item "name" to menu -void LLComboBox::add(const LLString& name, EAddPosition pos, BOOL enabled) +LLScrollListItem* LLComboBox::add(const LLString& name, EAddPosition pos, BOOL enabled) { - mList->addSimpleItem(name, pos, enabled); + LLScrollListItem* item = mList->addSimpleItem(name, pos, enabled); mList->selectFirstItem(); + return item; } // add item "name" with a unique id to menu -void LLComboBox::add(const LLString& name, const LLUUID& id, EAddPosition pos, BOOL enabled ) +LLScrollListItem* LLComboBox::add(const LLString& name, const LLUUID& id, EAddPosition pos, BOOL enabled ) { - mList->addSimpleItem(name, LLSD(id), pos, enabled); + LLScrollListItem* item = mList->addSimpleItem(name, LLSD(id), pos, enabled); mList->selectFirstItem(); + return item; } // add item "name" with attached userdata -void LLComboBox::add(const LLString& name, void* userdata, EAddPosition pos, BOOL enabled ) +LLScrollListItem* LLComboBox::add(const LLString& name, void* userdata, EAddPosition pos, BOOL enabled ) { LLScrollListItem* item = mList->addSimpleItem(name, pos, enabled); item->setUserdata( userdata ); mList->selectFirstItem(); + return item; } // add item "name" with attached generic data -void LLComboBox::add(const LLString& name, LLSD value, EAddPosition pos, BOOL enabled ) +LLScrollListItem* LLComboBox::add(const LLString& name, LLSD value, EAddPosition pos, BOOL enabled ) { - mList->addSimpleItem(name, value, pos, enabled); + LLScrollListItem* item = mList->addSimpleItem(name, value, pos, enabled); mList->selectFirstItem(); + return item; } +LLScrollListItem* LLComboBox::addSeparator(EAddPosition pos) +{ + return mList->addSeparator(pos); +} void LLComboBox::sortByName() { @@ -335,9 +330,9 @@ void LLComboBox::setValue(const LLSD& value) } } -const LLString& LLComboBox::getSimple() const +const LLString LLComboBox::getSimple() const { - const LLString& res = mList->getSimpleSelectedItem(); + const LLString res = mList->getSimpleSelectedItem(); if (res.empty() && mAllowTextEntry) { return mTextEntry->getText(); @@ -348,7 +343,7 @@ const LLString& LLComboBox::getSimple() const } } -const LLString& LLComboBox::getSimpleSelectedItem(S32 column) const +const LLString LLComboBox::getSimpleSelectedItem(S32 column) const { return mList->getSimpleSelectedItem(column); } @@ -373,7 +368,7 @@ LLSD LLComboBox::getValue() const void LLComboBox::setLabel(const LLStringExplicit& name) { - if ( mAllowTextEntry ) + if ( mTextEntry ) { mTextEntry->setText(name); if (mList->selectSimpleItem(name, FALSE)) @@ -385,7 +380,8 @@ void LLComboBox::setLabel(const LLStringExplicit& name) mTextEntry->setTentative(mTextEntryTentative); } } - else + + if (!mAllowTextEntry) { mButton->setLabelUnselected(name); mButton->setLabelSelected(name); @@ -433,16 +429,21 @@ void LLComboBox::onFocusLost() LLUICtrl::onFocusLost(); } +void LLComboBox::onLostTop() +{ + hideList(); +} + + void LLComboBox::setButtonVisible(BOOL visible) { mButton->setVisible(visible); - mDrawArrow = visible; if (mTextEntry) { LLRect text_entry_rect(0, mRect.getHeight(), mRect.getWidth(), 0); if (visible) { - text_entry_rect.mRight -= mArrowImageWidth + 2 * LLUI::sConfigGroup->getS32("DropShadowButton"); + text_entry_rect.mRight -= llmax(8,mArrowImage->getWidth(0)) + 2 * LLUI::sConfigGroup->getS32("DropShadowButton"); } //mTextEntry->setRect(text_entry_rect); mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), TRUE); @@ -457,22 +458,8 @@ void LLComboBox::draw() mButton->setEnabled(mEnabled /*&& !mList->isEmpty()*/); - // Draw children + // Draw children normally LLUICtrl::draw(); - - if (mDrawArrow) - { - // Paste the graphic on the right edge - if (!mArrowImage.isNull()) - { - S32 arrow_height = llmin(mRect.getHeight(), mArrowImage->getHeight()); - S32 arrow_width = llround((F32)mArrowImage->getWidth() * ((F32)arrow_height / (F32)mArrowImage->getHeight())); - - S32 left = mRect.getWidth() - mArrowImage->getWidth() - LLUI::sConfigGroup->getS32("DropShadowButton"); - - gl_draw_scaled_image( left, 0, arrow_width, arrow_height, mArrowImage, LLColor4::white); - } - } } } @@ -497,6 +484,67 @@ S32 LLComboBox::getCurrentIndex() const } +void LLComboBox::updateLayout() +{ + LLRect rect = getLocalRect(); + if (mAllowTextEntry) + { + S32 shadow_size = LLUI::sConfigGroup->getS32("DropShadowButton"); + mButton->setRect(LLRect( mRect.getWidth() - llmax(8,mArrowImage->getWidth(0)) - 2 * shadow_size, + rect.mTop, rect.mRight, rect.mBottom)); + mButton->setTabStop(FALSE); + + if (!mTextEntry) + { + LLRect text_entry_rect(0, mRect.getHeight(), mRect.getWidth(), 0); + text_entry_rect.mRight -= llmax(8,mArrowImage->getWidth(0)) + 2 * LLUI::sConfigGroup->getS32("DropShadowButton"); + // clear label on button + LLString cur_label = mButton->getLabelSelected(); + mTextEntry = new LLLineEditor("combo_text_entry", + text_entry_rect, + "", + LLFontGL::sSansSerifSmall, + mMaxChars, + onTextCommit, + onTextEntry, + NULL, + this, + NULL, // prevalidate func + LLViewBorder::BEVEL_NONE, + LLViewBorder::STYLE_LINE, + 0); // no border + mTextEntry->setSelectAllonFocusReceived(TRUE); + mTextEntry->setHandleEditKeysDirectly(TRUE); + mTextEntry->setCommitOnFocusLost(FALSE); + mTextEntry->setText(cur_label); + mTextEntry->setIgnoreTab(TRUE); + mTextEntry->setFollowsAll(); + addChild(mTextEntry); + } + else + { + mTextEntry->setVisible(TRUE); + mTextEntry->setMaxTextLength(mMaxChars); + } + + // clear label on button + setLabel(LLString::null); + + mButton->setFollows(FOLLOWS_BOTTOM | FOLLOWS_TOP | FOLLOWS_RIGHT); + } + else if (!mAllowTextEntry) + { + mButton->setRect(rect); + mButton->setTabStop(TRUE); + + if (mTextEntry) + { + mTextEntry->setVisible(FALSE); + } + mButton->setFollowsAll(); + } +} + void* LLComboBox::getCurrentUserdata() { LLScrollListItem* item = mList->getFirstSelected(); @@ -514,7 +562,7 @@ void LLComboBox::showList() LLCoordWindow window_size; getWindow()->getSize(&window_size); //HACK: shouldn't have to know about scale here - mList->arrange( 192, llfloor((F32)window_size.mY / LLUI::sGLScaleFactor.mV[VY]) - 50 ); + mList->fitContents( 192, llfloor((F32)window_size.mY / LLUI::sGLScaleFactor.mV[VY]) - 50 ); // Make sure that we can see the whole list LLRect root_view_local; @@ -523,7 +571,9 @@ void LLComboBox::showList() LLRect rect = mList->getRect(); - S32 list_width = mRect.getWidth() + SCROLLBAR_SIZE; + S32 min_width = mRect.getWidth(); + S32 max_width = llmax(min_width, MAX_COMBO_WIDTH); + S32 list_width = llclamp(mList->getMaxContentWidth(), min_width, max_width); if (mListPosition == BELOW) { @@ -583,12 +633,6 @@ void LLComboBox::showList() mList->translate(0, -y); } - // pass mouse capture on to list if button is depressed - if (mButton->hasMouseCapture()) - { - gFocusMgr.setMouseCapture(mList); - } - // NB: this call will trigger the focuslost callback which will hide the list, so do it first // before finally showing the list @@ -604,24 +648,29 @@ void LLComboBox::showList() mButton->setToggleState(TRUE); mList->setVisible(TRUE); - gFocusMgr.setTopCtrl(mList); + setUseBoundingRect(TRUE); + gFocusMgr.setTopCtrl(this); } void LLComboBox::hideList() { + //*HACK: store the original value explicitly somewhere, not just in label + LLString orig_selection = mAllowTextEntry ? mTextEntry->getText() : mButton->getLabelSelected(); + + // assert selection in list + mList->selectSimpleItem(orig_selection, FALSE); + mButton->setToggleState(FALSE); mList->setVisible(FALSE); mList->highlightNthItem(-1); - if( gFocusMgr.getTopCtrl() == mList ) + setUseBoundingRect(FALSE); + if( gFocusMgr.getTopCtrl() == this ) { gFocusMgr.setTopCtrl(NULL); } - - //mList->setFocus(FALSE); } - //------------------------------------------------------------------ // static functions //------------------------------------------------------------------ @@ -650,21 +699,20 @@ void LLComboBox::onButtonDown(void *userdata) self->showList(); } - if (self->mKeyboardFocusOnClick && !self->hasFocus()) + self->setFocus( TRUE ); + + // pass mouse capture on to list if button is depressed + if (self->mButton->hasMouseCapture()) { - self->setFocus( TRUE ); + gFocusMgr.setMouseCapture(self->mList); } } else { - // hide and release keyboard focus self->hideList(); - - self->onCommit(); } -} - +} // static void LLComboBox::onItemSelected(LLUICtrl* item, void *userdata) @@ -672,7 +720,7 @@ void LLComboBox::onItemSelected(LLUICtrl* item, void *userdata) // Note: item is the LLScrollListCtrl LLComboBox *self = (LLComboBox *) userdata; - const LLString& name = self->mList->getSimpleSelectedItem(); + const LLString name = self->mList->getSimpleSelectedItem(); S32 cur_id = self->getCurrentIndex(); if (cur_id != -1) @@ -681,40 +729,24 @@ void LLComboBox::onItemSelected(LLUICtrl* item, void *userdata) if (self->mAllowTextEntry) { - gFocusMgr.setKeyboardFocus(self->mTextEntry, NULL); + gFocusMgr.setKeyboardFocus(self->mTextEntry); self->mTextEntry->selectAll(); } } else { // invalid selection, just restore existing value - self->mList->selectSimpleItem(self->mButton->getLabelSelected()); + LLString orig_selection = self->mAllowTextEntry ? self->mTextEntry->getText() : self->mButton->getLabelSelected(); + + self->mList->selectSimpleItem(orig_selection); } self->onCommit(); self->hideList(); } -// static -void LLComboBox::onListFocusChanged(LLUICtrl* list, void* user_data) -{ - LLComboBox *self = (LLComboBox *) list->getParent(); - // user not manipulating list or clicking on drop down button - if (!self->mList->hasFocus() && !self->mButton->hasMouseCapture()) - { - //*HACK: store the original value explicitly somewhere, not just in label - LLString orig_selection = self->mAllowTextEntry ? self->mTextEntry->getText() : self->mButton->getLabelSelected(); - - self->hideList(); - - // reassert original selection - self->mList->selectSimpleItem(orig_selection, FALSE); - } -} - BOOL LLComboBox::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen) { - LLString tool_tip; if (LLUI::sShowXUINames) @@ -726,23 +758,19 @@ BOOL LLComboBox::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_ tool_tip = mToolTipMsg; } - if( getVisible() && pointInView( x, y ) ) + if( !tool_tip.empty() ) { - if( !tool_tip.empty() ) - { - msg = tool_tip; - - // Convert rect local to screen coordinates - localPointToScreen( - 0, 0, - &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); - localPointToScreen( - mRect.getWidth(), mRect.getHeight(), - &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) ); - } - return TRUE; + msg = tool_tip; + + // Convert rect local to screen coordinates + localPointToScreen( + 0, 0, + &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); + localPointToScreen( + mRect.getWidth(), mRect.getHeight(), + &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) ); } - return FALSE; + return TRUE; } BOOL LLComboBox::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent) @@ -793,63 +821,11 @@ BOOL LLComboBox::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent void LLComboBox::setAllowTextEntry(BOOL allow, S32 max_chars, BOOL set_tentative) { - LLRect rect( 0, mRect.getHeight(), mRect.getWidth(), 0); - if (allow && !mAllowTextEntry) - { - S32 shadow_size = LLUI::sConfigGroup->getS32("DropShadowButton"); - mButton->setRect(LLRect( mRect.getWidth() - mArrowImageWidth - 2 * shadow_size, - rect.mTop, rect.mRight, rect.mBottom)); - mButton->setTabStop(FALSE); - - // clear label on button - LLString cur_label = mButton->getLabelSelected(); - setLabel(LLString::null); - if (!mTextEntry) - { - LLRect text_entry_rect(0, mRect.getHeight(), mRect.getWidth(), 0); - text_entry_rect.mRight -= mArrowImageWidth + 2 * LLUI::sConfigGroup->getS32("DropShadowButton"); - mTextEntry = new LLLineEditor("combo_text_entry", - text_entry_rect, - "", - LLFontGL::sSansSerifSmall, - max_chars, - onTextCommit, - onTextEntry, - NULL, - this, - NULL, // prevalidate func - LLViewBorder::BEVEL_NONE, - LLViewBorder::STYLE_LINE, - 0); // no border - mTextEntry->setSelectAllonFocusReceived(TRUE); - mTextEntry->setHandleEditKeysDirectly(TRUE); - mTextEntry->setCommitOnFocusLost(FALSE); - mTextEntry->setText(cur_label); - mTextEntry->setIgnoreTab(TRUE); - mTextEntry->setFollowsAll(); - addChild(mTextEntry); - mMaxChars = max_chars; - } - else - { - mTextEntry->setVisible(TRUE); - } - - mButton->setFollows(FOLLOWS_BOTTOM | FOLLOWS_TOP | FOLLOWS_RIGHT); - } - else if (!allow && mAllowTextEntry) - { - mButton->setRect(rect); - mButton->setTabStop(TRUE); - - if (mTextEntry) - { - mTextEntry->setVisible(FALSE); - } - mButton->setFollowsAll(); - } mAllowTextEntry = allow; - mTextEntryTentative = set_tentative; + mTextEntryTentative = set_tentative; + mMaxChars = max_chars; + + updateLayout(); } void LLComboBox::setTextEntry(const LLStringExplicit& text) @@ -993,6 +969,10 @@ void LLComboBox::setFocus(BOOL b) if (b) { mList->clearSearchString(); + if (mList->getVisible()) + { + mList->setFocus(TRUE); + } } } @@ -1097,3 +1077,155 @@ BOOL LLComboBox::operateOnAll(EOperation op) } return FALSE; } + + + +// +// LLFlyoutButton +// + +const S32 FLYOUT_BUTTON_ARROW_WIDTH = 24; + +LLFlyoutButton::LLFlyoutButton( + const LLString& name, + const LLRect &rect, + const LLString& label, + void (*commit_callback)(LLUICtrl*, void*) , + void *callback_userdata) +: LLComboBox(name, rect, LLString::null, commit_callback, callback_userdata), + mToggleState(FALSE), + mActionButton(NULL) +{ + // Always use text box + // Text label button + mActionButton = new LLButton("flyout_button_main", + LLRect(), label, NULL, LLString::null, + NULL, this); + mActionButton->setScaleImage(TRUE); + + mActionButton->setClickedCallback(onActionButtonClick); + mActionButton->setFollowsAll(); + mActionButton->setHAlign( LLFontGL::HCENTER ); + mActionButton->setLabel(label); + addChild(mActionButton); + + mActionButtonImage = LLUI::getUIImageByName("flyout_btn_left.tga"); + mExpanderButtonImage = LLUI::getUIImageByName("flyout_btn_right.tga"); + mActionButtonImageSelected = LLUI::getUIImageByName("flyout_btn_left_selected.tga"); + mExpanderButtonImageSelected = LLUI::getUIImageByName("flyout_btn_right_selected.tga"); + + mActionButton->setImageSelected(mActionButtonImageSelected); + mActionButton->setImageUnselected(mActionButtonImage); + mActionButton->setImageDisabled(LLPointer<LLUIImage>(NULL)); + mActionButton->setImageDisabledSelected(LLPointer<LLUIImage>(NULL)); + + mButton->setImageSelected(mExpanderButtonImageSelected); + mButton->setImageUnselected(mExpanderButtonImage); + mButton->setImageDisabled(LLPointer<LLUIImage>(NULL)); + mButton->setImageDisabledSelected(LLPointer<LLUIImage>(NULL)); + mButton->setRightHPad(6); + + mBorder->setVisible(FALSE); + + updateLayout(); +} + +//static +LLView* LLFlyoutButton::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) +{ + LLString name = "flyout_button"; + node->getAttributeString("name", name); + + LLString label(""); + node->getAttributeString("label", label); + + LLRect rect; + createRect(node, rect, parent, LLRect()); + + LLUICtrlCallback callback = NULL; + + LLFlyoutButton* flyout_button = new LLFlyoutButton(name, + rect, + label, + callback, + NULL); + + LLString list_position; + node->getAttributeString("list_position", list_position); + if (list_position == "below") + { + flyout_button->mListPosition = BELOW; + } + else if (list_position == "above") + { + flyout_button->mListPosition = ABOVE; + } + + + flyout_button->initFromXML(node, parent); + + LLXMLNodePtr child; + for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) + { + if (child->hasName("flyout_button_item")) + { + LLString label = child->getTextContents(); + + LLString value = label; + child->getAttributeString("value", value); + + flyout_button->add(label, LLSD(value) ); + } + } + + flyout_button->updateLayout(); + + return flyout_button; +} + +void LLFlyoutButton::updateLayout() +{ + LLComboBox::updateLayout(); + + mButton->setOrigin(mRect.getWidth() - FLYOUT_BUTTON_ARROW_WIDTH, 0); + mButton->reshape(FLYOUT_BUTTON_ARROW_WIDTH, mRect.getHeight()); + mButton->setFollows(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM); + mButton->setTabStop(FALSE); + mButton->setImageOverlay(mListPosition == BELOW ? "down_arrow.tga" : "up_arrow.tga", LLFontGL::RIGHT); + + mActionButton->setOrigin(0, 0); + mActionButton->reshape(mRect.getWidth() - FLYOUT_BUTTON_ARROW_WIDTH, mRect.getHeight()); +} + +//static +void LLFlyoutButton::onActionButtonClick(void *user_data) +{ + LLFlyoutButton* buttonp = (LLFlyoutButton*)user_data; + // remember last list selection? + buttonp->mList->deselect(); + buttonp->onCommit(); +} + +void LLFlyoutButton::draw() +{ + mActionButton->setToggleState(mToggleState); + mButton->setToggleState(mToggleState); + + //FIXME: this should be an attribute of comboboxes, whether they have a distinct label or + // the label reflects the last selected item, for now we have to manually remove the label + mButton->setLabel(LLString::null); + LLComboBox::draw(); +} + +void LLFlyoutButton::setEnabled(BOOL enabled) +{ + mActionButton->setEnabled(enabled); + LLComboBox::setEnabled(enabled); +} + + +void LLFlyoutButton::setToggleState(BOOL state) +{ + mToggleState = state; +} + diff --git a/indra/llui/llcombobox.h b/indra/llui/llcombobox.h index ff17d2874f..6e77007aef 100644 --- a/indra/llui/llcombobox.h +++ b/indra/llui/llcombobox.h @@ -80,6 +80,7 @@ public: virtual void draw(); virtual void onFocusLost(); + virtual void onLostTop(); virtual void setEnabled(BOOL enabled); @@ -107,10 +108,11 @@ public: void setAllowTextEntry(BOOL allow, S32 max_chars = 50, BOOL make_tentative = TRUE); void setTextEntry(const LLStringExplicit& text); - void add(const LLString& name, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE); // add item "name" to menu - void add(const LLString& name, const LLUUID& id, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE); - void add(const LLString& name, void* userdata, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE); - void add(const LLString& name, LLSD value, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE); + LLScrollListItem* add(const LLString& name, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE); // add item "name" to menu + LLScrollListItem* add(const LLString& name, const LLUUID& id, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE); + LLScrollListItem* add(const LLString& name, void* userdata, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE); + LLScrollListItem* add(const LLString& name, LLSD value, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE); + LLScrollListItem* addSeparator(EAddPosition pos = ADD_BOTTOM); BOOL remove( S32 index ); // remove item by index, return TRUE if found and removed void removeall() { clearRows(); } @@ -119,9 +121,9 @@ public: // Select current item by name using selectSimpleItem. Returns FALSE if not found. BOOL setSimple(const LLStringExplicit& name); // Get name of current item. Returns an empty string if not found. - const LLString& getSimple() const; + const LLString getSimple() const; // Get contents of column x of selected row - const LLString& getSimpleSelectedItem(S32 column = 0) const; + const LLString getSimpleSelectedItem(S32 column = 0) const; // Sets the label, which doesn't have to exist in the label. // This is probably a UI abuse. @@ -132,6 +134,8 @@ public: BOOL setCurrentByIndex( S32 index ); S32 getCurrentIndex() const; + virtual void updateLayout(); + //======================================================================== LLCtrlSelectionInterface* getSelectionInterface() { return (LLCtrlSelectionInterface*)this; }; LLCtrlListInterface* getListInterface() { return (LLCtrlListInterface*)this; }; @@ -172,7 +176,6 @@ public: static void onButtonDown(void *userdata); static void onItemSelected(LLUICtrl* item, void *userdata); - static void onListFocusChanged(LLUICtrl* item, void *userdata); static void onTextEntry(LLLineEditor* line_editor, void* user_data); static void onTextCommit(LLUICtrl* caller, void* user_data); @@ -183,12 +186,10 @@ public: protected: LLButton* mButton; LLScrollListCtrl* mList; + S32 mButtonPadding; LLViewBorder* mBorder; - BOOL mKeyboardFocusOnClick; - BOOL mDrawArrow; LLLineEditor* mTextEntry; LLPointer<LLImageGL> mArrowImage; - S32 mArrowImageWidth; BOOL mAllowTextEntry; S32 mMaxChars; BOOL mTextEntryTentative; @@ -197,4 +198,36 @@ protected: void (*mTextEntryCallback)(LLLineEditor*, void*); }; +class LLFlyoutButton : public LLComboBox +{ +public: + LLFlyoutButton( + const LLString& name, + const LLRect &rect, + const LLString& label, + void (*commit_callback)(LLUICtrl*, void*) = NULL, + void *callback_userdata = NULL); + + virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_FLYOUT_BUTTON; } + virtual LLString getWidgetTag() const { return LL_FLYOUT_BUTTON_TAG; } + + virtual void updateLayout(); + virtual void draw(); + virtual void setEnabled(BOOL enabled); + + void setToggleState(BOOL state); + + static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); + static void onActionButtonClick(void *userdata); + static void onSelectAction(LLUICtrl* ctrl, void *userdata); + +protected: + LLButton* mActionButton; + LLPointer<LLUIImage> mActionButtonImage; + LLPointer<LLUIImage> mExpanderButtonImage; + LLPointer<LLUIImage> mActionButtonImageSelected; + LLPointer<LLUIImage> mExpanderButtonImageSelected; + BOOL mToggleState; +}; + #endif diff --git a/indra/llui/lldraghandle.h b/indra/llui/lldraghandle.h index 50d199fac5..cd3ce04718 100644 --- a/indra/llui/lldraghandle.h +++ b/indra/llui/lldraghandle.h @@ -54,7 +54,6 @@ public: virtual void setTitle( const LLString& title ) = 0; virtual const LLString& getTitle() const = 0; - virtual void draw() = 0; virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE) = 0; virtual BOOL handleHover(S32 x, S32 y, MASK mask); diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 6f1c281eb2..59741a799a 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -128,7 +128,11 @@ LLFloater::handle_map_t LLFloater::sFloaterMap; LLFloaterView* gFloaterView = NULL; -LLFloater::LLFloater() +LLFloater::LLFloater() : + //FIXME: we should initialize *all* member variables here + mResizable(FALSE), + mDragOnLeft(FALSE) + { // automatically take focus when opened mAutoFocus = TRUE; @@ -215,9 +219,14 @@ void LLFloater::init(const LLString& title, } mButtonScale = 1.f; - LLPanel::deleteAllChildren(); + BOOL need_border = mBorder != NULL; + + // this will delete mBorder too + deleteAllChildren(); + // make sure we don't have a pointer to an old, deleted border + mBorder = NULL; //sjb: HACK! we had a border which was just deleted, so re-create it - if (mBorder != NULL) + if (need_border) { addBorder(); } @@ -609,7 +618,7 @@ void LLFloater::releaseFocus() if( gFocusMgr.childHasKeyboardFocus( this ) ) { - gFocusMgr.setKeyboardFocus(NULL, NULL); + gFocusMgr.setKeyboardFocus(NULL); } if( gFocusMgr.childHasMouseCapture( this ) ) @@ -1023,13 +1032,10 @@ void LLFloater::setHost(LLMultiFloater* host) { mButtonsEnabled[BUTTON_TEAR_OFF] = TRUE; } - - mIsFocusRoot = FALSE; } else if (!mHostHandle.isDead() && !host) { mButtonScale = 1.f; - mIsFocusRoot = TRUE; //mButtonsEnabled[BUTTON_TEAR_OFF] = FALSE; } updateButtons(); @@ -1257,6 +1263,7 @@ void LLFloater::show(LLFloater* floaterp) { if (floaterp) { + gFocusMgr.triggerFocusFlash(); floaterp->open(); if (floaterp->getHost()) { @@ -2594,9 +2601,9 @@ void LLMultiFloater::draw() for (S32 i = 0; i < mTabContainer->getTabCount(); i++) { LLFloater* floaterp = (LLFloater*)mTabContainer->getPanelByIndex(i); - if (floaterp->getTitle() != mTabContainer->getPanelTitle(i)) + if (floaterp->getShortTitle() != mTabContainer->getPanelTitle(i)) { - mTabContainer->setPanelTitle(i, floaterp->getTitle()); + mTabContainer->setPanelTitle(i, floaterp->getShortTitle()); } } LLFloater::draw(); @@ -2714,7 +2721,7 @@ void LLMultiFloater::addFloater(LLFloater* floaterp, BOOL select_added_floater, if ( select_added_floater ) { - mTabContainer->selectLastTab(); + mTabContainer->selectTabPanel(floaterp); } floaterp->setHost(this); @@ -2959,8 +2966,9 @@ void LLMultiFloater::updateResizeLimits() // make sure upper left corner doesn't move translate(0, cur_height - mRect.getHeight()); - // Try to keep whole view onscreen, don't allow partial offscreen. - gFloaterView->adjustToFitScreen(this, FALSE); + // make sure this window is visible on screen when it has been modified + // (tab added, etc) + gFloaterView->adjustToFitScreen(this, TRUE); } } diff --git a/indra/llui/llfocusmgr.cpp b/indra/llui/llfocusmgr.cpp index 6358ccbdd7..e3337eb588 100644 --- a/indra/llui/llfocusmgr.cpp +++ b/indra/llui/llfocusmgr.cpp @@ -42,11 +42,10 @@ LLFocusMgr gFocusMgr; LLFocusMgr::LLFocusMgr() : mLockedView( NULL ), - mKeyboardLockedFocusLostCallback( NULL ), mMouseCaptor( NULL ), mKeyboardFocus( NULL ), + mLastKeyboardFocus( NULL ), mDefaultKeyboardFocus( NULL ), - mKeyboardFocusLostCallback( NULL ), mTopCtrl( NULL ), mFocusWeight(0.f), mAppHasFocus(TRUE) // Macs don't seem to notify us that we've gotten focus, so default to true @@ -75,12 +74,11 @@ void LLFocusMgr::releaseFocusIfNeeded( LLView* view ) if (view == mLockedView) { mLockedView = NULL; - mKeyboardLockedFocusLostCallback = NULL; - setKeyboardFocus( NULL, NULL ); + setKeyboardFocus( NULL ); } else { - setKeyboardFocus( mLockedView, mKeyboardLockedFocusLostCallback ); + setKeyboardFocus( mLockedView ); } } @@ -91,7 +89,7 @@ void LLFocusMgr::releaseFocusIfNeeded( LLView* view ) } -void LLFocusMgr::setKeyboardFocus(LLUICtrl* new_focus, FocusLostCallback on_focus_lost, BOOL lock) +void LLFocusMgr::setKeyboardFocus(LLUICtrl* new_focus, BOOL lock) { if (mLockedView && (new_focus == NULL || @@ -101,28 +99,27 @@ void LLFocusMgr::setKeyboardFocus(LLUICtrl* new_focus, FocusLostCallback on_focu // or one of its descendants return; } - FocusLostCallback old_callback = mKeyboardFocusLostCallback; - mKeyboardFocusLostCallback = on_focus_lost; //llinfos << "Keyboard focus handled by " << (new_focus ? new_focus->getName() : "nothing") << llendl; if( new_focus != mKeyboardFocus ) { - LLUICtrl* old_focus = mKeyboardFocus; + mLastKeyboardFocus = mKeyboardFocus; mKeyboardFocus = new_focus; + if( mLastKeyboardFocus ) + { + mLastKeyboardFocus->onFocusLost(); + } + // clear out any existing flash if (new_focus) { mFocusWeight = 0.f; + new_focus->onFocusReceived(); } mFocusTimer.reset(); - if( old_callback ) - { - old_callback( old_focus ); - } - #ifdef _DEBUG mKeyboardFocusName = new_focus ? new_focus->getName() : "none"; #endif @@ -204,13 +201,11 @@ void LLFocusMgr::removeKeyboardFocusWithoutCallback( LLView* focus ) if (focus == mLockedView) { mLockedView = NULL; - mKeyboardLockedFocusLostCallback = NULL; } if( mKeyboardFocus == focus ) { mKeyboardFocus = NULL; - mKeyboardFocusLostCallback = NULL; #ifdef _DEBUG mKeyboardFocusName = "none"; #endif @@ -293,13 +288,19 @@ BOOL LLFocusMgr::childIsTopCtrl( LLView* parent ) // set new_top = NULL to release top_view. void LLFocusMgr::setTopCtrl( LLUICtrl* new_top ) { - if( new_top != mTopCtrl ) + LLUICtrl* old_top = mTopCtrl; + if( new_top != old_top ) { mTopCtrl = new_top; #ifdef _DEBUG mTopCtrlName = new_top ? new_top->getName() : "none"; #endif + + if (old_top) + { + old_top->onLostTop(); + } } } @@ -317,13 +318,11 @@ void LLFocusMgr::removeTopCtrlWithoutCallback( LLUICtrl* top_view ) void LLFocusMgr::lockFocus() { mLockedView = mKeyboardFocus; - mKeyboardLockedFocusLostCallback = mKeyboardFocusLostCallback; } void LLFocusMgr::unlockFocus() { mLockedView = NULL; - mKeyboardLockedFocusLostCallback = NULL; } F32 LLFocusMgr::getFocusFlashAmt() @@ -356,9 +355,9 @@ void LLFocusMgr::setAppHasFocus(BOOL focus) } // release focus from "top ctrl"s, which generally hides them - if (!focus && mTopCtrl && mTopCtrl->hasFocus()) + if (!focus && mTopCtrl) { - mTopCtrl->setFocus(FALSE); + setTopCtrl(NULL); } mAppHasFocus = focus; } diff --git a/indra/llui/llfocusmgr.h b/indra/llui/llfocusmgr.h index 1c25153fbe..20dc21fc3a 100644 --- a/indra/llui/llfocusmgr.h +++ b/indra/llui/llfocusmgr.h @@ -44,8 +44,6 @@ class LLMouseHandler; class LLFocusMgr { public: - typedef void (*FocusLostCallback)(LLUICtrl*); - LLFocusMgr(); ~LLFocusMgr(); @@ -56,11 +54,11 @@ public: BOOL childHasMouseCapture( LLView* parent ); // Keyboard Focus - void setKeyboardFocus(LLUICtrl* new_focus, FocusLostCallback on_focus_lost, BOOL lock = FALSE); // new_focus = NULL to release the focus. + void setKeyboardFocus(LLUICtrl* new_focus, BOOL lock = FALSE); // new_focus = NULL to release the focus. LLUICtrl* getKeyboardFocus() const { return mKeyboardFocus; } + LLUICtrl* getLastKeyboardFocus() const { return mLastKeyboardFocus; } BOOL childHasKeyboardFocus( const LLView* parent ) const; void removeKeyboardFocusWithoutCallback( LLView* focus ); - FocusLostCallback getFocusCallback() { return mKeyboardFocusLostCallback; } F32 getFocusTime() const { return mFocusTimer.getElapsedTimeF32(); } F32 getFocusFlashAmt(); LLColor4 getFocusColor(); @@ -90,15 +88,14 @@ public: protected: LLUICtrl* mLockedView; - FocusLostCallback mKeyboardLockedFocusLostCallback; // Mouse Captor LLMouseHandler* mMouseCaptor; // Mouse events are premptively routed to this object // Keyboard Focus LLUICtrl* mKeyboardFocus; // Keyboard events are preemptively routed to this object + LLUICtrl* mLastKeyboardFocus; // who last had focus LLUICtrl* mDefaultKeyboardFocus; - FocusLostCallback mKeyboardFocusLostCallback; // The object to which keyboard events are routed is called before another object takes its place // Top View LLUICtrl* mTopCtrl; diff --git a/indra/llui/lliconctrl.cpp b/indra/llui/lliconctrl.cpp index 1e49210565..a063ebcd25 100644 --- a/indra/llui/lliconctrl.cpp +++ b/indra/llui/lliconctrl.cpp @@ -88,18 +88,11 @@ void LLIconCtrl::draw() { if( getVisible() ) { - // Border - BOOL has_image = !mImageID.isNull(); - - if( has_image ) + if( mImagep.notNull() ) { - if( mImagep.notNull() ) - { - gl_draw_scaled_image(0, 0, - mRect.getWidth(), mRect.getHeight(), - mImagep, - mColor ); - } + mImagep->draw(0, 0, + mRect.getWidth(), mRect.getHeight(), + mColor ); } LLUICtrl::draw(); @@ -154,6 +147,7 @@ LLView* LLIconCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory * LLUICtrlFactory::getAttributeColor(node,"color", color); LLIconCtrl* icon = new LLIconCtrl(name, rect, image_id); + icon->setColor(color); icon->initFromXML(node, parent); diff --git a/indra/llui/lliconctrl.h b/indra/llui/lliconctrl.h index b789269558..1e474d0935 100644 --- a/indra/llui/lliconctrl.h +++ b/indra/llui/lliconctrl.h @@ -73,7 +73,7 @@ protected: LLColor4 mColor; LLString mImageName; LLUUID mImageID; - LLPointer<LLImageGL> mImagep; + LLPointer<LLUIImage> mImagep; }; #endif diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 3c7cd17b92..4297f5fef8 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -128,7 +128,7 @@ LLLineEditor::LLLineEditor(const LLString& name, const LLRect& rect, S32 max_length_bytes, void (*commit_callback)(LLUICtrl* caller, void* user_data ), void (*keystroke_callback)(LLLineEditor* caller, void* user_data ), - void (*focus_lost_callback)(LLUICtrl* caller, void* user_data ), + void (*focus_lost_callback)(LLFocusableElement* caller, void* user_data ), void* userdata, LLLinePrevalidateFunc prevalidate_func, LLViewBorder::EBevel border_bevel, @@ -351,10 +351,14 @@ void LLLineEditor::setText(const LLStringExplicit &new_text) // Check to see if entire field is selected. S32 len = mText.length(); - BOOL allSelected = (len > 0) && (( mSelectionStart == 0 && mSelectionEnd == len ) - || ( mSelectionStart == len && mSelectionEnd == 0 )); + BOOL all_selected = (len > 0) + && (( mSelectionStart == 0 && mSelectionEnd == len ) + || ( mSelectionStart == len && mSelectionEnd == 0 )); // Do safe truncation so we don't split multi-byte characters + // also consider entire string selected when mSelectAllonFocusReceived is set on an empty, focused line editor + all_selected = all_selected || (len == 0 && hasFocus() && mSelectAllonFocusReceived); + LLString truncated_utf8 = new_text; if (truncated_utf8.size() > (U32)mMaxLengthBytes) { @@ -362,7 +366,7 @@ void LLLineEditor::setText(const LLStringExplicit &new_text) } mText.assign(truncated_utf8); - if (allSelected) + if (all_selected) { // ...keep whole thing selected selectAll(); diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index a019353856..0739315c4d 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -77,7 +77,7 @@ public: S32 max_length_bytes = 254, void (*commit_callback)(LLUICtrl* caller, void* user_data) = NULL, void (*keystroke_callback)(LLLineEditor* caller, void* user_data) = NULL, - void (*focus_lost_callback)(LLUICtrl* caller, void* user_data) = NULL, + void (*focus_lost_callback)(LLFocusableElement* caller, void* user_data) = NULL, void* userdata = NULL, LLLinePrevalidateFunc prevalidate_func = NULL, LLViewBorder::EBevel border_bevel = LLViewBorder::BEVEL_IN, diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 46f9f515d7..19a5085a25 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -4514,7 +4514,7 @@ BOOL LLMenuHolderGL::hideMenus() } //if (gFocusMgr.childHasKeyboardFocus(this)) //{ - // gFocusMgr.setKeyboardFocus(NULL, NULL); + // gFocusMgr.setKeyboardFocus(NULL); //} return menu_visible; @@ -4599,6 +4599,7 @@ void LLTearOffMenu::onFocusReceived() break; } } + LLFloater::onFocusReceived(); } void LLTearOffMenu::onFocusLost() diff --git a/indra/llui/llmodaldialog.cpp b/indra/llui/llmodaldialog.cpp index b8b8bf9443..ca8020fe70 100644 --- a/indra/llui/llmodaldialog.cpp +++ b/indra/llui/llmodaldialog.cpp @@ -287,7 +287,7 @@ void LLModalDialog::onAppFocusLost() if( gFocusMgr.childHasKeyboardFocus( instance ) ) { - gFocusMgr.setKeyboardFocus( NULL, NULL ); + gFocusMgr.setKeyboardFocus( NULL ); } } } diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp index 6d000f3e7f..294ce5df18 100644 --- a/indra/llui/llpanel.cpp +++ b/indra/llui/llpanel.cpp @@ -64,7 +64,6 @@ LLPanel::panel_map_t LLPanel::sPanelMap; LLPanel::alert_queue_t LLPanel::sAlertQueue; const S32 RESIZE_BAR_OVERLAP = 1; -const S32 PANEL_STACK_GAP = RESIZE_BAR_HEIGHT; void LLPanel::init() { @@ -88,6 +87,7 @@ LLPanel::LLPanel() : mRectControl() { init(); + setName("panel"); } LLPanel::LLPanel(const LLString& name) @@ -124,6 +124,7 @@ LLPanel::LLPanel(const LLString& name, const LLString& rect_control, BOOL border void LLPanel::addBorder(LLViewBorder::EBevel border_bevel, LLViewBorder::EStyle border_style, S32 border_thickness) { + removeBorder(); mBorder = new LLViewBorder( "panel border", LLRect(0, mRect.getHeight(), mRect.getWidth(), 0), border_bevel, border_style, border_thickness ); @@ -361,12 +362,6 @@ BOOL LLPanel::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent ) { BOOL handled = FALSE; - if( getVisible() && getEnabled() && gFocusMgr.childHasKeyboardFocus(this) && KEY_ESCAPE == key ) - { - gFocusMgr.setKeyboardFocus(NULL, NULL); - return TRUE; - } - if( getVisible() && getEnabled() && gFocusMgr.childHasKeyboardFocus(this) && !called_from_parent ) { @@ -472,7 +467,7 @@ void LLPanel::setFocus(BOOL b) { if( this == gFocusMgr.getKeyboardFocus() ) { - gFocusMgr.setKeyboardFocus( NULL, NULL ); + gFocusMgr.setKeyboardFocus( NULL ); } else { @@ -595,7 +590,8 @@ LLView* LLPanel::fromXML(LLXMLNodePtr node, LLView* parent, LLUICtrlFactory *fac { LLRect rect; createRect(node, rect, parent, LLRect()); - panelp = new LLPanel(name, rect); + // create a new panel without a border, by default + panelp = new LLPanel(name, rect, FALSE); panelp->initPanelXML(node, parent, factory); // preserve panel's width and height, but override the location const LLRect& panelrect = panelp->getRect(); @@ -608,12 +604,13 @@ LLView* LLPanel::fromXML(LLXMLNodePtr node, LLView* parent, LLUICtrlFactory *fac { panelp->initPanelXML(node, parent, factory); } + return panelp; } BOOL LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - LLString name("panel"); + LLString name = getName(); node->getAttributeString("name", name); setName(name); @@ -628,13 +625,15 @@ BOOL LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *f if (!xml_filename.empty()) { - // Preserve postion of embedded panel but allow panel to dictate width/height - LLRect rect(getRect()); didPost = factory->buildPanel(this, xml_filename, NULL); - S32 w = getRect().getWidth(); - S32 h = getRect().getHeight(); - rect.setLeftTopAndSize(rect.mLeft, rect.mTop, w, h); - setRect(rect); + + LLRect new_rect = getRect(); + // override rectangle with embedding parameters as provided + createRect(node, new_rect, parent); + setOrigin(new_rect.mLeft, new_rect.mBottom); + reshape(new_rect.getWidth(), new_rect.getHeight()); + // optionally override follows flags from including nodes + parseFollowsFlags(node); } else { @@ -678,7 +677,7 @@ void LLPanel::setPanelParameters(LLXMLNodePtr node, LLView* parent) initFromXML(node, parent); /////// Border attributes /////// - BOOL border = FALSE; + BOOL border = mBorder != NULL; node->getAttributeBOOL("border", border); if (border) { @@ -706,24 +705,24 @@ void LLPanel::setPanelParameters(LLXMLNodePtr node, LLView* parent) } /////// Background attributes /////// - BOOL background_visible = FALSE; + BOOL background_visible = mBgVisible; node->getAttributeBOOL("background_visible", background_visible); setBackgroundVisible(background_visible); - BOOL background_opaque = FALSE; + BOOL background_opaque = mBgOpaque; node->getAttributeBOOL("background_opaque", background_opaque); setBackgroundOpaque(background_opaque); LLColor4 color; - color = LLUI::sColorsGroup->getColor( "FocusBackgroundColor" ); + color = mBgColorOpaque; LLUICtrlFactory::getAttributeColor(node,"bg_opaque_color", color); setBackgroundColor(color); - color = LLUI::sColorsGroup->getColor( "DefaultBackgroundColor" ); + color = mBgColorAlpha; LLUICtrlFactory::getAttributeColor(node,"bg_alpha_color", color); setTransparentColor(color); - LLString label; + LLString label = getLabel(); node->getAttributeString("label", label); setLabel(label); } @@ -853,12 +852,12 @@ BOOL LLPanel::childHasFocus(const LLString& id) } -void LLPanel::childSetFocusChangedCallback(const LLString& id, void (*cb)(LLUICtrl*, void*)) +void LLPanel::childSetFocusChangedCallback(const LLString& id, void (*cb)(LLFocusableElement*, void*), void* user_data) { LLUICtrl* child = (LLUICtrl*)getChildByName(id, true); if (child) { - child->setFocusChangedCallback(cb); + child->setFocusChangedCallback(cb, user_data); } } @@ -1165,11 +1164,12 @@ void LLPanel::storeRectControl() // struct LLLayoutStack::LLEmbeddedPanel { - LLEmbeddedPanel(LLPanel* panelp, eLayoutOrientation orientation, S32 min_width, S32 min_height, BOOL auto_resize) : + LLEmbeddedPanel(LLPanel* panelp, eLayoutOrientation orientation, S32 min_width, S32 min_height, BOOL auto_resize, BOOL user_resize) : mPanel(panelp), mMinWidth(min_width), mMinHeight(min_height), mAutoResize(auto_resize), + mUserResize(user_resize), mOrientation(orientation), mVisibleAmt(1.f) // default to fully visible { @@ -1205,6 +1205,7 @@ struct LLLayoutStack::LLEmbeddedPanel S32 mMinWidth; S32 mMinHeight; BOOL mAutoResize; + BOOL mUserResize; LLResizeBar* mResizeBar; eLayoutOrientation mOrientation; F32 mVisibleAmt; @@ -1213,7 +1214,8 @@ struct LLLayoutStack::LLEmbeddedPanel LLLayoutStack::LLLayoutStack(eLayoutOrientation orientation) : mOrientation(orientation), mMinWidth(0), - mMinHeight(0) + mMinHeight(0), + mPanelSpacing(RESIZE_BAR_HEIGHT) { } @@ -1226,24 +1228,26 @@ void LLLayoutStack::draw() { updateLayout(); { - // clip if outside nominal bounds - LLLocalClipRect clip(getLocalRect(), mRect.getWidth() > mMinWidth || mRect.getHeight() > mMinHeight); e_panel_list_t::iterator panel_it; for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) { + // clip to layout rectangle, not bounding rectangle LLRect clip_rect = (*panel_it)->mPanel->getRect(); // scale clipping rectangle by visible amount if (mOrientation == HORIZONTAL) { - clip_rect.mRight = clip_rect.mLeft + llround(clip_rect.getWidth() * (*panel_it)->mVisibleAmt); + clip_rect.mRight = clip_rect.mLeft + llround((F32)clip_rect.getWidth() * (*panel_it)->mVisibleAmt); } else { - clip_rect.mBottom = clip_rect.mTop - llround(clip_rect.getHeight() * (*panel_it)->mVisibleAmt); + clip_rect.mBottom = clip_rect.mTop - llround((F32)clip_rect.getHeight() * (*panel_it)->mVisibleAmt); } - LLLocalClipRect clip(clip_rect, (*panel_it)->mVisibleAmt < 1.f); + + LLPanel* panelp = (*panel_it)->mPanel; + + LLLocalClipRect clip(clip_rect); // only force drawing invisible children if visible amount is non-zero - drawChild((*panel_it)->mPanel, 0, 0, (*panel_it)->mVisibleAmt > 0.f); + drawChild(panelp, 0, 0, !clip_rect.isNull()); } } } @@ -1258,17 +1262,13 @@ void LLLayoutStack::removeCtrl(LLUICtrl* ctrl) delete embedded_panelp; } + // need to update resizebars + calcMinExtents(); LLView::removeCtrl(ctrl); } -void LLLayoutStack::reshape(S32 width, S32 height, BOOL called_from_parent) -{ - LLView::reshape(width, height, called_from_parent); - //updateLayout(); -} - LLXMLNodePtr LLLayoutStack::getXML(bool save_children) const { LLXMLNodePtr node = LLView::getXML(); @@ -1298,6 +1298,14 @@ LLView* LLLayoutStack::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactor LLLayoutStack* layout_stackp = new LLLayoutStack(orientation); + node->getAttributeS32("border_size", layout_stackp->mPanelSpacing); + // don't allow negative spacing values + layout_stackp->mPanelSpacing = llmax(layout_stackp->mPanelSpacing, 0); + + LLString name("stack"); + node->getAttributeString("name", name); + + layout_stackp->setName(name); layout_stackp->initFromXML(node, parent); LLXMLNodePtr child; @@ -1308,16 +1316,18 @@ LLView* LLLayoutStack::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactor S32 min_width = 0; S32 min_height = 0; BOOL auto_resize = TRUE; + BOOL user_resize = TRUE; child->getAttributeS32("min_width", min_width); child->getAttributeS32("min_height", min_height); child->getAttributeBOOL("auto_resize", auto_resize); + child->getAttributeBOOL("user_resize", user_resize); LLPanel* panelp = (LLPanel*)LLPanel::fromXML(child, layout_stackp, factory); if (panelp) { panelp->setFollowsNone(); - layout_stackp->addPanel(panelp, min_width, min_height, auto_resize); + layout_stackp->addPanel(panelp, min_width, min_height, auto_resize, user_resize); } } } @@ -1335,11 +1345,36 @@ S32 LLLayoutStack::getMinHeight() return mMinHeight; } -void LLLayoutStack::addPanel(LLPanel* panel, S32 min_width, S32 min_height, BOOL auto_resize, S32 index) +S32 LLLayoutStack::getDefaultHeight(S32 cur_height) { - LLEmbeddedPanel* embedded_panel = new LLEmbeddedPanel(panel, mOrientation, min_width, min_height, auto_resize); + // if we are spanning our children (crude upward propagation of size) + // then don't enforce our size on our children + if (mOrientation == HORIZONTAL) + { + cur_height = llmax(mMinHeight, mRect.getHeight()); + } + + return cur_height; +} + +S32 LLLayoutStack::getDefaultWidth(S32 cur_width) +{ + // if we are spanning our children (crude upward propagation of size) + // then don't enforce our size on our children + if (mOrientation == VERTICAL) + { + cur_width = llmax(mMinWidth, mRect.getWidth()); + } + + return cur_width; +} + +void LLLayoutStack::addPanel(LLPanel* panel, S32 min_width, S32 min_height, BOOL auto_resize, BOOL user_resize, S32 index) +{ + LLEmbeddedPanel* embedded_panel = new LLEmbeddedPanel(panel, mOrientation, min_width, min_height, auto_resize, user_resize); mPanels.insert(mPanels.begin() + llclamp(index, 0, (S32)mPanels.size()), embedded_panel); + addChild(panel); addChild(embedded_panel->mResizeBar); @@ -1347,29 +1382,15 @@ void LLLayoutStack::addPanel(LLPanel* panel, S32 min_width, S32 min_height, BOOL // with a bit of overlap for (e_panel_list_t::iterator panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) { - e_panel_list_t::iterator next_it = panel_it; - ++next_it; - LLResizeBar* resize_barp = (*panel_it)->mResizeBar; sendChildToFront(resize_barp); - // last resize bar is disabled, since its not between any two panels - if ( next_it == mPanels.end() ) - { - resize_barp->setEnabled(FALSE); - } - else - { - resize_barp->setEnabled(TRUE); - } } - //updateLayout(); } void LLLayoutStack::removePanel(LLPanel* panel) { removeChild(panel); - //updateLayout(); } void LLLayoutStack::updateLayout(BOOL force_resize) @@ -1377,11 +1398,11 @@ void LLLayoutStack::updateLayout(BOOL force_resize) calcMinExtents(); // calculate current extents - S32 cur_width = 0; - S32 cur_height = 0; + S32 total_width = 0; + S32 total_height = 0; const F32 ANIM_OPEN_TIME = 0.02f; - const F32 ANIM_CLOSE_TIME = 0.02f; + const F32 ANIM_CLOSE_TIME = 0.03f; e_panel_list_t::iterator panel_it; for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) @@ -1403,23 +1424,22 @@ void LLLayoutStack::updateLayout(BOOL force_resize) (*panel_it)->mVisibleAmt = 0.f; } } + if (mOrientation == HORIZONTAL) { - // all panels get expanded to max of all the minimum dimensions - cur_height = llmax(mMinHeight, panelp->getRect().getHeight()); - cur_width += llround(panelp->getRect().getWidth() * (*panel_it)->mVisibleAmt); - if (panel_it != mPanels.end()) + total_width += llround(panelp->getRect().getWidth() * (*panel_it)->mVisibleAmt); + // want n-1 panel gaps for n panels + if (panel_it != mPanels.begin()) { - cur_width += PANEL_STACK_GAP; + total_width += mPanelSpacing; } } else //VERTICAL { - cur_width = llmax(mMinWidth, panelp->getRect().getWidth()); - cur_height += llround(panelp->getRect().getHeight() * (*panel_it)->mVisibleAmt); - if (panel_it != mPanels.end()) + total_height += llround(panelp->getRect().getHeight() * (*panel_it)->mVisibleAmt); + if (panel_it != mPanels.begin()) { - cur_height += PANEL_STACK_GAP; + total_height += mPanelSpacing; } } } @@ -1465,11 +1485,11 @@ void LLLayoutStack::updateLayout(BOOL force_resize) S32 pixels_to_distribute; if (mOrientation == HORIZONTAL) { - pixels_to_distribute = mRect.getWidth() - cur_width; + pixels_to_distribute = mRect.getWidth() - total_width; } else //VERTICAL { - pixels_to_distribute = mRect.getHeight() - cur_height; + pixels_to_distribute = mRect.getHeight() - total_height; } S32 cur_x = 0; @@ -1482,7 +1502,7 @@ void LLLayoutStack::updateLayout(BOOL force_resize) S32 cur_width = panelp->getRect().getWidth(); S32 cur_height = panelp->getRect().getHeight(); S32 new_width = llmax((*panel_it)->mMinWidth, cur_width); - S32 new_height = llmax((*panel_it)->mMinHeight, cur_height); + S32 new_height = llmax((*panel_it)->mMinHeight, cur_height); S32 delta_size = 0; @@ -1502,11 +1522,11 @@ void LLLayoutStack::updateLayout(BOOL force_resize) // grow all elements equally delta_size = llround((F32)pixels_to_distribute / (F32)num_resizable_panels); } - new_width = llmax((*panel_it)->mMinWidth, panelp->getRect().getWidth() + delta_size); + new_width = llmax((*panel_it)->mMinWidth, cur_width + delta_size); } else { - new_width = llmax(mMinWidth, mRect.getWidth()); + new_width = getDefaultWidth(new_width); } if (mOrientation == VERTICAL) @@ -1520,22 +1540,22 @@ void LLLayoutStack::updateLayout(BOOL force_resize) { delta_size = llround((F32)pixels_to_distribute / (F32)num_resizable_panels); } - new_height = llmax((*panel_it)->mMinHeight, panelp->getRect().getHeight() + delta_size); + new_height = llmax((*panel_it)->mMinHeight, cur_height + delta_size); } else { - new_height = llmax(mMinHeight, mRect.getHeight()); + new_height = getDefaultHeight(new_height); } } - else // don't resize + else { if (mOrientation == HORIZONTAL) { - new_height = llmax(mMinHeight, mRect.getHeight()); + new_height = getDefaultHeight(new_height); } else // VERTICAL { - new_width = llmax(mMinWidth, mRect.getWidth()); + new_width = getDefaultWidth(new_width); } } @@ -1550,22 +1570,22 @@ void LLLayoutStack::updateLayout(BOOL force_resize) if (mOrientation == HORIZONTAL) { resize_bar_rect.mLeft = panel_rect.mRight - RESIZE_BAR_OVERLAP; - resize_bar_rect.mRight = panel_rect.mRight + PANEL_STACK_GAP + RESIZE_BAR_OVERLAP; + resize_bar_rect.mRight = panel_rect.mRight + mPanelSpacing + RESIZE_BAR_OVERLAP; } else { resize_bar_rect.mTop = panel_rect.mBottom + RESIZE_BAR_OVERLAP; - resize_bar_rect.mBottom = panel_rect.mBottom - PANEL_STACK_GAP - RESIZE_BAR_OVERLAP; + resize_bar_rect.mBottom = panel_rect.mBottom - mPanelSpacing - RESIZE_BAR_OVERLAP; } (*panel_it)->mResizeBar->setRect(resize_bar_rect); if (mOrientation == HORIZONTAL) { - cur_x += llround(new_width * (*panel_it)->mVisibleAmt) + PANEL_STACK_GAP; + cur_x += llround(new_width * (*panel_it)->mVisibleAmt) + mPanelSpacing; } else //VERTICAL { - cur_y -= llround(new_height * (*panel_it)->mVisibleAmt) + PANEL_STACK_GAP; + cur_y -= llround(new_height * (*panel_it)->mVisibleAmt) + mPanelSpacing; } } @@ -1577,29 +1597,38 @@ void LLLayoutStack::updateLayout(BOOL force_resize) if (mOrientation == HORIZONTAL) { - (*panel_it)->mResizeBar->setResizeLimits((*panel_it)->mMinWidth, (*panel_it)->mMinWidth + shrink_headroom_total); + (*panel_it)->mResizeBar->setResizeLimits( + (*panel_it)->mMinWidth, + (*panel_it)->mMinWidth + shrink_headroom_total); } else //VERTICAL { - (*panel_it)->mResizeBar->setResizeLimits((*panel_it)->mMinHeight, (*panel_it)->mMinHeight + shrink_headroom_total); + (*panel_it)->mResizeBar->setResizeLimits( + (*panel_it)->mMinHeight, + (*panel_it)->mMinHeight + shrink_headroom_total); } - // hide resize bars for invisible panels - (*panel_it)->mResizeBar->setVisible(panelp->getVisible()); - if (panelp->getVisible()) + + // toggle resize bars based on panel visibility, resizability, etc + BOOL resize_bar_enabled = panelp->getVisible() && (*panel_it)->mUserResize; + (*panel_it)->mResizeBar->setVisible(resize_bar_enabled); + + if (resize_bar_enabled) { last_resize_bar = (*panel_it)->mResizeBar; } } // hide last resize bar as there is nothing past it + // resize bars need to be in between two resizable panels if (last_resize_bar) { last_resize_bar->setVisible(FALSE); } // not enough room to fit existing contents - if (!force_resize && - ((cur_y != -PANEL_STACK_GAP) || (cur_x != mRect.getWidth() + PANEL_STACK_GAP))) + if (!force_resize + && ((cur_y != -mPanelSpacing) + || (cur_x != mRect.getWidth() + mPanelSpacing))) { // do another layout pass with all stacked elements contributing // even those that don't usually resize @@ -1631,20 +1660,22 @@ void LLLayoutStack::calcMinExtents() { if (mOrientation == HORIZONTAL) { - mMinHeight = llmax(mMinHeight, (*panel_it)->mMinHeight); + mMinHeight = llmax( mMinHeight, + (*panel_it)->mMinHeight); mMinWidth += (*panel_it)->mMinWidth; if (panel_it != mPanels.begin()) { - mMinWidth += PANEL_STACK_GAP; + mMinWidth += mPanelSpacing; } } else //VERTICAL { - mMinWidth = llmax(mMinWidth, (*panel_it)->mMinWidth); + mMinWidth = llmax( mMinWidth, + (*panel_it)->mMinWidth); mMinHeight += (*panel_it)->mMinHeight; if (panel_it != mPanels.begin()) { - mMinHeight += PANEL_STACK_GAP; + mMinHeight += mPanelSpacing; } } } diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h index 78aa7cfc21..88b4ecb76b 100644 --- a/indra/llui/llpanel.h +++ b/indra/llui/llpanel.h @@ -173,7 +173,7 @@ public: // LLUICtrl void childSetFocus(const LLString& id, BOOL focus = TRUE); BOOL childHasFocus(const LLString& id); - void childSetFocusChangedCallback(const LLString& id, void (*cb)(LLUICtrl*, void*)); + void childSetFocusChangedCallback(const LLString& id, void (*cb)(LLFocusableElement*, void*), void* user_data = NULL); void childSetCommitCallback(const LLString& id, void (*cb)(LLUICtrl*, void*), void* userdata = NULL ); void childSetDoubleClickCallback(const LLString& id, void (*cb)(void*), void* userdata = NULL ); @@ -277,9 +277,9 @@ public: virtual ~LLLayoutStack(); /*virtual*/ void draw(); - /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent); /*virtual*/ LLXMLNodePtr getXML(bool save_children = true) const; /*virtual*/ void removeCtrl(LLUICtrl* ctrl); + virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_LAYOUT_STACK; } virtual LLString getWidgetTag() const { return LL_LAYOUT_STACK_TAG; } @@ -288,7 +288,7 @@ public: S32 getMinWidth(); S32 getMinHeight(); - void addPanel(LLPanel* panel, S32 min_width, S32 min_height, BOOL auto_resize, S32 index = S32_MAX); + void addPanel(LLPanel* panel, S32 min_width, S32 min_height, BOOL auto_resize, BOOL user_resize, S32 index = S32_MAX); void removePanel(LLPanel* panel); void updateLayout(BOOL force_resize = FALSE); @@ -299,6 +299,8 @@ protected: void calcMinExtents(); S32 getMinStackSize(); S32 getCurStackSize(); + S32 getDefaultHeight(S32 cur_height); + S32 getDefaultWidth(S32 cur_width); protected: eLayoutOrientation mOrientation; @@ -308,6 +310,7 @@ protected: S32 mMinWidth; S32 mMinHeight; + S32 mPanelSpacing; }; #endif diff --git a/indra/llui/llresizehandle.cpp b/indra/llui/llresizehandle.cpp index 17b76def71..120323e7d1 100644 --- a/indra/llui/llresizehandle.cpp +++ b/indra/llui/llresizehandle.cpp @@ -60,7 +60,7 @@ LLResizeHandle::LLResizeHandle( const LLString& name, const LLRect& rect, S32 mi if( RIGHT_BOTTOM == mCorner) { LLUUID image_id(LLUI::sConfigGroup->getString("UIImgResizeBottomRightUUID")); - mImage = LLUI::sImageProvider->getUIImageByID(image_id); + mImage = LLUI::sImageProvider->getImageByID(image_id); } switch( mCorner ) diff --git a/indra/llui/llscrollbar.cpp b/indra/llui/llscrollbar.cpp index 99908a6bc0..b106bb570d 100644 --- a/indra/llui/llscrollbar.cpp +++ b/indra/llui/llscrollbar.cpp @@ -159,30 +159,50 @@ void LLScrollbar::setDocParams( S32 size, S32 pos ) void LLScrollbar::setDocPos(S32 pos) { - mDocPos = llclamp( pos, 0, getDocPosMax() ); - mDocChanged = TRUE; + if (pos != mDocPos) + { + mDocPos = llclamp( pos, 0, getDocPosMax() ); + mDocChanged = TRUE; - updateThumbRect(); + updateThumbRect(); + } } void LLScrollbar::setDocSize(S32 size) { - mDocSize = size; - mDocPos = llclamp( mDocPos, 0, getDocPosMax() ); - mDocChanged = TRUE; + if (size != mDocSize) + { + mDocSize = size; + mDocPos = llclamp( mDocPos, 0, getDocPosMax() ); + mDocChanged = TRUE; - updateThumbRect(); + updateThumbRect(); + } } void LLScrollbar::setPageSize( S32 page_size ) { - mPageSize = page_size; - mDocPos = llclamp( mDocPos, 0, getDocPosMax() ); - mDocChanged = TRUE; + if (page_size != mPageSize) + { + mPageSize = page_size; + mDocPos = llclamp( mDocPos, 0, getDocPosMax() ); + mDocChanged = TRUE; - updateThumbRect(); + updateThumbRect(); + } +} + +BOOL LLScrollbar::isAtBeginning() +{ + return mDocPos == 0; +} + +BOOL LLScrollbar::isAtEnd() +{ + return mDocPos == getDocPosMax(); } + void LLScrollbar::updateThumbRect() { // llassert( 0 <= mDocSize ); @@ -479,7 +499,7 @@ void LLScrollbar::draw() // Draw background and thumb. LLUUID rounded_rect_image_id; rounded_rect_image_id.set(LLUI::sAssetsGroup->getString("rounded_square.tga")); - LLImageGL* rounded_rect_imagep = LLUI::sImageProvider->getUIImageByID(rounded_rect_image_id); + LLImageGL* rounded_rect_imagep = LLUI::sImageProvider->getImageByID(rounded_rect_image_id); if (!rounded_rect_imagep) { diff --git a/indra/llui/llscrollbar.h b/indra/llui/llscrollbar.h index 353935cfb8..50aa3cafe9 100644 --- a/indra/llui/llscrollbar.h +++ b/indra/llui/llscrollbar.h @@ -88,6 +88,9 @@ public: void setDocPos( S32 pos ); S32 getDocPos() { return mDocPos; } + BOOL isAtBeginning(); + BOOL isAtEnd(); + // How many "lines" of the "document" is can appear on a page. void setPageSize( S32 page_size ); S32 getPageSize() { return mPageSize; } diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp index 8b5d009b95..34a29cef51 100644 --- a/indra/llui/llscrollcontainer.cpp +++ b/indra/llui/llscrollcontainer.cpp @@ -394,33 +394,29 @@ BOOL LLScrollableContainerView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL LLScrollableContainerView::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect) { - if( getVisible() && pointInView(x,y) ) + S32 local_x, local_y; + for( S32 i = 0; i < SCROLLBAR_COUNT; i++ ) { - S32 local_x, local_y; - for( S32 i = 0; i < SCROLLBAR_COUNT; i++ ) + local_x = x - mScrollbar[i]->getRect().mLeft; + local_y = y - mScrollbar[i]->getRect().mBottom; + if( mScrollbar[i]->handleToolTip(local_x, local_y, msg, sticky_rect) ) { - local_x = x - mScrollbar[i]->getRect().mLeft; - local_y = y - mScrollbar[i]->getRect().mBottom; - if( mScrollbar[i]->handleToolTip(local_x, local_y, msg, sticky_rect) ) - { - return TRUE; - } + return TRUE; } - // Handle 'child' view. - if( mScrolledView ) + } + // Handle 'child' view. + if( mScrolledView ) + { + local_x = x - mScrolledView->getRect().mLeft; + local_y = y - mScrolledView->getRect().mBottom; + if( mScrolledView->handleToolTip(local_x, local_y, msg, sticky_rect) ) { - local_x = x - mScrolledView->getRect().mLeft; - local_y = y - mScrolledView->getRect().mBottom; - if( mScrolledView->handleToolTip(local_x, local_y, msg, sticky_rect) ) - { - return TRUE; - } + return TRUE; } - - // Opaque - return TRUE; } - return FALSE; + + // Opaque + return TRUE; } void LLScrollableContainerView::calcVisibleSize( S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 96a739418f..0c81b2da08 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -61,43 +61,55 @@ const S32 LIST_SNAP_PADDING = 5; // local structures & classes. struct SortScrollListItem { - SortScrollListItem(const S32 sort_col, BOOL sort_ascending) - { - mSortCol = sort_col; - mSortAscending = sort_ascending; - } + SortScrollListItem(const std::vector<std::pair<S32, BOOL> >& sort_orders) + : mSortOrders(sort_orders) + {} bool operator()(const LLScrollListItem* i1, const LLScrollListItem* i2) { - const LLScrollListCell *cell1; - const LLScrollListCell *cell2; - - cell1 = i1->getColumn(mSortCol); - cell2 = i2->getColumn(mSortCol); + if ( mSortOrders.empty() ) return true; + + const LLScrollListCell *cell1 = NULL; + const LLScrollListCell *cell2 = NULL; - S32 order = 1; - if (!mSortAscending) + sort_order_t::const_reverse_iterator end_it = mSortOrders.rend(); + sort_order_t::const_reverse_iterator it; + + // sort over all columns in order specified by mSortOrders + S32 sort_result = 0; + for (it = mSortOrders.rbegin(); it != end_it; ++it) { - order = -1; - } + S32 col_idx = it->first; + BOOL sort_ascending = it->second; - BOOL retval = FALSE; + cell1 = i1->getColumn(col_idx); + cell2 = i2->getColumn(col_idx); + // ascending or descending sort for this column? + S32 order = 1; + if (!sort_ascending) + { + order = -1; + } - if (cell1 && cell2) - { - retval = ((order * LLString::compareDict(cell1->getText(), cell2->getText())) < 0); + if (cell1 && cell2) + { + sort_result = (order * LLString::compareDict(cell1->getValue().asString(), cell2->getValue().asString())); + if (sort_result != 0) + { + // we have a sort order! + break; + } + } } - return (retval ? TRUE : FALSE); + return sort_result < 0; } -protected: - S32 mSortCol; - S32 mSortAscending; + typedef std::vector<std::pair<S32, BOOL> > sort_order_t; + const sort_order_t& mSortOrders; }; - // // LLScrollListIcon // @@ -120,6 +132,14 @@ LLScrollListIcon::~LLScrollListIcon() { } +void LLScrollListIcon::setValue(LLSD value) +{ + mImageUUID = value.asUUID(); + // don't use default image specified by LLUUID::null, use no image in that case + mIcon = mImageUUID.isNull() ? NULL : LLUI::sImageProvider->getImageByID(value.asUUID()); +} + + void LLScrollListIcon::setColor(const LLColor4& color) { mColor = color; @@ -127,7 +147,10 @@ void LLScrollListIcon::setColor(const LLColor4& color) void LLScrollListIcon::drawToWidth(S32 width, const LLColor4& color, const LLColor4& highlight_color) const { - gl_draw_image(0, 0, mIcon, mColor); + if (mIcon) + { + gl_draw_image(0, 0, mIcon, mColor); + } } // @@ -158,15 +181,15 @@ LLScrollListCheck::~LLScrollListCheck() void LLScrollListCheck::drawToWidth(S32 width, const LLColor4& color, const LLColor4& highlight_color) const { mCheckBox->draw(); - } BOOL LLScrollListCheck::handleClick() { - if ( mCheckBox->getEnabled() ) + if (mCheckBox->getEnabled()) { - LLCheckBoxCtrl::onButtonPress(mCheckBox); + mCheckBox->toggle(); } + // don't change selection when clicking on embedded checkbox return TRUE; } @@ -213,7 +236,7 @@ LLScrollListText::LLScrollListText( const LLString& text, const LLFontGL* font, // initialize rounded rect image if (!mRoundedRectImage) { - mRoundedRectImage = LLUI::sImageProvider->getUIImageByID(LLUUID(LLUI::sAssetsGroup->getString("rounded_square.tga"))); + mRoundedRectImage = LLUI::sImageProvider->getImageByID(LLUUID(LLUI::sAssetsGroup->getString("rounded_square.tga"))); } } @@ -223,6 +246,12 @@ LLScrollListText::~LLScrollListText() delete mColor; } +S32 LLScrollListText::getContentWidth() const +{ + return mFont->getWidth(mText.getString()); +} + + void LLScrollListText::setColor(const LLColor4& color) { if (!mColor) @@ -314,31 +343,6 @@ LLScrollListItem::~LLScrollListItem() std::for_each(mColumns.begin(), mColumns.end(), DeletePointer()); } -BOOL LLScrollListItem::handleClick(S32 x, S32 y, MASK mask) -{ - BOOL handled = FALSE; - - S32 left = 0; - S32 right = 0; - S32 width = 0; - - std::vector<LLScrollListCell *>::iterator iter = mColumns.begin(); - std::vector<LLScrollListCell *>::iterator end = mColumns.end(); - for ( ; iter != end; ++iter) - { - width = (*iter)->getWidth(); - right += width; - if (left <= x && x < right ) - { - handled = (*iter)->handleClick(); - break; - } - - left += width; - } - return handled; -} - void LLScrollListItem::setNumColumns(S32 columns) { S32 prev_columns = mColumns.size(); @@ -375,7 +379,7 @@ LLString LLScrollListItem::getContentsCSV() S32 count = getNumColumns(); for (S32 i=0; i<count; ++i) { - ret += getColumn(i)->getText(); + ret += getColumn(i)->getValue().asString(); if (i < count-1) { ret += ", "; @@ -387,16 +391,7 @@ LLString LLScrollListItem::getContentsCSV() void LLScrollListItem::setEnabled(BOOL b) { - if (b != mEnabled) - { - std::vector<LLScrollListCell *>::iterator iter = mColumns.begin(); - std::vector<LLScrollListCell *>::iterator end = mColumns.end(); - for ( ; iter != end; ++iter) - { - (*iter)->setEnabled(b); - } - mEnabled = b; - } + mEnabled = b; } //--------------------------------------------------------------------------- @@ -424,9 +419,8 @@ LLScrollListCtrl::LLScrollListCtrl(const LLString& name, const LLRect& rect, mCanSelect(TRUE), mDisplayColumnHeaders(FALSE), mCollapseEmptyColumns(FALSE), - mIsPopup(FALSE), mMaxItemCount(INT_MAX), - //mItemCount(0), + mMaxContentWidth(0), mBackgroundVisible( TRUE ), mDrawStripes(TRUE), mBgWriteableColor( LLUI::sColorsGroup->getColor( "ScrollBgWriteableColor" ) ), @@ -443,12 +437,13 @@ LLScrollListCtrl::LLScrollListCtrl(const LLString& name, const LLRect& rect, mOnSortChangedCallback( NULL ), mHighlightedItem(-1), mBorder(NULL), - mDefaultColumn("SIMPLE"), + mDefaultColumnName("SIMPLE"), mSearchColumn(0), mNumDynamicWidthColumns(0), mTotalStaticColumnWidth(0), - mSortColumn(-1), mSortAscending(TRUE), + mSecondarySortColumn(-1), + mSecondarySortAscending(TRUE), mSorted(TRUE), mDirty(FALSE), mOriginalSelection(-1), @@ -457,7 +452,7 @@ LLScrollListCtrl::LLScrollListCtrl(const LLString& name, const LLRect& rect, mItemListRect.setOriginAndSize( mBorderThickness + LIST_BORDER_PAD, mBorderThickness + LIST_BORDER_PAD, - mRect.getWidth() - 2*( mBorderThickness + LIST_BORDER_PAD ) - SCROLLBAR_SIZE, + mRect.getWidth() - 2*( mBorderThickness + LIST_BORDER_PAD ), mRect.getHeight() - 2*( mBorderThickness + LIST_BORDER_PAD ) ); updateLineHeight(); @@ -481,7 +476,8 @@ LLScrollListCtrl::LLScrollListCtrl(const LLString& name, const LLRect& rect, mScrollbar->setFollowsTop(); mScrollbar->setFollowsBottom(); mScrollbar->setEnabled( TRUE ); - mScrollbar->setVisible( TRUE ); + // scrollbar is visible only when needed + mScrollbar->setVisible(FALSE); addChild(mScrollbar); // Border @@ -539,7 +535,8 @@ void LLScrollListCtrl::clearRows() mScrollLines = 0; mLastSelected = NULL; - updateMaxContentWidth(NULL); + calcMaxContentWidth(NULL); + updateLayout(); mDirty = FALSE; } @@ -620,38 +617,64 @@ std::vector<LLScrollListItem*> LLScrollListCtrl::getAllData() const return ret; } +// returns first matching item +LLScrollListItem* LLScrollListCtrl::getItem(const LLSD& sd) const +{ + LLString string_val = sd.asString(); + + item_list::const_iterator iter; + for(iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem* item = *iter; + // assumes string representation is good enough for comparison + if (item->getValue().asString() == string_val) + { + return item; + } + } + return NULL; +} + void LLScrollListCtrl::reshape( S32 width, S32 height, BOOL called_from_parent ) { - S32 old_height = mRect.getHeight(); LLUICtrl::reshape( width, height, called_from_parent ); - S32 heading_size = (mDisplayColumnHeaders ? mHeadingHeight : 0); + updateLayout(); +} +void LLScrollListCtrl::updateLayout() +{ + // reserve room for column headers, if needed + S32 heading_size = (mDisplayColumnHeaders ? mHeadingHeight : 0); mItemListRect.setOriginAndSize( mBorderThickness + LIST_BORDER_PAD, mBorderThickness + LIST_BORDER_PAD, - mRect.getWidth() - 2*( mBorderThickness + LIST_BORDER_PAD ) - SCROLLBAR_SIZE, + mRect.getWidth() - 2*( mBorderThickness + LIST_BORDER_PAD ), mRect.getHeight() - 2*( mBorderThickness + LIST_BORDER_PAD ) - heading_size ); + // how many lines of content in a single "page" mPageLines = mLineHeight? mItemListRect.getHeight() / mLineHeight : 0; - if(old_height < height && getScrollPos() == mScrollbar->getDocPosMax()) + BOOL scrollbar_visible = getItemCount() > mPageLines; + if (scrollbar_visible) { - setScrollPos(mScrollbar->getDocPosMax()); + // provide space on the right for scrollbar + mItemListRect.mRight = mRect.getWidth() - ( mBorderThickness + LIST_BORDER_PAD ) - SCROLLBAR_SIZE; } - mScrollbar->setVisible(mPageLines < getItemCount()); + + mScrollbar->reshape(SCROLLBAR_SIZE, mItemListRect.getHeight() + (mDisplayColumnHeaders ? mHeadingHeight : 0)); mScrollbar->setPageSize( mPageLines ); - + mScrollbar->setDocSize( getItemCount() ); + mScrollbar->setVisible(scrollbar_visible); + updateColumns(); } // Attempt to size the control to show all items. // Do not make larger than width or height. -void LLScrollListCtrl::arrange(S32 max_width, S32 max_height) +void LLScrollListCtrl::fitContents(S32 max_width, S32 max_height) { - S32 height = mLineHeight * (getItemCount() + 1); - height = llmin( height, max_height ); - + S32 height = llmin( getRequiredRect().getHeight(), max_height ); S32 width = mRect.getWidth(); reshape( width, height ); @@ -660,7 +683,10 @@ void LLScrollListCtrl::arrange(S32 max_width, S32 max_height) LLRect LLScrollListCtrl::getRequiredRect() { - S32 height = mLineHeight * (getItemCount() + 1); + S32 heading_size = (mDisplayColumnHeaders ? mHeadingHeight : 0); + S32 height = (mLineHeight * getItemCount()) + + (2 * ( mBorderThickness + LIST_BORDER_PAD )) + + heading_size; S32 width = mRect.getWidth(); return LLRect(0, height, width, 0); @@ -680,15 +706,22 @@ BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos ) break; case ADD_SORTED: - if (mSortColumn == -1) { - mSortColumn = 0; - mSortAscending = TRUE; - } - mItemList.push_back(item); - std::sort(mItemList.begin(), mItemList.end(), SortScrollListItem(mSortColumn, mSortAscending)); - break; - + // sort by column 0, in ascending order + std::vector<sort_column_t> single_sort_column; + single_sort_column.push_back(std::make_pair(0, TRUE)); + + mItemList.push_back(item); + std::stable_sort( + mItemList.begin(), + mItemList.end(), + SortScrollListItem(single_sort_column)); + + // ADD_SORTED just sorts by first column... + // this might not match user sort criteria, so flag list as being in unsorted state + setSorted(FALSE); + break; + } case ADD_BOTTOM: mItemList.push_back(item); setSorted(FALSE); @@ -702,33 +735,31 @@ BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos ) } updateLineHeightInsert(item); - mPageLines = mLineHeight ? mItemListRect.getHeight() / mLineHeight : 0; - BOOL scrollbar_visible = mPageLines < getItemCount(); - - if (scrollbar_visible != mScrollbar->getVisible()) - { - mScrollbar->setVisible(mPageLines < getItemCount()); - updateColumns(); - } - mScrollbar->setPageSize( mPageLines ); - - mScrollbar->setDocSize( getItemCount() ); + calcMaxContentWidth(item); - updateMaxContentWidth(item); + updateLayout(); } return not_too_big; } -void LLScrollListCtrl::updateMaxContentWidth(LLScrollListItem* added_item) +void LLScrollListCtrl::calcMaxContentWidth(LLScrollListItem* added_item) { const S32 HEADING_TEXT_PADDING = 30; const S32 COLUMN_TEXT_PADDING = 20; - std::map<LLString, LLScrollListColumn>::iterator column_itor; - for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor) + if (added_item == NULL) + { + mMaxContentWidth = 0; + } + + S32 item_content_width = 0; + + ordered_columns_t::iterator column_itor; + for (column_itor = mColumnsIndexed.begin(); column_itor != mColumnsIndexed.end(); ++column_itor) { - LLScrollListColumn* column = &column_itor->second; + LLScrollListColumn* column = *column_itor; + if (!column) continue; if (!added_item) { @@ -740,7 +771,7 @@ void LLScrollListCtrl::updateMaxContentWidth(LLScrollListItem* added_item) LLScrollListCell* cellp = (*iter)->getColumn(column->mIndex); if (!cellp) continue; - column->mMaxContentWidth = llmax(LLFontGL::sSansSerifSmall->getWidth(cellp->getText()) + mColumnPadding + COLUMN_TEXT_PADDING, column->mMaxContentWidth); + column->mMaxContentWidth = llmax(LLFontGL::sSansSerifSmall->getWidth(cellp->getValue().asString()) + mColumnPadding + COLUMN_TEXT_PADDING, column->mMaxContentWidth); } } else @@ -748,9 +779,13 @@ void LLScrollListCtrl::updateMaxContentWidth(LLScrollListItem* added_item) LLScrollListCell* cellp = added_item->getColumn(column->mIndex); if (!cellp) continue; - column->mMaxContentWidth = llmax(LLFontGL::sSansSerifSmall->getWidth(cellp->getText()) + mColumnPadding + COLUMN_TEXT_PADDING, column->mMaxContentWidth); + column->mMaxContentWidth = llmax(LLFontGL::sSansSerifSmall->getWidth(cellp->getValue().asString()) + mColumnPadding + COLUMN_TEXT_PADDING, column->mMaxContentWidth); } + + item_content_width += column->mMaxContentWidth; } + + mMaxContentWidth = llmax(mMaxContentWidth, item_content_width); } const S32 SCROLL_LIST_ROW_PAD = 2; @@ -789,7 +824,6 @@ void LLScrollListCtrl::updateColumns() mColumnsIndexed.resize(mColumns.size()); std::map<LLString, LLScrollListColumn>::iterator column_itor; - bool first_dynamic = true; for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor) { LLScrollListColumn *column = &column_itor->second; @@ -801,11 +835,6 @@ void LLScrollListCtrl::updateColumns() else if (column->mDynamicWidth) { new_width = (mItemListRect.getWidth() - mTotalStaticColumnWidth) / mNumDynamicWidthColumns; - if(first_dynamic) - { - first_dynamic = false; - new_width += (mScrollbar->getVisible() ? 0 : SCROLLBAR_SIZE); - } } if (new_width != column->mWidth) @@ -854,43 +883,38 @@ void LLScrollListCtrl::updateColumns() } right = llmax(left, llmin(mItemListRect.getWidth(), right)); S32 header_width = right - left; - + last_header->reshape(header_width, mHeadingHeight); - last_header->translate(left - last_header->getRect().mLeft, top - last_header->getRect().mBottom); + last_header->translate( + left - last_header->getRect().mLeft, + top - last_header->getRect().mBottom); last_header->setVisible(mDisplayColumnHeaders && header_width > 0); left = right; } } // expand last column header we encountered to full list width - if (last_header) { - S32 header_strip_width = mItemListRect.getWidth() + (mScrollbar->getVisible() ? 0 : SCROLLBAR_SIZE); - S32 new_width = llmax(0, mItemListRect.mLeft + header_strip_width - last_header->getRect().mLeft); + S32 new_width = llmax(0, mItemListRect.mRight - last_header->getRect().mLeft); last_header->reshape(new_width, last_header->getRect().getHeight()); last_header->setVisible(mDisplayColumnHeaders && new_width > 0); } - } void LLScrollListCtrl::setDisplayHeading(BOOL display) { mDisplayColumnHeaders = display; - updateColumns(); - - setHeadingHeight(mHeadingHeight); + updateLayout(); } void LLScrollListCtrl::setHeadingHeight(S32 heading_height) { mHeadingHeight = heading_height; - reshape(mRect.getWidth(), mRect.getHeight()); + updateLayout(); - // Resize - mScrollbar->reshape(SCROLLBAR_SIZE, mItemListRect.getHeight() + (mDisplayColumnHeaders ? mHeadingHeight : 0)); } void LLScrollListCtrl::setCollapseEmptyColumns(BOOL collapse) @@ -934,6 +958,8 @@ BOOL LLScrollListCtrl::selectFirstItem() BOOL LLScrollListCtrl::selectNthItem( S32 target_index ) { + if (mItemList.empty()) return FALSE; + // Deselects all other items BOOL success = FALSE; S32 index = 0; @@ -1012,7 +1038,32 @@ void LLScrollListCtrl::deleteSingleItem(S32 target_index) } delete itemp; mItemList.erase(mItemList.begin() + target_index); - updateMaxContentWidth(NULL); + calcMaxContentWidth(NULL); +} + +//FIXME: refactor item deletion +void LLScrollListCtrl::deleteItems(const LLSD& sd) +{ + item_list::iterator iter; + for (iter = mItemList.begin(); iter < mItemList.end(); ) + { + LLScrollListItem* itemp = *iter; + if (itemp->getValue().asString() == sd.asString()) + { + if (itemp == mLastSelected) + { + mLastSelected = NULL; + } + delete itemp; + mItemList.erase(iter++); + } + else + { + iter++; + } + } + + calcMaxContentWidth(NULL); } void LLScrollListCtrl::deleteSelectedItems() @@ -1032,7 +1083,7 @@ void LLScrollListCtrl::deleteSelectedItems() } } mLastSelected = NULL; - updateMaxContentWidth(NULL); + calcMaxContentWidth(NULL); } void LLScrollListCtrl::highlightNthItem(S32 target_index) @@ -1108,7 +1159,8 @@ void LLScrollListCtrl::selectPrevItem( BOOL extend_selection) if (!getFirstSelected()) { - selectFirstItem(); + // select last item + selectNthItem(getItemCount() - 1); } else { @@ -1130,7 +1182,8 @@ void LLScrollListCtrl::selectPrevItem( BOOL extend_selection) break; } - prev_item = cur_item; + // don't allow navigation to disabled elements + prev_item = cur_item->getEnabled() ? cur_item : prev_item; } } @@ -1145,32 +1198,34 @@ void LLScrollListCtrl::selectPrevItem( BOOL extend_selection) void LLScrollListCtrl::selectNextItem( BOOL extend_selection) { + LLScrollListItem* next_item = NULL; + if (!getFirstSelected()) { selectFirstItem(); } else { - item_list::iterator iter; - for (iter = mItemList.begin(); iter != mItemList.end(); iter++) + item_list::reverse_iterator iter; + for (iter = mItemList.rbegin(); iter != mItemList.rend(); iter++) { - LLScrollListItem* item = *iter; - if (item->getSelected()) + LLScrollListItem* cur_item = *iter; + + if (cur_item->getSelected()) { - if (++iter != mItemList.end()) + if (next_item) { - LLScrollListItem *next_item = *iter; - if (next_item) - { - selectItem(next_item, !extend_selection); - } - else - { - reportInvalidInput(); - } + selectItem(next_item, !extend_selection); + } + else + { + reportInvalidInput(); } break; } + + // don't allow navigation to disabled items + next_item = cur_item->getEnabled() ? cur_item : next_item; } } @@ -1213,10 +1268,29 @@ LLScrollListItem* LLScrollListCtrl::addSimpleItem(const LLString& item_text, EAd item->setEnabled(enabled); item->addColumn( item_text, gResMgr->getRes( LLFONT_SANSSERIF_SMALL ) ); addItem( item, pos ); + + // create new column on demand for "simple" items + if (mColumns.empty()) + { + LLSD new_column; + new_column["name"] = mDefaultColumnName; + new_column["label"] = ""; + new_column["dynamicwidth"] = TRUE; + addColumn(new_column); + } } return item; } +LLScrollListItem* LLScrollListCtrl::addSeparator(EAddPosition pos) +{ + LLSD item; + item["enabled"] = FALSE; + item["columns"][0]["type"] = "separator"; + item["columns"][0]["column"] = mDefaultColumnName; + + return addElement(item, pos); +} // Selects first enabled item of the given name. // Returns false if item not found. @@ -1242,7 +1316,7 @@ BOOL LLScrollListCtrl::selectSimpleItem(const LLString& label, BOOL case_sensiti { LLScrollListItem* item = *iter; // Only select enabled items with matching names - LLString item_text = item->getColumn(0)->getText(); + LLString item_text = item->getColumn(0)->getValue().asString(); if (!case_sensitive) { LLString::toLower(item_text); @@ -1288,7 +1362,7 @@ BOOL LLScrollListCtrl::selectSimpleItemByPrefix(const LLWString& target, BOOL ca LLScrollListItem* item = *iter; // Only select enabled items with matching names LLScrollListCell* cellp = item->getColumn(mSearchColumn); - BOOL select = cellp ? item->getEnabled() && ('\0' == cellp->getText()[0]) : FALSE; + BOOL select = cellp ? item->getEnabled() && ('\0' == cellp->getValue().asString()[0]) : FALSE; if (select) { selectItem(item); @@ -1315,7 +1389,7 @@ BOOL LLScrollListCtrl::selectSimpleItemByPrefix(const LLWString& target, BOOL ca { continue; } - LLWString item_label = utf8str_to_wstring(cellp->getText()); + LLWString item_label = utf8str_to_wstring(cellp->getValue().asString()); if (!case_sensitive) { LLWString::toLower(item_label); @@ -1346,14 +1420,14 @@ BOOL LLScrollListCtrl::selectSimpleItemByPrefix(const LLWString& target, BOOL ca return found; } -const LLString& LLScrollListCtrl::getSimpleSelectedItem(S32 column) const +const LLString LLScrollListCtrl::getSimpleSelectedItem(S32 column) const { LLScrollListItem* item; item = getFirstSelected(); if (item) { - return item->getColumn(column)->getText(); + return item->getColumn(column)->getValue().asString(); } return LLString::null; @@ -1384,6 +1458,16 @@ LLScrollListItem* LLScrollListCtrl::addSimpleItem(const LLString& item_text, LLS item->setEnabled(enabled); item->addColumn(item_text, gResMgr->getRes(LLFONT_SANSSERIF_SMALL), column_width); addItem( item, pos ); + + // create new column on demand + if (mColumns.empty()) + { + LLSD new_column; + new_column["name"] = "default_column"; + new_column["label"] = ""; + new_column["dynamicwidth"] = TRUE; + addColumn(new_column); + } } return item; } @@ -1481,9 +1565,7 @@ void LLScrollListCtrl::drawItems() LLGLSUIDefault gls_ui; { - LLRect clip_rect = mItemListRect; - if(!mScrollbar->getVisible()) clip_rect.mRight += SCROLLBAR_SIZE; - LLLocalClipRect clip(clip_rect); + LLLocalClipRect clip(mItemListRect); S32 cur_x = x; S32 cur_y = y; @@ -1491,7 +1573,6 @@ void LLScrollListCtrl::drawItems() mDrewSelected = FALSE; S32 line = 0; - LLColor4 color; S32 max_columns = 0; item_list::iterator iter; @@ -1502,7 +1583,7 @@ void LLScrollListCtrl::drawItems() item_rect.setOriginAndSize( cur_x, cur_y, - mScrollbar->getVisible() ? mItemListRect.getWidth() : mItemListRect.getWidth() + SCROLLBAR_SIZE, + mScrollbar->getVisible() ? mItemListRect.getWidth() : mItemListRect.getWidth() + mScrollbar->getRect().getWidth(), mLineHeight ); //llinfos << item_rect.getWidth() << llendl; @@ -1514,37 +1595,43 @@ void LLScrollListCtrl::drawItems() max_columns = llmax(max_columns, item->getNumColumns()); + LLColor4 fg_color; LLRect bg_rect = item_rect; // pad background rectangle to separate it from contents bg_rect.stretch(LIST_BORDER_PAD, 0); + LLColor4 bg_color(0.f, 0.f, 0.f, 0.f); if( mScrollLines <= line && line < mScrollLines + num_page_lines ) { if( item->getSelected() && mCanSelect) { // Draw background of selected item - LLGLSNoTexture no_texture; - glColor4fv(mBgSelectedColor.mV); - gl_rect_2d( bg_rect ); - - color = mFgSelectedColor; + bg_color = mBgSelectedColor; + fg_color = (item->getEnabled() ? mFgSelectedColor : mFgDisabledColor); } else if (mHighlightedItem == line && mCanSelect) { - LLGLSNoTexture no_texture; - glColor4fv(mHighlightedColor.mV); - gl_rect_2d( bg_rect ); - color = (item->getEnabled() ? mFgUnselectedColor : mFgDisabledColor); + bg_color = mHighlightedColor; + fg_color = (item->getEnabled() ? mFgUnselectedColor : mFgDisabledColor); } else { - color = (item->getEnabled() ? mFgUnselectedColor : mFgDisabledColor); if (mDrawStripes && (line%2 == 0) && (max_columns > 1)) { - LLGLSNoTexture no_texture; - glColor4fv(mBgStripeColor.mV); - gl_rect_2d( bg_rect ); + bg_color = mBgStripeColor; } + fg_color = (item->getEnabled() ? mFgUnselectedColor : mFgDisabledColor); + } + + if (!item->getEnabled()) + { + bg_color = mBgReadOnlyColor; + } + // draw background rect + { + LLGLSNoTexture no_texture; + glColor4fv(bg_color.mV); + gl_rect_2d( bg_rect ); } S32 line_x = cur_x; @@ -1553,7 +1640,6 @@ void LLScrollListCtrl::drawItems() S32 cur_col = 0; S32 dynamic_width = 0; S32 dynamic_remainder = 0; - bool first_dynamic = true; if(mNumDynamicWidthColumns > 0) { dynamic_width = (mItemListRect.getWidth() - mTotalStaticColumnWidth) / mNumDynamicWidthColumns; @@ -1563,15 +1649,9 @@ void LLScrollListCtrl::drawItems() for (LLScrollListCell* cell = item->getColumn(0); cur_col < num_cols; cell = item->getColumn(++cur_col)) { S32 cell_width = cell->getWidth(); - if(mColumnsIndexed.size() > (U32)cur_col && mColumnsIndexed[cur_col] && mColumnsIndexed[cur_col]->mDynamicWidth) { cell_width = dynamic_width + (--dynamic_remainder ? 1 : 0); - if(first_dynamic) - { - cell_width += mScrollbar->getVisible() ? 0 : SCROLLBAR_SIZE; - first_dynamic = false; - } cell->setWidth(cell_width); } // Two ways a cell could be hidden @@ -1585,7 +1665,7 @@ void LLScrollListCtrl::drawItems() F32 type_ahead_timeout = LLUI::sConfigGroup->getF32("TypeAheadTimeout"); highlight_color.mV[VALPHA] = clamp_rescale(mSearchTimer.getElapsedTimeF32(), type_ahead_timeout * 0.7f, type_ahead_timeout, 0.4f, 0.f); - cell->drawToWidth( space_left, color, highlight_color ); + cell->drawToWidth( space_left, fg_color, highlight_color ); LLUI::popMatrix(); cur_x += cell_width + mColumnPadding; @@ -1605,6 +1685,12 @@ void LLScrollListCtrl::draw() { if( getVisible() ) { + // if user specifies sort, make sure it is maintained + if (needsSorting() && !isSorted()) + { + sortItems(); + } + if (mNeedsScroll) { scrollToShowSelected(); @@ -1645,6 +1731,54 @@ BOOL LLScrollListCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks) return handled; } +BOOL LLScrollListCtrl::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen) +{ + S32 column_index = getColumnIndexFromOffset(x); + LLScrollListColumn* columnp = getColumn(column_index); + + if (columnp == NULL) return FALSE; + + BOOL handled = FALSE; + // show tooltip for full name of hovered item if it has been truncated + LLScrollListItem* hit_item = hitItem(x, y); + if (hit_item) + { + LLScrollListCell* hit_cell = hit_item->getColumn(column_index); + if (!hit_cell) return FALSE; + S32 cell_required_width = hit_cell->getContentWidth(); + if (hit_cell + && hit_cell->isText() + && cell_required_width > columnp->mWidth) + { + + S32 rect_left = getColumnOffsetFromIndex(column_index) + mItemListRect.mLeft; + S32 rect_bottom = getRowOffsetFromIndex(getItemIndex(hit_item)); + LLRect cell_rect; + cell_rect.setOriginAndSize(rect_left, rect_bottom, rect_left + columnp->mWidth, mLineHeight); + // Convert rect local to screen coordinates + localPointToScreen( + cell_rect.mLeft, cell_rect.mBottom, + &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); + localPointToScreen( + cell_rect.mRight, cell_rect.mTop, + &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) ); + + msg = hit_cell->getValue().asString(); + handled = TRUE; + } + } + + // otherwise, look for a tooltip associated with this column + LLColumnHeader* headerp = columnp->mHeader; + if (headerp && !handled) + { + headerp->handleToolTip(x, y, msg, sticky_rect_screen); + handled = !msg.empty(); + } + + return handled; +} + BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) { if (!mCanSelect) return FALSE; @@ -1652,6 +1786,7 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) BOOL selection_changed = FALSE; LLScrollListItem* hit_item = hitItem(x, y); + if( hit_item ) { if( mAllowMultipleSelection ) @@ -1686,6 +1821,11 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) { selectItem(item, FALSE); selecting = !selecting; + if (hit_item == lastSelected) + { + // stop selecting now, since we just clicked on our last selected item + selecting = FALSE; + } } if (selecting) { @@ -1726,9 +1866,6 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) selectItem(hit_item); } - hit_item->handleClick(x - mBorderThickness - LIST_BORDER_PAD, - 1, mask); - selection_changed = mSelectionChanged; if (mCommitOnSelectionChange) { @@ -1750,19 +1887,17 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) BOOL LLScrollListCtrl::handleMouseDown(S32 x, S32 y, MASK mask) { - BOOL handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL; + BOOL handled = childrenHandleMouseDown(x, y, mask) != NULL; if( !handled ) { // set keyboard focus first, in case click action wants to move focus elsewhere setFocus(TRUE); - // clear selection changed flag so because user is starting a selection operation + // clear selection changed flag because user is starting a selection operation mSelectionChanged = FALSE; - gFocusMgr.setMouseCapture(this); - selectItemAt(x, y, mask); - mNeedsScroll = TRUE; + handleClick(x, y, mask); } return TRUE; @@ -1798,19 +1933,74 @@ BOOL LLScrollListCtrl::handleDoubleClick(S32 x, S32 y, MASK mask) //BOOL handled = FALSE; if(getVisible()) { - // Offer the click to the children, even if we aren't enabled - // so the scroll bars will work. - if (NULL == LLView::childrenHandleDoubleClick(x, y, mask)) + BOOL handled = handleClick(x, y, mask); + + if (!handled) { - if( mCanSelect && mOnDoubleClickCallback ) + // Offer the click to the children, even if we aren't enabled + // so the scroll bars will work. + if (NULL == LLView::childrenHandleDoubleClick(x, y, mask)) { - mOnDoubleClickCallback( mCallbackUserData ); + if( mCanSelect && mOnDoubleClickCallback ) + { + mOnDoubleClickCallback( mCallbackUserData ); + } } } } return TRUE; } +BOOL LLScrollListCtrl::handleClick(S32 x, S32 y, MASK mask) +{ + // which row was clicked on? + LLScrollListItem* hit_item = hitItem(x, y); + if (!hit_item) return FALSE; + + // get appropriate cell from that row + S32 column_index = getColumnIndexFromOffset(x); + LLScrollListCell* hit_cell = hit_item->getColumn(column_index); + if (!hit_cell) return FALSE; + + // select item (thus deselecting any currently selected item) + // only if item is not already selected + if (!hit_item->getSelected()) + { + selectItemAt(x, y, mask); + gFocusMgr.setMouseCapture(this); + mNeedsScroll = TRUE; + } + + if (hit_cell->handleClick()) + { + // propagate value of this cell to other selected items + // and commit the respective widgets + LLSD item_value = hit_cell->getValue(); + for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem* item = *iter; + if (item->getSelected()) + { + LLScrollListCell* cellp = item->getColumn(column_index); + cellp->setValue(item_value); + cellp->onCommit(); + } + } + //FIXME: find a better way to signal cell changes + onCommit(); + return TRUE; + } + else + { + // treat this as a normal single item selection + selectItemAt(x, y, mask); + gFocusMgr.setMouseCapture(this); + mNeedsScroll = TRUE; + // do not stop click processing (click callback, etc) + return FALSE; + } +} + LLScrollListItem* LLScrollListCtrl::hitItem( S32 x, S32 y ) { // Excludes disabled items. @@ -1847,6 +2037,59 @@ LLScrollListItem* LLScrollListCtrl::hitItem( S32 x, S32 y ) return hit_item; } +S32 LLScrollListCtrl::getColumnIndexFromOffset(S32 x) +{ + // which column did we hit? + S32 left = 0; + S32 right = 0; + S32 width = 0; + S32 column_index = 0; + + ordered_columns_t::const_iterator iter = mColumnsIndexed.begin(); + ordered_columns_t::const_iterator end = mColumnsIndexed.end(); + for ( ; iter != end; ++iter) + { + width = (*iter)->mWidth + mColumnPadding; + right += width; + if (left <= x && x < right ) + { + break; + } + + // set left for next column as right of current column + left = right; + column_index++; + } + + return llclamp(column_index, 0, getNumColumns() - 1); +} + + +S32 LLScrollListCtrl::getColumnOffsetFromIndex(S32 index) +{ + S32 column_offset = 0; + ordered_columns_t::const_iterator iter = mColumnsIndexed.begin(); + ordered_columns_t::const_iterator end = mColumnsIndexed.end(); + for ( ; iter != end; ++iter) + { + if (index-- <= 0) + { + return column_offset; + } + column_offset += (*iter)->mWidth + mColumnPadding; + } + + // when running off the end, return the rightmost pixel + return mItemListRect.mRight; +} + +S32 LLScrollListCtrl::getRowOffsetFromIndex(S32 index) +{ + S32 row_bottom = ((mItemListRect.mTop - (index - mScrollLines)) * mLineHeight) + - mLineHeight; + return row_bottom; +} + BOOL LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask) { @@ -1860,7 +2103,8 @@ BOOL LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask) mNeedsScroll = TRUE; } } - else if (mCanSelect) + else + if (mCanSelect) { LLScrollListItem* item = hitItem(x, y); if (item) @@ -1875,13 +2119,6 @@ BOOL LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask) handled = LLUICtrl::handleHover( x, y, mask ); - //if( !handled ) - //{ - // // Opaque - // getWindow()->setCursor(UI_CURSOR_ARROW); - // lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl; - // handled = TRUE; - //} return handled; } @@ -2082,7 +2319,7 @@ BOOL LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_ if (cellp) { // Only select enabled items with matching first characters - LLWString item_label = utf8str_to_wstring(cellp->getText()); + LLWString item_label = utf8str_to_wstring(cellp->getValue().asString()); if (item->getEnabled() && LLStringOps::toLower(item_label[0]) == uni_char) { selectItem(item); @@ -2176,7 +2413,7 @@ void LLScrollListCtrl::deselectItem(LLScrollListItem* itemp) LLScrollListCell* cellp = itemp->getColumn(mSearchColumn); if (cellp) { - cellp->highlightText(0, 0); + cellp->highlightText(0, 0); } mSelectionChanged = TRUE; } @@ -2202,38 +2439,52 @@ BOOL LLScrollListCtrl::isSorted() return mSorted; } -// Called by scrollbar -//static -void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar, void* userdata ) +struct SameSortColumn { - LLScrollListCtrl* self = (LLScrollListCtrl*) userdata; - self->mScrollLines = new_pos; -} + SameSortColumn(S32 column) : mColumn(column) {} + S32 mColumn; + bool operator()(std::pair<S32, BOOL> sort_column) { return sort_column.first == mColumn; } +}; -// First column is column 0 -void LLScrollListCtrl::sortByColumn(U32 column, BOOL ascending) +BOOL LLScrollListCtrl::setSort(S32 column, BOOL ascending) { - if (!mSorted || mSortColumn != column) + sort_column_t new_sort_column(column, ascending); + + if (mSortColumns.empty()) { - mSortColumn = column; - std::sort(mItemList.begin(), mItemList.end(), SortScrollListItem(mSortColumn, mSortAscending)); - setSorted(TRUE); + mSortColumns.push_back(new_sort_column); + return TRUE; } + else + { + // grab current sort column + sort_column_t cur_sort_column = mSortColumns.back(); + + // remove any existing sort criterion referencing this column + // and add the new one + remove_if(mSortColumns.begin(), mSortColumns.end(), SameSortColumn(column)); + mSortColumns.push_back(new_sort_column); - // just reverse the list if changing sort order - if(mSortAscending != ascending) - { - std::reverse(mItemList.begin(), mItemList.end()); - mSortAscending = ascending; + // did the sort criteria change? + return (cur_sort_column != new_sort_column); } } +// Called by scrollbar +//static +void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar, void* userdata ) +{ + LLScrollListCtrl* self = (LLScrollListCtrl*) userdata; + self->mScrollLines = new_pos; +} + + void LLScrollListCtrl::sortByColumn(LLString name, BOOL ascending) { if (name.empty()) { - sortByColumn(mSortColumn, mSortAscending); + sortItems(); return; } @@ -2244,6 +2495,26 @@ void LLScrollListCtrl::sortByColumn(LLString name, BOOL ascending) } } +// First column is column 0 +void LLScrollListCtrl::sortByColumn(U32 column, BOOL ascending) +{ + if (setSort(column, ascending)) + { + sortItems(); + } +} + +void LLScrollListCtrl::sortItems() +{ + // do stable sort to preserve any previous sorts + std::stable_sort( + mItemList.begin(), + mItemList.end(), + SortScrollListItem(mSortColumns)); + + setSorted(TRUE); +} + S32 LLScrollListCtrl::getScrollPos() { return mScrollbar->getDocPos(); @@ -2465,7 +2736,7 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac LLSD columns; S32 index = 0; LLXMLNodePtr child; - S32 total_static = 0, num_dynamic = 0; + S32 total_static = 0; for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) { if (child->hasName("column")) @@ -2491,8 +2762,10 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac S32 columnwidth = -1; child->getAttributeS32("width", columnwidth); + LLString tooltip; + child->getAttributeString("tool_tip", tooltip); + if(!columndynamicwidth) total_static += columnwidth; - else ++num_dynamic; F32 columnrelwidth = 0.f; child->getAttributeF32("relwidth", columnrelwidth); @@ -2509,10 +2782,11 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac columns[index]["relwidth"] = columnrelwidth; columns[index]["dynamicwidth"] = columndynamicwidth; columns[index]["halign"] = (S32)h_align; + columns[index]["tool_tip"] = tooltip; + index++; } } - scroll_list->setNumDynamicColumns(num_dynamic); scroll_list->setTotalStaticColumnWidth(total_static); scroll_list->setColumnHeadings(columns); @@ -2665,7 +2939,7 @@ void LLScrollListCtrl::addColumn(const LLSD& column, EAddPosition pos) LLString name = column["name"].asString(); if (mColumns.empty()) { - mDefaultColumn = 0; + mDefaultColumnName = name; } // if no column name provided, just use ordinal as name if (name.empty()) @@ -2691,6 +2965,7 @@ void LLScrollListCtrl::addColumn(const LLSD& column, EAddPosition pos) } else if(new_column->mDynamicWidth) { + mNumDynamicWidthColumns++; new_column->mWidth = (mItemListRect.getWidth() - mTotalStaticColumnWidth) / mNumDynamicWidthColumns; } S32 top = mItemListRect.mTop; @@ -2724,17 +2999,16 @@ void LLScrollListCtrl::addColumn(const LLSD& column, EAddPosition pos) new_column->mHeader->setLabel(new_column->mLabel); //new_column->mHeader->setLabel(new_column->mLabel); } + + new_column->mHeader->setToolTip(column["tool_tip"].asString()); + //RN: although it might be useful to change sort order with the keyboard, // mixing tab stops on child items along with the parent item is not supported yet new_column->mHeader->setTabStop(FALSE); addChild(new_column->mHeader); new_column->mHeader->setVisible(mDisplayColumnHeaders); - - // Move scroll to front - removeChild(mScrollbar); - addChild(mScrollbar); - + sendChildToFront(mScrollbar); } } updateColumns(); @@ -2753,18 +3027,18 @@ void LLScrollListCtrl::onClickColumn(void *userdata) LLScrollListColumn* column = parent->mColumnsIndexed[info->mIndex]; bool ascending = column->mSortAscending; - if (column->mSortingColumn != column->mName) + if (column->mSortingColumn != column->mName + && parent->mColumns.find(column->mSortingColumn) != parent->mColumns.end()) { - if (parent->mColumns.find(column->mSortingColumn) != parent->mColumns.end()) - { - LLScrollListColumn& info_redir = parent->mColumns[column->mSortingColumn]; - column_index = info_redir.mIndex; - } + LLScrollListColumn& info_redir = parent->mColumns[column->mSortingColumn]; + column_index = info_redir.mIndex; } - if (column_index == parent->mSortColumn) + // if this column is the primary sort key, reverse the direction + sort_column_t cur_sort_column; + if (!parent->mSortColumns.empty() && parent->mSortColumns.back().first == column_index) { - ascending = !parent->mSortAscending; + ascending = !parent->mSortColumns.back().second; } parent->sortByColumn(column_index, ascending); @@ -2777,12 +3051,17 @@ void LLScrollListCtrl::onClickColumn(void *userdata) std::string LLScrollListCtrl::getSortColumnName() { - LLScrollListColumn* column = mSortColumn >= 0 ? mColumnsIndexed[mSortColumn] : NULL; + LLScrollListColumn* column = mSortColumns.empty() ? NULL : mColumnsIndexed[mSortColumns.back().first]; if (column) return column->mName; else return ""; } +BOOL LLScrollListCtrl::needsSorting() +{ + return !mSortColumns.empty(); +} + void LLScrollListCtrl::clearColumns() { std::map<LLString, LLScrollListColumn>::iterator itor; @@ -2796,6 +3075,7 @@ void LLScrollListCtrl::clearColumns() } } mColumns.clear(); + mSortColumns.clear(); } void LLScrollListCtrl::setColumnLabel(const LLString& column, const LLString& label) @@ -2851,11 +3131,6 @@ LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& value, EAddPosition p { LLString column = (*itor)["column"].asString(); - if (mColumns.size() == 0) - { - mDefaultColumn = 0; - } - LLScrollListColumn* columnp = NULL; // empty columns strings index by ordinal @@ -2895,6 +3170,7 @@ LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& value, EAddPosition p LLString type = (*itor)["type"].asString(); BOOL has_color = (*itor).has("color"); LLColor4 color = ((*itor)["color"]); + BOOL enabled = !(*itor).has("enabled") || (*itor)["enabled"].asBoolean() == true; const LLFontGL *font = gResMgr->getRes(fontname); if (!font) @@ -2906,7 +3182,8 @@ LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& value, EAddPosition p if (type == "icon") { LLUUID image_id = value.asUUID(); - LLImageGL* icon = LLUI::sImageProvider->getUIImageByID(image_id); + // don't use special image with UUID::null, just don't draw an image + LLImageGL* icon = image_id.isNull() ? NULL : LLUI::sImageProvider->getImageByID(image_id); LLScrollListIcon* cell = new LLScrollListIcon(icon, width, image_id); if (has_color) { @@ -2916,8 +3193,10 @@ LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& value, EAddPosition p } else if (type == "checkbox") { - LLCheckBoxCtrl* ctrl = new LLCheckBoxCtrl(value.asString(), - LLRect(0, 0, width, width), "label"); + LLCheckBoxCtrl* ctrl = new LLCheckBoxCtrl("check", + LLRect(0, width, width, 0), " "); + ctrl->setEnabled(enabled); + ctrl->setValue(value); LLScrollListCheck* cell = new LLScrollListCheck(ctrl,width); if (has_color) { @@ -3070,18 +3349,12 @@ void LLScrollListCtrl::onFocusReceived() { // forget latent selection changes when getting focus mSelectionChanged = FALSE; + LLUICtrl::onFocusReceived(); } //virtual void LLScrollListCtrl::onFocusLost() { - if (mIsPopup) - { - if (getParent()) - { - getParent()->onFocusLost(); - } - } if (hasMouseCapture()) { gFocusMgr.setMouseCapture(NULL); @@ -3133,11 +3406,11 @@ void LLColumnHeader::draw() { if( getVisible() ) { - mDrawArrow = !mColumn->mLabel.empty() && mColumn->mParentCtrl->isSorted() && mColumn->mParentCtrl->getSortColumnName() == mColumn->mSortingColumn; + BOOL draw_arrow = !mColumn->mLabel.empty() && mColumn->mParentCtrl->isSorted() && mColumn->mParentCtrl->getSortColumnName() == mColumn->mSortingColumn; BOOL is_ascending = mColumn->mParentCtrl->getSortAscending(); - mArrowImage = is_ascending ? LLUI::sImageProvider->getUIImageByID(LLUUID(LLUI::sAssetsGroup->getString("up_arrow.tga"))) - : LLUI::sImageProvider->getUIImageByID(LLUUID(LLUI::sAssetsGroup->getString("down_arrow.tga"))); + mButton->setImageOverlay(is_ascending ? "up_arrow.tga" : "down_arrow.tga", LLFontGL::RIGHT, draw_arrow ? LLColor4::white : LLColor4::transparent); + mArrowImage = mButton->getImageOverlay()->getImage(); //BOOL clip = mRect.mRight > mColumn->mParentCtrl->getItemListRect().getWidth(); //LLGLEnable scissor_test(clip ? GL_SCISSOR_TEST : GL_FALSE); @@ -3237,11 +3510,11 @@ void LLColumnHeader::showList() { if (mColumn->mParentCtrl->getSortAscending()) { - low_item_text = cell->getText(); + low_item_text = cell->getValue().asString(); } else { - high_item_text = cell->getText(); + high_item_text = cell->getValue().asString(); } } } @@ -3254,11 +3527,11 @@ void LLColumnHeader::showList() { if (mColumn->mParentCtrl->getSortAscending()) { - high_item_text = cell->getText(); + high_item_text = cell->getValue().asString(); } else { - low_item_text = cell->getText(); + low_item_text = cell->getValue().asString(); } } } diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index a98a411efa..001e10184b 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -59,14 +59,16 @@ public: virtual ~LLScrollListCell() {}; virtual void drawToWidth(S32 width, const LLColor4& color, const LLColor4& highlight_color) const = 0; // truncate to given width, if possible virtual S32 getWidth() const = 0; + virtual S32 getContentWidth() const { return 0; } virtual S32 getHeight() const = 0; - virtual const LLString& getText() const { return LLString::null; } - virtual const LLString& getTextLower() const { return LLString::null; } + virtual const LLSD getValue() const { return LLString::null; } + virtual void setValue(LLSD value) { } virtual BOOL getVisible() const { return TRUE; } virtual void setWidth(S32 width) = 0; virtual void highlightText(S32 offset, S32 num_chars) {} virtual BOOL isText() = 0; virtual void setColor(const LLColor4&) = 0; + virtual void onCommit() {}; virtual BOOL handleClick() { return FALSE; } virtual void setEnabled(BOOL enable) { } @@ -96,20 +98,24 @@ public: virtual void drawToWidth(S32 width, const LLColor4& color, const LLColor4& highlight_color) const; virtual S32 getWidth() const { return mWidth; } + virtual S32 getContentWidth() const; virtual void setWidth(S32 width) { mWidth = width; } virtual S32 getHeight() const { return llround(mFont->getLineHeight()); } - virtual const LLString& getText() const { return mText.getString(); } + virtual const LLSD getValue() const { return LLSD(mText.getString()); } virtual BOOL getVisible() const { return mVisible; } virtual void highlightText(S32 offset, S32 num_chars) {mHighlightOffset = offset; mHighlightCount = num_chars;} - void setText(const LLStringExplicit& text); + virtual void setColor(const LLColor4&); virtual BOOL isText() { return TRUE; } + void setText(const LLStringExplicit& text); + void setFontStyle(const U8 font_style) { mFontStyle = font_style; } + private: LLUIString mText; const LLFontGL* mFont; LLColor4* mColor; - const U8 mFontStyle; + U8 mFontStyle; LLFontGL::HAlign mFontAlignment; S32 mWidth; BOOL mVisible; @@ -128,16 +134,16 @@ public: /*virtual*/ ~LLScrollListIcon(); virtual void drawToWidth(S32 width, const LLColor4& color, const LLColor4& highlight_color) const; virtual S32 getWidth() const { return mWidth; } - virtual S32 getHeight() const { return mIcon->getHeight(); } - virtual const LLString& getText() const { return mImageUUID; } - virtual const LLString& getTextLower() const { return mImageUUID; } + virtual S32 getHeight() const { return mIcon ? mIcon->getHeight() : 0; } + virtual const LLSD getValue() const { return LLSD(mImageUUID); } virtual void setWidth(S32 width) { mWidth = width; } virtual void setColor(const LLColor4&); virtual BOOL isText() { return FALSE; } + virtual void setValue(LLSD value); private: LLPointer<LLImageGL> mIcon; - LLString mImageUUID; + LLUUID mImageUUID; S32 mWidth; LLColor4 mColor; }; @@ -151,9 +157,12 @@ public: virtual S32 getWidth() const { return mWidth; } virtual S32 getHeight() const { return 0; } virtual void setWidth(S32 width) { mWidth = width; } + virtual const LLSD getValue() const { return mCheckBox->getValue(); } + virtual void setValue(LLSD value) { mCheckBox->setValue(value); } + virtual void onCommit() { mCheckBox->onCommit(); } virtual BOOL handleClick(); - virtual void setEnabled(BOOL enable) { if (mCheckBox) mCheckBox->setEnabled(enable); } + virtual void setEnabled(BOOL enable) { mCheckBox->setEnabled(enable); } virtual void setColor(const LLColor4& color) {}; LLCheckBoxCtrl* getCheckBox() { return mCheckBox; } @@ -266,6 +275,7 @@ public: /*virtual*/ void draw(); /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask); + /*virtual*/ void showList(); /*virtual*/ LLView* findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding); /*virtual*/ void userSetShape(const LLRect& new_rect); @@ -333,8 +343,6 @@ public: LLScrollListCell *getColumn(const S32 i) const { if (0 <= i && i < (S32)mColumns.size()) { return mColumns[i]; } return NULL; } - virtual BOOL handleClick(S32 x, S32 y, MASK mask); - LLString getContentsCSV(); private: @@ -370,9 +378,8 @@ public: void deleteAllItems() { clearRows(); } // Sets an array of column descriptors - void setColumnHeadings(LLSD headings); - // Numerical based sort by column function (used by LLComboBox) - void sortByColumn(U32 column, BOOL ascending); + void setColumnHeadings(LLSD headings); + void sortByColumn(U32 column, BOOL ascending); // LLCtrlListInterface functions virtual S32 getItemCount() const; @@ -421,18 +428,20 @@ public: BOOL isSorted(); virtual BOOL isSelected(LLSD value); - + + BOOL handleClick(S32 x, S32 y, MASK mask); BOOL selectFirstItem(); BOOL selectNthItem( S32 index ); BOOL selectItemAt(S32 x, S32 y, MASK mask); - void deleteSingleItem( S32 index ) ; + void deleteSingleItem( S32 index ); + void deleteItems(const LLSD& sd); void deleteSelectedItems(); void deselectAllItems(BOOL no_commit_on_change = FALSE); // by default, go ahead and commit on selection change void highlightNthItem( S32 index ); void setDoubleClickCallback( void (*cb)(void*) ) { mOnDoubleClickCallback = cb; } - void setMaxiumumSelectCallback( void (*cb)(void*) ) { mOnMaximumSelectCallback = cb; } + void setMaximumSelectCallback( void (*cb)(void*) ) { mOnMaximumSelectCallback = cb; } void setSortChangedCallback( void (*cb)(void*) ) { mOnSortChangedCallback = cb; } void swapWithNext(S32 index); @@ -449,11 +458,12 @@ public: LLScrollListItem* addSimpleItem( const LLString& item_text, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE ); // Add an item with an associated LLSD LLScrollListItem* addSimpleItem(const LLString& item_text, LLSD sd, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE, S32 column_width = 0 ); + LLScrollListItem* addSeparator(EAddPosition pos); BOOL selectSimpleItem( const LLString& item, BOOL case_sensitive = TRUE ); // FALSE if item not found BOOL selectSimpleItemByPrefix(const LLString& target, BOOL case_sensitive); BOOL selectSimpleItemByPrefix(const LLWString& target, BOOL case_sensitive); - const LLString& getSimpleSelectedItem(S32 column = 0) const; + const LLString getSimpleSelectedItem(S32 column = 0) const; LLSD getSimpleSelectedValue(); // DEPRECATED: Use LLSD versions of addSimpleItem() and getSimpleSelectedValue(). @@ -472,6 +482,8 @@ public: LLScrollListItem* getFirstData() const; LLScrollListItem* getLastData() const; std::vector<LLScrollListItem*> getAllData() const; + + LLScrollListItem* getItem(const LLSD& sd) const; void setAllowMultipleSelection(BOOL mult ) { mAllowMultipleSelection = mult; } @@ -501,28 +513,34 @@ public: S32 getSearchColumn() { return mSearchColumn; } void setSearchColumn(S32 column) { mSearchColumn = column; } + S32 getColumnIndexFromOffset(S32 x); + S32 getColumnOffsetFromIndex(S32 index); + S32 getRowOffsetFromIndex(S32 index); void clearSearchString() { mSearchString.clear(); } // Overridden from LLView - virtual void draw(); - virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); - virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); - virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask); - virtual BOOL handleHover(S32 x, S32 y, MASK mask); - virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent); - virtual BOOL handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent); - virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); - virtual void setEnabled(BOOL enabled); - virtual void setFocus( BOOL b ); - virtual void onFocusReceived(); - virtual void onFocusLost(); + /*virtual*/ void draw(); + /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent); + /*virtual*/ BOOL handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent); + /*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); + /*virtual*/ BOOL handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect); + /*virtual*/ void setEnabled(BOOL enabled); + /*virtual*/ void setFocus( BOOL b ); + /*virtual*/ void onFocusReceived(); + /*virtual*/ void onFocusLost(); + /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); virtual BOOL isDirty() const; virtual void resetDirty(); // Clear dirty state - virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); - virtual void arrange(S32 max_width, S32 max_height); + virtual void updateLayout(); + virtual void fitContents(S32 max_width, S32 max_height); + virtual LLRect getRequiredRect(); static BOOL rowPreceeds(LLScrollListItem *new_row, LLScrollListItem *test_row); @@ -534,12 +552,12 @@ public: static void onClickColumn(void *userdata); void updateColumns(); - void updateMaxContentWidth(LLScrollListItem* changed_item); + void calcMaxContentWidth(LLScrollListItem* changed_item); + S32 getMaxContentWidth() { return mMaxContentWidth; } void setDisplayHeading(BOOL display); void setHeadingHeight(S32 heading_height); void setCollapseEmptyColumns(BOOL collapse); - void setIsPopup(BOOL is_popup) { mIsPopup = is_popup; } LLScrollListItem* hitItem(S32 x,S32 y); virtual void scrollToShowSelected(); @@ -564,9 +582,11 @@ public: void setTotalStaticColumnWidth(int width) { mTotalStaticColumnWidth = width; } std::string getSortColumnName(); - BOOL getSortAscending() { return mSortAscending; } + BOOL getSortAscending() { return mSortColumns.empty() ? TRUE : mSortColumns.back().second; } + BOOL needsSorting(); S32 selectMultiple( LLDynamicArray<LLUUID> ids ); + void sortItems(); protected: // "Full" interface: use this when you're creating a list that has one or more of the following: @@ -596,6 +616,7 @@ protected: void deselectItem(LLScrollListItem* itemp); void commitIfChanged(); void setSorted(BOOL sorted); + BOOL setSort(S32 column, BOOL ascending); protected: S32 mCurIndex; // For get[First/Next]Data @@ -616,7 +637,6 @@ protected: BOOL mCanSelect; BOOL mDisplayColumnHeaders; BOOL mCollapseEmptyColumns; - BOOL mIsPopup; typedef std::deque<LLScrollListItem *> item_list; item_list mItemList; @@ -626,7 +646,7 @@ protected: S32 mMaxItemCount; LLRect mItemListRect; - + S32 mMaxContentWidth; S32 mColumnPadding; BOOL mBackgroundVisible; @@ -652,22 +672,29 @@ protected: LLWString mSearchString; LLFrameTimer mSearchTimer; - LLString mDefaultColumn; + LLString mDefaultColumnName; S32 mSearchColumn; S32 mNumDynamicWidthColumns; S32 mTotalStaticColumnWidth; S32 mSortColumn; + S32 mSecondarySortColumn; + BOOL mSecondarySortAscending; BOOL mSortAscending; BOOL mSorted; - + std::map<LLString, LLScrollListColumn> mColumns; - std::vector<LLScrollListColumn*> mColumnsIndexed; BOOL mDirty; S32 mOriginalSelection; + typedef std::vector<LLScrollListColumn*> ordered_columns_t; + ordered_columns_t mColumnsIndexed; + + typedef std::pair<S32, BOOL> sort_column_t; + std::vector<sort_column_t> mSortColumns; + public: // HACK: Did we draw one selected item this frame? BOOL mDrewSelected; diff --git a/indra/llui/llslider.cpp b/indra/llui/llslider.cpp index 29626c25d6..bd91d562aa 100644 --- a/indra/llui/llslider.cpp +++ b/indra/llui/llslider.cpp @@ -41,9 +41,6 @@ #include "llcontrol.h" #include "llimagegl.h" -const S32 THUMB_WIDTH = 8; -const S32 TRACK_HEIGHT = 6; - LLSlider::LLSlider( const LLString& name, const LLRect& rect, @@ -65,20 +62,24 @@ LLSlider::LLSlider( mIncrement( increment ), mVolumeSlider( volume ), mMouseOffset( 0 ), - mDragStartThumbRect( 0, mRect.getHeight(), THUMB_WIDTH, 0 ), - mThumbRect( 0, mRect.getHeight(), THUMB_WIDTH, 0 ), mTrackColor( LLUI::sColorsGroup->getColor( "SliderTrackColor" ) ), mThumbOutlineColor( LLUI::sColorsGroup->getColor( "SliderThumbOutlineColor" ) ), mThumbCenterColor( LLUI::sColorsGroup->getColor( "SliderThumbCenterColor" ) ), - mDisabledThumbColor(LLUI::sColorsGroup->getColor( "SliderDisabledThumbColor" ) ), mMouseDownCallback( NULL ), mMouseUpCallback( NULL ) { + mThumbImage = LLUI::sImageProvider->getImageByID(LLUUID(LLUI::sAssetsGroup->getString("icn_slide-thumb_dark.tga"))); + mTrackImage = LLUI::sImageProvider->getImageByID(LLUUID(LLUI::sAssetsGroup->getString("icn_slide-groove_dark.tga"))); + mTrackHighlightImage = LLUI::sImageProvider->getImageByID(LLUUID(LLUI::sAssetsGroup->getString("icn_slide-highlight.tga"))); + // properly handle setting the starting thumb rect // do it this way to handle both the operating-on-settings // and standalone ways of using this setControlName(control_name, NULL); setValue(getValueF32()); + + updateThumbRect(); + mDragStartThumbRect = mThumbRect; } EWidgetType LLSlider::getWidgetType() const @@ -107,17 +108,26 @@ void LLSlider::setValue(F32 value, BOOL from_event) } mValue = value; + updateThumbRect(); +} +void LLSlider::updateThumbRect() +{ F32 t = (mValue - mMinValue) / (mMaxValue - mMinValue); - S32 left_edge = THUMB_WIDTH/2; - S32 right_edge = mRect.getWidth() - (THUMB_WIDTH/2); + S32 thumb_width = mThumbImage->getWidth(); + S32 thumb_height = mThumbImage->getHeight(); + S32 left_edge = (thumb_width / 2); + S32 right_edge = mRect.getWidth() - (thumb_width / 2); S32 x = left_edge + S32( t * (right_edge - left_edge) ); - mThumbRect.mLeft = x - (THUMB_WIDTH/2); - mThumbRect.mRight = x + (THUMB_WIDTH/2); + mThumbRect.mLeft = x - (thumb_width / 2); + mThumbRect.mRight = mThumbRect.mLeft + thumb_width; + mThumbRect.mBottom = getLocalRect().getCenterY() - (thumb_height / 2); + mThumbRect.mTop = mThumbRect.mBottom + thumb_height; } + void LLSlider::setValueAndCommit(F32 value) { F32 old_value = mValue; @@ -139,8 +149,9 @@ BOOL LLSlider::handleHover(S32 x, S32 y, MASK mask) { if( hasMouseCapture() ) { - S32 left_edge = THUMB_WIDTH/2; - S32 right_edge = mRect.getWidth() - (THUMB_WIDTH/2); + S32 thumb_half_width = mThumbImage->getWidth()/2; + S32 left_edge = thumb_half_width; + S32 right_edge = mRect.getWidth() - (thumb_half_width); x += mMouseOffset; x = llclamp( x, left_edge, right_edge ); @@ -203,7 +214,7 @@ BOOL LLSlider::handleMouseDown(S32 x, S32 y, MASK mask) // Find the offset of the actual mouse location from the center of the thumb. if (mThumbRect.pointInRect(x,y)) { - mMouseOffset = (mThumbRect.mLeft + THUMB_WIDTH/2) - x; + mMouseOffset = (mThumbRect.mLeft + mThumbImage->getWidth()/2) - x; } else { @@ -251,6 +262,9 @@ void LLSlider::draw() { if( getVisible() ) { + // since thumb image might still be decoding, need thumb to accomodate image size + updateThumbRect(); + // Draw background and thumb. // drawing solids requires texturing be disabled @@ -260,104 +274,37 @@ void LLSlider::draw() F32 opacity = mEnabled ? 1.f : 0.3f; LLColor4 center_color = (mThumbCenterColor % opacity); - LLColor4 outline_color = (mThumbOutlineColor % opacity); LLColor4 track_color = (mTrackColor % opacity); - LLImageGL* thumb_imagep = NULL; - // Track - if (mVolumeSlider) - { - LLRect track(0, mRect.getHeight(), mRect.getWidth(), 0); - - track.mBottom += 3; - track.mTop -= 1; - track.mRight -= 1; - - gl_triangle_2d(track.mLeft, track.mBottom, - track.mRight, track.mBottom, - track.mRight, track.mTop, - center_color, - TRUE); - gl_triangle_2d(track.mLeft, track.mBottom, - track.mRight, track.mBottom, - track.mRight, track.mTop, - outline_color, - FALSE); - } - else - { - LLUUID thumb_image_id; - thumb_image_id.set(LLUI::sAssetsGroup->getString("rounded_square.tga")); - thumb_imagep = LLUI::sImageProvider->getUIImageByID(thumb_image_id); + LLRect track_rect(mThumbImage->getWidth() / 2, + getLocalRect().getCenterY() + (mTrackImage->getHeight() / 2), + mRect.getWidth() - mThumbImage->getWidth() / 2, + getLocalRect().getCenterY() - (mTrackImage->getHeight() / 2) ); - S32 height_offset = (mRect.getHeight() - TRACK_HEIGHT) / 2; - LLRect track_rect(0, mRect.getHeight() - height_offset, mRect.getWidth(), height_offset ); + gl_draw_scaled_image_with_border(track_rect.mLeft, track_rect.mBottom, 3, 3, track_rect.getWidth(), track_rect.getHeight(), + mTrackImage, track_color); + gl_draw_scaled_image_with_border(track_rect.mLeft, track_rect.mBottom, 3, 3, mThumbRect.mLeft, track_rect.getHeight(), + mTrackHighlightImage, track_color); - track_rect.stretch(-1); - gl_draw_scaled_image_with_border(track_rect.mLeft, track_rect.mBottom, 16, 16, track_rect.getWidth(), track_rect.getHeight(), - thumb_imagep, track_color); - } // Thumb - if (!thumb_imagep) - { - if (mVolumeSlider) - { - if (hasMouseCapture()) - { - LLRect rect(mDragStartThumbRect); - gl_rect_2d( rect, outline_color ); - rect.stretch(-1); - gl_rect_2d( rect, mThumbCenterColor % 0.3f ); - - if (hasFocus()) - { - LLRect thumb_rect = mThumbRect; - thumb_rect.stretch(llround(lerp(1.f, 3.f, gFocusMgr.getFocusFlashAmt()))); - gl_rect_2d(thumb_rect, gFocusMgr.getFocusColor()); - } - gl_rect_2d( mThumbRect, mThumbOutlineColor ); - } - else - { - if (hasFocus()) - { - LLRect thumb_rect = mThumbRect; - thumb_rect.stretch(llround(lerp(1.f, 3.f, gFocusMgr.getFocusFlashAmt()))); - gl_rect_2d(thumb_rect, gFocusMgr.getFocusColor()); - } - LLRect rect(mThumbRect); - gl_rect_2d(rect, outline_color); - rect.stretch(-1); - gl_rect_2d( rect, center_color); - } - } - else - { - gl_rect_2d(mThumbRect, mThumbCenterColor, TRUE); - if (hasMouseCapture()) - { - gl_rect_2d(mDragStartThumbRect, center_color, FALSE); - } - } - } - else if( hasMouseCapture() ) + if( hasMouseCapture() ) { - gl_draw_scaled_image_with_border(mDragStartThumbRect.mLeft, mDragStartThumbRect.mBottom, 16, 16, mDragStartThumbRect.getWidth(), mDragStartThumbRect.getHeight(), - thumb_imagep, mThumbCenterColor % 0.3f, TRUE); + gl_draw_scaled_image(mDragStartThumbRect.mLeft, mDragStartThumbRect.mBottom, mDragStartThumbRect.getWidth(), mDragStartThumbRect.getHeight(), + mThumbImage, mThumbCenterColor % 0.3f); if (hasFocus()) { F32 lerp_amt = gFocusMgr.getFocusFlashAmt(); LLRect highlight_rect = mThumbRect; highlight_rect.stretch(llround(lerp(1.f, 3.f, lerp_amt))); - gl_draw_scaled_image_with_border(highlight_rect.mLeft, highlight_rect.mBottom, 16, 16, highlight_rect.getWidth(), highlight_rect.getHeight(), - thumb_imagep, gFocusMgr.getFocusColor()); + gl_draw_scaled_image_with_border(highlight_rect.mLeft, highlight_rect.mBottom, 0, 0, highlight_rect.getWidth(), highlight_rect.getHeight(), + mThumbImage, gFocusMgr.getFocusColor(), TRUE); } - gl_draw_scaled_image_with_border(mThumbRect.mLeft, mThumbRect.mBottom, 16, 16, mThumbRect.getWidth(), mThumbRect.getHeight(), - thumb_imagep, mThumbOutlineColor, TRUE); + gl_draw_scaled_image(mThumbRect.mLeft, mThumbRect.mBottom, mThumbRect.getWidth(), mThumbRect.getHeight(), + mThumbImage, mThumbOutlineColor); } else @@ -367,12 +314,12 @@ void LLSlider::draw() F32 lerp_amt = gFocusMgr.getFocusFlashAmt(); LLRect highlight_rect = mThumbRect; highlight_rect.stretch(llround(lerp(1.f, 3.f, lerp_amt))); - gl_draw_scaled_image_with_border(highlight_rect.mLeft, highlight_rect.mBottom, 16, 16, highlight_rect.getWidth(), highlight_rect.getHeight(), - thumb_imagep, gFocusMgr.getFocusColor()); + gl_draw_scaled_image_with_border(highlight_rect.mLeft, highlight_rect.mBottom, 0, 0, highlight_rect.getWidth(), highlight_rect.getHeight(), + mThumbImage, gFocusMgr.getFocusColor(), TRUE); } - gl_draw_scaled_image_with_border(mThumbRect.mLeft, mThumbRect.mBottom, 16, 16, mThumbRect.getWidth(), mThumbRect.getHeight(), - thumb_imagep, center_color, TRUE); + gl_draw_scaled_image(mThumbRect.mLeft, mThumbRect.mBottom, mThumbRect.getWidth(), mThumbRect.getHeight(), + mThumbImage, center_color); } LLUICtrl::draw(); } diff --git a/indra/llui/llslider.h b/indra/llui/llslider.h index 2641eaac74..55be2afbcc 100644 --- a/indra/llui/llslider.h +++ b/indra/llui/llslider.h @@ -36,6 +36,7 @@ #include "v4color.h" class LLUICtrlFactory; +class LLImageGL; class LLSlider : public LLUICtrl { @@ -85,6 +86,7 @@ public: protected: void setValueAndCommit(F32 value); + void updateThumbRect(); protected: F32 mValue; @@ -97,11 +99,14 @@ protected: S32 mMouseOffset; LLRect mDragStartThumbRect; + LLImageGL* mThumbImage; + LLImageGL* mTrackImage; + LLImageGL* mTrackHighlightImage; + LLRect mThumbRect; LLColor4 mTrackColor; LLColor4 mThumbOutlineColor; LLColor4 mThumbCenterColor; - LLColor4 mDisabledThumbColor; void (*mMouseDownCallback)(LLUICtrl* ctrl, void* userdata); void (*mMouseUpCallback)(LLUICtrl* ctrl, void* userdata); diff --git a/indra/llui/llsliderctrl.cpp b/indra/llui/llsliderctrl.cpp index dd4a9941c5..3ff3a4f884 100644 --- a/indra/llui/llsliderctrl.cpp +++ b/indra/llui/llsliderctrl.cpp @@ -125,7 +125,7 @@ LLSliderCtrl::LLSliderCtrl(const LLString& name, const LLRect& rect, &LLLineEditor::prevalidateFloat ); mEditor->setFollowsLeft(); mEditor->setFollowsBottom(); - mEditor->setFocusReceivedCallback( &LLSliderCtrl::onEditorGainFocus ); + mEditor->setFocusReceivedCallback( &LLSliderCtrl::onEditorGainFocus, this ); mEditor->setIgnoreTab(TRUE); // don't do this, as selecting the entire text is single clicking in some cases // and double clicking in others @@ -150,7 +150,7 @@ LLSliderCtrl::~LLSliderCtrl() } // static -void LLSliderCtrl::onEditorGainFocus( LLUICtrl* caller, void *userdata ) +void LLSliderCtrl::onEditorGainFocus( LLFocusableElement* caller, void *userdata ) { LLSliderCtrl* self = (LLSliderCtrl*) userdata; llassert( caller == self->mEditor ); diff --git a/indra/llui/llsliderctrl.h b/indra/llui/llsliderctrl.h index fa6c0bccae..de1c09639c 100644 --- a/indra/llui/llsliderctrl.h +++ b/indra/llui/llsliderctrl.h @@ -117,7 +117,7 @@ public: static void onSliderMouseUp(LLUICtrl* caller,void* userdata); static void onEditorCommit(LLUICtrl* caller, void* userdata); - static void onEditorGainFocus(LLUICtrl* caller, void *userdata); + static void onEditorGainFocus(LLFocusableElement* caller, void *userdata); static void onEditorChangeFocus(LLUICtrl* caller, S32 direction, void *userdata); private: diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp index 2be2814080..c4b7de768a 100644 --- a/indra/llui/llspinctrl.cpp +++ b/indra/llui/llspinctrl.cpp @@ -128,7 +128,7 @@ LLSpinCtrl::LLSpinCtrl( const LLString& name, const LLRect& rect, const LLString &LLLineEditor::prevalidateFloat ); mEditor->setFollowsLeft(); mEditor->setFollowsBottom(); - mEditor->setFocusReceivedCallback( &LLSpinCtrl::onEditorGainFocus ); + mEditor->setFocusReceivedCallback( &LLSpinCtrl::onEditorGainFocus, this ); //RN: this seems to be a BAD IDEA, as it makes the editor behavior different when it has focus // than when it doesn't. Instead, if you always have to double click to select all the text, // it's easier to understand @@ -137,7 +137,7 @@ LLSpinCtrl::LLSpinCtrl( const LLString& name, const LLRect& rect, const LLString addChild(mEditor); updateEditor(); - setSpanChildren( TRUE ); + setUseBoundingRect( TRUE ); } LLSpinCtrl::~LLSpinCtrl() @@ -230,7 +230,7 @@ void LLSpinCtrl::onDownBtn( void *userdata ) } // static -void LLSpinCtrl::onEditorGainFocus( LLUICtrl* caller, void *userdata ) +void LLSpinCtrl::onEditorGainFocus( LLFocusableElement* caller, void *userdata ) { LLSpinCtrl* self = (LLSpinCtrl*) userdata; llassert( caller == self->mEditor ); diff --git a/indra/llui/llspinctrl.h b/indra/llui/llspinctrl.h index f2c7b40cfe..5fc3ccdcc1 100644 --- a/indra/llui/llspinctrl.h +++ b/indra/llui/llspinctrl.h @@ -113,7 +113,7 @@ public: virtual void draw(); static void onEditorCommit(LLUICtrl* caller, void* userdata); - static void onEditorGainFocus(LLUICtrl* caller, void *userdata); + static void onEditorGainFocus(LLFocusableElement* caller, void *userdata); static void onEditorChangeFocus(LLUICtrl* caller, S32 direction, void *userdata); static void onUpBtn(void *userdata); diff --git a/indra/llui/llstyle.cpp b/indra/llui/llstyle.cpp index 1f932f758c..2d3534e7be 100644 --- a/indra/llui/llstyle.cpp +++ b/indra/llui/llstyle.cpp @@ -230,7 +230,7 @@ void LLStyle::setImage(const LLString& src) } else { - mImagep = LLUI::sImageProvider->getUIImageByID(LLUUID(src)); + mImagep = LLUI::sImageProvider->getImageByID(LLUUID(src)); } } diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp index 1ab11f3644..148060359f 100644 --- a/indra/llui/lltabcontainer.cpp +++ b/indra/llui/lltabcontainer.cpp @@ -156,7 +156,12 @@ void LLTabContainerCommon::lockTabs(S32 num_tabs) { // count current tabs or use supplied value and ensure no new tabs get // inserted between them - mLockedTabCount = num_tabs > 0 ? num_tabs : getTabCount(); + mLockedTabCount = num_tabs > 0 ? llmin(getTabCount(), num_tabs) : getTabCount(); +} + +void LLTabContainerCommon::unlockTabs() +{ + mLockedTabCount = 0; } void LLTabContainerCommon::removeTabPanel(LLPanel* child) @@ -557,7 +562,7 @@ void LLTabContainerCommon::setTabImage(LLPanel* child, std::string img_name, con } void LLTabContainerCommon::setTitle(const LLString& title) -{ +{ if (mTitleBox) { mTitleBox->setText( title ); @@ -721,6 +726,14 @@ void LLTabContainerCommon::insertTuple(LLTabTuple * tuple, eInsertionPoint inser // insert the new tab in the front of the list mTabList.insert(mTabList.begin() + mLockedTabCount, tuple); break; + case LEFT_OF_CURRENT: + // insert the new tab before the current tab (but not before mLockedTabCount) + { + tuple_list_t::iterator current_iter = mTabList.begin() + llmax(mLockedTabCount, mCurrentTabIdx); + mTabList.insert(current_iter, tuple); + } + break; + case RIGHT_OF_CURRENT: // insert the new tab after the current tab (but not before mLockedTabCount) { @@ -946,14 +959,14 @@ void LLTabContainer::addTabPanel(LLPanel* child, if( LLTabContainer::TOP == mTabPosition ) { btn_rect.setLeftTopAndSize( 0, mRect.getHeight() - mTopBorderHeight + tab_fudge, button_width, TABCNTR_TAB_HEIGHT ); - tab_img = "UIImgBtnTabTopOutUUID"; - tab_selected_img = "UIImgBtnTabTopInUUID"; + tab_img = "tab_top_blue.tga"; + tab_selected_img = "tab_top_selected_blue.tga"; } else { btn_rect.setOriginAndSize( 0, 0 + tab_fudge, button_width, TABCNTR_TAB_HEIGHT ); - tab_img = "UIImgBtnTabBottomOutUUID"; - tab_selected_img = "UIImgBtnTabBottomInUUID"; + tab_img = "tab_bottom_blue.tga"; + tab_selected_img = "tab_bottom_selected_blue.tga"; } if (placeholder) @@ -979,7 +992,7 @@ void LLTabContainer::addTabPanel(LLPanel* child, LLButton* btn = new LLButton( LLString(child->getName()) + " tab", btn_rect, - tab_img, tab_selected_img, "", + "", "", "", &LLTabContainer::onTabBtn, NULL, // set userdata below font, trimmed_label, trimmed_label ); @@ -987,7 +1000,7 @@ void LLTabContainer::addTabPanel(LLPanel* child, btn->setVisible( FALSE ); btn->setToolTip( tooltip ); btn->setScaleImage(TRUE); - btn->setFixedBorder(14, 14); + btn->setImages(tab_img, tab_selected_img); // Try to squeeze in a bit more text btn->setLeftHPad( 4 ); @@ -1139,7 +1152,7 @@ BOOL LLTabContainer::selectTab(S32 which) //if( gFocusMgr.childHasKeyboardFocus( this ) ) //{ - // gFocusMgr.setKeyboardFocus( NULL, NULL ); + // gFocusMgr.setKeyboardFocus( NULL ); //} LLTabTuple* selected_tuple = mTabList[which]; @@ -1370,7 +1383,7 @@ BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask ) { LLButton* tab_button = mTabList[getCurrentPanelIndex()]->mButton; gFocusMgr.setMouseCapture(this); - gFocusMgr.setKeyboardFocus(tab_button, NULL); + gFocusMgr.setKeyboardFocus(tab_button); } } return handled; @@ -1475,7 +1488,7 @@ BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask ) BOOL LLTabContainer::handleToolTip( S32 x, S32 y, LLString& msg, LLRect* sticky_rect ) { BOOL handled = LLPanel::handleToolTip( x, y, msg, sticky_rect ); - if (!handled && mTabList.size() > 0 && getVisible() && pointInView( x, y ) ) + if (!handled && mTabList.size() > 0) { LLTabTuple* firsttuple = mTabList[0]; @@ -1645,12 +1658,12 @@ void LLTabContainer::setTabImage(LLPanel* child, std::string image_name, const L mTotalTabWidth -= tuple->mButton->getRect().getWidth(); S32 image_overlay_width = tuple->mButton->getImageOverlay().notNull() ? - tuple->mButton->getImageOverlay()->getWidth(0) : + tuple->mButton->getImageOverlay()->getImage()->getWidth(0) : 0; tuple->mPadding = image_overlay_width; - tuple->mButton->setRightHPad(tuple->mPadding + LLBUTTON_H_PAD); + tuple->mButton->setRightHPad(6); tuple->mButton->reshape(llclamp(fontp->getWidth(tuple->mButton->getLabelSelected()) + TAB_PADDING + tuple->mPadding, mMinTabWidth, mMaxTabWidth), tuple->mButton->getRect().getHeight()); // add back in button width to total tab strip width diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h index c77547a771..b72ecb8126 100644 --- a/indra/llui/lltabcontainer.h +++ b/indra/llui/lltabcontainer.h @@ -54,6 +54,7 @@ public: { START, END, + LEFT_OF_CURRENT, RIGHT_OF_CURRENT } eInsertionPoint; @@ -91,6 +92,8 @@ public: eInsertionPoint insertion_point = END) = 0; virtual void addPlaceholder(LLPanel* child, const LLString& label); virtual void lockTabs(S32 num_tabs = 0); + virtual void unlockTabs(); + S32 getNumLockedTabs() { return mLockedTabCount; } virtual void enableTabButton(S32 which, BOOL enable); diff --git a/indra/llui/lltextbox.cpp b/indra/llui/lltextbox.cpp index 8bd7b1509f..3b15e3a25f 100644 --- a/indra/llui/lltextbox.cpp +++ b/indra/llui/lltextbox.cpp @@ -56,6 +56,7 @@ LLTextBox::LLTextBox(const LLString& name, const LLRect& rect, const LLString& t mBorderVisible( FALSE ), mFontStyle(LLFontGL::DROP_SHADOW_SOFT), mBorderDropShadowVisible( FALSE ), + mUseEllipses( FALSE ), mHPad(0), mVPad(0), mHAlign( LLFontGL::LEFT ), @@ -84,6 +85,7 @@ LLTextBox::LLTextBox(const LLString& name, const LLString& text, F32 max_width, mBorderVisible(FALSE), mFontStyle(LLFontGL::DROP_SHADOW_SOFT), mBorderDropShadowVisible(FALSE), + mUseEllipses( FALSE ), mHPad(0), mVPad(0), mHAlign(LLFontGL::LEFT), @@ -393,7 +395,7 @@ void LLTextBox::drawText( S32 x, S32 y, const LLColor4& color ) mFontGL->render(mText.getWString(), cur_pos, (F32)x, (F32)y, color, mHAlign, mVAlign, mFontStyle, - line_length, mRect.getWidth(), NULL, TRUE ); + line_length, mRect.getWidth(), NULL, TRUE, mUseEllipses ); cur_pos += line_length + 1; y -= llfloor(mFontGL->getLineHeight()); } @@ -403,7 +405,7 @@ void LLTextBox::drawText( S32 x, S32 y, const LLColor4& color ) mFontGL->render(mText.getWString(), 0, (F32)x, (F32)y, color, mHAlign, mVAlign, mFontStyle, - S32_MAX, mRect.getWidth(), NULL, TRUE); + S32_MAX, mRect.getWidth(), NULL, TRUE, mUseEllipses); } } @@ -481,7 +483,7 @@ LLView* LLTextBox::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *f text_box->mFontStyle = LLFontGL::getStyleFromString(font_style); } - BOOL mouse_opaque; + BOOL mouse_opaque = text_box->getMouseOpaque(); if (node->getAttributeBOOL("mouse_opaque", mouse_opaque)) { text_box->setMouseOpaque(mouse_opaque); diff --git a/indra/llui/lltextbox.h b/indra/llui/lltextbox.h index c7c79464a0..d25452b941 100644 --- a/indra/llui/lltextbox.h +++ b/indra/llui/lltextbox.h @@ -79,6 +79,7 @@ public: void setText( const LLStringExplicit& text ); void setWrappedText(const LLStringExplicit& text, F32 max_width = -1.0); // default width means use existing control width + void setUseEllipses( BOOL use_ellipses ) { mUseEllipses = use_ellipses; } void setBackgroundVisible(BOOL visible) { mBackgroundVisible = visible; } void setBorderVisible(BOOL visible) { mBorderVisible = visible; } @@ -124,6 +125,7 @@ protected: U8 mFontStyle; // style bit flags for font BOOL mBorderDropShadowVisible; + BOOL mUseEllipses; S32 mHPad; S32 mVPad; diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 5c8b7c7281..8b9353eb8e 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -310,9 +310,9 @@ LLTextEditor::LLTextEditor( mWordWrap( FALSE ), mTabToNextField( TRUE ), mCommitOnFocusLost( FALSE ), - mTakesFocus( TRUE ), mHideScrollbarForShortDocs( FALSE ), mTakesNonScrollClicks( TRUE ), + mTrackBottom( TRUE ), mAllowEmbeddedItems( allow_embedded_items ), mAcceptCallingCardNames(FALSE), mHandleEditKeysDirectly( FALSE ), @@ -1141,46 +1141,42 @@ void LLTextEditor::selectAll() BOOL LLTextEditor::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen) { - if (pointInView(x, y) && getVisible()) + for ( child_list_const_iter_t child_it = getChildList()->begin(); + child_it != getChildList()->end(); ++child_it) { - for ( child_list_const_iter_t child_it = getChildList()->begin(); - child_it != getChildList()->end(); ++child_it) - { - LLView* viewp = *child_it; - S32 local_x = x - viewp->getRect().mLeft; - S32 local_y = y - viewp->getRect().mBottom; - if( viewp->handleToolTip(local_x, local_y, msg, sticky_rect_screen ) ) - { - return TRUE; - } - } - - if( mSegments.empty() ) + LLView* viewp = *child_it; + S32 local_x = x - viewp->getRect().mLeft; + S32 local_y = y - viewp->getRect().mBottom; + if( viewp->handleToolTip(local_x, local_y, msg, sticky_rect_screen ) ) { return TRUE; } + } - LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y ); - if( cur_segment ) - { - BOOL has_tool_tip = FALSE; - has_tool_tip = cur_segment->getToolTip( msg ); + if( mSegments.empty() ) + { + return TRUE; + } - if( has_tool_tip ) - { - // Just use a slop area around the cursor - // Convert rect local to screen coordinates - S32 SLOP = 8; - localPointToScreen( - x - SLOP, y - SLOP, - &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); - sticky_rect_screen->mRight = sticky_rect_screen->mLeft + 2 * SLOP; - sticky_rect_screen->mTop = sticky_rect_screen->mBottom + 2 * SLOP; - } + LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y ); + if( cur_segment ) + { + BOOL has_tool_tip = FALSE; + has_tool_tip = cur_segment->getToolTip( msg ); + + if( has_tool_tip ) + { + // Just use a slop area around the cursor + // Convert rect local to screen coordinates + S32 SLOP = 8; + localPointToScreen( + x - SLOP, y - SLOP, + &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); + sticky_rect_screen->mRight = sticky_rect_screen->mLeft + 2 * SLOP; + sticky_rect_screen->mTop = sticky_rect_screen->mBottom + 2 * SLOP; } - return TRUE; } - return FALSE; + return TRUE; } BOOL LLTextEditor::handleScrollWheel(S32 x, S32 y, S32 clicks) @@ -1263,7 +1259,7 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) handled = TRUE; } - if (mTakesFocus) + if (hasTabStop()) { setFocus( TRUE ); handled = TRUE; @@ -1436,11 +1432,6 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask) if( !handled && mTakesNonScrollClicks) { - if (mTakesFocus) - { - setFocus( TRUE ); - } - setCursorAtLocalPos( x, y, FALSE ); deselect(); @@ -3160,6 +3151,9 @@ void LLTextEditor::draw() } LLView::draw(); // Draw children (scrollbar and border) } + + // remember if we are supposed to be at the bottom of the buffer + mScrolledToBottom = isScrolledToBottom(); } void LLTextEditor::reportBadKeystroke() @@ -3328,6 +3322,17 @@ void LLTextEditor::changeLine( S32 delta ) unbindEmbeddedChars( mGLFont ); } +BOOL LLTextEditor::isScrolledToTop() +{ + return mScrollbar->isAtBeginning(); +} + +BOOL LLTextEditor::isScrolledToBottom() +{ + return mScrollbar->isAtEnd(); +} + + void LLTextEditor::startOfLine() { S32 line, offset; @@ -3448,6 +3453,13 @@ void LLTextEditor::reshape(S32 width, S32 height, BOOL called_from_parent) { LLView::reshape( width, height, called_from_parent ); + // if scrolled to bottom, stay at bottom + // unless user is editing text + if (mScrolledToBottom && mTrackBottom && !hasFocus()) + { + endOfDoc(); + } + updateTextRect(); S32 line_height = llround( mGLFont->getLineHeight() ); @@ -4234,6 +4246,8 @@ void LLTextEditor::setTextEditorParameters(LLXMLNodePtr node) node->getAttributeBOOL("word_wrap", word_wrap); setWordWrap(word_wrap); + node->getAttributeBOOL("track_bottom", mTrackBottom); + LLColor4 color; if (LLUICtrlFactory::getAttributeColor(node,"cursor_color", color)) { diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 7049de7b89..a2ce0d2c47 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -206,13 +206,13 @@ public: void setTabToNextField(BOOL b) { mTabToNextField = b; } void setCommitOnFocusLost(BOOL b) { mCommitOnFocusLost = b; } - // If takes focus, will take keyboard focus on click. - void setTakesFocus(BOOL b) { mTakesFocus = b; } - // Hack to handle Notecards virtual BOOL importBuffer(const LLString& buffer ); virtual BOOL exportBuffer(LLString& buffer ); + // If takes focus, will take keyboard focus on click. + void setTakesFocus(BOOL b) { mTakesFocus = b; } + void setSourceID(const LLUUID& id) { mSourceID = id; } void setAcceptCallingCardNames(BOOL enable) { mAcceptCallingCardNames = enable; } @@ -244,7 +244,10 @@ public: void startOfLine(); void endOfLine(); void endOfDoc(); - + + BOOL isScrolledToTop(); + BOOL isScrolledToBottom(); + // Getters const LLWString& getWText() const; llwchar getWChar(S32 pos); @@ -439,6 +442,8 @@ protected: BOOL mTakesFocus; BOOL mHideScrollbarForShortDocs; BOOL mTakesNonScrollClicks; + BOOL mTrackBottom; // if true, keeps scroll position at bottom during resize + BOOL mScrolledToBottom; BOOL mAllowEmbeddedItems; diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp index 00a230dff3..7561fe8b48 100644 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -79,7 +79,7 @@ LLVector2 LLUI::sGLScaleFactor(1.f, 1.f); LLWindow* LLUI::sWindow = NULL; LLHtmlHelp* LLUI::sHtmlHelp = NULL; BOOL LLUI::sShowXUINames = FALSE; -std::stack<LLRect> LLUI::sClipRectStack; +std::stack<LLRect> LLScreenClipRect::sClipRectStack; // // Functions @@ -410,39 +410,76 @@ void gl_corners_2d(S32 left, S32 top, S32 right, S32 bottom, S32 length, F32 max } -void gl_draw_image( S32 x, S32 y, LLImageGL* image, const LLColor4& color ) +void gl_draw_image( S32 x, S32 y, LLImageGL* image, const LLColor4& color, const LLRectf& uv_rect ) { - gl_draw_scaled_rotated_image( x, y, image->getWidth(0), image->getHeight(0), 0.f, image, color ); + if (NULL == image) + { + llwarns << "image == NULL; aborting function" << llendl; + return; + } + gl_draw_scaled_rotated_image( x, y, image->getWidth(0), image->getHeight(0), 0.f, image, color, uv_rect ); } -void gl_draw_scaled_image(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color) +void gl_draw_scaled_image(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color, const LLRectf& uv_rect) { - gl_draw_scaled_rotated_image( x, y, width, height, 0.f, image, color ); + if (NULL == image) + { + llwarns << "image == NULL; aborting function" << llendl; + return; + } + gl_draw_scaled_rotated_image( x, y, width, height, 0.f, image, color, uv_rect ); } -void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 border_width, S32 border_height, S32 width, S32 height, LLImageGL* image, const LLColor4& color, BOOL solid_color) +void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 border_width, S32 border_height, S32 width, S32 height, LLImageGL* image, const LLColor4& color, BOOL solid_color, const LLRectf& uv_rect) { - stop_glerror(); - F32 border_scale = 1.f; - if (NULL == image) { llwarns << "image == NULL; aborting function" << llendl; return; } - if (border_height * 2 > height) - { - border_scale = (F32)height / ((F32)border_height * 2.f); - } - if (border_width * 2 > width) + // scale screen size of borders down + F32 border_width_fraction = (F32)border_width / (F32)image->getWidth(0); + F32 border_height_fraction = (F32)border_height / (F32)image->getHeight(0); + + LLRectf scale_rect(border_width_fraction, 1.f - border_height_fraction, 1.f - border_width_fraction, border_height_fraction); + gl_draw_scaled_image_with_border(x, y, width, height, image, color, solid_color, uv_rect, scale_rect); +} + +void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color, BOOL solid_color, const LLRectf& uv_rect, const LLRectf& scale_rect) +{ + stop_glerror(); + + if (NULL == image) { - border_scale = llmin(border_scale, (F32)width / ((F32)border_width * 2.f)); + llwarns << "image == NULL; aborting function" << llendl; + return; } // scale screen size of borders down - S32 scaled_border_width = llfloor(border_scale * (F32)border_width); - S32 scaled_border_height = llfloor(border_scale * (F32)border_height); + LLRectf clipped_scale_rect = uv_rect; + clipped_scale_rect.intersectWith(scale_rect); + + LLRect draw_rect(0, height, width, 0); + LLRect draw_scale_rect(llround((F32)image->getWidth() * scale_rect.mLeft), + llround((F32)image->getHeight() * scale_rect.mTop), + llround((F32)image->getWidth() * scale_rect.mRight), + llround((F32)image->getHeight() * scale_rect.mBottom)); + // scale fixed region of image up with drawn region + draw_scale_rect.mRight += width - image->getWidth(); + draw_scale_rect.mTop += height - image->getHeight(); + + S32 border_shrink_width = llmax(0, draw_scale_rect.mLeft - draw_scale_rect.mRight); + S32 border_shrink_height = llmax(0, draw_scale_rect.mBottom - draw_scale_rect.mTop); + + F32 shrink_width_ratio = scale_rect.getWidth() == 1.f ? 0.f : border_shrink_width / ((F32)image->getWidth() * (1.f - scale_rect.getWidth())); + F32 shrink_height_ratio = scale_rect.getHeight() == 1.f ? 0.f : border_shrink_height / ((F32)image->getHeight() * (1.f - scale_rect.getHeight())); + + F32 shrink_scale = 1.f - llmax(shrink_width_ratio, shrink_height_ratio); + draw_scale_rect.mLeft = llround((F32)draw_scale_rect.mLeft * shrink_scale); + draw_scale_rect.mTop = llround(lerp((F32)height, (F32)draw_scale_rect.mTop, shrink_scale)); + draw_scale_rect.mRight = llround(lerp((F32)width, (F32)draw_scale_rect.mRight, shrink_scale)); + draw_scale_rect.mBottom = llround((F32)draw_scale_rect.mBottom * shrink_scale); LLGLSUIDefault gls_ui; @@ -470,127 +507,124 @@ void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 border_width, S32 border glColor4fv(color.mV); - F32 border_width_fraction = (F32)border_width / (F32)image->getWidth(0); - F32 border_height_fraction = (F32)border_height / (F32)image->getHeight(0); - glBegin(GL_QUADS); { // draw bottom left - glTexCoord2f(0.f, 0.f); + glTexCoord2d(uv_rect.mLeft, uv_rect.mBottom); glVertex2i(0, 0); - glTexCoord2f(border_width_fraction, 0.f); - glVertex2i(scaled_border_width, 0); + glTexCoord2f(clipped_scale_rect.mLeft, uv_rect.mBottom); + glVertex2i(draw_scale_rect.mLeft, 0); - glTexCoord2f(border_width_fraction, border_height_fraction); - glVertex2i(scaled_border_width, scaled_border_height); + glTexCoord2f(clipped_scale_rect.mLeft, clipped_scale_rect.mBottom); + glVertex2i(draw_scale_rect.mLeft, draw_scale_rect.mBottom); - glTexCoord2f(0.f, border_height_fraction); - glVertex2i(0, scaled_border_height); + glTexCoord2d(uv_rect.mLeft, clipped_scale_rect.mBottom); + glVertex2i(0, draw_scale_rect.mBottom); // draw bottom middle - glTexCoord2f(border_width_fraction, 0.f); - glVertex2i(scaled_border_width, 0); + glTexCoord2f(clipped_scale_rect.mLeft, uv_rect.mBottom); + glVertex2i(draw_scale_rect.mLeft, 0); - glTexCoord2f(1.f - border_width_fraction, 0.f); - glVertex2i(width - scaled_border_width, 0); + glTexCoord2d(clipped_scale_rect.mRight, uv_rect.mBottom); + glVertex2i(draw_scale_rect.mRight, 0); - glTexCoord2f(1.f - border_width_fraction, border_height_fraction); - glVertex2i(width - scaled_border_width, scaled_border_height); + glTexCoord2d(clipped_scale_rect.mRight, clipped_scale_rect.mBottom); + glVertex2i(draw_scale_rect.mRight, draw_scale_rect.mBottom); - glTexCoord2f(border_width_fraction, border_height_fraction); - glVertex2i(scaled_border_width, scaled_border_height); + glTexCoord2f(clipped_scale_rect.mLeft, clipped_scale_rect.mBottom); + glVertex2i(draw_scale_rect.mLeft, draw_scale_rect.mBottom); // draw bottom right - glTexCoord2f(1.f - border_width_fraction, 0.f); - glVertex2i(width - scaled_border_width, 0); + glTexCoord2d(clipped_scale_rect.mRight, uv_rect.mBottom); + glVertex2i(draw_scale_rect.mRight, 0); - glTexCoord2f(1.f, 0.f); + glTexCoord2d(uv_rect.mRight, uv_rect.mBottom); glVertex2i(width, 0); - glTexCoord2f(1.f, border_height_fraction); - glVertex2i(width, scaled_border_height); + glTexCoord2d(uv_rect.mRight, clipped_scale_rect.mBottom); + glVertex2i(width, draw_scale_rect.mBottom); - glTexCoord2f(1.f - border_width_fraction, border_height_fraction); - glVertex2i(width - scaled_border_width, scaled_border_height); + glTexCoord2d(clipped_scale_rect.mRight, clipped_scale_rect.mBottom); + glVertex2i(draw_scale_rect.mRight, draw_scale_rect.mBottom); // draw left - glTexCoord2f(0.f, border_height_fraction); - glVertex2i(0, scaled_border_height); + glTexCoord2d(uv_rect.mLeft, clipped_scale_rect.mBottom); + glVertex2i(0, draw_scale_rect.mBottom); - glTexCoord2f(border_width_fraction, border_height_fraction); - glVertex2i(scaled_border_width, scaled_border_height); + glTexCoord2f(clipped_scale_rect.mLeft, clipped_scale_rect.mBottom); + glVertex2i(draw_scale_rect.mLeft, draw_scale_rect.mBottom); - glTexCoord2f(border_width_fraction, 1.f - border_height_fraction); - glVertex2i(scaled_border_width, height - scaled_border_height); + glTexCoord2f(clipped_scale_rect.mLeft, clipped_scale_rect.mTop); + glVertex2i(draw_scale_rect.mLeft, draw_scale_rect.mTop); - glTexCoord2f(0.f, 1.f - border_height_fraction); - glVertex2i(0, height - scaled_border_height); + glTexCoord2d(uv_rect.mLeft, clipped_scale_rect.mTop); + glVertex2i(0, draw_scale_rect.mTop); // draw middle - glTexCoord2f(border_width_fraction, border_height_fraction); - glVertex2i(scaled_border_width, scaled_border_height); + glTexCoord2f(clipped_scale_rect.mLeft, clipped_scale_rect.mBottom); + glVertex2i(draw_scale_rect.mLeft, draw_scale_rect.mBottom); - glTexCoord2f(1.f - border_width_fraction, border_height_fraction); - glVertex2i(width - scaled_border_width, scaled_border_height); + glTexCoord2d(clipped_scale_rect.mRight, clipped_scale_rect.mBottom); + glVertex2i(draw_scale_rect.mRight, draw_scale_rect.mBottom); - glTexCoord2f(1.f - border_width_fraction, 1.f - border_height_fraction); - glVertex2i(width - scaled_border_width, height - scaled_border_height); + glTexCoord2d(clipped_scale_rect.mRight, clipped_scale_rect.mTop); + glVertex2i(draw_scale_rect.mRight, draw_scale_rect.mTop); - glTexCoord2f(border_width_fraction, 1.f - border_height_fraction); - glVertex2i(scaled_border_width, height - scaled_border_height); + glTexCoord2f(clipped_scale_rect.mLeft, clipped_scale_rect.mTop); + glVertex2i(draw_scale_rect.mLeft, draw_scale_rect.mTop); // draw right - glTexCoord2f(1.f - border_width_fraction, border_height_fraction); - glVertex2i(width - scaled_border_width, scaled_border_height); + glTexCoord2d(clipped_scale_rect.mRight, clipped_scale_rect.mBottom); + glVertex2i(draw_scale_rect.mRight, draw_scale_rect.mBottom); - glTexCoord2f(1.f, border_height_fraction); - glVertex2i(width, scaled_border_height); + glTexCoord2d(uv_rect.mRight, clipped_scale_rect.mBottom); + glVertex2i(width, draw_scale_rect.mBottom); - glTexCoord2f(1.f, 1.f - border_height_fraction); - glVertex2i(width, height - scaled_border_height); + glTexCoord2d(uv_rect.mRight, clipped_scale_rect.mTop); + glVertex2i(width, draw_scale_rect.mTop); - glTexCoord2f(1.f - border_width_fraction, 1.f - border_height_fraction); - glVertex2i(width - scaled_border_width, height - scaled_border_height); + glTexCoord2d(clipped_scale_rect.mRight, clipped_scale_rect.mTop); + glVertex2i(draw_scale_rect.mRight, draw_scale_rect.mTop); // draw top left - glTexCoord2f(0.f, 1.f - border_height_fraction); - glVertex2i(0, height - scaled_border_height); + glTexCoord2d(uv_rect.mLeft, clipped_scale_rect.mTop); + glVertex2i(0, draw_scale_rect.mTop); - glTexCoord2f(border_width_fraction, 1.f - border_height_fraction); - glVertex2i(scaled_border_width, height - scaled_border_height); + glTexCoord2f(clipped_scale_rect.mLeft, clipped_scale_rect.mTop); + glVertex2i(draw_scale_rect.mLeft, draw_scale_rect.mTop); - glTexCoord2f(border_width_fraction, 1.f); - glVertex2i(scaled_border_width, height); + glTexCoord2f(clipped_scale_rect.mLeft, uv_rect.mTop); + glVertex2i(draw_scale_rect.mLeft, height); - glTexCoord2f(0.f, 1.f); + glTexCoord2d(uv_rect.mLeft, uv_rect.mTop); glVertex2i(0, height); // draw top middle - glTexCoord2f(border_width_fraction, 1.f - border_height_fraction); - glVertex2i(scaled_border_width, height - scaled_border_height); + glTexCoord2f(clipped_scale_rect.mLeft, clipped_scale_rect.mTop); + glVertex2i(draw_scale_rect.mLeft, draw_scale_rect.mTop); - glTexCoord2f(1.f - border_width_fraction, 1.f - border_height_fraction); - glVertex2i(width - scaled_border_width, height - scaled_border_height); + glTexCoord2d(clipped_scale_rect.mRight, clipped_scale_rect.mTop); + glVertex2i(draw_scale_rect.mRight, draw_scale_rect.mTop); - glTexCoord2f(1.f - border_width_fraction, 1.f); - glVertex2i(width - scaled_border_width, height); + glTexCoord2d(clipped_scale_rect.mRight, uv_rect.mTop); + glVertex2i(draw_scale_rect.mRight, height); - glTexCoord2f(border_width_fraction, 1.f); - glVertex2i(scaled_border_width, height); + glTexCoord2f(clipped_scale_rect.mLeft, uv_rect.mTop); + glVertex2i(draw_scale_rect.mLeft, height); // draw top right - glTexCoord2f(1.f - border_width_fraction, 1.f - border_height_fraction); - glVertex2i(width - scaled_border_width, height - scaled_border_height); + glTexCoord2d(clipped_scale_rect.mRight, clipped_scale_rect.mTop); + glVertex2i(draw_scale_rect.mRight, draw_scale_rect.mTop); - glTexCoord2f(1.f, 1.f - border_height_fraction); - glVertex2i(width, height - scaled_border_height); + glTexCoord2d(uv_rect.mRight, clipped_scale_rect.mTop); + glVertex2i(width, draw_scale_rect.mTop); - glTexCoord2f(1.f, 1.f); + glTexCoord2d(uv_rect.mRight, uv_rect.mTop); glVertex2i(width, height); - glTexCoord2f(1.f - border_width_fraction, 1.f); - glVertex2i(width - scaled_border_width, height); + glTexCoord2d(clipped_scale_rect.mRight, uv_rect.mTop); + glVertex2i(draw_scale_rect.mRight, height); } glEnd(); } @@ -602,12 +636,12 @@ void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 border_width, S32 border } } -void gl_draw_rotated_image(S32 x, S32 y, F32 degrees, LLImageGL* image, const LLColor4& color) +void gl_draw_rotated_image(S32 x, S32 y, F32 degrees, LLImageGL* image, const LLColor4& color, const LLRectf& uv_rect) { - gl_draw_scaled_rotated_image( x, y, image->getWidth(0), image->getHeight(0), degrees, image, color ); + gl_draw_scaled_rotated_image( x, y, image->getWidth(0), image->getHeight(0), degrees, image, color, uv_rect ); } -void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degrees, LLImageGL* image, const LLColor4& color) +void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degrees, LLImageGL* image, const LLColor4& color, const LLRectf& uv_rect) { if (NULL == image) { @@ -635,16 +669,16 @@ void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degre glBegin(GL_QUADS); { - glTexCoord2f(1.f, 1.f); + glTexCoord2f(uv_rect.mRight, uv_rect.mTop); glVertex2i(width, height ); - glTexCoord2f(0.f, 1.f); + glTexCoord2f(uv_rect.mLeft, uv_rect.mTop); glVertex2i(0, height ); - glTexCoord2f(0.f, 0.f); + glTexCoord2f(uv_rect.mLeft, uv_rect.mBottom); glVertex2i(0, 0); - glTexCoord2f(1.f, 0.f); + glTexCoord2f(uv_rect.mRight, uv_rect.mBottom); glVertex2i(width, 0); } glEnd(); @@ -653,7 +687,7 @@ void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degre } -void gl_draw_scaled_image_inverted(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color) +void gl_draw_scaled_image_inverted(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color, const LLRectf& uv_rect) { if (NULL == image) { @@ -673,16 +707,16 @@ void gl_draw_scaled_image_inverted(S32 x, S32 y, S32 width, S32 height, LLImageG glBegin(GL_QUADS); { - glTexCoord2f(1.f, 0.f); + glTexCoord2f(uv_rect.mRight, uv_rect.mBottom); glVertex2i(width, height ); - glTexCoord2f(0.f, 0.f); + glTexCoord2f(uv_rect.mLeft, uv_rect.mBottom); glVertex2i(0, height ); - glTexCoord2f(0.f, 1.f); + glTexCoord2f(uv_rect.mLeft, uv_rect.mTop); glVertex2i(0, 0); - glTexCoord2f(1.f, 1.f); + glTexCoord2f(uv_rect.mRight, uv_rect.mTop); glVertex2i(width, 0); } glEnd(); @@ -1584,40 +1618,6 @@ void LLUI::loadIdentity() LLFontGL::sCurOrigin.mZ = 0; } -//static -void LLUI::setScissorRegionScreen(const LLRect& rect) -{ - stop_glerror(); - S32 x,y,w,h; - x = llround(rect.mLeft * LLUI::sGLScaleFactor.mV[VX]); - y = llround(rect.mBottom * LLUI::sGLScaleFactor.mV[VY]); - w = llround(rect.getWidth() * LLUI::sGLScaleFactor.mV[VX]); - h = llround(rect.getHeight() * LLUI::sGLScaleFactor.mV[VY]); - glScissor( x,y,w,h ); - stop_glerror(); -} - -//static -void LLUI::setScissorRegionLocal(const LLRect& rect) -{ - stop_glerror(); - S32 screen_left = LLFontGL::sCurOrigin.mX + rect.mLeft; - S32 screen_bottom = LLFontGL::sCurOrigin.mY + rect.mBottom; - - S32 x,y,w,h; - - x = llround((F32)screen_left * LLUI::sGLScaleFactor.mV[VX]); - y = llround((F32)screen_bottom * LLUI::sGLScaleFactor.mV[VY]); - w = llround((F32)rect.getWidth() * LLUI::sGLScaleFactor.mV[VX]); - h = llround((F32)rect.getHeight() * LLUI::sGLScaleFactor.mV[VY]); - - w = llmax(0,w); - h = llmax(0,h); - - glScissor(x,y,w,h); - stop_glerror(); -} - //static void LLUI::setScaleFactor(const LLVector2 &scale_factor) { @@ -1738,64 +1738,169 @@ LLUUID LLUI::findAssetUUIDByName(const LLString &asset_name) return LLUUID( foundValue ); } +//static +LLUIImage* LLUI::getUIImageByName(const LLString& name) +{ + return sImageProvider->getUIImageByID(findAssetUUIDByName(name)); +} + + // static void LLUI::setHtmlHelp(LLHtmlHelp* html_help) { LLUI::sHtmlHelp = html_help; } +LLScreenClipRect::LLScreenClipRect(const LLRect& rect, BOOL enabled) : mScissorState(GL_SCISSOR_TEST), mEnabled(enabled) +{ + if (mEnabled) + { + pushClipRect(rect); + } + mScissorState.setEnabled(!sClipRectStack.empty()); + updateScissorRegion(); +} + +LLScreenClipRect::~LLScreenClipRect() +{ + if (mEnabled) + { + popClipRect(); + } + updateScissorRegion(); +} + //static -void LLUI::pushClipRect(const LLRect& rect) +void LLScreenClipRect::pushClipRect(const LLRect& rect) { LLRect combined_clip_rect = rect; if (!sClipRectStack.empty()) { - combined_clip_rect.intersectWith(sClipRectStack.top()); + LLRect top = sClipRectStack.top(); + combined_clip_rect.intersectWith(top); } sClipRectStack.push(combined_clip_rect); - setScissorRegionScreen(combined_clip_rect); } //static -void LLUI::popClipRect() +void LLScreenClipRect::popClipRect() { sClipRectStack.pop(); - if (!sClipRectStack.empty()) - { - setScissorRegionScreen(sClipRectStack.top()); - } } -LLClipRect::LLClipRect(const LLRect& rect, BOOL enabled) : mScissorState(GL_SCISSOR_TEST, enabled), mEnabled(enabled) +//static +void LLScreenClipRect::updateScissorRegion() { - if (mEnabled) - { - LLUI::pushClipRect(rect); - } + if (sClipRectStack.empty()) return; + + LLRect rect = sClipRectStack.top(); + stop_glerror(); + S32 x,y,w,h; + x = llfloor(rect.mLeft * LLUI::sGLScaleFactor.mV[VX]); + y = llfloor(rect.mBottom * LLUI::sGLScaleFactor.mV[VY]); + w = llmax(0, llceil(rect.getWidth() * LLUI::sGLScaleFactor.mV[VX])) + 1; + h = llmax(0, llceil(rect.getHeight() * LLUI::sGLScaleFactor.mV[VY])) + 1; + glScissor( x,y,w,h ); + stop_glerror(); } -LLClipRect::~LLClipRect() + +LLLocalClipRect::LLLocalClipRect(const LLRect &rect, BOOL enabled) +: LLScreenClipRect(LLRect(rect.mLeft + LLFontGL::sCurOrigin.mX, + rect.mTop + LLFontGL::sCurOrigin.mY, + rect.mRight + LLFontGL::sCurOrigin.mX, + rect.mBottom + LLFontGL::sCurOrigin.mY), + enabled) { - if (mEnabled) - { - LLUI::popClipRect(); - } } -LLLocalClipRect::LLLocalClipRect(const LLRect &rect, BOOL enabled) : mScissorState(GL_SCISSOR_TEST, enabled), mEnabled(enabled) + +// +// LLUIImage +// + +LLUIImage::LLUIImage(LLPointer<LLImageGL> image) : + mImage(image), + mScaleRegion(0.f, 1.f, 1.f, 0.f), + mClipRegion(0.f, 1.f, 1.f, 0.f), + mUniformScaling(TRUE), + mNoClip(TRUE) +{ +} + +void LLUIImage::setClipRegion(const LLRectf& region) +{ + mClipRegion = region; + mNoClip = mClipRegion.mLeft == 0.f + && mClipRegion.mRight == 1.f + && mClipRegion.mBottom == 0.f + && mClipRegion.mTop == 1.f; +} + +void LLUIImage::setScaleRegion(const LLRectf& region) +{ + mScaleRegion = region; + mUniformScaling = mScaleRegion.mLeft == 0.f + && mScaleRegion.mRight == 1.f + && mScaleRegion.mBottom == 0.f + && mScaleRegion.mTop == 1.f; +} + +//TODO: move drawing implementation inside class +void LLUIImage::draw(S32 x, S32 y, const LLColor4& color) { - if (mEnabled) - { - LLRect scissor_rect = rect; - scissor_rect.translate(LLFontGL::sCurOrigin.mX, LLFontGL::sCurOrigin.mY); - LLUI::pushClipRect(scissor_rect); - } + gl_draw_image(x, y, mImage, color, mClipRegion); } -LLLocalClipRect::~LLLocalClipRect() +void LLUIImage::draw(S32 x, S32 y, S32 width, S32 height, const LLColor4& color) { - if (mEnabled) + if (mUniformScaling) { - LLUI::popClipRect(); + gl_draw_scaled_image(x, y, width, height, mImage, color, mClipRegion); } + else + { + gl_draw_scaled_image_with_border( + x, y, + width, height, + mImage, + color, + FALSE, + mClipRegion, + mScaleRegion); + } +} + +void LLUIImage::drawSolid(S32 x, S32 y, S32 width, S32 height, const LLColor4& color) +{ + gl_draw_scaled_image_with_border( + x, y, + width, height, + mImage, + color, + TRUE, + mClipRegion, + mScaleRegion); +} + +void LLUIImage::drawSolid(S32 x, S32 y, const LLColor4& color) +{ + gl_draw_scaled_image_with_border( + x, y, + getWidth(), getHeight(), + mImage, + color, + TRUE, + mClipRegion, + mScaleRegion); +} + +S32 LLUIImage::getWidth() +{ + return mImage->getWidth(0); +} + +S32 LLUIImage::getHeight() +{ + return mImage->getHeight(0); } diff --git a/indra/llui/llui.h b/indra/llui/llui.h index b98f4d5de2..05982aa9e2 100644 --- a/indra/llui/llui.h +++ b/indra/llui/llui.h @@ -41,14 +41,15 @@ #include "llhtmlhelp.h" #include "llgl.h" #include <stack> +#include "llimagegl.h" class LLColor4; class LLVector3; class LLVector2; -class LLImageGL; class LLUUID; class LLWindow; class LLView; +class LLUIImage; // UI colors extern const LLColor4 UI_VERTEX_COLOR; @@ -83,13 +84,14 @@ void gl_washer_2d(F32 outer_radius, F32 inner_radius, S32 steps, const LLColor4& void gl_washer_segment_2d(F32 outer_radius, F32 inner_radius, F32 start_radians, F32 end_radians, S32 steps, const LLColor4& inner_color, const LLColor4& outer_color); void gl_washer_spokes_2d(F32 outer_radius, F32 inner_radius, S32 count, const LLColor4& inner_color, const LLColor4& outer_color); -void gl_draw_image(S32 x, S32 y, LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR); -void gl_draw_scaled_image(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR); -void gl_draw_rotated_image(S32 x, S32 y, F32 degrees, LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR); -void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degrees,LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR); -void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 border_width, S32 border_height, S32 width, S32 height, LLImageGL* image, const LLColor4 &color, BOOL solid_color = FALSE); +void gl_draw_image(S32 x, S32 y, LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f)); +void gl_draw_scaled_image(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f)); +void gl_draw_rotated_image(S32 x, S32 y, F32 degrees, LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f)); +void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degrees,LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f)); +void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 border_width, S32 border_height, S32 width, S32 height, LLImageGL* image, const LLColor4 &color, BOOL solid_color = FALSE, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f)); +void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4 &color, BOOL solid_color = FALSE, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f), const LLRectf& scale_rect = LLRectf(0.f, 1.f, 1.f, 0.f)); // Flip vertical, used for LLFloaterHTML -void gl_draw_scaled_image_inverted(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR); +void gl_draw_scaled_image_inverted(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f)); void gl_rect_2d_xor(S32 left, S32 top, S32 right, S32 bottom); void gl_stippled_line_3d( const LLVector3& start, const LLVector3& end, const LLColor4& color, F32 phase = 0.f ); @@ -166,13 +168,12 @@ public: //helper functions (should probably move free standing rendering helper functions here) static LLString locateSkin(const LLString& filename); - static void pushClipRect(const LLRect& rect); - static void popClipRect(); static void setCursorPositionScreen(S32 x, S32 y); static void setCursorPositionLocal(LLView* viewp, S32 x, S32 y); static void setScaleFactor(const LLVector2& scale_factor); static void setLineWidth(F32 width); static LLUUID findAssetUUIDByName(const LLString& name); + static LLUIImage* getUIImageByName(const LLString& name); static LLVector2 getWindowSize(); static void screenPointToGL(S32 screen_x, S32 screen_y, S32 *gl_x, S32 *gl_y); static void glPointToScreen(S32 gl_x, S32 gl_y, S32 *screen_x, S32 *screen_y); @@ -180,10 +181,6 @@ public: static void glRectToScreen(const LLRect& gl, LLRect *screen); static void setHtmlHelp(LLHtmlHelp* html_help); -private: - static void setScissorRegionScreen(const LLRect& rect); - static void setScissorRegionLocal(const LLRect& rect); // works assuming LLUI::translate has been called - public: static LLControlGroup* sConfigGroup; static LLControlGroup* sColorsGroup; @@ -194,7 +191,6 @@ public: static LLWindow* sWindow; static BOOL sShowXUINames; static LLHtmlHelp* sHtmlHelp; - static std::stack<LLRect> sClipRectStack; }; @@ -286,6 +282,7 @@ typedef enum e_widget_type WIDGET_TYPE_MEMORY_VIEW, WIDGET_TYPE_FRAME_STAT_VIEW, WIDGET_TYPE_LAYOUT_STACK, + WIDGET_TYPE_FLYOUT_BUTTON, WIDGET_TYPE_DONTCARE, WIDGET_TYPE_COUNT } EWidgetType; @@ -382,24 +379,65 @@ protected: template <class T, class U> T* LLUISingleton<T,U>::sInstance = NULL; -class LLClipRect +class LLScreenClipRect { public: - LLClipRect(const LLRect& rect, BOOL enabled = TRUE); - virtual ~LLClipRect(); -protected: + LLScreenClipRect(const LLRect& rect, BOOL enabled = TRUE); + virtual ~LLScreenClipRect(); + +private: + static void pushClipRect(const LLRect& rect); + static void popClipRect(); + static void updateScissorRegion(); + +private: LLGLState mScissorState; BOOL mEnabled; + + static std::stack<LLRect> sClipRectStack; }; -class LLLocalClipRect +class LLLocalClipRect : public LLScreenClipRect { public: LLLocalClipRect(const LLRect& rect, BOOL enabled = TRUE); - virtual ~LLLocalClipRect(); +}; + +class LLUIImage : public LLRefCount +{ +public: + LLUIImage(LLPointer<LLImageGL> image); + + void setClipRegion(const LLRectf& region); + void setScaleRegion(const LLRectf& region); + + LLPointer<LLImageGL> getImage() { return mImage; } + + void draw(S32 x, S32 y, const LLColor4& color = UI_VERTEX_COLOR); + void draw(S32 x, S32 y, S32 width, S32 height, const LLColor4& color = UI_VERTEX_COLOR); + void drawSolid(S32 x, S32 y, S32 width, S32 height, const LLColor4& color); + void drawSolid(S32 x, S32 y, const LLColor4& color); + + S32 getWidth(); + S32 getHeight(); + protected: - LLGLState mScissorState; - BOOL mEnabled; + LLRectf mScaleRegion; + LLRectf mClipRegion; + LLPointer<LLImageGL> mImage; + BOOL mUniformScaling; + BOOL mNoClip; +}; + +//RN: maybe this needs to moved elsewhere? +class LLImageProviderInterface +{ +public: + LLImageProviderInterface() {}; + virtual ~LLImageProviderInterface() {}; + + virtual LLUIImage* getUIImageByID(const LLUUID& id, BOOL clamped = TRUE) = 0; + virtual LLImageGL* getImageByID(const LLUUID& id, BOOL clamped = TRUE) = 0; }; #endif diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index 7d354753d3..8645f50764 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -47,11 +47,53 @@ const U32 MAX_STRING_LENGTH = 10; -LLUICtrl::LLUICtrl() : - mCommitCallback(NULL), - mFocusLostCallback(NULL), +LLFocusableElement::LLFocusableElement() +: mFocusLostCallback(NULL), mFocusReceivedCallback(NULL), mFocusChangedCallback(NULL), + mFocusCallbackUserData(NULL) +{ +} + +void LLFocusableElement::onFocusReceived() +{ + if( mFocusReceivedCallback ) + { + mFocusReceivedCallback( this, mFocusCallbackUserData ); + } + if( mFocusChangedCallback ) + { + mFocusChangedCallback( this, mFocusCallbackUserData ); + } +} + +void LLFocusableElement::onFocusLost() +{ + if( mFocusLostCallback ) + { + mFocusLostCallback( this, mFocusCallbackUserData ); + } + + if( mFocusChangedCallback ) + { + mFocusChangedCallback( this, mFocusCallbackUserData ); + } +} + +BOOL LLFocusableElement::hasFocus() const +{ + return FALSE; +} + +void LLFocusableElement::setFocus(BOOL b) +{ +} + + + +LLUICtrl::LLUICtrl() : + mCommitCallback(NULL), + mLostTopCallback(NULL), mValidateCallback(NULL), mCallbackUserData(NULL), mTentative(FALSE), @@ -68,9 +110,7 @@ LLUICtrl::LLUICtrl(const LLString& name, const LLRect& rect, BOOL mouse_opaque, // of buttons in the UI. JC 7/20/2002 LLView( name, rect, mouse_opaque, reshape ), mCommitCallback( on_commit_callback) , - mFocusLostCallback( NULL ), - mFocusReceivedCallback( NULL ), - mFocusChangedCallback( NULL ), + mLostTopCallback( NULL ), mValidateCallback( NULL ), mCallbackUserData( callback_userdata ), mTentative( FALSE ), @@ -128,6 +168,86 @@ LLCtrlScrollInterface* LLUICtrl::getScrollInterface() return NULL; } +BOOL LLUICtrl::hasFocus() const +{ + return (gFocusMgr.childHasKeyboardFocus(this)); +} + +void LLUICtrl::setFocus(BOOL b) +{ + // focus NEVER goes to ui ctrls that are disabled! + if (!mEnabled) + { + return; + } + if( b ) + { + if (!hasFocus()) + { + gFocusMgr.setKeyboardFocus( this ); + } + } + else + { + if( gFocusMgr.childHasKeyboardFocus(this)) + { + gFocusMgr.setKeyboardFocus( NULL ); + } + } +} + +void LLUICtrl::onFocusReceived() +{ + // trigger callbacks + LLFocusableElement::onFocusReceived(); + + // find first view in hierarchy above new focus that is a LLUICtrl + LLView* viewp = getParent(); + LLUICtrl* last_focus = gFocusMgr.getLastKeyboardFocus(); + + while (viewp && !viewp->isCtrl()) + { + viewp = viewp->getParent(); + } + + // and if it has newly gained focus, call onFocusReceived() + LLUICtrl* ctrlp = static_cast<LLUICtrl*>(viewp); + if (ctrlp && (!last_focus || !last_focus->hasAncestor(ctrlp))) + { + ctrlp->onFocusReceived(); + } +} + +void LLUICtrl::onFocusLost() +{ + // trigger callbacks + LLFocusableElement::onFocusLost(); + + // find first view in hierarchy above old focus that is a LLUICtrl + LLView* viewp = getParent(); + while (viewp && !viewp->isCtrl()) + { + viewp = viewp->getParent(); + } + + // and if it has just lost focus, call onFocusReceived() + LLUICtrl* ctrlp = static_cast<LLUICtrl*>(viewp); + // hasFocus() includes any descendants + if (ctrlp && !ctrlp->hasFocus()) + { + ctrlp->onFocusLost(); + } +} + +void LLUICtrl::onLostTop() +{ + if (mLostTopCallback) + { + mLostTopCallback(this, mCallbackUserData); + } +} + + // virtual void LLUICtrl::setTabStop( BOOL b ) { @@ -168,67 +288,6 @@ BOOL LLUICtrl::getIsChrome() const return mIsChrome; } -void LLUICtrl::onFocusReceived() -{ - if( mFocusReceivedCallback ) - { - mFocusReceivedCallback( this, mCallbackUserData ); - } - if( mFocusChangedCallback ) - { - mFocusChangedCallback( this, mCallbackUserData ); - } -} - -void LLUICtrl::onFocusLost() -{ - if( mFocusLostCallback ) - { - mFocusLostCallback( this, mCallbackUserData ); - } - - if( mFocusChangedCallback ) - { - mFocusChangedCallback( this, mCallbackUserData ); - } -} - -BOOL LLUICtrl::hasFocus() const -{ - return (gFocusMgr.childHasKeyboardFocus(this)); -} - -void LLUICtrl::setFocus(BOOL b) -{ - // focus NEVER goes to ui ctrls that are disabled! - if (!mEnabled) - { - return; - } - if( b ) - { - if (!hasFocus()) - { - gFocusMgr.setKeyboardFocus( this, &LLUICtrl::onFocusLostCallback ); - onFocusReceived(); - } - } - else - { - if( gFocusMgr.childHasKeyboardFocus(this)) - { - gFocusMgr.setKeyboardFocus( NULL, NULL ); - onFocusLost(); - } - } -} - -// static -void LLUICtrl::onFocusLostCallback( LLUICtrl* old_focus ) -{ - old_focus->onFocusLost(); -} - // this comparator uses the crazy disambiguating logic of LLCompareByTabOrder, // but to switch up the order so that children that have the default tab group come first // and those that are prior to the default tab group come last @@ -262,6 +321,7 @@ public: } }; + BOOL LLUICtrl::focusFirstItem(BOOL prefer_text_fields) { // try to select default tab group child diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h index 00f78748a7..ae360f401f 100644 --- a/indra/llui/lluictrl.h +++ b/indra/llui/lluictrl.h @@ -53,8 +53,31 @@ class LLCtrlScrollInterface; typedef void (*LLUICtrlCallback)(LLUICtrl* ctrl, void* userdata); typedef BOOL (*LLUICtrlValidate)(LLUICtrl* ctrl, void* userdata); +class LLFocusableElement +{ + friend class LLFocusMgr; // allow access to focus change handlers +public: + LLFocusableElement(); + virtual ~LLFocusableElement() {}; + + virtual void setFocus( BOOL b ); + virtual BOOL hasFocus() const; + + void setFocusLostCallback(void (*cb)(LLFocusableElement* caller, void*), void* user_data = NULL) { mFocusLostCallback = cb; mFocusCallbackUserData = user_data; } + void setFocusReceivedCallback( void (*cb)(LLFocusableElement*, void*), void* user_data = NULL) { mFocusReceivedCallback = cb; mFocusCallbackUserData = user_data; } + void setFocusChangedCallback( void (*cb)(LLFocusableElement*, void*), void* user_data = NULL ) { mFocusChangedCallback = cb; mFocusCallbackUserData = user_data; } + +protected: + virtual void onFocusReceived(); + virtual void onFocusLost(); + void (*mFocusLostCallback)( LLFocusableElement* caller, void* userdata ); + void (*mFocusReceivedCallback)( LLFocusableElement* ctrl, void* userdata ); + void (*mFocusChangedCallback)( LLFocusableElement* ctrl, void* userdata ); + void* mFocusCallbackUserData; +}; + class LLUICtrl -: public LLView +: public LLView, public LLFocusableElement { public: LLUICtrl(); @@ -85,6 +108,11 @@ public: virtual void setFocus( BOOL b ); virtual BOOL hasFocus() const; + virtual void onFocusReceived(); + virtual void onFocusLost(); + + virtual void onLostTop(); // called when registered as top ctrl and user clicks elsewhere + virtual void setTabStop( BOOL b ); virtual BOOL hasTabStop() const; @@ -115,6 +143,7 @@ public: void setCommitCallback( void (*cb)(LLUICtrl*, void*) ) { mCommitCallback = cb; } void setValidateBeforeCommit( BOOL(*cb)(LLUICtrl*, void*) ) { mValidateCallback = cb; } + void setLostTopCallback( void (*cb)(LLUICtrl*, void*) ) { mLostTopCallback = cb; } // Defaults to no-op! virtual void setDoubleClickCallback( void (*cb)(void*) ); @@ -126,23 +155,8 @@ public: virtual void setMinValue(LLSD min_value); virtual void setMaxValue(LLSD max_value); - // In general, only LLPanel uses these. - void setFocusLostCallback(void (*cb)(LLUICtrl* caller, void* user_data)) { mFocusLostCallback = cb; } - void setFocusReceivedCallback( void (*cb)(LLUICtrl*, void*) ) { mFocusReceivedCallback = cb; } - void setFocusChangedCallback( void (*cb)(LLUICtrl*, void*) ) { mFocusChangedCallback = cb; } - - static void onFocusLostCallback(LLUICtrl* old_focus); - /*virtual*/ BOOL focusFirstItem(BOOL prefer_text_fields = FALSE ); - class LLTabStopPostFilter : public LLQueryFilter, public LLSingleton<LLTabStopPostFilter> - { - /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const - { - return filterResult_t(view->isCtrl() && static_cast<const LLUICtrl *>(view)->hasTabStop() && children.size() == 0, TRUE); - } - }; - class LLTextInputFilter : public LLQueryFilter, public LLSingleton<LLTextInputFilter> { /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const @@ -157,16 +171,9 @@ public: virtual void resetDirty() {}; protected: - virtual void onFocusReceived(); - virtual void onFocusLost(); - void onChangeFocus( S32 direction ); - -protected: void (*mCommitCallback)( LLUICtrl* ctrl, void* userdata ); - void (*mFocusLostCallback)( LLUICtrl* caller, void* userdata ); - void (*mFocusReceivedCallback)( LLUICtrl* ctrl, void* userdata ); - void (*mFocusChangedCallback)( LLUICtrl* ctrl, void* userdata ); + void (*mLostTopCallback)( LLUICtrl* ctrl, void* userdata ); BOOL (*mValidateCallback)( LLUICtrl* ctrl, void* userdata ); void* mCallbackUserData; diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp index 88e4e89a2b..1e8798e7f7 100644 --- a/indra/llui/lluictrlfactory.cpp +++ b/indra/llui/lluictrlfactory.cpp @@ -173,7 +173,7 @@ std::vector<LLString> LLUICtrlFactory::mXUIPaths; class LLUICtrlLocate : public LLUICtrl { public: - LLUICtrlLocate() : LLUICtrl("locate", LLRect(0,0,0,0), FALSE, NULL, NULL) {} + LLUICtrlLocate() : LLUICtrl("locate", LLRect(0,0,0,0), FALSE, NULL, NULL) { setTabStop(FALSE); } virtual void draw() { } virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_LOCATE; } @@ -181,7 +181,11 @@ public: static LLView *fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { + LLString name("pad"); + node->getAttributeString("name", name); + LLUICtrlLocate *new_ctrl = new LLUICtrlLocate(); + new_ctrl->setName(name); new_ctrl->initFromXML(node, parent); return new_ctrl; } @@ -196,6 +200,7 @@ LLUICtrlFactory::LLUICtrlFactory() LLUICtrlCreator<LLButton>::registerCreator(LL_BUTTON_TAG, this); LLUICtrlCreator<LLCheckBoxCtrl>::registerCreator(LL_CHECK_BOX_CTRL_TAG, this); LLUICtrlCreator<LLComboBox>::registerCreator(LL_COMBO_BOX_TAG, this); + LLUICtrlCreator<LLFlyoutButton>::registerCreator(LL_FLYOUT_BUTTON_TAG, this); LLUICtrlCreator<LLLineEditor>::registerCreator(LL_LINE_EDITOR_TAG, this); LLUICtrlCreator<LLSearchEditor>::registerCreator(LL_SEARCH_EDITOR_TAG, this); LLUICtrlCreator<LLScrollListCtrl>::registerCreator(LL_SCROLL_LIST_CTRL_TAG, this); diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index a047f9912e..370288e949 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -113,7 +113,7 @@ LLView::LLView() : mSaveToXML(TRUE), mIsFocusRoot(FALSE), mLastVisible(TRUE), - mSpanChildren(FALSE), + mUseBoundingRect(FALSE), mVisible(TRUE), mHidden(FALSE), mNextInsertionOrdinal(0) @@ -133,7 +133,7 @@ LLView::LLView(const LLString& name, BOOL mouse_opaque) : mSaveToXML(TRUE), mIsFocusRoot(FALSE), mLastVisible(TRUE), - mSpanChildren(FALSE), + mUseBoundingRect(FALSE), mVisible(TRUE), mHidden(FALSE), mNextInsertionOrdinal(0) @@ -148,6 +148,7 @@ LLView::LLView( mParentView(NULL), mName(name), mRect(rect), + mBoundingRect(rect), mReshapeFlags(reshape), mDefaultTabGroup(0), mEnabled(TRUE), @@ -156,7 +157,7 @@ LLView::LLView( mSaveToXML(TRUE), mIsFocusRoot(FALSE), mLastVisible(TRUE), - mSpanChildren(FALSE), + mUseBoundingRect(FALSE), mVisible(TRUE), mHidden(FALSE), mNextInsertionOrdinal(0) @@ -235,10 +236,16 @@ BOOL LLView::setToolTipArg(const LLStringExplicit& key, const LLStringExplicit& return TRUE; } +void LLView::setToolTipArgs( const LLString::format_map_t& args ) +{ + mToolTipMsg.setArgList(args); +} + // virtual void LLView::setRect(const LLRect& rect) { mRect = rect; + updateBoundingRect(); } @@ -287,9 +294,18 @@ void LLView::setName(LLString name) mName = name; } -void LLView::setSpanChildren( BOOL span_children ) +void LLView::setUseBoundingRect( BOOL use_bounding_rect ) +{ + if (mUseBoundingRect != use_bounding_rect) + { + mUseBoundingRect = use_bounding_rect; + updateBoundingRect(); + } +} + +BOOL LLView::getUseBoundingRect() { - mSpanChildren = span_children; updateRect(); + return mUseBoundingRect; } const LLString& LLView::getToolTip() @@ -306,7 +322,7 @@ const LLString& LLView::getName() const void LLView::sendChildToFront(LLView* child) { - if (child->mParentView == this) + if (child && child->getParent() == this) { mChildList.remove( child ); mChildList.push_front(child); @@ -315,7 +331,7 @@ void LLView::sendChildToFront(LLView* child) void LLView::sendChildToBack(LLView* child) { - if (child->mParentView == this) + if (child && child->getParent() == this) { mChildList.remove( child ); mChildList.push_back(child); @@ -330,6 +346,14 @@ void LLView::moveChildToFrontOfTabGroup(LLUICtrl* child) } } +void LLView::moveChildToBackOfTabGroup(LLUICtrl* child) +{ + if(mCtrlOrder.find(child) != mCtrlOrder.end()) + { + mCtrlOrder[child].second = mNextInsertionOrdinal++; + } +} + void LLView::addChild(LLView* child, S32 tab_group) { if (mParentView == child) @@ -353,7 +377,7 @@ void LLView::addChild(LLView* child, S32 tab_group) } child->mParentView = this; - updateRect(); + updateBoundingRect(); } @@ -380,7 +404,7 @@ void LLView::addChildAtEnd(LLView* child, S32 tab_group) } child->mParentView = this; - updateRect(); + updateBoundingRect(); } // remove the specified child from the view, and set it's parent to NULL. @@ -403,6 +427,7 @@ void LLView::removeChild(LLView* child, BOOL deleteIt) { llerrs << "LLView::removeChild called with non-child" << llendl; } + updateBoundingRect(); } void LLView::addCtrlAtEnd(LLUICtrl* ctrl, S32 tab_group) @@ -782,6 +807,7 @@ void LLView::setVisible(BOOL visible) // tell all children of this view that the visibility may have changed onVisibilityChange( visible ); } + updateBoundingRect(); } } @@ -815,6 +841,7 @@ void LLView::onVisibilityChange ( BOOL new_visibility ) void LLView::translate(S32 x, S32 y) { mRect.translate(x, y); + updateBoundingRect(); } // virtual @@ -831,7 +858,8 @@ void LLView::snappedTo(LLView* snap_view) BOOL LLView::handleHover(S32 x, S32 y, MASK mask) { BOOL handled = childrenHandleHover( x, y, mask ) != NULL; - if( !handled && mMouseOpaque && pointInView( x, y ) ) + if( !handled + && blockMouseEvent(x, y) ) { LLUI::sWindow->setCursor(UI_CURSOR_ARROW); lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl; @@ -876,45 +904,46 @@ BOOL LLView::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_scre LLString tool_tip; - if ( getVisible() && getEnabled()) + for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) { - for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) + LLView* viewp = *child_it; + S32 local_x = x - viewp->mRect.mLeft; + S32 local_y = y - viewp->mRect.mBottom; + if( viewp->pointInView(local_x, local_y) + && viewp->getVisible() + && viewp->getEnabled() + && viewp->handleToolTip(local_x, local_y, msg, sticky_rect_screen )) { - LLView* viewp = *child_it; - S32 local_x = x - viewp->mRect.mLeft; - S32 local_y = y - viewp->mRect.mBottom; - if( viewp->handleToolTip(local_x, local_y, msg, sticky_rect_screen ) ) - { - handled = TRUE; - break; - } + handled = TRUE; + break; } + } - tool_tip = mToolTipMsg.getString(); - if (LLUI::sShowXUINames && (tool_tip.find(".xml", 0) == LLString::npos) && - (mName.find("Drag", 0) == LLString::npos)) - { - tool_tip = getShowNamesToolTip(); - } - + tool_tip = mToolTipMsg.getString(); + if ( + LLUI::sShowXUINames && + (tool_tip.find(".xml", 0) == LLString::npos) && + (mName.find("Drag", 0) == LLString::npos)) + { + tool_tip = getShowNamesToolTip(); + } - BOOL showNamesTextBox = LLUI::sShowXUINames && (getWidgetType() == WIDGET_TYPE_TEXT_BOX); + BOOL showNamesTextBox = LLUI::sShowXUINames && (getWidgetType() == WIDGET_TYPE_TEXT_BOX); - if( !handled && (mMouseOpaque || showNamesTextBox) && pointInView( x, y ) && !tool_tip.empty()) - { + if( !handled && (blockMouseEvent(x, y) || showNamesTextBox) && !tool_tip.empty()) + { - msg = tool_tip; + msg = tool_tip; - // Convert rect local to screen coordinates - localPointToScreen( - 0, 0, - &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); - localPointToScreen( - mRect.getWidth(), mRect.getHeight(), - &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) ); - - handled = TRUE; - } + // Convert rect local to screen coordinates + localPointToScreen( + 0, 0, + &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); + localPointToScreen( + mRect.getWidth(), mRect.getHeight(), + &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) ); + + handled = TRUE; } return handled; @@ -1025,7 +1054,7 @@ BOOL LLView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, cargo_data, accept, tooltip_msg) != NULL; - if( !handled && mMouseOpaque ) + if( !handled && blockMouseEvent(x, y) ) { *accept = ACCEPT_NO; handled = TRUE; @@ -1081,7 +1110,7 @@ BOOL LLView::hasMouseCapture() BOOL LLView::handleMouseUp(S32 x, S32 y, MASK mask) { BOOL handled = childrenHandleMouseUp( x, y, mask ) != NULL; - if( !handled && mMouseOpaque ) + if( !handled && blockMouseEvent(x, y) ) { handled = TRUE; } @@ -1092,7 +1121,7 @@ BOOL LLView::handleMouseDown(S32 x, S32 y, MASK mask) { LLView* handled_view = childrenHandleMouseDown( x, y, mask ); BOOL handled = (handled_view != NULL); - if( !handled && mMouseOpaque ) + if( !handled && blockMouseEvent(x, y) ) { handled = TRUE; handled_view = this; @@ -1118,7 +1147,7 @@ BOOL LLView::handleMouseDown(S32 x, S32 y, MASK mask) BOOL LLView::handleDoubleClick(S32 x, S32 y, MASK mask) { BOOL handled = childrenHandleDoubleClick( x, y, mask ) != NULL; - if( !handled && mMouseOpaque ) + if( !handled && blockMouseEvent(x, y) ) { handleMouseDown(x, y, mask); handled = TRUE; @@ -1132,7 +1161,7 @@ BOOL LLView::handleScrollWheel(S32 x, S32 y, S32 clicks) if( getVisible() && mEnabled ) { handled = childrenHandleScrollWheel( x, y, clicks ) != NULL; - if( !handled && mMouseOpaque ) + if( !handled && blockMouseEvent(x, y) ) { handled = TRUE; } @@ -1143,7 +1172,7 @@ BOOL LLView::handleScrollWheel(S32 x, S32 y, S32 clicks) BOOL LLView::handleRightMouseDown(S32 x, S32 y, MASK mask) { BOOL handled = childrenHandleRightMouseDown( x, y, mask ) != NULL; - if( !handled && mMouseOpaque ) + if( !handled && blockMouseEvent(x, y) ) { handled = TRUE; } @@ -1153,7 +1182,7 @@ BOOL LLView::handleRightMouseDown(S32 x, S32 y, MASK mask) BOOL LLView::handleRightMouseUp(S32 x, S32 y, MASK mask) { BOOL handled = childrenHandleRightMouseUp( x, y, mask ) != NULL; - if( !handled && mMouseOpaque ) + if( !handled && blockMouseEvent(x, y) ) { handled = TRUE; } @@ -1428,10 +1457,10 @@ void LLView::draw() focus_view = NULL; } + ++sDepth; for (child_list_reverse_iter_t child_iter = mChildList.rbegin(); child_iter != mChildList.rend(); ++child_iter) { LLView *viewp = *child_iter; - ++sDepth; if (viewp->getVisible() && viewp != focus_view) { @@ -1449,8 +1478,8 @@ void LLView::draw() } } - --sDepth; } + --sDepth; if (focus_view && focus_view->getVisible()) { @@ -1467,50 +1496,61 @@ void LLView::draw() //Draw a box for debugging. void LLView::drawDebugRect() { - // drawing solids requires texturing be disabled - LLGLSNoTexture no_texture; - - // draw red rectangle for the border - LLColor4 border_color(0.f, 0.f, 0.f, 1.f); - if (sEditingUI) + LLUI::pushMatrix(); { - border_color.mV[0] = 1.f; - } - else - { - border_color.mV[sDepth%3] = 1.f; - } + // drawing solids requires texturing be disabled + LLGLSNoTexture no_texture; - glColor4fv( border_color.mV ); + if (mUseBoundingRect) + { + LLUI::translate((F32)mBoundingRect.mLeft - (F32)mRect.mLeft, (F32)mBoundingRect.mBottom - (F32)mRect.mBottom, 0.f); + } - glBegin(GL_LINES); - glVertex2i(0, mRect.getHeight() - 1); - glVertex2i(0, 0); + LLRect debug_rect = mUseBoundingRect ? mBoundingRect : mRect; - glVertex2i(0, 0); - glVertex2i(mRect.getWidth() - 1, 0); + // draw red rectangle for the border + LLColor4 border_color(0.f, 0.f, 0.f, 1.f); + if (sEditingUI) + { + border_color.mV[0] = 1.f; + } + else + { + border_color.mV[sDepth%3] = 1.f; + } - glVertex2i(mRect.getWidth() - 1, 0); - glVertex2i(mRect.getWidth() - 1, mRect.getHeight() - 1); + glColor4fv( border_color.mV ); - glVertex2i(mRect.getWidth() - 1, mRect.getHeight() - 1); - glVertex2i(0, mRect.getHeight() - 1); - glEnd(); + glBegin(GL_LINES); + glVertex2i(0, debug_rect.getHeight() - 1); + glVertex2i(0, 0); - // Draw the name if it's not a leaf node - if (mChildList.size() && !sEditingUI) - { - //char temp[256]; - S32 x, y; - glColor4fv( border_color.mV ); - x = mRect.getWidth()/2; - y = mRect.getHeight()/2; - LLString debug_text = llformat("%s (%d x %d)", getName().c_str(), - mRect.getWidth(), mRect.getHeight()); - LLFontGL::sSansSerifSmall->renderUTF8(debug_text, 0, (F32)x, (F32)y, border_color, - LLFontGL::HCENTER, LLFontGL::BASELINE, LLFontGL::NORMAL, - S32_MAX, S32_MAX, NULL, FALSE); + glVertex2i(0, 0); + glVertex2i(debug_rect.getWidth() - 1, 0); + + glVertex2i(debug_rect.getWidth() - 1, 0); + glVertex2i(debug_rect.getWidth() - 1, debug_rect.getHeight() - 1); + + glVertex2i(debug_rect.getWidth() - 1, debug_rect.getHeight() - 1); + glVertex2i(0, debug_rect.getHeight() - 1); + glEnd(); + + // Draw the name if it's not a leaf node + if (mChildList.size() && !sEditingUI) + { + //char temp[256]; + S32 x, y; + glColor4fv( border_color.mV ); + x = debug_rect.getWidth()/2; + y = debug_rect.getHeight()/2; + LLString debug_text = llformat("%s (%d x %d)", getName().c_str(), + debug_rect.getWidth(), debug_rect.getHeight()); + LLFontGL::sSansSerifSmall->renderUTF8(debug_text, 0, (F32)x, (F32)y, border_color, + LLFontGL::HCENTER, LLFontGL::BASELINE, LLFontGL::NORMAL, + S32_MAX, S32_MAX, NULL, FALSE); + } } + LLUI::popMatrix(); } void LLView::drawChild(LLView* childp, S32 x_offset, S32 y_offset, BOOL force_draw) @@ -1537,9 +1577,6 @@ void LLView::drawChild(LLView* childp, S32 x_offset, S32 y_offset, BOOL force_dr void LLView::reshape(S32 width, S32 height, BOOL called_from_parent) { - // make sure this view contains all its children - updateRect(); - // compute how much things changed and apply reshape logic to children S32 delta_width = width - mRect.getWidth(); S32 delta_height = height - mRect.getHeight(); @@ -1608,6 +1645,8 @@ void LLView::reshape(S32 width, S32 height, BOOL called_from_parent) mParentView->reshape(mParentView->getRect().getWidth(), mParentView->getRect().getHeight(), FALSE); } } + + updateBoundingRect(); } LLRect LLView::getRequiredRect() @@ -1615,6 +1654,53 @@ LLRect LLView::getRequiredRect() return mRect; } +void LLView::updateBoundingRect() +{ + if (isDead()) return; + + if (mUseBoundingRect) + { + LLRect local_bounding_rect = LLRect::null; + + child_list_const_iter_t child_it; + for ( child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) + { + LLView* childp = *child_it; + if (!childp->getVisible()) continue; + + LLRect child_bounding_rect = childp->getBoundingRect(); + + if (local_bounding_rect.isNull()) + { + // start out with bounding rect equal to first visible child's bounding rect + local_bounding_rect = child_bounding_rect; + } + else + { + // accumulate non-null children rectangles + if (!child_bounding_rect.isNull()) + { + local_bounding_rect.unionWith(child_bounding_rect); + } + } + } + + mBoundingRect = local_bounding_rect; + // translate into parent-relative coordinates + mBoundingRect.translate(mRect.mLeft, mRect.mBottom); + } + else + { + mBoundingRect = mRect; + } + + // give parent view a chance to resize, in case we just moved, for example + if (getParent() && getParent()->mUseBoundingRect) + { + getParent()->updateBoundingRect(); + } +} + const LLRect LLView::getScreenRect() const { // *FIX: check for one-off error @@ -1624,6 +1710,15 @@ const LLRect LLView::getScreenRect() const return screen_rect; } +const LLRect LLView::getLocalBoundingRect() const +{ + LLRect local_bounding_rect = getBoundingRect(); + local_bounding_rect.translate(-mRect.mLeft, -mRect.mBottom); + + return local_bounding_rect; +} + + const LLRect LLView::getLocalRect() const { LLRect local_rect(0, mRect.getHeight(), mRect.getWidth(), 0); @@ -1637,38 +1732,7 @@ const LLRect LLView::getLocalSnapRect() const return local_snap_rect; } -void LLView::updateRect() -{ - if (mSpanChildren && mChildList.size()) - { - LLView* first_child = (*mChildList.begin()); - LLRect child_spanning_rect = first_child->mRect; - - for ( child_list_iter_t child_it = ++mChildList.begin(); child_it != mChildList.end(); ++child_it) - { - LLView* viewp = *child_it; - if (viewp->getVisible()) - { - child_spanning_rect.unionWith(viewp->mRect); - } - } - - S32 translate_x = llmin(0, child_spanning_rect.mLeft); - S32 translate_y = llmin(0, child_spanning_rect.mBottom); - S32 new_width = llmax(mRect.getWidth() + translate_x, child_spanning_rect.getWidth()); - S32 new_height = llmax(mRect.getHeight() + translate_y, child_spanning_rect.getHeight()); - - mRect.setOriginAndSize(mRect.mLeft + translate_x, mRect.mBottom + translate_y, new_width, new_height); - - for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) - { - LLView* viewp = *child_it; - viewp->mRect.translate(-translate_x, -translate_y); - } - } -} - -BOOL LLView::hasAncestor(LLView* parentp) +BOOL LLView::hasAncestor(const LLView* parentp) { if (!parentp) { @@ -1743,14 +1807,23 @@ LLView* LLView::getChildByName(const LLString& name, BOOL recurse) const return NULL; } -// virtual -void LLView::onFocusLost() -{ +BOOL LLView::parentPointInView(S32 x, S32 y, EHitTestType type) const +{ + return (mUseBoundingRect && type == HIT_TEST_USE_BOUNDING_RECT) + ? mBoundingRect.pointInRect( x, y ) + : mRect.pointInRect( x, y ); } -// virtual -void LLView::onFocusReceived() +BOOL LLView::pointInView(S32 x, S32 y, EHitTestType type) const +{ + return (mUseBoundingRect && type == HIT_TEST_USE_BOUNDING_RECT) + ? mBoundingRect.pointInRect( x + mRect.mLeft, y + mRect.mBottom ) + : mRect.localPointInRect( x, y ); +} + +BOOL LLView::blockMouseEvent(S32 x, S32 y) const { + return mMouseOpaque && pointInView(x, y, HIT_TEST_IGNORE_BOUNDING_RECT); } // virtual @@ -2024,9 +2097,9 @@ LLXMLNodePtr LLView::getXML(bool save_children) const // Export all widgets as enabled and visible - code must disable. node->createChild("hidden", TRUE)->setBoolValue(mHidden); node->createChild("mouse_opaque", TRUE)->setBoolValue(mMouseOpaque ); - if (!mToolTipMsg.empty()) + if (!mToolTipMsg.getString().empty()) { - node->createChild("tool_tip", TRUE)->setStringValue(mToolTipMsg); + node->createChild("tool_tip", TRUE)->setStringValue(mToolTipMsg.getString()); } if (mSoundFlags != MOUSE_UP) { @@ -2116,7 +2189,7 @@ const LLCtrlQuery & LLView::getTabOrderQuery() query.addPreFilter(LLVisibleFilter::getInstance()); query.addPreFilter(LLEnabledFilter::getInstance()); query.addPreFilter(LLTabStopFilter::getInstance()); - query.addPostFilter(LLUICtrl::LLTabStopPostFilter::getInstance()); + query.addPostFilter(LLLeavesFilter::getInstance()); } return query; } @@ -2129,6 +2202,7 @@ const LLCtrlQuery & LLView::getFocusRootsQuery() query.addPreFilter(LLVisibleFilter::getInstance()); query.addPreFilter(LLEnabledFilter::getInstance()); query.addPreFilter(LLView::LLFocusRootsFilter::getInstance()); + query.addPostFilter(LLRootsFilter::getInstance()); } return query; } @@ -2593,10 +2667,10 @@ const S32 VPAD = 4; U32 LLView::createRect(LLXMLNodePtr node, LLRect &rect, LLView* parent_view, const LLRect &required_rect) { U32 follows = 0; - S32 x = FLOATER_H_MARGIN; - S32 y = 0; - S32 w = 0; - S32 h = 0; + S32 x = rect.mLeft; + S32 y = rect.mBottom; + S32 w = rect.getWidth(); + S32 h = rect.getHeight(); U32 last_x = 0; U32 last_y = 0; @@ -2639,8 +2713,15 @@ U32 LLView::createRect(LLXMLNodePtr node, LLRect &rect, LLView* parent_view, con // view if you don't specify a width. if (parent_view) { - w = llmax(required_rect.getWidth(), parent_view->getRect().getWidth() - (FLOATER_H_MARGIN) - x); - h = llmax(MIN_WIDGET_HEIGHT, required_rect.getHeight()); + if(w == 0) + { + w = llmax(required_rect.getWidth(), parent_view->getRect().getWidth() - (FLOATER_H_MARGIN) - x); + } + + if(h == 0) + { + h = llmax(MIN_WIDGET_HEIGHT, required_rect.getHeight()); + } } if (node->hasAttribute("width")) @@ -2765,44 +2846,7 @@ void LLView::initFromXML(LLXMLNodePtr node, LLView* parent) setRect(view_rect); setFollows(follows_flags); - if (node->hasAttribute("follows")) - { - setFollowsNone(); - - LLString follows; - node->getAttributeString("follows", follows); - - typedef boost::tokenizer<boost::char_separator<char> > tokenizer; - boost::char_separator<char> sep("|"); - tokenizer tokens(follows, sep); - tokenizer::iterator token_iter = tokens.begin(); - - while(token_iter != tokens.end()) - { - const std::string& token_str = *token_iter; - if (token_str == "left") - { - setFollowsLeft(); - } - else if (token_str == "right") - { - setFollowsRight(); - } - else if (token_str == "top") - { - setFollowsTop(); - } - else if (token_str == "bottom") - { - setFollowsBottom(); - } - else if (token_str == "all") - { - setFollowsAll(); - } - ++token_iter; - } - } + parseFollowsFlags(node); if (node->hasAttribute("control_name")) { @@ -2839,11 +2883,57 @@ void LLView::initFromXML(LLXMLNodePtr node, LLView* parent) setHidden(hidden); } + node->getAttributeBOOL("use_bounding_rect", mUseBoundingRect); + node->getAttributeBOOL("mouse_opaque", mMouseOpaque); + node->getAttributeS32("default_tab_group", mDefaultTabGroup); reshape(view_rect.getWidth(), view_rect.getHeight()); } +void LLView::parseFollowsFlags(LLXMLNodePtr node) +{ + if (node->hasAttribute("follows")) + { + setFollowsNone(); + + LLString follows; + node->getAttributeString("follows", follows); + + typedef boost::tokenizer<boost::char_separator<char> > tokenizer; + boost::char_separator<char> sep("|"); + tokenizer tokens(follows, sep); + tokenizer::iterator token_iter = tokens.begin(); + + while(token_iter != tokens.end()) + { + const std::string& token_str = *token_iter; + if (token_str == "left") + { + setFollowsLeft(); + } + else if (token_str == "right") + { + setFollowsRight(); + } + else if (token_str == "top") + { + setFollowsTop(); + } + else if (token_str == "bottom") + { + setFollowsBottom(); + } + else if (token_str == "all") + { + setFollowsAll(); + } + ++token_iter; + } + } +} + + // static LLFontGL* LLView::selectFont(LLXMLNodePtr node) { diff --git a/indra/llui/llview.h b/indra/llui/llview.h index 8248d50d9d..e54983d67d 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -50,6 +50,7 @@ #include "llviewquery.h" #include "llxmlnode.h" #include "stdenums.h" +#include "lluistring.h" class LLColor4; class LLWindow; @@ -146,6 +147,7 @@ protected: LLString mName; // location in pixels, relative to surrounding structure, bottom,left=0,0 LLRect mRect; + LLRect mBoundingRect; U32 mReshapeFlags; @@ -161,11 +163,11 @@ protected: BOOL mSaveToXML; BOOL mIsFocusRoot; + BOOL mUseBoundingRect; // hit test against bounding rectangle that includes all child elements public: LLViewHandle mViewHandle; BOOL mLastVisible; - BOOL mSpanChildren; private: BOOL mVisible; @@ -217,6 +219,7 @@ public: void setMouseOpaque( BOOL b ); void setToolTip( const LLStringExplicit& msg ); BOOL setToolTipArg( const LLStringExplicit& key, const LLStringExplicit& text ); + void setToolTipArgs( const LLString::format_map_t& args ); virtual void setRect(const LLRect &rect); void setFollows(U32 flags); @@ -231,13 +234,15 @@ public: void setSoundFlags(U8 flags); void setName(LLString name); - void setSpanChildren( BOOL span_children ); + void setUseBoundingRect( BOOL use_bounding_rect ); + BOOL getUseBoundingRect(); const LLString& getToolTip(); void sendChildToFront(LLView* child); void sendChildToBack(LLView* child); void moveChildToFrontOfTabGroup(LLUICtrl* child); + void moveChildToBackOfTabGroup(LLUICtrl* child); void addChild(LLView* view, S32 tab_group = 0); void addChildAtEnd(LLView* view, S32 tab_group = 0); @@ -264,7 +269,7 @@ public: { /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const { - return filterResult_t(view->isCtrl() && view->isFocusRoot(), !view->isFocusRoot()); + return filterResult_t(view->isCtrl() && view->isFocusRoot(), TRUE); } }; @@ -312,20 +317,22 @@ public: BOOL followsAll() const { return mReshapeFlags & FOLLOWS_ALL; } const LLRect& getRect() const { return mRect; } + const LLRect& getBoundingRect() const { return mBoundingRect; } + const LLRect getLocalBoundingRect() const; const LLRect getScreenRect() const; const LLRect getLocalRect() const; virtual const LLRect getSnapRect() const { return mRect; } virtual const LLRect getLocalSnapRect() const; virtual LLRect getRequiredRect(); // Get required size for this object. 0 for width/height means don't care. - virtual void updateRect(); // apply procedural updates to own rectangle + void updateBoundingRect(); LLView* getRootView(); LLView* getParent() const { return mParentView; } LLView* getFirstChild() { return (mChildList.empty()) ? NULL : *(mChildList.begin()); } S32 getChildCount() const { return (S32)mChildList.size(); } template<class _Pr3> void sortChildren(_Pr3 _Pred) { mChildList.sort(_Pred); } - BOOL hasAncestor(LLView* parentp); + BOOL hasAncestor(const LLView* parentp); BOOL hasChild(const LLString& childname, BOOL recurse = FALSE) const; @@ -390,6 +397,7 @@ public: static U32 createRect(LLXMLNodePtr node, LLRect &rect, LLView* parent_view, const LLRect &required_rect = LLRect()); virtual void initFromXML(LLXMLNodePtr node, LLView* parent); + void parseFollowsFlags(LLXMLNodePtr node); static LLFontGL* selectFont(LLXMLNodePtr node); static LLFontGL::HAlign selectFontHAlign(LLXMLNodePtr node); @@ -428,12 +436,16 @@ public: BOOL getVisible() const { return mVisible && !mHidden; } U8 getSoundFlags() const { return mSoundFlags; } - // Default to no action - virtual void onFocusLost(); - virtual void onFocusReceived(); + typedef enum e_hit_test_type + { + HIT_TEST_USE_BOUNDING_RECT, + HIT_TEST_IGNORE_BOUNDING_RECT + }EHitTestType; + + BOOL parentPointInView(S32 x, S32 y, EHitTestType type = HIT_TEST_USE_BOUNDING_RECT) const; + BOOL pointInView(S32 x, S32 y, EHitTestType type = HIT_TEST_USE_BOUNDING_RECT) const; + BOOL blockMouseEvent(S32 x, S32 y) const; - BOOL parentPointInView(S32 x, S32 y) const { return mRect.pointInRect( x, y ); } - BOOL pointInView(S32 x, S32 y) const { return mRect.localPointInRect( x, y ); } virtual void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const; virtual void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const; virtual BOOL localPointToOtherView( S32 x, S32 y, S32 *other_x, S32 *other_y, LLView* other_view); diff --git a/indra/llui/llviewborder.cpp b/indra/llui/llviewborder.cpp index f6cbe3aac1..8fbe671613 100644 --- a/indra/llui/llviewborder.cpp +++ b/indra/llui/llviewborder.cpp @@ -84,7 +84,7 @@ void LLViewBorder::setColorsExtended( const LLColor4& shadow_light, const LLColo void LLViewBorder::setTexture( const LLUUID &image_id ) { - mTexture = LLUI::sImageProvider->getUIImageByID(image_id); + mTexture = LLUI::sImageProvider->getImageByID(image_id); } diff --git a/indra/llui/llviewquery.cpp b/indra/llui/llviewquery.cpp index c07587f0ff..db00c76821 100644 --- a/indra/llui/llviewquery.cpp +++ b/indra/llui/llviewquery.cpp @@ -37,9 +37,14 @@ void LLQuerySorter::operator() (LLView * parent, viewList_t &children) const {} -filterResult_t LLNoLeavesFilter::operator() (const LLView* const view, const viewList_t & children) const +filterResult_t LLLeavesFilter::operator() (const LLView* const view, const viewList_t & children) const { - return filterResult_t(!(view->getChildList()->size() == 0), TRUE); + return filterResult_t(children.empty(), TRUE); +} + +filterResult_t LLRootsFilter::operator() (const LLView* const view, const viewList_t & children) const +{ + return filterResult_t(TRUE, FALSE); } filterResult_t LLVisibleFilter::operator() (const LLView* const view, const viewList_t & children) const @@ -56,6 +61,16 @@ filterResult_t LLTabStopFilter::operator() (const LLView* const view, const view view->canFocusChildren()); } +filterResult_t LLCtrlFilter::operator() (const LLView* const view, const viewList_t & children) const +{ + return filterResult_t(view->isCtrl(),TRUE); +} + +filterResult_t LLWidgetTypeFilter::operator() (const LLView* const view, const viewList_t & children) const +{ + return filterResult_t(view->getWidgetType() == mType, TRUE); +} + // LLViewQuery LLViewQuery::LLViewQuery(): mPreFilters(), mPostFilters(), mSorterp() @@ -73,45 +88,53 @@ const LLViewQuery::filterList_t & LLViewQuery::getPostFilters() const { return m void LLViewQuery::setSorter(const LLQuerySorter* sorterp) { mSorterp = sorterp; } const LLQuerySorter* LLViewQuery::getSorter() const { return mSorterp; } -viewList_t LLViewQuery::run(LLView * view) const +viewList_t LLViewQuery::run(LLView* view) const { viewList_t result; - filterResult_t pre = runFilters(view, viewList_t(), mPreFilters); + // prefilter gets immediate children of view + filterResult_t pre = runFilters(view, *view->getChildList(), mPreFilters); if(!pre.first && !pre.second) { - // skip post filters completely if we're not including ourselves or the children + // not including ourselves or the children + // nothing more to do return result; } + + viewList_t filtered_children; + filterResult_t post(TRUE, TRUE); if(pre.second) { // run filters on children - viewList_t filtered_children; filterChildren(view, filtered_children); - filterResult_t post = runFilters(view, filtered_children, mPostFilters); - if(pre.first && post.first) - { - result.push_back(view); - } - if(post.second) + // only run post filters if this element passed pre filters + // so if you failed to pass the pre filter, you can't filter out children in post + if (pre.first) { - result.insert(result.end(), filtered_children.begin(), filtered_children.end()); + post = runFilters(view, filtered_children, mPostFilters); } } - else + + if(pre.first && post.first) { - if(pre.first) - { - result.push_back(view); - } + result.push_back(view); + } + + if(pre.second && post.second) + { + result.insert(result.end(), filtered_children.begin(), filtered_children.end()); } + return result; } void LLViewQuery::filterChildren(LLView * view, viewList_t & filtered_children) const { LLView::child_list_t views(*(view->getChildList())); - (*mSorterp)(view, views); // sort the children per the sorter + if (mSorterp) + { + (*mSorterp)(view, views); // sort the children per the sorter + } for(LLView::child_list_iter_t iter = views.begin(); iter != views.end(); iter++) diff --git a/indra/llui/llviewquery.h b/indra/llui/llviewquery.h index 3919ba4bcb..63559e8240 100644 --- a/indra/llui/llviewquery.h +++ b/indra/llui/llviewquery.h @@ -35,6 +35,7 @@ #include <list> #include "llmemory.h" +#include "llui.h" class LLView; @@ -42,35 +43,60 @@ typedef std::list<LLView *> viewList_t; typedef std::pair<BOOL, BOOL> filterResult_t; // Abstract base class for all filters. -class LLQueryFilter : public LLRefCount +class LLQueryFilter { public: + virtual ~LLQueryFilter() {}; virtual filterResult_t operator() (const LLView* const view, const viewList_t & children) const =0; }; -class LLQuerySorter : public LLRefCount +class LLQuerySorter { public: + virtual ~LLQuerySorter() {}; virtual void operator() (LLView * parent, viewList_t &children) const; }; -class LLNoLeavesFilter : public LLQueryFilter, public LLSingleton<LLNoLeavesFilter> +class LLLeavesFilter : public LLQueryFilter, public LLSingleton<LLLeavesFilter> { /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const; }; + +class LLRootsFilter : public LLQueryFilter, public LLSingleton<LLRootsFilter> +{ + /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const; +}; + class LLVisibleFilter : public LLQueryFilter, public LLSingleton<LLVisibleFilter> { /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const; }; + class LLEnabledFilter : public LLQueryFilter, public LLSingleton<LLEnabledFilter> { /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const; }; + class LLTabStopFilter : public LLQueryFilter, public LLSingleton<LLTabStopFilter> { /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const; }; +class LLCtrlFilter : public LLQueryFilter, public LLSingleton<LLCtrlFilter> +{ + /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const; +}; + +class LLWidgetTypeFilter : public LLQueryFilter +{ +public: + LLWidgetTypeFilter(EWidgetType type) : mType(type) {}; +private: + /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const; + + EWidgetType mType; +}; + // Algorithm for flattening class LLViewQuery { |