EXT-7158 FIXED Implemented filter in "My Outfits" tab.
- Added accordion tab title highlighting setter and title getter. - Added filtered tabs title highlighting. - Tabs which don't pass filter are hidden. - Added applying filter on list refresh event to avoid refreshing list on every filter change. - Moved part of LLTextUtil to llui/lluitextutil to reuse code in llaccordionctrltab. - Fixed passing list size to mRefreshCompleteSignal. - Added list refresh callback to LLInventoryItemsList for checking tab visibility without re-applying filter sub-string. Committed to proceed with dependent tasks. If there are any comments/suggestions related to text utils this part of code may be changed without requiring much effort. Reviewed by Mike Antipov at https://codereview.productengine.com/secondlife/r/363/ --HG-- branch : product-enginemeow-7.2.2
parent
90940d0b06
commit
a968046282
|
|
@ -101,6 +101,7 @@ set(llui_SOURCE_FILES
|
|||
lluictrlfactory.cpp
|
||||
lluiimage.cpp
|
||||
lluistring.cpp
|
||||
lluitextutil.cpp
|
||||
llundo.cpp
|
||||
llurlaction.cpp
|
||||
llurlentry.cpp
|
||||
|
|
@ -197,6 +198,7 @@ set(llui_HEADER_FILES
|
|||
llui.h
|
||||
lluiimage.h
|
||||
lluistring.h
|
||||
lluitextutil.h
|
||||
llundo.h
|
||||
llurlaction.h
|
||||
llurlentry.h
|
||||
|
|
|
|||
|
|
@ -32,12 +32,13 @@
|
|||
|
||||
#include "linden_common.h"
|
||||
|
||||
#include "lluictrl.h"
|
||||
#include "llscrollbar.h"
|
||||
#include "llaccordionctrltab.h"
|
||||
#include "lllocalcliprect.h"
|
||||
|
||||
#include "lllocalcliprect.h"
|
||||
#include "llscrollbar.h"
|
||||
#include "lltextbox.h"
|
||||
#include "lluictrl.h"
|
||||
#include "lluitextutil.h"
|
||||
|
||||
static const std::string DD_BUTTON_NAME = "dd_button";
|
||||
static const std::string DD_TEXTBOX_NAME = "dd_textbox";
|
||||
|
|
@ -72,7 +73,8 @@ public:
|
|||
|
||||
virtual BOOL postBuild();
|
||||
|
||||
void setTitle(const std::string& title);
|
||||
std::string getTitle();
|
||||
void setTitle(const std::string& title, const std::string& hl);
|
||||
|
||||
virtual void onMouseEnter(S32 x, S32 y, MASK mask);
|
||||
virtual void onMouseLeave(S32 x, S32 y, MASK mask);
|
||||
|
|
@ -146,10 +148,28 @@ BOOL LLAccordionCtrlTab::LLAccordionCtrlTabHeader::postBuild()
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitle(const std::string& title)
|
||||
std::string LLAccordionCtrlTab::LLAccordionCtrlTabHeader::getTitle()
|
||||
{
|
||||
if(mHeaderTextbox)
|
||||
mHeaderTextbox->setText(title);
|
||||
{
|
||||
return mHeaderTextbox->getText();
|
||||
}
|
||||
else
|
||||
{
|
||||
return LLStringUtil::null;
|
||||
}
|
||||
}
|
||||
|
||||
void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitle(const std::string& title, const std::string& hl)
|
||||
{
|
||||
if(mHeaderTextbox)
|
||||
{
|
||||
LLTextUtil::textboxSetHighlightedVal(
|
||||
mHeaderTextbox,
|
||||
LLStyle::Params(),
|
||||
title,
|
||||
hl);
|
||||
}
|
||||
}
|
||||
|
||||
void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::draw()
|
||||
|
|
@ -436,12 +456,25 @@ void LLAccordionCtrlTab::setAccordionView(LLView* panel)
|
|||
addChild(panel,0);
|
||||
}
|
||||
|
||||
void LLAccordionCtrlTab::setTitle(const std::string& title)
|
||||
std::string LLAccordionCtrlTab::getTitle()
|
||||
{
|
||||
LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME);
|
||||
if (header)
|
||||
{
|
||||
header->setTitle(title);
|
||||
return header->getTitle();
|
||||
}
|
||||
else
|
||||
{
|
||||
return LLStringUtil::null;
|
||||
}
|
||||
}
|
||||
|
||||
void LLAccordionCtrlTab::setTitle(const std::string& title, const std::string& hl)
|
||||
{
|
||||
LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME);
|
||||
if (header)
|
||||
{
|
||||
header->setTitle(title, hl);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -903,5 +936,3 @@ BOOL LLAccordionCtrlTab::handleToolTip(S32 x, S32 y, MASK mask)
|
|||
}
|
||||
return LLUICtrl::handleToolTip(x, y, mask);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -113,8 +113,10 @@ public:
|
|||
void setAccordionView(LLView* panel);
|
||||
LLView* getAccordionView() { return mContainerPanel; };
|
||||
|
||||
// Set text in LLAccordionCtrlTabHeader
|
||||
void setTitle(const std::string& title);
|
||||
std::string getTitle();
|
||||
|
||||
// Set text and highlight substring in LLAccordionCtrlTabHeader
|
||||
void setTitle(const std::string& title, const std::string& hl = LLStringUtil::null);
|
||||
|
||||
boost::signals2::connection setFocusReceivedCallback(const focus_signal_t::slot_type& cb);
|
||||
boost::signals2::connection setFocusLostCallback(const focus_signal_t::slot_type& cb);
|
||||
|
|
|
|||
|
|
@ -316,9 +316,7 @@ void LLAvatarList::refresh()
|
|||
}
|
||||
|
||||
// Send refresh_complete signal.
|
||||
std::vector<LLSD> cur_values;
|
||||
getValues(cur_values);
|
||||
mRefreshCompleteSignal(this, LLSD((S32)cur_values.size()));
|
||||
mRefreshCompleteSignal(this, LLSD((S32)size(false)));
|
||||
}
|
||||
|
||||
// Commit if we've added/removed items.
|
||||
|
|
|
|||
|
|
@ -36,12 +36,13 @@
|
|||
#include "llavataractions.h"
|
||||
#include "llavatarlistitem.h"
|
||||
|
||||
#include "llfloaterreg.h"
|
||||
#include "llagent.h"
|
||||
#include "lloutputmonitorctrl.h"
|
||||
#include "llavatariconctrl.h"
|
||||
#include "lltextutil.h"
|
||||
#include "llbutton.h"
|
||||
#include "llfloaterreg.h"
|
||||
#include "lluitextutil.h"
|
||||
|
||||
#include "llagent.h"
|
||||
#include "llavatariconctrl.h"
|
||||
#include "lloutputmonitorctrl.h"
|
||||
|
||||
bool LLAvatarListItem::sStaticInitialized = false;
|
||||
S32 LLAvatarListItem::sLeftPadding = 0;
|
||||
|
|
|
|||
|
|
@ -40,12 +40,12 @@
|
|||
#include "llmenugl.h"
|
||||
#include "lltextbox.h"
|
||||
#include "lltrans.h"
|
||||
#include "lluitextutil.h"
|
||||
|
||||
// newview
|
||||
#include "llagent.h"
|
||||
#include "llgroupactions.h"
|
||||
#include "llfloaterreg.h"
|
||||
#include "lltextutil.h"
|
||||
#include "llviewercontrol.h" // for gSavedSettings
|
||||
#include "llviewermenu.h" // for gMenuHolder
|
||||
#include "llvoiceclient.h"
|
||||
|
|
|
|||
|
|
@ -40,11 +40,13 @@
|
|||
// llcommon
|
||||
#include "llcommonutils.h"
|
||||
|
||||
// llui
|
||||
#include "lliconctrl.h"
|
||||
#include "lluitextutil.h"
|
||||
|
||||
#include "llcallbacklist.h"
|
||||
#include "llinventoryfunctions.h"
|
||||
#include "llinventorymodel.h"
|
||||
#include "lltextutil.h"
|
||||
#include "lltrans.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -320,6 +322,7 @@ LLInventoryItemsList::Params::Params()
|
|||
LLInventoryItemsList::LLInventoryItemsList(const LLInventoryItemsList::Params& p)
|
||||
: LLFlatListViewEx(p)
|
||||
, mNeedsRefresh(false)
|
||||
, mPrevVisibility(false)
|
||||
{
|
||||
// TODO: mCommitOnSelectionChange is set to "false" in LLFlatListView
|
||||
// but reset to true in all derived classes. This settings might need to
|
||||
|
|
@ -327,6 +330,8 @@ LLInventoryItemsList::LLInventoryItemsList(const LLInventoryItemsList::Params& p
|
|||
setCommitOnSelectionChange(true);
|
||||
|
||||
setNoFilteredItemsMsg(LLTrans::getString("InventoryNoMatchingItems"));
|
||||
|
||||
gIdleCallbacks.addFunction(idle, this);
|
||||
}
|
||||
|
||||
// virtual
|
||||
|
|
@ -344,12 +349,31 @@ void LLInventoryItemsList::refreshList(const LLInventoryModel::item_array_t item
|
|||
mNeedsRefresh = true;
|
||||
}
|
||||
|
||||
void LLInventoryItemsList::draw()
|
||||
boost::signals2::connection LLInventoryItemsList::setRefreshCompleteCallback(const commit_signal_t::slot_type& cb)
|
||||
{
|
||||
LLFlatListViewEx::draw();
|
||||
if(mNeedsRefresh)
|
||||
return mRefreshCompleteSignal.connect(cb);
|
||||
}
|
||||
|
||||
void LLInventoryItemsList::doIdle()
|
||||
{
|
||||
bool cur_visibility = getVisible();
|
||||
if(cur_visibility != mPrevVisibility || mNeedsRefresh)
|
||||
{
|
||||
refresh();
|
||||
|
||||
mRefreshCompleteSignal(this, LLSD());
|
||||
|
||||
mPrevVisibility = getVisible();
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
void LLInventoryItemsList::idle(void* user_data)
|
||||
{
|
||||
LLInventoryItemsList* self = static_cast<LLInventoryItemsList*>(user_data);
|
||||
if ( self )
|
||||
{ // Do the real idle
|
||||
self->doIdle();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -212,14 +212,23 @@ public:
|
|||
|
||||
void refreshList(const LLDynamicArray<LLPointer<LLViewerInventoryItem> > item_array);
|
||||
|
||||
boost::signals2::connection setRefreshCompleteCallback(const commit_signal_t::slot_type& cb);
|
||||
|
||||
/**
|
||||
* Let list know items need to be refreshed in next draw()
|
||||
* Let list know items need to be refreshed in next doIdle()
|
||||
*/
|
||||
void setNeedsRefresh(bool needs_refresh){ mNeedsRefresh = needs_refresh; }
|
||||
|
||||
bool getNeedsRefresh(){ return mNeedsRefresh; }
|
||||
|
||||
/*virtual*/ void draw();
|
||||
/**
|
||||
* Idle routine used to refresh the list regardless of the current list
|
||||
* visibility, unlike draw() which is called only for the visible list.
|
||||
* This is needed for example to filter items of the list hidden by closed
|
||||
* accordion tab.
|
||||
*/
|
||||
void doIdle(); // Real idle routine
|
||||
static void idle(void* user_data); // static glue to doIdle()
|
||||
|
||||
protected:
|
||||
friend class LLUICtrlFactory;
|
||||
|
|
@ -229,7 +238,7 @@ protected:
|
|||
|
||||
/**
|
||||
* Refreshes list items, adds new items and removes deleted items.
|
||||
* Called from draw() until all new items are added, ,
|
||||
* Called from doIdle() until all new items are added,
|
||||
* maximum 50 items can be added during single call.
|
||||
*/
|
||||
void refresh();
|
||||
|
|
@ -249,6 +258,10 @@ private:
|
|||
uuid_vec_t mIDs; // IDs of items that were added in refreshList().
|
||||
// Will be used in refresh() to determine added and removed ids
|
||||
bool mNeedsRefresh;
|
||||
|
||||
bool mPrevVisibility;
|
||||
|
||||
commit_signal_t mRefreshCompleteSignal;
|
||||
};
|
||||
|
||||
#endif //LL_LLINVENTORYITEMSLIST_H
|
||||
|
|
|
|||
|
|
@ -169,6 +169,9 @@ void LLOutfitsList::refreshList(const LLUUID& category_id)
|
|||
// Setting list commit callback to monitor currently selected wearable item.
|
||||
list->setCommitCallback(boost::bind(&LLOutfitsList::onSelectionChange, this, _1));
|
||||
|
||||
// Setting list refresh callback to apply filter on list change.
|
||||
list->setRefreshCompleteCallback(boost::bind(&LLOutfitsList::onWearableItemsListRefresh, this, _1));
|
||||
|
||||
// Fetch the new outfit contents.
|
||||
cat->fetch();
|
||||
|
||||
|
|
@ -244,35 +247,9 @@ void LLOutfitsList::performAction(std::string action)
|
|||
|
||||
void LLOutfitsList::setFilterSubString(const std::string& string)
|
||||
{
|
||||
applyFilter(string);
|
||||
|
||||
mFilterSubString = string;
|
||||
|
||||
for (outfits_map_t::iterator
|
||||
iter = mOutfitsMap.begin(),
|
||||
iter_end = mOutfitsMap.end();
|
||||
iter != iter_end; ++iter)
|
||||
{
|
||||
LLAccordionCtrlTab* tab = iter->second;
|
||||
if (tab)
|
||||
{
|
||||
LLWearableItemsList* list = dynamic_cast<LLWearableItemsList*> (tab->getAccordionView());
|
||||
if (list)
|
||||
{
|
||||
list->setFilterSubString(mFilterSubString);
|
||||
}
|
||||
|
||||
if(!mFilterSubString.empty())
|
||||
{
|
||||
//store accordion tab state when filter is not empty
|
||||
tab->notifyChildren(LLSD().with("action","store_state"));
|
||||
tab->setDisplayChildren(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
//restore accordion state after all those accodrion tab manipulations
|
||||
tab->notifyChildren(LLSD().with("action","restore_state"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -350,4 +327,102 @@ void LLOutfitsList::changeOutfitSelection(LLWearableItemsList* list, const LLUUI
|
|||
mSelectedOutfitUUID = category_id;
|
||||
}
|
||||
|
||||
void LLOutfitsList::onWearableItemsListRefresh(LLUICtrl* ctrl)
|
||||
{
|
||||
if (!ctrl || mFilterSubString.empty())
|
||||
return;
|
||||
|
||||
for (outfits_map_t::iterator
|
||||
iter = mOutfitsMap.begin(),
|
||||
iter_end = mOutfitsMap.end();
|
||||
iter != iter_end; ++iter)
|
||||
{
|
||||
LLAccordionCtrlTab* tab = iter->second;
|
||||
if (tab) continue;
|
||||
|
||||
LLWearableItemsList* list = dynamic_cast<LLWearableItemsList*>(tab->getAccordionView());
|
||||
if (list != ctrl) continue;
|
||||
|
||||
std::string title = tab->getTitle();
|
||||
LLStringUtil::toUpper(title);
|
||||
|
||||
std::string cur_filter = mFilterSubString;
|
||||
LLStringUtil::toUpper(cur_filter);
|
||||
|
||||
if (std::string::npos == title.find(cur_filter))
|
||||
{
|
||||
// hide tab if its title doesn't pass filter
|
||||
// and it has no visible items
|
||||
tab->setVisible(list->size() != 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
tab->setTitle(tab->getTitle(), cur_filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LLOutfitsList::applyFilter(const std::string& new_filter_substring)
|
||||
{
|
||||
for (outfits_map_t::iterator
|
||||
iter = mOutfitsMap.begin(),
|
||||
iter_end = mOutfitsMap.end();
|
||||
iter != iter_end; ++iter)
|
||||
{
|
||||
LLAccordionCtrlTab* tab = iter->second;
|
||||
if (!tab) continue;
|
||||
|
||||
bool more_restrictive = mFilterSubString.size() < new_filter_substring.size() && !new_filter_substring.substr(0, mFilterSubString.size()).compare(mFilterSubString);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
if(mFilterSubString.empty() && !new_filter_substring.empty())
|
||||
{
|
||||
//store accordion tab state when filter is not empty
|
||||
tab->notifyChildren(LLSD().with("action","store_state"));
|
||||
}
|
||||
|
||||
if (!new_filter_substring.empty())
|
||||
{
|
||||
tab->setDisplayChildren(true);
|
||||
|
||||
std::string title = tab->getTitle();
|
||||
LLStringUtil::toUpper(title);
|
||||
|
||||
std::string cur_filter = new_filter_substring;
|
||||
LLStringUtil::toUpper(cur_filter);
|
||||
|
||||
if (std::string::npos == title.find(cur_filter))
|
||||
{
|
||||
// hide tab if its title doesn't pass filter
|
||||
// and it has no visible items
|
||||
tab->setVisible(list->size() != 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
tab->setTitle(tab->getTitle(), cur_filter);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EOF
|
||||
|
|
|
|||
|
|
@ -94,6 +94,17 @@ private:
|
|||
*/
|
||||
void changeOutfitSelection(LLWearableItemsList* list, const LLUUID& category_id);
|
||||
|
||||
/**
|
||||
* 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 onWearableItemsListRefresh(LLUICtrl* ctrl);
|
||||
|
||||
/**
|
||||
* Highlights filtered items and hides tabs which haven't passed filter.
|
||||
*/
|
||||
void applyFilter(const std::string& new_filter_substring);
|
||||
|
||||
LLInventoryCategoriesObserver* mCategoriesObserver;
|
||||
|
||||
LLAccordionCtrl* mAccordion;
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
#include "llsidetray.h"
|
||||
#include "llworldmap.h"
|
||||
#include "llteleporthistorystorage.h"
|
||||
#include "lltextutil.h"
|
||||
#include "lluitextutil.h"
|
||||
|
||||
#include "llaccordionctrl.h"
|
||||
#include "llaccordionctrltab.h"
|
||||
|
|
|
|||
Loading…
Reference in New Issue