SL-20432 Filtering My Outfits with big number of items freezes UI

master
Alexander Gavriliuk 2023-10-10 23:21:08 +02:00 committed by Guru
parent af17d4ca38
commit 42e062888f
16 changed files with 158 additions and 171 deletions

View File

@ -126,12 +126,12 @@ public:
void setSelected(bool is_selected);
bool getCollapsible() {return mCollapsible;};
bool getCollapsible() { return mCollapsible; };
void setCollapsible(bool collapsible) {mCollapsible = collapsible;};
void setCollapsible(bool collapsible) { mCollapsible = collapsible; };
void changeOpenClose(bool is_open);
void canOpenClose(bool can_open_close) { mCanOpenClose = can_open_close;};
void canOpenClose(bool can_open_close) { mCanOpenClose = can_open_close; };
bool canOpenClose() const { return mCanOpenClose; };
virtual BOOL postBuild();
@ -142,8 +142,8 @@ public:
void draw();
void storeOpenCloseState ();
void restoreOpenCloseState ();
void storeOpenCloseState();
void restoreOpenCloseState();
protected:
LLAccordionCtrlTab(const LLAccordionCtrlTab::Params&);

View File

@ -1361,26 +1361,28 @@ void LLFlatListViewEx::setForceShowingUnmatchedItems(bool show)
mForceShowingUnmatchedItems = show;
}
void LLFlatListViewEx::setFilterSubString(const std::string& filter_str)
void LLFlatListViewEx::setFilterSubString(const std::string& filter_str, bool notify_parent)
{
if (0 != LLStringUtil::compareInsensitive(filter_str, mFilterSubString))
{
mFilterSubString = filter_str;
updateNoItemsMessage(mFilterSubString);
filterItems();
filterItems(false, notify_parent);
}
}
void LLFlatListViewEx::updateItemVisibility(LLPanel* item, const LLSD &action)
bool LLFlatListViewEx::updateItemVisibility(LLPanel* item, const LLSD &action)
{
if (!item) return;
if (!item)
return false;
BOOL visible = TRUE;
// 0 signifies that filter is matched,
// i.e. we don't hide items that don't support 'match_filter' action, separators etc.
if (0 == item->notify(action))
{
mHasMatchedItems = true;
item->setVisible(true);
}
else
{
@ -1388,12 +1390,20 @@ void LLFlatListViewEx::updateItemVisibility(LLPanel* item, const LLSD &action)
if (!mForceShowingUnmatchedItems)
{
selectItem(item, false);
visible = FALSE;
}
item->setVisible(mForceShowingUnmatchedItems);
}
if (item->getVisible() != visible)
{
item->setVisible(visible);
return true;
}
return false;
}
void LLFlatListViewEx::filterItems()
void LLFlatListViewEx::filterItems(bool re_sort, bool notify_parent)
{
typedef std::vector <LLPanel*> item_panel_list_t;
@ -1403,19 +1413,24 @@ void LLFlatListViewEx::filterItems()
LLSD action;
action.with("match_filter", cur_filter);
item_panel_list_t items;
getItems(items);
mHasMatchedItems = false;
item_panel_list_t::iterator iter = items.begin(), iter_end = items.end();
while (iter < iter_end)
bool visibility_changed = false;
pairs_const_iterator_t iter = getItemPairs().begin(), iter_end = getItemPairs().end();
while (iter != iter_end)
{
LLPanel* pItem = *(iter++);
updateItemVisibility(pItem, action);
LLPanel* pItem = (*(iter++))->first;
visibility_changed |= updateItemVisibility(pItem, action);
}
sort();
notifyParentItemsRectChanged();
if (re_sort)
{
sort();
}
if (visibility_changed && notify_parent)
{
notifyParentItemsRectChanged();
}
}
bool LLFlatListViewEx::hasMatchedItems()

View File

