From ea8fb7238e6f12383ee4bc081475fa6235637581 Mon Sep 17 00:00:00 2001 From: Josh Bell Date: Sat, 31 Mar 2007 01:41:19 +0000 Subject: svn merge -r 59364:59813 svn+ssh://svn.lindenlab.com/svn/linden/branches/maintenance --> release --- indra/llui/llbutton.cpp | 37 +- indra/llui/llbutton.h | 7 +- indra/llui/llcombobox.cpp | 280 +++++------- indra/llui/llcombobox.h | 29 +- indra/llui/lldraghandle.cpp | 8 +- indra/llui/llfloater.cpp | 35 +- indra/llui/llfocusmgr.cpp | 69 ++- indra/llui/llfocusmgr.h | 16 +- indra/llui/lllineeditor.cpp | 34 +- indra/llui/lllineeditor.h | 7 +- indra/llui/llmenugl.cpp | 30 +- indra/llui/llmenugl.h | 1 + indra/llui/llmodaldialog.cpp | 20 +- indra/llui/llresizebar.cpp | 129 +++--- indra/llui/llresizebar.h | 4 +- indra/llui/llresizehandle.cpp | 117 ++--- indra/llui/llresizehandle.h | 4 +- indra/llui/llscrollbar.cpp | 10 +- indra/llui/llscrollcontainer.cpp | 2 +- indra/llui/llscrolllistctrl.cpp | 908 +++++++++++++++++++++++++++++++-------- indra/llui/llscrolllistctrl.h | 133 ++++-- indra/llui/llslider.cpp | 12 +- indra/llui/llsliderctrl.cpp | 2 +- indra/llui/llspinctrl.cpp | 4 +- indra/llui/lltabcontainer.cpp | 8 +- indra/llui/lltextbox.cpp | 6 +- indra/llui/lltexteditor.cpp | 16 +- indra/llui/lltexteditor.h | 3 +- indra/llui/llui.cpp | 4 + indra/llui/lluictrl.cpp | 13 + indra/llui/lluictrl.h | 2 + indra/llui/lluictrlfactory.cpp | 4 + indra/llui/llview.cpp | 38 +- indra/llui/llview.h | 10 +- 34 files changed, 1288 insertions(+), 714 deletions(-) (limited to 'indra/llui') diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index ffa9262e21..8e81fec33b 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -46,7 +46,9 @@ LLButton::LLButton( const LLString& name, const LLRect& rect, const LLString& co mMouseUpCallback( NULL ), mHeldDownCallback( NULL ), mGLFont( NULL ), + mMouseDownFrame( 0 ), mHeldDownDelay( 0.5f ), // seconds until held-down callback is called + mHeldDownFrameDelay( 0 ), mImageUnselected( NULL ), mImageSelected( NULL ), mImageHoverSelected( NULL ), @@ -99,7 +101,9 @@ LLButton::LLButton(const LLString& name, const LLRect& rect, mMouseUpCallback( NULL ), mHeldDownCallback( NULL ), mGLFont( NULL ), + mMouseDownFrame( 0 ), mHeldDownDelay( 0.5f ), // seconds until held-down callback is called + mHeldDownFrameDelay( 0 ), mImageUnselected( NULL ), mImageSelected( NULL ), mImageHoverSelected( NULL ), @@ -196,9 +200,9 @@ void LLButton::init(void (*click_callback)(void*), void *callback_data, const LL LLButton::~LLButton() { - if( this == gFocusMgr.getMouseCapture() ) + if( hasMouseCapture() ) { - gFocusMgr.setMouseCapture( NULL, NULL ); + gFocusMgr.setMouseCapture( NULL ); } } @@ -284,7 +288,7 @@ BOOL LLButton::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent ) BOOL LLButton::handleMouseDown(S32 x, S32 y, MASK mask) { // Route future Mouse messages here preemptively. (Release on mouse up.) - gFocusMgr.setMouseCapture( this, &LLButton::onMouseCaptureLost ); + gFocusMgr.setMouseCapture( this ); if (hasTabStop() && !getIsChrome()) { @@ -297,6 +301,7 @@ BOOL LLButton::handleMouseDown(S32 x, S32 y, MASK mask) } mMouseDownTimer.start(); + mMouseDownFrame = LLFrameTimer::getFrameCount(); if (mSoundFlags & MOUSE_DOWN) { @@ -310,19 +315,17 @@ BOOL LLButton::handleMouseDown(S32 x, S32 y, MASK mask) BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask) { // We only handle the click if the click both started and ended within us - if( this == gFocusMgr.getMouseCapture() ) + if( hasMouseCapture() ) { + // Always release the mouse + gFocusMgr.setMouseCapture( NULL ); + // Regardless of where mouseup occurs, handle callback if (mMouseUpCallback) { (*mMouseUpCallback)(mCallbackUserData); } - mMouseDownTimer.stop(); - - // Always release the mouse - gFocusMgr.setMouseCapture( NULL, NULL ); - // 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)) @@ -337,6 +340,9 @@ BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask) (*mClickedCallback)( mCallbackUserData ); } } + + mMouseDownTimer.stop(); + mMouseDownTimer.reset(); } return TRUE; @@ -356,14 +362,14 @@ BOOL LLButton::handleHover(S32 x, S32 y, MASK mask) if (mMouseDownTimer.getStarted() && NULL != mHeldDownCallback) { F32 elapsed = mMouseDownTimer.getElapsedTimeF32(); - if( mHeldDownDelay < elapsed ) + if( mHeldDownDelay <= elapsed && mHeldDownFrameDelay <= LLFrameTimer::getFrameCount() - mMouseDownFrame) { mHeldDownCallback( mCallbackUserData ); } } // We only handle the click if the click both started and ended within us - if( this == gFocusMgr.getMouseCapture() ) + if( hasMouseCapture() ) { handled = TRUE; } @@ -412,7 +418,7 @@ 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 || (this == gFocusMgr.getMouseCapture() && pointInView(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 ) @@ -800,11 +806,10 @@ void LLButton::setHoverImages( const LLString& image_name, const LLString& selec setImageHoverSelected(selected_name); } -// static -void LLButton::onMouseCaptureLost( LLMouseHandler* old_captor ) +void LLButton::onMouseCaptureLost() { - LLButton* self = (LLButton*) old_captor; - self->mMouseDownTimer.stop(); + mMouseDownTimer.stop(); + mMouseDownTimer.reset(); } //------------------------------------------------------------------------- diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h index 1982c2e36f..3dea4434a9 100644 --- a/indra/llui/llbutton.h +++ b/indra/llui/llbutton.h @@ -75,6 +75,8 @@ public: virtual BOOL handleHover(S32 x, S32 y, MASK mask); virtual void draw(); + virtual void onMouseCaptureLost(); + // HACK: "committing" a button is the same as clicking on it. virtual void onCommit(); @@ -85,7 +87,7 @@ public: void setMouseDownCallback( void (*cb)(void *data) ) { mMouseDownCallback = cb; } // mouse down within button void setMouseUpCallback( void (*cb)(void *data) ) { mMouseUpCallback = cb; } // mouse up, EVEN IF NOT IN BUTTON void setHeldDownCallback( void (*cb)(void *data) ) { mHeldDownCallback = cb; } // Mouse button held down and in button - void setHeldDownDelay( F32 seconds) { mHeldDownDelay = seconds; } + void setHeldDownDelay( F32 seconds, S32 frames = 0) { mHeldDownDelay = seconds; mHeldDownFrameDelay = frames; } F32 getHeldDownTime() const { return mMouseDownTimer.getElapsedTimeF32(); } @@ -140,7 +142,6 @@ public: void setBorderEnabled(BOOL b) { mBorderEnabled = b; } static void onHeldDown(void *userdata); // to be called by gIdleCallbacks - static void onMouseCaptureLost(LLMouseHandler* old_captor); void setFixedBorder(S32 width, S32 height) { mFixedWidth = width; mFixedHeight = height; } void setHoverGlowStrength(F32 strength) { mHoverGlowStrength = strength; } @@ -175,7 +176,9 @@ protected: const LLFontGL *mGLFont; LLFrameTimer mMouseDownTimer; + S32 mMouseDownFrame; F32 mHeldDownDelay; // seconds, after which held-down callbacks get called + S32 mHeldDownFrameDelay; // frames, after which held-down callbacks get called LLPointer mImageUnselected; LLUIString mUnselectedLabel; diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp index 667985c699..e439cd0acc 100644 --- a/indra/llui/llcombobox.cpp +++ b/indra/llui/llcombobox.cpp @@ -40,7 +40,7 @@ LLComboBox::LLComboBox( const LLString& name, const LLRect &rect, const LLString ) : LLUICtrl(name, rect, TRUE, commit_callback, callback_userdata, FOLLOWS_LEFT | FOLLOWS_TOP), - mDrawButton(TRUE), + mDrawArrow(TRUE), mTextEntry(NULL), mArrowImage(NULL), mArrowImageWidth(8), @@ -48,8 +48,8 @@ LLComboBox::LLComboBox( const LLString& name, const LLRect &rect, const LLString mMaxChars(20), mTextEntryTentative(TRUE), mPrearrangeCallback( NULL ), - mTextEntryCallback( NULL ), - mListWidth(list_width) + mListPosition(BELOW), + mTextEntryCallback( NULL ) { // For now, all comboboxes don't take keyboard focus when clicked. // This might change if it is part of a modal dialog. @@ -57,7 +57,7 @@ LLComboBox::LLComboBox( const LLString& name, const LLRect &rect, const LLString // 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 TopView. When keyboard focus is cleared all + // 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; @@ -68,7 +68,8 @@ LLComboBox::LLComboBox( const LLString& name, const LLRect &rect, const LLString // Text label button mButton = new LLSquareButton("comboxbox button", r, label, NULL, LLString::null, - &LLComboBox::onButtonClick, this); + NULL, this); + mButton->setMouseDownCallback(onButtonDown); mButton->setFont(LLFontGL::sSansSerifSmall); mButton->setFollows(FOLLOWS_LEFT | FOLLOWS_BOTTOM | FOLLOWS_RIGHT); mButton->setHAlign( LLFontGL::LEFT ); @@ -91,6 +92,7 @@ LLComboBox::LLComboBox( const LLString& name, const LLRect &rect, const LLString 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); @@ -204,111 +206,6 @@ void LLComboBox::setEnabled(BOOL enabled) mButton->setEnabled(enabled); } -// *HACK: these are all hacks to support the fact that the combobox -// has mouse capture so we can hide the list when we don't handle the -// mouse up event -BOOL LLComboBox::handleHover(S32 x, S32 y, MASK mask) -{ - if (mList->getVisible()) - { - S32 local_x, local_y; - LLView::localPointToOtherView(x, y, &local_x, &local_y, mList); - if (mList->pointInView(local_x, local_y)) - { - return mList->handleHover(local_x, local_y, mask); - } - } - return LLUICtrl::handleHover(x, y, mask); -} - -BOOL LLComboBox::handleMouseDown(S32 x, S32 y, MASK mask) -{ - if (mList->getVisible()) - { - S32 local_x, local_y; - LLView::localPointToOtherView(x, y, &local_x, &local_y, mList); - if (mList->pointInView(local_x, local_y)) - { - return mList->handleMouseDown(local_x, local_y, mask); - } - } - BOOL has_focus_now = hasFocus(); - BOOL handled = LLUICtrl::handleMouseDown(x, y, mask); - if (handled && !has_focus_now) - { - onFocusReceived(); - } - - return handled; -} - -BOOL LLComboBox::handleRightMouseDown(S32 x, S32 y, MASK mask) -{ - if (mList->getVisible()) - { - S32 local_x, local_y; - LLView::localPointToOtherView(x, y, &local_x, &local_y, mList); - if (mList->pointInView(local_x, local_y)) - { - return mList->handleRightMouseDown(local_x, local_y, mask); - } - } - return LLUICtrl::handleRightMouseDown(x, y, mask); -} - -BOOL LLComboBox::handleRightMouseUp(S32 x, S32 y, MASK mask) -{ - if (mList->getVisible()) - { - S32 local_x, local_y; - LLView::localPointToOtherView(x, y, &local_x, &local_y, mList); - if (mList->pointInView(local_x, local_y)) - { - return mList->handleRightMouseUp(local_x, local_y, mask); - } - } - return LLUICtrl::handleRightMouseUp(x, y, mask); -} - -BOOL LLComboBox::handleDoubleClick(S32 x, S32 y, MASK mask) -{ - if (mList->getVisible()) - { - S32 local_x, local_y; - LLView::localPointToOtherView(x, y, &local_x, &local_y, mList); - if (mList->pointInView(local_x, local_y)) - { - return mList->handleDoubleClick(local_x, local_y, mask); - } - } - return LLUICtrl::handleDoubleClick(x, y, mask); -} - -BOOL LLComboBox::handleMouseUp(S32 x, S32 y, MASK mask) -{ - BOOL handled = childrenHandleMouseUp(x, y, mask) != NULL; - - if (!handled && mList->getVisible()) - { - S32 local_x, local_y; - LLView::localPointToOtherView(x, y, &local_x, &local_y, mList); - if (mList->pointInView(local_x, local_y)) - { - handled = mList->handleMouseUp(local_x, local_y, mask); - } - } - - if( !handled && gFocusMgr.getMouseCapture() == this ) - { - // Mouse events that we didn't handle cause the list to be hidden. - // Eat mouse event, regardless of where on the screen it happens. - hideList(); - handled = TRUE; - } - - return handled; -} - void LLComboBox::clear() { if (mTextEntry) @@ -493,12 +390,13 @@ void LLComboBox::onFocusLost() { mTextEntry->selectAll(); } + LLUICtrl::onFocusLost(); } void LLComboBox::setButtonVisible(BOOL visible) { mButton->setVisible(visible); - mDrawButton = visible; + mDrawArrow = visible; if (mTextEntry) { LLRect text_entry_rect(0, mRect.getHeight(), mRect.getWidth(), 0); @@ -522,15 +420,17 @@ void LLComboBox::draw() // Draw children LLUICtrl::draw(); - if (mDrawButton) + if (mDrawArrow) { // Paste the graphic on the right edge if (!mArrowImage.isNull()) { - S32 left = mRect.getWidth() - mArrowImageWidth - LLUI::sConfigGroup->getS32("DropShadowButton"); + 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_image( left, 0, mArrowImage, - LLColor4::white); + gl_draw_scaled_image( left, 0, arrow_width, arrow_height, mArrowImage, LLColor4::white); } } } @@ -576,18 +476,60 @@ void LLComboBox::showList() //HACK: shouldn't have to know about scale here mList->arrange( 192, llfloor((F32)window_size.mY / LLUI::sGLScaleFactor.mV[VY]) - 50 ); - // Move rect so it hangs off the bottom of this view + // Make sure that we can see the whole list + LLRect floater_area_local; + gFloaterView->localRectToOtherView(gFloaterView->getLocalSnapRect(), &floater_area_local, this); + LLRect rect = mList->getRect(); - rect.setLeftTopAndSize(0, 0, rect.getWidth(), rect.getHeight() ); - mList->setRect(rect); + if (mListPosition == BELOW) + { + if (rect.getHeight() <= -floater_area_local.mBottom) + { + // Move rect so it hangs off the bottom of this view + rect.setLeftTopAndSize(0, 0, rect.getWidth(), rect.getHeight() ); + } + else + { + // stack on top or bottom, depending on which has more room + if (-floater_area_local.mBottom > floater_area_local.mTop - mRect.getHeight()) + { + // Move rect so it hangs off the bottom of this view + rect.setLeftTopAndSize(0, 0, rect.getWidth(), llmin(-floater_area_local.mBottom, rect.getHeight())); + } + else + { + // move rect so it stacks on top of this view (clipped to size of screen) + rect.setOriginAndSize(0, mRect.getHeight(), rect.getWidth(), llmin(floater_area_local.mTop - mRect.getHeight(), rect.getHeight())); + } + } + } + else // ABOVE + { + if (rect.getHeight() <= floater_area_local.mTop - mRect.getHeight()) + { + // move rect so it stacks on top of this view (clipped to size of screen) + rect.setOriginAndSize(0, mRect.getHeight(), rect.getWidth(), llmin(floater_area_local.mTop - mRect.getHeight(), rect.getHeight())); + } + else + { + // stack on top or bottom, depending on which has more room + if (-floater_area_local.mBottom > floater_area_local.mTop - mRect.getHeight()) + { + // Move rect so it hangs off the bottom of this view + rect.setLeftTopAndSize(0, 0, rect.getWidth(), llmin(-floater_area_local.mBottom, rect.getHeight())); + } + else + { + // move rect so it stacks on top of this view (clipped to size of screen) + rect.setOriginAndSize(0, mRect.getHeight(), rect.getWidth(), llmin(floater_area_local.mTop - mRect.getHeight(), rect.getHeight())); + } + } - // Make sure that we can see the whole list - LLRect floater_area_screen; - LLRect floater_area_local; - gFloaterView->getParent()->localRectToScreen( gFloaterView->getRect(), &floater_area_screen ); - screenRectToLocal( floater_area_screen, &floater_area_local ); - mList->translateIntoRect( floater_area_local, FALSE ); + } + mList->setOrigin(rect.mLeft, rect.mBottom); + mList->reshape(rect.getWidth(), rect.getHeight()); + mList->translateIntoRect(floater_area_local, FALSE); // Make sure we didn't go off bottom of screen S32 x, y; @@ -598,7 +540,12 @@ void LLComboBox::showList() mList->translate(0, -y); } - gFocusMgr.setMouseCapture( this, LLComboBox::onMouseCaptureLost ); + // 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 @@ -608,14 +555,13 @@ void LLComboBox::showList() // so that the callback is not immediately triggered on setFocus() mList->selectFirstItem(); } - gFocusMgr.setKeyboardFocus(mList, onListFocusLost); + mList->setFocus(TRUE); // Show the list and push the button down mButton->setToggleState(TRUE); mList->setVisible(TRUE); - gFocusMgr.setTopView(mList, LLComboBox::onTopViewLost ); - + gFocusMgr.setTopCtrl(mList); } void LLComboBox::hideList() @@ -624,37 +570,21 @@ void LLComboBox::hideList() mList->setVisible(FALSE); mList->highlightNthItem(-1); - if( gFocusMgr.getTopView() == mList ) + if( gFocusMgr.getTopCtrl() == mList ) { - gFocusMgr.setTopView(NULL, NULL); + gFocusMgr.setTopCtrl(NULL); } - if( gFocusMgr.getMouseCapture() == this ) - { - gFocusMgr.setMouseCapture( NULL, NULL ); - } - - if( gFocusMgr.getKeyboardFocus() == mList ) - { - if (mAllowTextEntry) - { - mTextEntry->setFocus(TRUE); - } - else - { - setFocus(TRUE); - } - } + //mList->setFocus(FALSE); } - //------------------------------------------------------------------ // static functions //------------------------------------------------------------------ // static -void LLComboBox::onButtonClick(void *userdata) +void LLComboBox::onButtonDown(void *userdata) { LLComboBox *self = (LLComboBox *)userdata; @@ -701,50 +631,46 @@ void LLComboBox::onItemSelected(LLUICtrl* item, void *userdata) const LLString& name = self->mList->getSimpleSelectedItem(); - self->hideList(); - S32 cur_id = self->getCurrentIndex(); if (cur_id != -1) { - self->setLabel(self->mList->getSimpleSelectedItem()); + self->setLabel(name); if (self->mAllowTextEntry) { - self->mTextEntry->setText(name); - self->mTextEntry->setTentative(FALSE); gFocusMgr.setKeyboardFocus(self->mTextEntry, NULL); self->mTextEntry->selectAll(); } - else - { - self->mButton->setLabelUnselected( name ); - self->mButton->setLabelSelected( name ); - self->mButton->setDisabledLabel( name ); - self->mButton->setDisabledSelectedLabel( name ); - } + } + else + { + // invalid selection, just restore existing value + self->mList->selectSimpleItem(self->mButton->getLabelSelected()); } self->onCommit(); -} -// static -void LLComboBox::onTopViewLost(LLView* old_focus) -{ - LLComboBox *self = (LLComboBox *) old_focus->getParent(); self->hideList(); } - // static -void LLComboBox::onMouseCaptureLost(LLMouseHandler*) +void LLComboBox::onListFocusChanged(LLUICtrl* list, void* user_data) { - // Can't hide the list here. If the list scrolls off the screen, - // and you click in the arrow buttons of the scroll bar, they must capture - // the mouse to handle scrolling-while-mouse-down. + LLComboBox *self = (LLComboBox *) list->getParent(); + if (!list->hasFocus()) + { + //*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) @@ -1122,15 +1048,3 @@ BOOL LLComboBox::operateOnAll(EOperation op) } return FALSE; } - -//static -void LLComboBox::onListFocusLost(LLUICtrl* old_focus) -{ - // if focus is going to nothing (user hit ESC), take it back - LLComboBox* combo = (LLComboBox*)old_focus->getParent(); - combo->hideList(); - if (gFocusMgr.getKeyboardFocus() == NULL) - { - combo->focusFirstItem(); - } -} diff --git a/indra/llui/llcombobox.h b/indra/llui/llcombobox.h index faf99937c9..2b19c3c2e5 100644 --- a/indra/llui/llcombobox.h +++ b/indra/llui/llcombobox.h @@ -34,6 +34,12 @@ class LLComboBox : public LLUICtrl, public LLCtrlListInterface { public: + typedef enum e_preferred_position + { + ABOVE, + BELOW + } EPreferredPosition; + LLComboBox( const LLString& name, const LLRect &rect, @@ -58,12 +64,6 @@ public: virtual BOOL handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect); virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent); virtual BOOL handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent); - virtual BOOL handleHover(S32 x, S32 y, MASK mask); - virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask); - virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); - virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); - virtual BOOL handleRightMouseUp(S32 x, S32 y, MASK mask); - virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); // LLUICtrl interface virtual void clear(); // select nothing @@ -146,34 +146,31 @@ public: void setButtonVisible(BOOL visible); - static void onButtonClick(void *userdata); + static void onButtonDown(void *userdata); static void onItemSelected(LLUICtrl* item, void *userdata); - static void onTopViewLost(LLView* old_focus); - static void onMouseCaptureLost(LLMouseHandler* old_captor); + static void onListFocusChanged(LLUICtrl* item, void *userdata); static void onTextEntry(LLLineEditor* line_editor, void* user_data); static void onTextCommit(LLUICtrl* caller, void* user_data); void updateSelection(); - void showList(); - void hideList(); - - static void onListFocusLost(LLUICtrl* old_focus); - + virtual void showList(); + virtual void hideList(); + protected: LLButton* mButton; LLScrollListCtrl* mList; LLViewBorder* mBorder; BOOL mKeyboardFocusOnClick; - BOOL mDrawButton; + BOOL mDrawArrow; LLLineEditor* mTextEntry; LLPointer mArrowImage; S32 mArrowImageWidth; BOOL mAllowTextEntry; S32 mMaxChars; BOOL mTextEntryTentative; + EPreferredPosition mListPosition; void (*mPrearrangeCallback)(LLUICtrl*,void*); void (*mTextEntryCallback)(LLLineEditor*, void*); - S32 mListWidth; // width of pop-up list, 0 = use combobox width }; #endif diff --git a/indra/llui/lldraghandle.cpp b/indra/llui/lldraghandle.cpp index a88fbb7744..599a85021b 100644 --- a/indra/llui/lldraghandle.cpp +++ b/indra/llui/lldraghandle.cpp @@ -269,7 +269,7 @@ BOOL LLDragHandle::handleMouseDown(S32 x, S32 y, MASK mask) { // Route future Mouse messages here preemptively. (Release on mouse up.) // No handler needed for focus lost since this clas has no state that depends on it. - gFocusMgr.setMouseCapture(this, NULL ); + gFocusMgr.setMouseCapture(this); localPointToScreen(x, y, &mDragLastScreenX, &mDragLastScreenY); mLastMouseScreenX = mDragLastScreenX; @@ -282,10 +282,10 @@ BOOL LLDragHandle::handleMouseDown(S32 x, S32 y, MASK mask) BOOL LLDragHandle::handleMouseUp(S32 x, S32 y, MASK mask) { - if( gFocusMgr.getMouseCapture() == this ) + if( hasMouseCapture() ) { // Release the mouse - gFocusMgr.setMouseCapture( NULL, NULL ); + gFocusMgr.setMouseCapture( NULL ); } // Note: don't pass on to children @@ -298,7 +298,7 @@ BOOL LLDragHandle::handleHover(S32 x, S32 y, MASK mask) BOOL handled = FALSE; // We only handle the click if the click both started and ended within us - if( gFocusMgr.getMouseCapture() == this ) + if( hasMouseCapture() ) { S32 screen_x; S32 screen_y; diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 0922de70af..1f71de1c87 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -279,7 +279,6 @@ void LLFloater::init(const LLString& title, mRect.getHeight() - LLPANEL_BORDER_WIDTH - close_box_size); mDragHandle = new LLDragHandleLeft("drag", drag_handle_rect, title ); } - mDragHandle->setSaveToXML(false); addChild(mDragHandle); // Resize Handle @@ -295,28 +294,24 @@ void LLFloater::init(const LLString& title, "resizebar_left", LLRect( 0, mRect.getHeight(), RESIZE_BAR_THICKNESS, 0), min_width, min_height, LLResizeBar::LEFT ); - mResizeBar[0]->setSaveToXML(false); addChild( mResizeBar[0] ); mResizeBar[1] = new LLResizeBar( "resizebar_top", LLRect( 0, mRect.getHeight(), mRect.getWidth(), mRect.getHeight() - RESIZE_BAR_THICKNESS), min_width, min_height, LLResizeBar::TOP ); - mResizeBar[1]->setSaveToXML(false); addChild( mResizeBar[1] ); mResizeBar[2] = new LLResizeBar( "resizebar_right", LLRect( mRect.getWidth() - RESIZE_BAR_THICKNESS, mRect.getHeight(), mRect.getWidth(), 0), min_width, min_height, LLResizeBar::RIGHT ); - mResizeBar[2]->setSaveToXML(false); addChild( mResizeBar[2] ); mResizeBar[3] = new LLResizeBar( "resizebar_bottom", LLRect( 0, RESIZE_BAR_THICKNESS, mRect.getWidth(), 0), min_width, min_height, LLResizeBar::BOTTOM ); - mResizeBar[3]->setSaveToXML(false); addChild( mResizeBar[3] ); @@ -327,7 +322,6 @@ void LLFloater::init(const LLString& title, min_width, min_height, LLResizeHandle::RIGHT_BOTTOM); - mResizeHandle[0]->setSaveToXML(false); addChild(mResizeHandle[0]); mResizeHandle[1] = new LLResizeHandle( "resize", @@ -335,7 +329,6 @@ void LLFloater::init(const LLString& title, min_width, min_height, LLResizeHandle::RIGHT_TOP ); - mResizeHandle[1]->setSaveToXML(false); addChild(mResizeHandle[1]); mResizeHandle[2] = new LLResizeHandle( "resize", @@ -343,7 +336,6 @@ void LLFloater::init(const LLString& title, min_width, min_height, LLResizeHandle::LEFT_BOTTOM ); - mResizeHandle[2]->setSaveToXML(false); addChild(mResizeHandle[2]); mResizeHandle[3] = new LLResizeHandle( "resize", @@ -351,7 +343,6 @@ void LLFloater::init(const LLString& title, min_width, min_height, LLResizeHandle::LEFT_TOP ); - mResizeHandle[3]->setSaveToXML(false); addChild(mResizeHandle[3]); } else @@ -463,14 +454,14 @@ void LLFloater::setVisible( BOOL visible ) if( !visible ) { - if( gFocusMgr.childIsTopView( this ) ) + if( gFocusMgr.childIsTopCtrl( this ) ) { - gFocusMgr.setTopView(NULL, NULL); + gFocusMgr.setTopCtrl(NULL); } if( gFocusMgr.childHasMouseCapture( this ) ) { - gFocusMgr.setMouseCapture(NULL, NULL); + gFocusMgr.setMouseCapture(NULL); } } @@ -584,9 +575,9 @@ void LLFloater::close(bool app_quitting) void LLFloater::releaseFocus() { - if( gFocusMgr.childIsTopView( this ) ) + if( gFocusMgr.childIsTopCtrl( this ) ) { - gFocusMgr.setTopView(NULL, NULL); + gFocusMgr.setTopCtrl(NULL); } if( gFocusMgr.childHasKeyboardFocus( this ) ) @@ -596,7 +587,7 @@ void LLFloater::releaseFocus() if( gFocusMgr.childHasMouseCapture( this ) ) { - gFocusMgr.setMouseCapture(NULL, NULL); + gFocusMgr.setMouseCapture(NULL); } } @@ -1464,28 +1455,24 @@ void LLFloater::setCanResize(BOOL can_resize) "resizebar_left", LLRect( 0, mRect.getHeight(), RESIZE_BAR_THICKNESS, 0), mMinWidth, mMinHeight, LLResizeBar::LEFT ); - mResizeBar[0]->setSaveToXML(false); addChild( mResizeBar[0] ); mResizeBar[1] = new LLResizeBar( "resizebar_top", LLRect( 0, mRect.getHeight(), mRect.getWidth(), mRect.getHeight() - RESIZE_BAR_THICKNESS), mMinWidth, mMinHeight, LLResizeBar::TOP ); - mResizeBar[1]->setSaveToXML(false); addChild( mResizeBar[1] ); mResizeBar[2] = new LLResizeBar( "resizebar_right", LLRect( mRect.getWidth() - RESIZE_BAR_THICKNESS, mRect.getHeight(), mRect.getWidth(), 0), mMinWidth, mMinHeight, LLResizeBar::RIGHT ); - mResizeBar[2]->setSaveToXML(false); addChild( mResizeBar[2] ); mResizeBar[3] = new LLResizeBar( "resizebar_bottom", LLRect( 0, RESIZE_BAR_THICKNESS, mRect.getWidth(), 0), mMinWidth, mMinHeight, LLResizeBar::BOTTOM ); - mResizeBar[3]->setSaveToXML(false); addChild( mResizeBar[3] ); @@ -1496,7 +1483,6 @@ void LLFloater::setCanResize(BOOL can_resize) mMinWidth, mMinHeight, LLResizeHandle::RIGHT_BOTTOM); - mResizeHandle[0]->setSaveToXML(false); addChild(mResizeHandle[0]); mResizeHandle[1] = new LLResizeHandle( "resize", @@ -1504,7 +1490,6 @@ void LLFloater::setCanResize(BOOL can_resize) mMinWidth, mMinHeight, LLResizeHandle::RIGHT_TOP ); - mResizeHandle[1]->setSaveToXML(false); addChild(mResizeHandle[1]); mResizeHandle[2] = new LLResizeHandle( "resize", @@ -1512,7 +1497,6 @@ void LLFloater::setCanResize(BOOL can_resize) mMinWidth, mMinHeight, LLResizeHandle::LEFT_BOTTOM ); - mResizeHandle[2]->setSaveToXML(false); addChild(mResizeHandle[2]); mResizeHandle[3] = new LLResizeHandle( "resize", @@ -1520,7 +1504,6 @@ void LLFloater::setCanResize(BOOL can_resize) mMinWidth, mMinHeight, LLResizeHandle::LEFT_TOP ); - mResizeHandle[3]->setSaveToXML(false); addChild(mResizeHandle[3]); } mResizable = can_resize; @@ -2022,8 +2005,7 @@ void LLFloaterView::focusFrontFloater() void LLFloaterView::getMinimizePosition(S32 *left, S32 *bottom) { S32 col = 0; - LLRect snap_rect_local = getSnapRect(); - snap_rect_local.translate(-mRect.mLeft, -mRect.mBottom); + LLRect snap_rect_local = getLocalSnapRect(); for(S32 row = snap_rect_local.mBottom; row < snap_rect_local.getHeight() - LLFLOATER_HEADER_SIZE; row += LLFLOATER_HEADER_SIZE ) //loop rows @@ -2146,8 +2128,7 @@ void LLFloaterView::adjustToFitScreen(LLFloater* floater, BOOL allow_partial_out S32 screen_width = getSnapRect().getWidth(); S32 screen_height = getSnapRect().getHeight(); // convert to local coordinate frame - LLRect snap_rect_local = getSnapRect(); - snap_rect_local.translate(-mRect.mLeft, -mRect.mBottom); + LLRect snap_rect_local = getLocalSnapRect(); if( floater->isResizable() ) { diff --git a/indra/llui/llfocusmgr.cpp b/indra/llui/llfocusmgr.cpp index 030fbf0653..da53e3d104 100644 --- a/indra/llui/llfocusmgr.cpp +++ b/indra/llui/llfocusmgr.cpp @@ -21,18 +21,16 @@ LLFocusMgr::LLFocusMgr() mLockedView( NULL ), mKeyboardLockedFocusLostCallback( NULL ), mMouseCaptor( NULL ), - mMouseCaptureLostCallback( NULL ), mKeyboardFocus( NULL ), mDefaultKeyboardFocus( NULL ), mKeyboardFocusLostCallback( NULL ), - mTopView( NULL ), - mTopViewLostCallback( NULL ), + mTopCtrl( NULL ), mFocusWeight(0.f), mAppHasFocus(TRUE) // Macs don't seem to notify us that we've gotten focus, so default to true #ifdef _DEBUG , mMouseCaptorName("none") , mKeyboardFocusName("none") - , mTopViewName("none") + , mTopCtrlName("none") #endif { } @@ -46,7 +44,7 @@ void LLFocusMgr::releaseFocusIfNeeded( LLView* view ) { if( childHasMouseCapture( view ) ) { - setMouseCapture( NULL, NULL ); + setMouseCapture( NULL ); } if( childHasKeyboardFocus( view )) @@ -63,9 +61,9 @@ void LLFocusMgr::releaseFocusIfNeeded( LLView* view ) } } - if( childIsTopView( view ) ) + if( childIsTopCtrl( view ) ) { - setTopView( NULL, NULL ); + setTopCtrl( NULL ); } } @@ -108,13 +106,13 @@ void LLFocusMgr::setKeyboardFocus(LLUICtrl* new_focus, FocusLostCallback on_focu // If we've got a default keyboard focus, and the caller is // releasing keyboard focus, move to the default. - if (mDefaultKeyboardFocus != NULL && new_focus == NULL) + if (mDefaultKeyboardFocus != NULL && mKeyboardFocus == NULL) { mDefaultKeyboardFocus->setFocus(TRUE); } - LLView* focus_subtree = new_focus; - LLView* viewp = new_focus; + LLView* focus_subtree = mKeyboardFocus; + LLView* viewp = mKeyboardFocus; // find root-most focus root while(viewp) { @@ -128,13 +126,13 @@ void LLFocusMgr::setKeyboardFocus(LLUICtrl* new_focus, FocusLostCallback on_focu if (focus_subtree) { - mFocusHistory[focus_subtree->mViewHandle] = new_focus ? new_focus->mViewHandle : LLViewHandle::sDeadHandle; + mFocusHistory[focus_subtree->mViewHandle] = mKeyboardFocus ? mKeyboardFocus->mViewHandle : LLViewHandle::sDeadHandle; } } if (lock) { - mLockedView = new_focus; + mLockedView = mKeyboardFocus; mKeyboardLockedFocusLostCallback = on_focus_lost; } } @@ -198,16 +196,13 @@ void LLFocusMgr::removeKeyboardFocusWithoutCallback( LLView* focus ) } -void LLFocusMgr::setMouseCapture( LLMouseHandler* new_captor, void (*on_capture_lost)(LLMouseHandler* old_captor) ) +void LLFocusMgr::setMouseCapture( LLMouseHandler* new_captor ) { //if (mFocusLocked) //{ // return; //} - void (*old_callback)(LLMouseHandler*) = mMouseCaptureLostCallback; - mMouseCaptureLostCallback = on_capture_lost; - if( new_captor != mMouseCaptor ) { LLMouseHandler* old_captor = mMouseCaptor; @@ -230,9 +225,9 @@ void LLFocusMgr::setMouseCapture( LLMouseHandler* new_captor, void (*on_capture_ } */ - if( old_callback ) + if( old_captor ) { - old_callback( old_captor ); + old_captor->onMouseCaptureLost(); } #ifdef _DEBUG @@ -250,7 +245,6 @@ void LLFocusMgr::removeMouseCaptureWithoutCallback( LLMouseHandler* captor ) if( mMouseCaptor == captor ) { mMouseCaptor = NULL; - mMouseCaptureLostCallback = NULL; #ifdef _DEBUG mMouseCaptorName = "none"; #endif @@ -258,9 +252,9 @@ void LLFocusMgr::removeMouseCaptureWithoutCallback( LLMouseHandler* captor ) } -BOOL LLFocusMgr::childIsTopView( LLView* parent ) +BOOL LLFocusMgr::childIsTopCtrl( LLView* parent ) { - LLView* top_view = mTopView; + LLView* top_view = (LLView*)mTopCtrl; while( top_view ) { if( top_view == parent ) @@ -275,36 +269,25 @@ BOOL LLFocusMgr::childIsTopView( LLView* parent ) // set new_top = NULL to release top_view. -void LLFocusMgr::setTopView( LLView* new_top, void (*on_top_lost)(LLView* old_top) ) +void LLFocusMgr::setTopCtrl( LLUICtrl* new_top ) { - void (*old_callback)(LLView*) = mTopViewLostCallback; - mTopViewLostCallback = on_top_lost; - - if( new_top != mTopView ) + if( new_top != mTopCtrl ) { - LLView* old_top = mTopView; - mTopView = new_top; - if( old_callback ) - { - old_callback( old_top ); - } - - mTopView = new_top; + mTopCtrl = new_top; #ifdef _DEBUG - mTopViewName = new_top ? new_top->getName() : "none"; + mTopCtrlName = new_top ? new_top->getName() : "none"; #endif } } -void LLFocusMgr::removeTopViewWithoutCallback( LLView* top_view ) +void LLFocusMgr::removeTopCtrlWithoutCallback( LLUICtrl* top_view ) { - if( mTopView == top_view ) + if( mTopCtrl == top_view ) { - mTopView = NULL; - mTopViewLostCallback = NULL; + mTopCtrl = NULL; #ifdef _DEBUG - mTopViewName = "none"; + mTopCtrlName = "none"; #endif } } @@ -343,6 +326,12 @@ void LLFocusMgr::setAppHasFocus(BOOL focus) { triggerFocusFlash(); } + + // release focus from "top ctrl"s, which generally hides them + if (!focus && mTopCtrl && mTopCtrl->hasFocus()) + { + mTopCtrl->setFocus(FALSE); + } mAppHasFocus = focus; } diff --git a/indra/llui/llfocusmgr.h b/indra/llui/llfocusmgr.h index cb555fca91..dc8025d4c0 100644 --- a/indra/llui/llfocusmgr.h +++ b/indra/llui/llfocusmgr.h @@ -27,7 +27,7 @@ public: ~LLFocusMgr(); // Mouse Captor - void setMouseCapture(LLMouseHandler* new_captor,void (*on_capture_lost)(LLMouseHandler* old_captor)); // new_captor = NULL to release the mouse. + void setMouseCapture(LLMouseHandler* new_captor); // new_captor = NULL to release the mouse. LLMouseHandler* getMouseCapture() { return mMouseCaptor; } void removeMouseCaptureWithoutCallback( LLMouseHandler* captor ); BOOL childHasMouseCapture( LLView* parent ); @@ -54,10 +54,10 @@ public: // Top View - void setTopView(LLView* new_top, void (*on_top_lost)(LLView* old_top)); - LLView* getTopView() const { return mTopView; } - void removeTopViewWithoutCallback( LLView* top_view ); - BOOL childIsTopView( LLView* parent ); + void setTopCtrl(LLUICtrl* new_top); + LLUICtrl* getTopCtrl() const { return mTopCtrl; } + void removeTopCtrlWithoutCallback( LLUICtrl* top_view ); + BOOL childIsTopCtrl( LLView* parent ); // All Three void releaseFocusIfNeeded( LLView* top_view ); @@ -70,7 +70,6 @@ protected: // Mouse Captor LLMouseHandler* mMouseCaptor; // Mouse events are premptively routed to this object - void (*mMouseCaptureLostCallback)(LLMouseHandler*); // The object to which mouse events are routed is called before another object takes its place // Keyboard Focus LLUICtrl* mKeyboardFocus; // Keyboard events are preemptively routed to this object @@ -78,8 +77,7 @@ protected: FocusLostCallback mKeyboardFocusLostCallback; // The object to which keyboard events are routed is called before another object takes its place // Top View - LLView* mTopView; - void (*mTopViewLostCallback)(LLView*); + LLUICtrl* mTopCtrl; LLFrameTimer mFocusTimer; F32 mFocusWeight; @@ -92,7 +90,7 @@ protected: #ifdef _DEBUG LLString mMouseCaptorName; LLString mKeyboardFocusName; - LLString mTopViewName; + LLString mTopCtrlName; #endif }; diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 46d66b3cd4..7422a039dd 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -96,7 +96,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)(LLLineEditor* caller, void* user_data ), + void (*focus_lost_callback)(LLUICtrl* caller, void* user_data ), void* userdata, LLLinePrevalidateFunc prevalidate_func, LLViewBorder::EBevel border_bevel, @@ -113,7 +113,6 @@ LLLineEditor::LLLineEditor(const LLString& name, const LLRect& rect, mCommitOnFocusLost( TRUE ), mRevertOnEsc( TRUE ), mKeystrokeCallback( keystroke_callback ), - mFocusLostCallback( focus_lost_callback ), mIsSelecting( FALSE ), mSelectionStart( 0 ), mSelectionEnd( 0 ), @@ -147,6 +146,8 @@ LLLineEditor::LLLineEditor(const LLString& name, const LLRect& rect, mGLFont = LLFontGL::sSansSerifSmall; } + setFocusLostCallback(focus_lost_callback); + mMinHPixels = mBorderThickness + UI_LINEEDITOR_H_PAD + mBorderLeft; mMaxHPixels = mRect.getWidth() - mMinHPixels - mBorderThickness - mBorderRight; @@ -167,7 +168,6 @@ LLLineEditor::LLLineEditor(const LLString& name, const LLRect& rect, LLLineEditor::~LLLineEditor() { - mFocusLostCallback = NULL; mCommitOnFocusLost = FALSE; gFocusMgr.releaseFocusIfNeeded( this ); @@ -192,11 +192,8 @@ LLString LLLineEditor::getWidgetTag() const void LLLineEditor::onFocusLost() { - if( mFocusLostCallback ) - { - mFocusLostCallback( this, mCallbackUserData ); - } - + LLUICtrl::onFocusLost(); + if( mCommitOnFocusLost && mText.getString() != mPrevText) { onCommit(); @@ -483,7 +480,7 @@ BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask) startSelection(); } - gFocusMgr.setMouseCapture( this, &LLLineEditor::onMouseCaptureLost ); + gFocusMgr.setMouseCapture( this ); } // delay cursor flashing @@ -496,14 +493,14 @@ BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask) BOOL LLLineEditor::handleHover(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; - if (gFocusMgr.getMouseCapture() != this && (x < mBorderLeft || x > (mRect.getWidth() - mBorderRight))) + if (!hasMouseCapture() && (x < mBorderLeft || x > (mRect.getWidth() - mBorderRight))) { return LLUICtrl::handleHover(x, y, mask); } if( getVisible() ) { - if( (gFocusMgr.getMouseCapture() == this) && mIsSelecting ) + if( (hasMouseCapture()) && mIsSelecting ) { if (x != mLastSelectionX || y != mLastSelectionY) { @@ -561,9 +558,9 @@ BOOL LLLineEditor::handleMouseUp(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; - if( gFocusMgr.getMouseCapture() == this ) + if( hasMouseCapture() ) { - gFocusMgr.setMouseCapture( NULL, NULL ); + gFocusMgr.setMouseCapture( NULL ); handled = TRUE; } @@ -1897,11 +1894,9 @@ BOOL LLLineEditor::prevalidateASCII(const LLWString &str) return rv; } -//static -void LLLineEditor::onMouseCaptureLost( LLMouseHandler* old_captor ) +void LLLineEditor::onMouseCaptureLost() { - LLLineEditor* self = (LLLineEditor*) old_captor; - self->endSelection(); + endSelection(); } @@ -1916,11 +1911,6 @@ void LLLineEditor::setKeystrokeCallback(void (*keystroke_callback)(LLLineEditor* mKeystrokeCallback = keystroke_callback; } -void LLLineEditor::setFocusLostCallback(void (*keystroke_callback)(LLLineEditor* caller, void* user_data)) -{ - mFocusLostCallback = keystroke_callback; -} - // virtual LLXMLNodePtr LLLineEditor::getXML(bool save_children) const { diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index ef2f43a1d3..27ae351d1f 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -51,7 +51,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)(LLLineEditor* caller, void* user_data) = NULL, + void (*focus_lost_callback)(LLUICtrl* caller, void* user_data) = NULL, void* userdata = NULL, LLLinePrevalidateFunc prevalidate_func = NULL, LLViewBorder::EBevel border_bevel = LLViewBorder::BEVEL_IN, @@ -72,6 +72,7 @@ public: /*virtual*/ BOOL handleDoubleClick(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*/ void onMouseCaptureLost(); // LLEditMenuHandler overrides virtual void cut(); @@ -166,7 +167,6 @@ public: void setSelectAllonFocusReceived(BOOL b); void setKeystrokeCallback(void (*keystroke_callback)(LLLineEditor* caller, void* user_data)); - void setFocusLostCallback(void (*keystroke_callback)(LLLineEditor* caller, void* user_data)); void setMaxTextLength(S32 max_text_length); void setBorderWidth(S32 left, S32 right); @@ -186,8 +186,6 @@ public: static BOOL postvalidateFloat(const LLString &str); - static void onMouseCaptureLost( LLMouseHandler* old_captor ); - protected: void removeChar(); void addChar(const llwchar c); @@ -222,7 +220,6 @@ protected: BOOL mRevertOnEsc; void (*mKeystrokeCallback)( LLLineEditor* caller, void* userdata ); - void (*mFocusLostCallback)( LLLineEditor* caller, void* userdata ); BOOL mIsSelecting; // Selection for clipboard operations S32 mSelectionStart; diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index d46a866e2b..79c38a87c0 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -3235,6 +3235,7 @@ LLPieMenu::LLPieMenu(const LLString& name, const LLString& label) mUseInfiniteRadius(FALSE), mHoverItem(NULL), mHoverThisFrame(FALSE), + mHoveredAnyItem(FALSE), mOuterRingAlpha(1.f), mCurRadius(0.f), mRightMouseDown(FALSE) @@ -3249,6 +3250,7 @@ LLPieMenu::LLPieMenu(const LLString& name) mUseInfiniteRadius(FALSE), mHoverItem(NULL), mHoverThisFrame(FALSE), + mHoveredAnyItem(FALSE), mOuterRingAlpha(1.f), mCurRadius(0.f), mRightMouseDown(FALSE) @@ -3319,12 +3321,12 @@ BOOL LLPieMenu::handleHover( S32 x, S32 y, MASK mask ) // release mouse capture after short period of visibility if we're using a finite boundary // so that right click outside of boundary will trigger new pie menu - if (gFocusMgr.getMouseCapture() == this && + if (hasMouseCapture() && !mRightMouseDown && mShrinkBorderTimer.getStarted() && mShrinkBorderTimer.getElapsedTimeF32() >= PIE_SHRINK_TIME) { - gFocusMgr.setMouseCapture(NULL, NULL); + gFocusMgr.setMouseCapture(NULL); mUseInfiniteRadius = FALSE; } @@ -3376,6 +3378,7 @@ BOOL LLPieMenu::handleHover( S32 x, S32 y, MASK mask ) break; } } + mHoveredAnyItem = TRUE; } else { @@ -3437,7 +3440,7 @@ BOOL LLPieMenu::handleRightMouseDown(S32 x, S32 y, MASK mask) if (clicked_in_pie) { // capture mouse cursor as if on initial menu show - gFocusMgr.setMouseCapture(this, NULL); + gFocusMgr.setMouseCapture(this); mShrinkBorderTimer.stop(); mUseInfiniteRadius = TRUE; handled = TRUE; @@ -3463,11 +3466,22 @@ BOOL LLPieMenu::handleRightMouseUp( S32 x, S32 y, MASK mask ) mShrinkBorderTimer.getElapsedTimeF32() > PIE_SHRINK_TIME) { mUseInfiniteRadius = FALSE; - gFocusMgr.setMouseCapture(NULL, NULL); + gFocusMgr.setMouseCapture(NULL); } + S32 delta_x = x /*+ mShiftHoriz*/ - getLocalRect().getCenterX(); + S32 delta_y = y /*+ mShiftVert*/ - getLocalRect().getCenterY(); + if (!mHoveredAnyItem && !mFirstMouseDown && (delta_x * delta_x) + (delta_y * delta_y) < PIE_CENTER_SIZE * PIE_CENTER_SIZE) + { + // user released right mouse button in middle of pie, interpret this as closing the menu + sMenuContainer->hideMenus(); + return TRUE; + } + + BOOL result = handleMouseUp( x, y, mask ); mRightMouseDown = FALSE; + mHoveredAnyItem = FALSE; return result; } @@ -3874,6 +3888,8 @@ void LLPieMenu::show(S32 x, S32 y, BOOL mouse_down) mRightMouseDown = mouse_down; mFirstMouseDown = mouse_down; mUseInfiniteRadius = TRUE; + mHoveredAnyItem = FALSE; + if (!mFirstMouseDown) { make_ui_sound("UISndPieMenuAppear"); @@ -3883,7 +3899,7 @@ void LLPieMenu::show(S32 x, S32 y, BOOL mouse_down) // we want all mouse events in case user does quick right click again off of pie menu // rectangle, to support gestural menu traversal - gFocusMgr.setMouseCapture(this, NULL); + gFocusMgr.setMouseCapture(this); if (mouse_down) { @@ -3910,10 +3926,11 @@ void LLPieMenu::hide(BOOL item_selected) mFirstMouseDown = FALSE; mRightMouseDown = FALSE; mUseInfiniteRadius = FALSE; + mHoveredAnyItem = FALSE; LLView::setVisible(FALSE); - gFocusMgr.setMouseCapture(NULL, NULL); + gFocusMgr.setMouseCapture(NULL); } ///============================================================================ @@ -4510,6 +4527,7 @@ void LLTearOffMenu::onFocusLost() { // remove highlight from parent item and our own menu mMenu->clearHoverItem(); + LLFloater::onFocusLost(); } BOOL LLTearOffMenu::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent) diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index e60f267702..36ca815ef3 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -672,6 +672,7 @@ private: BOOL mUseInfiniteRadius; // allow picking pie menu items anywhere outside of center circle LLMenuItemGL* mHoverItem; BOOL mHoverThisFrame; + BOOL mHoveredAnyItem; LLFrameTimer mShrinkBorderTimer; F32 mOuterRingAlpha; // for rendering pie menus as both bounded and unbounded F32 mCurRadius; diff --git a/indra/llui/llmodaldialog.cpp b/indra/llui/llmodaldialog.cpp index 4eaf6b7559..4453767f92 100644 --- a/indra/llui/llmodaldialog.cpp +++ b/indra/llui/llmodaldialog.cpp @@ -65,8 +65,8 @@ void LLModalDialog::startModal() } // This is a modal dialog. It sucks up all mouse and keyboard operations. - gFocusMgr.setMouseCapture( this, NULL ); - gFocusMgr.setTopView( this, NULL ); + gFocusMgr.setMouseCapture( this ); + gFocusMgr.setTopCtrl( this ); setFocus(TRUE); sModalStack.push_front( this ); @@ -107,10 +107,10 @@ void LLModalDialog::setVisible( BOOL visible ) if( visible ) { // This is a modal dialog. It sucks up all mouse and keyboard operations. - gFocusMgr.setMouseCapture( this, NULL ); + gFocusMgr.setMouseCapture( this ); // The dialog view is a root view - gFocusMgr.setTopView( this, NULL ); + gFocusMgr.setTopCtrl( this ); setFocus( TRUE ); } else @@ -222,9 +222,9 @@ void LLModalDialog::draw() if (mModal) { // If we've lost focus to a non-child, get it back ASAP. - if( gFocusMgr.getTopView() != this ) + if( gFocusMgr.getTopCtrl() != this ) { - gFocusMgr.setTopView( this, NULL); + gFocusMgr.setTopCtrl( this ); } if( !gFocusMgr.childHasKeyboardFocus( this ) ) @@ -234,7 +234,7 @@ void LLModalDialog::draw() if( !gFocusMgr.childHasMouseCapture( this ) ) { - gFocusMgr.setMouseCapture( this, NULL ); + gFocusMgr.setMouseCapture( this ); } } } @@ -259,7 +259,7 @@ void LLModalDialog::onAppFocusLost() LLModalDialog* instance = LLModalDialog::sModalStack.front(); if( gFocusMgr.childHasMouseCapture( instance ) ) { - gFocusMgr.setMouseCapture( NULL, NULL ); + gFocusMgr.setMouseCapture( NULL ); } if( gFocusMgr.childHasKeyboardFocus( instance ) ) @@ -277,9 +277,9 @@ void LLModalDialog::onAppFocusGained() LLModalDialog* instance = LLModalDialog::sModalStack.front(); // This is a modal dialog. It sucks up all mouse and keyboard operations. - gFocusMgr.setMouseCapture( instance, NULL ); + gFocusMgr.setMouseCapture( instance ); instance->setFocus(TRUE); - gFocusMgr.setTopView( instance, NULL ); + gFocusMgr.setTopCtrl( instance ); instance->centerOnScreen(); } diff --git a/indra/llui/llresizebar.cpp b/indra/llui/llresizebar.cpp index c6bc7fb355..0128b4ebbc 100644 --- a/indra/llui/llresizebar.cpp +++ b/indra/llui/llresizebar.cpp @@ -21,8 +21,8 @@ LLResizeBar::LLResizeBar( const LLString& name, const LLRect& rect, S32 min_width, S32 min_height, Side side ) : LLView( name, rect, TRUE ), - mDragStartScreenX( 0 ), - mDragStartScreenY( 0 ), + mDragLastScreenX( 0 ), + mDragLastScreenY( 0 ), mLastMouseScreenX( 0 ), mLastMouseScreenY( 0 ), mMinWidth( min_width ), @@ -55,6 +55,8 @@ LLResizeBar::LLResizeBar( const LLString& name, const LLRect& rect, S32 min_widt default: break; } + // this is just a decorator + setSaveToXML(FALSE); } @@ -64,12 +66,11 @@ BOOL LLResizeBar::handleMouseDown(S32 x, S32 y, MASK mask) { // Route future Mouse messages here preemptively. (Release on mouse up.) // No handler needed for focus lost since this clas has no state that depends on it. - gFocusMgr.setMouseCapture( this, NULL ); + gFocusMgr.setMouseCapture( this ); - //localPointToScreen(x, y, &mDragStartScreenX, &mDragStartScreenX); - localPointToOtherView(x, y, &mDragStartScreenX, &mDragStartScreenY, getParent()->getParent()); - mLastMouseScreenX = mDragStartScreenX; - mLastMouseScreenY = mDragStartScreenY; + localPointToScreen(x, y, &mDragLastScreenX, &mDragLastScreenY); + mLastMouseScreenX = mDragLastScreenX; + mLastMouseScreenY = mDragLastScreenY; } return TRUE; @@ -80,10 +81,10 @@ BOOL LLResizeBar::handleMouseUp(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; - if( gFocusMgr.getMouseCapture() == this ) + if( hasMouseCapture() ) { // Release the mouse - gFocusMgr.setMouseCapture( NULL, NULL ); + gFocusMgr.setMouseCapture( NULL ); handled = TRUE; } else @@ -108,74 +109,73 @@ BOOL LLResizeBar::handleHover(S32 x, S32 y, MASK mask) BOOL handled = FALSE; // We only handle the click if the click both started and ended within us - if( gFocusMgr.getMouseCapture() == this ) + if( hasMouseCapture() ) { - // *NOTE: this, of course, is fragile - LLView* floater_view = getParent()->getParent(); - S32 floater_view_x; - S32 floater_view_y; - localPointToOtherView(x, y, &floater_view_x, &floater_view_y, floater_view); + S32 screen_x; + S32 screen_y; + localPointToScreen(x, y, &screen_x, &screen_y); - S32 delta_x = floater_view_x - mDragStartScreenX; - S32 delta_y = floater_view_y - mDragStartScreenY; + S32 delta_x = screen_x - mDragLastScreenX; + S32 delta_y = screen_y - mDragLastScreenY; LLCoordGL mouse_dir; // use hysteresis on mouse motion to preserve user intent when mouse stops moving - mouse_dir.mX = (floater_view_x == mLastMouseScreenX) ? mLastMouseDir.mX : floater_view_x - mLastMouseScreenX; - mouse_dir.mY = (floater_view_y == mLastMouseScreenY) ? mLastMouseDir.mY : floater_view_y - mLastMouseScreenY; + mouse_dir.mX = (screen_x == mLastMouseScreenX) ? mLastMouseDir.mX : screen_x - mLastMouseScreenX; + mouse_dir.mY = (screen_y == mLastMouseScreenY) ? mLastMouseDir.mY : screen_y - mLastMouseScreenY; mLastMouseDir = mouse_dir; - mLastMouseScreenX = floater_view_x; - mLastMouseScreenY = floater_view_y; + mLastMouseScreenX = screen_x; + mLastMouseScreenY = screen_y; // Make sure the mouse in still over the application. We don't want to make the parent // so big that we can't see the resize handle any more. - LLRect valid_rect = floater_view->getRect(); - LLView* parentView = getParent(); - if( valid_rect.localPointInRect( floater_view_x, floater_view_y ) && parentView ) + LLRect valid_rect = getRootView()->getRect(); + LLView* resizing_view = getParent(); + + if( valid_rect.localPointInRect( screen_x, screen_y ) && resizing_view ) { // Resize the parent - LLRect parent_rect = parentView->getRect(); - LLRect scaled_rect = parent_rect; + LLRect orig_rect = resizing_view->getRect(); + LLRect scaled_rect = orig_rect; - S32 new_width = parent_rect.getWidth(); - S32 new_height = parent_rect.getHeight(); + S32 new_width = orig_rect.getWidth(); + S32 new_height = orig_rect.getHeight(); switch( mSide ) { case LEFT: - new_width = parent_rect.getWidth() - delta_x; + new_width = orig_rect.getWidth() - delta_x; if( new_width < mMinWidth ) { new_width = mMinWidth; - delta_x = parent_rect.getWidth() - mMinWidth; + delta_x = orig_rect.getWidth() - mMinWidth; } scaled_rect.translate(delta_x, 0); break; case TOP: - new_height = parent_rect.getHeight() + delta_y; + new_height = orig_rect.getHeight() + delta_y; if( new_height < mMinHeight ) { new_height = mMinHeight; - delta_y = mMinHeight - parent_rect.getHeight(); + delta_y = mMinHeight - orig_rect.getHeight(); } break; case RIGHT: - new_width = parent_rect.getWidth() + delta_x; + new_width = orig_rect.getWidth() + delta_x; if( new_width < mMinWidth ) { new_width = mMinWidth; - delta_x = mMinWidth - parent_rect.getWidth(); + delta_x = mMinWidth - orig_rect.getWidth(); } break; case BOTTOM: - new_height = parent_rect.getHeight() - delta_y; + new_height = orig_rect.getHeight() - delta_y; if( new_height < mMinHeight ) { new_height = mMinHeight; - delta_y = parent_rect.getHeight() - mMinHeight; + delta_y = orig_rect.getHeight() - mMinHeight; } scaled_rect.translate(0, delta_y); break; @@ -183,56 +183,59 @@ BOOL LLResizeBar::handleHover(S32 x, S32 y, MASK mask) scaled_rect.mTop = scaled_rect.mBottom + new_height; scaled_rect.mRight = scaled_rect.mLeft + new_width; - parentView->setRect(scaled_rect); - - S32 snap_delta_x = 0; - S32 snap_delta_y = 0; + resizing_view->setRect(scaled_rect); LLView* snap_view = NULL; switch( mSide ) { case LEFT: - snap_view = parentView->findSnapEdge(snap_delta_x, mouse_dir, SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin")); - snap_delta_x -= scaled_rect.mLeft; - scaled_rect.mLeft += snap_delta_x; + snap_view = resizing_view->findSnapEdge(scaled_rect.mLeft, mouse_dir, SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin")); break; case TOP: - snap_view = parentView->findSnapEdge(snap_delta_y, mouse_dir, SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin")); - snap_delta_y -= scaled_rect.mTop; - scaled_rect.mTop += snap_delta_y; + snap_view = resizing_view->findSnapEdge(scaled_rect.mTop, mouse_dir, SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin")); break; case RIGHT: - snap_view = parentView->findSnapEdge(snap_delta_x, mouse_dir, SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin")); - snap_delta_x -= scaled_rect.mRight; - scaled_rect.mRight += snap_delta_x; + snap_view = resizing_view->findSnapEdge(scaled_rect.mRight, mouse_dir, SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin")); break; case BOTTOM: - snap_view = parentView->findSnapEdge(snap_delta_y, mouse_dir, SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin")); - snap_delta_y -= scaled_rect.mBottom; - scaled_rect.mBottom += snap_delta_y; + snap_view = resizing_view->findSnapEdge(scaled_rect.mBottom, mouse_dir, SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin")); break; } - parentView->snappedTo(snap_view); + // register "snap" behavior with snapped view + resizing_view->snappedTo(snap_view); - parentView->setRect(parent_rect); + // restore original rectangle so the appropriate changes are detected + resizing_view->setRect(orig_rect); + // change view shape as user operation + resizing_view->userSetShape(scaled_rect); - parentView->reshape(scaled_rect.getWidth(), scaled_rect.getHeight(), FALSE); - parentView->translate(scaled_rect.mLeft - parentView->getRect().mLeft, scaled_rect.mBottom - parentView->getRect().mBottom); - - floater_view_x = mDragStartScreenX + delta_x; - floater_view_y = mDragStartScreenY + delta_y; - mDragStartScreenX = floater_view_x + snap_delta_x; - mDragStartScreenY = floater_view_y + snap_delta_y; + // update last valid mouse cursor position based on resized view's actual size + LLRect new_rect = resizing_view->getRect(); + switch(mSide) + { + case LEFT: + mDragLastScreenX += new_rect.mLeft - orig_rect.mLeft; + break; + case RIGHT: + mDragLastScreenX += new_rect.mRight - orig_rect.mRight; + break; + case TOP: + mDragLastScreenY += new_rect.mTop - orig_rect.mTop; + break; + case BOTTOM: + mDragLastScreenY += new_rect.mBottom- orig_rect.mBottom; + break; + default: + break; + } } - lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl; handled = TRUE; } else { - lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl; handled = TRUE; } diff --git a/indra/llui/llresizebar.h b/indra/llui/llresizebar.h index c2a07fd3e3..7a77cce8a6 100644 --- a/indra/llui/llresizebar.h +++ b/indra/llui/llresizebar.h @@ -30,8 +30,8 @@ public: void setResizeLimits( S32 min_width, S32 min_height ) { mMinWidth = min_width; mMinHeight = min_height; } protected: - S32 mDragStartScreenX; - S32 mDragStartScreenY; + S32 mDragLastScreenX; + S32 mDragLastScreenY; S32 mLastMouseScreenX; S32 mLastMouseScreenY; LLCoordGL mLastMouseDir; diff --git a/indra/llui/llresizehandle.cpp b/indra/llui/llresizehandle.cpp index 77101fa296..e4035b3ab2 100644 --- a/indra/llui/llresizehandle.cpp +++ b/indra/llui/llresizehandle.cpp @@ -23,8 +23,8 @@ const S32 RESIZE_BORDER_WIDTH = 3; LLResizeHandle::LLResizeHandle( const LLString& name, const LLRect& rect, S32 min_width, S32 min_height, ECorner corner ) : LLView( name, rect, TRUE ), - mDragStartScreenX( 0 ), - mDragStartScreenY( 0 ), + mDragLastScreenX( 0 ), + mDragLastScreenY( 0 ), mLastMouseScreenX( 0 ), mLastMouseScreenY( 0 ), mImage( NULL ), @@ -47,6 +47,9 @@ LLResizeHandle::LLResizeHandle( const LLString& name, const LLRect& rect, S32 mi case RIGHT_TOP: setFollows( FOLLOWS_RIGHT | FOLLOWS_TOP ); break; case RIGHT_BOTTOM: setFollows( FOLLOWS_RIGHT | FOLLOWS_BOTTOM ); break; } + + // decorator object, don't serialize + setSaveToXML(FALSE); } EWidgetType LLResizeHandle::getWidgetType() const @@ -69,11 +72,11 @@ BOOL LLResizeHandle::handleMouseDown(S32 x, S32 y, MASK mask) { // Route future Mouse messages here preemptively. (Release on mouse up.) // No handler needed for focus lost since this clas has no state that depends on it. - gFocusMgr.setMouseCapture( this, NULL ); + gFocusMgr.setMouseCapture( this ); - localPointToScreen(x, y, &mDragStartScreenX, &mDragStartScreenY); - mLastMouseScreenX = mDragStartScreenX; - mLastMouseScreenY = mDragStartScreenY; + localPointToScreen(x, y, &mDragLastScreenX, &mDragLastScreenY); + mLastMouseScreenX = mDragLastScreenX; + mLastMouseScreenY = mDragLastScreenY; } } @@ -85,10 +88,10 @@ BOOL LLResizeHandle::handleMouseUp(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; - if( gFocusMgr.getMouseCapture() == this ) + if( hasMouseCapture() ) { // Release the mouse - gFocusMgr.setMouseCapture( NULL, NULL ); + gFocusMgr.setMouseCapture( NULL ); handled = TRUE; } else @@ -106,7 +109,7 @@ BOOL LLResizeHandle::handleHover(S32 x, S32 y, MASK mask) BOOL handled = FALSE; // We only handle the click if the click both started and ended within us - if( gFocusMgr.getMouseCapture() == this ) + if( hasMouseCapture() ) { // Make sure the mouse in still over the application. We don't want to make the parent // so big that we can't see the resize handle any more. @@ -114,18 +117,18 @@ BOOL LLResizeHandle::handleHover(S32 x, S32 y, MASK mask) S32 screen_x; S32 screen_y; localPointToScreen(x, y, &screen_x, &screen_y); - const LLRect& valid_rect = gFloaterView->getRect(); // Assumes that the parent is a floater. + const LLRect valid_rect = getRootView()->getRect(); screen_x = llclamp( screen_x, valid_rect.mLeft, valid_rect.mRight ); screen_y = llclamp( screen_y, valid_rect.mBottom, valid_rect.mTop ); - LLView* parentView = getParent(); - if( parentView ) + LLView* resizing_view = getParent(); + if( resizing_view ) { // Resize the parent - LLRect parent_rect = parentView->getRect(); - LLRect scaled_rect = parent_rect; - S32 delta_x = screen_x - mDragStartScreenX; - S32 delta_y = screen_y - mDragStartScreenY; + LLRect orig_rect = resizing_view->getRect(); + LLRect scaled_rect = orig_rect; + S32 delta_x = screen_x - mDragLastScreenX; + S32 delta_y = screen_y - mDragLastScreenY; LLCoordGL mouse_dir; // use hysteresis on mouse motion to preserve user intent when mouse stops moving mouse_dir.mX = (screen_x == mLastMouseScreenX) ? mLastMouseDir.mX : screen_x - mLastMouseScreenX; @@ -156,18 +159,18 @@ BOOL LLResizeHandle::handleHover(S32 x, S32 y, MASK mask) break; } - S32 new_width = parent_rect.getWidth() + x_multiple * delta_x; + S32 new_width = orig_rect.getWidth() + x_multiple * delta_x; if( new_width < mMinWidth ) { new_width = mMinWidth; - delta_x = x_multiple * (mMinWidth - parent_rect.getWidth()); + delta_x = x_multiple * (mMinWidth - orig_rect.getWidth()); } - S32 new_height = parent_rect.getHeight() + y_multiple * delta_y; + S32 new_height = orig_rect.getHeight() + y_multiple * delta_y; if( new_height < mMinHeight ) { new_height = mMinHeight; - delta_y = y_multiple * (mMinHeight - parent_rect.getHeight()); + delta_y = y_multiple * (mMinHeight - orig_rect.getHeight()); } switch( mCorner ) @@ -188,10 +191,7 @@ BOOL LLResizeHandle::handleHover(S32 x, S32 y, MASK mask) // temporarily set new parent rect scaled_rect.mRight = scaled_rect.mLeft + new_width; scaled_rect.mTop = scaled_rect.mBottom + new_height; - parentView->setRect(scaled_rect); - - S32 snap_delta_x = 0; - S32 snap_delta_y = 0; + resizing_view->setRect(scaled_rect); LLView* snap_view = NULL; LLView* test_view = NULL; @@ -200,77 +200,78 @@ BOOL LLResizeHandle::handleHover(S32 x, S32 y, MASK mask) switch(mCorner) { case LEFT_TOP: - snap_view = parentView->findSnapEdge(snap_delta_x, mouse_dir, SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin")); - snap_delta_x -= scaled_rect.mLeft; - test_view = parentView->findSnapEdge(snap_delta_y, mouse_dir, SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin")); - snap_delta_y -= scaled_rect.mTop; + snap_view = resizing_view->findSnapEdge(scaled_rect.mLeft, mouse_dir, SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin")); + test_view = resizing_view->findSnapEdge(scaled_rect.mTop, mouse_dir, SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin")); if (!snap_view) { snap_view = test_view; } - scaled_rect.mLeft += snap_delta_x; - scaled_rect.mTop += snap_delta_y; break; case LEFT_BOTTOM: - snap_view = parentView->findSnapEdge(snap_delta_x, mouse_dir, SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin")); - snap_delta_x -= scaled_rect.mLeft; - test_view = parentView->findSnapEdge(snap_delta_y, mouse_dir, SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin")); - snap_delta_y -= scaled_rect.mBottom; + snap_view = resizing_view->findSnapEdge(scaled_rect.mLeft, mouse_dir, SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin")); + test_view = resizing_view->findSnapEdge(scaled_rect.mBottom, mouse_dir, SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin")); if (!snap_view) { snap_view = test_view; } - scaled_rect.mLeft += snap_delta_x; - scaled_rect.mBottom += snap_delta_y; break; case RIGHT_TOP: - snap_view = parentView->findSnapEdge(snap_delta_x, mouse_dir, SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin")); - snap_delta_x -= scaled_rect.mRight; - test_view = parentView->findSnapEdge(snap_delta_y, mouse_dir, SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin")); - snap_delta_y -= scaled_rect.mTop; + snap_view = resizing_view->findSnapEdge(scaled_rect.mRight, mouse_dir, SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin")); + test_view = resizing_view->findSnapEdge(scaled_rect.mTop, mouse_dir, SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin")); if (!snap_view) { snap_view = test_view; } - scaled_rect.mRight += snap_delta_x; - scaled_rect.mTop += snap_delta_y; break; case RIGHT_BOTTOM: - snap_view = parentView->findSnapEdge(snap_delta_x, mouse_dir, SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin")); - snap_delta_x -= scaled_rect.mRight; - test_view = parentView->findSnapEdge(snap_delta_y, mouse_dir, SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin")); - snap_delta_y -= scaled_rect.mBottom; + snap_view = resizing_view->findSnapEdge(scaled_rect.mRight, mouse_dir, SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin")); + test_view = resizing_view->findSnapEdge(scaled_rect.mBottom, mouse_dir, SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin")); if (!snap_view) { snap_view = test_view; } - scaled_rect.mRight += snap_delta_x; - scaled_rect.mBottom += snap_delta_y; break; } - parentView->snappedTo(snap_view); + // register "snap" behavior with snapped view + resizing_view->snappedTo(snap_view); // reset parent rect - parentView->setRect(parent_rect); + resizing_view->setRect(orig_rect); // translate and scale to new shape - parentView->reshape(scaled_rect.getWidth(), scaled_rect.getHeight(), FALSE); - parentView->translate(scaled_rect.mLeft - parentView->getRect().mLeft, scaled_rect.mBottom - parentView->getRect().mBottom); + resizing_view->userSetShape(scaled_rect); - screen_x = mDragStartScreenX + delta_x + snap_delta_x; - screen_y = mDragStartScreenY + delta_y + snap_delta_y; - mDragStartScreenX = screen_x; - mDragStartScreenY = screen_y; + // update last valid mouse cursor position based on resized view's actual size + LLRect new_rect = resizing_view->getRect(); + switch(mCorner) + { + case LEFT_TOP: + mDragLastScreenX += new_rect.mLeft - orig_rect.mLeft; + mDragLastScreenY += new_rect.mTop - orig_rect.mTop; + break; + case LEFT_BOTTOM: + mDragLastScreenX += new_rect.mLeft - orig_rect.mLeft; + mDragLastScreenY += new_rect.mBottom- orig_rect.mBottom; + break; + case RIGHT_TOP: + mDragLastScreenX += new_rect.mRight - orig_rect.mRight; + mDragLastScreenY += new_rect.mTop - orig_rect.mTop; + break; + case RIGHT_BOTTOM: + mDragLastScreenX += new_rect.mRight - orig_rect.mRight; + mDragLastScreenY += new_rect.mBottom- orig_rect.mBottom; + break; + default: + break; + } } - lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active) " << llendl; handled = TRUE; } else if( getVisible() && pointInHandle( x, y ) ) { - lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive) " << llendl; handled = TRUE; } diff --git a/indra/llui/llresizehandle.h b/indra/llui/llresizehandle.h index 1350d1af20..cf383a33cc 100644 --- a/indra/llui/llresizehandle.h +++ b/indra/llui/llresizehandle.h @@ -37,8 +37,8 @@ protected: BOOL pointInHandle( S32 x, S32 y ); protected: - S32 mDragStartScreenX; - S32 mDragStartScreenY; + S32 mDragLastScreenX; + S32 mDragLastScreenY; S32 mLastMouseScreenX; S32 mLastMouseScreenY; LLCoordGL mLastMouseDir; diff --git a/indra/llui/llscrollbar.cpp b/indra/llui/llscrollbar.cpp index c21bbdcc75..f4aea3fa11 100644 --- a/indra/llui/llscrollbar.cpp +++ b/indra/llui/llscrollbar.cpp @@ -214,7 +214,7 @@ BOOL LLScrollbar::handleMouseDown(S32 x, S32 y, MASK mask) { // Start dragging the thumb // No handler needed for focus lost since this clas has no state that depends on it. - gFocusMgr.setMouseCapture( this, NULL ); + gFocusMgr.setMouseCapture( this ); mDragStartX = x; mDragStartY = y; mOrigRect.mTop = mThumbRect.mTop; @@ -255,7 +255,7 @@ BOOL LLScrollbar::handleHover(S32 x, S32 y, MASK mask) // because they'll capture the mouse whenever they need hover events. BOOL handled = FALSE; - if( gFocusMgr.getMouseCapture() == this ) + if( hasMouseCapture() ) { S32 height = mRect.getHeight(); S32 width = mRect.getWidth(); @@ -408,9 +408,9 @@ BOOL LLScrollbar::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, BOOL LLScrollbar::handleMouseUp(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; - if( gFocusMgr.getMouseCapture() == this ) + if( hasMouseCapture() ) { - gFocusMgr.setMouseCapture( NULL, NULL ); + gFocusMgr.setMouseCapture( NULL ); handled = TRUE; } else @@ -442,7 +442,7 @@ void LLScrollbar::draw() screenPointToLocal(cursor_pos_gl.mX, cursor_pos_gl.mY, &local_mouse_x, &local_mouse_y); BOOL other_captor = gFocusMgr.getMouseCapture() && gFocusMgr.getMouseCapture() != this; - BOOL hovered = mEnabled && !other_captor && (gFocusMgr.getMouseCapture() == this || mThumbRect.pointInRect(local_mouse_x, local_mouse_y)); + BOOL hovered = mEnabled && !other_captor && (hasMouseCapture() || mThumbRect.pointInRect(local_mouse_x, local_mouse_y)); if (hovered) { mCurGlowStrength = lerp(mCurGlowStrength, mHoverGlowStrength, LLCriticalDamp::getInterpolant(0.05f)); diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp index 15bb8e3f24..e5e31e45a9 100644 --- a/indra/llui/llscrollcontainer.cpp +++ b/indra/llui/llscrollcontainer.cpp @@ -437,7 +437,7 @@ void LLScrollableContainerView::draw() // auto-focus when scrollbar active // this allows us to capture user intent (i.e. stop automatically scrolling the view/etc) if (!gFocusMgr.childHasKeyboardFocus(this) && - (gFocusMgr.getMouseCapture() == mScrollbar[VERTICAL] || gFocusMgr.getMouseCapture() == mScrollbar[HORIZONTAL])) + (mScrollbar[VERTICAL]->hasMouseCapture() || mScrollbar[HORIZONTAL]->hasMouseCapture())) { focusFirstItem(); } diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 35d5affa5d..98dddfb542 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -29,8 +29,11 @@ #include "llwindow.h" #include "llcontrol.h" #include "llkeyboard.h" +#include "llresizebar.h" const S32 LIST_BORDER_PAD = 2; // white space inside the border and to the left of the scrollbar +const S32 MIN_COLUMN_WIDTH = 20; +const S32 LIST_SNAP_PADDING = 5; // local structures & classes. struct SortScrollListItem @@ -254,7 +257,7 @@ LLScrollListItem::~LLScrollListItem() std::for_each(mColumns.begin(), mColumns.end(), DeletePointer()); } -BOOL LLScrollListItem::handleMouseDown(S32 x, S32 y, MASK mask) +BOOL LLScrollListItem::handleClick(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; @@ -355,14 +358,13 @@ LLScrollListCtrl::LLScrollListCtrl(const LLString& name, const LLRect& rect, mPageLines(0), mHeadingHeight(20), mMaxSelectable(0), - mHeadingFont(NULL), mAllowMultipleSelection( allow_multiple_selection ), mAllowKeyboardMovement(TRUE), mCommitOnKeyboardMovement(TRUE), mCommitOnSelectionChange(FALSE), mSelectionChanged(FALSE), mCanSelect(TRUE), - mDisplayColumnButtons(FALSE), + mDisplayColumnHeaders(FALSE), mCollapseEmptyColumns(FALSE), mIsPopup(FALSE), mMaxItemCount(INT_MAX), @@ -377,21 +379,20 @@ LLScrollListCtrl::LLScrollListCtrl(const LLString& name, const LLRect& rect, mFgUnselectedColor( LLUI::sColorsGroup->getColor("ScrollUnselectedColor") ), mFgDisabledColor( LLUI::sColorsGroup->getColor("ScrollDisabledColor") ), mHighlightedColor( LLUI::sColorsGroup->getColor("ScrollHighlightedColor") ), + mHighlightedItem(-1), mBorderThickness( 2 ), mOnDoubleClickCallback( NULL ), mOnMaximumSelectCallback( NULL ), mOnSortChangedCallback( NULL ), - mHighlightedItem(-1), + mDrewSelected(FALSE), mBorder(NULL), - mDefaultColumn("SIMPLE"), mSearchColumn(0), + mDefaultColumn("SIMPLE"), mNumDynamicWidthColumns(0), mTotalStaticColumnWidth(0), - mSortColumn(0), - mSortAscending(TRUE), - - mDrewSelected(FALSE) + mSortColumn(-1), + mSortAscending(TRUE) { mItemListRect.setOriginAndSize( mBorderThickness + LIST_BORDER_PAD, @@ -478,6 +479,7 @@ void LLScrollListCtrl::clearRows() mScrollLines = 0; mLastSelected = NULL; + updateMaxContentWidth(NULL); } @@ -527,7 +529,6 @@ S32 LLScrollListCtrl::getFirstSelectedIndex() return -1; } - LLScrollListItem* LLScrollListCtrl::getFirstData() const { if (mItemList.size() == 0) @@ -537,6 +538,15 @@ LLScrollListItem* LLScrollListCtrl::getFirstData() const return mItemList[0]; } +LLScrollListItem* LLScrollListCtrl::getLastData() const +{ + if (mItemList.size() == 0) + { + return NULL; + } + return mItemList[mItemList.size() - 1]; +} + std::vector LLScrollListCtrl::getAllData() const { std::vector ret; @@ -554,7 +564,7 @@ void LLScrollListCtrl::reshape( S32 width, S32 height, BOOL called_from_parent ) { LLUICtrl::reshape( width, height, called_from_parent ); - S32 heading_size = (mDisplayColumnButtons ? mHeadingHeight : 0); + S32 heading_size = (mDisplayColumnHeaders ? mHeadingHeight : 0); mItemListRect.setOriginAndSize( mBorderThickness + LIST_BORDER_PAD, @@ -567,10 +577,8 @@ void LLScrollListCtrl::reshape( S32 width, S32 height, BOOL called_from_parent ) mScrollbar->setPageSize( mPageLines ); updateColumns(); - updateColumnButtons(); } - // 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) @@ -623,14 +631,56 @@ BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos ) updateLineHeight(); mPageLines = mLineHeight ? mItemListRect.getHeight() / mLineHeight : 0; - mScrollbar->setVisible(mPageLines < getItemCount()); + BOOL scrollbar_visible = mPageLines < getItemCount(); + + if (scrollbar_visible != mScrollbar->getVisible()) + { + mScrollbar->setVisible(mPageLines < getItemCount()); + updateColumns(); + } mScrollbar->setPageSize( mPageLines ); mScrollbar->setDocSize( getItemCount() ); + + updateMaxContentWidth(item); } + return not_too_big; } +void LLScrollListCtrl::updateMaxContentWidth(LLScrollListItem* added_item) +{ + const S32 HEADING_TEXT_PADDING = 30; + const S32 COLUMN_TEXT_PADDING = 20; + + std::map::iterator column_itor; + for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor) + { + LLScrollListColumn* column = &column_itor->second; + + if (!added_item) + { + // update on all items + column->mMaxContentWidth = column->mHeader ? LLFontGL::sSansSerifSmall->getWidth(column->mLabel) + mColumnPadding + HEADING_TEXT_PADDING : 0; + item_list::iterator iter; + for (iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListCell* cellp = (*iter)->getColumn(column->mIndex); + if (!cellp) continue; + + column->mMaxContentWidth = llmax(LLFontGL::sSansSerifSmall->getWidth(cellp->getText()) + mColumnPadding + COLUMN_TEXT_PADDING, column->mMaxContentWidth); + } + } + else + { + LLScrollListCell* cellp = added_item->getColumn(column->mIndex); + if (!cellp) continue; + + column->mMaxContentWidth = llmax(LLFontGL::sSansSerifSmall->getWidth(cellp->getText()) + mColumnPadding + COLUMN_TEXT_PADDING, column->mMaxContentWidth); + } + } +} + // Line height is the max height of all the cells in all the items. void LLScrollListCtrl::updateLineHeight() @@ -659,60 +709,82 @@ void LLScrollListCtrl::updateColumns() for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor) { LLScrollListColumn *column = &column_itor->second; + S32 new_width = column->mWidth; if (column->mRelWidth >= 0) { - column->mWidth = (S32)llround(column->mRelWidth*mItemListRect.getWidth()); + new_width = (S32)llround(column->mRelWidth*mItemListRect.getWidth()); } else if (column->mDynamicWidth) { - column->mWidth = (mItemListRect.getWidth() - mTotalStaticColumnWidth) / mNumDynamicWidthColumns; - + new_width = (mItemListRect.getWidth() - mTotalStaticColumnWidth) / mNumDynamicWidthColumns; + } + + if (new_width != column->mWidth) + { + column->mWidth = new_width; } mColumnsIndexed[column_itor->second.mIndex] = column; } -} -void LLScrollListCtrl::updateColumnButtons() -{ - std::map::iterator column_itor; - for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor) + item_list::iterator iter; + for (iter = mItemList.begin(); iter != mItemList.end(); iter++) { - LLScrollListColumn* column = &column_itor->second; - LLButton *button = column->mButton; - - if (button) + LLScrollListItem *itemp = *iter; + S32 num_cols = itemp->getNumColumns(); + S32 i = 0; + for (LLScrollListCell* cell = itemp->getColumn(i); i < num_cols; cell = itemp->getColumn(++i)) { - mColumnsIndexed[column->mIndex] = column; + if (i >= (S32)mColumnsIndexed.size()) break; + + cell->setWidth(mColumnsIndexed[i]->mWidth); + } + } + // update headers + std::vector::iterator column_ordered_it; + S32 left = mItemListRect.mLeft; + LLColumnHeader* last_header = NULL; + for (column_ordered_it = mColumnsIndexed.begin(); column_ordered_it != mColumnsIndexed.end(); ++column_ordered_it) + { + if ((*column_ordered_it)->mWidth <= 0) + { + // skip hidden columns + } + LLScrollListColumn* column = *column_ordered_it; + + if (column->mHeader) + { + last_header = column->mHeader; S32 top = mItemListRect.mTop; - S32 left = mItemListRect.mLeft; - { - std::map::iterator itor; - for (itor = mColumns.begin(); itor != mColumns.end(); ++itor) - { - if (itor->second.mIndex < column->mIndex && - itor->second.mWidth > 0) - { - left += itor->second.mWidth + mColumnPadding; - } - } - } - S32 right = left+column->mWidth; - if (column->mIndex != (S32)mColumns.size()-1) + S32 right = left + column->mWidth; + + if (column->mIndex != (S32)mColumnsIndexed.size()-1) { right += mColumnPadding; } - LLRect temp_rect = LLRect(left,top+mHeadingHeight,right,top); - button->setRect(temp_rect); - button->setFont(mHeadingFont); - button->setVisible(mDisplayColumnButtons); + 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->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); + last_header->reshape(new_width, last_header->getRect().getHeight()); + } } void LLScrollListCtrl::setDisplayHeading(BOOL display) { - mDisplayColumnButtons = display; + mDisplayColumnHeaders = display; updateColumns(); @@ -726,15 +798,7 @@ void LLScrollListCtrl::setHeadingHeight(S32 heading_height) reshape(mRect.getWidth(), mRect.getHeight()); // Resize - mScrollbar->reshape(SCROLLBAR_SIZE, mItemListRect.getHeight()); - - updateColumnButtons(); -} - -void LLScrollListCtrl::setHeadingFont(const LLFontGL* heading_font) -{ - mHeadingFont = heading_font; - updateColumnButtons(); + mScrollbar->reshape(SCROLLBAR_SIZE, mItemListRect.getHeight() + (mDisplayColumnHeaders ? mHeadingHeight : 0)); } void LLScrollListCtrl::setCollapseEmptyColumns(BOOL collapse) @@ -854,6 +918,7 @@ void LLScrollListCtrl::deleteSingleItem(S32 target_index) } delete itemp; mItemList.erase(mItemList.begin() + target_index); + updateMaxContentWidth(NULL); } void LLScrollListCtrl::deleteSelectedItems() @@ -873,6 +938,7 @@ void LLScrollListCtrl::deleteSelectedItems() } } mLastSelected = NULL; + updateMaxContentWidth(NULL); } void LLScrollListCtrl::highlightNthItem(S32 target_index) @@ -880,6 +946,7 @@ void LLScrollListCtrl::highlightNthItem(S32 target_index) if (mHighlightedItem != target_index) { mHighlightedItem = target_index; + llinfos << "Highlighting item " << target_index << llendl; } } @@ -1467,109 +1534,154 @@ BOOL LLScrollListCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks) return handled; } - -BOOL LLScrollListCtrl::handleMouseDown(S32 x, S32 y, MASK mask) +BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) { - BOOL handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL; + if (!mCanSelect) return FALSE; - // set keyboard focus first, in case click action wants to move focus elsewhere - setFocus(TRUE); + BOOL selection_changed = FALSE; - if( !handled && mCanSelect) + LLScrollListItem* hit_item = hitItem(x, y); + if( hit_item ) { - LLScrollListItem* hit_item = hitItem(x, y); - if( hit_item ) + if( mAllowMultipleSelection ) { - if( mAllowMultipleSelection ) + if (mask & MASK_SHIFT) { - if (mask & MASK_SHIFT) + if (mLastSelected == NULL) { - if (mLastSelected == NULL) - { - selectItem(hit_item); - } - else + selectItem(hit_item); + } + else + { + // Select everthing between mLastSelected and hit_item + bool selecting = false; + item_list::iterator itor; + // If we multiselect backwards, we'll stomp on mLastSelected, + // meaning that we never stop selecting until hitting max or + // the end of the list. + LLScrollListItem* lastSelected = mLastSelected; + for (itor = mItemList.begin(); itor != mItemList.end(); ++itor) { - // Select everthing between mLastSelected and hit_item - bool selecting = false; - item_list::iterator itor; - // If we multiselect backwards, we'll stomp on mLastSelected, - // meaning that we never stop selecting until hitting max or - // the end of the list. - LLScrollListItem* lastSelected = mLastSelected; - for (itor = mItemList.begin(); itor != mItemList.end(); ++itor) + if(mMaxSelectable > 0 && getAllSelected().size() >= mMaxSelectable) { - if(mMaxSelectable > 0 && getAllSelected().size() >= mMaxSelectable) - { - if(mOnMaximumSelectCallback) - { - mOnMaximumSelectCallback(mCallbackUserData); - } - break; - } - LLScrollListItem *item = *itor; - if (item == hit_item || item == lastSelected) - { - selectItem(item, FALSE); - selecting = !selecting; - } - if (selecting) + if(mOnMaximumSelectCallback) { - selectItem(item, FALSE); + mOnMaximumSelectCallback(mCallbackUserData); } + break; + } + LLScrollListItem *item = *itor; + if (item == hit_item || item == lastSelected) + { + selectItem(item, FALSE); + selecting = !selecting; + } + if (selecting) + { + selectItem(item, FALSE); } } } - else if (mask & MASK_CONTROL) + } + else if (mask & MASK_CONTROL) + { + if (hit_item->getSelected()) { - if (hit_item->getSelected()) + deselectItem(hit_item); + } + else + { + if(!(mMaxSelectable > 0 && getAllSelected().size() >= mMaxSelectable)) { - deselectItem(hit_item); + selectItem(hit_item, FALSE); } else { - if(!(mMaxSelectable > 0 && getAllSelected().size() >= mMaxSelectable)) + if(mOnMaximumSelectCallback) { - selectItem(hit_item, FALSE); - } - else - { - if(mOnMaximumSelectCallback) - { - mOnMaximumSelectCallback(mCallbackUserData); - } + mOnMaximumSelectCallback(mCallbackUserData); } } } - else - { - deselectAllItems(TRUE); - selectItem(hit_item); - } } else { + deselectAllItems(TRUE); selectItem(hit_item); } - - hit_item->handleMouseDown(x - mBorderThickness - LIST_BORDER_PAD, - 1, mask); - // always commit on mousedown - onCommit(); - mSelectionChanged = FALSE; - - // clear search string on mouse operations - mSearchString.clear(); } else { - mLastSelected = NULL; + selectItem(hit_item); } + + hit_item->handleClick(x - mBorderThickness - LIST_BORDER_PAD, + 1, mask); + + selection_changed = mSelectionChanged; + if (mCommitOnSelectionChange) + { + commitIfChanged(); + } + + // clear search string on mouse operations + mSearchString.clear(); + } + else + { + //mLastSelected = NULL; + //deselectAllItems(TRUE); + } + + return selection_changed; +} + + +BOOL LLScrollListCtrl::handleMouseDown(S32 x, S32 y, MASK mask) +{ + BOOL handled = LLView::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 + mSelectionChanged = FALSE; + + gFocusMgr.setMouseCapture(this); + selectItemAt(x, y, mask); } return TRUE; } +BOOL LLScrollListCtrl::handleMouseUp(S32 x, S32 y, MASK mask) +{ + if (hasMouseCapture()) + { + if(mask == MASK_NONE) + { + selectItemAt(x, y, mask); + } + } + + if (hasMouseCapture()) + { + gFocusMgr.setMouseCapture(NULL); + } + + // always commit when mouse operation is completed inside list + // this only needs to be done for lists that don't commit on selection change + if (!mCommitOnSelectionChange && pointInView(x,y)) + { + mSelectionChanged = FALSE; + onCommit(); + } + + return LLUICtrl::handleMouseUp(x, y, mask); +} + BOOL LLScrollListCtrl::handleDoubleClick(S32 x, S32 y, MASK mask) { //BOOL handled = FALSE; @@ -1628,31 +1740,35 @@ BOOL LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask) { BOOL handled = FALSE; - if(getVisible()) + if (hasMouseCapture()) { - if (mCanSelect) + if(mask == MASK_NONE) { - LLScrollListItem* item = hitItem(x, y); - if (item) - { - highlightNthItem(getItemIndex(item)); - } - else - { - highlightNthItem(-1); - } + selectItemAt(x, y, mask); } - - handled = LLView::handleHover( x, y, mask ); - - if( !handled ) + } + else if (mCanSelect) + { + LLScrollListItem* item = hitItem(x, y); + if (item) + { + highlightNthItem(getItemIndex(item)); + } + else { - // Opaque - getWindow()->setCursor(UI_CURSOR_ARROW); - lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl; - handled = TRUE; + highlightNthItem(-1); } } + + 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; } @@ -1954,7 +2070,7 @@ void LLScrollListCtrl::deselectItem(LLScrollListItem* itemp) void LLScrollListCtrl::commitIfChanged() { - if (mSelectionChanged) + if (mSelectionChanged && !hasMouseCapture()) { mSelectionChanged = FALSE; onCommit(); @@ -1973,9 +2089,18 @@ void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar, void // First column is column 0 void LLScrollListCtrl::sortByColumn(U32 column, BOOL ascending) { - mSortColumn = column; - mSortAscending = ascending; - std::sort(mItemList.begin(), mItemList.end(), SortScrollListItem(mSortColumn, mSortAscending)); + if (mSortColumn != column) + { + mSortColumn = column; + std::sort(mItemList.begin(), mItemList.end(), SortScrollListItem(mSortColumn, mSortAscending)); + } + + // just reverse the list if changing sort order + if(mSortAscending != ascending) + { + std::reverse(mItemList.begin(), mItemList.end()); + mSortAscending = ascending; + } } void LLScrollListCtrl::sortByColumn(LLString name, BOOL ascending) @@ -2047,7 +2172,7 @@ LLXMLNodePtr LLScrollListCtrl::getXML(bool save_children) const node->createChild("draw_border", TRUE)->setBoolValue((mBorder != NULL)); - node->createChild("draw_heading", TRUE)->setBoolValue(mDisplayColumnButtons); + node->createChild("draw_heading", TRUE)->setBoolValue(mDisplayColumnHeaders); node->createChild("background_visible", TRUE)->setBoolValue(mBackgroundVisible); @@ -2196,13 +2321,6 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac node->getAttributeS32("heading_height", heading_height); scroll_list->setHeadingHeight(heading_height); } - if (node->hasAttribute("heading_font")) - { - LLString heading_font(""); - node->getAttributeString("heading_font", heading_font); - LLFontGL* gl_font = LLFontGL::fontFromName(heading_font.c_str()); - scroll_list->setHeadingFont(gl_font); - } scroll_list->setCollapseEmptyColumns(collapse_empty_columns); scroll_list->setScrollListParameters(node); @@ -2408,7 +2526,7 @@ BOOL LLScrollListCtrl::canDeselect() void LLScrollListCtrl::addColumn(const LLSD& column, EAddPosition pos) { LLString name = column["name"].asString(); - if (mColumns.size() == 0) + if (mColumns.empty()) { mDefaultColumn = 0; } @@ -2451,30 +2569,28 @@ void LLScrollListCtrl::addColumn(const LLSD& column, EAddPosition pos) right += mColumnPadding; } LLRect temp_rect = LLRect(left,top+mHeadingHeight,right,top); - new_column->mButton = new LLSquareButton(button_name, temp_rect, "", mHeadingFont, "", onClickColumn, NULL); + new_column->mHeader = new LLColumnHeader(button_name, temp_rect, new_column); if(column["image"].asString() != "") { - //new_column->mButton->setScaleImage(false); - new_column->mButton->setImageSelected(column["image"].asString()); - new_column->mButton->setImageUnselected(column["image"].asString()); + //new_column->mHeader->setScaleImage(false); + new_column->mHeader->setImage(column["image"].asString()); } else { - new_column->mButton->setLabelSelected(new_column->mLabel); - new_column->mButton->setLabelUnselected(new_column->mLabel); + new_column->mHeader->setLabel(new_column->mLabel); + //new_column->mHeader->setLabel(new_column->mLabel); } //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->mButton->setTabStop(FALSE); - addChild(new_column->mButton); - new_column->mButton->setVisible(mDisplayColumnButtons); + new_column->mHeader->setTabStop(FALSE); + addChild(new_column->mHeader); + new_column->mHeader->setVisible(mDisplayColumnHeaders); // Move scroll to front removeChild(mScrollbar); addChild(mScrollbar); - - new_column->mButton->setCallbackUserData(new_column); + } } updateColumns(); @@ -2517,7 +2633,7 @@ void LLScrollListCtrl::onClickColumn(void *userdata) std::string LLScrollListCtrl::getSortColumnName() { - LLScrollListColumn* column = mColumnsIndexed[mSortColumn]; + LLScrollListColumn* column = mSortColumn >= 0 ? mColumnsIndexed[mSortColumn] : NULL; if (column) return column->mName; else return ""; @@ -2528,11 +2644,11 @@ void LLScrollListCtrl::clearColumns() std::map::iterator itor; for (itor = mColumns.begin(); itor != mColumns.end(); ++itor) { - LLButton *button = itor->second.mButton; - if (button) + LLColumnHeader *header = itor->second.mHeader; + if (header) { - removeChild(button); - delete button; + removeChild(header); + delete header; } } mColumns.clear(); @@ -2544,14 +2660,22 @@ void LLScrollListCtrl::setColumnLabel(const LLString& column, const LLString& la if (itor != mColumns.end()) { itor->second.mLabel = label; - if (itor->second.mButton) + if (itor->second.mHeader) { - itor->second.mButton->setLabelSelected(label); - itor->second.mButton->setLabelUnselected(label); + itor->second.mHeader->setLabel(label); } } } +LLScrollListColumn* LLScrollListCtrl::getColumn(S32 index) +{ + if (index < 0 || index >= (S32)mColumnsIndexed.size()) + { + return NULL; + } + return mColumnsIndexed[index]; +} + void LLScrollListCtrl::setColumnHeadings(LLSD headings) { mColumns.clear(); @@ -2629,6 +2753,10 @@ LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& value, EAddPosition p else { new_item->setColumn(index, new LLScrollListText(value.asString(), font, width, font_style, font_alignment)); + if (column_itor->second.mHeader && !value.asString().empty()) + { + column_itor->second.mHeader->setHasResizableElement(TRUE); + } } } @@ -2725,6 +2853,14 @@ void LLScrollListCtrl::setFocus(BOOL b) } LLUICtrl::setFocus(b); } + +//virtual +void LLScrollListCtrl::onFocusReceived() +{ + // forget latent selection changes when getting focus + mSelectionChanged = FALSE; +} + //virtual void LLScrollListCtrl::onFocusLost() { @@ -2735,5 +2871,433 @@ void LLScrollListCtrl::onFocusLost() getParent()->onFocusLost(); } } + if (hasMouseCapture()) + { + gFocusMgr.setMouseCapture(NULL); + } + LLUICtrl::onFocusLost(); +} + +LLColumnHeader::LLColumnHeader(const LLString& label, const LLRect &rect, LLScrollListColumn* column, const LLFontGL* fontp) : + LLComboBox(label, rect, label, NULL, NULL), + mColumn(column), + mOrigLabel(label), + mShowSortOptions(FALSE), + mHasResizableElement(FALSE) +{ + mListPosition = LLComboBox::ABOVE; + setCommitCallback(onSelectSort); + setCallbackUserData(this); + mButton->setTabStop(FALSE); + // require at least two frames between mouse down and mouse up event to capture intentional "hold" not just bad framerate + mButton->setHeldDownDelay(LLUI::sConfigGroup->getF32("ColumnHeaderDropDownDelay"), 2); + mButton->setHeldDownCallback(onHeldDown); + mButton->setClickedCallback(onClick); + mButton->setMouseDownCallback(onMouseDown); + + mButton->setCallbackUserData(this); + + mAscendingText = "[LOW]...[HIGH](Ascending)"; + mDescendingText = "[HIGH]...[LOW](Descending)"; + + LLSD row; + row["columns"][0]["column"] = "label"; + row["columns"][0]["value"] = mAscendingText.getString(); + row["columns"][0]["font"] = "SANSSERIF_SMALL"; + row["columns"][0]["width"] = 80; + + row["columns"][1]["column"] = "arrow"; + row["columns"][1]["type"] = "icon"; + row["columns"][1]["value"] = LLUI::sAssetsGroup->getString("up_arrow.tga"); + row["columns"][1]["width"] = 20; + + mList->addElement(row); + + row["columns"][0]["column"] = "label"; + row["columns"][0]["type"] = "text"; + row["columns"][0]["value"] = mDescendingText.getString(); + row["columns"][0]["font"] = "SANSSERIF_SMALL"; + row["columns"][0]["width"] = 80; + + row["columns"][1]["column"] = "arrow"; + row["columns"][1]["type"] = "icon"; + row["columns"][1]["value"] = LLUI::sAssetsGroup->getString("down_arrow.tga"); + row["columns"][1]["width"] = 20; + + mList->addElement(row); + + mList->reshape(llmax(mList->getRect().getWidth(), 110, mRect.getWidth()), mList->getRect().getHeight()); + + // resize handles on left and right + const S32 RESIZE_BAR_THICKNESS = 3; + mResizeBar = new LLResizeBar( + "resizebar", + LLRect( mRect.getWidth() - RESIZE_BAR_THICKNESS, mRect.getHeight(), mRect.getWidth(), 0), + MIN_COLUMN_WIDTH, mRect.getHeight(), LLResizeBar::RIGHT ); + addChild(mResizeBar); + + mResizeBar->setEnabled(FALSE); +} + +LLColumnHeader::~LLColumnHeader() +{ +} + +void LLColumnHeader::draw() +{ + if( getVisible() ) + { + mDrawArrow = !mColumn->mLabel.empty() && 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"))); + + //BOOL clip = mRect.mRight > mColumn->mParentCtrl->getItemListRect().getWidth(); + //LLGLEnable scissor_test(clip ? GL_SCISSOR_TEST : GL_FALSE); + + //LLRect column_header_local_rect(-mRect.mLeft, mRect.getHeight(), mColumn->mParentCtrl->getItemListRect().getWidth() - mRect.mLeft, 0); + //LLUI::setScissorRegionLocal(column_header_local_rect); + + // Draw children + LLComboBox::draw(); + + if (mList->getVisible()) + { + // sync sort order with list selection every frame + mColumn->mParentCtrl->sortByColumn(mColumn->mSortingColumn, getCurrentIndex() == 0); + } + + } +} + +BOOL LLColumnHeader::handleDoubleClick(S32 x, S32 y, MASK mask) +{ + if (canResize() && mResizeBar->getRect().pointInRect(x, y)) + { + // reshape column to max content width + LLRect column_rect = getRect(); + column_rect.mRight = column_rect.mLeft + mColumn->mMaxContentWidth; + userSetShape(column_rect); + } + else + { + onClick(this); + } + return TRUE; +} + +void LLColumnHeader::setImage(const LLString &image_name) +{ + if (mButton) + { + mButton->setImageSelected(image_name); + mButton->setImageUnselected(image_name); + } +} + +//static +void LLColumnHeader::onClick(void* user_data) +{ + LLColumnHeader* headerp = (LLColumnHeader*)user_data; + if (!headerp) return; + + LLScrollListColumn* column = headerp->mColumn; + if (!column) return; + + if (headerp->mList->getVisible()) + { + headerp->hideList(); + } + + LLScrollListCtrl::onClickColumn(column); + + // propage new sort order to sort order list + headerp->mList->selectNthItem(column->mParentCtrl->getSortAscending() ? 0 : 1); +} + +//static +void LLColumnHeader::onMouseDown(void* user_data) +{ + // for now, do nothing but block the normal showList() behavior + return; +} + +//static +void LLColumnHeader::onHeldDown(void* user_data) +{ + LLColumnHeader* headerp = (LLColumnHeader*)user_data; + headerp->showList(); +} + +void LLColumnHeader::showList() +{ + if (mShowSortOptions) + { + //LLSD item_val = mColumn->mParentCtrl->getFirstData()->getValue(); + mOrigLabel = mButton->getLabelSelected(); + + // move sort column over to this column and do initial sort + mColumn->mParentCtrl->sortByColumn(mColumn->mSortingColumn, mColumn->mParentCtrl->getSortAscending()); + + LLString low_item_text; + LLString high_item_text; + + LLScrollListItem* itemp = mColumn->mParentCtrl->getFirstData(); + if (itemp) + { + LLScrollListCell* cell = itemp->getColumn(mColumn->mIndex); + if (cell && cell->isText()) + { + if (mColumn->mParentCtrl->getSortAscending()) + { + low_item_text = cell->getText(); + } + else + { + high_item_text = cell->getText(); + } + } + } + + itemp = mColumn->mParentCtrl->getLastData(); + if (itemp) + { + LLScrollListCell* cell = itemp->getColumn(mColumn->mIndex); + if (cell && cell->isText()) + { + if (mColumn->mParentCtrl->getSortAscending()) + { + high_item_text = cell->getText(); + } + else + { + low_item_text = cell->getText(); + } + } + } + + LLString::truncate(low_item_text, 3); + LLString::truncate(high_item_text, 3); + + LLString ascending_string; + LLString descending_string; + + if (low_item_text.empty() || high_item_text.empty()) + { + ascending_string = "Ascending"; + descending_string = "Descending"; + } + else + { + mAscendingText.setArg("[LOW]", low_item_text); + mAscendingText.setArg("[HIGH]", high_item_text); + mDescendingText.setArg("[LOW]", low_item_text); + mDescendingText.setArg("[HIGH]", high_item_text); + ascending_string = mAscendingText.getString(); + descending_string = mDescendingText.getString(); + } + + S32 text_width = LLFontGL::sSansSerifSmall->getWidth(ascending_string); + text_width = llmax(text_width, LLFontGL::sSansSerifSmall->getWidth(descending_string)) + 10; + text_width = llmax(text_width, mRect.getWidth() - 30); + + mList->getColumn(0)->mWidth = text_width; + ((LLScrollListText*)mList->getFirstData()->getColumn(0))->setText(ascending_string); + ((LLScrollListText*)mList->getLastData()->getColumn(0))->setText(descending_string); + + mList->reshape(llmax(text_width + 30, 110, mRect.getWidth()), mList->getRect().getHeight()); + + LLComboBox::showList(); + } } +//static +void LLColumnHeader::onSelectSort(LLUICtrl* ctrl, void* user_data) +{ + LLColumnHeader* headerp = (LLColumnHeader*)user_data; + if (!headerp) return; + + LLScrollListColumn* column = headerp->mColumn; + if (!column) return; + LLScrollListCtrl *parent = column->mParentCtrl; + if (!parent) return; + + if (headerp->getCurrentIndex() == 0) + { + // ascending + parent->sortByColumn(column->mSortingColumn, TRUE); + } + else + { + // descending + parent->sortByColumn(column->mSortingColumn, FALSE); + } + + // restore original column header + headerp->setLabel(headerp->mOrigLabel); +} + +LLView* LLColumnHeader::findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding) +{ + // this logic assumes dragging on right + llassert(snap_edge == SNAP_RIGHT); + + // use higher snap threshold for column headers + threshold = llmin(threshold, 15); + + LLRect snap_rect = getSnapRect(); + + S32 snap_delta = mColumn->mMaxContentWidth - snap_rect.getWidth(); + + // x coord growing means column growing, so same signs mean we're going in right direction + if (llabs(snap_delta) <= threshold && mouse_dir.mX * snap_delta > 0 ) + { + new_edge_val = snap_rect.mRight + snap_delta; + } + else + { + LLScrollListColumn* next_column = mColumn->mParentCtrl->getColumn(mColumn->mIndex + 1); + while (next_column) + { + if (next_column->mHeader) + { + snap_delta = (next_column->mHeader->getSnapRect().mRight - next_column->mMaxContentWidth) - snap_rect.mRight; + if (llabs(snap_delta) <= threshold && mouse_dir.mX * snap_delta > 0 ) + { + new_edge_val = snap_rect.mRight + snap_delta; + } + break; + } + next_column = mColumn->mParentCtrl->getColumn(next_column->mIndex + 1); + } + } + + return this; +} + +void LLColumnHeader::userSetShape(const LLRect& new_rect) +{ + S32 new_width = new_rect.getWidth(); + S32 delta_width = new_width - (mRect.getWidth() + mColumn->mParentCtrl->getColumnPadding()); + + if (delta_width != 0) + { + S32 remaining_width = delta_width; + S32 col; + for (col = mColumn->mIndex + 1; col < mColumn->mParentCtrl->getNumColumns(); col++) + { + LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col); + if (!columnp) break; + + if (columnp->mHeader && columnp->mHeader->canResize()) + { + // how many pixels in width can this column afford to give up? + S32 resize_buffer_amt = llmax(0, columnp->mWidth - MIN_COLUMN_WIDTH); + + // user shrinking column, need to add width to other columns + if (delta_width < 0) + { + if (!columnp->mDynamicWidth && columnp->mWidth > 0) + { + // statically sized column, give all remaining width to this column + columnp->mWidth -= remaining_width; + if (columnp->mRelWidth > 0.f) + { + columnp->mRelWidth = (F32)columnp->mWidth / (F32)mColumn->mParentCtrl->getItemListRect().getWidth(); + } + } + break; + } + else + { + // user growing column, need to take width from other columns + remaining_width -= resize_buffer_amt; + + if (!columnp->mDynamicWidth && columnp->mWidth > 0) + { + columnp->mWidth -= llmin(columnp->mWidth - MIN_COLUMN_WIDTH, delta_width); + if (columnp->mRelWidth > 0.f) + { + columnp->mRelWidth = (F32)columnp->mWidth / (F32)mColumn->mParentCtrl->getItemListRect().getWidth(); + } + } + + if (remaining_width <= 0) + { + // width sucked up from neighboring columns, done + break; + } + } + } + } + + // clamp resize amount to maximum that can be absorbed by other columns + if (delta_width > 0) + { + delta_width -= llmax(remaining_width, 0); + } + + // propagate constrained delta_width to new width for this column + new_width = mRect.getWidth() + delta_width - mColumn->mParentCtrl->getColumnPadding(); + + // use requested width + mColumn->mWidth = new_width; + + // update proportional spacing + if (mColumn->mRelWidth > 0.f) + { + mColumn->mRelWidth = (F32)new_width / (F32)mColumn->mParentCtrl->getItemListRect().getWidth(); + } + + // tell scroll list to layout columns again + mColumn->mParentCtrl->updateColumns(); + } +} + +void LLColumnHeader::setHasResizableElement(BOOL resizable) +{ + // for now, dynamically spaced columns can't be resized + if (mColumn->mDynamicWidth) return; + + if (resizable != mHasResizableElement) + { + mHasResizableElement = resizable; + + S32 num_resizable_columns = 0; + S32 col; + for (col = 0; col < mColumn->mParentCtrl->getNumColumns(); col++) + { + LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col); + if (columnp->mHeader && columnp->mHeader->canResize()) + { + num_resizable_columns++; + } + } + + S32 num_resizers_enabled = 0; + + // now enable/disable resize handles on resizable columns if we have at least two + for (col = 0; col < mColumn->mParentCtrl->getNumColumns(); col++) + { + LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col); + if (!columnp->mHeader) continue; + BOOL enable = num_resizable_columns >= 2 && num_resizers_enabled < (num_resizable_columns - 1) && columnp->mHeader->canResize(); + columnp->mHeader->enableResizeBar(enable); + if (enable) + { + num_resizers_enabled++; + } + } + } +} + +void LLColumnHeader::enableResizeBar(BOOL enable) +{ + mResizeBar->setEnabled(enable); +} + +BOOL LLColumnHeader::canResize() +{ + return getVisible() && (mHasResizableElement || mColumn->mDynamicWidth); +} diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index 9c16e4dff9..99218ab3c4 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -23,9 +23,12 @@ #include "llviewborder.h" #include "llframetimer.h" #include "llcheckboxctrl.h" +#include "llcombobox.h" class LLScrollbar; class LLScrollListCtrl; +class LLColumnHeader; +class LLResizeBar; class LLScrollListCell { @@ -39,6 +42,7 @@ public: virtual const BOOL getVisible() const { return TRUE; } virtual void setWidth(S32 width) = 0; virtual void highlightText(S32 offset, S32 num_chars) {} + virtual BOOL isText() = 0; virtual BOOL handleClick() { return FALSE; } virtual void setEnabled(BOOL enable) { } @@ -58,6 +62,7 @@ public: virtual const BOOL getVisible() const { return mVisible; } virtual void highlightText(S32 offset, S32 num_chars) {mHighlightOffset = offset; mHighlightCount = num_chars;} void setText(const LLString& text); + virtual BOOL isText() { return TRUE; } private: LLUIString mText; @@ -86,6 +91,7 @@ public: virtual const LLString& getText() const { return mImageUUID; } virtual const LLString& getTextLower() const { return mImageUUID; } virtual void setWidth(S32 width) { mWidth = width; } + virtual BOOL isText() { return FALSE; } private: LLPointer mIcon; @@ -107,6 +113,7 @@ public: virtual void setEnabled(BOOL enable) { if (mCheckBox) mCheckBox->setEnabled(enable); } LLCheckBoxCtrl* getCheckBox() { return mCheckBox; } + virtual BOOL isText() { return FALSE; } private: LLCheckBoxCtrl* mCheckBox; @@ -117,14 +124,37 @@ class LLScrollListColumn { public: // Default constructor - LLScrollListColumn() : mName(""), mSortingColumn(""), mLabel(""), mWidth(-1), mRelWidth(-1.0), mDynamicWidth(FALSE), mIndex(-1), mParentCtrl(NULL), mButton(NULL), mFontAlignment(LLFontGL::LEFT) + LLScrollListColumn() : + mName(""), + mSortingColumn(""), + mLabel(""), + mWidth(-1), + mRelWidth(-1.0), + mDynamicWidth(FALSE), + mMaxContentWidth(0), + mIndex(-1), + mParentCtrl(NULL), + mHeader(NULL), + mFontAlignment(LLFontGL::LEFT) { } - LLScrollListColumn(LLString name, LLString label, S32 width, F32 relwidth) - : mName(name), mSortingColumn(name), mLabel(label), mWidth(width), mRelWidth(relwidth), mDynamicWidth(FALSE), mIndex(-1), mParentCtrl(NULL), mButton(NULL) { } + LLScrollListColumn(LLString name, LLString label, S32 width, F32 relwidth) : + mName(name), + mSortingColumn(name), + mLabel(label), + mWidth(width), + mRelWidth(relwidth), + mDynamicWidth(FALSE), + mMaxContentWidth(0), + mIndex(-1), + mParentCtrl(NULL), + mHeader(NULL) + { } LLScrollListColumn(const LLSD &sd) { + mMaxContentWidth = 0; + mName = sd.get("name").asString(); mSortingColumn = mName; if (sd.has("sort")) @@ -160,7 +190,7 @@ public: mIndex = -1; mParentCtrl = NULL; - mButton = NULL; + mHeader = NULL; } LLString mName; @@ -169,12 +199,47 @@ public: S32 mWidth; F32 mRelWidth; BOOL mDynamicWidth; + S32 mMaxContentWidth; S32 mIndex; LLScrollListCtrl* mParentCtrl; - LLButton* mButton; + LLColumnHeader* mHeader; LLFontGL::HAlign mFontAlignment; }; +class LLColumnHeader : public LLComboBox +{ +public: + LLColumnHeader(const LLString& label, const LLRect &rect, LLScrollListColumn* column, const LLFontGL *font = NULL); + ~LLColumnHeader(); + + /*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); + + void setImage(const LLString &image_name); + LLScrollListColumn* getColumn() { return mColumn; } + void setHasResizableElement(BOOL resizable); + BOOL canResize(); + void enableResizeBar(BOOL enable); + LLString getLabel() { return mOrigLabel; } + + static void onSelectSort(LLUICtrl* ctrl, void* user_data); + static void onClick(void* user_data); + static void onMouseDown(void* user_data); + static void onHeldDown(void* user_data); + +protected: + LLScrollListColumn* mColumn; + LLResizeBar* mResizeBar; + LLString mOrigLabel; + LLUIString mAscendingText; + LLUIString mDescendingText; + BOOL mShowSortOptions; + BOOL mHasResizableElement; +}; + class LLScrollListItem { public: @@ -216,7 +281,7 @@ public: LLScrollListCell *getColumn(const S32 i) const { if (i < (S32)mColumns.size()) { return mColumns[i]; } return NULL; } - virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); + virtual BOOL handleClick(S32 x, S32 y, MASK mask); LLString getContentsCSV(); @@ -263,6 +328,10 @@ public: virtual void addColumn(const LLSD& column, EAddPosition pos = ADD_BOTTOM); virtual void clearColumns(); virtual void setColumnLabel(const LLString& column, const LLString& label); + + virtual LLScrollListColumn* getColumn(S32 index); + virtual S32 getNumColumns() const { return mColumnsIndexed.size(); } + // Adds a single element, from an array of: // "columns" => [ "column" => column name, "value" => value, "type" => type, "font" => font, "font-style" => style ], "id" => uuid // Creates missing columns automatically. @@ -301,7 +370,8 @@ public: BOOL selectFirstItem(); BOOL selectNthItem( S32 index ); - + BOOL selectItemAt(S32 x, S32 y, MASK mask); + void deleteSingleItem( S32 index ) ; void deleteSelectedItems(); void deselectAllItems(BOOL no_commit_on_change = FALSE); // by default, go ahead and commit on selection change @@ -338,23 +408,6 @@ public: LLScrollListItem* addStringUUIDItem(const LLString& item_text, const LLUUID& id, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE, S32 column_width = 0); LLUUID getStringUUIDSelectedItem(); - // "Full" interface: use this when you're creating a list that has one or more of the following: - // * contains icons - // * contains multiple columns - // * allows multiple selection - // * has items that are not guarenteed to have unique names - // * has additional per-item data (e.g. a UUID or void* userdata) - // - // To add items using this approach, create new LLScrollListItems and LLScrollListCells. Add the - // cells (column entries) to each item, and add the item to the LLScrollListCtrl. - // - // The LLScrollListCtrl owns its items and is responsible for deleting them - // (except in the case that the addItem() call fails, in which case it is up - // to the caller to delete the item) - - // returns FALSE if item faile to be added to list, does NOT delete 'item' - // TomY TODO - Deprecate this API and remove it - BOOL addItem( LLScrollListItem* item, EAddPosition pos = ADD_BOTTOM ); LLScrollListItem* getFirstSelected() const; virtual S32 getFirstSelectedIndex(); std::vector getAllSelected() const; @@ -363,6 +416,7 @@ public: // iterate over all items LLScrollListItem* getFirstData() const; + LLScrollListItem* getLastData() const; std::vector getAllData() const; void setAllowMultipleSelection(BOOL mult ) { mAllowMultipleSelection = mult; } @@ -379,6 +433,7 @@ public: void setBackgroundVisible(BOOL b) { mBackgroundVisible = b; } void setDrawStripes(BOOL b) { mDrawStripes = b; } void setColumnPadding(const S32 c) { mColumnPadding = c; } + S32 getColumnPadding() { return mColumnPadding; } void setCommitOnKeyboardMovement(BOOL b) { mCommitOnKeyboardMovement = b; } void setCommitOnSelectionChange(BOOL b) { mCommitOnSelectionChange = b; } void setAllowKeyboardMovement(BOOL b) { mAllowKeyboardMovement = b; } @@ -398,6 +453,7 @@ public: // 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); @@ -405,6 +461,7 @@ public: 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 reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); @@ -412,17 +469,18 @@ public: virtual LLRect getRequiredRect(); static BOOL rowPreceeds(LLScrollListItem *new_row, LLScrollListItem *test_row); + LLRect getItemListRect() { return mItemListRect; } + // Used "internally" by the scroll bar. static void onScrollChange( S32 new_pos, LLScrollbar* src, void* userdata ); static void onClickColumn(void *userdata); void updateColumns(); - void updateColumnButtons(); + void updateMaxContentWidth(LLScrollListItem* changed_item); void setDisplayHeading(BOOL display); void setHeadingHeight(S32 heading_height); - void setHeadingFont(const LLFontGL* heading_font); void setCollapseEmptyColumns(BOOL collapse); void setIsPopup(BOOL is_popup) { mIsPopup = is_popup; } @@ -454,6 +512,22 @@ public: S32 selectMultiple( LLDynamicArray ids ); protected: + // "Full" interface: use this when you're creating a list that has one or more of the following: + // * contains icons + // * contains multiple columns + // * allows multiple selection + // * has items that are not guarenteed to have unique names + // * has additional per-item data (e.g. a UUID or void* userdata) + // + // To add items using this approach, create new LLScrollListItems and LLScrollListCells. Add the + // cells (column entries) to each item, and add the item to the LLScrollListCtrl. + // + // The LLScrollListCtrl owns its items and is responsible for deleting them + // (except in the case that the addItem() call fails, in which case it is up + // to the caller to delete the item) + + // returns FALSE if item faile to be added to list, does NOT delete 'item' + BOOL addItem( LLScrollListItem* item, EAddPosition pos = ADD_BOTTOM ); void selectPrevItem(BOOL extend_selection); void selectNextItem(BOOL extend_selection); void drawItems(); @@ -473,7 +547,6 @@ protected: S32 mPageLines; // max number of lines is it possible to see on the screen given mRect and mLineHeight S32 mHeadingHeight; // the height of the column header buttons, if visible U32 mMaxSelectable; - const LLFontGL* mHeadingFont; // the font to use for column head buttons, if visible LLScrollbar* mScrollbar; BOOL mAllowMultipleSelection; BOOL mAllowKeyboardMovement; @@ -481,7 +554,7 @@ protected: BOOL mCommitOnSelectionChange; BOOL mSelectionChanged; BOOL mCanSelect; - BOOL mDisplayColumnButtons; + BOOL mDisplayColumnHeaders; BOOL mCollapseEmptyColumns; BOOL mIsPopup; @@ -525,8 +598,8 @@ protected: S32 mNumDynamicWidthColumns; S32 mTotalStaticColumnWidth; - U32 mSortColumn; - BOOL mSortAscending; + S32 mSortColumn; + BOOL mSortAscending; std::map mColumns; std::vector mColumnsIndexed; diff --git a/indra/llui/llslider.cpp b/indra/llui/llslider.cpp index 2a1c2f7845..3a01013943 100644 --- a/indra/llui/llslider.cpp +++ b/indra/llui/llslider.cpp @@ -98,7 +98,7 @@ F32 LLSlider::getValueF32() const BOOL LLSlider::handleHover(S32 x, S32 y, MASK mask) { - if( gFocusMgr.getMouseCapture() == this ) + if( hasMouseCapture() ) { S32 left_edge = THUMB_WIDTH/2; S32 right_edge = mRect.getWidth() - (THUMB_WIDTH/2); @@ -125,9 +125,9 @@ BOOL LLSlider::handleMouseUp(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; - if( gFocusMgr.getMouseCapture() == this ) + if( hasMouseCapture() ) { - gFocusMgr.setMouseCapture( NULL, NULL ); + gFocusMgr.setMouseCapture( NULL ); if( mMouseUpCallback ) { @@ -175,7 +175,7 @@ BOOL LLSlider::handleMouseDown(S32 x, S32 y, MASK mask) // Start dragging the thumb // No handler needed for focus lost since this class has no state that depends on it. - gFocusMgr.setMouseCapture( this, NULL ); + gFocusMgr.setMouseCapture( this ); mDragStartThumbRect = mThumbRect; } make_ui_sound("UISndClick"); @@ -242,12 +242,12 @@ void LLSlider::draw() if (!thumb_imagep) { gl_rect_2d(mThumbRect, mThumbCenterColor, TRUE); - if (gFocusMgr.getMouseCapture() == this) + if (hasMouseCapture()) { gl_rect_2d(mDragStartThumbRect, mThumbCenterColor % opacity, FALSE); } } - else if( gFocusMgr.getMouseCapture() == this ) + else if( hasMouseCapture() ) { gl_draw_scaled_image_with_border(mDragStartThumbRect.mLeft, mDragStartThumbRect.mBottom, 16, 16, mDragStartThumbRect.getWidth(), mDragStartThumbRect.getHeight(), thumb_imagep, mThumbCenterColor % 0.3f, TRUE); diff --git a/indra/llui/llsliderctrl.cpp b/indra/llui/llsliderctrl.cpp index 3079726434..b3c49e81f1 100644 --- a/indra/llui/llsliderctrl.cpp +++ b/indra/llui/llsliderctrl.cpp @@ -186,7 +186,7 @@ void LLSliderCtrl::clear() BOOL LLSliderCtrl::isMouseHeldDown() { - return gFocusMgr.getMouseCapture() == mSlider; + return mSlider->hasMouseCapture(); } void LLSliderCtrl::updateText() diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp index 4c1700175b..fbd8335e6c 100644 --- a/indra/llui/llspinctrl.cpp +++ b/indra/llui/llspinctrl.cpp @@ -316,8 +316,8 @@ void LLSpinCtrl::setTentative(BOOL b) BOOL LLSpinCtrl::isMouseHeldDown() { return - gFocusMgr.getMouseCapture() == mDownBtn || - gFocusMgr.getMouseCapture() == mUpBtn; + mDownBtn->hasMouseCapture() + || mUpBtn->hasMouseCapture(); } void LLSpinCtrl::onCommit() diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp index 1ba991f916..c412d77922 100644 --- a/indra/llui/lltabcontainer.cpp +++ b/indra/llui/lltabcontainer.cpp @@ -983,7 +983,7 @@ void LLTabContainer::updateMaxScrollPos() void LLTabContainer::commitHoveredButton(S32 x, S32 y) { - if (gFocusMgr.getMouseCapture() == this) + if (hasMouseCapture()) { for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) { @@ -1244,7 +1244,7 @@ BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask ) if( tab_rect.pointInRect( x, y ) ) { LLButton* tab_button = mTabList[getCurrentPanelIndex()]->mButton; - gFocusMgr.setMouseCapture(this, NULL); + gFocusMgr.setMouseCapture(this); gFocusMgr.setKeyboardFocus(tab_button, NULL); } } @@ -1307,7 +1307,7 @@ BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask ) commitHoveredButton(x, y); LLPanel* cur_panel = getCurrentPanel(); - if (gFocusMgr.getMouseCapture() == this) + if (hasMouseCapture()) { if (cur_panel) { @@ -1318,7 +1318,7 @@ BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask ) mTabList[getCurrentPanelIndex()]->mButton->setFocus(TRUE); } } - gFocusMgr.setMouseCapture(NULL, NULL); + gFocusMgr.setMouseCapture(NULL); } return handled; } diff --git a/indra/llui/lltextbox.cpp b/indra/llui/lltextbox.cpp index 0cd2e98514..f6f7067540 100644 --- a/indra/llui/lltextbox.cpp +++ b/indra/llui/lltextbox.cpp @@ -94,7 +94,7 @@ BOOL LLTextBox::handleMouseDown(S32 x, S32 y, MASK mask) handled = TRUE; // Route future Mouse messages here preemptively. (Release on mouse up.) - gFocusMgr.setMouseCapture( this, NULL ); + gFocusMgr.setMouseCapture( this ); if (mSoundFlags & MOUSE_DOWN) { @@ -115,12 +115,12 @@ BOOL LLTextBox::handleMouseUp(S32 x, S32 y, MASK mask) // HACK: Only do this if there actually is a click callback, so that // overly large text boxes in the older UI won't start eating clicks. if (mClickedCallback - && this == gFocusMgr.getMouseCapture()) + && hasMouseCapture()) { handled = TRUE; // Release the mouse - gFocusMgr.setMouseCapture( NULL, NULL ); + gFocusMgr.setMouseCapture( NULL ); if (mSoundFlags & MOUSE_UP) { diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index de34aabb1f..366b1956c4 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -1182,7 +1182,7 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) setCursorAtLocalPos( x, y, TRUE ); startSelection(); } - gFocusMgr.setMouseCapture( this, &LLTextEditor::onMouseCaptureLost ); + gFocusMgr.setMouseCapture( this ); } handled = TRUE; @@ -1208,7 +1208,7 @@ BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask) mHoverSegment = NULL; if( getVisible() ) { - if(gFocusMgr.getMouseCapture() == this ) + if(hasMouseCapture() ) { if( mIsSelecting ) { @@ -1341,9 +1341,9 @@ BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask) // Delay cursor flashing mKeystrokeTimer.reset(); - if( gFocusMgr.getMouseCapture() == this ) + if( hasMouseCapture() ) { - gFocusMgr.setMouseCapture( NULL, NULL ); + gFocusMgr.setMouseCapture( NULL ); handled = TRUE; } @@ -2454,6 +2454,8 @@ void LLTextEditor::onFocusLost() // Make sure cursor is shown again getWindow()->showCursorFromMouseMove(); + + LLUICtrl::onFocusLost(); } void LLTextEditor::setEnabled(BOOL enabled) @@ -3734,11 +3736,9 @@ S32 LLTextEditor::getSegmentIdxAtOffset(S32 offset) } } -//static -void LLTextEditor::onMouseCaptureLost( LLMouseHandler* old_captor ) +void LLTextEditor::onMouseCaptureLost() { - LLTextEditor* self = (LLTextEditor*) old_captor; - self->endSelection(); + endSelection(); } void LLTextEditor::setOnScrollEndCallback(void (*callback)(void*), void* userdata) diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index ce85e35fe3..21db32b33f 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -75,6 +75,8 @@ public: virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, void *cargo_data, EAcceptance *accept, LLString& tooltip_msg); + virtual void onMouseCaptureLost(); + // view overrides virtual void reshape(S32 width, S32 height, BOOL called_from_parent); @@ -190,7 +192,6 @@ public: void setHandleEditKeysDirectly( BOOL b ) { mHandleEditKeysDirectly = b; } // Callbacks - static void onMouseCaptureLost( LLMouseHandler* old_captor ); static void setLinkColor(LLColor4 color) { mLinkColor = color; } static void setURLCallbacks( void (*callback1) (const char* url), BOOL (*callback2) (LLString url) ) diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp index e8e3f271a0..f32266faa8 100644 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -1723,6 +1723,10 @@ LLString LLUI::locateSkin(const LLString& filename) if (!gDirUtilp->fileExists(found_file)) { LLString localization(sConfigGroup->getString("Language")); + if(localization == "default") + { + localization = sConfigGroup->getString("SystemLanguage"); + } LLString local_skin = "xui" + slash + localization + slash + filename; found_file = gDirUtilp->getExpandedFilename(LL_PATH_SKINS, local_skin); } diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index 0d9791c660..abf796fde0 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -26,6 +26,7 @@ const U32 MAX_STRING_LENGTH = 10; LLUICtrl::LLUICtrl() : mCommitCallback(NULL), + mFocusLostCallback(NULL), mFocusReceivedCallback(NULL), mFocusChangedCallback(NULL), mValidateCallback(NULL), @@ -44,6 +45,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 ), mValidateCallback( NULL ), @@ -57,6 +59,12 @@ LLUICtrl::LLUICtrl(const LLString& name, const LLRect& rect, BOOL mouse_opaque, LLUICtrl::~LLUICtrl() { gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() + + if( gFocusMgr.getTopCtrl() == this ) + { + llwarns << "UI Control holding top ctrl deleted: " << getName() << ". Top view removed." << llendl; + gFocusMgr.removeTopCtrlWithoutCallback( this ); + } } void LLUICtrl::onCommit() @@ -151,6 +159,11 @@ void LLUICtrl::onFocusReceived() void LLUICtrl::onFocusLost() { + if( mFocusLostCallback ) + { + mFocusLostCallback( this, mCallbackUserData ); + } + if( mFocusChangedCallback ) { mFocusChangedCallback( this, mCallbackUserData ); diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h index 1418151201..1c2ac677df 100644 --- a/indra/llui/lluictrl.h +++ b/indra/llui/lluictrl.h @@ -104,6 +104,7 @@ public: 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; } @@ -135,6 +136,7 @@ protected: 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 ); BOOL (*mValidateCallback)( LLUICtrl* ctrl, void* userdata ); diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp index 21278455da..6410208189 100644 --- a/indra/llui/lluictrlfactory.cpp +++ b/indra/llui/lluictrlfactory.cpp @@ -222,6 +222,10 @@ void LLUICtrlFactory::setupPaths() if (LLUI::sConfigGroup) { language = LLUI::sConfigGroup->getString("Language"); + if(language == "default") + { + language = LLUI::sConfigGroup->getString("SystemLanguage"); + } } path_val_ui.setArg("[Language]", language); LLString fullpath = app_dir + path_val_ui.getString(); diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 02a99b00cd..da5c77fc94 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -155,18 +155,12 @@ LLView::~LLView() gFocusMgr.removeKeyboardFocusWithoutCallback( this ); } - if( gFocusMgr.getMouseCapture() == this ) + if( hasMouseCapture() ) { llwarns << "View holding mouse capture deleted: " << getName() << ". Mouse capture removed." << llendl; gFocusMgr.removeMouseCaptureWithoutCallback( this ); } - if( gFocusMgr.getTopView() == this ) - { - llwarns << "View holding top view deleted: " << getName() << ". Top view removed." << llendl; - gFocusMgr.removeTopViewWithoutCallback( this ); - } - sViewHandleMap.erase(mViewHandle); deleteAllChildren(); @@ -733,9 +727,9 @@ void LLView::setEnabled(BOOL enabled) // virtual void LLView::setVisible(BOOL visible) { - if( !visible && (gFocusMgr.getTopView() == this) ) + if( !visible && (gFocusMgr.getTopCtrl() == this) ) { - gFocusMgr.setTopView( NULL, NULL ); + gFocusMgr.setTopCtrl( NULL ); } if ( mVisible != visible ) @@ -1034,7 +1028,14 @@ LLView* LLView::childrenHandleDragAndDrop(S32 x, S32 y, MASK mask, return handled_view; } +void LLView::onMouseCaptureLost() +{ +} +BOOL LLView::hasMouseCapture() +{ + return gFocusMgr.getMouseCapture() == this; +} BOOL LLView::handleMouseUp(S32 x, S32 y, MASK mask) { @@ -1591,6 +1592,13 @@ const LLRect LLView::getLocalRect() const return local_rect; } +const LLRect LLView::getLocalSnapRect() const +{ + LLRect local_snap_rect = getSnapRect(); + local_snap_rect.translate(-mRect.mLeft, -mRect.mBottom); + return local_snap_rect; +} + void LLView::updateRect() { if (mSpanChildren && mChildList.size()) @@ -2087,6 +2095,12 @@ const LLCtrlQuery & LLView::getFocusRootsQuery() } +void LLView::userSetShape(const LLRect& new_rect) +{ + reshape(new_rect.getWidth(), new_rect.getHeight()); + translate(new_rect.mLeft - mRect.mLeft, new_rect.mBottom - mRect.mBottom); +} + LLView* LLView::findSnapRect(LLRect& new_rect, const LLCoordGL& mouse_dir, LLView::ESnapType snap_type, S32 threshold, S32 padding) { @@ -2108,8 +2122,7 @@ LLView* LLView::findSnapRect(LLRect& new_rect, const LLCoordGL& mouse_dir, BOOL snapped_x = FALSE; BOOL snapped_y = FALSE; - LLRect parent_local_snap_rect = mParentView->getSnapRect(); - parent_local_snap_rect.translate(-mParentView->getRect().mLeft, -mParentView->getRect().mBottom); + LLRect parent_local_snap_rect = mParentView->getLocalSnapRect(); if (snap_type == SNAP_PARENT || snap_type == SNAP_PARENT_AND_SIBLINGS) { @@ -2284,8 +2297,7 @@ LLView* LLView::findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESna BOOL snapped_x = FALSE; BOOL snapped_y = FALSE; - LLRect parent_local_snap_rect = mParentView->getSnapRect(); - parent_local_snap_rect.translate(-mParentView->getRect().mLeft, -mParentView->getRect().mBottom); + LLRect parent_local_snap_rect = mParentView->getLocalSnapRect(); if (snap_type == SNAP_PARENT || snap_type == SNAP_PARENT_AND_SIBLINGS) { diff --git a/indra/llui/llview.h b/indra/llui/llview.h index b794c087b5..504558a132 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -294,6 +294,7 @@ public: 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 @@ -315,12 +316,13 @@ public: // Default behavior is to use reshape flags to resize child views virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); - virtual void translate( S32 x, S32 y ); virtual void setOrigin( S32 x, S32 y ) { mRect.translate( x - mRect.mLeft, y - mRect.mBottom ); } BOOL translateIntoRect( const LLRect& constraint, BOOL allow_partial_outside ); - LLView* findSnapRect(LLRect& new_rect, const LLCoordGL& mouse_dir, LLView::ESnapType snap_type, S32 threshold, S32 padding = 0); - LLView* findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding = 0); + + virtual void userSetShape(const LLRect& new_rect); + virtual LLView* findSnapRect(LLRect& new_rect, const LLCoordGL& mouse_dir, LLView::ESnapType snap_type, S32 threshold, S32 padding = 0); + virtual LLView* findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding = 0); // Defaults to other_view->getVisible() virtual BOOL canSnapTo(LLView* other_view); @@ -345,6 +347,8 @@ public: /*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ void onMouseCaptureLost(); + /*virtual*/ BOOL hasMouseCapture(); // Default behavior is to pass the tooltip event to children, // then display mToolTipMsg if no child handled it. -- cgit v1.2.3