From e04dabd2b3309b595bbc1afa0dfa7d4081439eba Mon Sep 17 00:00:00 2001 From: Andrew Dyukov Date: Mon, 6 Sep 2010 21:35:01 +0300 Subject: VWR-20705 VWR-20706 FIXED Implemented drag'n'drop of buttons in bottomtray. - Though visually user drags buttons, layout panels are really moved. To move one panel before other, new method movePanelBeforeOther() was added to layout stack. - When drag'n'drop is finished, order of panels in layout stack mToolbarStack is changed, and also order vectors are updated in bottomtray.These are vectors mButtonsProcessOrder and mButtonsOrder. mButtonsOrder was introduced in this changeset to store order of all bottomtray buttons that may change place via drag'n'drop and should save and load it between sessions. mButtonsProcessOrder is not enough for it because it contains only buttons that may be hidden(and for example Speak button is not included in it). - To pass mouse events from buttons to bottomtray, new class LLBottomtrayButton was added (and new widget bottomtray_button for it). Reviewed by Vadim Savchuk. --- indra/newview/llbottomtray.cpp | 219 +++++++++++++++++++++ indra/newview/llbottomtray.h | 93 +++++++++ indra/newview/llnearbychatbar.cpp | 4 +- indra/newview/llnearbychatbar.h | 3 +- indra/newview/llspeakbutton.cpp | 8 +- indra/newview/llspeakbutton.h | 9 +- .../skins/default/xui/en/panel_bottomtray.xml | 32 +-- 7 files changed, 343 insertions(+), 25 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llbottomtray.cpp b/indra/newview/llbottomtray.cpp index 6ee4387236..9869ae6e2d 100644 --- a/indra/newview/llbottomtray.cpp +++ b/indra/newview/llbottomtray.cpp @@ -49,6 +49,45 @@ #include "lltoolmgr.h" #include "llviewerparcelmgr.h" +#include "llviewerwindow.h" + +// Distance from mouse down on which drag'n'drop should be started. +#define DRAG_START_DISTANCE 3 + +LLDefaultChildRegistry::Register bottomtray_button("bottomtray_button"); + +// LLBottomtrayButton methods + +// virtual +BOOL LLBottomtrayButton::handleHover(S32 x, S32 y, MASK mask) +{ + S32 screenX, screenY; + localPointToScreen(x, y, &screenX, &screenY); + // pass hover to bottomtray + LLBottomTray::getInstance()->handleHover(screenX, screenY, mask); + return FALSE; +} +//virtual +BOOL LLBottomtrayButton::handleMouseUp(S32 x, S32 y, MASK mask) +{ + S32 screenX, screenY; + localPointToScreen(x, y, &screenX, &screenY); + // pass mouse up to bottomtray + LLBottomTray::getInstance()->onDraggableButtonMouseUp(this,screenX, screenY, mask); + LLButton::handleMouseUp(x, y, mask); + return FALSE; +} +//virtual +BOOL LLBottomtrayButton::handleMouseDown(S32 x, S32 y, MASK mask) +{ + S32 screenX, screenY; + localPointToScreen(x, y, &screenX, &screenY); + // pass mouse up to bottomtray + LLBottomTray::getInstance()->onDraggableButtonMouseDown(this,screenX, screenY, mask); + LLButton::handleMouseDown(x, y, mask); + return FALSE; +} + static void update_build_button_enable_state() { bool can_edit = LLToolMgr::getInstance()->canEdit(); @@ -153,6 +192,10 @@ LLBottomTray::LLBottomTray(const LLSD&) , mCamButton(NULL) , mBottomTrayLite(NULL) , mIsInLiteMode(false) +, mDragStarted(false) +, mDraggedItem(NULL) +, mLandingTab(NULL) +, mCheckForDrag(false) { // Firstly add ourself to IMSession observers, so we catch session events // before chiclets do that. @@ -177,6 +220,8 @@ LLBottomTray::LLBottomTray(const LLSD&) mBottomTrayLite->setFollowsAll(); mBottomTrayLite->setVisible(FALSE); } + + mImageDragIndication = LLUI::getUIImage(getString("DragIndicationImageName")); } LLBottomTray::~LLBottomTray() @@ -509,6 +554,177 @@ BOOL LLBottomTray::postBuild() return TRUE; } +//Drag-n-drop + +void LLBottomTray::onDraggableButtonMouseDown(LLUICtrl* ctrl, S32 x, S32 y, MASK mask) +{ + if (ctrl == NULL) return; + LLView* parent_view = ctrl->getParent(); + if(parent_view != NULL) + { + // we actually drag'n'drop panel (not button) in code, so have to find a parent + // of button which called this method on mouse down. + LLPanel* parent = dynamic_cast(parent_view); + // It may happen that we clicked not usual button, but button inside widget(speak, gesture) + // so we'll need to get a level higher to reach layout panel as a parent. + if(parent == NULL) parent = dynamic_cast(parent_view->getParent()); + if (parent && parent->getVisible()) + { + mDraggedItem = parent; + mCheckForDrag = true; + mStartX = x; + mStartY = y; + } + } +} + +LLPanel* LLBottomTray::findChildPanelByLocalCoords(S32 x, S32 y) +{ + LLPanel* ctrl = 0; + S32 screenX, screenY; + const child_list_t* list = mToolbarStack->getChildList(); + + localPointToScreen(x, y, &screenX, &screenY); + + // look for a child panel which contains the point (screenX, screenY) in it's rectangle + for (child_list_const_iter_t i = list->begin(); i != list->end(); ++i) + { + LLRect rect; + localRectToScreen((*i)->getRect(), &rect); + + if (rect.pointInRect(screenX, screenY)) + { + ctrl = dynamic_cast(*i); + break; + } + } + + return ctrl; +} + +BOOL LLBottomTray::handleHover(S32 x, S32 y, MASK mask) +{ + // if mouse down on draggable item was done, check whether we should start DnD + if (mCheckForDrag) + { + // Start drag'n'drop if mouse cursor was dragged away frome mouse down location enough + if(sqrt((float)((mStartX-x)*(mStartX-x)+(mStartY-y)*(mStartY-y))) > DRAG_START_DISTANCE) + { + mDragStarted = true; + mCheckForDrag = false; + } + } + if (mDragStarted) + { + // Check whether the cursor is over draggable area, find which panel it is and set is as + // landing tab for drag'n'drop + if(isCursorOverDraggableArea(x, y)) + { + LLPanel* panel = findChildPanelByLocalCoords(x,y); + if (panel && panel != mDraggedItem) mLandingTab = panel; + gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROWDRAG); + } + else + { + gViewerWindow->getWindow()->setCursor(UI_CURSOR_NO); + } + } + + return TRUE; +} + +bool LLBottomTray::isCursorOverDraggableArea(S32 x, S32 y) +{ + bool result = getRect().pointInRect(x, y); + result = result && mNearbyChatBar->calcScreenRect().mRight < x; + result = result && mChicletPanel->calcScreenRect().mRight > x; + return result; +} + +void LLBottomTray::updateButtonsOrdersAfterDnD() +{ + // *TODO: change implementation of this method to support simplify it + // (and according to future possible changes in the way button order is saved between sessions). + state_object_map_t::const_iterator it = mStateProcessedObjectMap.begin(); + state_object_map_t::const_iterator it_end = mStateProcessedObjectMap.end(); + // Speak button is currently the only draggable button not in mStateProcessedObjectMap, + // so if dragged_state is not found in that map, it should be RS_BUTTON_SPEAK. Change this code if any other + // exclusions from mStateProcessedObjectMap will become draggable. + EResizeState dragged_state = RS_BUTTON_SPEAK, landing_state; + bool landing_state_found = false; + // Find states for dragged item and landing tab + for (; it != it_end; ++it) + { + if (it->second == mDraggedItem) + { + dragged_state = it->first; + } + else if (it->second == mLandingTab) + { + landing_state = it->first; + landing_state_found = true; + } + } + + // Update order of buttons according to drag'n'drop + mButtonsOrder.erase(std::find(mButtonsOrder.begin(), mButtonsOrder.end(), dragged_state)); + if (!landing_state_found && mLandingTab == getChild(PANEL_CHICLET_NAME)) + { + mButtonsOrder.push_back(dragged_state); + } + else + { + if (!landing_state_found) landing_state = RS_BUTTON_SPEAK; + mButtonsOrder.insert(std::find(mButtonsOrder.begin(), mButtonsOrder.end(), landing_state), dragged_state); + } + // Synchronize button process order with their order + resize_state_vec_t::const_iterator it1 = mButtonsOrder.begin(); + const resize_state_vec_t::const_iterator it_end1 = mButtonsOrder.end(); + resize_state_vec_t::iterator it2 = mButtonsProcessOrder.begin(); + for (; it1 != it_end1; ++it1) + { + // Skip Speak because it is not in mButtonsProcessOrder(it's the reason why mButtonsOrder was introduced). + // If any other draggable items will be added to bottomtray later, they should also be skipped here. + if (*it1 != RS_BUTTON_SPEAK) + { + *it2 = *it1; + ++it2; + } + } +} + +void LLBottomTray::onDraggableButtonMouseUp(LLUICtrl* ctrl, S32 x, S32 y, MASK mask) +{ + //if mouse up happened over area where drop is possible, change order of buttons + if (mLandingTab != NULL && mDraggedItem != NULL && mDragStarted) + { + if(isCursorOverDraggableArea(x, y)) + { + // change order of panels in layout stack + mToolbarStack->movePanel(mDraggedItem, (LLPanel*)mLandingTab); + // change order of buttons in order vectors + updateButtonsOrdersAfterDnD(); + } + } + gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROW); + mDragStarted = false; + mDraggedItem = NULL; + mLandingTab = NULL; + mCheckForDrag = false; +} + +void LLBottomTray::draw() +{ + LLPanel::draw(); + if (mLandingTab) + { + static S32 w = mImageDragIndication->getWidth(); + static S32 h = mImageDragIndication->getHeight(); + LLRect rect = mLandingTab->calcScreenRect(); + mImageDragIndication->draw(rect.mLeft - w/2, rect.getHeight(), w, h); + } +} + bool LLBottomTray::onContextMenuItemEnabled(const LLSD& userdata) { std::string item = userdata.asString(); @@ -1181,6 +1397,9 @@ void LLBottomTray::initResizeStateContainers() mButtonsProcessOrder.push_back(RS_BUTTON_WORLD_MAP); mButtonsProcessOrder.push_back(RS_BUTTON_MINI_MAP); + mButtonsOrder.push_back(RS_BUTTON_SPEAK); + mButtonsOrder.insert(mButtonsOrder.end(), mButtonsProcessOrder.begin(), mButtonsProcessOrder.end()); + // init default widths // process buttons that can be hidden on resize... diff --git a/indra/newview/llbottomtray.h b/indra/newview/llbottomtray.h index bd9d35f209..6697d6f679 100644 --- a/indra/newview/llbottomtray.h +++ b/indra/newview/llbottomtray.h @@ -47,6 +47,30 @@ class LLBottomTrayLite; extern template class LLBottomTray* LLSingleton::getInstance(); #endif +/** + * Class for buttons that should have drag'n'drop ability in bottomtray. + * These buttons pass mouse events handling to bottomtray. + */ +class LLBottomtrayButton : public LLButton +{ +public: + struct Params : public LLInitParam::Block + { + Params(){} + }; + /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); + +protected: + LLBottomtrayButton::LLBottomtrayButton(const Params& p) + : LLButton(p) + { + + } + friend class LLUICtrlFactory; +}; + class LLBottomTray : public LLSingleton , public LLPanel @@ -101,6 +125,18 @@ public: */ LLIMChiclet* createIMChiclet(const LLUUID& session_id); + // Below are methods that were introduced or overriden in bottomtray to handle drag'n'drop + + virtual void draw(); + + /** + * These three methods handle drag'n'drop, they may be called directly from child buttons. + */ + /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); + void onDraggableButtonMouseDown(LLUICtrl* button, S32 x, S32 y, MASK mask); + void onDraggableButtonMouseUp(LLUICtrl* button, S32 x, S32 y, MASK mask); + + private: typedef enum e_resize_status_type { @@ -134,6 +170,24 @@ private: | RS_BUTTON_BUILD | RS_BUTTON_SEARCH | RS_BUTTON_WORLD_MAP | RS_BUTTON_MINI_MAP }EResizeState; + // Below are three methods that were introduced to handle drag'n'drop + + /** + * finds a panel under the specified LOCAL point + */ + LLPanel* findChildPanelByLocalCoords(S32 x, S32 y); + + /** + * checks whether the cursor is over an area where the dragged button may be dropped + */ + bool isCursorOverDraggableArea(S32 x, S32 y); + + /** + * Updates process(shrink/show/hide) order of buttons and order in which they'll be stored for further save/load. + * It is called when dragged button is dropped + */ + void updateButtonsOrdersAfterDnD(); + /** * Updates child controls size and visibility when it is necessary to reduce total width. * @@ -360,6 +414,13 @@ private: * Contains order in which child buttons should be processed in show/hide, extend/shrink methods. */ resize_state_vec_t mButtonsProcessOrder; + /** + * Contains order in which child buttons are shown. + * It traces order of all bottomtray buttons that may change place via drag'n'drop and should + * save and load it between sessions. mButtonsProcessOrder is not enough for it because it contains only + * buttons that may be hidden. + */ + resize_state_vec_t mButtonsOrder; protected: @@ -381,6 +442,38 @@ protected: LLButton* mMovementButton; LLBottomTrayLite* mBottomTrayLite; bool mIsInLiteMode; + + // Drag'n'Drop + + /** + * Is true if mouse down happened on draggable button. + * Set false whether on drag start or on mouse up. + */ + bool mCheckForDrag; + /** + * These two variables hold corrdinates of mouse down on draggable button. + * They are used to compare with current coordinates of cursor and determine whether drag'n'drop should start. + */ + S32 mStartX; + S32 mStartY; + /** + * True if drag'n'drop is happening. + */ + bool mDragStarted; + + /** + * Pointer to panel which is currently dragged (though it seems to user that button is dragged, + * we are changing place of layout panel). + */ + LLPanel* mDraggedItem; + /** + * Panel before which the dragged button will be inserted. + */ + LLPanel* mLandingTab; + /** + * Image used to show position where dragged button will be dropped. + */ + LLUIImage* mImageDragIndication; }; #endif // LL_LLBOTTOMPANEL_H diff --git a/indra/newview/llnearbychatbar.cpp b/indra/newview/llnearbychatbar.cpp index 43630c13fd..a8e4a759b7 100644 --- a/indra/newview/llnearbychatbar.cpp +++ b/indra/newview/llnearbychatbar.cpp @@ -103,10 +103,10 @@ LLGestureComboList::LLGestureComboList(const LLGestureComboList::Params& p) , mViewAllItemIndex(0) , mGetMoreItemIndex(0) { - LLButton::Params button_params = p.combo_button; + LLBottomtrayButton::Params button_params = p.combo_button; button_params.follows.flags(FOLLOWS_LEFT|FOLLOWS_BOTTOM|FOLLOWS_RIGHT); - mButton = LLUICtrlFactory::create(button_params); + mButton = LLUICtrlFactory::create(button_params); mButton->reshape(getRect().getWidth(),getRect().getHeight()); mButton->setCommitCallback(boost::bind(&LLGestureComboList::onButtonCommit, this)); diff --git a/indra/newview/llnearbychatbar.h b/indra/newview/llnearbychatbar.h index 955a665624..cc905736fd 100644 --- a/indra/newview/llnearbychatbar.h +++ b/indra/newview/llnearbychatbar.h @@ -34,6 +34,7 @@ #include "llvoiceclient.h" #include "lloutputmonitorctrl.h" #include "llspeakers.h" +#include "llbottomtray.h" class LLGestureComboList @@ -43,7 +44,7 @@ class LLGestureComboList public: struct Params : public LLInitParam::Block { - Optional combo_button; + Optional combo_button; Optional combo_list; Params(); diff --git a/indra/newview/llspeakbutton.cpp b/indra/newview/llspeakbutton.cpp index b8838346d0..3dce66f394 100644 --- a/indra/newview/llspeakbutton.cpp +++ b/indra/newview/llspeakbutton.cpp @@ -37,6 +37,8 @@ #include "llspeakbutton.h" +#include "llbottomtray.h" + static LLDefaultChildRegistry::Register t1("talk_button"); ////////////////////////////////////////////////////////////////////////// @@ -67,7 +69,7 @@ void LLSpeakButton::setSpeakBtnEnabled(bool enabled) } void LLSpeakButton::setFlyoutBtnEnabled(bool enabled) { - LLButton* show_btn = getChild("speak_flyout_btn"); + LLButton* show_btn = getChild("speak_flyout_btn"); show_btn->setEnabled(enabled); } @@ -96,9 +98,9 @@ LLSpeakButton::LLSpeakButton(const Params& p) mSpeakBtn->setMouseUpCallback(boost::bind(&LLSpeakButton::onMouseUp_SpeakBtn, this)); mSpeakBtn->setToggleState(FALSE); - LLButton::Params show_params = p.show_button; + LLBottomtrayButton::Params show_params = p.show_button; show_params.rect(show_rect); - mShowBtn = LLUICtrlFactory::create(show_params); + mShowBtn = LLUICtrlFactory::create(show_params); addChild(mShowBtn); LLTransientFloaterMgr::getInstance()->addControlView(mShowBtn); diff --git a/indra/newview/llspeakbutton.h b/indra/newview/llspeakbutton.h index ec1d07b633..2fdf80c1f2 100644 --- a/indra/newview/llspeakbutton.h +++ b/indra/newview/llspeakbutton.h @@ -33,6 +33,7 @@ class LLCallFloater; class LLButton; class LLOutputMonitorCtrl; +class LLBottomtrayButton; /* * Button displaying voice chat status. Displays voice chat options when @@ -44,10 +45,8 @@ public: struct Params : public LLInitParam::Block { - Optional - speak_button, - show_button; - + Optional speak_button; + Optional show_button; Optional monitor; Params(); @@ -86,7 +85,7 @@ protected: private: LLButton* mSpeakBtn; - LLButton* mShowBtn; + LLBottomtrayButton* mShowBtn; LLHandle mPrivateCallPanel; LLOutputMonitorCtrl* mOutputMonitor; }; diff --git a/indra/newview/skins/default/xui/en/panel_bottomtray.xml b/indra/newview/skins/default/xui/en/panel_bottomtray.xml index 4b622691b3..cdd596222d 100644 --- a/indra/newview/skins/default/xui/en/panel_bottomtray.xml +++ b/indra/newview/skins/default/xui/en/panel_bottomtray.xml @@ -11,6 +11,9 @@ name="bottom_tray" top="28" width="1310"> + @@ -135,7 +138,7 @@ name="movement_panel" user_resize="false" width="83"> - + + - + - + - + - + - + - +