@ -300,6 +300,7 @@ public:
virtual S32 notify(const LLSD& info) ;
virtual ~LLFlatListView();
protected:
/** Pairs LLpanel representing a single item LLPanel and LLSD associated with it */
@ -375,7 +376,9 @@ protected:
LLRect getLastSelectedItemRect();
void ensureSelectedVisible();
void ensureSelectedVisible();
const pairs_list_t& getItemPairs() { return mItemPairs; }
private:
@ -482,14 +485,14 @@ public:
/**
* Sets up new filter string and filters the list.
*/
void setFilterSubString(const std::string& filter_str);
void setFilterSubString(const std::string& filter_str, bool notify_parent);
std::string getFilterSubString() { return mFilterSubString; }
/**
* Filters the list, rearranges and notifies parent about shape changes.
* Derived classes may want to overload rearrangeItems() to exclude repeated separators after filtration.
*/
void filterItems();
void filterItems(bool re_sort, bool notify_parent);
/**
* Returns true if last call of filterItems() found at least one matching item
@ -513,7 +516,7 @@ protected:
* @param item - item we are changing
* @param item - action - parameters to determin visibility from
*/
void updateItemVisibility(LLPanel* item, const LLSD &action);
bool updateItemVisibility(LLPanel* item, const LLSD &action);
private:
std::string mNoFilteredItemsMsg;

View File

