From 45a1a94a35142da71c507c724ae4d1498217e449 Mon Sep 17 00:00:00 2001
From: Alexander Gavriliuk <alexandrgproductengine@lindenlab.com>
Date: Tue, 10 Oct 2023 23:21:08 +0200
Subject: SL-20432 Filtering My Outfits with big number of items freezes UI

---
 indra/llui/llaccordionctrltab.h           |  10 +--
 indra/llui/llflatlistview.cpp             |  47 ++++++++-----
 indra/llui/llflatlistview.h               |  11 +--
 indra/newview/llinventoryitemslist.cpp    |   2 +-
 indra/newview/llinventoryitemslist.h      |   8 +--
 indra/newview/lloutfitgallery.cpp         |  41 +++++------
 indra/newview/lloutfitgallery.h           |   4 +-
 indra/newview/lloutfitslist.cpp           | 110 +++++++++++-------------------
 indra/newview/lloutfitslist.h             |   9 +--
 indra/newview/llpanelappearancetab.cpp    |  27 +++++++-
 indra/newview/llpanelappearancetab.h      |  13 +++-
 indra/newview/llpaneloutfitedit.cpp       |   5 +-
 indra/newview/llpaneloutfitsinventory.cpp |  30 +++-----
 indra/newview/llpaneloutfitsinventory.h   |   1 -
 indra/newview/llpanelwearing.cpp          |   9 ++-
 indra/newview/llpanelwearing.h            |   2 +-
 16 files changed, 158 insertions(+), 171 deletions(-)

diff --git a/indra/llui/llaccordionctrltab.h b/indra/llui/llaccordionctrltab.h
index 896a34cac4..496c34c38b 100644
--- a/indra/llui/llaccordionctrltab.h
+++ b/indra/llui/llaccordionctrltab.h
@@ -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&);
diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp
index 460bd0945b..e4f2dd11a4 100644
--- a/indra/llui/llflatlistview.cpp
+++ b/indra/llui/llflatlistview.cpp
@@ -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()
diff --git a/indra/llui/llflatlistview.h b/indra/llui/llflatlistview.h
index d47c1cf333..adb0e3e553 100644
--- a/indra/llui/llflatlistview.h
+++ b/indra/llui/llflatlistview.h
@@ -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;
diff --git a/indra/newview/llinventoryitemslist.cpp b/indra/newview/llinventoryitemslist.cpp
index 23129f7d44..cac859387c 100644
--- a/indra/newview/llinventoryitemslist.cpp
+++ b/indra/newview/llinventoryitemslist.cpp
@@ -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)
             {
diff --git a/indra/newview/llinventoryitemslist.h b/indra/newview/llinventoryitemslist.h
index ce41105f98..5b83298bb9 100644
--- a/indra/newview/llinventoryitemslist.h
+++ b/indra/newview/llinventoryitemslist.h
@@ -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);
 
diff --git a/indra/newview/lloutfitgallery.cpp b/indra/newview/lloutfitgallery.cpp
index de988555c5..e621c32911 100644
--- a/indra/newview/lloutfitgallery.cpp
+++ b/indra/newview/lloutfitgallery.cpp
@@ -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
diff --git a/indra/newview/lloutfitgallery.h b/indra/newview/lloutfitgallery.h
index 9915752962..f2366e6494 100644
--- a/indra/newview/lloutfitgallery.h
+++ b/indra/newview/lloutfitgallery.h
@@ -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();
diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp
index 8042b87a44..27d73fc4ae 100644
--- a/indra/newview/lloutfitslist.cpp
+++ b/indra/newview/lloutfitslist.cpp
@@ -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);
diff --git a/indra/newview/lloutfitslist.h b/indra/newview/lloutfitslist.h
index 7d212420a2..43c3ba75b5 100644
--- a/indra/newview/lloutfitslist.h
+++ b/indra/newview/lloutfitslist.h
@@ -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
diff --git a/indra/newview/llpanelappearancetab.cpp b/indra/newview/llpanelappearancetab.cpp
index 8fa8867c69..16bd8a1380 100644
--- a/indra/newview/llpanelappearancetab.cpp
+++ b/indra/newview/llpanelappearancetab.cpp
@@ -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;
diff --git a/indra/newview/llpanelappearancetab.h b/indra/newview/llpanelappearancetab.h
index 2ed6b00497..e81394dd3c 100644
--- a/indra/newview/llpanelappearancetab.h
+++ b/indra/newview/llpanelappearancetab.h
@@ -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
diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp
index 4a755a6e93..c7ae4eb0d9 100644
--- a/indra/newview/llpaneloutfitedit.cpp
+++ b/indra/newview/llpaneloutfitedit.cpp
@@ -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)
diff --git a/indra/newview/llpaneloutfitsinventory.cpp b/indra/newview/llpaneloutfitsinventory.cpp
index d8c34d5c40..af06de379d 100644
--- a/indra/newview/llpaneloutfitsinventory.cpp
+++ b/indra/newview/llpaneloutfitsinventory.cpp
@@ -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();
diff --git a/indra/newview/llpaneloutfitsinventory.h b/indra/newview/llpaneloutfitsinventory.h
index 50d7074d4b..91873a427a 100644
--- a/indra/newview/llpaneloutfitsinventory.h
+++ b/indra/newview/llpaneloutfitsinventory.h
@@ -66,7 +66,6 @@ protected:
 
 private:
 	LLTabContainer*			mAppearanceTabs;
-	std::string 			mFilterSubString;
 
 	//////////////////////////////////////////////////////////////////////////////////
 	// tab panels                                                                   //
diff --git a/indra/newview/llpanelwearing.cpp b/indra/newview/llpanelwearing.cpp
index 5242c4fef9..bfdb0fbc88 100644
--- a/indra/newview/llpanelwearing.cpp
+++ b/indra/newview/llpanelwearing.cpp
@@ -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
diff --git a/indra/newview/llpanelwearing.h b/indra/newview/llpanelwearing.h
index 18e543eec6..2f3f14956a 100644
--- a/indra/newview/llpanelwearing.h
+++ b/indra/newview/llpanelwearing.h
@@ -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);
 
-- 
cgit v1.2.3