partial implementation of EXT-6723 Create specialized view of inventory for "clothing" accordion tab of outfit editor

- added grouping clothing items by wearable type (shirt, jeans etc.)
- added sorting clothing items by wearing order in groups
- added separators between items of the same wearable type (lists: clothing, body parts)
- added list specific button bars
- partially added dummy items for missing wearable types in the COF (clothing list)

Reviewed by Mike Antipov at https://codereview.productengine.com/secondlife/r/322

--HG--
branch : product-engine
meow-7.2.2
Igor Borovkov 2010-04-28 17:47:08 +03:00
parent 8e0a73f0ed
commit f9a120e898
7 changed files with 232 additions and 33 deletions

View File

@ -1092,7 +1092,8 @@ void LLAppearanceMgr::updateAppearanceFromCOF()
}
//preparing the list of wearables in the correct order for LLAgentWearables
std::sort(wear_items.begin(), wear_items.end(), sort_by_description);
sortItemsByActualDescription(wear_items);
LLWearableHoldingPattern* holder = new LLWearableHoldingPattern;
@ -1910,6 +1911,13 @@ bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_b
return result;
}
//static
void LLAppearanceMgr::sortItemsByActualDescription(LLInventoryModel::item_array_t& items)
{
if (items.size() < 2) return;
std::sort(items.begin(), items.end(), sort_by_description);
}
//#define DUMP_CAT_VERBOSE

View File

@ -48,6 +48,8 @@ class LLAppearanceMgr: public LLSingleton<LLAppearanceMgr>
friend class LLSingleton<LLAppearanceMgr>;
public:
typedef std::vector<LLInventoryModel::item_array_t> wearables_by_type_t;
void updateAppearanceFromCOF();
bool needToSaveCOF();
void updateCOF(const LLUUID& category, bool append = false);
@ -143,17 +145,17 @@ public:
bool moveWearable(LLViewerInventoryItem* item, bool closer_to_body);
static void sortItemsByActualDescription(LLInventoryModel::item_array_t& items);
//Divvy items into arrays by wearable type
static void divvyWearablesByType(const LLInventoryModel::item_array_t& items, wearables_by_type_t& items_by_type);
protected:
LLAppearanceMgr();
~LLAppearanceMgr();
private:
typedef std::vector<LLInventoryModel::item_array_t> wearables_by_type_t;
//Divvy items into arrays by wearable type
static void divvyWearablesByType(const LLInventoryModel::item_array_t& items, wearables_by_type_t& items_by_type);
//Check ordering information on wearables stored in links' descriptions and update if it is invalid
void updateClothingOrderingInfo();

View File