@ -240,7 +240,7 @@ void LLInventoryItemsList::refresh()
case REFRESH_LIST_SORT:
{
// Filter, sort, rearrange and notify parent about shape changes
filterItems();
filterItems(true, true);
if (mAddedItems.size() == 0)
{

View File

@ -51,20 +51,20 @@ public:
/**
* Let list know items need to be refreshed in next doIdle()
*/
void setNeedsRefresh(bool needs_refresh){ mRefreshState = needs_refresh ? REFRESH_ALL : REFRESH_COMPLETE; }
void setNeedsRefresh(bool needs_refresh) { mRefreshState = needs_refresh ? REFRESH_ALL : REFRESH_COMPLETE; }
U32 getNeedsRefresh(){ return mRefreshState; }
U32 getNeedsRefresh() { return mRefreshState; }
/**
* Sets the flag indicating that the list needs to be refreshed even if it is
* not currently visible.
*/
void setForceRefresh(bool force_refresh){ mForceRefresh = force_refresh; }
void setForceRefresh(bool force_refresh) { mForceRefresh = force_refresh; }
/**
* If refreshes when invisible.
*/
bool getForceRefresh(){ return mForceRefresh; }
bool getForceRefresh() { return mForceRefresh; }
virtual bool selectItemByValue(const LLSD& value, bool select = true);

View File

@ -433,8 +433,7 @@ bool compareGalleryItem(LLOutfitGalleryItem* item1, LLOutfitGalleryItem* item2)
}
void LLOutfitGallery::reArrangeRows(S32 row_diff)
{
{
std::vector<LLOutfitGalleryItem*> buf_items = mItems;
for (std::vector<LLOutfitGalleryItem*>::const_reverse_iterator it = buf_items.rbegin(); it != buf_items.rend(); ++it)
{
@ -446,16 +445,24 @@ void LLOutfitGallery::reArrangeRows(S32 row_diff)
}
mHiddenItems.clear();
mItemsInRow+= row_diff;
mItemsInRow += row_diff;
updateGalleryWidth();
std::sort(buf_items.begin(), buf_items.end(), compareGalleryItem);
std::string cur_filter = getFilterSubString();
LLStringUtil::toUpper(cur_filter);
for (std::vector<LLOutfitGalleryItem*>::const_iterator it = buf_items.begin(); it != buf_items.end(); ++it)
{
(*it)->setHidden(false);
applyFilter(*it,sFilterSubString);
std::string outfit_name = (*it)->getItemName();
LLStringUtil::toUpper(outfit_name);
bool hidden = (std::string::npos == outfit_name.find(cur_filter));
(*it)->setHidden(hidden);
addToGallery(*it);
}
updateMessageVisibility();
}
@ -725,9 +732,9 @@ LLOutfitGallery::~LLOutfitGallery()
}
}
void LLOutfitGallery::setFilterSubString(const std::string& string)
// virtual
void LLOutfitGallery::onFilterSubStringChanged(const std::string& new_string, const std::string& old_string)
{
sFilterSubString = string;
reArrangeRows();
}
@ -743,20 +750,6 @@ void LLOutfitGallery::onHighlightBaseOutfit(LLUUID base_id, LLUUID prev_id)
}
}
void LLOutfitGallery::applyFilter(LLOutfitGalleryItem* item, const std::string& filter_substring)
{
if (!item) return;
std::string outfit_name = item->getItemName();
LLStringUtil::toUpper(outfit_name);
std::string cur_filter = filter_substring;
LLStringUtil::toUpper(cur_filter);
bool hidden = (std::string::npos == outfit_name.find(cur_filter));
item->setHidden(hidden);
}
void LLOutfitGallery::onSetSelectedOutfitByUUID(const LLUUID& outfit_uuid)
{
}
@ -904,11 +897,11 @@ bool LLOutfitGallery::hasDefaultImage(const LLUUID& outfit_cat_id)
void LLOutfitGallery::updateMessageVisibility()
{
if(mItems.empty())
if (mItems.empty())
{
mMessageTextBox->setVisible(TRUE);
mScrollPanel->setVisible(FALSE);
std::string message = sFilterSubString.empty()? getString("no_outfits_msg") : getString("no_matched_outfits_msg");
std::string message = getString(getFilterSubString().empty() ? "no_outfits_msg" : "no_matched_outfits_msg");
mMessageTextBox->setValue(message);
}
else

View File

@ -90,7 +90,7 @@ public:
void wearSelectedOutfit();
/*virtual*/ void setFilterSubString(const std::string& string);
/*virtual*/ void onFilterSubStringChanged(const std::string& new_string, const std::string& old_string);
/*virtual*/ void getCurrentCategories(uuid_vec_t& vcur);
/*virtual*/ void updateAddedCategory(LLUUID cat_id);
@ -117,8 +117,6 @@ protected:
/*virtual*/ void onExpandAllFolders() {}
/*virtual*/ LLOutfitListGearMenuBase* createGearMenu();
void applyFilter(LLOutfitGalleryItem* item, const std::string& filter_substring);
private:
LLUUID getPhotoAssetId(const LLUUID& outfit_id);
LLUUID getDefaultPhoto();

View File

@ -188,7 +188,7 @@ void LLOutfitsList::updateAddedCategory(LLUUID cat_id)
list->setCommitCallback(boost::bind(&LLOutfitsList::onListSelectionChange, this, _1));
// Setting list refresh callback to apply filter on list change.
list->setRefreshCompleteCallback(boost::bind(&LLOutfitsList::onFilteredWearableItemsListRefresh, this, _1));
list->setRefreshCompleteCallback(boost::bind(&LLOutfitsList::onRefreshComplete, this, _1));
list->setRightMouseDownCallback(boost::bind(&LLOutfitsList::onWearableItemsListRightClick, this, _1, _2, _3));
@ -199,19 +199,17 @@ void LLOutfitsList::updateAddedCategory(LLUUID cat_id)
// Further list updates will be triggered by the category observer.
list->updateList(cat_id);
// If filter is currently applied we store the initial tab state and
// open it to show matched items if any.
if (!sFilterSubString.empty())
// If filter is currently applied we store the initial tab state.
if (!getFilterSubString().empty())
{
tab->notifyChildren(LLSD().with("action", "store_state"));
tab->setDisplayChildren(true);
// Setting mForceRefresh flag will make the list refresh its contents
// even if it is not currently visible. This is required to apply the
// filter to the newly added list.
list->setForceRefresh(true);
list->setFilterSubString(sFilterSubString);
list->setFilterSubString(getFilterSubString(), false);
}
}
@ -313,14 +311,6 @@ void LLOutfitsList::onSetSelectedOutfitByUUID(const LLUUID& outfit_uuid)
}
}
// virtual
void LLOutfitsList::setFilterSubString(const std::string& string)
{
applyFilter(string);
sFilterSubString = string;
}
// virtual
bool LLOutfitListBase::isActionEnabled(const LLSD& userdata)
{
@ -494,9 +484,9 @@ void LLOutfitsList::restoreOutfitSelection(LLAccordionCtrlTab* tab, const LLUUID
}
}
void LLOutfitsList::onFilteredWearableItemsListRefresh(LLUICtrl* ctrl)
void LLOutfitsList::onRefreshComplete(LLUICtrl* ctrl)
{
if (!ctrl || sFilterSubString.empty())
if (!ctrl || getFilterSubString().empty())
return;
for (outfits_map_t::iterator
@ -510,57 +500,50 @@ void LLOutfitsList::onFilteredWearableItemsListRefresh(LLUICtrl* ctrl)
LLWearableItemsList* list = dynamic_cast<LLWearableItemsList*>(tab->getAccordionView());
if (list != ctrl) continue;
applyFilterToTab(iter->first, tab, sFilterSubString);
applyFilterToTab(iter->first, tab, getFilterSubString());
}
}
void LLOutfitsList::applyFilter(const std::string& new_filter_substring)
// virtual
void LLOutfitsList::onFilterSubStringChanged(const std::string& new_string, const std::string& old_string)
{
mAccordion->setFilterSubString(new_filter_substring);
mAccordion->setFilterSubString(new_string);
for (outfits_map_t::iterator
iter = mOutfitsMap.begin(),
iter_end = mOutfitsMap.end();
iter != iter_end; ++iter)
outfits_map_t::iterator iter = mOutfitsMap.begin(), iter_end = mOutfitsMap.end();
while (iter != iter_end)
{
LLAccordionCtrlTab* tab = iter->second;
const LLUUID& category_id = iter->first;
LLAccordionCtrlTab* tab = iter++->second;
if (!tab) continue;
bool more_restrictive = sFilterSubString.size() < new_filter_substring.size() && !new_filter_substring.substr(0, sFilterSubString.size()).compare(sFilterSubString);
// Restore tab visibility in case of less restrictive filter
// to compare it with updated string if it was previously hidden.
if (!more_restrictive)
{
tab->setVisible(TRUE);
}
LLWearableItemsList* list = dynamic_cast<LLWearableItemsList*>(tab->getAccordionView());
if (list)
{
list->setFilterSubString(new_filter_substring);
list->setFilterSubString(new_string, tab->getDisplayChildren());
}
if(sFilterSubString.empty() && !new_filter_substring.empty())
if (old_string.empty())
{
//store accordion tab state when filter is not empty
tab->notifyChildren(LLSD().with("action","store_state"));
// Store accordion tab state when filter is not empty
tab->notifyChildren(LLSD().with("action", "store_state"));
}
if (!new_filter_substring.empty())
if (!new_string.empty())
{
applyFilterToTab(iter->first, tab, new_filter_substring);
applyFilterToTab(category_id, tab, new_string);
}
else
{
// restore tab title when filter is empty
tab->setVisible(TRUE);
// Restore tab title when filter is empty
tab->setTitle(tab->getTitle());
//restore accordion state after all those accodrion tab manipulations
tab->notifyChildren(LLSD().with("action","restore_state"));
// Restore accordion state after all those accodrion tab manipulations
tab->notifyChildren(LLSD().with("action", "restore_state"));
// Try restoring the tab selection.
restoreOutfitSelection(tab, iter->first);
restoreOutfitSelection(tab, category_id);
}
}
@ -586,11 +569,11 @@ void LLOutfitsList::applyFilterToTab(
if (std::string::npos == title.find(cur_filter))
{
// hide tab if its title doesn't pass filter
// and it has no visible items
// Hide tab if its title doesn't pass filter
// and it has no matched items
tab->setVisible(list->hasMatchedItems());
// remove title highlighting because it might
// Remove title highlighting because it might
// have been previously highlighted by less restrictive filter
tab->setTitle(tab->getTitle());
@ -602,18 +585,6 @@ void LLOutfitsList::applyFilterToTab(
// Try restoring the tab selection.
restoreOutfitSelection(tab, category_id);
}
if (tab->getVisible())
{
// Open tab if it has passed the filter.
tab->setDisplayChildren(true);
}
else
{
// Set force refresh flag to refresh not visible list
// when some changes occur in it.
list->setForceRefresh(true);
}
}
bool LLOutfitsList::canWearSelected()
@ -698,11 +669,10 @@ void LLOutfitsList::onCOFChanged()
// These links UUIDs are not the same UUIDs that we have in each wearable items list.
// So we collect base items' UUIDs to find them or links that point to them in wearable
// items lists and update their worn state there.
for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin();
iter != item_array.end();
++iter)
LLInventoryModel::item_array_t::const_iterator array_iter = item_array.begin(), array_end = item_array.end();
while (array_iter < array_end)
{
vnew.push_back((*iter)->getLinkedUUID());
vnew.push_back((*(array_iter++))->getLinkedUUID());
}
// We need to update only items that were added or removed from COF.
@ -711,20 +681,20 @@ void LLOutfitsList::onCOFChanged()
// Store the ids of items currently linked from COF.
mCOFLinkedItems = vnew;
for (outfits_map_t::iterator iter = mOutfitsMap.begin();
iter != mOutfitsMap.end();
++iter)
// Append removed ids to added ids because we should update all of them.
vadded.reserve(vadded.size() + vremoved.size());
vadded.insert(vadded.end(), vremoved.begin(), vremoved.end());
vremoved.clear();
outfits_map_t::iterator map_iter = mOutfitsMap.begin(), map_end = mOutfitsMap.end();
while (map_iter != map_end)
{
LLAccordionCtrlTab* tab = iter->second;
LLAccordionCtrlTab* tab = (map_iter++)->second;
if (!tab) continue;
LLWearableItemsList* list = dynamic_cast<LLWearableItemsList*>(tab->getAccordionView());
if (!list) continue;
// Append removed ids to added ids because we should update all of them.
vadded.reserve(vadded.size() + vremoved.size());
vadded.insert(vadded.end(), vremoved.begin(), vremoved.end());
// Every list updates the labels of changed items or
// the links that point to these items.
list->updateChangedItems(vadded);

View File

@ -237,7 +237,7 @@ public:
//void performAction(std::string action);
/*virtual*/ void setFilterSubString(const std::string& string);
/*virtual*/ void onFilterSubStringChanged(const std::string& new_string, const std::string& old_string);
/*virtual*/ void getSelectedItemsUUIDs(uuid_vec_t& selected_uuids) const;
@ -307,12 +307,7 @@ private:
* Called upon list refresh event to update tab visibility depending on
* the results of applying filter to the title and list items of the tab.
*/
void onFilteredWearableItemsListRefresh(LLUICtrl* ctrl);
/**
* Highlights filtered items and hides tabs which haven't passed filter.
*/
void applyFilter(const std::string& new_filter_substring);
void onRefreshComplete(LLUICtrl* ctrl);
/**
* Applies filter to the given tab

View File

@ -28,12 +28,35 @@
#include "llpanelappearancetab.h"
#include "llinventoryfunctions.h"
#include "llinventorymodel.h"
#include "llviewerinventory.h"
//virtual
std::string LLPanelAppearanceTab::sRecentFilterSubString;
void LLPanelAppearanceTab::setFilterSubString(const std::string& new_string)
{
if (new_string != mFilterSubString)
{
std::string old_string = mFilterSubString;
mFilterSubString = new_string;
onFilterSubStringChanged(mFilterSubString, old_string);
}
sRecentFilterSubString = new_string;
}
void LLPanelAppearanceTab::checkFilterSubString()
{
if (sRecentFilterSubString != mFilterSubString)
{
std::string old_string = mFilterSubString;
mFilterSubString = sRecentFilterSubString;
onFilterSubStringChanged(mFilterSubString, old_string);
}
}
// virtual
bool LLPanelAppearanceTab::canTakeOffSelected()
{
uuid_vec_t selected_uuids;

View File

@ -35,13 +35,17 @@ public:
LLPanelAppearanceTab() : LLPanel() {}
virtual ~LLPanelAppearanceTab() {}
virtual void setFilterSubString(const std::string& string) = 0;
void setFilterSubString(const std::string& new_string);
void checkFilterSubString();
virtual void onFilterSubStringChanged(const std::string& new_string, const std::string& old_string) = 0;
virtual bool isActionEnabled(const LLSD& userdata) = 0;
virtual void getSelectedItemsUUIDs(uuid_vec_t& selected_uuids) const {}
static const std::string& getFilterSubString() { return sFilterSubString; }
const std::string& getFilterSubString() { return mFilterSubString; }
protected:
@ -50,7 +54,10 @@ protected:
*/
bool canTakeOffSelected();
static std::string sFilterSubString;
private:
std::string mFilterSubString;
static std::string sRecentFilterSubString;
};
#endif //LL_LLPANELAPPEARANCETAB_H

View File

@ -733,7 +733,7 @@ void LLPanelOutfitEdit::onSearchEdit(const std::string& string)
if (mSearchString == "")
{
mInventoryItemsPanel->setFilterSubString(LLStringUtil::null);
mWearableItemsList->setFilterSubString(LLStringUtil::null);
mWearableItemsList->setFilterSubString(LLStringUtil::null, true);
// re-open folders that were initially open
mSavedFolderState->setApply(TRUE);
mInventoryItemsPanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState);
@ -763,8 +763,7 @@ void LLPanelOutfitEdit::onSearchEdit(const std::string& string)
// set new filter string
mInventoryItemsPanel->setFilterSubString(mSearchString);
mWearableItemsList->setFilterSubString(mSearchString);
mWearableItemsList->setFilterSubString(mSearchString, true);
}
void LLPanelOutfitEdit::onPlusBtnClicked(void)

