From 4395d8e9f1302e8efcda4b35da3b0e3ea6c36928 Mon Sep 17 00:00:00 2001 From: Mike Antipov Date: Wed, 21 Apr 2010 10:17:09 +0300 Subject: WIP: critical bug EXT-4837 ([NUX] When filter results in null state, provide a message suggesting the user try global search.) Partial implementation of help text in case of empty list when filtered for Nearby and Recent lists of People panel: * Extracted support implementation of several messages for empty list depend of filtering from LLGroupList to LLFlatListEx * change inheritence of LLGroupList and LLAvatarList from LLFlatListView to LLFlatListViewEx * updated panel people to init Nearby & Recent lists with appropriate messages. --HG-- branch : product-engine --- indra/llui/llflatlistview.cpp | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) (limited to 'indra/llui/llflatlistview.cpp') diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp index 82f054c4b7..990bf5cd22 100644 --- a/indra/llui/llflatlistview.cpp +++ b/indra/llui/llflatlistview.cpp @@ -1,10 +1,10 @@ /** * @file llflatlistview.cpp - * @brief LLFlatListView base class + * @brief LLFlatListView base class and extension to support messages for several cases of an empty list. * * $LicenseInfo:firstyear=2009&license=viewergpl$ * - * Copyright (c) 2009, Linden Research, Inc. + * Copyright (c) 2009-2010, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab @@ -1122,4 +1122,38 @@ void LLFlatListView::detachItems(std::vector& detached_items) } } + +/************************************************************************/ +/* LLFlatListViewEx implementation */ +/************************************************************************/ +LLFlatListViewEx::Params::Params() +: no_items_msg("no_items_msg") +, no_filtered_items_msg("no_filtered_items_msg") +{ + +} + +LLFlatListViewEx::LLFlatListViewEx(const Params& p) +: LLFlatListView(p) +, mNoFilteredItemsMsg(p.no_filtered_items_msg) +, mNoItemsMsg(p.no_items_msg) +{ + +} + +void LLFlatListViewEx::updateNoItemsMessage(bool items_filtered) +{ + if (items_filtered) + { + // items were filtered + setNoItemsCommentText(mNoFilteredItemsMsg); + } + else + { + // list does not contain any items at all + setNoItemsCommentText(mNoItemsMsg); + } + +} + //EOF -- cgit v1.3 From a00712e95133001b29fb15b15ccb6e66f2ec075b Mon Sep 17 00:00:00 2001 From: Mike Antipov Date: Mon, 26 Apr 2010 17:19:43 +0300 Subject: Fixed major bug EXT-6092 [crashhunters] Crash in LLFlatListView::selectItemPair Reason: A) incorrect UP arrow handling in accordion control: invisible accordion tab was selected to handle selection. B) invalid std::map item (in empty map) was used to make selection in Flat List Fix: added checks against empty map before use front/back item pair before selecting first/last items. Also updated processing of the UP key in accordion control to not select invisible accordion tab. Reviewed by Vadim Savchuk at https://codereview.productengine.com/secondlife/r/318/ --HG-- branch : product-engine --- indra/llui/llaccordionctrl.cpp | 16 ++++++++++++---- indra/llui/llflatlistview.cpp | 6 ++++++ 2 files changed, 18 insertions(+), 4 deletions(-) (limited to 'indra/llui/llflatlistview.cpp') diff --git a/indra/llui/llaccordionctrl.cpp b/indra/llui/llaccordionctrl.cpp index 136fd2a9ac..dca43c5784 100644 --- a/indra/llui/llaccordionctrl.cpp +++ b/indra/llui/llaccordionctrl.cpp @@ -668,15 +668,23 @@ S32 LLAccordionCtrl::notifyParent(const LLSD& info) LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[i]); if(accordion_tab->hasFocus() && i>0) { + bool prev_visible_tab_found = false; while(i>0) { if(mAccordionTabs[--i]->getVisible()) + { + prev_visible_tab_found = true; break; + } } - - accordion_tab = dynamic_cast(mAccordionTabs[i]); - accordion_tab->notify(LLSD().with("action","select_last")); - return 1; + + if (prev_visible_tab_found) + { + accordion_tab = dynamic_cast(mAccordionTabs[i]); + accordion_tab->notify(LLSD().with("action","select_last")); + return 1; + } + break; } } return 0; diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp index 990bf5cd22..e0b2244654 100644 --- a/indra/llui/llflatlistview.cpp +++ b/indra/llui/llflatlistview.cpp @@ -744,12 +744,18 @@ LLRect LLFlatListView::getLastSelectedItemRect() void LLFlatListView::selectFirstItem () { + // No items - no actions! + if (mItemPairs.empty()) return; + selectItemPair(mItemPairs.front(), true); ensureSelectedVisible(); } void LLFlatListView::selectLastItem () { + // No items - no actions! + if (mItemPairs.empty()) return; + selectItemPair(mItemPairs.back(), true); ensureSelectedVisible(); } -- cgit v1.3 From 872b4d7ed0bd532e349d3c177a63d48e17d0bdb3 Mon Sep 17 00:00:00 2001 From: Mike Antipov Date: Wed, 28 Apr 2010 11:00:53 +0300 Subject: Fixed critical bug EXT-4837 ( [NUX] When filter results in null state, provide a message suggesting the user try global search.) Implemented passing of entered filter substring without head spaces to search URI in help message for null filtered results. * Updated setting "no item message" to use search term from filter editor for avatar & group lists and for inventory panels. * Updated appropriate translatable strings to get [SEARCH_TERM] substitution. * Updated processing of filter substring to pass original string to methods applied new filter. Additional necessary changes * Changed place to set "no item message" for group list from refresh to where filter is updated (like in avatar lists) * Removed converting of filter substring to upper case in Places & My Appearance sidepanels (this conversion has already been implemented in where filter is applied) * Added a separate message for Landmarks to set search URI to secondlife:///app/search/places for Lanmarks panel; other inventory related panels have URL to all searched categories (secondlife:///app/search/all) Reviewed by Vadim Savchuk at https://codereview.productengine.com/secondlife/r/326/ --HG-- branch : product-engine --- indra/llui/llflatlistview.cpp | 9 +++++++-- indra/llui/llflatlistview.h | 6 +++--- indra/newview/llavatarlist.cpp | 8 +++++--- indra/newview/llfolderview.cpp | 4 +++- indra/newview/llgrouplist.cpp | 13 ++++++++----- indra/newview/llinventoryfilter.cpp | 5 +++-- indra/newview/llinventoryfilter.h | 2 ++ indra/newview/llpanellandmarks.cpp | 1 + indra/newview/llpanelpeople.cpp | 20 ++++++++++++-------- indra/newview/llpanelpeople.h | 1 + indra/newview/llpanelplaces.cpp | 3 +-- indra/newview/llsidepanelappearance.cpp | 3 +-- indra/newview/skins/default/xui/en/panel_people.xml | 8 ++++---- indra/newview/skins/default/xui/en/strings.xml | 3 ++- 14 files changed, 53 insertions(+), 33 deletions(-) (limited to 'indra/llui/llflatlistview.cpp') diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp index e0b2244654..ec247b25c3 100644 --- a/indra/llui/llflatlistview.cpp +++ b/indra/llui/llflatlistview.cpp @@ -1147,12 +1147,17 @@ LLFlatListViewEx::LLFlatListViewEx(const Params& p) } -void LLFlatListViewEx::updateNoItemsMessage(bool items_filtered) +void LLFlatListViewEx::updateNoItemsMessage(const std::string& filter_string) { + bool items_filtered = !filter_string.empty(); if (items_filtered) { // items were filtered - setNoItemsCommentText(mNoFilteredItemsMsg); + LLStringUtil::format_map_t args; + args["[SEARCH_TERM]"] = LLURI::escape(filter_string); + std::string text = mNoFilteredItemsMsg; + LLStringUtil::format(text, args); + setNoItemsCommentText(text); } else { diff --git a/indra/llui/llflatlistview.h b/indra/llui/llflatlistview.h index f7d094f7e7..4f718ab0dc 100644 --- a/indra/llui/llflatlistview.h +++ b/indra/llui/llflatlistview.h @@ -470,10 +470,10 @@ protected: /** * Applies a message for empty list depend on passed argument. * - * @param items_filtered - if true message for filtered items will be set, otherwise for - * completely empty list. + * @param filter_string - if is not empty, message for filtered items will be set, otherwise for + * completely empty list. Value of filter string will be passed as search_term in SLURL. */ - void updateNoItemsMessage(bool items_filtered); + void updateNoItemsMessage(const std::string& filter_string); private: std::string mNoFilteredItemsMsg; diff --git a/indra/newview/llavatarlist.cpp b/indra/newview/llavatarlist.cpp index 8ba47b5198..e32daf55ca 100644 --- a/indra/newview/llavatarlist.cpp +++ b/indra/newview/llavatarlist.cpp @@ -176,13 +176,15 @@ void LLAvatarList::clear() void LLAvatarList::setNameFilter(const std::string& filter) { - if (mNameFilter != filter) + std::string filter_upper = filter; + LLStringUtil::toUpper(filter_upper); + if (mNameFilter != filter_upper) { - mNameFilter = filter; + mNameFilter = filter_upper; // update message for empty state here instead of refresh() to avoid blinking when switch // between tabs. - updateNoItemsMessage(!mNameFilter.empty()); + updateNoItemsMessage(filter); setDirty(); } } diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index c492bfcef1..eba4cdfa31 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -946,7 +946,9 @@ void LLFolderView::draw() } else { - mStatusText = LLTrans::getString(getFilter()->getEmptyLookupMessage()); + LLStringUtil::format_map_t args; + args["[SEARCH_TERM]"] = LLURI::escape(getFilter()->getFilterSubStringOrig()); + mStatusText = LLTrans::getString(getFilter()->getEmptyLookupMessage(), args); //font->renderUTF8(mStatusText, 0, 2, 1, sSearchStatusColor, LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE ); } mStatusTextBox->setValue(mStatusText); diff --git a/indra/newview/llgrouplist.cpp b/indra/newview/llgrouplist.cpp index f21b6e1085..252c34cf9c 100644 --- a/indra/newview/llgrouplist.cpp +++ b/indra/newview/llgrouplist.cpp @@ -131,9 +131,15 @@ BOOL LLGroupList::handleRightMouseDown(S32 x, S32 y, MASK mask) void LLGroupList::setNameFilter(const std::string& filter) { - if (mNameFilter != filter) + std::string filter_upper = filter; + LLStringUtil::toUpper(filter_upper); + if (mNameFilter != filter_upper) { - mNameFilter = filter; + mNameFilter = filter_upper; + + // set no items message depend on filter state + updateNoItemsMessage(filter); + setDirty(); } } @@ -151,9 +157,6 @@ void LLGroupList::refresh() LLUUID id; bool have_filter = !mNameFilter.empty(); - // set no items message depend on filter state & total count of groups - updateNoItemsMessage(have_filter); - clear(); for(S32 i = 0; i < count; ++i) diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp index 1a488175ac..901a570487 100644 --- a/indra/newview/llinventoryfilter.cpp +++ b/indra/newview/llinventoryfilter.cpp @@ -329,9 +329,10 @@ void LLInventoryFilter::setFilterSubString(const std::string& string) // appending new characters const BOOL more_restrictive = mFilterSubString.size() < string.size() && !string.substr(0, mFilterSubString.size()).compare(mFilterSubString); - mFilterSubString = string; + mFilterSubStringOrig = string; + LLStringUtil::trimHead(mFilterSubStringOrig); + mFilterSubString = mFilterSubStringOrig; LLStringUtil::toUpper(mFilterSubString); - LLStringUtil::trimHead(mFilterSubString); if (less_restrictive) { setModified(FILTER_LESS_RESTRICTIVE); diff --git a/indra/newview/llinventoryfilter.h b/indra/newview/llinventoryfilter.h index b01554edc8..2376ba5d22 100644 --- a/indra/newview/llinventoryfilter.h +++ b/indra/newview/llinventoryfilter.h @@ -84,6 +84,7 @@ public: void setFilterSubString(const std::string& string); const std::string& getFilterSubString(BOOL trim = FALSE) const; + const std::string& getFilterSubStringOrig() const { return mFilterSubStringOrig; } BOOL hasFilterString() const; void setFilterPermissions(PermissionMask perms); @@ -181,6 +182,7 @@ private: std::string::size_type mSubStringMatchOffset; std::string mFilterSubString; + std::string mFilterSubStringOrig; const std::string mName; S32 mFilterGeneration; diff --git a/indra/newview/llpanellandmarks.cpp b/indra/newview/llpanellandmarks.cpp index 67d40a39b1..0a978d1b26 100644 --- a/indra/newview/llpanellandmarks.cpp +++ b/indra/newview/llpanellandmarks.cpp @@ -583,6 +583,7 @@ void LLLandmarksPanel::initLandmarksPanel(LLPlacesInventoryPanel* inventory_list if (!inventory_list->getFilter()) return; + inventory_list->getFilter()->setEmptyLookupMessage("PlacesNoMatchingItems"); inventory_list->setFilterTypes(0x1 << LLInventoryType::IT_LANDMARK); inventory_list->setSelectCallback(boost::bind(&LLLandmarksPanel::onSelectionChange, this, inventory_list, _1, _2)); diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index f60951ca66..f2c0f92f9b 100644 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -443,6 +443,7 @@ public: LLPanelPeople::LLPanelPeople() : LLPanel(), mFilterSubString(LLStringUtil::null), + mFilterSubStringOrig(LLStringUtil::null), mFilterEditor(NULL), mTabContainer(NULL), mOnlineFriendList(NULL), @@ -658,7 +659,9 @@ void LLPanelPeople::updateFriendListHelpText() { //update help text for empty lists std::string message_name = mFilterSubString.empty() ? "no_friends_msg" : "no_filtered_friends_msg"; - no_friends_text->setText(getString(message_name)); + LLStringUtil::format_map_t args; + args["[SEARCH_TERM]"] = LLURI::escape(mFilterSubStringOrig); + no_friends_text->setText(getString(message_name, args)); } } @@ -981,10 +984,11 @@ bool LLPanelPeople::isRealGroup() void LLPanelPeople::onFilterEdit(const std::string& search_string) { - std::string search_upper = search_string; + mFilterSubStringOrig = search_string; + LLStringUtil::trimHead(mFilterSubStringOrig); // Searches are case-insensitive + std::string search_upper = mFilterSubStringOrig; LLStringUtil::toUpper(search_upper); - LLStringUtil::trimHead(search_upper); if (mFilterSubString == search_upper) return; @@ -999,11 +1003,11 @@ void LLPanelPeople::onFilterEdit(const std::string& search_string) // Apply new filter. - mNearbyList->setNameFilter(mFilterSubString); - mOnlineFriendList->setNameFilter(mFilterSubString); - mAllFriendList->setNameFilter(mFilterSubString); - mRecentList->setNameFilter(mFilterSubString); - mGroupList->setNameFilter(mFilterSubString); + mNearbyList->setNameFilter(mFilterSubStringOrig); + mOnlineFriendList->setNameFilter(mFilterSubStringOrig); + mAllFriendList->setNameFilter(mFilterSubStringOrig); + mRecentList->setNameFilter(mFilterSubStringOrig); + mGroupList->setNameFilter(mFilterSubStringOrig); setAccordionCollapsedByUser("tab_online", false); setAccordionCollapsedByUser("tab_all", false); diff --git a/indra/newview/llpanelpeople.h b/indra/newview/llpanelpeople.h index 1bd3ea471c..17c45a034b 100644 --- a/indra/newview/llpanelpeople.h +++ b/indra/newview/llpanelpeople.h @@ -169,6 +169,7 @@ private: Updater* mRecentListUpdater; std::string mFilterSubString; + std::string mFilterSubStringOrig; }; #endif //LL_LLPANELPEOPLE_H diff --git a/indra/newview/llpanelplaces.cpp b/indra/newview/llpanelplaces.cpp index 89293d0e50..51a11e97e4 100644 --- a/indra/newview/llpanelplaces.cpp +++ b/indra/newview/llpanelplaces.cpp @@ -528,8 +528,7 @@ void LLPanelPlaces::onFilterEdit(const std::string& search_string, bool force_fi std::string string = search_string; // Searches are case-insensitive - LLStringUtil::toUpper(string); - LLStringUtil::trimHead(string); + // but we don't convert the typed string to upper-case so that it can be fed to the web search as-is. mActivePanel->onSearchEdit(string); } diff --git a/indra/newview/llsidepanelappearance.cpp b/indra/newview/llsidepanelappearance.cpp index 87494daaa9..f38df19de0 100644 --- a/indra/newview/llsidepanelappearance.cpp +++ b/indra/newview/llsidepanelappearance.cpp @@ -197,8 +197,7 @@ void LLSidepanelAppearance::onFilterEdit(const std::string& search_string) mFilterSubString = search_string; // Searches are case-insensitive - LLStringUtil::toUpper(mFilterSubString); - LLStringUtil::trimHead(mFilterSubString); + // but we don't convert the typed string to upper-case so that it can be fed to the web search as-is. mPanelOutfitsInventory->onSearchEdit(mFilterSubString); } diff --git a/indra/newview/skins/default/xui/en/panel_people.xml b/indra/newview/skins/default/xui/en/panel_people.xml index fa4213667b..066ea3be6e 100644 --- a/indra/newview/skins/default/xui/en/panel_people.xml +++ b/indra/newview/skins/default/xui/en/panel_people.xml @@ -16,13 +16,13 @@ value="No recent people. Looking for people to hang out with? Try [secondlife:///app/search/people Search] or the [secondlife:///app/worldmap World Map]." /> + value="Didn't find what you're looking for? Try [secondlife:///app/search/people/[SEARCH_TERM] Search]." /> + value="Didn't find what you're looking for? Try [secondlife:///app/search/people/[SEARCH_TERM] Search]." /> @@ -36,7 +36,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M - Didn't find what you're looking for? Try [secondlife:///app/search/people Search]. + Didn't find what you're looking for? Try [secondlife:///app/search/people/[SEARCH_TERM] Search]. + value="Didn't find what you're looking for? Try [secondlife:///app/search/groups/[SEARCH_TERM] Search]." /> diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index bf30e89a59..b0e43e72ed 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -1842,7 +1842,8 @@ Clears (deletes) the media and all params from the given face. Press ESC to return to World View - Didn't find what you're looking for? Try [secondlife:///app/search/all Search]. + Didn't find what you're looking for? Try [secondlife:///app/search/all/[SEARCH_TERM] Search]. + Didn't find what you're looking for? Try [secondlife:///app/search/places/[SEARCH_TERM] Search]. Drag a landmark here to add it to your favorites. You do not have a copy of this texture in your inventory -- cgit v1.3 From d6edc4cd12c015003cd665028b69a7b34b3aa54f Mon Sep 17 00:00:00 2001 From: Eugene Mutavchi Date: Fri, 30 Apr 2010 18:27:24 +0300 Subject: 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-engine --- indra/llui/llflatlistview.cpp | 160 ++++++++++++++++++++++++++------- indra/llui/llflatlistview.h | 17 +++- indra/newview/llinventoryitemslist.cpp | 58 ++++++++++-- indra/newview/llinventoryitemslist.h | 26 ++++-- indra/newview/lloutfitslist.cpp | 28 ++++++ 5 files changed, 240 insertions(+), 49 deletions(-) (limited to 'indra/llui/llflatlistview.cpp') diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp index ec247b25c3..bea2572ff8 100644 --- a/indra/llui/llflatlistview.cpp +++ b/indra/llui/llflatlistview.cpp @@ -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 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 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 diff --git a/indra/llui/llflatlistview.h b/indra/llui/llflatlistview.h index 4f718ab0dc..6395805aab 100644 --- a/indra/llui/llflatlistview.h +++ b/indra/llui/llflatlistview.h @@ -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 diff --git a/indra/newview/llinventoryitemslist.cpp b/indra/newview/llinventoryitemslist.cpp index 8dfdb0788a..9719de4650 100644 --- a/indra/newview/llinventoryitemslist.cpp +++ b/indra/newview/llinventoryitemslist.cpp @@ -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; diff --git a/indra/newview/llinventoryitemslist.h b/indra/newview/llinventoryitemslist.h index 152aafbd7e..bc04eb6f5b 100644 --- a/indra/newview/llinventoryitemslist.h +++ b/indra/newview/llinventoryitemslist.h @@ -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 + struct Params : public LLInitParam::Block { 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(). diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp index b103ec45d0..18bd610dd9 100644 --- a/indra/newview/lloutfitslist.cpp +++ b/indra/newview/lloutfitslist.cpp @@ -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 (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")); + } + } + } } ////////////////////////////////////////////////////////////////////////// -- cgit v1.3 From 582f6502ff755d3c86c1daa854425e29ae2654ba Mon Sep 17 00:00:00 2001 From: Eugene Mutavchi Date: Wed, 5 May 2010 14:23:22 +0300 Subject: Related to low EXT-6823 (Implement a possibility to select several residents by "Shift" button) - fix improved to keep order of multiple selection and to reset the inconsecutive selection. Reviewed by Richard Nelson at https://codereview.productengine.com/secondlife/r/238/ --HG-- branch : product-engine --- indra/llui/llflatlistview.cpp | 44 ++++++++++++++++++++++++++++++++++++------- indra/llui/llflatlistview.h | 2 ++ 2 files changed, 39 insertions(+), 7 deletions(-) (limited to 'indra/llui/llflatlistview.cpp') diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp index bea2572ff8..6bd16c9ee6 100644 --- a/indra/llui/llflatlistview.cpp +++ b/indra/llui/llflatlistview.cpp @@ -379,6 +379,7 @@ LLFlatListView::LLFlatListView(const LLFlatListView::Params& p) , mCommitOnSelectionChange(false) , mPrevNotifyParentRect(LLRect()) , mNoItemsCommentTextbox(NULL) + , mIsConsecutiveSelection(false) { mBorderThickness = getBorderWidth(); @@ -536,6 +537,7 @@ void LLFlatListView::onItemMouseClick(item_pair_t* item_pair, MASK mask) return; bool grab_items = false; + bool reverse = false; pairs_list_t pairs_to_select; // Pick out items from list between last selected and current clicked item_pair. @@ -547,6 +549,8 @@ void LLFlatListView::onItemMouseClick(item_pair_t* item_pair, MASK mask) item_pair_t* cur = *iter; if (cur == last_selected_pair || cur == item_pair) { + // We've got reverse selection if last grabed item isn't a new selection. + reverse = grab_items && (cur != item_pair); grab_items = !grab_items; // Skip last selected and current clicked item pairs. continue; @@ -562,26 +566,35 @@ void LLFlatListView::onItemMouseClick(item_pair_t* item_pair, MASK mask) } } - if (select_item) + if (reverse) { - pairs_to_select.push_back(item_pair); + pairs_to_select.reverse(); } + pairs_to_select.push_back(item_pair); + for (pairs_iterator_t iter = pairs_to_select.begin(), iter_end = pairs_to_select.end(); iter != iter_end; ++iter) { item_pair_t* pair_to_select = *iter; - selectItemPair(pair_to_select, true); + if (isSelected(pair_to_select)) + { + // Item was already selected but there is a need to keep order from last selected pair to new selection. + // Do it here to prevent extra mCommitOnSelectionChange in selectItemPair(). + mSelectedItemPairs.remove(pair_to_select); + mSelectedItemPairs.push_back(pair_to_select); + } + else + { + selectItemPair(pair_to_select, true); + } } if (!select_item) { - // Item was already selected but there is a need to update last selected item and its border. - // Do it here to prevent extra mCommitOnSelectionChange in selectItemPair(). - mSelectedItemPairs.remove(item_pair); - mSelectedItemPairs.push_back(item_pair); + // Update last selected item border. mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1)); } return; @@ -749,6 +762,8 @@ bool LLFlatListView::selectItemPair(item_pair_t* item_pair, bool select) // Stretch selected item rect to ensure it won't be clipped mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1)); + // By default mark it as not consecutive selection + mIsConsecutiveSelection = false; return true; } @@ -823,6 +838,17 @@ bool LLFlatListView::selectNextItemPair(bool is_up_direction, bool reset_selecti if ( 0 == size() ) return false; + if (!mIsConsecutiveSelection) + { + // Leave only one item selected if list has not consecutive selection + if (mSelectedItemPairs.size() && !reset_selection) + { + item_pair_t* cur_sel_pair = mSelectedItemPairs.back(); + resetSelection(); + selectItemPair (cur_sel_pair, true); + } + } + if ( mSelectedItemPairs.size() ) { item_pair_t* to_sel_pair = NULL; @@ -877,6 +903,8 @@ bool LLFlatListView::selectNextItemPair(bool is_up_direction, bool reset_selecti } // Select/Deselect next item selectItemPair(select ? to_sel_pair : cur_sel_pair, select); + // Mark it as consecutive selection + mIsConsecutiveSelection = true; return true; } } @@ -888,6 +916,8 @@ bool LLFlatListView::selectNextItemPair(bool is_up_direction, bool reset_selecti selectLastItem(); else selectFirstItem(); + // Mark it as consecutive selection + mIsConsecutiveSelection = true; return true; } diff --git a/indra/llui/llflatlistview.h b/indra/llui/llflatlistview.h index 6395805aab..f4e0426f15 100644 --- a/indra/llui/llflatlistview.h +++ b/indra/llui/llflatlistview.h @@ -410,6 +410,8 @@ private: bool mKeepOneItemSelected; + bool mIsConsecutiveSelection; + /** All pairs of the list */ pairs_list_t mItemPairs; -- cgit v1.3