Implemented normal EXT-7002 (Inventory flat list needs optimization.):
- changed the LLInventoryItemsList::addNewItem() to add item to the list without immediately rearranging - implemented LLFlatListViewEx::setFilterSubString(), sets up new filter string and filters the list. - implemented LLFlatListViewEx::filterItems(), filters the list, rearranges and notifies parent about shape changes. The list items are filtered using the notify() functionality, without need in adding/removing them on each filter call. It sends 'match_filter' request to items and interprets the returned zero as sign of matched filter string, i.e. we don't hide items that don't support 'match_filter' action(separators etc). - filtring of LLOutfitsList. Reviewed by Mike Antipov at https://codereview.productengine.com/secondlife/r/342/ --HG-- branch : product-enginemeow-7.2.2
parent
779bf4bb59
commit
d6edc4cd12
|
|
@ -297,6 +297,27 @@ void LLFlatListView::setNoItemsCommentText(const std::string& comment_text)
|
|||
mNoItemsCommentTextbox->setValue(comment_text);
|
||||
}
|
||||
|
||||
U32 LLFlatListView::size(const bool only_visible_items) const
|
||||
{
|
||||
if (only_visible_items)
|
||||
{
|
||||
U32 size = 0;
|
||||
for (pairs_const_iterator_t
|
||||
iter = mItemPairs.begin(),
|
||||
iter_end = mItemPairs.end();
|
||||
iter != iter_end; ++iter)
|
||||
{
|
||||
if ((*iter)->first->getVisible())
|
||||
++size;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
else
|
||||
{
|
||||
return mItemPairs.size();
|
||||
}
|
||||
}
|
||||
|
||||
void LLFlatListView::clear()
|
||||
{
|
||||
// do not use LLView::deleteAllChildren to avoid removing nonvisible items. drag-n-drop for ex.
|
||||
|
|
@ -426,7 +447,7 @@ void LLFlatListView::rearrangeItems()
|
|||
{
|
||||
static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
|
||||
|
||||
setNoItemsCommentVisible(mItemPairs.empty());
|
||||
setNoItemsCommentVisible(0==size());
|
||||
|
||||
if (mItemPairs.empty()) return;
|
||||
|
||||
|
|
@ -745,19 +766,43 @@ LLRect LLFlatListView::getLastSelectedItemRect()
|
|||
void LLFlatListView::selectFirstItem ()
|
||||
{
|
||||
// No items - no actions!
|
||||
if (mItemPairs.empty()) return;
|
||||
if (0 == size()) return;
|
||||
|
||||
selectItemPair(mItemPairs.front(), true);
|
||||
ensureSelectedVisible();
|
||||
// Select first visible item
|
||||
for (pairs_iterator_t
|
||||
iter = mItemPairs.begin(),
|
||||
iter_end = mItemPairs.end();
|
||||
iter != iter_end; ++iter)
|
||||
{
|
||||
// skip invisible items
|
||||
if ( (*iter)->first->getVisible() )
|
||||
{
|
||||
selectItemPair(*iter, true);
|
||||
ensureSelectedVisible();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LLFlatListView::selectLastItem ()
|
||||
{
|
||||
// No items - no actions!
|
||||
if (mItemPairs.empty()) return;
|
||||
if (0 == size()) return;
|
||||
|
||||
selectItemPair(mItemPairs.back(), true);
|
||||
ensureSelectedVisible();
|
||||
// Select last visible item
|
||||
for (pairs_list_t::reverse_iterator
|
||||
r_iter = mItemPairs.rbegin(),
|
||||
r_iter_end = mItemPairs.rend();
|
||||
r_iter != r_iter_end; ++r_iter)
|
||||
{
|
||||
// skip invisible items
|
||||
if ( (*r_iter)->first->getVisible() )
|
||||
{
|
||||
selectItemPair(*r_iter, true);
|
||||
ensureSelectedVisible();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LLFlatListView::ensureSelectedVisible()
|
||||
|
|
@ -775,14 +820,14 @@ void LLFlatListView::ensureSelectedVisible()
|
|||
bool LLFlatListView::selectNextItemPair(bool is_up_direction, bool reset_selection)
|
||||
{
|
||||
// No items - no actions!
|
||||
if ( !mItemPairs.size() )
|
||||
if ( 0 == size() )
|
||||
return false;
|
||||
|
||||
|
||||
item_pair_t* to_sel_pair = NULL;
|
||||
item_pair_t* cur_sel_pair = NULL;
|
||||
if ( mSelectedItemPairs.size() )
|
||||
{
|
||||
item_pair_t* to_sel_pair = NULL;
|
||||
item_pair_t* cur_sel_pair = NULL;
|
||||
|
||||
// Take the last selected pair
|
||||
cur_sel_pair = mSelectedItemPairs.back();
|
||||
// Bases on given direction choose next item to select
|
||||
|
|
@ -816,42 +861,42 @@ bool LLFlatListView::selectNextItemPair(bool is_up_direction, bool reset_selecti
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( to_sel_pair )
|
||||
{
|
||||
bool select = true;
|
||||
if ( reset_selection )
|
||||
{
|
||||
// Reset current selection if we were asked about it
|
||||
resetSelection();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If item already selected and no reset request than we should deselect last selected item.
|
||||
select = (mSelectedItemPairs.end() == std::find(mSelectedItemPairs.begin(), mSelectedItemPairs.end(), to_sel_pair));
|
||||
}
|
||||
// Select/Deselect next item
|
||||
selectItemPair(select ? to_sel_pair : cur_sel_pair, select);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there weren't selected items then choose the first one bases on given direction
|
||||
cur_sel_pair = (is_up_direction) ? mItemPairs.back() : mItemPairs.front();
|
||||
// Force selection to first item
|
||||
to_sel_pair = cur_sel_pair;
|
||||
}
|
||||
|
||||
|
||||
if ( to_sel_pair )
|
||||
{
|
||||
bool select = true;
|
||||
|
||||
if ( reset_selection )
|
||||
{
|
||||
// Reset current selection if we were asked about it
|
||||
resetSelection();
|
||||
}
|
||||
if (is_up_direction)
|
||||
selectLastItem();
|
||||
else
|
||||
{
|
||||
// If item already selected and no reset request than we should deselect last selected item.
|
||||
select = (mSelectedItemPairs.end() == std::find(mSelectedItemPairs.begin(), mSelectedItemPairs.end(), to_sel_pair));
|
||||
}
|
||||
|
||||
// Select/Deselect next item
|
||||
selectItemPair(select ? to_sel_pair : cur_sel_pair, select);
|
||||
|
||||
selectFirstItem();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
BOOL LLFlatListView::canSelectAll() const
|
||||
{
|
||||
return !mItemPairs.empty() && mAllowSelection && mMultipleSelection;
|
||||
return 0 != size() && mAllowSelection && mMultipleSelection;
|
||||
}
|
||||
|
||||
void LLFlatListView::selectAll()
|
||||
|
|
@ -1167,4 +1212,51 @@ void LLFlatListViewEx::updateNoItemsMessage(const std::string& filter_string)
|
|||
|
||||
}
|
||||
|
||||
void LLFlatListViewEx::setFilterSubString(const std::string& filter_str)
|
||||
{
|
||||
if (0 != LLStringUtil::compareInsensitive(filter_str, mFilterSubString))
|
||||
{
|
||||
mFilterSubString = filter_str;
|
||||
updateNoItemsMessage(mFilterSubString);
|
||||
filterItems();
|
||||
}
|
||||
}
|
||||
|
||||
void LLFlatListViewEx::filterItems()
|
||||
{
|
||||
typedef std::vector <LLPanel*> item_panel_list_t;
|
||||
|
||||
std::string cur_filter = mFilterSubString;
|
||||
LLStringUtil::toUpper(cur_filter);
|
||||
|
||||
LLSD action;
|
||||
action.with("match_filter", cur_filter);
|
||||
|
||||
item_panel_list_t items;
|
||||
getItems(items);
|
||||
|
||||
for (item_panel_list_t::iterator
|
||||
iter = items.begin(),
|
||||
iter_end = items.end();
|
||||
iter != iter_end; ++iter)
|
||||
{
|
||||
LLPanel* pItem = (*iter);
|
||||
// 0 signifies that filter is matched,
|
||||
// i.e. we don't hide items that don't support 'match_filter' action, separators etc.
|
||||
if (0 == pItem->notify(action))
|
||||
{
|
||||
pItem->setVisible(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: implement (re)storing of current selection.
|
||||
selectItem(pItem, false);
|
||||
pItem->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
rearrangeItems();
|
||||
notifyParentItemsRectChanged();
|
||||
}
|
||||
|
||||
//EOF
|
||||
|
|
|
|||
|
|
@ -264,9 +264,8 @@ public:
|
|||
/** Get number of selected items in the list */
|
||||
U32 numSelected() const {return mSelectedItemPairs.size(); }
|
||||
|
||||
/** Get number of items in the list */
|
||||
U32 size() const { return mItemPairs.size(); }
|
||||
|
||||
/** Get number of (visible) items in the list */
|
||||
U32 size(const bool only_visible_items = true) const;
|
||||
|
||||
/** Removes all items from the list */
|
||||
virtual void clear();
|
||||
|
|
@ -464,6 +463,17 @@ public:
|
|||
void setNoItemsMsg(const std::string& msg) { mNoItemsMsg = msg; }
|
||||
void setNoFilteredItemsMsg(const std::string& msg) { mNoFilteredItemsMsg = msg; }
|
||||
|
||||
/**
|
||||
* Sets up new filter string and filters the list.
|
||||
*/
|
||||
void setFilterSubString(const std::string& filter_str);
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
protected:
|
||||
LLFlatListViewEx(const Params& p);
|
||||
|
||||
|
|
@ -478,6 +488,7 @@ protected:
|
|||
private:
|
||||
std::string mNoFilteredItemsMsg;
|
||||
std::string mNoItemsMsg;
|
||||
std::string mFilterSubString;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
#include "llinventoryfunctions.h"
|
||||
#include "llinventorymodel.h"
|
||||
#include "lltextutil.h"
|
||||
#include "lltrans.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -63,6 +64,16 @@ LLPanelInventoryListItemBase* LLPanelInventoryListItemBase::create(LLViewerInven
|
|||
return list_item;
|
||||
}
|
||||
|
||||
void LLPanelInventoryListItemBase::draw()
|
||||
{
|
||||
if (getNeedsRefresh())
|
||||
{
|
||||
updateItem();
|
||||
setNeedsRefresh(false);
|
||||
}
|
||||
LLPanel::draw();
|
||||
}
|
||||
|
||||
void LLPanelInventoryListItemBase::updateItem()
|
||||
{
|
||||
setIconImage(mIconImage);
|
||||
|
|
@ -121,7 +132,7 @@ BOOL LLPanelInventoryListItemBase::postBuild()
|
|||
|
||||
mIconImage = get_item_icon(mItem->getType(), mItem->getInventoryType(), mItem->getFlags(), FALSE);
|
||||
|
||||
updateItem();
|
||||
setNeedsRefresh(true);
|
||||
|
||||
setWidgetsVisible(false);
|
||||
reshapeWidgets();
|
||||
|
|
@ -148,6 +159,34 @@ void LLPanelInventoryListItemBase::onMouseLeave(S32 x, S32 y, MASK mask)
|
|||
LLPanel::onMouseLeave(x, y, mask);
|
||||
}
|
||||
|
||||
S32 LLPanelInventoryListItemBase::notify(const LLSD& info)
|
||||
{
|
||||
S32 rv = 0;
|
||||
if(info.has("match_filter"))
|
||||
{
|
||||
mHighlightedText = info["match_filter"].asString();
|
||||
|
||||
std::string test(mItem->getName());
|
||||
LLStringUtil::toUpper(test);
|
||||
|
||||
if(mHighlightedText.empty() || std::string::npos != test.find(mHighlightedText))
|
||||
{
|
||||
rv = 0; // substring is found
|
||||
}
|
||||
else
|
||||
{
|
||||
rv = -1;
|
||||
}
|
||||
|
||||
setNeedsRefresh(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
rv = LLPanel::notify(info);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
LLPanelInventoryListItemBase::LLPanelInventoryListItemBase(LLViewerInventoryItem* item)
|
||||
: LLPanel()
|
||||
, mItem(item)
|
||||
|
|
@ -156,6 +195,7 @@ LLPanelInventoryListItemBase::LLPanelInventoryListItemBase(LLViewerInventoryItem
|
|||
, mWidgetSpacing(WIDGET_SPACING)
|
||||
, mLeftWidgetsWidth(0)
|
||||
, mRightWidgetsWidth(0)
|
||||
, mNeedsRefresh(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -278,13 +318,15 @@ LLInventoryItemsList::Params::Params()
|
|||
{}
|
||||
|
||||
LLInventoryItemsList::LLInventoryItemsList(const LLInventoryItemsList::Params& p)
|
||||
: LLFlatListView(p)
|
||||
: LLFlatListViewEx(p)
|
||||
, mNeedsRefresh(false)
|
||||
{
|
||||
// TODO: mCommitOnSelectionChange is set to "false" in LLFlatListView
|
||||
// but reset to true in all derived classes. This settings might need to
|
||||
// be added to LLFlatListView::Params() and/or set to "true" by default.
|
||||
setCommitOnSelectionChange(true);
|
||||
|
||||
setNoFilteredItemsMsg(LLTrans::getString("InventoryNoMatchingItems"));
|
||||
}
|
||||
|
||||
// virtual
|
||||
|
|
@ -304,7 +346,7 @@ void LLInventoryItemsList::refreshList(const LLInventoryModel::item_array_t item
|
|||
|
||||
void LLInventoryItemsList::draw()
|
||||
{
|
||||
LLFlatListView::draw();
|
||||
LLFlatListViewEx::draw();
|
||||
if(mNeedsRefresh)
|
||||
{
|
||||
refresh();
|
||||
|
|
@ -332,7 +374,8 @@ void LLInventoryItemsList::refresh()
|
|||
break;
|
||||
}
|
||||
LLViewerInventoryItem* item = gInventory.getItem(*it);
|
||||
addNewItem(item);
|
||||
// Do not rearrange items on each adding, let's do that on filter call
|
||||
addNewItem(item, false);
|
||||
++nadded;
|
||||
}
|
||||
|
||||
|
|
@ -342,6 +385,9 @@ void LLInventoryItemsList::refresh()
|
|||
removeItemByUUID(*it);
|
||||
}
|
||||
|
||||
// Filter, rearrange and notify parent about shape changes
|
||||
filterItems();
|
||||
|
||||
bool needs_refresh = add_limit_exceeded;
|
||||
setNeedsRefresh(needs_refresh);
|
||||
}
|
||||
|
|
@ -363,7 +409,7 @@ void LLInventoryItemsList::computeDifference(
|
|||
LLCommonUtils::computeDifference(vnew, vcur, vadded, vremoved);
|
||||
}
|
||||
|
||||
void LLInventoryItemsList::addNewItem(LLViewerInventoryItem* item)
|
||||
void LLInventoryItemsList::addNewItem(LLViewerInventoryItem* item, bool rearrange /*= true*/)
|
||||
{
|
||||
if (!item)
|
||||
{
|
||||
|
|
@ -375,7 +421,7 @@ void LLInventoryItemsList::addNewItem(LLViewerInventoryItem* item)
|
|||
if (!list_item)
|
||||
return;
|
||||
|
||||
bool is_item_added = addItem(list_item, item->getUUID());
|
||||
bool is_item_added = addItem(list_item, item->getUUID(), ADD_BOTTOM, rearrange);
|
||||
if (!is_item_added)
|
||||
{
|
||||
llwarns << "Couldn't add flat list item." << llendl;
|
||||
|
|
|
|||
|
|
@ -64,13 +64,16 @@ class LLViewerInventoryItem;
|
|||
class LLPanelInventoryListItemBase : public LLPanel
|
||||
{
|
||||
public:
|
||||
|
||||
static LLPanelInventoryListItemBase* create(LLViewerInventoryItem* item);
|
||||
|
||||
virtual void draw();
|
||||
|
||||
/**
|
||||
* Called after inventory item was updated, update panel widgets to reflect inventory changes.
|
||||
* Let item know it need to be refreshed in next draw()
|
||||
*/
|
||||
virtual void updateItem();
|
||||
void setNeedsRefresh(bool needs_refresh){ mNeedsRefresh = needs_refresh; }
|
||||
|
||||
bool getNeedsRefresh(){ return mNeedsRefresh; }
|
||||
|
||||
/**
|
||||
* Add widget to left side
|
||||
|
|
@ -107,6 +110,11 @@ public:
|
|||
*/
|
||||
/*virtual*/ void setValue(const LLSD& value);
|
||||
|
||||
/**
|
||||
* Handles filter request
|
||||
*/
|
||||
/*virtual*/ S32 notify(const LLSD& info);
|
||||
|
||||
/* Highlights item */
|
||||
/*virtual*/ void onMouseEnter(S32 x, S32 y, MASK mask);
|
||||
/* Removes item highlight */
|
||||
|
|
@ -125,6 +133,11 @@ protected:
|
|||
*/
|
||||
virtual void init();
|
||||
|
||||
/**
|
||||
* Called after inventory item was updated, update panel widgets to reflect inventory changes.
|
||||
*/
|
||||
virtual void updateItem();
|
||||
|
||||
/** setter for mIconCtrl */
|
||||
void setIconCtrl(LLIconCtrl* icon) { mIconCtrl = icon; }
|
||||
/** setter for MTitleCtrl */
|
||||
|
|
@ -178,14 +191,15 @@ private:
|
|||
|
||||
S32 mLeftWidgetsWidth;
|
||||
S32 mRightWidgetsWidth;
|
||||
bool mNeedsRefresh;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class LLInventoryItemsList : public LLFlatListView
|
||||
class LLInventoryItemsList : public LLFlatListViewEx
|
||||
{
|
||||
public:
|
||||
struct Params : public LLInitParam::Block<Params, LLFlatListView::Params>
|
||||
struct Params : public LLInitParam::Block<Params, LLFlatListViewEx::Params>
|
||||
{
|
||||
Params();
|
||||
};
|
||||
|
|
@ -225,7 +239,7 @@ protected:
|
|||
/**
|
||||
* Add an item to the list
|
||||
*/
|
||||
virtual void addNewItem(LLViewerInventoryItem* item);
|
||||
virtual void addNewItem(LLViewerInventoryItem* item, bool rearrange = true);
|
||||
|
||||
private:
|
||||
uuid_vec_t mIDs; // IDs of items that were added in refreshList().
|
||||
|
|
|
|||
|
|
@ -245,6 +245,34 @@ void LLOutfitsList::performAction(std::string action)
|
|||
void LLOutfitsList::setFilterSubString(const std::string& 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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
Loading…
Reference in New Issue