From 9eabffe430cd0c7b64f2cb796997dfd74a0cab82 Mon Sep 17 00:00:00 2001 From: Maxim Nikolenko Date: Mon, 12 Jun 2023 19:44:29 +0300 Subject: [PATCH 01/27] SL-19825 do not mix bool and BOOL types --- indra/llui/llfolderview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp index 7e5a9efb37..fa5ff76a2e 100644 --- a/indra/llui/llfolderview.cpp +++ b/indra/llui/llfolderview.cpp @@ -1472,7 +1472,7 @@ BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask ) } } - bool item_clicked = false; + BOOL item_clicked = FALSE; for (selected_items_t::iterator item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) { item_clicked |= (*item_it)->getRect().pointInRect(x, y); From bc67a7445cf8113708d94c5759a3509361a23e78 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 12 Jun 2023 23:00:34 +0300 Subject: [PATCH 02/27] SL-19823 Fix active panel not existing --- indra/newview/llinventorybridge.cpp | 4 ++-- indra/newview/llpanelmaininventory.cpp | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 3c1e58205a..8b439449c5 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -925,7 +925,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, addDeleteContextMenuOptions(items, disabled_items); } - if (!isPanelActive("All Items") && !isPanelActive("single_folder_inv") && !isPanelActive("comb_single_folder_inv")) + if (!isPanelActive("All Items") && !isPanelActive("comb_single_folder_inv")) { items.push_back(std::string("Show in Main Panel")); } @@ -4419,7 +4419,7 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& { items.push_back(std::string("open_in_new_window")); items.push_back(std::string("Open Folder Separator")); - if(isPanelActive("single_folder_inv")) + if(isPanelActive("comb_single_folder_inv")) { items.push_back(std::string("open_in_current_window")); } diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index e133bd37a1..7259ecf788 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -1503,9 +1503,10 @@ void LLPanelMainInventory::onAddButtonClick() void LLPanelMainInventory::setActivePanel() { + // Todo: should cover gallery mode in some way if(mSingleFolderMode && isListViewMode()) { - mActivePanel = getChild("single_folder_inv"); + mActivePanel = getChild("comb_single_folder_inv"); } else if(mSingleFolderMode && isCombinationViewMode()) { From 546c6a09d8ddcd8a327dd9dc76e122f6d98effb9 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 12 Jun 2023 23:08:12 +0300 Subject: [PATCH 03/27] SL-19823 Fix list view not focusing properly --- indra/newview/llpanelmaininventory.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index 7259ecf788..713ae21198 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -2425,14 +2425,17 @@ void LLPanelMainInventory::updateCombinationVisibility() mCombinationListLayoutPanel->setShape(list_latout, true /*tell stack to account for new shape*/); } } + } - if (mCombInvUUIDNeedsRename.notNull() && mCombinationInventoryPanel->areViewsInitialized()) - { - mCombinationInventoryPanel->setSelectionByID(mCombInvUUIDNeedsRename, TRUE); - mCombinationInventoryPanel->getRootFolder()->scrollToShowSelection(); - mCombinationInventoryPanel->getRootFolder()->setNeedsAutoRename(TRUE); - mCombInvUUIDNeedsRename.setNull(); - } + if (mSingleFolderMode + && !isGalleryViewMode() + && mCombInvUUIDNeedsRename.notNull() + && mCombinationInventoryPanel->areViewsInitialized()) + { + mCombinationInventoryPanel->setSelectionByID(mCombInvUUIDNeedsRename, TRUE); + mCombinationInventoryPanel->getRootFolder()->scrollToShowSelection(); + mCombinationInventoryPanel->getRootFolder()->setNeedsAutoRename(TRUE); + mCombInvUUIDNeedsRename.setNull(); } } From 7f6ad82a2a76ed1f96bf1ca61cacc51151fe74bf Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 12 Jun 2023 23:19:02 +0300 Subject: [PATCH 04/27] SL-19823 Additional logging --- indra/llui/llfolderview.cpp | 3 +++ indra/newview/llinventorypanel.cpp | 8 +++++++- indra/newview/llpanelmaininventory.cpp | 12 +++++++++--- indra/newview/llviewerinventory.cpp | 1 + 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp index fa5ff76a2e..630d93cde0 100644 --- a/indra/llui/llfolderview.cpp +++ b/indra/llui/llfolderview.cpp @@ -702,6 +702,7 @@ void LLFolderView::draw() { // renamer is not connected to the item we are renaming in any form so manage it manually // TODO: consider stopping on any scroll action instead of when out of visible area + LL_DEBUGS("Inventory") << "Renamer out of bounds, hiding" << LL_ENDL; finishRenamingItem(); } @@ -1046,6 +1047,8 @@ void LLFolderView::paste() // public rename functionality - can only start the process void LLFolderView::startRenamingSelectedItem( void ) { + LL_DEBUGS("Inventory") << "Starting inventory renamer" << LL_ENDL; + // make sure selection is visible scrollToShowSelection(); diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 067758b99c..8f38141e87 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -1486,6 +1486,10 @@ void LLInventoryPanel::onSelectionChange(const std::deque& it { fv->startRenamingSelectedItem(); } + else + { + LL_DEBUGS("Inventory") << "Failed to start renemr, no items selected" << LL_ENDL; + } } std::set selected_items = mFolderRoot.get()->getSelectionList(); @@ -1838,6 +1842,7 @@ void LLInventoryPanel::openInventoryPanelAndSetSelection(BOOL auto_open, const L LLPanelMainInventory* main_panel = inventory_panel->getMainInventoryPanel(); if(main_panel->isSingleFolderMode() && main_panel->isGalleryViewMode()) { + LL_DEBUGS("Inventory") << "Opening gallery panel for item" << obj_id << LL_ENDL; main_panel->setGallerySelection(obj_id); return; } @@ -1850,6 +1855,7 @@ void LLInventoryPanel::openInventoryPanelAndSetSelection(BOOL auto_open, const L const LLInventoryObject *obj = gInventory.getObject(obj_id); if (obj) { + LL_DEBUGS("Inventory") << "Opening main inventory panel for item" << obj_id << LL_ENDL; main_inventory->setSingleFolderViewRoot(obj->getParentUUID(), false); main_inventory->setGallerySelection(obj_id); return; @@ -1859,7 +1865,7 @@ void LLInventoryPanel::openInventoryPanelAndSetSelection(BOOL auto_open, const L if (active_panel) { - LL_DEBUGS("Messaging") << "Highlighting" << obj_id << LL_ENDL; + LL_DEBUGS("Messaging", "Inventory") << "Highlighting" << obj_id << LL_ENDL; if (reset_filter) { diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index 713ae21198..05816f5f37 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -199,7 +199,7 @@ BOOL LLPanelMainInventory::postBuild() } // Now load the stored settings from disk, if available. std::string filterSaveName(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, FILTERS_FILENAME)); - LL_INFOS() << "LLPanelMainInventory::init: reading from " << filterSaveName << LL_ENDL; + LL_INFOS("Inventory") << "LLPanelMainInventory::init: reading from " << filterSaveName << LL_ENDL; llifstream file(filterSaveName.c_str()); LLSD savedFilterState; if (file.is_open()) @@ -510,8 +510,6 @@ void LLPanelMainInventory::doCreate(const LLSD& userdata) { if(isCombinationViewMode()) { - //show layout and inventory panel before adding the item - //to avoid wrong position of the 'renamer' mForceShowInvLayout = true; } @@ -524,6 +522,13 @@ void LLPanelMainInventory::doCreate(const LLSD& userdata) { // might need to refresh visibility, delay rename panel->mCombInvUUIDNeedsRename = new_id; + + if (panel->isCombinationViewMode()) + { + panel->mForceShowInvLayout = true; + } + + LL_DEBUGS("Inventory") << "Done creating inventory: " << new_id << LL_ENDL; } }; menu_create_inventory_item(NULL, getCurrentSFVRoot(), userdata, LLUUID::null, callback_created); @@ -541,6 +546,7 @@ void LLPanelMainInventory::doCreate(const LLSD& userdata) if (panel) { panel->setGallerySelection(new_id); + LL_DEBUGS("Inventory") << "Done creating inventory: " << new_id << LL_ENDL; } } }; diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 117cdc5367..68619cb1a8 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -1774,6 +1774,7 @@ void menu_create_inventory_item(LLInventoryPanel* panel, LLUUID dest_id, const L { panel->setSelectionByID(new_category_id, TRUE); } + LL_DEBUGS(LOG_INV) << "Done creating inventory: " << new_category_id << LL_ENDL; }; } else if (created_cb != NULL) From 19ed999be0d0cb2460e8962824478a6658e026c9 Mon Sep 17 00:00:00 2001 From: Maxim Nikolenko Date: Tue, 13 Jun 2023 16:23:19 +0300 Subject: [PATCH 05/27] SL-19858 FIXED Viewer crash when creating a new Setting in inventory --- indra/newview/llsettingsvo.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/indra/newview/llsettingsvo.cpp b/indra/newview/llsettingsvo.cpp index 4cfa6f2f85..0cfdf67ed5 100644 --- a/indra/newview/llsettingsvo.cpp +++ b/indra/newview/llsettingsvo.cpp @@ -97,10 +97,15 @@ namespace //========================================================================= void LLSettingsVOBase::createNewInventoryItem(LLSettingsType::type_e stype, const LLUUID& parent_id, std::function created_cb) { - inventory_result_fn cb = [created_cb](LLUUID asset_id, LLUUID inventory_id, LLUUID object_id, LLSD results) + inventory_result_fn cb = NULL; + + if (created_cb != NULL) { - created_cb(inventory_id); - }; + cb = [created_cb](LLUUID asset_id, LLUUID inventory_id, LLUUID object_id, LLSD results) + { + created_cb(inventory_id); + }; + } createNewInventoryItem(stype, parent_id, cb); } From 14eadadf993eb4f89188aa4cd8ea0859a86374d3 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 13 Jun 2023 22:41:49 +0300 Subject: [PATCH 06/27] SL-19857 Exception at category creation --- indra/newview/llinventorymodel.cpp | 5 +++-- indra/newview/llviewerinventory.cpp | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 59b34cc95c..ed375661b8 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -1031,13 +1031,14 @@ void LLInventoryModel::createNewCategory(const LLUUID& parent_id, { if (new_category.isNull()) { - if (callback) + if (callback && !callback.empty()) { callback(new_category); } return; } + // todo: not needed since AIS does the accounting? LLViewerInventoryCategory* folderp = gInventory.getCategory(new_category); if (!folderp) { @@ -1057,7 +1058,7 @@ void LLInventoryModel::createNewCategory(const LLUUID& parent_id, updateCategory(cat); } - if (callback) + if (callback && !callback.empty()) { callback(new_category); } diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 68619cb1a8..94390b899d 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -1766,7 +1766,7 @@ void menu_create_inventory_item(LLInventoryPanel* panel, LLUUID dest_id, const L if (panel) { LLHandle handle = panel->getHandle(); - std::function callback_cat_created = [handle](const LLUUID& new_category_id) + callback_cat_created = [handle](const LLUUID& new_category_id) { gInventory.notifyObservers(); LLInventoryPanel* panel = static_cast(handle.get()); From 71534d8fa7083355271eba69a7b6045f0c34463f Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 14 Jun 2023 02:03:38 +0300 Subject: [PATCH 07/27] SL-19686 WIP Switching single folder view for large inventories causes stalls --- indra/newview/llinventorypanel.cpp | 54 ++++++++++++------- indra/newview/llinventorypanel.h | 3 +- indra/newview/llpanelmaininventory.cpp | 10 ++++ indra/newview/llpanelmaininventory.h | 1 + indra/newview/llsidepanelinventory.cpp | 13 +++++ indra/newview/llsidepanelinventory.h | 1 + .../default/xui/en/panel_main_inventory.xml | 3 ++ 7 files changed, 64 insertions(+), 21 deletions(-) diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 8f38141e87..ac156b9e72 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -162,7 +162,8 @@ LLInventoryPanel::LLInventoryPanel(const LLInventoryPanel::Params& p) : mInventoryViewModel(p.name), mGroupedItemBridge(new LLFolderViewGroupedItemBridge), mFocusSelection(false), - mBuildChildrenViews(true) + mBuildChildrenViews(true), + mRootInited(false) { mInvFVBridgeBuilder = &INVENTORY_BRIDGE_BUILDER; @@ -286,6 +287,7 @@ void LLInventoryPanel::initFolderRoot() // build the views starting with that folder. LLFolderView* folder_view = createFolderRoot(root_id); mFolderRoot = folder_view->getHandle(); + mRootInited = true; addItemID(root_id, mFolderRoot.get()); } @@ -318,22 +320,9 @@ void LLInventoryPanel::initFolderRoot() mCompletionObserver = new LLInvPanelComplObserver(boost::bind(&LLInventoryPanel::onItemsCompletion, this)); mInventory->addObserver(mCompletionObserver); - if (mBuildViewsOnInit && mViewsInitialized == VIEWS_UNINITIALIZED) + if (mBuildViewsOnInit) { - // Build view of inventory if we need default full hierarchy and inventory is ready, otherwise do in onIdle. - // Initializing views takes a while so always do it onIdle if viewer already loaded. - if (mInventory->isInventoryUsable() - && LLStartUp::getStartupState() <= STATE_WEARABLES_WAIT) - { - // Usually this happens on login, so we have less time constraits, but too long and we can cause a disconnect - const F64 max_time = 20.f; - initializeViews(max_time); - } - else - { - mViewsInitialized = VIEWS_INITIALIZING; - gIdleCallbacks.addFunction(onIdle, (void*)this); - } + initializeViewBuilding(); } if (mSortOrderSetting != INHERIT_SORT_ORDER) @@ -366,13 +355,38 @@ void LLInventoryPanel::initFolderRoot() mClipboardState = LLClipboard::instance().getGeneration(); } +void LLInventoryPanel::initializeViewBuilding() +{ + if (mViewsInitialized == VIEWS_UNINITIALIZED) + { + LL_DEBUGS("Inventory") << "Setting views for " << getName() << " to initialize" << LL_ENDL; + // Build view of inventory if we need default full hierarchy and inventory is ready, otherwise do in onIdle. + // Initializing views takes a while so always do it onIdle if viewer already loaded. + if (mInventory->isInventoryUsable() + && LLStartUp::getStartupState() <= STATE_WEARABLES_WAIT) + { + // Usually this happens on login, so we have less time constraits, but too long and we can cause a disconnect + const F64 max_time = 20.f; + initializeViews(max_time); + } + else + { + mViewsInitialized = VIEWS_INITIALIZING; + gIdleCallbacks.addFunction(onIdle, (void*)this); + } + } +} + /*virtual*/ void LLInventoryPanel::onVisibilityChange(BOOL new_visibility) { if (new_visibility && mViewsInitialized == VIEWS_UNINITIALIZED) { - mViewsInitialized = VIEWS_INITIALIZING; - gIdleCallbacks.addFunction(onIdle, (void*)this); + // first call can be from tab initialization + if (gFloaterView->getParentFloater(this) != NULL) + { + initializeViewBuilding(); + } } LLPanel::onVisibilityChange(new_visibility); } @@ -893,6 +907,7 @@ void LLInventoryPanel::idle(void* user_data) void LLInventoryPanel::initializeViews(F64 max_time) { if (!gInventory.isInventoryUsable()) return; + if (!mRootInited) return; mViewsInitialized = VIEWS_BUILDING; @@ -1063,7 +1078,7 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id, if (objectp->getType() >= LLAssetType::AT_COUNT) { // Example: Happens when we add assets of new, not yet supported type to library - LL_DEBUGS() << "LLInventoryPanel::buildViewsTree called with unknown objectp->mType : " + LL_DEBUGS("Inventory") << "LLInventoryPanel::buildViewsTree called with unknown objectp->mType : " << ((S32) objectp->getType()) << " name " << objectp->getName() << " UUID " << objectp->getUUID() << LL_ENDL; @@ -2134,7 +2149,6 @@ LLInventorySingleFolderPanel::LLInventorySingleFolderPanel(const Params& params) : LLInventoryPanel(params) { mBuildChildrenViews = false; - mRootInited = false; getFilter().setSingleFolderMode(true); getFilter().setEmptyLookupMessage("InventorySingleFolderNoMatches"); getFilter().setDefaultEmptyLookupMessage("InventorySingleFolderEmpty"); diff --git a/indra/newview/llinventorypanel.h b/indra/newview/llinventorypanel.h index 8d05fcb8b9..20c9584015 100644 --- a/indra/newview/llinventorypanel.h +++ b/indra/newview/llinventorypanel.h @@ -269,6 +269,7 @@ public: void changeFolderRoot(const LLUUID& new_id) {}; void initFolderRoot(); + void initializeViewBuilding(); protected: void openStartFolderOrMyInventory(); // open the first level of inventory @@ -306,7 +307,7 @@ protected: */ const LLInventoryFolderViewModelBuilder* mInvFVBridgeBuilder; - bool mBuildChildrenViews; + bool mBuildChildrenViews; // build root and children bool mRootInited; diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index 05816f5f37..eb0ed52317 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -1530,6 +1530,16 @@ void LLPanelMainInventory::initSingleFolderRoot(const LLUUID& start_folder_id) mCombinationInventoryPanel->initFolderRoot(start_folder_id); } +void LLPanelMainInventory::initInventoryViews() +{ + LLInventoryPanel* all_item = getChild(ALL_ITEMS); + all_item->initializeViewBuilding(); + LLInventoryPanel* recent_item = getChild(RECENT_ITEMS); + recent_item->initializeViewBuilding(); + LLInventoryPanel* worn_item = getChild(WORN_ITEMS); + worn_item->initializeViewBuilding(); +} + void LLPanelMainInventory::toggleViewMode() { if(mSingleFolderMode && isCombinationViewMode()) diff --git a/indra/newview/llpanelmaininventory.h b/indra/newview/llpanelmaininventory.h index ee314d1cf3..bdc62fd8f7 100644 --- a/indra/newview/llpanelmaininventory.h +++ b/indra/newview/llpanelmaininventory.h @@ -116,6 +116,7 @@ public: void onViewModeClick(); void toggleViewMode(); void initSingleFolderRoot(const LLUUID& start_folder_id = LLUUID::null); + void initInventoryViews(); void onUpFolderClicked(); void onBackFolderClicked(); void onForwardFolderClicked(); diff --git a/indra/newview/llsidepanelinventory.cpp b/indra/newview/llsidepanelinventory.cpp index bd6f846268..5f30ef3219 100644 --- a/indra/newview/llsidepanelinventory.cpp +++ b/indra/newview/llsidepanelinventory.cpp @@ -212,6 +212,14 @@ BOOL LLSidepanelInventory::postBuild() gSavedSettings.getControl("InventoryDisplayInbox")->getCommitSignal()->connect(boost::bind(&handleInventoryDisplayInboxChanged)); + LLFloater *floater = dynamic_cast(getParent()); + if (floater && floater->getKey().isUndefined() && !sLoginCompleted) + { + // see get_instance_num(); + // Primary inventory floater will have undefined key + initInventoryViews(); + } + return TRUE; } @@ -425,6 +433,11 @@ void LLSidepanelInventory::showInventoryPanel() mInventoryPanel->setVisible(TRUE); } +void LLSidepanelInventory::initInventoryViews() +{ + mPanelMainInventory->initInventoryViews(); +} + bool LLSidepanelInventory::canShare() { LLInventoryPanel* inbox = mInventoryPanelInbox.get(); diff --git a/indra/newview/llsidepanelinventory.h b/indra/newview/llsidepanelinventory.h index 3a252a87c6..08989bb6af 100644 --- a/indra/newview/llsidepanelinventory.h +++ b/indra/newview/llsidepanelinventory.h @@ -67,6 +67,7 @@ public: std::set getInboxSelectionList(); void showInventoryPanel(); + void initInventoryViews(); // checks can share selected item(s) bool canShare(); diff --git a/indra/newview/skins/default/xui/en/panel_main_inventory.xml b/indra/newview/skins/default/xui/en/panel_main_inventory.xml index 3391c1d00c..64b885c222 100644 --- a/indra/newview/skins/default/xui/en/panel_main_inventory.xml +++ b/indra/newview/skins/default/xui/en/panel_main_inventory.xml @@ -234,6 +234,7 @@ name="All Items" sort_order_setting="InventorySortOrder" show_item_link_overlays="true" + preinitialize_views="false" top="16" width="288"> @@ -252,6 +253,7 @@ left_delta="0" name="Recent Items" show_item_link_overlays="true" + preinitialize_views="false" width="290"> @@ -265,6 +267,7 @@ bg_opaque_color="DkGray2" bg_alpha_color="DkGray2" background_visible="true" + preinitialize_views="false" border="false" bevel_style="none" scroll.reserve_scroll_corner="false"> From 3a359224565aabec518a097ae502735a0d8244b0 Mon Sep 17 00:00:00 2001 From: Maxim Nikolenko Date: Wed, 14 Jun 2023 16:24:56 +0300 Subject: [PATCH 08/27] SL-19867 'Open in new window' option is missing for 'Trash' and 'Current Outfit' folders --- indra/newview/llinventorybridge.cpp | 34 +++++++++++++++++++---------- indra/newview/llinventorybridge.h | 1 + 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 8b439449c5..02a29f2ac9 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -4268,6 +4268,7 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items if (model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT) == mUUID) { items.push_back(std::string("Copy outfit list to clipboard")); + addOpenFolderMenuOptions(flags, items); } //Added by aura to force inventory pull on right-click to display folder options correctly. 07-17-06 @@ -4374,9 +4375,12 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& if(!category) return; const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); - if (trash_id == mUUID) return; - if (isItemInTrash()) return; - + if ((trash_id == mUUID) || isItemInTrash()) + { + addOpenFolderMenuOptions(flags, items); + return; + } + if (!isItemRemovable()) { disabled_items.push_back(std::string("Delete")); @@ -4415,15 +4419,7 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& return; } - if ((flags & ITEM_IN_MULTI_SELECTION) == 0) - { - items.push_back(std::string("open_in_new_window")); - items.push_back(std::string("Open Folder Separator")); - if(isPanelActive("comb_single_folder_inv")) - { - items.push_back(std::string("open_in_current_window")); - } - } + addOpenFolderMenuOptions(flags, items); #ifndef LL_RELEASE_FOR_DOWNLOAD if (LLFolderType::lookupIsProtectedType(type) && is_agent_inventory) @@ -4502,6 +4498,20 @@ void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) menu.arrangeAndClear(); } +void LLFolderBridge::addOpenFolderMenuOptions(U32 flags, menuentry_vec_t& items) +{ + if ((flags & ITEM_IN_MULTI_SELECTION) == 0) + { + items.push_back(std::string("open_in_new_window")); + items.push_back(std::string("Open Folder Separator")); + items.push_back(std::string("Copy Separator")); + if(isPanelActive("comb_single_folder_inv")) + { + items.push_back(std::string("open_in_current_window")); + } + } +} + bool LLFolderBridge::hasChildren() const { LLInventoryModel* model = getInventoryModel(); diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index eaa7166631..30a9bbe372 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -339,6 +339,7 @@ public: protected: void buildContextMenuOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items); void buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items); + void addOpenFolderMenuOptions(U32 flags, menuentry_vec_t& items); //-------------------------------------------------------------------- // Menu callbacks From 143e103bcf2b9be2b249ce96e7f84a52469174c7 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 14 Jun 2023 23:59:43 +0300 Subject: [PATCH 09/27] SL-19686 WIP Switching single folder view for large inventories causes stalls #2 --- indra/newview/llinventorygallery.cpp | 34 ++++++++++++++++--- indra/newview/llinventorygallery.h | 4 +++ indra/newview/llinventorypanel.cpp | 45 +++++++++++++++++++++++++- indra/newview/llpanelmaininventory.cpp | 4 +++ indra/newview/llsidepanelinventory.cpp | 3 ++ 5 files changed, 85 insertions(+), 5 deletions(-) diff --git a/indra/newview/llinventorygallery.cpp b/indra/newview/llinventorygallery.cpp index 5edcb3148c..421949d9aa 100644 --- a/indra/newview/llinventorygallery.cpp +++ b/indra/newview/llinventorygallery.cpp @@ -85,6 +85,7 @@ LLInventoryGallery::LLInventoryGallery(const LLInventoryGallery::Params& p) mRowPanWidthFactor(p.row_panel_width_factor), mGalleryWidthFactor(p.gallery_width_factor), mIsInitialized(false), + mRootDirty(false), mNeedsArrange(false), mSearchType(LLInventoryFilter::SEARCHTYPE_NAME) { @@ -188,7 +189,19 @@ void LLInventoryGallery::setRootFolder(const LLUUID cat_id) mBackwardFolders.push_back(mFolderID); } mFolderID = cat_id; - updateRootFolder(); + dirtyRootFolder(); +} + +void LLInventoryGallery::dirtyRootFolder() +{ + if (getVisible()) + { + updateRootFolder(); + } + else + { + mRootDirty = true; + } } void LLInventoryGallery::updateRootFolder() @@ -260,6 +273,7 @@ void LLInventoryGallery::updateRootFolder() } reArrangeRows(); mIsInitialized = true; + mRootDirty = false; if (mScrollPanel) { @@ -305,6 +319,15 @@ void LLInventoryGallery::draw() } } +void LLInventoryGallery::onVisibilityChange(BOOL new_visibility) +{ + if (new_visibility && mRootDirty) + { + updateRootFolder(); + } + LLPanel::onVisibilityChange(new_visibility); +} + bool LLInventoryGallery::updateRowsIfNeeded() { if(((getRect().getWidth() - mRowPanelWidth) > mItemWidth) && mRowCount > 1) @@ -542,6 +565,7 @@ LLInventoryGalleryItem* LLInventoryGallery::buildGalleryItem(std::string name, L void LLInventoryGallery::buildGalleryPanel(int row_count) { LLPanel::Params params; + params.use_bounding_rect = false; mGalleryPanel = LLUICtrlFactory::create(params); reshapeGalleryPanel(row_count); } @@ -561,11 +585,12 @@ void LLInventoryGallery::reshapeGalleryPanel(int row_count) LLPanel* LLInventoryGallery::buildItemPanel(int left) { - LLPanel::Params lpparams; int top = 0; LLPanel* lpanel = NULL; if(mUnusedItemPanels.empty()) { + LLPanel::Params lpparams; + lpparams.use_bounding_rect = false; lpanel = LLUICtrlFactory::create(lpparams); } else @@ -585,6 +610,7 @@ LLPanel* LLInventoryGallery::buildItemPanel(int left) LLPanel* LLInventoryGallery::buildRowPanel(int left, int bottom) { LLPanel::Params sparams; + sparams.use_bounding_rect = false; LLPanel* stack = NULL; if(mUnusedRowPanels.empty()) { @@ -1787,7 +1813,7 @@ void LLInventoryGallery::onForwardFolder() mBackwardFolders.push_back(mFolderID); mFolderID = mForwardFolders.back(); mForwardFolders.pop_back(); - updateRootFolder(); + dirtyRootFolder(); } } @@ -1798,7 +1824,7 @@ void LLInventoryGallery::onBackwardFolder() mForwardFolders.push_back(mFolderID); mFolderID = mBackwardFolders.back(); mBackwardFolders.pop_back(); - updateRootFolder(); + dirtyRootFolder(); } } diff --git a/indra/newview/llinventorygallery.h b/indra/newview/llinventorygallery.h index 6fbd1556e5..8519ee0731 100644 --- a/indra/newview/llinventorygallery.h +++ b/indra/newview/llinventorygallery.h @@ -76,6 +76,7 @@ public: BOOL postBuild() override; void initGallery(); void draw() override; + void onVisibilityChange(BOOL new_visibility) override; BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, std::string& tooltip_msg) override; BOOL handleRightMouseDown(S32 x, S32 y, MASK mask) override; @@ -103,6 +104,7 @@ public: void setRootFolder(const LLUUID cat_id); void updateRootFolder(); LLUUID getRootFolder() { return mFolderID; } + bool isRootDirty() { return mRootDirty; } boost::signals2::connection setRootChangedCallback(callback_t cb); void onForwardFolder(); void onBackwardFolder(); @@ -168,6 +170,7 @@ protected: void applyFilter(LLInventoryGalleryItem* item, const std::string& filter_substring); bool checkAgainstFilters(LLInventoryGalleryItem* item, const std::string& filter_substring); static void onIdle(void* userdata); + void dirtyRootFolder(); LLInventoryCategoriesObserver* mCategoriesObserver; LLThumbnailsObserver* mThumbnailsObserver; @@ -176,6 +179,7 @@ protected: LLUUID mSelectedItemID; LLUUID mItemToSelect; bool mIsInitialized; + bool mRootDirty; selection_change_signal_t mSelectionChangeSignal; boost::signals2::signal mRootChangedSignal; diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index ac156b9e72..f7a24a09c0 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -1177,7 +1177,7 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id, else { create_children = true; - folder_view_item->setChildrenInited(true); + folder_view_item->setChildrenInited(mBuildChildrenViews); } break; } @@ -1218,6 +1218,11 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id, LLViewerInventoryItem::item_array_t* items; mInventory->lockDirectDescendentArrays(id, categories, items); + // Make sure panel won't lock in a loop over existing items if + // folder is enormous and at least some work gets done + const S32 MIN_ITEMS_PER_CALL = 500; + const S32 starting_item_count = mItemMap.size(); + LLFolderViewFolder *parentp = dynamic_cast(folder_view_item); if(categories) @@ -1243,6 +1248,22 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id, buildViewsTree(cat->getUUID(), id, cat, NULL, parentp, (mode == BUILD_ONE_FOLDER ? BUILD_NO_CHILDREN : mode), depth); } } + + if (!mBuildChildrenViews + && mode == BUILD_TIMELIMIT + && MIN_ITEMS_PER_CALL + starting_item_count < mItemMap.size()) + { + // Single folder view, check if we still have time + // + // Todo: make sure this causes no dupplciates, breaks nothing, + // especially filters and arrange + F64 curent_time = LLTimer::getTotalSeconds(); + if (mBuildViewsEndTime < curent_time) + { + mBuildViewsQueue.push_back(id); + break; + } + } } } @@ -1262,8 +1283,30 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id, LLFolderViewItem* view_itemp = getItemByID(item->getUUID()); buildViewsTree(item->getUUID(), id, item, view_itemp, parentp, mode, depth); } + + if (!mBuildChildrenViews + && mode == BUILD_TIMELIMIT + && MIN_ITEMS_PER_CALL + starting_item_count < mItemMap.size()) + { + // Single folder view, check if we still have time + // + // Todo: make sure this causes no dupplciates, breaks nothing, + // especially filters and arrange + F64 curent_time = LLTimer::getTotalSeconds(); + if (mBuildViewsEndTime < curent_time) + { + mBuildViewsQueue.push_back(id); + break; + } + } } } + + if (!mBuildChildrenViews) + { + // flat list is done initializing folder + folder_view_item->setChildrenInited(true); + } mInventory->unlockDirectDescendentArrays(id); } diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index eb0ed52317..a785d5adb7 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -2350,6 +2350,7 @@ void LLPanelMainInventory::updatePanelVisibility() // visibility will be controled by updateCombinationVisibility() mCombinationGalleryLayoutPanel->setVisible(true); + mCombinationGalleryPanel->setVisible(true); mCombinationListLayoutPanel->setVisible(true); } else @@ -2363,12 +2364,14 @@ void LLPanelMainInventory::updatePanelVisibility() comb_gallery_filter.markDefault(); mCombinationGalleryLayoutPanel->setVisible(mSingleFolderMode && isGalleryViewMode()); + mCombinationGalleryPanel->setVisible(mSingleFolderMode && isGalleryViewMode()); // to prevent or process updates mCombinationListLayoutPanel->setVisible(mSingleFolderMode && isListViewMode()); } } else { mCombinationGalleryLayoutPanel->setVisible(false); + mCombinationGalleryPanel->setVisible(false); // to prevent updates mCombinationListLayoutPanel->setVisible(false); } } @@ -2380,6 +2383,7 @@ void LLPanelMainInventory::updateCombinationVisibility() bool is_gallery_empty = !mCombinationGalleryPanel->hasVisibleItems(); bool show_inv_pane = mCombinationInventoryPanel->hasVisibleItems() || is_gallery_empty || mForceShowInvLayout; mCombinationGalleryLayoutPanel->setVisible(!is_gallery_empty); + mCombinationGalleryPanel->setVisible(true); // to make sure root updates are getting processed mCombinationListLayoutPanel->setVisible(show_inv_pane); mCombinationInventoryPanel->getRootFolder()->setForceArrange(!show_inv_pane); if(mCombinationInventoryPanel->hasVisibleItems()) diff --git a/indra/newview/llsidepanelinventory.cpp b/indra/newview/llsidepanelinventory.cpp index 5f30ef3219..5eb853f44c 100644 --- a/indra/newview/llsidepanelinventory.cpp +++ b/indra/newview/llsidepanelinventory.cpp @@ -215,6 +215,9 @@ BOOL LLSidepanelInventory::postBuild() LLFloater *floater = dynamic_cast(getParent()); if (floater && floater->getKey().isUndefined() && !sLoginCompleted) { + // Prefill inventory for primary inventory floater + // Other floaters should fill on visibility change + // // see get_instance_num(); // Primary inventory floater will have undefined key initInventoryViews(); From 275fa896c0cc35acee24d7fc4e952ab73428b7f8 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 15 Jun 2023 00:27:11 +0300 Subject: [PATCH 10/27] SL-19686 WIP Switching single folder view for large inventories causes stalls #3 --- indra/newview/llinventorygallery.cpp | 35 +++++++++++++--------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/indra/newview/llinventorygallery.cpp b/indra/newview/llinventorygallery.cpp index 421949d9aa..fe9605daa8 100644 --- a/indra/newview/llinventorygallery.cpp +++ b/indra/newview/llinventorygallery.cpp @@ -545,12 +545,11 @@ void LLInventoryGallery::removeFromLastRow(LLInventoryGalleryItem* item) LLInventoryGalleryItem* LLInventoryGallery::buildGalleryItem(std::string name, LLUUID item_id, LLAssetType::EType type, LLUUID thumbnail_id, LLInventoryType::EType inventory_type, U32 flags, bool is_link, bool is_worn) { LLInventoryGalleryItem::Params giparams; + giparams.visible = true; + giparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP); + giparams.rect(LLRect(0,mItemHeight, mItemWidth, 0)); + giparams.name = name; LLInventoryGalleryItem* gitem = LLUICtrlFactory::create(giparams); - gitem->reshape(mItemWidth, mItemHeight); - gitem->setVisible(true); - gitem->setFollowsLeft(); - gitem->setFollowsTop(); - gitem->setName(name); gitem->setUUID(item_id); gitem->setGallery(this); gitem->setType(type, inventory_type, flags, is_link); @@ -565,6 +564,8 @@ LLInventoryGalleryItem* LLInventoryGallery::buildGalleryItem(std::string name, L void LLInventoryGallery::buildGalleryPanel(int row_count) { LLPanel::Params params; + params.follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP); + params.visible = true; params.use_bounding_rect = false; mGalleryPanel = LLUICtrlFactory::create(params); reshapeGalleryPanel(row_count); @@ -578,9 +579,6 @@ void LLInventoryGallery::reshapeGalleryPanel(int row_count) LLRect rect = LLRect(left, bottom + height, left + mGalleryWidth, bottom); mGalleryPanel->setRect(rect); mGalleryPanel->reshape(mGalleryWidth, height); - mGalleryPanel->setVisible(true); - mGalleryPanel->setFollowsLeft(); - mGalleryPanel->setFollowsTop(); } LLPanel* LLInventoryGallery::buildItemPanel(int left) @@ -590,6 +588,9 @@ LLPanel* LLInventoryGallery::buildItemPanel(int left) if(mUnusedItemPanels.empty()) { LLPanel::Params lpparams; + lpparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP); + lpparams.visible = true; + lpparams.rect(LLRect(left, top + mItemHeight, left + mItemWidth + mItemHorizontalGap, top)); lpparams.use_bounding_rect = false; lpanel = LLUICtrlFactory::create(lpparams); } @@ -597,23 +598,22 @@ LLPanel* LLInventoryGallery::buildItemPanel(int left) { lpanel = mUnusedItemPanels.back(); mUnusedItemPanels.pop_back(); + + LLRect rect = LLRect(left, top + mItemHeight, left + mItemWidth + mItemHorizontalGap, top); + lpanel->setShape(rect, false); } - LLRect rect = LLRect(left, top + mItemHeight, left + mItemWidth + mItemHorizontalGap, top); - lpanel->setRect(rect); - lpanel->reshape(mItemWidth + mItemHorizontalGap, mItemHeight); - lpanel->setVisible(true); - lpanel->setFollowsLeft(); - lpanel->setFollowsTop(); return lpanel; } LLPanel* LLInventoryGallery::buildRowPanel(int left, int bottom) { - LLPanel::Params sparams; - sparams.use_bounding_rect = false; LLPanel* stack = NULL; if(mUnusedRowPanels.empty()) { + LLPanel::Params sparams; + sparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP); + sparams.use_bounding_rect = false; + sparams.visible = true; stack = LLUICtrlFactory::create(sparams); } else @@ -630,9 +630,6 @@ void LLInventoryGallery::moveRowPanel(LLPanel* stack, int left, int bottom) LLRect rect = LLRect(left, bottom + mRowPanelHeight, left + mRowPanelWidth, bottom); stack->setRect(rect); stack->reshape(mRowPanelWidth, mRowPanelHeight); - stack->setVisible(true); - stack->setFollowsLeft(); - stack->setFollowsTop(); } void LLInventoryGallery::setFilterSubString(const std::string& string) From 7ac094e92105122b22b4693dafae7f0cee72d9a0 Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Tue, 13 Jun 2023 08:45:13 +0200 Subject: [PATCH 11/27] SL-18977 Borders of inventory view in inventory floater seem to be cut a bit --- indra/llui/llscrollcontainer.cpp | 8 +- indra/llui/lltabcontainer.cpp | 8 +- indra/newview/llinventorypanel.cpp | 1 - .../default/xui/en/floater_my_inventory.xml | 28 +- .../default/xui/en/panel_main_inventory.xml | 479 +++++++++--------- .../default/xui/en/sidepanel_inventory.xml | 244 ++++----- 6 files changed, 363 insertions(+), 405 deletions(-) diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp index c4cb739f0a..5872b79bb0 100644 --- a/indra/llui/llscrollcontainer.cpp +++ b/indra/llui/llscrollcontainer.cpp @@ -124,9 +124,9 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p) mScrollbar[VERTICAL] = LLUICtrlFactory::create (sbparams); LLView::addChild( mScrollbar[VERTICAL] ); - LLRect horizontal_scroll_rect = mInnerRect; - horizontal_scroll_rect.mTop = horizontal_scroll_rect.mBottom + scrollbar_size; - horizontal_scroll_rect.mLeft += 3; + LLRect horizontal_scroll_rect; + horizontal_scroll_rect.mRight = mInnerRect.getWidth(); + horizontal_scroll_rect.mTop = scrollbar_size; sbparams.name("scrollable horizontal"); sbparams.rect(horizontal_scroll_rect); sbparams.orientation(LLScrollbar::HORIZONTAL); @@ -135,7 +135,7 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p) sbparams.page_size(mInnerRect.getWidth()); sbparams.step_size(VERTICAL_MULTIPLE); sbparams.visible(false); - sbparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_RIGHT); + sbparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM); sbparams.change_callback(p.scroll_callback); mScrollbar[HORIZONTAL] = LLUICtrlFactory::create (sbparams); LLView::addChild( mScrollbar[HORIZONTAL] ); diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp index 8c841540a5..d5d337848c 100644 --- a/indra/llui/lltabcontainer.cpp +++ b/indra/llui/lltabcontainer.cpp @@ -986,7 +986,7 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel) // Tab panel S32 tab_panel_top; S32 tab_panel_bottom; - if (!getTabsHidden()) + if (!getTabsHidden()) { if( getTabPosition() == LLTabContainer::TOP ) { @@ -1002,7 +1002,7 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel) } else { - //Scip tab button space if they are invisible(EXT - 576) + // Skip tab button space if tabs are invisible (EXT-576) tab_panel_top = getRect().getHeight(); tab_panel_bottom = LLPANEL_BORDER_WIDTH; } @@ -1017,9 +1017,9 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel) } else { - tab_panel_rect = LLRect(LLPANEL_BORDER_WIDTH, + tab_panel_rect = LLRect(LLPANEL_BORDER_WIDTH * 2, tab_panel_top, - getRect().getWidth()-LLPANEL_BORDER_WIDTH, + getRect().getWidth(), tab_panel_bottom ); } child->setFollowsAll(); diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index f7a24a09c0..2fefacf90b 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -298,7 +298,6 @@ void LLInventoryPanel::initFolderRoot() // Scroller LLRect scroller_view_rect = getRect(); scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom); - scroller_view_rect.mTop -= 3; LLScrollContainer::Params scroller_params(mParams.scroll()); scroller_params.rect(scroller_view_rect); mScroller = LLUICtrlFactory::create(scroller_params); diff --git a/indra/newview/skins/default/xui/en/floater_my_inventory.xml b/indra/newview/skins/default/xui/en/floater_my_inventory.xml index a9900f05b7..b75744d432 100644 --- a/indra/newview/skins/default/xui/en/floater_my_inventory.xml +++ b/indra/newview/skins/default/xui/en/floater_my_inventory.xml @@ -1,22 +1,22 @@ - + reuse_instance="true" > + diff --git a/indra/newview/skins/default/xui/en/panel_main_inventory.xml b/indra/newview/skins/default/xui/en/panel_main_inventory.xml index 64b885c222..b170885178 100644 --- a/indra/newview/skins/default/xui/en/panel_main_inventory.xml +++ b/indra/newview/skins/default/xui/en/panel_main_inventory.xml @@ -1,13 +1,12 @@ + min_width="240" > @@ -27,18 +26,18 @@ Multi_Folder_Mode Single_Folder_Mode + name="ItemcountText" + type="string" + length="1" + follows="left|top|right" + layout="topleft" + height="13" + left="10" + right="-10" + font="SansSerifMedium" + text_color="InventoryItemLinkColor" + use_ellipses="true" + top_pad="0"> Items: -