@ -94,48 +94,140 @@ void LLCOFWearables::refresh()
clear();
LLInventoryModel::cat_array_t cats;
LLInventoryModel::item_array_t items;
gInventory.collectDescendents(LLAppearanceMgr::getInstance()->getCOF(), cats, items, LLInventoryModel::EXCLUDE_TRASH);
if (items.empty()) return;
LLInventoryModel::item_array_t cof_items;
for (U32 i = 0; i < items.size(); ++i)
gInventory.collectDescendents(LLAppearanceMgr::getInstance()->getCOF(), cats, cof_items, LLInventoryModel::EXCLUDE_TRASH);
populateAttachmentsAndBodypartsLists(cof_items);
LLAppearanceMgr::wearables_by_type_t clothing_by_type(WT_COUNT);
LLAppearanceMgr::getInstance()->divvyWearablesByType(cof_items, clothing_by_type);
populateClothingList(clothing_by_type);
}
void LLCOFWearables::populateAttachmentsAndBodypartsLists(const LLInventoryModel::item_array_t& cof_items)
{
for (U32 i = 0; i < cof_items.size(); ++i)
{
LLViewerInventoryItem* item = items.get(i);
LLViewerInventoryItem* item = cof_items.get(i);
if (!item) continue;
const LLAssetType::EType item_type = item->getType();
if (item_type == LLAssetType::AT_CLOTHING) continue;
LLPanelInventoryListItem* item_panel = LLPanelInventoryListItem::createItemPanel(item);
if (!item_panel) continue;
switch (item->getType())
if (item_type == LLAssetType::AT_OBJECT)
{
case LLAssetType::AT_OBJECT:
mAttachments->addItem(item_panel, item->getUUID(), ADD_BOTTOM, false);
break;
case LLAssetType::AT_BODYPART:
mBodyParts->addItem(item_panel, item->getUUID(), ADD_BOTTOM, false);
break;
case LLAssetType::AT_CLOTHING:
mClothing->addItem(item_panel, item->getUUID(), ADD_BOTTOM, false);
break;
default: break;
mAttachments->addItem(item_panel, item->getUUID(), ADD_BOTTOM, false);
}
else if (item_type == LLAssetType::AT_BODYPART)
{
mBodyParts->addItem(item_panel, item->getUUID(), ADD_BOTTOM, false);
addWearableTypeSeparator(mBodyParts);
}
}
mAttachments->sort(); //*TODO by Name
mAttachments->notify(REARRANGE); //notifying the parent about the list's size change (cause items were added with rearrange=false)
mClothing->sort(); //*TODO by actual inventory item description
mClothing->notify(REARRANGE);
mBodyParts->sort(); //*TODO by name
if (mAttachments->size())
{
mAttachments->sort(); //*TODO by Name
mAttachments->notify(REARRANGE); //notifying the parent about the list's size change (cause items were added with rearrange=false)
}
if (mBodyParts->size())
{
mBodyParts->sort(); //*TODO by name
}
addListButtonBar(mBodyParts, "panel_bodyparts_list_button_bar.xml");
mBodyParts->notify(REARRANGE);
}
void LLCOFWearables::populateClothingList(LLAppearanceMgr::wearables_by_type_t& clothing_by_type)
{
llassert(clothing_by_type.size() == WT_COUNT);
addListButtonBar(mClothing, "panel_clothing_list_button_bar.xml");
for (U32 type = WT_SHIRT; type < WT_COUNT; ++type)
{
U32 size = clothing_by_type[type].size();
if (!size) continue;
LLAppearanceMgr::sortItemsByActualDescription(clothing_by_type[type]);
for (U32 i = 0; i < size; i++)
{
LLViewerInventoryItem* item = clothing_by_type[type][i];
LLPanelInventoryListItem* item_panel = LLPanelInventoryListItem::createItemPanel(item);
if (!item_panel) continue;
mClothing->addItem(item_panel, item->getUUID(), ADD_BOTTOM, false);
}
addWearableTypeSeparator(mClothing);
}
addClothingTypesDummies(clothing_by_type);
mClothing->notify(REARRANGE);
}
void LLCOFWearables::addListButtonBar(LLFlatListView* list, std::string xml_filename)
{
llassert(list);
llassert(xml_filename.length());
LLPanel::Params params;
LLPanel* button_bar = LLUICtrlFactory::create<LLPanel>(params);
LLUICtrlFactory::instance().buildPanel(button_bar, xml_filename);
LLRect rc = button_bar->getRect();
button_bar->reshape(list->getItemsRect().getWidth(), rc.getHeight());
list->addItem(button_bar, LLUUID::null, ADD_TOP, false);
}
//adding dummy items for missing wearable types
void LLCOFWearables::addClothingTypesDummies(const LLAppearanceMgr::wearables_by_type_t& clothing_by_type)
{
llassert(clothing_by_type.size() == WT_COUNT);
for (U32 type = WT_SHIRT; type < WT_COUNT; type++)
{
U32 size = clothing_by_type[type].size();
if (size) continue;
//*TODO create dummy item panel
//*TODO add dummy item panel -> mClothing->addItem(dummy_item_panel, item->getUUID(), ADD_BOTTOM, false);
addWearableTypeSeparator(mClothing);
}
}
void LLCOFWearables::addWearableTypeSeparator(LLFlatListView* list)
{
llassert(list);
static LLXMLNodePtr separator_xml_node = getXMLNode("panel_wearable_type_separator.xml");
if (separator_xml_node->isNull()) return;
LLPanel* separator = LLUICtrlFactory::defaultBuilder<LLPanel>(separator_xml_node, NULL, NULL);
LLRect rc = separator->getRect();
rc.setOriginAndSize(0, 0, list->getItemsRect().getWidth(), rc.getHeight());
separator->setRect(rc);
list->addItem(separator, LLUUID::null, ADD_BOTTOM, false);
}
LLUUID LLCOFWearables::getSelectedUUID()
{
if (!mLastSelectedList) return LLUUID::null;
@ -150,4 +242,17 @@ void LLCOFWearables::clear()
mBodyParts->clear();
}
LLXMLNodePtr LLCOFWearables::getXMLNode(std::string xml_filename)
{
LLXMLNodePtr xmlNode = NULL;
bool success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, xmlNode);
if (!success)
{
llwarning("Failed to read xml", 0);
return NULL;
}
return xmlNode;
}
//EOF

View File

@ -34,6 +34,8 @@
#define LL_LLCOFWEARABLES_H
#include "llpanel.h"
#include "llinventorymodel.h"
#include "llappearancemgr.h"
class LLFlatListView;
@ -52,8 +54,16 @@ public:
protected:
void populateAttachmentsAndBodypartsLists(const LLInventoryModel::item_array_t& cof_items);
void populateClothingList(LLAppearanceMgr::wearables_by_type_t& clothing_by_type);
void addListButtonBar(LLFlatListView* list, std::string xml_filename);
void addClothingTypesDummies(const LLAppearanceMgr::wearables_by_type_t& clothing_by_type);
void addWearableTypeSeparator(LLFlatListView* list);
void onSelectionChange(LLFlatListView* selected_list);
LLXMLNodePtr LLCOFWearables::getXMLNode(std::string xml_filename);
LLFlatListView* mAttachments;
LLFlatListView* mClothing;
LLFlatListView* mBodyParts;

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<panel
follows="left|right|top"
height="35"
layout="topleft"
left="0"
name="clothing_list_button_bar_panel"
top="0"
visible="true"
width="300">
<button
follows="top|left"
height="25"
image_hover_unselected="Toolbar_Middle_Over"
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
label="Switch"
layout="topleft"
left="5"
name="switch_btn"
top="5"
width="45" />
<button
follows="top|right"
height="25"
image_hover_unselected="Toolbar_Middle_Over"
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
label="Shop >"
layout="topleft"
right="-5"
name="bodyparts_shop_btn"
top="5"
width="61" />
</panel>

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<panel
follows="left|right|top"
height="35"
layout="topleft"
left="0"
name="clothing_list_button_bar_panel"
top="0"
visible="true"
width="500">
<button
follows="top|left"
height="25"
image_hover_unselected="Toolbar_Middle_Over"
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
is_toggle="true"
label="Add +"
layout="topleft"
left="5"
name="add_btn"
top="5"
width="45" />
<button
follows="top|right"
height="25"
image_hover_unselected="Toolbar_Middle_Over"
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
label="Shop >"
layout="topleft"
right="-5"
name="clothing_shop_btn"
top="5"
width="61" />
</panel>

View File

@ -14,6 +14,7 @@
height="373"
layout="topleft"
left="3"
single_expansion="true"
top="0"
name="cof_wearables_accordion"
background_visible="true"