Skip to content
Snippets Groups Projects
Select Git revision
  • main default protected
  • next protected
  • UI-EvenMoreTweaks
  • merge/materials_featurette protected
  • merge/webrtc protected
  • darl/linux-sh-installer
  • xenhat/maint/bolt
  • xenhat/features/cinematic-mode-new
  • screensquare
  • ssestuff
  • spdlog
  • 7.1.7.2486-beta
  • 7.1.4.2442-beta
  • 7.1.4.2413-beta
  • 7.1.3.2338-beta
  • 7.1.3.2332-beta
  • 7.1.2.2304-beta
  • 7.1.1.2251-beta
  • 7.0.1.2244-beta
  • 7.0.1.2240-beta
  • 7.0.1.2230-beta
  • 7.0.1.2206-beta
22 results

llsidetray.cpp

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    llsidetray.cpp 37.92 KiB
    /** 
     * @file llsidetray.cpp
     * @brief SideBar implementation
     *
     * $LicenseInfo:firstyear=2009&license=viewerlgpl$
     * Second Life Viewer Source Code
     * Copyright (C) 2010, Linden Research, Inc.
     * 
     * This library is free software; you can redistribute it and/or
     * modify it under the terms of the GNU Lesser General Public
     * License as published by the Free Software Foundation;
     * version 2.1 of the License only.
     * 
     * This library is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     * Lesser General Public License for more details.
     * 
     * You should have received a copy of the GNU Lesser General Public
     * License along with this library; if not, write to the Free Software
     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
     * 
     * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
     * $/LicenseInfo$
     */
    
    #include "llviewerprecompiledheaders.h"
    
    #include "lltextbox.h"
    
    #include "llagentcamera.h"
    #include "llappviewer.h"
    #include "llbadge.h"
    #include "llbottomtray.h"
    #include "llfloaterreg.h"
    #include "llfirstuse.h"
    #include "llhints.h"
    #include "llsidetray.h"
    #include "llviewerwindow.h"
    #include "llaccordionctrl.h"
    #include "llfocusmgr.h"
    #include "llrootview.h"
    #include "llnavigationbar.h"
    #include "llpanelmarketplaceinbox.h"
    
    #include "llaccordionctrltab.h"
    
    #include "llfloater.h" //for gFloaterView
    #include "lliconctrl.h"//for OpenClose tab icon
    #include "llsidetraypanelcontainer.h"
    #include "llscreenchannel.h"
    #include "llchannelmanager.h"
    #include "llwindow.h"//for SetCursor
    #include "lltransientfloatermgr.h"
    
    #include "llsidepanelappearance.h"
    
    #include "llsidetraylistener.h"
    
    //#include "llscrollcontainer.h"
    
    using namespace std;
    using namespace LLNotificationsUI;
    
    static LLRootViewRegistry::Register<LLSideTray>	t1("side_tray");
    static LLDefaultChildRegistry::Register<LLSideTrayTab>	t2("sidetray_tab");
    
    static const S32 BOTTOM_BAR_PAD = 5;
    
    static const std::string COLLAPSED_NAME = "<<";
    static const std::string EXPANDED_NAME  = ">>";
    
    static const std::string TAB_PANEL_CAPTION_NAME = "sidetray_tab_panel";
    static const std::string TAB_PANEL_CAPTION_TITLE_BOX = "sidetray_tab_title";
    
    LLSideTray* LLSideTray::sInstance = 0;
    
    static LLSideTrayListener sSideTrayListener(LLSideTray::getInstance);
    
    // static
    LLSideTray* LLSideTray::getInstance()
    {
    	if (!sInstance)
    	{
    		sInstance = LLUICtrlFactory::createFromFile<LLSideTray>("panel_side_tray.xml",NULL, LLRootView::child_registry_t::instance());
    		sInstance->setXMLFilename("panel_side_tray.xml");
    	}
    
    	return sInstance;
    }
    
    // static
    bool	LLSideTray::instanceCreated	()
    {
    	return sInstance!=0;
    }
    
    //////////////////////////////////////////////////////////////////////////////
    // LLSideTrayTab
    // Represents a single tab in the side tray, only used by LLSideTray
    //////////////////////////////////////////////////////////////////////////////
    
    class LLSideTrayTab: public LLPanel
    {
    	LOG_CLASS(LLSideTrayTab);
    	friend class LLUICtrlFactory;
    	friend class LLSideTray;
    public:
    	
    	struct Params 
    	:	public LLInitParam::Block<Params, LLPanel::Params>
    	{
    		// image name
    		Optional<std::string>		image;
    		Optional<std::string>		image_selected;
    		Optional<std::string>		tab_title;
    		Optional<std::string>		description;
    		Optional<LLBadge::Params>	badge;
    		
    		Params()
    		:	image("image"),
    			image_selected("image_selected"),
    			tab_title("tab_title","no title"),
    			description("description","no description"),
    			badge("badge")
    		{};
    	};
    protected:
    	LLSideTrayTab(const Params& params);
    	
    	void			dock(LLFloater* floater_tab);
    	void			undock(LLFloater* floater_tab);
    
    	LLSideTray*		getSideTray();
    	
    public:
    	virtual ~LLSideTrayTab();
    	
        /*virtual*/ BOOL	postBuild	();
    	/*virtual*/ bool	addChild	(LLView* view, S32 tab_group);
    	
    	
    	void			reshape		(S32 width, S32 height, BOOL called_from_parent = TRUE);
    	
    	static LLSideTrayTab*  createInstance	();
    	
    	const std::string& getDescription () const { return mDescription;}
    	
    	void			onOpen		(const LLSD& key);
    	
    	void			toggleTabDocked(bool toggle_floater = true);
    	void			setDocked(bool dock);
    	bool			isDocked() const;
    
    	BOOL			handleScrollWheel(S32 x, S32 y, S32 clicks);
    
    	LLPanel*		getPanel();
    
    	LLButton*		createButton(bool allowTearOff, LLUICtrl::commit_callback_t callback);
    
    private:
    	std::string mTabTitle;
    	std::string mImage;
    	std::string mImageSelected;
    	std::string	mDescription;
    	
    	LLView*	mMainPanel;
    
    	bool			mHasBadge;
    	LLBadge::Params	mBadgeParams;
    };
    
    LLSideTrayTab::LLSideTrayTab(const Params& p)
    :	LLPanel(),
    	mTabTitle(p.tab_title),
    	mImage(p.image),
    	mImageSelected(p.image_selected),
    	mDescription(p.description),
    	mMainPanel(NULL),
    	mBadgeParams(p.badge)
    {
    	mHasBadge = p.badge.isProvided();
    }
    
    LLSideTrayTab::~LLSideTrayTab()
    {
    }
    
    bool LLSideTrayTab::addChild(LLView* view, S32 tab_group)
    {
    	if(mMainPanel == 0 && TAB_PANEL_CAPTION_NAME != view->getName())//skip our caption panel
    		mMainPanel = view;
    	return LLPanel::addChild(view,tab_group);
    	//return res;
    }
    
    //virtual 
    BOOL LLSideTrayTab::postBuild()
    {
    	LLPanel* title_panel = LLUICtrlFactory::getInstance()->createFromFile<LLPanel>("panel_side_tray_tab_caption.xml",this, child_registry_t::instance());
    	string name = title_panel->getName();
    	LLPanel::addChild(title_panel);
    	
    	title_panel->getChild<LLTextBox>(TAB_PANEL_CAPTION_TITLE_BOX)->setValue(mTabTitle);
    
    	getChild<LLButton>("undock")->setCommitCallback(boost::bind(&LLSideTrayTab::setDocked, this, false));
    	getChild<LLButton>("dock")->setCommitCallback(boost::bind(&LLSideTrayTab::setDocked, this, true));
    
    	return LLPanel::postBuild();
    }
    
    static const S32 splitter_margin = 1;
    
    void LLSideTrayTab::reshape		(S32 width, S32 height, BOOL called_from_parent )
    {
    	LLPanel::reshape(width, height, called_from_parent);
    	LLView* title_panel = findChildView(TAB_PANEL_CAPTION_NAME, true);
    	if (!title_panel)
    	{
    		// not fully constructed yet
    		return;
    	}
    
    	S32 title_height = title_panel->getRect().getHeight();
    	title_panel->setOrigin( 0, height - title_height );
    	title_panel->reshape(width,title_height);
    
    	LLRect sRect;
    	sRect.setLeftTopAndSize( splitter_margin, height - title_height - splitter_margin, 
    							width - 2*splitter_margin, height - title_height - 2*splitter_margin);
    	mMainPanel->setShape(sRect);
    }
    
    void	LLSideTrayTab::onOpen		(const LLSD& key)
    {
    	LLPanel *panel = getPanel();
    	if(panel)
    		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(bool toggle_floater /* = true */)
    {
    	// *FIX: Calling this method twice per frame would crash the viewer.
    
    	std::string tab_name = getName();
    
    	LLFloater* floater_tab = LLFloaterReg::getInstance("side_bar_tab", tab_name);
    	if (!floater_tab) return;
    
    	bool docking = !isDocked();
    
    	// 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(floater_tab);
    	}
    	else
    	{
    		undock(floater_tab);
    	}
    
    	// Open/close the floater *after* we reparent the tab panel,
    	// so that it doesn't receive redundant visibility change notifications.
    	if (toggle_floater)
    	{
    		LLFloaterReg::toggleInstance("side_bar_tab", tab_name);
    	}
    }
    
    // Same as toggleTabDocked() apart from making sure that we do exactly what we want.
    void LLSideTrayTab::setDocked(bool dock)
    {
    	if (isDocked() == dock)
    	{
    		llwarns << "Tab " << getName() << " is already " << (dock ? "docked" : "undocked") << llendl;
    		return;
    	}
    
    	toggleTabDocked();
    }
    
    bool LLSideTrayTab::isDocked() const
    {
    	return dynamic_cast<LLSideTray*>(getParent()) != NULL;
    }
    
    BOOL LLSideTrayTab::handleScrollWheel(S32 x, S32 y, S32 clicks)
    {
    	// Let children handle the event
    	LLUICtrl::handleScrollWheel(x, y, clicks);
    
    	// and then eat it to prevent in-world scrolling (STORM-351).
    	return TRUE;
    }
    
    void LLSideTrayTab::dock(LLFloater* floater_tab)
    {
    	LLSideTray* side_tray = getSideTray();
    	if (!side_tray) return;
    
    	// Before docking the tab, reset its (and its children's) transparency to default (STORM-688).
    	floater_tab->updateTransparency(TT_DEFAULT);
    
    	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(false);
    	}
    }
    
    static void on_minimize(LLSidepanelAppearance* panel, LLSD minimized)
    {
    	if (!panel) return;
    	bool visible = !minimized.asBoolean();
    	LLSD visibility;
    	visibility["visible"] = visible;
    	// Do not reset accordion state on minimize (STORM-375)
    	visibility["reset_accordion"] = false;
    	panel->updateToVisibility(visibility);
    }
    
    void LLSideTrayTab::undock(LLFloater* floater_tab)
    {
    	LLSideTray* side_tray = getSideTray();
    	if (!side_tray) return;
    
    	// Remember whether the tab have been active before detaching
    	// because removeTab() will change active tab.
    	bool was_active = side_tray->getActiveTab() == this;
    
    	// 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;
    	}
    
    	// If we're undocking while side tray is collapsed we need to explicitly show the panel.
    	if (!getVisible())
    	{
    		setVisible(true);
    	}
    
    	floater_tab->addChild(this);
    	floater_tab->setTitle(mTabTitle);
    	floater_tab->setName(getName());
    
    	// Resize handles get obscured by added panel so move them to front.
    	floater_tab->moveResizeHandlesToFront();
    
    	// 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();
    
    		// Reduce detached floater height by small BOTTOM_BAR_PAD not to make it flush with the bottom bar.
    		floater_rect.mBottom += LLBottomTray::getInstance()->getRect().getHeight() + BOTTOM_BAR_PAD;
    		floater_rect.makeValid();
    		floater_tab->reshape(floater_rect.getWidth(), floater_rect.getHeight());
    	}
    
    	// Reshape the panel.
    	{
    		LLRect panel_rect = floater_tab->getLocalRect();
    		panel_rect.mTop -= floater_tab->getHeaderHeight();
    		panel_rect.makeValid();
    		setRect(panel_rect);
    		reshape(panel_rect.getWidth(), panel_rect.getHeight());
    	}
    
    	// Set FOLLOWS_ALL flag for the tab to follow floater dimensions upon resizing.
    	setFollowsAll();
    
    	// Camera view may need to be changed for appearance panel(STORM-301) on minimize of floater,
    	// so setting callback here. 
    	if (getName() == "sidebar_appearance")
    	{
    		LLSidepanelAppearance* panel_appearance = dynamic_cast<LLSidepanelAppearance*>(getPanel());
    		if(panel_appearance)
    		{
    			floater_tab->setMinimizeCallback(boost::bind(&on_minimize, panel_appearance, _2));
    		}
    	}
    
    	if (!side_tray->getCollapsed())
    	{
    		side_tray->collapseSideBar();
    	}
    
    	if (!was_active)
    	{
    		// 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);
    	return panel;
    }
    
    LLSideTrayTab*  LLSideTrayTab::createInstance	()
    {
    	LLSideTrayTab::Params tab_params; 
    	tab_params.tab_title("openclose");
    
    	LLSideTrayTab* tab = LLUICtrlFactory::create<LLSideTrayTab>(tab_params);
    	return tab;
    }
    
    // Now that we know the definition of LLSideTrayTab, we can implement
    // tab_cast.
    template <>
    LLPanel* tab_cast<LLPanel*>(LLSideTrayTab* tab) { return tab; }
    
    //////////////////////////////////////////////////////////////////////////////
    // 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(mTabName)) 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(mTabName);
    			if (!tab) return FALSE;
    
    			tab->setDocked(false);
    
    			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;
    	}
    
    	void setBadgeDriver(LLSideTrayTabBadgeDriver* driver)
    	{
    		mBadgeDriver = driver;
    	}
    
    protected:
    	LLSideTrayButton(const LLButton::Params& p)
    		: LLButton(p)
    		, mDragLastScreenX(0)
    		, mDragLastScreenY(0)
    		, mBadgeDriver(NULL)
    	{
    		// Find out the tab name to use in handleHover().
    		size_t pos = getName().find("_button");
    		llassert(pos != std::string::npos);
    		mTabName = getName().substr(0, pos);
    	}
    
    	friend class LLUICtrlFactory;
    
    	void draw()
    	{
    		if (mBadgeDriver)
    		{
    			setBadgeLabel(mBadgeDriver->getBadgeString());
    		}
    
    		LLButton::draw();
    	}
    
    private:
    	S32		mDragLastScreenX;
    	S32		mDragLastScreenY;
    
    	std::string					mTabName;
    	LLSideTrayTabBadgeDriver*	mBadgeDriver;
    };
    
    //////////////////////////////////////////////////////////////////////////////
    // LLSideTray
    //////////////////////////////////////////////////////////////////////////////
    
    LLSideTray::Params::Params()
    :	collapsed("collapsed",false),
    	tab_btn_image_normal("tab_btn_image",LLUI::getUIImage("taskpanel/TaskPanel_Tab_Off.png")),
    	tab_btn_image_selected("tab_btn_image_selected",LLUI::getUIImage("taskpanel/TaskPanel_Tab_Selected.png")),
    	default_button_width("tab_btn_width",32),
    	default_button_height("tab_btn_height",32),
    	default_button_margin("tab_btn_margin",0)
    {}
    
    //virtual 
    LLSideTray::LLSideTray(const Params& params)
    	   : LLPanel(params)
    	    ,mActiveTab(0)
    		,mCollapsed(false)
    		,mCollapseButton(0)
    {
    	mCollapsed=params.collapsed;
    
    	LLUICtrl::CommitCallbackRegistry::Registrar& commit = LLUICtrl::CommitCallbackRegistry::currentRegistrar();
    
    	// register handler function to process data from the xml. 
    	// panel_name should be specified via "parameter" attribute.
    	commit.add("SideTray.ShowPanel", boost::bind(&LLSideTray::showPanel, this, _2, LLUUID::null));
    	commit.add("SideTray.Toggle", boost::bind(&LLSideTray::onToggleCollapse, this));
    	commit.add("SideTray.Collapse", boost::bind(&LLSideTray::collapseSideBar, this));
    	LLTransientFloaterMgr::getInstance()->addControlView(this);
    	LLView* side_bar_tabs  = gViewerWindow->getRootView()->getChildView("side_bar_tabs");
    	if (side_bar_tabs != NULL)
    	{
    		LLTransientFloaterMgr::getInstance()->addControlView(side_bar_tabs);
    	}
    
    	LLPanel::Params p;
    	p.name = "buttons_panel";
    	p.mouse_opaque = false;
    	mButtonsPanel = LLUICtrlFactory::create<LLPanel>(p);
    }
    
    
    BOOL LLSideTray::postBuild()
    {
    	createButtons();
    
    	arrange();
    	selectTabByName("sidebar_home");
    
    	if(mCollapsed)
    		collapseSideBar();
    
    	setMouseOpaque(false);
    
    	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();
    	for(std::vector<LLChannelManager::ChannelElem>::iterator it = channels.begin();it!=channels.end();++it)
    	{
    		if ((*it).channel)
    		{
    			setVisibleWidthChangeCallback(boost::bind(&LLScreenChannelBase::resetPositionAndSize, (*it).channel));
    		}
    	}
    
    	return true;
    }
    
    void LLSideTray::setTabButtonBadgeDriver(std::string tabName, LLSideTrayTabBadgeDriver* driver)
    {
    	mTabButtonBadgeDrivers[tabName] = driver;
    }
    
    void LLSideTray::handleLoginComplete()
    {
    	//reset tab to "home" tab if it was changesd during login process
    	selectTabByName("sidebar_home");
    
    	for (badge_map_t::iterator it = mTabButtonBadgeDrivers.begin(); it != mTabButtonBadgeDrivers.end(); ++it)
    	{
    		LLButton* button = mTabButtons[it->first];
    		LLSideTrayButton* side_button = dynamic_cast<LLSideTrayButton*>(button);
    
    		if (side_button)
    		{
    			side_button->setBadgeDriver(it->second);
    		}
    		else
    		{
    			llwarns << "Unable to find button " << it->first << " to set the badge driver. " << llendl;
    		}
    	}
    
    	detachTabs();
    }
    
    LLSideTrayTab* LLSideTray::getTab(const std::string& name)
    {
    	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)
    {
    	if(tab == NULL)
    		return;
    	std::string name = tab->getName();
    	std::map<std::string,LLButton*>::iterator it = mTabButtons.find(name);
    	if(it != mTabButtons.end())
    	{
    		LLButton* btn = it->second;
    		bool new_state = !btn->getToggleState();
    		btn->setToggleState(new_state); 
    		// Only highlight the tab if side tray is expanded (STORM-157).
    		btn->setImageOverlay( new_state && !getCollapsed() ? tab->mImageSelected : tab->mImage );
    	}
    }
    
    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();
    
    	bool tab_attached = isTabAttached(tab_name);
    
    	if (tab_attached && LLUI::sSettingGroups["config"]->getBOOL("OpenSidePanelsInFloaters"))
    	{
    		tab->setDocked(false);
    		tab_attached = false;
    	}
    
    	// Select tab and expand Side Tray only when a tab is attached.
    	if (tab_attached)
    	{
    		selectTabByName(tab_name);
    		if (mCollapsed)
    			expandSideBar();
    	}
    	else
    	{
    		LLFloater* floater_tab = LLFloaterReg::getInstance("side_bar_tab", tab_name);
    		if (!floater_tab) return NULL;
    
    		floater_tab->openFloater(tab_name);
    	}
    
    	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())
    		return false;
    
    	LLSideTrayTab* sidebar_tab = mTabs[index];
    	return selectTabByName(sidebar_tab->getName());
    }
    
    bool LLSideTray::selectTabByName(const std::string& name, bool keep_prev_visible)
    {
    	LLSideTrayTab* tab_to_keep_visible = NULL;
    	LLSideTrayTab* new_tab = getTab(name);
    	if (!new_tab) return false;
    
    	// Bail out if already selected.
    	if (new_tab == mActiveTab)
    		return false;
    
    	//deselect old tab
    	if (mActiveTab)
    	{
    		// Keep previously active tab visible if requested.
    		if (keep_prev_visible) tab_to_keep_visible = mActiveTab;
    	toggleTabButton(mActiveTab);
    	}
    
    	//select new tab
    	mActiveTab = new_tab;
    
    	if (mActiveTab)
    	{
    	toggleTabButton(mActiveTab);
    	LLSD key;//empty
    	mActiveTab->onOpen(key);
    	}
    
    	//arrange();
    	
    	//hide all tabs - show active tab
    	child_vector_const_iter_t child_it;
    	for ( child_it = mTabs.begin(); child_it != mTabs.end(); ++child_it)
    	{
    		LLSideTrayTab* sidebar_tab = *child_it;
    
    		bool vis = sidebar_tab == mActiveTab;
    
    		// Force keeping the tab visible if requested.
    		vis |= sidebar_tab == tab_to_keep_visible;
    
    		// 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.
    		vis &= (*child_it)->getName() != "sidebar_openclose";
    
    		sidebar_tab->setVisible(vis);
    	}
    	return true;
    }
    
    bool LLSideTray::addChild(LLView* view, S32 tab_group)
    {
    	LLSideTrayTab* tab_panel = dynamic_cast<LLSideTrayTab*>(view);
    
    	if (tab_panel)
    	{
    		mTabs.push_back(tab_panel);
    	}
    	
    	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)
    	{
    		// Select the next tab (or first one, if we're removing the last tab),
    		// skipping the fake open/close tab (STORM-155).
    		child_vector_iter_t next_tab_it = tab_it;
    		do
    		{
    			next_tab_it = (next_tab_it < (mTabs.end() - 1)) ? next_tab_it + 1 : mTabs.begin();
    		}
    		while ((*next_tab_it)->getName() == "sidebar_openclose");
    
    		selectTabByName((*next_tab_it)->getName(), true); // Don't hide the tab being removed.
    	}
    
    	// 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;
    }
    
    LLButton* LLSideTrayTab::createButton(bool allowTearOff, LLUICtrl::commit_callback_t callback)
    {
    	static LLSideTray::Params sidetray_params(LLUICtrlFactory::getDefaultParams<LLSideTray>());	
    
    	LLRect rect;
    	rect.setOriginAndSize(0, 0, sidetray_params.default_button_width, sidetray_params.default_button_height); 
    
    	LLButton::Params bparams;
    
    	// Append "_button" to the side tray tab name
    	std::string button_name = getName() + "_button";
    	bparams.name(button_name);
    	bparams.follows.flags (FOLLOWS_LEFT | FOLLOWS_TOP);
    	bparams.rect (rect);
    	bparams.tab_stop(false);
    	bparams.image_unselected(sidetray_params.tab_btn_image_normal);
    	bparams.image_selected(sidetray_params.tab_btn_image_selected);
    	bparams.image_disabled(sidetray_params.tab_btn_image_normal);
    	bparams.image_disabled_selected(sidetray_params.tab_btn_image_selected);
    
    	if (mHasBadge)
    	{
    		bparams.badge = mBadgeParams;
    	}
    
    	LLButton* button;
    	if (allowTearOff)
    	{
    		button = LLUICtrlFactory::create<LLSideTrayButton>(bparams);
    	}
    	else
    	{
    		// "Open/Close" button shouldn't allow "tear off"
    		// hence it is created as LLButton instance.
    		button = LLUICtrlFactory::create<LLButton>(bparams);
    	}
    
    	button->setClickedCallback(callback);
    
    	button->setToolTip(mTabTitle);
    
    	if(mImage.length())
    	{
    		button->setImageOverlay(mImage);
    	}
    
    	return button;
    }
    
    void LLSideTray::createButtons()
    {
    	//create buttons for tabs
    	child_vector_const_iter_t child_it = mTabs.begin();
    	for ( ; child_it != mTabs.end(); ++child_it)
    	{
    		LLSideTrayTab* sidebar_tab = *child_it;
    
    		std::string name = sidebar_tab->getName();
    		
    		// The "OpenClose" button will open/close the whole panel
    		if (name == "sidebar_openclose")
    		{
    			mCollapseButton = sidebar_tab->createButton(false, boost::bind(&LLSideTray::onToggleCollapse, this));
    
    			mButtonsPanel->addChildInBack(mCollapseButton);
    
    			LLHints::registerHintTarget("side_panel_btn", mCollapseButton->getHandle());
    		}
    		else
    		{
    			LLButton* button = sidebar_tab->createButton(true, boost::bind(&LLSideTray::onTabButtonClick, this, name));
    
    			mButtonsPanel->addChildInBack(button);
    
    			mTabButtons[name] = button;
    		}
    	}
    
    	LLHints::registerHintTarget("inventory_btn", mTabButtons["sidebar_inventory"]->getHandle());
    }
    
    void		LLSideTray::processTriState ()
    {
    	if(mCollapsed)
    		expandSideBar();
    	else
    	{
    #if 0 // *TODO: EXT-2092
    		
    		// Tell the active task panel to switch to its default view
    		// or collapse side tray if already on the default view.
    		LLSD info;
    		info["task-panel-action"] = "handle-tri-state";
    		mActiveTab->notifyChildren(info);
    #else
    		collapseSideBar();
    #endif
    	}
    }
    
    void		LLSideTray::onTabButtonClick(string name)
    {
    	LLSideTrayTab* tab = getTab(name);
    	if (!tab) return;
    
    	if(tab == mActiveTab)
    	{
    		processTriState ();
    		return;
    	}
    	selectTabByName	(name);
    	if(mCollapsed)
    		expandSideBar();
    }
    
    void		LLSideTray::onToggleCollapse()
    {
    	LLFirstUse::notUsingSidePanel(false);
    	if(mCollapsed)
    	{
    		expandSideBar();
    		//selectTabByName("sidebar_openclose");
    	}
    	else
    		collapseSideBar();
    }
    
    
    void LLSideTray::reflectCollapseChange()
    {
    	updateSidetrayVisibility();
    
    	setFocus(!mCollapsed);
    
    	gFloaterView->refresh();
    }
    
    void LLSideTray::arrange()
    {
    	static LLSideTray::Params sidetray_params(LLUICtrlFactory::getDefaultParams<LLSideTray>());	
    
    	updateSidetrayVisibility();
    	
    	LLRect ctrl_rect;
    	ctrl_rect.setLeftTopAndSize(0,
    								mButtonsPanel->getRect().getHeight() - sidetray_params.default_button_width,
    								sidetray_params.default_button_width,
    								sidetray_params.default_button_height);
    
    	mCollapseButton->setRect(ctrl_rect);
    
    	//arrange tab buttons
    	//arrange tab buttons
    	child_vector_const_iter_t child_it;
    	int offset = (sidetray_params.default_button_height+sidetray_params.default_button_margin)*2;
    	for ( child_it = mTabs.begin(); child_it != mTabs.end(); ++child_it)	
    	{
    		LLSideTrayTab* sidebar_tab = *child_it;
    		
    		ctrl_rect.setLeftTopAndSize(0,
    									mButtonsPanel->getRect().getHeight()-offset,
    									sidetray_params.default_button_width,
    									sidetray_params.default_button_height);
    
    		if(mTabButtons.find(sidebar_tab->getName()) == mTabButtons.end())
    			continue;
    
    		LLButton* btn = mTabButtons[sidebar_tab->getName()];
    
    		btn->setRect(ctrl_rect);
    		offset+=sidetray_params.default_button_height;
    		offset+=sidetray_params.default_button_margin;
    
    		btn->setVisible(ctrl_rect.mBottom > 0);
    	}
    
    	//arrange tabs
    	for ( child_vector_t::iterator child_it = mTabs.begin(); child_it != mTabs.end(); ++child_it)
    	{
    		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.
    	// Also hide them in mouse-look mode.
    	mButtonsPanel->setVisible(hasTabs() && !gAgentCamera.cameraMouselook());
    }
    
    // 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 (!LLFloater::getControlGroup()->controlExists(vis_ctrl_name)) continue;
    
    		bool is_visible = LLFloater::getControlGroup()->getBOOL(vis_ctrl_name);
    		if (!is_visible) continue;
    
    		llassert(isTabAttached(tab->getName()));
    		tab->setDocked(false);
    	}
    }
    
    void LLSideTray::collapseSideBar()
    {
    	mCollapsed = true;
    	// Reset all overlay images, because there is no "selected" tab when the
    	// whole side tray is hidden.
    	child_vector_const_iter_t it = mTabs.begin();
    	for ( ; it != mTabs.end(); ++it )
    	{
    		LLSideTrayTab* tab = *it;
    		std::string name = tab->getName();
    		std::map<std::string,LLButton*>::const_iterator btn_it =
    			mTabButtons.find(name);
    		if (btn_it != mTabButtons.end())
    		{
    			LLButton* btn = btn_it->second;
    			btn->setImageOverlay( tab->mImage );
    		}
    	}
    		
    	// OpenClose tab doesn't put its button in mTabButtons
    	LLSideTrayTab* openclose_tab = getTab("sidebar_openclose");
    	if (openclose_tab)
    	{
    		mCollapseButton->setImageOverlay( openclose_tab->mImage );
    	}
    	//mActiveTab->setVisible(FALSE);
    	reflectCollapseChange();
    	setFocus( FALSE );
    }
    
    void LLSideTray::expandSideBar(bool open_active)
    {
    	mCollapsed = false;
    	LLSideTrayTab* openclose_tab = getTab("sidebar_openclose");
    	if (openclose_tab)
    	{
    		mCollapseButton->setImageOverlay( openclose_tab->mImageSelected );
    	}
    
    	if (open_active)
    	{
    		mActiveTab->onOpen(LLSD());
    	}
    
    	reflectCollapseChange();
    
    
    	std::string name = mActiveTab->getName();
    	std::map<std::string,LLButton*>::const_iterator btn_it =
    		mTabButtons.find(name);
    	if (btn_it != mTabButtons.end())
    	{
    		LLButton* btn = btn_it->second;
    		btn->setImageOverlay( mActiveTab->mImageSelected  );
    	}
    }
    
    void LLSideTray::highlightFocused()
    {
    	/* uncomment in case something change
    	if(!mActiveTab)
    		return;
    	BOOL dependent_has_focus = gFocusMgr.childHasKeyboardFocus(this);
    	setBackgroundOpaque( dependent_has_focus ); 
    	mActiveTab->setBackgroundOpaque( dependent_has_focus ); 
    	*/
    }
    
    //virtual
    BOOL		LLSideTray::handleMouseDown	(S32 x, S32 y, MASK mask)
    {
    	BOOL ret = LLPanel::handleMouseDown(x,y,mask);
    	if(ret)
    		setFocus(true);	
    	return ret;
    }
    
    void LLSideTray::reshape(S32 width, S32 height, BOOL called_from_parent)
    {
    	LLPanel::reshape(width, height, called_from_parent);
    	if(!mActiveTab)
    		return;
    	
    	arrange();
    }
    
    // This is just LLView::findChildView specialized to restrict the search to LLPanels.
    // Optimization for EXT-4068 to avoid searching down to the individual item level
    // when inventories are large.
    LLPanel *findChildPanel(LLPanel *panel, const std::string& name, bool recurse)
    {
    	for (LLView::child_list_const_iter_t child_it = panel->beginChild();
    		 child_it != panel->endChild(); ++child_it)
    	{
    		LLPanel *child_panel = dynamic_cast<LLPanel*>(*child_it);
    		if (!child_panel)
    			continue;
    		if (child_panel->getName() == name)
    			return child_panel;
    	}
    	if (recurse)
    	{
    		for (LLView::child_list_const_iter_t child_it = panel->beginChild();
    			 child_it != panel->endChild(); ++child_it)
    		{
    			LLPanel *child_panel = dynamic_cast<LLPanel*>(*child_it);
    			if (!child_panel)
    				continue;
    			LLPanel *found_panel = findChildPanel(child_panel,name,recurse);
    			if (found_panel)
    			{
    				return found_panel;
    			}
    		}
    	}
    	return NULL;
    }
    
    /**
     * Activate tab with "panel_name" panel
     * if no such tab - return false, otherwise true.
     * TODO* In some cases a pointer to a panel of
     * a specific class may be needed so this method
     * would need to use templates.
     */
    LLPanel*	LLSideTray::showPanel		(const std::string& panel_name, const LLSD& params)
    {
    	LLPanel* new_panel = NULL;
    
    	// Look up the tab in the list of detached tabs.
    	child_vector_const_iter_t child_it;
    	for ( child_it = mDetachedTabs.begin(); child_it != mDetachedTabs.end(); ++child_it)
    	{
    		new_panel = openChildPanel(*child_it, panel_name, params);
    		if (new_panel) break;
    	}
    
    	// Look up the tab in the list of attached tabs.
    	for ( child_it = mTabs.begin(); child_it != mTabs.end(); ++child_it)
    	{
    		new_panel = openChildPanel(*child_it, panel_name, params);
    		if (new_panel) break;
    	}
    
    	return new_panel;
    }
    
    bool LLSideTray::hidePanel(const std::string& panel_name)
    {
    	bool panelHidden = false;
    	
    	LLPanel* panelp = getPanel(panel_name);
    
    	if (panelp)
    	{
    		LLView* parentp = panelp->getParent();
    		
    		// Collapse the side bar if the panel or the panel's parent is an attached tab
    		if (isTabAttached(panel_name) || (parentp && isTabAttached(parentp->getName())))
    		{
    			collapseSideBar();
    			panelHidden = true;
    		}
    		else
    		{
    			panelHidden = LLFloaterReg::hideInstance("side_bar_tab", panel_name);
    			
    			if (!panelHidden)
    			{
    				// 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 *detached_panel = dynamic_cast<LLPanel*>(*child_it);
    					
    					if (detached_panel)
    					{
    						// Hide this detached panel if it is a parent of our panel
    						if (findChildPanel(detached_panel, panel_name, true) != NULL)
    						{
    							panelHidden = LLFloaterReg::hideInstance("side_bar_tab", detached_panel->getName());
    							break;
    						}
    					}
    				}
    			}
    		}
    	}
    	
    	return panelHidden;
    }
    
    void LLSideTray::togglePanel(LLPanel* &sub_panel, const std::string& panel_name, const LLSD& params)
    {
    	if(!sub_panel)
    		return;
    
    	// 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();
    	}
    	else
    	{
    		LLSideTray::getInstance()->showPanel(panel_name, params);
    	}
    }
    
    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);
    		if(panel)
    		{
    			return panel;
    		}
    	}
    	return NULL;
    }
    
    LLPanel*	LLSideTray::getActivePanel()
    {
    	if (mActiveTab && !mCollapsed)
    	{
    		return mActiveTab->getPanel();
    	}
    	return NULL;
    }
    
    bool		LLSideTray::isPanelActive(const std::string& panel_name)
    {
    	LLPanel *panel = getActivePanel();
    	if (!panel) return false;
    	return (panel->getName() == panel_name);
    }
    
    void LLSideTray::setTabDocked(const std::string& tab_name, bool dock, bool toggle_floater /* = true*/)
    {
    	// Lookup tab by name.
    	LLSideTrayTab* tab = getTab(tab_name);
    	if (!tab)
    	{	// not a docked tab, look through detached tabs
    		for(child_vector_iter_t tab_it = mDetachedTabs.begin(), tab_end_it = mDetachedTabs.end();
    			tab_it != tab_end_it;
    			++tab_it)
    		{
    			if ((*tab_it)->getName() == tab_name)
    			{
    				tab = *tab_it;
    				break;
    			}
    		}
    
    	}
    
    	llassert(tab != NULL);
    
    	// Toggle its dock state.
    	if (tab && tab->isDocked() != dock)
    	{
    		tab->toggleTabDocked(toggle_floater);
    	}
    }
    
    
    void	LLSideTray::updateSidetrayVisibility()
    {
    	// set visibility of parent container based on collapsed state
    	LLView* parent = getParent();
    	if (parent)
    	{
    		bool old_visibility = parent->getVisible();
    		bool new_visibility = !mCollapsed && !gAgentCamera.cameraMouselook();
    
    		if (old_visibility != new_visibility)
    		{
    			parent->setVisible(new_visibility);
    
    			// Signal change of visible width.
    			//llinfos << "Visible: " << new_visibility << llendl;
    			mVisibleWidthChangeSignal(this, new_visibility);
    		}
    	}
    }
    
    S32 LLSideTray::getVisibleWidth()
    {
    	return (isInVisibleChain() && !mCollapsed) ? getRect().getWidth() : 0;
    }
    
    void LLSideTray::setVisibleWidthChangeCallback(const commit_signal_t::slot_type& cb)
    {
    	mVisibleWidthChangeSignal.connect(cb);
    }