SWAT-484, SWAT-485: add LLSideTrayListener, a new LLEventAPI.

Expand XUI-visible LLUICtrl::CommitCallbackRegistry operations to include
"SideTray.Toggle" and "SideTray.Collapse".
Give LLSideTrayListener friend access to LLSideTray so it can query the
attached and detached tabs. Introduce tab_cast template to deal with the
unavailability of the LLSideTrayTab class outside of llsidetray.cpp.
meow-7.2.2
Nat Goodspeed 2011-02-16 12:49:35 -05:00
parent 34ba85dd59
commit 5946d84eef
5 changed files with 226 additions and 0 deletions

View File

@ -412,6 +412,7 @@ set(viewer_SOURCE_FILES
llsidepaneliteminfo.cpp
llsidepaneltaskinfo.cpp
llsidetray.cpp
llsidetraylistener.cpp
llsidetraypanelcontainer.cpp
llsky.cpp
llslurl.cpp
@ -947,6 +948,7 @@ set(viewer_HEADER_FILES
llsidepaneliteminfo.h
llsidepaneltaskinfo.h
llsidetray.h
llsidetraylistener.h
llsidetraypanelcontainer.h
llsky.h
llslurl.h

View File

@ -53,6 +53,8 @@
#include "llsidepanelappearance.h"
#include "llsidetraylistener.h"
//#include "llscrollcontainer.h"
using namespace std;
@ -71,6 +73,8 @@ 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()
{
@ -417,6 +421,11 @@ LLSideTrayTab* LLSideTrayTab::createInstance ()
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.
@ -530,6 +539,8 @@ LLSideTray::LLSideTray(const Params& params)
// 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)

View File

@ -33,6 +33,13 @@
class LLAccordionCtrl;
class LLSideTrayTab;
// Deal with LLSideTrayTab being opaque. Generic do-nothing cast...
template <class T>
T tab_cast(LLSideTrayTab* tab) { return tab; }
// specialized for implementation in presence of LLSideTrayTab definition
template <>
LLPanel* tab_cast<LLPanel*>(LLSideTrayTab* tab);
// added inheritance from LLDestroyClass<LLSideTray> to enable Side Tray perform necessary actions
// while disconnecting viewer in LLAppViewer::disconnectViewer().
// LLDestroyClassList::instance().fireCallbacks() calls destroyClass method. See EXT-245.
@ -217,6 +224,9 @@ private:
}
private:
// Since we provide no public way to query mTabs and mDetachedTabs, give
// LLSideTrayListener friend access.
friend class LLSideTrayListener;
LLPanel* mButtonsPanel;
typedef std::map<std::string,LLButton*> button_map_t;
button_map_t mTabButtons;

View File

@ -0,0 +1,167 @@
/**
* @file llsidetraylistener.cpp
* @author Nat Goodspeed
* @date 2011-02-15
* @brief Implementation for llsidetraylistener.
*
* $LicenseInfo:firstyear=2011&license=lgpl$
* Copyright (c) 2011, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "llviewerprecompiledheaders.h"
// associated header
#include "llsidetraylistener.h"
// STL headers
// std headers
// external library headers
// other Linden headers
#include "llsidetray.h"
#include "llsdutil.h"
LLSideTrayListener::LLSideTrayListener(const Getter& getter):
LLEventAPI("LLSideTray",
"Operations on side tray (e.g. query state, query tabs)"),
mGetter(getter)
{
add("getCollapsed", "Send on [\"reply\"] an [\"open\"] Boolean",
&LLSideTrayListener::getCollapsed, LLSDMap("reply", LLSD()));
add("getTabs",
"Send on [\"reply\"] a map of tab names and info about them",
&LLSideTrayListener::getTabs, LLSDMap("reply", LLSD()));
add("getPanels",
"Send on [\"reply\"] data about panels available with SideTray.ShowPanel",
&LLSideTrayListener::getPanels, LLSDMap("reply", LLSD()));
}
void LLSideTrayListener::getCollapsed(const LLSD& event) const
{
LLReqID reqID(event);
LLSD reply(reqID.makeResponse());
reply["open"] = ! mGetter()->getCollapsed();
LLEventPumps::instance().obtain(event["reply"]).post(reply);
}
void LLSideTrayListener::getTabs(const LLSD& event) const
{
LLReqID reqID(event);
LLSD reply(reqID.makeResponse());
LLSideTray* tray = mGetter();
LLSD::Integer ord(0);
for (LLSideTray::child_list_const_iter_t chi(tray->beginChild()), chend(tray->endChild());
chi != chend; ++chi, ++ord)
{
LLView* child = *chi;
// How much info is important? Toss in as much as seems reasonable for
// each tab. But to me, at least for the moment, the most important
// item is the tab name.
LLSD info;
// I like the idea of returning a map keyed by tab name. But as
// compared to an array of maps, that loses sequence information.
// Address that by indicating the original order in each map entry.
info["ord"] = ord;
info["visible"] = bool(child->getVisible());
info["enabled"] = bool(child->getEnabled());
info["available"] = child->isAvailable();
reply[child->getName()] = info;
}
LLEventPumps::instance().obtain(event["reply"]).post(reply);
}
static LLSD getTabInfo(LLPanel* tab)
{
LLSD panels;
for (LLPanel::tree_iterator_t ti(tab->beginTreeDFS()), tend(tab->endTreeDFS());
ti != tend; ++ti)
{
// *ti is actually an LLView*, which had better not be NULL
LLView* view(*ti);
if (! view)
{
LL_ERRS("LLSideTrayListener") << "LLSideTrayTab '" << tab->getName()
<< "' has a NULL child LLView*" << LL_ENDL;
}
// The logic we use to decide what "panel" names to return is heavily
// based on LLSideTray::showPanel(): the function that actually
// implements the "SideTray.ShowPanel" operation. showPanel(), in
// turn, depends on LLSideTray::openChildPanel(): when
// openChildPanel() returns non-NULL, showPanel() stops searching
// attached and detached LLSideTrayTab tabs.
// For each LLSideTrayTab, openChildPanel() first calls
// findChildView(panel_name, true). In other words, panel_name need
// not be a direct LLSideTrayTab child, it's sought recursively.
// That's why we use (begin|end)TreeDFS() in this loop.
// But this tree_iterator_t loop will actually traverse every widget
// in every panel. Returning all those names will not help our caller:
// passing most such names to openChildPanel() would not do what we
// want. Even though the code suggests that passing ANY valid
// side-panel widget name to openChildPanel() will open the tab
// containing that widget, results could get confusing since followup
// (onOpen()) logic wouldn't be invoked, and showPanel() wouldn't stop
// searching because openChildPanel() would return NULL.
// We must filter these LLView items, using logic that (sigh!) mirrors
// openChildPanel()'s own.
// openChildPanel() returns a non-NULL LLPanel* when either:
// - the LLView is a direct child of an LLSideTrayPanelContainer
// - the LLView is itself an LLPanel.
// But as LLSideTrayPanelContainer can directly contain LLView items
// that are NOT themselves LLPanels (e.g. "sidebar_me" contains an
// LLButton called "Jump Right Arrow"), we'd better focus only on
// LLSideTrayPanelContainer children that are themselves LLPanel
// items. Which means that the second test completely subsumes the
// first.
LLPanel* panel(dynamic_cast<LLPanel*>(view));
if (panel)
{
// Maybe it's overkill to construct an LLSD::Map for each panel, but
// the possibility remains that we might want to deliver more info
// about each panel than just its name.
panels.append(LLSDMap("name", panel->getName()));
}
}
return LLSDMap("panels", panels);
}
void LLSideTrayListener::getPanels(const LLSD& event) const
{
LLReqID reqID(event);
LLSD reply(reqID.makeResponse());
LLSideTray* tray = mGetter();
// Iterate through the attached tabs.
LLSD::Integer ord(0);
for (LLSideTray::child_vector_t::const_iterator
ati(tray->mTabs.begin()), atend(tray->mTabs.end());
ati != atend; ++ati)
{
// We don't have access to LLSideTrayTab: the class definition is
// hidden in llsidetray.cpp. But as LLSideTrayTab isa LLPanel, use the
// LLPanel API. Unfortunately, without the LLSideTrayTab definition,
// the compiler doesn't even know this LLSideTrayTab* is an LLPanel*.
// Persuade it.
LLPanel* tab(tab_cast<LLPanel*>(*ati));
reply[tab->getName()] = getTabInfo(tab).with("attached", true).with("ord", ord);
}
// Now iterate over the detached tabs. These can also be opened via
// SideTray.ShowPanel.
ord = 0;
for (LLSideTray::child_vector_t::const_iterator
dti(tray->mDetachedTabs.begin()), dtend(tray->mDetachedTabs.end());
dti != dtend; ++dti)
{
LLPanel* tab(tab_cast<LLPanel*>(*dti));
reply[tab->getName()] = getTabInfo(tab).with("attached", false).with("ord", ord);
}
LLEventPumps::instance().obtain(event["reply"]).post(reply);
}

View File

@ -0,0 +1,36 @@
/**
* @file llsidetraylistener.h
* @author Nat Goodspeed
* @date 2011-02-15
* @brief
*
* $LicenseInfo:firstyear=2011&license=lgpl$
* Copyright (c) 2011, Linden Research, Inc.
* $/LicenseInfo$
*/
#if ! defined(LL_LLSIDETRAYLISTENER_H)
#define LL_LLSIDETRAYLISTENER_H
#include "lleventapi.h"
#include <boost/function.hpp>
class LLSideTray;
class LLSD;
class LLSideTrayListener: public LLEventAPI
{
typedef boost::function<LLSideTray*()> Getter;
public:
LLSideTrayListener(const Getter& getter);
private:
void getCollapsed(const LLSD& event) const;
void getTabs(const LLSD& event) const;
void getPanels(const LLSD& event) const;
Getter mGetter;
};
#endif /* ! defined(LL_LLSIDETRAYLISTENER_H) */