View File

@ -28,19 +28,19 @@
#include "llpaneloutfitsinventory.h"
#include "llnotificationsutil.h"
#include "lltabcontainer.h"
#include "llagentwearables.h"
#include "llappearancemgr.h"
#include "llfloatersidepanelcontainer.h"
#include "llinventoryfunctions.h"
#include "llinventorymodelbackgroundfetch.h"
#include "llagentwearables.h"
#include "llappearancemgr.h"
#include "lloutfitobserver.h"
#include "llnotificationsutil.h"
#include "lloutfitgallery.h"
#include "lloutfitobserver.h"
#include "lloutfitslist.h"
#include "llpanelappearancetab.h"
#include "llpanelwearing.h"
#include "llsidepanelappearance.h"
#include "lltabcontainer.h"
#include "llviewercontrol.h"
#include "llviewerfoldertype.h"
@ -159,25 +159,12 @@ void LLPanelOutfitsInventory::onSearchEdit(const std::string& string)
{
if (!mActivePanel) return;
mFilterSubString = string;
if (string == "")
{
mActivePanel->setFilterSubString(LLStringUtil::null);
}
if (!LLInventoryModelBackgroundFetch::instance().inventoryFetchStarted())
{
llassert(false); // this should have been done on startup
LLInventoryModelBackgroundFetch::instance().start();
}
if (mActivePanel->getFilterSubString().empty() && string.empty())
{
// current filter and new filter empty, do nothing
return;
}
// set new filter string
mActivePanel->setFilterSubString(string);
}
@ -302,6 +289,7 @@ bool LLPanelOutfitsInventory::isActionEnabled(const LLSD& userdata)
{
return mActivePanel && mActivePanel->isActionEnabled(userdata);
}
// List Commands //
//////////////////////////////////////////////////////////////////////////////////
@ -330,7 +318,7 @@ void LLPanelOutfitsInventory::onTabChange()
mActivePanel = dynamic_cast<LLPanelAppearanceTab*>(mAppearanceTabs->getCurrentPanel());
if (!mActivePanel) return;
mActivePanel->setFilterSubString(mFilterSubString);
mActivePanel->checkFilterSubString();
mActivePanel->onOpen(LLSD());
updateVerbs();
@ -357,8 +345,6 @@ bool LLPanelOutfitsInventory::isOutfitsGalleryPanelActive() const
return mActivePanel->getName() == OUTFIT_GALLERY_TAB_NAME;
}
void LLPanelOutfitsInventory::setWearablesLoading(bool val)
{
updateVerbs();

View File

@ -66,7 +66,6 @@ protected:
private:
LLTabContainer* mAppearanceTabs;
std::string mFilterSubString;
//////////////////////////////////////////////////////////////////////////////////
// tab panels //

View File

@ -209,8 +209,6 @@ protected:
//////////////////////////////////////////////////////////////////////////
std::string LLPanelAppearanceTab::sFilterSubString = LLStringUtil::null;
static LLPanelInjector<LLPanelWearing> t_panel_wearing("panel_wearing");
LLPanelWearing::LLPanelWearing()
@ -328,10 +326,11 @@ void LLPanelWearing::startUpdateTimer()
}
// virtual
void LLPanelWearing::setFilterSubString(const std::string& string)
void LLPanelWearing::onFilterSubStringChanged(const std::string& new_string, const std::string& old_string)
{
sFilterSubString = string;
mCOFItemsList->setFilterSubString(sFilterSubString);
mCOFItemsList->setFilterSubString(new_string, true);
mAccordionCtrl->arrange();
}
// virtual

View File

@ -61,7 +61,7 @@ public:
/*virtual*/ void onOpen(const LLSD& info);
/*virtual*/ void setFilterSubString(const std::string& string);
/*virtual*/ void onFilterSubStringChanged(const std::string& new_string, const std::string& old_string);
/*virtual*/ bool isActionEnabled(const LLSD& userdata);