diff options
Diffstat (limited to 'indra/newview/llsidetray.cpp')
-rw-r--r-- | indra/newview/llsidetray.cpp | 539 |
1 files changed, 492 insertions, 47 deletions
diff --git a/indra/newview/llsidetray.cpp b/indra/newview/llsidetray.cpp index 78890fae93..7af3ad9896 100644 --- a/indra/newview/llsidetray.cpp +++ b/indra/newview/llsidetray.cpp @@ -31,6 +31,7 @@ #include "llagentcamera.h" #include "llappviewer.h" #include "llbottomtray.h" +#include "llfloaterreg.h" #include "llsidetray.h" #include "llviewerwindow.h" #include "llaccordionctrl.h" @@ -64,6 +65,7 @@ static const std::string TAB_PANEL_CAPTION_TITLE_BOX = "sidetray_tab_title"; LLSideTray* LLSideTray::sInstance = 0; +// static LLSideTray* LLSideTray::getInstance() { if (!sInstance) @@ -75,6 +77,7 @@ LLSideTray* LLSideTray::getInstance() return sInstance; } +// static bool LLSideTray::instanceCreated () { return sInstance!=0; @@ -108,7 +111,11 @@ public: }; protected: LLSideTrayTab(const Params& params); - + + void dock(); + void undock(LLFloater* floater_tab); + + LLSideTray* getSideTray(); public: virtual ~LLSideTrayTab(); @@ -125,7 +132,9 @@ public: const std::string& getTabTitle() const { return mTabTitle;} void onOpen (const LLSD& key); - + + void toggleTabDocked(); + LLPanel *getPanel(); private: std::string mTabTitle; @@ -171,6 +180,9 @@ BOOL LLSideTrayTab::postBuild() title_panel->getChild<LLTextBox>(TAB_PANEL_CAPTION_TITLE_BOX)->setValue(mTabTitle); + getChild<LLButton>("undock")->setCommitCallback(boost::bind(&LLSideTrayTab::toggleTabDocked, this)); + getChild<LLButton>("dock")->setCommitCallback(boost::bind(&LLSideTrayTab::toggleTabDocked, this)); + return true; } @@ -203,6 +215,133 @@ void LLSideTrayTab::onOpen (const LLSD& key) panel->onOpen(key); } +// Attempts to get the existing side tray instance. +// Needed to avoid recursive calls of LLSideTray::getInstance(). +LLSideTray* LLSideTrayTab::getSideTray() +{ + // First, check if the side tray is our parent (i.e. we're attached). + LLSideTray* side_tray = dynamic_cast<LLSideTray*>(getParent()); + if (!side_tray) + { + // Detached? Ok, check if the instance exists at all/ + if (LLSideTray::instanceCreated()) + { + side_tray = LLSideTray::getInstance(); + } + else + { + llerrs << "No safe way to get the side tray instance" << llendl; + } + } + + return side_tray; +} + +void LLSideTrayTab::toggleTabDocked() +{ + std::string tab_name = getName(); + + LLFloater* floater_tab = LLFloaterReg::getInstance("side_bar_tab", tab_name); + if (!floater_tab) return; + + LLFloaterReg::toggleInstance("side_bar_tab", tab_name); + + bool docking = !LLFloater::isShown(floater_tab); + + // Hide the "Tear Off" button when a tab gets undocked + // and show "Dock" button instead. + getChild<LLButton>("undock")->setVisible(docking); + getChild<LLButton>("dock")->setVisible(!docking); + + if (docking) + { + dock(); + } + else + { + undock(floater_tab); + } +} + +void LLSideTrayTab::dock() +{ + LLSideTray* side_tray = getSideTray(); + if (!side_tray) return; + + if (!side_tray->addTab(this)) + { + llwarns << "Failed to add tab " << getName() << " to side tray" << llendl; + return; + } + + setRect(side_tray->getLocalRect()); + reshape(getRect().getWidth(), getRect().getHeight()); + + // Select the re-docked tab. + side_tray->selectTabByName(getName()); + + if (side_tray->getCollapsed()) + { + side_tray->expandSideBar(); + } +} + +void LLSideTrayTab::undock(LLFloater* floater_tab) +{ + LLSideTray* side_tray = getSideTray(); + if (!side_tray) return; + + // Remove the tab from Side Tray's tabs list. + // We have to do it despite removing the tab from Side Tray's child view tree + // by addChild(). Otherwise the tab could be accessed by the pointer in LLSideTray::mTabs. + if (!side_tray->removeTab(this)) + { + llwarns << "Failed to remove tab " << getName() << " from side tray" << llendl; + return; + } + + setVisible(true); // *HACK: restore visibility after being hidden by LLSideTray::selectTabByName(). + floater_tab->addChild(this); + floater_tab->setTitle(mTabTitle); + + // Reshape the floater if needed. + LLRect floater_rect; + if (floater_tab->hasSavedRect()) + { + // We've got saved rect for the floater, hence no need to reshape it. + floater_rect = floater_tab->getLocalRect(); + } + else + { + // Detaching for the first time. Reshape the floater. + floater_rect = side_tray->getLocalRect(); + floater_tab->reshape(floater_rect.getWidth(), floater_rect.getHeight()); + } + + // Reshape the panel. + { + LLRect panel_rect = floater_rect; + panel_rect.mTop -= floater_tab->getHeaderHeight(); + setRect(panel_rect); + reshape(panel_rect.getWidth(), panel_rect.getHeight()); + } + + // Set FOLLOWS_ALL flag for the tab to follow floater dimensions upon resizing. + setFollowsAll(); + + if (!side_tray->getCollapsed()) + { + side_tray->collapseSideBar(); + } + + if (side_tray->getActiveTab() != this) + { + // When a tab other then current active tab is detached from Side Tray + // onOpen() should be called as tab visibility is changed. + onOpen(LLSD()); + } +} + LLPanel* LLSideTrayTab::getPanel() { LLPanel* panel = dynamic_cast<LLPanel*>(mMainPanel); @@ -219,6 +358,92 @@ LLSideTrayTab* LLSideTrayTab::createInstance () } ////////////////////////////////////////////////////////////////////////////// +// LLSideTrayButton +// Side Tray tab button with "tear off" handling. +////////////////////////////////////////////////////////////////////////////// + +class LLSideTrayButton : public LLButton +{ +public: + /*virtual*/ BOOL 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 class has no state that depends on it. + gFocusMgr.setMouseCapture(this); + + localPointToScreen(x, y, &mDragLastScreenX, &mDragLastScreenY); + + // Note: don't pass on to children + return TRUE; + } + + /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask) + { + // We only handle the click if the click both started and ended within us + if( !hasMouseCapture() ) return FALSE; + + S32 screen_x; + S32 screen_y; + localPointToScreen(x, y, &screen_x, &screen_y); + + S32 delta_x = screen_x - mDragLastScreenX; + S32 delta_y = screen_y - mDragLastScreenY; + + LLSideTray* side_tray = LLSideTray::getInstance(); + + // Check if the tab we are dragging is docked. + if (!side_tray->isTabAttached(getName())) return FALSE; + + // Same value is hardcoded in LLDragHandle::handleHover(). + const S32 undock_threshold = 12; + + // Detach a tab if it has been pulled further than undock_threshold. + if (delta_x <= -undock_threshold || delta_x >= undock_threshold || + delta_y <= -undock_threshold || delta_y >= undock_threshold) + { + LLSideTrayTab* tab = side_tray->getTab(getName()); + if (!tab) return FALSE; + + tab->toggleTabDocked(); + + LLFloater* floater_tab = LLFloaterReg::getInstance("side_bar_tab", tab->getName()); + if (!floater_tab) return FALSE; + + LLRect original_rect = floater_tab->getRect(); + S32 header_snap_y = floater_tab->getHeaderHeight() / 2; + S32 snap_x = screen_x - original_rect.mLeft - original_rect.getWidth() / 2; + S32 snap_y = screen_y - original_rect.mTop + header_snap_y; + + // Move the floater to appear "under" the mouse pointer. + floater_tab->setRect(original_rect.translate(snap_x, snap_y)); + + // Snap the mouse pointer to the center of the floater header + // and call 'mouse down' event handler to begin dragging. + floater_tab->handleMouseDown(original_rect.getWidth() / 2, + original_rect.getHeight() - header_snap_y, + mask); + + return TRUE; + } + + return FALSE; + } + +protected: + LLSideTrayButton(const LLButton::Params& p) + : LLButton(p) + , mDragLastScreenX(0) + , mDragLastScreenY(0) + {} + + friend class LLUICtrlFactory; + +private: + S32 mDragLastScreenX; + S32 mDragLastScreenY; +}; + +////////////////////////////////////////////////////////////////////////////// // LLSideTray ////////////////////////////////////////////////////////////////////////////// @@ -273,6 +498,13 @@ BOOL LLSideTray::postBuild() LLAppViewer::instance()->setOnLoginCompletedCallback(boost::bind(&LLSideTray::handleLoginComplete, this)); + // Remember original tabs order, so that we can restore it if user detaches and then re-attaches a tab. + for (child_vector_const_iter_t it = mTabs.begin(); it != mTabs.end(); ++it) + { + std::string tab_name = (*it)->getName(); + mOriginalTabOrder.push_back(tab_name); + } + //EXT-8045 //connect all already created channels to reflect sidetray collapse/expand std::vector<LLChannelManager::ChannelElem>& channels = LLChannelManager::getInstance()->getChannelList(); @@ -283,6 +515,7 @@ BOOL LLSideTray::postBuild() getCollapseSignal().connect(boost::bind(&LLScreenChannelBase::resetPositionAndSize, (*it).channel, _2)); } } + return true; } @@ -290,13 +523,28 @@ void LLSideTray::handleLoginComplete() { //reset tab to "home" tab if it was changesd during login process selectTabByName("sidebar_home"); + + detachTabs(); } LLSideTrayTab* LLSideTray::getTab(const std::string& name) { - return getChild<LLSideTrayTab>(name,false); + return findChild<LLSideTrayTab>(name,false); +} + +bool LLSideTray::isTabAttached(const std::string& name) +{ + LLSideTrayTab* tab = getTab(name); + if (!tab) return false; + + return std::find(mTabs.begin(), mTabs.end(), tab) != mTabs.end(); } +bool LLSideTray::hasTabs() +{ + // The open/close tab doesn't count. + return mTabs.size() > 1; +} void LLSideTray::toggleTabButton(LLSideTrayTab* tab) { @@ -313,6 +561,54 @@ void LLSideTray::toggleTabButton(LLSideTrayTab* tab) } } +LLPanel* LLSideTray::openChildPanel(LLSideTrayTab* tab, const std::string& panel_name, const LLSD& params) +{ + LLView* view = tab->findChildView(panel_name, true); + if (!view) return NULL; + + std::string tab_name = tab->getName(); + + // Select tab and expand Side Tray only when a tab is attached. + if (isTabAttached(tab_name)) + { + selectTabByName(tab_name); + if (mCollapsed) + expandSideBar(); + } + else + { + LLFloater* floater_tab = LLFloaterReg::getInstance("side_bar_tab", tab_name); + if (!floater_tab) return NULL; + + // Restore the floater if it was minimized. + if (floater_tab->isMinimized()) + { + floater_tab->setMinimized(FALSE); + } + + // Send the floater to the front. + floater_tab->setFrontmost(); + } + + LLSideTrayPanelContainer* container = dynamic_cast<LLSideTrayPanelContainer*>(view->getParent()); + if (container) + { + LLSD new_params = params; + new_params[LLSideTrayPanelContainer::PARAM_SUB_PANEL_NAME] = panel_name; + container->onOpen(new_params); + + return container->getCurrentPanel(); + } + + LLPanel* panel = dynamic_cast<LLPanel*>(view); + if (panel) + { + panel->onOpen(params); + } + + return panel; +} + bool LLSideTray::selectTabByIndex(size_t index) { if(index>=mTabs.size()) @@ -324,22 +620,28 @@ bool LLSideTray::selectTabByIndex(size_t index) bool LLSideTray::selectTabByName (const std::string& name) { - LLSideTrayTab* side_bar = getTab(name); + LLSideTrayTab* new_tab = getTab(name); + if (!new_tab) return false; - if(side_bar == mActiveTab) + // Bail out if already selected. + if (new_tab == mActiveTab) return false; + //deselect old tab - toggleTabButton(mActiveTab); - if(mActiveTab) - mActiveTab->setVisible(false); + if (mActiveTab) + { + toggleTabButton(mActiveTab); + } //select new tab - mActiveTab = side_bar; - toggleTabButton(mActiveTab); - LLSD key;//empty - mActiveTab->onOpen(key); + mActiveTab = new_tab; - mActiveTab->setVisible(true); + if (mActiveTab) + { + toggleTabButton(mActiveTab); + LLSD key;//empty + mActiveTab->onOpen(key); + } //arrange(); @@ -348,7 +650,9 @@ bool LLSideTray::selectTabByName (const std::string& name) for ( child_it = mTabs.begin(); child_it != mTabs.end(); ++child_it) { LLSideTrayTab* sidebar_tab = *child_it; - sidebar_tab->setVisible(sidebar_tab == mActiveTab); + // When the last tab gets detached, for a short moment the "Toggle Sidebar" pseudo-tab + // is shown. So, to avoid the flicker we make sure it never gets visible. + sidebar_tab->setVisible(sidebar_tab == mActiveTab && (*child_it)->getName() != "sidebar_openclose"); } return true; } @@ -372,8 +676,18 @@ LLButton* LLSideTray::createButton (const std::string& name,const std::string& i bparams.image_disabled(sidetray_params.tab_btn_image_normal); bparams.image_disabled_selected(sidetray_params.tab_btn_image_selected); - LLButton* button = LLUICtrlFactory::create<LLButton> (bparams); - button->setLabel(name); + LLButton* button; + if (name == "sidebar_openclose") + { + // "Open/Close" button shouldn't allow "tear off" + // hence it is created as LLButton instance. + button = LLUICtrlFactory::create<LLButton>(bparams); + } + else + { + button = LLUICtrlFactory::create<LLSideTrayButton>(bparams); + } + button->setClickedCallback(callback); button->setToolTip(tooltip); @@ -400,6 +714,113 @@ bool LLSideTray::addChild(LLView* view, S32 tab_group) return LLUICtrl::addChild(view, tab_group); } +bool LLSideTray::removeTab(LLSideTrayTab* tab) +{ + if (!tab) return false; + std::string tab_name = tab->getName(); + + // Look up the tab in the list of known tabs. + child_vector_iter_t tab_it = std::find(mTabs.begin(), mTabs.end(), tab); + if (tab_it == mTabs.end()) + { + llwarns << "Cannot find tab named " << tab_name << llendl; + return false; + } + + // Find the button corresponding to the tab. + button_map_t::iterator btn_it = mTabButtons.find(tab_name); + if (btn_it == mTabButtons.end()) + { + llwarns << "Cannot find button for tab named " << tab_name << llendl; + return false; + } + LLButton* btn = btn_it->second; + + // Deselect the tab. + if (mActiveTab == tab) + { + child_vector_iter_t next_tab_it = + (tab_it < (mTabs.end() - 1)) ? tab_it + 1 : mTabs.begin(); + selectTabByName((*next_tab_it)->getName()); + } + + // Remove the tab. + removeChild(tab); + mTabs.erase(tab_it); + + // Add the tab to detached tabs list. + mDetachedTabs.push_back(tab); + + // Remove the button from the buttons panel so that it isn't drawn anymore. + mButtonsPanel->removeChild(btn); + + // Re-arrange remaining tabs. + arrange(); + + return true; +} + +bool LLSideTray::addTab(LLSideTrayTab* tab) +{ + if (tab == NULL) return false; + + std::string tab_name = tab->getName(); + + // Make sure the tab isn't already in the list. + if (std::find(mTabs.begin(), mTabs.end(), tab) != mTabs.end()) + { + llwarns << "Attempt to re-add existing tab " << tab_name << llendl; + return false; + } + + // Look up the corresponding button. + button_map_t::const_iterator btn_it = mTabButtons.find(tab_name); + if (btn_it == mTabButtons.end()) + { + llwarns << "Tab " << tab_name << " has no associated button" << llendl; + return false; + } + LLButton* btn = btn_it->second; + + // Insert the tab at its original position. + LLUICtrl::addChild(tab); + { + tab_order_vector_const_iter_t new_tab_orig_pos = + std::find(mOriginalTabOrder.begin(), mOriginalTabOrder.end(), tab_name); + llassert(new_tab_orig_pos != mOriginalTabOrder.end()); + child_vector_iter_t insert_pos = mTabs.end(); + + for (child_vector_iter_t tab_it = mTabs.begin(); tab_it != mTabs.end(); ++tab_it) + { + tab_order_vector_const_iter_t cur_tab_orig_pos = + std::find(mOriginalTabOrder.begin(), mOriginalTabOrder.end(), (*tab_it)->getName()); + llassert(cur_tab_orig_pos != mOriginalTabOrder.end()); + + if (new_tab_orig_pos < cur_tab_orig_pos) + { + insert_pos = tab_it; + break; + } + } + + mTabs.insert(insert_pos, tab); + } + + // Add the button to the buttons panel so that it's drawn again. + mButtonsPanel->addChildInBack(btn); + + // Arrange tabs after inserting a new one. + arrange(); + + // Remove the tab from the list of detached tabs. + child_vector_iter_t tab_it = std::find(mDetachedTabs.begin(), mDetachedTabs.end(), tab); + if (tab_it != mDetachedTabs.end()) + { + mDetachedTabs.erase(tab_it); + } + + return true; +} void LLSideTray::createButtons () { @@ -414,12 +835,12 @@ void LLSideTray::createButtons () // The "OpenClose" button will open/close the whole panel if (name == "sidebar_openclose") { - mCollapseButton = createButton("",sidebar_tab->mImage,sidebar_tab->getTabTitle(), + mCollapseButton = createButton(name,sidebar_tab->mImage,sidebar_tab->getTabTitle(), boost::bind(&LLSideTray::onToggleCollapse, this)); } else { - LLButton* button = createButton("",sidebar_tab->mImage,sidebar_tab->getTabTitle(), + LLButton* button = createButton(name,sidebar_tab->mImage,sidebar_tab->getTabTitle(), boost::bind(&LLSideTray::onTabButtonClick, this, name)); mTabButtons[name] = button; } @@ -447,8 +868,10 @@ void LLSideTray::processTriState () void LLSideTray::onTabButtonClick(string name) { - LLSideTrayTab* side_bar = getTab(name); - if(side_bar == mActiveTab) + LLSideTrayTab* tab = getTab(name); + if (!tab) return; + + if(tab == mActiveTab) { processTriState (); return; @@ -536,6 +959,31 @@ void LLSideTray::arrange() LLSideTrayTab* sidebar_tab = *child_it; sidebar_tab->setShape(getLocalRect()); } + + // The tab buttons should be shown only if there is at least one non-detached tab. + mButtonsPanel->setVisible(hasTabs()); +} + +// Detach those tabs that were detached when the viewer exited last time. +void LLSideTray::detachTabs() +{ + // copy mTabs because LLSideTray::toggleTabDocked() modifies it. + child_vector_t tabs = mTabs; + + for (child_vector_const_iter_t it = tabs.begin(); it != tabs.end(); ++it) + { + LLSideTrayTab* tab = *it; + + std::string floater_ctrl_name = LLFloater::getControlName("side_bar_tab", LLSD(tab->getName())); + std::string vis_ctrl_name = LLFloaterReg::getVisibilityControlName(floater_ctrl_name); + if (!LLUI::sSettingGroups["floater"]->controlExists(vis_ctrl_name)) continue; + + bool is_visible = LLUI::sSettingGroups["floater"]->getBOOL(vis_ctrl_name); + if (!is_visible) continue; + + llassert(isTabAttached(tab->getName())); + tab->toggleTabDocked(); + } } void LLSideTray::collapseSideBar() @@ -630,35 +1078,19 @@ void LLSideTray::reshape(S32 width, S32 height, BOOL called_from_parent) */ LLPanel* LLSideTray::showPanel (const std::string& panel_name, const LLSD& params) { - //arrange tabs + // Look up the tab in the list of detached tabs. child_vector_const_iter_t child_it; - for ( child_it = mTabs.begin(); child_it != mTabs.end(); ++child_it) + for ( child_it = mDetachedTabs.begin(); child_it != mDetachedTabs.end(); ++child_it) { - LLView* view = (*child_it)->findChildView(panel_name,true); - if(view) - { - selectTabByName ((*child_it)->getName()); - if(mCollapsed) - expandSideBar(); - - LLSideTrayPanelContainer* container = dynamic_cast<LLSideTrayPanelContainer*>(view->getParent()); - if(container) - { - LLSD new_params = params; - new_params[LLSideTrayPanelContainer::PARAM_SUB_PANEL_NAME] = panel_name; - container->onOpen(new_params); - - return container->getCurrentPanel(); - } - - LLPanel* panel = dynamic_cast<LLPanel*>(view); - if(panel) - { - panel->onOpen(params); - } + LLPanel* panel = openChildPanel(*child_it, panel_name, params); + if (panel) return panel; + } - return panel; - } + // Look up the tab in the list of attached tabs. + for ( child_it = mTabs.begin(); child_it != mTabs.end(); ++child_it) + { + LLPanel* panel = openChildPanel(*child_it, panel_name, params); + if (panel) return panel; } return NULL; } @@ -668,7 +1100,9 @@ void LLSideTray::togglePanel(LLPanel* &sub_panel, const std::string& panel_name, if(!sub_panel) return; - if (sub_panel->isInVisibleChain()) + // If a panel is visible and attached to Side Tray (has LLSideTray among its ancestors) + // it should be toggled off by collapsing Side Tray. + if (sub_panel->isInVisibleChain() && sub_panel->hasAncestor(this)) { LLSideTray::getInstance()->collapseSideBar(); } @@ -712,6 +1146,17 @@ LLPanel *findChildPanel(LLPanel *panel, const std::string& name, bool recurse) LLPanel* LLSideTray::getPanel(const std::string& panel_name) { + // Look up the panel in the list of detached tabs. + for ( child_vector_const_iter_t child_it = mDetachedTabs.begin(); child_it != mDetachedTabs.end(); ++child_it) + { + LLPanel *panel = findChildPanel(*child_it,panel_name,true); + if(panel) + { + return panel; + } + } + + // Look up the panel in the list of attached tabs. for ( child_vector_const_iter_t child_it = mTabs.begin(); child_it != mTabs.end(); ++child_it) { LLPanel *panel = findChildPanel(*child_it,panel_name,true); |